2 // FolderViewController.m
5 // Created by Mike Mayo on 12/15/10.
6 // The OpenStack project is provided under the Apache 2.0 license.
9 #import "FolderViewController.h"
10 #import "OpenStackAccount.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"
27 #import "APICallback.h"
28 #import <MobileCoreServices/MobileCoreServices.h>
29 #import "ComputeModel.h"
30 #import "MediaViewController.h"
32 static NSString *kSortTypeKey = @"sortType";
33 static NSString *kSortNameDirectionKey = @"sortNameDirection";
34 static NSString *kSortDateDirectionKey = @"sortDateDirection";
36 @implementation FolderViewController
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;
43 #pragma mark - View lifecycle
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;
56 [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
59 - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
60 [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
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;
72 self.searchActiveOnRotation = NO;
82 - (void)viewWillAppear:(BOOL)animated {
83 [super viewWillAppear:animated];
84 if (self.name && self.name.length) {
85 self.navigationItem.title = self.name;
87 self.navigationItem.title = self.container.name;
90 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
92 self.sortType = [userDefaults integerForKey:kSortTypeKey];
93 self.sortTypeButton.title = (self.sortType == FolderSortTypeDate) ? @"date" : @"name";
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";
100 self.sortDirectionButton.title = (self.sortNameDirection == FolderSortNameDirectionZA) ? @"Z-A" : @"A-Z";
106 - (void)viewDidAppear:(BOOL)animated {
107 [super viewDidAppear:animated];
109 if (folderHasBeenRemoved) {
111 self.parentFolderViewController.needsRefreshing = YES;
112 [self.navigationController popViewControllerAnimated:YES];
114 [self refreshButtonPressed:nil];
116 } else if (self.needsRefreshing) {
117 self.parentFolderViewController.needsRefreshing = YES;
118 if (refreshWhenAppeared) {
119 [self refreshButtonPressed:nil];
120 refreshWhenAppeared = NO;
122 if (account.shared && !folder.objectsAndFoldersCount) {
123 [self.navigationController popViewControllerAnimated:YES];
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];
137 #pragma mark - Memory management
143 [containersViewController release];
144 [selectedContainerIndexPath release];
145 [parentFolderViewController release];
146 [selectedFolderIndexPath release];
147 [refreshButton release];
149 [searchDisplayController release];
150 [searchFilter release];
151 [sortTypeButton release];
152 [sortDirectionButton release];
153 [documentInteractionController release];
157 #pragma mark - Internal
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);
165 if (self.searchDisplayController.active)
166 [self.searchDisplayController setActive:NO animated:NO];
167 self.tableView.tableHeaderView = nil;
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];
184 - (void)reloadFolderViewControllers {
185 NSArray *viewControllers = [self.navigationController viewControllers];
187 int rootFolderControllerIndex = [viewControllers indexOfObject:self.containersViewController] + 1;
188 FolderViewController *rootFolderViewController = [viewControllers objectAtIndex:rootFolderControllerIndex];
189 rootFolderViewController.folder = self.container.rootFolder;
191 for (int i = rootFolderControllerIndex + 1; i < [viewControllers count]; i++) {
192 FolderViewController *folderViewController = [viewControllers objectAtIndex:i];
193 Folder *parentFolder = folderViewController.parentFolderViewController.folder;
195 folderViewController.folder = [parentFolder.folders objectForKey:folderViewController.folder.name];
196 if (!folderViewController.folder)
197 folderViewController.folderHasBeenRemoved = YES;
199 if (!self.folder || (account.shared && !folder.objectsAndFoldersCount)) {
200 if (needsRefreshing && self.parentFolderViewController) {
201 self.parentFolderViewController.needsRefreshing = YES;
203 [self.navigationController popViewControllerAnimated:YES];
204 } else if (([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) && account.shared) {
205 [self setDetailViewController];
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];
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;
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)];
235 #pragma mark - Properties
237 - (void)setFolder:(Folder *)aFolder {
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];
244 self.selectedObjectViewController.object = newObject;
245 // We don't set nil otherwise, this will be done in reloadData, when setDetailViewController is called.
250 #pragma mark - Actions
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];
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
267 scrollPosition:UITableViewScrollPositionNone];
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;
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;
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
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];
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";
313 self.sortType = FolderSortTypeDate;
314 self.sortTypeButton.title = @"date";
315 self.sortDirectionButton.title = (self.sortDateDirection == FolderSortDateDirectionNewestFirst) ? @"newest first" : @"oldest first";
318 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
319 [userDefaults setInteger:self.sortType forKey:kSortTypeKey];
320 [userDefaults synchronize];
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";
330 self.sortDateDirection = FolderSortDateDirectionNewestFirst;
331 self.sortDirectionButton.title = @"newest first";
333 [userDefaults setInteger:self.sortDateDirection forKey:kSortDateDirectionKey];
335 if (self.sortNameDirection == FolderSortNameDirectionZA) {
336 self.sortNameDirection = FolderSortNameDirectionAZ;
337 self.sortDirectionButton.title = @"A-Z";
339 self.sortNameDirection = FolderSortNameDirectionZA;
340 self.sortDirectionButton.title = @"Z-A";
342 [userDefaults setInteger:self.sortNameDirection forKey:kSortNameDirectionKey];
345 [userDefaults synchronize];
348 #pragma mark - UITableViewDataSource
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) {
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;
368 aTableView.scrollEnabled = NO;
369 aTableView.allowsSelection = NO;
370 return aTableView.frame.size.height;
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;
384 if ([self.container.rootFolder isEqual:self.folder]) {
385 return [self tableView:aTableView
386 emptyCellWithImage:[UIImage imageNamed:@"empty-files.png"]
388 subtitle:noFoldersSubtitle];
390 return [self tableView:aTableView
391 emptyCellWithImage:[UIImage imageNamed:@"empty-files.png"]
393 subtitle:noFoldersSubtitle
394 deleteButtonTitle:deleteFolderButtonTitle
395 deleteButtonSelector:deleteButtonSelector];
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];
402 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
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;
412 cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
415 cell.imageView.image = [UIImage imageNamed:@"folder-icon.png"];
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, ";
423 // NSString *objectString = @"";
424 // if (cellFolder.objects.count != 1) {
425 // objectString = [NSString stringWithFormat:@"%u objects", cellFolder.objects.count];
427 // objectString = @"1 object";
429 // cell.detailTextLabel.text = [NSString stringWithFormat:@"%@%@", folderString, objectString];
431 if ([[UIDevice currentDevice].systemVersion hasPrefix:@"4"]) {
433 if (cellFolder.lastModifiedString) {
434 cell.detailTextLabel.text = [NSString stringWithFormat:@"%u fold. %u obj. %@",
435 cellFolder.folders.count, cellFolder.objects.count, cellFolder.lastModifiedString];
437 cell.detailTextLabel.text = [NSString stringWithFormat:@"%u folders %u objects", cellFolder.folders.count, cellFolder.objects.count];
441 if (cellFolder.lastModifiedString) {
442 cell.detailTextLabel.text = [NSString stringWithFormat:@"%u 📁 %u 📄 %@",
443 cellFolder.folders.count, cellFolder.objects.count, cellFolder.lastModifiedString];
445 cell.detailTextLabel.text = [NSString stringWithFormat:@"%u 📁 %u 📄", cellFolder.folders.count, cellFolder.objects.count];
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;
455 cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
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];
467 if (cellObject.lastModifiedString) {
468 cell.detailTextLabel.text = [NSString stringWithFormat:@"%@, %@", cellObject.humanizedBytes, cellObject.lastModifiedString];
470 cell.detailTextLabel.text = cellObject.humanizedBytes;
473 if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
482 #pragma mark - UITableViewDelegate
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;
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];
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;
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];
529 StorageObject *cellObject = (StorageObject *)item;
530 BOOL fileDownloaded = NO;
531 OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
532 NSString *filePath = [app.cachedObjectsDictionary objectForKey:cellObject.hash];
534 if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
535 fileDownloaded = YES;
537 [app.cachedObjectsDictionary removeObjectForKey:cellObject.hash];
538 [app saveCacheDictionary];
541 if (fileDownloaded) {
542 self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
543 self.documentInteractionController.delegate = self;
544 self.documentInteractionController.name = cellObject.name;
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];
559 [self alert:@"Error" message:@"This file could not be opened."];
563 // XXX else load download view controller
565 [aTableView deselectRowAtIndexPath:indexPath animated:YES];
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;
579 vc.folderViewController = self;
580 [self.navigationController pushViewController:vc animated:YES];
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;
588 vc.folderViewController = self;
589 [self.navigationController pushViewController:vc animated:YES];
595 #pragma mark - UISearchDisplayDelegate
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];
608 if (selectedScope == 0) {
610 self.searchBar.placeholder = [NSString stringWithFormat:@"e.g. .pdf or %@",
611 [dateFormatterSearchPlaceholder stringFromDate:[NSDate date]]];
612 } else if (selectedScope == 1) {
614 self.searchBar.placeholder = @"e.g.: .pdf";
615 } else if (selectedScope == 2) {
617 self.searchBar.placeholder = [NSString stringWithFormat:@"e.g.: %@",
618 [dateFormatterSearchPlaceholder stringFromDate:[NSDate date]]];
619 } else if (selectedScope == 3) {
621 self.searchBar.placeholder = @"e.g.: .pdf or text/plain";
624 self.searchBar.placeholder = @"Search Folder";
628 - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
629 self.searchFilter = searchString;
634 - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
636 [self updateSearchBarPlaceholder:searchOption];
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;
646 - (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
647 [self updateSearchBarPlaceholder:controller.searchBar.selectedScopeButtonIndex];
650 - (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
651 self.searchFilter = nil;
653 [self updateSearchBarPlaceholder:-1];
656 #pragma mark - Button Handlers
658 - (IBAction)homeButtonPressed:(id)sender {
659 [self.navigationController popToViewController:containersViewController.accountHomeViewController animated:YES];
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)
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];
685 [[NSNotificationCenter defaultCenter] removeObserver:successObserver];
686 [[NSNotificationCenter defaultCenter] removeObserver:failureObserver];
688 failureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getObjectsFailed"
689 object:self.container
690 queue:[NSOperationQueue mainQueue]
691 usingBlock:^(NSNotification* notification)
693 [activityIndicatorView removeFromSuperview];
694 needsRefreshing = NO;
696 [self alert:@"Error" message:@"Failed to retrieve files from server."];
697 [[NSNotificationCenter defaultCenter] removeObserver:successObserver];
698 [[NSNotificationCenter defaultCenter] removeObserver:failureObserver];
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];
715 [self presentModalViewControllerWithNavigation:vc animated:YES];
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."
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];
729 [deleteActionSheet showInView:self.view];
733 #pragma mark - UIActionSheetDelegate
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];
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];
748 [self.container.rootFolder removeFolder:self.folder];
750 [self.account persist];
752 [activityIndicatorView removeFromSuperview];
753 [self.navigationController popViewControllerAnimated:YES];
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
762 scrollPosition:UITableViewScrollPositionNone];
763 if (self.parentFolderViewController.searchDisplayController.active &&
764 self.parentFolderViewController.searchFilter && self.parentFolderViewController.searchFilter.length)
765 [self.parentFolderViewController.searchDisplayController.searchResultsTableView selectRowAtIndexPath:selectedFolderIndexPath
767 scrollPosition:UITableViewScrollPositionNone];
768 [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteFolderRow) userInfo:nil repeats:NO];
770 if (([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) && self.folder.parent)
771 [self.parentFolderViewController setDetailViewController];
774 failure:^(OpenStackRequest *request) {
775 [activityIndicatorView removeFromSuperview];
776 [self alert:@"There was a problem deleting this folder." request:request];
782 #pragma mark - UIDocumentInteractionControllerDelegate
784 - (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller {
785 return self.navigationController;