Add preference to skip hidden files during sync.
[pithos-macos] / pithos-macos / PithosPreferencesController.m
index f80a0a4..aa00b9a 100644 (file)
@@ -38,6 +38,7 @@
 #import "PithosPreferencesController.h"
 #import "PithosBrowserController.h"
 #import "PithosAccountNode.h"
+#import "PithosSharingAccountsNode.h"
 #import "PithosContainerNode.h"
 #import "PithosSubdirNode.h"
 #import "PithosObjectNode.h"
@@ -69,8 +70,8 @@
 @synthesize accountsArrayController;
 @synthesize accountRemoveEnable;
 @synthesize serverURL, authUser, authToken, manual, loginEnable, loginCancelEnable;
-@synthesize syncActive, syncDirectoryPath, syncContainersDictionary, syncApplyEnable, syncCancelEnable, 
-            syncContainersOutlineView, syncContainersRootFilesNodes;
+@synthesize syncActive, syncSkipHidden, syncDirectoryPath, syncAccountsDictionary, syncApplyEnable, syncCancelEnable, 
+            syncAccountsOutlineView, syncAccountsRootFilesNodes;
 @synthesize groupsDictionaryController, selectedGroupMembersDictionaryController;
 
 #pragma mark -
@@ -90,7 +91,9 @@
 //     // Select the first tab when the window is loaded for the first time.
 //     [[window valueForKeyPath:@"toolbar"] setSelectedItemIdentifier:@"0"];
     
-    [[[syncContainersOutlineView tableColumns] objectAtIndex:1] setDataCell:[[[PithosPreferencesSyncOutlineViewCell alloc] init] autorelease]];
+    [[[syncAccountsOutlineView tableColumns] objectAtIndex:1] setDataCell:[[[PithosPreferencesSyncOutlineViewCell alloc] init] autorelease]];
+    syncAccountsMyAccountNode = [[PithosEmptyNode alloc] initWithDisplayName:@"<my account>" 
+                                                                        icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kUserIcon)]];
     
     [groupsDictionaryController setInitialKey:@"group"];
     [groupsDictionaryController setInitialValue:@"user"];
 }
 
 - (void)selectedPithosAccountNodeChildrenUpdated:(NSNotification *)notification {
-    [syncContainersOutlineView reloadData];
-    [syncContainersOutlineView expandItem:nil expandChildren:YES];
+    [syncAccountsOutlineView reloadData];
+//    [syncAccountsOutlineView expandItem:nil expandChildren:YES];
 }
 
 #pragma mark -
     BOOL isDirectory;
     self.syncApplyEnable = (selectedPithosAccount.active && 
                             ((selectedPithosAccount.syncActive != syncActive) || 
+                             (selectedPithosAccount.syncSkipHidden != syncSkipHidden) || 
                              (![selectedPithosAccount.syncDirectoryPath isEqualToString:syncDirectoryPath] && 
                               (![[NSFileManager defaultManager] fileExistsAtPath:syncDirectoryPath isDirectory:&isDirectory] || 
                                isDirectory)) ||
-                             ![selectedPithosAccount.syncContainersDictionary isEqualToDictionary:syncContainersDictionary]));
+                             ![selectedPithosAccount.syncAccountsDictionary isEqualToDictionary:syncAccountsDictionary]));
     self.syncCancelEnable = (selectedPithosAccount.active && 
                              ((selectedPithosAccount.syncActive != syncActive) || 
+                              (selectedPithosAccount.syncSkipHidden != syncSkipHidden) || 
                               ![selectedPithosAccount.syncDirectoryPath isEqualToString:syncDirectoryPath] ||
-                              ![selectedPithosAccount.syncContainersDictionary isEqualToDictionary:syncContainersDictionary]));
+                              ![selectedPithosAccount.syncAccountsDictionary isEqualToDictionary:syncAccountsDictionary]));
 }
 
 #pragma mark -
 - (void)setSelectedPithosAccount:(PithosAccount *)aSelectedPithosAccount {
     if (aSelectedPithosAccount && ![aSelectedPithosAccount isEqualTo:selectedPithosAccount]) {
         selectedPithosAccount.accountNode.childrenUpdatedNotificationName = nil;
+        selectedPithosAccount.sharingAccountsNode.childrenUpdatedNotificationName = nil;
         [selectedPithosAccount release];
         selectedPithosAccount = [aSelectedPithosAccount retain];
-        selectedPithosAccount.accountNode.childrenUpdatedNotificationName = [NSString stringWithString:@"SelectedPithosAccountNodeChildrenUpdated"];
+        selectedPithosAccount.accountNode.childrenUpdatedNotificationName = @"SelectedPithosAccountNodeChildrenUpdated";
+        selectedPithosAccount.sharingAccountsNode.childrenUpdatedNotificationName = @"SelectedPithosAccountNodeChildrenUpdated";
         
         [self updateAccounts];
         [self loginCancel:self];
     [self updateSync];
 }
 
+- (void)setSyncSkipHidden:(BOOL)aSyncSkipHidden {
+    syncSkipHidden = aSyncSkipHidden;
+    [self updateSync];
+    [self selectedPithosAccountNodeChildrenUpdated:nil];
+}
+
 - (void)setSyncDirectoryPath:(NSString *)aSyncDirectoryPath {
     [syncDirectoryPath release];
     syncDirectoryPath = [aSyncDirectoryPath copy];
     [self updateSync];
 }
 
-- (void)setSyncContainersDictionary:(NSMutableDictionary *)aSyncContainersDictionary {
-    [syncContainersDictionary release];
-    syncContainersDictionary = [[NSMutableDictionary alloc] initWithCapacity:[aSyncContainersDictionary count]];
-    for (NSString *containerName in aSyncContainersDictionary) {
-        [syncContainersDictionary setObject:[NSMutableArray arrayWithArray:[aSyncContainersDictionary objectForKey:containerName]] 
-                                                                    forKey:containerName];
+- (void)setSyncAccountsDictionary:(NSMutableDictionary *)aSyncAccountsDictionary {
+    [syncAccountsDictionary release];
+    syncAccountsDictionary = [[NSMutableDictionary alloc] initWithCapacity:[aSyncAccountsDictionary count]];
+    for (NSString *accountName in aSyncAccountsDictionary) {
+        NSDictionary *aSyncContainersDictionary = [aSyncAccountsDictionary objectForKey:accountName];
+        NSMutableDictionary *syncContainersDictionary = [NSMutableDictionary dictionary];
+        for (NSString *containerName in aSyncContainersDictionary) {
+            if (![accountName isEqualToString:@""] || ![[containerName lowercaseString] isEqualToString:@"shared to me"])
+                [syncContainersDictionary setObject:[NSMutableSet setWithSet:[aSyncContainersDictionary objectForKey:containerName]] 
+                                             forKey:containerName];
+        }
+        if ([syncContainersDictionary count])
+            [syncAccountsDictionary setObject:syncContainersDictionary forKey:accountName];
     }
     [self updateSync];
 }
 #pragma Login Actions
 
 - (IBAction)login:(id)sender {
+    self.syncAccountsRootFilesNodes = [NSMutableDictionary dictionary];
     if (!manual) {
         [selectedPithosAccount loginWithServerURL:serverURL];
     } else {
     self.serverURL = selectedPithosAccount.serverURL;
     self.authUser = selectedPithosAccount.authUser;
     self.authToken = selectedPithosAccount.authToken;
+    self.manual = NO;
 }
 
 #pragma Sync Actions
 - (IBAction)syncApply:(id)sender {
     [selectedPithosAccount updateSyncWithSyncActive:syncActive 
                                   syncDirectoryPath:syncDirectoryPath 
-                           syncContainersDictionary:syncContainersDictionary];
+                           syncAccountsDictionary:syncAccountsDictionary 
+                                     syncSkipHidden:syncSkipHidden];
     [self updateSync];
     pithos_macosAppDelegate *delegate = (pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate];
     [delegate savePithosAccounts:self];
 - (IBAction)syncCancel:(id)sender {
     self.syncActive = selectedPithosAccount.syncActive;
     self.syncDirectoryPath = selectedPithosAccount.syncDirectoryPath;
-    self.syncContainersDictionary = selectedPithosAccount.syncContainersDictionary;
-    self.syncContainersRootFilesNodes = [NSMutableDictionary dictionary];
-    [self selectedPithosAccountNodeChildrenUpdated:nil];
+    self.syncAccountsDictionary = selectedPithosAccount.syncAccountsDictionary;
+    self.syncAccountsRootFilesNodes = [NSMutableDictionary dictionary];
+    self.syncSkipHidden = selectedPithosAccount.syncSkipHidden;
 }
 
 - (IBAction)syncRefresh:(id)sender {
     selectedPithosAccount.accountNode.forcedRefresh = YES;
     [selectedPithosAccount.accountNode invalidateChildrenRecursive];
-    if (selectedPithosAccount.accountNode.children) {
+    selectedPithosAccount.sharingAccountsNode.forcedRefresh = YES;
+    [selectedPithosAccount.sharingAccountsNode invalidateChildrenRecursive];
+    if (selectedPithosAccount.accountNode.children && selectedPithosAccount.sharingAccountsNode.children) {
     }
 }
 
 #pragma mark -
 #pragma mark NSOutlineViewDataSource
 
+// <my account> [PithosEmptyNode]
+// - <container>+ [PithosContainerNode]
+// -- <subdir>+ [PithosSubdirNode]
+// -- <root files> [PithosEmptyNode]
+// <sharing account>+ [PithosSharingAccountNode]
+// - <container>+ [PithosContainerNode]
+// -- <subdir>+ [PithosSubdirNode]
+// -- <root files> [PithosEmptyNode]
+
 - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
-    if (outlineView == syncContainersOutlineView) {
-        if (item == nil)
-            return selectedPithosAccount.accountNode.children.count;
-        if ([item class] == [PithosContainerNode class]) {
-            // We add 1 for the root files node
-            NSInteger subdirCount = 1;
-            for (PithosNode *node in [(PithosContainerNode *)item children]) {
-                if ([node class] == [PithosSubdirNode class])
-                    subdirCount++;
+    if (!selectedPithosAccount.active)
+        return 0;
+    if (outlineView == syncAccountsOutlineView) {
+        if (item == nil) {
+            // root: <my account> + #<sharing account>
+            NSInteger accountsCount = 0;
+            if ([selectedPithosAccount.accountNode.children count])
+                accountsCount = 1;
+            if (selectedPithosAccount.sharingAccountsNode.children)
+                accountsCount += selectedPithosAccount.sharingAccountsNode.children.count;
+            return accountsCount;
+        } else if (item == syncAccountsMyAccountNode) {
+            // root/<my account>: #<container>
+            if (selectedPithosAccount.accountNode.children) {
+                NSInteger containersCount = 0;
+                for (PithosContainerNode *node in selectedPithosAccount.accountNode.children) {
+                    if (![[node.displayName lowercaseString] isEqualToString:@"shared to me"])
+                        containersCount++;
+                }
+                return containersCount;
+            }
+        } else if ([item class] == [PithosAccountNode class]) {
+            // root/<sharing account>: #<container>
+            PithosAccountNode *accountNode = (PithosAccountNode *)item;
+            if (accountNode.children)
+                return accountNode.children.count;
+        } else if ([item class] == [PithosContainerNode class]) {
+            // root/{<my account>, <sharing account>}/<container>: #<subdir> + <root files>
+            PithosContainerNode *containerNode = (PithosContainerNode *)item;
+            if (containerNode.children) {
+                // We add 1 for the root files node
+                NSInteger subdirCount = 1;
+                for (PithosNode *node in containerNode.children) {
+                    if (([node class] == [PithosSubdirNode class]) && (!syncSkipHidden || ![node.displayName hasPrefix:@"."]))
+                        subdirCount++;
+                }
+                return subdirCount;
             }
-            return subdirCount;
         }
     }
     return 0;
 }
 
 - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
-    if (outlineView == syncContainersOutlineView) {
-        if (item == nil)
-            return [selectedPithosAccount.accountNode.children objectAtIndex:index];
-        if ([item class] == [PithosContainerNode class]) {
+    if (outlineView == syncAccountsOutlineView) {
+        if (item == nil) {
+            // root: [ <my account>, <sharing account>+ ]
+            if ([selectedPithosAccount.accountNode.children count]) {
+                if (index == 0)
+                    return syncAccountsMyAccountNode;
+                else
+                    return [selectedPithosAccount.sharingAccountsNode.children objectAtIndex:(index - 1)];
+            } else {
+                return [selectedPithosAccount.sharingAccountsNode.children objectAtIndex:index];
+            }
+        } else if (item == syncAccountsMyAccountNode) {
+            // root/<my account>: [ <container>+ ]
+            NSInteger currentContainerIndex = -1;
+            for (PithosContainerNode *node in selectedPithosAccount.accountNode.children) {
+                if (![[node.displayName lowercaseString] isEqualToString:@"shared to me"]) {
+                    currentContainerIndex++;
+                    if (currentContainerIndex == index)
+                        return node;
+                }
+            }
+        } else if ([item class] == [PithosAccountNode class]) {
+            // root/<sharing account>: [ <container>+ ]
+            return [((PithosAccountNode *)item).children objectAtIndex:index];
+        } else if ([item class] == [PithosContainerNode class]) {
+            // root/{<my account>, <sharing account>}/<container>: [ <subdir>+, <root files> ]
             PithosContainerNode *containerNode = (PithosContainerNode *)item;
             NSInteger currentSubdirIndex = -1;
             for (PithosNode *node in containerNode.children) {
-                if ([node class] == [PithosSubdirNode class]) {
+                if (([node class] == [PithosSubdirNode class]) && (!syncSkipHidden || ![node.displayName hasPrefix:@"."])) {
                     currentSubdirIndex++;
                     if (currentSubdirIndex == index)
                         return node;
                 }
             }
             if (++currentSubdirIndex == index) {
-                PithosEmptyNode *rootFilesNode = [syncContainersRootFilesNodes objectForKey:containerNode.displayName];
+                NSString *accountName = containerNode.sharingAccount;
+                if (!accountName)
+                    accountName = @"";
+                PithosEmptyNode *rootFilesNode = [[syncAccountsRootFilesNodes objectForKey:accountName] 
+                                                  objectForKey:containerNode.displayName];
                 if (!rootFilesNode) {
+                    if (![syncAccountsRootFilesNodes objectForKey:accountName])
+                        [syncAccountsRootFilesNodes setObject:[NSMutableDictionary dictionary] forKey:accountName];
                     rootFilesNode = [[[PithosEmptyNode alloc] initWithDisplayName:@"<root files>" 
                                                                              icon:[[NSWorkspace sharedWorkspace] iconForFileType:@""]] 
                                      autorelease];
                     rootFilesNode.parent = containerNode;
-                    [syncContainersRootFilesNodes setObject:rootFilesNode forKey:containerNode.displayName];
+                    [[syncAccountsRootFilesNodes objectForKey:accountName] setObject:rootFilesNode forKey:containerNode.displayName];
                 }
                 return rootFilesNode;
             }
 }
 
 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
-    if (outlineView == syncContainersOutlineView) {
-        if ([item class] == [PithosContainerNode class])
+    if (outlineView == syncAccountsOutlineView) {
+        if ((item == syncAccountsMyAccountNode) || 
+            ([item class] == [PithosAccountNode class]) || 
+            ([item class] == [PithosContainerNode class]))
             return YES;
     }
     return NO;
 }
 
 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
-    if (outlineView == syncContainersOutlineView) {
+    if (outlineView == syncAccountsOutlineView) {
         if ([[tableColumn identifier] isEqualToString:@"sync"]) {
-            if ([item class] == [PithosContainerNode class]) {
+            if (item == syncAccountsMyAccountNode) {
+                // root/<my account>
+                // My account is 
+                // off if not in dictionary
+                // mixed if in dictionary with exclusions
+                // on if in dictionary without exclusions
+                NSMutableDictionary *syncContainersDictionary = [syncAccountsDictionary objectForKey:@""];
+                if (syncContainersDictionary) {
+                    for (PithosContainerNode *node in selectedPithosAccount.accountNode.children) {
+                        if (![[node.displayName lowercaseString] isEqualToString:@"shared to me"]) {
+                            NSMutableSet *containerExcludedDirectories = [syncContainersDictionary objectForKey:node.displayName];
+                            if (!containerExcludedDirectories || [containerExcludedDirectories count])
+                                return [NSNumber numberWithUnsignedInteger:NSMixedState];
+                        }
+                    }
+                    return [NSNumber numberWithUnsignedInteger:NSOnState];
+                }
+                return [NSNumber numberWithUnsignedInteger:NSOffState];
+            } else if ([item class] == [PithosAccountNode class]) {
+                // root/<sharing account>
+                // A sharing account is 
+                // off if not in dictionary
+                // mixed if in dictionary with exclusions
+                // on if in dictionary without exclusions
+                PithosAccountNode *accountNode = (PithosAccountNode *)item;
+                NSMutableDictionary *syncContainersDictionary = [syncAccountsDictionary objectForKey:accountNode.displayName];
+                if (syncContainersDictionary) {
+                    for (PithosContainerNode *node in accountNode.children) {
+                        NSMutableSet *containerExcludedDirectories = [syncContainersDictionary objectForKey:node.displayName];
+                        if (!containerExcludedDirectories || [containerExcludedDirectories count])
+                            return [NSNumber numberWithUnsignedInteger:NSMixedState];
+                    }
+                    return [NSNumber numberWithUnsignedInteger:NSOnState];
+                }
+                return [NSNumber numberWithUnsignedInteger:NSOffState];
+            } else if ([item class] == [PithosContainerNode class]) {
+                // root/{<my account>, <sharing account>}/<container>
                 // A container is 
                 // off if not in dictionary
                 // mixed if in dictionary with exclusions
                 // on if in dictionary without exclusions
                 PithosContainerNode *node = (PithosContainerNode *)item;
-                NSMutableArray *containerExcludedDirectories = [syncContainersDictionary objectForKey:node.displayName];
+                NSString *accountName = node.sharingAccount;
+                if (!accountName)
+                    accountName = @"";
+                NSMutableSet *containerExcludedDirectories = [[syncAccountsDictionary objectForKey:accountName] 
+                                                              objectForKey:node.displayName];
                 if (containerExcludedDirectories) {
                     if ([containerExcludedDirectories count])
                         return [NSNumber numberWithUnsignedInteger:NSMixedState];
                 }
                 return [NSNumber numberWithUnsignedInteger:NSOffState];
             } else if ([item class] == [PithosSubdirNode class]) {
-                // Root files are off if parent container not in dictionary or if excluded
+                // root/{<my account>, <sharing account>}/<container>/<subdir>
+                // Directory is off if parent container not in dictionary or if excluded
                 // displayName should be localized and lowercased
                 PithosSubdirNode *node = (PithosSubdirNode *)item;
-                NSMutableArray *containerExcludedDirectories = [syncContainersDictionary objectForKey:node.parent.displayName];
-                NSString *directoryName = [[node.displayName lowercaseString] stringByReplacingOccurrencesOfString:@"/" withString:@":"];
-                if (!containerExcludedDirectories || [containerExcludedDirectories containsObject:directoryName])
+                NSString *accountName = node.sharingAccount;
+                if (!accountName)
+                    accountName = @"";
+                NSMutableSet *containerExcludedDirectories = [[syncAccountsDictionary objectForKey:accountName] 
+                                                              objectForKey:node.parent.displayName];
+                if (!containerExcludedDirectories || 
+                    [containerExcludedDirectories 
+                     containsObject:[[node.displayName lowercaseString] stringByReplacingOccurrencesOfString:@"/" withString:@":"]])
                     return [NSNumber numberWithUnsignedInteger:NSOffState];
                 else
                     return [NSNumber numberWithUnsignedInteger:NSOnState];
             } else if ([item class] == [PithosEmptyNode class]) {
-                // Root files are off if parent container not in dictionary or if excluded
+                // root/{<my account>, <sharing account>}/<container>/<root files>
+                // Root files is off if parent container not in dictionary or if excluded
                 PithosEmptyNode *node = (PithosEmptyNode *)item;
-                NSMutableArray *containerExcludedDirectories = [syncContainersDictionary objectForKey:node.parent.displayName];
+                NSString *accountName = node.parent.sharingAccount;
+                if (!accountName)
+                    accountName = @"";
+                NSMutableSet *containerExcludedDirectories = [[syncAccountsDictionary objectForKey:accountName] 
+                                                                objectForKey:node.parent.displayName];
                 if (!containerExcludedDirectories || [containerExcludedDirectories containsObject:@""])
                     return [NSNumber numberWithUnsignedInteger:NSOffState];
                 else
 }
 
 - (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
-    if (outlineView == syncContainersOutlineView) {
+    if (outlineView == syncAccountsOutlineView) {
         if ([[tableColumn identifier] isEqualToString:@"sync"]) {
             NSCellStateValue newState = [object unsignedIntegerValue];
-            if ([item class] == [PithosContainerNode class]) {
+            if (item == syncAccountsMyAccountNode) {
+                // root/<my account>
+                // If new state is
+                // mixed/on include my account with no exclusions
+                // off exclude my account
+                if ((newState == NSOnState) || (newState == NSMixedState)) {
+                    NSMutableDictionary *syncContainersDictionary = [NSMutableDictionary dictionary];
+                    for (PithosContainerNode *node in selectedPithosAccount.accountNode.children) {
+                        if (![[node.displayName lowercaseString] isEqualToString:@"shared to me"])
+                            [syncContainersDictionary setObject:[NSMutableSet set] forKey:node.displayName];
+                    }
+                    [syncAccountsDictionary setObject:syncContainersDictionary forKey:@""];
+                } else {
+                    [syncAccountsDictionary removeObjectForKey:@""];
+                }
+                [outlineView reloadItem:item reloadChildren:YES];
+            } else if ([item class] == [PithosAccountNode class]) {
+                // root/<sharing account>
+                // If new state is
+                // mixed/on include sharing account with no exclusions
+                // off exclude sharing account
+                PithosAccountNode *accountNode = (PithosAccountNode *)item;
+                if ((newState == NSOnState) || (newState == NSMixedState)) {
+                    NSMutableDictionary *syncContainersDictionary = [NSMutableDictionary dictionary];
+                    for (PithosContainerNode *node in accountNode.children) {
+                        [syncContainersDictionary setObject:[NSMutableSet set] forKey:node.displayName];
+                    }
+                    [syncAccountsDictionary setObject:syncContainersDictionary forKey:accountNode.displayName];
+                } else {
+                    [syncAccountsDictionary removeObjectForKey:accountNode.displayName];
+                }
+                [outlineView reloadItem:item reloadChildren:YES];
+            } else if ([item class] == [PithosContainerNode class]) {
+                // root/{<my account>, <sharing account>}/<container>
                 // If new state is
                 // mixed/on include container with no excluded directories
                 // off exclude container
                 PithosContainerNode *node = (PithosContainerNode *)item;
+                NSString *accountName = node.sharingAccount;
+                PithosNode *accountNode = node.parent;
+                if (!accountName) {
+                    accountName = @"";
+                    accountNode = syncAccountsMyAccountNode;
+                }
+                NSMutableDictionary *syncContainersDictionary = [syncAccountsDictionary objectForKey:accountName];
                 if ((newState == NSOnState) || (newState == NSMixedState)) {
-                    [syncContainersDictionary setObject:[NSMutableArray array] forKey:node.displayName];
-                } else {
+                    if (!syncContainersDictionary) {
+                        syncContainersDictionary = [NSMutableDictionary dictionary];
+                        [syncAccountsDictionary setObject:syncContainersDictionary forKey:accountName];
+                    }
+                    [syncContainersDictionary setObject:[NSMutableSet set] forKey:node.displayName];
+                } else if (syncContainersDictionary) {
                     [syncContainersDictionary removeObjectForKey:node.displayName];
+                    if (![syncContainersDictionary count])
+                        [syncAccountsDictionary removeObjectForKey:accountName];
                 }
-                [outlineView reloadItem:item reloadChildren:YES];
+                [outlineView reloadItem:accountNode reloadChildren:YES];
             } else if ([item class] == [PithosSubdirNode class]) {
+                // root/{<my account>, <sharing account>}/<container>/<subdir>
                 // If new state is
                 // mixed/on include directory (if container not included, include and exclude all others)
                 // off exclude directory
                 PithosSubdirNode *node = (PithosSubdirNode *)item;
-                NSMutableArray *containerExcludedDirectories = [syncContainersDictionary objectForKey:node.parent.displayName];
+                NSString *accountName = node.sharingAccount;
+                PithosNode *accountNode = node.parent.parent;
+                if (!accountName) {
+                    accountName = @"";
+                    accountNode = syncAccountsMyAccountNode;
+                }
+                NSMutableDictionary *syncContainersDictionary = [syncAccountsDictionary objectForKey:accountName];
+                NSMutableSet *containerExcludedDirectories = [syncContainersDictionary objectForKey:node.parent.displayName];
                 NSString *directoryName = [[node.displayName lowercaseString] stringByReplacingOccurrencesOfString:@"/" withString:@":"];
                 if ((newState == NSOnState) || (newState == NSMixedState)) {
                     if (containerExcludedDirectories) {
                         [containerExcludedDirectories removeObject:directoryName];
                     } else {
-                        NSMutableArray *newContainerExcludeDirectories = [NSMutableArray arrayWithObject:@""];
+                        if (!syncContainersDictionary) {
+                            syncContainersDictionary = [NSMutableDictionary dictionary];
+                            [syncAccountsDictionary setObject:syncContainersDictionary forKey:accountName];
+                        }
+                        NSMutableSet *newContainerExcludeDirectories = [NSMutableSet setWithObject:@""];
                         for (PithosNode *siblingNode in node.parent.children) {
-                            if ([siblingNode class] == [PithosSubdirNode class]) {
-                                NSString *siblingDirectoryName = [[siblingNode.displayName lowercaseString] stringByReplacingOccurrencesOfString:@"/" withString:@":"];
+                            if (([siblingNode class] == [PithosSubdirNode class]) && 
+                                (!syncSkipHidden || ![siblingNode.displayName hasPrefix:@"."])) {
+                                NSString *siblingDirectoryName = [[siblingNode.displayName lowercaseString] 
+                                                                  stringByReplacingOccurrencesOfString:@"/" withString:@":"];
                                 if (![siblingDirectoryName isEqualToString:directoryName] && 
                                     ![newContainerExcludeDirectories containsObject:siblingDirectoryName])
                                     [newContainerExcludeDirectories addObject:siblingDirectoryName];
                         }
                         [syncContainersDictionary setObject:newContainerExcludeDirectories forKey:node.parent.displayName];
                     }
-                } else if (![containerExcludedDirectories containsObject:directoryName]) {
+                } else if (syncContainersDictionary && 
+                           containerExcludedDirectories && 
+                           ![containerExcludedDirectories containsObject:directoryName]) {
                     [containerExcludedDirectories addObject:directoryName];
                 }
-                [outlineView reloadItem:[outlineView parentForItem:item]];
+                [outlineView reloadItem:accountNode reloadChildren:YES];
             } else if ([item class] == [PithosEmptyNode class]) {
                 // If new state is
                 // mixed/on include root files (if container not included, include and exclude all others)
                 // off exclude root files
                 PithosEmptyNode *node = (PithosEmptyNode *)item;
-                NSMutableArray *containerExcludedDirectories = [syncContainersDictionary objectForKey:node.parent.displayName];
+                NSString *accountName = node.parent.sharingAccount;
+                PithosNode *accountNode = node.parent.parent;
+                if (!accountName) {
+                    accountName = @"";
+                    accountNode = syncAccountsMyAccountNode;
+                }
+                NSMutableDictionary *syncContainersDictionary = [syncAccountsDictionary objectForKey:accountName];
+                NSMutableSet *containerExcludedDirectories = [syncContainersDictionary objectForKey:node.parent.displayName];
                 if ((newState == NSOnState) || (newState == NSMixedState)) {
                     if (containerExcludedDirectories) {
                         [containerExcludedDirectories removeObject:@""];
                     } else {
-                        NSMutableArray *newContainerExcludeDirectories = [NSMutableArray array];
+                        if (!syncContainersDictionary) {
+                            syncContainersDictionary = [NSMutableDictionary dictionary];
+                            [syncAccountsDictionary setObject:syncContainersDictionary forKey:accountName];
+                        }
+                        NSMutableSet *newContainerExcludeDirectories = [NSMutableSet set];
                         for (PithosNode *siblingNode in node.parent.children) {
-                            if ([siblingNode class] == [PithosSubdirNode class]) {
-                                NSString *siblingDirectoryName = [[siblingNode.displayName lowercaseString] stringByReplacingOccurrencesOfString:@"/" withString:@":"];
+                            if (([siblingNode class] == [PithosSubdirNode class]) && 
+                                (!syncSkipHidden || ![siblingNode.displayName hasPrefix:@"."])) {
+                                NSString *siblingDirectoryName = [[siblingNode.displayName lowercaseString] 
+                                                                  stringByReplacingOccurrencesOfString:@"/" withString:@":"];
                                 if (![newContainerExcludeDirectories containsObject:siblingDirectoryName])
                                     [newContainerExcludeDirectories addObject:siblingDirectoryName];
                             }
                         }
                         [syncContainersDictionary setObject:newContainerExcludeDirectories forKey:node.parent.displayName];
                     }
-                } else if (![containerExcludedDirectories containsObject:@""]) {
+                } else if (syncContainersDictionary && 
+                           containerExcludedDirectories && 
+                           ![containerExcludedDirectories containsObject:@""]) {
                     [containerExcludedDirectories addObject:@""];
                 }
-                [outlineView reloadItem:[outlineView parentForItem:item]];
+                [outlineView reloadItem:accountNode reloadChildren:YES];
             }
             [self updateSync];
         }