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];
43 - (void)viewWillAppear:(BOOL)animated {
44 [super viewWillAppear:animated];
46 if (folder.name && ![@"" isEqualToString:folder.name]) {
47 self.navigationItem.title = self.folder.name;
49 self.navigationItem.title = self.container.name;
53 - (void)viewDidAppear:(BOOL)animated {
54 [super viewDidAppear:animated];
57 activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:@"Loading..."] text:@"Loading..."];
58 [activityIndicatorView addToView:self.view];
60 [self.account.manager getObjects:self.container];
64 successObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getObjectsSucceeded" object:self.container
65 queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification)
67 self.folder = self.container.rootFolder;
69 [self.tableView reloadData];
70 [[NSNotificationCenter defaultCenter] removeObserver:successObserver];
71 [activityIndicatorView removeFromSuperviewAndRelease];
74 failureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"renameServerSucceeded" object:self.container
75 queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification)
77 [self.tableView reloadData];
78 [[NSNotificationCenter defaultCenter] removeObserver:failureObserver];
79 [activityIndicatorView removeFromSuperviewAndRelease];
84 - (void)viewWillDisappear:(BOOL)animated {
85 [super viewWillDisappear:animated];
87 if (!(folder.name && ![@"" isEqualToString:folder.name])) {
88 // [self.containersViewController.tableView deselectRowAtIndexPath:self.selectedContainerIndexPath animated:YES];
92 - (IBAction)homeButtonPressed:(id)sender {
93 [self.navigationController popToRootViewControllerAnimated:YES];
97 #pragma mark Table view data source
99 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
104 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
105 return MAX(contentsLoaded ? 1 : 0, [folder.objects count] + [folder.folders count]);
108 - (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
109 if ([folder.objects count] + [folder.folders count] == 0) {
110 aTableView.scrollEnabled = NO;
111 aTableView.allowsSelection = NO;
112 return aTableView.frame.size.height;
114 aTableView.scrollEnabled = YES;
115 aTableView.allowsSelection = YES;
116 return aTableView.rowHeight;
120 // Customize the appearance of table view cells.
121 - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
122 if ([folder.objects count] + [folder.folders count] == 0) {
123 if ([self.container.rootFolder isEqual:self.folder]) {
124 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."];
126 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:)];
129 static NSString *CellIdentifier = @"Cell";
131 UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
133 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
136 id item = [self.folder.sortedContents objectAtIndex:indexPath.row];
137 cell.textLabel.text = [item name];
139 if ([[item class] isEqual:[Folder class]]) {
140 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
141 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
143 cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
145 cell.imageView.image = [UIImage imageNamed:@"folder-icon.png"];
147 NSString *folderString = @"";
148 NSString *objectString = @"";
149 if ([[item folders] count] > 1) {
150 folderString = [NSString stringWithFormat:@"%i folders, ", [[item folders] count]];
151 } else if ([[item folders] count] > 0) {
152 folderString = @"1 folder, ";
154 if ([[item objects] count] != 1) {
155 objectString = [NSString stringWithFormat:@"%i objects", [[item objects] count]];
156 } else if ([[item objects] count]) {
157 objectString = @"1 object";
159 cell.detailTextLabel.text = [NSString stringWithFormat:@"%@%@", folderString, objectString];
160 } else if ([[item class] isEqual:[StorageObject class]]) {
161 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
163 StorageObject *object = item;
164 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
165 NSString *documentsDirectory = [paths objectAtIndex:0];
166 NSString *shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, object.fullPath];
167 NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
170 if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
171 UIDocumentInteractionController *udic = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
172 if ([udic.icons count] > 0) {
173 cell.imageView.image = [udic.icons objectAtIndex:0];
176 NSString *emptyPath = [[NSBundle mainBundle] pathForResource:@"empty-file" ofType:@""];
177 UIDocumentInteractionController *udic = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:emptyPath]];
178 if ([udic.icons count] > 0) {
179 cell.imageView.image = [udic.icons objectAtIndex:0]; //[UIImage imageNamed:@"file-icon.png"];
183 cell.detailTextLabel.text = [NSString stringWithFormat:@"%@, %@", [item humanizedBytes], [item contentType]];
191 #pragma mark Table view delegate
193 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
194 if ([folder.objects count] + [folder.folders count] > 0) {
195 id item = [self.folder.sortedContents objectAtIndex:indexPath.row];
196 if ([[item class] isEqual:[Folder class]]) {
197 FolderViewController *vc = [[FolderViewController alloc] initWithNibName:@"FolderViewController" bundle:nil];
198 vc.account = self.account;
199 vc.container = self.container;
201 vc.contentsLoaded = YES;
202 vc.selectedFolderIndexPath = indexPath;
203 vc.parentFolderViewController = self;
204 [self.navigationController pushViewController:vc animated:YES];
206 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
207 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
208 Folder *folderForDetailView;
209 if ([folder.sortedContents count] > 0) {
210 folderForDetailView = [self.folder.sortedContents objectAtIndex:indexPath.row];
212 FolderDetailViewController *folderDetailViewController = [[FolderDetailViewController alloc] initWithNibName:@"FolderDetailViewController" bundle:nil];
213 folderDetailViewController.account = account;
214 folderDetailViewController.container = container;
215 folderDetailViewController.folder = folderForDetailView;
216 [self presentPrimaryViewController:folderDetailViewController];
217 [folderDetailViewController release];
219 } else if ([[item class] isEqual:[StorageObject class]]) {
220 StorageObjectViewController *vc = [[StorageObjectViewController alloc] initWithNibName:@"StorageObjectViewController" bundle:nil];
221 vc.account = self.account;
222 vc.container = self.container;
223 vc.folder = self.folder;
225 vc.folderViewController = self;
226 vc.selectedIndexPath = indexPath;
227 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
228 [self.navigationController presentPrimaryViewController:vc];
229 OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
230 if (app.rootViewController.popoverController != nil) {
231 [app.rootViewController.popoverController dismissPopoverAnimated:YES];
234 [self.navigationController pushViewController:vc animated:YES];
235 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
242 - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
243 Folder *folderForDetailView;
244 if ([folder.sortedContents count] > 0) {
245 folderForDetailView = [self.folder.sortedContents objectAtIndex:indexPath.row];
247 FolderDetailViewController *vc = [[FolderDetailViewController alloc] initWithNibName:@"FolderDetailViewController" bundle:nil];
248 vc.account = account;
249 vc.container = container;
250 vc.folder = folderForDetailView;
252 [self.navigationController pushViewController:vc animated:YES];
259 - (void)addButtonPressed:(id)sender {
260 AddObjectViewController *vc = [[AddObjectViewController alloc] initWithNibName:@"AddObjectViewController" bundle:nil];
261 vc.account = self.account;
262 vc.container = self.container;
263 vc.folder = self.folder;
264 vc.folderViewController = self;
265 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
266 vc.modalPresentationStyle = UIModalPresentationFormSheet;
267 OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
268 if (app.rootViewController.popoverController != nil) {
269 [app.rootViewController.popoverController dismissPopoverAnimated:YES];
272 [self presentModalViewControllerWithNavigation:vc animated:YES];
276 - (void)deleteButtonPressed:(id)sender {
277 [deleteActionSheet showInView:self.view];
281 #pragma mark Action Sheet Delegate
283 - (void)deleteFolderRow {
284 [self.parentFolderViewController.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selectedFolderIndexPath] withRowAnimation:UITableViewRowAnimationLeft];
287 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
288 if (buttonIndex == 0) {
289 StorageObject *object = [[StorageObject alloc] init];
290 object.name = object.fullPath = [self.folder fullPath];
292 NSString *activityMessage = @"Deleting folder...";
293 activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
294 [activityIndicatorView addToView:self.view scrollOffset:self.tableView.contentOffset.y];
296 [self.account.manager deleteObject:self.container object:object];
299 successObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"deleteObjectSucceeded" object:object
300 queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification)
302 if (self.folder.parent) {
303 [self.folder.parent.folders removeObjectForKey:self.folder.name];
305 [self.container.rootFolder.folders removeObjectForKey:self.folder.name];
307 [self.account persist];
309 [activityIndicatorView removeFromSuperviewAndRelease];
310 [self.navigationController popViewControllerAnimated:YES];
312 if (self.folder.parent) {
313 if ([self.folder.parent.folders count] + [self.folder.parent.objects count] == 0) {
314 [self.parentFolderViewController.tableView reloadData];
316 [self.parentFolderViewController.tableView selectRowAtIndexPath:selectedFolderIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
317 [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteFolderRow) userInfo:nil repeats:NO];
320 if ([self.container.rootFolder.folders count] + [self.container.rootFolder.objects count] == 0) {
321 [self.parentFolderViewController.tableView reloadData];
323 [self.parentFolderViewController.tableView selectRowAtIndexPath:selectedFolderIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
324 [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteFolderRow) userInfo:nil repeats:NO];
330 [[NSNotificationCenter defaultCenter] removeObserver:successObserver];
334 failureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"deleteObjectFailed" object:object
335 queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification)
337 [activityIndicatorView removeFromSuperviewAndRelease];
338 [self alert:@"There was a problem deleting this folder." request:[notification.userInfo objectForKey:@"request"]];
339 [[NSNotificationCenter defaultCenter] removeObserver:failureObserver];
346 #pragma mark Memory management
352 [containersViewController release];
353 [selectedContainerIndexPath release];
354 [deleteActionSheet release];
355 [parentFolderViewController release];
356 [selectedFolderIndexPath release];