mirror of
https://github.com/zydeco/minivmac4ios.git
synced 2024-11-25 07:32:30 +00:00
dead799d16
this saves the preferences, but still prevents accidentally locking up the app by setting a fast speed on a low end device
396 lines
18 KiB
Objective-C
396 lines
18 KiB
Objective-C
//
|
|
// SettingsViewController.m
|
|
// Mini vMac
|
|
//
|
|
// Created by Jesús A. Álvarez on 07/05/2016.
|
|
// Copyright © 2016-2018 namedfork. All rights reserved.
|
|
//
|
|
|
|
#import "SettingsViewController.h"
|
|
#import "AppDelegate.h"
|
|
|
|
@interface SettingsViewController ()
|
|
|
|
@end
|
|
|
|
typedef enum : NSInteger {
|
|
SettingsSectionSpeed,
|
|
SettingsSectionMouse,
|
|
SettingsSectionKeyboard,
|
|
SettingsSectionDisplay,
|
|
SettingsSectionMachine,
|
|
SettingsSectionAbout
|
|
} SettingsSection;
|
|
|
|
@implementation SettingsViewController
|
|
{
|
|
NSArray *keyboardLayouts;
|
|
NSArray<NSBundle*> *emulatorBundles;
|
|
NSMutableArray *machineList; // NSString (header) or NSBundle (emulator bundle)
|
|
NSMutableDictionary<NSString*,UIImage*> *bundleIcons;
|
|
NSMutableSet<NSBundle*> *groupedEmulatorBundles;
|
|
NSBundle *selectedEmulatorBundle;
|
|
NSString *aboutTitle;
|
|
NSArray<NSDictionary<NSString*,NSString*>*> *aboutItems;
|
|
UITextView *footerView;
|
|
}
|
|
|
|
- (void)viewDidLoad {
|
|
[super viewDidLoad];
|
|
keyboardLayouts = [[NSBundle mainBundle] pathsForResourcesOfType:@"nfkeyboardlayout" inDirectory:@"Keyboard Layouts"];
|
|
NSString *userKeyboardLayoutsPath = [AppDelegate sharedInstance].userKeyboardLayoutsPath;
|
|
NSArray *userKeyboardLayouts = [[[NSFileManager defaultManager] contentsOfDirectoryAtPath:userKeyboardLayoutsPath error:nil] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"pathExtension.lowercaseString = %@", @"nfkeyboardlayout"]];
|
|
if (userKeyboardLayouts.count > 0) {
|
|
keyboardLayouts = [keyboardLayouts arrayByAddingObjectsFromArray:userKeyboardLayouts];
|
|
}
|
|
[self loadEmulatorBundles];
|
|
[self loadCredits];
|
|
}
|
|
|
|
- (void)loadEmulatorBundles {
|
|
emulatorBundles = [AppDelegate sharedInstance].emulatorBundles;
|
|
bundleIcons = [NSMutableDictionary dictionaryWithCapacity:emulatorBundles.count];
|
|
NSMutableDictionary<NSString*,NSMutableArray<NSBundle*>*> *bundlesByName = [NSMutableDictionary dictionaryWithCapacity:emulatorBundles.count];
|
|
NSString *selectedBundleName = [[NSUserDefaults standardUserDefaults] stringForKey:@"machine"];
|
|
for (NSBundle *bundle in emulatorBundles) {
|
|
NSString *bundleName = bundle.bundlePath.lastPathComponent.stringByDeletingPathExtension;
|
|
if ([selectedBundleName isEqualToString:bundleName]) {
|
|
selectedEmulatorBundle = bundle;
|
|
}
|
|
NSString *displayName = [bundle objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
|
if (bundlesByName[displayName] == nil) {
|
|
bundlesByName[displayName] = [NSMutableArray arrayWithCapacity:1];
|
|
}
|
|
[bundlesByName[displayName] addObject:bundle];
|
|
}
|
|
NSArray *sortedNames = [bundlesByName.allKeys sortedArrayUsingSelector:@selector(compare:)];
|
|
machineList = [NSMutableArray arrayWithCapacity:emulatorBundles.count];
|
|
groupedEmulatorBundles = [NSMutableSet setWithCapacity:emulatorBundles.count];
|
|
for (NSString *name in sortedNames) {
|
|
NSArray<NSBundle*>* bundles = bundlesByName[name];
|
|
if (bundles.count > 1) {
|
|
[machineList addObject:name];
|
|
[groupedEmulatorBundles addObjectsFromArray:bundles];
|
|
}
|
|
[machineList addObjectsFromArray:bundles];
|
|
}
|
|
}
|
|
|
|
- (void)loadCredits {
|
|
NSDictionary *aboutData = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"about" ofType:@"plist"]];
|
|
aboutTitle = aboutData[@"title"];
|
|
aboutItems = aboutData[@"items"];
|
|
footerView = [[UITextView alloc] initWithFrame:CGRectZero];
|
|
NSString *footerHTML = [aboutData[@"footer.html"] stringByReplacingOccurrencesOfString:@"$mnvmversion" withString:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"MNVMVersion"]];
|
|
NSAttributedString *str = [[NSAttributedString alloc] initWithData:[footerHTML dataUsingEncoding:NSUTF8StringEncoding]
|
|
options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
|
|
NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)}
|
|
documentAttributes:nil
|
|
error:NULL];
|
|
footerView.attributedText = str;
|
|
[footerView sizeToFit];
|
|
footerView.editable = NO;
|
|
footerView.textAlignment = NSTextAlignmentCenter;
|
|
footerView.textColor = [UIColor darkGrayColor];
|
|
footerView.font = [UIFont systemFontOfSize:[UIFont smallSystemFontSize]];
|
|
footerView.backgroundColor = [UIColor clearColor];
|
|
footerView.scrollEnabled = NO;
|
|
}
|
|
|
|
- (void)viewDidAppear:(BOOL)animated {
|
|
[super viewDidAppear:animated];
|
|
[AppDelegate sharedEmulator].running = NO;
|
|
if ([UIApplication instancesRespondToSelector:@selector(btcMouseSetRawMode:)]) {
|
|
[[UIApplication sharedApplication] btcMouseSetRawMode:NO];
|
|
}
|
|
}
|
|
|
|
- (void)viewWillDisappear:(BOOL)animated {
|
|
[super viewWillDisappear:animated];
|
|
[AppDelegate sharedEmulator].running = YES;
|
|
if (![selectedEmulatorBundle isEqual:[AppDelegate sharedEmulator].bundle] && ![AppDelegate sharedEmulator].anyDiskInserted) {
|
|
[[AppDelegate sharedInstance] loadAndStartEmulator];
|
|
}
|
|
}
|
|
|
|
- (void)showInsertDisk:(id)sender {
|
|
[[AppDelegate sharedInstance] showInsertDisk:sender];
|
|
}
|
|
|
|
- (void)dismiss:(id)sender {
|
|
[self dismissViewControllerAnimated:YES completion:nil];
|
|
}
|
|
|
|
- (IBAction)changeSpeed:(UISegmentedControl*)sender {
|
|
if ([sender isKindOfClass:[UISegmentedControl class]]) {
|
|
EmulatorSpeed speedValue = sender.selectedSegmentIndex == sender.numberOfSegments - 1 ? EmulatorSpeedAllOut : sender.selectedSegmentIndex;
|
|
[[NSUserDefaults standardUserDefaults] setInteger:speedValue forKey:@"speedValue"];
|
|
}
|
|
}
|
|
|
|
- (IBAction)changeMouseType:(UISegmentedControl*)sender {
|
|
if ([sender isKindOfClass:[UISegmentedControl class]]) {
|
|
[[NSUserDefaults standardUserDefaults] setBool:sender.selectedSegmentIndex == 1 forKey:@"trackpad"];
|
|
}
|
|
}
|
|
|
|
- (IBAction)changeScreenScaling:(UISegmentedControl*)sender {
|
|
if ([sender isKindOfClass:[UISegmentedControl class]]) {
|
|
NSString *filter = @[kCAFilterNearest, kCAFilterLinear, kCAFilterTrilinear][sender.selectedSegmentIndex];
|
|
[[NSUserDefaults standardUserDefaults] setObject:filter forKey:@"screenFilter"];
|
|
}
|
|
}
|
|
|
|
- (void)changeRunInBackground:(UISwitch*)sender {
|
|
if ([sender isKindOfClass:[UISwitch class]]) {
|
|
[[NSUserDefaults standardUserDefaults] setBool:sender.on forKey:@"runInBackground"];
|
|
}
|
|
}
|
|
|
|
- (void)changeAutoSlow:(UISwitch*)sender {
|
|
if ([sender isKindOfClass:[UISwitch class]]) {
|
|
[[NSUserDefaults standardUserDefaults] setBool:sender.on forKey:@"autoSlow"];
|
|
}
|
|
}
|
|
|
|
- (void)changeAutoShowGestureHelp:(UISwitch*)sender {
|
|
if ([sender isKindOfClass:[UISwitch class]]) {
|
|
[[NSUserDefaults standardUserDefaults] setBool:sender.on forKey:@"autoShowGestureHelp"];
|
|
}
|
|
}
|
|
|
|
#pragma mark - Table view data source
|
|
|
|
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
|
return 6;
|
|
}
|
|
|
|
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
|
switch (section) {
|
|
case SettingsSectionSpeed: {
|
|
NSDictionary *capabilities = [selectedEmulatorBundle objectForInfoDictionaryKey:@"MNVMCapabilities"];
|
|
BOOL hasAutoSlow = [capabilities[@"AutoSlow"] boolValue];
|
|
return hasAutoSlow ? 3 : 2;
|
|
}
|
|
case SettingsSectionKeyboard:
|
|
return keyboardLayouts.count;
|
|
case SettingsSectionMachine:
|
|
return machineList.count;
|
|
case SettingsSectionAbout:
|
|
return aboutItems.count + 1;
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
|
|
switch (section) {
|
|
case SettingsSectionSpeed:
|
|
return NSLocalizedString(@"Speed", nil);
|
|
case SettingsSectionMouse:
|
|
return NSLocalizedString(@"Mouse Type", nil);
|
|
case SettingsSectionMachine:
|
|
return NSLocalizedString(@"Emulated Machine", nil);
|
|
case SettingsSectionKeyboard:
|
|
return NSLocalizedString(@"Keyboard Layout", nil);
|
|
case SettingsSectionDisplay:
|
|
return NSLocalizedString(@"Display Scaling", nil);
|
|
case SettingsSectionAbout:
|
|
return aboutTitle;
|
|
default:return nil;
|
|
}
|
|
}
|
|
|
|
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
|
|
if (section == SettingsSectionAbout) {
|
|
return footerView;
|
|
} else {
|
|
return nil;
|
|
}
|
|
}
|
|
|
|
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
|
|
if (section == SettingsSectionSpeed) {
|
|
return NSLocalizedString(@"Faster speeds and running in background drain the battery faster", nil);
|
|
} else if (section == SettingsSectionMachine && [AppDelegate sharedEmulator].anyDiskInserted) {
|
|
return NSLocalizedString(@"The emulated machine cannot be changed while disks are inserted.", nil);
|
|
} else {
|
|
return nil;
|
|
}
|
|
}
|
|
|
|
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
|
|
if (section == SettingsSectionAbout) {
|
|
return footerView.bounds.size.height;
|
|
} else {
|
|
return UITableViewAutomaticDimension;
|
|
}
|
|
}
|
|
|
|
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
|
UITableViewCell *cell = nil;
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
NSInteger section = indexPath.section;
|
|
if (section == SettingsSectionSpeed) {
|
|
if (indexPath.row == 0) {
|
|
cell = [tableView dequeueReusableCellWithIdentifier:@"speed" forIndexPath:indexPath];
|
|
UISegmentedControl *speedControl = (UISegmentedControl*)[cell viewWithTag:128];
|
|
EmulatorSpeed speedValue = [defaults integerForKey:@"speedValue"];
|
|
speedControl.selectedSegmentIndex = speedValue == EmulatorSpeedAllOut ? speedControl.numberOfSegments - 1 : speedValue;
|
|
} else if (indexPath.row == 1) {
|
|
cell = [self switchCellForTableView:tableView indexPath:indexPath action:@selector(changeRunInBackground:) on:[defaults boolForKey:@"runInBackground"]];
|
|
cell.textLabel.text = NSLocalizedString(@"Run in background", nil);
|
|
} else if (indexPath.row == 2) {
|
|
cell = [self switchCellForTableView:tableView indexPath:indexPath action:@selector(changeAutoSlow:) on:[defaults boolForKey:@"autoSlow"]];
|
|
cell.textLabel.text = NSLocalizedString(@"AutoSlow", nil);
|
|
}
|
|
} else if (section == SettingsSectionMouse) {
|
|
cell = [tableView dequeueReusableCellWithIdentifier:@"mouse" forIndexPath:indexPath];
|
|
UISegmentedControl *mouseControl = (UISegmentedControl*)[cell viewWithTag:128];
|
|
mouseControl.selectedSegmentIndex = [defaults boolForKey:@"trackpad"] ? 1 : 0;
|
|
} else if (section == SettingsSectionKeyboard) {
|
|
cell = [tableView dequeueReusableCellWithIdentifier:@"keyboard" forIndexPath:indexPath];
|
|
NSString *layout = keyboardLayouts[indexPath.row];
|
|
cell.textLabel.text = layout.lastPathComponent.stringByDeletingPathExtension;
|
|
BOOL selected = [[defaults stringForKey:@"keyboardLayout"] isEqualToString:layout.lastPathComponent];
|
|
cell.accessoryType = selected ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
|
|
} else if (section == SettingsSectionMachine) {
|
|
id item = machineList[indexPath.row];
|
|
BOOL rowIsHeader = [item isKindOfClass:[NSString class]];
|
|
BOOL rowHasHeader = [groupedEmulatorBundles containsObject:item];
|
|
NSBundle *bundle = rowIsHeader ? machineList[indexPath.row + 1] : item;
|
|
cell = [tableView dequeueReusableCellWithIdentifier:@"machine" forIndexPath:indexPath];
|
|
if (rowIsHeader) {
|
|
cell.textLabel.text = [bundle objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
|
cell.detailTextLabel.text = NSLocalizedString(@"multiple configurations available", nil);
|
|
} else if (rowHasHeader) {
|
|
cell.textLabel.text = [bundle objectForInfoDictionaryKey:@"CFBundleGetInfoString"];
|
|
cell.detailTextLabel.text = nil;
|
|
} else {
|
|
cell.textLabel.text = [bundle objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
|
cell.detailTextLabel.text = [bundle objectForInfoDictionaryKey:@"CFBundleGetInfoString"];
|
|
}
|
|
|
|
if (rowHasHeader) {
|
|
cell.imageView.image = nil;
|
|
cell.indentationLevel = 1;
|
|
} else {
|
|
cell.imageView.image = [self iconForBundle:bundle];
|
|
cell.indentationLevel = 0;
|
|
}
|
|
cell.accessoryType = (item == selectedEmulatorBundle) ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
|
|
cell.selectionStyle = rowIsHeader ? UITableViewCellSelectionStyleNone : UITableViewCellSelectionStyleDefault;
|
|
} else if (section == SettingsSectionAbout) {
|
|
if (indexPath.row >= aboutItems.count) {
|
|
cell = [self switchCellForTableView:tableView indexPath:indexPath action:@selector(changeAutoShowGestureHelp:) on:[defaults boolForKey:@"autoShowGestureHelp"]];
|
|
cell.textLabel.text = NSLocalizedString(@"Show Gesture Help", nil);
|
|
} else {
|
|
cell = [self aboutCellForTableView:tableView indexPath:indexPath];
|
|
}
|
|
} else if (section == SettingsSectionDisplay) {
|
|
cell = [tableView dequeueReusableCellWithIdentifier:@"display" forIndexPath:indexPath];
|
|
UISegmentedControl *filterControl = (UISegmentedControl*)[cell viewWithTag:128];
|
|
NSString *filter = [defaults stringForKey:@"screenFilter"];
|
|
if ([filter isEqualToString:kCAFilterNearest]) {
|
|
filterControl.selectedSegmentIndex = 0;
|
|
} else if ([filter isEqualToString:kCAFilterTrilinear]) {
|
|
filterControl.selectedSegmentIndex = 2;
|
|
} else {
|
|
filterControl.selectedSegmentIndex = 1;
|
|
}
|
|
}
|
|
return cell;
|
|
}
|
|
|
|
- (UIImage*)iconForBundle:(NSBundle*)bundle {
|
|
UIImage *icon = bundleIcons[bundle.bundlePath];
|
|
if (icon != nil) {
|
|
return icon;
|
|
}
|
|
NSString *iconPath = [NSString stringWithFormat:@"%@/Icon.png", bundle.bundlePath];
|
|
icon = [UIImage imageWithContentsOfFile:iconPath];
|
|
if (icon != nil) {
|
|
bundleIcons[bundle.bundlePath] = icon;
|
|
}
|
|
return icon;
|
|
}
|
|
|
|
- (UITableViewCell*)aboutCellForTableView:(UITableView *)tableView indexPath:(NSIndexPath *)indexPath {
|
|
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"about" forIndexPath:indexPath];
|
|
NSDictionary<NSString*,NSString*> *item = aboutItems[indexPath.row];
|
|
cell.textLabel.text = item[@"text"];
|
|
NSString *detailText = item[@"detailText"];
|
|
if ([detailText isEqualToString:@"$version"]) {
|
|
NSString *versionString = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
|
|
NSString *commitString = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"GitVersion"];
|
|
cell.detailTextLabel.text = commitString.length > 0 ? [NSString stringWithFormat:@"%@ (%@)", versionString, commitString] : versionString;
|
|
} else {
|
|
cell.detailTextLabel.text = detailText;
|
|
}
|
|
cell.accessoryType = item[@"link"] == nil ? UITableViewCellAccessoryNone : UITableViewCellAccessoryDisclosureIndicator;
|
|
return cell;
|
|
}
|
|
|
|
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
|
if (indexPath.section == SettingsSectionMachine && [AppDelegate sharedEmulator].anyDiskInserted) {
|
|
return nil;
|
|
}
|
|
return indexPath;
|
|
}
|
|
|
|
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
|
if (indexPath.section == SettingsSectionKeyboard) {
|
|
// selected keyboard layout
|
|
NSString *layout = keyboardLayouts[indexPath.row];
|
|
[defaults setValue:layout.lastPathComponent forKey:@"keyboardLayout"];
|
|
[tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationAutomatic];
|
|
} else if (indexPath.section == SettingsSectionMachine) {
|
|
// selected emulated machine
|
|
NSBundle *bundle = machineList[indexPath.row];
|
|
if (bundle == selectedEmulatorBundle || ![bundle isKindOfClass:[NSBundle class]]) {
|
|
return;
|
|
}
|
|
NSString *bundleName = bundle.bundlePath.lastPathComponent.stringByDeletingPathExtension;
|
|
[defaults setValue:bundleName forKey:@"machine"];
|
|
NSUInteger lastSelectedIndex = [machineList indexOfObject:selectedEmulatorBundle];
|
|
if (lastSelectedIndex != NSNotFound) {
|
|
[tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:lastSelectedIndex inSection:SettingsSectionMachine]].accessoryType = UITableViewCellAccessoryNone;
|
|
}
|
|
UITableViewCell *currentCell = [tableView cellForRowAtIndexPath:indexPath];
|
|
currentCell.accessoryType = UITableViewCellAccessoryCheckmark;
|
|
selectedEmulatorBundle = bundle;
|
|
[tableView reloadSections:[NSIndexSet indexSetWithIndex:SettingsSectionSpeed] withRowAnimation:UITableViewRowAnimationAutomatic];
|
|
} else if (indexPath.section == SettingsSectionAbout) {
|
|
if (indexPath.row >= aboutItems.count) {
|
|
// show gesture help
|
|
[self dismissViewControllerAnimated:YES completion:^{
|
|
[[AppDelegate sharedInstance] showGestureHelp:self];
|
|
}];
|
|
} else {
|
|
// links in about
|
|
NSString *linkURL = aboutItems[indexPath.row][@"link"];
|
|
if (linkURL != nil) {
|
|
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:linkURL]];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (UITableViewCell*)switchCellForTableView:(UITableView*)tableView indexPath:(NSIndexPath*)indexPath action:(SEL)action on:(BOOL)on {
|
|
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"toggle" forIndexPath:indexPath];
|
|
UISwitch *cellSwitch = (UISwitch*)cell.accessoryView;
|
|
if (cellSwitch == nil) {
|
|
cellSwitch = [UISwitch new];
|
|
cell.accessoryView = cellSwitch;
|
|
} else {
|
|
[cellSwitch removeTarget:nil action:nil forControlEvents:UIControlEventAllEvents];
|
|
}
|
|
cellSwitch.on = on;
|
|
[cellSwitch addTarget:self action:action forControlEvents:UIControlEventValueChanged];
|
|
return cell;
|
|
}
|
|
|
|
@end
|