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