Initial implementation of browser context menu.
[pithos-macos] / pithos-macos / PithosBrowserController.m
index b79e3c4..11e2bdc 100644 (file)
 // or implied, of GRNET S.A.
 
 #import "PithosBrowserController.h"
-#import "ASIPithosRequest.h"
+#import "PithosNode.h"
 #import "PithosAccountNode.h"
 #import "PithosContainerNode.h"
+#import "PithosSubdirNode.h"
+#import "PithosObjectNode.h"
 #import "PithosEmptyNode.h"
 #import "ImageAndTextCell.h"
 #import "FileSystemBrowserCell.h"
+#import "ASIPithosRequest.h"
+#import "ASIPithosContainerRequest.h"
+#import "ASIPithosObjectRequest.h"
+#import "ASIPithosContainer.h"
+#import "ASIPithosObject.h"
+#import "PithosFileUtilities.h"
 
 //@interface PithosBrowserCell : NSBrowserCell {}
 @interface PithosBrowserCell : FileSystemBrowserCell {}
@@ -91,6 +99,7 @@
 
 @interface PithosBrowserController (Private) {}
 - (void)resetContainers;
+- (void)getInfo:(NSMenuItem *)sender;
 @end
 
 @implementation PithosBrowserController
 
 - (void)dealloc {
     [[NSNotificationCenter defaultCenter] removeObserver:self];
+    [browserMenu release];
     [sharedPreviewController release];
     [outlineViewDataSourceArray release];
+    [accountNode release];
     [rootNode release];
-    [browser release];
-    [splitView release];
-    [outlineView release];
     [super dealloc];
 }
 
 - (void)awakeFromNib {
+    [super awakeFromNib];
+    
+    [browser setDraggingSourceOperationMask:NSDragOperationNone forLocal:YES];
+    [browser setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
+    
     [browser setCellClass:[PithosBrowserCell class]];
+    
+    browserMenu = [[NSMenu alloc] init];
+    [browserMenu setDelegate:self];
+    [browser setMenu:browserMenu];
 }
 
 - (void)resetContainers {
     // CONTAINERS
        NSTreeNode *containersTreeNode = [NSTreeNode treeNodeWithRepresentedObject:
                             [[[PithosEmptyNode alloc] initWithDisplayName:@"CONTAINERS" icon:nil] autorelease]];
-    // CONTAINERS/pithos
-       [[containersTreeNode mutableChildNodes] addObject:
-     [NSTreeNode treeNodeWithRepresentedObject:
-      [[[PithosContainerNode alloc] initWithContainerName:@"pithos" 
-                                                     icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kToolbarHomeIcon)]
-        ] autorelease]]];
-    // CONTAINERS/trash
-       [[containersTreeNode mutableChildNodes] addObject:
-     [NSTreeNode treeNodeWithRepresentedObject:
-      [[[PithosContainerNode alloc] initWithContainerName:@"trash"
-                                                     icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kFullTrashIcon)]
-        ] autorelease]]];
+//    // CONTAINERS/pithos
+//     [[containersTreeNode mutableChildNodes] addObject:
+//     [NSTreeNode treeNodeWithRepresentedObject:
+//      [[[PithosContainerNode alloc] initWithContainerName:@"pithos" 
+//                                                     icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kToolbarHomeIcon)]
+//        ] autorelease]]];
+//    // CONTAINERS/trash
+//     [[containersTreeNode mutableChildNodes] addObject:
+//     [NSTreeNode treeNodeWithRepresentedObject:
+//      [[[PithosContainerNode alloc] initWithContainerName:@"trash"
+//                                                     icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kFullTrashIcon)]
+//        ] autorelease]]];
     // SHARED
        NSTreeNode *sharedTreeNode = [NSTreeNode treeNodeWithRepresentedObject:
                                       [[[PithosEmptyNode alloc] initWithDisplayName:@"SHARED" icon:nil] autorelease]];
        // Expand the folder outline view
     [outlineView expandItem:nil expandChildren:YES];
        [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
+    
+    // Create accountNode and trigger a refresh
+    accountNode = [[PithosAccountNode alloc] init];
+    accountNode.children;
 }
 
 - (void)windowDidLoad {
+    [super windowDidLoad];
+    
     [[[outlineView tableColumns] objectAtIndex:0] setDataCell:[[[PithosOutlineViewCell alloc] init] autorelease]];
     
     // Register for updates
                                                  name:@"PithosSubdirNodeChildrenUpdated" 
                                                object:nil];
     [[NSNotificationCenter defaultCenter] addObserver:self 
+                                             selector:@selector(pithosAccountNodeChildrenUpdated:) 
+                                                 name:@"PithosAccountNodeChildrenUpdated" 
+                                               object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:self 
                                              selector:@selector(resetContainers) 
                                                  name:@"PithosAuthenticationCredentialsUpdated" 
                                                object:nil];
     }
 }
 
+- (void)pithosAccountNodeChildrenUpdated:(NSNotification *)notification {
+    BOOL containerPithosFound = NO;
+    BOOL containerTrashFound = NO;
+    //NSMutableArray *containersTreeNodeChildren = [[outlineViewDataSourceArray objectAtIndex:0] mutableChildNodes];
+    NSMutableArray *containersTreeNodeChildren = [NSMutableArray array];
+    for (PithosContainerNode *containerNode in accountNode.children) {
+        if ([containerNode.pithosContainer.name isEqualToString:@"pithos"]) {
+            containerNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kToolbarHomeIcon)];
+            [containersTreeNodeChildren insertObject:[NSTreeNode treeNodeWithRepresentedObject:containerNode] atIndex:0];
+            containerPithosFound = YES;
+        } else if ([containerNode.pithosContainer.name isEqualToString:@"trash"]) {
+            containerNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kFullTrashIcon)];            
+            NSUInteger insertIndex = 1;
+            if (!containerPithosFound)
+                insertIndex = 0;
+            [containersTreeNodeChildren insertObject:[NSTreeNode treeNodeWithRepresentedObject:containerNode] atIndex:insertIndex];
+            containerTrashFound = YES;
+        } else {
+            [containersTreeNodeChildren addObject:[NSTreeNode treeNodeWithRepresentedObject:containerNode]];
+        }
+    }
+    BOOL refreshAccountNode = NO;
+    if (!containerPithosFound) {
+        // create pithos
+        ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithContainerName:@"pithos"];
+        [containerRequest startSynchronous];
+        if ([containerRequest error]) {
+            NSLog(@"error:%@", [containerRequest error]);
+            // XXX do something on error
+        } else {
+            refreshAccountNode = YES;
+        }
+    }
+    if (!containerTrashFound) {
+        // create trash
+        ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithContainerName:@"trash"];
+        [containerRequest startSynchronous];
+        if ([containerRequest error]) {
+            NSLog(@"error:%@", [containerRequest error]);
+            // XXX do something on error
+        } else {
+            refreshAccountNode = YES;
+        }
+    }
+    if (refreshAccountNode) {
+        [accountNode invalidateChildren];
+        accountNode.children;
+    } else {
+        [[[outlineViewDataSourceArray objectAtIndex:0] mutableChildNodes] setArray:containersTreeNodeChildren];
+        self.outlineViewDataSourceArray = outlineViewDataSourceArray;
+        
+        // Expand the folder outline view
+        [outlineView expandItem:nil expandChildren:YES];
+        [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
+    }
+}
+
 #pragma mark -
 #pragma Actions
 
     return NO;
 }
 
+#pragma mark Drag and Drop
+
+//- (BOOL)browser:(NSBrowser *)aBrowser canDragRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column 
+//      withEvent:(NSEvent *)event {    
+//    // XXX allow only objects? or when a subdir is dragged download the whole tree?
+//    NSIndexPath *baseIndexPath = [browser indexPathForColumn:column]; 
+//    for (NSUInteger i = [rowIndexes firstIndex]; i <= [rowIndexes lastIndex]; i = [rowIndexes indexGreaterThanIndex:i]) {
+//        PithosNode *node = [browser itemAtIndexPath:[baseIndexPath indexPathByAddingIndex:i]];
+//        if (node.pithosObject.subdir)
+//            return NO;
+//    }
+//    return YES;
+//}
+
+- (BOOL)browser:(NSBrowser *)aBrowser writeRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column 
+   toPasteboard:(NSPasteboard *)pasteboard {
+    NSMutableArray *propertyList = [NSMutableArray arrayWithCapacity:[rowIndexes count]];
+    NSIndexPath *baseIndexPath = [browser indexPathForColumn:column]; 
+    for (NSUInteger i = [rowIndexes firstIndex]; i <= [rowIndexes lastIndex]; i = [rowIndexes indexGreaterThanIndex:i]) {
+        PithosNode *node = [browser itemAtIndexPath:[baseIndexPath indexPathByAddingIndex:i]];
+        [propertyList addObject:[node.pithosObject.name pathExtension]];
+    }
+
+    [pasteboard declareTypes:[NSArray arrayWithObject:NSFilesPromisePboardType] owner:self];
+    [pasteboard setPropertyList:propertyList forType:NSFilesPromisePboardType];
+    
+    return YES;
+}
+
+- (NSArray *)browser:(NSBrowser *)aBrowser namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination 
+forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
+    NSMutableArray *names = [NSMutableArray arrayWithCapacity:[rowIndexes count]];
+    NSIndexPath *baseIndexPath = [browser indexPathForColumn:column]; 
+    for (NSUInteger i = [rowIndexes firstIndex]; i <= [rowIndexes lastIndex]; i = [rowIndexes indexGreaterThanIndex:i]) {
+        PithosNode *node = [browser itemAtIndexPath:[baseIndexPath indexPathByAddingIndex:i]];
+        
+        
+        // If the node is a subdir ask if the whole tree should be downloaded
+        if (node.pithosObject.subdir) {
+            NSAlert *alert = [[NSAlert alloc] init];
+            [alert setMessageText:@"Download directory"];
+            [alert setInformativeText:[NSString stringWithFormat:@"'%@' is a directory, do you want to download its contents?", node.displayName]];
+            [alert addButtonWithTitle:@"OK"];
+            [alert addButtonWithTitle:@"Cancel"];
+            NSInteger choice = [alert runModal];
+            if (choice == NSAlertFirstButtonReturn) {
+                NSArray *objectRequests = [PithosFileUtilities objectDataRequestsForSubdirWithContainerName:node.pithosContainer.name 
+                                                                                                 objectName:node.pithosObject.name 
+                                                                                                toDirectory:[dropDestination path] 
+                                                                                              checkIfExists:YES];
+                if (objectRequests) {
+                    for (ASIPithosObjectRequest *objectRequest in objectRequests) {
+                        [names addObject:[objectRequest.userInfo valueForKey:@"fileName"]];
+                        // XXX set delegates and queue
+                        [objectRequest setCompletionBlock:^{
+                            NSLog(@"dl completed: %@", [objectRequest url]);
+                        }];
+                        [objectRequest setFailedBlock:^{
+                            NSLog(@"dl failed: %@, error: %@", [objectRequest url], [objectRequest error]);
+                        }];
+                        [objectRequest startAsynchronous];
+                    }
+                }
+            }
+        } else {
+            ASIPithosObjectRequest *objectRequest = [PithosFileUtilities objectDataRequestWithContainerName:node.pithosContainer.name 
+                                                                                                 objectName:node.pithosObject.name 
+                                                                                                toDirectory:[dropDestination path] 
+                                                                                              checkIfExists:YES];
+            if (objectRequest) {
+                [names addObject:[objectRequest.userInfo valueForKey:@"fileName"]];
+                // XXX set delegates and queue
+                [objectRequest setCompletionBlock:^{
+                    NSLog(@"dl completed: %@", [objectRequest url]);
+                }];
+                [objectRequest setFailedBlock:^{
+                    NSLog(@"dl failed: %@, error: %@", [objectRequest url], [objectRequest error]);
+                }];
+                [objectRequest startAsynchronous];
+            }
+        }
+    }
+    return names;
+}
+
 #pragma mark -
-#pragma NSSplitViewDelegate
+#pragma mark NSSplitViewDelegate
 
 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex {
     return 120;
     }
 }
 
+#pragma mark -
+#pragma mark NSMenuDelegate
+
+- (void)menuNeedsUpdate:(NSMenu *)menu {
+    NSInteger column = [browser clickedColumn];
+    NSInteger row = [browser clickedRow];
+    [menu removeAllItems];
+    if ((column == -1) || (row == -1)) {
+        // General context menu has 0
+    } else {
+        // PithosNode menu has 1 items
+        // Get Info
+        NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:@"Get Info" action:@selector(getInfo:) keyEquivalent:@""];
+        [menuItem setRepresentedObject:[browser itemAtRow:row inColumn:column]];
+        [menu addItem:menuItem];
+    }
+}
+
+#pragma mark -
+#pragma mark Menu Actions
+
+- (void)getInfo:(NSMenuItem *)sender {
+    [(PithosNode *)[sender representedObject] showPithosNodeInfo:sender];
+}
+
 @end
\ No newline at end of file