Initial implementation of object info window.
[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     [super windowDidLoad];
175     
176     [[[outlineView tableColumns] objectAtIndex:0] setDataCell:[[[PithosOutlineViewCell alloc] init] autorelease]];
177     
178     // Register for updates
179     [[NSNotificationCenter defaultCenter] addObserver:self 
180                                              selector:@selector(pithosNodeChildrenUpdated:) 
181                                                  name:@"PithosContainerNodeChildrenUpdated" 
182                                                object:nil];
183     [[NSNotificationCenter defaultCenter] addObserver:self 
184                                              selector:@selector(pithosNodeChildrenUpdated:) 
185                                                  name:@"PithosSubdirNodeChildrenUpdated" 
186                                                object:nil];
187     [[NSNotificationCenter defaultCenter] addObserver:self 
188                                              selector:@selector(pithosAccountNodeChildrenUpdated:) 
189                                                  name:@"PithosAccountNodeChildrenUpdated" 
190                                                object:nil];
191     [[NSNotificationCenter defaultCenter] addObserver:self 
192                                              selector:@selector(resetContainers) 
193                                                  name:@"PithosAuthenticationCredentialsUpdated" 
194                                                object:nil];
195 }
196
197 #pragma mark -
198 #pragma Observers
199
200 - (void)pithosNodeChildrenUpdated:(NSNotification *)notification {
201     PithosNode *node = (PithosNode *)[notification object];
202     NSInteger lastColumn = [browser lastColumn];
203     for (NSInteger column = lastColumn; column >= 0; column--) {
204         if ([[browser parentForItemsInColumn:column] isEqualTo:node]) {
205             [browser reloadColumn:column];
206             if ((column == lastColumn - 1) && ([[browser parentForItemsInColumn:lastColumn] isLeafItem])) {
207                 // This reloads the preview column
208                 [browser setLastColumn:column];
209                 [browser addColumn];
210             }
211             return;
212         }
213     }
214 }
215
216 - (void)pithosAccountNodeChildrenUpdated:(NSNotification *)notification {
217     BOOL containerPithosFound = NO;
218     BOOL containerTrashFound = NO;
219     //NSMutableArray *containersTreeNodeChildren = [[outlineViewDataSourceArray objectAtIndex:0] mutableChildNodes];
220     NSMutableArray *containersTreeNodeChildren = [NSMutableArray array];
221     for (PithosContainerNode *containerNode in accountNode.children) {
222         if ([containerNode.pithosContainer.name isEqualToString:@"pithos"]) {
223             containerNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kToolbarHomeIcon)];
224             [containersTreeNodeChildren insertObject:[NSTreeNode treeNodeWithRepresentedObject:containerNode] atIndex:0];
225             containerPithosFound = YES;
226         } else if ([containerNode.pithosContainer.name isEqualToString:@"trash"]) {
227             containerNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kFullTrashIcon)];            
228             NSUInteger insertIndex = 1;
229             if (!containerPithosFound)
230                 insertIndex = 0;
231             [containersTreeNodeChildren insertObject:[NSTreeNode treeNodeWithRepresentedObject:containerNode] atIndex:insertIndex];
232             containerTrashFound = YES;
233         } else {
234             [containersTreeNodeChildren addObject:[NSTreeNode treeNodeWithRepresentedObject:containerNode]];
235         }
236     }
237     BOOL refreshAccountNode = NO;
238     if (!containerPithosFound) {
239         // create pithos
240         ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithContainerName:@"pithos"];
241         [containerRequest startSynchronous];
242         if ([containerRequest error]) {
243             NSLog(@"error:%@", [containerRequest error]);
244             // XXX do something on error
245         } else {
246             refreshAccountNode = YES;
247         }
248     }
249     if (!containerTrashFound) {
250         // create trash
251         ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithContainerName:@"trash"];
252         [containerRequest startSynchronous];
253         if ([containerRequest error]) {
254             NSLog(@"error:%@", [containerRequest error]);
255             // XXX do something on error
256         } else {
257             refreshAccountNode = YES;
258         }
259     }
260     if (refreshAccountNode) {
261         [accountNode invalidateChildren];
262         accountNode.children;
263     } else {
264         [[[outlineViewDataSourceArray objectAtIndex:0] mutableChildNodes] setArray:containersTreeNodeChildren];
265         self.outlineViewDataSourceArray = outlineViewDataSourceArray;
266         
267         // Expand the folder outline view
268         [outlineView expandItem:nil expandChildren:YES];
269         [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
270     }
271 }
272
273
274 #pragma mark -
275 #pragma Actions
276
277 - (IBAction)refresh:(id)sender {
278     for (NSInteger column = [browser lastColumn]; column >= 0; column--) {
279         [(PithosNode *)[browser parentForItemsInColumn:column] invalidateChildren];
280     }
281     [browser validateVisibleColumns];
282 }
283
284 #pragma mark -
285 #pragma NSBrowserDelegate
286
287 - (id)rootItemForBrowser:(NSBrowser *)browser {
288     return rootNode;    
289 }
290
291 - (NSInteger)browser:(NSBrowser *)browser numberOfChildrenOfItem:(id)item {
292     PithosNode *node = (PithosNode *)item;
293     return node.children.count;
294 }
295
296 - (id)browser:(NSBrowser *)browser child:(NSInteger)index ofItem:(id)item {
297     PithosNode *node = (PithosNode *)item;
298     return [node.children objectAtIndex:index];
299 }
300
301 - (BOOL)browser:(NSBrowser *)browser isLeafItem:(id)item {
302     PithosNode *node = (PithosNode *)item;
303     return node.isLeafItem;
304 }
305
306 - (id)browser:(NSBrowser *)browser objectValueForItem:(id)item {
307     PithosNode *node = (PithosNode *)item;
308     return node;
309 }
310
311 - (NSViewController *)browser:(NSBrowser *)browser previewViewControllerForLeafItem:(id)item {
312     if (sharedPreviewController == nil)
313         sharedPreviewController = [[NSViewController alloc] initWithNibName:@"PithosBrowserPreviewController" bundle:[NSBundle bundleForClass:[self class]]];
314     return sharedPreviewController;
315 }
316
317 //- (CGFloat)browser:(NSBrowser *)browser shouldSizeColumn:(NSInteger)columnIndex forUserResize:(BOOL)forUserResize toWidth:(CGFloat)suggestedWidth  {
318 //    if (!forUserResize) {
319 //        id item = [browser parentForItemsInColumn:columnIndex]; 
320 //        if ([self browser:browser isLeafItem:item]) {
321 //            suggestedWidth = 200; 
322 //        }
323 //    }
324 //    return suggestedWidth;
325 //}
326
327 - (BOOL)browser:(NSBrowser *)sender isColumnValid:(NSInteger)column {
328     return NO;
329 }
330
331 #pragma mark -
332 #pragma NSSplitViewDelegate
333
334 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex {
335     return 120;
336 }
337
338 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex {
339     return 220;
340 }
341
342 #pragma mark -
343 #pragma mark NSOutlineViewDelegate
344
345 - (BOOL)outlineView:outlineView shouldSelectItem:(id)item {
346     return ([[item representedObject] isLeaf]);
347 }
348
349 - (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item {
350         return (![[item representedObject] isLeaf]);
351 }
352
353 - (void)outlineViewSelectionDidChange:(NSNotification *)notification {
354     PithosNode *node = [[[outlineView itemAtRow:[outlineView selectedRow]] representedObject] representedObject];
355     if (node) {
356         rootNode = node;
357         [browser loadColumnZero];
358     }
359 }
360
361 @end