FreeRDP
Loading...
Searching...
No Matches
BookmarkListController.m
1/*
2 bookmarks and active session view controller
3
4 Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
5
6 This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
7 If a copy of the MPL was not distributed with this file, You can obtain one at
8 http://mozilla.org/MPL/2.0/.
9 */
10
11#import "BookmarkListController.h"
12#import "Utils.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"
19
20#define SECTION_SESSIONS 0
21#define SECTION_BOOKMARKS 1
22#define NUM_SECTIONS 2
23
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;
38@end
39
40@implementation BookmarkListController
41
42@synthesize searchBar = _searchBar, tableView = _tableView, bmTableCell = _bmTableCell,
43 sessTableCell = _sessTableCell;
44
45// The designated initializer. Override if you create the controller programmatically and want to
46// perform customization that is not appropriate for viewDidLoad.
47- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
48{
49 if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]))
50 {
51 // load bookmarks
52 [self readManualBookmarksFromDataStore];
53
54 // load connection history
55 [self readConnectionHistoryFromDataStore];
56
57 // init search result array
58 _manual_search_result = nil;
59
60 // register for session notifications
61 [[NSNotificationCenter defaultCenter] addObserver:self
62 selector:@selector(sessionDisconnected:)
63 name:TSXSessionDidDisconnectNotification
64 object:nil];
65 [[NSNotificationCenter defaultCenter] addObserver:self
66 selector:@selector(sessionFailedToConnect:)
67 name:TSXSessionDidFailToConnectNotification
68 object:nil];
69
70 // set title and tabbar controller image
71 [self setTitle:NSLocalizedString(@"Connections",
72 @"'Connections': bookmark controller title")];
73 [self setTabBarItem:[[[UITabBarItem alloc]
74 initWithTabBarSystemItem:UITabBarSystemItemBookmarks
75 tag:0] autorelease]];
76
77 // load images
78 _star_on_img = [[UIImage
79 imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"icon_accessory_star_on"
80 ofType:@"png"]] retain];
81 _star_off_img =
82 [[UIImage imageWithContentsOfFile:[[NSBundle mainBundle]
83 pathForResource:@"icon_accessory_star_off"
84 ofType:@"png"]] retain];
85
86 // init reachability detection
87 [[NSNotificationCenter defaultCenter] addObserver:self
88 selector:@selector(reachabilityChanged:)
89 name:kReachabilityChangedNotification
90 object:nil];
91
92 // init other properties
93 _active_sessions = [[NSMutableArray alloc] init];
94 _temporary_bookmark = nil;
95 }
96 return self;
97}
98
99- (void)loadView
100{
101 [super loadView];
102}
103
104// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
105- (void)viewDidLoad
106{
107 [super viewDidLoad];
108
109 // set edit button to allow bookmark list editing
110 [[self navigationItem] setRightBarButtonItem:[self editButtonItem]];
111}
112
113- (void)viewWillAppear:(BOOL)animated
114{
115 [super viewWillAppear:animated];
116
117 // in case we had a search - search again cause the bookmark searchable items could have changed
118 if ([[_searchBar text] length] > 0)
119 [self performSearch:[_searchBar text]];
120
121 // to reflect any bookmark changes - reload table
122 [_tableView reloadData];
123}
124
125// recalc safe area for modern device
126- (void)viewSafeAreaInsetsDidChange
127{
128 [super viewSafeAreaInsetsDidChange];
129 [[self view] setNeedsLayout];
130}
131
132- (void)viewDidLayoutSubviews
133{
134 [super viewDidLayoutSubviews];
135
136 const CGRect bounds = [[self view] bounds];
137 const UIEdgeInsets safe = [[self view] safeAreaInsets];
138 const CGFloat x = safe.left;
139 const CGFloat w = bounds.size.width - safe.left - safe.right;
140 CGFloat searchH = CGRectGetHeight([_searchBar frame]);
141
142 [_searchBar setFrame:CGRectMake(x, safe.top, w, searchH)];
143 [_tableView
144 setFrame:CGRectMake(x, safe.top + searchH, w, bounds.size.height - safe.top - searchH)];
145}
146
147- (void)viewWillDisappear:(BOOL)animated
148{
149 [super viewWillDisappear:animated];
150
151 // clear any search
152 [_searchBar setText:@""];
153 [_searchBar resignFirstResponder];
154 [self performSearch:@""];
155}
156
157// Override to allow orientations other than the default portrait orientation.
158- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
159{
160 // Return YES for supported orientations
161 return YES;
162}
163
164- (void)didReceiveMemoryWarning
165{
166 // Releases the view if it doesn't have a superview.
167 [super didReceiveMemoryWarning];
168
169 // Release any cached data, images, etc that aren't in use.
170}
171
172- (void)viewDidUnload
173{
174 [super viewDidUnload];
175 // Release any retained subviews of the main view.
176 // e.g. self.myOutlet = nil;
177}
178
179- (void)dealloc
180{
181 [[NSNotificationCenter defaultCenter] removeObserver:self];
182
183 [_temporary_bookmark release];
184 [_connection_history release];
185 [_active_sessions release];
186 [_manual_search_result release];
187 [_manual_bookmarks release];
188
189 [_star_on_img release];
190 [_star_off_img release];
191
192 [super dealloc];
193}
194
195#pragma mark -
196#pragma mark Table view data source
197
198- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
199{
200 // Return the number of sections.
201 return NUM_SECTIONS;
202}
203
204- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
205{
206
207 switch (section)
208 {
209 case SECTION_SESSIONS:
210 return 0;
211 break;
212
213 case SECTION_BOOKMARKS:
214 {
215 // (+1 for Add Bookmark entry)
216 if (_manual_search_result != nil)
217 return ([_manual_search_result count] + [_history_search_result count] + 1);
218 return ([_manual_bookmarks count] + 1);
219 }
220 break;
221
222 default:
223 break;
224 }
225 return 0;
226}
227
228- (UITableViewCell *)cellForGenericListEntry
229{
230 static NSString *CellIdentifier = @"BookmarkListCell";
231 UITableViewCell *cell = [[self tableView] dequeueReusableCellWithIdentifier:CellIdentifier];
232 if (cell == nil)
233 {
234 cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
235 reuseIdentifier:CellIdentifier];
236 [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
237 [cell setAccessoryView:[self disclosureButtonWithImage:_star_off_img]];
238 }
239
240 return cell;
241}
242
243- (BookmarkTableCell *)cellForBookmark
244{
245 static NSString *BookmarkCellIdentifier = @"BookmarkCell";
246 BookmarkTableCell *cell = (BookmarkTableCell *)[[self tableView]
247 dequeueReusableCellWithIdentifier:BookmarkCellIdentifier];
248 if (cell == nil)
249 {
250 [[NSBundle mainBundle] loadNibNamed:@"BookmarkTableViewCell" owner:self options:nil];
251 [_bmTableCell setAccessoryView:[self disclosureButtonWithImage:_star_on_img]];
252 cell = _bmTableCell;
253 _bmTableCell = nil;
254 }
255
256 return cell;
257}
258
259// Customize the appearance of table view cells.
260- (UITableViewCell *)tableView:(UITableView *)tableView
261 cellForRowAtIndexPath:(NSIndexPath *)indexPath
262{
263
264 switch ([indexPath section])
265 {
266 case SECTION_SESSIONS:
267 {
268 // get custom session cell
269 static NSString *SessionCellIdentifier = @"SessionCell";
270 SessionTableCell *cell = (SessionTableCell *)[tableView
271 dequeueReusableCellWithIdentifier:SessionCellIdentifier];
272 if (cell == nil)
273 {
274 [[NSBundle mainBundle] loadNibNamed:@"SessionTableViewCell" owner:self options:nil];
275 cell = _sessTableCell;
276 _sessTableCell = nil;
277 }
278
279 // set cell data
280 RDPSession *session = [_active_sessions objectAtIndex:[indexPath row]];
281 [[cell title] setText:[session sessionName]];
282 [[cell server] setText:[[session params] StringForKey:@"hostname"]];
283 [[cell username] setText:[[session params] StringForKey:@"username"]];
284 [[cell screenshot]
285 setImage:[session getScreenshotWithSize:[[cell screenshot] bounds].size]];
286 [[cell disconnectButton] setTag:[indexPath row]];
287 return cell;
288 }
289
290 case SECTION_BOOKMARKS:
291 {
292 // special handling for first cell - quick connect/quick create Bookmark cell
293 if ([indexPath row] == 0)
294 {
295 // if a search text is entered the cell becomes a quick connect/quick create
296 // bookmark cell - otherwise it's just an add bookmark cell
297 UITableViewCell *cell = [self cellForGenericListEntry];
298 if ([[_searchBar text] length] == 0)
299 {
300 [[cell textLabel]
301 setText:[@" " stringByAppendingString:
302 NSLocalizedString(@"Add Connection",
303 @"'Add Connection': button label")]];
304 [((UIButton *)[cell accessoryView]) setHidden:YES];
305 }
306 else
307 {
308 [[cell textLabel] setText:[@" " stringByAppendingString:[_searchBar text]]];
309 [((UIButton *)[cell accessoryView]) setHidden:NO];
310 }
311
312 return cell;
313 }
314 else
315 {
316 // do we have a history cell or bookmark cell?
317 if ([self isIndexPathToHistoryItem:indexPath])
318 {
319 UITableViewCell *cell = [self cellForGenericListEntry];
320 [[cell textLabel]
321 setText:[@" " stringByAppendingString:
322 [_history_search_result
323 objectAtIndex:
324 [self historyIndexFromIndexPath:indexPath]]]];
325 [((UIButton *)[cell accessoryView]) setHidden:NO];
326 return cell;
327 }
328 else
329 {
330 // set cell properties
331 ComputerBookmark *entry;
332 BookmarkTableCell *cell = [self cellForBookmark];
333 if (_manual_search_result == nil)
334 entry = [_manual_bookmarks
335 objectAtIndex:[self bookmarkIndexFromIndexPath:indexPath]];
336 else
337 entry = [[_manual_search_result
338 objectAtIndex:[self bookmarkIndexFromIndexPath:indexPath]]
339 valueForKey:@"bookmark"];
340
341 [[cell title] setText:[entry label]];
342 [[cell subTitle] setText:[[entry params] StringForKey:@"hostname"]];
343 return cell;
344 }
345 }
346 }
347
348 default:
349 break;
350 }
351
352 NSAssert(0, @"Failed to create cell");
353 return nil;
354}
355
356// Override to support conditional editing of the table view.
357- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
358{
359 // dont allow to edit Add Bookmark item
360 if ([indexPath section] == SECTION_SESSIONS)
361 return NO;
362 if ([indexPath section] == SECTION_BOOKMARKS && [indexPath row] == 0)
363 return NO;
364 return YES;
365}
366
367// Override to support editing the table view.
368- (void)tableView:(UITableView *)tableView
369 commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
370 forRowAtIndexPath:(NSIndexPath *)indexPath
371{
372 if (editingStyle == UITableViewCellEditingStyleDelete)
373 {
374 // Delete the row from the data source
375 switch ([indexPath section])
376 {
377 case SECTION_BOOKMARKS:
378 {
379 if (_manual_search_result == nil)
380 [_manual_bookmarks
381 removeObjectAtIndex:[self bookmarkIndexFromIndexPath:indexPath]];
382 else
383 {
384 // history item or bookmark?
385 if ([self isIndexPathToHistoryItem:indexPath])
386 {
387 [_connection_history
388 removeObject:
389 [_history_search_result
390 objectAtIndex:[self historyIndexFromIndexPath:indexPath]]];
391 [_history_search_result
392 removeObjectAtIndex:[self historyIndexFromIndexPath:indexPath]];
393 }
394 else
395 {
396 [_manual_bookmarks
397 removeObject:
398 [[_manual_search_result
399 objectAtIndex:[self bookmarkIndexFromIndexPath:indexPath]]
400 valueForKey:@"bookmark"]];
401 [_manual_search_result
402 removeObjectAtIndex:[self bookmarkIndexFromIndexPath:indexPath]];
403 }
404 }
405 [self scheduleWriteManualBookmarksToDataStore];
406 break;
407 }
408 }
409
410 [tableView reloadSections:[NSIndexSet indexSetWithIndex:[indexPath section]]
411 withRowAnimation:UITableViewRowAnimationNone];
412 }
413}
414
415// Override to support rearranging the table view.
416- (void)tableView:(UITableView *)tableView
417 moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
418 toIndexPath:(NSIndexPath *)toIndexPath
419{
420 if ([fromIndexPath compare:toIndexPath] != NSOrderedSame)
421 {
422 switch ([fromIndexPath section])
423 {
424 case SECTION_BOOKMARKS:
425 {
426 int fromIdx = [self bookmarkIndexFromIndexPath:fromIndexPath];
427 int toIdx = [self bookmarkIndexFromIndexPath:toIndexPath];
428 ComputerBookmark *temp_bookmark =
429 [[_manual_bookmarks objectAtIndex:fromIdx] retain];
430 [_manual_bookmarks removeObjectAtIndex:fromIdx];
431 if (toIdx >= [_manual_bookmarks count])
432 [_manual_bookmarks addObject:temp_bookmark];
433 else
434 [_manual_bookmarks insertObject:temp_bookmark atIndex:toIdx];
435 [temp_bookmark release];
436
437 [self scheduleWriteManualBookmarksToDataStore];
438 break;
439 }
440 }
441 }
442}
443
444// prevent that an item is moved before the Add Bookmark item
445- (NSIndexPath *)tableView:(UITableView *)tableView
446 targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath
447 toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath
448{
449 // don't allow to move:
450 // - items between sections
451 // - the quick connect/quick create bookmark cell
452 // - any item while a search is applied
453 if ([proposedDestinationIndexPath row] == 0 ||
454 ([sourceIndexPath section] != [proposedDestinationIndexPath section]) ||
455 _manual_search_result != nil)
456 {
457 return sourceIndexPath;
458 }
459 else
460 {
461 return proposedDestinationIndexPath;
462 }
463}
464
465// Override to support conditional rearranging of the table view.
466- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
467{
468 // dont allow to reorder Add Bookmark item
469 if ([indexPath section] == SECTION_BOOKMARKS && [indexPath row] == 0)
470 return NO;
471 return YES;
472}
473
474- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
475{
476 if (section == SECTION_SESSIONS && [_active_sessions count] > 0)
477 return NSLocalizedString(@"My Sessions", @"'My Session': section sessions header");
478 if (section == SECTION_BOOKMARKS)
479 return NSLocalizedString(@"Manual Connections",
480 @"'Manual Connections': section manual bookmarks header");
481 return nil;
482}
483
484- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
485{
486 return nil;
487}
488
489- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
490{
491 if ([indexPath section] == SECTION_SESSIONS)
492 return 72;
493 return [tableView rowHeight];
494}
495
496#pragma mark -
497#pragma mark Table view delegate
498
499- (void)setEditing:(BOOL)editing animated:(BOOL)animated
500{
501 [super setEditing:editing animated:animated];
502 [[self tableView] setEditing:editing animated:animated];
503}
504
505- (void)accessoryButtonTapped:(UIControl *)button withEvent:(UIEvent *)event
506{
507 // forward a tap on our custom accessory button to the real accessory button handler
508 NSIndexPath *indexPath =
509 [[self tableView] indexPathForRowAtPoint:[[[event touchesForView:button] anyObject]
510 locationInView:[self tableView]]];
511 if (indexPath == nil)
512 return;
513
514 [[[self tableView] delegate] tableView:[self tableView]
515 accessoryButtonTappedForRowWithIndexPath:indexPath];
516}
517
518- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
519{
520 if ([indexPath section] == SECTION_SESSIONS)
521 {
522 // resume session
523 RDPSession *session = [_active_sessions objectAtIndex:[indexPath row]];
524 UIViewController *ctrl =
525 [[[RDPSessionViewController alloc] initWithNibName:@"RDPSessionView"
526 bundle:nil
527 session:session] autorelease];
528 [ctrl setHidesBottomBarWhenPushed:YES];
529 [[self navigationController] pushViewController:ctrl animated:YES];
530 }
531 else
532 {
533 ComputerBookmark *bookmark = nil;
534 if ([indexPath section] == SECTION_BOOKMARKS)
535 {
536 // first row has either quick connect or add bookmark item
537 if ([indexPath row] == 0)
538 {
539 if ([[_searchBar text] length] == 0)
540 {
541 // show add bookmark controller
542 ComputerBookmark *bookmark =
543 [[[ComputerBookmark alloc] initWithBaseDefaultParameters] autorelease];
544 BookmarkEditorController *bookmarkEditorController =
545 [[[BookmarkEditorController alloc] initWithBookmark:bookmark] autorelease];
546 [bookmarkEditorController
547 setTitle:NSLocalizedString(@"Add Connection", @"Add Connection title")];
548 [bookmarkEditorController setDelegate:self];
549 [bookmarkEditorController setHidesBottomBarWhenPushed:YES];
550 [[self navigationController] pushViewController:bookmarkEditorController
551 animated:YES];
552 }
553 else
554 {
555 // create a quick connect bookmark and add an entry to the quick connect history
556 // (if not already in the history)
557 bookmark = [self bookmarkForQuickConnectTo:[_searchBar text]];
558 if (![_connection_history containsObject:[_searchBar text]])
559 {
560 [_connection_history addObject:[_searchBar text]];
561 [self scheduleWriteConnectionHistoryToDataStore];
562 }
563 }
564 }
565 else
566 {
567 if (_manual_search_result != nil)
568 {
569 if ([self isIndexPathToHistoryItem:indexPath])
570 {
571 // create a quick connect bookmark for a history item
572 NSString *item = [_history_search_result
573 objectAtIndex:[self historyIndexFromIndexPath:indexPath]];
574 bookmark = [self bookmarkForQuickConnectTo:item];
575 }
576 else
577 bookmark = [[_manual_search_result
578 objectAtIndex:[self bookmarkIndexFromIndexPath:indexPath]]
579 valueForKey:@"bookmark"];
580 }
581 else
582 bookmark = [_manual_bookmarks
583 objectAtIndex:[self bookmarkIndexFromIndexPath:
584 indexPath]]; // -1 because of ADD BOOKMARK entry
585 }
586
587 // set reachability status
588 [bookmark
589 setConntectedViaWLAN:[[Reachability
590 reachabilityWithHostName:[[bookmark params]
591 StringForKey:@"hostname"]]
592 currentReachabilityStatus] == ReachableViaWiFi];
593 }
594
595 if (bookmark != nil)
596 {
597 // create rdp session
598 RDPSession *session = [[[RDPSession alloc] initWithBookmark:bookmark] autorelease];
599 UIViewController *ctrl =
600 [[[RDPSessionViewController alloc] initWithNibName:@"RDPSessionView"
601 bundle:nil
602 session:session] autorelease];
603 [ctrl setHidesBottomBarWhenPushed:YES];
604 [[self navigationController] pushViewController:ctrl animated:YES];
605 [_active_sessions addObject:session];
606 }
607 }
608}
609
610- (void)tableView:(UITableView *)tableView
611 accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
612{
613 // get the bookmark
614 NSString *bookmark_editor_title =
615 NSLocalizedString(@"Edit Connection", @"Edit Connection title");
616 ComputerBookmark *bookmark = nil;
617 if ([indexPath section] == SECTION_BOOKMARKS)
618 {
619 if ([indexPath row] == 0)
620 {
621 // create a new bookmark and init hostname and label
622 bookmark = [self bookmarkForQuickConnectTo:[_searchBar text]];
623 bookmark_editor_title = NSLocalizedString(@"Add Connection", @"Add Connection title");
624 }
625 else
626 {
627 if (_manual_search_result != nil)
628 {
629 if ([self isIndexPathToHistoryItem:indexPath])
630 {
631 // create a new bookmark and init hostname and label
632 NSString *item = [_history_search_result
633 objectAtIndex:[self historyIndexFromIndexPath:indexPath]];
634 bookmark = [self bookmarkForQuickConnectTo:item];
635 bookmark_editor_title =
636 NSLocalizedString(@"Add Connection", @"Add Connection title");
637 }
638 else
639 bookmark = [[_manual_search_result
640 objectAtIndex:[self bookmarkIndexFromIndexPath:indexPath]]
641 valueForKey:@"bookmark"];
642 }
643 else
644 bookmark = [_manual_bookmarks
645 objectAtIndex:[self bookmarkIndexFromIndexPath:indexPath]]; // -1 because of ADD
646 // BOOKMARK entry
647 }
648 }
649
650 // bookmark found? - start the editor
651 if (bookmark != nil)
652 {
653 BookmarkEditorController *editBookmarkController =
654 [[[BookmarkEditorController alloc] initWithBookmark:bookmark] autorelease];
655 [editBookmarkController setHidesBottomBarWhenPushed:YES];
656 [editBookmarkController setTitle:bookmark_editor_title];
657 [editBookmarkController setDelegate:self];
658 [[self navigationController] pushViewController:editBookmarkController animated:YES];
659 }
660}
661
662#pragma mark -
663#pragma mark Search Bar Delegates
664
665- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
666{
667 // show cancel button
668 [searchBar setShowsCancelButton:YES animated:YES];
669 return YES;
670}
671
672- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
673{
674 // clear search result
675 [_manual_search_result release];
676 _manual_search_result = nil;
677
678 // clear text and remove cancel button
679 [searchBar setText:@""];
680 [searchBar resignFirstResponder];
681}
682
683- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar
684{
685 [searchBar setShowsCancelButton:NO animated:YES];
686
687 // re-enable table selection
688 [_tableView setAllowsSelection:YES];
689
690 return YES;
691}
692
693- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
694{
695 [_searchBar resignFirstResponder];
696}
697
698- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
699{
700 [self performSearch:searchText];
701 [_tableView reloadData];
702}
703
704#pragma mark - Session handling
705
706// session was added
707- (void)sessionDisconnected:(NSNotification *)notification
708{
709 // remove session from active sessions
710 RDPSession *session = (RDPSession *)[notification object];
711 [_active_sessions removeObject:session];
712
713 // if this view is currently active refresh entries
714 if ([[self navigationController] visibleViewController] == self)
715 [_tableView reloadSections:[NSIndexSet indexSetWithIndex:SECTION_SESSIONS]
716 withRowAnimation:UITableViewRowAnimationNone];
717
718 // if session's bookmark is not in the bookmark list ask the user if he wants to add it
719 // (this happens if the session is created using the quick connect feature)
720 if (![_manual_bookmarks containsObject:[session bookmark]])
721 {
722 // retain the bookmark in case we want to save it later
723 _temporary_bookmark = [[session bookmark] retain];
724
725 // ask the user if he wants to save the bookmark
726 NSString *title =
727 NSLocalizedString(@"Save Connection Settings?", @"Save connection settings title");
728 NSString *message = NSLocalizedString(
729 @"Your Connection Settings have not been saved. Do you want to save them?",
730 @"Save connection settings message");
731 BlockAlertView *alert = [BlockAlertView alertWithTitle:title message:message];
732 [alert setCancelButtonWithTitle:NSLocalizedString(@"No", @"No Button") block:nil];
733 [alert addButtonWithTitle:NSLocalizedString(@"Yes", @"Yes Button")
734 block:^{
735 if (_temporary_bookmark)
736 {
737 [_manual_bookmarks addObject:_temporary_bookmark];
738 [_tableView
739 reloadSections:[NSIndexSet
740 indexSetWithIndex:SECTION_BOOKMARKS]
741 withRowAnimation:UITableViewRowAnimationNone];
742 [_temporary_bookmark autorelease];
743 _temporary_bookmark = nil;
744 }
745 }];
746 [alert show];
747 }
748}
749
750- (void)sessionFailedToConnect:(NSNotification *)notification
751{
752 // remove session from active sessions
753 RDPSession *session = (RDPSession *)[notification object];
754 [_active_sessions removeObject:session];
755
756 // display error toast
757 [[self view] makeToast:NSLocalizedString(@"Failed to connect to session!",
758 @"Failed to connect error message")
759 duration:ToastDurationNormal
760 position:@"center"];
761}
762
763#pragma mark - Reachability notification
764- (void)reachabilityChanged:(NSNotification *)notification
765{
766 // no matter how the network changed - we will disconnect
767 // disconnect session (if there is any)
768 if ([_active_sessions count] > 0)
769 {
770 RDPSession *session = [_active_sessions objectAtIndex:0];
771 [session disconnect];
772 }
773}
774
775#pragma mark - BookmarkEditorController delegate
776
777- (void)commitBookmark:(ComputerBookmark *)bookmark
778{
779 // if we got a manual bookmark that is not in the list yet - add it otherwise replace it
780 BOOL found = NO;
781 for (int idx = 0; idx < [_manual_bookmarks count]; ++idx)
782 {
783 if ([[bookmark uuid] isEqualToString:[[_manual_bookmarks objectAtIndex:idx] uuid]])
784 {
785 [_manual_bookmarks replaceObjectAtIndex:idx withObject:bookmark];
786 found = YES;
787 break;
788 }
789 }
790 if (!found)
791 [_manual_bookmarks addObject:bookmark];
792
793 // remove any quick connect history entry with the same hostname
794 NSString *hostname = [[bookmark params] StringForKey:@"hostname"];
795 if ([_connection_history containsObject:hostname])
796 {
797 [_connection_history removeObject:hostname];
798 [self scheduleWriteConnectionHistoryToDataStore];
799 }
800
801 [self scheduleWriteManualBookmarksToDataStore];
802}
803
804- (IBAction)disconnectButtonPressed:(id)sender
805{
806 // disconnect session and refresh table view
807 RDPSession *session = [_active_sessions objectAtIndex:[sender tag]];
808 [session disconnect];
809}
810
811#pragma mark - Misc functions
812
813- (BOOL)hasNoBookmarks
814{
815 return ([_manual_bookmarks count] == 0);
816}
817
818- (UIButton *)disclosureButtonWithImage:(UIImage *)image
819{
820 // we make the button a little bit bigger (image width * 2, height + 10) so that the user
821 // doesn't accidentally connect to the bookmark ...
822 UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
823 [button setFrame:CGRectMake(0, 0, [image size].width * 2, [image size].height + 10)];
824 [button setImage:image forState:UIControlStateNormal];
825 [button addTarget:self
826 action:@selector(accessoryButtonTapped:withEvent:)
827 forControlEvents:UIControlEventTouchUpInside];
828 [button setUserInteractionEnabled:YES];
829 return button;
830}
831
832- (void)performSearch:(NSString *)searchText
833{
834 [_manual_search_result autorelease];
835
836 if ([searchText length] > 0)
837 {
838 _manual_search_result = [FilterBookmarks(
839 _manual_bookmarks,
840 [searchText componentsSeparatedByCharactersInSet:[NSCharacterSet
841 whitespaceAndNewlineCharacterSet]])
842 retain];
843 _history_search_result = [FilterHistory(_connection_history, searchText) retain];
844 }
845 else
846 {
847 _history_search_result = nil;
848 _manual_search_result = nil;
849 }
850}
851
852- (int)bookmarkIndexFromIndexPath:(NSIndexPath *)indexPath
853{
854 return [indexPath row] -
855 ((_history_search_result != nil) ? [_history_search_result count] : 0) - 1;
856}
857
858- (int)historyIndexFromIndexPath:(NSIndexPath *)indexPath
859{
860 return [indexPath row] - 1;
861}
862
863- (BOOL)isIndexPathToHistoryItem:(NSIndexPath *)indexPath
864{
865 return (([indexPath row] - 1) < [_history_search_result count]);
866}
867
868- (ComputerBookmark *)bookmarkForQuickConnectTo:(NSString *)host
869{
870 ComputerBookmark *bookmark =
871 [[[ComputerBookmark alloc] initWithBaseDefaultParameters] autorelease];
872 [bookmark setLabel:host];
873 [[bookmark params] setValue:host forKey:@"hostname"];
874 return bookmark;
875}
876
877#pragma mark - Persisting bookmarks
878
879- (void)scheduleWriteBookmarksToDataStore
880{
881 [[NSOperationQueue mainQueue] addOperationWithBlock:^{
882 [self writeBookmarksToDataStore];
883 }];
884}
885
886- (void)writeBookmarksToDataStore
887{
888 [self writeManualBookmarksToDataStore];
889}
890
891- (void)scheduleWriteManualBookmarksToDataStore
892{
893 [[NSOperationQueue mainQueue]
894 addOperation:[[[NSInvocationOperation alloc]
895 initWithTarget:self
896 selector:@selector(writeManualBookmarksToDataStore)
897 object:nil] autorelease]];
898}
899
900- (void)writeManualBookmarksToDataStore
901{
902 [self writeArray:_manual_bookmarks toDataStoreURL:[self manualBookmarksDataStoreURL]];
903}
904
905- (void)scheduleWriteConnectionHistoryToDataStore
906{
907 [[NSOperationQueue mainQueue]
908 addOperation:[[[NSInvocationOperation alloc]
909 initWithTarget:self
910 selector:@selector(writeConnectionHistoryToDataStore)
911 object:nil] autorelease]];
912}
913
914- (void)writeConnectionHistoryToDataStore
915{
916 [self writeArray:_connection_history toDataStoreURL:[self connectionHistoryDataStoreURL]];
917}
918
919- (void)writeArray:(NSArray *)bookmarks toDataStoreURL:(NSURL *)url
920{
921 NSData *archived_data = [NSKeyedArchiver archivedDataWithRootObject:bookmarks];
922 [archived_data writeToURL:url atomically:YES];
923}
924
925- (void)readManualBookmarksFromDataStore
926{
927 [_manual_bookmarks autorelease];
928 _manual_bookmarks = [self arrayFromDataStoreURL:[self manualBookmarksDataStoreURL]];
929
930 if (_manual_bookmarks == nil)
931 {
932 _manual_bookmarks = [[NSMutableArray alloc] init];
933 }
934}
935
936- (void)readConnectionHistoryFromDataStore
937{
938 [_connection_history autorelease];
939 _connection_history = [self arrayFromDataStoreURL:[self connectionHistoryDataStoreURL]];
940
941 if (_connection_history == nil)
942 _connection_history = [[NSMutableArray alloc] init];
943}
944
945- (NSMutableArray *)arrayFromDataStoreURL:(NSURL *)url
946{
947 NSData *archived_data = [NSData dataWithContentsOfURL:url];
948
949 if (!archived_data)
950 return nil;
951
952 return [[NSKeyedUnarchiver unarchiveObjectWithData:archived_data] retain];
953}
954
955- (NSURL *)manualBookmarksDataStoreURL
956{
957 return [NSURL
958 fileURLWithPath:[NSString stringWithFormat:@"%@/%@",
959 [NSSearchPathForDirectoriesInDomains(
960 NSDocumentDirectory, NSUserDomainMask, YES)
961 lastObject],
962 @"com.freerdp.ifreerdp.bookmarks.plist"]];
963}
964
965- (NSURL *)connectionHistoryDataStoreURL
966{
967 return [NSURL
968 fileURLWithPath:[NSString
969 stringWithFormat:@"%@/%@",
970 [NSSearchPathForDirectoriesInDomains(
971 NSDocumentDirectory, NSUserDomainMask, YES)
972 lastObject],
973 @"com.freerdp.ifreerdp.connection_history.plist"]];
974}
975
976@end