+- (void)menuNewFolder:(NSMenuItem *)sender {
+ PithosNode *node = (PithosNode *)[sender representedObject];
+ if ([node class] == [PithosContainerNode class]) {
+ // Operation: Create (upload) a new root application/directory object
+ __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
+ @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]];
+ }
+ }];
+ [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:^{
+ @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]];
+ }
+ }];
+ [uploadQueue addOperation:operation];
+ }
+}
+
+- (void)menuGetInfo:(NSMenuItem *)sender {
+ for (PithosNode *node in ((NSArray *)[sender representedObject])) {
+ [node showPithosNodeInfo:sender];
+ }
+}
+
+- (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:^{
+ @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]];
+ }
+ }];
+ [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:^{
+ @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]];
+ }
+ }
+ }
+ }];
+ [deleteQueue addOperation:operation];
+ }
+ }
+}
+
+- (void)menuMoveToTrash:(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: Move to trash an object or subdir/ node
+ __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
+ @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:);
+ 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]];
+ }
+ }
+ }
+ }];
+ [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];
+ }
+ }
+}
+
+- (void)menuCut:(NSMenuItem *)sender {
+ self.clipboardNodes = (NSArray *)[sender representedObject];
+ self.clipboardParentNode = ((PithosNode *)[clipboardNodes objectAtIndex:0]).parent;
+ self.clipboardCopy = NO;
+}
+
+- (void)menuCopy:(NSMenuItem *)sender {
+ self.clipboardNodes = (NSArray *)[sender representedObject];
+ self.clipboardParentNode = ((PithosNode *)[clipboardNodes objectAtIndex:0]).parent;
+ self.clipboardCopy = YES;
+}
+
+- (void)menuPaste:(NSMenuItem *)sender {
+ if (!clipboardNodes || ![clipboardNodes count])
+ return;
+ PithosNode *dropNode = (PithosNode *)[sender representedObject];
+ NSArray *localClipboardNodes = [NSArray arrayWithArray:clipboardNodes];
+ if (clipboardCopy) {
+ [self cpyNodes:localClipboardNodes toNode:dropNode];
+ } else if (![dropNode isEqualTo:clipboardParentNode]) {
+ self.clipboardNodes = nil;
+ self.clipboardParentNode = nil;
+ [self moveNodes:localClipboardNodes toNode:dropNode];
+ }
+}
+
+#pragma mark -
+#pragma mark PithosActivityFacilityDelegate
+
+- (void)activityUpdate:(NSDictionary *)info {
+ NSString *message = [info objectForKey:@"message"];
+ NSUInteger runningActivitiesCount = [[info objectForKey:@"runningActivitiesCount"] unsignedIntegerValue];
+// NSUInteger endingActivitiesCount = [[info objectForKey:@"endingActivitiesCount"] unsignedIntegerValue];
+ NSUInteger totalUploadBytes = [[info objectForKey:@"totalUploadBytes"] unsignedIntegerValue];
+ NSUInteger currentUploadBytes = [[info objectForKey:@"currentUploadBytes"] unsignedIntegerValue];
+ NSUInteger totalDownloadBytes = [[info objectForKey:@"totalDownloadBytes"] unsignedIntegerValue];
+ NSUInteger currentDownloadBytes = [[info objectForKey:@"currentDownloadBytes"] unsignedIntegerValue];
+ NSUInteger totalBytes = totalUploadBytes + totalDownloadBytes;
+ if (runningActivitiesCount && totalBytes) {
+ [activityProgressIndicator setDoubleValue:((currentUploadBytes + currentDownloadBytes + 0.0)/(totalBytes + 0.0))];
+ [activityProgressIndicator startAnimation:self];
+ } else {
+ [activityProgressIndicator setDoubleValue:1.0];
+ [activityProgressIndicator stopAnimation:self];
+ }
+
+ if (!message)
+ message = [[[UsingSizeTransformer alloc] init] transformedValue:accountNode.pithosAccount];
+ [activityTextField setStringValue:message];