X-Git-Url: https://code.grnet.gr/git/pithos-macos/blobdiff_plain/4e6acad7231735bba61328243aaa2ca1995f82c9..e8e3d71e3b001f4783889dcab46bf356f488b279:/pithos-macos/PithosBrowserController.m diff --git a/pithos-macos/PithosBrowserController.m b/pithos-macos/PithosBrowserController.m index d8ef69d..e8f0743 100644 --- a/pithos-macos/PithosBrowserController.m +++ b/pithos-macos/PithosBrowserController.m @@ -104,16 +104,14 @@ @interface PithosBrowserController (Private) - (BOOL)uploadFiles:(NSArray *)filenames toNode:(PithosNode *)destinationNode; - (BOOL)moveNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode; -- (BOOL)copyNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode; +- (BOOL)cpyNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode; @end @implementation PithosBrowserController @synthesize pithos; -@synthesize accountNode; -@synthesize verticalSplitView, horizontalSplitView, leftTopView, leftBottomView, outlineView, browser, outlineViewMenu, browserMenu; +@synthesize pithosAccountManager, accountNode; @synthesize draggedNodes, draggedParentNode; @synthesize clipboardNodes, clipboardParentNode, clipboardCopy; -@synthesize activityTextField, activityProgressIndicator; #pragma mark - #pragma Object Lifecycle @@ -144,6 +142,7 @@ [browser setAllowsMultipleSelection:YES]; [browser setAllowsEmptySelection:YES]; [browser setAllowsTypeSelect:YES]; + [browser setDoubleAction:@selector(browserDoubleAction:)]; moveNetworkQueue = [[ASINetworkQueue alloc] init]; moveNetworkQueue.shouldCancelAllRequestsOnFailure = NO; @@ -210,19 +209,19 @@ [activityProgressIndicator setMaxValue:1.0]; activityFacility = [PithosActivityFacility defaultPithosActivityFacility]; - self.accountNode = [[[PithosAccountNode alloc] initWithPithos:pithos] autorelease]; + self.accountNode = [[PithosAccountNode alloc] initWithPithosAccountManager:pithosAccountManager andPithos:pithos]; containersNode = [[PithosEmptyNode alloc] initWithDisplayName:@"CONTAINERS" icon:nil]; containersNodeChildren = [[NSMutableArray alloc] init]; sharedNode = [[PithosEmptyNode alloc] initWithDisplayName:@"SHARED" icon:nil]; - mySharedNode = [[PithosAccountNode alloc] initWithPithos:pithos]; + mySharedNode = [[PithosAccountNode alloc] initWithPithosAccountManager:pithosAccountManager andPithos:pithos]; mySharedNode.displayName = @"shared by me"; mySharedNode.shared = YES; mySharedNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kUserIcon)]; - othersSharedNode = [[PithosSharingAccountsNode alloc] initWithPithos:pithos]; - othersSharedNode.displayName = @"shared to me"; + othersSharedNode = [[PithosSharingAccountsNode alloc] initWithPithosAccountManager:pithosAccountManager andPithos:pithos]; + othersSharedNode.displayName = @"shared with me"; othersSharedNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGroupIcon)]; - [[[outlineView tableColumns] objectAtIndex:0] setDataCell:[[[PithosOutlineViewCell alloc] init] autorelease]]; + [[[outlineView tableColumns] objectAtIndex:0] setDataCell:[[PithosOutlineViewCell alloc] init]]; // Register for updates // PithosAccountNode accountNode updates outlineView container nodes @@ -238,7 +237,7 @@ // Request for browser refresh [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pithosBrowserRefreshNeeded:) - name:@"PithosBrowserRefreshNeeeded" + name:@"PithosBrowserRefreshNeeded" object:nil]; } @@ -249,7 +248,6 @@ } [refreshTimer invalidate]; - [refreshTimer release]; [moveNetworkQueue reset]; [copyNetworkQueue reset]; @@ -279,6 +277,10 @@ [downloadCallbackQueue cancelAllOperations]; [downloadCallbackQueue setSuspended:YES]; + [accountNode pithosNodeWillBeRemoved]; + [mySharedNode pithosNodeWillBeRemoved]; + [othersSharedNode pithosNodeWillBeRemoved]; + rootNode = nil; [browser loadColumnZero]; [containersNodeChildren removeAllObjects]; @@ -336,20 +338,23 @@ [downloadCallbackQueue setSuspended:NO]; accountNode.pithos = pithos; + accountNode.pithosAccountManager = pithosAccountManager; [accountNode forceRefresh]; mySharedNode.pithos = pithos; + mySharedNode.pithosAccountManager = pithosAccountManager; [mySharedNode forceRefresh]; othersSharedNode.pithos = pithos; + othersSharedNode.pithosAccountManager = pithosAccountManager; [othersSharedNode forceRefresh]; // [activityFacility reset]; activityFacility.delegate = self; - refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:REFRESH_TIMER_INTERVAL + refreshTimer = [NSTimer scheduledTimerWithTimeInterval:REFRESH_TIMER_INTERVAL target:self selector:@selector(forceRefresh:) userInfo:self - repeats:YES] retain]; + repeats:YES]; @synchronized(self) { browserActive = YES; } @@ -376,30 +381,6 @@ - (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 { @@ -409,8 +390,7 @@ ![aPithos.storageURLPrefix isEqualToString:pithos.storageURLPrefix] || ![aPithos.publicURLPrefix isEqualToString:pithos.publicURLPrefix]) { [self resetBrowser]; - [pithos release]; - pithos = [aPithos retain]; + pithos = aPithos; [self startBrowser]; } else { [self startBrowser]; @@ -423,10 +403,14 @@ #pragma mark Observers - (void)pithosNodeChildrenUpdated:(NSNotification *)notification { + if (![NSThread isMainThread]) { + [self performSelectorOnMainThread:@selector(pithosNodeChildrenUpdated:) withObject:notification waitUntilDone:NO]; + return; + } PithosNode *node = (PithosNode *)[notification object]; if ((node == accountNode) || ![node.pithos isEqualTo:pithos]) return; - NSLog(@"pithosNodeChildrenUpdated:%@", node.url); + DLog(@"pithosNodeChildrenUpdated:%@", node.url); NSInteger lastColumn = [browser lastColumn]; for (NSInteger column = lastColumn; column >= 0; column--) { if ([[browser parentForItemsInColumn:column] isEqualTo:node]) { @@ -437,6 +421,10 @@ } - (void)pithosAccountNodeChildrenUpdated:(NSNotification *)notification { + if (![NSThread isMainThread]) { + [self performSelectorOnMainThread:@selector(pithosAccountNodeChildrenUpdated:) withObject:notification waitUntilDone:NO]; + return; + } BOOL containerPithosFound = NO; BOOL containerTrashFound = NO; NSMutableIndexSet *removedContainersNodeChildren = [NSMutableIndexSet indexSet]; @@ -466,13 +454,9 @@ // Create pithos node ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithPithos:pithos containerName:@"pithos"]; - ASINetworkQueue *networkQueue = [ASINetworkQueue queue]; - [networkQueue go]; - [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:containerRequest]] waitUntilFinished:YES]; + [PithosUtilities startAndWaitForRequest:containerRequest]; if ([containerRequest error]) { - dispatch_async(dispatch_get_main_queue(), ^{ - [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest]; - }); + [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest]; } else { refreshAccountNode = YES; } @@ -481,13 +465,9 @@ // Create trash node ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithPithos:pithos containerName:@"trash"]; - ASINetworkQueue *networkQueue = [ASINetworkQueue queue]; - [networkQueue go]; - [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:containerRequest]] waitUntilFinished:YES]; + [PithosUtilities startAndWaitForRequest:containerRequest]; if ([containerRequest error]) { - dispatch_async(dispatch_get_main_queue(), ^{ - [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest]; - }); + [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest]; } else { refreshAccountNode = YES; } @@ -518,6 +498,10 @@ #pragma mark Actions - (IBAction)forceRefresh:(id)sender { + if (![NSThread isMainThread]) { + [self performSelectorOnMainThread:@selector(forceRefresh:) withObject:sender waitUntilDone:NO]; + return; + } if (editingItem) return; if (sender) @@ -531,6 +515,10 @@ } - (IBAction)refresh:(id)sender { + if (![NSThread isMainThread]) { + [self performSelectorOnMainThread:@selector(refresh:) withObject:sender waitUntilDone:NO]; + return; + } if (editingItem) return; if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) { @@ -618,128 +606,40 @@ (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) { // 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 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; - } - 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:); - NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", - [objectRequest.userInfo objectForKey:@"sourceContainerName"], - [objectRequest.userInfo objectForKey:@"sourceObjectName"], - [objectRequest.userInfo objectForKey:@"destinationContainerName"], - [objectRequest.userInfo objectForKey:@"destinationObjectName"]]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove - message:messagePrefix]; - [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: - [NSDictionary dictionaryWithObjectsAndKeys: - [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", - [NSNumber numberWithBool:YES], @"refresh", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - moveNetworkQueue, @"networkQueue", - @"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; - // 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 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 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; - } + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName]; + if ([newName hasSuffix:@"/"]) + destinationObjectName = [destinationObjectName stringByAppendingString:@"/"]; + NSError *error = nil; + BOOL isDirectory; + 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]; + [alert setMessageText:@"Name Taken"]; + [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + }); + return; + } else if (error) { + return; + } + if (operation.isCancelled) + 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:); @@ -765,13 +665,87 @@ moveNetworkQueue, @"networkQueue", @"move", @"operationType", nil]]; - if (previousObjectRequest) - [objectRequest addDependency:previousObjectRequest]; - previousObjectRequest = objectRequest; [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; } } - [pool drain]; + }]; + [moveQueue addOperation:operation]; + } else if ([node class] == [PithosSubdirNode class]) { + if (firstSlashRange.length == 1) + return; + // Operation: Rename (move) a subdir node and its descendants + // The resulting ASIPithosObjectRequests are chained through dependencies + __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName]; + NSError *error = nil; + BOOL isDirectory; + 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]; + [alert setMessageText:@"Name Taken"]; + [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + }); + return; + } else if (error) { + return; + } + if (operation.isCancelled) + return; + if (node.pithosObject.subdir) + destinationObjectName = [destinationObjectName stringByAppendingString:@"/"]; + 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) + return; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", + [objectRequest.userInfo objectForKey:@"sourceContainerName"], + [objectRequest.userInfo objectForKey:@"sourceObjectName"], + [objectRequest.userInfo objectForKey:@"destinationContainerName"], + [objectRequest.userInfo objectForKey:@"destinationObjectName"]]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove + message:messagePrefix]; + [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: + [NSDictionary dictionaryWithObjectsAndKeys: + [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", + [NSNumber numberWithBool:YES], @"refresh", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + moveNetworkQueue, @"networkQueue", + @"move", @"operationType", + nil]]; + if (previousObjectRequest) + [objectRequest addDependency:previousObjectRequest]; + previousObjectRequest = objectRequest; + [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; + } + } + } }]; [moveQueue addOperation:operation]; } @@ -818,16 +792,16 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { [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]; + NSAlert *alert = [[NSAlert alloc] init]; [alert setMessageText:@"Download directory"]; [alert setInformativeText:[NSString stringWithFormat:@"'%@' is a directory, do you want to download its contents?", node.displayName]]; [alert addButtonWithTitle:@"OK"]; [alert addButtonWithTitle:@"Cancel"]; NSInteger choice = [alert runModal]; if (choice == NSAlertFirstButtonReturn) - [self downloadNode:node toDirectory:[dropDestination path] withNewFileName:nil version:nil]; + [self downloadNode:node toDirectory:[dropDestination path] withNewFileName:nil version:nil checkIfExists:YES]; } else { - [self downloadNode:node toDirectory:[dropDestination path] withNewFileName:nil version:nil]; + [self downloadNode:node toDirectory:[dropDestination path] withNewFileName:nil version:nil checkIfExists:YES]; } } return names; @@ -913,71 +887,142 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { dropOperation:(NSBrowserDropOperation)dropOperation { if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) { NSArray *filenames = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType]; - NSLog(@"drag in operation: %lu filenames: %@", [info draggingSourceOperationMask], filenames); + DLog(@"drag in operation: %lu filenames: %@", [info draggingSourceOperationMask], filenames); if ((column != -1) && (filenames != nil)) { PithosNode *node; if (row != -1) node = [browser itemAtRow:row inColumn:column]; else node = [browser parentForItemsInColumn:column]; - NSLog(@"drag in node: %@", node.url); + DLog(@"drag in node: %@", node.url); return [self uploadFiles:filenames toNode:node]; } } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) { - NSLog(@"drag local operation: %lu nodes: %@", [info draggingSourceOperationMask], draggedNodes); + DLog(@"drag local operation: %lu nodes: %@", [info draggingSourceOperationMask], draggedNodes); if ((column != -1) && (draggedNodes != nil)) { PithosNode *node; if (row != -1) node = [browser itemAtRow:row inColumn:column]; else node = [browser parentForItemsInColumn:column]; - NSLog(@"drag local node: %@", node.url); + DLog(@"drag local node: %@", node.url); if ([info draggingSourceOperationMask] & NSDragOperationMove) return [self moveNodes:draggedNodes toNode:node]; else if ([info draggingSourceOperationMask] & NSDragOperationCopy) - return [self copyNodes:draggedNodes toNode:node]; + return [self cpyNodes:draggedNodes toNode:node]; } } return NO; } #pragma mark - +#pragma mark NSBrowser Actions + +- (void)browserDoubleAction:(id)sender { + NSInteger column = [browser clickedColumn]; + NSInteger row = [browser clickedRow]; + if ((column == -1) || (row == -1)) + return; + NSIndexPath *clickedNodeIndexPath = [[browser indexPathForColumn:column] indexPathByAddingIndex:row]; + NSArray *menuNodesIndexPaths = [browser selectionIndexPaths]; + NSMutableArray *menuNodes = [NSMutableArray arrayWithCapacity:[menuNodesIndexPaths count]]; + if ([menuNodesIndexPaths containsObject:clickedNodeIndexPath]) { + for (NSIndexPath *nodeIndexPath in menuNodesIndexPaths) { + [menuNodes addObject:[browser itemAtIndexPath:nodeIndexPath]]; + } + } else { + [menuNodes addObject:[browser itemAtIndexPath:clickedNodeIndexPath]]; + } + NSMenuItem *menuItem = [[NSMenuItem alloc] init]; + menuItem.representedObject = menuNodes; + [self menuDownload:menuItem]; +} + +#pragma mark - #pragma mark Drag and Drop methods -- (void)downloadNode:(PithosNode *)node - toDirectory:(NSString *)dirPath - withNewFileName:(NSString *)newFileName - version:(NSString *)version { +- (void)downloadNode:(PithosNode *)node toDirectory:(NSString *)dirPath withNewFileName:(NSString *)newFileName + version:(NSString *)version checkIfExists:(BOOL)checkIfExists { 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; + @autoreleasepool { + if (operation.isCancelled) + return; + NSArray *objectRequests = [PithosUtilities objectDataRequestsForSubdirWithPithos:pithos + containerName:node.pithosContainer.name + objectName:node.pithosObject.name + toDirectory:dirPath + checkIfExists:checkIfExists + sharingAccount:node.sharingAccount]; + if (!operation.isCancelled && objectRequests) { + ASIPithosObjectRequest *previousObjectRequest = nil; + for (__block ASIPithosObjectRequest *objectRequest in objectRequests) { + if (operation.isCancelled) + 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]]; } + } + } + }]; + [downloadQueue addOperation:operation]; + } else if ([node class] == [PithosObjectNode class]) { + // Operation: Download an object node + __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + @autoreleasepool { + if (operation.isCancelled) + return; + __block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectDataRequestWithPithos:pithos + containerName:node.pithosContainer.name + objectName:node.pithosObject.name + version:version + toDirectory:dirPath + withNewFileName:newFileName + checkIfExists:checkIfExists + 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:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue] + totalBytes:node.pithosObject.bytes currentBytes:0]; dispatch_async(dispatch_get_main_queue(), ^{ [activityFacility updateActivity:activity withMessage:activity.message]; @@ -1001,65 +1046,9 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { 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]; } @@ -1078,9 +1067,7 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { if ((destinationNode.pithosContainer.blockHash == nil) || (destinationNode.pithosContainer.blockSize == 0)) { ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest containerMetadataRequestWithPithos:pithos containerName:containerName]; - ASINetworkQueue *networkQueue = [ASINetworkQueue queue]; - [networkQueue go]; - [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:containerRequest]] waitUntilFinished:YES]; + [PithosUtilities startAndWaitForRequest:containerRequest]; if ([containerRequest error]) { [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest]; return NO; @@ -1099,191 +1086,183 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { if ([fileManager fileExistsAtPath:filePath isDirectory:&isDirectory]) { if (!isDirectory) { // Upload file - NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]]; + NSString *objectName = [[objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]] + precomposedStringWithCanonicalMapping]; // 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) - contentType = @"application/octet-stream"; - if (error) - NSLog(@"contentType detection error: %@", error); - NSArray *hashes = nil; - 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:); - NSString *messagePrefix = [NSString stringWithFormat:@"Uploading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload - message:[messagePrefix stringByAppendingString:@" (0%)"] - totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue] - currentBytes:0]; - [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary: - [NSDictionary dictionaryWithObjectsAndKeys: - containerName, @"containerName", - objectName, @"objectName", - contentType, @"contentType", - [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize", - blockHash, @"blockHash", - filePath, @"filePath", - hashes, @"hashes", - [NSArray arrayWithObject:destinationNode], @"refreshNodes", - [NSNumber numberWithBool:YES], @"refresh", - [NSNumber numberWithUnsignedInteger:10], @"iteration", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - uploadNetworkQueue, @"networkQueue", - @"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]; - [alert setMessageText:@"Upload directory"]; - [alert setInformativeText:[NSString stringWithFormat:@"'%@' is a directory, do you want to upload it and its contents?", filePath]]; - [alert addButtonWithTitle:@"OK"]; - [alert addButtonWithTitle:@"Cancel"]; - NSInteger choice = [alert runModal]; - if (choice == NSAlertFirstButtonReturn) { - NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]]; - // 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]; + @autoreleasepool { + if (operation.isCancelled) return; - } - NSMutableArray *objectNames = nil; - NSMutableArray *contentTypes = nil; - NSMutableArray *filePaths = nil; - NSMutableArray *hashesArrays = nil; - NSMutableArray *directoryObjectRequests = nil; - 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]; + NSError *error = nil; + NSString *contentType = [PithosUtilities contentTypeOfFile:filePath error:&error]; + if (contentType == nil) + contentType = @"application/octet-stream"; + #if DEBUG_PITHOS + if (error) + DLog(@"contentType detection error: %@", error); + #endif + NSArray *hashes = nil; + if (operation.isCancelled) return; - } - ASIPithosObjectRequest *previousObjectRequest = nil; - for (ASIPithosObjectRequest *objectRequest in directoryObjectRequests) { - 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:); - NSString *messagePrefix = [NSString stringWithFormat:@"Creating directory '%@'", [objectRequest.userInfo objectForKey:@"fileName"]]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory - message:messagePrefix]; + NSString *messagePrefix = [NSString stringWithFormat:@"Uploading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload + message:[messagePrefix stringByAppendingString:@" (0%)"] + totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue] + currentBytes:0]; [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary: [NSDictionary dictionaryWithObjectsAndKeys: + containerName, @"containerName", + objectName, @"objectName", + contentType, @"contentType", + [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize", + blockHash, @"blockHash", + filePath, @"filePath", + hashes, @"hashes", + [NSArray arrayWithObject:destinationNode], @"refreshNodes", [NSNumber numberWithBool:YES], @"refresh", + [NSNumber numberWithUnsignedInteger:10], @"iteration", activity, @"activity", [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage", [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", uploadNetworkQueue, @"networkQueue", @"upload", @"operationType", nil]]; - if (previousObjectRequest) - [objectRequest addDependency:previousObjectRequest]; - previousObjectRequest = objectRequest; + if (destinationNode.sharingAccount) + [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"]; [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]]; } - 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:); - objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Uploading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload - message:[messagePrefix stringByAppendingString:@" (0%)"] - totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue] - currentBytes:0]; - [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary: - [NSDictionary dictionaryWithObjectsAndKeys: - containerName, @"containerName", - [objectNames objectAtIndex:i], @"objectName", - [contentTypes objectAtIndex:i], @"contentType", - [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize", - blockHash, @"blockHash", - [filePaths objectAtIndex:i], @"filePath", - [hashesArrays objectAtIndex:i], @"hashes", - [NSNumber numberWithBool:YES], @"refresh", - [NSNumber numberWithUnsignedInteger:10], @"iteration", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + } + }]; + [uploadQueue addOperation:operation]; + } else { + // Upload directory, confirm first + NSAlert *alert = [[NSAlert alloc] init]; + [alert setMessageText:@"Upload directory"]; + [alert setInformativeText:[NSString stringWithFormat:@"'%@' is a directory, do you want to upload it and its contents?", filePath]]; + [alert addButtonWithTitle:@"OK"]; + [alert addButtonWithTitle:@"Cancel"]; + NSInteger choice = [alert runModal]; + if (choice == NSAlertFirstButtonReturn) { + NSString *objectName = [[objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]] + precomposedStringWithCanonicalMapping]; + // Operation: Upload a local directory and its descendants + // The resulting ASIPithosObjectRequests are chained through dependencies + __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + @autoreleasepool { + if (operation.isCancelled) + return; + NSMutableArray *objectNames = nil; + NSMutableArray *contentTypes = nil; + NSMutableArray *filePaths = nil; + NSMutableArray *hashesArrays = nil; + NSMutableArray *directoryObjectRequests = nil; + 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) + return; + ASIPithosObjectRequest *previousObjectRequest = nil; + for (ASIPithosObjectRequest *objectRequest in directoryObjectRequests) { + if (operation.isCancelled) + return; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Creating directory '%@'", [objectRequest.userInfo objectForKey:@"fileName"]]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory + message:messagePrefix]; + [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary: + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithBool:YES], @"refresh", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", uploadNetworkQueue, @"networkQueue", @"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]]; } + if (!operation.isCancelled && objectRequests) { + for (NSUInteger i = 0 ; i < [objectRequests count] ; i++) { + if (operation.isCancelled) + return; + ASIPithosObjectRequest *objectRequest = [objectRequests objectAtIndex:i]; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Uploading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload + message:[messagePrefix stringByAppendingString:@" (0%)"] + totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue] + currentBytes:0]; + [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary: + [NSDictionary dictionaryWithObjectsAndKeys: + containerName, @"containerName", + [objectNames objectAtIndex:i], @"objectName", + [contentTypes objectAtIndex:i], @"contentType", + [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize", + blockHash, @"blockHash", + [filePaths objectAtIndex:i], @"filePath", + [hashesArrays objectAtIndex:i], @"hashes", + [NSNumber numberWithBool:YES], @"refresh", + [NSNumber numberWithUnsignedInteger:10], @"iteration", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + uploadNetworkQueue, @"networkQueue", + @"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]; } @@ -1309,75 +1288,19 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) { // 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 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:); - NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", - [objectRequest.userInfo objectForKey:@"sourceContainerName"], - [objectRequest.userInfo objectForKey:@"sourceObjectName"], - [objectRequest.userInfo objectForKey:@"destinationContainerName"], - [objectRequest.userInfo objectForKey:@"destinationObjectName"]]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove - message:messagePrefix]; - [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: - [NSDictionary dictionaryWithObjectsAndKeys: - [NSArray arrayWithObjects:node.parent, destinationNode, nil], @"forceRefreshNodes", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - moveNetworkQueue, @"networkQueue", - @"move", @"operationType", - nil]]; - [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; - } - [pool drain]; - }]; - [moveQueue addOperation:operation]; - } else if ([node class] == [PithosSubdirNode class]) { - // 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 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; - } + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName]; + if ([node.pithosObject.name hasSuffix:@"/"]) + destinationObjectName = [destinationObjectName stringByAppendingString:@"/"]; + 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:); @@ -1391,7 +1314,6 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: [NSDictionary dictionaryWithObjectsAndKeys: [NSArray arrayWithObjects:node.parent, destinationNode, nil], @"forceRefreshNodes", - [NSNumber numberWithBool:YES], @"refresh", activity, @"activity", [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", @@ -1403,13 +1325,64 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { moveNetworkQueue, @"networkQueue", @"move", @"operationType", nil]]; - if (previousObjectRequest) - [objectRequest addDependency:previousObjectRequest]; - previousObjectRequest = objectRequest; [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; } } - [pool drain]; + }]; + [moveQueue addOperation:operation]; + } else if ([node class] == [PithosSubdirNode class]) { + // Operation: Move a subdir node and its descendants + // The resulting ASIPithosObjectRequests are chained through dependencies + __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName]; + if (node.pithosObject.subdir) + destinationObjectName = [destinationObjectName stringByAppendingString:@"/"]; + 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) + return; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", + [objectRequest.userInfo objectForKey:@"sourceContainerName"], + [objectRequest.userInfo objectForKey:@"sourceObjectName"], + [objectRequest.userInfo objectForKey:@"destinationContainerName"], + [objectRequest.userInfo objectForKey:@"destinationObjectName"]]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove + message:messagePrefix]; + [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: + [NSDictionary dictionaryWithObjectsAndKeys: + [NSArray arrayWithObjects:node.parent, destinationNode, nil], @"forceRefreshNodes", + [NSNumber numberWithBool:YES], @"refresh", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + moveNetworkQueue, @"networkQueue", + @"move", @"operationType", + nil]]; + if (previousObjectRequest) + [objectRequest addDependency:previousObjectRequest]; + previousObjectRequest = objectRequest; + [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; + } + } + } }]; [moveQueue addOperation:operation]; } @@ -1417,7 +1390,7 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { return YES; } -- (BOOL)copyNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode { +- (BOOL)cpyNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode { if ((([destinationNode class] != [PithosSubdirNode class]) && ([destinationNode class] != [PithosContainerNode class])) || (([destinationNode class] == [PithosSubdirNode class]) && !destinationNode.pithosObject.subdir && [destinationNode.pithosObject.name hasSuffix:@"/"])) return NO; @@ -1433,99 +1406,29 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) { // 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 safeObjectNameForPithos:pithos - containerName:containerName - objectName:node.pithosObject.name]; - } - 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:); - NSString *messagePrefix = [NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@'", - [objectRequest.userInfo objectForKey:@"sourceContainerName"], - [objectRequest.userInfo objectForKey:@"sourceObjectName"], - [objectRequest.userInfo objectForKey:@"destinationContainerName"], - [objectRequest.userInfo objectForKey:@"destinationObjectName"]]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCopy - message:messagePrefix]; - [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: - [NSDictionary dictionaryWithObjectsAndKeys: - [NSArray arrayWithObject:destinationNode], @"forceRefreshNodes", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(copyFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - copyNetworkQueue, @"networkQueue", - @"copy", @"operationType", - nil]]; - [copyNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; - } - [pool drain]; - }]; - [copyQueue addOperation:operation]; - } else if ([node class] == [PithosSubdirNode class]) { - // 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 safeSubdirNameForPithos:pithos - containerName:containerName - subdirName:node.pithosObject.name]; - } - if (operation.isCancelled) { - [pool drain]; - return; - } - 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; - } + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *destinationObjectName; + if (![destinationNode isEqualTo:node.parent]) { + destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName]; + if ([node.pithosObject.name hasSuffix:@"/"]) + destinationObjectName = [destinationObjectName stringByAppendingString:@"/"]; + } else { + destinationObjectName = [PithosUtilities safeObjectNameForPithos:pithos + containerName:containerName + objectName:node.pithosObject.name]; + } + if (operation.isCancelled) + return; + ASIPithosObjectRequest *objectRequest = [PithosUtilities cpyObjectRequestWithPithos: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:); @@ -1550,13 +1453,73 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { copyNetworkQueue, @"networkQueue", @"copy", @"operationType", nil]]; - if (previousObjectRequest) - [objectRequest addDependency:previousObjectRequest]; - previousObjectRequest = objectRequest; [copyNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; } } - [pool drain]; + }]; + [copyQueue addOperation:operation]; + } else if ([node class] == [PithosSubdirNode class]) { + // Operation: Copy a subdir node and its descendants + // The resulting ASIPithosObjectRequests are chained through dependencies + __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *destinationObjectName; + if (![destinationNode isEqualTo:node.parent]) { + destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName]; + if (node.pithosObject.subdir) + destinationObjectName = [destinationObjectName stringByAppendingString:@"/"]; + } else { + destinationObjectName = [PithosUtilities safeSubdirNameForPithos:pithos + containerName:containerName + subdirName:node.pithosObject.name]; + } + if (operation.isCancelled) + return; + NSArray *objectRequests = [PithosUtilities cpyObjectRequestsForSubdirWithPithos: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) + return; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@'", + [objectRequest.userInfo objectForKey:@"sourceContainerName"], + [objectRequest.userInfo objectForKey:@"sourceObjectName"], + [objectRequest.userInfo objectForKey:@"destinationContainerName"], + [objectRequest.userInfo objectForKey:@"destinationObjectName"]]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCopy + message:messagePrefix]; + [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: + [NSDictionary dictionaryWithObjectsAndKeys: + [NSArray arrayWithObject:destinationNode], @"forceRefreshNodes", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(copyFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + copyNetworkQueue, @"networkQueue", + @"copy", @"operationType", + nil]]; + if (previousObjectRequest) + [objectRequest addDependency:previousObjectRequest]; + previousObjectRequest = objectRequest; + [copyNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; + } + } + } }]; [copyQueue addOperation:operation]; } @@ -1588,18 +1551,18 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { return; } // Add an operation to the callbackQueue with a completionBlock for the case of cancellation - NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self + NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:NSSelectorFromString([request.userInfo objectForKey:@"didFinishSelector"]) - object:request] autorelease]; + object:request]; 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"]]; - }); + @autoreleasepool { + 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]; @@ -1632,18 +1595,18 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { return; } // Add an operation to the callbackQueue with a completionBlock for the case of cancellation - NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self + NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:NSSelectorFromString([request.userInfo objectForKey:@"didFailSelector"]) - object:request] autorelease]; + object:request]; 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"]]; - }); + @autoreleasepool { + 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]; @@ -1651,253 +1614,188 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { } - (void)requestFailed:(ASIPithosRequest *)request { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [request.userInfo objectForKey:@"operation"]; - NSLog(@"Request failed: %@", request.url); - 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"]]; - }); - [pool drain]; - return; - } - NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue]; - if (retries > 0) { - ASIPithosRequest *newRequest = (ASIPithosRequest *)[[PithosUtilities copyRequest:request] autorelease]; - [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; - [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithBool:NO] forKey:@"unexpectedResponseStatus"]; - [[newRequest.userInfo objectForKey:@"networkQueue"] addOperation:[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]]]; - } else { - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] - withMessage:[request.userInfo objectForKey:@"failedActivityMessage"]]; + @autoreleasepool { + NSOperation *operation = [request.userInfo objectForKey:@"operation"]; + DLog(@"Request failed: %@", request.url); + if (operation.isCancelled) + return; + if (request.isCancelled) { + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] + withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]]; + }); + return; + } + NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue]; + if (retries > 0) { + ASIPithosRequest *newRequest = (ASIPithosRequest *)[PithosUtilities copyRequest:request]; + [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; + [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithBool:NO] forKey:@"unexpectedResponseStatus"]; + [[newRequest.userInfo objectForKey:@"networkQueue"] addOperation:[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]]]; + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] + withMessage:[request.userInfo objectForKey:@"failedActivityMessage"]]; + }); if ([[request.userInfo objectForKey:@"unexpectedResponseStatus"] boolValue]) [PithosUtilities unexpectedResponseStatusAlertWithRequest:request]; else [PithosUtilities httpRequestErrorAlertWithRequest:request]; - }); + } } - [pool drain]; } - (void)downloadObjectFinished:(ASIPithosObjectRequest *)objectRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; - NSLog(@"Download finished: %@", objectRequest.url); - 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) { - // 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]) { - if (![fileManager createFileAtPath:filePath contents:nil attributes:nil]) { - dispatch_async(dispatch_get_main_queue(), ^{ - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - [alert setMessageText:@"Create File Error"]; - [alert setInformativeText:[NSString stringWithFormat:@"Cannot create zero length file at %@", filePath]]; - [alert addButtonWithTitle:@"OK"]; - [alert runModal]; - }); + @autoreleasepool { + NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; + DLog(@"Download finished: %@", objectRequest.url); + 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) { + // 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. + DLog(@"Downloaded 0 bytes"); + NSFileManager *fileManager = [NSFileManager defaultManager]; + if (![fileManager fileExistsAtPath:filePath]) { + if (![fileManager createFileAtPath:filePath contents:nil attributes:nil]) { + dispatch_async(dispatch_get_main_queue(), ^{ + NSAlert *alert = [[NSAlert alloc] init]; + [alert setMessageText:@"Create File Error"]; + [alert setInformativeText:[NSString stringWithFormat:@"Cannot create zero length file at %@", filePath]]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + }); + } } } + + NSUInteger currentBytes = [objectRequest objectContentLength]; + if (currentBytes == 0) + currentBytes = totalBytes; + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:activity + withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] + totalBytes:totalBytes + currentBytes:currentBytes]; + }); + } else { + [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; + [self requestFailed:objectRequest]; } - - NSUInteger currentBytes = [objectRequest objectContentLength]; - if (currentBytes == 0) - currentBytes = totalBytes; - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:activity - withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] - totalBytes:totalBytes - currentBytes:currentBytes]; - }); - } else { - [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; - [self requestFailed:objectRequest]; } - [pool drain]; } - (void)uploadDirectoryObjectFinished:(ASIPithosObjectRequest *)objectRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; - NSLog(@"Upload directory object finished: %@", objectRequest.url); - 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]; - }); - } else { - [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; - [self requestFailed:objectRequest]; + @autoreleasepool { + NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; + DLog(@"Upload directory object finished: %@", objectRequest.url); + if (operation.isCancelled) { + [self requestFailed:objectRequest]; + } else if (objectRequest.responseStatusCode == 201) { + DLog(@"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]; + }); + } else { + [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; + [self requestFailed:objectRequest]; + } } - [pool drain]; } - (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 (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]; - }); - } else if (objectRequest.responseStatusCode == 409) { - NSUInteger iteration = [[objectRequest.userInfo objectForKey:@"iteration"] unsignedIntegerValue]; - if (iteration == 0) { - NSLog(@"Upload iteration limit reached: %@", objectRequest.url); + @autoreleasepool { + NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; + DLog(@"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 (operation.isCancelled) { + [self requestFailed:objectRequest]; + } else if (objectRequest.responseStatusCode == 201) { + DLog(@"Object created: %@", objectRequest.url); dispatch_async(dispatch_get_main_queue(), ^{ [activityFacility endActivity:activity - withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]]; - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - [alert setMessageText:@"Upload Timeout"]; - [alert setInformativeText:[NSString stringWithFormat:@"Upload iteration limit reached for object with path '%@'", - [objectRequest.userInfo objectForKey:@"objectName"]]]; - [alert addButtonWithTitle:@"OK"]; - [alert runModal]; + 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]; }); - [pool drain]; - return; - } - NSLog(@"object is missing hashes: %@", objectRequest.url); - NSIndexSet *missingBlocks = [PithosUtilities missingBlocksForHashes:[objectRequest.userInfo objectForKey:@"hashes"] - withMissingHashes:[objectRequest hashes]]; - NSUInteger blockSize = [[objectRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue]; - if (totalBytes >= [missingBlocks count]*blockSize) - currentBytes = totalBytes - [missingBlocks count]*blockSize; - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility updateActivity:activity - withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (totalBytes ? (100*(currentBytes + 0.0)/(totalBytes + 0.0)) : 100)] - totalBytes:totalBytes - currentBytes:currentBytes]; - }); - NSUInteger missingBlockIndex = [missingBlocks firstIndex]; - __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:); - newContainerRequest.userInfo = objectRequest.userInfo; - [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:(--iteration)] forKey:@"iteration"]; - [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; - [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:missingBlocks forKey:@"missingBlocks"]; - [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"]; - [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:NSStringFromSelector(@selector(uploadMissingBlockFinished:)) forKey:@"didFinishSelector"]; - [newContainerRequest setBytesSentBlock:^(unsigned long long size, unsigned long long total){ - [activityFacility updateActivity:activity - withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)] - totalBytes:activity.totalBytes - currentBytes:(activity.currentBytes + size)]; - }]; - [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]]; - } else { - [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; - [self requestFailed:objectRequest]; - } - [pool drain]; -} - -- (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 (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 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:); - newObjectRequest.userInfo = containerRequest.userInfo; - [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; - [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlocks"]; - [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlockIndex"]; - [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)) forKey:@"didFinishSelector"]; - [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]]; - } else { + } else if (objectRequest.responseStatusCode == 409) { + NSUInteger iteration = [[objectRequest.userInfo objectForKey:@"iteration"] unsignedIntegerValue]; + if (iteration == 0) { + DLog(@"Upload iteration limit reached: %@", objectRequest.url); + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:activity + withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]]; + NSAlert *alert = [[NSAlert alloc] init]; + [alert setMessageText:@"Upload Timeout"]; + [alert setInformativeText:[NSString stringWithFormat:@"Upload iteration limit reached for object with path '%@'", + [objectRequest.userInfo objectForKey:@"objectName"]]]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + }); + return; + } + DLog(@"object is missing hashes: %@", objectRequest.url); + NSIndexSet *missingBlocks = [PithosUtilities missingBlocksForHashes:[objectRequest.userInfo objectForKey:@"hashes"] + withMissingHashes:[objectRequest hashes]]; + NSUInteger blockSize = [[objectRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue]; + if (totalBytes >= [missingBlocks count]*blockSize) + currentBytes = totalBytes - [missingBlocks count]*blockSize; + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility updateActivity:activity + withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (totalBytes ? (100*(currentBytes + 0.0)/(totalBytes + 0.0)) : 100)] + totalBytes:totalBytes + currentBytes:currentBytes]; + }); + NSUInteger missingBlockIndex = [missingBlocks firstIndex]; __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithPithos:pithos - containerName:[containerRequest.userInfo objectForKey:@"containerName"] - blockSize:[[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue] - forFile:[containerRequest.userInfo objectForKey:@"filePath"] + containerName:[objectRequest.userInfo objectForKey:@"containerName"] + blockSize:blockSize + forFile:[objectRequest.userInfo objectForKey:@"filePath"] missingBlockIndex:missingBlockIndex - sharingAccount:[containerRequest.userInfo objectForKey:@"sharingAccount"]]; + sharingAccount:[objectRequest.userInfo objectForKey:@"sharingAccount"]]; newContainerRequest.delegate = self; newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - newContainerRequest.userInfo = containerRequest.userInfo; + newContainerRequest.userInfo = objectRequest.userInfo; + [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:(--iteration)] forKey:@"iteration"]; [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; + [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:missingBlocks forKey:@"missingBlocks"]; [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"]; + [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:NSStringFromSelector(@selector(uploadMissingBlockFinished:)) forKey:@"didFinishSelector"]; [newContainerRequest setBytesSentBlock:^(unsigned long long size, unsigned long long total){ [activityFacility updateActivity:activity withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)] @@ -1905,96 +1803,157 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { currentBytes:(activity.currentBytes + size)]; }]; [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]]; + } else { + [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; + [self requestFailed:objectRequest]; } - } else { - [(NSMutableDictionary *)(containerRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; - [self requestFailed:containerRequest]; } - [pool drain]; } -- (void)moveFinished:(ASIPithosObjectRequest *)objectRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; - NSLog(@"Move object finished: %@", objectRequest.url); - 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]; +- (void)uploadMissingBlockFinished:(ASIPithosContainerRequest *)containerRequest { + @autoreleasepool { + NSOperation *operation = [containerRequest.userInfo objectForKey:@"operation"]; + DLog(@"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 (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 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:); + newObjectRequest.userInfo = containerRequest.userInfo; + [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; + [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlocks"]; + [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlockIndex"]; + [(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 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:); + newContainerRequest.userInfo = containerRequest.userInfo; + [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; + [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"]; + [newContainerRequest setBytesSentBlock:^(unsigned long long size, unsigned long long total){ + [activityFacility updateActivity:activity + withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)] + totalBytes:activity.totalBytes + currentBytes:(activity.currentBytes + size)]; + }]; + [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]]; } - 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]; + } else { + [(NSMutableDictionary *)(containerRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; + [self requestFailed:containerRequest]; + } + } +} + +- (void)moveFinished:(ASIPithosObjectRequest *)objectRequest { + @autoreleasepool { + NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; + DLog(@"Move object finished: %@", objectRequest.url); + 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]; + }); + } else { + [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; + [self requestFailed:objectRequest]; + } } - [pool drain]; } - (void)copyFinished:(ASIPithosObjectRequest *)objectRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; - NSLog(@"Copy object finished: %@", objectRequest.url); - 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]; - }); - } else { - [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; - [self requestFailed:objectRequest]; + @autoreleasepool { + NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; + DLog(@"Copy object finished: %@", objectRequest.url); + 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]; + }); + } else { + [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; + [self requestFailed:objectRequest]; + } } - [pool drain]; } - (void)deleteObjectFinished:(ASIPithosObjectRequest *)objectRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; - NSLog(@"Delete object finished: %@", objectRequest.url); - 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]; - }); - } else { - [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; - [self requestFailed:objectRequest]; + @autoreleasepool { + NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; + DLog(@"Delete object finished: %@", objectRequest.url); + 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]; + }); + } else { + [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; + [self requestFailed:objectRequest]; + } } - [pool drain]; } #pragma mark - @@ -2002,9 +1961,9 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex { if (splitView == verticalSplitView) - return 120; + return 140; else - return ([horizontalSplitView bounds].size.height - 108); + return ([horizontalSplitView bounds].size.height - 142); } - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex { @@ -2014,17 +1973,12 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { return ([horizontalSplitView bounds].size.height - 108); } -- (CGFloat)splitView:(NSSplitView *)splitView constrainSplitPosition:(CGFloat)proposedPosition ofSubviewAt:(NSInteger)dividerIndex { - if (splitView == verticalSplitView) { - if (proposedPosition < 120) - return 120; - else if (proposedPosition > 220) - return 220; - else - return proposedPosition; - } else { - return ([horizontalSplitView bounds].size.height - 108); +- (BOOL)splitView:(NSSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)view { + if (((splitView == verticalSplitView) && (view == leftView)) || + ((splitView == horizontalSplitView) && (view == leftBottomView))) { + return NO; } + return YES; } #pragma mark - @@ -2094,22 +2048,22 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id)info item:(id)item childIndex:(NSInteger)index { if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) { NSArray *filenames = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType]; - NSLog(@"drag in operation: %lu filenames: %@", [info draggingSourceOperationMask], filenames); + DLog(@"drag in operation: %lu filenames: %@", [info draggingSourceOperationMask], filenames); if (item && (index == NSOutlineViewDropOnItemIndex) && (filenames != nil)) { PithosNode *node = (PithosNode *)item; - NSLog(@"drag in node: %@", node.url); + DLog(@"drag in node: %@", node.url); return [self uploadFiles:filenames toNode:node]; } } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) { - NSLog(@"drag local operation: %lu nodes: %@", [info draggingSourceOperationMask], draggedNodes); + DLog(@"drag local operation: %lu nodes: %@", [info draggingSourceOperationMask], draggedNodes); if (item && (index == NSOutlineViewDropOnItemIndex) && (draggedNodes != nil)) { PithosNode *node = (PithosNode *)item; - NSLog(@"drag local node: %@", node.url); + DLog(@"drag local node: %@", node.url); 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]; + return [self cpyNodes:draggedNodes toNode:node]; } } return NO; @@ -2153,15 +2107,26 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { NSInteger column = [browser clickedColumn]; NSInteger row = [browser clickedRow]; if ((column == -1) || (row == -1)) { - // General context menu - NSArray *menuNodesIndexPaths = [browser selectionIndexPaths]; - 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)]; + if (column == -1) { + // General context menu + NSArray *menuNodesIndexPaths = [browser selectionIndexPaths]; + 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]]; + } } else { - menuNode = [browser itemAtIndexPath:[menuNodesIndexPaths objectAtIndex:0]]; + menuNode = [browser parentForItemsInColumn:column]; + if ([menuNode class] == [PithosObjectNode class]) { + // Node context menu + menuNodes = [NSMutableArray arrayWithObject:menuNode]; + nodeContextMenu = YES; + } + // else + // General context menu } } else { // Node context menu @@ -2192,123 +2157,193 @@ 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) { - menuItem = [[[NSMenuItem alloc] initWithTitle:@"New Folder" action:@selector(menuNewFolder:) keyEquivalent:@""] autorelease]; + if (!menuNode.shared && !menuNode.sharingAccount) { + menuItem = [[NSMenuItem alloc] initWithTitle:@"New Folder" action:@selector(menuNewFolder:) keyEquivalent:@""]; [menuItem setRepresentedObject:menuNode]; [menu addItem:menuItem]; [menu addItem:[NSMenuItem separatorItem]]; } // Refresh - menuItem = [[[NSMenuItem alloc] initWithTitle:@"Refresh" action:@selector(refresh:) keyEquivalent:@""] autorelease]; + menuItem = [[NSMenuItem alloc] initWithTitle:@"Refresh" action:@selector(refresh:) keyEquivalent:@""]; [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:@""]; [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 = @"Paste Item"; + else + menuItemTitle = @"Paste Items"; + [menu addItem:[NSMenuItem separatorItem]]; + menuItem = [[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuPaste:) keyEquivalent:@""]; + [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]; + // Download + if (([firstMenuNode class] != [PithosContainerNode class]) && ([firstMenuNode class] != [PithosAccountNode class])) { + menuItem = [[NSMenuItem alloc] initWithTitle:@"Download" action:@selector(menuDownload:) keyEquivalent:@""]; + [menuItem setRepresentedObject:menuNodes]; + [menu addItem:menuItem]; + [menu addItem:[NSMenuItem separatorItem]]; + } // 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:@""]; [menuItem setRepresentedObject:menuNodes]; [menu addItem:menuItem]; - [menu addItem:[NSMenuItem separatorItem]]; } + menuItem = [[NSMenuItem alloc] initWithTitle:@"Delete" action:@selector(menuDelete:) keyEquivalent:@""]; + [menuItem setRepresentedObject:menuNodes]; + [menu addItem:menuItem]; + [menu addItem:[NSMenuItem separatorItem]]; } // Refresh - menuItem = [[[NSMenuItem alloc] initWithTitle:@"Refresh" action:@selector(refresh:) keyEquivalent:@""] autorelease]; + menuItem = [[NSMenuItem alloc] initWithTitle:@"Refresh" action:@selector(refresh:) keyEquivalent:@""]; [menu addItem:menuItem]; // Get Info - if (!sharingAccount || ([firstMenuNode class] != [PithosAccountNode class])) { + if (!firstMenuNode.sharingAccount || ([firstMenuNode class] != [PithosAccountNode class])) { [menu addItem:[NSMenuItem separatorItem]]; - menuItem = [[[NSMenuItem alloc] initWithTitle:@"Get Info" action:@selector(menuGetInfo:) keyEquivalent:@""] autorelease]; + menuItem = [[NSMenuItem alloc] initWithTitle:(([firstMenuNode class] == [PithosContainerNode class]) ? @"Info" : @"Info and Sharing") + action:@selector(menuGetInfo:) + keyEquivalent:@""]; [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 menuItemTitle = [NSString stringWithFormat:@"Cut %lu Items", menuNodesCount]; - menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuCut:) keyEquivalent:@""] autorelease]; + menuItem = [[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuCut:) keyEquivalent:@""]; [menuItem setRepresentedObject:menuNodes]; [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]; else menuItemTitle = [NSString stringWithFormat:@"Copy %lu Items", menuNodesCount]; - menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuCopy:) keyEquivalent:@""] autorelease]; + menuItem = [[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuCopy:) keyEquivalent:@""]; [menuItem setRepresentedObject:menuNodes]; [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 = @"Paste Item"; + else + menuItemTitle = @"Paste Items"; + menuItem = [[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuPaste:) keyEquivalent:@""]; + [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 { @@ -2316,108 +2351,100 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { if ([node class] == [PithosContainerNode class]) { // Operation: Create (upload) a new root application/directory object __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (operation.isCancelled) { - [pool drain]; - return; - } - NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos - containerName:node.pithosContainer.name - subdirName:@"untitled folder"]; - NSString *fileName = [safeObjectName lastPathComponent]; - if (operation.isCancelled) { - [pool drain]; - return; + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos + containerName:node.pithosContainer.name + subdirName:@"untitled folder"]; + NSString *fileName = [safeObjectName lastPathComponent]; + if (operation.isCancelled) + 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:); + NSString *messagePrefix = [NSString stringWithFormat:@"Creating directory '%@'", fileName]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory + message:messagePrefix]; + objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + fileName, @"fileName", + [NSArray arrayWithObject:node], @"refreshNodes", + [NSNumber numberWithBool:YES], @"refresh", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + uploadNetworkQueue, @"networkQueue", + @"upload", @"operationType", + nil]; + [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; } - 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:); - NSString *messagePrefix = [NSString stringWithFormat:@"Creating directory '%@'", fileName]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory - message:messagePrefix]; - objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: - fileName, @"fileName", - [NSArray arrayWithObject:node], @"refreshNodes", - [NSNumber numberWithBool:YES], @"refresh", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - uploadNetworkQueue, @"networkQueue", - @"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:@"/"])) { // Operation: Create (upload) a new aplication/directory object __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - 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]; - if (operation.isCancelled) { - [pool drain]; - return; + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos + containerName:node.pithosContainer.name + subdirName:[node.pithosObject.name stringByAppendingPathComponent:@"untitled folder"]]; + NSString *fileName = [safeObjectName lastPathComponent]; + if (operation.isCancelled) + 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:); + NSString *messagePrefix = [NSString stringWithFormat:@"Creating directory '%@'", fileName]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory + message:messagePrefix]; + objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + fileName, @"fileName", + [NSArray arrayWithObject:node], @"refreshNodes", + [NSNumber numberWithBool:YES], @"refresh", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + uploadNetworkQueue, @"networkQueue", + @"upload", @"operationType", + nil]; + [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; } - 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:); - NSString *messagePrefix = [NSString stringWithFormat:@"Creating directory '%@'", fileName]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory - message:messagePrefix]; - objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: - fileName, @"fileName", - [NSArray arrayWithObject:node], @"refreshNodes", - [NSNumber numberWithBool:YES], @"refresh", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - uploadNetworkQueue, @"networkQueue", - @"upload", @"operationType", - nil]; - [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; - [pool drain]; }]; [uploadQueue addOperation:operation]; } @@ -2429,97 +2456,121 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { } } +- (void)menuDownload:(NSMenuItem *)sender { + NSArray *nodes = (NSArray *)[sender representedObject]; + PithosNode *firstNode = [nodes objectAtIndex:0]; + if (([nodes count] == 1) && ([firstNode class] == [PithosObjectNode class])) { + NSSavePanel *save = [NSSavePanel savePanel]; + save.nameFieldStringValue = firstNode.displayName; + NSInteger result = [save runModal]; + if (result == NSOKButton) { + NSString *destinationPath = save.URL.path; + NSString *directoryPath = [destinationPath stringByDeletingLastPathComponent]; + NSString *newFileName = [destinationPath lastPathComponent]; + if ([destinationPath hasSuffix:@"/"]) + newFileName = [newFileName stringByAppendingString:@"/"]; + if ([firstNode.displayName isEqualToString:newFileName]) + newFileName = nil; + [self downloadNode:firstNode toDirectory:directoryPath withNewFileName:newFileName version:nil checkIfExists:NO]; + } + } else { + NSOpenPanel *open = [NSOpenPanel openPanel]; + open.canChooseFiles = NO; + open.canChooseDirectories = YES; + open.canCreateDirectories = YES; + NSInteger result = [open runModal]; + if (result == NSOKButton) { + NSString *directoryPath = open.URL.path; + for (PithosNode *node in nodes) { + [self downloadNode:node toDirectory:directoryPath withNewFileName:nil version:nil checkIfExists:YES]; + } + } + } +} + - (void)menuDelete:(NSMenuItem *)sender { for (PithosNode *node in ((NSArray *)[sender representedObject])) { if (([node class] == [PithosObjectNode class]) || (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) { // 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 deleteObjectRequestWithPithos:pithos - containerName:node.pithosContainer.name - objectName:node.pithosObject.name]; - if (operation.isCancelled) { - [pool drain]; - return; + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *fileName = [node.pithosObject.name lastPathComponent]; + if ([node.pithosObject.name hasSuffix:@"/"]) + fileName = [fileName stringByAppendingString:@"/"]; + ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos + containerName:node.pithosContainer.name + objectName:node.pithosObject.name]; + if (operation.isCancelled) + return; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Deleting '%@'", fileName]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete + message:messagePrefix]; + objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + fileName, @"fileName", + [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + deleteNetworkQueue, @"networkQueue", + @"delete", @"operationType", + nil]; + [deleteNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; } - objectRequest.delegate = self; - objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Deleting '%@'", fileName]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete - message:messagePrefix]; - objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: - fileName, @"fileName", - [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - deleteNetworkQueue, @"networkQueue", - @"delete", @"operationType", - nil]; - [deleteNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; - [pool drain]; }]; [deleteQueue addOperation:operation]; } else if ([node class] == [PithosSubdirNode class]) { // 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]; - 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; + @autoreleasepool { + if (operation.isCancelled) + 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) + return; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Deleting '%@'", [objectRequest.userInfo objectForKey:@"fileName"]]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete + message:messagePrefix]; + [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: + [NSDictionary dictionaryWithObjectsAndKeys: + [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + deleteNetworkQueue, @"networkQueue", + @"delete", @"operationType", + nil]]; + if (previousObjectRequest) + [objectRequest addDependency:previousObjectRequest]; + previousObjectRequest = objectRequest; + [deleteNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; } - objectRequest.delegate = self; - objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Deleting '%@'", [objectRequest.userInfo objectForKey:@"fileName"]]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete - message:messagePrefix]; - [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: - [NSDictionary dictionaryWithObjectsAndKeys: - [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - deleteNetworkQueue, @"networkQueue", - @"delete", @"operationType", - nil]]; - if (previousObjectRequest) - [objectRequest addDependency:previousObjectRequest]; - previousObjectRequest = objectRequest; - [deleteNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; } } - [pool drain]; }]; [deleteQueue addOperation:operation]; } @@ -2534,78 +2585,20 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { [node.pithosObject.name hasSuffix:@"/"])) { // Operation: Move to trash an object or subdir/ node __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - 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:); - NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", - [objectRequest.userInfo objectForKey:@"sourceContainerName"], - [objectRequest.userInfo objectForKey:@"sourceObjectName"], - [objectRequest.userInfo objectForKey:@"destinationContainerName"], - [objectRequest.userInfo objectForKey:@"destinationObjectName"]]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove - message:messagePrefix]; - [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: - [NSDictionary dictionaryWithObjectsAndKeys: - [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - moveNetworkQueue, @"networkQueue", - @"move", @"operationType", - nil]]; - [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; - } - } - [pool drain]; - }]; - [moveQueue addOperation:operation]; - } else if ([node class] == [PithosSubdirNode class]) { - // 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]; - 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; - } + @autoreleasepool { + if (operation.isCancelled) + 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:); @@ -2630,14 +2623,66 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { moveNetworkQueue, @"networkQueue", @"move", @"operationType", nil]]; - if (previousObjectRequest) - [objectRequest addDependency:previousObjectRequest]; - previousObjectRequest = objectRequest; [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; } } } - [pool drain]; + }]; + [moveQueue addOperation:operation]; + } else if ([node class] == [PithosSubdirNode class]) { + // Operation: Move to trash a subdir node and its descendants + // The resulting ASIPithosObjectRequests are chained through dependencies + __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + @autoreleasepool { + if (operation.isCancelled) + 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) + return; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", + [objectRequest.userInfo objectForKey:@"sourceContainerName"], + [objectRequest.userInfo objectForKey:@"sourceObjectName"], + [objectRequest.userInfo objectForKey:@"destinationContainerName"], + [objectRequest.userInfo objectForKey:@"destinationObjectName"]]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove + message:messagePrefix]; + [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: + [NSDictionary dictionaryWithObjectsAndKeys: + [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + moveNetworkQueue, @"networkQueue", + @"move", @"operationType", + nil]]; + if (previousObjectRequest) + [objectRequest addDependency:previousObjectRequest]; + previousObjectRequest = objectRequest; + [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; + } + } + } + } }]; [moveQueue addOperation:operation]; } @@ -2661,12 +2706,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 cpyNodes: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]; } } @@ -2691,7 +2736,7 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { } if (!message) - message = [[[[UsingSizeTransformer alloc] init] autorelease] transformedValue:accountNode.pithosAccount]; + message = [[[UsingSizeTransformer alloc] init] transformedValue:accountNode.pithosAccount]; [activityTextField setStringValue:message]; }