iOS: initial release of iFreeRDP

This is the initial release of iFreeRDP the FreeRDP iOS client.
Build instructions can be found in docs/README.ios.
This commit is contained in:
Bernhard Miklautz
2013-02-14 14:59:12 +01:00
parent e168da48e1
commit e5cf8ff7fb
183 changed files with 18818 additions and 47 deletions

View File

@@ -0,0 +1,19 @@
/*
Application info controller
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import <UIKit/UIKit.h>
@interface AboutController : UIViewController <UIWebViewDelegate, UIAlertViewDelegate>
{
NSString* last_link_clicked;
UIWebView* webView;
}
@end

View File

@@ -0,0 +1,99 @@
/*
Application info controller
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "AboutController.h"
#import "Utils.h"
@implementation AboutController
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
// set title and tab-bar image
[self setTitle:NSLocalizedString(@"About", @"About Controller title")];
UIImage* tabBarIcon = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"tabbar_icon_about" ofType:@"png"]];
[self setTabBarItem:[[UITabBarItem alloc] initWithTitle:NSLocalizedString(@"About", @"Tabbar item about") image:tabBarIcon tag:0]];
last_link_clicked = nil;
}
return self;
}
- (void)dealloc
{
[super dealloc];
[last_link_clicked release];
}
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView
{
webView = [[[UIWebView alloc] initWithFrame:CGRectZero] autorelease];
[webView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
[webView setAutoresizesSubviews:YES];
[webView setDelegate:self];
[webView setDataDetectorTypes:UIDataDetectorTypeNone];
[self setView:webView];
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *filename = (IsPhone() ? @"about_phone" : @"about");
NSString *htmlString = [[[NSString alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:filename ofType:@"html" inDirectory:@"about_page"] encoding:NSUTF8StringEncoding error:nil] autorelease];
[webView loadHTMLString:[NSString stringWithFormat:htmlString,
TSXAppFullVersion(),
[[UIDevice currentDevice] systemName],
[[UIDevice currentDevice] systemVersion],
[[UIDevice currentDevice] model]] baseURL:[NSURL fileURLWithPath:[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"about_page"]]];
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
#pragma mark -
#pragma mark UIWebView callbacks
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
if([[request URL] isFileURL])
return YES;
if(navigationType == UIWebViewNavigationTypeLinkClicked)
{
[last_link_clicked release];
last_link_clicked = [[[request URL] absoluteString] retain];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"External Link", @"External Link Alert Title")
message:[NSString stringWithFormat:NSLocalizedString(@"Open [%@] in Browser?", @"Open link in browser (with link as parameter)"), last_link_clicked]
delegate:self
cancelButtonTitle:NSLocalizedString(@"OK", @"OK Button")
otherButtonTitles:NSLocalizedString(@"No", @"No Button"), nil];
[alert show];
[alert release];
return NO;
}
return YES;
}
#pragma mark UIAlertView delegate
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0)
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:last_link_clicked]];
}
@end

View File

@@ -0,0 +1,25 @@
/*
Controller to edit advanced bookmark settings
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "EditorBaseController.h"
@class ComputerBookmark;
@class ConnectionParams;
@interface AdvancedBookmarkEditorController : EditorBaseController
{
@private
ComputerBookmark* _bookmark;
ConnectionParams* _params;
}
// init for the given bookmark
- (id)initWithBookmark:(ComputerBookmark*)bookmark;
@end

View File

@@ -0,0 +1,310 @@
/*
Controller to edit advanced bookmark settings
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "AdvancedBookmarkEditorController.h"
#import "Bookmark.h"
#import "Utils.h"
#import "EditorSelectionController.h"
#import "ScreenSelectionController.h"
#import "PerformanceEditorController.h"
@interface AdvancedBookmarkEditorController ()
@end
#define SECTION_ADVANCED_SETTINGS 0
#define SECTION_COUNT 1
@implementation AdvancedBookmarkEditorController
- (id)initWithBookmark:(ComputerBookmark*)bookmark
{
if ((self = [super initWithStyle:UITableViewStyleGrouped]))
{
// set additional settings state according to bookmark data
_bookmark = [bookmark retain];
_params = [bookmark params];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self setTitle:NSLocalizedString(@"Advanced Settings", @"Advanced Settings title")];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// we need to reload the table view data here to have up-to-date data for the
// advanced settings accessory items (like for resolution/color mode settings)
[[self tableView] reloadData];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return SECTION_COUNT;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
switch (section)
{
case SECTION_ADVANCED_SETTINGS: // advanced settings
return 7;
default:
break;
}
return 0;
}
// set section headers
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
switch(section)
{
case SECTION_ADVANCED_SETTINGS:
return NSLocalizedString(@"Advanced", @"'Advanced': advanced settings header");
}
return @"unknown";
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// determine the required cell type
NSString* cellType = nil;
switch([indexPath section])
{
case SECTION_ADVANCED_SETTINGS: // advanced settings
{
switch([indexPath row])
{
case 0: // 3G Settings
cellType = TableCellIdentifierYesNo;
break;
case 1: // 3G screen/color depth
cellType = TableCellIdentifierSelection;
break;
case 2: // 3G performance settings
cellType = TableCellIdentifierSubEditor;
break;
case 3: // security mode
cellType = TableCellIdentifierSelection;
break;
case 4: // remote program
case 5: // work dir
cellType = TableCellIdentifierText;
break;
case 6: // console mode
cellType = TableCellIdentifierYesNo;
break;
default:
break;
}
break;
}
}
NSAssert(cellType != nil, @"Couldn't determine cell type");
// get the table view cell
UITableViewCell *cell = [self tableViewCellFromIdentifier:cellType];
NSAssert(cell, @"Invalid cell");
// set cell values
switch([indexPath section])
{
// advanced settings
case SECTION_ADVANCED_SETTINGS:
[self initAdvancedSettings:indexPath cell:cell];
break;
default:
break;
}
return cell;
}
// updates advanced settings in the UI
- (void)initAdvancedSettings:(NSIndexPath*)indexPath cell:(UITableViewCell*)cell
{
BOOL enable_3G_settings = [_params boolForKey:@"enable_3g_settings"];
switch(indexPath.row)
{
case 0:
{
EditFlagTableViewCell* flagCell = (EditFlagTableViewCell*)cell;
[[flagCell label] setText:NSLocalizedString(@"3G Settings", @"'3G Settings': Bookmark enable 3G settings")];
[[flagCell toggle] setTag:GET_TAG_FROM_PATH(indexPath)];
[[flagCell toggle] setOn:[_params boolForKey:@"enable_3g_settings"]];
[[flagCell toggle] addTarget:self action:@selector(toggleSettingValue:) forControlEvents:UIControlEventValueChanged];
break;
}
case 1:
{
EditSelectionTableViewCell* selCell = (EditSelectionTableViewCell*)cell;
[[selCell label] setText:NSLocalizedString(@"3G Screen", @"'3G Screen': Bookmark 3G Screen settings")];
NSString* resolution = ScreenResolutionDescription([_params intForKeyPath:@"settings_3g.screen_resolution_type"], [_params intForKeyPath:@"settings_3g.width"], [_params intForKeyPath:@"settings_3g.height"]);
int colorBits = [_params intForKeyPath:@"settings_3g.colors"];
[[selCell selection] setText:[NSString stringWithFormat:@"%@@%d", resolution, colorBits]];
[[selCell label] setEnabled:enable_3G_settings];
[[selCell selection] setEnabled:enable_3G_settings];
[selCell setSelectionStyle:enable_3G_settings ? UITableViewCellSelectionStyleBlue : UITableViewCellSelectionStyleNone];
break;
}
case 2:
{
EditSubEditTableViewCell* editCell = (EditSubEditTableViewCell*)cell;
[[editCell label] setText:NSLocalizedString(@"3G Performance", @"'3G Performance': Bookmark 3G Performance Settings")];
[[editCell label] setEnabled:enable_3G_settings];
[editCell setSelectionStyle:enable_3G_settings ? UITableViewCellSelectionStyleBlue : UITableViewCellSelectionStyleNone];
break;
}
case 3:
{
EditSelectionTableViewCell* selCell = (EditSelectionTableViewCell*)cell;
[[selCell label] setText:NSLocalizedString(@"Security", @"'Security': Bookmark protocl security settings")];
[[selCell selection] setText:ProtocolSecurityDescription([_params intForKey:@"security"])];
break;
}
case 4:
{
EditTextTableViewCell* textCell = (EditTextTableViewCell*)cell;
[[textCell label] setText:NSLocalizedString(@"Remote Program", @"'Remote Program': Bookmark remote program settings")];
[[textCell textfield] setText:[_params StringForKey:@"remote_program"]];
[[textCell textfield] setTag:GET_TAG_FROM_PATH(indexPath)];
[[textCell textfield] setPlaceholder:NSLocalizedString(@"not set", @"not set placeholder")];
[self adjustEditTextTableViewCell:textCell];
break;
}
case 5:
{
EditTextTableViewCell* textCell = (EditTextTableViewCell*)cell;
[[textCell label] setText:NSLocalizedString(@"Working Directory", @"'Working Directory': Bookmark working directory settings")];
[[textCell textfield] setText:[_params StringForKey:@"working_dir"]];
[[textCell textfield] setTag:GET_TAG_FROM_PATH(indexPath)];
[[textCell textfield] setPlaceholder:NSLocalizedString(@"not set", @"not set placeholder")];
[self adjustEditTextTableViewCell:textCell];
break;
}
case 6:
{
EditFlagTableViewCell* flagCell = (EditFlagTableViewCell*)cell;
[[flagCell label] setText:NSLocalizedString(@"Console Mode", @"'Console Mode': Bookmark console mode settings")];
[[flagCell toggle] setTag:GET_TAG_FROM_PATH(indexPath)];
[[flagCell toggle] setOn:[_params boolForKey:@"console"]];
[[flagCell toggle] addTarget:self action:@selector(toggleSettingValue:) forControlEvents:UIControlEventValueChanged];
break;
}
default:
NSLog(@"Invalid row index in settings table!");
break;
}
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIViewController* viewCtrl = nil;
// determine view
switch ([indexPath row])
{
case 1:
if ([_params boolForKey:@"enable_3g_settings"])
viewCtrl = [[[ScreenSelectionController alloc] initWithConnectionParams:_params keyPath:@"settings_3g"] autorelease];
break;
case 2:
if ([_params boolForKey:@"enable_3g_settings"])
viewCtrl = [[[PerformanceEditorController alloc] initWithConnectionParams:_params keyPath:@"settings_3g"] autorelease];
break;
case 3:
viewCtrl = [[[EditorSelectionController alloc] initWithConnectionParams:_params entries:[NSArray arrayWithObject:@"security"] selections:[NSArray arrayWithObject:SelectionForSecuritySetting()]] autorelease];
break;
default:
break;
}
// display view
if(viewCtrl)
[[self navigationController] pushViewController:viewCtrl animated:YES];
}
#pragma mark -
#pragma mark Text Field delegate
- (BOOL)textFieldShouldReturn:(UITextField*)textField
{
[textField resignFirstResponder];
return NO;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
switch(textField.tag)
{
// update remote program/work dir settings
case GET_TAG(SECTION_ADVANCED_SETTINGS, 4):
{
[_params setValue:[textField text] forKey:@"remote_program"];
break;
}
case GET_TAG(SECTION_ADVANCED_SETTINGS, 5):
{
[_params setValue:[textField text] forKey:@"working_dir"];
break;
}
default:
break;
}
return YES;
}
#pragma mark - Action handlers
- (void)toggleSettingValue:(id)sender
{
UISwitch* valueSwitch = (UISwitch*)sender;
switch(valueSwitch.tag)
{
case GET_TAG(SECTION_ADVANCED_SETTINGS, 0):
[_params setBool:[valueSwitch isOn] forKey:@"enable_3g_settings"];
NSArray* indexPaths = [NSArray arrayWithObjects:[NSIndexPath indexPathForRow:1 inSection:SECTION_ADVANCED_SETTINGS], [NSIndexPath indexPathForRow:2 inSection:SECTION_ADVANCED_SETTINGS], nil];
[[self tableView] reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
break;
case GET_TAG(SECTION_ADVANCED_SETTINGS, 6):
[_params setBool:[valueSwitch isOn] forKey:@"console"];
break;
default:
break;
}
}
@end

View File

@@ -0,0 +1,14 @@
/*
Controller to specify application wide settings
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "EditorBaseController.h"
@interface AppSettingsController : EditorBaseController
@end

View File

@@ -0,0 +1,309 @@
/*
Controller to specify application wide settings
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "AppSettingsController.h"
#import "Utils.h"
#import "Toast+UIView.h"
@implementation AppSettingsController
// keep this up-to-date for correct section display/hidding
#define SECTION_UI_SETTINGS 0
#define SECTION_CERTIFICATE_HANDLING_SETTINGS 1
#define SECTION_NUM_SECTIONS 2
#pragma mark -
#pragma mark Initialization
- (id)initWithStyle:(UITableViewStyle)style
{
if ((self = [super initWithStyle:style]))
{
UIImage* tabBarIcon = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"tabbar_icon_settings" ofType:@"png"]];
[self setTabBarItem:[[UITabBarItem alloc] initWithTitle:NSLocalizedString(@"Settings", @"Tabbar item settings") image:tabBarIcon tag:0]];
}
return self;
}
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
// set title
[self setTitle:NSLocalizedString(@"Settings", @"App Settings title")];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return SECTION_NUM_SECTIONS;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
switch (section)
{
case SECTION_UI_SETTINGS: // UI settings
return 5;
case SECTION_CERTIFICATE_HANDLING_SETTINGS: // certificate handling settings
return 2;
default:
break;
}
return 0;
}
// set section headers
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
switch(section)
{
case SECTION_UI_SETTINGS:
return NSLocalizedString(@"User Interface", @"UI settings section title");
case SECTION_CERTIFICATE_HANDLING_SETTINGS:
return NSLocalizedString(@"Server Certificate Handling", @"Server Certificate Handling section title");
default:
return nil;
}
return @"unknown";
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// determine the required cell type
NSString* cellIdentifier = nil;
switch([indexPath section])
{
case SECTION_UI_SETTINGS:
{
switch([indexPath row])
{
case 0:
case 1:
case 2:
case 3:
case 4:
cellIdentifier = TableCellIdentifierYesNo;
break;
}
break;
}
case SECTION_CERTIFICATE_HANDLING_SETTINGS:
{
switch([indexPath row])
{
case 0:
cellIdentifier = TableCellIdentifierYesNo;
break;
case 1:
cellIdentifier = TableCellIdentifierSubEditor;
break;
}
break;
}
}
NSAssert(cellIdentifier != nil, @"Couldn't determine cell type");
// get the table view cell
UITableViewCell *cell = [self tableViewCellFromIdentifier:cellIdentifier];
NSAssert(cell, @"Invalid cell");
// set cell values
switch([indexPath section])
{
case SECTION_UI_SETTINGS:
[self initUISettings:indexPath cell:cell];
break;
case SECTION_CERTIFICATE_HANDLING_SETTINGS:
[self initCertificateHandlingSettings:indexPath cell:cell];
break;
default:
break;
}
return cell;
}
#pragma mark - Initialization helpers
// updates UI settings in the UI
- (void)initUISettings:(NSIndexPath*)indexPath cell:(UITableViewCell*)cell
{
switch([indexPath row])
{
case 0:
{
EditFlagTableViewCell* flagCell = (EditFlagTableViewCell*)cell;
[[flagCell label] setText:NSLocalizedString(@"Hide Status Bar", "Show/Hide Phone Status Bar setting")];
[[flagCell toggle] setTag:GET_TAG_FROM_PATH(indexPath)];
[[flagCell toggle] setOn:[[NSUserDefaults standardUserDefaults] boolForKey:@"ui.hide_status_bar"]];
[[flagCell toggle] addTarget:self action:@selector(toggleSettingValue:) forControlEvents:UIControlEventValueChanged];
break;
}
case 1:
{
EditFlagTableViewCell* flagCell = (EditFlagTableViewCell*)cell;
[[flagCell label] setText:NSLocalizedString(@"Hide Tool Bar", "Show/Hide Tool Bar setting")];
[[flagCell toggle] setTag:GET_TAG_FROM_PATH(indexPath)];
[[flagCell toggle] setOn:[[NSUserDefaults standardUserDefaults] boolForKey:@"ui.hide_tool_bar"]];
[[flagCell toggle] addTarget:self action:@selector(toggleSettingValue:) forControlEvents:UIControlEventValueChanged];
break;
}
case 2:
{
EditFlagTableViewCell* flagCell = (EditFlagTableViewCell*)cell;
[[flagCell label] setText:NSLocalizedString(@"Swap Mouse Buttons", "Swap Mouse Button UI setting")];
[[flagCell toggle] setTag:GET_TAG_FROM_PATH(indexPath)];
[[flagCell toggle] setOn:[[NSUserDefaults standardUserDefaults] boolForKey:@"ui.swap_mouse_buttons"]];
[[flagCell toggle] addTarget:self action:@selector(toggleSettingValue:) forControlEvents:UIControlEventValueChanged];
break;
}
case 3:
{
EditFlagTableViewCell* flagCell = (EditFlagTableViewCell*)cell;
[[flagCell label] setText:NSLocalizedString(@"Invert Scrolling", "Invert Scrolling UI setting")];
[[flagCell toggle] setTag:GET_TAG_FROM_PATH(indexPath)];
[[flagCell toggle] setOn:[[NSUserDefaults standardUserDefaults] boolForKey:@"ui.invert_scrolling"]];
[[flagCell toggle] addTarget:self action:@selector(toggleSettingValue:) forControlEvents:UIControlEventValueChanged];
break;
}
case 4:
{
EditFlagTableViewCell* flagCell = (EditFlagTableViewCell*)cell;
[[flagCell label] setText:NSLocalizedString(@"Touch Pointer Auto Scroll", "Touch Pointer Auto Scroll UI setting")];
[[flagCell toggle] setTag:GET_TAG_FROM_PATH(indexPath)];
[[flagCell toggle] setOn:[[NSUserDefaults standardUserDefaults] boolForKey:@"ui.auto_scroll_touchpointer"]];
[[flagCell toggle] addTarget:self action:@selector(toggleSettingValue:) forControlEvents:UIControlEventValueChanged];
break;
}
default:
NSLog(@"Invalid row index in settings table!");
break;
}
}
// updates certificate handling settings in the UI
- (void)initCertificateHandlingSettings:(NSIndexPath*)indexPath cell:(UITableViewCell*)cell
{
switch([indexPath row])
{
case 0:
{
EditFlagTableViewCell* flagCell = (EditFlagTableViewCell*)cell;
[[flagCell label] setText:NSLocalizedString(@"Accept all Certificates", "Accept All Certificates setting")];
[[flagCell toggle] setTag:GET_TAG_FROM_PATH(indexPath)];
[[flagCell toggle] setOn:[[NSUserDefaults standardUserDefaults] boolForKey:@"security.accept_certificates"]];
[[flagCell toggle] addTarget:self action:@selector(toggleSettingValue:) forControlEvents:UIControlEventValueChanged];
break;
}
case 1:
{
EditSubEditTableViewCell* subCell = (EditSubEditTableViewCell*)cell;
[[subCell label] setText:NSLocalizedString(@"Erase Certificate Cache", @"Erase certificate cache button")];
break;
}
default:
NSLog(@"Invalid row index in settings table!");
break;
}
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// deselect any row to fake a button-pressed like effect
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// ensure everything is stored in our settings before we proceed
[[self view] endEditing:NO];
// clear certificate cache
if([indexPath section] == SECTION_CERTIFICATE_HANDLING_SETTINGS && [indexPath row] == 1)
{
// delete certificates cache
NSError* err;
if ([[NSFileManager defaultManager] removeItemAtPath:[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"/.freerdp"] error:&err])
[[self view] makeToast:NSLocalizedString(@"Certificate Cache cleared!", @"Clear Certificate cache success message") duration:ToastDurationNormal position:@"center"];
else
[[self view] makeToast:NSLocalizedString(@"Error clearing the Certificate Cache!", @"Clear Certificate cache failed message") duration:ToastDurationNormal position:@"center"];
}
}
#pragma mark -
#pragma mark Action Handlers
- (void)toggleSettingValue:(id)sender
{
UISwitch* valueSwitch = (UISwitch*)sender;
switch([valueSwitch tag])
{
case GET_TAG(SECTION_UI_SETTINGS, 0):
[[NSUserDefaults standardUserDefaults] setBool:[valueSwitch isOn] forKey:@"ui.hide_status_bar"];
break;
case GET_TAG(SECTION_UI_SETTINGS, 1):
[[NSUserDefaults standardUserDefaults] setBool:[valueSwitch isOn] forKey:@"ui.hide_tool_bar"];
break;
case GET_TAG(SECTION_UI_SETTINGS, 2):
[[NSUserDefaults standardUserDefaults] setBool:[valueSwitch isOn] forKey:@"ui.swap_mouse_buttons"];
SetSwapMouseButtonsFlag([valueSwitch isOn]);
break;
case GET_TAG(SECTION_UI_SETTINGS, 3):
[[NSUserDefaults standardUserDefaults] setBool:[valueSwitch isOn] forKey:@"ui.invert_scrolling"];
SetInvertScrollingFlag([valueSwitch isOn]);
break;
case GET_TAG(SECTION_UI_SETTINGS, 4):
[[NSUserDefaults standardUserDefaults] setBool:[valueSwitch isOn] forKey:@"ui.auto_scroll_touchpointer"];
SetInvertScrollingFlag([valueSwitch isOn]);
break;
case GET_TAG(SECTION_CERTIFICATE_HANDLING_SETTINGS, 0):
[[NSUserDefaults standardUserDefaults] setBool:[valueSwitch isOn] forKey:@"security.accept_certificates"];
break;
default:
break;
}
}
@end

View File

@@ -0,0 +1,40 @@
/*
Bookmark editor controller
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import <UIKit/UIKit.h>
#import "EditorBaseController.h"
@class ComputerBookmark;
@class ConnectionParams;
@protocol BookmarkEditorDelegate <NSObject>
// bookmark editing finsihed
- (void)commitBookmark:(ComputerBookmark*)bookmark;
@end
@interface BookmarkEditorController : EditorBaseController <UIAlertViewDelegate>
{
@private
ComputerBookmark* _bookmark;
ConnectionParams* _params;
BOOL _display_server_settings;
id<BookmarkEditorDelegate> delegate;
}
@property (nonatomic, assign) id<BookmarkEditorDelegate> delegate;
// init for the given bookmark
- (id)initWithBookmark:(ComputerBookmark*)bookmark;
@end

View File

@@ -0,0 +1,400 @@
/*
Bookmark editor controller
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "BookmarkEditorController.h"
#import "Bookmark.h"
#import "Utils.h"
#import "ScreenSelectionController.h"
#import "PerformanceEditorController.h"
#import "CredentialsEditorController.h"
#import "AdvancedBookmarkEditorController.h"
@implementation BookmarkEditorController
@synthesize delegate;
#define SECTION_SERVER 0
#define SECTION_CREDENTIALS 1
#define SECTION_SETTINGS 2
#define SECTION_COUNT 3
#pragma mark -
#pragma mark Initialization
- (id)initWithBookmark:(ComputerBookmark*)bookmark
{
if ((self = [super initWithStyle:UITableViewStyleGrouped]))
{
// set additional settings state according to bookmark data
_bookmark = [bookmark retain];
_params = [bookmark params];
// if this is a TSX Connect bookmark - disable server settings
if([_bookmark isKindOfClass:NSClassFromString(@"TSXConnectComputerBookmark")])
_display_server_settings = NO;
else
_display_server_settings = YES;
}
return self;
}
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
// replace back button with a custom handler that checks if the required bookmark settings were specified
UIBarButtonItem* saveButton = [[[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Save", @"Save Button title") style:UIBarButtonItemStyleDone target:self action:@selector(handleSave:)] autorelease];
UIBarButtonItem* cancelButton = [[[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Cancel", @"Cancel Button title") style:UIBarButtonItemStyleBordered target:self action:@selector(handleCancel:)] autorelease];
[[self navigationItem] setLeftBarButtonItem:cancelButton];
[[self navigationItem] setRightBarButtonItem:saveButton];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// we need to reload the table view data here to have up-to-date data for the
// advanced settings accessory items (like for resolution/color mode settings)
[[self tableView] reloadData];
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return SECTION_COUNT;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
switch (section)
{
case SECTION_SERVER: // server settings
return (_display_server_settings ? 3 : 0);
case SECTION_CREDENTIALS: // credentials
return 1;
case SECTION_SETTINGS: // session settings
return 3;
default:
break;
}
return 0;
}
// set section headers
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
switch(section)
{
case SECTION_SERVER:
return (_display_server_settings ? NSLocalizedString(@"Host", @"'Host': host settings header") : nil);
case SECTION_CREDENTIALS:
return NSLocalizedString(@"Credentials", @"'Credentials': credentials settings header");
case SECTION_SETTINGS:
return NSLocalizedString(@"Settings", @"'Session Settings': session settings header");
}
return @"unknown";
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// determine the required cell type
NSString* cellType = nil;
switch([indexPath section])
{
case SECTION_SERVER:
cellType = TableCellIdentifierText;
break;
case SECTION_CREDENTIALS:
cellType = TableCellIdentifierSelection;
break;
case SECTION_SETTINGS: // settings
{
switch([indexPath row])
{
case 0: // screen/color depth
cellType = TableCellIdentifierSelection;
break;
case 1: // performance settings
case 2: // advanced settings
cellType = TableCellIdentifierSubEditor;
break;
default:
break;
}
}
break;
default:
break;
}
NSAssert(cellType != nil, @"Couldn't determine cell type");
// get the table view cell
UITableViewCell *cell = [self tableViewCellFromIdentifier:cellType];
NSAssert(cell, @"Invalid cell");
// set cell values
switch([indexPath section])
{
// server settings
case SECTION_SERVER:
[self initServerSettings:indexPath cell:cell];
break;
// credentials
case SECTION_CREDENTIALS:
[self initCredentialSettings:indexPath cell:cell];
break;
// session settings
case SECTION_SETTINGS:
[self initSettings:indexPath cell:cell];
break;
default:
break;
}
return cell;
}
// updates server settings in the UI
- (void)initServerSettings:(NSIndexPath*)indexPath cell:(UITableViewCell*)cell
{
EditTextTableViewCell* textCell = (EditTextTableViewCell*)cell;
[[textCell textfield] setTag:GET_TAG_FROM_PATH(indexPath)];
switch([indexPath row])
{
case 0:
[[textCell label] setText:NSLocalizedString(@"Label", @"'Label': Bookmark label")];
[[textCell textfield] setText:[_bookmark label]];
[[textCell textfield] setPlaceholder:NSLocalizedString(@"not set", @"not set placeholder")];
break;
case 1:
[[textCell label] setText:NSLocalizedString(@"Host", @"'Host': Bookmark hostname")];
[[textCell textfield] setText:[_params StringForKey:@"hostname"]];
[[textCell textfield] setPlaceholder:NSLocalizedString(@"not set", @"not set placeholder")];
break;
case 2:
[[textCell label] setText:NSLocalizedString(@"Port", @"'Port': Bookmark port")];
[[textCell textfield] setText:[NSString stringWithFormat:@"%d", [_params intForKey:@"port"]]];
[[textCell textfield] setKeyboardType:UIKeyboardTypeNumberPad];
break;
default:
NSLog(@"Invalid row index in settings table!");
break;
}
[self adjustEditTextTableViewCell:textCell];
}
// updates credentials in the UI
- (void)initCredentialSettings:(NSIndexPath*)indexPath cell:(UITableViewCell*)cell
{
EditSelectionTableViewCell* selCell = (EditSelectionTableViewCell*)cell;
switch(indexPath.row)
{
case 0:
[[selCell label] setText:NSLocalizedString(@"Credentials", @"'Credentials': Bookmark credentials")];
[[selCell selection] setText:[_params StringForKey:@"username"]];
break;
default:
NSLog(@"Invalid row index in settings table!");
break;
}
}
// updates session settings in the UI
- (void)initSettings:(NSIndexPath*)indexPath cell:(UITableViewCell*)cell
{
switch(indexPath.row)
{
case 0:
{
EditSelectionTableViewCell* selCell = (EditSelectionTableViewCell*)cell;
[[selCell label] setText:NSLocalizedString(@"Screen", @"'Screen': Bookmark Screen settings")];
NSString* resolution = ScreenResolutionDescription([_params intForKey:@"screen_resolution_type"], [_params intForKey:@"width"], [_params intForKey:@"height"]);
int colorBits = [_params intForKey:@"colors"];
[[selCell selection] setText:[NSString stringWithFormat:@"%@@%d", resolution, colorBits]];
break;
}
case 1:
{
EditSubEditTableViewCell* editCell = (EditSubEditTableViewCell*)cell;
[[editCell label] setText:NSLocalizedString(@"Performance", @"'Performance': Bookmark Performance Settings")];
break;
}
case 2:
{
EditSubEditTableViewCell* editCell = (EditSubEditTableViewCell*)cell;
[[editCell label] setText:NSLocalizedString(@"Advanced", @"'Advanced': Bookmark Advanced Settings")];
break;
}
default:
NSLog(@"Invalid row index in settings table!");
break;
}
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIViewController* viewCtrl = nil;
// determine view
switch([indexPath section])
{
case SECTION_CREDENTIALS:
{
if ([indexPath row] == 0)
viewCtrl = [[[CredentialsEditorController alloc] initWithBookmark:_bookmark] autorelease];
break;
}
case SECTION_SETTINGS:
{
switch ([indexPath row])
{
case 0:
viewCtrl = [[[ScreenSelectionController alloc] initWithConnectionParams:_params] autorelease];
break;
case 1:
viewCtrl = [[[PerformanceEditorController alloc] initWithConnectionParams:_params] autorelease];
break;
case 2:
viewCtrl = [[[AdvancedBookmarkEditorController alloc] initWithBookmark:_bookmark] autorelease];
break;
default:
break;
}
break;
}
}
// display view
if(viewCtrl)
[[self navigationController] pushViewController:viewCtrl animated:YES];
}
#pragma mark -
#pragma mark Text Field delegate
- (BOOL)textFieldShouldReturn:(UITextField*)textField
{
[textField resignFirstResponder];
return NO;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
switch(textField.tag)
{
// update server settings
case GET_TAG(SECTION_SERVER, 0):
[_bookmark setLabel:[textField text]];
break;
case GET_TAG(SECTION_SERVER, 1):
[_params setValue:[textField text] forKey:@"hostname"];
break;
case GET_TAG(SECTION_SERVER, 2):
[_params setInt:[[textField text] intValue] forKey:@"port"];
break;
default:
break;
}
return YES;
}
#pragma mark - UIAlertViewDelegate methods
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
// clicked yes?
if (buttonIndex == 0)
{
// cancel bookmark editing and return to previous view controller
[[self navigationController] popViewControllerAnimated:YES];
}
}
#pragma mark -
#pragma mark Action Handlers
- (void)handleSave:(id)sender
{
// resign any first responder (so that we finish editing any bookmark parameter that might be currently edited)
[[self view] endEditing:NO];
// verify that bookmark is complete (only for manual bookmarks)
if (![_bookmark isKindOfClass:NSClassFromString(@"TSXConnectComputerBookmark")])
{
if ([[_bookmark label] length] == 0 || [[_params StringForKey:@"hostname"] length] == 0 || [_params intForKey:@"port"] == 0)
{
UIAlertView* alertView = [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Cancel without saving?", @"Incomplete bookmark error title") message:NSLocalizedString(@"Press 'Cancel' to abort!\nPress 'Continue' to specify the required fields!", @"Incomplete bookmark error message") delegate:self cancelButtonTitle:NSLocalizedString(@"Cancel", @"Cancel Button") otherButtonTitles:NSLocalizedString(@"Continue", @"Continue Button"), nil] autorelease];
[alertView show];
return;
}
}
// commit bookmark
if ([[self delegate] respondsToSelector:@selector(commitBookmark:)])
[[self delegate] commitBookmark:_bookmark];
// return to previous view controller
[[self navigationController] popViewControllerAnimated:YES];
}
- (void)handleCancel:(id)sender
{
// return to previous view controller
[[self navigationController] popViewControllerAnimated:YES];
}
#pragma mark -
#pragma mark Memory management
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Relinquish ownership any cached data, images, etc that aren't in use.
}
- (void)dealloc {
[super dealloc];
[_bookmark autorelease];
}
@end

View File

@@ -0,0 +1,59 @@
/*
bookmarks and active session view controller
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import <UIKit/UIKit.h>
#import "Bookmark.h"
#import "BookmarkTableCell.h"
#import "SessionTableCell.h"
#import "BookmarkEditorController.h"
#import "Reachability.h"
@interface BookmarkListController : UIViewController <UISearchBarDelegate, UITableViewDelegate, UITableViewDataSource, UIAlertViewDelegate, BookmarkEditorDelegate>
{
// custom bookmark and session table cells
BookmarkTableCell* _bmTableCell;
SessionTableCell* _sessTableCell;
// child views
UISearchBar* _searchBar;
UITableView* _tableView;
// array with search results (or nil if no search active)
NSMutableArray* _manual_search_result;
NSMutableArray* _tsxconnect_search_result;
NSMutableArray* _history_search_result;
// bookmark arrays
NSMutableArray* _manual_bookmarks;
NSMutableArray* _tsxconnect_bookmarks;
// bookmark star images
UIImage* _star_on_img;
UIImage* _star_off_img;
// array with active sessions
NSMutableArray* _active_sessions;
// array with connection history entries
NSMutableArray* _connection_history;
// temporary bookmark when asking if the user wants to store a bookmark for a session initiated by a quick connect
ComputerBookmark* _temporary_bookmark;
// reachability notification helper for tsx connect
Reachability* _tsxconnect_reachability;
}
@property (nonatomic, retain) IBOutlet UISearchBar* searchBar;
@property (nonatomic, retain) IBOutlet UITableView* tableView;
@property (nonatomic, retain) IBOutlet BookmarkTableCell* bmTableCell;
@property (nonatomic, retain) IBOutlet SessionTableCell* sessTableCell;
@end

View File

@@ -0,0 +1,839 @@
/*
bookmarks and active session view controller
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "BookmarkListController.h"
#import "Utils.h"
#import "BookmarkEditorController.h"
#import "RDPSessionViewController.h"
#import "Toast+UIView.h"
#import "Reachability.h"
#define SECTION_SESSIONS 0
#define SECTION_BOOKMARKS 1
#define NUM_SECTIONS 2
@interface BookmarkListController (Private)
#pragma mark misc functions
- (UIButton*)disclosureButtonWithImage:(UIImage*)image;
- (void)performSearch:(NSString*)searchText;
#pragma mark Persisting bookmarks
- (void)scheduleWriteBookmarksToDataStore;
- (void)writeBookmarksToDataStore;
- (void)scheduleWriteManualBookmarksToDataStore;
- (void)writeManualBookmarksToDataStore;
- (void)readManualBookmarksFromDataStore;
- (void)writeArray:(NSArray*)bookmarks toDataStoreURL:(NSURL*)url;
- (NSMutableArray*)arrayFromDataStoreURL:(NSURL*)url;
- (NSURL*)manualBookmarksDataStoreURL;
- (NSURL*)connectionHistoryDataStoreURL;
@end
@implementation BookmarkListController
@synthesize searchBar = _searchBar, tableView = _tableView, bmTableCell = _bmTableCell, sessTableCell = _sessTableCell;
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]))
{
// load bookmarks
[self readManualBookmarksFromDataStore];
// load connection history
[self readConnectionHistoryFromDataStore];
// init search result array
_manual_search_result = nil;
// register for session notifications
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionDisconnected:) name:TSXSessionDidDisconnectNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionFailedToConnect:) name:TSXSessionDidFailToConnectNotification object:nil];
// set title and tabbar controller image
[self setTitle:NSLocalizedString(@"Connections", @"'Connections': bookmark controller title")];
[self setTabBarItem:[[UITabBarItem alloc] initWithTabBarSystemItem:UITabBarSystemItemBookmarks tag:0]];
// load images
_star_on_img = [[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"icon_accessory_star_on" ofType:@"png"]] retain];
_star_off_img = [[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"icon_accessory_star_off" ofType:@"png"]] retain];
// init reachability detection
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:) name:kReachabilityChangedNotification object:nil];
// init other properties
_active_sessions = [[NSMutableArray alloc] init];
_temporary_bookmark = nil;
}
return self;
}
- (void)loadView
{
[super loadView];
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
// set edit button to allow bookmark list editing
[[self navigationItem] setRightBarButtonItem:[self editButtonItem]];
/*
if (![[InAppPurchaseManager sharedInAppPurchaseManager] isProVersion])
[[self navigationItem] setLeftBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:@"Go Pro" style:UIBarButtonItemStyleDone target:self action:@selector(goProButtonPressed:)]];
*/
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// in case we had a search - search again cause the bookmark searchable items could have changed
if ([[_searchBar text] length] > 0)
[self performSearch:[_searchBar text]];
// to reflect any bookmark changes - reload table
[_tableView reloadData];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// clear any search
[_searchBar setText:@""];
[_searchBar resignFirstResponder];
[self performSearch:@""];
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return YES;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc
{
[_tsxconnect_reachability stopNotifier];
[_tsxconnect_reachability release];
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_temporary_bookmark release];
[_connection_history release];
[_active_sessions release];
[_tsxconnect_search_result release];
[_manual_search_result release];
[_manual_bookmarks release];
[_tsxconnect_bookmarks release];
[super dealloc];
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return NUM_SECTIONS;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
switch(section)
{
case SECTION_SESSIONS:
return 0;
break;
case SECTION_BOOKMARKS:
{
// (+1 for Add Bookmark entry)
if(_manual_search_result != nil)
return ([_manual_search_result count] + [_history_search_result count] + 1);
return ([_manual_bookmarks count] + 1);
}
break;
default:
break;
}
return 0;
}
- (UITableViewCell*)cellForGenericListEntry
{
static NSString *CellIdentifier = @"BookmarkListCell";
UITableViewCell *cell = [[self tableView] dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[cell setAccessoryView:[self disclosureButtonWithImage:_star_off_img]];
}
return cell;
}
- (BookmarkTableCell*)cellForBookmark
{
static NSString *BookmarkCellIdentifier = @"BookmarkCell";
BookmarkTableCell *cell = (BookmarkTableCell*)[[self tableView] dequeueReusableCellWithIdentifier:BookmarkCellIdentifier];
if(cell == nil)
{
[[NSBundle mainBundle] loadNibNamed:@"BookmarkTableViewCell" owner:self options:nil];
[_bmTableCell setAccessoryView:[self disclosureButtonWithImage:_star_on_img]];
cell = _bmTableCell;
_bmTableCell = nil;
}
return cell;
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
switch ([indexPath section])
{
case SECTION_SESSIONS:
{
// get custom session cell
static NSString *SessionCellIdentifier = @"SessionCell";
SessionTableCell *cell = (SessionTableCell*)[tableView dequeueReusableCellWithIdentifier:SessionCellIdentifier];
if(cell == nil)
{
[[NSBundle mainBundle] loadNibNamed:@"SessionTableViewCell" owner:self options:nil];
cell = _sessTableCell;
_sessTableCell = nil;
}
// set cell data
RDPSession* session = [_active_sessions objectAtIndex:[indexPath row]];
[[cell title] setText:[session sessionName]];
[[cell server] setText:[[session params] StringForKey:@"hostname"]];
if([[[cell server] text] length] == 0)
[[cell server] setText:@"TSX Connect"];
[[cell username] setText:[[session params] StringForKey:@"username"]];
[[cell screenshot] setImage:[session getScreenshotWithSize:[[cell screenshot] bounds].size]];
[[cell disconnectButton] setTag:[indexPath row]];
return cell;
}
case SECTION_BOOKMARKS:
{
// special handling for first cell - quick connect/quick create Bookmark cell
if([indexPath row] == 0)
{
// if a search text is entered the cell becomes a quick connect/quick create bookmark cell - otherwise it's just an add bookmark cell
UITableViewCell* cell = [self cellForGenericListEntry];
if ([[_searchBar text] length] == 0)
{
[[cell textLabel] setText:[@" " stringByAppendingString:NSLocalizedString(@"Add Connection", @"'Add Connection': button label")]];
[((UIButton*)[cell accessoryView]) setHidden:YES];
}
else
{
[[cell textLabel] setText:[@" " stringByAppendingString:[_searchBar text]]];
[((UIButton*)[cell accessoryView]) setHidden:NO];
}
return cell;
}
else
{
// do we have a history cell or bookmark cell?
if ([self isIndexPathToHistoryItem:indexPath])
{
UITableViewCell* cell = [self cellForGenericListEntry];
[[cell textLabel] setText:[@" " stringByAppendingString:[_history_search_result objectAtIndex:[self historyIndexFromIndexPath:indexPath]]]];
[((UIButton*)[cell accessoryView]) setHidden:NO];
return cell;
}
else
{
// set cell properties
ComputerBookmark* entry;
BookmarkTableCell* cell = [self cellForBookmark];
if(_manual_search_result == nil)
entry = [_manual_bookmarks objectAtIndex:[self bookmarkIndexFromIndexPath:indexPath]];
else
entry = [[_manual_search_result objectAtIndex:[self bookmarkIndexFromIndexPath:indexPath]] valueForKey:@"bookmark"];
[[cell title] setText:[entry label]];
[[cell subTitle] setText:[[entry params] StringForKey:@"hostname"]];
return cell;
}
}
}
default:
break;
}
NSAssert(0, @"Failed to create cell");
return nil;
}
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// dont allow to edit Add Bookmark item
if([indexPath section] == SECTION_SESSIONS)
return NO;
if([indexPath section] == SECTION_BOOKMARKS && [indexPath row] == 0)
return NO;
return YES;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if(editingStyle == UITableViewCellEditingStyleDelete)
{
// Delete the row from the data source
switch([indexPath section])
{
case SECTION_BOOKMARKS:
{
if (_manual_search_result == nil)
[_manual_bookmarks removeObjectAtIndex:[self bookmarkIndexFromIndexPath:indexPath]];
else
{
// history item or bookmark?
if ([self isIndexPathToHistoryItem:indexPath])
{
[_connection_history removeObject:[_history_search_result objectAtIndex:[self historyIndexFromIndexPath:indexPath]]];
[_history_search_result removeObjectAtIndex:[self historyIndexFromIndexPath:indexPath]];
}
else
{
[_manual_bookmarks removeObject:[[_manual_search_result objectAtIndex:[self bookmarkIndexFromIndexPath:indexPath]] valueForKey:@"bookmark"]];
[_manual_search_result removeObjectAtIndex:[self bookmarkIndexFromIndexPath:indexPath]];
}
}
[self scheduleWriteManualBookmarksToDataStore];
break;
}
}
[tableView reloadSections:[NSIndexSet indexSetWithIndex:[indexPath section]] withRowAnimation:UITableViewRowAnimationNone];
}
}
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
if([fromIndexPath compare:toIndexPath] != NSOrderedSame)
{
switch([fromIndexPath section])
{
case SECTION_BOOKMARKS:
{
int fromIdx = [self bookmarkIndexFromIndexPath:fromIndexPath];
int toIdx = [self bookmarkIndexFromIndexPath:toIndexPath];
ComputerBookmark* temp_bookmark = [[_manual_bookmarks objectAtIndex:fromIdx] retain];
[_manual_bookmarks removeObjectAtIndex:fromIdx];
if (toIdx >= [_manual_bookmarks count])
[_manual_bookmarks addObject:temp_bookmark];
else
[_manual_bookmarks insertObject:temp_bookmark atIndex:toIdx];
[temp_bookmark release];
[self scheduleWriteManualBookmarksToDataStore];
break;
}
}
}
}
// prevent that an item is moved befoer the Add Bookmark item
-(NSIndexPath*)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath
{
// don't allow to move:
// - items between sections
// - the quick connect/quick create bookmark cell
// - any item while a search is applied
if([proposedDestinationIndexPath row] == 0 || ([sourceIndexPath section] != [proposedDestinationIndexPath section]) ||
_manual_search_result != nil || _tsxconnect_search_result != nil)
{
return sourceIndexPath;
}
else
{
return proposedDestinationIndexPath;
}
}
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
// dont allow to reorder Add Bookmark item
if([indexPath section] == SECTION_BOOKMARKS && [indexPath row] == 0)
return NO;
return YES;
}
- (NSString*)tableView:(UITableView*)tableView titleForHeaderInSection:(NSInteger)section
{
if(section == SECTION_SESSIONS && [_active_sessions count] > 0)
return NSLocalizedString(@"My Sessions", @"'My Session': section sessions header");
if(section == SECTION_BOOKMARKS)
return NSLocalizedString(@"Manual Connections", @"'Manual Connections': section manual bookmarks header");
return nil;
}
- (NSString*)tableView:(UITableView*)tableView titleForFooterInSection:(NSInteger)section
{
return nil;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if([indexPath section] == SECTION_SESSIONS)
return 72;
return [tableView rowHeight];
}
#pragma mark -
#pragma mark Table view delegate
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
[[self tableView] setEditing:editing animated:animated];
}
- (void)accessoryButtonTapped:(UIControl*)button withEvent:(UIEvent*)event
{
// forward a tap on our custom accessory button to the real accessory button handler
NSIndexPath* indexPath = [[self tableView] indexPathForRowAtPoint:[[[event touchesForView:button] anyObject] locationInView:[self tableView]]];
if (indexPath == nil)
return;
[[[self tableView] delegate] tableView:[self tableView] accessoryButtonTappedForRowWithIndexPath:indexPath];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if([indexPath section] == SECTION_SESSIONS)
{
// resume session
RDPSession* session = [_active_sessions objectAtIndex:[indexPath row]];
UIViewController* ctrl = [[RDPSessionViewController alloc] initWithNibName:@"RDPSessionView" bundle:nil session:session];
[ctrl setHidesBottomBarWhenPushed:YES];
[[self navigationController] pushViewController:ctrl animated:YES];
}
else
{
ComputerBookmark* bookmark = nil;
if([indexPath section] == SECTION_BOOKMARKS)
{
// first row has either quick connect or add bookmark item
if([indexPath row] == 0)
{
if ([[_searchBar text] length] == 0)
{
// show add bookmark controller
BookmarkEditorController* bookmarkEditorController = [[[BookmarkEditorController alloc] initWithBookmark:[[ComputerBookmark alloc] initWithBaseDefaultParameters]] autorelease];
[bookmarkEditorController setTitle:NSLocalizedString(@"Add Connection", @"Add Connection title")];
[bookmarkEditorController setDelegate:self];
[bookmarkEditorController setHidesBottomBarWhenPushed:YES];
[[self navigationController] pushViewController:bookmarkEditorController animated:YES];
}
else
{
// create a quick connect bookmark and add an entry to the quick connect history (if not already in the history)
bookmark = [self bookmarkForQuickConnectTo:[_searchBar text]];
if (![_connection_history containsObject:[_searchBar text]])
{
[_connection_history addObject:[_searchBar text]];
[self scheduleWriteConnectionHistoryToDataStore];
}
}
}
else
{
if(_manual_search_result != nil)
{
if ([self isIndexPathToHistoryItem:indexPath])
{
// create a quick connect bookmark for a history item
NSString* item = [_history_search_result objectAtIndex:[self historyIndexFromIndexPath:indexPath]];
bookmark = [self bookmarkForQuickConnectTo:item];
}
else
bookmark = [[_manual_search_result objectAtIndex:[self bookmarkIndexFromIndexPath:indexPath]] valueForKey:@"bookmark"];
}
else
bookmark = [_manual_bookmarks objectAtIndex:[self bookmarkIndexFromIndexPath:indexPath]]; // -1 because of ADD BOOKMARK entry
}
// set reachability status
WakeUpWWAN();
[bookmark setConntectedViaWLAN:[[Reachability reachabilityWithHostName:[[bookmark params] StringForKey:@"hostname"]] currentReachabilityStatus] == ReachableViaWiFi];
}
if(bookmark != nil)
{
// create rdp session
RDPSession* session = [[[RDPSession alloc] initWithBookmark:bookmark] autorelease];
UIViewController* ctrl = [[RDPSessionViewController alloc] initWithNibName:@"RDPSessionView" bundle:nil session:session];
[ctrl setHidesBottomBarWhenPushed:YES];
[[self navigationController] pushViewController:ctrl animated:YES];
[_active_sessions addObject:session];
}
}
}
- (void)tableView:(UITableView*)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath*)indexPath
{
// get the bookmark
NSString* bookmark_editor_title = NSLocalizedString(@"Edit Connection", @"Edit Connection title");
ComputerBookmark* bookmark = nil;
if ([indexPath section] == SECTION_BOOKMARKS)
{
if ([indexPath row] == 0)
{
// create a new bookmark and init hostname and label
bookmark = [self bookmarkForQuickConnectTo:[_searchBar text]];
bookmark_editor_title = NSLocalizedString(@"Add Connection", @"Add Connection title");
}
else
{
if (_manual_search_result != nil)
{
if ([self isIndexPathToHistoryItem:indexPath])
{
// create a new bookmark and init hostname and label
NSString* item = [_history_search_result objectAtIndex:[self historyIndexFromIndexPath:indexPath]];
bookmark = [self bookmarkForQuickConnectTo:item];
bookmark_editor_title = NSLocalizedString(@"Add Connection", @"Add Connection title");
}
else
bookmark = [[_manual_search_result objectAtIndex:[self bookmarkIndexFromIndexPath:indexPath]] valueForKey:@"bookmark"];
}
else
bookmark = [_manual_bookmarks objectAtIndex:[self bookmarkIndexFromIndexPath:indexPath]]; // -1 because of ADD BOOKMARK entry
}
}
// bookmark found? - start the editor
if (bookmark != nil)
{
BookmarkEditorController* editBookmarkController = [[[BookmarkEditorController alloc] initWithBookmark:bookmark] autorelease];
[editBookmarkController setHidesBottomBarWhenPushed:YES];
[editBookmarkController setTitle:bookmark_editor_title];
[editBookmarkController setDelegate:self];
[[self navigationController] pushViewController:editBookmarkController animated:YES];
}
}
#pragma mark -
#pragma mark Search Bar Delegates
- (BOOL)searchBarShouldBeginEditing:(UISearchBar*)searchBar
{
// show cancel button
[searchBar setShowsCancelButton:YES animated:YES];
return YES;
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
// clear search result
[_tsxconnect_search_result release];
_tsxconnect_search_result = nil;
[_manual_search_result release];
_manual_search_result = nil;
// clear text and remove cancel button
[searchBar setText:@""];
[searchBar resignFirstResponder];
}
- (BOOL)searchBarShouldEndEditing:(UISearchBar*)searchBar
{
[searchBar setShowsCancelButton:NO animated:YES];
// re-enable table selection
[_tableView setAllowsSelection:YES];
return YES;
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
[_searchBar resignFirstResponder];
}
- (void)searchBar:(UISearchBar*)searchBar textDidChange:(NSString*)searchText
{
[self performSearch:searchText];
[_tableView reloadData];
}
#pragma mark - Session handling
// session was added
- (void)sessionDisconnected:(NSNotification*)notification
{
// remove session from active sessions
RDPSession* session = (RDPSession*)[notification object];
[_active_sessions removeObject:session];
// if this view is currently active refresh tsxconnect entries
if([[self navigationController] visibleViewController] == self)
[_tableView reloadSections:[NSIndexSet indexSetWithIndex:SECTION_SESSIONS] withRowAnimation:UITableViewRowAnimationNone];
// if session's bookmark is not in the bookmark list ask the user if he wants to add it
// (this happens if the session is created using the quick connect feature)
if (![[session bookmark] isKindOfClass:NSClassFromString(@"TSXConnectComputerBookmark")] &&
![_manual_bookmarks containsObject:[session bookmark]])
{
// retain the bookmark in case we want to save it later
_temporary_bookmark = [[session bookmark] retain];
// ask the user if he wants to save the bookmark
NSString* title = NSLocalizedString(@"Save Connection Settings?", @"Save connection settings title");
NSString* message = NSLocalizedString(@"Your Connection Settings have not been saved. Do you want to save them?", @"Save connection settings message");
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:self
cancelButtonTitle:NSLocalizedString(@"Yes", @"Yes Button") otherButtonTitles:NSLocalizedString(@"No", @"No Button"), nil];
[alert show];
}
}
- (void)sessionFailedToConnect:(NSNotification*)notification
{
// remove session from active sessions
RDPSession* session = (RDPSession*)[notification object];
[_active_sessions removeObject:session];
// display error toast
[[self view] makeToast:NSLocalizedString(@"Failed to connect to session!", @"Failed to connect error message") duration:ToastDurationNormal position:@"center"];
}
#pragma mark - UIAlertView delegates
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
// yes clicked?
if (buttonIndex == 0 && _temporary_bookmark)
{
[_manual_bookmarks addObject:_temporary_bookmark];
[_tableView reloadSections:[NSIndexSet indexSetWithIndex:SECTION_BOOKMARKS] withRowAnimation:UITableViewRowAnimationNone];
}
[_temporary_bookmark autorelease];
_temporary_bookmark = nil;
}
#pragma mark - Reachability notification
- (void)reachabilityChanged:(NSNotification*)notification
{
// no matter how the network changed - we will disconnect
// disconnect session (if there is any)
if ([_active_sessions count] > 0)
{
RDPSession* session = [_active_sessions objectAtIndex:0];
[session disconnect];
}
}
#pragma mark - BookmarkEditorController delegate
- (void)commitBookmark:(ComputerBookmark *)bookmark
{
// if we got a manual bookmark that is not in the list yet - add it
if (![_manual_bookmarks containsObject:bookmark])
[_manual_bookmarks addObject:bookmark];
// remove any quick connect history entry with the same hostname
NSString* hostname = [[bookmark params] StringForKey:@"hostname"];
if ([_connection_history containsObject:hostname])
{
[_connection_history removeObject:hostname];
[self scheduleWriteConnectionHistoryToDataStore];
}
[self scheduleWriteManualBookmarksToDataStore];
}
- (IBAction)disconnectButtonPressed:(id)sender
{
// disconnect session and refresh table view
RDPSession* session = [_active_sessions objectAtIndex:[sender tag]];
[session disconnect];
}
#pragma mark - Misc functions
- (BOOL)hasNoBookmarks
{
return ([_manual_bookmarks count] == 0 && [_tsxconnect_bookmarks count] == 0);
}
- (UIButton*)disclosureButtonWithImage:(UIImage*)image
{
// we make the button a little bit bigger (image widht * 2, height + 10) so that the user doesn't accidentally connect to the bookmark ...
UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setFrame:CGRectMake(0, 0, [image size].width * 2, [image size].height + 10)];
[button setImage:image forState:UIControlStateNormal];
[button addTarget:self action:@selector(accessoryButtonTapped:withEvent:) forControlEvents:UIControlEventTouchUpInside];
[button setUserInteractionEnabled:YES];
return button;
}
- (void)performSearch:(NSString*)searchText
{
[_manual_search_result autorelease];
[_tsxconnect_search_result autorelease];
if([searchText length] > 0)
{
_manual_search_result = [FilterBookmarks(_manual_bookmarks, [searchText componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]) retain];
_tsxconnect_search_result = [FilterBookmarks(_tsxconnect_bookmarks, [searchText componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]) retain];
_history_search_result = [FilterHistory(_connection_history, searchText) retain];
}
else
{
_history_search_result = nil;
_tsxconnect_search_result = nil;
_manual_search_result = nil;
}
}
- (int)bookmarkIndexFromIndexPath:(NSIndexPath*)indexPath
{
return [indexPath row] - ((_history_search_result != nil) ? [_history_search_result count] : 0) - 1;
}
- (int)historyIndexFromIndexPath:(NSIndexPath*)indexPath
{
return [indexPath row] - 1;
}
- (BOOL)isIndexPathToHistoryItem:(NSIndexPath*)indexPath
{
return (([indexPath row] - 1) < [_history_search_result count]);
}
- (ComputerBookmark*)bookmarkForQuickConnectTo:(NSString*)host
{
ComputerBookmark* bookmark = [[[ComputerBookmark alloc] initWithBaseDefaultParameters] autorelease];
[bookmark setLabel:host];
[[bookmark params] setValue:host forKey:@"hostname"];
return bookmark;
}
#pragma mark - Persisting bookmarks
- (void)scheduleWriteBookmarksToDataStore
{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self writeBookmarksToDataStore];
}];
}
- (void)writeBookmarksToDataStore
{
[self writeManualBookmarksToDataStore];
}
- (void)scheduleWriteManualBookmarksToDataStore
{
[[NSOperationQueue mainQueue] addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(writeManualBookmarksToDataStore) object:nil] autorelease]];
}
- (void)writeManualBookmarksToDataStore
{
[self writeArray:_manual_bookmarks toDataStoreURL:[self manualBookmarksDataStoreURL]];
}
- (void)scheduleWriteConnectionHistoryToDataStore
{
[[NSOperationQueue mainQueue] addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(writeConnectionHistoryToDataStore) object:nil] autorelease]];
}
- (void)writeConnectionHistoryToDataStore
{
[self writeArray:_connection_history toDataStoreURL:[self connectionHistoryDataStoreURL]];
}
- (void)writeArray:(NSArray*)bookmarks toDataStoreURL:(NSURL*)url
{
NSData* archived_data = [NSKeyedArchiver archivedDataWithRootObject:bookmarks];
[archived_data writeToURL:url atomically:YES];
}
- (void)readManualBookmarksFromDataStore
{
[_manual_bookmarks autorelease];
_manual_bookmarks = [self arrayFromDataStoreURL:[self manualBookmarksDataStoreURL]];
if(_manual_bookmarks == nil)
_manual_bookmarks = [[NSMutableArray alloc] init];
}
- (void)readConnectionHistoryFromDataStore
{
[_connection_history autorelease];
_connection_history = [self arrayFromDataStoreURL:[self connectionHistoryDataStoreURL]];
if(_connection_history == nil)
_connection_history = [[NSMutableArray alloc] init];
}
- (NSMutableArray*)arrayFromDataStoreURL:(NSURL*)url
{
NSData* archived_data = [NSData dataWithContentsOfURL:url];
if (!archived_data)
return nil;
return [[NSKeyedUnarchiver unarchiveObjectWithData:archived_data] retain];
}
- (NSURL*)manualBookmarksDataStoreURL
{
return [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@", [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject], @"com.thinstuff.tsx-rdc-ios.bookmarks.plist"]];
}
- (NSURL*)connectionHistoryDataStoreURL
{
return [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@", [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject], @"com.thinstuff.tsx-rdc-ios.connection_history.plist"]];
}
@end

View File

@@ -0,0 +1,25 @@
/*
Controller to edit bookmark credentials
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "EditorBaseController.h"
@class ComputerBookmark;
@class ConnectionParams;
@interface CredentialsEditorController : EditorBaseController
{
@private
ComputerBookmark* _bookmark;
ConnectionParams* _params;
}
// init for the given bookmark
- (id)initWithBookmark:(ComputerBookmark*)bookmark;
@end

View File

@@ -0,0 +1,203 @@
/*
Controller to edit bookmark credentials
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "CredentialsEditorController.h"
#import "Bookmark.h"
#import "Utils.h"
@interface CredentialsEditorController ()
@end
#define SECTION_CREDENTIALS 0
#define SECTION_COUNT 1
@implementation CredentialsEditorController
- (id)initWithBookmark:(ComputerBookmark*)bookmark
{
if ((self = [super initWithStyle:UITableViewStyleGrouped]))
{
// set additional settings state according to bookmark data
_bookmark = [bookmark retain];
_params = [bookmark params];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self setTitle:NSLocalizedString(@"Credentials", @"Credentials title")];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// foce any active editing to stop
[[self view] endEditing:NO];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return SECTION_COUNT;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
switch (section)
{
case SECTION_CREDENTIALS: // credentials
return 3;
default:
break;
}
return 0;
}
// set section headers
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
switch(section)
{
case SECTION_CREDENTIALS:
return NSLocalizedString(@"Credentials", @"'Credentials': credentials settings header");
}
return @"unknown";
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// determine the required cell type
NSString* cellType = nil;
switch([indexPath section])
{
case SECTION_CREDENTIALS: // credentials
if([indexPath row] == 1)
cellType = TableCellIdentifierSecretText; // password field
else
cellType = TableCellIdentifierText;
break;
default:
break;
}
NSAssert(cellType != nil, @"Couldn't determine cell type");
// get the table view cell
UITableViewCell *cell = [self tableViewCellFromIdentifier:cellType];
NSAssert(cell, @"Invalid cell");
// set cell values
switch([indexPath section])
{
// credentials
case SECTION_CREDENTIALS:
[self initCredentialSettings:indexPath cell:cell];
break;
default:
break;
}
return cell;
}
// updates credentials in the UI
- (void)initCredentialSettings:(NSIndexPath*)indexPath cell:(UITableViewCell*)cell
{
switch(indexPath.row)
{
case 0:
{
EditTextTableViewCell* textCell = (EditTextTableViewCell*)cell;
[[textCell textfield] setTag:GET_TAG_FROM_PATH(indexPath)];
[[textCell label] setText:NSLocalizedString(@"Username", @"'Username': Bookmark username")];
[[textCell textfield] setText:[_params StringForKey:@"username"]];
[[textCell textfield] setPlaceholder:NSLocalizedString(@"not set", @"not set placeholder")];
break;
}
case 1:
{
EditSecretTextTableViewCell* textCell = (EditSecretTextTableViewCell*)cell;
[[textCell textfield] setTag:GET_TAG_FROM_PATH(indexPath)];
[[textCell label] setText:NSLocalizedString(@"Password", @"'Password': Bookmark password")];
[[textCell textfield] setText:[_params StringForKey:@"password"]];
[[textCell textfield] setPlaceholder:NSLocalizedString(@"not set", @"not set placeholder")];
break;
}
case 2:
{
EditTextTableViewCell* textCell = (EditTextTableViewCell*)cell;
[[textCell textfield] setTag:GET_TAG_FROM_PATH(indexPath)];
[[textCell label] setText:NSLocalizedString(@"Domain", @"'Domain': Bookmark domain")];
[[textCell textfield] setText:[_params StringForKey:@"domain"]];
[[textCell textfield] setPlaceholder:NSLocalizedString(@"not set", @"not set placeholder")];
break;
}
default:
NSLog(@"Invalid row index in settings table!");
break;
}
}
#pragma mark -
#pragma mark Text Field delegate
- (BOOL)textFieldShouldReturn:(UITextField*)textField
{
[textField resignFirstResponder];
return NO;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
switch(textField.tag)
{
// update credentials settings
case GET_TAG(SECTION_CREDENTIALS, 0):
[_params setValue:[textField text] forKey:@"username"];
break;
case GET_TAG(SECTION_CREDENTIALS, 1):
[_params setValue:[textField text] forKey:@"password"];
break;
case GET_TAG(SECTION_CREDENTIALS, 2):
[_params setValue:[textField text] forKey:@"domain"];
break;
default:
break;
}
return YES;
}
@end

View File

@@ -0,0 +1,31 @@
/*
Credentials input controller
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import <UIKit/UIKit.h>
@class RDPSession;
@interface CredentialsInputController : UIViewController
{
@private
IBOutlet UITextField* _textfield_username;
IBOutlet UITextField* _textfield_password;
IBOutlet UITextField* _textfield_domain;
IBOutlet UIButton* _btn_login;
IBOutlet UIButton* _btn_cancel;
IBOutlet UIScrollView* _scroll_view;
IBOutlet UILabel* _lbl_message;
RDPSession* _session;
NSMutableDictionary* _params;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil session:(RDPSession*)session params:(NSMutableDictionary*)params;
@end

View File

@@ -0,0 +1,133 @@
/*
Credentials input controller
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "CredentialsInputController.h"
#import "RDPSession.h"
#import "Utils.h"
@implementation CredentialsInputController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil session:(RDPSession *)session params:(NSMutableDictionary *)params
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
_session = session;
_params = params;
[self setModalPresentationStyle:UIModalPresentationFormSheet];
// on iphone we have the problem that the buttons are hidden by the keyboard
// we solve this issue by registering keyboard notification handlers and adjusting the scrollview accordingly
if (IsPhone())
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name: UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name: UIKeyboardWillHideNotification object:nil];
}
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// set localized strings
[_lbl_message setText:NSLocalizedString(@"Please provide the missing user information in order to proceed and login.", @"Credentials input view message")];
[_textfield_username setPlaceholder:NSLocalizedString(@"Username", @"Credentials Input Username hint")];
[_textfield_password setPlaceholder:NSLocalizedString(@"Password", @"Credentials Input Password hint")];
[_textfield_domain setPlaceholder:NSLocalizedString(@"Domain", @"Credentials Input Domain hint")];
[_btn_login setTitle:NSLocalizedString(@"Login", @"Login Button") forState:UIControlStateNormal];
[_btn_cancel setTitle:NSLocalizedString(@"Cancel", @"Cancel Button") forState:UIControlStateNormal];
// init scrollview content size
[_scroll_view setContentSize:[_scroll_view frame].size];
// set params in the view
[_textfield_username setText:[_params valueForKey:@"username"]];
[_textfield_password setText:[_params valueForKey:@"password"]];
[_textfield_domain setText:[_params valueForKey:@"domain"]];
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
// set signal
[[_session uiRequestCompleted] signal];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
- (void)dealloc
{
[super dealloc];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark -
#pragma mark iOS Keyboard Notification Handlers
- (void)keyboardWillShow:(NSNotification *)notification
{
CGRect keyboardEndFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect keyboardFrame = [[self view] convertRect:keyboardEndFrame toView:nil];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:[[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
[UIView setAnimationDuration:[[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
CGRect frame = [_scroll_view frame];
frame.size.height -= keyboardFrame.size.height;
[_scroll_view setFrame:frame];
[UIView commitAnimations];
}
- (void)keyboardWillHide:(NSNotification *)notification
{
CGRect keyboardEndFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect keyboardFrame = [[self view] convertRect:keyboardEndFrame toView:nil];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:[[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
[UIView setAnimationDuration:[[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
CGRect frame = [_scroll_view frame];
frame.size.height += keyboardFrame.size.height;
[_scroll_view setFrame:frame];
[UIView commitAnimations];
}
#pragma mark - Action handlers
- (IBAction)loginPressed:(id)sender
{
// read input back in
[_params setValue:[_textfield_username text] forKey:@"username"];
[_params setValue:[_textfield_password text] forKey:@"password"];
[_params setValue:[_textfield_domain text] forKey:@"domain"];
[_params setValue:[NSNumber numberWithBool:YES] forKey:@"result"];
// dismiss controller
[self dismissModalViewControllerAnimated:YES];
}
- (IBAction)cancelPressed:(id)sender
{
[_params setValue:[NSNumber numberWithBool:NO] forKey:@"result"];
// dismiss controller
[self dismissModalViewControllerAnimated:YES];
}
@end

View File

@@ -0,0 +1,44 @@
/*
Basic interface for settings editors
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import <UIKit/UIKit.h>
#import "EditTextTableViewCell.h"
#import "EditFlagTableViewCell.h"
#import "EditSelectionTableViewCell.h"
#import "EditSubEditTableViewCell.h"
#import "EditSecretTextTableViewCell.h"
#import "EditButtonTableViewCell.h"
extern NSString* TableCellIdentifierText;
extern NSString* TableCellIdentifierSecretText;
extern NSString* TableCellIdentifierYesNo;
extern NSString* TableCellIdentifierSelection;
extern NSString* TableCellIdentifierSubEditor;
extern NSString* TableCellIdentifierMultiChoice;
extern NSString* TableCellIdentifierButton;
@interface EditorBaseController : UITableViewController <UITextFieldDelegate>
{
@private
IBOutlet EditTextTableViewCell* _textTableViewCell;
IBOutlet EditSecretTextTableViewCell* _secretTextTableViewCell;
IBOutlet EditFlagTableViewCell* _flagTableViewCell;
IBOutlet EditSelectionTableViewCell* _selectionTableViewCell;
IBOutlet EditSubEditTableViewCell* _subEditTableViewCell;
IBOutlet EditButtonTableViewCell* _buttonTableViewCell;
}
// returns one of the requested table view cells
- (UITableViewCell*)tableViewCellFromIdentifier:(NSString*)identifier;
// Adjust text input cells label/textfield widht according to the label's text size
- (void)adjustEditTextTableViewCell:(EditTextTableViewCell*)cell;
@end

View File

@@ -0,0 +1,108 @@
/*
Basic interface for settings editors
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "EditorBaseController.h"
@interface EditorBaseController ()
@end
NSString* TableCellIdentifierText = @"cellIdText";
NSString* TableCellIdentifierSecretText = @"cellIdSecretText";
NSString* TableCellIdentifierYesNo = @"cellIdYesNo";
NSString* TableCellIdentifierSelection = @"cellIdSelection";
NSString* TableCellIdentifierSubEditor = @"cellIdSubEditor";
NSString* TableCellIdentifierMultiChoice = @"cellIdMultiChoice";
NSString* TableCellIdentifierButton = @"cellIdButton";
@implementation EditorBaseController
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#pragma mark - Create table view cells
- (UITableViewCell*)tableViewCellFromIdentifier:(NSString*)identifier
{
// try to reuse a cell
UITableViewCell* cell = [[self tableView] dequeueReusableCellWithIdentifier:identifier];
if (cell != nil)
return cell;
// we have to create a new cell
if ([identifier isEqualToString:TableCellIdentifierText])
{
[[NSBundle mainBundle] loadNibNamed:@"EditTextTableViewCell" owner:self options:nil];
cell = _textTableViewCell;
_textTableViewCell = nil;
}
else if ([identifier isEqualToString:TableCellIdentifierSecretText])
{
[[NSBundle mainBundle] loadNibNamed:@"EditSecretTextTableViewCell" owner:self options:nil];
cell = _secretTextTableViewCell;
_secretTextTableViewCell = nil;
}
else if ([identifier isEqualToString:TableCellIdentifierYesNo])
{
[[NSBundle mainBundle] loadNibNamed:@"EditFlagTableViewCell" owner:self options:nil];
cell = _flagTableViewCell;
_flagTableViewCell = nil;
}
else if ([identifier isEqualToString:TableCellIdentifierSelection])
{
[[NSBundle mainBundle] loadNibNamed:@"EditSelectionTableViewCell" owner:self options:nil];
cell = _selectionTableViewCell;
_selectionTableViewCell = nil;
}
else if ([identifier isEqualToString:TableCellIdentifierSubEditor])
{
[[NSBundle mainBundle] loadNibNamed:@"EditSubEditTableViewCell" owner:self options:nil];
cell = _subEditTableViewCell;
_subEditTableViewCell = nil;
}
else if ([identifier isEqualToString:TableCellIdentifierButton])
{
[[NSBundle mainBundle] loadNibNamed:@"EditButtonTableViewCell" owner:self options:nil];
cell = _buttonTableViewCell;
_buttonTableViewCell = nil;
}
else if ([identifier isEqualToString:TableCellIdentifierMultiChoice])
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier] autorelease];
}
else
{
NSAssert(false, @"Unknown table cell identifier");
}
return cell;
}
#pragma mark - Utility functions
- (void)adjustEditTextTableViewCell:(EditTextTableViewCell*)cell
{
UILabel* label = [cell label];
UITextField* textField = [cell textfield];
// adjust label
CGFloat width = [[label text] sizeWithFont:[label font]].width;
CGRect frame = [label frame];
CGFloat delta = width - frame.size.width;
frame.size.width = width;
[label setFrame:frame];
// adjust text field
frame = [textField frame];
frame.origin.x += delta;
frame.size.width -= delta;
[textField setFrame:frame];
}
@end

View File

@@ -0,0 +1,30 @@
/*
Generic controller to select a single item from a list of options
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "EditorBaseController.h"
@class ConnectionParams;
@interface EditorSelectionController : EditorBaseController
{
ConnectionParams* _params;
// array with entries in connection parameters that are altered
NSArray* _entries;
// array with dictionaries containing label/value pairs that represent the available values for each entry
NSArray* _selections;
// current selections
NSMutableArray* _cur_selections;
}
- (id)initWithConnectionParams:(ConnectionParams*)params entries:(NSArray*)entries selections:(NSArray*)selections;
@end

View File

@@ -0,0 +1,136 @@
/*
Generic controller to select a single item from a list of options
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "EditorSelectionController.h"
#import "ConnectionParams.h"
#import "OrderedDictionary.h"
@interface EditorSelectionController (Private)
- (OrderedDictionary*)selectionForIndex:(int)index;
@end
@implementation EditorSelectionController
- (id)initWithConnectionParams:(ConnectionParams*)params entries:(NSArray *)entries selections:(NSArray *)selections
{
self = [super initWithStyle:UITableViewStyleGrouped];
if (self)
{
_params = [params retain];
_entries = [entries retain];
_selections = [selections retain];
// allocate and init current selections array
_cur_selections = [[NSMutableArray alloc] initWithCapacity:[_entries count]];
for (int i = 0; i < [entries count]; ++i)
{
NSString* entry = [entries objectAtIndex:i];
if([_params hasValueForKeyPath:entry])
{
NSUInteger idx = [(OrderedDictionary*)[selections objectAtIndex:i] indexForValue:[NSNumber numberWithInt:[_params intForKeyPath:entry]]];
[_cur_selections addObject:[NSNumber numberWithInt:(idx != NSNotFound ? idx : 0)]];
}
else
[_cur_selections addObject:[NSNumber numberWithInt:0]];
}
}
return self;
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
[_params autorelease];
[_entries autorelease];
[_selections autorelease];
[_cur_selections autorelease];
}
#pragma mark - View lifecycle
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [_entries count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [[self selectionForIndex:section] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self tableViewCellFromIdentifier:TableCellIdentifierMultiChoice];
// get selection
OrderedDictionary* selection = [self selectionForIndex:[indexPath section]];
// set cell properties
[[cell textLabel] setText:[selection keyAtIndex:[indexPath row]]];
// set default checkmark
if([indexPath row] == [[_cur_selections objectAtIndex:[indexPath section]] intValue])
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
else
[cell setAccessoryType:UITableViewCellAccessoryNone];
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// has selection change?
int cur_selection = [[_cur_selections objectAtIndex:[indexPath section]] intValue];
if([indexPath row] != cur_selection)
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
NSIndexPath* oldIndexPath = [NSIndexPath indexPathForRow:cur_selection inSection:[indexPath section]];
// clear old checkmark
UITableViewCell* old_sel_cell = [tableView cellForRowAtIndexPath:oldIndexPath];
old_sel_cell.accessoryType = UITableViewCellAccessoryNone;
// set new checkmark
UITableViewCell* new_sel_cell = [tableView cellForRowAtIndexPath:indexPath];
new_sel_cell.accessoryType = UITableViewCellAccessoryCheckmark;
// get value from selection dictionary
OrderedDictionary* dict = [self selectionForIndex:[indexPath section]];
int sel_value = [[dict valueForKey:[dict keyAtIndex:[indexPath row]]] intValue];
// update selection index and params value
[_cur_selections replaceObjectAtIndex:[indexPath section] withObject:[NSNumber numberWithInt:[indexPath row]]];
[_params setInt:sel_value forKeyPath:[_entries objectAtIndex:[indexPath section]]];
}
}
#pragma mark - Convenience functions
- (OrderedDictionary*)selectionForIndex:(int)index
{
return (OrderedDictionary*)[_selections objectAtIndex:index];
}
@end

View File

@@ -0,0 +1,24 @@
/*
Password Encryption Controller
Copyright 2013 Thinstuff Technologies GmbH, Author: Dorian Johnson
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import <Foundation/Foundation.h>
#import "Encryptor.h"
@interface EncryptionController : NSObject
{
Encryptor* _shared_encryptor;
}
+ (EncryptionController*)sharedEncryptionController;
// Return a Encryptor suitable for encrypting or decrypting with the master password
- (Encryptor*)decryptor;
- (Encryptor*)encryptor;
@end

View File

@@ -0,0 +1,133 @@
/*
Password Encryption Controller
Copyright 2013 Thinstuff Technologies GmbH, Author: Dorian Johnson
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "EncryptionController.h"
#import "SFHFKeychainUtils.h"
@interface EncryptionController (Private)
- (BOOL)verifyPassword:(Encryptor*)decryptor;
- (NSData*)encryptedVerificationData;
- (void)setEncryptedVerificationData:(Encryptor*)encryptor;
- (NSString*)keychainServerName;
- (NSString*)keychainUsername;
- (void)setKeychainPassword:(NSString*)password;
- (NSString*)keychainPassword;
- (NSString*)keychainDefaultPassword;
@end
static EncryptionController* _shared_encryption_controller = nil;
#pragma mark -
@implementation EncryptionController
+ (EncryptionController*)sharedEncryptionController
{
@synchronized(self)
{
if (_shared_encryption_controller == nil)
_shared_encryption_controller = [[EncryptionController alloc] init];
}
return _shared_encryption_controller;
}
#pragma mark Getting an encryptor or decryptor
- (Encryptor*)encryptor
{
if (_shared_encryptor)
return _shared_encryptor;
NSString* saved_password = [self keychainPassword];
if (saved_password == nil)
{
saved_password = [self keychainDefaultPassword];
Encryptor* encryptor = [[[Encryptor alloc] initWithPassword:saved_password] autorelease];
[self setEncryptedVerificationData:encryptor];
_shared_encryptor = [encryptor retain];
}
else
{
Encryptor* encryptor = [[[Encryptor alloc] initWithPassword:saved_password] autorelease];
if ([self verifyPassword:encryptor])
_shared_encryptor = [encryptor retain];
}
return _shared_encryptor;
}
// For the current implementation, decryptors and encryptors are equivilant.
- (Encryptor*)decryptor { return [self encryptor]; }
@end
#pragma mark -
@implementation EncryptionController (Private)
#pragma mark -
#pragma mark Keychain password storage
- (NSString*)keychainServerName
{
return [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
}
- (NSString*)keychainUsername
{
return @"master.password";
}
- (void)setKeychainPassword:(NSString*)password
{
NSError* error;
if (password == nil)
{
[SFHFKeychainUtils deleteItemForUsername:[self keychainUsername] andServerName:[self keychainServerName] error:&error];
return;
}
[SFHFKeychainUtils storeUsername:[self keychainUsername] andPassword:password forServerName:[self keychainServerName] updateExisting:YES error:&error];
}
- (NSString*)keychainPassword
{
NSError* error;
return [SFHFKeychainUtils getPasswordForUsername:[self keychainUsername] andServerName:[self keychainServerName] error:&error];
}
- (NSString*)keychainDefaultPassword
{
return [[UIDevice currentDevice] uniqueIdentifier];
}
#pragma mark -
#pragma mark Verification of encryption key against verification data
- (BOOL)verifyPassword:(Encryptor*)decryptor
{
return [[decryptor plaintextPassword] isEqualToString:[decryptor decryptString:[self encryptedVerificationData]]];
}
- (NSData*)encryptedVerificationData
{
return [[NSUserDefaults standardUserDefaults] dataForKey:@"TSXMasterPasswordVerification"];
}
- (void)setEncryptedVerificationData:(Encryptor*)encryptor
{
[[NSUserDefaults standardUserDefaults] setObject:[encryptor encryptString:[encryptor plaintextPassword]] forKey:@"TSXMasterPasswordVerification"];
}
@end

View File

@@ -0,0 +1,16 @@
/*
Application help controller
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import <UIKit/UIKit.h>
@interface HelpController : UIViewController <UIWebViewDelegate>
{
UIWebView* webView;
}
@end

View File

@@ -0,0 +1,81 @@
/*
Application help controller
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "HelpController.h"
#import "Utils.h"
@implementation HelpController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// set title and tab-bar image
[self setTitle:NSLocalizedString(@"Help", @"Help Controller title")];
UIImage* tabBarIcon = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"tabbar_icon_help" ofType:@"png"]];
[self setTabBarItem:[[UITabBarItem alloc] initWithTitle:NSLocalizedString(@"Help", @"Tabbar item help") image:tabBarIcon tag:0]];
}
return self;
}
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView
{
webView = [[[UIWebView alloc] initWithFrame:CGRectZero] autorelease];
[webView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
[webView setAutoresizesSubviews:YES];
[webView setDelegate:self];
[webView setDataDetectorTypes:UIDataDetectorTypeNone];
[self setView:webView];
}
- (void)dealloc {
[super dealloc];
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *filename = (IsPhone() ? @"gestures_phone" : @"gestures");
NSString *htmlString = [[[NSString alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:filename ofType:@"html" inDirectory:@"help_page"] encoding:NSUTF8StringEncoding error:nil] autorelease];
[webView loadHTMLString:htmlString baseURL:[NSURL fileURLWithPath:[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"help_page"]]];
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
#pragma mark -
#pragma mark UIWebView callbacks
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
if([[request URL] isFileURL])
return YES;
if(navigationType == UIWebViewNavigationTypeLinkClicked)
{
NSString* lastClickedLink = [[request URL] absoluteString];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"External Link"
message:[NSString stringWithFormat:@"Open [%@] in Browser?", lastClickedLink]
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:@"No", nil];
[alert show];
[alert release];
return NO;
}
return YES;
}
@end

View File

@@ -0,0 +1,17 @@
/*
main tabbar controller
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import <Foundation/Foundation.h>
@interface MainTabBarController : UITabBarController {
}
@end

View File

@@ -0,0 +1,20 @@
/*
main tabbar controller
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "MainTabBarController.h"
@implementation MainTabBarController
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return YES;
}
@end

View File

@@ -0,0 +1,24 @@
/*
controller for performance settings selection
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "EditorBaseController.h"
@class ConnectionParams;
@interface PerformanceEditorController : EditorBaseController
{
@private
ConnectionParams* _params;
NSString* _keyPath;
}
- (id)initWithConnectionParams:(ConnectionParams*)params;
- (id)initWithConnectionParams:(ConnectionParams*)params keyPath:(NSString*)keyPath;
@end

View File

@@ -0,0 +1,187 @@
/*
controller for performance settings selection
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "PerformanceEditorController.h"
#import "ConnectionParams.h"
#import "Utils.h"
@interface PerformanceEditorController (Private)
-(NSString*)keyPathForKey:(NSString*)key;
@end
@implementation PerformanceEditorController
- (id)initWithConnectionParams:(ConnectionParams*)params
{
return [self initWithConnectionParams:params keyPath:nil];
}
- (id)initWithConnectionParams:(ConnectionParams*)params keyPath:(NSString*)keyPath;
{
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
_params = [params retain];
_keyPath = (keyPath != nil ? [keyPath retain] : nil);
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
-(NSString*)keyPathForKey:(NSString*)key
{
if (_keyPath)
return [_keyPath stringByAppendingFormat:@".%@", key];
return key;
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 7;
}
// set section headers
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return NSLocalizedString(@"Performance Settings", @"'Performance Settings': performance settings header");
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// get the table view cell
EditFlagTableViewCell *cell = (EditFlagTableViewCell*)[self tableViewCellFromIdentifier:TableCellIdentifierYesNo];
NSAssert(cell, @"Invalid cell");
switch ([indexPath row])
{
case 0:
{
[[cell label] setText:NSLocalizedString(@"RemoteFX", @"RemoteFX performance setting")];
[[cell toggle] setOn:[_params boolForKeyPath:[self keyPathForKey:@"perf_remotefx"]]];
break;
}
case 1:
{
[[cell label] setText:NSLocalizedString(@"Desktop Background", @"Desktop background performance setting")];
[[cell toggle] setOn:[_params boolForKeyPath:[self keyPathForKey:@"perf_show_desktop"]]];
break;
}
case 2:
{
[[cell label] setText:NSLocalizedString(@"Font Smoothing", @"Font smoothing performance setting")];
[[cell toggle] setOn:[_params boolForKeyPath:[self keyPathForKey:@"perf_font_smoothing"]]];
break;
}
case 3:
{
[[cell label] setText:NSLocalizedString(@"Desktop Composition", @"Desktop composition performance setting")];
[[cell toggle] setOn:[_params boolForKeyPath:[self keyPathForKey:@"perf_desktop_composition"]]];
break;
}
case 4:
{
[[cell label] setText:NSLocalizedString(@"Window contents while dragging", @"Window Dragging performance setting")];
[[cell toggle] setOn:[_params boolForKeyPath:[self keyPathForKey:@"perf_window_dragging"]]];
break;
}
case 5:
{
[[cell label] setText:NSLocalizedString(@"Menu Animation", @"Menu Animations performance setting")];
[[cell toggle] setOn:[_params boolForKeyPath:[self keyPathForKey:@"perf_menu_animation"]]];
break;
}
case 6:
{
[[cell label] setText:NSLocalizedString(@"Visual Styles", @"Use Themes performance setting")];
[[cell toggle] setOn:[_params boolForKeyPath:[self keyPathForKey:@"perf_windows_themes"]]];
break;
}
default:
break;
}
[[cell toggle] setTag:GET_TAG_FROM_PATH(indexPath)];
[[cell toggle] addTarget:self action:@selector(togglePerformanceSetting:) forControlEvents:UIControlEventValueChanged];
return cell;
}
#pragma mark -
#pragma mark Action Handlers
- (void)togglePerformanceSetting:(id)sender
{
UISwitch* valueSwitch = (UISwitch*)sender;
switch(valueSwitch.tag)
{
case GET_TAG(0, 0):
[_params setBool:[valueSwitch isOn] forKeyPath:[self keyPathForKey:@"perf_remotefx"]];
break;
case GET_TAG(0, 1):
[_params setBool:[valueSwitch isOn] forKeyPath:[self keyPathForKey:@"perf_show_desktop"]];
break;
case GET_TAG(0, 2):
[_params setBool:[valueSwitch isOn] forKeyPath:[self keyPathForKey:@"perf_font_smoothing"]];
break;
case GET_TAG(0, 3):
[_params setBool:[valueSwitch isOn] forKeyPath:[self keyPathForKey:@"perf_desktop_composition"]];
break;
case GET_TAG(0, 4):
[_params setBool:[valueSwitch isOn] forKeyPath:[self keyPathForKey:@"perf_window_dragging"]];
break;
case GET_TAG(0, 5):
[_params setBool:[valueSwitch isOn] forKeyPath:[self keyPathForKey:@"perf_menu_animation"]];
break;
case GET_TAG(0, 6):
[_params setBool:[valueSwitch isOn] forKeyPath:[self keyPathForKey:@"perf_windows_themes"]];
break;
default:
break;
}
}
@end

View File

@@ -0,0 +1,69 @@
/*
RDP Session View Controller
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import <UIKit/UIKit.h>
#import "RDPSession.h"
#import "RDPKeyboard.h"
#import "RDPSessionView.h"
#import "TouchPointerView.h"
#import "AdvancedKeyboardView.h"
@interface RDPSessionViewController : UIViewController <RDPSessionDelegate, TouchPointerDelegate, AdvancedKeyboardDelegate, RDPKeyboardDelegate, UIScrollViewDelegate, UITextFieldDelegate, UIAlertViewDelegate>
{
// scrollview that hosts the rdp session view
IBOutlet UIScrollView* _session_scrollview;
// rdp session view
IBOutlet RDPSessionView* _session_view;
// touch pointer view
IBOutlet TouchPointerView* _touchpointer_view;
BOOL _autoscroll_with_touchpointer;
BOOL _is_autoscrolling;
// rdp session toolbar
IBOutlet UIToolbar* _session_toolbar;
BOOL _session_toolbar_visible;
// dummy text field used to display the keyboard
IBOutlet UITextField* _dummy_textfield;
// connecting view and the controls within that view
IBOutlet UIView* _connecting_view;
IBOutlet UILabel* _lbl_connecting;
IBOutlet UIActivityIndicatorView* _connecting_indicator_view;
IBOutlet UIButton* _cancel_connect_button;
// extended keyboard toolbar
UIToolbar* _keyboard_toolbar;
// rdp session
RDPSession* _session;
BOOL _session_initilized;
// flag that indicates whether the keyboard is visible or not
BOOL _keyboard_visible;
// flag to switch between left/right mouse button mode
BOOL _toggle_mouse_button;
// keyboard extension view
AdvancedKeyboardView* _advanced_keyboard_view;
BOOL _advanced_keyboard_visible;
BOOL _requesting_advanced_keyboard;
// delayed mouse move event sending
NSTimer* _mouse_move_event_timer;
int _mouse_move_events_skipped;
CGPoint _prev_long_press_position;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil session:(RDPSession*)session;
@end

View File

@@ -0,0 +1,980 @@
/*
RDP Session View Controller
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import <QuartzCore/QuartzCore.h>
#import "RDPSessionViewController.h"
#import "RDPKeyboard.h"
#import "Utils.h"
#import "Toast+UIView.h"
#import "ConnectionParams.h"
#import "CredentialsInputController.h"
#import "VerifyCertificateController.h"
#define TOOLBAR_HEIGHT 30
#define AUTOSCROLLDISTANCE 20
#define AUTOSCROLLTIMEOUT 0.05
@interface RDPSessionViewController (Private)
-(void)showSessionToolbar:(BOOL)show;
-(UIToolbar*)keyboardToolbar;
-(void)initGestureRecognizers;
- (void)suspendSession;
- (NSDictionary*)eventDescriptorForMouseEvent:(int)event position:(CGPoint)position;
- (void)handleMouseMoveForPosition:(CGPoint)position;
@end
@implementation RDPSessionViewController
#pragma mark class methods
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil session:(RDPSession *)session
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
_session = [session retain];
[_session setDelegate:self];
_session_initilized = NO;
_mouse_move_events_skipped = 0;
_mouse_move_event_timer = nil;
_advanced_keyboard_view = nil;
_advanced_keyboard_visible = NO;
_requesting_advanced_keyboard = NO;
_session_toolbar_visible = NO;
_toggle_mouse_button = NO;
_autoscroll_with_touchpointer = [[NSUserDefaults standardUserDefaults] boolForKey:@"ui.auto_scroll_touchpointer"];
_is_autoscrolling = NO;
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(animationStopped:finished:context:)];
}
return self;
}
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView
{
// load default view and set background color and resizing mask
[super loadView];
// init keyboard handling vars and register required notification handlers
_keyboard_visible = NO;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name: UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name: UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name: UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:) name: UIKeyboardDidHideNotification object:nil];
// init keyboard toolbar
_keyboard_toolbar = [[self keyboardToolbar] retain];
[_dummy_textfield setInputAccessoryView:_keyboard_toolbar];
// init gesture recognizers
[self initGestureRecognizers];
// hide session toolbar
[_session_toolbar setFrame:CGRectMake(0.0, -TOOLBAR_HEIGHT, [[self view] bounds].size.width, TOOLBAR_HEIGHT)];
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
if (![_touchpointer_view isHidden])
[_touchpointer_view ensurePointerIsVisible];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc. that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// hide navigation bar and (if enabled) the status bar
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"ui.hide_status_bar"])
{
if(animated == YES)
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
else
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
}
[[self navigationController] setNavigationBarHidden:YES animated:animated];
// if sesssion is suspended - notify that we got a new bitmap context
if ([_session isSuspended])
[self sessionBitmapContextWillChange:_session];
// init keyboard
[[RDPKeyboard getSharedRDPKeyboard] initWithSession:_session delegate:self];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (!_session_initilized)
{
if ([_session isSuspended])
{
[_session resume];
[self sessionBitmapContextDidChange:_session];
[_session_view setNeedsDisplay];
}
else
[_session connect];
_session_initilized = YES;
}
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// show navigation and status bar again
if(animated == YES)
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
else
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
[[self navigationController] setNavigationBarHidden:NO animated:animated];
// reset all modifier keys on rdp keyboard
[[RDPKeyboard getSharedRDPKeyboard] reset];
// hide toolbar and keyboard
[self showSessionToolbar:NO];
[_dummy_textfield resignFirstResponder];
}
- (void)dealloc {
// remove any observers
[[NSNotificationCenter defaultCenter] removeObserver:self];
// the session lives on longer so set the delegate to nil
[_session setDelegate:nil];
[_advanced_keyboard_view release];
[_keyboard_toolbar release];
[_session release];
[super dealloc];
}
#pragma mark -
#pragma mark ScrollView delegate methods
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return _session_view;
}
-(void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{
NSLog(@"New zoom scale: %f", scale);
[_session_view setNeedsDisplay];
}
#pragma mark -
#pragma mark TextField delegate methods
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
_keyboard_visible = YES;
_advanced_keyboard_visible = NO;
return YES;
}
-(BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
_keyboard_visible = NO;
_advanced_keyboard_visible = NO;
return YES;
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if([string length] > 0)
{
for(int i = 0 ; i < [string length] ; i++)
{
NSString *characterTyped = [string substringWithRange:NSMakeRange(i, 1)];
unichar curChar = [characterTyped characterAtIndex:0];
// special handling for return/enter key
if(curChar == '\n')
[[RDPKeyboard getSharedRDPKeyboard] sendEnterKeyStroke];
else
[[RDPKeyboard getSharedRDPKeyboard] sendUnicode:curChar];
}
}
else
{
[[RDPKeyboard getSharedRDPKeyboard] sendBackspaceKeyStroke];
}
return NO;
}
#pragma mark -
#pragma mark AdvancedKeyboardDelegate functions
-(void)advancedKeyPressedVKey:(int)key
{
[[RDPKeyboard getSharedRDPKeyboard] sendVirtualKeyCode:key];
}
-(void)advancedKeyPressedUnicode:(int)key
{
[[RDPKeyboard getSharedRDPKeyboard] sendUnicode:key];
}
#pragma mark - RDP keyboard handler
- (void)modifiersChangedForKeyboard:(RDPKeyboard *)keyboard
{
UIBarButtonItem* curItem;
// shift button (only on iPad)
int objectIdx = 0;
if (IsPad())
{
objectIdx = 2;
curItem = (UIBarButtonItem*)[[_keyboard_toolbar items] objectAtIndex:objectIdx];
[curItem setStyle:[keyboard shiftPressed] ? UIBarButtonItemStyleDone : UIBarButtonItemStyleBordered];
}
// ctrl button
objectIdx += 2;
curItem = (UIBarButtonItem*)[[_keyboard_toolbar items] objectAtIndex:objectIdx];
[curItem setStyle:[keyboard ctrlPressed] ? UIBarButtonItemStyleDone : UIBarButtonItemStyleBordered];
// win button
objectIdx += 2;
curItem = (UIBarButtonItem*)[[_keyboard_toolbar items] objectAtIndex:objectIdx];
[curItem setStyle:[keyboard winPressed] ? UIBarButtonItemStyleDone : UIBarButtonItemStyleBordered];
// alt button
objectIdx += 2;
curItem = (UIBarButtonItem*)[[_keyboard_toolbar items] objectAtIndex:objectIdx];
[curItem setStyle:[keyboard altPressed] ? UIBarButtonItemStyleDone : UIBarButtonItemStyleBordered];
}
#pragma mark -
#pragma mark RDPSessionDelegate functions
- (void)session:(RDPSession*)session didFailToConnect:(int)reason
{
// remove and release connecting view
[_connecting_indicator_view stopAnimating];
[_connecting_view removeFromSuperview];
[_connecting_view autorelease];
// return to bookmark list
[[self navigationController] popViewControllerAnimated:YES];
}
- (void)sessionWillConnect:(RDPSession*)session
{
// load connecting view
[[NSBundle mainBundle] loadNibNamed:@"RDPConnectingView" owner:self options:nil];
// set strings
[_lbl_connecting setText:NSLocalizedString(@"Connecting", @"Connecting progress view - label")];
[_cancel_connect_button setTitle:NSLocalizedString(@"Cancel", @"Cancel Button") forState:UIControlStateNormal];
// center view and give it round corners
[_connecting_view setCenter:[[self view] center]];
[[_connecting_view layer] setCornerRadius:10];
// display connecting view and start indicator
[[self view] addSubview:_connecting_view];
[_connecting_indicator_view startAnimating];
}
- (void)sessionDidConnect:(RDPSession*)session
{
// remove and release connecting view
[_connecting_indicator_view stopAnimating];
[_connecting_view removeFromSuperview];
[_connecting_view autorelease];
// check if session settings changed ...
// The 2nd width check is to ignore changes in resolution settings due to the RDVH display bug (refer to RDPSEssion.m for more details)
ConnectionParams* orig_params = [session params];
rdpSettings* sess_params = [session getSessionParams];
if (([orig_params intForKey:@"width"] != sess_params->DesktopWidth && [orig_params intForKey:@"width"] != (sess_params->DesktopWidth + 1)) ||
[orig_params intForKey:@"height"] != sess_params->DesktopHeight || [orig_params intForKey:@"colors"] != sess_params->ColorDepth)
{
// display notification that the session params have been changed by the server
NSString* message = [NSString stringWithFormat:NSLocalizedString(@"The server changed the screen settings to %dx%dx%d", @"Screen settings not supported message with width, height and colors parameter"), sess_params->DesktopWidth, sess_params->DesktopHeight, sess_params->ColorDepth];
[[self view] makeToast:message duration:ToastDurationNormal position:@"bottom"];
}
}
- (void)sessionWillDisconnect:(RDPSession*)session
{
}
- (void)sessionDidDisconnect:(RDPSession*)session
{
// return to bookmark list
[[self navigationController] popViewControllerAnimated:YES];
}
- (void)sessionBitmapContextWillChange:(RDPSession*)session
{
// calc new view frame
rdpSettings* sess_params = [session getSessionParams];
CGRect view_rect = CGRectMake(0, 0, sess_params->DesktopWidth, sess_params->DesktopHeight);
// reset zoom level and update content size
[_session_scrollview setZoomScale:1.0];
[_session_scrollview setContentSize:view_rect.size];
// set session view size
[_session_view setFrame:view_rect];
// show/hide toolbar
[_session setToolbarVisible:![[NSUserDefaults standardUserDefaults] boolForKey:@"ui.hide_tool_bar"]];
[self showSessionToolbar:[_session toolbarVisible]];
}
- (void)sessionBitmapContextDidChange:(RDPSession*)session
{
// associate view with session
[_session_view setSession:session];
}
- (void)session:(RDPSession*)session needsRedrawInRect:(CGRect)rect
{
[_session_view setNeedsDisplayInRect:rect];
}
- (void)session:(RDPSession *)session requestsAuthenticationWithParams:(NSMutableDictionary *)params
{
CredentialsInputController* view_controller = [[[CredentialsInputController alloc] initWithNibName:@"CredentialsInputView" bundle:nil session:_session params:params] autorelease];
[self presentModalViewController:view_controller animated:YES];
}
- (void)session:(RDPSession *)session verifyCertificateWithParams:(NSMutableDictionary *)params
{
VerifyCertificateController* view_controller = [[[VerifyCertificateController alloc] initWithNibName:@"VerifyCertificateView" bundle:nil session:_session params:params] autorelease];
[self presentModalViewController:view_controller animated:YES];
}
- (CGSize)sizeForFitScreenForSession:(RDPSession*)session
{
if (IsPad())
return [self view].bounds.size;
else
{
// on phones make a resolution that has a 16:10 ratio with the phone's height
CGSize size = [self view].bounds.size;
CGFloat maxSize = (size.width > size.height) ? size.width : size.height;
return CGSizeMake(maxSize * 1.6f, maxSize);
}
}
- (void)showGoProScreen:(RDPSession*)session
{
UIAlertView* alertView = [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Pro Version", @"Pro version dialog title")
message:NSLocalizedString(@"Do you want to buy Thinstuff RDC Pro and enable the full RDP Experience", @"Pro version dialog message") delegate:self cancelButtonTitle:NSLocalizedString(@"No", @"No Button title") otherButtonTitles:NSLocalizedString(@"Yes", @"Yes button title"), nil] autorelease];
[alertView show];
}
#pragma mark - Keyboard Toolbar Handlers
-(void)showAdvancedKeyboardAnimated
{
// calc initial and final rect of the advanced keyboard view
CGRect rect = [[_keyboard_toolbar superview] bounds];
rect.origin.y = [_keyboard_toolbar bounds].size.height;
rect.size.height -= rect.origin.y;
// create new view (hidden) and add to host-view of keyboard toolbar
_advanced_keyboard_view = [[AdvancedKeyboardView alloc] initWithFrame:CGRectMake(rect.origin.x,
[[_keyboard_toolbar superview] bounds].size.height,
rect.size.width, rect.size.height) delegate:self];
[[_keyboard_toolbar superview] addSubview:_advanced_keyboard_view];
// we set autoresize to YES for the keyboard toolbar's superview so that our adv. keyboard view gets properly resized
[[_keyboard_toolbar superview] setAutoresizesSubviews:YES];
// show view with animation
[UIView beginAnimations:nil context:NULL];
[_advanced_keyboard_view setFrame:rect];
[UIView commitAnimations];
}
-(IBAction)toggleKeyboardWhenOtherVisible:(id)sender
{
if(_advanced_keyboard_visible == NO)
{
[self showAdvancedKeyboardAnimated];
}
else
{
// hide existing view
[UIView beginAnimations:@"hide_advanced_keyboard_view" context:NULL];
CGRect rect = [_advanced_keyboard_view frame];
rect.origin.y = [[_keyboard_toolbar superview] bounds].size.height;
[_advanced_keyboard_view setFrame:rect];
[UIView commitAnimations];
// the view is released in the animationDidStop selector registered in init
}
// toggle flag
_advanced_keyboard_visible = !_advanced_keyboard_visible;
}
-(IBAction)toggleWinKey:(id)sender
{
[[RDPKeyboard getSharedRDPKeyboard] toggleWinKey];
}
-(IBAction)toggleShiftKey:(id)sender
{
[[RDPKeyboard getSharedRDPKeyboard] toggleShiftKey];
}
-(IBAction)toggleCtrlKey:(id)sender
{
[[RDPKeyboard getSharedRDPKeyboard] toggleCtrlKey];
}
-(IBAction)toggleAltKey:(id)sender
{
[[RDPKeyboard getSharedRDPKeyboard] toggleAltKey];
}
-(IBAction)pressEscKey:(id)sender
{
[[RDPKeyboard getSharedRDPKeyboard] sendEscapeKeyStroke];
}
#pragma mark -
#pragma mark event handlers
- (void)animationStopped:(NSString*)animationID finished:(NSNumber*)finished context:(void*)context
{
if ([animationID isEqualToString:@"hide_advanced_keyboard_view"])
{
// cleanup advanced keyboard view
[_advanced_keyboard_view removeFromSuperview];
[_advanced_keyboard_view autorelease];
_advanced_keyboard_view = nil;
}
}
- (IBAction)switchSession:(id)sender
{
[self suspendSession];
}
- (IBAction)toggleKeyboard:(id)sender
{
if(!_keyboard_visible)
[_dummy_textfield becomeFirstResponder];
else
[_dummy_textfield resignFirstResponder];
}
- (IBAction)toggleExtKeyboard:(id)sender
{
// if the sys kb is shown but not the advanced kb then toggle the advanced kb
if(_keyboard_visible && !_advanced_keyboard_visible)
[self toggleKeyboardWhenOtherVisible:nil];
else
{
// if not visible request the advanced keyboard view
if(_advanced_keyboard_visible == NO)
_requesting_advanced_keyboard = YES;
[self toggleKeyboard:nil];
}
}
- (IBAction)toggleTouchPointer:(id)sender
{
BOOL toggle_visibilty = ![_touchpointer_view isHidden];
[_touchpointer_view setHidden:toggle_visibilty];
if(toggle_visibilty)
[_session_scrollview setContentInset:UIEdgeInsetsZero];
else
[_session_scrollview setContentInset:[_touchpointer_view getEdgeInsets]];
}
- (IBAction)disconnectSession:(id)sender
{
[_session disconnect];
}
-(IBAction)cancelButtonPressed:(id)sender
{
[_session disconnect];
}
#pragma mark In-App purchase transaction notification handlers
- (void)onTransactionSuccess:(NSNotification*)notification
{
UIAlertView* alertView = [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Transaction Succeeded", @"Pro version bought dialog title")
message:NSLocalizedString(@"Thanks for buying Thinstuff RDC Pro. In order for the purchase to take effect please reconnect your current session.", @"Pro version bought dialog message") delegate:nil cancelButtonTitle:NSLocalizedString(@"OK", @"OK Button title") otherButtonTitles:nil] autorelease];
[alertView show];
}
- (void)onTransactionFailed:(NSNotification*)notification
{
UIAlertView* alertView = [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Transaction Failed", @"Pro version buy failed dialog title")
message:NSLocalizedString(@"The transaction did not complete successfully!", @"Pro version buy failed dialog message") delegate:nil cancelButtonTitle:NSLocalizedString(@"OK", @"OK Button title") otherButtonTitles:nil] autorelease];
[alertView show];
}
#pragma mark -
#pragma mark iOS Keyboard Notification Handlers
- (void)keyboardWillShow:(NSNotification *)notification
{
CGRect keyboardEndFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:[[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
[UIView setAnimationDuration:[[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
CGRect frame = [_session_scrollview frame];
frame.size.height -= [[self view] convertRect:keyboardEndFrame toView:nil].size.height;
[_session_scrollview setFrame:frame];
[_touchpointer_view setFrame:frame];
[UIView commitAnimations];
[_touchpointer_view ensurePointerIsVisible];
}
- (void)keyboardDidShow:(NSNotification *)notification
{
if(_requesting_advanced_keyboard)
{
[self showAdvancedKeyboardAnimated];
_advanced_keyboard_visible = YES;
_requesting_advanced_keyboard = NO;
}
}
- (void)keyboardWillHide:(NSNotification *)notification
{
CGRect keyboardEndFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:[[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
[UIView setAnimationDuration:[[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
CGRect frame = [_session_scrollview frame];
frame.size.height += [[self view] convertRect:keyboardEndFrame toView:nil].size.height;
[_session_scrollview setFrame:frame];
[_touchpointer_view setFrame:frame];
[UIView commitAnimations];
}
- (void)keyboardDidHide:(NSNotification*)notification
{
// release adanced keyboard view
if(_advanced_keyboard_visible == YES)
{
_advanced_keyboard_visible = NO;
[_advanced_keyboard_view removeFromSuperview];
[_advanced_keyboard_view autorelease];
_advanced_keyboard_view = nil;
}
}
#pragma mark -
#pragma mark Gesture handlers
- (void)handleSingleTap:(UITapGestureRecognizer*)gesture
{
CGPoint pos = [gesture locationInView:_session_view];
if (_toggle_mouse_button)
{
[_session sendInputEvent:[self eventDescriptorForMouseEvent:GetRightMouseButtonClickEvent(YES) position:pos]];
[_session sendInputEvent:[self eventDescriptorForMouseEvent:GetRightMouseButtonClickEvent(NO) position:pos]];
}
else
{
[_session sendInputEvent:[self eventDescriptorForMouseEvent:GetLeftMouseButtonClickEvent(YES) position:pos]];
[_session sendInputEvent:[self eventDescriptorForMouseEvent:GetLeftMouseButtonClickEvent(NO) position:pos]];
}
_toggle_mouse_button = NO;
}
- (void)handleDoubleTap:(UITapGestureRecognizer*)gesture
{
CGPoint pos = [gesture locationInView:_session_view];
[_session sendInputEvent:[self eventDescriptorForMouseEvent:GetLeftMouseButtonClickEvent(YES) position:pos]];
[_session sendInputEvent:[self eventDescriptorForMouseEvent:GetLeftMouseButtonClickEvent(NO) position:pos]];
[_session sendInputEvent:[self eventDescriptorForMouseEvent:GetLeftMouseButtonClickEvent(YES) position:pos]];
[_session sendInputEvent:[self eventDescriptorForMouseEvent:GetLeftMouseButtonClickEvent(NO) position:pos]];
_toggle_mouse_button = NO;
}
- (void)handleLongPress:(UILongPressGestureRecognizer*)gesture
{
CGPoint pos = [gesture locationInView:_session_view];
if([gesture state] == UIGestureRecognizerStateBegan)
[_session sendInputEvent:[self eventDescriptorForMouseEvent:GetLeftMouseButtonClickEvent(YES) position:pos]];
else if([gesture state] == UIGestureRecognizerStateChanged)
[self handleMouseMoveForPosition:pos];
else if([gesture state] == UIGestureRecognizerStateEnded)
[_session sendInputEvent:[self eventDescriptorForMouseEvent:GetLeftMouseButtonClickEvent(NO) position:pos]];
}
- (void)handleDoubleLongPress:(UILongPressGestureRecognizer*)gesture
{
// this point is mapped against the scroll view because we want to have relative movement to the screen/scrollview
CGPoint pos = [gesture locationInView:_session_scrollview];
if([gesture state] == UIGestureRecognizerStateBegan)
_prev_long_press_position = pos;
else if([gesture state] == UIGestureRecognizerStateChanged)
{
int delta = _prev_long_press_position.y - pos.y;
if(delta > GetScrollGestureDelta())
{
[_session sendInputEvent:[self eventDescriptorForMouseEvent:GetMouseWheelEvent(YES) position:pos]];
_prev_long_press_position = pos;
}
else if(delta < -GetScrollGestureDelta())
{
[_session sendInputEvent:[self eventDescriptorForMouseEvent:GetMouseWheelEvent(NO) position:pos]];
_prev_long_press_position = pos;
}
}
}
-(void)handleSingle2FingersTap:(UITapGestureRecognizer*)gesture
{
_toggle_mouse_button = !_toggle_mouse_button;
}
-(void)handleSingle3FingersTap:(UITapGestureRecognizer*)gesture
{
[_session setToolbarVisible:![_session toolbarVisible]];
[self showSessionToolbar:[_session toolbarVisible]];
}
#pragma mark -
#pragma mark Touch Pointer delegates
// callback if touch pointer should be closed
-(void)touchPointerClose
{
[self toggleTouchPointer:nil];
}
// callback for a left click action
-(void)touchPointerLeftClick:(CGPoint)pos down:(BOOL)down
{
CGPoint session_view_pos = [_touchpointer_view convertPoint:pos toView:_session_view];
[_session sendInputEvent:[self eventDescriptorForMouseEvent:GetLeftMouseButtonClickEvent(down) position:session_view_pos]];
}
// callback for a right click action
-(void)touchPointerRightClick:(CGPoint)pos down:(BOOL)down
{
CGPoint session_view_pos = [_touchpointer_view convertPoint:pos toView:_session_view];
[_session sendInputEvent:[self eventDescriptorForMouseEvent:GetRightMouseButtonClickEvent(down) position:session_view_pos]];
}
- (void)doAutoScrolling
{
int scrollX = 0;
int scrollY = 0;
CGPoint curPointerPos = [_touchpointer_view getPointerPosition];
CGRect viewBounds = [_touchpointer_view bounds];
CGRect scrollBounds = [_session_view bounds];
// add content insets to scroll bounds
scrollBounds.size.width += [_session_scrollview contentInset].right;
scrollBounds.size.height += [_session_scrollview contentInset].bottom;
// add zoom factor
scrollBounds.size.width *= [_session_scrollview zoomScale];
scrollBounds.size.height *= [_session_scrollview zoomScale];
if (curPointerPos.x > (viewBounds.size.width - [_touchpointer_view getPointerWidth]))
scrollX = AUTOSCROLLDISTANCE;
else if (curPointerPos.x < 0)
scrollX = -AUTOSCROLLDISTANCE;
if (curPointerPos.y > (viewBounds.size.height - [_touchpointer_view getPointerHeight]))
scrollY = AUTOSCROLLDISTANCE;
else if (curPointerPos.y < (_session_toolbar_visible ? TOOLBAR_HEIGHT : 0))
scrollY = -AUTOSCROLLDISTANCE;
CGPoint newOffset = [_session_scrollview contentOffset];
newOffset.x += scrollX;
newOffset.y += scrollY;
// if offset is going off screen - stop scrolling in that direction
if (newOffset.x < 0)
{
scrollX = 0;
newOffset.x = 0;
}
else if (newOffset.x > (scrollBounds.size.width - viewBounds.size.width))
{
scrollX = 0;
newOffset.x = MAX(scrollBounds.size.width - viewBounds.size.width, 0);
}
if (newOffset.y < 0)
{
scrollY = 0;
newOffset.y = 0;
}
else if (newOffset.y > (scrollBounds.size.height - viewBounds.size.height))
{
scrollY = 0;
newOffset.y = MAX(scrollBounds.size.height - viewBounds.size.height, 0);
}
// perform scrolling
[_session_scrollview setContentOffset:newOffset];
// continue scrolling?
if (scrollX != 0 || scrollY != 0)
[self performSelector:@selector(doAutoScrolling) withObject:nil afterDelay:AUTOSCROLLTIMEOUT];
else
_is_autoscrolling = NO;
}
// callback for a right click action
-(void)touchPointerMove:(CGPoint)pos
{
CGPoint session_view_pos = [_touchpointer_view convertPoint:pos toView:_session_view];
[self handleMouseMoveForPosition:session_view_pos];
if (_autoscroll_with_touchpointer && !_is_autoscrolling)
{
_is_autoscrolling = YES;
[self performSelector:@selector(doAutoScrolling) withObject:nil afterDelay:AUTOSCROLLTIMEOUT];
}
}
// callback if scrolling is performed
-(void)touchPointerScrollDown:(BOOL)down
{
[_session sendInputEvent:[self eventDescriptorForMouseEvent:GetMouseWheelEvent(down) position:CGPointZero]];
}
// callback for toggling the standard keyboard
-(void)touchPointerToggleKeyboard
{
if(_advanced_keyboard_visible)
[self toggleKeyboardWhenOtherVisible:nil];
else
[self toggleKeyboard:nil];
}
// callback for toggling the extended keyboard
-(void)touchPointerToggleExtendedKeyboard
{
[self toggleExtKeyboard:nil];
}
// callback for reset view
-(void)touchPointerResetSessionView
{
[_session_scrollview setZoomScale:1.0 animated:YES];
}
@end
@implementation RDPSessionViewController (Private)
#pragma mark -
#pragma mark Helper functions
-(void)showSessionToolbar:(BOOL)show
{
// already shown or hidden?
if (_session_toolbar_visible == show)
return;
if(show)
{
[UIView beginAnimations:@"showToolbar" context:nil];
[UIView setAnimationDuration:.4];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[_session_toolbar setFrame:CGRectMake(0.0, 0.0, [[self view] bounds].size.width, TOOLBAR_HEIGHT)];
[UIView commitAnimations];
_session_toolbar_visible = YES;
}
else
{
[UIView beginAnimations:@"hideToolbar" context:nil];
[UIView setAnimationDuration:.4];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[_session_toolbar setFrame:CGRectMake(0.0, -TOOLBAR_HEIGHT, [[self view] bounds].size.width, TOOLBAR_HEIGHT)];
[UIView commitAnimations];
_session_toolbar_visible = NO;
}
}
-(UIToolbar*)keyboardToolbar
{
UIToolbar* keyboard_toolbar = [[[UIToolbar alloc] initWithFrame:CGRectNull] autorelease];
[keyboard_toolbar setBarStyle:UIBarStyleBlackOpaque];
UIBarButtonItem* esc_btn = [[[UIBarButtonItem alloc] initWithTitle:@"Esc" style:UIBarButtonItemStyleBordered target:self action:@selector(pressEscKey:)] autorelease];
UIImage* win_icon = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"toolbar_icon_win" ofType:@"png"]];
UIBarButtonItem* win_btn = [[[UIBarButtonItem alloc] initWithImage:win_icon style:UIBarButtonItemStyleBordered target:self action:@selector(toggleWinKey:)] autorelease];
UIBarButtonItem* ctrl_btn = [[[UIBarButtonItem alloc] initWithTitle:@"Ctrl" style:UIBarButtonItemStyleBordered target:self action:@selector(toggleCtrlKey:)] autorelease];
UIBarButtonItem* alt_btn = [[[UIBarButtonItem alloc] initWithTitle:@"Alt" style:UIBarButtonItemStyleBordered target:self action:@selector(toggleAltKey:)] autorelease];
UIBarButtonItem* ext_btn = [[[UIBarButtonItem alloc] initWithTitle:@"Ext" style:UIBarButtonItemStyleBordered target:self action:@selector(toggleKeyboardWhenOtherVisible:)] autorelease];
UIBarButtonItem* done_btn = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(toggleKeyboard:)] autorelease];
UIBarButtonItem* flex_spacer = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil] autorelease];
// iPad gets a shift button, iphone doesn't (there's just not enough space ...)
NSArray* items;
if(IsPad())
{
UIBarButtonItem* shift_btn = [[[UIBarButtonItem alloc] initWithTitle:@"Shift" style:UIBarButtonItemStyleBordered target:self action:@selector(toggleShiftKey:)] autorelease];
items = [NSArray arrayWithObjects:esc_btn, flex_spacer,
shift_btn, flex_spacer,
ctrl_btn, flex_spacer,
win_btn, flex_spacer,
alt_btn, flex_spacer,
ext_btn, flex_spacer, done_btn, nil];
}
else
{
items = [NSArray arrayWithObjects:esc_btn, flex_spacer, ctrl_btn, flex_spacer, win_btn, flex_spacer, alt_btn, flex_spacer, ext_btn, flex_spacer, done_btn, nil];
}
[keyboard_toolbar setItems:items];
[keyboard_toolbar sizeToFit];
return keyboard_toolbar;
}
- (void)initGestureRecognizers
{
// single and double tap recognizer
UITapGestureRecognizer* doubleTapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)] autorelease];
[doubleTapRecognizer setNumberOfTouchesRequired:1];
[doubleTapRecognizer setNumberOfTapsRequired:2];
UITapGestureRecognizer* singleTapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)] autorelease];
[singleTapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer];
[singleTapRecognizer setNumberOfTouchesRequired:1];
[singleTapRecognizer setNumberOfTapsRequired:1];
// 2 fingers - tap recognizer
UITapGestureRecognizer* single2FingersTapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingle2FingersTap:)] autorelease];
[single2FingersTapRecognizer setNumberOfTouchesRequired:2];
[single2FingersTapRecognizer setNumberOfTapsRequired:1];
// long press gesture recognizer
UILongPressGestureRecognizer* longPressRecognizer = [[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)] autorelease];
[longPressRecognizer setMinimumPressDuration:0.5];
// double long press gesture recognizer
UILongPressGestureRecognizer* doubleLongPressRecognizer = [[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleLongPress:)] autorelease];
[doubleLongPressRecognizer setNumberOfTouchesRequired:2];
[doubleLongPressRecognizer setMinimumPressDuration:0.5];
// 3 finger, single tap gesture for showing/hiding the toolbar
UITapGestureRecognizer* single3FingersTapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingle3FingersTap:)] autorelease];
[single3FingersTapRecognizer setNumberOfTapsRequired:1];
[single3FingersTapRecognizer setNumberOfTouchesRequired:3];
// add gestures to scroll view
[_session_scrollview addGestureRecognizer:singleTapRecognizer];
[_session_scrollview addGestureRecognizer:doubleTapRecognizer];
[_session_scrollview addGestureRecognizer:single2FingersTapRecognizer];
[_session_scrollview addGestureRecognizer:longPressRecognizer];
[_session_scrollview addGestureRecognizer:doubleLongPressRecognizer];
[_session_scrollview addGestureRecognizer:single3FingersTapRecognizer];
}
- (void)suspendSession
{
// suspend session and pop navigation controller
[_session suspend];
// pop current view controller
[[self navigationController] popViewControllerAnimated:YES];
}
- (NSDictionary*)eventDescriptorForMouseEvent:(int)event position:(CGPoint)position
{
return [NSDictionary dictionaryWithObjectsAndKeys:
@"mouse", @"type",
[NSNumber numberWithUnsignedShort:event], @"flags",
[NSNumber numberWithUnsignedShort:lrintf(position.x)], @"coord_x",
[NSNumber numberWithUnsignedShort:lrintf(position.y)], @"coord_y",
nil];
}
- (void)sendDelayedMouseEventWithTimer:(NSTimer*)timer
{
_mouse_move_event_timer = nil;
NSDictionary* event = [timer userInfo];
[_session sendInputEvent:event];
[timer autorelease];
}
- (void)handleMouseMoveForPosition:(CGPoint)position
{
NSDictionary* event = [self eventDescriptorForMouseEvent:PTR_FLAGS_MOVE position:position];
// cancel pending mouse move events
[_mouse_move_event_timer invalidate];
_mouse_move_events_skipped++;
if (_mouse_move_events_skipped >= 5)
{
[_session sendInputEvent:event];
_mouse_move_events_skipped = 0;
}
else
{
[_mouse_move_event_timer autorelease];
_mouse_move_event_timer = [[NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(sendDelayedMouseEventWithTimer:) userInfo:event repeats:NO] retain];
}
}
@end

View File

@@ -0,0 +1,33 @@
/*
controller for screen settings selection
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "EditorBaseController.h"
@class ConnectionParams;
@class OrderedDictionary;
@interface ScreenSelectionController : EditorBaseController
{
@private
NSString* _keyPath;
ConnectionParams* _params;
// avaiable options
OrderedDictionary* _color_options;
NSArray* _resolution_modes;
// current selections
int _selection_color;
int _selection_resolution;
}
- (id)initWithConnectionParams:(ConnectionParams*)params;
- (id)initWithConnectionParams:(ConnectionParams*)params keyPath:(NSString*)keyPath;
@end

View File

@@ -0,0 +1,234 @@
/*
controller for screen settings selection
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "ScreenSelectionController.h"
#import "Utils.h"
#import "OrderedDictionary.h"
#import "ConnectionParams.h"
@interface ScreenSelectionController (Private)
-(NSString*)keyPathForKey:(NSString*)key;
@end
@implementation ScreenSelectionController
- (id)initWithConnectionParams:(ConnectionParams*)params
{
return [self initWithConnectionParams:params keyPath:nil];
}
- (id)initWithConnectionParams:(ConnectionParams*)params keyPath:(NSString*)keyPath
{
self = [super initWithStyle:UITableViewStyleGrouped];
if (self)
{
_params = [params retain];
_keyPath = (keyPath != nil ? [keyPath retain] : nil);
_color_options = (OrderedDictionary*)[SelectionForColorSetting() retain];
_resolution_modes = [ResolutionModes() retain];
// init current selections
NSUInteger idx = [_color_options indexForValue:[NSNumber numberWithInt:[_params intForKeyPath:[self keyPathForKey:@"colors"]]]];
_selection_color = (idx != NSNotFound) ? idx : 0;
idx = [_resolution_modes indexOfObject:ScreenResolutionDescription([_params intForKeyPath:[self keyPathForKey:@"screen_resolution_type"]],
[_params intForKeyPath:[self keyPathForKey:@"width"]],
[_params intForKeyPath:[self keyPathForKey:@"height"]])];
_selection_resolution = (idx != NSNotFound) ? idx : 0;
}
return self;
}
- (void)dealloc
{
[super dealloc];
[_params autorelease];
[_keyPath autorelease];
[_color_options autorelease];
[_resolution_modes autorelease];
}
-(NSString*)keyPathForKey:(NSString*)key
{
if (_keyPath)
return [_keyPath stringByAppendingFormat:@".%@", key];
return key;
}
#pragma mark - View lifecycle
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
if (section == 0)
return [_color_options count];
return [_resolution_modes count] + 2; // +2 for custom width/height input fields
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = nil;
switch ([indexPath section])
{
case 0:
cell = [self tableViewCellFromIdentifier:TableCellIdentifierMultiChoice];
[[cell textLabel] setText:[_color_options keyAtIndex:[indexPath row]]];
break;
case 1:
if ([indexPath row] < [_resolution_modes count])
{
cell = [self tableViewCellFromIdentifier:TableCellIdentifierMultiChoice];
[[cell textLabel] setText:[_resolution_modes objectAtIndex:[indexPath row]]];
}
else
cell = [self tableViewCellFromIdentifier:TableCellIdentifierText];
break;
default:
break;
}
if ([indexPath section] == 1)
{
BOOL enabled = ([_params intForKeyPath:[self keyPathForKey:@"screen_resolution_type"]] == TSXScreenOptionCustom);
if ([indexPath row] == [_resolution_modes count])
{
int value = [_params intForKeyPath:[self keyPathForKey:@"width"]];
EditTextTableViewCell* textCell = (EditTextTableViewCell*)cell;
[[textCell label] setText:NSLocalizedString(@"Width", @"Custom Screen Width")];
[[textCell textfield] setText:[NSString stringWithFormat:@"%d", value ? value : 800]];
[[textCell textfield] setKeyboardType:UIKeyboardTypeNumberPad];
[[textCell label] setEnabled:enabled];
[[textCell textfield] setEnabled:enabled];
[[textCell textfield] setTag:1];
}
else if ([indexPath row] == ([_resolution_modes count] + 1))
{
int value = [_params intForKeyPath:[self keyPathForKey:@"height"]];
EditTextTableViewCell* textCell = (EditTextTableViewCell*)cell;
[[textCell label] setText:NSLocalizedString(@"Height", @"Custom Screen Height")];
[[textCell textfield] setText:[NSString stringWithFormat:@"%d", value ? value : 600]];
[[textCell textfield] setKeyboardType:UIKeyboardTypeNumberPad];
[[textCell label] setEnabled:enabled];
[[textCell textfield] setEnabled:enabled];
[[textCell textfield] setTag:2];
}
}
// set default checkmark
if([indexPath row] == ([indexPath section] == 0 ? _selection_color : _selection_resolution))
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
else
[cell setAccessoryType:UITableViewCellAccessoryNone];
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// custom widht/height cells are not selectable
if ([indexPath section] == 1 && [indexPath row] >= [_resolution_modes count])
return;
// has selection change?
int cur_selection = ([indexPath section] == 0 ? _selection_color : _selection_resolution);
if([indexPath row] != cur_selection)
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
NSIndexPath* oldIndexPath = [NSIndexPath indexPathForRow:cur_selection inSection:[indexPath section]];
// clear old checkmark
UITableViewCell* old_sel_cell = [tableView cellForRowAtIndexPath:oldIndexPath];
old_sel_cell.accessoryType = UITableViewCellAccessoryNone;
// set new checkmark
UITableViewCell* new_sel_cell = [tableView cellForRowAtIndexPath:indexPath];
new_sel_cell.accessoryType = UITableViewCellAccessoryCheckmark;
if ([indexPath section] == 0)
{
// get value from color dictionary
int sel_value = [[_color_options valueForKey:[_color_options keyAtIndex:[indexPath row]]] intValue];
// update selection index and params value
[_params setInt:sel_value forKeyPath:[self keyPathForKey:@"colors"]];
_selection_color = [indexPath row];
}
else
{
// update selection index and params value
int width, height;
TSXScreenOptions mode;
ScanScreenResolution([_resolution_modes objectAtIndex:[indexPath row]], &width, &height, &mode);
[_params setInt:mode forKeyPath:[self keyPathForKey:@"screen_resolution_type"]];
if (mode != TSXScreenOptionCustom)
{
[_params setInt:width forKeyPath:[self keyPathForKey:@"width"]];
[_params setInt:height forKeyPath:[self keyPathForKey:@"height"]];
}
_selection_resolution = [indexPath row];
// refresh width/height edit fields if custom selection changed
NSArray* indexPaths = [NSArray arrayWithObjects:[NSIndexPath indexPathForRow:[_resolution_modes count] inSection:1],
[NSIndexPath indexPathForRow:([_resolution_modes count] + 1) inSection:1], nil];
[[self tableView] reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
}
}
}
#pragma mark -
#pragma mark Text Field delegate
- (BOOL)textFieldShouldReturn:(UITextField*)textField
{
[textField resignFirstResponder];
return NO;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
switch([textField tag])
{
// update resolution settings (and check for invalid input)
case 1:
if ([[textField text] intValue] < 640) [textField setText:@"640"];
[_params setInt:[[textField text] intValue] forKeyPath:[self keyPathForKey:@"width"]];
break;
case 2:
if ([[textField text] intValue] < 480) [textField setText:@"480"];
[_params setInt:[[textField text] intValue] forKeyPath:[self keyPathForKey:@"height"]];
break;
default:
break;
}
return YES;
}
@end

View File

@@ -0,0 +1,29 @@
/*
Certificate verification controller
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import <UIKit/UIKit.h>
@class RDPSession;
@interface VerifyCertificateController : UIViewController
{
@private
IBOutlet UILabel* _label_issuer;
IBOutlet UIButton* _btn_accept;
IBOutlet UIButton* _btn_decline;
IBOutlet UILabel* _label_message;
IBOutlet UILabel* _label_for_issuer;
RDPSession* _session;
NSMutableDictionary* _params;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil session:(RDPSession*)session params:(NSMutableDictionary*)params;
@end

View File

@@ -0,0 +1,79 @@
/*
Certificate verification controller
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#import "VerifyCertificateController.h"
#import "RDPSession.h"
@implementation VerifyCertificateController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil session:(RDPSession *)session params:(NSMutableDictionary *)params
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
_session = session;
_params = params;
[self setModalPresentationStyle:UIModalPresentationFormSheet];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSString* message = NSLocalizedString(@"The identity of the remote computer cannot be verified. Do you want to connect anyway?", @"Verify certificate view message");
// init strings
[_label_message setText:message];
[_label_for_issuer setText:NSLocalizedString(@"Issuer:", @"Verify certificate view issuer label")];
[_btn_accept setTitle:NSLocalizedString(@"Yes", @"Yes Button") forState:UIControlStateNormal];
[_btn_decline setTitle:NSLocalizedString(@"No", @"No Button") forState:UIControlStateNormal];
[_label_issuer setText:[_params valueForKey:@"issuer"]];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
// set signal
[[_session uiRequestCompleted] signal];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#pragma mark - Action handlers
- (IBAction)acceptPressed:(id)sender
{
[_params setValue:[NSNumber numberWithBool:YES] forKey:@"result"];
// dismiss controller
[self dismissModalViewControllerAnimated:YES];
}
- (IBAction)declinePressed:(id)sender
{
[_params setValue:[NSNumber numberWithBool:NO] forKey:@"result"];
// dismiss controller
[self dismissModalViewControllerAnimated:YES];
}
@end