Using system icons based on file extension.
[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 "ASIPithosRequest.h"
40 #import "PithosAccountNode.h"
41 #import "PithosContainerNode.h"
42 #import "PithosEmptyNode.h"
43 #import "ImageAndTextCell.h"
44 #import "FileSystemBrowserCell.h"
45
46 //@interface PithosBrowserCell : NSBrowserCell {}
47 @interface PithosBrowserCell : FileSystemBrowserCell {}
48 @end
49
50 @implementation PithosBrowserCell
51
52 - (id)init {
53     if ((self = [super init])) {
54         [self setLineBreakMode:NSLineBreakByTruncatingMiddle];
55     }
56     return self;
57 }
58
59 - (void)setObjectValue:(id)object {
60     if ([object isKindOfClass:[PithosNode class]]) {
61         PithosNode *node = (PithosNode *)object;
62         [self setStringValue:node.displayName];
63         [self setImage:node.icon];
64 //        // All cells are set as leafs because a branchingImage is already set!
65 //        // Maybe this cell is already inside an NSBrowserCell
66 //        [self setLeaf:YES];
67     } else {
68         [super setObjectValue:object];
69     }
70 }
71
72 @end
73
74 @interface PithosOutlineViewCell : ImageAndTextCell {}
75 @end
76
77 @implementation PithosOutlineViewCell
78
79 - (void)setObjectValue:(id)object {
80     if ([object isKindOfClass:[PithosNode class]]) {
81         PithosNode *node = (PithosNode *)object;
82         [self setStringValue:node.displayName];
83         [self setImage:node.icon];
84         [self setEditable:NO];
85     } else {
86         [super setObjectValue:object];
87     }
88 }
89
90 @end
91
92 @interface PithosBrowserController (Private) {}
93 - (void)authenticateWithAuthUser:(NSString *)authUser authToken:(NSString *)authToken;
94 - (void)resetContainers;
95 @end
96
97 @implementation PithosBrowserController
98 @synthesize userDefaultsController, outlineViewDataSourceArray, splitView, outlineView, browser;
99 @synthesize authenticationPanel, authenticationUserTextField, authenticationTokenTextField, authenticationRenewCheckBox, 
100             authenticationCancelPushButton, authenticationManualPushButton, authenticationLoginPushButton;
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     [rootNode release];
114     [authenticationLoginPushButton release];
115     [authenticationManualPushButton release];
116     [authenticationCancelPushButton release];
117     [authenticationRenewCheckBox release];
118     [authenticationTokenTextField release];
119     [authenticationUserTextField release];
120     [authenticationPanel release];
121     [browser release];
122     [splitView release];
123     [outlineView release];
124     [userDefaultsController release];
125     [super dealloc];
126 }
127
128 - (void)awakeFromNib {
129     [browser setCellClass:[PithosBrowserCell class]];
130 }
131
132 - (void)resetContainers {
133     rootNode = nil;
134     [browser loadColumnZero];
135     self.outlineViewDataSourceArray = nil;
136     
137     // Create the outlineView tree
138         NSTreeNode *treeNode = [NSTreeNode treeNodeWithRepresentedObject:
139                             [[[PithosEmptyNode alloc] initWithDisplayName:@"CONTAINERS" icon:nil] autorelease]];
140         [[treeNode mutableChildNodes] addObject:
141      [NSTreeNode treeNodeWithRepresentedObject:
142       [[[PithosContainerNode alloc] initWithContainerName:@"pithos"] autorelease]]];
143         [[treeNode mutableChildNodes] addObject:
144      [NSTreeNode treeNodeWithRepresentedObject:
145       [[[PithosContainerNode alloc] initWithContainerName:@"trash"] autorelease]]];
146     
147     self.outlineViewDataSourceArray = [NSMutableArray arrayWithObject:treeNode];
148     
149         // Expand the folder outline view
150     [outlineView expandItem:nil expandChildren:YES];
151         [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
152 }
153
154 - (void)windowDidLoad {
155 //    [userDefaultsController setAppliesImmediately:NO];
156     
157     [[[outlineView tableColumns] objectAtIndex:0] setDataCell:[[[PithosOutlineViewCell alloc] init] autorelease]];
158     
159     // Register for updates
160     [[NSNotificationCenter defaultCenter] addObserver:self 
161                                              selector:@selector(pithosNodeChildrenUpdated:) 
162                                                  name:@"PithosNodeChildrenUpdated" 
163                                                object:nil];
164
165     [self authenticateWithAuthUser:[authenticationUserTextField stringValue] authToken:[authenticationTokenTextField stringValue]];
166 }
167
168 #pragma mark -
169 #pragma Observers
170
171 - (void)pithosNodeChildrenUpdated:(NSNotification *)notification {
172     if ([[browser parentForItemsInColumn:[browser lastColumn]] isEqualTo:[notification object]]) 
173         [browser reloadColumn:[browser lastColumn]];
174 }
175
176 #pragma mark -
177 #pragma Actions
178
179 - (IBAction)refresh:(id)sender {
180     [[browser parentForItemsInColumn:[browser lastColumn]] invalidateChildren];
181     [browser reloadColumn:[browser lastColumn]];
182 }
183
184 #pragma mark -
185 #pragma Authentication
186
187 - (void)authenticateFromURLWithAuthUser:(NSString *)authUser authToken:(NSString *)authToken {
188     if ([authUser length] && [authToken length]) {
189         [authenticationUserTextField setStringValue:authUser];
190         [authenticationTokenTextField setStringValue:authToken];
191         [userDefaultsController save:self];
192         [self authenticateWithAuthUser:authUser authToken:authToken];
193     }
194     // XXX else maybe an error message?
195 }
196
197 - (void)authenticateWithAuthUser:(NSString *)authUser authToken:(NSString *)authToken {
198     // XXX hardcoded for now
199     NSString *storageURLPrefix = @"https://pithos.dev.grnet.gr/v1/";
200     
201     NSLog(@"Authentication - storageURLPrefix:%@, authUser:%@, authToken:%@", storageURLPrefix, authUser, authToken);
202     if ([authUser length] && [authToken length]) {
203     //if (authUser && ([authUser length] > 0) && authToken && ([authToken length] > 0)) {
204         [ASIPithosRequest setStorageURL:[storageURLPrefix stringByAppendingString:authUser]];
205         [ASIPithosRequest setAuthToken:authToken];
206         [self resetContainers];
207     } else {
208         [self authenticationSelect:nil];
209     }
210 }
211
212 - (IBAction)authenticationSelect:(id)sender {
213         [self.window makeFirstResponder:nil];
214         [NSApp beginSheet:authenticationPanel
215            modalForWindow:self.window
216         modalDelegate:self
217            didEndSelector:NULL
218                   contextInfo:nil];
219 }
220
221 - (IBAction)authenticationLogin:(id)sender {
222         [NSApp endSheet:authenticationPanel];
223         [authenticationPanel orderOut:self];
224     // XXX hardcoded for now
225     NSProcessInfo *processInfo = [NSProcessInfo processInfo];
226     NSString *loginURL = [NSString stringWithFormat:@"https://pithos.dev.grnet.gr/login?next=pithos://%@_%d",
227                           [processInfo processName], [processInfo processIdentifier]];
228     if ([authenticationRenewCheckBox state] == NSOnState)
229         loginURL = [loginURL stringByAppendingString:@"&renew="];
230     NSLog(@"loginURL: %@", loginURL);
231     [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:loginURL]];
232     // XXX Should we wait for results or do something else?
233     // XXX check the case where this happens for the first time
234     // XXX maybe don't remove the Panel, and let the handler do it
235 }
236
237 - (IBAction)authenticationCancel:(id)sender {
238         [NSApp endSheet:authenticationPanel];
239         [authenticationPanel orderOut:self];
240     [userDefaultsController revert:sender];
241 }
242
243 - (IBAction)authenticationManual:(id)sender {
244         [NSApp endSheet:authenticationPanel];
245         [authenticationPanel orderOut:self];
246     [userDefaultsController save:sender];
247     // Because of delayed saves of the userDefaultsController, we use the TextField values directly, instead of 
248     //NSString *authUser = [[userDefaultsController values] valueForKey:@"authUser"];
249     //NSString *authToken = [[userDefaultsController values] valueForKey:@"authToken"];    
250     [self authenticateWithAuthUser:[authenticationUserTextField stringValue] authToken:[authenticationTokenTextField stringValue]];
251 }
252
253 #pragma mark -
254 #pragma NSBrowserDelegate
255
256 - (id)rootItemForBrowser:(NSBrowser *)browser {
257     return rootNode;    
258 }
259
260 - (NSInteger)browser:(NSBrowser *)browser numberOfChildrenOfItem:(id)item {
261     PithosNode *node = (PithosNode *)item;
262     return node.children.count;
263 }
264
265 - (id)browser:(NSBrowser *)browser child:(NSInteger)index ofItem:(id)item {
266     PithosNode *node = (PithosNode *)item;
267     return [node.children objectAtIndex:index];
268 }
269
270 - (BOOL)browser:(NSBrowser *)browser isLeafItem:(id)item {
271     PithosNode *node = (PithosNode *)item;
272     return node.isLeafItem;
273 }
274
275 - (id)browser:(NSBrowser *)browser objectValueForItem:(id)item {
276     PithosNode *node = (PithosNode *)item;
277     return node;
278 }
279
280 - (NSViewController *)browser:(NSBrowser *)browser previewViewControllerForLeafItem:(id)item {
281     if (sharedPreviewController == nil)
282         sharedPreviewController = [[NSViewController alloc] initWithNibName:@"PithosBrowserPreviewController" bundle:[NSBundle bundleForClass:[self class]]];
283     return sharedPreviewController;
284 }
285
286
287 #pragma mark -
288 #pragma NSSplitViewDelegate
289
290 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex {
291     return 100;
292 }
293
294 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex {
295     return 260;
296 }
297
298 #pragma mark -
299 #pragma mark NSOutlineViewDelegate
300
301 - (BOOL)outlineView:outlineView shouldSelectItem:(id)item {
302     return ([[item representedObject] isLeaf]);
303 }
304
305 - (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item {
306         return (![[item representedObject] isLeaf]);
307 }
308
309 - (void)outlineViewSelectionDidChange:(NSNotification *)notification {
310     PithosNode *node = [[[outlineView itemAtRow:[outlineView selectedRow]] representedObject] representedObject];
311     if (node) {
312         rootNode = node;
313         [browser loadColumnZero];
314     }
315 }
316
317 @end