Statistics
| Branch: | Tag: | Revision:

root / pithos-macos / PithosPreferencesController.m @ 3916144e

History | View | Annotate | Download (22.9 kB)

1
//
2
//  PithosPreferencesController.m
3
//  pithos-macos
4
//
5
// Copyright 2011-2012 GRNET S.A. All rights reserved.
6
//
7
// Redistribution and use in source and binary forms, with or
8
// without modification, are permitted provided that the following
9
// conditions are met:
10
// 
11
//   1. Redistributions of source code must retain the above
12
//      copyright notice, this list of conditions and the following
13
//      disclaimer.
14
// 
15
//   2. Redistributions in binary form must reproduce the above
16
//      copyright notice, this list of conditions and the following
17
//      disclaimer in the documentation and/or other materials
18
//      provided with the distribution.
19
// 
20
// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
21
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
24
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31
// POSSIBILITY OF SUCH DAMAGE.
32
// 
33
// The views and conclusions contained in the software and
34
// documentation are those of the authors and should not be
35
// interpreted as representing official policies, either expressed
36
// or implied, of GRNET S.A.
37

    
38
#import "PithosPreferencesController.h"
39
#import "PithosBrowserController.h"
40
#import "PithosAccountNode.h"
41
#import "PithosContainerNode.h"
42
#import "PithosSubdirNode.h"
43
#import "PithosObjectNode.h"
44
#import "PithosEmptyNode.h"
45
#import "PithosAccount.h"
46
#import "pithos_macosAppDelegate.h"
47

    
48
#import "ImageAndTextCell.h"
49
@interface PithosPreferencesSyncOutlineViewCell : ImageAndTextCell {}
50
@end
51

    
52
@implementation PithosPreferencesSyncOutlineViewCell
53

    
54
- (void)setObjectValue:(id)object {
55
    if ([object isKindOfClass:[PithosNode class]]) {
56
        PithosNode *node = (PithosNode *)object;
57
        [self setStringValue:node.displayName];
58
        [self setImage:node.icon];
59
        [self setEditable:NO];
60
    } else {
61
        [super setObjectValue:object];
62
    }
63
}
64

    
65
@end
66

    
67
@implementation PithosPreferencesController
68
@synthesize selectedPithosAccount;
69
@synthesize accountsArrayController;
70
@synthesize accountRemoveEnable;
71
@synthesize serverURL, authUser, authToken, manual, loginEnable, loginCancelEnable;
72
@synthesize syncActive, syncDirectoryPath, syncContainersDictionary, syncApplyEnable, syncCancelEnable, 
73
            syncContainersOutlineView, syncContainersRootFilesNodes;
74
@synthesize groupsDictionaryController, selectedGroupMembersDictionaryController;
75

    
76
#pragma mark -
77
#pragma mark Object Lifecycle
78

    
79
- (id)init {
80
    return [super initWithWindowNibName:@"PithosPreferencesController"];
81
}
82

    
83
- (void)windowDidLoad {
84
    [super windowDidLoad];
85
    
86
    NSWindow *window = [self window];
87
    [window setHidesOnDeactivate:NO];
88
    [window setExcludedFromWindowsMenu:YES];
89
    
90
//	// Select the first tab when the window is loaded for the first time.
91
//	[[window valueForKeyPath:@"toolbar"] setSelectedItemIdentifier:@"0"];
92
    
93
    [[[syncContainersOutlineView tableColumns] objectAtIndex:1] setDataCell:[[[PithosPreferencesSyncOutlineViewCell alloc] init] autorelease]];
94
    
95
    [groupsDictionaryController setInitialKey:@"group"];
96
    [groupsDictionaryController setInitialValue:@"user"];
97
    [selectedGroupMembersDictionaryController setInitialKey:@"user"];
98
    [selectedGroupMembersDictionaryController setInitialValue:@""];
99

    
100
    [window setDelegate:self];
101
    
102
    self.selectedPithosAccount = [[accountsArrayController selectedObjects] objectAtIndex:0];
103
    [accountsArrayController addObserver:self forKeyPath:@"selection" options:NSKeyValueObservingOptionNew context:NULL];
104
    [[NSNotificationCenter defaultCenter] addObserver:self 
105
                                             selector:@selector(selectedPithosAccountNodeChildrenUpdated:) 
106
                                                 name:@"SelectedPithosAccountNodeChildrenUpdated" 
107
                                               object:nil];
108
}
109

    
110
- (BOOL)windowShouldClose:(id)sender {
111
    return [(pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate] activated];
112
}
113

    
114
//- (void)windowWillClose:(NSNotification *)notification {
115
//}
116

    
117
//- (IBAction)toolbarItemSelected:(id)sender {
118
//}
119

    
120
#pragma mark -
121
#pragma Observers
122

    
123
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
124
    if ([object isEqualTo:accountsArrayController] && 
125
        [keyPath isEqualToString:@"selection"] && 
126
        [[accountsArrayController selectedObjects] count]) {
127
        self.selectedPithosAccount = [[accountsArrayController selectedObjects] objectAtIndex:0];
128
    }
129
}
130

    
131
- (void)selectedPithosAccountNodeChildrenUpdated:(NSNotification *)notification {
132
    [syncContainersOutlineView reloadData];
133
    [syncContainersOutlineView expandItem:nil expandChildren:YES];
134
}
135

    
136
#pragma mark -
137
#pragma Update
138

    
139
- (void)updateAccounts {
140
    pithos_macosAppDelegate *delegate = (pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate];
141
    self.accountRemoveEnable = (delegate.activated && ([delegate.pithosAccounts count] > 1));
142
}
143

    
144
- (void)updateLogin {
145
    self.loginEnable = ([selectedPithosAccount urlIsValid:serverURL] && (!manual || ([authUser length] && [authToken length])));
146
    self.loginCancelEnable = (![selectedPithosAccount.serverURL isEqualToString:serverURL] || 
147
                              (selectedPithosAccount.authUser && ![selectedPithosAccount.authUser isEqualToString:authUser]) || 
148
                              (selectedPithosAccount.authToken && ![selectedPithosAccount.authToken isEqualToString:authToken]));
149
}
150

    
151
- (void)updateSync {
152
    BOOL isDirectory;
153
    self.syncApplyEnable = (selectedPithosAccount.active && 
154
                            ((selectedPithosAccount.syncActive != syncActive) || 
155
                             (![selectedPithosAccount.syncDirectoryPath isEqualToString:syncDirectoryPath] && 
156
                              (![[NSFileManager defaultManager] fileExistsAtPath:syncDirectoryPath isDirectory:&isDirectory] || 
157
                               isDirectory)) ||
158
                             ![selectedPithosAccount.syncContainersDictionary isEqualToDictionary:syncContainersDictionary]));
159
    self.syncCancelEnable = (selectedPithosAccount.active && 
160
                             ((selectedPithosAccount.syncActive != syncActive) || 
161
                              ![selectedPithosAccount.syncDirectoryPath isEqualToString:syncDirectoryPath] ||
162
                              ![selectedPithosAccount.syncContainersDictionary isEqualToDictionary:syncContainersDictionary]));
163
}
164

    
165
#pragma mark -
166
#pragma Properties
167

    
168
- (void)setSelectedPithosAccount:(PithosAccount *)aSelectedPithosAccount {
169
    if (aSelectedPithosAccount && ![aSelectedPithosAccount isEqualTo:selectedPithosAccount]) {
170
        selectedPithosAccount.accountNode.childrenUpdatedNotificationName = nil;
171
        [selectedPithosAccount release];
172
        selectedPithosAccount = [aSelectedPithosAccount retain];
173
        selectedPithosAccount.accountNode.childrenUpdatedNotificationName = [NSString stringWithString:@"SelectedPithosAccountNodeChildrenUpdated"];
174
        
175
        [self updateAccounts];
176
        [self loginCancel:self];
177
        [self syncCancel:self];
178
        [self groupsRevert:self];
179
    }
180
}
181

    
182
#pragma Login Properties
183

    
184
- (void)setServerURL:(NSString *)aServerURL {
185
    [serverURL release];
186
    serverURL = [aServerURL copy];
187
    [self updateLogin];
188
}
189

    
190
- (void)setAuthUser:(NSString *)anAuthUser {
191
    [authUser release];
192
    authUser = [anAuthUser copy];
193
    [self updateLogin];
194
}
195

    
196
- (void)setAuthToken:(NSString *)anAuthToken {
197
    [authToken release];
198
    authToken = [anAuthToken copy];
199
    [self updateLogin];
200
}
201

    
202
- (void)setManual:(BOOL)aManual {
203
    manual = aManual;
204
    [self updateLogin];
205
    if (!manual) {
206
        self.authUser = selectedPithosAccount.authUser;
207
        self.authToken = selectedPithosAccount.authToken;
208
    }
209
}
210

    
211
#pragma Sync Properties
212

    
213
- (void)setSyncActive:(BOOL)aSyncActive {
214
    syncActive = aSyncActive;
215
    [self updateSync];
216
}
217

    
218
- (void)setSyncDirectoryPath:(NSString *)aSyncDirectoryPath {
219
    [syncDirectoryPath release];
220
    syncDirectoryPath = [aSyncDirectoryPath copy];
221
    [self updateSync];
222
}
223

    
224
- (void)setSyncContainersDictionary:(NSMutableDictionary *)aSyncContainersDictionary {
225
    [syncContainersDictionary release];
226
    syncContainersDictionary = [[NSMutableDictionary alloc] initWithCapacity:[aSyncContainersDictionary count]];
227
    for (NSString *containerName in aSyncContainersDictionary) {
228
        [syncContainersDictionary setObject:[NSMutableArray arrayWithArray:[aSyncContainersDictionary objectForKey:containerName]] 
229
                                                                    forKey:containerName];
230
    }
231
    [self updateSync];
232
}
233

    
234
#pragma mark -
235
#pragma Actions
236

    
237
- (IBAction)addAccount:(id)sender {
238
    [accountsArrayController addObject:[PithosAccount pithosAccount]];
239
    [self updateAccounts];
240
    pithos_macosAppDelegate *delegate = (pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate];
241
    [delegate.pithosAccountsDictionary setObject:selectedPithosAccount forKey:selectedPithosAccount.name];
242
    [delegate savePithosAccounts:self];
243
}
244

    
245
- (IBAction)removeAccount:(id)sender {
246
    [self updateAccounts];
247
    if (!accountRemoveEnable)
248
        return;
249
    PithosAccount *removedPithosAccount = [selectedPithosAccount retain];
250
    pithos_macosAppDelegate *delegate = (pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate];
251
    if ([delegate.currentPithosAccount isEqualTo:removedPithosAccount] && [delegate.pithosBrowserController operationsPending]) {
252
        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
253
        [alert setMessageText:@"Operations Pending"];
254
        [alert setInformativeText:@"There are pending operations in the browser, do you want to remove the account and cancel them?"];
255
        [alert addButtonWithTitle:@"OK"];
256
        [alert addButtonWithTitle:@"Cancel"];
257
        NSInteger choice = [alert runModal];
258
        if (choice == NSAlertSecondButtonReturn) {
259
            [removedPithosAccount release];
260
            return;
261
        }
262
    }
263
    [accountsArrayController removeObject:selectedPithosAccount];
264
    [delegate.pithosAccountsDictionary removeObjectForKey:removedPithosAccount.name];
265
    [delegate removedPithosAccount:removedPithosAccount];
266
    [delegate savePithosAccounts:self];
267
    [removedPithosAccount release];
268
    [self updateAccounts];
269
}
270

    
271
#pragma Login Actions
272

    
273
- (IBAction)login:(id)sender {
274
    if (!manual) {
275
        [selectedPithosAccount loginWithServerURL:serverURL];
276
    } else {
277
        [selectedPithosAccount authenticateWithServerURL:serverURL authUser:authUser authToken:authToken];
278
        self.manual = NO;
279
        pithos_macosAppDelegate *delegate = (pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate];
280
        [delegate savePithosAccounts:self];
281
        if (!delegate.activated) {
282
            delegate.activated = YES;
283
            [delegate showPithosBrowser:self];
284
        }
285
        if ([selectedPithosAccount isEqualTo:delegate.currentPithosAccount])
286
            delegate.pithosBrowserController.pithos = selectedPithosAccount.pithos;
287
    }
288
}
289

    
290
- (IBAction)loginCancel:(id)server {
291
    self.serverURL = selectedPithosAccount.serverURL;
292
    self.authUser = selectedPithosAccount.authUser;
293
    self.authToken = selectedPithosAccount.authToken;
294
}
295

    
296
#pragma Sync Actions
297

    
298
- (IBAction)syncApply:(id)sender {
299
    [selectedPithosAccount updateSyncWithSyncActive:syncActive 
300
                                  syncDirectoryPath:syncDirectoryPath 
301
                           syncContainersDictionary:syncContainersDictionary];
302
    [self updateSync];
303
    pithos_macosAppDelegate *delegate = (pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate];
304
    [delegate savePithosAccounts:self];
305
    [delegate sync];
306
}
307

    
308
- (IBAction)syncCancel:(id)sender {
309
    self.syncActive = selectedPithosAccount.syncActive;
310
    self.syncDirectoryPath = selectedPithosAccount.syncDirectoryPath;
311
    self.syncContainersDictionary = selectedPithosAccount.syncContainersDictionary;
312
    self.syncContainersRootFilesNodes = [NSMutableDictionary dictionary];
313
    [self selectedPithosAccountNodeChildrenUpdated:nil];
314
}
315

    
316
- (IBAction)syncRefresh:(id)sender {
317
    selectedPithosAccount.accountNode.forcedRefresh = YES;
318
    [selectedPithosAccount.accountNode invalidateChildrenRecursive];
319
    if (selectedPithosAccount.accountNode.children) {
320
    }
321
}
322

    
323
#pragma mark Groups Actions
324

    
325
- (IBAction)groupsApply:(id)sender {
326
    [[self window] makeFirstResponder:nil];
327
    if (selectedPithosAccount.active)
328
        [selectedPithosAccount.accountNode applyInfo];
329
}
330

    
331
- (IBAction)groupsRevert:(id)sender {
332
    if (selectedPithosAccount.active && selectedPithosAccount.accountNode)
333
        [selectedPithosAccount.accountNode refreshInfo];
334
}
335

    
336
#pragma mark -
337
#pragma mark NSOutlineViewDataSource
338

    
339
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
340
    if (!selectedPithosAccount.active)
341
        return 0;
342
    if (outlineView == syncContainersOutlineView) {
343
        if (item == nil) {
344
            if (selectedPithosAccount.accountNode.children)
345
                return selectedPithosAccount.accountNode.children.count;
346
        } else if ([item class] == [PithosContainerNode class]) {
347
            // We add 1 for the root files node
348
            NSInteger subdirCount = 1;
349
            for (PithosNode *node in [(PithosContainerNode *)item children]) {
350
                if ([node class] == [PithosSubdirNode class])
351
                    subdirCount++;
352
            }
353
            return subdirCount;
354
        }
355
    }
356
    return 0;
357
}
358

    
359
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
360
    if (outlineView == syncContainersOutlineView) {
361
        if (item == nil)
362
            return [selectedPithosAccount.accountNode.children objectAtIndex:index];
363
        if ([item class] == [PithosContainerNode class]) {
364
            PithosContainerNode *containerNode = (PithosContainerNode *)item;
365
            NSInteger currentSubdirIndex = -1;
366
            for (PithosNode *node in containerNode.children) {
367
                if ([node class] == [PithosSubdirNode class]) {
368
                    currentSubdirIndex++;
369
                    if (currentSubdirIndex == index)
370
                        return node;
371
                }
372
            }
373
            if (++currentSubdirIndex == index) {
374
                PithosEmptyNode *rootFilesNode = [syncContainersRootFilesNodes objectForKey:containerNode.displayName];
375
                if (!rootFilesNode) {
376
                    rootFilesNode = [[[PithosEmptyNode alloc] initWithDisplayName:@"<root files>" 
377
                                                                             icon:[[NSWorkspace sharedWorkspace] iconForFileType:@""]] 
378
                                     autorelease];
379
                    rootFilesNode.parent = containerNode;
380
                    [syncContainersRootFilesNodes setObject:rootFilesNode forKey:containerNode.displayName];
381
                }
382
                return rootFilesNode;
383
            }
384
        }
385
    }
386
    return nil;
387
}
388

    
389
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
390
    if (outlineView == syncContainersOutlineView) {
391
        if ([item class] == [PithosContainerNode class])
392
            return YES;
393
    }
394
    return NO;
395
}
396

    
397
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
398
    if (outlineView == syncContainersOutlineView) {
399
        if ([[tableColumn identifier] isEqualToString:@"sync"]) {
400
            if ([item class] == [PithosContainerNode class]) {
401
                // A container is 
402
                // off if not in dictionary
403
                // mixed if in dictionary with exclusions
404
                // on if in dictionary without exclusions
405
                PithosContainerNode *node = (PithosContainerNode *)item;
406
                NSMutableArray *containerExcludedDirectories = [syncContainersDictionary objectForKey:node.displayName];
407
                if (containerExcludedDirectories) {
408
                    if ([containerExcludedDirectories count])
409
                        return [NSNumber numberWithUnsignedInteger:NSMixedState];
410
                    else
411
                        return [NSNumber numberWithUnsignedInteger:NSOnState];
412
                }
413
                return [NSNumber numberWithUnsignedInteger:NSOffState];
414
            } else if ([item class] == [PithosSubdirNode class]) {
415
                // Root files are off if parent container not in dictionary or if excluded
416
                // displayName should be localized and lowercased
417
                PithosSubdirNode *node = (PithosSubdirNode *)item;
418
                NSMutableArray *containerExcludedDirectories = [syncContainersDictionary objectForKey:node.parent.displayName];
419
                NSString *directoryName = [[node.displayName lowercaseString] stringByReplacingOccurrencesOfString:@"/" withString:@":"];
420
                if (!containerExcludedDirectories || [containerExcludedDirectories containsObject:directoryName])
421
                    return [NSNumber numberWithUnsignedInteger:NSOffState];
422
                else
423
                    return [NSNumber numberWithUnsignedInteger:NSOnState];
424
            } else if ([item class] == [PithosEmptyNode class]) {
425
                // Root files are off if parent container not in dictionary or if excluded
426
                PithosEmptyNode *node = (PithosEmptyNode *)item;
427
                NSMutableArray *containerExcludedDirectories = [syncContainersDictionary objectForKey:node.parent.displayName];
428
                if (!containerExcludedDirectories || [containerExcludedDirectories containsObject:@""])
429
                    return [NSNumber numberWithUnsignedInteger:NSOffState];
430
                else
431
                    return [NSNumber numberWithUnsignedInteger:NSOnState];
432
            }
433
            return [NSNumber numberWithUnsignedInteger:NSOffState];
434
        } else if ([[tableColumn identifier] isEqualToString:@"path"]) {
435
            return (PithosNode *)item;
436
        }
437
    }
438
    return nil;
439
}
440

    
441
- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
442
    if (outlineView == syncContainersOutlineView) {
443
        if ([[tableColumn identifier] isEqualToString:@"sync"]) {
444
            NSCellStateValue newState = [object unsignedIntegerValue];
445
            if ([item class] == [PithosContainerNode class]) {
446
                // If new state is
447
                // mixed/on include container with no excluded directories
448
                // off exclude container
449
                PithosContainerNode *node = (PithosContainerNode *)item;
450
                if ((newState == NSOnState) || (newState == NSMixedState)) {
451
                    [syncContainersDictionary setObject:[NSMutableArray array] forKey:node.displayName];
452
                } else {
453
                    [syncContainersDictionary removeObjectForKey:node.displayName];
454
                }
455
                [outlineView reloadItem:item reloadChildren:YES];
456
            } else if ([item class] == [PithosSubdirNode class]) {
457
                // If new state is
458
                // mixed/on include directory (if container not included, include and exclude all others)
459
                // off exclude directory
460
                PithosSubdirNode *node = (PithosSubdirNode *)item;
461
                NSMutableArray *containerExcludedDirectories = [syncContainersDictionary objectForKey:node.parent.displayName];
462
                NSString *directoryName = [[node.displayName lowercaseString] stringByReplacingOccurrencesOfString:@"/" withString:@":"];
463
                if ((newState == NSOnState) || (newState == NSMixedState)) {
464
                    if (containerExcludedDirectories) {
465
                        [containerExcludedDirectories removeObject:directoryName];
466
                    } else {
467
                        NSMutableArray *newContainerExcludeDirectories = [NSMutableArray arrayWithObject:@""];
468
                        for (PithosNode *siblingNode in node.parent.children) {
469
                            if ([siblingNode class] == [PithosSubdirNode class]) {
470
                                NSString *siblingDirectoryName = [[siblingNode.displayName lowercaseString] stringByReplacingOccurrencesOfString:@"/" withString:@":"];
471
                                if (![siblingDirectoryName isEqualToString:directoryName] && 
472
                                    ![newContainerExcludeDirectories containsObject:siblingDirectoryName])
473
                                    [newContainerExcludeDirectories addObject:siblingDirectoryName];
474
                            }
475
                        }
476
                        [syncContainersDictionary setObject:newContainerExcludeDirectories forKey:node.parent.displayName];
477
                    }
478
                } else if (![containerExcludedDirectories containsObject:directoryName]) {
479
                    [containerExcludedDirectories addObject:directoryName];
480
                }
481
                [outlineView reloadItem:[outlineView parentForItem:item]];
482
            } else if ([item class] == [PithosEmptyNode class]) {
483
                // If new state is
484
                // mixed/on include root files (if container not included, include and exclude all others)
485
                // off exclude root files
486
                PithosEmptyNode *node = (PithosEmptyNode *)item;
487
                NSMutableArray *containerExcludedDirectories = [syncContainersDictionary objectForKey:node.parent.displayName];
488
                if ((newState == NSOnState) || (newState == NSMixedState)) {
489
                    if (containerExcludedDirectories) {
490
                        [containerExcludedDirectories removeObject:@""];
491
                    } else {
492
                        NSMutableArray *newContainerExcludeDirectories = [NSMutableArray array];
493
                        for (PithosNode *siblingNode in node.parent.children) {
494
                            if ([siblingNode class] == [PithosSubdirNode class]) {
495
                                NSString *siblingDirectoryName = [[siblingNode.displayName lowercaseString] stringByReplacingOccurrencesOfString:@"/" withString:@":"];
496
                                if (![newContainerExcludeDirectories containsObject:siblingDirectoryName])
497
                                    [newContainerExcludeDirectories addObject:siblingDirectoryName];
498
                            }
499
                        }
500
                        [syncContainersDictionary setObject:newContainerExcludeDirectories forKey:node.parent.displayName];
501
                    }
502
                } else if (![containerExcludedDirectories containsObject:@""]) {
503
                    [containerExcludedDirectories addObject:@""];
504
                }
505
                [outlineView reloadItem:[outlineView parentForItem:item]];
506
            }
507
            [self updateSync];
508
        }
509
    }
510
}
511

    
512
@end