11 #import "BookmarkListController.h"
13 #import "BookmarkEditorController.h"
14 #import "RDPSessionViewController.h"
15 #import "Toast+UIView.h"
16 #import "Reachability.h"
17 #import "GlobalDefaults.h"
18 #import "BlockAlertView.h"
20 #define SECTION_SESSIONS 0
21 #define SECTION_BOOKMARKS 1
22 #define NUM_SECTIONS 2
24 @interface BookmarkListController (Private)
25 #pragma mark misc functions
26 - (UIButton *)disclosureButtonWithImage:(UIImage *)image;
27 - (void)performSearch:(NSString *)searchText;
28 #pragma mark Persisting bookmarks
29 - (void)scheduleWriteBookmarksToDataStore;
30 - (void)writeBookmarksToDataStore;
31 - (void)scheduleWriteManualBookmarksToDataStore;
32 - (void)writeManualBookmarksToDataStore;
33 - (void)readManualBookmarksFromDataStore;
34 - (void)writeArray:(NSArray *)bookmarks toDataStoreURL:(NSURL *)url;
35 - (NSMutableArray *)arrayFromDataStoreURL:(NSURL *)url;
36 - (NSURL *)manualBookmarksDataStoreURL;
37 - (NSURL *)connectionHistoryDataStoreURL;
42 @synthesize searchBar = _searchBar, tableView = _tableView, bmTableCell = _bmTableCell,
43 sessTableCell = _sessTableCell;
47 - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
49 if ((
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]))
52 [
self readManualBookmarksFromDataStore];
55 [
self readConnectionHistoryFromDataStore];
58 _manual_search_result = nil;
61 [[NSNotificationCenter defaultCenter] addObserver:self
62 selector:@selector(sessionDisconnected:)
63 name:TSXSessionDidDisconnectNotification
65 [[NSNotificationCenter defaultCenter] addObserver:self
66 selector:@selector(sessionFailedToConnect:)
67 name:TSXSessionDidFailToConnectNotification
71 [
self setTitle:NSLocalizedString(@"Connections",
72 @"'Connections': bookmark controller title")];
73 [
self setTabBarItem:[[[UITabBarItem alloc]
74 initWithTabBarSystemItem:UITabBarSystemItemBookmarks
78 _star_on_img = [[UIImage
79 imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"icon_accessory_star_on"
80 ofType:@"png"]] retain];
82 [[UIImage imageWithContentsOfFile:[[NSBundle mainBundle]
83 pathForResource:@"icon_accessory_star_off"
84 ofType:@"png"]] retain];
87 [[NSNotificationCenter defaultCenter] addObserver:self
88 selector:@selector(reachabilityChanged:)
89 name:kReachabilityChangedNotification
93 _active_sessions = [[NSMutableArray alloc] init];
94 _temporary_bookmark = nil;
110 [[
self navigationItem] setRightBarButtonItem:[
self editButtonItem]];
113 - (void)viewWillAppear:(BOOL)animated
115 [
super viewWillAppear:animated];
118 if ([[_searchBar text] length] > 0)
119 [
self performSearch:[_searchBar text]];
122 [_tableView reloadData];
125 - (void)viewWillDisappear:(BOOL)animated
127 [
super viewWillDisappear:animated];
130 [_searchBar setText:@""];
131 [_searchBar resignFirstResponder];
132 [
self performSearch:@""];
136 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
142 - (void)didReceiveMemoryWarning
145 [
super didReceiveMemoryWarning];
150 - (void)viewDidUnload
152 [
super viewDidUnload];
159 [[NSNotificationCenter defaultCenter] removeObserver:self];
161 [_temporary_bookmark release];
162 [_connection_history release];
163 [_active_sessions release];
164 [_manual_search_result release];
165 [_manual_bookmarks release];
167 [_star_on_img release];
168 [_star_off_img release];
174 #pragma mark Table view data source
176 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
182 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
187 case SECTION_SESSIONS:
191 case SECTION_BOOKMARKS:
194 if (_manual_search_result != nil)
195 return ([_manual_search_result count] + [_history_search_result count] + 1);
196 return ([_manual_bookmarks count] + 1);
206 - (UITableViewCell *)cellForGenericListEntry
208 static NSString *CellIdentifier =
@"BookmarkListCell";
209 UITableViewCell *cell = [[
self tableView] dequeueReusableCellWithIdentifier:CellIdentifier];
212 cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
213 reuseIdentifier:CellIdentifier];
214 [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
215 [cell setAccessoryView:[
self disclosureButtonWithImage:_star_off_img]];
223 static NSString *BookmarkCellIdentifier =
@"BookmarkCell";
225 dequeueReusableCellWithIdentifier:BookmarkCellIdentifier];
228 [[NSBundle mainBundle] loadNibNamed:@"BookmarkTableViewCell" owner:self options:nil];
229 [_bmTableCell setAccessoryView:[
self disclosureButtonWithImage:_star_on_img]];
238 - (UITableViewCell *)tableView:(UITableView *)tableView
239 cellForRowAtIndexPath:(NSIndexPath *)indexPath
242 switch ([indexPath section])
244 case SECTION_SESSIONS:
247 static NSString *SessionCellIdentifier =
@"SessionCell";
249 dequeueReusableCellWithIdentifier:SessionCellIdentifier];
252 [[NSBundle mainBundle] loadNibNamed:@"SessionTableViewCell" owner:self options:nil];
253 cell = _sessTableCell;
254 _sessTableCell = nil;
258 RDPSession *session = [_active_sessions objectAtIndex:[indexPath row]];
259 [[cell title] setText:[session sessionName]];
260 [[cell server] setText:[[session params] StringForKey:@"hostname"]];
261 [[cell username] setText:[[session params] StringForKey:@"username"]];
263 setImage:[session getScreenshotWithSize:[[cell screenshot] bounds].size]];
264 [[cell disconnectButton] setTag:[indexPath row]];
268 case SECTION_BOOKMARKS:
271 if ([indexPath row] == 0)
275 UITableViewCell *cell = [
self cellForGenericListEntry];
276 if ([[_searchBar text] length] == 0)
279 setText:[@" " stringByAppendingString:
280 NSLocalizedString(@"Add Connection",
281 @"'Add Connection': button label")]];
282 [((UIButton *)[cell accessoryView]) setHidden:YES];
286 [[cell textLabel] setText:[@" " stringByAppendingString:[_searchBar text]]];
287 [((UIButton *)[cell accessoryView]) setHidden:NO];
295 if ([
self isIndexPathToHistoryItem:indexPath])
297 UITableViewCell *cell = [
self cellForGenericListEntry];
299 setText:[@" " stringByAppendingString:
300 [_history_search_result
302 [
self historyIndexFromIndexPath:indexPath]]]];
303 [((UIButton *)[cell accessoryView]) setHidden:NO];
311 if (_manual_search_result == nil)
312 entry = [_manual_bookmarks
313 objectAtIndex:[
self bookmarkIndexFromIndexPath:indexPath]];
315 entry = [[_manual_search_result
316 objectAtIndex:[
self bookmarkIndexFromIndexPath:indexPath]]
317 valueForKey:@"bookmark"];
319 [[cell title] setText:[entry label]];
320 [[cell subTitle] setText:[[entry params] StringForKey:@"hostname"]];
330 NSAssert(0,
@"Failed to create cell");
335 - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
338 if ([indexPath section] == SECTION_SESSIONS)
340 if ([indexPath section] == SECTION_BOOKMARKS && [indexPath row] == 0)
346 - (void)tableView:(UITableView *)tableView
347 commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
348 forRowAtIndexPath:(NSIndexPath *)indexPath
350 if (editingStyle == UITableViewCellEditingStyleDelete)
353 switch ([indexPath section])
355 case SECTION_BOOKMARKS:
357 if (_manual_search_result == nil)
359 removeObjectAtIndex:[
self bookmarkIndexFromIndexPath:indexPath]];
363 if ([
self isIndexPathToHistoryItem:indexPath])
367 [_history_search_result
368 objectAtIndex:[
self historyIndexFromIndexPath:indexPath]]];
369 [_history_search_result
370 removeObjectAtIndex:[
self historyIndexFromIndexPath:indexPath]];
376 [[_manual_search_result
377 objectAtIndex:[
self bookmarkIndexFromIndexPath:indexPath]]
378 valueForKey:@"bookmark"]];
379 [_manual_search_result
380 removeObjectAtIndex:[
self bookmarkIndexFromIndexPath:indexPath]];
383 [
self scheduleWriteManualBookmarksToDataStore];
388 [tableView reloadSections:[NSIndexSet indexSetWithIndex:[indexPath section]]
389 withRowAnimation:UITableViewRowAnimationNone];
394 - (void)tableView:(UITableView *)tableView
395 moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
396 toIndexPath:(NSIndexPath *)toIndexPath
398 if ([fromIndexPath compare:toIndexPath] != NSOrderedSame)
400 switch ([fromIndexPath section])
402 case SECTION_BOOKMARKS:
404 int fromIdx = [
self bookmarkIndexFromIndexPath:fromIndexPath];
405 int toIdx = [
self bookmarkIndexFromIndexPath:toIndexPath];
407 [[_manual_bookmarks objectAtIndex:fromIdx] retain];
408 [_manual_bookmarks removeObjectAtIndex:fromIdx];
409 if (toIdx >= [_manual_bookmarks count])
410 [_manual_bookmarks addObject:temp_bookmark];
412 [_manual_bookmarks insertObject:temp_bookmark atIndex:toIdx];
413 [temp_bookmark release];
415 [
self scheduleWriteManualBookmarksToDataStore];
423 - (NSIndexPath *)tableView:(UITableView *)tableView
424 targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath
425 toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath
431 if ([proposedDestinationIndexPath row] == 0 ||
432 ([sourceIndexPath section] != [proposedDestinationIndexPath section]) ||
433 _manual_search_result != nil)
435 return sourceIndexPath;
439 return proposedDestinationIndexPath;
444 - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
447 if ([indexPath section] == SECTION_BOOKMARKS && [indexPath row] == 0)
452 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
454 if (section == SECTION_SESSIONS && [_active_sessions count] > 0)
455 return NSLocalizedString(
@"My Sessions",
@"'My Session': section sessions header");
456 if (section == SECTION_BOOKMARKS)
457 return NSLocalizedString(
@"Manual Connections",
458 @"'Manual Connections': section manual bookmarks header");
462 - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
467 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
469 if ([indexPath section] == SECTION_SESSIONS)
471 return [tableView rowHeight];
475 #pragma mark Table view delegate
477 - (void)setEditing:(BOOL)editing animated:(BOOL)animated
479 [
super setEditing:editing animated:animated];
480 [[
self tableView] setEditing:editing animated:animated];
483 - (void)accessoryButtonTapped:(UIControl *)button withEvent:(UIEvent *)event
486 NSIndexPath *indexPath =
487 [[
self tableView] indexPathForRowAtPoint:[[[event touchesForView:button] anyObject]
488 locationInView:[
self tableView]]];
489 if (indexPath == nil)
492 [[[
self tableView] delegate] tableView:[
self tableView]
493 accessoryButtonTappedForRowWithIndexPath:indexPath];
496 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
498 if ([indexPath section] == SECTION_SESSIONS)
501 RDPSession *session = [_active_sessions objectAtIndex:[indexPath row]];
502 UIViewController *ctrl =
505 session:session] autorelease];
506 [ctrl setHidesBottomBarWhenPushed:YES];
507 [[
self navigationController] pushViewController:ctrl animated:YES];
512 if ([indexPath section] == SECTION_BOOKMARKS)
515 if ([indexPath row] == 0)
517 if ([[_searchBar text] length] == 0)
524 [bookmarkEditorController
525 setTitle:NSLocalizedString(@"Add Connection", @"Add Connection title")];
526 [bookmarkEditorController setDelegate:self];
527 [bookmarkEditorController setHidesBottomBarWhenPushed:YES];
528 [[
self navigationController] pushViewController:bookmarkEditorController
535 bookmark = [
self bookmarkForQuickConnectTo:[_searchBar text]];
536 if (![_connection_history containsObject:[_searchBar text]])
538 [_connection_history addObject:[_searchBar text]];
539 [
self scheduleWriteConnectionHistoryToDataStore];
545 if (_manual_search_result != nil)
547 if ([
self isIndexPathToHistoryItem:indexPath])
550 NSString *item = [_history_search_result
551 objectAtIndex:[
self historyIndexFromIndexPath:indexPath]];
552 bookmark = [
self bookmarkForQuickConnectTo:item];
555 bookmark = [[_manual_search_result
556 objectAtIndex:[
self bookmarkIndexFromIndexPath:indexPath]]
557 valueForKey:@"bookmark"];
560 bookmark = [_manual_bookmarks
561 objectAtIndex:[
self bookmarkIndexFromIndexPath:
569 reachabilityWithHostName:[[bookmark params]
570 StringForKey:@"hostname"]]
571 currentReachabilityStatus] == ReachableViaWiFi];
578 UIViewController *ctrl =
581 session:session] autorelease];
582 [ctrl setHidesBottomBarWhenPushed:YES];
583 [[
self navigationController] pushViewController:ctrl animated:YES];
584 [_active_sessions addObject:session];
589 - (void)tableView:(UITableView *)tableView
590 accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
593 NSString *bookmark_editor_title =
594 NSLocalizedString(
@"Edit Connection",
@"Edit Connection title");
596 if ([indexPath section] == SECTION_BOOKMARKS)
598 if ([indexPath row] == 0)
601 bookmark = [
self bookmarkForQuickConnectTo:[_searchBar text]];
602 bookmark_editor_title = NSLocalizedString(
@"Add Connection",
@"Add Connection title");
606 if (_manual_search_result != nil)
608 if ([
self isIndexPathToHistoryItem:indexPath])
611 NSString *item = [_history_search_result
612 objectAtIndex:[
self historyIndexFromIndexPath:indexPath]];
613 bookmark = [
self bookmarkForQuickConnectTo:item];
614 bookmark_editor_title =
615 NSLocalizedString(
@"Add Connection",
@"Add Connection title");
618 bookmark = [[_manual_search_result
619 objectAtIndex:[
self bookmarkIndexFromIndexPath:indexPath]]
620 valueForKey:@"bookmark"];
623 bookmark = [_manual_bookmarks
624 objectAtIndex:[
self bookmarkIndexFromIndexPath:indexPath]];
634 [editBookmarkController setHidesBottomBarWhenPushed:YES];
635 [editBookmarkController setTitle:bookmark_editor_title];
636 [editBookmarkController setDelegate:self];
637 [[
self navigationController] pushViewController:editBookmarkController animated:YES];
642 #pragma mark Search Bar Delegates
644 - (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
647 [searchBar setShowsCancelButton:YES animated:YES];
651 - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
654 [_manual_search_result release];
655 _manual_search_result = nil;
658 [searchBar setText:@""];
659 [searchBar resignFirstResponder];
662 - (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar
664 [searchBar setShowsCancelButton:NO animated:YES];
667 [_tableView setAllowsSelection:YES];
672 - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
674 [_searchBar resignFirstResponder];
677 - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
679 [
self performSearch:searchText];
680 [_tableView reloadData];
683 #pragma mark - Session handling
686 - (void)sessionDisconnected:(NSNotification *)notification
690 [_active_sessions removeObject:session];
693 if ([[
self navigationController] visibleViewController] ==
self)
694 [_tableView reloadSections:[NSIndexSet indexSetWithIndex:SECTION_SESSIONS]
695 withRowAnimation:UITableViewRowAnimationNone];
699 if (![_manual_bookmarks containsObject:[session bookmark]])
702 _temporary_bookmark = [[session bookmark] retain];
706 NSLocalizedString(
@"Save Connection Settings?",
@"Save connection settings title");
707 NSString *message = NSLocalizedString(
708 @"Your Connection Settings have not been saved. Do you want to save them?",
709 @"Save connection settings message");
711 [alert setCancelButtonWithTitle:NSLocalizedString(@"No", @"No Button") block:nil];
712 [alert addButtonWithTitle:NSLocalizedString(@"Yes", @"Yes Button")
714 if (_temporary_bookmark)
716 [_manual_bookmarks addObject:_temporary_bookmark];
718 reloadSections:[NSIndexSet
719 indexSetWithIndex:SECTION_BOOKMARKS]
720 withRowAnimation:UITableViewRowAnimationNone];
721 [_temporary_bookmark autorelease];
722 _temporary_bookmark = nil;
729 - (void)sessionFailedToConnect:(NSNotification *)notification
733 [_active_sessions removeObject:session];
736 [[
self view] makeToast:NSLocalizedString(@"Failed to connect to session!",
737 @"Failed to connect error message")
738 duration:ToastDurationNormal
742 #pragma mark - Reachability notification
743 - (void)reachabilityChanged:(NSNotification *)notification
747 if ([_active_sessions count] > 0)
749 RDPSession *session = [_active_sessions objectAtIndex:0];
750 [session disconnect];
754 #pragma mark - BookmarkEditorController delegate
760 for (
int idx = 0; idx < [_manual_bookmarks count]; ++idx)
762 if ([[bookmark uuid] isEqualToString:[[_manual_bookmarks objectAtIndex:idx] uuid]])
764 [_manual_bookmarks replaceObjectAtIndex:idx withObject:bookmark];
770 [_manual_bookmarks addObject:bookmark];
773 NSString *hostname = [[bookmark params] StringForKey:@"hostname"];
774 if ([_connection_history containsObject:hostname])
776 [_connection_history removeObject:hostname];
777 [
self scheduleWriteConnectionHistoryToDataStore];
780 [
self scheduleWriteManualBookmarksToDataStore];
783 - (IBAction)disconnectButtonPressed:(
id)sender
786 RDPSession *session = [_active_sessions objectAtIndex:[sender tag]];
787 [session disconnect];
790 #pragma mark - Misc functions
792 - (BOOL)hasNoBookmarks
794 return ([_manual_bookmarks count] == 0);
797 - (UIButton *)disclosureButtonWithImage:(UIImage *)image
801 UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
802 [button setFrame:CGRectMake(0, 0, [image size].width * 2, [image size].height + 10)];
803 [button setImage:image forState:UIControlStateNormal];
804 [button addTarget:self
805 action:@selector(accessoryButtonTapped:withEvent:)
806 forControlEvents:UIControlEventTouchUpInside];
807 [button setUserInteractionEnabled:YES];
811 - (void)performSearch:(NSString *)searchText
813 [_manual_search_result autorelease];
815 if ([searchText length] > 0)
817 _manual_search_result = [FilterBookmarks(
819 [searchText componentsSeparatedByCharactersInSet:[NSCharacterSet
820 whitespaceAndNewlineCharacterSet]])
822 _history_search_result = [FilterHistory(_connection_history, searchText) retain];
826 _history_search_result = nil;
827 _manual_search_result = nil;
831 - (int)bookmarkIndexFromIndexPath:(NSIndexPath *)indexPath
833 return [indexPath row] -
834 ((_history_search_result != nil) ? [_history_search_result count] : 0) - 1;
837 - (int)historyIndexFromIndexPath:(NSIndexPath *)indexPath
839 return [indexPath row] - 1;
842 - (BOOL)isIndexPathToHistoryItem:(NSIndexPath *)indexPath
844 return (([indexPath row] - 1) < [_history_search_result count]);
851 [bookmark setLabel:host];
852 [[bookmark params] setValue:host forKey:@"hostname"];
856 #pragma mark - Persisting bookmarks
858 - (void)scheduleWriteBookmarksToDataStore
860 [[NSOperationQueue mainQueue] addOperationWithBlock:^{
861 [
self writeBookmarksToDataStore];
865 - (void)writeBookmarksToDataStore
867 [
self writeManualBookmarksToDataStore];
870 - (void)scheduleWriteManualBookmarksToDataStore
872 [[NSOperationQueue mainQueue]
873 addOperation:[[[NSInvocationOperation alloc]
875 selector:@selector(writeManualBookmarksToDataStore)
876 object:nil] autorelease]];
879 - (void)writeManualBookmarksToDataStore
881 [
self writeArray:_manual_bookmarks toDataStoreURL:[
self manualBookmarksDataStoreURL]];
884 - (void)scheduleWriteConnectionHistoryToDataStore
886 [[NSOperationQueue mainQueue]
887 addOperation:[[[NSInvocationOperation alloc]
889 selector:@selector(writeConnectionHistoryToDataStore)
890 object:nil] autorelease]];
893 - (void)writeConnectionHistoryToDataStore
895 [
self writeArray:_connection_history toDataStoreURL:[
self connectionHistoryDataStoreURL]];
898 - (void)writeArray:(NSArray *)bookmarks toDataStoreURL:(NSURL *)url
900 NSData *archived_data = [NSKeyedArchiver archivedDataWithRootObject:bookmarks];
901 [archived_data writeToURL:url atomically:YES];
904 - (void)readManualBookmarksFromDataStore
906 [_manual_bookmarks autorelease];
907 _manual_bookmarks = [
self arrayFromDataStoreURL:[
self manualBookmarksDataStoreURL]];
909 if (_manual_bookmarks == nil)
911 _manual_bookmarks = [[NSMutableArray alloc] init];
913 addObject:[[[
GlobalDefaults sharedGlobalDefaults] newTestServerBookmark] autorelease]];
917 - (void)readConnectionHistoryFromDataStore
919 [_connection_history autorelease];
920 _connection_history = [
self arrayFromDataStoreURL:[
self connectionHistoryDataStoreURL]];
922 if (_connection_history == nil)
923 _connection_history = [[NSMutableArray alloc] init];
926 - (NSMutableArray *)arrayFromDataStoreURL:(NSURL *)url
928 NSData *archived_data = [NSData dataWithContentsOfURL:url];
933 return [[NSKeyedUnarchiver unarchiveObjectWithData:archived_data] retain];
936 - (NSURL *)manualBookmarksDataStoreURL
939 fileURLWithPath:[NSString stringWithFormat:@"%@/%@",
940 [NSSearchPathForDirectoriesInDomains(
941 NSDocumentDirectory, NSUserDomainMask, YES)
943 @"com.freerdp.ifreerdp.bookmarks.plist"]];
946 - (NSURL *)connectionHistoryDataStoreURL
949 fileURLWithPath:[NSString
950 stringWithFormat:@"%@/%@",
951 [NSSearchPathForDirectoriesInDomains(
952 NSDocumentDirectory, NSUserDomainMask, YES)
954 @"com.freerdp.ifreerdp.connection_history.plist"]];