Initial commit of pithos-macos.
[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 "PithosAccount.h"
41 #import "PithosContainer.h"
42 #import "PithosEmpty.h"
43 #import "ImageAndTextCell.h"
44
45 @interface PithosBrowserCell : NSBrowserCell {}
46 @end
47
48 @implementation PithosBrowserCell
49
50 - (void)setObjectValue:(id)object {
51     if ([object isKindOfClass:[PithosNode class]]) {
52         PithosNode *node = (PithosNode *)object;
53         [self setStringValue:node.displayName];
54         [self setImage:node.icon];
55         // All cells are set as leafs because a branchingImage is already set!
56         // Maybe this cell is already inside an NSBrowserCell
57         [self setLeaf:YES];
58     } else {
59         [super setObjectValue:object];
60     }
61 }
62
63 @end
64
65 @interface PithosOutlineViewCell : ImageAndTextCell {}
66 @end
67
68 @implementation PithosOutlineViewCell
69
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];
75         [self setEditable:NO];
76     } else {
77         [super setObjectValue:object];
78     }
79 }
80
81 @end
82
83 @interface PithosBrowserController (Private) {}
84 - (void)authenticate;
85 - (void)resetContainers;
86 @end
87
88 @implementation PithosBrowserController
89 @synthesize userDefaultsController, outlineViewDataSourceArray, splitView, outlineView, browser, authenticationPanel;
90
91 #pragma mark -
92 #pragma Object Lifecycle
93
94 - (id)init {
95     return [super initWithWindowNibName:@"PithosBrowserController"];
96 }
97
98 - (void)dealloc {
99     [[NSNotificationCenter defaultCenter] removeObserver:self];
100     [outlineViewDataSourceArray release];
101     [rootNode release];
102     [authenticationPanel release];
103     [browser release];
104     [splitView release];
105     [outlineView release];
106     [userDefaultsController release];
107     [super dealloc];
108 }
109
110 - (void)awakeFromNib {
111     [browser setCellClass:[PithosBrowserCell class]];
112 }
113
114 - (void)resetContainers {
115     rootNode = nil;
116     [browser loadColumnZero];
117     self.outlineViewDataSourceArray = nil;
118     
119     // Create the outlineView tree
120         NSTreeNode *treeNode = [NSTreeNode treeNodeWithRepresentedObject:
121                             [[[PithosEmpty alloc] initWithDisplayName:@"CONTAINERS" icon:nil] autorelease]];
122         [[treeNode mutableChildNodes] addObject:
123      [NSTreeNode treeNodeWithRepresentedObject:
124       [[[PithosContainer alloc] initWithContainerName:@"pithos"] autorelease]]];
125         [[treeNode mutableChildNodes] addObject:
126      [NSTreeNode treeNodeWithRepresentedObject:
127       [[[PithosContainer alloc] initWithContainerName:@"trash"] autorelease]]];
128     
129     self.outlineViewDataSourceArray = [NSMutableArray arrayWithObject:treeNode];
130     
131         // Expand the folder outline view
132     [outlineView expandItem:nil expandChildren:YES];
133         [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
134 }
135
136 - (void)windowDidLoad {
137 //    [userDefaultsController setAppliesImmediately:NO];
138     
139     [[[outlineView tableColumns] objectAtIndex:0] setDataCell:[[[PithosOutlineViewCell alloc] init] autorelease]];
140     
141     // Register for updates
142     [[NSNotificationCenter defaultCenter] addObserver:self 
143                                              selector:@selector(pithosNodeChildrenUpdated:) 
144                                                  name:@"PithosNodeChildrenUpdated" 
145                                                object:nil];
146
147     [self authenticate];
148 }
149
150 #pragma mark -
151 #pragma Observers
152
153 - (void)pithosNodeChildrenUpdated:(NSNotification *)notification {
154     if ([[browser parentForItemsInColumn:[browser lastColumn]] isEqualTo:[notification object]]) 
155         [browser reloadColumn:[browser lastColumn]];
156 }
157
158 #pragma mark -
159 #pragma Actions
160
161 - (IBAction)refresh:(id)sender {
162     [[browser parentForItemsInColumn:[browser lastColumn]] invalidateChildren];
163     [browser reloadColumn:[browser lastColumn]];
164 }
165
166 #pragma mark -
167 #pragma Authentication
168
169 - (void)authenticate {
170     NSString *authURL = [[userDefaultsController values] valueForKey:@"authURL"];
171     NSString *username = [[userDefaultsController values] valueForKey:@"username"];
172     NSString *apiKey = [[userDefaultsController values] valueForKey:@"apiKey"];
173     NSString *storageURL = [[userDefaultsController values] valueForKey:@"storageURL"];
174     NSString *authToken = [[userDefaultsController values] valueForKey:@"authToken"];
175
176     NSLog(@"Authentication - authURL:%@, username:%@, apiKey:%@, storageURL:%@, authToken:%@", authURL, username, apiKey, storageURL, authToken);
177     if (storageURL && authToken) {
178         [ASIPithosRequest setStorageURL:storageURL];
179         [ASIPithosRequest setAuthToken:authToken];
180         [self resetContainers];
181     } else if (authURL && username && apiKey) {
182         [ASIPithosRequest setAuthURL:authURL];
183         [ASIPithosRequest setUsername:username];
184         [ASIPithosRequest setApiKey:apiKey];        
185         NSError *error = [ASIPithosRequest authenticate];
186         if (error) {
187             // XXX Do something on error.
188             NSLog(@"error:%@", error);
189         } else {
190             [[userDefaultsController values] setValue:[ASIPithosRequest storageURL] forKey:@"storageURL"];
191             [[userDefaultsController values] setValue:[ASIPithosRequest authToken] forKey:@"authToken"];
192             [userDefaultsController save:nil];
193         }
194         [self resetContainers];
195     } else {
196         [self authenticationSelect:nil];
197     }
198 }
199
200 - (IBAction)authenticationSelect:(id)sender {
201         [self.window makeFirstResponder:nil];
202     
203         [NSApp beginSheet:authenticationPanel
204            modalForWindow:self.window
205         modalDelegate:self
206            didEndSelector:NULL
207                   contextInfo:nil];
208 }
209
210 - (IBAction)authenticationDone:(id)sender {
211         [NSApp endSheet:authenticationPanel];
212         [authenticationPanel orderOut:self];
213     [userDefaultsController save:sender];
214     [self authenticate];
215 }
216
217 - (IBAction)authenticationCancel:(id)sender {
218         [NSApp endSheet:authenticationPanel];
219         [authenticationPanel orderOut:self];
220     [userDefaultsController revert:sender];
221 }
222
223 #pragma mark -
224 #pragma NSBrowserDelegate
225
226 - (id)rootItemForBrowser:(NSBrowser *)browser {
227     return rootNode;    
228 }
229
230 - (NSInteger)browser:(NSBrowser *)browser numberOfChildrenOfItem:(id)item {
231     PithosNode *node = (PithosNode *)item;
232     return node.children.count;
233 }
234
235 - (id)browser:(NSBrowser *)browser child:(NSInteger)index ofItem:(id)item {
236     PithosNode *node = (PithosNode *)item;
237     return [node.children objectAtIndex:index];
238 }
239
240 - (BOOL)browser:(NSBrowser *)browser isLeafItem:(id)item {
241     PithosNode *node = (PithosNode *)item;
242     return node.isLeafItem;
243 }
244
245 - (id)browser:(NSBrowser *)browser objectValueForItem:(id)item {
246     PithosNode *node = (PithosNode *)item;
247     return node;
248 }
249
250 #pragma mark -
251 #pragma NSSplitViewDelegate
252
253 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex {
254     return 100;
255 }
256
257 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex {
258     return 260;
259 }
260
261 #pragma mark -
262 #pragma mark NSOutlineViewDelegate
263
264 - (BOOL)outlineView:outlineView shouldSelectItem:(id)item {
265     return ([[item representedObject] isLeaf]);
266 }
267
268 - (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item {
269         return (![[item representedObject] isLeaf]);
270 }
271
272 - (void)outlineViewSelectionDidChange:(NSNotification *)notification {
273     PithosNode *node = [[[outlineView itemAtRow:[outlineView selectedRow]] representedObject] representedObject];
274     if (node) {
275         rootNode = node;
276         [browser loadColumnZero];
277     }
278 }
279
280 @end