Loading all account containers.
[pithos-macos] / pithos-macos / PithosBrowserController.m
1 //
2 //  PithosBrowserController.m
3 //  pithos-macos
4 //
5 // Copyright 2011 GRNET S.A. All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or
8 // without modification, are permitted provided that the following
9 // conditions are met:
10 // 
11 //   1. Redistributions of source code must retain the above
12 //      copyright notice, this list of conditions and the following
13 //      disclaimer.
14 // 
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.
19 // 
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.
32 // 
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.
37
38 #import "PithosBrowserController.h"
39 #import "PithosNode.h"
40 #import "PithosAccountNode.h"
41 #import "PithosContainerNode.h"
42 #import "PithosEmptyNode.h"
43 #import "ImageAndTextCell.h"
44 #import "FileSystemBrowserCell.h"
45 #import "ASIPithosRequest.h"
46 #import "ASIPithosContainerRequest.h"
47 #import "ASIPithosContainer.h"
48
49 //@interface PithosBrowserCell : NSBrowserCell {}
50 @interface PithosBrowserCell : FileSystemBrowserCell {}
51 @end
52
53 @implementation PithosBrowserCell
54
55 - (id)init {
56     if ((self = [super init])) {
57         [self setLineBreakMode:NSLineBreakByTruncatingMiddle];
58     }
59     return self;
60 }
61
62 - (void)setObjectValue:(id)object {
63     if ([object isKindOfClass:[PithosNode class]]) {
64         PithosNode *node = (PithosNode *)object;
65         [self setStringValue:node.displayName];
66         [self setImage:node.icon];
67 //        // All cells are set as leafs because a branchingImage is already set!
68 //        // Maybe this cell is already inside an NSBrowserCell
69 //        [self setLeaf:YES];
70     } else {
71         [super setObjectValue:object];
72     }
73 }
74
75 @end
76
77 @interface PithosOutlineViewCell : ImageAndTextCell {}
78 @end
79
80 @implementation PithosOutlineViewCell
81
82 - (void)setObjectValue:(id)object {
83     if ([object isKindOfClass:[PithosNode class]]) {
84         PithosNode *node = (PithosNode *)object;
85         [self setStringValue:node.displayName];
86         [self setImage:node.icon];
87         [self setEditable:NO];
88     } else {
89         [super setObjectValue:object];
90     }
91 }
92
93 @end
94
95 @interface PithosBrowserController (Private) {}
96 - (void)resetContainers;
97 @end
98
99 @implementation PithosBrowserController
100 @synthesize outlineViewDataSourceArray, splitView, outlineView, browser;
101
102 #pragma mark -
103 #pragma Object Lifecycle
104
105 - (id)init {
106     return [super initWithWindowNibName:@"PithosBrowserController"];
107 }
108
109 - (void)dealloc {
110     [[NSNotificationCenter defaultCenter] removeObserver:self];
111     [sharedPreviewController release];
112     [outlineViewDataSourceArray release];
113     [accountNode release];
114     [rootNode release];
115     [browser release];
116     [splitView release];
117     [outlineView release];
118     [super dealloc];
119 }
120
121 - (void)awakeFromNib {
122     [browser setCellClass:[PithosBrowserCell class]];
123 }
124
125 - (void)resetContainers {
126     rootNode = nil;
127     [browser loadColumnZero];
128     self.outlineViewDataSourceArray = nil;
129     
130     // Create the outlineView tree
131     // CONTAINERS
132         NSTreeNode *containersTreeNode = [NSTreeNode treeNodeWithRepresentedObject:
133                             [[[PithosEmptyNode alloc] initWithDisplayName:@"CONTAINERS" icon:nil] autorelease]];
134 //    // CONTAINERS/pithos
135 //      [[containersTreeNode mutableChildNodes] addObject:
136 //     [NSTreeNode treeNodeWithRepresentedObject:
137 //      [[[PithosContainerNode alloc] initWithContainerName:@"pithos" 
138 //                                                     icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kToolbarHomeIcon)]
139 //        ] autorelease]]];
140 //    // CONTAINERS/trash
141 //      [[containersTreeNode mutableChildNodes] addObject:
142 //     [NSTreeNode treeNodeWithRepresentedObject:
143 //      [[[PithosContainerNode alloc] initWithContainerName:@"trash"
144 //                                                     icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kFullTrashIcon)]
145 //        ] autorelease]]];
146     // SHARED
147         NSTreeNode *sharedTreeNode = [NSTreeNode treeNodeWithRepresentedObject:
148                                       [[[PithosEmptyNode alloc] initWithDisplayName:@"SHARED" icon:nil] autorelease]];
149     // SHARED/my shared
150         [[sharedTreeNode mutableChildNodes] addObject:
151      [NSTreeNode treeNodeWithRepresentedObject:
152       [[[PithosEmptyNode alloc] initWithDisplayName:@"my shared" 
153                                                icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kUserIcon)]
154         ] autorelease]]];
155     // SHARED/others shared
156         [[sharedTreeNode mutableChildNodes] addObject:
157      [NSTreeNode treeNodeWithRepresentedObject:
158       [[[PithosEmptyNode alloc] initWithDisplayName:@"others shared"
159                                                icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGroupIcon)]
160         ] autorelease]]];
161     
162     self.outlineViewDataSourceArray = [NSMutableArray arrayWithObjects:containersTreeNode, sharedTreeNode, nil];
163     
164         // Expand the folder outline view
165     [outlineView expandItem:nil expandChildren:YES];
166         [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
167     
168     // Create accountNode and trigger a refresh
169     accountNode = [[PithosAccountNode alloc] init];
170     accountNode.children;
171 }
172
173 - (void)windowDidLoad {
174     [[[outlineView tableColumns] objectAtIndex:0] setDataCell:[[[PithosOutlineViewCell alloc] init] autorelease]];
175     
176     // Register for updates
177     [[NSNotificationCenter defaultCenter] addObserver:self 
178                                              selector:@selector(pithosNodeChildrenUpdated:) 
179                                                  name:@"PithosContainerNodeChildrenUpdated" 
180                                                object:nil];
181     [[NSNotificationCenter defaultCenter] addObserver:self 
182                                              selector:@selector(pithosNodeChildrenUpdated:) 
183                                                  name:@"PithosSubdirNodeChildrenUpdated" 
184                                                object:nil];
185     [[NSNotificationCenter defaultCenter] addObserver:self 
186                                              selector:@selector(pithosAccountNodeChildrenUpdated:) 
187                                                  name:@"PithosAccountNodeChildrenUpdated" 
188                                                object:nil];
189     [[NSNotificationCenter defaultCenter] addObserver:self 
190                                              selector:@selector(resetContainers) 
191                                                  name:@"PithosAuthenticationCredentialsUpdated" 
192                                                object:nil];
193 }
194
195 #pragma mark -
196 #pragma Observers
197
198 - (void)pithosNodeChildrenUpdated:(NSNotification *)notification {
199     PithosNode *node = (PithosNode *)[notification object];
200     NSInteger lastColumn = [browser lastColumn];
201     for (NSInteger column = lastColumn; column >= 0; column--) {
202         if ([[browser parentForItemsInColumn:column] isEqualTo:node]) {
203             [browser reloadColumn:column];
204             if ((column == lastColumn - 1) && ([[browser parentForItemsInColumn:lastColumn] isLeafItem])) {
205                 // This reloads the preview column
206                 [browser setLastColumn:column];
207                 [browser addColumn];
208             }
209             return;
210         }
211     }
212 }
213
214 - (void)pithosAccountNodeChildrenUpdated:(NSNotification *)notification {
215     BOOL containerPithosFound = NO;
216     BOOL containerTrashFound = NO;
217     //NSMutableArray *containersTreeNodeChildren = [[outlineViewDataSourceArray objectAtIndex:0] mutableChildNodes];
218     NSMutableArray *containersTreeNodeChildren = [NSMutableArray array];
219     for (PithosContainerNode *containerNode in accountNode.children) {
220         if ([containerNode.pithosContainer.name isEqualToString:@"pithos"]) {
221             containerNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kToolbarHomeIcon)];
222             [containersTreeNodeChildren insertObject:[NSTreeNode treeNodeWithRepresentedObject:containerNode] atIndex:0];
223             containerPithosFound = YES;
224         } else if ([containerNode.pithosContainer.name isEqualToString:@"trash"]) {
225             containerNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kFullTrashIcon)];            
226             NSUInteger insertIndex = 1;
227             if (!containerPithosFound)
228                 insertIndex = 0;
229             [containersTreeNodeChildren insertObject:[NSTreeNode treeNodeWithRepresentedObject:containerNode] atIndex:insertIndex];
230             containerTrashFound = YES;
231         } else {
232             [containersTreeNodeChildren addObject:[NSTreeNode treeNodeWithRepresentedObject:containerNode]];
233         }
234     }
235     BOOL refreshAccountNode = NO;
236     if (!containerPithosFound) {
237         // create pithos
238         ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithContainerName:@"pithos"];
239         [containerRequest startSynchronous];
240         if ([containerRequest error]) {
241             NSLog(@"error:%@", [containerRequest error]);
242             // XXX do something on error
243         } else {
244             refreshAccountNode = YES;
245         }
246     }
247     if (!containerTrashFound) {
248         // create trash
249         ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithContainerName:@"trash"];
250         [containerRequest startSynchronous];
251         if ([containerRequest error]) {
252             NSLog(@"error:%@", [containerRequest error]);
253             // XXX do something on error
254         } else {
255             refreshAccountNode = YES;
256         }
257     }
258     if (refreshAccountNode) {
259         [accountNode invalidateChildren];
260         accountNode.children;
261     } else {
262         [[[outlineViewDataSourceArray objectAtIndex:0] mutableChildNodes] setArray:containersTreeNodeChildren];
263         self.outlineViewDataSourceArray = outlineViewDataSourceArray;
264         
265         // Expand the folder outline view
266         [outlineView expandItem:nil expandChildren:YES];
267         [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
268     }
269 }
270
271
272 #pragma mark -
273 #pragma Actions
274
275 - (IBAction)refresh:(id)sender {
276     for (NSInteger column = [browser lastColumn]; column >= 0; column--) {
277         [(PithosNode *)[browser parentForItemsInColumn:column] invalidateChildren];
278     }
279     [browser validateVisibleColumns];
280 }
281
282 #pragma mark -
283 #pragma NSBrowserDelegate
284
285 - (id)rootItemForBrowser:(NSBrowser *)browser {
286     return rootNode;    
287 }
288
289 - (NSInteger)browser:(NSBrowser *)browser numberOfChildrenOfItem:(id)item {
290     PithosNode *node = (PithosNode *)item;
291     return node.children.count;
292 }
293
294 - (id)browser:(NSBrowser *)browser child:(NSInteger)index ofItem:(id)item {
295     PithosNode *node = (PithosNode *)item;
296     return [node.children objectAtIndex:index];
297 }
298
299 - (BOOL)browser:(NSBrowser *)browser isLeafItem:(id)item {
300     PithosNode *node = (PithosNode *)item;
301     return node.isLeafItem;
302 }
303
304 - (id)browser:(NSBrowser *)browser objectValueForItem:(id)item {
305     PithosNode *node = (PithosNode *)item;
306     return node;
307 }
308
309 - (NSViewController *)browser:(NSBrowser *)browser previewViewControllerForLeafItem:(id)item {
310     if (sharedPreviewController == nil)
311         sharedPreviewController = [[NSViewController alloc] initWithNibName:@"PithosBrowserPreviewController" bundle:[NSBundle bundleForClass:[self class]]];
312     return sharedPreviewController;
313 }
314
315 //- (CGFloat)browser:(NSBrowser *)browser shouldSizeColumn:(NSInteger)columnIndex forUserResize:(BOOL)forUserResize toWidth:(CGFloat)suggestedWidth  {
316 //    if (!forUserResize) {
317 //        id item = [browser parentForItemsInColumn:columnIndex]; 
318 //        if ([self browser:browser isLeafItem:item]) {
319 //            suggestedWidth = 200; 
320 //        }
321 //    }
322 //    return suggestedWidth;
323 //}
324
325 - (BOOL)browser:(NSBrowser *)sender isColumnValid:(NSInteger)column {
326     return NO;
327 }
328
329 #pragma mark -
330 #pragma NSSplitViewDelegate
331
332 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex {
333     return 120;
334 }
335
336 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex {
337     return 220;
338 }
339
340 #pragma mark -
341 #pragma mark NSOutlineViewDelegate
342
343 - (BOOL)outlineView:outlineView shouldSelectItem:(id)item {
344     return ([[item representedObject] isLeaf]);
345 }
346
347 - (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item {
348         return (![[item representedObject] isLeaf]);
349 }
350
351 - (void)outlineViewSelectionDidChange:(NSNotification *)notification {
352     PithosNode *node = [[[outlineView itemAtRow:[outlineView selectedRow]] representedObject] representedObject];
353     if (node) {
354         rootNode = node;
355         [browser loadColumnZero];
356     }
357 }
358
359 @end