Search
lxdream.org :: lxdream/src/cocoaui/cocoa_cfg.m :: diff
lxdream 0.9.1
released Jun 29
Download Now
filename src/cocoaui/cocoa_cfg.m
changeset 1072:d82e04e6d497
next1298:d0eb2307b847
author nkeynes
date Tue Jul 21 20:33:21 2009 +1000 (12 years ago)
permissions -rw-r--r--
last change Heavy configuration management refactor
- Configuration groups now take both an on_change event handler and a
default keybinding handler, making most keybinding tasks quite simple
- GUI configuration all merged into a unified model, drastically reducing
the amount of GUI config code.

Bonuses
- OSX now has a hotkey preference pane
- GTK keybinding editor is much more usable
file annotate diff log raw
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/src/cocoaui/cocoa_cfg.m Tue Jul 21 20:33:21 2009 +1000
1.3 @@ -0,0 +1,383 @@
1.4 +/**
1.5 + * $Id$
1.6 + *
1.7 + * Construct and manage a configuration pane based on an underlying
1.8 + * configuration group.
1.9 + *
1.10 + * Copyright (c) 2009 Nathan Keynes.
1.11 + *
1.12 + * This program is free software; you can redistribute it and/or modify
1.13 + * it under the terms of the GNU General Public License as published by
1.14 + * the Free Software Foundation; either version 2 of the License, or
1.15 + * (at your option) any later version.
1.16 + *
1.17 + * This program is distributed in the hope that it will be useful,
1.18 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.19 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.20 + * GNU General Public License for more details.
1.21 + */
1.22 +
1.23 +#include "cocoaui.h"
1.24 +#include "display.h"
1.25 +#include "lxpaths.h"
1.26 +#include "maple/maple.h"
1.27 +
1.28 +static void cocoa_config_keysym_hook(void *data, const gchar *keysym);
1.29 +
1.30 +@interface KeyBindingEditor (Private)
1.31 +- (void)updateKeysym: (const gchar *)sym;
1.32 +@end
1.33 +
1.34 +@implementation KeyBindingEditor
1.35 +- (id)init
1.36 +{
1.37 + self = [super init];
1.38 + isPrimed = NO;
1.39 + lastValue = nil;
1.40 + [self setFieldEditor: YES];
1.41 + [self setEditable: FALSE];
1.42 + return self;
1.43 +}
1.44 +- (void)dealloc
1.45 +{
1.46 + if( lastValue != nil ) {
1.47 + [lastValue release];
1.48 + lastValue = nil;
1.49 + }
1.50 + [super dealloc];
1.51 +}
1.52 +- (void)setPrimed: (BOOL)primed
1.53 +{
1.54 + if( primed != isPrimed ) {
1.55 + isPrimed = primed;
1.56 + if( primed ) {
1.57 + lastValue = [[NSString stringWithString: [self string]] retain];
1.58 + [self setString: @"<press key>"];
1.59 + input_set_keysym_hook(cocoa_config_keysym_hook, self);
1.60 + } else {
1.61 + [lastValue release];
1.62 + lastValue = nil;
1.63 + input_set_keysym_hook(NULL,NULL);
1.64 + }
1.65 + }
1.66 +}
1.67 +- (BOOL)resignFirstResponder
1.68 +{
1.69 + if( isPrimed ) {
1.70 + [self setString: lastValue];
1.71 + [self setPrimed: NO];
1.72 + }
1.73 + return [super resignFirstResponder];
1.74 +}
1.75 +- (void)fireBindingChanged
1.76 +{
1.77 + id delegate = [self delegate];
1.78 + if( delegate != nil && [delegate respondsToSelector:@selector(textDidChange:)] ) {
1.79 + [delegate textDidChange: [NSNotification notificationWithName: NSTextDidChangeNotification object: self]];
1.80 + }
1.81 +}
1.82 +
1.83 +- (void)updateKeysym: (const gchar *)sym
1.84 +{
1.85 + if( sym != NULL ) {
1.86 + [self setString: [NSString stringWithCString: sym]];
1.87 + [self setPrimed: NO];
1.88 + [self fireBindingChanged];
1.89 + }
1.90 +}
1.91 +- (void)updateMousesym: (int)button
1.92 +{
1.93 + gchar *keysym = input_keycode_to_keysym( &system_mouse_driver, (button+1) );
1.94 + if( keysym != NULL ) {
1.95 + [self updateKeysym: keysym ];
1.96 + g_free(keysym);
1.97 + }
1.98 +}
1.99 +- (void)keyPressed: (int)keycode
1.100 +{
1.101 + gchar *keysym = input_keycode_to_keysym(NULL, keycode);
1.102 + if( keysym != NULL ) {
1.103 + [self updateKeysym: keysym];
1.104 + g_free(keysym);
1.105 + }
1.106 +}
1.107 +- (void)insertText:(id)string
1.108 +{
1.109 + // Do nothing
1.110 +}
1.111 +- (void)mouseDown: (NSEvent *)event
1.112 +{
1.113 + if( isPrimed ) {
1.114 + [self updateMousesym: 0];
1.115 + } else {
1.116 + [self setPrimed: YES];
1.117 + [super mouseDown: event];
1.118 + }
1.119 +}
1.120 +- (void)rightMouseDown: (NSEvent *)event
1.121 +{
1.122 + if( isPrimed ) {
1.123 + [self updateMousesym: 1];
1.124 + }
1.125 +}
1.126 +- (void)otherMouseDown: (NSEvent *)event
1.127 +{
1.128 + if( isPrimed ) {
1.129 + [self updateMousesym: [event buttonNumber]];
1.130 + }
1.131 +}
1.132 +- (void)keyDown: (NSEvent *) event
1.133 +{
1.134 + NSString *chars = [event characters];
1.135 + if( isPrimed ) {
1.136 + if( chars != NULL && [chars length] == 1 && [chars characterAtIndex: 0] == 27 ) {
1.137 + // Escape char = abort change
1.138 + [self setString: lastValue];
1.139 + [self setPrimed: NO];
1.140 + } else {
1.141 + [self keyPressed: ([event keyCode]+1)];
1.142 + }
1.143 + } else {
1.144 + if( chars != NULL && [chars length] == 1 ) {
1.145 + int ch = [chars characterAtIndex: 0];
1.146 + switch( ch ) {
1.147 + case 0x7F:
1.148 + [self setString: @""];
1.149 + [self fireBindingChanged];
1.150 + break;
1.151 + case '\r':
1.152 + [self setPrimed: YES];
1.153 + break;
1.154 + default:
1.155 + [super keyDown: event];
1.156 + break;
1.157 + }
1.158 + } else {
1.159 + [super keyDown: event];
1.160 + }
1.161 + }
1.162 +}
1.163 +- (void)flagsChanged: (NSEvent *) event
1.164 +{
1.165 + if( isPrimed ) {
1.166 + [self keyPressed: ([event keyCode]+1)];
1.167 + }
1.168 + [super flagsChanged: event];
1.169 +}
1.170 +@end
1.171 +
1.172 +static void cocoa_config_keysym_hook(void *data, const gchar *keysym)
1.173 +{
1.174 + KeyBindingEditor *editor = (KeyBindingEditor *)data;
1.175 + [editor updateKeysym: keysym];
1.176 +}
1.177 +
1.178 +@implementation KeyBindingField
1.179 +@end
1.180 +
1.181 +/*************************** Configuration sub-view ***********************/
1.182 +
1.183 +#define KEYBINDING_SIZE 110
1.184 +#define DEFAULT_LABEL_WIDTH 150
1.185 +#define RIGHT_MARGIN 40
1.186 +
1.187 +@implementation ConfigurationView
1.188 +- (id)initWithFrame: (NSRect)frameRect
1.189 +{
1.190 + return [self initWithFrame: frameRect configGroup: NULL];
1.191 +}
1.192 +- (id)initWithFrame: (NSRect)frameRect configGroup: (lxdream_config_group_t)config
1.193 +{
1.194 + if( [super initWithFrame: frameRect] == nil ) {
1.195 + return nil;
1.196 + } else {
1.197 + group = NULL;
1.198 + labelWidth = DEFAULT_LABEL_WIDTH;
1.199 + [self setConfigGroup: config];
1.200 + return self;
1.201 + }
1.202 +}
1.203 +- (BOOL)isFlipped
1.204 +{
1.205 + return YES;
1.206 +}
1.207 +- (void)setLabelWidth: (int)width
1.208 +{
1.209 + labelWidth = width;
1.210 +}
1.211 +- (void)removeSubviews
1.212 +{
1.213 + [[self subviews] makeObjectsPerformSelector: @selector(removeFromSuperview)];
1.214 +}
1.215 +- (void)updateField: (int)binding
1.216 +{
1.217 + const gchar *p = NULL;
1.218 + NSString *val1 = [fields[binding][0] stringValue];
1.219 + if( fields[binding][1] == NULL ) {
1.220 + p = [val1 UTF8String];
1.221 + lxdream_set_config_value( group, binding, p );
1.222 + } else {
1.223 + NSString *val2 = [fields[binding][1] stringValue];
1.224 + char buf[ [val1 length] + [val2 length] + 2 ];
1.225 +
1.226 + if( [val1 length] == 0 ) {
1.227 + if( [val2 length] != 0 ) {
1.228 + p = [val2 UTF8String];
1.229 + }
1.230 + } else if( [val2 length] == 0 ) {
1.231 + p = [val1 UTF8String];
1.232 + } else {
1.233 + sprintf( buf, "%s,%s", [val1 UTF8String], [val2 UTF8String] );
1.234 + p = buf;
1.235 + }
1.236 + lxdream_set_config_value( group, binding, p );
1.237 + }
1.238 + lxdream_save_config();
1.239 +}
1.240 +
1.241 +- (void)controlTextDidChange: (NSNotification *)notify
1.242 +{
1.243 + if( [[notify object] isKindOfClass: [KeyBindingField class]] ) {
1.244 + [self updateField: [[notify object] tag]];
1.245 + }
1.246 +}
1.247 +- (void)controlTextDidEndEditing: (NSNotification *)notify
1.248 +{
1.249 + [self updateField: [[notify object] tag]];
1.250 +}
1.251 +
1.252 +- (void)openFileDialog: (id)sender
1.253 +{
1.254 + int tag = [sender tag];
1.255 + NSString *text = [fields[tag][0] stringValue];
1.256 + NSOpenPanel *panel = [NSOpenPanel openPanel];
1.257 + int result = [panel runModalForDirectory: nil file: nil types: nil];
1.258 + if( result == NSOKButton && [[panel filenames] count] > 0 ) {
1.259 + NSString *filename = [[panel filenames] objectAtIndex: 0];
1.260 + gchar *str = get_escaped_path( [filename UTF8String] );
1.261 + [fields[tag][0] setStringValue: [NSString stringWithUTF8String: str]];
1.262 + lxdream_set_global_config_value(tag,str);
1.263 + lxdream_save_config();
1.264 + }
1.265 +}
1.266 +- (void)openDirDialog: (id)sender
1.267 +{
1.268 + int tag = [sender tag];
1.269 + NSString *text = [fields[tag][0] stringValue];
1.270 + NSOpenPanel *panel = [NSOpenPanel openPanel];
1.271 + [panel setCanChooseDirectories: YES];
1.272 + [panel setCanCreateDirectories: YES];
1.273 + int result = [panel runModalForDirectory: nil file: nil types: nil];
1.274 + if( result == NSOKButton && [[panel filenames] count] > 0 ) {
1.275 + NSString *filename = [[panel filenames] objectAtIndex: 0];
1.276 + gchar *str = get_escaped_path( [filename UTF8String] );
1.277 + [fields[tag][0] setStringValue: [NSString stringWithUTF8String: str]];
1.278 + lxdream_set_global_config_value(tag,str);
1.279 + lxdream_save_config();
1.280 + }
1.281 +}
1.282 +
1.283 +- (void)setConfigGroup: (lxdream_config_group_t) config
1.284 +{
1.285 + if( group == config ) {
1.286 + return;
1.287 + }
1.288 + int width = [self frame].size.width;
1.289 + int fieldWidth;
1.290 +
1.291 + group = config;
1.292 + [self removeSubviews];
1.293 + if( config != NULL && config->params[0].key != NULL ) {
1.294 + int count, i, y, x;
1.295 +
1.296 + for( count=0; config->params[count].label != NULL; count++ );
1.297 + int minWidth = labelWidth+KEYBINDING_SIZE*2+TEXT_GAP*4;
1.298 + if( minWidth > width ) {
1.299 + width = minWidth;
1.300 + }
1.301 + NSSize size = NSMakeSize( width, count*(TEXT_HEIGHT+TEXT_GAP)+TEXT_GAP);
1.302 + [self setFrameSize: size];
1.303 + [self scrollRectToVisible: NSMakeRect(0,0,1,1)];
1.304 +
1.305 + x = TEXT_GAP;
1.306 + y = TEXT_GAP;
1.307 + for( i=0; config->params[i].label != NULL; i++ ) {
1.308 + /* Add label */
1.309 + NSRect frame = NSMakeRect(x, y + 2, labelWidth, LABEL_HEIGHT);
1.310 + NSTextField *label = cocoa_gui_add_label(self, NS_(config->params[i].label), frame);
1.311 + [label setAlignment: NSRightTextAlignment];
1.312 +
1.313 + switch(config->params[i].type) {
1.314 + case CONFIG_TYPE_KEY:
1.315 + frame = NSMakeRect( x + labelWidth + TEXT_GAP, y, KEYBINDING_SIZE, TEXT_HEIGHT);
1.316 + fields[i][0] = [[KeyBindingField alloc] initWithFrame: frame];
1.317 + [fields[i][0] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
1.318 + [fields[i][0] setTag: i];
1.319 + [fields[i][0] setDelegate: self];
1.320 + [self addSubview: fields[i][0]];
1.321 +
1.322 + frame = NSMakeRect( x + labelWidth + KEYBINDING_SIZE + (TEXT_GAP*2), y, KEYBINDING_SIZE, TEXT_HEIGHT);
1.323 + fields[i][1] = [[KeyBindingField alloc] initWithFrame: frame];
1.324 + [fields[i][1] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
1.325 + [fields[i][1] setTag: i];
1.326 + [fields[i][1] setDelegate: self];
1.327 + [self addSubview: fields[i][1]];
1.328 +
1.329 + if( config->params[i].value != NULL ) {
1.330 + gchar **parts = g_strsplit(config->params[i].value,",",3);
1.331 + if( parts[0] != NULL ) {
1.332 + [fields[i][0] setStringValue: [NSString stringWithCString: parts[0]]];
1.333 + if( parts[1] != NULL ) {
1.334 + [fields[i][1] setStringValue: [NSString stringWithCString: parts[1]]];
1.335 + }
1.336 + }
1.337 + g_strfreev(parts);
1.338 + }
1.339 + break;
1.340 + case CONFIG_TYPE_FILE:
1.341 + case CONFIG_TYPE_PATH:
1.342 + fieldWidth = width - labelWidth - x - TEXT_HEIGHT - RIGHT_MARGIN - (TEXT_GAP*2);
1.343 + frame = NSMakeRect( x + labelWidth + TEXT_GAP, y, fieldWidth, TEXT_HEIGHT );
1.344 + NSTextField *field = [[NSTextField alloc] initWithFrame: frame];
1.345 + [field setTag: i];
1.346 + [field setStringValue: [NSString stringWithCString: config->params[i].value]];
1.347 + [field setDelegate: self];
1.348 + [field setAutoresizingMask: (NSViewMinYMargin|NSViewWidthSizable)];
1.349 +
1.350 + frame = NSMakeRect( x+ labelWidth + fieldWidth + (TEXT_GAP*2), y, TEXT_HEIGHT, TEXT_HEIGHT );
1.351 + NSButton *button = [[NSButton alloc] initWithFrame: frame];
1.352 + [button setTag: i];
1.353 + [button setTitle: @""];
1.354 + [button setButtonType: NSMomentaryPushInButton];
1.355 + [button setBezelStyle: NSRoundedDisclosureBezelStyle];
1.356 + [button setAutoresizingMask: (NSViewMinYMargin|NSViewMinXMargin)];
1.357 + [button setTarget: self];
1.358 + if( config->params[i].type == CONFIG_TYPE_FILE ) {
1.359 + [button setAction: @selector(openFileDialog:)];
1.360 + } else {
1.361 + [button setAction: @selector(openDirDialog:)];
1.362 + }
1.363 +
1.364 + [self addSubview: label];
1.365 + [self addSubview: field];
1.366 + [self addSubview: button];
1.367 + fields[i][0] = field;
1.368 + fields[i][1] = NULL;
1.369 + }
1.370 + y += (TEXT_HEIGHT + TEXT_GAP);
1.371 + }
1.372 + } else {
1.373 + [self setFrameSize: NSMakeSize(100,TEXT_HEIGHT+TEXT_GAP) ];
1.374 + }
1.375 +}
1.376 +
1.377 +- (void)setDevice: (maple_device_t)newDevice
1.378 +{
1.379 + if( newDevice != NULL && !MAPLE_IS_VMU(newDevice) ) {
1.380 + [self setConfigGroup: maple_get_device_config(newDevice)];
1.381 + } else {
1.382 + [self setConfigGroup: NULL];
1.383 + }
1.384 +}
1.385 +@end
1.386 +
.