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