f8a3f5fc4339fda98e94d98c6d78a0ab2b8b8e94
[pithos-ios] / Classes / FolderViewController.m
1 //
2 //  FolderViewController.m
3 //  OpenStack
4 //
5 //  Created by Mike Mayo on 12/15/10.
6 //  The OpenStack project is provided under the Apache 2.0 license.
7 //
8
9 #import "FolderViewController.h"
10 #import "OpenStackAccount.h"
11 #import "Container.h"
12 #import "StorageObject.h"
13 #import "StorageObjectViewController.h"
14 #import "ContainerDetailViewController.h"
15 #import "AccountManager.h"
16 #import "ActivityIndicatorView.h"
17 #import "ContainersViewController.h"
18 #import "UIViewController+Conveniences.h"
19 #import "AddObjectViewController.h"
20 #import "OpenStackAppDelegate.h"
21 #import "RootViewController.h"
22 #import "FolderDetailViewController.h"
23 #import "AccountHomeViewController.h"
24 #import "PithosImageViewController.h"
25 #import "OpenStackRequest.h"
26 #import "JSON.h"
27 #import "APICallback.h"
28 #import <MobileCoreServices/MobileCoreServices.h>
29 #import "ComputeModel.h"
30 #import "MediaViewController.h"
31
32 static NSString *kSortTypeKey = @"sortType";
33 static NSString *kSortNameDirectionKey = @"sortNameDirection";
34 static NSString *kSortDateDirectionKey = @"sortDateDirection";
35
36 @implementation FolderViewController
37
38 @synthesize account, container, folder, name, containersViewController, selectedContainerIndexPath, contentsLoaded, parentFolderViewController, selectedFolderIndexPath, tableView, needsRefreshing, folderHasBeenRemoved, refreshWhenAppeared, folderDetailVC, selectedObjectViewController, refreshButton;
39 @synthesize searchBar, searchDisplayController, searchFilter, searchActiveOnRotation;
40 @synthesize sortTypeButton, sortDirectionButton, sortType, sortNameDirection, sortDateDirection;
41 @synthesize documentInteractionController;
42
43 #pragma mark - View lifecycle
44
45 - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
46     if (([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) &&
47         self.searchDisplayController.active && self.searchBar.isFirstResponder) {
48         UIInterfaceOrientation statusBarOrientation = [[UIApplication sharedApplication] statusBarOrientation];
49         if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation) && UIInterfaceOrientationIsLandscape(statusBarOrientation)) {
50             [self.searchBar resignFirstResponder];
51         } else if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation) && UIInterfaceOrientationIsPortrait(statusBarOrientation)) {
52             self.searchActiveOnRotation = YES;
53         }
54     }
55
56     [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
57 }
58
59 - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
60     [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
61
62     if (([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) &&
63         UIInterfaceOrientationIsPortrait(fromInterfaceOrientation) &&
64         UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
65         if (self.searchDisplayController.active && self.searchActiveOnRotation) {
66             [self.searchBar becomeFirstResponder];
67             CGRect tableViewFrame = self.tableView.frame;
68             tableViewFrame.size.height = self.toolbar.frame.origin.y - tableViewFrame.origin.y;
69             self.tableView.frame = tableViewFrame;
70             self.searchDisplayController.searchResultsTableView.frame = tableViewFrame;
71         }
72         self.searchActiveOnRotation = NO;
73     }
74 }
75
76 - (void)viewDidLoad {
77     [super viewDidLoad];
78     [self addAddButton];
79     [self addHomeButton];
80 }
81
82 - (void)viewWillAppear:(BOOL)animated {
83     [super viewWillAppear:animated];
84     if (self.name && self.name.length) {
85         self.navigationItem.title = self.name;
86     } else {
87         self.navigationItem.title = self.container.name;
88     }
89     
90     NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
91     
92     self.sortType = [userDefaults integerForKey:kSortTypeKey];
93     self.sortTypeButton.title = (self.sortType == FolderSortTypeDate) ? @"date" : @"name";
94     
95     self.sortNameDirection = [userDefaults integerForKey:kSortNameDirectionKey];
96     self.sortDateDirection = [userDefaults integerForKey:kSortDateDirectionKey];
97     if (self.sortType == FolderSortTypeDate) {
98         self.sortDirectionButton.title = (self.sortDateDirection == FolderSortDateDirectionNewestFirst) ? @"newest first" : @"oldest first";
99     } else {
100         self.sortDirectionButton.title = (self.sortNameDirection == FolderSortNameDirectionZA) ? @"Z-A" : @"A-Z";
101     }
102     
103     [self reloadData];
104 }
105
106 - (void)viewDidAppear:(BOOL)animated {
107     [super viewDidAppear:animated];
108     if (!self.folder) {
109         if (folderHasBeenRemoved) {
110             if (needsRefreshing) 
111                 self.parentFolderViewController.needsRefreshing = YES;
112             [self.navigationController popViewControllerAnimated:YES];
113         } else {
114             [self refreshButtonPressed:nil];
115         }
116     } else if (self.needsRefreshing) {
117         self.parentFolderViewController.needsRefreshing = YES;
118         if (refreshWhenAppeared) {
119             [self refreshButtonPressed:nil];
120             refreshWhenAppeared = NO;
121         }
122         if (account.shared && !folder.objectsAndFoldersCount) {
123             [self.navigationController popViewControllerAnimated:YES];
124         }
125     }
126 }
127
128 - (void)viewWillDisappear:(BOOL)animated {
129     [super viewWillDisappear:animated];
130     if (![self.navigationController.viewControllers containsObject:self] &&
131         self.parentViewController &&
132         ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)) {
133         [parentFolderViewController setDetailViewController];
134     }
135 }
136
137 #pragma mark - Memory management
138
139 - (void)dealloc {
140     [account release];
141     [container release];
142     [folder release];
143     [containersViewController release];
144     [selectedContainerIndexPath release];
145     [parentFolderViewController release];
146     [selectedFolderIndexPath release];
147     [refreshButton release];
148     [searchBar release];
149     [searchDisplayController release];
150     [searchFilter release];
151     [sortTypeButton release];
152     [sortDirectionButton release];
153     [documentInteractionController release];
154     [super dealloc];
155 }
156
157 #pragma mark - Internal
158
159 - (void)showSearchBar:(BOOL)show {
160     if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
161         if (show && !self.tableView.tableHeaderView) {
162             self.tableView.tableHeaderView = self.searchBar;
163             self.tableView.contentOffset = CGPointMake(0.0, 44.0);
164         } else if (!show) {
165             if (self.searchDisplayController.active)
166                 [self.searchDisplayController setActive:NO animated:NO];
167             self.tableView.tableHeaderView = nil;
168         }
169     } else {
170         if (show && self.searchBar.hidden) {
171             self.searchBar.hidden = NO;
172             self.searchBar.frame = CGRectMake(0.0, 0.0, self.searchBar.frame.size.width, self.searchBar.frame.size.height);
173             self.tableView.frame = CGRectMake(0.0, self.searchBar.frame.size.height, self.tableView.frame.size.width, self.tableView.frame.size.height - self.searchBar.frame.size.height);
174         } else if (!show && !self.searchBar.hidden) {
175             self.searchBar.frame = CGRectMake(0.0, -self.searchBar.frame.size.height, self.searchBar.frame.size.width, self.searchBar.frame.size.height);
176             self.tableView.frame = CGRectMake(0.0, 0, self.tableView.frame.size.width, self.tableView.frame.size.height + self.searchBar.frame.size.height);
177             self.searchBar.hidden = YES;
178             if (self.searchDisplayController.active)
179                 [self.searchDisplayController setActive:NO animated:NO];
180         }
181     }
182 }
183
184 - (void)reloadFolderViewControllers {
185     NSArray *viewControllers = [self.navigationController viewControllers];
186     
187     int rootFolderControllerIndex = [viewControllers indexOfObject:self.containersViewController] + 1;
188     FolderViewController *rootFolderViewController = [viewControllers objectAtIndex:rootFolderControllerIndex];
189     rootFolderViewController.folder = self.container.rootFolder;
190     
191     for (int i = rootFolderControllerIndex + 1; i < [viewControllers count]; i++) {
192         FolderViewController *folderViewController = [viewControllers objectAtIndex:i];
193         Folder *parentFolder = folderViewController.parentFolderViewController.folder;
194         
195         folderViewController.folder = [parentFolder.folders objectForKey:folderViewController.folder.name];
196         if (!folderViewController.folder)
197             folderViewController.folderHasBeenRemoved = YES;
198     }
199     if (!self.folder || (account.shared && !folder.objectsAndFoldersCount)) {
200         if (needsRefreshing && self.parentFolderViewController) {
201             self.parentFolderViewController.needsRefreshing = YES;
202         }
203         [self.navigationController popViewControllerAnimated:YES];
204     } else if (([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) && account.shared) {
205         [self setDetailViewController];
206     }
207 }
208
209 - (void)deleteFolderRow {
210     [self.parentFolderViewController.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selectedFolderIndexPath]
211                                                      withRowAnimation:UITableViewRowAnimationLeft];
212     if (self.parentFolderViewController.searchDisplayController.active &&
213         self.parentFolderViewController.searchFilter && self.parentFolderViewController.searchFilter.length)
214         [self.parentFolderViewController.searchDisplayController.searchResultsTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selectedFolderIndexPath]
215                                                                                               withRowAnimation:UITableViewRowAnimationLeft];
216 }
217
218 - (void)deleteObjectRow:(NSTimer *)timer {
219     [self.folder removeObject:[timer.userInfo objectForKey:@"object"]];
220     [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[timer.userInfo objectForKey:@"indexPath"]]
221                           withRowAnimation:UITableViewRowAnimationLeft];
222     if (self.searchDisplayController.active && self.searchFilter && self.searchFilter.length)
223         [self.searchDisplayController.searchResultsTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[timer.userInfo objectForKey:@"indexPath"]]
224                                                                    withRowAnimation:UITableViewRowAnimationLeft];
225     self.refreshButton.enabled = YES;
226 }
227
228 - (NSArray *)searchResults {
229 return [self.folder sortedContentsUsingFilter:self.searchFilter
230                                         scope:[self.searchBar.scopeButtonTitles objectAtIndex:self.searchBar.selectedScopeButtonIndex]
231                                      sortType:self.sortType
232                                 sortDirection:((self.sortType == FolderSortTypeDate) ? self.sortDateDirection : self.sortNameDirection)];
233 }
234
235 #pragma mark - Properties
236
237 - (void)setFolder:(Folder *)aFolder {
238     [folder release];
239     folder = [aFolder retain];
240     [self showSearchBar:(self.contentsLoaded && self.folder && self.folder.objectsAndFoldersCount)];
241     if (([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) && self.selectedObjectViewController && folder) {
242         StorageObject *newObject = [folder.objects objectForKey:self.selectedObjectViewController.object.name];
243         if (newObject)
244             self.selectedObjectViewController.object = newObject;
245         // We don't set nil otherwise, this will be done in reloadData, when setDetailViewController is called.
246     }
247     [self reloadData];
248 }
249
250 #pragma mark - Actions
251
252 - (void)reloadData {
253     [self.tableView reloadData];
254     BOOL searchResultsShown = self.searchDisplayController.active && self.searchFilter && self.searchFilter.length;
255     if (searchResultsShown)
256         [self.searchDisplayController.searchResultsTableView reloadData];
257     if (([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) && self.selectedObjectViewController) {
258         NSUInteger selectedIndex = [[self searchResults] indexOfObject:self.selectedObjectViewController.object];
259         if (selectedIndex == NSNotFound) {
260             [self setDetailViewController];
261         } else {
262             NSIndexPath *selectIndexPath = [NSIndexPath indexPathForRow:selectedIndex inSection:0];
263             [self.tableView selectRowAtIndexPath:selectIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
264             if (searchResultsShown)
265                 [self.searchDisplayController.searchResultsTableView selectRowAtIndexPath:selectIndexPath
266                                                                                  animated:NO
267                                                                            scrollPosition:UITableViewScrollPositionNone];
268         }
269     }
270 }
271
272 - (void)setDetailViewController {
273     self.selectedObjectViewController = nil;
274     if ([self.folder isEqual:self.container.rootFolder]) {
275         ContainerDetailViewController *vc = [[ContainerDetailViewController alloc] initWithNibName:@"ContainerDetailViewController" bundle:nil];
276         vc.account = self.account;
277         vc.container = self.container;
278         vc.containersViewController = self.containersViewController;
279         vc.rootFolderViewController = self;
280         vc.selectedContainerIndexPath = self.selectedContainerIndexPath;
281         [self presentPrimaryViewController:vc];
282         self.containersViewController.containerDetailViewController = vc;
283         [vc release];
284     } else {
285         FolderDetailViewController *vc = [[FolderDetailViewController alloc] initWithNibName:@"FolderDetailViewController" bundle:nil];
286         vc.account = self.account;
287         vc.container = self.container;
288         vc.folder = self.folder;
289         vc.folderViewController = self.parentFolderViewController;
290         [self presentPrimaryViewController:vc];
291         self.parentFolderViewController.folderDetailVC = vc;
292         [vc release];
293     }
294 }
295
296 - (void)deleteAnimatedObject:(StorageObject *)object {
297     NSIndexPath *deleteIndexPath = [NSIndexPath indexPathForRow:[[self searchResults] indexOfObject:object] inSection:0];
298     [self.tableView selectRowAtIndexPath:deleteIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
299     if (self.searchDisplayController.active && self.searchFilter && self.searchFilter.length)
300         [self.searchDisplayController.searchResultsTableView selectRowAtIndexPath:deleteIndexPath
301                                                                          animated:YES
302                                                                    scrollPosition:UITableViewScrollPositionNone];
303     NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:object, @"object", deleteIndexPath, @"indexPath", nil];
304     [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteObjectRow:) userInfo:userInfo repeats:NO];
305 }
306
307 - (IBAction)toggleSortType:(id)sender {
308     if (self.sortType == FolderSortTypeDate) {
309         self.sortType = FolderSortTypeName;
310         self.sortTypeButton.title = @"name";
311         self.sortDirectionButton.title = (self.sortNameDirection == FolderSortNameDirectionZA) ? @"Z-A" : @"A-Z";
312     } else {
313         self.sortType = FolderSortTypeDate;
314         self.sortTypeButton.title = @"date";
315         self.sortDirectionButton.title = (self.sortDateDirection == FolderSortDateDirectionNewestFirst) ? @"newest first" : @"oldest first";
316     }
317     [self reloadData];
318     NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
319     [userDefaults setInteger:self.sortType forKey:kSortTypeKey];
320     [userDefaults synchronize];
321 }
322
323 - (IBAction)toggleSortDirection:(id)sender {
324     NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
325     if (self.sortType == FolderSortTypeDate) {
326         if (self.sortDateDirection == FolderSortDateDirectionNewestFirst) {
327             self.sortDateDirection = FolderSortDateDirectionOldestFirst;
328             self.sortDirectionButton.title = @"oldest first";
329         } else {
330             self.sortDateDirection = FolderSortDateDirectionNewestFirst;
331             self.sortDirectionButton.title = @"newest first";
332         }
333         [userDefaults setInteger:self.sortDateDirection forKey:kSortDateDirectionKey];
334     } else {
335         if (self.sortNameDirection == FolderSortNameDirectionZA) {
336             self.sortNameDirection = FolderSortNameDirectionAZ;
337             self.sortDirectionButton.title = @"A-Z";
338         } else {
339             self.sortNameDirection = FolderSortNameDirectionZA;
340             self.sortDirectionButton.title = @"Z-A";
341         }
342         [userDefaults setInteger:self.sortNameDirection forKey:kSortNameDirectionKey];
343     }
344     [self reloadData];
345     [userDefaults synchronize];
346 }
347
348 #pragma mark - UITableViewDataSource
349
350 - (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {
351     if (self.folder.objectsAndFoldersCount) {
352         // For the search results tableView this will be returned because search is allowed only when the folder is not empty.
353         return [[self searchResults] count];
354     } else if (contentsLoaded) {
355         return 1;
356     } else {
357         return 0;
358     }
359 }
360
361 - (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
362     if (self.folder.objectsAndFoldersCount) {
363         // For the search results tableView this will be returned because search is allowed only when the folder is not empty.
364         aTableView.scrollEnabled = YES;
365         aTableView.allowsSelection = YES;
366         return aTableView.rowHeight;
367     } else {
368         aTableView.scrollEnabled = NO;
369         aTableView.allowsSelection = NO;
370         return aTableView.frame.size.height;
371     }
372 }
373
374 - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
375     if (!self.folder.objectsAndFoldersCount) {
376         NSString *noFoldersTitle = (self.account.shared || self.account.sharingAccount) ?
377             @"No shared Files or Folders" : @"No Files or Folders";
378         NSString *noFoldersSubtitle = (self.account.shared || self.account.sharingAccount || [self.folder.name hasSuffix:@"/"]) ?
379             @"" : @"Tap the + button to create a new one";
380         NSString *deleteFolderButtonTitle = (self.account.shared || self.account.sharingAccount) ? nil : @"Delete Folder";
381         SEL deleteButtonSelector = (self.account.shared || self.account.sharingAccount) ? nil : @selector(deleteButtonPressed:);
382         self.navigationItem.rightBarButtonItem.enabled = (self.account.shared || [self.folder.name hasSuffix:@"/"]) ? NO : YES;
383         
384         if ([self.container.rootFolder isEqual:self.folder]) {
385             return [self tableView:aTableView
386                 emptyCellWithImage:[UIImage imageNamed:@"empty-files.png"]
387                              title:noFoldersTitle
388                           subtitle:noFoldersSubtitle];
389         } else {
390             return [self tableView:aTableView
391                 emptyCellWithImage:[UIImage imageNamed:@"empty-files.png"]
392                              title:noFoldersTitle
393                           subtitle:noFoldersSubtitle
394                  deleteButtonTitle:deleteFolderButtonTitle
395               deleteButtonSelector:deleteButtonSelector];
396         }
397     } else {
398         // For the search results tableView this will be returned because search is allowed only when the folder is not empty.
399         static NSString *CellIdentifier = @"Cell";
400         UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
401         if (!cell)
402             cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
403         
404         id item = [[self searchResults] objectAtIndex:indexPath.row];
405         if ([[item class] isEqual:[Folder class]]) {
406             Folder *cellFolder = (Folder *)item;
407             cell.textLabel.text = cellFolder.name;
408             [cell.textLabel setLineBreakMode:UILineBreakModeTailTruncation];
409             if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
410                 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
411             } else {
412                 cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
413             }
414             
415             cell.imageView.image = [UIImage imageNamed:@"folder-icon.png"];
416             
417 //            NSString *folderString = @"";
418 //            if (cellFolder.folders.count > 1) {
419 //                folderString = [NSString stringWithFormat:@"%u folders, ", cellFolder.folders.count];
420 //            } else if (cellFolder.folders.count > 0) {
421 //                folderString = @"1 folder, ";
422 //            }
423 //            NSString *objectString = @"";
424 //            if (cellFolder.objects.count != 1) {
425 //                objectString = [NSString stringWithFormat:@"%u objects", cellFolder.objects.count];
426 //            } else {
427 //                objectString = @"1 object";
428 //            }
429 //            cell.detailTextLabel.text = [NSString stringWithFormat:@"%@%@", folderString, objectString];
430
431             if ([[UIDevice currentDevice].systemVersion hasPrefix:@"4"]) {
432                 // iOS 4.3
433                 if (cellFolder.lastModifiedString) {
434                     cell.detailTextLabel.text = [NSString stringWithFormat:@"%u fold. %u obj. %@",
435                                                  cellFolder.folders.count, cellFolder.objects.count, cellFolder.lastModifiedString];
436                 } else {
437                     cell.detailTextLabel.text = [NSString stringWithFormat:@"%u folders %u objects", cellFolder.folders.count, cellFolder.objects.count];
438                 }
439             } else {
440                 // iOS 5.0+
441                 if (cellFolder.lastModifiedString) {
442                     cell.detailTextLabel.text = [NSString stringWithFormat:@"%u 📁 %u 📄 %@",
443                                                  cellFolder.folders.count, cellFolder.objects.count, cellFolder.lastModifiedString];
444                 } else {
445                     cell.detailTextLabel.text = [NSString stringWithFormat:@"%u 📁 %u 📄", cellFolder.folders.count, cellFolder.objects.count];
446                 }
447             }
448         } else if ([[item class] isEqual:[StorageObject class]]) {
449             StorageObject *cellObject = (StorageObject *)item;
450             cell.textLabel.text = cellObject.name;
451             [cell.textLabel setLineBreakMode:UILineBreakModeMiddleTruncation];
452             if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
453                 cell.accessoryType = UITableViewCellAccessoryNone;
454             } else {
455                 cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
456             }
457             
458             CFStringRef mimeType = (CFStringRef)cellObject.contentType;
459             CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL);
460             NSString *emptyPath = [[NSBundle mainBundle] pathForResource:@"empty-file" ofType:@""];
461             UIDocumentInteractionController *udic = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:emptyPath]];
462             if (((NSString *)uti).length > 0)
463                 udic.UTI = (NSString *)uti;
464             if (udic.icons.count > 0)
465                 cell.imageView.image = [udic.icons objectAtIndex:0];
466             
467             if (cellObject.lastModifiedString) {
468                 cell.detailTextLabel.text = [NSString stringWithFormat:@"%@, %@", cellObject.humanizedBytes, cellObject.lastModifiedString];
469             } else {
470                 cell.detailTextLabel.text = cellObject.humanizedBytes;
471             }
472             
473             if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
474                 
475             }
476         }
477         
478         return cell;
479     }        
480 }
481
482 #pragma mark - UITableViewDelegate
483
484 - (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
485     if (folder.objectsAndFoldersCount) {
486         id item = [[self searchResults] objectAtIndex:indexPath.row];
487         if ([[item class] isEqual:[Folder class]]) {
488             FolderViewController *vc = (([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) ?
489                                         [[FolderViewController alloc] initWithNibName:@"FolderViewController-iPad" bundle:nil] :
490                                         [[FolderViewController alloc] initWithNibName:@"FolderViewController" bundle:nil]);
491             vc.account = self.account;
492             vc.container = self.container;
493             vc.name = [item name];
494             vc.selectedFolderIndexPath = indexPath;
495             vc.parentFolderViewController = self;
496             vc.containersViewController = self.containersViewController;
497             [self.navigationController pushViewController:vc animated:YES];
498             vc.contentsLoaded = YES;
499             vc.folder = item;
500             [aTableView deselectRowAtIndexPath:indexPath animated:YES];
501             if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
502                 self.selectedObjectViewController = nil;
503                 FolderDetailViewController *folderDetailViewController = [[FolderDetailViewController alloc] initWithNibName:@"FolderDetailViewController" bundle:nil];
504                 folderDetailViewController.account = account;
505                 folderDetailViewController.container = container;
506                 folderDetailViewController.folder = item;
507                 folderDetailViewController.folderViewController = vc;
508                 [self presentPrimaryViewController:folderDetailViewController];
509                 self.folderDetailVC = folderDetailViewController;
510                 [folderDetailViewController release];
511             }
512             [vc release];
513         } else if ([[item class] isEqual:[StorageObject class]]) {
514             if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
515                 StorageObjectViewController *vc = [[StorageObjectViewController alloc] initWithNibName:@"StorageObjectViewController" bundle:nil];
516                 vc.account = self.account;
517                 vc.container = self.container;
518                 vc.folder = self.folder;
519                 vc.object = item;
520                 vc.folderViewController = self;
521                 [self presentPrimaryViewController:vc];
522                 self.selectedObjectViewController = vc;
523                 OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
524                 if (app.rootViewController.popoverController) {
525                     [app.rootViewController.popoverController dismissPopoverAnimated:YES];
526                 }
527                 [vc release];
528             } else {
529                 StorageObject *cellObject = (StorageObject *)item;
530                 BOOL fileDownloaded = NO;
531                 OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
532                 NSString *filePath = [app.cachedObjectsDictionary objectForKey:cellObject.hash];
533                 if (filePath) {
534                     if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
535                         fileDownloaded = YES;
536                     } else {
537                         [app.cachedObjectsDictionary removeObjectForKey:cellObject.hash];
538                         [app saveCacheDictionary];
539                     }
540                 }
541                 if (fileDownloaded) {
542                     self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
543                     self.documentInteractionController.delegate = self;
544                     self.documentInteractionController.name = cellObject.name;
545                     
546                     UITableViewCell *openFileCell = [self.tableView cellForRowAtIndexPath:indexPath];
547                     CGRect frameToPresentMenuFrom = CGRectMake(openFileCell.frame.origin.x,
548                                                                openFileCell.frame.origin.y - self.tableView.contentOffset.y,
549                                                                openFileCell.frame.size.width,
550                                                                openFileCell.frame.size.height);
551                     if (![self.documentInteractionController presentOptionsMenuFromRect:frameToPresentMenuFrom inView:self.view animated:YES]) {
552                         if ([cellObject isPlayableMedia]) {
553                             MediaViewController *vc = [[MediaViewController alloc] initWithNibName:@"MediaViewController" bundle:nil];
554                             vc.container = self.container;
555                             vc.object = cellObject;
556                             [self.navigationController pushViewController:vc animated:YES];
557                             [vc release];
558                         } else {
559                             [self alert:@"Error" message:@"This file could not be opened."];
560                         }
561                     }
562                 } else {
563                     // XXX else load download view controller
564                 }
565                 [aTableView deselectRowAtIndexPath:indexPath animated:YES];
566             }
567         }
568     }
569 }
570
571 - (void)tableView:(UITableView *)aTableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
572     if (folder.objectsAndFoldersCount) {
573         id item = [[self searchResults] objectAtIndex:indexPath.row];
574         if ([[item class] isEqual:[Folder class]]) {
575             FolderDetailViewController *vc = [[FolderDetailViewController alloc] initWithNibName:@"FolderDetailViewController" bundle:nil];
576             vc.account = account;
577             vc.container = container;
578             vc.folder = item;
579             vc.folderViewController = self;
580             [self.navigationController pushViewController:vc animated:YES];
581             [vc release];
582         } else if ([[item class] isEqual:[StorageObject class]]) {
583             StorageObjectViewController *vc = [[StorageObjectViewController alloc] initWithNibName:@"StorageObjectViewController" bundle:nil];
584             vc.account = self.account;
585             vc.container = self.container;
586             vc.folder = self.folder;
587             vc.object = item;
588             vc.folderViewController = self;
589             [self.navigationController pushViewController:vc animated:YES];
590             [vc release];
591         }
592     }
593 }
594
595 #pragma mark - UISearchDisplayDelegate
596
597 - (void)updateSearchBarPlaceholder:(NSInteger)selectedScope {
598     NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary];
599     NSDateFormatter *dateFormatterSearchPlaceholder = [threadDict objectForKey:@"dateFormatterSearchPlaceholder"];
600     if (!dateFormatterSearchPlaceholder) {
601         dateFormatterSearchPlaceholder = [[NSDateFormatter alloc] init];
602         [dateFormatterSearchPlaceholder setTimeZone:[NSTimeZone localTimeZone]];
603         [dateFormatterSearchPlaceholder setDateFormat:@"yyyy-MM-dd"];
604         [threadDict setObject:dateFormatterSearchPlaceholder forKey:@"dateFormatterSearchPlaceholder"];
605         [dateFormatterSearchPlaceholder release];
606     }
607     
608     if (selectedScope == 0) {
609         // all
610         self.searchBar.placeholder = [NSString stringWithFormat:@"e.g. .pdf or %@",
611                                       [dateFormatterSearchPlaceholder stringFromDate:[NSDate date]]];
612     } else if (selectedScope == 1) {
613         // name
614         self.searchBar.placeholder = @"e.g.: .pdf";
615     } else if (selectedScope == 2) {
616         // date
617         self.searchBar.placeholder = [NSString stringWithFormat:@"e.g.: %@",
618                                       [dateFormatterSearchPlaceholder stringFromDate:[NSDate date]]];
619     } else if (selectedScope == 3) {
620         // type
621         self.searchBar.placeholder = @"e.g.: .pdf or text/plain";
622     } else {
623         // default
624         self.searchBar.placeholder = @"Search Folder";
625     }
626 }
627
628 - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
629     self.searchFilter = searchString;
630     [self reloadData];
631     return NO;
632 }
633
634 - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
635     [self reloadData];
636     [self updateSearchBarPlaceholder:searchOption];
637     return NO;
638 }
639
640 - (void)searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)aTableView {
641     CGRect tableViewFrame = self.searchDisplayController.searchResultsTableView.frame;
642     tableViewFrame.size.height -= self.toolbar.frame.size.height;
643     self.searchDisplayController.searchResultsTableView.frame = tableViewFrame;
644 }
645
646 - (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
647     [self updateSearchBarPlaceholder:controller.searchBar.selectedScopeButtonIndex];
648 }
649
650 - (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
651     self.searchFilter = nil;
652     [self reloadData];
653     [self updateSearchBarPlaceholder:-1];
654 }
655
656 #pragma mark - Button Handlers
657
658 - (IBAction)homeButtonPressed:(id)sender {
659     [self.navigationController popToViewController:containersViewController.accountHomeViewController animated:YES];
660 }
661
662 - (IBAction)refreshButtonPressed:(id)sender {
663     __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Loading..."
664                                                                                                    andAddToView:self.view];
665     [self.account.manager getObjects:self.container];
666     successObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getObjectsSucceeded"
667                                                                         object:self.container
668                                                                          queue:[NSOperationQueue mainQueue]
669                                                                     usingBlock:^(NSNotification* notification)
670     {
671         [activityIndicatorView removeFromSuperview];
672         contentsLoaded = YES;
673         [self reloadFolderViewControllers];
674         needsRefreshing = NO;
675         if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
676            if (self.selectedObjectViewController) {
677                [self.selectedObjectViewController reloadMetadataSection];
678            } else if ([self.folder isEqual:container.rootFolder]) {
679                [containersViewController.containerDetailViewController reloadMetadataSection];
680            } else if ([parentFolderViewController.folderDetailVC.navigationController.topViewController 
681                        isEqual:parentFolderViewController.folderDetailVC]) {
682                [parentFolderViewController.folderDetailVC reloadMetadataSection];
683            }
684         }
685         [[NSNotificationCenter defaultCenter] removeObserver:successObserver];
686         [[NSNotificationCenter defaultCenter] removeObserver:failureObserver];
687     }];
688     failureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getObjectsFailed"
689                                                                         object:self.container
690                                                                          queue:[NSOperationQueue mainQueue]
691                                                                     usingBlock:^(NSNotification* notification)
692     {
693         [activityIndicatorView removeFromSuperview];
694         needsRefreshing = NO;
695         [self reloadData];
696         [self alert:@"Error" message:@"Failed to retrieve files from server."];
697         [[NSNotificationCenter defaultCenter] removeObserver:successObserver];
698         [[NSNotificationCenter defaultCenter] removeObserver:failureObserver];
699     }];
700 }
701
702 - (void)addButtonPressed:(id)sender {
703     AddObjectViewController *vc = [[AddObjectViewController alloc] initWithNibName:@"AddObjectViewController" bundle:nil];
704     vc.account = self.account;
705     vc.container = self.container;
706     vc.folder = self.folder;
707     vc.folderViewController = self;
708     if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
709         vc.modalPresentationStyle = UIModalPresentationFormSheet;
710         OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
711         if (app.rootViewController.popoverController != nil) {
712             [app.rootViewController.popoverController dismissPopoverAnimated:YES];
713         }
714     }
715     [self presentModalViewControllerWithNavigation:vc animated:YES];
716     [vc release];
717 }
718
719 - (void)deleteButtonPressed:(id)sender {
720     UIActionSheet *deleteActionSheet = [[[UIActionSheet alloc] initWithTitle:@"Are you sure you want to delete this folder? This operation cannot be undone."
721                                                                     delegate:self
722                                                            cancelButtonTitle:@"Cancel"
723                                                       destructiveButtonTitle:@"Delete Folder"
724                                                            otherButtonTitles:nil] autorelease];
725     if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
726         OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
727         [deleteActionSheet showInView:app.masterNavigationController.view];
728     } else {
729         [deleteActionSheet showInView:self.view];
730     }
731 }
732
733 #pragma mark - UIActionSheetDelegate
734
735 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
736     if (buttonIndex == 0) {
737         StorageObject *object = [[StorageObject alloc] init];
738         object.name = object.fullPath = [self.folder fullPath];
739         
740         __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Deleting folder..."
741                                                                                                        andAddToView:self.view
742                                                                                                        scrollOffset:self.tableView.contentOffset.y];
743         [[self.account.manager deleteObject:self.container object:object]
744          success:^(OpenStackRequest *request) {
745              if (self.folder.parent) {
746                  [self.folder.parent removeFolder:self.folder];
747              } else {
748                  [self.container.rootFolder removeFolder:self.folder];
749              }
750              [self.account persist];
751              
752              [activityIndicatorView removeFromSuperview];
753              [self.navigationController popViewControllerAnimated:YES];
754              
755              if ((self.folder.parent && !self.folder.parent.objectsAndFoldersCount) ||
756                  (!self.folder.parent && !self.container.rootFolder.objectsAndFoldersCount)) {
757                  self.parentFolderViewController.folder = self.parentFolderViewController.folder;
758              } else if ((self.folder.parent && self.folder.parent.objectsAndFoldersCount) ||
759                         (!self.folder.parent && self.container.rootFolder.objectsAndFoldersCount)) {
760                  [self.parentFolderViewController.tableView selectRowAtIndexPath:selectedFolderIndexPath
761                                                                         animated:YES
762                                                                   scrollPosition:UITableViewScrollPositionNone];
763                  if (self.parentFolderViewController.searchDisplayController.active &&
764                      self.parentFolderViewController.searchFilter && self.parentFolderViewController.searchFilter.length)
765                      [self.parentFolderViewController.searchDisplayController.searchResultsTableView selectRowAtIndexPath:selectedFolderIndexPath
766                                                                                                                  animated:YES
767                                                                                                            scrollPosition:UITableViewScrollPositionNone];
768                  [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteFolderRow) userInfo:nil repeats:NO];
769              }
770              if (([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) && self.folder.parent)
771                  [self.parentFolderViewController setDetailViewController];
772              [object release];
773          }
774          failure:^(OpenStackRequest *request) {
775              [activityIndicatorView removeFromSuperview];
776              [self alert:@"There was a problem deleting this folder." request:request];
777              [object release];
778          }];
779     }
780 }
781
782 #pragma mark - UIDocumentInteractionControllerDelegate
783
784 - (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller {
785     return self.navigationController;
786 }
787
788 @end