4 * Define the main (emu) GTK window, along with its menubars,
7 * Copyright (c) 2005 Nathan Keynes.
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.
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.
24 #include <gdk/gdkkeysyms.h>
28 #include "gtkui/gtkui.h"
29 #include "maple/maple.h"
30 #include "vmu/vmulist.h"
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 {
41 void (*config_func)(maple_device_t device);
44 typedef struct maple_slot_data {
45 maple_device_t old_device;
46 maple_device_t new_device;
53 static struct maple_slot_data maple_data[MAPLE_MAX_DEVICES];
56 * Flag set when changing the selection on one of the combo boxes manually -
57 * avoids the followup changed event.
59 static gboolean maple_device_adjusting = FALSE;
61 static gboolean maple_properties_activated( GtkButton *button, gpointer user_data )
63 maple_slot_data_t data = (maple_slot_data_t)user_data;
64 if( data->new_device != NULL ) {
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);
74 gtk_configuration_panel_run(_("Controller Configuration"), config);
80 static gboolean maple_device_changed( GtkComboBox *combo, gpointer user_data )
82 if( maple_device_adjusting ) {
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;
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 ) {
102 vmu_filename = open_file_dialog( _("Load VMU"), "*.vmu", "VMU Files",
104 if( vmu_filename != NULL ) {
105 vmu_volume_t vol = vmu_volume_load( vmu_filename );
108 vmulist_add_vmu(vmu_filename, vol);
109 set_selection = TRUE;
111 ERROR( "Unable to load VMU file (not a valid VMU)" );
114 } else if( devclz == CREATE_VMU_TAG ) {
116 vmu_filename = save_file_dialog( _("Create VMU"), "*.vmu", "VMU Files", ".vmu",
118 if( vmu_filename != NULL ) {
120 set_selection = TRUE;
121 int idx = vmulist_create_vmu( vmu_filename, FALSE );
123 ERROR( "Unable to save VMU file: %s", strerror(errno) );
126 } else if( vmu_filename != NULL ) {
130 if( devclz == NULL ) {
131 maple_set_device_selection(data->combo, data->new_device);
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);
140 data->new_device = devclz->new_device();
143 data->new_device = devclz->new_device();
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);
156 MAPLE_SET_VMU_NAME(data->new_device,vmu_filename);
159 if( set_selection ) {
160 maple_set_device_selection(data->combo, data->new_device);
163 if( data->new_device != NULL && data->new_device != data->old_device ) {
164 data->new_device->destroy(data->new_device);
166 data->new_device = NULL;
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) );
181 static void maple_build_device_model( GtkListStore *dev_model )
183 const struct maple_device_class **devices = maple_get_device_classes();
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 );
197 * (Re)build the subdevice combo-box model.
199 static void maple_build_subdevice_model( GtkListStore *subdev_model )
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 );
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 );
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 );
218 static gboolean maple_vmulist_changed( vmulist_change_type_t type, int idx, void *data )
220 GtkListStore *list = (GtkListStore *)data;
224 /* Search for the row and update accordingly. There's probably better ways
228 gboolean valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list), &iter);
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 )
235 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(list), &iter);
238 for( i=0; i<idx && valid; i++ ) {
239 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(list), &iter);
243 if( type == VMU_ADDED ) {
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 );
255 * Set the device popup selection based on the device (works for both primary
256 * and secondary devices)
258 static void maple_set_device_selection( GtkWidget *combo, maple_device_t device )
260 GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
263 maple_device_adjusting = TRUE;
264 if( device == NULL ) {
265 gtk_combo_box_set_active( GTK_COMBO_BOX(combo), 0 );
267 gboolean valid = gtk_tree_model_get_iter_first(model, &iter);
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 );
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 );
283 valid = gtk_tree_model_iter_next(model, &iter);
286 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
289 maple_device_adjusting = FALSE;
292 static void maple_dialog_done( GtkWidget *panel, gboolean isOK )
294 void *p = g_object_get_data( G_OBJECT(panel), "subdev_model" );
295 unregister_vmulist_change_hook( maple_vmulist_changed, p );
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));
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) );
309 lxdream_save_config();
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);
322 static GtkWidget *maple_panel_new()
324 GtkWidget *table = gtk_table_new( MAPLE_PORTS * (MAPLE_USER_SLOTS+1), 3, TRUE);
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;
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 );
342 for( i=0; i< MAPLE_PORTS; i++ ) {
344 GtkWidget *combo, *button;
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)] );
374 for( k=0; k< MAPLE_USER_SLOTS; k++ ) {
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);
392 gtk_widget_set_sensitive(button, device != NULL && device->get_config != NULL && !MAPLE_IS_VMU(device));
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)] );
406 gtk_table_set_row_spacing( GTK_TABLE(table), y-1, 10 );
411 void maple_dialog_run( )
413 gtk_gui_run_property_dialog( _("Controller Settings"), maple_panel_new(), maple_dialog_done );
.