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 "ASIPithosRequest.h"
40 #import "PithosAccountNode.h"
41 #import "PithosContainerNode.h"
42 #import "PithosEmptyNode.h"
43 #import "ImageAndTextCell.h"
44 #import "FileSystemBrowserCell.h"
46 //@interface PithosBrowserCell : NSBrowserCell {}
47 @interface PithosBrowserCell : FileSystemBrowserCell {}
50 @implementation PithosBrowserCell
53 if ((self = [super init])) {
54 [self setLineBreakMode:NSLineBreakByTruncatingMiddle];
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];
68 [super setObjectValue:object];
74 @interface PithosOutlineViewCell : ImageAndTextCell {}
77 @implementation PithosOutlineViewCell
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];
86 [super setObjectValue:object];
92 @interface PithosBrowserController (Private) {}
93 - (void)authenticateWithAuthUser:(NSString *)authUser authToken:(NSString *)authToken;
94 - (void)resetContainers;
97 @implementation PithosBrowserController
98 @synthesize userDefaultsController, outlineViewDataSourceArray, splitView, outlineView, browser;
99 @synthesize authenticationPanel, authenticationUserTextField, authenticationTokenTextField, authenticationRenewCheckBox,
100 authenticationCancelPushButton, authenticationManualPushButton, authenticationLoginPushButton;
103 #pragma Object Lifecycle
106 return [super initWithWindowNibName:@"PithosBrowserController"];
110 [[NSNotificationCenter defaultCenter] removeObserver:self];
111 [sharedPreviewController release];
112 [outlineViewDataSourceArray release];
114 [authenticationLoginPushButton release];
115 [authenticationManualPushButton release];
116 [authenticationCancelPushButton release];
117 [authenticationRenewCheckBox release];
118 [authenticationTokenTextField release];
119 [authenticationUserTextField release];
120 [authenticationPanel release];
123 [outlineView release];
124 [userDefaultsController release];
128 - (void)awakeFromNib {
129 [browser setCellClass:[PithosBrowserCell class]];
132 - (void)resetContainers {
134 [browser loadColumnZero];
135 self.outlineViewDataSourceArray = nil;
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]]];
147 self.outlineViewDataSourceArray = [NSMutableArray arrayWithObject:treeNode];
149 // Expand the folder outline view
150 [outlineView expandItem:nil expandChildren:YES];
151 [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
154 - (void)windowDidLoad {
155 // [userDefaultsController setAppliesImmediately:NO];
157 [[[outlineView tableColumns] objectAtIndex:0] setDataCell:[[[PithosOutlineViewCell alloc] init] autorelease]];
159 // Register for updates
160 [[NSNotificationCenter defaultCenter] addObserver:self
161 selector:@selector(pithosNodeChildrenUpdated:)
162 name:@"PithosNodeChildrenUpdated"
165 [self authenticateWithAuthUser:[authenticationUserTextField stringValue] authToken:[authenticationTokenTextField stringValue]];
171 - (void)pithosNodeChildrenUpdated:(NSNotification *)notification {
172 if ([[browser parentForItemsInColumn:[browser lastColumn]] isEqualTo:[notification object]])
173 [browser reloadColumn:[browser lastColumn]];
179 - (IBAction)refresh:(id)sender {
180 [[browser parentForItemsInColumn:[browser lastColumn]] invalidateChildren];
181 [browser reloadColumn:[browser lastColumn]];
185 #pragma Authentication
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];
194 // XXX else maybe an error message?
197 - (void)authenticateWithAuthUser:(NSString *)authUser authToken:(NSString *)authToken {
198 // XXX hardcoded for now
199 NSString *storageURLPrefix = @"https://pithos.dev.grnet.gr/v1/";
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];
208 [self authenticationSelect:nil];
212 - (IBAction)authenticationSelect:(id)sender {
213 [self.window makeFirstResponder:nil];
214 [NSApp beginSheet:authenticationPanel
215 modalForWindow:self.window
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
237 - (IBAction)authenticationCancel:(id)sender {
238 [NSApp endSheet:authenticationPanel];
239 [authenticationPanel orderOut:self];
240 [userDefaultsController revert:sender];
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]];
254 #pragma NSBrowserDelegate
256 - (id)rootItemForBrowser:(NSBrowser *)browser {
260 - (NSInteger)browser:(NSBrowser *)browser numberOfChildrenOfItem:(id)item {
261 PithosNode *node = (PithosNode *)item;
262 return node.children.count;
265 - (id)browser:(NSBrowser *)browser child:(NSInteger)index ofItem:(id)item {
266 PithosNode *node = (PithosNode *)item;
267 return [node.children objectAtIndex:index];
270 - (BOOL)browser:(NSBrowser *)browser isLeafItem:(id)item {
271 PithosNode *node = (PithosNode *)item;
272 return node.isLeafItem;
275 - (id)browser:(NSBrowser *)browser objectValueForItem:(id)item {
276 PithosNode *node = (PithosNode *)item;
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;
288 #pragma NSSplitViewDelegate
290 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex {
294 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex {
299 #pragma mark NSOutlineViewDelegate
301 - (BOOL)outlineView:outlineView shouldSelectItem:(id)item {
302 return ([[item representedObject] isLeaf]);
305 - (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item {
306 return (![[item representedObject] isLeaf]);
309 - (void)outlineViewSelectionDidChange:(NSNotification *)notification {
310 PithosNode *node = [[[outlineView itemAtRow:[outlineView selectedRow]] representedObject] representedObject];
313 [browser loadColumnZero];