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"
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"
26 @implementation FolderViewController
28 @synthesize account, container, folder, containersViewController, selectedContainerIndexPath, contentsLoaded, parentFolderViewController, selectedFolderIndexPath, tableView;
30 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
31 return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
35 #pragma mark View lifecycle
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];
42 UIImage *buttonImage = [UIImage imageNamed:@"HomeFolderIcon.png"];
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;
54 - (void)viewWillAppear:(BOOL)animated {
55 [super viewWillAppear:animated];
57 if (folder.name && ![@"" isEqualToString:folder.name]) {
58 self.navigationItem.title = self.folder.name;
60 self.navigationItem.title = self.container.name;
64 - (void)viewDidAppear:(BOOL)animated {
65 [super viewDidAppear:animated];
68 [self refreshButtonPressed:nil];
72 - (void)viewWillDisappear:(BOOL)animated {
73 [super viewWillDisappear:animated];
75 if (!(folder.name && ![@"" isEqualToString:folder.name])) {
76 // [self.containersViewController.tableView deselectRowAtIndexPath:self.selectedContainerIndexPath animated:YES];
80 - (IBAction)homeButtonPressed:(id)sender {
81 [self.navigationController popToViewController:containersViewController animated:YES];
84 - (IBAction)refreshButtonPressed:(id)sender {
85 activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:@"Loading..."] text:@"Loading..."];
86 [activityIndicatorView addToView:self.view];
88 [self.account.manager getObjects:self.container];
91 successObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getObjectsSucceeded" object:self.container
92 queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification)
96 [[NSNotificationCenter defaultCenter] removeObserver:successObserver];
97 [activityIndicatorView removeFromSuperviewAndRelease];
100 failureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"renameServerSucceeded" object:self.container
101 queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification)
103 [self.tableView reloadData];
104 [[NSNotificationCenter defaultCenter] removeObserver:failureObserver];
105 [activityIndicatorView removeFromSuperviewAndRelease];
111 - (void)reloadFolders
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];
119 for (int i = rootFolderControllerIndex + 1; i < [viewControllers count]; i++) {
120 FolderViewController *folderViewController = [viewControllers objectAtIndex:i];
121 Folder *parentFolder = folderViewController.parentFolderViewController.folder;
123 folderViewController.folder = [parentFolder.folders objectForKey:folderViewController.folder.name];
124 if (!folderViewController.folder)
125 [self.navigationController popViewControllerAnimated:YES];
127 [folderViewController.tableView reloadData];
130 [self.containersViewController refreshButtonPressed:nil];
134 #pragma mark Table view data source
136 - (NSInteger)numberOfSectionsInTableView:(UITableView *)aTableView {
141 - (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {
142 return MAX(contentsLoaded ? 1 : 0, [folder.objects count] + [folder.folders count]);
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;
151 aTableView.scrollEnabled = YES;
152 aTableView.allowsSelection = YES;
153 return aTableView.rowHeight;
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."];
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:)];
166 static NSString *CellIdentifier = @"Cell";
168 UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
170 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
173 id item = [self.folder.sortedContents objectAtIndex:indexPath.row];
174 cell.textLabel.text = [item name];
176 if ([[item class] isEqual:[Folder class]]) {
177 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
178 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
180 cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
182 cell.imageView.image = [UIImage imageNamed:@"folder-icon.png"];
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, ";
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";
196 cell.detailTextLabel.text = [NSString stringWithFormat:@"%@%@", folderString, objectString];
197 } else if ([[item class] isEqual:[StorageObject class]]) {
198 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
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];
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];
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"];
220 cell.detailTextLabel.text = [NSString stringWithFormat:@"%@, %@", [item humanizedBytes], [item contentType]];
228 #pragma mark Table view delegate
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;
238 vc.contentsLoaded = YES;
239 vc.selectedFolderIndexPath = indexPath;
240 vc.parentFolderViewController = self;
241 vc.containersViewController = self.containersViewController;
242 [self.navigationController pushViewController:vc animated:YES];
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];
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];
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;
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];
272 [self.navigationController pushViewController:vc animated:YES];
273 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
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];
285 FolderDetailViewController *vc = [[FolderDetailViewController alloc] initWithNibName:@"FolderDetailViewController" bundle:nil];
286 vc.account = account;
287 vc.container = container;
288 vc.folder = folderForDetailView;
290 [self.navigationController pushViewController:vc animated:YES];
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];
310 [self presentModalViewControllerWithNavigation:vc animated:YES];
314 - (void)deleteButtonPressed:(id)sender {
315 [deleteActionSheet showInView:self.view];
319 #pragma mark Action Sheet Delegate
321 - (void)deleteFolderRow {
322 [self.parentFolderViewController.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selectedFolderIndexPath] withRowAnimation:UITableViewRowAnimationLeft];
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];
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];
334 [self.account.manager deleteObject:self.container object:object];
337 successObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"deleteObjectSucceeded" object:object
338 queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification)
340 if (self.folder.parent) {
341 [self.folder.parent.folders removeObjectForKey:self.folder.name];
343 [self.container.rootFolder.folders removeObjectForKey:self.folder.name];
345 [self.account persist];
347 [activityIndicatorView removeFromSuperviewAndRelease];
348 [self.navigationController popViewControllerAnimated:YES];
350 if (self.folder.parent) {
351 if ([self.folder.parent.folders count] + [self.folder.parent.objects count] == 0) {
352 [self.parentFolderViewController.tableView reloadData];
354 [self.parentFolderViewController.tableView selectRowAtIndexPath:selectedFolderIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
355 [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteFolderRow) userInfo:nil repeats:NO];
358 if ([self.container.rootFolder.folders count] + [self.container.rootFolder.objects count] == 0) {
359 [self.parentFolderViewController.tableView reloadData];
361 [self.parentFolderViewController.tableView selectRowAtIndexPath:selectedFolderIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
362 [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteFolderRow) userInfo:nil repeats:NO];
368 [[NSNotificationCenter defaultCenter] removeObserver:successObserver];
372 failureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"deleteObjectFailed" object:object
373 queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification)
375 [activityIndicatorView removeFromSuperviewAndRelease];
376 [self alert:@"There was a problem deleting this folder." request:[notification.userInfo objectForKey:@"request"]];
377 [[NSNotificationCenter defaultCenter] removeObserver:failureObserver];
384 #pragma mark Memory management
390 [containersViewController release];
391 [selectedContainerIndexPath release];
392 [deleteActionSheet release];
393 [parentFolderViewController release];
394 [selectedFolderIndexPath release];