Show dock icon and menu. Fix context menu bugs.
[pithos-macos] / pithos-macos / PithosBrowserController.m
index 5e203e0..2186c60 100644 (file)
@@ -2,7 +2,7 @@
 //  PithosBrowserController.m
 //  pithos-macos
 //
-// Copyright 2011 GRNET S.A. All rights reserved.
+// Copyright 2011-2012 GRNET S.A. All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or
 // without modification, are permitted provided that the following
@@ -47,6 +47,7 @@
 #import "FileSystemBrowserCell.h"
 #import "ASINetworkQueue.h"
 #import "ASIPithosRequest.h"
+#import "ASIPithos.h"
 #import "ASIPithosContainerRequest.h"
 #import "ASIPithosObjectRequest.h"
 #import "ASIPithosAccount.h"
@@ -55,6 +56,8 @@
 #import "PithosUtilities.h"
 #import "UsingSizeTransformer.h"
 
+#define REFRESH_TIMER_INTERVAL 5
+
 @interface PithosBrowserCell : FileSystemBrowserCell {}
 @end
 
 @end
 
 @interface PithosBrowserController (Private)
-- (void)resetContainers:(NSNotification *)notification;
 - (BOOL)uploadFiles:(NSArray *)filenames toNode:(PithosNode *)destinationNode;
 - (BOOL)moveNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode;
 - (BOOL)copyNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode;
 @end
 
 @implementation PithosBrowserController
+@synthesize pithos;
 @synthesize accountNode;
 @synthesize verticalSplitView, horizontalSplitView, leftTopView, leftBottomView, outlineView, browser, outlineViewMenu, browserMenu;
 @synthesize draggedNodes, draggedParentNode;
     return [super initWithWindowNibName:@"PithosBrowserController"];
 }
 
-- (void)dealloc {
-    [[NSNotificationCenter defaultCenter] removeObserver:self];
-    dispatch_release(moveQueue);
-    dispatch_release(copyQueue);
-    dispatch_release(deleteQueue);
-    dispatch_release(uploadQueue);
-    dispatch_release(downloadQueue);
-    [moveNetworkQueue cancelAllOperations];
-    [moveNetworkQueue release];
-    [copyNetworkQueue cancelAllOperations];
-    [copyNetworkQueue release];
-    [deleteNetworkQueue cancelAllOperations];
-    [deleteNetworkQueue release];
-    [uploadNetworkQueue cancelAllOperations];
-    [uploadNetworkQueue release];
-    [downloadNetworkQueue cancelAllOperations];
-    [downloadNetworkQueue release];
-    [refreshTimer invalidate];
-    [refreshTimer release];
-    [clipboardParentNode release];
-    [clipboardNodes release];
-    [draggedParentNode release];
-    [draggedNodes release];
-    [sharedPreviewController release];
-    [othersSharedNode release];
-    [mySharedNode release];
-    [sharedNode release];
-    [containersNodeChildren release];
-    [containersNode release];
-    [accountNode release];
-    [rootNode release];
-    [super dealloc];
+- (void)windowDidLoad {
+    [super windowDidLoad];
+    if (browser && !browserInitialized) {
+        browserInitialized = YES;
+        [self initBrowser];
+    }
 }
 
-- (void)awakeFromNib {
-    [super awakeFromNib];
-    
+- (void)initBrowser {
     [browser registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSFilesPromisePboardType, nil]];
     [browser setDraggingSourceOperationMask:(NSDragOperationCopy|NSDragOperationMove) forLocal:YES];
     [browser setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
     [outlineView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
     
     [browser setCellClass:[PithosBrowserCell class]];
+    [browser setAllowsBranchSelection:YES];
+    [browser setAllowsMultipleSelection:YES];
+    [browser setAllowsEmptySelection:YES];
+    [browser setAllowsTypeSelect:YES];
     
     moveNetworkQueue = [[ASINetworkQueue alloc] init];
     moveNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
-    [moveNetworkQueue go];
+//    moveNetworkQueue.maxConcurrentOperationCount = 1;
     copyNetworkQueue = [[ASINetworkQueue alloc] init];
     copyNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
-    [copyNetworkQueue go];
+//    copyNetworkQueue.maxConcurrentOperationCount = 1;
     deleteNetworkQueue = [[ASINetworkQueue alloc] init];
     deleteNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
-    [deleteNetworkQueue go];
+//    deleteNetworkQueue.maxConcurrentOperationCount = 1;
     uploadNetworkQueue = [[ASINetworkQueue alloc] init];
     uploadNetworkQueue.showAccurateProgress = YES;
     uploadNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
-    [uploadNetworkQueue go];
+//    uploadNetworkQueue.maxConcurrentOperationCount = 1;
     downloadNetworkQueue = [[ASINetworkQueue alloc] init];
     downloadNetworkQueue.showAccurateProgress = YES;
     downloadNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
-    [downloadNetworkQueue go];
-    
-    moveQueue = dispatch_queue_create("gr.grnet.pithos.MoveQueue", NULL);
-    copyQueue = dispatch_queue_create("gr.grnet.pithos.CopyQueue", NULL);
-    deleteQueue = dispatch_queue_create("gr.grnet.pithos.DeleteQueue", NULL);
-    uploadQueue = dispatch_queue_create("gr.grnet.pithos.UploadQueue", NULL);
-    downloadQueue = dispatch_queue_create("gr.grnet.pithos.DownloadQueue", NULL);
-}
-
-- (void)resetContainers:(NSNotification *)notification {
-    [refreshTimer invalidate];
-    [refreshTimer release];
+//    downloadNetworkQueue.maxConcurrentOperationCount = 1;
     
-    [moveNetworkQueue cancelAllOperations];
-    [copyNetworkQueue cancelAllOperations];
-    [deleteNetworkQueue cancelAllOperations];
-    [uploadNetworkQueue cancelAllOperations];
-    [downloadNetworkQueue cancelAllOperations];
+    moveQueue = [[NSOperationQueue alloc] init];
+    [moveQueue setSuspended:YES];
+    moveQueue.name = @"gr.grnet.pithos.MoveQueue";
+//    moveQueue.maxConcurrentOperationCount = 1;
+    copyQueue = [[NSOperationQueue alloc] init];
+    [copyQueue setSuspended:YES];
+    copyQueue.name = @"gr.grnet.pithos.CopyQueue";
+//    copyQueue.maxConcurrentOperationCount = 1;
+    deleteQueue = [[NSOperationQueue alloc] init];
+    [deleteQueue setSuspended:YES];
+    deleteQueue.name = @"gr.grnet.pithos.DeleteQueue";
+//    deleteQueue.maxConcurrentOperationCount = 1;
+    uploadQueue = [[NSOperationQueue alloc] init];
+    [uploadQueue setSuspended:YES];
+    uploadQueue.name = @"gr.grnet.pithos.UploadQueue";
+//    uploadQueue.maxConcurrentOperationCount = 1;
+    downloadQueue = [[NSOperationQueue alloc] init];
+    [downloadQueue setSuspended:YES];
+    downloadQueue.name = @"gr.grnet.pithos.DownloadQueue";
+//    downloadQueue.maxConcurrentOperationCount = 1;
     
-    rootNode = nil;
-    [browser loadColumnZero];
-    [containersNodeChildren removeAllObjects];
-    [outlineView reloadData];
-       // Expand the folder outline view
-    [outlineView expandItem:nil expandChildren:YES];
-       [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
-    
-    // Refresh account
-    [accountNode refresh];
-    [mySharedNode refresh];
-    [othersSharedNode refresh];
-    
-    [activityFacility reset];
-    
-    refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(forceRefresh:) userInfo:self repeats:YES] retain];
-}
-
-- (void)windowDidLoad {
-    [super windowDidLoad];
+    moveCallbackQueue = [[NSOperationQueue alloc] init];
+    [moveCallbackQueue setSuspended:YES];
+    moveCallbackQueue.name = @"gr.grnet.pithos.MoveCallbackQueue";
+//    moveCallbackQueue.maxConcurrentOperationCount = 1;
+    copyCallbackQueue = [[NSOperationQueue alloc] init];
+    [copyCallbackQueue setSuspended:YES];
+    copyCallbackQueue.name = @"gr.grnet.pithos.CopyCallbackQueue";
+//    copyCallbackQueue.maxConcurrentOperationCount = 1;
+    deleteCallbackQueue = [[NSOperationQueue alloc] init];
+    [deleteCallbackQueue setSuspended:YES];
+    deleteCallbackQueue.name = @"gr.grnet.pithos.DeleteCallbackQueue";
+//    deleteCallbackQueue.maxConcurrentOperationCount = 1;
+    uploadCallbackQueue = [[NSOperationQueue alloc] init];
+    [uploadCallbackQueue setSuspended:YES];
+    uploadCallbackQueue.name = @"gr.grnet.pithos.UploadCallbackQueue";
+//    uploadCallbackQueue.maxConcurrentOperationCount = 1;
+    downloadCallbackQueue = [[NSOperationQueue alloc] init];
+    [downloadCallbackQueue setSuspended:YES];
+    downloadCallbackQueue.name = @"gr.grnet.pithos.DownloadCallbackQueue";
+//    downloadCallbackQueue.maxConcurrentOperationCount = 1;
     
     [activityProgressIndicator setUsesThreadedAnimation:YES];
     [activityProgressIndicator setMinValue:0.0];
     [activityProgressIndicator setMaxValue:1.0];
     activityFacility = [PithosActivityFacility defaultPithosActivityFacility];
-    activityFacility.delegate = self;
     
-    self.accountNode = [[[PithosAccountNode alloc] init] autorelease];
+    self.accountNode = [[[PithosAccountNode alloc] initWithPithos:pithos] autorelease];
     containersNode = [[PithosEmptyNode alloc] initWithDisplayName:@"CONTAINERS" icon:nil];
     containersNodeChildren = [[NSMutableArray alloc] init];
     sharedNode = [[PithosEmptyNode alloc] initWithDisplayName:@"SHARED" icon:nil];
-    mySharedNode = [[PithosAccountNode alloc] init];
-    mySharedNode.displayName = @"my shared";
+    mySharedNode = [[PithosAccountNode alloc] initWithPithos:pithos];
+    mySharedNode.displayName = @"shared by me";
     mySharedNode.shared = YES;
     mySharedNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kUserIcon)];
-    othersSharedNode = [[PithosSharingAccountsNode alloc] init];
-    othersSharedNode.displayName = @"others shared";
+    othersSharedNode = [[PithosSharingAccountsNode alloc] initWithPithos:pithos];
+    othersSharedNode.displayName = @"shared to me";
     othersSharedNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGroupIcon)];
     
     [[[outlineView tableColumns] objectAtIndex:0] setDataCell:[[[PithosOutlineViewCell alloc] init] autorelease]];
     
     // Register for updates
-    // PithosContainerNode updates browser nodes
-    [[NSNotificationCenter defaultCenter] addObserver:self 
-                                             selector:@selector(pithosNodeChildrenUpdated:) 
-                                                 name:@"PithosContainerNodeChildrenUpdated" 
-                                               object:nil];
-    // PithosSubdirNode updates browser nodes
-    [[NSNotificationCenter defaultCenter] addObserver:self 
-                                             selector:@selector(pithosNodeChildrenUpdated:) 
-                                                 name:@"PithosSubdirNodeChildrenUpdated" 
-                                               object:nil];
     // PithosAccountNode accountNode updates outlineView container nodes 
     [[NSNotificationCenter defaultCenter] addObserver:self 
                                              selector:@selector(pithosAccountNodeChildrenUpdated:) 
-                                                 name:@"PithosAccountNodeChildrenUpdated" 
+                                                 name:@"PithosNodeChildrenUpdated" 
                                                object:accountNode];
-    // PithosAccountNode other than accountNode updates nodes 
+    // PithosNode updates browser nodes
     [[NSNotificationCenter defaultCenter] addObserver:self 
                                              selector:@selector(pithosNodeChildrenUpdated:) 
-                                                 name:@"PithosAccountNodeChildrenUpdated" 
-                                               object:nil];
-    // PithosSharingAccountsNode othersSharedNode updates browser nodes 
-    [[NSNotificationCenter defaultCenter] addObserver:self 
-                                             selector:@selector(pithosNodeChildrenUpdated:) 
-                                                 name:@"PithosSharingAccountsNodeChildrenUpdated" 
-                                               object:othersSharedNode];
-    // Updated authentication credentials reset containers in the outline view 
-    [[NSNotificationCenter defaultCenter] addObserver:self 
-                                             selector:@selector(resetContainers:) 
-                                                 name:@"PithosAuthenticationCredentialsUpdated" 
+                                                 name:@"PithosNodeChildrenUpdated" 
                                                object:nil];
     // Request for browser refresh 
     [[NSNotificationCenter defaultCenter] addObserver:self 
                                              selector:@selector(pithosBrowserRefreshNeeded:) 
                                                  name:@"PithosBrowserRefreshNeeeded" 
-                                               object:nil];    
+                                               object:nil];
+}
+
+- (void)resetBrowser {
+    @synchronized(self) {
+        if (!browserActive)
+            return;
+    }
+
+    [refreshTimer invalidate];
+    [refreshTimer release];
+    
+    [moveNetworkQueue reset];
+    [copyNetworkQueue reset];
+    [deleteNetworkQueue reset];
+    [uploadNetworkQueue reset];
+    [downloadNetworkQueue reset];
+    
+    [moveQueue cancelAllOperations];
+    [moveQueue setSuspended:YES];
+    [copyQueue cancelAllOperations];
+    [copyQueue setSuspended:YES];
+    [deleteQueue cancelAllOperations];
+    [deleteQueue setSuspended:YES];
+    [uploadQueue cancelAllOperations];
+    [uploadQueue setSuspended:YES];
+    [downloadQueue cancelAllOperations];
+    [downloadQueue setSuspended:YES];
+
+    [moveCallbackQueue cancelAllOperations];
+    [moveCallbackQueue setSuspended:YES];
+    [copyCallbackQueue cancelAllOperations];
+    [copyCallbackQueue setSuspended:YES];
+    [deleteCallbackQueue cancelAllOperations];
+    [deleteCallbackQueue setSuspended:YES];
+    [uploadCallbackQueue cancelAllOperations];
+    [uploadCallbackQueue setSuspended:YES];
+    [downloadCallbackQueue cancelAllOperations];
+    [downloadCallbackQueue setSuspended:YES];
+
+    rootNode = nil;
+    [browser loadColumnZero];
+    [containersNodeChildren removeAllObjects];
+    [outlineView reloadData];
+    // Expand the folder outline view
+    [outlineView expandItem:nil expandChildren:YES];
+    [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
+    
+    activityFacility.delegate = nil;
+    [activityProgressIndicator setDoubleValue:1.0];
+    [activityProgressIndicator stopAnimation:self];
+    
+    @synchronized(self) {
+        browserActive = NO;
+    }
 }
 
+- (void)startBrowser {
+    @synchronized(self) {
+        if (browserActive)
+            return;
+    }
+    
+    // In the improbable case of leftover operations
+    [moveNetworkQueue reset];
+    [copyNetworkQueue reset];
+    [deleteNetworkQueue reset];
+    [uploadNetworkQueue reset];
+    [downloadNetworkQueue reset];
+    [moveQueue cancelAllOperations];
+    [copyQueue cancelAllOperations];
+    [deleteQueue cancelAllOperations];
+    [uploadQueue cancelAllOperations];
+    [downloadQueue cancelAllOperations];
+    [moveCallbackQueue cancelAllOperations];
+    [copyCallbackQueue cancelAllOperations];
+    [deleteCallbackQueue cancelAllOperations];
+    [uploadCallbackQueue cancelAllOperations];
+    [downloadCallbackQueue cancelAllOperations];
+
+    [moveNetworkQueue go];
+    [copyNetworkQueue go];
+    [deleteNetworkQueue go];
+    [uploadNetworkQueue go];
+    [downloadNetworkQueue go];
+    [moveQueue setSuspended:NO];
+    [copyQueue setSuspended:NO];
+    [deleteQueue setSuspended:NO];
+    [uploadQueue setSuspended:NO];
+    [downloadQueue setSuspended:NO];
+    [moveCallbackQueue setSuspended:NO];
+    [copyCallbackQueue setSuspended:NO];
+    [deleteCallbackQueue setSuspended:NO];
+    [uploadCallbackQueue setSuspended:NO];
+    [downloadCallbackQueue setSuspended:NO];
+
+    accountNode.pithos = pithos;
+    [accountNode forceRefresh];
+    mySharedNode.pithos = pithos;
+    [mySharedNode forceRefresh];
+    othersSharedNode.pithos = pithos;
+    [othersSharedNode forceRefresh];
+            
+//    [activityFacility reset];
+    activityFacility.delegate = self;
+            
+    refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:REFRESH_TIMER_INTERVAL 
+                                                     target:self 
+                                                   selector:@selector(forceRefresh:) 
+                                                   userInfo:self 
+                                                    repeats:YES] retain];
+    @synchronized(self) {
+        browserActive = YES;
+    }
+}
+
+- (BOOL)operationsPending {
+    return ([moveNetworkQueue operationCount] || 
+            [copyNetworkQueue operationCount] || 
+            [deleteNetworkQueue operationCount] || 
+            [uploadNetworkQueue operationCount] || 
+            [downloadNetworkQueue operationCount] || 
+            [moveQueue operationCount] || 
+            [copyQueue operationCount] || 
+            [deleteQueue operationCount] || 
+            [uploadQueue operationCount] || 
+            [downloadQueue operationCount] || 
+            [moveCallbackQueue operationCount] || 
+            [copyCallbackQueue operationCount] || 
+            [deleteCallbackQueue operationCount] || 
+            [uploadCallbackQueue operationCount] || 
+            [downloadCallbackQueue operationCount]);
+}
+
+- (void)dealloc {
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
+    [self resetBrowser];
+    [moveQueue release];
+    [copyQueue release];
+    [deleteQueue release];
+    [uploadQueue release];
+    [downloadQueue release];
+    [moveNetworkQueue release];
+    [copyNetworkQueue release];
+    [deleteNetworkQueue release];
+    [uploadNetworkQueue release];
+    [downloadNetworkQueue release];
+    [clipboardParentNode release];
+    [clipboardNodes release];
+    [draggedParentNode release];
+    [draggedNodes release];
+    [sharedPreviewController release];
+    [othersSharedNode release];
+    [mySharedNode release];
+    [sharedNode release];
+    [containersNodeChildren release];
+    [containersNode release];
+    [accountNode release];
+    [rootNode release];
+    [pithos release];
+    [super dealloc];
+}
+
+- (void)setPithos:(ASIPithos *)aPithos {
+    if (aPithos) {
+        if (![aPithos.authUser isEqualToString:pithos.authUser] || 
+            ![aPithos.authToken isEqualToString:pithos.authToken] || 
+            ![aPithos.storageURLPrefix isEqualToString:pithos.storageURLPrefix] ||
+            ![aPithos.publicURLPrefix isEqualToString:pithos.publicURLPrefix]) {
+            [self resetBrowser];
+            [pithos release];
+            pithos = [aPithos retain];
+            [self startBrowser];
+        } else {
+            [self startBrowser];
+        }
+    }
+}
+
+
 #pragma mark -
 #pragma mark Observers
 
 - (void)pithosNodeChildrenUpdated:(NSNotification *)notification {
     PithosNode *node = (PithosNode *)[notification object];
-    if (node == accountNode)
+    if ((node == accountNode) || ![node.pithos isEqualTo:pithos])
         return;
     NSLog(@"pithosNodeChildrenUpdated:%@", node.url);
     NSInteger lastColumn = [browser lastColumn];
     BOOL refreshAccountNode = NO;
     if (!containerPithosFound) {
         // Create pithos node
-        ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithContainerName:@"pithos"];
-        [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
-        while (![containerRequest isFinished]) {
-            usleep(1);
-        }
+        ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithPithos:pithos 
+                                                                                                            containerName:@"pithos"];
+        ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
+        [networkQueue go];
+        [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:containerRequest]] waitUntilFinished:YES];
         if ([containerRequest error]) {
-            [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
+            });
         } else {
             refreshAccountNode = YES;
         }
     }
     if (!containerTrashFound) {
         // Create trash node
-        ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithContainerName:@"trash"];
-        [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
-        while (![containerRequest isFinished]) {
-            usleep(1);
-        }
+        ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithPithos:pithos 
+                                                                                                            containerName:@"trash"];
+        ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
+        [networkQueue go];
+        [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:containerRequest]] waitUntilFinished:YES];
         if ([containerRequest error]) {
-            [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
+            });
         } else {
             refreshAccountNode = YES;
         }
     // Expand the folder outline view
     [outlineView expandItem:nil expandChildren:YES];
     
-    if ((rootNode == nil) || (rootNode == containersNode) || (rootNode == sharedNode)) {
+    if (((rootNode == nil) || (rootNode == containersNode) || (rootNode == sharedNode)) && [containersNodeChildren count]) {
         rootNode = [containersNodeChildren objectAtIndex:0];
         [browser loadColumnZero];
     }
 #pragma mark Actions
 
 - (IBAction)forceRefresh:(id)sender {
+    if (editingItem)
+        return;
     if (sender)
         [accountNode forceRefresh];
     for (NSInteger column = [browser lastColumn]; column >= 0; column--) {
 }
 
 - (IBAction)refresh:(id)sender {
+    if (editingItem)
+        return;
     if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) {
         [self forceRefresh:sender];
     } else {
     if (node.shared || node.sharingAccount || 
         ([node class] == [PithosContainerNode class]) || ([node class] == [PithosAccountNode class]))
         return NO;
+    editingItem = YES;
     return YES;
 }
 
 - (void)browser:(NSBrowser *)browser setObjectValue:(id)object forItem:(id)item {
+    editingItem = NO;
     PithosNode *node = (PithosNode *)item;
     NSString *newName = (NSString *)object;
     NSUInteger newNameLength = [newName length];
     }
     if (([node class] == [PithosObjectNode class]) || 
         (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
-        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+        // Operation: Rename (move) an object or subdir/ node
+        __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
             NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+            if (operation.isCancelled) {
+                [pool drain];
+                return;
+            }
             NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];
             if ([newName hasSuffix:@"/"])
                 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
             NSError *error = nil;
             BOOL isDirectory;
-            if ([PithosUtilities objectExistsAtContainerName:node.pithosContainer.name 
-                                                  objectName:destinationObjectName 
-                                                       error:&error 
-                                                 isDirectory:&isDirectory 
-                                              sharingAccount:nil]) {
-                NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-                [alert setMessageText:@"Name Taken"];
-                [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
-                [alert addButtonWithTitle:@"OK"];
-                [alert runModal];
+            if ([PithosUtilities objectExistsAtPithos:pithos 
+                                        containerName:node.pithosContainer.name 
+                                           objectName:destinationObjectName 
+                                                error:&error 
+                                          isDirectory:&isDirectory 
+                                       sharingAccount:nil]) {
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
+                    [alert setMessageText:@"Name Taken"];
+                    [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
+                    [alert addButtonWithTitle:@"OK"];
+                    [alert runModal];
+                });
                 [pool drain];
                 return;
             } else if (error) {
                 [pool drain];
                 return;
             }
-            ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithContainerName:node.pithosContainer.name 
-                                                                                             objectName:node.pithosObject.name 
-                                                                               destinationContainerName:node.pithosContainer.name 
-                                                                                  destinationObjectName:destinationObjectName 
-                                                                                          checkIfExists:NO];
-            if (objectRequest) {
+            if (operation.isCancelled) {
+                [pool drain];
+                return;
+            }
+            ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos 
+                                                                                   containerName:node.pithosContainer.name 
+                                                                                      objectName:node.pithosObject.name 
+                                                                        destinationContainerName:node.pithosContainer.name 
+                                                                           destinationObjectName:destinationObjectName 
+                                                                                   checkIfExists:NO];
+            if (!operation.isCancelled && objectRequest) {
                 objectRequest.delegate = self;
                 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
                   NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
                   NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                   moveNetworkQueue, @"networkQueue", 
-                  @"moveQueue", @"queueType", 
+                  @"move", @"operationType", 
                   nil]];
                 [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
             }
             [pool drain];
-        });
+        }];
+        [moveQueue addOperation:operation];
     } else if ([node class] == [PithosSubdirNode class]) {
         if (firstSlashRange.length == 1)
             return;
-        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+        // Operation: Rename (move) a subdir node and its descendants
+        // The resulting ASIPithosObjectRequests are chained through dependencies
+        __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
             NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+            if (operation.isCancelled) {
+                [pool drain];
+                return;
+            }
             NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];
             NSError *error = nil;
             BOOL isDirectory;
-            if ([PithosUtilities objectExistsAtContainerName:node.pithosContainer.name 
-                                                  objectName:destinationObjectName 
-                                                       error:&error 
-                                                 isDirectory:&isDirectory 
-                                              sharingAccount:nil]) {
-                NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-                [alert setMessageText:@"Name Taken"];
-                [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
-                [alert addButtonWithTitle:@"OK"];
-                [alert runModal];
+            if ([PithosUtilities objectExistsAtPithos:pithos 
+                                        containerName:node.pithosContainer.name 
+                                           objectName:destinationObjectName 
+                                                error:&error 
+                                          isDirectory:&isDirectory 
+                                       sharingAccount:nil]) {
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
+                    [alert setMessageText:@"Name Taken"];
+                    [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
+                    [alert addButtonWithTitle:@"OK"];
+                    [alert runModal];
+                });
                 [pool drain];
                 return;
             } else if (error) {
                 [pool drain];
                 return;
             }
+            if (operation.isCancelled) {
+                [pool drain];
+                return;
+            }
             if (node.pithosObject.subdir)
                 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
-            NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithContainerName:node.pithosContainer.name 
-                                                                                         objectName:node.pithosObject.name 
-                                                                           destinationContainerName:node.pithosContainer.name 
-                                                                              destinationObjectName:destinationObjectName 
-                                                                                      checkIfExists:NO];
-            if (objectRequests) {
+            NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithPithos:pithos 
+                                                                               containerName:node.pithosContainer.name 
+                                                                                  objectName:node.pithosObject.name 
+                                                                    destinationContainerName:node.pithosContainer.name 
+                                                                       destinationObjectName:destinationObjectName 
+                                                                               checkIfExists:NO];
+            if (!operation.isCancelled && objectRequests) {
+                ASIPithosObjectRequest *previousObjectRequest = nil;
                 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
+                    if (operation.isCancelled) {
+                        [pool drain];
+                        return;
+                    }
                     objectRequest.delegate = self;
                     objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                     objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
                       NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
                       NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                       moveNetworkQueue, @"networkQueue", 
-                      @"moveQueue", @"queueType", 
+                      @"move", @"operationType", 
                       nil]];
+                    if (previousObjectRequest)
+                        [objectRequest addDependency:previousObjectRequest];
+                    previousObjectRequest = objectRequest;
                     [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                 }
             }
             [pool drain];
-        });
+        }];
+        [moveQueue addOperation:operation];
     }
 }
 
 - (NSArray *)browser:(NSBrowser *)aBrowser namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination 
 forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
     NSMutableArray *names = [NSMutableArray arrayWithCapacity:[draggedNodes count]];
-    for (PithosNode *node in draggedNodes) {        
+    for (PithosNode *node in draggedNodes) {
+        [names addObject:node.displayName];
         // If the node is a subdir ask if the whole tree should be downloaded
         if ([node class] == [PithosSubdirNode class]) {
             NSAlert *alert = [[[NSAlert alloc] init] autorelease];
@@ -637,97 +824,10 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
             [alert addButtonWithTitle:@"OK"];
             [alert addButtonWithTitle:@"Cancel"];
             NSInteger choice = [alert runModal];
-            if (choice == NSAlertFirstButtonReturn) {
-                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-                    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-                    NSArray *objectRequests = [PithosUtilities objectDataRequestsForSubdirWithContainerName:node.pithosContainer.name 
-                                                                                                     objectName:node.pithosObject.name 
-                                                                                                    toDirectory:[dropDestination path] 
-                                                                                                  checkIfExists:YES 
-                                                                                                 sharingAccount:node.sharingAccount];
-                    if (objectRequests) {
-                        for (__block ASIPithosObjectRequest *objectRequest in objectRequests) {
-                            [names addObject:[objectRequest.userInfo objectForKey:@"fileName"]];
-                            objectRequest.delegate = self;
-                            objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
-                            objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
-                            NSString *messagePrefix = [NSString stringWithFormat:@"Downloading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]];
-                            PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload 
-                                                                                       message:[messagePrefix stringByAppendingString:@" (0%)"]
-                                                                                    totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue] 
-                                                                                  currentBytes:0];
-                            dispatch_async(dispatch_get_main_queue(), ^{
-                                [activityFacility updateActivity:activity withMessage:activity.message];  
-                            });
-                            [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
-                             [NSDictionary dictionaryWithObjectsAndKeys:
-                              activity, @"activity", 
-                              [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
-                              [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
-                              [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage", 
-                              [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
-                              [NSNumber numberWithUnsignedInteger:10], @"retries", 
-                              NSStringFromSelector(@selector(downloadObjectFinished:)), @"didFinishSelector", 
-                              NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
-                              downloadNetworkQueue, @"networkQueue", 
-                              @"downloadQueue", @"queueType", 
-                              nil]];
-                            [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
-                                [activityFacility updateActivity:activity 
-                                                     withMessage:[messagePrefix stringByAppendingFormat:@" (%.0f%%)", (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)] 
-                                                      totalBytes:activity.totalBytes 
-                                                    currentBytes:(activity.currentBytes + size)];
-                            }];
-                            [downloadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
-                        }
-                    }
-                    [pool drain];
-                });
-            }
+            if (choice == NSAlertFirstButtonReturn)
+                [self downloadNode:node toDirectory:[dropDestination path] withNewFileName:nil version:nil];
         } else {
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-                __block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectDataRequestWithContainerName:node.pithosContainer.name 
-                                                                                                         objectName:node.pithosObject.name 
-                                                                                                        toDirectory:[dropDestination path] 
-                                                                                                      checkIfExists:YES 
-                                                                                                     sharingAccount:node.sharingAccount];
-                if (objectRequest) {
-                    [names addObject:[objectRequest.userInfo objectForKey:@"fileName"]];
-                    objectRequest.delegate = self;
-                    objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
-                    objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
-                    NSString *messagePrefix = [NSString stringWithFormat:@"Downloading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]];
-                    PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload 
-                                                                               message:[messagePrefix stringByAppendingString:@" (0%)"]
-                                                                            totalBytes:node.pithosObject.bytes 
-                                                                          currentBytes:0];
-                    dispatch_async(dispatch_get_main_queue(), ^{
-                        [activityFacility updateActivity:activity withMessage:activity.message];  
-                    });
-                    [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
-                     [NSDictionary dictionaryWithObjectsAndKeys:
-                      activity, @"activity", 
-                      [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
-                      [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
-                      [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage", 
-                      [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
-                      [NSNumber numberWithUnsignedInteger:10], @"retries", 
-                      NSStringFromSelector(@selector(downloadObjectFinished:)), @"didFinishSelector", 
-                      NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
-                      downloadNetworkQueue, @"networkQueue", 
-                      @"downloadQueue", @"queueType", 
-                      nil]];
-                    [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
-                        [activityFacility updateActivity:activity 
-                                             withMessage:[messagePrefix stringByAppendingFormat:@" (%.0f%%)", (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)] 
-                                              totalBytes:activity.totalBytes 
-                                            currentBytes:(activity.currentBytes + size)];
-                    }];
-                    [downloadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
-                    [pool drain];
-                }
-            });
+            [self downloadNode:node toDirectory:[dropDestination path] withNewFileName:nil version:nil];
         }
     }
     return names;
@@ -844,6 +944,127 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 #pragma mark -
 #pragma mark Drag and Drop methods
 
+- (void)downloadNode:(PithosNode *)node 
+         toDirectory:(NSString *)dirPath 
+     withNewFileName:(NSString *)newFileName 
+             version:(NSString *)version {
+    if ([node class] == [PithosSubdirNode class]) {
+        // XXX newFilename and version are ignored in the case of a subdir node for now
+        // Operation: Download a subdir node and its descendants
+        // The resulting ASIPithosObjectRequests are chained through dependencies
+        __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
+            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+            if (operation.isCancelled) {
+                [pool drain];
+                return;
+            }
+            NSArray *objectRequests = [PithosUtilities objectDataRequestsForSubdirWithPithos:pithos 
+                                                                               containerName:node.pithosContainer.name 
+                                                                                  objectName:node.pithosObject.name 
+                                                                                 toDirectory:dirPath 
+                                                                               checkIfExists:YES 
+                                                                              sharingAccount:node.sharingAccount];
+            if (!operation.isCancelled && objectRequests) {
+                ASIPithosObjectRequest *previousObjectRequest = nil;
+                for (__block ASIPithosObjectRequest *objectRequest in objectRequests) {
+                    if (operation.isCancelled) {
+                        [pool drain];
+                        return;
+                    }
+                    objectRequest.delegate = self;
+                    objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                    objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+                    NSString *messagePrefix = [NSString stringWithFormat:@"Downloading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]];
+                    PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload 
+                                                                               message:[messagePrefix stringByAppendingString:@" (0%)"]
+                                                                            totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue] 
+                                                                          currentBytes:0];
+                    dispatch_async(dispatch_get_main_queue(), ^{
+                        [activityFacility updateActivity:activity withMessage:activity.message];  
+                    });
+                    [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
+                     [NSDictionary dictionaryWithObjectsAndKeys:
+                      activity, @"activity", 
+                      [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                      [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                      [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage", 
+                      [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
+                      [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                      NSStringFromSelector(@selector(downloadObjectFinished:)), @"didFinishSelector", 
+                      NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
+                      downloadNetworkQueue, @"networkQueue", 
+                      @"download", @"operationType", 
+                      nil]];
+                    [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
+                        [activityFacility updateActivity:activity 
+                                             withMessage:[messagePrefix stringByAppendingFormat:@" (%.0f%%)", (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)] 
+                                              totalBytes:activity.totalBytes 
+                                            currentBytes:(activity.currentBytes + size)];
+                    }];
+                    if (previousObjectRequest)
+                        [objectRequest addDependency:previousObjectRequest];
+                    previousObjectRequest = objectRequest;
+                    [downloadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
+                }
+            }
+            [pool drain];
+        }];
+        [downloadQueue addOperation:operation];
+    } else if ([node class] == [PithosObjectNode class]) {
+        // Operation: Download an object node
+        __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
+            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+            if (operation.isCancelled) {
+                [pool drain];
+                return;
+            }
+            __block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectDataRequestWithPithos:pithos 
+                                                                                           containerName:node.pithosContainer.name 
+                                                                                              objectName:node.pithosObject.name 
+                                                                                                 version:version 
+                                                                                            toDirectory:dirPath 
+                                                                                         withNewFileName:newFileName 
+                                                                                           checkIfExists:(version ? NO : YES) 
+                                                                                          sharingAccount:node.sharingAccount];
+            if (!operation.isCancelled && objectRequest) {
+                objectRequest.delegate = self;
+                objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+                NSString *messagePrefix = [NSString stringWithFormat:@"Downloading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]];
+                PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload 
+                                                                           message:[messagePrefix stringByAppendingString:@" (0%)"]
+                                                                        totalBytes:node.pithosObject.bytes 
+                                                                      currentBytes:0];
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    [activityFacility updateActivity:activity withMessage:activity.message];  
+                });
+                [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
+                 [NSDictionary dictionaryWithObjectsAndKeys:
+                  activity, @"activity", 
+                  [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                  [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                  [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage", 
+                  [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
+                  [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                  NSStringFromSelector(@selector(downloadObjectFinished:)), @"didFinishSelector", 
+                  NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
+                  downloadNetworkQueue, @"networkQueue", 
+                  @"download", @"operationType", 
+                  nil]];
+                [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
+                    [activityFacility updateActivity:activity 
+                                         withMessage:[messagePrefix stringByAppendingFormat:@" (%.0f%%)", (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)] 
+                                          totalBytes:activity.totalBytes 
+                                        currentBytes:(activity.currentBytes + size)];
+                }];
+                [downloadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
+                [pool drain];
+            }
+        }];
+        [downloadQueue addOperation:operation];
+    }
+}
+
 - (BOOL)uploadFiles:(NSArray *)filenames toNode:(PithosNode *)destinationNode {
     if (([destinationNode class] != [PithosSubdirNode class]) && ([destinationNode class] != [PithosContainerNode class]))
         return NO;
@@ -855,11 +1076,11 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
     else
         objectNamePrefix = [NSString string];
     if ((destinationNode.pithosContainer.blockHash == nil) || (destinationNode.pithosContainer.blockSize == 0)) {
-        ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest containerMetadataRequestWithContainerName:containerName];
-        [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
-        while (![containerRequest isFinished]) {
-            usleep(1);
-        }
+        ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest containerMetadataRequestWithPithos:pithos 
+                                                                                                      containerName:containerName];
+        ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
+        [networkQueue go];
+        [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:containerRequest]] waitUntilFinished:YES];
         if ([containerRequest error]) {
             [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
             return NO;
@@ -879,8 +1100,13 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
             if (!isDirectory) {
                 // Upload file
                 NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
-                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                // Operation: Upload a local file
+                __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                    if (operation.isCancelled) {
+                        [pool drain];
+                        return;
+                    }
                     NSError *error = nil;
                     NSString *contentType = [PithosUtilities contentTypeOfFile:filePath error:&error];
                     if (contentType == nil)
@@ -888,16 +1114,21 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                     if (error)
                         NSLog(@"contentType detection error: %@", error);
                     NSArray *hashes = nil;
-                    ASIPithosObjectRequest *objectRequest = [PithosUtilities writeObjectDataRequestWithContainerName:containerName 
-                                                                                                          objectName:objectName 
-                                                                                                         contentType:contentType 
-                                                                                                           blockSize:blockSize 
-                                                                                                           blockHash:blockHash 
-                                                                                                             forFile:filePath 
-                                                                                                       checkIfExists:YES 
-                                                                                                              hashes:&hashes 
-                                                                                                      sharingAccount:destinationNode.sharingAccount];
-                    if (objectRequest) {
+                    if (operation.isCancelled) {
+                        [pool drain];
+                        return;
+                    }
+                    ASIPithosObjectRequest *objectRequest = [PithosUtilities writeObjectDataRequestWithPithos:pithos 
+                                                                                                containerName:containerName 
+                                                                                                   objectName:objectName 
+                                                                                                  contentType:contentType 
+                                                                                                    blockSize:blockSize 
+                                                                                                    blockHash:blockHash 
+                                                                                                      forFile:filePath 
+                                                                                                checkIfExists:YES 
+                                                                                                       hashes:&hashes 
+                                                                                               sharingAccount:destinationNode.sharingAccount];
+                    if (!operation.isCancelled && objectRequest) {
                         objectRequest.delegate = self;
                         objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                         objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -927,14 +1158,15 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                           NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", 
                           NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                           uploadNetworkQueue, @"networkQueue", 
-                          @"uploadQueue", @"queueType", 
+                          @"upload", @"operationType", 
                           nil]];
                         if (destinationNode.sharingAccount)
                             [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"];
                         [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
                     }
                     [pool drain];
-                });
+                }];
+                [uploadQueue addOperation:operation];
             } else {
                 // Upload directory, confirm first
                 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
@@ -945,26 +1177,42 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                 NSInteger choice = [alert runModal];
                 if (choice == NSAlertFirstButtonReturn) {
                     NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
-                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                    // Operation: Upload a local directory and its descendants
+                    // The resulting ASIPithosObjectRequests are chained through dependencies
+                    __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                        if (operation.isCancelled) {
+                            [pool drain];
+                            return;
+                        }
                         NSMutableArray *objectNames = nil;
                         NSMutableArray *contentTypes = nil;
                         NSMutableArray *filePaths = nil;
                         NSMutableArray *hashesArrays = nil;
                         NSMutableArray *directoryObjectRequests = nil;
-                        NSArray *objectRequests = [PithosUtilities writeObjectDataRequestsWithContainerName:containerName 
-                                                                                                     objectName:objectName 
-                                                                                                      blockSize:blockSize 
-                                                                                                      blockHash:blockHash 
-                                                                                                   forDirectory:filePath 
-                                                                                                  checkIfExists:YES 
-                                                                                                    objectNames:&objectNames
-                                                                                                   contentTypes:&contentTypes
-                                                                                                      filePaths:&filePaths
-                                                                                                   hashesArrays:&hashesArrays 
-                                                                                        directoryObjectRequests:&directoryObjectRequests 
-                                                                                                 sharingAccount:destinationNode.sharingAccount];
+                        NSArray *objectRequests = [PithosUtilities writeObjectDataRequestsWithPithos:pithos 
+                                                                                       containerName:containerName 
+                                                                                          objectName:objectName 
+                                                                                           blockSize:blockSize 
+                                                                                           blockHash:blockHash 
+                                                                                        forDirectory:filePath 
+                                                                                       checkIfExists:YES 
+                                                                                         objectNames:&objectNames
+                                                                                        contentTypes:&contentTypes
+                                                                                           filePaths:&filePaths
+                                                                                        hashesArrays:&hashesArrays 
+                                                                             directoryObjectRequests:&directoryObjectRequests 
+                                                                                      sharingAccount:destinationNode.sharingAccount];
+                        if (operation.isCancelled) {
+                            [pool drain];
+                            return;
+                        }
+                        ASIPithosObjectRequest *previousObjectRequest = nil;
                         for (ASIPithosObjectRequest *objectRequest in directoryObjectRequests) {
+                            if (operation.isCancelled) {
+                                [pool drain];
+                                return;
+                            }
                             objectRequest.delegate = self;
                             objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                             objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -983,12 +1231,19 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                               NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", 
                               NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                               uploadNetworkQueue, @"networkQueue", 
-                              @"uploadQueue", @"queueType", 
+                              @"upload", @"operationType", 
                               nil]];
+                            if (previousObjectRequest)
+                                [objectRequest addDependency:previousObjectRequest];
+                            previousObjectRequest = objectRequest;
                             [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
                         }
-                        if (objectRequests) {
+                        if (!operation.isCancelled && objectRequests) {
                             for (NSUInteger i = 0 ; i < [objectRequests count] ; i++) {
+                                if (operation.isCancelled) {
+                                    [pool drain];
+                                    return;
+                                }
                                 ASIPithosObjectRequest *objectRequest = [objectRequests objectAtIndex:i];
                                 objectRequest.delegate = self;
                                 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
@@ -1018,15 +1273,19 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                   NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", 
                                   NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                                   uploadNetworkQueue, @"networkQueue", 
-                                  @"uploadQueue", @"queueType", 
+                                  @"upload", @"operationType", 
                                   nil]];
                                 if (destinationNode.sharingAccount)
                                     [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"];
+                                if (previousObjectRequest)
+                                    [objectRequest addDependency:previousObjectRequest];
+                                previousObjectRequest = objectRequest;
                                 [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
                             }
                         }
                         [pool drain];
-                    });
+                    }];
+                    [uploadQueue addOperation:operation];
                 }
             }
         }
@@ -1048,17 +1307,23 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
     for (PithosNode *node in nodes) {
         if (([node class] == [PithosObjectNode class]) || 
             (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            // Operation: Move an object or subdir/ node
+            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
                 NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
                 if ([node.pithosObject.name hasSuffix:@"/"])
                     destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
-                ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithContainerName:node.pithosContainer.name 
-                                                                                                     objectName:node.pithosObject.name 
-                                                                                       destinationContainerName:containerName 
-                                                                                          destinationObjectName:destinationObjectName 
-                                                                                                  checkIfExists:YES];
-                if (objectRequest) {
+                ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos 
+                                                                                       containerName:node.pithosContainer.name 
+                                                                                          objectName:node.pithosObject.name 
+                                                                            destinationContainerName:containerName 
+                                                                               destinationObjectName:destinationObjectName 
+                                                                                       checkIfExists:YES];
+                if (!operation.isCancelled && objectRequest) {
                     objectRequest.delegate = self;
                     objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                     objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -1081,25 +1346,38 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                       NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
                       NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                       moveNetworkQueue, @"networkQueue", 
-                      @"moveQueue", @"queueType", 
+                      @"move", @"operationType", 
                       nil]];
                     [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                 }
                 [pool drain];
-            });
+            }];
+            [moveQueue addOperation:operation];
         } else if ([node class] == [PithosSubdirNode class]) {
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            // Operation: Move a subdir node and its descendants
+            // The resulting ASIPithosObjectRequests are chained through dependencies
+            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
                 NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
                 if (node.pithosObject.subdir)
                     destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
-                NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithContainerName:node.pithosContainer.name 
-                                                                                                 objectName:node.pithosObject.name 
-                                                                                   destinationContainerName:containerName 
-                                                                                      destinationObjectName:destinationObjectName 
-                                                                                              checkIfExists:YES];
-                if (objectRequests) {
+                NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithPithos:pithos 
+                                                                                   containerName:node.pithosContainer.name 
+                                                                                      objectName:node.pithosObject.name 
+                                                                        destinationContainerName:containerName 
+                                                                           destinationObjectName:destinationObjectName 
+                                                                                   checkIfExists:YES];
+                if (!operation.isCancelled && objectRequests) {
+                    ASIPithosObjectRequest *previousObjectRequest = nil;
                     for (ASIPithosObjectRequest *objectRequest in objectRequests) {
+                        if (operation.isCancelled) {
+                            [pool drain];
+                            return;
+                        }
                         objectRequest.delegate = self;
                         objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                         objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -1123,13 +1401,17 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                           NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
                           NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                           moveNetworkQueue, @"networkQueue", 
-                          @"moveQueue", @"queueType", 
+                          @"move", @"operationType", 
                           nil]];
+                        if (previousObjectRequest)
+                            [objectRequest addDependency:previousObjectRequest];
+                        previousObjectRequest = objectRequest;
                         [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                     }
                 }
                 [pool drain];
-            });
+            }];
+            [moveQueue addOperation:operation];
         }
     }
     return YES;
@@ -1149,24 +1431,35 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
     for (PithosNode *node in nodes) {
         if (([node class] == [PithosObjectNode class]) || 
             (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            // Operation: Copy an object or subdir/ node
+            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
                 NSString *destinationObjectName;
                 if (![destinationNode isEqualTo:node.parent]) {
                     destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
                     if ([node.pithosObject.name hasSuffix:@"/"])
                         destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
                 } else {
-                    destinationObjectName = [PithosUtilities safeObjectNameForContainerName:containerName 
-                                                                                 objectName:node.pithosObject.name];
+                    destinationObjectName = [PithosUtilities safeObjectNameForPithos:pithos 
+                                                                       containerName:containerName 
+                                                                          objectName:node.pithosObject.name];
                 }
-                ASIPithosObjectRequest *objectRequest = [PithosUtilities copyObjectRequestWithContainerName:node.pithosContainer.name 
-                                                                                                     objectName:node.pithosObject.name 
-                                                                                       destinationContainerName:containerName 
-                                                                                          destinationObjectName:destinationObjectName 
-                                                                                                  checkIfExists:YES 
-                                                                                                 sharingAccount:node.sharingAccount];
-                if (objectRequest) {
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
+                ASIPithosObjectRequest *objectRequest = [PithosUtilities copyObjectRequestWithPithos:pithos 
+                                                                                       containerName:node.pithosContainer.name 
+                                                                                          objectName:node.pithosObject.name 
+                                                                            destinationContainerName:containerName 
+                                                                               destinationObjectName:destinationObjectName 
+                                                                                       checkIfExists:YES 
+                                                                                      sharingAccount:node.sharingAccount];
+                if (!operation.isCancelled && objectRequest) {
                     objectRequest.delegate = self;
                     objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                     objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -1189,32 +1482,50 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                       NSStringFromSelector(@selector(copyFinished:)), @"didFinishSelector", 
                       NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                       copyNetworkQueue, @"networkQueue", 
-                      @"copyQueue", @"queueType", 
+                      @"copy", @"operationType", 
                       nil]];
                     [copyNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                 }
                 [pool drain];
-            });
+            }];
+            [copyQueue addOperation:operation];
         } else if ([node class] == [PithosSubdirNode class]) {
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            // Operation: Copy a subdir node and its descendants
+            // The resulting ASIPithosObjectRequests are chained through dependencies
+            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
                 NSString *destinationObjectName;
                 if (![destinationNode isEqualTo:node.parent]) {
                     destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
                     if (node.pithosObject.subdir)
                         destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
                 } else {
-                    destinationObjectName = [PithosUtilities safeSubdirNameForContainerName:containerName 
-                                                                                     subdirName:node.pithosObject.name];
+                    destinationObjectName = [PithosUtilities safeSubdirNameForPithos:pithos 
+                                                                       containerName:containerName 
+                                                                          subdirName:node.pithosObject.name];
+                }
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
                 }
-                NSArray *objectRequests = [PithosUtilities copyObjectRequestsForSubdirWithContainerName:node.pithosContainer.name 
-                                                                                                 objectName:node.pithosObject.name 
-                                                                                   destinationContainerName:containerName 
-                                                                                      destinationObjectName:destinationObjectName 
-                                                                                              checkIfExists:YES 
-                                                                                             sharingAccount:node.sharingAccount];
-                if (objectRequests) {
+                NSArray *objectRequests = [PithosUtilities copyObjectRequestsForSubdirWithPithos:pithos 
+                                                                                   containerName:node.pithosContainer.name 
+                                                                                      objectName:node.pithosObject.name 
+                                                                        destinationContainerName:containerName 
+                                                                           destinationObjectName:destinationObjectName 
+                                                                                   checkIfExists:YES 
+                                                                                  sharingAccount:node.sharingAccount];
+                if (!operation.isCancelled && objectRequests) {
+                    ASIPithosObjectRequest *previousObjectRequest = nil;
                     for (ASIPithosObjectRequest *objectRequest in objectRequests) {
+                        if (operation.isCancelled) {
+                            [pool drain];
+                            return;
+                        }
                         objectRequest.delegate = self;
                         objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                         objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -1237,13 +1548,17 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                           NSStringFromSelector(@selector(copyFinished:)), @"didFinishSelector", 
                           NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                           copyNetworkQueue, @"networkQueue", 
-                          @"copyQueue", @"queueType", 
+                          @"copy", @"operationType", 
                           nil]];
+                        if (previousObjectRequest)
+                            [objectRequest addDependency:previousObjectRequest];
+                        previousObjectRequest = objectRequest;
                         [copyNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                     }
                 }
                 [pool drain];
-            });
+            }];
+            [copyQueue addOperation:operation];
         }
     }
     return YES;
@@ -1253,47 +1568,97 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 #pragma mark ASIHTTPRequestDelegate
 
 - (void)performRequestFinishedDelegateInBackground:(ASIPithosRequest *)request {
-    dispatch_queue_t queue;
-    if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"moveQueue"])
-        queue = moveQueue;
-    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"copyQueue"])
-        queue = copyQueue;
-    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"deleteQueue"])
-        queue = deleteQueue;
-    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"uploadQueue"])
-        queue = uploadQueue;
-    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"downloadQueue"])
-        queue = downloadQueue;
-    else
+    NSOperationQueue *callbackQueue;
+    NSString *operationType = [request.userInfo objectForKey:@"operationType"];
+    if ([operationType isEqualToString:@"move"])
+        callbackQueue = moveCallbackQueue;
+    else if ([operationType isEqualToString:@"copy"])
+        callbackQueue = copyCallbackQueue;
+    else if ([operationType isEqualToString:@"delete"])
+        callbackQueue = deleteCallbackQueue;
+    else if ([operationType isEqualToString:@"upload"])
+        callbackQueue = uploadCallbackQueue;
+    else if ([operationType isEqualToString:@"download"])
+        callbackQueue = downloadCallbackQueue;
+    else {
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
+                              withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
+        });
         return;
-    dispatch_async(queue, ^{
-        [self performSelector:NSSelectorFromString([request.userInfo objectForKey:@"didFinishSelector"]) withObject:request];
-    });
+    }
+    // Add an operation to the callbackQueue with a completionBlock for the case of cancellation
+    NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self 
+                                                                             selector:NSSelectorFromString([request.userInfo objectForKey:@"didFinishSelector"]) 
+                                                                               object:request] autorelease];
+    operation.completionBlock = ^{
+        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+        if ([[request.userInfo objectForKey:@"operation"] isCancelled]) {
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
+                                  withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
+            });
+        }
+        [pool drain];
+    };
+    [(NSMutableDictionary *)request.userInfo setObject:operation forKey:@"operation"];
+    [callbackQueue addOperation:operation];
 }
 
 - (void)performRequestFailedDelegateInBackground:(ASIPithosRequest *)request {
-    dispatch_queue_t queue;
-    if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"moveQueue"])
-        queue = moveQueue;
-    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"copyQueue"])
-        queue = copyQueue;
-    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"deleteQueue"])
-        queue = deleteQueue;
-    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"uploadQueue"])
-        queue = uploadQueue;
-    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"downloadQueue"])
-        queue = downloadQueue;
-    else
-        return;
-    dispatch_async(queue, ^{
-        [self performSelector:NSSelectorFromString([request.userInfo objectForKey:@"didFailSelector"]) withObject:request];
-    });
+    if (request.isCancelled) {
+        // Request has been cancelled 
+        // The callbackQueue might be suspended so we call directly the callback method, since it does minimal work anyway
+        [self performSelector:NSSelectorFromString([request.userInfo objectForKey:@"didFailSelector"]) 
+                   withObject:request];
+    } else {
+        NSOperationQueue *callbackQueue;
+        NSString *operationType = [request.userInfo objectForKey:@"operationType"];
+        if ([operationType isEqualToString:@"move"])
+            callbackQueue = moveCallbackQueue;
+        else if ([operationType isEqualToString:@"copy"])
+            callbackQueue = copyCallbackQueue;
+        else if ([operationType isEqualToString:@"delete"])
+            callbackQueue = deleteCallbackQueue;
+        else if ([operationType isEqualToString:@"upload"])
+            callbackQueue = uploadCallbackQueue;
+        else if ([operationType isEqualToString:@"download"])
+            callbackQueue = downloadCallbackQueue;
+        else {
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
+                                  withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
+            });
+            return;
+        }
+        // Add an operation to the callbackQueue with a completionBlock for the case of cancellation
+        NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self 
+                                                                                 selector:NSSelectorFromString([request.userInfo objectForKey:@"didFailSelector"]) 
+                                                                                   object:request] autorelease];
+        operation.completionBlock = ^{
+            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+            if ([[request.userInfo objectForKey:@"operation"] isCancelled]) {
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
+                                      withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
+                });
+            }
+            [pool drain];
+        };
+        [(NSMutableDictionary *)request.userInfo setObject:operation forKey:@"operation"];
+        [callbackQueue addOperation:operation];
+    }
 }
 
 - (void)requestFailed:(ASIPithosRequest *)request {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSOperation *operation = [request.userInfo objectForKey:@"operation"];
     NSLog(@"Request failed: %@", request.url);
-    if ([request isCancelled]) {
+    if (operation.isCancelled) {
+        [pool drain];
+        return;        
+    }
+    if (request.isCancelled) {
         dispatch_async(dispatch_get_main_queue(), ^{
             [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
                               withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
@@ -1322,14 +1687,23 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 
 - (void)downloadObjectFinished:(ASIPithosObjectRequest *)objectRequest {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
     NSLog(@"Download finished: %@", objectRequest.url);
-    if (objectRequest.responseStatusCode == 200) {
+    if (operation.isCancelled) {
+        [self requestFailed:objectRequest];
+    } else if (objectRequest.responseStatusCode == 200) {
         NSString *filePath = [objectRequest.userInfo objectForKey:@"filePath"];
         PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"];
         NSUInteger totalBytes = activity.totalBytes;
         
         // XXX change contentLength to objectContentLength if it is fixed in the server
-        if (([objectRequest contentLength] == 0) && ![PithosUtilities isContentTypeDirectory:[objectRequest contentType]]) {
+        if ([objectRequest contentLength] == 0) {
+            // The check above was:
+            // if (([objectRequest contentLength] == 0) && ![PithosUtilities isContentTypeDirectory:[objectRequest contentType]]) {
+            // I checked for directory content types in order not to create a file in place of a directory,
+            // but this callback method is not called in the case of a directory download.
+            // It maybe the case though, when downloading an old version of an object, is of a directory content type.
+            // In this case, a file should be created. This is actually a feature that allows you to hide data in a directory object.
             NSLog(@"Downloaded  0 bytes");
             NSFileManager *fileManager = [NSFileManager defaultManager];
             if (![fileManager fileExistsAtPath:filePath]) {
@@ -1363,23 +1737,26 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 
 - (void)uploadDirectoryObjectFinished:(ASIPithosObjectRequest *)objectRequest {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
     NSLog(@"Upload directory object finished: %@", objectRequest.url);
-    if (objectRequest.responseStatusCode == 201) {
+    if (operation.isCancelled) {
+        [self requestFailed:objectRequest];
+    } else if (objectRequest.responseStatusCode == 201) {
         NSLog(@"Directory object created: %@", objectRequest.url);
         dispatch_async(dispatch_get_main_queue(), ^{
             [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
                               withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
+            for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
+                [node forceRefresh];
+            }
+            for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
+                [node refresh];
+            }
+            if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
+                [self forceRefresh:self];
+            else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
+                [self refresh:self];
         });
-        for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
-            [node forceRefresh];
-        }
-        for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
-            [node refresh];
-        }
-        if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
-            [self forceRefresh:self];
-        else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
-            [self refresh:self];
     } else {
         [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"];
         [self requestFailed:objectRequest];
@@ -1389,29 +1766,32 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 
 - (void)uploadObjectUsingHashMapFinished:(ASIPithosObjectRequest *)objectRequest {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
     NSLog(@"Upload using hashmap finished: %@", objectRequest.url);
     NSString *fileName = [objectRequest.userInfo objectForKey:@"fileName"];
     PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"];
     NSUInteger totalBytes = activity.totalBytes;
     NSUInteger currentBytes = activity.currentBytes;
-    if (objectRequest.responseStatusCode == 201) {
+    if (operation.isCancelled) {
+        [self requestFailed:objectRequest];
+    } else if (objectRequest.responseStatusCode == 201) {
         NSLog(@"Object created: %@", objectRequest.url);
         dispatch_async(dispatch_get_main_queue(), ^{
             [activityFacility endActivity:activity 
                               withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] 
                                totalBytes:totalBytes 
                              currentBytes:totalBytes];
+            for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
+                [node forceRefresh];
+            }
+            for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
+                [node refresh];
+            }
+            if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
+                [self forceRefresh:self];
+            else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
+                [self refresh:self];        
         });
-        for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
-            [node forceRefresh];
-        }
-        for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
-            [node refresh];
-        }
-        if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
-            [self forceRefresh:self];
-        else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
-            [self refresh:self];        
     } else if (objectRequest.responseStatusCode == 409) {
         NSUInteger iteration = [[objectRequest.userInfo objectForKey:@"iteration"] unsignedIntegerValue];
         if (iteration == 0) {
@@ -1431,7 +1811,7 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
         }
         NSLog(@"object is missing hashes: %@", objectRequest.url);
         NSIndexSet *missingBlocks = [PithosUtilities missingBlocksForHashes:[objectRequest.userInfo objectForKey:@"hashes"]
-                                                  withMissingHashesResponse:[objectRequest responseString]];
+                                                          withMissingHashes:[objectRequest hashes]];
         NSUInteger blockSize = [[objectRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue];
         if (totalBytes >= [missingBlocks count]*blockSize)
             currentBytes = totalBytes - [missingBlocks count]*blockSize;
@@ -1442,11 +1822,12 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                 currentBytes:currentBytes];
         });
         NSUInteger missingBlockIndex = [missingBlocks firstIndex];
-        __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithContainerName:[objectRequest.userInfo objectForKey:@"containerName"] 
-                                                                                                                    blockSize:blockSize 
-                                                                                                                      forFile:[objectRequest.userInfo objectForKey:@"filePath"] 
-                                                                                                            missingBlockIndex:missingBlockIndex 
-                                                                                                               sharingAccount:[objectRequest.userInfo objectForKey:@"sharingAccount"]];
+        __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithPithos:pithos 
+                                                                                                         containerName:[objectRequest.userInfo objectForKey:@"containerName"] 
+                                                                                                             blockSize:blockSize 
+                                                                                                               forFile:[objectRequest.userInfo objectForKey:@"filePath"] 
+                                                                                                     missingBlockIndex:missingBlockIndex 
+                                                                                                        sharingAccount:[objectRequest.userInfo objectForKey:@"sharingAccount"]];
         newContainerRequest.delegate = self;
         newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
         newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -1472,25 +1853,29 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 
 - (void)uploadMissingBlockFinished:(ASIPithosContainerRequest *)containerRequest {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSOperation *operation = [containerRequest.userInfo objectForKey:@"operation"];
     NSLog(@"Upload of missing block finished: %@", containerRequest.url);
     NSUInteger blockSize = [[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue];
     NSString *fileName = [containerRequest.userInfo objectForKey:@"fileName"];
     PithosActivity *activity = [containerRequest.userInfo objectForKey:@"activity"];
-    if (containerRequest.responseStatusCode == 202) {
+    if (operation.isCancelled) {
+        [self requestFailed:containerRequest];
+    } else if (containerRequest.responseStatusCode == 202) {
         NSIndexSet *missingBlocks = [containerRequest.userInfo objectForKey:@"missingBlocks"];
         NSUInteger missingBlockIndex = [[containerRequest.userInfo objectForKey:@"missingBlockIndex"] unsignedIntegerValue];
         missingBlockIndex = [missingBlocks indexGreaterThanIndex:missingBlockIndex];
         if (missingBlockIndex == NSNotFound) {
             NSArray *hashes = [containerRequest.userInfo objectForKey:@"hashes"];
-            ASIPithosObjectRequest *newObjectRequest = [PithosUtilities writeObjectDataRequestWithContainerName:[containerRequest.userInfo objectForKey:@"containerName"] 
-                                                                                                     objectName:[containerRequest.userInfo objectForKey:@"objectName"] 
-                                                                                                    contentType:[containerRequest.userInfo objectForKey:@"contentType"] 
-                                                                                                      blockSize:blockSize 
-                                                                                                      blockHash:[containerRequest.userInfo objectForKey:@"blockHash"]
-                                                                                                        forFile:[containerRequest.userInfo objectForKey:@"filePath"] 
-                                                                                                  checkIfExists:NO 
-                                                                                                         hashes:&hashes 
-                                                                                                 sharingAccount:[containerRequest.userInfo objectForKey:@"sharingAccount"]];
+            ASIPithosObjectRequest *newObjectRequest = [PithosUtilities writeObjectDataRequestWithPithos:pithos 
+                                                                                           containerName:[containerRequest.userInfo objectForKey:@"containerName"] 
+                                                                                              objectName:[containerRequest.userInfo objectForKey:@"objectName"] 
+                                                                                             contentType:[containerRequest.userInfo objectForKey:@"contentType"] 
+                                                                                               blockSize:blockSize 
+                                                                                               blockHash:[containerRequest.userInfo objectForKey:@"blockHash"]
+                                                                                                 forFile:[containerRequest.userInfo objectForKey:@"filePath"] 
+                                                                                           checkIfExists:NO 
+                                                                                                  hashes:&hashes 
+                                                                                          sharingAccount:[containerRequest.userInfo objectForKey:@"sharingAccount"]];
             newObjectRequest.delegate = self;
             newObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
             newObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -1501,11 +1886,12 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
             [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)) forKey:@"didFinishSelector"];
             [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]];
         } else {
-            __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithContainerName:[containerRequest.userInfo objectForKey:@"containerName"]
-                                                                                                                        blockSize:[[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue]
-                                                                                                                          forFile:[containerRequest.userInfo objectForKey:@"filePath"] 
-                                                                                                                missingBlockIndex:missingBlockIndex 
-                                                                                                                   sharingAccount:[containerRequest.userInfo objectForKey:@"sharingAccount"]];
+            __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithPithos:pithos 
+                                                                                                             containerName:[containerRequest.userInfo objectForKey:@"containerName"]
+                                                                                                                 blockSize:[[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue]
+                                                                                                                   forFile:[containerRequest.userInfo objectForKey:@"filePath"] 
+                                                                                                         missingBlockIndex:missingBlockIndex 
+                                                                                                            sharingAccount:[containerRequest.userInfo objectForKey:@"sharingAccount"]];
             newContainerRequest.delegate = self;
             newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
             newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -1529,22 +1915,25 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 
 - (void)moveFinished:(ASIPithosObjectRequest *)objectRequest {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
     NSLog(@"Move object finished: %@", objectRequest.url);
-    if (objectRequest.responseStatusCode == 201) {
+    if (operation.isCancelled) {
+        [self requestFailed:objectRequest];
+    } else if (objectRequest.responseStatusCode == 201) {
         dispatch_async(dispatch_get_main_queue(), ^{
             [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
                               withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
+            for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
+                [node forceRefresh];
+            }
+            for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
+                [node refresh];
+            }
+            if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
+                [self forceRefresh:self];
+            else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
+                [self refresh:self];
         });
-        for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
-            [node forceRefresh];
-        }
-        for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
-            [node refresh];
-        }
-        if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
-            [self forceRefresh:self];
-        else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
-            [self refresh:self];
     } else {
         [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"];
         [self requestFailed:objectRequest];
@@ -1554,22 +1943,25 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 
 - (void)copyFinished:(ASIPithosObjectRequest *)objectRequest {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
     NSLog(@"Copy object finished: %@", objectRequest.url);
-    if (objectRequest.responseStatusCode == 201) {
+    if (operation.isCancelled) {
+        [self requestFailed:objectRequest];
+    } else if (objectRequest.responseStatusCode == 201) {
         dispatch_async(dispatch_get_main_queue(), ^{
             [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
                               withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
+            for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
+                [node forceRefresh];
+            }
+            for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
+                [node refresh];
+            }
+            if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
+                [self forceRefresh:self];
+            else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
+                [self refresh:self];
         });
-        for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
-            [node forceRefresh];
-        }
-        for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
-            [node refresh];
-        }
-        if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
-            [self forceRefresh:self];
-        else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
-            [self refresh:self];
     } else {
         [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"];
         [self requestFailed:objectRequest];
@@ -1579,22 +1971,25 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 
 - (void)deleteObjectFinished:(ASIPithosObjectRequest *)objectRequest {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
     NSLog(@"Delete object finished: %@", objectRequest.url);
-    if (objectRequest.responseStatusCode == 204) {
+    if (operation.isCancelled) {
+        [self requestFailed:objectRequest];
+    } else if (objectRequest.responseStatusCode == 204) {
         dispatch_async(dispatch_get_main_queue(), ^{
             [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
                               withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
+            for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
+                [node forceRefresh];
+            }
+            for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
+                [node refresh];
+            }
+            if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
+                [self forceRefresh:self];
+            else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
+                [self refresh:self];
         });
-        for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
-            [node forceRefresh];
-        }
-        for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
-            [node refresh];
-        }
-        if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
-            [self forceRefresh:self];
-        else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
-            [self refresh:self];
     } else {
         [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"];
         [self requestFailed:objectRequest];
@@ -1636,6 +2031,8 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 #pragma mark NSOutlineViewDataSource
 
 - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
+    if (!browserInitialized)
+        return 0;
     if (item == nil)
         return 2;
     if (item == containersNode)
@@ -1646,6 +2043,8 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 }
 
 - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
+    if (!browserInitialized)
+        return nil;
     if (item == nil)
         return (!index ? containersNode : sharedNode);
     if (item == sharedNode)
@@ -1679,7 +2078,8 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
     if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
         result = NSDragOperationCopy;
     } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) {
-        if ([info draggingSourceOperationMask] & NSDragOperationMove) {
+        if (![[draggedNodes objectAtIndex:0] shared] && ![[draggedNodes objectAtIndex:0] sharingAccount] && 
+            ([info draggingSourceOperationMask] & NSDragOperationMove)) {
             // NSDragOperationCopy | NSDragOperationMove -> NSDragOperationMove
             if (![dropNode isEqualTo:draggedParentNode])
                 result = NSDragOperationMove;
@@ -1705,7 +2105,8 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
         if (item && (index == NSOutlineViewDropOnItemIndex) && (draggedNodes != nil)) {
             PithosNode *node = (PithosNode *)item;
             NSLog(@"drag local node: %@", node.url);
-            if ([info draggingSourceOperationMask] & NSDragOperationMove)
+            if (![[draggedNodes objectAtIndex:0] shared] && ![[draggedNodes objectAtIndex:0] sharingAccount] && 
+                ([info draggingSourceOperationMask] & NSDragOperationMove))
                 return [self moveNodes:draggedNodes toNode:node];
             else if ([info draggingSourceOperationMask] & NSDragOperationCopy)
                 return [self copyNodes:draggedNodes toNode:node];
@@ -1791,69 +2192,78 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
             ([menuNode class] == [PithosSharingAccountsNode class]) ||
             ([menuNode class] == [PithosEmptyNode class]))
             return;
-        BOOL shared = menuNode.shared;
-        BOOL sharingAccount = (menuNode.sharingAccount != nil);
         // New Folder
-        if (!shared && !sharingAccount) {
+        if (!menuNode.shared && !menuNode.sharingAccount) {
             menuItem = [[[NSMenuItem alloc] initWithTitle:@"New Folder" action:@selector(menuNewFolder:) keyEquivalent:@""] autorelease];
             [menuItem setRepresentedObject:menuNode];
             [menu addItem:menuItem];
             [menu addItem:[NSMenuItem separatorItem]];
         }
+        // Refresh
+        menuItem = [[[NSMenuItem alloc] initWithTitle:@"Refresh" action:@selector(refresh:) keyEquivalent:@""] autorelease];
+        [menu addItem:menuItem];
+        [menu addItem:[NSMenuItem separatorItem]];
         // Get Info
-        menuItem = [[[NSMenuItem alloc] initWithTitle:@"Get Info" action:@selector(menuGetInfo:) keyEquivalent:@""] autorelease];
+        menuItem = [[[NSMenuItem alloc] initWithTitle:(([menuNode class] == [PithosContainerNode class]) ? @"Info" : @"Info and Sharing") 
+                                               action:@selector(menuGetInfo:) 
+                                        keyEquivalent:@""] autorelease];
         [menuItem setRepresentedObject:[NSArray arrayWithObject:menuNode]];
         [menu addItem:menuItem];
         // Paste
-        if (!shared && !sharingAccount) {
-            if (clipboardNodes) {
-                NSUInteger clipboardNodesCount = [clipboardNodes count];
-                if (clipboardNodesCount == 0) {
-                    self.clipboardNodes = nil;
-                } else if (clipboardCopy || ![menuNode isEqualTo:clipboardParentNode]) {
-                    if (clipboardNodesCount == 1)
-                        menuItemTitle = [NSString stringWithString:@"Paste Item"];
-                    else
-                        menuItemTitle = [NSString stringWithString:@"Paste Items"];
-                    [menu addItem:[NSMenuItem separatorItem]];
-                    menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuPaste:) keyEquivalent:@""] autorelease];
-                    [menuItem setRepresentedObject:menuNode];
-                    [menu addItem:menuItem];
-                }
+        if (clipboardNodes && !menuNode.shared && !menuNode.sharingAccount && 
+            (([menuNode class] == [PithosContainerNode class]) || 
+             (([menuNode class] == [PithosSubdirNode class]) && 
+              (menuNode.pithosObject.subdir || ![menuNode.pithosObject.name hasSuffix:@"/"])))) {
+            NSUInteger clipboardNodesCount = [clipboardNodes count];
+            if (clipboardNodesCount == 0) {
+                self.clipboardNodes = nil;
+            } else if (clipboardCopy || ![menuNode isEqualTo:clipboardParentNode]) {
+                if (clipboardNodesCount == 1)
+                    menuItemTitle = [NSString stringWithString:@"Paste Item"];
+                else
+                    menuItemTitle = [NSString stringWithString:@"Paste Items"];
+                [menu addItem:[NSMenuItem separatorItem]];
+                menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuPaste:) keyEquivalent:@""] autorelease];
+                [menuItem setRepresentedObject:menuNode];
+                [menu addItem:menuItem];
             }
         }
     } else {
         // Node context menu
         NSUInteger menuNodesCount = [menuNodes count];
-        PithosNode *firstMenuNode = (PithosNode *)[menuNodes objectAtIndex:0];
-        BOOL shared = firstMenuNode.shared;
-        BOOL sharingAccount = (firstMenuNode.sharingAccount != nil);
+        PithosNode *firstMenuNode = [menuNodes objectAtIndex:0];
         // Move to Trash (pithos container only)
         // Delete
-        if (!shared && !sharingAccount) {
-            if ([rootNode class] == [PithosContainerNode class]) {
-                if ([rootNode.pithosContainer.name isEqualToString:@"pithos"]) {
-                    menuItem = [[[NSMenuItem alloc] initWithTitle:@"Move to Trash" action:@selector(menuMoveToTrash:) keyEquivalent:@""] autorelease];
-                    [menuItem setRepresentedObject:menuNodes];
-                    [menu addItem:menuItem];
-                }
-                menuItem = [[[NSMenuItem alloc] initWithTitle:@"Delete" action:@selector(menuDelete:) keyEquivalent:@""] autorelease];
+        if (!firstMenuNode.shared && !firstMenuNode.sharingAccount && ([rootNode class] == [PithosContainerNode class])) {
+            if ([rootNode.pithosContainer.name isEqualToString:@"pithos"]) {
+                menuItem = [[[NSMenuItem alloc] initWithTitle:@"Move to Trash" 
+                                                       action:@selector(menuMoveToTrash:) 
+                                                keyEquivalent:@""] autorelease];
                 [menuItem setRepresentedObject:menuNodes];
                 [menu addItem:menuItem];
-                [menu addItem:[NSMenuItem separatorItem]];
             }
+            menuItem = [[[NSMenuItem alloc] initWithTitle:@"Delete" action:@selector(menuDelete:) keyEquivalent:@""] autorelease];
+            [menuItem setRepresentedObject:menuNodes];
+            [menu addItem:menuItem];
+            [menu addItem:[NSMenuItem separatorItem]];
         }
+        // Refresh
+        menuItem = [[[NSMenuItem alloc] initWithTitle:@"Refresh" action:@selector(refresh:) keyEquivalent:@""] autorelease];
+        [menu addItem:menuItem];
         // Get Info
-        if (!sharingAccount || ([firstMenuNode class] != [PithosAccountNode class])) {
-            menuItem = [[[NSMenuItem alloc] initWithTitle:@"Get Info" action:@selector(menuGetInfo:) keyEquivalent:@""] autorelease];
+        if (!firstMenuNode.sharingAccount || ([firstMenuNode class] != [PithosAccountNode class])) {
+            [menu addItem:[NSMenuItem separatorItem]];
+            menuItem = [[[NSMenuItem alloc] initWithTitle:(([firstMenuNode class] == [PithosContainerNode class]) ? @"Info" : @"Info and Sharing") 
+                                                   action:@selector(menuGetInfo:) 
+                                            keyEquivalent:@""] autorelease];
             [menuItem setRepresentedObject:menuNodes];
             [menu addItem:menuItem];
             
-            if ((!shared && !sharingAccount) || ([firstMenuNode class] != [PithosContainerNode class]))
+            if ((!firstMenuNode.shared && !firstMenuNode.sharingAccount) || ([firstMenuNode class] != [PithosContainerNode class]))
                 [menu addItem:[NSMenuItem separatorItem]];
         }
         // Cut
-        if (!shared && !sharingAccount) {
+        if (!firstMenuNode.shared && !firstMenuNode.sharingAccount) {
             if (menuNodesCount == 1)
                 menuItemTitle = [NSString stringWithFormat:@"Cut \"%@\"", ((PithosNode *)[menuNodes objectAtIndex:0]).displayName];
             else 
@@ -1863,7 +2273,7 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
             [menu addItem:menuItem];
         }
         // Copy
-        if ((!shared && !sharingAccount) || 
+        if ((!firstMenuNode.shared && !firstMenuNode.sharingAccount) || 
             (([firstMenuNode class] != [PithosContainerNode class]) && ([firstMenuNode class] != [PithosAccountNode class]))) {
             if (menuNodesCount == 1)
                 menuItemTitle = [NSString stringWithFormat:@"Copy \"%@\"", ((PithosNode *)[menuNodes objectAtIndex:0]).displayName];
@@ -1874,53 +2284,126 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
             [menu addItem:menuItem];
         }
         // Paste
-        if (!shared && !sharingAccount) {
-            if (menuNodesCount == 1) {
-                PithosNode *menuNode = [menuNodes objectAtIndex:0];
-                if (([menuNode class] == [PithosSubdirNode class]) && 
-                    (menuNode.pithosObject.subdir || ![menuNode.pithosObject.name hasSuffix:@"/"])) {
-                    if (clipboardNodes) {
-                        NSUInteger clipboardNodesCount = [clipboardNodes count];
-                        if (clipboardNodesCount == 0) {
-                            self.clipboardNodes = nil;
-                        } else if (clipboardCopy || ![menuNode isEqualTo:clipboardParentNode]) {
-                            if (clipboardNodesCount == 1)
-                                menuItemTitle = [NSString stringWithString:@"Paste Item"];
-                            else
-                                menuItemTitle = [NSString stringWithString:@"Paste Items"];
-                            menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuPaste:) keyEquivalent:@""] autorelease];
-                            [menuItem setRepresentedObject:menuNode];
-                            [menu addItem:menuItem];
-                        }
-                    }
-                }
+        if (clipboardNodes && !firstMenuNode.shared && !firstMenuNode.sharingAccount && (menuNodesCount == 1) && 
+            ([firstMenuNode class] == [PithosSubdirNode class]) && 
+            (firstMenuNode.pithosObject.subdir || ![firstMenuNode.pithosObject.name hasSuffix:@"/"])) {
+            NSUInteger clipboardNodesCount = [clipboardNodes count];
+            if (clipboardNodesCount == 0) {
+                self.clipboardNodes = nil;
+            } else if (clipboardCopy || ![firstMenuNode isEqualTo:clipboardParentNode]) {
+                if (clipboardNodesCount == 1)
+                    menuItemTitle = [NSString stringWithString:@"Paste Item"];
+                else
+                    menuItemTitle = [NSString stringWithString:@"Paste Items"];
+                menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuPaste:) keyEquivalent:@""] autorelease];
+                [menuItem setRepresentedObject:firstMenuNode];
+                [menu addItem:menuItem];
             }
         }
     }
 }
 
 #pragma mark -
+#pragma mark NSMenuValidation
+
+- (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
+    if ((menuItem.action == @selector(cut:)) || (menuItem.action == @selector(copy:)) || (menuItem.action == @selector(delete:))) {
+        NSArray *menuNodesIndexPaths = [browser selectionIndexPaths];
+        if ([menuNodesIndexPaths count] == 0)
+            return NO;
+        
+        PithosNode *firstMenuNode = [browser itemAtIndexPath:[menuNodesIndexPaths objectAtIndex:0]];
+        if (((menuItem.action == @selector(cut:)) && (firstMenuNode.shared || firstMenuNode.sharingAccount)) || 
+            ((menuItem.action == @selector(copy:)) && (firstMenuNode.shared || firstMenuNode.sharingAccount) && 
+             (([firstMenuNode class] == [PithosContainerNode class]) || ([firstMenuNode class] == [PithosAccountNode class]))) ||
+            ((menuItem.action == @selector(delete:)) && 
+             (firstMenuNode.shared || firstMenuNode.sharingAccount || ([rootNode class] != [PithosContainerNode class]) || 
+              ((menuItem.tag == 0) && ![rootNode.pithosContainer.name isEqualToString:@"pithos"]))))
+            return NO;
+        
+        NSMutableArray *menuNodes = [NSMutableArray arrayWithCapacity:[menuNodesIndexPaths count]];
+        for (NSIndexPath *nodeIndexPath in menuNodesIndexPaths) {
+            [menuNodes addObject:[browser itemAtIndexPath:nodeIndexPath]];
+        }
+        menuItem.representedObject = menuNodes;
+    } else if (menuItem.action == @selector(paste:)) {
+        if (!clipboardNodes || ![clipboardNodes count])
+            return NO;
+        
+        NSArray *menuNodesIndexPaths = [browser selectionIndexPaths];
+        PithosNode *menuNode;
+        if ([menuNodesIndexPaths count] == 0)
+            menuNode = [browser parentForItemsInColumn:0];
+        else if (([menuNodesIndexPaths count] != 1) || 
+                 ([[browser itemAtIndexPath:[menuNodesIndexPaths objectAtIndex:0]] class] == [PithosObjectNode class]))
+            menuNode = [browser parentForItemsInColumn:([[menuNodesIndexPaths objectAtIndex:0] length] - 1)];
+        else
+            menuNode = [browser itemAtIndexPath:[menuNodesIndexPaths objectAtIndex:0]];
+        
+        if (menuNode.shared || menuNode.sharingAccount || 
+            (([menuNode class] != [PithosContainerNode class]) && 
+             (([menuNode class] != [PithosSubdirNode class]) || 
+              (!menuNode.pithosObject.subdir && [menuNode.pithosObject.name hasSuffix:@"/"]))) || 
+            (!clipboardCopy && [menuNode isEqualTo:clipboardParentNode]))
+            return NO;
+
+        menuItem.representedObject = menuNode;
+    }
+    return YES;
+}
+
+- (void)cut:(NSMenuItem *)sender {
+    [self menuCut:sender];
+}
+
+- (void)copy:(NSMenuItem *)sender {
+    [self menuCopy:sender];
+}
+
+- (void)paste:(NSMenuItem *)sender {
+    [self menuPaste:sender];
+}
+
+- (void)delete:(NSMenuItem *)sender {
+    if (sender.tag == 0)
+        [self menuMoveToTrash:sender];
+    else
+        [self menuDelete:sender];
+}
+
+#pragma mark -
 #pragma mark Menu Actions
 
 - (void)menuNewFolder:(NSMenuItem *)sender {
     PithosNode *node = (PithosNode *)[sender representedObject];
     if ([node class] == [PithosContainerNode class]) {
-        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+        // Operation: Create (upload) a new root application/directory object
+        __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
             NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-            NSString *safeObjectName = [PithosUtilities safeSubdirNameForContainerName:node.pithosContainer.name 
-                                                                                subdirName:@"untitled folder"];
+            if (operation.isCancelled) {
+                [pool drain];
+                return;
+            }
+            NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos 
+                                                                  containerName:node.pithosContainer.name 
+                                                                     subdirName:@"untitled folder"];
             NSString *fileName = [safeObjectName lastPathComponent];
-            ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:node.pithosContainer.name 
-                                                                                                         objectName:safeObjectName 
-                                                                                                           eTag:nil 
-                                                                                                    contentType:@"application/directory" 
-                                                                                                contentEncoding:nil 
-                                                                                             contentDisposition:nil 
-                                                                                                       manifest:nil 
-                                                                                                        sharing:nil 
-                                                                                                       isPublic:ASIPithosObjectRequestPublicIgnore 
-                                                                                                       metadata:nil 
-                                                                                                           data:[NSData data]];
+            if (operation.isCancelled) {
+                [pool drain];
+                return;
+            }
+            ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos 
+                                                                                               containerName:node.pithosContainer.name 
+                                                                                                  objectName:safeObjectName 
+                                                                                                        eTag:nil 
+                                                                                                 contentType:@"application/directory" 
+                                                                                             contentEncoding:nil 
+                                                                                          contentDisposition:nil 
+                                                                                                    manifest:nil 
+                                                                                                     sharing:nil 
+                                                                                                    isPublic:ASIPithosObjectRequestPublicIgnore 
+                                                                                                    metadata:nil 
+                                                                                                        data:[NSData data]];
             objectRequest.delegate = self;
             objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
             objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -1940,29 +2423,41 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                       NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", 
                                       NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                                       uploadNetworkQueue, @"networkQueue", 
-                                      @"uploadQueue", @"queueType", 
+                                      @"upload", @"operationType", 
                                       nil];
             [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
             [pool drain];
-        });
+        }];
+        [uploadQueue addOperation:operation];
     } else if (([node class] == [PithosSubdirNode class]) && 
                (node.pithosObject.subdir || ![node.pithosObject.name hasSuffix:@"/"])) {
-        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+        // Operation: Create (upload) a new aplication/directory object
+        __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
             NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-            NSString *safeObjectName = [PithosUtilities safeSubdirNameForContainerName:node.pithosContainer.name 
-                                                                               subdirName:[node.pithosObject.name stringByAppendingPathComponent:@"untitled folder"]];
+            if (operation.isCancelled) {
+                [pool drain];
+                return;
+            }
+            NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos 
+                                                                  containerName:node.pithosContainer.name 
+                                                                     subdirName:[node.pithosObject.name stringByAppendingPathComponent:@"untitled folder"]];
             NSString *fileName = [safeObjectName lastPathComponent];
-            ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:node.pithosContainer.name 
-                                                                                                        objectName:safeObjectName 
-                                                                                                              eTag:nil 
-                                                                                                       contentType:@"application/directory" 
-                                                                                                   contentEncoding:nil 
-                                                                                                contentDisposition:nil 
-                                                                                                          manifest:nil 
-                                                                                                           sharing:nil 
-                                                                                                          isPublic:ASIPithosObjectRequestPublicIgnore 
-                                                                                                          metadata:nil 
-                                                                                                              data:[NSData data]];
+            if (operation.isCancelled) {
+                [pool drain];
+                return;
+            }
+            ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos 
+                                                                                               containerName:node.pithosContainer.name 
+                                                                                                  objectName:safeObjectName 
+                                                                                                        eTag:nil 
+                                                                                                 contentType:@"application/directory" 
+                                                                                             contentEncoding:nil 
+                                                                                          contentDisposition:nil 
+                                                                                                    manifest:nil 
+                                                                                                     sharing:nil 
+                                                                                                    isPublic:ASIPithosObjectRequestPublicIgnore 
+                                                                                                    metadata:nil 
+                                                                                                        data:[NSData data]];
             objectRequest.delegate = self;
             objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
             objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -1982,11 +2477,12 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                       NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", 
                                       NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                                       uploadNetworkQueue, @"networkQueue", 
-                                      @"uploadQueue", @"queueType", 
+                                      @"upload", @"operationType", 
                                       nil];
             [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
             [pool drain];
-        });
+        }];
+        [uploadQueue addOperation:operation];
     }
 }
 
@@ -2000,13 +2496,23 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
     for (PithosNode *node in ((NSArray *)[sender representedObject])) {
         if (([node class] == [PithosObjectNode class]) || 
             (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            // Operation: Delete an object or subdir/ node
+            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
                 NSString *fileName = [node.pithosObject.name lastPathComponent];
                 if ([node.pithosObject.name hasSuffix:@"/"])
                     fileName = [fileName stringByAppendingString:@"/"];
-                ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithContainerName:node.pithosContainer.name 
-                                                                                                          objectName:node.pithosObject.name];
+                ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos 
+                                                                                                containerName:node.pithosContainer.name 
+                                                                                                   objectName:node.pithosObject.name];
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
                 objectRequest.delegate = self;
                 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -2025,18 +2531,31 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                           NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", 
                                           NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                                           deleteNetworkQueue, @"networkQueue", 
-                                          @"deleteQueue", @"queueType", 
+                                          @"delete", @"operationType", 
                                           nil];
                 [deleteNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                 [pool drain];
-            });
+            }];
+            [deleteQueue addOperation:operation];
         } else if ([node class] == [PithosSubdirNode class]) {
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            // Operation: Delete a subdir node and its descendants
+            // The resulting ASIPithosObjectRequests are chained through dependencies
+            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-                NSArray *objectRequests = [PithosUtilities deleteObjectRequestsForSubdirWithContainerName:node.pithosContainer.name 
-                                                                                               objectName:node.pithosObject.name];
-                if (objectRequests) {
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
+                NSArray *objectRequests = [PithosUtilities deleteObjectRequestsForSubdirWithPithos:pithos 
+                                                                                     containerName:node.pithosContainer.name 
+                                                                                        objectName:node.pithosObject.name];
+                if (!operation.isCancelled && objectRequests) {
+                    ASIPithosObjectRequest *previousObjectRequest = nil;
                     for (ASIPithosObjectRequest *objectRequest in objectRequests) {
+                        if (operation.isCancelled) {
+                            [pool drain];
+                            return;
+                        }
                         objectRequest.delegate = self;
                         objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                         objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -2055,13 +2574,17 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                           NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", 
                           NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                           deleteNetworkQueue, @"networkQueue", 
-                          @"deleteQueue", @"queueType", 
+                          @"delete", @"operationType", 
                           nil]];
+                        if (previousObjectRequest)
+                            [objectRequest addDependency:previousObjectRequest];
+                        previousObjectRequest = objectRequest;
                         [deleteNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                     }
                 }
                 [pool drain];
-            });
+            }];
+            [deleteQueue addOperation:operation];
         }
     }
 }
@@ -2072,17 +2595,24 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
             (([node class] == [PithosSubdirNode class]) && 
              !node.pithosObject.subdir &&
              [node.pithosObject.name hasSuffix:@"/"])) {
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            // Operation: Move to trash an object or subdir/ node
+            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-                NSString *safeObjectName = [PithosUtilities safeObjectNameForContainerName:@"trash" 
-                                                                                    objectName:node.pithosObject.name];
-                if (safeObjectName) {
-                    ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithContainerName:node.pithosContainer.name 
-                                                                                                         objectName:node.pithosObject.name 
-                                                                                           destinationContainerName:@"trash" 
-                                                                                              destinationObjectName:safeObjectName 
-                                                                                                      checkIfExists:NO];
-                    if (objectRequest) {
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
+                NSString *safeObjectName = [PithosUtilities safeObjectNameForPithos:pithos 
+                                                                      containerName:@"trash" 
+                                                                         objectName:node.pithosObject.name];
+                if (!operation.isCancelled && safeObjectName) {
+                    ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos 
+                                                                                           containerName:node.pithosContainer.name 
+                                                                                              objectName:node.pithosObject.name 
+                                                                                destinationContainerName:@"trash" 
+                                                                                   destinationObjectName:safeObjectName 
+                                                                                           checkIfExists:NO];
+                    if (!operation.isCancelled && objectRequest) {
                         objectRequest.delegate = self;
                         objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                         objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -2105,26 +2635,40 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                           NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
                           NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                           moveNetworkQueue, @"networkQueue", 
-                          @"moveQueue", @"queueType", 
+                          @"move", @"operationType", 
                           nil]];
                         [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                     }
                 }
                 [pool drain];
-            });
+            }];
+            [moveQueue addOperation:operation];
         } else if ([node class] == [PithosSubdirNode class]) {
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            // Operation: Move to trash a subdir node and its descendants
+            // The resulting ASIPithosObjectRequests are chained through dependencies
+            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-                NSString *safeObjectName = [PithosUtilities safeSubdirNameForContainerName:@"trash" 
-                                                                                    subdirName:node.pithosObject.name];
-                if (safeObjectName) {
-                    NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithContainerName:node.pithosContainer.name 
-                                                                                                     objectName:node.pithosObject.name 
-                                                                                       destinationContainerName:@"trash" 
-                                                                                          destinationObjectName:safeObjectName 
-                                                                                                  checkIfExists:NO];
-                    if (objectRequests) {
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
+                NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos 
+                                                                      containerName:@"trash" 
+                                                                         subdirName:node.pithosObject.name];
+                if (!operation.isCancelled && safeObjectName) {
+                    NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithPithos:pithos 
+                                                                                       containerName:node.pithosContainer.name 
+                                                                                          objectName:node.pithosObject.name 
+                                                                            destinationContainerName:@"trash" 
+                                                                               destinationObjectName:safeObjectName 
+                                                                                       checkIfExists:NO];
+                    if (!operation.isCancelled && objectRequests) {
+                        ASIPithosObjectRequest *previousObjectRequest = nil;
                         for (ASIPithosObjectRequest *objectRequest in objectRequests) {
+                            if (operation.isCancelled) {
+                                [pool drain];
+                                return;
+                            }
                             objectRequest.delegate = self;
                             objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                             objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -2147,14 +2691,18 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                               NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
                               NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                               moveNetworkQueue, @"networkQueue", 
-                              @"moveQueue", @"queueType", 
+                              @"move", @"operationType", 
                               nil]];
+                            if (previousObjectRequest)
+                                [objectRequest addDependency:previousObjectRequest];
+                            previousObjectRequest = objectRequest;
                             [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                         }
                     }
                 }
                 [pool drain];
-            });
+            }];
+            [moveQueue addOperation:operation];
         }
     }
 }
@@ -2176,12 +2724,12 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
         return;
     PithosNode *dropNode = (PithosNode *)[sender representedObject];
     NSArray *localClipboardNodes = [NSArray arrayWithArray:clipboardNodes];
-    if (!clipboardCopy && ![dropNode isEqualTo:clipboardParentNode]) {
+    if (clipboardCopy) {
+        [self copyNodes:localClipboardNodes toNode:dropNode];
+    } else if (![dropNode isEqualTo:clipboardParentNode]) {
         self.clipboardNodes = nil;
         self.clipboardParentNode = nil;
         [self moveNodes:localClipboardNodes toNode:dropNode];
-    } else {
-        [self copyNodes:localClipboardNodes toNode:dropNode];
     }
 }