Expanded open file functionality to use available apps.
[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 "Folder.h"
13 #import "StorageObject.h"
14 #import "StorageObjectViewController.h"
15 #import "ContainerDetailViewController.h"
16 #import "AccountManager.h"
17 #import "ActivityIndicatorView.h"
18 #import "ContainersViewController.h"
19 #import "UIViewController+Conveniences.h"
20 #import "AddObjectViewController.h"
21 #import "OpenStackAppDelegate.h"
22 #import "RootViewController.h"
23 #import "FolderDetailViewController.h"
24
25
26 @implementation FolderViewController
27
28 @synthesize account, container, folder, containersViewController, selectedContainerIndexPath, contentsLoaded, parentFolderViewController, selectedFolderIndexPath, tableView;
29
30 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
31     return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
32 }
33
34 #pragma mark -
35 #pragma mark View lifecycle
36
37 - (void)viewDidLoad {
38     [super viewDidLoad];
39     [self addAddButton];
40     deleteActionSheet = [[UIActionSheet alloc] initWithTitle:@"Are you sure you want to delete this folder?  This operation cannot be undone." delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete Folder" otherButtonTitles:nil];
41     
42     UIImage *buttonImage = [UIImage imageNamed:@"HomeFolderIcon.png"];
43         
44     UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
45     [button setImage:buttonImage forState:UIControlStateNormal];        
46     button.frame = CGRectMake(0, 0, buttonImage.size.width, buttonImage.size.height);
47         [button addTarget:self action:@selector(homeButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
48     UIBarButtonItem *customBarItem = [[UIBarButtonItem alloc] initWithCustomView:button];
49     NSMutableArray *toolbarItems = [NSMutableArray arrayWithArray:self.toolbar.items];
50     [toolbarItems addObject:customBarItem];
51     self.toolbar.items = toolbarItems;
52 }
53
54 - (void)viewWillAppear:(BOOL)animated {
55     [super viewWillAppear:animated];
56     
57     if (folder.name && ![@"" isEqualToString:folder.name]) {
58         self.navigationItem.title = self.folder.name;
59     } else {
60         self.navigationItem.title = self.container.name;
61     }
62 }
63
64 - (void)viewDidAppear:(BOOL)animated {
65     [super viewDidAppear:animated];
66     
67     if (!self.folder) {
68         [self refreshButtonPressed:nil];
69     }
70 }
71
72 - (void)viewWillDisappear:(BOOL)animated {
73     [super viewWillDisappear:animated];
74     
75     if (!(folder.name && ![@"" isEqualToString:folder.name])) {
76 //        [self.containersViewController.tableView deselectRowAtIndexPath:self.selectedContainerIndexPath animated:YES];
77     }
78 }
79
80 - (IBAction)homeButtonPressed:(id)sender {
81     [self.navigationController popToViewController:containersViewController animated:YES];
82 }
83
84 - (IBAction)refreshButtonPressed:(id)sender {
85     activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:@"Loading..."] text:@"Loading..."];
86     [activityIndicatorView addToView:self.view];
87         
88     [self.account.manager getObjects:self.container];
89     
90     
91     successObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getObjectsSucceeded" object:self.container
92                                                                          queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) 
93                        {   
94                            contentsLoaded = YES;
95                            [self reloadFolders];
96                            [[NSNotificationCenter defaultCenter] removeObserver:successObserver];
97                            [activityIndicatorView removeFromSuperviewAndRelease];
98                        }];
99     
100     failureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"renameServerSucceeded" object:self.container
101                                                                          queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) 
102                        {
103                            [self.tableView reloadData];                           
104                            [[NSNotificationCenter defaultCenter] removeObserver:failureObserver];
105                            [activityIndicatorView removeFromSuperviewAndRelease];
106                        }];
107     
108     
109 }
110
111 - (void)reloadFolders
112 {
113     NSArray *viewControllers = [self.navigationController viewControllers];
114     int rootFolderControllerIndex = [viewControllers indexOfObject:self.containersViewController] + 1;
115     FolderViewController *rootFolderViewController = [viewControllers objectAtIndex:rootFolderControllerIndex];
116     rootFolderViewController.folder = self.container.rootFolder;
117     [rootFolderViewController.tableView reloadData];
118     
119     for (int i = rootFolderControllerIndex + 1; i < [viewControllers count]; i++) {
120         FolderViewController *folderViewController = [viewControllers objectAtIndex:i];
121         Folder *parentFolder = folderViewController.parentFolderViewController.folder;
122         
123         folderViewController.folder = [parentFolder.folders objectForKey:folderViewController.folder.name];
124         if (!folderViewController.folder)
125             [self.navigationController popViewControllerAnimated:YES];
126         else
127             [folderViewController.tableView reloadData];
128     }
129     
130     [self.containersViewController refreshButtonPressed:nil];
131 }
132
133 #pragma mark -
134 #pragma mark Table view data source
135
136 - (NSInteger)numberOfSectionsInTableView:(UITableView *)aTableView {
137     return 1;
138 }
139
140
141 - (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {
142     return MAX(contentsLoaded ? 1 : 0, [folder.objects count] + [folder.folders count]);
143 }
144
145 - (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
146     if ([folder.objects count] + [folder.folders count] == 0) {
147         aTableView.scrollEnabled = NO;
148         aTableView.allowsSelection = NO;
149         return aTableView.frame.size.height;
150     } else {
151         aTableView.scrollEnabled = YES;
152         aTableView.allowsSelection = YES;
153         return aTableView.rowHeight;
154     }
155 }
156
157 // Customize the appearance of table view cells.
158 - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
159     if ([folder.objects count] + [folder.folders count] == 0) {
160         if ([self.container.rootFolder isEqual:self.folder]) {
161             return [self tableView:aTableView emptyCellWithImage:[UIImage imageNamed:@"empty-files.png"] title:@"No Files or Folders" subtitle:@"Tap the + button to add a new file or folder."];
162         } else {
163             return [self tableView:aTableView emptyCellWithImage:[UIImage imageNamed:@"empty-files.png"] title:@"No Files or Folders" subtitle:@"Tap the + button to add a new file or folder." deleteButtonTitle:@"Delete Folder" deleteButtonSelector:@selector(deleteButtonPressed:)];
164         }
165     } else {
166         static NSString *CellIdentifier = @"Cell";
167         
168         UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
169         if (cell == nil) {
170             cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
171         }
172         
173         id item = [self.folder.sortedContents objectAtIndex:indexPath.row];    
174         cell.textLabel.text = [item name];
175         
176         if ([[item class] isEqual:[Folder class]]) {
177             if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
178                 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
179             } else {
180                 cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
181             }
182             cell.imageView.image = [UIImage imageNamed:@"folder-icon.png"];
183             
184             NSString *folderString = @"";
185             NSString *objectString = @"";
186             if ([[item folders] count] > 1) {
187                 folderString = [NSString stringWithFormat:@"%i folders, ", [[item folders] count]];
188             } else if ([[item folders] count] > 0) {
189                 folderString = @"1 folder, ";
190             }
191             if ([[item objects] count] != 1) {
192                 objectString = [NSString stringWithFormat:@"%i objects", [[item objects] count]];
193             } else if ([[item objects] count]) {
194                 objectString = @"1 object";
195             }
196             cell.detailTextLabel.text = [NSString stringWithFormat:@"%@%@", folderString, objectString];
197         } else if ([[item class] isEqual:[StorageObject class]]) {
198             cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
199             
200             StorageObject *object = item;
201             NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
202             NSString *documentsDirectory = [paths objectAtIndex:0];
203             NSString *shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, object.fullPath];
204             NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
205
206             
207             if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
208                 UIDocumentInteractionController *udic = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
209                 if ([udic.icons count] > 0) {
210                     cell.imageView.image = [udic.icons objectAtIndex:0];                    
211                 }
212             } else {
213                 NSString *emptyPath = [[NSBundle mainBundle] pathForResource:@"empty-file" ofType:@""];
214                 UIDocumentInteractionController *udic = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:emptyPath]];
215                 if ([udic.icons count] > 0) {
216                     cell.imageView.image = [udic.icons objectAtIndex:0]; //[UIImage imageNamed:@"file-icon.png"];        
217                 }
218             }
219             
220             cell.detailTextLabel.text = [NSString stringWithFormat:@"%@, %@", [item humanizedBytes], [item contentType]];
221         }
222         
223         return cell;
224     }        
225 }
226
227 #pragma mark -
228 #pragma mark Table view delegate
229
230 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
231     if ([folder.objects count] + [folder.folders count] > 0) {
232         id item = [self.folder.sortedContents objectAtIndex:indexPath.row];        
233         if ([[item class] isEqual:[Folder class]]) {
234             FolderViewController *vc = [[FolderViewController alloc] initWithNibName:@"FolderViewController" bundle:nil];
235             vc.account = self.account;
236             vc.container = self.container;
237             vc.folder = item;
238             vc.contentsLoaded = YES;
239             vc.selectedFolderIndexPath = indexPath;
240             vc.parentFolderViewController = self;
241             vc.containersViewController = self.containersViewController;
242             [self.navigationController pushViewController:vc animated:YES];
243             [vc release];
244             [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
245             if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
246                 Folder *folderForDetailView;
247                 if ([folder.sortedContents count] > 0) {
248                     folderForDetailView = [self.folder.sortedContents objectAtIndex:indexPath.row];
249                 }
250                 FolderDetailViewController *folderDetailViewController = [[FolderDetailViewController alloc] initWithNibName:@"FolderDetailViewController" bundle:nil];
251                 folderDetailViewController.account = account;
252                 folderDetailViewController.container = container;
253                 folderDetailViewController.folder = folderForDetailView;
254                 [self presentPrimaryViewController:folderDetailViewController];
255                 [folderDetailViewController release];
256             }
257         } else if ([[item class] isEqual:[StorageObject class]]) {
258             StorageObjectViewController *vc = [[StorageObjectViewController alloc] initWithNibName:@"StorageObjectViewController" bundle:nil];
259             vc.account = self.account;
260             vc.container = self.container;
261             vc.folder = self.folder;
262             vc.object = item;
263             vc.folderViewController = self;
264             vc.selectedIndexPath = indexPath;
265             if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
266                 [self.navigationController presentPrimaryViewController:vc];
267                 OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
268                 if (app.rootViewController.popoverController != nil) {
269                     [app.rootViewController.popoverController dismissPopoverAnimated:YES];
270                 }
271             } else {
272                 [self.navigationController pushViewController:vc animated:YES];
273                 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
274             }
275             [vc release];
276         }
277     }
278 }
279
280 - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
281     Folder *folderForDetailView;
282     if ([folder.sortedContents count] > 0) {
283         folderForDetailView = [self.folder.sortedContents objectAtIndex:indexPath.row];
284     }
285     FolderDetailViewController *vc = [[FolderDetailViewController alloc] initWithNibName:@"FolderDetailViewController" bundle:nil];
286     vc.account = account;
287     vc.container = container;
288     vc.folder = folderForDetailView;
289
290     [self.navigationController pushViewController:vc animated:YES];
291     [vc release];
292 }
293
294 #pragma mark -
295 #pragma mark Buttons
296
297 - (void)addButtonPressed:(id)sender {
298     AddObjectViewController *vc = [[AddObjectViewController alloc] initWithNibName:@"AddObjectViewController" bundle:nil];
299     vc.account = self.account;
300     vc.container = self.container;
301     vc.folder = self.folder;
302     vc.folderViewController = self;
303     if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
304         vc.modalPresentationStyle = UIModalPresentationFormSheet;
305         OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
306         if (app.rootViewController.popoverController != nil) {
307             [app.rootViewController.popoverController dismissPopoverAnimated:YES];
308         }
309     }
310     [self presentModalViewControllerWithNavigation:vc animated:YES];
311     [vc release];
312 }
313
314 - (void)deleteButtonPressed:(id)sender {
315     [deleteActionSheet showInView:self.view];    
316 }
317
318 #pragma mark -
319 #pragma mark Action Sheet Delegate
320
321 - (void)deleteFolderRow {
322     [self.parentFolderViewController.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selectedFolderIndexPath] withRowAnimation:UITableViewRowAnimationLeft];
323 }
324
325 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
326     if (buttonIndex == 0) {
327         StorageObject *object = [[StorageObject alloc] init];
328         object.name = object.fullPath = [self.folder fullPath];
329         
330         NSString *activityMessage = @"Deleting folder...";
331         activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
332         [activityIndicatorView addToView:self.view scrollOffset:self.tableView.contentOffset.y];    
333
334         [self.account.manager deleteObject:self.container object:object];
335         
336         
337         successObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"deleteObjectSucceeded" object:object
338                                                                              queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) 
339         {
340             if (self.folder.parent) {
341                 [self.folder.parent.folders removeObjectForKey:self.folder.name];
342             } else {
343                 [self.container.rootFolder.folders removeObjectForKey:self.folder.name];
344             }
345             [self.account persist];
346
347             [activityIndicatorView removeFromSuperviewAndRelease];
348             [self.navigationController popViewControllerAnimated:YES];
349
350             if (self.folder.parent) {
351                 if ([self.folder.parent.folders count] + [self.folder.parent.objects count] == 0) {
352                     [self.parentFolderViewController.tableView reloadData];
353                 } else {
354                     [self.parentFolderViewController.tableView selectRowAtIndexPath:selectedFolderIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
355                     [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteFolderRow) userInfo:nil repeats:NO];
356                 }
357             } else {
358                 if ([self.container.rootFolder.folders count] + [self.container.rootFolder.objects count] == 0) {
359                     [self.parentFolderViewController.tableView reloadData];
360                 } else {
361                     [self.parentFolderViewController.tableView selectRowAtIndexPath:selectedFolderIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
362                     [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteFolderRow) userInfo:nil repeats:NO];
363                 }
364             }
365             
366             
367             
368             [[NSNotificationCenter defaultCenter] removeObserver:successObserver];
369             [object release];
370         }];
371         
372         failureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"deleteObjectFailed" object:object
373                                                                              queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) 
374         {
375             [activityIndicatorView removeFromSuperviewAndRelease];
376             [self alert:@"There was a problem deleting this folder." request:[notification.userInfo objectForKey:@"request"]];
377             [[NSNotificationCenter defaultCenter] removeObserver:failureObserver];
378             [object release];
379         }];
380     }
381 }
382
383 #pragma mark -
384 #pragma mark Memory management
385
386 - (void)dealloc {
387     [account release];
388     [container release];
389     [folder release];
390     [containersViewController release];
391     [selectedContainerIndexPath release];
392     [deleteActionSheet release];
393     [parentFolderViewController release];
394     [selectedFolderIndexPath release];
395     [super dealloc];
396 }
397
398
399 @end
400