filename | src/cocoaui/cocoa_ctrl.m |
changeset | 964:f2f3c7612d06 |
next | 1034:7044e01148f0 |
author | nkeynes |
date | Wed Jun 03 10:29:16 2009 +0000 (14 years ago) |
permissions | -rw-r--r-- |
last change | Allow multiple input hooks to be registered for the same key - useful to allow eg binding the same hotkey for run/stop so that it works as a toggle. |
view | annotate | diff | log | raw |
1 /**
2 * $Id$
3 *
4 * Construct and manage the controller configuration pane
5 *
6 * Copyright (c) 2008 Nathan Keynes.
7 *
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.
12 *
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.
17 */
19 #include "cocoaui.h"
20 #include "config.h"
21 #include "display.h"
22 #include "maple/maple.h"
24 #include <glib/gstrfuncs.h>
26 #define MAX_DEVICES 4
28 #define KEYBINDING_SIZE 110
30 static void cocoa_config_keysym_hook(void *data, const gchar *keysym);
32 @interface KeyBindingEditor (Private)
33 - (void)updateKeysym: (const gchar *)sym;
34 @end
36 @implementation KeyBindingEditor
37 - (id)init
38 {
39 self = [super init];
40 isPrimed = NO;
41 lastValue = nil;
42 [self setFieldEditor: YES];
43 [self setEditable: FALSE];
44 return self;
45 }
46 - (void)dealloc
47 {
48 if( lastValue != nil ) {
49 [lastValue release];
50 lastValue = nil;
51 }
52 [super dealloc];
53 }
54 - (void)setPrimed: (BOOL)primed
55 {
56 if( primed != isPrimed ) {
57 isPrimed = primed;
58 if( primed ) {
59 lastValue = [[NSString stringWithString: [self string]] retain];
60 [self setString: @"<press key>"];
61 input_set_keysym_hook(cocoa_config_keysym_hook, self);
62 } else {
63 [lastValue release];
64 lastValue = nil;
65 input_set_keysym_hook(NULL,NULL);
66 }
67 }
68 }
69 - (void)resignFirstResponder
70 {
71 if( isPrimed ) {
72 [self setString: lastValue];
73 [self setPrimed: NO];
74 }
75 [super resignFirstResponder];
76 }
77 - (void)fireBindingChanged
78 {
79 id delegate = [self delegate];
80 if( delegate != nil && [delegate respondsToSelector:@selector(textDidChange:)] ) {
81 [delegate textDidChange: [NSNotification notificationWithName: NSTextDidChangeNotification object: self]];
82 }
83 }
85 - (void)updateKeysym: (const gchar *)sym
86 {
87 if( sym != NULL ) {
88 [self setString: [NSString stringWithCString: sym]];
89 [self setPrimed: NO];
90 [self fireBindingChanged];
91 }
92 }
93 - (void)updateMousesym: (int)button
94 {
95 gchar *keysym = input_keycode_to_keysym( &system_mouse_driver, (button+1) );
96 if( keysym != NULL ) {
97 [self updateKeysym: keysym ];
98 g_free(keysym);
99 }
100 }
101 - (void)keyPressed: (int)keycode
102 {
103 gchar *keysym = input_keycode_to_keysym(NULL, keycode);
104 if( keysym != NULL ) {
105 [self updateKeysym: keysym];
106 g_free(keysym);
107 }
108 }
109 - (void)insertText:(id)string
110 {
111 // Do nothing
112 }
113 - (void)mouseDown: (NSEvent *)event
114 {
115 if( isPrimed ) {
116 [self updateMousesym: 0];
117 } else {
118 [self setPrimed: YES];
119 [super mouseDown: event];
120 }
121 }
122 - (void)rightMouseDown: (NSEvent *)event
123 {
124 if( isPrimed ) {
125 [self updateMousesym: 1];
126 }
127 }
128 - (void)otherMouseDown: (NSEvent *)event
129 {
130 if( isPrimed ) {
131 [self updateMousesym: [event buttonNumber]];
132 }
133 }
134 - (void)keyDown: (NSEvent *) event
135 {
136 NSString *chars = [event characters];
137 if( isPrimed ) {
138 if( chars != NULL && [chars length] == 1 && [chars characterAtIndex: 0] == 27 ) {
139 // Escape char = abort change
140 [self setString: lastValue];
141 [self setPrimed: NO];
142 } else {
143 [self keyPressed: ([event keyCode]+1)];
144 }
145 } else {
146 if( chars != NULL && [chars length] == 1 ) {
147 int ch = [chars characterAtIndex: 0];
148 switch( ch ) {
149 case 0x7F:
150 [self setString: @""];
151 [self fireBindingChanged];
152 break;
153 case '\r':
154 [self setPrimed: YES];
155 break;
156 default:
157 [super keyDown: event];
158 break;
159 }
160 } else {
161 [super keyDown: event];
162 }
163 }
164 }
165 - (void)flagsChanged: (NSEvent *) event
166 {
167 if( isPrimed ) {
168 [self keyPressed: ([event keyCode]+1)];
169 }
170 [super flagsChanged: event];
171 }
172 @end
174 static void cocoa_config_keysym_hook(void *data, const gchar *keysym)
175 {
176 KeyBindingEditor *editor = (KeyBindingEditor *)data;
177 [editor updateKeysym: keysym];
178 }
181 @implementation KeyBindingField
182 @end
184 /*************************** Key-binding sub-view ***********************/
186 #define MAX_KEY_BINDINGS 32
188 @interface ControllerKeyBindingView : NSView
189 {
190 maple_device_t device;
191 KeyBindingField *field[MAX_KEY_BINDINGS][2];
192 }
193 - (id)initWithFrame: (NSRect)frameRect;
194 - (void)setDevice: (maple_device_t)device;
195 @end
197 @implementation ControllerKeyBindingView
198 - (id)initWithFrame: (NSRect)frameRect
199 {
200 if( [super initWithFrame: frameRect] == nil ) {
201 return nil;
202 } else {
203 device = NULL;
204 return self;
205 }
206 }
207 - (BOOL)isFlipped
208 {
209 return YES;
210 }
211 - (void)removeSubviews
212 {
213 [[self subviews] makeObjectsPerformSelector: @selector(removeFromSuperview)];
214 }
215 - (void)controlTextDidChange: (NSNotification *)notify
216 {
217 int binding = [[notify object] tag];
218 NSString *val1 = [field[binding][0] stringValue];
219 NSString *val2 = [field[binding][1] stringValue];
220 char buf[ [val1 length] + [val2 length] + 2 ];
221 const gchar *p = NULL;
223 if( [val1 length] == 0 ) {
224 if( [val2 length] != 0 ) {
225 p = [val2 UTF8String];
226 }
227 } else if( [val2 length] == 0 ) {
228 p = [val1 UTF8String];
229 } else {
230 sprintf( buf, "%s,%s", [val1 UTF8String], [val2 UTF8String] );
231 p = buf;
232 }
233 maple_set_device_config_value( device, binding, p );
234 lxdream_save_config();
235 }
236 - (void)setDevice: (maple_device_t)newDevice
237 {
238 device = newDevice;
239 [self removeSubviews];
240 if( device != NULL ) {
241 lxdream_config_entry_t config = maple_get_device_config(device);
242 if( config != NULL ) {
243 int count, i, y, x;
245 for( count=0; config[count].key != NULL; count++ );
246 x = TEXT_GAP;
247 NSSize size = NSMakeSize(85+KEYBINDING_SIZE*2+TEXT_GAP*4, count*(TEXT_HEIGHT+TEXT_GAP)+TEXT_GAP);
248 [self setFrameSize: size];
249 [self scrollRectToVisible: NSMakeRect(0,0,1,1)];
250 y = TEXT_GAP;
251 for( i=0; config[i].key != NULL; i++ ) {
252 NSRect frame = NSMakeRect(x, y + 2, 85, LABEL_HEIGHT);
253 NSTextField *label = cocoa_gui_add_label(self, NS_(config[i].label), frame);
254 [label setAlignment: NSRightTextAlignment];
256 frame = NSMakeRect( x + 85 + TEXT_GAP, y, KEYBINDING_SIZE, TEXT_HEIGHT);
257 field[i][0] = [[KeyBindingField alloc] initWithFrame: frame];
258 [field[i][0] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
259 [field[i][0] setTag: i];
260 [field[i][0] setDelegate: self];
261 [self addSubview: field[i][0]];
263 frame = NSMakeRect( x + 85 + KEYBINDING_SIZE + (TEXT_GAP*2), y, KEYBINDING_SIZE, TEXT_HEIGHT);
264 field[i][1] = [[KeyBindingField alloc] initWithFrame: frame];
265 [field[i][1] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
266 [field[i][1] setTag: i];
267 [field[i][1] setDelegate: self];
268 [self addSubview: field[i][1]];
270 if( config[i].value != NULL ) {
271 gchar **parts = g_strsplit(config[i].value,",",3);
272 if( parts[0] != NULL ) {
273 [field[i][0] setStringValue: [NSString stringWithCString: parts[0]]];
274 if( parts[1] != NULL ) {
275 [field[i][1] setStringValue: [NSString stringWithCString: parts[1]]];
276 }
277 }
278 g_strfreev(parts);
279 }
281 y += (TEXT_HEIGHT + TEXT_GAP);
282 }
283 } else {
284 [self setFrameSize: NSMakeSize(100,TEXT_HEIGHT+TEXT_GAP) ];
285 }
286 } else {
287 [self setFrameSize: NSMakeSize(100,TEXT_HEIGHT+TEXT_GAP) ];
288 }
289 }
290 @end
292 /*************************** Top-level controller pane ***********************/
294 @interface LxdreamPrefsControllerPane: LxdreamPrefsPane
295 {
296 struct maple_device *save_controller[4];
297 NSButton *radio[4];
298 ControllerKeyBindingView *key_bindings;
299 }
300 + (LxdreamPrefsControllerPane *)new;
301 @end
303 @implementation LxdreamPrefsControllerPane
304 + (LxdreamPrefsControllerPane *)new
305 {
306 return [[LxdreamPrefsControllerPane alloc] initWithFrame: NSMakeRect(0,0,600,400)];
307 }
308 - (id)initWithFrame: (NSRect)frameRect
309 {
310 if( [super initWithFrame: frameRect title: NS_("Controllers")] == nil ) {
311 return nil;
312 } else {
313 const struct maple_device_class **devices = maple_get_device_classes();
314 char buf[16];
315 int i,j;
316 int y = [self contentHeight] - TEXT_HEIGHT - TEXT_GAP;
318 NSBox *rule = [[NSBox alloc] initWithFrame:
319 NSMakeRect(210+(TEXT_GAP*3), 1, 1, [self contentHeight] + TEXT_GAP - 2)];
320 [rule setAutoresizingMask: (NSViewMaxXMargin|NSViewHeightSizable)];
321 [rule setBoxType: NSBoxSeparator];
322 [self addSubview: rule];
324 NSRect bindingFrame = NSMakeRect(210+(TEXT_GAP*4), 0,
325 frameRect.size.width - (210+(TEXT_GAP*4)), [self contentHeight] + TEXT_GAP );
326 NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame: bindingFrame];
327 key_bindings = [[ControllerKeyBindingView alloc] initWithFrame: bindingFrame ];
328 [scrollView setAutoresizingMask: (NSViewWidthSizable|NSViewHeightSizable)];
329 [scrollView setDocumentView: key_bindings];
330 [scrollView setDrawsBackground: NO];
331 [scrollView setHasVerticalScroller: YES];
332 [scrollView setAutohidesScrollers: YES];
334 [self addSubview: scrollView];
335 [key_bindings setDevice: maple_get_device(0,0)];
337 for( i=0; i<MAX_DEVICES; i++ ) {
338 int x = TEXT_GAP;
339 save_controller[i] = NULL;
340 maple_device_t device = maple_get_device(i,0);
342 snprintf( buf, sizeof(buf), _("Slot %d."), i );
343 radio[i] = [[NSButton alloc] initWithFrame: NSMakeRect( x, y, 60, TEXT_HEIGHT )];
344 [radio[i] setTitle: [NSString stringWithUTF8String: buf]];
345 [radio[i] setTag: i];
346 [radio[i] setButtonType: NSRadioButton];
347 [radio[i] setAlignment: NSRightTextAlignment];
348 [radio[i] setTarget: self];
349 [radio[i] setAction: @selector(radioChanged:)];
350 [radio[i] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
351 [self addSubview: radio[i]];
352 x += 60 + TEXT_GAP;
354 NSPopUpButton *popup = [[NSPopUpButton alloc] initWithFrame: NSMakeRect(x,y,150,TEXT_HEIGHT)
355 pullsDown: NO];
356 [popup addItemWithTitle: NS_("<empty>")];
357 [popup setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
358 [[popup itemAtIndex: 0] setTag: 0];
359 for( j=0; devices[j] != NULL; j++ ) {
360 [popup addItemWithTitle: [NSString stringWithUTF8String: devices[j]->name]];
361 if( device != NULL && device->device_class == devices[j] ) {
362 [popup selectItemAtIndex: (j+1)];
363 }
364 [[popup itemAtIndex: (j+1)] setTag: (j+1)];
365 }
366 [popup setTarget: self];
367 [popup setAction: @selector(deviceChanged:)];
368 [popup setTag: i];
369 [self addSubview: popup];
370 y -= (TEXT_HEIGHT+TEXT_GAP);
371 }
373 [radio[0] setState: NSOnState];
374 return self;
375 }
376 }
377 - (void)radioChanged: (id)sender
378 {
379 int slot = [sender tag];
380 int i;
381 for( i=0; i<MAX_DEVICES; i++ ) {
382 if( i != slot ) {
383 [radio[i] setState: NSOffState];
384 }
385 }
386 [key_bindings setDevice: maple_get_device(slot,0)];
387 }
388 - (void)deviceChanged: (id)sender
389 {
390 int slot = [sender tag];
391 int new_device_idx = [sender indexOfSelectedItem] - 1, i;
392 maple_device_class_t new_device_class = NULL;
394 for( i=0; i<MAX_DEVICES; i++ ) {
395 if( i == slot ) {
396 [radio[i] setState: NSOnState];
397 } else {
398 [radio[i] setState: NSOffState];
399 }
400 }
402 maple_device_t current = maple_get_device(slot,0);
403 maple_device_t new_device = NULL;
404 if( new_device_idx != -1 ) {
405 new_device_class = maple_get_device_classes()[new_device_idx];
406 }
407 if( current == NULL ? new_device_class == NULL : current->device_class == new_device_class ) {
408 // No change
409 [key_bindings setDevice: current];
410 return;
411 }
412 if( current != NULL && current->device_class == &controller_class ) {
413 save_controller[slot] = current->clone(current);
414 }
415 if( new_device_class == NULL ) {
416 maple_detach_device(slot,0);
417 } else {
418 if( new_device_class == &controller_class && save_controller[slot] != NULL ) {
419 new_device = save_controller[slot];
420 save_controller[slot] = NULL;
421 } else {
422 new_device = maple_new_device( new_device_class->name );
423 }
424 maple_attach_device(new_device,slot,0);
425 }
426 [key_bindings setDevice: maple_get_device(slot,0)];
427 lxdream_save_config();
428 }
429 @end
431 NSView *cocoa_gui_create_prefs_controller_pane()
432 {
433 return [LxdreamPrefsControllerPane new];
434 }
.