2 // PithosBrowserController.m
5 // Copyright 2011 GRNET S.A. All rights reserved.
7 // Redistribution and use in source and binary forms, with or
8 // without modification, are permitted provided that the following
11 // 1. Redistributions of source code must retain the above
12 // copyright notice, this list of conditions and the following
15 // 2. Redistributions in binary form must reproduce the above
16 // copyright notice, this list of conditions and the following
17 // disclaimer in the documentation and/or other materials
18 // provided with the distribution.
20 // THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
21 // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
24 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27 // USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
33 // The views and conclusions contained in the software and
34 // documentation are those of the authors and should not be
35 // interpreted as representing official policies, either expressed
36 // or implied, of GRNET S.A.
38 #import "PithosBrowserController.h"
39 #import "PithosNode.h"
40 #import "PithosAccountNode.h"
41 #import "PithosContainerNode.h"
42 #import "PithosSubdirNode.h"
43 #import "PithosObjectNode.h"
44 #import "PithosSharingAccountsNode.h"
45 #import "PithosEmptyNode.h"
46 #import "ImageAndTextCell.h"
47 #import "FileSystemBrowserCell.h"
48 #import "ASIPithosRequest.h"
49 #import "ASIPithosContainerRequest.h"
50 #import "ASIPithosObjectRequest.h"
51 #import "ASIPithosAccount.h"
52 #import "ASIPithosContainer.h"
53 #import "ASIPithosObject.h"
54 #import "PithosUtilities.h"
55 #import "BytesSizeTransformer.h"
57 @interface PithosBrowserCell : FileSystemBrowserCell {}
60 @implementation PithosBrowserCell
63 if ((self = [super init])) {
64 [self setLineBreakMode:NSLineBreakByTruncatingMiddle];
65 [self setEditable:YES];
70 - (void)setObjectValue:(id)object {
71 if ([object isKindOfClass:[PithosNode class]]) {
72 PithosNode *node = (PithosNode *)object;
73 [self setStringValue:node.displayName];
74 [self setImage:node.icon];
76 [super setObjectValue:object];
82 @interface PithosOutlineViewCell : ImageAndTextCell {}
85 @implementation PithosOutlineViewCell
87 - (void)setObjectValue:(id)object {
88 if ([object isKindOfClass:[PithosNode class]]) {
89 PithosNode *node = (PithosNode *)object;
90 [self setStringValue:node.displayName];
91 [self setImage:node.icon];
92 [self setEditable:NO];
94 [super setObjectValue:object];
100 @interface PithosBrowserController (Private) {}
101 - (void)resetContainers:(NSNotification *)notification;
102 - (BOOL)uploadFiles:(NSArray *)filenames toNode:(PithosNode *)destinationNode;
103 - (BOOL)moveNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode;
104 - (BOOL)copyNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode;
107 @implementation PithosBrowserController
108 @synthesize accountNode;
109 @synthesize verticalSplitView, horizontalSplitView, leftTopView, leftBottomView, outlineView, browser;
110 @synthesize draggedNodes, draggedParentNode;
111 @synthesize clipboardNodes, clipboardParentNode, clipboardCopy;
112 @synthesize activityTextField, activityProgressIndicator;
115 #pragma Object Lifecycle
118 return [super initWithWindowNibName:@"PithosBrowserController"];
122 [[NSNotificationCenter defaultCenter] removeObserver:self];
123 [clipboardParentNode release];
124 [clipboardNodes release];
125 [draggedParentNode release];
126 [draggedNodes release];
127 [sharedPreviewController release];
128 [othersSharedNode release];
129 [mySharedNode release];
130 [sharedNode release];
131 [containersNodeChildren release];
132 [containersNode release];
133 [accountNode release];
138 - (void)awakeFromNib {
139 [super awakeFromNib];
141 [browser registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSFilesPromisePboardType, nil]];
142 [browser setDraggingSourceOperationMask:(NSDragOperationCopy|NSDragOperationMove) forLocal:YES];
143 [browser setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
145 [outlineView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSFilesPromisePboardType, nil]];
146 [outlineView setDraggingSourceOperationMask:(NSDragOperationCopy|NSDragOperationMove) forLocal:YES];
147 [outlineView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
149 [browser setCellClass:[PithosBrowserCell class]];
152 - (void)resetContainers:(NSNotification *)notification {
154 [browser loadColumnZero];
155 [containersNodeChildren removeAllObjects];
156 [outlineView reloadData];
157 // Expand the folder outline view
158 [outlineView expandItem:nil expandChildren:YES];
159 [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
162 [accountNode refresh];
163 [mySharedNode refresh];
164 [othersSharedNode refresh];
166 [activityFacility reset];
169 - (void)windowDidLoad {
170 [super windowDidLoad];
172 [activityProgressIndicator setUsesThreadedAnimation:YES];
173 [activityProgressIndicator setMinValue:0.0];
174 [activityProgressIndicator setMaxValue:1.0];
175 activityFacility = [PithosActivityFacility defaultPithosActivityFacility];
176 activityFacility.delegate = self;
178 accountNode = [[PithosAccountNode alloc] init];
179 containersNode = [[PithosEmptyNode alloc] initWithDisplayName:@"CONTAINERS" icon:nil];
180 containersNodeChildren = [[NSMutableArray alloc] init];
181 sharedNode = [[PithosEmptyNode alloc] initWithDisplayName:@"SHARED" icon:nil];
182 mySharedNode = [[PithosAccountNode alloc] init];
183 mySharedNode.displayName = @"my shared";
184 mySharedNode.shared = YES;
185 mySharedNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kUserIcon)];
186 othersSharedNode = [[PithosSharingAccountsNode alloc] init];
187 othersSharedNode.displayName = @"others shared";
188 othersSharedNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGroupIcon)];
190 [[[outlineView tableColumns] objectAtIndex:0] setDataCell:[[[PithosOutlineViewCell alloc] init] autorelease]];
192 // Register for updates
193 // PithosContainerNode updates browser nodes
194 [[NSNotificationCenter defaultCenter] addObserver:self
195 selector:@selector(pithosNodeChildrenUpdated:)
196 name:@"PithosContainerNodeChildrenUpdated"
198 // PithosSubdirNode updates browser nodes
199 [[NSNotificationCenter defaultCenter] addObserver:self
200 selector:@selector(pithosNodeChildrenUpdated:)
201 name:@"PithosSubdirNodeChildrenUpdated"
203 // PithosAccountNode accountNode updates outlineView container nodes
204 [[NSNotificationCenter defaultCenter] addObserver:self
205 selector:@selector(pithosAccountNodeChildrenUpdated:)
206 name:@"PithosAccountNodeChildrenUpdated"
208 // PithosAccountNode other than accountNode updates nodes
209 [[NSNotificationCenter defaultCenter] addObserver:self
210 selector:@selector(pithosNodeChildrenUpdated:)
211 name:@"PithosAccountNodeChildrenUpdated"
213 // PithosSharingAccountsNode othersSharedNode updates browser nodes
214 [[NSNotificationCenter defaultCenter] addObserver:self
215 selector:@selector(pithosNodeChildrenUpdated:)
216 name:@"PithosSharingAccountsNodeChildrenUpdated"
217 object:othersSharedNode];
218 // Updated authentication credentials reset containers in the outline view
219 [[NSNotificationCenter defaultCenter] addObserver:self
220 selector:@selector(resetContainers:)
221 name:@"PithosAuthenticationCredentialsUpdated"
223 // Request for browser refresh
224 [[NSNotificationCenter defaultCenter] addObserver:self
225 selector:@selector(pithosBrowserRefreshNeeded:)
226 name:@"PithosBrowserRefreshNeeeded"
231 #pragma mark Observers
233 - (void)pithosNodeChildrenUpdated:(NSNotification *)notification {
234 PithosNode *node = (PithosNode *)[notification object];
235 if (node == accountNode)
237 NSLog(@"pithosNodeChildrenUpdated:%@", node.url);
238 NSInteger lastColumn = [browser lastColumn];
239 for (NSInteger column = lastColumn; column >= 0; column--) {
240 if ([[browser parentForItemsInColumn:column] isEqualTo:node]) {
241 [browser reloadColumn:column];
247 - (void)pithosAccountNodeChildrenUpdated:(NSNotification *)notification {
248 BOOL containerPithosFound = NO;
249 BOOL containerTrashFound = NO;
250 NSMutableIndexSet *removedContainersNodeChildren = [NSMutableIndexSet indexSet];
251 for (NSUInteger i = 0 ; i < [containersNodeChildren count] ; i++) {
252 if (![accountNode.children containsObject:[containersNodeChildren objectAtIndex:i]])
253 [removedContainersNodeChildren addIndex:i];
255 [containersNodeChildren removeObjectsAtIndexes:removedContainersNodeChildren];
256 for (PithosContainerNode *containerNode in accountNode.children) {
257 if ([containerNode.pithosContainer.name isEqualToString:@"pithos"]) {
258 if (![containersNodeChildren containsObject:containerNode])
259 [containersNodeChildren insertObject:containerNode atIndex:0];
260 containerPithosFound = YES;
261 } else if ([containerNode.pithosContainer.name isEqualToString:@"trash"]) {
262 NSUInteger insertIndex = 1;
263 if (!containerPithosFound)
265 if (![containersNodeChildren containsObject:containerNode])
266 [containersNodeChildren insertObject:containerNode atIndex:insertIndex];
267 containerTrashFound = YES;
268 } else if (![containersNodeChildren containsObject:containerNode]) {
269 [containersNodeChildren addObject:containerNode];
272 BOOL refreshAccountNode = NO;
273 if (!containerPithosFound) {
274 // Create pithos node
275 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithContainerName:@"pithos"];
276 [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
277 while (![containerRequest isFinished]) {
280 if ([containerRequest error]) {
281 [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
283 refreshAccountNode = YES;
286 if (!containerTrashFound) {
288 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithContainerName:@"trash"];
289 [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
290 while (![containerRequest isFinished]) {
293 if ([containerRequest error]) {
294 [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
296 refreshAccountNode = YES;
300 if (refreshAccountNode)
301 [accountNode refresh];
303 [outlineView reloadData];
305 // Expand the folder outline view
306 [outlineView expandItem:nil expandChildren:YES];
308 if ((rootNode == nil) || (rootNode == containersNode) || (rootNode == sharedNode)) {
309 rootNode = [containersNodeChildren objectAtIndex:0];
310 [browser loadColumnZero];
317 - (void)pithosBrowserRefreshNeeded:(NSNotification *)notification {
324 - (IBAction)forceRefresh:(id)sender {
326 [accountNode forceRefresh];
327 for (NSInteger column = [browser lastColumn]; column >= 0; column--) {
328 PithosNode *node = (PithosNode *)[browser parentForItemsInColumn:column];
329 node.forcedRefresh = YES;
330 [(PithosNode *)[browser parentForItemsInColumn:column] invalidateChildren];
332 [browser validateVisibleColumns];
335 - (IBAction)refresh:(id)sender {
336 if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) {
337 [self forceRefresh:sender];
340 [accountNode refresh];
341 for (NSInteger column = [browser lastColumn]; column >= 0; column--) {
342 [(PithosNode *)[browser parentForItemsInColumn:column] invalidateChildren];
344 [browser validateVisibleColumns];
349 #pragma mark NSBrowserDelegate
351 - (id)rootItemForBrowser:(NSBrowser *)browser {
355 - (NSInteger)browser:(NSBrowser *)browser numberOfChildrenOfItem:(id)item {
356 PithosNode *node = (PithosNode *)item;
357 return node.children.count;
360 - (id)browser:(NSBrowser *)browser child:(NSInteger)index ofItem:(id)item {
361 PithosNode *node = (PithosNode *)item;
362 return [node.children objectAtIndex:index];
365 - (BOOL)browser:(NSBrowser *)browser isLeafItem:(id)item {
366 PithosNode *node = (PithosNode *)item;
367 return node.isLeafItem;
370 - (id)browser:(NSBrowser *)browser objectValueForItem:(id)item {
371 PithosNode *node = (PithosNode *)item;
375 - (NSViewController *)browser:(NSBrowser *)browser previewViewControllerForLeafItem:(id)item {
376 if (sharedPreviewController == nil)
377 sharedPreviewController = [[NSViewController alloc] initWithNibName:@"PithosBrowserPreviewController" bundle:[NSBundle bundleForClass:[self class]]];
378 return sharedPreviewController;
381 //- (CGFloat)browser:(NSBrowser *)browser shouldSizeColumn:(NSInteger)columnIndex forUserResize:(BOOL)forUserResize toWidth:(CGFloat)suggestedWidth {
382 // if (!forUserResize) {
383 // id item = [browser parentForItemsInColumn:columnIndex];
384 // if ([self browser:browser isLeafItem:item]) {
385 // suggestedWidth = 200;
388 // return suggestedWidth;
391 - (BOOL)browser:(NSBrowser *)sender isColumnValid:(NSInteger)column {
397 - (BOOL)browser:(NSBrowser *)browser shouldEditItem:(id)item {
398 PithosNode *node = (PithosNode *)item;
399 if (node.shared || node.sharingAccount ||
400 ([node class] == [PithosContainerNode class]) || ([node class] == [PithosAccountNode class]))
405 - (void)browser:(NSBrowser *)browser setObjectValue:(id)object forItem:(id)item {
406 PithosNode *node = (PithosNode *)item;
407 NSString *newName = (NSString *)object;
408 NSUInteger newNameLength = [newName length];
409 NSRange firstSlashRange = [newName rangeOfString:@"/"];
410 if ((newNameLength == 0) ||
411 ((firstSlashRange.length == 1) && (firstSlashRange.location != (newNameLength - 1))) ||
412 ([newName isEqualToString:node.displayName])) {
415 if (([node class] == [PithosObjectNode class]) ||
416 (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
417 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
418 dispatch_async(queue, ^{
419 NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];
420 if ([newName hasSuffix:@"/"])
421 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
422 NSError *error = nil;
424 if ([PithosUtilities objectExistsAtContainerName:node.pithosContainer.name
425 objectName:destinationObjectName
427 isDirectory:&isDirectory
428 sharingAccount:nil]) {
429 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
430 [alert setMessageText:@"Name Taken"];
431 [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
432 [alert addButtonWithTitle:@"OK"];
438 ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithContainerName:node.pithosContainer.name
439 objectName:node.pithosObject.name
440 destinationContainerName:node.pithosContainer.name
441 destinationObjectName:destinationObjectName
444 objectRequest.delegate = self;
445 objectRequest.didFinishSelector = @selector(moveFinished:);
446 objectRequest.didFailSelector = @selector(moveFailed:);
447 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
448 message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
449 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
450 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
451 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
452 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
453 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
454 [NSDictionary dictionaryWithObjectsAndKeys:
455 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
456 [NSNumber numberWithBool:YES], @"refresh",
457 activity, @"activity",
458 [NSNumber numberWithUnsignedInteger:10], @"retries",
460 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
463 } else if ([node class] == [PithosSubdirNode class]) {
464 if (firstSlashRange.length == 1)
466 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
467 dispatch_async(queue, ^{
468 NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];
469 NSError *error = nil;
471 if ([PithosUtilities objectExistsAtContainerName:node.pithosContainer.name
472 objectName:destinationObjectName
474 isDirectory:&isDirectory
475 sharingAccount:nil]) {
476 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
477 [alert setMessageText:@"Name Taken"];
478 [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
479 [alert addButtonWithTitle:@"OK"];
485 if (node.pithosObject.subdir)
486 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
487 NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
488 objectName:node.pithosObject.name
489 destinationContainerName:node.pithosContainer.name
490 destinationObjectName:destinationObjectName
492 if (objectRequests) {
493 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
494 objectRequest.delegate = self;
495 objectRequest.didFinishSelector = @selector(moveFinished:);
496 objectRequest.didFailSelector = @selector(moveFailed:);
497 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
498 message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
499 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
500 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
501 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
502 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
503 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
504 [NSDictionary dictionaryWithObjectsAndKeys:
505 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
506 [NSNumber numberWithBool:YES], @"refresh",
507 activity, @"activity",
508 [NSNumber numberWithUnsignedInteger:10], @"retries",
510 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
517 #pragma mark Drag and Drop source
519 - (BOOL)browser:(NSBrowser *)aBrowser canDragRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column
520 withEvent:(NSEvent *)event {
521 NSIndexPath *baseIndexPath = [browser indexPathForColumn:column];
522 __block BOOL result = YES;
523 [rowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
524 PithosNode *node = [browser itemAtIndexPath:[baseIndexPath indexPathByAddingIndex:idx]];
525 if (([node class] == [PithosContainerNode class]) || ([node class] == [PithosAccountNode class])) {
533 - (BOOL)browser:(NSBrowser *)aBrowser writeRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column
534 toPasteboard:(NSPasteboard *)pasteboard {
535 NSMutableArray *propertyList = [NSMutableArray arrayWithCapacity:[rowIndexes count]];
536 NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:[rowIndexes count]];
537 NSIndexPath *baseIndexPath = [browser indexPathForColumn:column];
538 [rowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
539 PithosNode *node = [browser itemAtIndexPath:[baseIndexPath indexPathByAddingIndex:idx]];
540 [propertyList addObject:[node.pithosObject.name pathExtension]];
541 [nodes addObject:node];
544 [pasteboard declareTypes:[NSArray arrayWithObject:NSFilesPromisePboardType] owner:self];
545 [pasteboard setPropertyList:propertyList forType:NSFilesPromisePboardType];
546 self.draggedNodes = nodes;
547 self.draggedParentNode = [browser parentForItemsInColumn:column];
551 - (NSArray *)browser:(NSBrowser *)aBrowser namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
552 forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
553 NSMutableArray *names = [NSMutableArray arrayWithCapacity:[draggedNodes count]];
554 for (PithosNode *node in draggedNodes) {
555 // If the node is a subdir ask if the whole tree should be downloaded
556 if ([node class] == [PithosSubdirNode class]) {
557 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
558 [alert setMessageText:@"Download directory"];
559 [alert setInformativeText:[NSString stringWithFormat:@"'%@' is a directory, do you want to download its contents?", node.displayName]];
560 [alert addButtonWithTitle:@"OK"];
561 [alert addButtonWithTitle:@"Cancel"];
562 NSInteger choice = [alert runModal];
563 if (choice == NSAlertFirstButtonReturn) {
564 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
565 dispatch_async(queue, ^{
566 NSArray *objectRequests = [PithosUtilities objectDataRequestsForSubdirWithContainerName:node.pithosContainer.name
567 objectName:node.pithosObject.name
568 toDirectory:[dropDestination path]
570 sharingAccount:node.sharingAccount];
571 if (objectRequests) {
572 for (__block ASIPithosObjectRequest *objectRequest in objectRequests) {
573 [names addObject:[objectRequest.userInfo valueForKey:@"fileName"]];
574 objectRequest.delegate = self;
575 objectRequest.didFinishSelector = @selector(downloadObjectFinished:);
576 objectRequest.didFailSelector = @selector(downloadObjectFailed:);
577 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload
578 message:[NSString stringWithFormat:@"Downloading '%@' (0%%)", [objectRequest.userInfo objectForKey:@"fileName"]]
579 totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue]
581 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
582 [NSDictionary dictionaryWithObjectsAndKeys:
583 activity, @"activity",
584 [NSNumber numberWithUnsignedInteger:10], @"retries",
586 [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
587 [activityFacility updateActivity:activity
588 withMessage:[NSString stringWithFormat:@"Downloading '%@' (%.0f%%)", [objectRequest.userInfo valueForKey:@"fileName"], (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))]
589 totalBytes:activity.totalBytes
590 currentBytes:(activity.currentBytes + size)];
592 [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
598 __block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectDataRequestWithContainerName:node.pithosContainer.name
599 objectName:node.pithosObject.name
600 toDirectory:[dropDestination path]
602 sharingAccount:node.sharingAccount];
604 [names addObject:[objectRequest.userInfo valueForKey:@"fileName"]];
605 objectRequest.delegate = self;
606 objectRequest.didFinishSelector = @selector(downloadObjectFinished:);
607 objectRequest.didFailSelector = @selector(downloadObjectFailed:);
608 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload
609 message:[NSString stringWithFormat:@"Downloading '%@' (0%%)", [objectRequest.userInfo valueForKey:@"fileName"]]
610 totalBytes:node.pithosObject.bytes
612 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
613 [NSDictionary dictionaryWithObjectsAndKeys:
614 activity, @"activity",
615 [NSNumber numberWithUnsignedInteger:10], @"retries",
617 [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
618 [activityFacility updateActivity:activity
619 withMessage:[NSString stringWithFormat:@"Downloading '%@' (%.0f%%)", [objectRequest.userInfo valueForKey:@"fileName"], (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))]
620 totalBytes:activity.totalBytes
621 currentBytes:(activity.currentBytes + size)];
623 [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
630 #pragma mark Drag and Drop destination
632 - (NSDragOperation)browser:aBrowser
633 validateDrop:(id<NSDraggingInfo>)info
634 proposedRow:(NSInteger *)row
635 column:(NSInteger *)column
636 dropOperation:(NSBrowserDropOperation *)dropOperation {
637 NSDragOperation result = NSDragOperationNone;
638 if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
639 // For a drop above, the drop is redirected to the parent item
640 if (*dropOperation == NSBrowserDropAbove)
642 // Only allow dropping in folders
644 PithosNode *dropNode;
646 // Check if the node is not a folder and if so redirect to the parent item
647 dropNode = [browser itemAtRow:*row inColumn:*column];
648 if ([dropNode class] == [PithosObjectNode class])
652 dropNode = [browser parentForItemsInColumn:*column];
654 if (!dropNode.shared &&
655 (!dropNode.sharingAccount ||
656 ([dropNode class] == [PithosSubdirNode class]) ||
657 ([dropNode class] == [PithosContainerNode class]))) {
658 *dropOperation = NSBrowserDropOn;
659 result = NSDragOperationCopy;
662 } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) {
663 // For a drop above, the drop is redirected to the parent item
664 if (*dropOperation == NSBrowserDropAbove)
666 // Only allow dropping in folders
668 PithosNode *dropNode;
670 // Check if the node is not a folder and if so redirect to the parent item
671 dropNode = [browser itemAtRow:*row inColumn:*column];
672 if ([dropNode class] == [PithosObjectNode class])
676 dropNode = [browser parentForItemsInColumn:*column];
678 if (!dropNode.shared && !dropNode.sharingAccount) {
679 if ([info draggingSourceOperationMask] & NSDragOperationMove) {
680 // NSDragOperationCopy | NSDragOperationMove -> NSDragOperationMove
681 if ((([dropNode class] == [PithosContainerNode class]) ||
682 dropNode.pithosObject.subdir ||
683 ![dropNode.pithosObject.name hasSuffix:@"/"]) &&
684 ![dropNode isEqualTo:draggedParentNode]) {
685 // ![dropNode isEqualTo:draggedParentNode] &&
686 // ![draggedNodes containsObject:dropNode]) {
687 result = NSDragOperationMove;
689 } else if ([info draggingSourceOperationMask] & NSDragOperationCopy) {
690 // NSDragOperationCopy (Option modifier) -> NSDragOperationCopy
691 if (([dropNode class] == [PithosContainerNode class]) ||
692 dropNode.pithosObject.subdir ||
693 ![dropNode.pithosObject.name hasSuffix:@"/"]) {
694 result = NSDragOperationCopy;
703 - (BOOL)browser:(NSBrowser *)aBrowser
704 acceptDrop:(id<NSDraggingInfo>)info
706 column:(NSInteger)column
707 dropOperation:(NSBrowserDropOperation)dropOperation {
708 if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
709 NSArray *filenames = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
710 NSLog(@"drag in operation: %lu filenames: %@", [info draggingSourceOperationMask], filenames);
711 if ((column != -1) && (filenames != nil)) {
714 node = [browser itemAtRow:row inColumn:column];
716 node = [browser parentForItemsInColumn:column];
717 NSLog(@"drag in node: %@", node.url);
718 return [self uploadFiles:filenames toNode:node];
720 } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) {
721 NSLog(@"drag local operation: %lu nodes: %@", [info draggingSourceOperationMask], draggedNodes);
722 if ((column != -1) && (draggedNodes != nil)) {
725 node = [browser itemAtRow:row inColumn:column];
727 node = [browser parentForItemsInColumn:column];
728 NSLog(@"drag local node: %@", node.url);
729 if ([info draggingSourceOperationMask] & NSDragOperationMove)
730 return [self moveNodes:draggedNodes toNode:node];
731 else if ([info draggingSourceOperationMask] & NSDragOperationCopy)
732 return [self copyNodes:draggedNodes toNode:node];
739 #pragma mark Drag and Drop methods
741 - (BOOL)uploadFiles:(NSArray *)filenames toNode:(PithosNode *)destinationNode {
742 if (([destinationNode class] != [PithosSubdirNode class]) && ([destinationNode class] != [PithosContainerNode class]))
744 NSFileManager *defaultManager = [NSFileManager defaultManager];
745 NSString *containerName = [NSString stringWithString:destinationNode.pithosContainer.name];
746 NSString *objectNamePrefix;
747 if ([destinationNode class] == [PithosSubdirNode class])
748 objectNamePrefix = [NSString stringWithString:destinationNode.pithosObject.name];
750 objectNamePrefix = [NSString string];
751 if ((destinationNode.pithosContainer.blockHash == nil) || (destinationNode.pithosContainer.blockSize == 0)) {
752 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest containerMetadataRequestWithContainerName:containerName];
753 [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
754 while (![containerRequest isFinished]) {
757 if ([containerRequest error]) {
758 [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
760 } else if (containerRequest.responseStatusCode != 200) {
761 [PithosUtilities unexpectedResponseStatusAlertWithRequest:containerRequest];
764 destinationNode.pithosContainer.blockHash = [containerRequest blockHash];
765 destinationNode.pithosContainer.blockSize = [containerRequest blockSize];
767 NSUInteger blockSize = destinationNode.pithosContainer.blockSize;
768 NSString *blockHash = destinationNode.pithosContainer.blockHash;
770 for (NSString *filePath in filenames) {
772 if ([defaultManager fileExistsAtPath:filePath isDirectory:&isDirectory]) {
775 NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
776 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
777 dispatch_async(queue, ^{
778 NSError *error = nil;
779 NSString *contentType = [PithosUtilities contentTypeOfFile:filePath error:&error];
780 if (contentType == nil)
781 contentType = @"application/octet-stream";
783 NSLog(@"contentType detection error: %@", error);
784 NSArray *hashes = nil;
785 ASIPithosObjectRequest *objectRequest = [PithosUtilities writeObjectDataRequestWithContainerName:containerName
786 objectName:objectName
787 contentType:contentType
793 sharingAccount:destinationNode.sharingAccount];
795 objectRequest.delegate = self;
796 objectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
797 objectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
798 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload
799 message:[NSString stringWithFormat:@"Uploading '%@' (0%%)", [objectRequest.userInfo valueForKey:@"fileName"]]
800 totalBytes:[[objectRequest.userInfo valueForKey:@"bytes"] unsignedIntegerValue]
802 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
803 [NSDictionary dictionaryWithObjectsAndKeys:
804 containerName, @"containerName",
805 objectName, @"objectName",
806 contentType, @"contentType",
807 [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize",
808 blockHash, @"blockHash",
809 filePath, @"filePath",
811 [NSArray arrayWithObject:destinationNode], @"refreshNodes",
812 [NSNumber numberWithBool:YES], @"refresh",
813 [NSNumber numberWithUnsignedInteger:10], @"iteration",
814 activity, @"activity",
815 [NSNumber numberWithUnsignedInteger:10], @"retries",
817 if (destinationNode.sharingAccount)
818 [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"];
819 [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
823 // Upload directory, confirm first
824 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
825 [alert setMessageText:@"Upload directory"];
826 [alert setInformativeText:[NSString stringWithFormat:@"'%@' is a directory, do you want to upload it and its contents?", filePath]];
827 [alert addButtonWithTitle:@"OK"];
828 [alert addButtonWithTitle:@"Cancel"];
829 NSInteger choice = [alert runModal];
830 if (choice == NSAlertFirstButtonReturn) {
831 NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
832 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
833 dispatch_async(queue, ^{
834 NSMutableArray *objectNames = nil;
835 NSMutableArray *contentTypes = nil;
836 NSMutableArray *filePaths = nil;
837 NSMutableArray *hashesArrays = nil;
838 NSMutableArray *directoryObjectRequests = nil;
839 NSArray *objectRequests = [PithosUtilities writeObjectDataRequestsWithContainerName:containerName
840 objectName:objectName
843 forDirectory:filePath
845 objectNames:&objectNames
846 contentTypes:&contentTypes
848 hashesArrays:&hashesArrays
849 directoryObjectRequests:&directoryObjectRequests
850 sharingAccount:destinationNode.sharingAccount];
851 for (ASIPithosObjectRequest *objectRequest in directoryObjectRequests) {
852 objectRequest.delegate = self;
853 objectRequest.didFinishSelector = @selector(uploadDirectoryObjectFinished:);
854 objectRequest.didFailSelector = @selector(uploadDirectoryObjectFailed:);
855 [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
857 if (objectRequests) {
858 for (NSUInteger i = 0 ; i < [objectRequests count] ; i++) {
859 ASIPithosObjectRequest *objectRequest = [objectRequests objectAtIndex:i];
860 objectRequest.delegate = self;
861 objectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
862 objectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
863 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload
864 message:[NSString stringWithFormat:@"Uploading '%@' (0%%)", [objectRequest.userInfo valueForKey:@"fileName"]]
865 totalBytes:[[objectRequest.userInfo valueForKey:@"bytes"] unsignedIntegerValue]
867 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
868 [NSDictionary dictionaryWithObjectsAndKeys:
869 containerName, @"containerName",
870 [objectNames objectAtIndex:i], @"objectName",
871 [contentTypes objectAtIndex:i], @"contentType",
872 [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize",
873 blockHash, @"blockHash",
874 [filePaths objectAtIndex:i], @"filePath",
875 [hashesArrays objectAtIndex:i], @"hashes",
876 [NSNumber numberWithBool:YES], @"refresh",
877 [NSNumber numberWithUnsignedInteger:10], @"iteration",
878 activity, @"activity",
879 [NSNumber numberWithUnsignedInteger:10], @"retries",
881 if (destinationNode.sharingAccount)
882 [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"];
883 [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
894 - (BOOL)moveNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode {
895 if ((([destinationNode class] != [PithosSubdirNode class]) && ([destinationNode class] != [PithosContainerNode class])) ||
896 (([destinationNode class] == [PithosSubdirNode class]) && !destinationNode.pithosObject.subdir && [destinationNode.pithosObject.name hasSuffix:@"/"]))
898 NSString *containerName = [NSString stringWithString:destinationNode.pithosContainer.name];
899 NSString *objectNamePrefix;
900 if ([destinationNode class] == [PithosSubdirNode class])
901 objectNamePrefix = [NSString stringWithString:destinationNode.pithosObject.name];
903 objectNamePrefix = [NSString string];
905 for (PithosNode *node in nodes) {
906 if (([node class] == [PithosObjectNode class]) ||
907 (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
908 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
909 dispatch_async(queue, ^{
910 NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
911 if ([node.pithosObject.name hasSuffix:@"/"])
912 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
913 ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithContainerName:node.pithosContainer.name
914 objectName:node.pithosObject.name
915 destinationContainerName:containerName
916 destinationObjectName:destinationObjectName
919 objectRequest.delegate = self;
920 objectRequest.didFinishSelector = @selector(moveFinished:);
921 objectRequest.didFailSelector = @selector(moveFailed:);
922 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
923 message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
924 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
925 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
926 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
927 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
928 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
929 [NSDictionary dictionaryWithObjectsAndKeys:
930 [NSArray arrayWithObjects:node.parent, destinationNode, nil], @"forceRefreshNodes",
931 activity, @"activity",
932 [NSNumber numberWithUnsignedInteger:10], @"retries",
934 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
937 } else if ([node class] == [PithosSubdirNode class]) {
938 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
939 dispatch_async(queue, ^{
940 NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
941 if (node.pithosObject.subdir)
942 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
943 NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
944 objectName:node.pithosObject.name
945 destinationContainerName:containerName
946 destinationObjectName:destinationObjectName
948 if (objectRequests) {
949 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
950 objectRequest.delegate = self;
951 objectRequest.didFinishSelector = @selector(moveFinished:);
952 objectRequest.didFailSelector = @selector(moveFailed:);
953 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
954 message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
955 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
956 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
957 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
958 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
959 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
960 [NSDictionary dictionaryWithObjectsAndKeys:
961 [NSArray arrayWithObjects:node.parent, destinationNode, nil], @"forceRefreshNodes",
962 [NSNumber numberWithBool:YES], @"refresh",
963 activity, @"activity",
964 [NSNumber numberWithUnsignedInteger:10], @"retries",
966 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
975 - (BOOL)copyNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode {
976 if ((([destinationNode class] != [PithosSubdirNode class]) && ([destinationNode class] != [PithosContainerNode class])) ||
977 (([destinationNode class] == [PithosSubdirNode class]) && !destinationNode.pithosObject.subdir && [destinationNode.pithosObject.name hasSuffix:@"/"]))
979 NSString *containerName = [NSString stringWithString:destinationNode.pithosContainer.name];
980 NSString *objectNamePrefix;
981 if ([destinationNode class] == [PithosSubdirNode class])
982 objectNamePrefix = [NSString stringWithString:destinationNode.pithosObject.name];
984 objectNamePrefix = [NSString string];
986 for (PithosNode *node in nodes) {
987 if (([node class] == [PithosObjectNode class]) ||
988 (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
989 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
990 dispatch_async(queue, ^{
991 NSString *destinationObjectName;
992 if (![destinationNode isEqualTo:node.parent]) {
993 destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
994 if ([node.pithosObject.name hasSuffix:@"/"])
995 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
997 destinationObjectName = [PithosUtilities safeObjectNameForContainerName:containerName
998 objectName:node.pithosObject.name];
1000 ASIPithosObjectRequest *objectRequest = [PithosUtilities copyObjectRequestWithContainerName:node.pithosContainer.name
1001 objectName:node.pithosObject.name
1002 destinationContainerName:containerName
1003 destinationObjectName:destinationObjectName
1005 sharingAccount:node.sharingAccount];
1006 if (objectRequest) {
1007 objectRequest.delegate = self;
1008 objectRequest.didFinishSelector = @selector(copyFinished:);
1009 objectRequest.didFailSelector = @selector(copyFailed:);
1010 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCopy
1011 message:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@'",
1012 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1013 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1014 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1015 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1016 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
1017 [NSDictionary dictionaryWithObjectsAndKeys:
1018 [NSArray arrayWithObject:destinationNode], @"forceRefreshNodes",
1019 activity, @"activity",
1020 [NSNumber numberWithUnsignedInteger:10], @"retries",
1022 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1025 } else if ([node class] == [PithosSubdirNode class]) {
1026 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1027 dispatch_async(queue, ^{
1028 NSString *destinationObjectName;
1029 if (![destinationNode isEqualTo:node.parent]) {
1030 destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
1031 if (node.pithosObject.subdir)
1032 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
1034 destinationObjectName = [PithosUtilities safeSubdirNameForContainerName:containerName
1035 subdirName:node.pithosObject.name];
1037 NSArray *objectRequests = [PithosUtilities copyObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
1038 objectName:node.pithosObject.name
1039 destinationContainerName:containerName
1040 destinationObjectName:destinationObjectName
1042 sharingAccount:node.sharingAccount];
1043 if (objectRequests) {
1044 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
1045 objectRequest.delegate = self;
1046 objectRequest.didFinishSelector = @selector(copyFinished:);
1047 objectRequest.didFailSelector = @selector(copyFailed:);
1048 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCopy
1049 message:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@'",
1050 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1051 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1052 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1053 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1054 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
1055 [NSDictionary dictionaryWithObjectsAndKeys:
1056 [NSArray arrayWithObject:destinationNode], @"forceRefreshNodes",
1057 activity, @"activity",
1058 [NSNumber numberWithUnsignedInteger:10], @"retries",
1060 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1070 #pragma mark ASIHTTPRequestDelegate
1072 - (void)downloadObjectFinished:(ASIPithosObjectRequest *)objectRequest {
1073 NSLog(@"Download finished: %@", objectRequest.url);
1074 if (objectRequest.responseStatusCode == 200) {
1075 NSString *filePath = [objectRequest.userInfo objectForKey:@"filePath"];
1076 PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"];
1077 NSUInteger totalBytes = activity.totalBytes;
1078 NSUInteger currentBytes = activity.currentBytes;
1080 // XXX change contentLength to objectContentLength if it is fixed in the server
1081 if (([objectRequest contentLength] == 0) && (![[objectRequest contentType] isEqualToString:@"application/directory"])) {
1082 NSLog(@"Downloaded 0 bytes");
1083 NSFileManager *defaultManager = [NSFileManager defaultManager];
1084 if (![defaultManager fileExistsAtPath:filePath]) {
1085 if (![defaultManager createFileAtPath:filePath contents:nil attributes:nil]) {
1086 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1087 [alert setMessageText:@"Create File Error"];
1088 [alert setInformativeText:[NSString stringWithFormat:@"Cannot create zero length file at %@", filePath]];
1089 [alert addButtonWithTitle:@"OK"];
1095 currentBytes = [objectRequest objectContentLength];
1096 if (currentBytes == 0)
1097 currentBytes = totalBytes;
1098 [activityFacility endActivity:activity
1099 withMessage:[NSString stringWithFormat:@"Downloading '%@' (100%%)",
1100 [objectRequest.userInfo objectForKey:@"fileName"]]
1101 totalBytes:totalBytes
1102 currentBytes:currentBytes];
1104 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1106 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1107 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1108 [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
1110 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1111 withMessage:[NSString stringWithFormat:@"Downloading '%@' (failed)",
1112 [objectRequest.userInfo objectForKey:@"fileName"]]];
1113 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1118 - (void)downloadObjectFailed:(ASIPithosObjectRequest *)objectRequest {
1119 NSLog(@"Download failed: %@", objectRequest.url);
1120 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1122 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1123 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1124 [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
1126 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1127 withMessage:[NSString stringWithFormat:@"Downloading '%@' (failed)",
1128 [objectRequest.userInfo objectForKey:@"fileName"]]];
1129 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1133 - (void)uploadDirectoryObjectFinished:(ASIPithosObjectRequest *)objectRequest {
1134 NSLog(@"Upload directory object completed: %@", [objectRequest url]);
1135 if (objectRequest.responseStatusCode == 201) {
1136 NSLog(@"Directory object created: %@", [objectRequest url]);
1139 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1143 - (void)uploadDirectoryObjectFailed:(ASIPithosObjectRequest *)objectRequest {
1144 NSLog(@"Upload directory object failed");
1145 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1148 - (void)uploadObjectUsingHashMapFinished:(ASIPithosObjectRequest *)objectRequest {
1149 NSLog(@"Upload using hashmap finished: %@", objectRequest.url);
1150 NSString *fileName = [objectRequest.userInfo objectForKey:@"fileName"];
1151 PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"];
1152 NSUInteger totalBytes = activity.totalBytes;
1153 NSUInteger currentBytes = activity.currentBytes;
1154 if (objectRequest.responseStatusCode == 201) {
1155 NSLog(@"Object created: %@", objectRequest.url);
1156 [activityFacility endActivity:activity
1157 withMessage:[NSString stringWithFormat:@"Uploading '%@' (100%%)", fileName]
1158 totalBytes:totalBytes
1159 currentBytes:totalBytes];
1160 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
1161 [node forceRefresh];
1163 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
1166 if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
1167 [self forceRefresh:self];
1168 else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
1169 [self refresh:self];
1170 } else if (objectRequest.responseStatusCode == 409) {
1171 NSUInteger iteration = [[objectRequest.userInfo objectForKey:@"iteration"] unsignedIntegerValue];
1172 if (iteration == 0) {
1173 NSLog(@"Upload iteration limit reached: %@", objectRequest.url);
1174 [activityFacility endActivity:activity
1175 withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)", fileName]];
1176 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1177 [alert setMessageText:@"Upload Timeout"];
1178 [alert setInformativeText:[NSString stringWithFormat:@"Upload iteration limit reached for object with path '%@'",
1179 [objectRequest.userInfo objectForKey:@"objectName"]]];
1180 [alert addButtonWithTitle:@"OK"];
1184 NSLog(@"object is missing hashes: %@", objectRequest.url);
1185 NSIndexSet *missingBlocks = [PithosUtilities missingBlocksForHashes:[objectRequest.userInfo objectForKey:@"hashes"]
1186 withMissingHashesResponse:[objectRequest responseString]];
1187 NSUInteger blockSize = [[objectRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue];
1188 if (totalBytes >= [missingBlocks count]*blockSize)
1189 currentBytes = totalBytes - [missingBlocks count]*blockSize;
1190 [activityFacility updateActivity:activity
1191 withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (100*(currentBytes + 0.0)/(totalBytes + 0.0))]
1192 totalBytes:totalBytes
1193 currentBytes:currentBytes];
1194 NSUInteger missingBlockIndex = [missingBlocks firstIndex];
1195 __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithContainerName:[objectRequest.userInfo objectForKey:@"containerName"]
1197 forFile:[objectRequest.userInfo objectForKey:@"filePath"]
1198 missingBlockIndex:missingBlockIndex
1199 sharingAccount:[objectRequest.userInfo objectForKey:@"sharingAccount"]];
1200 newContainerRequest.delegate = self;
1201 newContainerRequest.didFinishSelector = @selector(uploadMissingBlockFinished:);
1202 newContainerRequest.didFailSelector = @selector(uploadMissingBlockFailed:);
1203 newContainerRequest.userInfo = objectRequest.userInfo;
1204 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:(--iteration)] forKey:@"iteration"];
1205 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
1206 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:missingBlocks forKey:@"missingBlocks"];
1207 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"];
1208 [newContainerRequest setBytesSentBlock:^(unsigned long long size, unsigned long long total){
1209 [activityFacility updateActivity:activity
1210 withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))]
1211 totalBytes:activity.totalBytes
1212 currentBytes:(activity.currentBytes + size)];
1214 [[PithosUtilities prepareRequest:newContainerRequest] startAsynchronous];
1216 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1218 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1219 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1220 [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
1222 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1223 withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)", fileName]];
1224 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1229 - (void)uploadObjectUsingHashMapFailed:(ASIPithosObjectRequest *)objectRequest {
1230 NSLog(@"Upload using hashmap failed: %@", objectRequest.url);
1231 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1233 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1234 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1235 [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
1237 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1238 withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)",
1239 [objectRequest.userInfo objectForKey:@"fileName"]]];
1240 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1244 - (void)uploadMissingBlockFinished:(ASIPithosContainerRequest *)containerRequest {
1245 NSLog(@"Upload of missing block finished: %@", containerRequest.url);
1246 NSUInteger blockSize = [[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue];
1247 NSString *fileName = [containerRequest.userInfo objectForKey:@"fileName"];
1248 PithosActivity *activity = [containerRequest.userInfo objectForKey:@"activity"];
1249 NSUInteger totalBytes = activity.totalBytes;
1250 NSUInteger currentBytes = activity.currentBytes + blockSize;
1251 if (currentBytes > totalBytes)
1252 currentBytes = totalBytes;
1253 if (containerRequest.responseStatusCode == 202) {
1254 [activityFacility updateActivity:activity
1255 withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (100*(currentBytes + 0.0)/(totalBytes + 0.0))]
1256 totalBytes:totalBytes
1257 currentBytes:currentBytes];
1258 NSIndexSet *missingBlocks = [containerRequest.userInfo objectForKey:@"missingBlocks"];
1259 NSUInteger missingBlockIndex = [[containerRequest.userInfo objectForKey:@"missingBlockIndex"] unsignedIntegerValue];
1260 missingBlockIndex = [missingBlocks indexGreaterThanIndex:missingBlockIndex];
1261 if (missingBlockIndex == NSNotFound) {
1262 NSArray *hashes = [containerRequest.userInfo objectForKey:@"hashes"];
1263 ASIPithosObjectRequest *newObjectRequest = [PithosUtilities writeObjectDataRequestWithContainerName:[containerRequest.userInfo objectForKey:@"containerName"]
1264 objectName:[containerRequest.userInfo objectForKey:@"objectName"]
1265 contentType:[containerRequest.userInfo objectForKey:@"contentType"]
1267 blockHash:[containerRequest.userInfo objectForKey:@"blockHash"]
1268 forFile:[containerRequest.userInfo objectForKey:@"filePath"]
1271 sharingAccount:[containerRequest.userInfo objectForKey:@"sharingAccount"]];
1272 newObjectRequest.delegate = self;
1273 newObjectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
1274 newObjectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
1275 newObjectRequest.userInfo = containerRequest.userInfo;
1276 [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
1277 [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlocks"];
1278 [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlockIndex"];
1279 [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
1281 __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithContainerName:[containerRequest.userInfo objectForKey:@"containerName"]
1282 blockSize:[[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue]
1283 forFile:[containerRequest.userInfo objectForKey:@"filePath"]
1284 missingBlockIndex:missingBlockIndex
1285 sharingAccount:[containerRequest.userInfo objectForKey:@"sharingAccount"]];
1286 newContainerRequest.delegate = self;
1287 newContainerRequest.didFinishSelector = @selector(uploadMissingBlockFinished:);
1288 newContainerRequest.didFailSelector = @selector(uploadMissingBlockFailed:);
1289 newContainerRequest.userInfo = containerRequest.userInfo;
1290 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
1291 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"];
1292 [newContainerRequest setBytesSentBlock:^(unsigned long long size, unsigned long long total){
1293 [activityFacility updateActivity:activity
1294 withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))]
1295 totalBytes:activity.totalBytes
1296 currentBytes:(activity.currentBytes + size)];
1298 [[PithosUtilities prepareRequest:newContainerRequest] startAsynchronous];
1301 NSUInteger retries = [[containerRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1303 ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[PithosUtilities copyRequest:containerRequest];
1304 [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1305 [[PithosUtilities prepareRequest:newContainerRequest] startAsynchronous];
1307 [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"]
1308 withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)", fileName]];
1309 [PithosUtilities unexpectedResponseStatusAlertWithRequest:containerRequest];
1314 - (void)uploadMissingBlockFailed:(ASIPithosContainerRequest *)containerRequest {
1315 NSLog(@"Upload of missing block failed: %@", containerRequest.url);
1316 NSUInteger retries = [[containerRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1318 ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[PithosUtilities copyRequest:containerRequest];
1319 [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1320 [[PithosUtilities prepareRequest:newContainerRequest] startAsynchronous];
1322 [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"]
1323 withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)",
1324 [containerRequest.userInfo objectForKey:@"fileName"]]];
1325 [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
1329 - (void)moveFinished:(ASIPithosObjectRequest *)objectRequest {
1330 NSLog(@"Move object finished: %@", objectRequest.url);
1331 if (objectRequest.responseStatusCode == 201) {
1332 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1333 withMessage:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@' (finished)",
1334 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1335 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1336 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1337 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1338 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
1339 [node forceRefresh];
1341 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
1344 if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
1345 [self forceRefresh:self];
1346 else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
1347 [self refresh:self];
1349 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1351 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1352 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1353 [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1355 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1356 withMessage:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@' (failed)",
1357 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1358 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1359 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1360 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1361 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1366 - (void)moveFailed:(ASIPithosObjectRequest *)objectRequest {
1367 NSLog(@"Move object failed: %@", objectRequest.url);
1368 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1370 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1371 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1372 [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1374 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1375 withMessage:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@' failed",
1376 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1377 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1378 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1379 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1380 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1384 - (void)copyFinished:(ASIPithosObjectRequest *)objectRequest {
1385 NSLog(@"Copy object finished: %@", objectRequest.url);
1386 if (objectRequest.responseStatusCode == 201) {
1387 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1388 withMessage:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@' (finished)",
1389 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1390 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1391 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1392 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1393 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
1394 [node forceRefresh];
1396 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
1399 if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
1400 [self forceRefresh:self];
1401 else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
1402 [self refresh:self];
1404 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1406 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1407 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1408 [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1410 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1411 withMessage:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@' (failed)",
1412 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1413 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1414 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1415 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1416 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1421 - (void)copyFailed:(ASIPithosObjectRequest *)objectRequest {
1422 NSLog(@"Copy object failed: %@", objectRequest.url);
1423 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1425 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1426 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1427 [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1429 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1430 withMessage:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@' failed",
1431 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1432 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1433 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1434 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1435 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1440 #pragma mark NSSplitViewDelegate
1442 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex {
1443 if (splitView == verticalSplitView)
1446 return ([horizontalSplitView bounds].size.height - 108);
1449 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex {
1450 if (splitView == verticalSplitView)
1453 return ([horizontalSplitView bounds].size.height - 108);
1456 - (CGFloat)splitView:(NSSplitView *)splitView constrainSplitPosition:(CGFloat)proposedPosition ofSubviewAt:(NSInteger)dividerIndex {
1457 if (splitView == verticalSplitView) {
1458 if (proposedPosition < 120)
1460 else if (proposedPosition > 220)
1463 return proposedPosition;
1465 return ([horizontalSplitView bounds].size.height - 108);
1470 #pragma mark NSOutlineViewDataSource
1472 - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
1475 if (item == containersNode)
1476 return containersNodeChildren.count;
1477 if (item == sharedNode)
1482 - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
1484 return (!index ? containersNode : sharedNode);
1485 if (item == sharedNode)
1486 return (!index ? mySharedNode : othersSharedNode);
1487 return [containersNodeChildren objectAtIndex:index];
1490 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
1491 if ((item == containersNode) || (item == sharedNode))
1496 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
1497 PithosNode *node = (PithosNode *)item;
1501 #pragma mark Drag and Drop destination
1503 - (NSDragOperation)outlineView:(NSOutlineView *)anOutlineView
1504 validateDrop:(id<NSDraggingInfo>)info
1505 proposedItem:(id)item
1506 proposedChildIndex:(NSInteger)index {
1507 NSDragOperation result = NSDragOperationNone;
1508 if ((item == nil) || (index != NSOutlineViewDropOnItemIndex))
1510 PithosNode *dropNode = (PithosNode *)item;
1511 if ([dropNode class] != [PithosContainerNode class])
1513 if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
1514 result = NSDragOperationCopy;
1515 } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) {
1516 if ([info draggingSourceOperationMask] & NSDragOperationMove) {
1517 // NSDragOperationCopy | NSDragOperationMove -> NSDragOperationMove
1518 if (![dropNode isEqualTo:draggedParentNode])
1519 result = NSDragOperationMove;
1520 } else if ([info draggingSourceOperationMask] & NSDragOperationCopy) {
1521 // NSDragOperationCopy (Option modifier) -> NSDragOperationCopy
1522 result = NSDragOperationCopy;
1528 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id<NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index {
1529 if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
1530 NSArray *filenames = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
1531 NSLog(@"drag in operation: %lu filenames: %@", [info draggingSourceOperationMask], filenames);
1532 if (item && (index == NSOutlineViewDropOnItemIndex) && (filenames != nil)) {
1533 PithosNode *node = (PithosNode *)item;
1534 NSLog(@"drag in node: %@", node.url);
1535 return [self uploadFiles:filenames toNode:node];
1537 } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) {
1538 NSLog(@"drag local operation: %lu nodes: %@", [info draggingSourceOperationMask], draggedNodes);
1539 if (item && (index == NSOutlineViewDropOnItemIndex) && (draggedNodes != nil)) {
1540 PithosNode *node = (PithosNode *)item;
1541 NSLog(@"drag local node: %@", node.url);
1542 if ([info draggingSourceOperationMask] & NSDragOperationMove)
1543 return [self moveNodes:draggedNodes toNode:node];
1544 else if ([info draggingSourceOperationMask] & NSDragOperationCopy)
1545 return [self copyNodes:draggedNodes toNode:node];
1552 #pragma mark NSOutlineViewDelegate
1554 - (BOOL)outlineView:outlineView shouldSelectItem:(id)item {
1555 if ((item == containersNode) || (item == sharedNode))
1560 - (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item {
1561 if ((item == containersNode) || (item == sharedNode))
1566 - (void)outlineViewSelectionDidChange:(NSNotification *)notification {
1567 PithosNode *node = (PithosNode *)[outlineView itemAtRow:[outlineView selectedRow]];
1570 [browser loadColumnZero];
1576 #pragma mark NSMenuDelegate
1578 - (void)menuNeedsUpdate:(NSMenu *)menu {
1579 [menu removeAllItems];
1580 NSMenuItem *menuItem;
1581 NSString *menuItemTitle;
1582 BOOL nodeContextMenu = NO;
1583 PithosNode *menuNode;
1584 NSMutableArray *menuNodes;
1585 if (menu == browserMenu) {
1586 NSInteger column = [browser clickedColumn];
1587 NSInteger row = [browser clickedRow];
1588 if ((column == -1) || (row == -1)) {
1589 // General context menu
1590 NSArray *menuNodesIndexPaths = [browser selectionIndexPaths];
1591 if ([menuNodesIndexPaths count] == 0) {
1592 menuNode = [browser parentForItemsInColumn:0];
1593 } else if (([menuNodesIndexPaths count] != 1) ||
1594 ([[browser itemAtIndexPath:[menuNodesIndexPaths objectAtIndex:0]] class] == [PithosObjectNode class])) {
1595 menuNode = [browser parentForItemsInColumn:([[menuNodesIndexPaths objectAtIndex:0] length] - 1)];
1597 menuNode = [browser itemAtIndexPath:[menuNodesIndexPaths objectAtIndex:0]];
1600 // Node context menu
1601 NSIndexPath *clickedNodeIndexPath = [[browser indexPathForColumn:column] indexPathByAddingIndex:row];
1602 NSArray *menuNodesIndexPaths = [browser selectionIndexPaths];
1603 menuNodes = [NSMutableArray arrayWithCapacity:[menuNodesIndexPaths count]];
1604 if ([menuNodesIndexPaths containsObject:clickedNodeIndexPath]) {
1605 for (NSIndexPath *nodeIndexPath in menuNodesIndexPaths) {
1606 [menuNodes addObject:[browser itemAtIndexPath:nodeIndexPath]];
1609 [menuNodes addObject:[browser itemAtIndexPath:clickedNodeIndexPath]];
1611 nodeContextMenu = YES;
1613 } else if (menu == outlineViewMenu) {
1614 NSInteger row = [outlineView clickedRow];
1616 row = [outlineView selectedRow];
1619 menuNode = [outlineView itemAtRow:row];
1622 if (!nodeContextMenu) {
1623 // General context menu
1624 if (([menuNode class] == [PithosAccountNode class]) ||
1625 ([menuNode class] == [PithosSharingAccountsNode class]) ||
1626 ([menuNode class] == [PithosEmptyNode class]))
1628 BOOL shared = menuNode.shared;
1629 BOOL sharingAccount = (menuNode.sharingAccount != nil);
1631 if (!shared && !sharingAccount) {
1632 menuItem = [[[NSMenuItem alloc] initWithTitle:@"New Folder" action:@selector(menuNewFolder:) keyEquivalent:@""] autorelease];
1633 [menuItem setRepresentedObject:menuNode];
1634 [menu addItem:menuItem];
1635 [menu addItem:[NSMenuItem separatorItem]];
1638 menuItem = [[[NSMenuItem alloc] initWithTitle:@"Get Info" action:@selector(menuGetInfo:) keyEquivalent:@""] autorelease];
1639 [menuItem setRepresentedObject:[NSArray arrayWithObject:menuNode]];
1640 [menu addItem:menuItem];
1642 if (!shared && !sharingAccount) {
1643 if (clipboardNodes) {
1644 NSUInteger clipboardNodesCount = [clipboardNodes count];
1645 if (clipboardNodesCount == 0) {
1646 self.clipboardNodes = nil;
1647 } else if (clipboardCopy || ![menuNode isEqualTo:clipboardParentNode]) {
1648 if (clipboardNodesCount == 1)
1649 menuItemTitle = [NSString stringWithString:@"Paste Item"];
1651 menuItemTitle = [NSString stringWithString:@"Paste Items"];
1652 [menu addItem:[NSMenuItem separatorItem]];
1653 menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuPaste:) keyEquivalent:@""] autorelease];
1654 [menuItem setRepresentedObject:menuNode];
1655 [menu addItem:menuItem];
1660 // Node context menu
1661 NSUInteger menuNodesCount = [menuNodes count];
1662 PithosNode *firstMenuNode = (PithosNode *)[menuNodes objectAtIndex:0];
1663 BOOL shared = firstMenuNode.shared;
1664 BOOL sharingAccount = (firstMenuNode.sharingAccount != nil);
1665 // Move to Trash (pithos container only)
1667 if (!shared && !sharingAccount) {
1668 if ([rootNode class] == [PithosContainerNode class]) {
1669 if ([rootNode.pithosContainer.name isEqualToString:@"pithos"]) {
1670 menuItem = [[[NSMenuItem alloc] initWithTitle:@"Move to Trash" action:@selector(menuMoveToTrash:) keyEquivalent:@""] autorelease];
1671 [menuItem setRepresentedObject:menuNodes];
1672 [menu addItem:menuItem];
1674 menuItem = [[[NSMenuItem alloc] initWithTitle:@"Delete" action:@selector(menuDelete:) keyEquivalent:@""] autorelease];
1675 [menuItem setRepresentedObject:menuNodes];
1676 [menu addItem:menuItem];
1677 [menu addItem:[NSMenuItem separatorItem]];
1681 if (!sharingAccount || ([firstMenuNode class] != [PithosAccountNode class])) {
1682 menuItem = [[[NSMenuItem alloc] initWithTitle:@"Get Info" action:@selector(menuGetInfo:) keyEquivalent:@""] autorelease];
1683 [menuItem setRepresentedObject:menuNodes];
1684 [menu addItem:menuItem];
1686 if ((!shared && !sharingAccount) || ([firstMenuNode class] != [PithosContainerNode class]))
1687 [menu addItem:[NSMenuItem separatorItem]];
1690 if (!shared && !sharingAccount) {
1691 if (menuNodesCount == 1)
1692 menuItemTitle = [NSString stringWithFormat:@"Cut \"%@\"", ((PithosNode *)[menuNodes objectAtIndex:0]).displayName];
1694 menuItemTitle = [NSString stringWithFormat:@"Cut %lu Items", menuNodesCount];
1695 menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuCut:) keyEquivalent:@""] autorelease];
1696 [menuItem setRepresentedObject:menuNodes];
1697 [menu addItem:menuItem];
1700 if ((!shared && !sharingAccount) ||
1701 (([firstMenuNode class] != [PithosContainerNode class]) && ([firstMenuNode class] != [PithosAccountNode class]))) {
1702 if (menuNodesCount == 1)
1703 menuItemTitle = [NSString stringWithFormat:@"Copy \"%@\"", ((PithosNode *)[menuNodes objectAtIndex:0]).displayName];
1705 menuItemTitle = [NSString stringWithFormat:@"Copy %lu Items", menuNodesCount];
1706 menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuCopy:) keyEquivalent:@""] autorelease];
1707 [menuItem setRepresentedObject:menuNodes];
1708 [menu addItem:menuItem];
1711 if (!shared && !sharingAccount) {
1712 if (menuNodesCount == 1) {
1713 PithosNode *menuNode = [menuNodes objectAtIndex:0];
1714 if (([menuNode class] == [PithosSubdirNode class]) &&
1715 (menuNode.pithosObject.subdir || ![menuNode.pithosObject.name hasSuffix:@"/"])) {
1716 if (clipboardNodes) {
1717 NSUInteger clipboardNodesCount = [clipboardNodes count];
1718 if (clipboardNodesCount == 0) {
1719 self.clipboardNodes = nil;
1720 } else if (clipboardCopy || ![menuNode isEqualTo:clipboardParentNode]) {
1721 if (clipboardNodesCount == 1)
1722 menuItemTitle = [NSString stringWithString:@"Paste Item"];
1724 menuItemTitle = [NSString stringWithString:@"Paste Items"];
1725 menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuPaste:) keyEquivalent:@""] autorelease];
1726 [menuItem setRepresentedObject:menuNode];
1727 [menu addItem:menuItem];
1737 #pragma mark Menu Actions
1739 - (void)menuNewFolder:(NSMenuItem *)sender {
1740 PithosNode *node = (PithosNode *)[sender representedObject];
1741 if ([node class] == [PithosContainerNode class]) {
1742 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1743 dispatch_async(queue, ^{
1744 NSString *safeObjectName = [PithosUtilities safeSubdirNameForContainerName:node.pithosContainer.name
1745 subdirName:@"untitled folder"];
1746 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:node.pithosContainer.name
1747 objectName:safeObjectName
1749 contentType:@"application/directory"
1751 contentDisposition:nil
1754 isPublic:ASIPithosObjectRequestPublicIgnore
1756 data:[NSData data]];
1757 objectRequest.delegate = self;
1758 objectRequest.didFinishSelector = @selector(newFolderFinished:);
1759 objectRequest.didFailSelector = @selector(newFolderFailed:);
1760 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1763 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1765 } else if (([node class] == [PithosSubdirNode class]) &&
1766 (node.pithosObject.subdir ||
1767 ![node.pithosObject.name hasSuffix:@"/"])) {
1768 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1769 dispatch_async(queue, ^{
1770 NSString *safeObjectName = [PithosUtilities safeSubdirNameForContainerName:node.pithosContainer.name
1771 subdirName:[node.pithosObject.name stringByAppendingPathComponent:@"untitled folder"]];
1772 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:node.pithosContainer.name
1773 objectName:safeObjectName
1775 contentType:@"application/directory"
1777 contentDisposition:nil
1780 isPublic:ASIPithosObjectRequestPublicIgnore
1782 data:[NSData data]];
1783 objectRequest.delegate = self;
1784 objectRequest.didFinishSelector = @selector(newFolderFinished:);
1785 objectRequest.didFailSelector = @selector(newFolderFailed:);
1786 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1789 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1794 - (void)menuGetInfo:(NSMenuItem *)sender {
1795 for (PithosNode *node in ((NSArray *)[sender representedObject])) {
1796 [node showPithosNodeInfo:sender];
1800 - (void)menuDelete:(NSMenuItem *)sender {
1801 for (PithosNode *node in ((NSArray *)[sender representedObject])) {
1802 if (([node class] == [PithosObjectNode class]) ||
1803 (([node class] == [PithosSubdirNode class]) &&
1804 !node.pithosObject.subdir &&
1805 [node.pithosObject.name hasSuffix:@"/"])) {
1806 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithContainerName:node.pithosContainer.name
1807 objectName:node.pithosObject.name];
1808 objectRequest.delegate = self;
1809 objectRequest.didFinishSelector = @selector(deleteObjectFinished:);
1810 objectRequest.didFailSelector = @selector(deleteObjectFailed:);
1811 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1812 } else if ([node class] == [PithosSubdirNode class]) {
1813 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1814 dispatch_async(queue, ^{
1815 NSArray *objectRequests = [PithosUtilities deleteObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
1816 objectName:node.pithosObject.name];
1817 if (objectRequests) {
1818 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
1819 objectRequest.delegate = self;
1820 objectRequest.didFinishSelector = @selector(deleteObjectFinished:);
1821 objectRequest.didFailSelector = @selector(deleteObjectFailed:);
1822 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1830 - (void)menuMoveToTrash:(NSMenuItem *)sender {
1831 for (PithosNode *node in ((NSArray *)[sender representedObject])) {
1832 if (([node class] == [PithosObjectNode class]) ||
1833 (([node class] == [PithosSubdirNode class]) &&
1834 !node.pithosObject.subdir &&
1835 [node.pithosObject.name hasSuffix:@"/"])) {
1836 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1837 dispatch_async(queue, ^{
1838 NSString *safeObjectName = [PithosUtilities safeObjectNameForContainerName:@"trash"
1839 objectName:node.pithosObject.name];
1840 if (safeObjectName) {
1841 ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithContainerName:node.pithosContainer.name
1842 objectName:node.pithosObject.name
1843 destinationContainerName:@"trash"
1844 destinationObjectName:safeObjectName
1846 if (objectRequest) {
1847 objectRequest.delegate = self;
1848 objectRequest.didFinishSelector = @selector(moveToTrashFinished:);
1849 objectRequest.didFailSelector = @selector(moveToTrashFailed:);
1850 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1854 } else if ([node class] == [PithosSubdirNode class]) {
1855 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1856 dispatch_async(queue, ^{
1857 NSString *safeObjectName = [PithosUtilities safeSubdirNameForContainerName:@"trash"
1858 subdirName:node.pithosObject.name];
1859 if (safeObjectName) {
1860 NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
1861 objectName:node.pithosObject.name
1862 destinationContainerName:@"trash"
1863 destinationObjectName:safeObjectName
1865 if (objectRequests) {
1866 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
1867 objectRequest.delegate = self;
1868 objectRequest.didFinishSelector = @selector(moveToTrashFinished:);
1869 objectRequest.didFailSelector = @selector(moveToTrashFailed:);
1870 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1879 - (void)menuCut:(NSMenuItem *)sender {
1880 self.clipboardNodes = (NSArray *)[sender representedObject];
1881 self.clipboardParentNode = ((PithosNode *)[clipboardNodes objectAtIndex:0]).parent;
1882 self.clipboardCopy = NO;
1885 - (void)menuCopy:(NSMenuItem *)sender {
1886 self.clipboardNodes = (NSArray *)[sender representedObject];
1887 self.clipboardParentNode = ((PithosNode *)[clipboardNodes objectAtIndex:0]).parent;
1888 self.clipboardCopy = YES;
1891 - (void)menuPaste:(NSMenuItem *)sender {
1892 if (!clipboardNodes || ![clipboardNodes count])
1894 PithosNode *dropNode = (PithosNode *)[sender representedObject];
1895 NSArray *localClipboardNodes = [NSArray arrayWithArray:clipboardNodes];
1896 if (!clipboardCopy && ![dropNode isEqualTo:clipboardParentNode]) {
1897 self.clipboardNodes = nil;
1898 self.clipboardParentNode = nil;
1899 [self moveNodes:localClipboardNodes toNode:dropNode];
1901 [self copyNodes:localClipboardNodes toNode:dropNode];
1906 #pragma mark Menu Actions ASIHTTPRequestDelegate
1908 - (void)newFolderFinished:(ASIPithosObjectRequest *)objectRequest {
1909 if (objectRequest.responseStatusCode == 201) {
1910 NSLog(@"New application/directory object created: %@", [objectRequest url]);
1911 PithosNode *node = [objectRequest.userInfo objectForKey:@"node"];
1917 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1921 - (void)newFolderFailed:(ASIPithosObjectRequest *)objectRequest {
1922 NSLog(@"Creation of new application/directory object failed");
1923 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1926 - (void)deleteObjectFinished:(ASIPithosObjectRequest *)objectRequest {
1927 if (objectRequest.responseStatusCode == 204) {
1928 NSLog(@"Object deleted: %@", [objectRequest url]);
1931 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1935 - (void)deleteObjectFailed:(ASIPithosObjectRequest *)objectRequest {
1936 NSLog(@"Delete of object failed");
1937 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1940 - (void)moveToTrashFinished:(ASIPithosObjectRequest *)objectRequest {
1941 if (objectRequest.responseStatusCode == 201) {
1942 NSLog(@"Object moved: %@", [objectRequest url]);
1945 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1949 - (void)moveToTrashFailed:(ASIPithosObjectRequest *)objectRequest {
1950 NSLog(@"Move of object failed");
1951 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1955 #pragma mark PithosActivityFacilityDelegate
1957 - (void)activityUpdate:(NSDictionary *)info {
1958 NSString *message = [info objectForKey:@"message"];
1959 NSUInteger runningActivitiesCount = [[info objectForKey:@"runningActivitiesCount"] unsignedIntegerValue];
1960 // NSUInteger endingActivitiesCount = [[info objectForKey:@"endingActivitiesCount"] unsignedIntegerValue];
1961 NSUInteger totalUploadBytes = [[info objectForKey:@"totalUploadBytes"] unsignedIntegerValue];
1962 NSUInteger currentUploadBytes = [[info objectForKey:@"currentUploadBytes"] unsignedIntegerValue];
1963 NSUInteger totalDownloadBytes = [[info objectForKey:@"totalDownloadBytes"] unsignedIntegerValue];
1964 NSUInteger currentDownloadBytes = [[info objectForKey:@"currentDownloadBytes"] unsignedIntegerValue];
1965 NSUInteger totalBytes = totalUploadBytes + totalDownloadBytes;
1966 if (runningActivitiesCount && totalBytes) {
1967 [activityProgressIndicator setDoubleValue:((currentUploadBytes + currentDownloadBytes + 0.0)/(totalBytes + 0.0))];
1968 [activityProgressIndicator startAnimation:self];
1970 [activityProgressIndicator setDoubleValue:1.0];
1971 [activityProgressIndicator stopAnimation:self];
1975 message = [NSString stringWithFormat:@"%@ used",
1976 [[[[BytesSizeTransformer alloc] init] autorelease] transformedValue:
1977 [NSNumber numberWithUnsignedInteger:accountNode.pithosAccount.bytesUsed]]];
1978 [activityTextField setStringValue:message];