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 "PithosEmptyNode.h"
45 #import "ImageAndTextCell.h"
46 #import "FileSystemBrowserCell.h"
47 #import "ASIPithosRequest.h"
48 #import "ASIPithosContainerRequest.h"
49 #import "ASIPithosObjectRequest.h"
50 #import "ASIPithosContainer.h"
51 #import "ASIPithosObject.h"
52 #import "PithosFileUtilities.h"
54 //@interface PithosBrowserCell : NSBrowserCell {}
55 @interface PithosBrowserCell : FileSystemBrowserCell {}
58 @implementation PithosBrowserCell
61 if ((self = [super init])) {
62 [self setLineBreakMode:NSLineBreakByTruncatingMiddle];
67 - (void)setObjectValue:(id)object {
68 if ([object isKindOfClass:[PithosNode class]]) {
69 PithosNode *node = (PithosNode *)object;
70 [self setStringValue:node.displayName];
71 [self setImage:node.icon];
72 // // All cells are set as leafs because a branchingImage is already set!
73 // // Maybe this cell is already inside an NSBrowserCell
74 // [self setLeaf:YES];
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;
102 - (void)getInfo:(NSMenuItem *)sender;
103 - (void)uploadObjectUsingHashMapFinished:(ASIPithosObjectRequest *)objectRequest;
104 - (void)uploadObjectUsingHashMapFailed:(ASIPithosObjectRequest *)objectRequest;
105 - (void)uploadMissingHashesFinished:(ASIPithosObjectRequest *)objectRequest;
106 - (void)uploadMissingHashesFailed:(ASIPithosObjectRequest *)objectRequest;
109 @implementation PithosBrowserController
110 @synthesize outlineViewDataSourceArray, splitView, outlineView, browser;
113 #pragma Object Lifecycle
116 return [super initWithWindowNibName:@"PithosBrowserController"];
120 [[NSNotificationCenter defaultCenter] removeObserver:self];
121 [browserMenu release];
122 [sharedPreviewController release];
123 [outlineViewDataSourceArray release];
124 [accountNode release];
129 - (void)awakeFromNib {
130 [super awakeFromNib];
132 [browser registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
133 [browser setDraggingSourceOperationMask:NSDragOperationNone forLocal:YES];
134 [browser setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
136 [browser setCellClass:[PithosBrowserCell class]];
138 browserMenu = [[NSMenu alloc] init];
139 [browserMenu setDelegate:self];
140 [browser setMenu:browserMenu];
143 - (void)resetContainers {
145 [browser loadColumnZero];
146 self.outlineViewDataSourceArray = nil;
148 // Create the outlineView tree
150 NSTreeNode *containersTreeNode = [NSTreeNode treeNodeWithRepresentedObject:
151 [[[PithosEmptyNode alloc] initWithDisplayName:@"CONTAINERS" icon:nil] autorelease]];
152 // // CONTAINERS/pithos
153 // [[containersTreeNode mutableChildNodes] addObject:
154 // [NSTreeNode treeNodeWithRepresentedObject:
155 // [[[PithosContainerNode alloc] initWithContainerName:@"pithos"
156 // icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kToolbarHomeIcon)]
158 // // CONTAINERS/trash
159 // [[containersTreeNode mutableChildNodes] addObject:
160 // [NSTreeNode treeNodeWithRepresentedObject:
161 // [[[PithosContainerNode alloc] initWithContainerName:@"trash"
162 // icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kFullTrashIcon)]
165 NSTreeNode *sharedTreeNode = [NSTreeNode treeNodeWithRepresentedObject:
166 [[[PithosEmptyNode alloc] initWithDisplayName:@"SHARED" icon:nil] autorelease]];
168 [[sharedTreeNode mutableChildNodes] addObject:
169 [NSTreeNode treeNodeWithRepresentedObject:
170 [[[PithosEmptyNode alloc] initWithDisplayName:@"my shared"
171 icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kUserIcon)]
173 // SHARED/others shared
174 [[sharedTreeNode mutableChildNodes] addObject:
175 [NSTreeNode treeNodeWithRepresentedObject:
176 [[[PithosEmptyNode alloc] initWithDisplayName:@"others shared"
177 icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGroupIcon)]
180 self.outlineViewDataSourceArray = [NSMutableArray arrayWithObjects:containersTreeNode, sharedTreeNode, nil];
182 // Expand the folder outline view
183 [outlineView expandItem:nil expandChildren:YES];
184 [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
186 // Create accountNode and trigger a refresh
187 accountNode = [[PithosAccountNode alloc] init];
188 accountNode.children;
191 - (void)windowDidLoad {
192 [super windowDidLoad];
194 [[[outlineView tableColumns] objectAtIndex:0] setDataCell:[[[PithosOutlineViewCell alloc] init] autorelease]];
196 // Register for updates
197 [[NSNotificationCenter defaultCenter] addObserver:self
198 selector:@selector(pithosNodeChildrenUpdated:)
199 name:@"PithosContainerNodeChildrenUpdated"
201 [[NSNotificationCenter defaultCenter] addObserver:self
202 selector:@selector(pithosNodeChildrenUpdated:)
203 name:@"PithosSubdirNodeChildrenUpdated"
205 [[NSNotificationCenter defaultCenter] addObserver:self
206 selector:@selector(pithosAccountNodeChildrenUpdated:)
207 name:@"PithosAccountNodeChildrenUpdated"
209 [[NSNotificationCenter defaultCenter] addObserver:self
210 selector:@selector(resetContainers)
211 name:@"PithosAuthenticationCredentialsUpdated"
218 - (void)pithosNodeChildrenUpdated:(NSNotification *)notification {
219 PithosNode *node = (PithosNode *)[notification object];
220 NSInteger lastColumn = [browser lastColumn];
221 for (NSInteger column = lastColumn; column >= 0; column--) {
222 if ([[browser parentForItemsInColumn:column] isEqualTo:node]) {
223 [browser reloadColumn:column];
224 if ((column == lastColumn - 1) && ([[browser parentForItemsInColumn:lastColumn] isLeafItem])) {
225 // This reloads the preview column
226 [browser setLastColumn:column];
234 - (void)pithosAccountNodeChildrenUpdated:(NSNotification *)notification {
235 BOOL containerPithosFound = NO;
236 BOOL containerTrashFound = NO;
237 //NSMutableArray *containersTreeNodeChildren = [[outlineViewDataSourceArray objectAtIndex:0] mutableChildNodes];
238 NSMutableArray *containersTreeNodeChildren = [NSMutableArray array];
239 for (PithosContainerNode *containerNode in accountNode.children) {
240 if ([containerNode.pithosContainer.name isEqualToString:@"pithos"]) {
241 containerNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kToolbarHomeIcon)];
242 [containersTreeNodeChildren insertObject:[NSTreeNode treeNodeWithRepresentedObject:containerNode] atIndex:0];
243 containerPithosFound = YES;
244 } else if ([containerNode.pithosContainer.name isEqualToString:@"trash"]) {
245 containerNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kFullTrashIcon)];
246 NSUInteger insertIndex = 1;
247 if (!containerPithosFound)
249 [containersTreeNodeChildren insertObject:[NSTreeNode treeNodeWithRepresentedObject:containerNode] atIndex:insertIndex];
250 containerTrashFound = YES;
252 [containersTreeNodeChildren addObject:[NSTreeNode treeNodeWithRepresentedObject:containerNode]];
255 BOOL refreshAccountNode = NO;
256 if (!containerPithosFound) {
258 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithContainerName:@"pithos"];
259 [containerRequest startSynchronous];
260 if ([containerRequest error]) {
261 NSLog(@"error:%@", [containerRequest error]);
262 // XXX do something on error
264 refreshAccountNode = YES;
267 if (!containerTrashFound) {
269 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithContainerName:@"trash"];
270 [containerRequest startSynchronous];
271 if ([containerRequest error]) {
272 NSLog(@"error:%@", [containerRequest error]);
273 // XXX do something on error
275 refreshAccountNode = YES;
278 if (refreshAccountNode) {
279 [accountNode invalidateChildren];
280 accountNode.children;
282 [[[outlineViewDataSourceArray objectAtIndex:0] mutableChildNodes] setArray:containersTreeNodeChildren];
283 self.outlineViewDataSourceArray = outlineViewDataSourceArray;
285 // Expand the folder outline view
286 [outlineView expandItem:nil expandChildren:YES];
287 [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
296 - (IBAction)refresh:(id)sender {
297 for (NSInteger column = [browser lastColumn]; column >= 0; column--) {
298 [(PithosNode *)[browser parentForItemsInColumn:column] invalidateChildren];
300 [browser validateVisibleColumns];
304 #pragma NSBrowserDelegate
306 - (id)rootItemForBrowser:(NSBrowser *)browser {
310 - (NSInteger)browser:(NSBrowser *)browser numberOfChildrenOfItem:(id)item {
311 PithosNode *node = (PithosNode *)item;
312 return node.children.count;
315 - (id)browser:(NSBrowser *)browser child:(NSInteger)index ofItem:(id)item {
316 PithosNode *node = (PithosNode *)item;
317 return [node.children objectAtIndex:index];
320 - (BOOL)browser:(NSBrowser *)browser isLeafItem:(id)item {
321 PithosNode *node = (PithosNode *)item;
322 return node.isLeafItem;
325 - (id)browser:(NSBrowser *)browser objectValueForItem:(id)item {
326 PithosNode *node = (PithosNode *)item;
330 - (NSViewController *)browser:(NSBrowser *)browser previewViewControllerForLeafItem:(id)item {
331 if (sharedPreviewController == nil)
332 sharedPreviewController = [[NSViewController alloc] initWithNibName:@"PithosBrowserPreviewController" bundle:[NSBundle bundleForClass:[self class]]];
333 return sharedPreviewController;
336 //- (CGFloat)browser:(NSBrowser *)browser shouldSizeColumn:(NSInteger)columnIndex forUserResize:(BOOL)forUserResize toWidth:(CGFloat)suggestedWidth {
337 // if (!forUserResize) {
338 // id item = [browser parentForItemsInColumn:columnIndex];
339 // if ([self browser:browser isLeafItem:item]) {
340 // suggestedWidth = 200;
343 // return suggestedWidth;
346 - (BOOL)browser:(NSBrowser *)sender isColumnValid:(NSInteger)column {
350 #pragma mark Drag and Drop source
352 - (BOOL)browser:(NSBrowser *)aBrowser writeRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column
353 toPasteboard:(NSPasteboard *)pasteboard {
354 NSMutableArray *propertyList = [NSMutableArray arrayWithCapacity:[rowIndexes count]];
355 NSIndexPath *baseIndexPath = [browser indexPathForColumn:column];
356 for (NSUInteger i = [rowIndexes firstIndex]; i <= [rowIndexes lastIndex]; i = [rowIndexes indexGreaterThanIndex:i]) {
357 PithosNode *node = [browser itemAtIndexPath:[baseIndexPath indexPathByAddingIndex:i]];
358 [propertyList addObject:[node.pithosObject.name pathExtension]];
361 [pasteboard declareTypes:[NSArray arrayWithObject:NSFilesPromisePboardType] owner:self];
362 [pasteboard setPropertyList:propertyList forType:NSFilesPromisePboardType];
367 - (NSArray *)browser:(NSBrowser *)aBrowser namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
368 forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
369 NSMutableArray *names = [NSMutableArray arrayWithCapacity:[rowIndexes count]];
370 NSIndexPath *baseIndexPath = [browser indexPathForColumn:column];
371 for (NSUInteger i = [rowIndexes firstIndex]; i <= [rowIndexes lastIndex]; i = [rowIndexes indexGreaterThanIndex:i]) {
372 PithosNode *node = [browser itemAtIndexPath:[baseIndexPath indexPathByAddingIndex:i]];
374 // If the node is a subdir ask if the whole tree should be downloaded
375 if (node.pithosObject.subdir) {
376 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
377 [alert setMessageText:@"Download directory"];
378 [alert setInformativeText:[NSString stringWithFormat:@"'%@' is a directory, do you want to download its contents?", node.displayName]];
379 [alert addButtonWithTitle:@"OK"];
380 [alert addButtonWithTitle:@"Cancel"];
381 NSInteger choice = [alert runModal];
382 if (choice == NSAlertFirstButtonReturn) {
383 NSArray *objectRequests = [PithosFileUtilities objectDataRequestsForSubdirWithContainerName:node.pithosContainer.name
384 objectName:node.pithosObject.name
385 toDirectory:[dropDestination path]
387 if (objectRequests) {
388 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
389 [names addObject:[objectRequest.userInfo valueForKey:@"fileName"]];
390 // XXX set delegates and queue
391 [objectRequest setCompletionBlock:^{
392 NSLog(@"dl completed: %@", [objectRequest url]);
394 [objectRequest setFailedBlock:^{
395 NSLog(@"dl failed: %@, error: %@", [objectRequest url], [objectRequest error]);
397 [objectRequest startAsynchronous];
402 ASIPithosObjectRequest *objectRequest = [PithosFileUtilities objectDataRequestWithContainerName:node.pithosContainer.name
403 objectName:node.pithosObject.name
404 toDirectory:[dropDestination path]
407 [names addObject:[objectRequest.userInfo valueForKey:@"fileName"]];
408 // XXX set delegates and queue
409 [objectRequest setCompletionBlock:^{
410 NSLog(@"dl completed: %@", [objectRequest url]);
412 [objectRequest setFailedBlock:^{
413 NSLog(@"dl failed: %@, error: %@", [objectRequest url], [objectRequest error]);
415 [objectRequest startAsynchronous];
422 #pragma mark Drag and Drop destination
424 - (NSDragOperation)browser:aBrowser
425 validateDrop:(id<NSDraggingInfo>)info
426 proposedRow:(NSInteger *)row
427 column:(NSInteger *)column
428 dropOperation:(NSBrowserDropOperation *)dropOperation {
429 NSDragOperation result = NSDragOperationNone;
430 // Files from the finder are accepted
431 if ([[[info draggingPasteboard] types] indexOfObject:NSFilenamesPboardType] != -1) {
432 // For a between drop, we let the user drop "on" the parent item
433 if (*dropOperation == NSBrowserDropAbove)
435 // Only allow dropping in folders
438 PithosNode *node = [browser itemAtRow:*row inColumn:*column];
439 if ([node class] != [PithosSubdirNode class])
441 *dropOperation = NSBrowserDropOn;
442 result = NSDragOperationCopy;
446 // XXX else local file promises
450 - (BOOL)browser:(NSBrowser *)aBrowser
451 acceptDrop:(id<NSDraggingInfo>)info
453 column:(NSInteger)column
454 dropOperation:(NSBrowserDropOperation)dropOperation {
455 NSArray *filenames = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
456 NSLog(@"drag in filenames: %@", filenames);
457 PithosNode *node = nil;
458 if ((column != -1) && (filenames != nil)) {
460 node = [browser itemAtRow:row inColumn:column];
462 node = [browser parentForItemsInColumn:column];
463 NSLog(@"drag in node: %@", node.url);
464 if (([node class] != [PithosSubdirNode class]) && ([node class] != [PithosContainerNode class]))
467 NSFileManager *defaultManager = [NSFileManager defaultManager];
468 NSString *containerName = [NSString stringWithString:node.pithosContainer.name];
469 NSString *objectNamePrefix;
470 if ([node class] == [PithosSubdirNode class])
471 objectNamePrefix = [NSString stringWithString:node.pithosObject.name];
473 objectNamePrefix = [NSString stringWithString:@""];
474 NSUInteger blockSize = node.pithosContainer.blockSize;
475 NSString *blockHash = node.pithosContainer.blockHash;
477 for (NSString *filePath in filenames) {
479 if ([defaultManager fileExistsAtPath:filePath isDirectory:&isDirectory]) {
482 NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
484 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
485 dispatch_async(queue, ^{
486 NSError *error = nil;
487 NSURLResponse *response = nil;
488 [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:filePath]
489 cachePolicy:NSURLCacheStorageNotAllowed
491 returningResponse:&response
493 NSString *contentType = [response MIMEType];
494 if (contentType == nil)
495 contentType = @"application/binary";
497 NSLog(@"contentType detection error: %@", error);
498 NSArray *hashes = nil;
499 ASIPithosObjectRequest *objectRequest = [PithosFileUtilities writeObjectDataRequestWithContainerName:containerName
500 objectName:objectName
501 contentType:contentType
508 // XXX set delegates and queue
509 objectRequest.delegate = self;
510 objectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
511 objectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
512 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
513 containerName, @"containerName",
514 objectName, @"objectName",
515 contentType, @"contentType",
516 [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize",
517 blockHash, @"blockHash",
518 filePath, @"filePath",
520 [NSNumber numberWithBool:YES], @"checkIfExists",
522 [NSNumber numberWithUnsignedInteger:0], @"iteration",
524 [objectRequest startAsynchronous];
526 // XXX else show alert?
529 // Upload directory, confirm first
530 // XXX implement this
542 #pragma mark ASIHTTPRequestDelegate
544 - (void)uploadObjectUsingHashMapFinished:(ASIPithosObjectRequest *)objectRequest {
545 NSLog(@"upload using hashmap completed: %@", [objectRequest url]);
546 if (objectRequest.responseStatusCode == 201) {
547 NSLog(@"object created: %@", [objectRequest url]);
548 PithosNode *node = [objectRequest.userInfo objectForKey:@"node"];
549 [node invalidateChildren];
551 } else if (objectRequest.responseStatusCode == 409) {
552 NSUInteger iteration = [[objectRequest.userInfo objectForKey:@"iteration"] unsignedIntegerValue] + 1;
553 if (iteration > 10) {
554 NSLog(@"upload iteration limit reached: %@", [objectRequest url]);
558 NSLog(@"object is missing hashes: %@", [objectRequest url]);
559 ASIPithosObjectRequest *newObjectRequest = [PithosFileUtilities updateObjectDataRequestWithContainerName:[objectRequest.userInfo objectForKey:@"containerName"]
560 objectName:@".upload"
561 contentType:[objectRequest.userInfo objectForKey:@"contentType"]
562 blockSize:[[objectRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue]
563 forFile:[objectRequest.userInfo objectForKey:@"filePath"]
564 hashes:[objectRequest.userInfo objectForKey:@"hashes"]
565 missingHashesResponse:[objectRequest responseString]
566 checkIfExists:[[objectRequest.userInfo objectForKey:@"checkIfExists"] boolValue]];
567 newObjectRequest.shouldAttemptPersistentConnection = NO;
568 newObjectRequest.delegate = self;
569 newObjectRequest.didFinishSelector = @selector(uploadMissingHashesFinished:);
570 newObjectRequest.didFailSelector = @selector(uploadMissingHashesFailed:);
571 newObjectRequest.userInfo = objectRequest.userInfo;
572 [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithBool:NO] forKey:@"checkIfExists"];
573 [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:iteration] forKey:@"iteration"];
574 [newObjectRequest startAsynchronous];
576 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
577 [alert setMessageText:@"Unexpected Response Status"];
578 [alert setInformativeText:[NSString stringWithFormat:@"Unexpected response status %d - %@", objectRequest.responseStatusCode, objectRequest.responseStatusMessage]];
579 [alert addButtonWithTitle:@"OK"];
584 - (void)uploadObjectUsingHashMapFailed:(ASIPithosObjectRequest *)objectRequest {
585 NSLog(@"upload failed: %@, error: %@", [objectRequest url], [objectRequest error]);
586 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
587 [alert setMessageText:@"HTTP Request Error"];
588 [alert setInformativeText:[NSString stringWithFormat:@"An error occured: %@", [objectRequest error]]];
589 [alert addButtonWithTitle:@"OK"];
593 - (void)uploadMissingHashesFinished:(ASIPithosObjectRequest *)objectRequest {
594 NSLog(@"upload of missing hashes completed: %@", [objectRequest url]);
595 if ((objectRequest.responseStatusCode == 201) || (objectRequest.responseStatusCode == 204)) {
596 NSArray *hashes = [objectRequest.userInfo objectForKey:@"hashes"];
597 ASIPithosObjectRequest *newObjectRequest = [PithosFileUtilities writeObjectDataRequestWithContainerName:[objectRequest.userInfo objectForKey:@"containerName"]
598 objectName:[objectRequest.userInfo objectForKey:@"objectName"]
599 contentType:[objectRequest.userInfo objectForKey:@"contentType"]
600 blockSize:[[objectRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue]
601 blockHash:[objectRequest.userInfo objectForKey:@"blockHash"]
602 forFile:[objectRequest.userInfo objectForKey:@"filePath"]
605 newObjectRequest.delegate = self;
606 newObjectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
607 newObjectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
608 newObjectRequest.userInfo = objectRequest.userInfo;
609 [newObjectRequest startAsynchronous];
611 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
612 [alert setMessageText:@"Unexpected Response Status"];
613 [alert setInformativeText:[NSString stringWithFormat:@"Unexpected response status %d - %@", objectRequest.responseStatusCode, objectRequest.responseStatusMessage]];
614 [alert addButtonWithTitle:@"OK"];
619 - (void)uploadMissingHashesFailed:(ASIPithosObjectRequest *)objectRequest {
620 NSLog(@"upload of missing hashes failed: %@, error: %@", [objectRequest url], [objectRequest error]);
621 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
622 [alert setMessageText:@"HTTP Request Error"];
623 [alert setInformativeText:[NSString stringWithFormat:@"An error occured: %@", [objectRequest error]]];
624 [alert addButtonWithTitle:@"OK"];
629 #pragma mark NSSplitViewDelegate
631 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex {
635 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex {
640 #pragma mark NSOutlineViewDelegate
642 - (BOOL)outlineView:outlineView shouldSelectItem:(id)item {
643 return ([[item representedObject] isLeaf]);
646 - (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item {
647 return (![[item representedObject] isLeaf]);
650 - (void)outlineViewSelectionDidChange:(NSNotification *)notification {
651 PithosNode *node = [[[outlineView itemAtRow:[outlineView selectedRow]] representedObject] representedObject];
654 [browser loadColumnZero];
659 #pragma mark NSMenuDelegate
661 - (void)menuNeedsUpdate:(NSMenu *)menu {
662 NSInteger column = [browser clickedColumn];
663 NSInteger row = [browser clickedRow];
664 [menu removeAllItems];
665 if ((column == -1) || (row == -1)) {
666 // General context menu has 0
668 // PithosNode menu has 1 items
670 NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:@"Get Info" action:@selector(getInfo:) keyEquivalent:@""];
671 [menuItem setRepresentedObject:[browser itemAtRow:row inColumn:column]];
672 [menu addItem:menuItem];
677 #pragma mark Menu Actions
679 - (void)getInfo:(NSMenuItem *)sender {
680 [(PithosNode *)[sender representedObject] showPithosNodeInfo:sender];