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
139 NSTreeNode *containersTreeNode = [NSTreeNode treeNodeWithRepresentedObject:
140 [[[PithosEmptyNode alloc] initWithDisplayName:@"CONTAINERS" icon:nil] autorelease]];
142 [[containersTreeNode mutableChildNodes] addObject:
143 [NSTreeNode treeNodeWithRepresentedObject:
144 [[[PithosContainerNode alloc] initWithContainerName:@"pithos"
145 icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kToolbarHomeIcon)]
148 [[containersTreeNode mutableChildNodes] addObject:
149 [NSTreeNode treeNodeWithRepresentedObject:
150 [[[PithosContainerNode alloc] initWithContainerName:@"trash"
151 icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kFullTrashIcon)]
154 NSTreeNode *sharedTreeNode = [NSTreeNode treeNodeWithRepresentedObject:
155 [[[PithosEmptyNode alloc] initWithDisplayName:@"SHARED" icon:nil] autorelease]];
157 [[sharedTreeNode mutableChildNodes] addObject:
158 [NSTreeNode treeNodeWithRepresentedObject:
159 [[[PithosEmptyNode alloc] initWithDisplayName:@"my shared"
160 icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kUserIcon)]
162 // SHARED/others shared
163 [[sharedTreeNode mutableChildNodes] addObject:
164 [NSTreeNode treeNodeWithRepresentedObject:
165 [[[PithosEmptyNode alloc] initWithDisplayName:@"others shared"
166 icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGroupIcon)]
169 self.outlineViewDataSourceArray = [NSMutableArray arrayWithObjects:containersTreeNode, sharedTreeNode, nil];
171 // Expand the folder outline view
172 [outlineView expandItem:nil expandChildren:YES];
173 [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
176 - (void)windowDidLoad {
177 // [userDefaultsController setAppliesImmediately:NO];
179 [[[outlineView tableColumns] objectAtIndex:0] setDataCell:[[[PithosOutlineViewCell alloc] init] autorelease]];
181 // Register for updates
182 [[NSNotificationCenter defaultCenter] addObserver:self
183 selector:@selector(pithosNodeChildrenUpdated:)
184 name:@"PithosContainerNodeChildrenUpdated"
186 [[NSNotificationCenter defaultCenter] addObserver:self
187 selector:@selector(pithosNodeChildrenUpdated:)
188 name:@"PithosSubdirNodeChildrenUpdated"
191 [self authenticateWithAuthUser:[authenticationUserTextField stringValue] authToken:[authenticationTokenTextField stringValue]];
197 - (void)pithosNodeChildrenUpdated:(NSNotification *)notification {
198 if ([[browser parentForItemsInColumn:[browser lastColumn]] isEqualTo:[notification object]])
199 [browser reloadColumn:[browser lastColumn]];
205 - (IBAction)refresh:(id)sender {
206 [[browser parentForItemsInColumn:[browser lastColumn]] invalidateChildren];
207 [browser reloadColumn:[browser lastColumn]];
211 #pragma Authentication
213 - (void)authenticateFromURLWithAuthUser:(NSString *)authUser authToken:(NSString *)authToken {
214 if ([authUser length] && [authToken length]) {
215 [authenticationUserTextField setStringValue:authUser];
216 [authenticationTokenTextField setStringValue:authToken];
217 [userDefaultsController save:self];
218 [self authenticateWithAuthUser:authUser authToken:authToken];
220 // XXX else maybe an error message?
223 - (void)authenticateWithAuthUser:(NSString *)authUser authToken:(NSString *)authToken {
224 // XXX hardcoded for now
225 NSString *storageURLPrefix = @"https://pithos.dev.grnet.gr/v1/";
227 NSLog(@"Authentication - storageURLPrefix:%@, authUser:%@, authToken:%@", storageURLPrefix, authUser, authToken);
228 if ([authUser length] && [authToken length]) {
229 [ASIPithosRequest setStorageURL:[storageURLPrefix stringByAppendingString:[authUser stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
230 [ASIPithosRequest setAuthToken:authToken];
231 [self resetContainers];
233 [self authenticationSelect:nil];
237 - (IBAction)authenticationSelect:(id)sender {
238 [self.window makeFirstResponder:nil];
239 [NSApp beginSheet:authenticationPanel
240 modalForWindow:self.window
246 - (IBAction)authenticationLogin:(id)sender {
247 [NSApp endSheet:authenticationPanel];
248 [authenticationPanel orderOut:self];
249 // XXX hardcoded for now
250 NSProcessInfo *processInfo = [NSProcessInfo processInfo];
251 NSString *loginURL = [NSString stringWithFormat:@"https://pithos.dev.grnet.gr/login?next=pithos://%@_%d",
252 [processInfo processName], [processInfo processIdentifier]];
253 if ([authenticationRenewCheckBox state] == NSOnState)
254 loginURL = [loginURL stringByAppendingString:@"&renew="];
255 NSLog(@"loginURL: %@", loginURL);
256 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:loginURL]];
257 // XXX Should we wait for results or do something else?
258 // XXX check the case where this happens for the first time
259 // XXX maybe don't remove the Panel, and let the handler do it
262 - (IBAction)authenticationCancel:(id)sender {
263 [NSApp endSheet:authenticationPanel];
264 [authenticationPanel orderOut:self];
265 [userDefaultsController revert:sender];
268 - (IBAction)authenticationManual:(id)sender {
269 [NSApp endSheet:authenticationPanel];
270 [authenticationPanel orderOut:self];
271 [userDefaultsController save:sender];
272 // Because of delayed saves of the userDefaultsController, we use the TextField values directly, instead of
273 //NSString *authUser = [[userDefaultsController values] valueForKey:@"authUser"];
274 //NSString *authToken = [[userDefaultsController values] valueForKey:@"authToken"];
275 [self authenticateWithAuthUser:[authenticationUserTextField stringValue] authToken:[authenticationTokenTextField stringValue]];
279 #pragma NSBrowserDelegate
281 - (id)rootItemForBrowser:(NSBrowser *)browser {
285 - (NSInteger)browser:(NSBrowser *)browser numberOfChildrenOfItem:(id)item {
286 PithosNode *node = (PithosNode *)item;
287 return node.children.count;
290 - (id)browser:(NSBrowser *)browser child:(NSInteger)index ofItem:(id)item {
291 PithosNode *node = (PithosNode *)item;
292 return [node.children objectAtIndex:index];
295 - (BOOL)browser:(NSBrowser *)browser isLeafItem:(id)item {
296 PithosNode *node = (PithosNode *)item;
297 return node.isLeafItem;
300 - (id)browser:(NSBrowser *)browser objectValueForItem:(id)item {
301 PithosNode *node = (PithosNode *)item;
305 - (NSViewController *)browser:(NSBrowser *)browser previewViewControllerForLeafItem:(id)item {
306 if (sharedPreviewController == nil)
307 sharedPreviewController = [[NSViewController alloc] initWithNibName:@"PithosBrowserPreviewController" bundle:[NSBundle bundleForClass:[self class]]];
308 return sharedPreviewController;
311 //- (CGFloat)browser:(NSBrowser *)browser shouldSizeColumn:(NSInteger)columnIndex forUserResize:(BOOL)forUserResize toWidth:(CGFloat)suggestedWidth {
312 // if (!forUserResize) {
313 // id item = [browser parentForItemsInColumn:columnIndex];
314 // if ([self browser:browser isLeafItem:item]) {
315 // suggestedWidth = 200;
318 // return suggestedWidth;
322 #pragma NSSplitViewDelegate
324 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex {
328 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex {
333 #pragma mark NSOutlineViewDelegate
335 - (BOOL)outlineView:outlineView shouldSelectItem:(id)item {
336 return ([[item representedObject] isLeaf]);
339 - (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item {
340 return (![[item representedObject] isLeaf]);
343 - (void)outlineViewSelectionDidChange:(NSNotification *)notification {
344 PithosNode *node = [[[outlineView itemAtRow:[outlineView selectedRow]] representedObject] representedObject];
347 [browser loadColumnZero];