filename | src/gtkui/gtk_ctrl.c |
changeset | 1072:d82e04e6d497 |
prev | 1065:bc1cc0c54917 |
next | 1124:aacaae9812ea |
author | nkeynes |
date | Mon Feb 15 17:27:14 2010 +1000 (14 years ago) |
permissions | -rw-r--r-- |
last change | Hook up the fake bios boot Use fakebios if invoked with -b, or if there's no boot rom loaded |
view | annotate | diff | log | raw |
1 /**
2 * $Id$
3 *
4 * Define the main (emu) GTK window, along with its menubars,
5 * toolbars, etc.
6 *
7 * Copyright (c) 2005 Nathan Keynes.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 */
20 #include <assert.h>
21 #include <string.h>
22 #include <errno.h>
23 #include <gtk/gtk.h>
24 #include <gdk/gdkkeysyms.h>
26 #include "lxdream.h"
27 #include "display.h"
28 #include "gtkui/gtkui.h"
29 #include "maple/maple.h"
30 #include "vmu/vmulist.h"
32 #define MAX_DEVICES 4
34 #define LOAD_VMU_TAG ((void *)-1)
35 #define CREATE_VMU_TAG ((void *)-2)
37 static void maple_set_device_selection( GtkWidget *combo, maple_device_t device );
39 struct maple_config_class {
40 const char *name;
41 void (*config_func)(maple_device_t device);
42 };
44 typedef struct maple_slot_data {
45 maple_device_t old_device;
46 maple_device_t new_device;
47 GtkWidget *button;
48 GtkWidget *combo;
49 gboolean primarySlot;
50 } *maple_slot_data_t;
53 static struct maple_slot_data maple_data[MAPLE_MAX_DEVICES];
55 /**
56 * Flag set when changing the selection on one of the combo boxes manually -
57 * avoids the followup changed event.
58 */
59 static gboolean maple_device_adjusting = FALSE;
61 static gboolean maple_properties_activated( GtkButton *button, gpointer user_data )
62 {
63 maple_slot_data_t data = (maple_slot_data_t)user_data;
64 if( data->new_device != NULL ) {
65 int i;
66 lxdream_config_group_t config = data->new_device->get_config(data->new_device);
67 if( config != NULL ) {
69 if( data->new_device == data->old_device ) {
70 // Make a copy at this point if we haven't already
71 data->new_device = data->old_device->clone(data->old_device);
72 }
74 gtk_configuration_panel_run(_("Controller Configuration"), config);
75 }
76 }
77 return TRUE;
78 }
80 static gboolean maple_device_changed( GtkComboBox *combo, gpointer user_data )
81 {
82 if( maple_device_adjusting ) {
83 return TRUE;
84 }
86 maple_slot_data_t data = (maple_slot_data_t)user_data;
87 int active = gtk_combo_box_get_active(combo), i;
88 gboolean has_config = FALSE;
89 gboolean set_selection = FALSE;
90 int has_slots = 0;
91 if( active != 0 ) {
92 GtkTreeIter iter;
93 maple_device_class_t devclz;
94 const gchar *vmu_filename;
96 GtkTreeModel *model = gtk_combo_box_get_model(combo);
97 gtk_combo_box_get_active_iter(combo, &iter);
98 gtk_tree_model_get(model, &iter, 1, &devclz, 2, &vmu_filename, -1 );
100 if( devclz == LOAD_VMU_TAG ) {
101 devclz = NULL;
102 vmu_filename = open_file_dialog( _("Load VMU"), "*.vmu", "VMU Files",
103 CONFIG_VMU_PATH );
104 if( vmu_filename != NULL ) {
105 vmu_volume_t vol = vmu_volume_load( vmu_filename );
106 if( vol != NULL ) {
107 devclz = &vmu_class;
108 vmulist_add_vmu(vmu_filename, vol);
109 set_selection = TRUE;
110 } else {
111 ERROR( "Unable to load VMU file (not a valid VMU)" );
112 }
113 }
114 } else if( devclz == CREATE_VMU_TAG ) {
115 devclz = NULL;
116 vmu_filename = save_file_dialog( _("Create VMU"), "*.vmu", "VMU Files",
117 CONFIG_VMU_PATH );
118 if( vmu_filename != NULL ) {
119 devclz = &vmu_class;
120 set_selection = TRUE;
121 int idx = vmulist_create_vmu( vmu_filename, FALSE );
122 if( idx == -1 ) {
123 ERROR( "Unable to save VMU file: %s", strerror(errno) );
124 }
125 }
126 } else if( vmu_filename != NULL ) {
127 devclz = &vmu_class;
128 }
130 if( devclz == NULL ) {
131 maple_set_device_selection(data->combo, data->new_device);
132 return TRUE;
133 }
135 if( data->new_device != NULL ) {
136 if( data->new_device->device_class != devclz ) {
137 if( data->new_device != data->old_device ) {
138 data->new_device->destroy(data->new_device);
139 }
140 data->new_device = devclz->new_device();
141 }
142 } else {
143 data->new_device = devclz->new_device();
144 }
145 has_config = data->new_device != NULL && data->new_device->get_config != NULL && !MAPLE_IS_VMU(data->new_device);
146 has_slots = data->new_device == NULL ? 0 : MAPLE_SLOTS(devclz);
147 if( MAPLE_IS_VMU(data->new_device) ) {
148 for( i=0; i<MAPLE_MAX_DEVICES; i++ ) {
149 if( maple_data[i].new_device != NULL && MAPLE_IS_VMU(maple_data[i].new_device) &&
150 MAPLE_VMU_HAS_NAME(maple_data[i].new_device, vmu_filename) ) {
151 maple_data[i].new_device->destroy(maple_data[i].new_device);
152 maple_data[i].new_device = NULL;
153 gtk_combo_box_set_active(GTK_COMBO_BOX(maple_data[i].combo),0);
154 }
155 }
156 MAPLE_SET_VMU_NAME(data->new_device,vmu_filename);
157 }
159 if( set_selection ) {
160 maple_set_device_selection(data->combo, data->new_device);
161 }
162 } else {
163 if( data->new_device != NULL && data->new_device != data->old_device ) {
164 data->new_device->destroy(data->new_device);
165 }
166 data->new_device = NULL;
167 }
168 gtk_widget_set_sensitive(data->button, has_config);
170 if( data->primarySlot ) {
171 for( i=0; i<MAPLE_USER_SLOTS; i++ ) {
172 /* This is a little morally dubious... */
173 maple_slot_data_t subdata = data + MAPLE_DEVID(0,(i+1));
174 gtk_widget_set_sensitive(subdata->combo, i < has_slots );
175 gtk_widget_set_sensitive(subdata->button, i < has_slots && subdata->new_device != NULL && subdata->new_device->get_config != NULL && !MAPLE_IS_VMU(subdata->new_device) );
176 }
177 }
178 return TRUE;
179 }
181 static void maple_build_device_model( GtkListStore *dev_model )
182 {
183 const struct maple_device_class **devices = maple_get_device_classes();
184 int i;
186 gtk_list_store_clear(dev_model);
187 gtk_list_store_insert_with_values( dev_model, NULL, 0, 0, _("<empty>"), 1, NULL, 2, NULL, -1 );
188 for( i=0; devices[i] != NULL; i++ ) {
189 if( devices[i]->flags & MAPLE_TYPE_PRIMARY ) {
190 gtk_list_store_insert_with_values( dev_model, NULL, i+1, 0, devices[i]->name, 1, devices[i], 2, NULL, -1 );
191 }
192 }
194 }
196 /**
197 * (Re)build the subdevice combo-box model.
198 */
199 static void maple_build_subdevice_model( GtkListStore *subdev_model )
200 {
201 int i, j;
202 const struct maple_device_class **devices = maple_get_device_classes();
204 gtk_list_store_clear(subdev_model);
205 gtk_list_store_insert_with_values( subdev_model, NULL, 0, 0, _("<empty>"), 1, NULL, 2, NULL, -1 );
206 for( i=0; devices[i] != NULL; i++ ) {
207 if( !(devices[i]->flags & MAPLE_TYPE_PRIMARY) && !MAPLE_IS_VMU_CLASS(devices[i]) ) {
208 gtk_list_store_insert_with_values( subdev_model, NULL, i+1, 0, devices[i]->name, 1, devices[i], 2, NULL, -1 );
209 }
210 }
211 for( j=0; j < vmulist_get_size(); j++ ) {
212 gtk_list_store_insert_with_values( subdev_model, NULL, i+j+1, 0, vmulist_get_name(j), 1, NULL, 2, vmulist_get_filename(j), -1 );
213 }
214 gtk_list_store_insert_with_values( subdev_model, NULL, i+j+1, 0, _("Load VMU..."), 1, LOAD_VMU_TAG, 2, NULL, -1 );
215 gtk_list_store_insert_with_values( subdev_model, NULL, i+j+2, 0, _("Create VMU..."), 1, CREATE_VMU_TAG, 2, NULL, -1 );
216 }
218 static gboolean maple_vmulist_changed( vmulist_change_type_t type, int idx, void *data )
219 {
220 GtkListStore *list = (GtkListStore *)data;
221 GtkTreeIter iter;
222 int i,j;
224 /* Search for the row and update accordingly. There's probably better ways
225 * to do this
226 */
228 gboolean valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list), &iter);
229 while( valid ) {
230 gchar *vmu_filename;
231 gpointer devclz;
232 gtk_tree_model_get(GTK_TREE_MODEL(list), &iter, 1, &devclz, 2, &vmu_filename, -1 );
233 if( vmu_filename != NULL || devclz == LOAD_VMU_TAG || devclz == CREATE_VMU_TAG )
234 break;
235 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(list), &iter);
236 }
237 if( valid ) {
238 for( i=0; i<idx && valid; i++ ) {
239 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(list), &iter);
240 }
241 }
242 if( valid ) {
243 if( type == VMU_ADDED ) {
244 GtkTreeIter newiter;
245 gtk_list_store_insert_before(list, &newiter, &iter);
246 gtk_list_store_set(list, &newiter, 0, vmulist_get_name(idx), 1, NULL, 2, vmulist_get_filename(idx), -1 );
247 } else if( type == VMU_REMOVED ) {
248 gtk_list_store_remove(list, &iter );
249 }
250 }
251 return TRUE;
252 }
254 /**
255 * Set the device popup selection based on the device (works for both primary
256 * and secondary devices)
257 */
258 static void maple_set_device_selection( GtkWidget *combo, maple_device_t device )
259 {
260 GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
261 GtkTreeIter iter;
263 maple_device_adjusting = TRUE;
264 if( device == NULL ) {
265 gtk_combo_box_set_active( GTK_COMBO_BOX(combo), 0 );
266 } else {
267 gboolean valid = gtk_tree_model_get_iter_first(model, &iter);
268 while( valid ) {
269 const struct maple_device_class *clz;
270 const gchar *vmu_filename;
272 gtk_tree_model_get(model, &iter, 1, &clz, 2, &vmu_filename, -1 );
274 if( device->device_class == clz ) {
275 gtk_combo_box_set_active_iter( GTK_COMBO_BOX(combo), &iter );
276 break;
277 } else if( vmu_filename != NULL && MAPLE_IS_VMU(device) &&
278 MAPLE_VMU_HAS_NAME(device, vmu_filename) ) {
279 gtk_combo_box_set_active_iter( GTK_COMBO_BOX(combo), &iter );
280 break;
281 }
283 valid = gtk_tree_model_iter_next(model, &iter);
284 }
285 if( !valid ) {
286 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
287 }
288 }
289 maple_device_adjusting = FALSE;
290 }
292 static void maple_dialog_done( GtkWidget *panel, gboolean isOK )
293 {
294 void *p = g_object_get_data( G_OBJECT(panel), "subdev_model" );
295 unregister_vmulist_change_hook( maple_vmulist_changed, p );
297 if( isOK ) {
298 int i;
299 for( i=0; i<MAPLE_MAX_DEVICES; i++ ) {
300 if( maple_data[i].new_device != maple_data[i].old_device ) {
301 if( maple_data[i].old_device != NULL ) {
302 maple_detach_device(MAPLE_DEVID_PORT(i),MAPLE_DEVID_SLOT(i));
303 }
304 if( maple_data[i].new_device != NULL ) {
305 maple_attach_device(maple_data[i].new_device, MAPLE_DEVID_PORT(i), MAPLE_DEVID_SLOT(i) );
306 }
307 }
308 }
309 lxdream_save_config();
310 } else {
311 int i;
312 for( i=0; i<MAPLE_MAX_DEVICES; i++ ) {
313 if( maple_data[i].new_device != NULL &&
314 maple_data[i].new_device != maple_data[i].old_device ) {
315 maple_data[i].new_device->destroy(maple_data[i].new_device);
316 }
317 }
318 }
320 }
322 static GtkWidget *maple_panel_new()
323 {
324 GtkWidget *table = gtk_table_new( MAPLE_PORTS * (MAPLE_USER_SLOTS+1), 3, TRUE);
325 int i,j,k;
326 const struct maple_device_class **devices = maple_get_device_classes();
328 gtk_table_set_row_spacings(GTK_TABLE(table), 3);
329 gtk_table_set_col_spacings(GTK_TABLE(table), 5);
330 maple_device_adjusting = FALSE;
332 /* Device models */
333 GtkListStore *dev_model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING);
334 maple_build_device_model(dev_model);
336 GtkListStore *subdev_model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING);
337 maple_build_subdevice_model(subdev_model);
338 g_object_set_data( G_OBJECT(table), "subdev_model", subdev_model );
339 register_vmulist_change_hook( maple_vmulist_changed, subdev_model );
341 int y =0;
342 for( i=0; i< MAPLE_PORTS; i++ ) {
343 char buf[16];
344 GtkWidget *combo, *button;
345 int length = 1;
346 maple_device_t device = maple_get_device(i,0);
347 int has_slots = device == NULL ? 0 : MAPLE_SLOTS(device->device_class);
349 snprintf( buf, sizeof(buf), _("Port %c."), 'A'+i );
350 gtk_table_attach_defaults( GTK_TABLE(table), gtk_label_new(buf), 0, 1, y, y+1 );
352 combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(dev_model));
353 GtkCellRenderer *rend = gtk_cell_renderer_text_new();
354 gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(combo), rend, TRUE);
355 gtk_cell_layout_add_attribute( GTK_CELL_LAYOUT(combo), rend, "text", 0 );
356 maple_set_device_selection(combo,device);
357 gtk_table_attach_defaults( GTK_TABLE(table), combo, 1, 2, y, y+1 );
359 button = gtk_button_new_from_stock( GTK_STOCK_PROPERTIES );
360 gtk_widget_set_sensitive(button, device != NULL && device->get_config != NULL);
361 gtk_table_attach_defaults( GTK_TABLE(table), button, 2, 3, y, y+1 );
363 maple_data[MAPLE_DEVID(i,0)].old_device = device;
364 maple_data[MAPLE_DEVID(i,0)].new_device = device;
365 maple_data[MAPLE_DEVID(i,0)].combo = combo;
366 maple_data[MAPLE_DEVID(i,0)].button = button;
367 maple_data[MAPLE_DEVID(i,0)].primarySlot = TRUE;
368 g_signal_connect( button, "clicked",
369 G_CALLBACK( maple_properties_activated ), &maple_data[MAPLE_DEVID(i,0)] );
370 g_signal_connect( combo, "changed",
371 G_CALLBACK( maple_device_changed ), &maple_data[MAPLE_DEVID(i,0)] );
372 y++;
374 for( k=0; k< MAPLE_USER_SLOTS; k++ ) {
375 char tmp[32] = " ";
376 device = maple_get_device(i,k+1);
377 snprintf( tmp+8, sizeof(tmp)-8, _("VMU %d."), (k+1) );
378 gtk_table_attach_defaults( GTK_TABLE(table), gtk_label_new(tmp), 0, 1, y, y+1 );
379 combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(subdev_model));
380 GtkCellRenderer *rend = gtk_cell_renderer_text_new();
381 gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(combo), rend, TRUE);
382 gtk_cell_layout_add_attribute( GTK_CELL_LAYOUT(combo), rend, "text", 0 );
383 maple_set_device_selection(combo, device);
385 gtk_table_attach_defaults( GTK_TABLE(table), combo, 1, 2, y, y+1 );
386 button = gtk_button_new_from_stock( GTK_STOCK_PROPERTIES );
387 gtk_table_attach_defaults( GTK_TABLE(table), button, 2, 3, y, y+1 );
388 if( k >= has_slots ) {
389 gtk_widget_set_sensitive(combo, FALSE);
390 gtk_widget_set_sensitive(button, FALSE);
391 } else {
392 gtk_widget_set_sensitive(button, device != NULL && device->get_config != NULL && !MAPLE_IS_VMU(device));
393 }
395 maple_data[MAPLE_DEVID(i,k+1)].old_device = device;
396 maple_data[MAPLE_DEVID(i,k+1)].new_device = device;
397 maple_data[MAPLE_DEVID(i,k+1)].combo = combo;
398 maple_data[MAPLE_DEVID(i,k+1)].button = button;
399 maple_data[MAPLE_DEVID(i,k+1)].primarySlot = FALSE;
400 g_signal_connect( button, "clicked",
401 G_CALLBACK( maple_properties_activated ), &maple_data[MAPLE_DEVID(i,k+1)] );
402 g_signal_connect( combo, "changed",
403 G_CALLBACK( maple_device_changed ), &maple_data[MAPLE_DEVID(i,k+1)] );
404 y++;
405 }
406 gtk_table_set_row_spacing( GTK_TABLE(table), y-1, 10 );
407 }
408 return table;
409 }
411 void maple_dialog_run( )
412 {
413 gtk_gui_run_property_dialog( _("Controller Settings"), maple_panel_new(), maple_dialog_done );
414 }
.