4 * Construct and manage the controller configuration pane
6 * Copyright (c) 2008 Nathan Keynes.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
22 #include "maple/maple.h"
24 #include <glib/gstrfuncs.h>
28 static void cocoa_config_keysym_hook(void *data, const gchar *keysym);
30 @interface KeyBindingEditor (Private)
31 - (void)updateKeysym: (const gchar *)sym;
34 @implementation KeyBindingEditor
40 [self setFieldEditor: YES];
41 [self setEditable: FALSE];
46 if( lastValue != nil ) {
52 - (void)setPrimed: (BOOL)primed
54 if( primed != isPrimed ) {
57 lastValue = [[NSString stringWithString: [self string]] retain];
58 [self setString: @"<press key>"];
59 input_set_keysym_hook(cocoa_config_keysym_hook, self);
63 input_set_keysym_hook(NULL,NULL);
67 - (void)fireBindingChanged
69 id delegate = [self delegate];
70 if( delegate != nil && [delegate respondsToSelector:@selector(textDidChange:)] ) {
71 [delegate textDidChange: [NSNotification notificationWithName: NSTextDidChangeNotification object: self]];
75 - (void)updateKeysym: (const gchar *)sym
78 [self setString: [NSString stringWithCString: sym]];
80 [self fireBindingChanged];
83 - (void)keyPressed: (int)keycode
85 gchar *keysym = input_keycode_to_keysym(NULL, keycode);
86 if( keysym != NULL ) {
87 [self updateKeysym: keysym];
91 - (void)insertText:(id)string
95 - (void)mouseDown: (NSEvent *)event
97 [self setPrimed: YES];
98 [super mouseDown: event];
100 - (void)keyDown: (NSEvent *) event
102 NSString *chars = [event characters];
104 if( chars != NULL && [chars length] == 1 && [chars characterAtIndex: 0] == 27 ) {
105 // Escape char = abort change
106 [self setString: lastValue];
107 [self setPrimed: NO];
109 [self keyPressed: ([event keyCode]+1)];
112 if( chars != NULL && [chars length] == 1 ) {
113 int ch = [chars characterAtIndex: 0];
116 [self setString: @""];
117 [self fireBindingChanged];
120 [self setPrimed: YES];
123 [super keyDown: event];
127 [super keyDown: event];
131 - (void)flagsChanged: (NSEvent *) event
134 [self keyPressed: ([event keyCode]+1)];
136 [super flagsChanged: event];
140 static void cocoa_config_keysym_hook(void *data, const gchar *keysym)
142 KeyBindingEditor *editor = (KeyBindingEditor *)data;
143 [editor updateKeysym: keysym];
147 @implementation KeyBindingField
150 /*************************** Key-binding sub-view ***********************/
152 #define MAX_KEY_BINDINGS 32
154 @interface ControllerKeyBindingView : NSView
156 maple_device_t device;
157 KeyBindingField *field[MAX_KEY_BINDINGS][2];
159 - (id)initWithFrame: (NSRect)frameRect;
160 - (void)setDevice: (maple_device_t)device;
163 @implementation ControllerKeyBindingView
164 - (id)initWithFrame: (NSRect)frameRect
166 if( [super initWithFrame: frameRect] == nil ) {
177 - (void)removeSubviews
179 [[self subviews] makeObjectsPerformSelector: @selector(removeFromSuperview)];
181 - (void)controlTextDidChange: (NSNotification *)notify
183 int binding = [[notify object] tag];
184 NSString *val1 = [field[binding][0] stringValue];
185 NSString *val2 = [field[binding][1] stringValue];
186 char buf[ [val1 length] + [val2 length] + 2 ];
187 const gchar *p = NULL;
189 if( [val1 length] == 0 ) {
190 if( [val2 length] != 0 ) {
191 p = [val2 UTF8String];
193 } else if( [val2 length] == 0 ) {
194 p = [val1 UTF8String];
196 sprintf( buf, "%s,%s", [val1 UTF8String], [val2 UTF8String] );
199 maple_set_device_config_value( device, binding, p );
200 lxdream_save_config();
202 - (void)setDevice: (maple_device_t)newDevice
205 [self removeSubviews];
206 if( device != NULL ) {
207 lxdream_config_entry_t config = maple_get_device_config(device);
208 if( config != NULL ) {
211 for( count=0; config[count].key != NULL; count++ );
213 NSSize size = NSMakeSize(85*3+TEXT_GAP*4, count*(TEXT_HEIGHT+TEXT_GAP)+TEXT_GAP);
214 [self setFrameSize: size];
215 [self scrollRectToVisible: NSMakeRect(0,0,1,1)];
217 for( i=0; config[i].key != NULL; i++ ) {
218 NSRect frame = NSMakeRect(x, y + 2, 85, LABEL_HEIGHT);
219 NSTextField *label = cocoa_gui_add_label(self, NS_(config[i].label), frame);
220 [label setAlignment: NSRightTextAlignment];
222 frame = NSMakeRect( x + 85 + TEXT_GAP, y, 85, TEXT_HEIGHT);
223 field[i][0] = [[KeyBindingField alloc] initWithFrame: frame];
224 [field[i][0] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
225 [field[i][0] setTag: i];
226 [field[i][0] setDelegate: self];
227 [self addSubview: field[i][0]];
229 frame = NSMakeRect( x + (85*2) + (TEXT_GAP*2), y, 85, TEXT_HEIGHT);
230 field[i][1] = [[KeyBindingField alloc] initWithFrame: frame];
231 [field[i][1] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
232 [field[i][1] setTag: i];
233 [field[i][1] setDelegate: self];
234 [self addSubview: field[i][1]];
236 if( config[i].value != NULL ) {
237 gchar **parts = g_strsplit(config[i].value,",",3);
238 if( parts[0] != NULL ) {
239 [field[i][0] setStringValue: [NSString stringWithCString: parts[0]]];
240 if( parts[1] != NULL ) {
241 [field[i][1] setStringValue: [NSString stringWithCString: parts[1]]];
247 y += (TEXT_HEIGHT + TEXT_GAP);
250 [self setFrameSize: NSMakeSize(100,TEXT_HEIGHT+TEXT_GAP) ];
253 [self setFrameSize: NSMakeSize(100,TEXT_HEIGHT+TEXT_GAP) ];
258 /*************************** Top-level controller pane ***********************/
260 @interface LxdreamPrefsControllerPane: LxdreamPrefsPane
262 struct maple_device *save_controller[4];
264 ControllerKeyBindingView *key_bindings;
266 + (LxdreamPrefsControllerPane *)new;
269 @implementation LxdreamPrefsControllerPane
270 + (LxdreamPrefsControllerPane *)new
272 return [[LxdreamPrefsControllerPane alloc] initWithFrame: NSMakeRect(0,0,600,400)];
274 - (id)initWithFrame: (NSRect)frameRect
276 if( [super initWithFrame: frameRect title: NS_("Controllers")] == nil ) {
279 const struct maple_device_class **devices = maple_get_device_classes();
282 int y = [self contentHeight] - TEXT_HEIGHT - TEXT_GAP;
284 NSBox *rule = [[NSBox alloc] initWithFrame:
285 NSMakeRect(210+(TEXT_GAP*3), 1, 1, [self contentHeight] + TEXT_GAP - 2)];
286 [rule setAutoresizingMask: (NSViewMaxXMargin|NSViewHeightSizable)];
287 [rule setBoxType: NSBoxSeparator];
288 [self addSubview: rule];
290 NSRect bindingFrame = NSMakeRect(210+(TEXT_GAP*4), 0,
291 frameRect.size.width - (210+(TEXT_GAP*4)), [self contentHeight] + TEXT_GAP );
292 NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame: bindingFrame];
293 key_bindings = [[ControllerKeyBindingView alloc] initWithFrame: bindingFrame ];
294 [scrollView setAutoresizingMask: (NSViewWidthSizable|NSViewHeightSizable)];
295 [scrollView setDocumentView: key_bindings];
296 [scrollView setDrawsBackground: NO];
297 [scrollView setHasVerticalScroller: YES];
298 [scrollView setAutohidesScrollers: YES];
300 [self addSubview: scrollView];
301 [key_bindings setDevice: maple_get_device(0,0)];
303 for( i=0; i<MAX_DEVICES; i++ ) {
305 save_controller[i] = NULL;
306 maple_device_t device = maple_get_device(i,0);
308 snprintf( buf, sizeof(buf), _("Slot %d."), i );
309 radio[i] = [[NSButton alloc] initWithFrame: NSMakeRect( x, y, 60, TEXT_HEIGHT )];
310 [radio[i] setTitle: [NSString stringWithUTF8String: buf]];
311 [radio[i] setTag: i];
312 [radio[i] setButtonType: NSRadioButton];
313 [radio[i] setAlignment: NSRightTextAlignment];
314 [radio[i] setTarget: self];
315 [radio[i] setAction: @selector(radioChanged:)];
316 [radio[i] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
317 [self addSubview: radio[i]];
320 NSPopUpButton *popup = [[NSPopUpButton alloc] initWithFrame: NSMakeRect(x,y,150,TEXT_HEIGHT)
322 [popup addItemWithTitle: NS_("<empty>")];
323 [popup setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
324 [[popup itemAtIndex: 0] setTag: 0];
325 for( j=0; devices[j] != NULL; j++ ) {
326 [popup addItemWithTitle: [NSString stringWithUTF8String: devices[j]->name]];
327 if( device != NULL && device->device_class == devices[j] ) {
328 [popup selectItemAtIndex: (j+1)];
330 [[popup itemAtIndex: (j+1)] setTag: (j+1)];
332 [popup setTarget: self];
333 [popup setAction: @selector(deviceChanged:)];
335 [self addSubview: popup];
336 y -= (TEXT_HEIGHT+TEXT_GAP);
339 [radio[0] setState: NSOnState];
343 - (void)radioChanged: (id)sender
345 int slot = [sender tag];
347 for( i=0; i<MAX_DEVICES; i++ ) {
349 [radio[i] setState: NSOffState];
352 [key_bindings setDevice: maple_get_device(slot,0)];
354 - (void)deviceChanged: (id)sender
356 int slot = [sender tag];
357 int new_device_idx = [sender indexOfSelectedItem] - 1, i;
358 maple_device_class_t new_device_class = NULL;
360 for( i=0; i<MAX_DEVICES; i++ ) {
362 [radio[i] setState: NSOnState];
364 [radio[i] setState: NSOffState];
368 maple_device_t current = maple_get_device(slot,0);
369 maple_device_t new_device = NULL;
370 if( new_device_idx != -1 ) {
371 new_device_class = maple_get_device_classes()[new_device_idx];
373 if( current == NULL ? new_device_class == NULL : current->device_class == new_device_class ) {
375 [key_bindings setDevice: current];
378 if( current != NULL && current->device_class == &controller_class ) {
379 save_controller[slot] = current->clone(current);
381 if( new_device_class == NULL ) {
382 maple_detach_device(slot,0);
384 if( new_device_class == &controller_class && save_controller[slot] != NULL ) {
385 new_device = save_controller[slot];
386 save_controller[slot] = NULL;
388 new_device = maple_new_device( new_device_class->name );
390 maple_attach_device(new_device,slot,0);
392 [key_bindings setDevice: maple_get_device(slot,0)];
393 lxdream_save_config();
397 NSView *cocoa_gui_create_prefs_controller_pane()
399 return [LxdreamPrefsControllerPane new];
.