SameBoy/Cocoa/GBPreferencesWindow.m

545 lines
18 KiB
Objective-C

#import "GBPreferencesWindow.h"
#import "NSString+StringForKey.h"
#import "GBButtons.h"
#import <Carbon/Carbon.h>
@implementation GBPreferencesWindow
{
bool is_button_being_modified;
NSInteger button_being_modified;
signed joystick_configuration_state;
NSString *joystick_being_configured;
signed last_axis;
NSPopUpButton *_graphicsFilterPopupButton;
NSPopUpButton *_highpassFilterPopupButton;
NSPopUpButton *_colorCorrectionPopupButton;
NSPopUpButton *_colorPalettePopupButton;
NSPopUpButton *_rewindPopupButton;
NSButton *_aspectRatioCheckbox;
NSEventModifierFlags previousModifiers;
NSPopUpButton *_dmgPopupButton, *_sgbPopupButton, *_cgbPopupButton;
NSPopUpButton *_preferredJoypadButton;
}
+ (NSArray *)filterList
{
/* The filter list as ordered in the popup button */
static NSArray * filters = nil;
if (!filters) {
filters = @[
@"NearestNeighbor",
@"Bilinear",
@"SmoothBilinear",
@"MonoLCD",
@"LCD",
@"CRT",
@"Scale2x",
@"Scale4x",
@"AAScale2x",
@"AAScale4x",
@"HQ2x",
@"OmniScale",
@"OmniScaleLegacy",
@"AAOmniScaleLegacy",
];
}
return filters;
}
- (void)close
{
joystick_configuration_state = -1;
[self.configureJoypadButton setEnabled:YES];
[self.skipButton setEnabled:NO];
[self.configureJoypadButton setTitle:@"Configure Joypad"];
[super close];
}
- (NSPopUpButton *)graphicsFilterPopupButton
{
return _graphicsFilterPopupButton;
}
- (void)setGraphicsFilterPopupButton:(NSPopUpButton *)graphicsFilterPopupButton
{
_graphicsFilterPopupButton = graphicsFilterPopupButton;
NSString *filter = [[NSUserDefaults standardUserDefaults] objectForKey:@"GBFilter"];
[_graphicsFilterPopupButton selectItemAtIndex:[[[self class] filterList] indexOfObject:filter]];
}
- (NSPopUpButton *)highpassFilterPopupButton
{
return _highpassFilterPopupButton;
}
- (void)setColorCorrectionPopupButton:(NSPopUpButton *)colorCorrectionPopupButton
{
_colorCorrectionPopupButton = colorCorrectionPopupButton;
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorCorrection"];
[_colorCorrectionPopupButton selectItemAtIndex:mode];
}
- (NSPopUpButton *)colorCorrectionPopupButton
{
return _colorCorrectionPopupButton;
}
- (void)setColorPalettePopupButton:(NSPopUpButton *)colorPalettePopupButton
{
_colorPalettePopupButton = colorPalettePopupButton;
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorPalette"];
[_colorPalettePopupButton selectItemAtIndex:mode];
}
- (NSPopUpButton *)colorPalettePopupButton
{
return _colorPalettePopupButton;
}
- (void)setRewindPopupButton:(NSPopUpButton *)rewindPopupButton
{
_rewindPopupButton = rewindPopupButton;
NSInteger length = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBRewindLength"];
[_rewindPopupButton selectItemWithTag:length];
}
- (NSPopUpButton *)rewindPopupButton
{
return _rewindPopupButton;
}
- (void)setHighpassFilterPopupButton:(NSPopUpButton *)highpassFilterPopupButton
{
_highpassFilterPopupButton = highpassFilterPopupButton;
[_highpassFilterPopupButton selectItemAtIndex:[[[NSUserDefaults standardUserDefaults] objectForKey:@"GBHighpassFilter"] unsignedIntegerValue]];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
if (self.playerListButton.selectedTag == 0) {
return GBButtonCount;
}
return GBGameBoyButtonCount;
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
if ([tableColumn.identifier isEqualToString:@"keyName"]) {
return GBButtonNames[row];
}
if (is_button_being_modified && button_being_modified == row) {
return @"Select a new key...";
}
NSNumber *key = [[NSUserDefaults standardUserDefaults] valueForKey:button_to_preference_name(row, self.playerListButton.selectedTag)];
if (key) {
return [NSString displayStringForKeyCode: [key unsignedIntegerValue]];
}
return @"";
}
- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
dispatch_async(dispatch_get_main_queue(), ^{
is_button_being_modified = true;
button_being_modified = row;
tableView.enabled = NO;
self.playerListButton.enabled = NO;
[tableView reloadData];
[self makeFirstResponder:self];
});
return NO;
}
-(void)keyDown:(NSEvent *)theEvent
{
if (!is_button_being_modified) {
if (self.firstResponder != self.controlsTableView && [theEvent type] != NSEventTypeFlagsChanged) {
[super keyDown:theEvent];
}
return;
}
is_button_being_modified = false;
[[NSUserDefaults standardUserDefaults] setInteger:theEvent.keyCode
forKey:button_to_preference_name(button_being_modified, self.playerListButton.selectedTag)];
self.controlsTableView.enabled = YES;
self.playerListButton.enabled = YES;
[self.controlsTableView reloadData];
[self makeFirstResponder:self.controlsTableView];
}
- (void) flagsChanged:(NSEvent *)event
{
if (event.modifierFlags > previousModifiers) {
[self keyDown:event];
}
previousModifiers = event.modifierFlags;
}
- (IBAction)graphicFilterChanged:(NSPopUpButton *)sender
{
[[NSUserDefaults standardUserDefaults] setObject:[[self class] filterList][[sender indexOfSelectedItem]]
forKey:@"GBFilter"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBFilterChanged" object:nil];
}
- (IBAction)highpassFilterChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
forKey:@"GBHighpassFilter"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBHighpassFilterChanged" object:nil];
}
- (IBAction)changeAspectRatio:(id)sender
{
[[NSUserDefaults standardUserDefaults] setBool: [(NSButton *)sender state] != NSOnState
forKey:@"GBAspectRatioUnkept"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBAspectChanged" object:nil];
}
- (IBAction)colorCorrectionChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
forKey:@"GBColorCorrection"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorCorrectionChanged" object:nil];
}
- (IBAction)colorPaletteChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
forKey:@"GBColorPalette"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorPaletteChanged" object:nil];
}
- (IBAction)rewindLengthChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedTag])
forKey:@"GBRewindLength"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBRewindLengthChanged" object:nil];
}
- (IBAction) configureJoypad:(id)sender
{
[self.configureJoypadButton setEnabled:NO];
[self.skipButton setEnabled:YES];
joystick_being_configured = nil;
[self advanceConfigurationStateMachine];
last_axis = -1;
}
- (IBAction) skipButton:(id)sender
{
[self advanceConfigurationStateMachine];
}
- (void) advanceConfigurationStateMachine
{
joystick_configuration_state++;
if (joystick_configuration_state < GBButtonCount) {
[self.configureJoypadButton setTitle:[NSString stringWithFormat:@"Press Button for %@", GBButtonNames[joystick_configuration_state]]];
}
else if (joystick_configuration_state == GBButtonCount) {
[self.configureJoypadButton setTitle:@"Move the Analog Stick"];
}
else {
joystick_configuration_state = -1;
[self.configureJoypadButton setEnabled:YES];
[self.skipButton setEnabled:NO];
[self.configureJoypadButton setTitle:@"Configure Joypad"];
}
}
- (void) joystick:(NSString *)joystick_name button: (unsigned)button changedState: (bool) state
{
if (!state) return;
if (joystick_configuration_state == -1) return;
if (joystick_configuration_state == GBButtonCount) return;
if (!joystick_being_configured) {
joystick_being_configured = joystick_name;
}
else if (![joystick_being_configured isEqualToString:joystick_name]) {
return;
}
NSMutableDictionary *all_mappings = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBJoypadMappings"] mutableCopy];
if (!all_mappings) {
all_mappings = [[NSMutableDictionary alloc] init];
}
NSMutableDictionary *mapping = [[all_mappings objectForKey:joystick_name] mutableCopy];
if (!mapping) {
mapping = [[NSMutableDictionary alloc] init];
}
mapping[GBButtonNames[joystick_configuration_state]] = @(button);
all_mappings[joystick_name] = mapping;
[[NSUserDefaults standardUserDefaults] setObject:all_mappings forKey:@"GBJoypadMappings"];
[self refreshJoypadMenu:nil];
[self advanceConfigurationStateMachine];
}
- (void) joystick:(NSString *)joystick_name axis: (unsigned)axis movedTo: (signed) value
{
if (abs(value) < 0x4000) return;
if (joystick_configuration_state != GBButtonCount) return;
if (!joystick_being_configured) {
joystick_being_configured = joystick_name;
}
else if (![joystick_being_configured isEqualToString:joystick_name]) {
return;
}
if (last_axis == -1) {
last_axis = axis;
return;
}
if (axis == last_axis) {
return;
}
NSMutableDictionary *all_mappings = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBJoypadMappings"] mutableCopy];
if (!all_mappings) {
all_mappings = [[NSMutableDictionary alloc] init];
}
NSMutableDictionary *mapping = [[all_mappings objectForKey:joystick_name] mutableCopy];
if (!mapping) {
mapping = [[NSMutableDictionary alloc] init];
}
mapping[@"XAxis"] = @(MIN(axis, last_axis));
mapping[@"YAxis"] = @(MAX(axis, last_axis));
all_mappings[joystick_name] = mapping;
[[NSUserDefaults standardUserDefaults] setObject:all_mappings forKey:@"GBJoypadMappings"];
[self advanceConfigurationStateMachine];
}
- (void) joystick:(NSString *)joystick_name hat: (unsigned)hat changedState: (int8_t) state
{
/* Hats are always mapped to the D-pad, ignore them on non-Dpad keys and skip the D-pad configuration if used*/
if (!state) return;
if (joystick_configuration_state == -1) return;
if (joystick_configuration_state > GBDown) return;
if (!joystick_being_configured) {
joystick_being_configured = joystick_name;
}
else if (![joystick_being_configured isEqualToString:joystick_name]) {
return;
}
NSMutableDictionary *all_mappings = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBJoypadMappings"] mutableCopy];
if (!all_mappings) {
all_mappings = [[NSMutableDictionary alloc] init];
}
NSMutableDictionary *mapping = [[all_mappings objectForKey:joystick_name] mutableCopy];
if (!mapping) {
mapping = [[NSMutableDictionary alloc] init];
}
for (joystick_configuration_state = 0;; joystick_configuration_state++) {
[mapping removeObjectForKey:GBButtonNames[joystick_configuration_state]];
if (joystick_configuration_state == GBDown) break;
}
all_mappings[joystick_name] = mapping;
[[NSUserDefaults standardUserDefaults] setObject:all_mappings forKey:@"GBJoypadMappings"];
[self refreshJoypadMenu:nil];
[self advanceConfigurationStateMachine];
}
- (NSButton *)aspectRatioCheckbox
{
return _aspectRatioCheckbox;
}
- (void)setAspectRatioCheckbox:(NSButton *)aspectRatioCheckbox
{
_aspectRatioCheckbox = aspectRatioCheckbox;
[_aspectRatioCheckbox setState: ![[NSUserDefaults standardUserDefaults] boolForKey:@"GBAspectRatioUnkept"]];
}
- (void)awakeFromNib
{
[super awakeFromNib];
[self updateBootROMFolderButton];
[[NSDistributedNotificationCenter defaultCenter] addObserver:self.controlsTableView selector:@selector(reloadData) name:(NSString*)kTISNotifySelectedKeyboardInputSourceChanged object:nil];
}
- (void)dealloc
{
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self.controlsTableView];
}
- (IBAction)selectOtherBootROMFolder:(id)sender
{
NSOpenPanel *panel = [[NSOpenPanel alloc] init];
[panel setCanChooseDirectories:YES];
[panel setCanChooseFiles:NO];
[panel setPrompt:@"Select"];
[panel setDirectoryURL:[[NSUserDefaults standardUserDefaults] URLForKey:@"GBBootROMsFolder"]];
[panel beginSheetModalForWindow:self completionHandler:^(NSModalResponse result) {
if (result == NSModalResponseOK) {
NSURL *url = [[panel URLs] firstObject];
[[NSUserDefaults standardUserDefaults] setURL:url forKey:@"GBBootROMsFolder"];
}
[self updateBootROMFolderButton];
}];
}
- (void) updateBootROMFolderButton
{
NSURL *url = [[NSUserDefaults standardUserDefaults] URLForKey:@"GBBootROMsFolder"];
BOOL is_dir = false;
[[NSFileManager defaultManager] fileExistsAtPath:[url path] isDirectory:&is_dir];
if (!is_dir) url = nil;
if (url) {
[self.bootROMsFolderItem setTitle:[url lastPathComponent]];
NSImage *icon = [[NSWorkspace sharedWorkspace] iconForFile:[url path]];
[icon setSize:NSMakeSize(16, 16)];
[self.bootROMsFolderItem setHidden:NO];
[self.bootROMsFolderItem setImage:icon];
[self.bootROMsButton selectItemAtIndex:1];
}
else {
[self.bootROMsFolderItem setHidden:YES];
[self.bootROMsButton selectItemAtIndex:0];
}
}
- (IBAction)useBuiltinBootROMs:(id)sender
{
[[NSUserDefaults standardUserDefaults] setURL:nil forKey:@"GBBootROMsFolder"];
[self updateBootROMFolderButton];
}
- (void)setDmgPopupButton:(NSPopUpButton *)dmgPopupButton
{
_dmgPopupButton = dmgPopupButton;
[_dmgPopupButton selectItemWithTag:[[NSUserDefaults standardUserDefaults] integerForKey:@"GBDMGModel"]];
}
- (NSPopUpButton *)dmgPopupButton
{
return _dmgPopupButton;
}
- (void)setSgbPopupButton:(NSPopUpButton *)sgbPopupButton
{
_sgbPopupButton = sgbPopupButton;
[_sgbPopupButton selectItemWithTag:[[NSUserDefaults standardUserDefaults] integerForKey:@"GBSGBModel"]];
}
- (NSPopUpButton *)sgbPopupButton
{
return _sgbPopupButton;
}
- (void)setCgbPopupButton:(NSPopUpButton *)cgbPopupButton
{
_cgbPopupButton = cgbPopupButton;
[_cgbPopupButton selectItemWithTag:[[NSUserDefaults standardUserDefaults] integerForKey:@"GBCGBModel"]];
}
- (NSPopUpButton *)cgbPopupButton
{
return _cgbPopupButton;
}
- (IBAction)dmgModelChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedTag])
forKey:@"GBDMGModel"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBDMGModelChanged" object:nil];
}
- (IBAction)sgbModelChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedTag])
forKey:@"GBSGBModel"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBSGBModelChanged" object:nil];
}
- (IBAction)cgbModelChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedTag])
forKey:@"GBCGBModel"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBCGBModelChanged" object:nil];
}
- (IBAction)reloadButtonsData:(id)sender
{
[self.controlsTableView reloadData];
}
- (void)setPreferredJoypadButton:(NSPopUpButton *)preferredJoypadButton
{
_preferredJoypadButton = preferredJoypadButton;
[self refreshJoypadMenu:nil];
}
- (NSPopUpButton *)preferredJoypadButton
{
return _preferredJoypadButton;
}
- (IBAction)refreshJoypadMenu:(id)sender
{
NSArray *joypads = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBJoypadMappings"] allKeys];
for (NSString *joypad in joypads) {
if ([self.preferredJoypadButton indexOfItemWithTitle:joypad] == -1) {
[self.preferredJoypadButton addItemWithTitle:joypad];
}
}
NSString *player_string = [NSString stringWithFormat: @"%ld", (long)self.playerListButton.selectedTag];
NSString *selected_joypad = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBDefaultJoypads"][player_string];
if (selected_joypad && [self.preferredJoypadButton indexOfItemWithTitle:selected_joypad] != -1) {
[self.preferredJoypadButton selectItemWithTitle:selected_joypad];
}
else {
[self.preferredJoypadButton selectItemWithTitle:@"None"];
}
[self.controlsTableView reloadData];
}
- (IBAction)changeDefaultJoypad:(id)sender
{
NSMutableDictionary *default_joypads = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBDefaultJoypads"] mutableCopy];
if (!default_joypads) {
default_joypads = [[NSMutableDictionary alloc] init];
}
NSString *player_string = [NSString stringWithFormat: @"%ld", self.playerListButton.selectedTag];
if ([[sender titleOfSelectedItem] isEqualToString:@"None"]) {
[default_joypads removeObjectForKey:player_string];
}
else {
default_joypads[player_string] = [sender titleOfSelectedItem];
}
[[NSUserDefaults standardUserDefaults] setObject:default_joypads forKey:@"GBDefaultJoypads"];
}
@end