filename | src/cocoaui/cocoa_ctrl.m |
changeset | 1072:d82e04e6d497 |
prev | 1069:7e2b65496762 |
next | 1297:7e98a164b2d9 |
author | nkeynes |
date | Fri Sep 10 08:50:55 2010 +1000 (13 years ago) |
permissions | -rw-r--r-- |
last change | Add missing sh4_translate_breakpoint_hit to the symbol table Change asm() to __asm__() as it's more likely to work |
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 "lxpaths.h"
22 #include "display.h"
23 #include "maple/maple.h"
24 #include "vmu/vmulist.h"
26 #include <glib/gstrfuncs.h>
28 #define FIRST_SECONDARY_DEVICE MAPLE_PORTS
30 #define FIRST_VMU_TAG 0x1000
31 #define LOAD_VMU_TAG -1
32 #define CREATE_VMU_TAG -2
34 #define LABEL_WIDTH 85
37 static gboolean cocoa_config_vmulist_hook(vmulist_change_type_t type, int idx, void *data);
38 /*************************** Top-level controller pane ***********************/
39 static NSButton *addRadioButton( int port, int sub, int x, int y, id parent )
40 {
41 char buf[16];
43 if( sub == 0 ) {
44 snprintf( buf, sizeof(buf), _("Port %c."), 'A'+port );
45 } else {
46 snprintf( buf, sizeof(buf), _("VMU %d."), sub );
47 }
49 NSButton *radio = [[NSButton alloc] initWithFrame: NSMakeRect( x, y, 60, TEXT_HEIGHT )];
50 [radio setTitle: [NSString stringWithUTF8String: buf]];
51 [radio setTag: MAPLE_DEVID(port,sub) ];
52 [radio setButtonType: NSRadioButton];
53 [radio setAlignment: NSRightTextAlignment];
54 [radio setTarget: parent];
55 [radio setAction: @selector(radioChanged:)];
56 [radio setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
57 [parent addSubview: radio];
58 return radio;
59 }
61 static void setDevicePopupSelection( NSPopUpButton *popup, maple_device_t device )
62 {
63 if( device == NULL ) {
64 [popup selectItemAtIndex: 0];
65 } else if( MAPLE_IS_VMU(device) ) {
66 int idx = vmulist_get_index_by_filename( MAPLE_VMU_NAME(device) );
67 if( idx == -1 ) {
68 [popup selectItemAtIndex: 0];
69 } else {
70 [popup selectItemWithTag: FIRST_VMU_TAG + idx];
71 }
72 } else {
73 const struct maple_device_class **devices = maple_get_device_classes();
74 int i;
75 for( i=0; devices[i] != NULL; i++ ) {
76 if( devices[i] == device->device_class ) {
77 [popup selectItemWithTag: i+1];
78 return;
79 }
80 }
81 // Should never get here, but if so...
82 [popup selectItemAtIndex: 0];
83 }
84 }
86 static void buildDevicePopupMenu( NSPopUpButton *popup, maple_device_t device, BOOL primary )
87 {
88 int j;
89 const struct maple_device_class **devices = maple_get_device_classes();
91 [popup removeAllItems];
92 [popup addItemWithTitle: NS_("<empty>")];
93 [[popup itemAtIndex: 0] setTag: 0];
94 for( j=0; devices[j] != NULL; j++ ) {
95 int isPrimaryDevice = devices[j]->flags & MAPLE_TYPE_PRIMARY;
96 if( primary ? isPrimaryDevice : (!isPrimaryDevice && !MAPLE_IS_VMU_CLASS(devices[j])) ) {
97 [popup addItemWithTitle: [NSString stringWithUTF8String: devices[j]->name]];
98 if( device != NULL && device->device_class == devices[j] ) {
99 [popup selectItemAtIndex: ([popup numberOfItems]-1)];
100 }
101 [[popup itemAtIndex: ([popup numberOfItems]-1)] setTag: (j+1)];
102 }
103 }
105 if( !primary ) {
106 BOOL vmu_selected = NO;
107 const char *vmu_name = NULL;
108 if( device != NULL && MAPLE_IS_VMU(device) ) {
109 vmu_name = MAPLE_VMU_NAME(device);
110 if( vmu_name == NULL ) {
111 device = NULL;
112 } else {
113 vmu_selected = YES;
114 }
115 }
116 if( [popup numberOfItems] > 0 ) {
117 [[popup menu] addItem: [NSMenuItem separatorItem]];
118 }
120 unsigned int vmu_count = vmulist_get_size();
121 for( j=0; j<vmu_count; j++ ) {
122 const char *name = vmulist_get_name(j);
123 [popup addItemWithTitle: [NSString stringWithUTF8String: name]];
124 if( vmu_selected && strcmp(vmu_name, vmulist_get_filename(j)) == 0 ) {
125 [popup selectItemAtIndex: ([popup numberOfItems]-1)];
126 }
127 [[popup itemAtIndex: ([popup numberOfItems]-1)] setTag: FIRST_VMU_TAG + j];
128 }
130 [popup addItemWithTitle: NS_("Load VMU...")];
131 [[popup itemAtIndex: ([popup numberOfItems]-1)] setTag: LOAD_VMU_TAG];
132 [popup addItemWithTitle: NS_("Create VMU...")];
133 [[popup itemAtIndex: ([popup numberOfItems]-1)] setTag: CREATE_VMU_TAG];
134 }
136 }
138 static NSPopUpButton *addDevicePopup( int port, int sub, int x, int y, maple_device_t device, BOOL primary, id parent )
139 {
140 NSPopUpButton *popup = [[NSPopUpButton alloc] initWithFrame: NSMakeRect(x,y,150,TEXT_HEIGHT)
141 pullsDown: NO];
142 [popup setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
143 buildDevicePopupMenu(popup,device,primary);
145 [popup setTarget: parent];
146 [popup setAction: @selector(deviceChanged:)];
147 [popup setTag: MAPLE_DEVID(port,sub) ];
148 [parent addSubview: popup];
149 return popup;
150 }
152 @interface VMULoadValidator : NSObject
153 {
154 }
155 - (BOOL)panel:(id) sender isValidFilename: (NSString *)filename;
156 @end
158 @implementation VMULoadValidator
159 - (BOOL)panel:(id) sender isValidFilename: (NSString *)filename
160 {
161 const char *c_fn = [filename UTF8String];
162 vmu_volume_t vol = vmu_volume_load( c_fn );
163 if( vol != NULL ) {
164 vmulist_add_vmu(c_fn, vol);
165 return YES;
166 } else {
167 ERROR( "Unable to load VMU file (not a valid VMU)" );
168 return NO;
169 }
170 }
172 @end
174 @interface VMUCreateValidator : NSObject
175 {
176 }
177 - (BOOL)panel:(id) sender isValidFilename: (NSString *)filename;
178 @end
180 @implementation VMUCreateValidator
181 - (BOOL)panel:(id) sender isValidFilename: (NSString *)filename
182 {
183 const char *vmu_filename = [filename UTF8String];
184 int idx = vmulist_create_vmu(vmu_filename, FALSE);
185 if( idx == -1 ) {
186 ERROR( "Unable to create file: %s\n", strerror(errno) );
187 return NO;
188 } else {
189 return YES;
190 }
191 }
192 @end
195 @interface LxdreamPrefsControllerPane: LxdreamPrefsPane
196 {
197 struct maple_device *save_controller[MAPLE_MAX_DEVICES];
198 NSButton *radio[MAPLE_MAX_DEVICES];
199 NSPopUpButton *popup[MAPLE_MAX_DEVICES];
200 ConfigurationView *key_bindings;
201 }
202 + (LxdreamPrefsControllerPane *)new;
203 - (void)vmulistChanged: (id)sender;
204 @end
206 static gboolean cocoa_config_vmulist_hook(vmulist_change_type_t type, int idx, void *data)
207 {
208 LxdreamPrefsControllerPane *pane = (LxdreamPrefsControllerPane *)data;
209 [pane vmulistChanged: nil];
210 return TRUE;
211 }
213 @implementation LxdreamPrefsControllerPane
214 + (LxdreamPrefsControllerPane *)new
215 {
216 return [[LxdreamPrefsControllerPane alloc] initWithFrame: NSMakeRect(0,0,600,400)];
217 }
218 - (id)initWithFrame: (NSRect)frameRect
219 {
220 if( [super initWithFrame: frameRect title: NS_("Controllers")] == nil ) {
221 return nil;
222 } else {
223 int i,j;
224 int y = [self contentHeight] - TEXT_HEIGHT - TEXT_GAP;
226 memset( radio, 0, sizeof(radio) );
227 memset( save_controller, 0, sizeof(save_controller) );
228 NSBox *rule = [[NSBox alloc] initWithFrame:
229 NSMakeRect(210+(TEXT_GAP*3), 1, 1, [self contentHeight] + TEXT_GAP - 2)];
230 [rule setAutoresizingMask: (NSViewMaxXMargin|NSViewHeightSizable)];
231 [rule setBoxType: NSBoxSeparator];
232 [self addSubview: rule];
234 NSRect bindingFrame = NSMakeRect(210+(TEXT_GAP*4), 0,
235 frameRect.size.width - (210+(TEXT_GAP*4)), [self contentHeight] + TEXT_GAP );
236 NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame: bindingFrame];
237 key_bindings = [[ConfigurationView alloc] initWithFrame: bindingFrame ];
238 [key_bindings setLabelWidth: LABEL_WIDTH];
239 [scrollView setAutoresizingMask: (NSViewWidthSizable|NSViewHeightSizable)];
240 [scrollView setDocumentView: key_bindings];
241 [scrollView setDrawsBackground: NO];
242 [scrollView setHasVerticalScroller: YES];
243 [scrollView setAutohidesScrollers: YES];
245 [self addSubview: scrollView];
246 [key_bindings setDevice: maple_get_device(0,0)];
248 for( i=0; i<MAPLE_PORTS; i++ ) {
249 maple_device_t device = maple_get_device(i,0);
251 radio[i] = addRadioButton(i,0,TEXT_GAP,y,self);
252 popup[i] = addDevicePopup(i,0,60 + (TEXT_GAP*2),y,device, YES,self);
253 y -= (TEXT_HEIGHT+TEXT_GAP);
255 int j,max = device == NULL ? 0 : MAPLE_SLOTS(device->device_class);
256 for( j=1; j<=MAPLE_USER_SLOTS; j++ ) {
257 radio[MAPLE_DEVID(i,j)] = addRadioButton(i, j, TEXT_GAP*2, y, self);
258 popup[MAPLE_DEVID(i,j)] = addDevicePopup(i, j, 60 + TEXT_GAP*2, y, maple_get_device(i,j), NO, self);
259 y -= (TEXT_HEIGHT+TEXT_GAP);
260 if( j > max ) {
261 [radio[MAPLE_DEVID(i,j)] setEnabled: NO];
262 [popup[MAPLE_DEVID(i,j)] setEnabled: NO];
263 }
264 }
265 }
267 [radio[0] setState: NSOnState];
269 register_vmulist_change_hook(cocoa_config_vmulist_hook, self);
270 return self;
271 }
272 }
273 - (void)dealloc
274 {
275 unregister_vmulist_change_hook(cocoa_config_vmulist_hook,self);
276 [super dealloc];
277 }
278 - (void)vmulistChanged: (id)sender
279 {
280 int i;
281 for( i=FIRST_SECONDARY_DEVICE; i<MAPLE_MAX_DEVICES; i++ ) {
282 if( popup[i] != NULL ) {
283 buildDevicePopupMenu(popup[i], maple_get_device(MAPLE_DEVID_PORT(i), MAPLE_DEVID_SLOT(i)), NO );
284 }
285 }
286 }
287 - (void)radioChanged: (id)sender
288 {
289 int tag = [sender tag];
290 int i;
291 for( i=0; i<MAPLE_MAX_DEVICES; i++ ) {
292 if( i != tag && radio[i] != NULL ) {
293 [radio[i] setState: NSOffState];
294 }
295 }
296 [key_bindings setDevice: maple_get_device(MAPLE_DEVID_PORT(tag),MAPLE_DEVID_SLOT(tag))];
297 }
298 - (void)deviceChanged: (id)sender
299 {
300 int tag = [sender tag];
301 int port = MAPLE_DEVID_PORT(tag);
302 int slot = MAPLE_DEVID_SLOT(tag);
303 int new_device_idx = [[sender selectedItem] tag], i;
304 maple_device_class_t new_device_class = NULL;
305 const gchar *vmu_filename = NULL;
307 for( i=0; i<MAPLE_MAX_DEVICES; i++ ) {
308 if( radio[i] != NULL ) {
309 if( i == tag ) {
310 [radio[i] setState: NSOnState];
311 } else {
312 [radio[i] setState: NSOffState];
313 }
314 }
315 }
317 maple_device_t current = maple_get_device(port,slot);
318 maple_device_t new_device = NULL;
319 if( new_device_idx == LOAD_VMU_TAG ) {
320 NSArray *array = [NSArray arrayWithObjects: @"vmu", nil];
321 NSOpenPanel *panel = [NSOpenPanel openPanel];
322 VMULoadValidator *valid = [[VMULoadValidator alloc] autorelease];
323 [panel setDelegate: valid];
324 int result = [panel runModalForDirectory: [NSString stringWithUTF8String: get_gui_path(CONFIG_VMU_PATH)]
325 file: nil types: array];
326 if( result == NSOKButton ) {
327 vmu_filename = [[panel filename] UTF8String];
328 int idx = vmulist_get_index_by_filename(vmu_filename);
329 [sender selectItemWithTag: (FIRST_VMU_TAG+idx)];
330 new_device_class = &vmu_class;
331 set_gui_path(CONFIG_VMU_PATH, [[panel directory] UTF8String]);
332 } else {
333 /* Cancelled - restore previous value */
334 setDevicePopupSelection( sender, current );
335 return;
336 }
337 } else if( new_device_idx == CREATE_VMU_TAG ) {
338 NSSavePanel *panel = [NSSavePanel savePanel];
339 [panel setTitle: NS_("Create VMU")];
340 [panel setCanCreateDirectories: YES];
341 [panel setRequiredFileType: @"vmu"];
342 VMUCreateValidator *valid = [[VMUCreateValidator alloc] autorelease];
343 [panel setDelegate: valid];
344 int result = [panel runModalForDirectory: [NSString stringWithUTF8String: get_gui_path(CONFIG_VMU_PATH)]
345 file: nil];
346 if( result == NSFileHandlingPanelOKButton ) {
347 /* Validator has already created the file by now */
348 vmu_filename = [[panel filename] UTF8String];
349 int idx = vmulist_get_index_by_filename(vmu_filename);
350 [sender selectItemWithTag: (FIRST_VMU_TAG+idx)];
351 new_device_class = &vmu_class;
352 set_gui_path(CONFIG_VMU_PATH, [[panel directory] UTF8String]);
353 } else {
354 setDevicePopupSelection( sender, current );
355 return;
356 }
357 } else if( new_device_idx >= FIRST_VMU_TAG ) {
358 vmu_filename = vmulist_get_filename( new_device_idx - FIRST_VMU_TAG );
359 new_device_class = &vmu_class;
360 } else if( new_device_idx > 0) {
361 new_device_class = maple_get_device_classes()[new_device_idx-1];
362 }
364 if( current == NULL ? new_device_class == NULL :
365 (current->device_class == new_device_class &&
366 (!MAPLE_IS_VMU(current) || MAPLE_VMU_HAS_NAME(current, vmu_filename))) ) {
367 // No change
368 [key_bindings setDevice: current];
369 return;
370 }
371 if( current != NULL && current->device_class == &controller_class ) {
372 save_controller[tag] = current->clone(current);
373 }
374 if( new_device_class == NULL ) {
375 maple_detach_device(port,slot);
376 if( slot == 0 ) {
377 /* If we detached the top-level dev, any children are automatically detached */
378 for( i=1; i<=MAPLE_USER_SLOTS; i++ ) {
379 [popup[MAPLE_DEVID(port,i)] selectItemWithTag: 0];
380 }
381 }
382 } else {
383 if( new_device_class == &controller_class && save_controller[tag] != NULL ) {
384 new_device = save_controller[tag];
385 save_controller[tag] = NULL;
386 } else {
387 new_device = maple_new_device( new_device_class->name );
388 }
389 if( MAPLE_IS_VMU(new_device) ) {
390 /* Remove the VMU from any other attachment point */
391 for( i=0; i<MAPLE_MAX_DEVICES; i++ ) {
392 maple_device_t dev = maple_get_device(MAPLE_DEVID_PORT(i),MAPLE_DEVID_SLOT(i));
393 if( dev != NULL && MAPLE_IS_VMU(dev) && MAPLE_VMU_HAS_NAME(dev,vmu_filename) ) {
394 maple_detach_device(MAPLE_DEVID_PORT(i),MAPLE_DEVID_SLOT(i));
395 [popup[i] selectItemWithTag: 0];
396 }
397 }
398 MAPLE_SET_VMU_NAME(new_device,vmu_filename);
399 }
400 maple_attach_device(new_device,port,slot);
401 }
402 [key_bindings setDevice: maple_get_device(port,slot)];
404 if( slot == 0 ) { /* Change primary */
405 int max = new_device_class == NULL ? 0 : MAPLE_SLOTS(new_device_class);
406 for( i=1; i<=MAPLE_USER_SLOTS; i++ ) {
407 if( i <= max ) {
408 [radio[MAPLE_DEVID(port,i)] setEnabled: YES];
409 [popup[MAPLE_DEVID(port,i)] setEnabled: YES];
410 } else {
411 [radio[MAPLE_DEVID(port,i)] setEnabled: NO];
412 [popup[MAPLE_DEVID(port,i)] setEnabled: NO];
413 }
414 }
415 }
416 lxdream_save_config();
417 }
418 @end
420 NSView *cocoa_gui_create_prefs_controller_pane()
421 {
422 return [LxdreamPrefsControllerPane new];
423 }
.