Search
lxdream.org :: lxdream/src/gtkui/gtk_ctrl.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/gtkui/gtk_ctrl.c
changeset 1043:ec716f2b8ffb
prev1036:af7b0c5905dd
next1057:6f2158c421bb
author nkeynes
date Sat Jun 27 09:55:00 2009 +0000 (14 years ago)
permissions -rw-r--r--
last change Ensure each VMU is only attached once
Handle removal of sub-devices when detaching parent device
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 controller_device_configure(maple_device_t device);
    38 static void maple_set_device_selection( GtkWidget *combo, maple_device_t device );
    40 struct maple_config_class {
    41     const char *name;
    42     void (*config_func)(maple_device_t device);
    43 };
    45 typedef struct maple_slot_data {
    46     maple_device_t old_device;
    47     maple_device_t new_device;
    48     GtkWidget *button;
    49     GtkWidget *combo;
    50     gboolean primarySlot;
    51 } *maple_slot_data_t;
    54 static struct maple_config_class maple_device_config[] = {
    55         { "Sega Controller", controller_device_configure },
    56         { "Sega Lightgun", controller_device_configure },
    57         { NULL, NULL } };
    59 static struct maple_slot_data maple_data[MAPLE_MAX_DEVICES];
    61 static void config_keysym_hook( void *data, const gchar *keysym )
    62 {
    63     GtkWidget *widget = (GtkWidget *)data;
    64     gtk_entry_set_text( GTK_ENTRY(widget), keysym );
    65     g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(FALSE) );
    66     input_set_keysym_hook(NULL, NULL);
    67 }
    69 static gboolean config_key_buttonpress( GtkWidget *widget, GdkEventButton *event, gpointer user_data )
    70 {
    71     gboolean keypress_mode = GPOINTER_TO_INT(g_object_get_data( G_OBJECT(widget), "keypress_mode"));
    72     if( keypress_mode ) {
    73         gchar *keysym = input_keycode_to_keysym( &system_mouse_driver, event->button);
    74         if( keysym != NULL ) {
    75             config_keysym_hook( widget, keysym );
    76             g_free(keysym);
    77         }
    78         return TRUE;
    79     } else {
    80         gtk_entry_set_text( GTK_ENTRY(widget), _("<press key>") );
    81         g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(TRUE) );
    82         input_set_keysym_hook(config_keysym_hook, widget);
    83     }
    84     return FALSE;
    85 }
    87 static gboolean config_key_keypress( GtkWidget *widget, GdkEventKey *event, gpointer user_data )
    88 {
    89     gboolean keypress_mode = GPOINTER_TO_INT(g_object_get_data( G_OBJECT(widget), "keypress_mode"));
    90     if( keypress_mode ) {
    91         if( event->keyval == GDK_Escape ) {
    92             gtk_entry_set_text( GTK_ENTRY(widget), "" );
    93             g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(FALSE) );
    94             return TRUE;
    95         }
    96         GdkKeymap *keymap = gdk_keymap_get_default();
    97         guint keyval;
    99         gdk_keymap_translate_keyboard_state( keymap, event->hardware_keycode, 0, 0, &keyval, 
   100                                              NULL, NULL, NULL );
   101         gtk_entry_set_text( GTK_ENTRY(widget), gdk_keyval_name(keyval) );
   102         g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(FALSE) );
   103         input_set_keysym_hook(NULL, NULL);
   104         return TRUE;
   105     } else {
   106         switch( event->keyval ) {
   107         case GDK_Return:
   108         case GDK_KP_Enter:
   109             gtk_entry_set_text( GTK_ENTRY(widget), _("<press key>") );
   110             g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(TRUE) );
   111             input_set_keysym_hook(config_keysym_hook, widget);
   112             return TRUE;
   113         case GDK_BackSpace:
   114         case GDK_Delete:
   115             gtk_entry_set_text( GTK_ENTRY(widget), "" );
   116             return TRUE;
   117         }
   118         return FALSE;
   119     }
   121 }
   123 static void controller_config_done( GtkWidget *panel, gboolean isOK )
   124 {
   125     if( isOK ) {
   126         maple_device_t device = (maple_device_t)gtk_object_get_data( GTK_OBJECT(panel), "maple_device" );
   127         lxdream_config_entry_t conf = device->get_config(device);
   128         int i;
   129         for( i=0; conf[i].key != NULL; i++ ) {
   130             char buf[64];
   131             GtkWidget *entry1, *entry2;
   132             const gchar *key1, *key2;
   133             snprintf( buf, sizeof(buf), "%s.1", conf[i].key );
   134             entry1 = GTK_WIDGET(g_object_get_qdata( G_OBJECT(panel), g_quark_from_string(buf)));
   135             key1 = gtk_entry_get_text(GTK_ENTRY(entry1));
   136             snprintf( buf, sizeof(buf), "%s.2", conf[i].key );
   137             entry2 = GTK_WIDGET(g_object_get_qdata( G_OBJECT(panel), g_quark_from_string(buf)));
   138             key2 = gtk_entry_get_text(GTK_ENTRY(entry2));
   139             if( key1 == NULL || key1[0] == '\0') {
   140                 lxdream_set_config_value( &conf[i], key2 );
   141             } else if( key2 == NULL || key2[0] == '\0') {
   142                 lxdream_set_config_value( &conf[i], key1 );
   143             } else {
   144                 char buf[64];
   145                 snprintf( buf, sizeof(buf), "%s, %s", key1, key2 );
   146                 lxdream_set_config_value( &conf[i], buf );
   147             }
   148         }
   149     }
   150     input_set_keysym_hook(NULL, NULL);
   151 }
   153 static void controller_device_configure( maple_device_t device )
   154 {
   155     lxdream_config_entry_t conf = maple_get_device_config(device);
   156     int count, i;
   157     for( count=0; conf[count].key != NULL; count++ );
   159     GtkWidget *table = gtk_table_new( (count+1)>>1, 6, FALSE );
   160     GList *focus_chain = NULL;
   161     gtk_object_set_data( GTK_OBJECT(table), "maple_device", device );
   162     for( i=0; i<count; i++ ) {
   163         GtkWidget *text, *text2;
   164         char buf[64];
   165         int x=0;
   166         int y=i;
   167         if( i >= (count+1)>>1 ) {
   168             x = 3;
   169             y -= (count+1)>>1;
   170         }
   171         gtk_table_attach( GTK_TABLE(table), gtk_label_new(gettext(conf[i].label)), x, x+1, y, y+1, 
   172                           GTK_SHRINK, GTK_SHRINK, 0, 0 );
   173         text = gtk_entry_new();
   174         gtk_entry_set_width_chars( GTK_ENTRY(text), 11 );
   175         gtk_entry_set_editable( GTK_ENTRY(text), FALSE );
   176         g_signal_connect( text, "key_press_event", 
   177                           G_CALLBACK(config_key_keypress), NULL );
   178         g_signal_connect( text, "button_press_event",
   179                           G_CALLBACK(config_key_buttonpress), NULL );
   180         snprintf( buf, sizeof(buf), "%s.1", conf[i].key );
   181         g_object_set_data( G_OBJECT(text), "keypress_mode", GINT_TO_POINTER(FALSE) );
   182         g_object_set_qdata( G_OBJECT(table), g_quark_from_string(buf), text );
   183         gtk_table_attach_defaults( GTK_TABLE(table), text, x+1, x+2, y, y+1);
   184         focus_chain = g_list_append( focus_chain, text );
   185         text2 = gtk_entry_new();
   186         gtk_entry_set_width_chars( GTK_ENTRY(text2), 11 );
   187         gtk_entry_set_editable( GTK_ENTRY(text2), FALSE );
   188         g_signal_connect( text2, "key_press_event", 
   189                           G_CALLBACK(config_key_keypress), NULL );
   190         g_signal_connect( text2, "button_press_event",
   191                           G_CALLBACK(config_key_buttonpress), NULL );
   192         snprintf( buf, sizeof(buf), "%s.2", conf[i].key );
   193         g_object_set_data( G_OBJECT(text2), "keypress_mode", GINT_TO_POINTER(FALSE) );
   194         g_object_set_qdata( G_OBJECT(table), g_quark_from_string(buf), text2 );
   195         gtk_table_attach_defaults( GTK_TABLE(table), text2, x+2, x+3, y, y+1);
   196         focus_chain = g_list_append( focus_chain, text2 );
   197         if( conf[i].value != NULL ) {
   198             gchar **parts = g_strsplit(conf[i].value,",",3);
   199             if( parts[0] != NULL ) {
   200                 gtk_entry_set_text( GTK_ENTRY(text), g_strstrip(parts[0]) );
   201                 if( parts[1] != NULL ) {
   202                     gtk_entry_set_text( GTK_ENTRY(text2), g_strstrip(parts[1]) );
   203                 }
   204             }
   205             g_strfreev(parts);
   206         }
   207     }
   208     gtk_container_set_focus_chain( GTK_CONTAINER(table), focus_chain );
   209     gtk_gui_run_property_dialog( _("Controller Configuration"), table, controller_config_done );
   210 }
   212 static gboolean maple_properties_activated( GtkButton *button, gpointer user_data )
   213 {
   214     maple_slot_data_t data = (maple_slot_data_t)user_data;
   215     if( data->new_device != NULL ) {
   216         int i;
   217         for( i=0; maple_device_config[i].name != NULL; i++ ) {
   218             if( strcmp(data->new_device->device_class->name, maple_device_config[i].name) == 0 ) {
   219                 if( data->new_device == data->old_device ) {
   220                     // Make a copy at this point if we haven't already
   221                     data->new_device = data->old_device->clone(data->old_device);
   222                 }
   223                 maple_device_config[i].config_func(data->new_device);
   224                 break;
   225             }
   226         }
   227         if( maple_device_config[i].name == NULL ) {
   228             gui_error_dialog( _("No configuration page available for device type") );
   229         }
   230     }
   231     return TRUE;
   232 }
   234 static gboolean maple_device_changed( GtkComboBox *combo, gpointer user_data )
   235 {
   236     maple_slot_data_t data = (maple_slot_data_t)user_data;
   237     int active = gtk_combo_box_get_active(combo), i;
   238     gboolean has_config = FALSE;
   239     gboolean set_selection = FALSE;
   240     int has_slots = 0;
   241     if( active != 0 ) {
   242         GtkTreeIter iter;
   243         maple_device_class_t devclz;
   244         const gchar *vmu_filename;
   246         GtkTreeModel *model = gtk_combo_box_get_model(combo);
   247         gtk_combo_box_get_active_iter(combo, &iter);
   248         gtk_tree_model_get(model, &iter, 1, &devclz, 2, &vmu_filename, -1 );
   250         if( devclz == LOAD_VMU_TAG ) {
   251             devclz = NULL;
   252             vmu_filename = open_file_dialog( _("Load VMU"), "*.vmu", "VMU Files",
   253                     CONFIG_VMU_PATH );
   254             if( vmu_filename != NULL ) {
   255                 vmu_volume_t vol = vmu_volume_load( vmu_filename );
   256                 if( vol != NULL ) {
   257                     devclz = &vmu_class;
   258                     vmulist_add_vmu(vmu_filename, vol);
   259                     set_selection = TRUE;
   260                 } else {
   261                     ERROR( "Unable to load VMU file (not a valid VMU)" );
   262                 }
   263             }
   264         } else if( devclz == CREATE_VMU_TAG ) {
   265             devclz = NULL;
   266             vmu_filename = save_file_dialog( _("Create VMU"), "*.vmu", "VMU Files",
   267                     CONFIG_VMU_PATH );
   268             if( vmu_filename != NULL ) {
   269                 devclz = &vmu_class;
   270                 set_selection = TRUE;
   271                 int idx = vmulist_create_vmu( vmu_filename, FALSE );
   272                 if( idx == -1 ) {
   273                     ERROR( "Unable to save VMU file: %s", strerror(errno) );
   274                 }
   275             }
   276         } else if( vmu_filename != NULL ) {
   277             devclz = &vmu_class;
   278         }
   280         if( devclz == NULL ) {
   281             maple_set_device_selection(data->combo, data->new_device);
   282             return TRUE;
   283         }
   285         if( data->new_device != NULL ) {
   286             if( data->new_device->device_class != devclz ) {
   287                 if( data->new_device != data->old_device ) {
   288                     data->new_device->destroy(data->new_device);
   289                 }
   290                 data->new_device = devclz->new_device();
   291             }
   292         } else {
   293             data->new_device = devclz->new_device();
   294         }
   295         has_config = data->new_device != NULL && data->new_device->get_config != NULL && !MAPLE_IS_VMU(data->new_device);
   296         has_slots = data->new_device == NULL ? 0 : MAPLE_SLOTS(devclz);
   297         if( MAPLE_IS_VMU(data->new_device) ) {
   298             for( i=0; i<MAPLE_MAX_DEVICES; i++ ) {
   299                 if( maple_data[i].new_device != NULL && MAPLE_IS_VMU(maple_data[i].new_device) &&
   300                         MAPLE_VMU_HAS_NAME(maple_data[i].new_device, vmu_filename) ) {
   301                     maple_data[i].new_device->destroy(maple_data[i].new_device);
   302                     maple_data[i].new_device = NULL;
   303                     gtk_combo_box_set_active(maple_data[i].combo,0);
   304                 }
   305             }
   306             MAPLE_SET_VMU_NAME(data->new_device,vmu_filename);
   307         }
   309         if( set_selection ) {
   310             maple_set_device_selection(data->combo, data->new_device);
   311         }
   312     } else {
   313         if( data->new_device != NULL && data->new_device != data->old_device ) {
   314             data->new_device->destroy(data->new_device);
   315         }
   316         data->new_device = NULL;
   317     }
   318     gtk_widget_set_sensitive(data->button, has_config);
   320     if( data->primarySlot ) {
   321         for( i=0; i<MAPLE_USER_SLOTS; i++ ) {
   322             /* This is a little morally dubious... */
   323             maple_slot_data_t subdata = data + MAPLE_DEVID(0,(i+1));
   324             gtk_widget_set_sensitive(subdata->combo, i < has_slots );
   325             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) );
   326         }
   327     }
   328     return TRUE;
   329 }
   331 static void maple_build_device_model( GtkListStore *dev_model )
   332 {
   333     const struct maple_device_class **devices = maple_get_device_classes();
   334     int i;
   336     gtk_list_store_clear(dev_model);
   337     gtk_list_store_insert_with_values( dev_model, NULL, 0, 0, _("<empty>"), 1, NULL, 2, NULL, -1 );
   338     for( i=0; devices[i] != NULL; i++ ) {
   339         if( devices[i]->flags & MAPLE_TYPE_PRIMARY ) {
   340             gtk_list_store_insert_with_values( dev_model, NULL, i+1, 0, devices[i]->name, 1, devices[i], 2, NULL, -1 );
   341         }
   342     }
   344 }
   346 /**
   347  * (Re)build the subdevice combo-box model. 
   348  */
   349 static void maple_build_subdevice_model( GtkListStore *subdev_model )
   350 {
   351     int i, j;
   352     const struct maple_device_class **devices = maple_get_device_classes();
   354     gtk_list_store_clear(subdev_model);
   355     gtk_list_store_insert_with_values( subdev_model, NULL, 0, 0, _("<empty>"), 1, NULL, 2, NULL, -1 );
   356     for( i=0; devices[i] != NULL; i++ ) {
   357         if( !(devices[i]->flags & MAPLE_TYPE_PRIMARY) && !MAPLE_IS_VMU_CLASS(devices[i]) ) {
   358             gtk_list_store_insert_with_values( subdev_model, NULL, i+1, 0, devices[i]->name, 1, devices[i], 2, NULL, -1 );
   359         }
   360     }
   361     for( j=0; j < vmulist_get_size(); j++ ) {
   362         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 );
   363     }
   364     gtk_list_store_insert_with_values( subdev_model, NULL, i+j+1, 0, _("Load VMU..."), 1, LOAD_VMU_TAG, 2, NULL, -1 );
   365     gtk_list_store_insert_with_values( subdev_model, NULL, i+j+2, 0, _("Create VMU..."), 1, CREATE_VMU_TAG, 2, NULL, -1 );
   366 }
   368 static gboolean maple_vmulist_changed( vmulist_change_type_t type, int idx, void *data )
   369 {
   370     GtkListStore *list = (GtkListStore *)data;
   371     GtkTreeIter iter;
   372     int i,j;
   374     /* Search for the row and update accordingly. There's probably better ways
   375      * to do this 
   376      */
   378     gboolean valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list), &iter);
   379     while( valid ) {
   380         gchar *vmu_filename;
   381         gtk_tree_model_get(GTK_TREE_MODEL(list), &iter, 2, &vmu_filename, -1 );
   382         if( vmu_filename != NULL )
   383             break;
   384         valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(list), &iter);
   385     }
   386     if( valid ) {
   387         for( i=0; i<idx && valid; i++ ) {
   388             valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(list), &iter);
   389         }
   390     }
   391     if( valid ) {
   392         if( type == VMU_ADDED ) {
   393             GtkTreeIter newiter;
   394             gtk_list_store_insert_before(list, &newiter, &iter);
   395             gtk_list_store_set(list, &newiter, 0, vmulist_get_name(idx), 1, NULL, 2, vmulist_get_filename(idx), -1 );
   396         } else if( type == VMU_REMOVED ) {
   397             gtk_list_store_remove(list, &iter );
   398         }
   399     }
   400     return TRUE;
   401 }
   403 /**
   404  * Set the device popup selection based on the device (works for both primary
   405  * and secondary devices)
   406  */
   407 static void maple_set_device_selection( GtkWidget *combo, maple_device_t device )
   408 {
   409     GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
   410     GtkTreeIter iter;
   412     if( device == NULL ) {
   413         gtk_combo_box_set_active( GTK_COMBO_BOX(combo), 0 );
   414         return;
   415     }
   417     gboolean valid = gtk_tree_model_get_iter_first(model, &iter);
   418     while( valid ) {
   419         const struct maple_device_class *clz;
   420         const gchar *vmu_filename;
   422         gtk_tree_model_get(model, &iter, 1, &clz, 2, &vmu_filename, -1 );
   424         if( device->device_class == clz ) {
   425             gtk_combo_box_set_active_iter( GTK_COMBO_BOX(combo), &iter );
   426             return;
   427         } else if( vmu_filename != NULL && MAPLE_IS_VMU(device) && 
   428                 MAPLE_VMU_HAS_NAME(device, vmu_filename) ) {
   429             gtk_combo_box_set_active_iter( GTK_COMBO_BOX(combo), &iter );
   430             return;
   431         }
   433         valid = gtk_tree_model_iter_next(model, &iter);
   434     }
   435     gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
   436 }
   438 static void maple_dialog_done( GtkWidget *panel, gboolean isOK )
   439 {
   440     void *p = g_object_get_data( G_OBJECT(panel), "subdev_model" );
   441     unregister_vmulist_change_hook( maple_vmulist_changed, p );
   443     if( isOK ) {
   444         int i;
   445         for( i=0; i<MAPLE_MAX_DEVICES; i++ ) {
   446             if( maple_data[i].new_device != maple_data[i].old_device ) {
   447                 if( maple_data[i].old_device != NULL ) {
   448                     maple_detach_device(MAPLE_DEVID_PORT(i),MAPLE_DEVID_SLOT(i));
   449                 }
   450                 if( maple_data[i].new_device != NULL ) {
   451                     maple_attach_device(maple_data[i].new_device, MAPLE_DEVID_PORT(i), MAPLE_DEVID_SLOT(i) );
   452                 }
   453             }
   454         }
   455         lxdream_save_config();
   456     } else {
   457         int i;
   458         for( i=0; i<MAPLE_MAX_DEVICES; i++ ) {
   459             if( maple_data[i].new_device != NULL && 
   460                     maple_data[i].new_device != maple_data[i].old_device ) {
   461                 maple_data[i].new_device->destroy(maple_data[i].new_device);
   462             }
   463         }
   464     }
   466 }
   468 static GtkWidget *maple_panel_new()
   469 {
   470     GtkWidget *table = gtk_table_new( MAPLE_PORTS * (MAPLE_USER_SLOTS+1), 3, TRUE);
   471     int i,j,k;
   472     const struct maple_device_class **devices = maple_get_device_classes();
   474     gtk_table_set_row_spacings(GTK_TABLE(table), 3);
   475     gtk_table_set_col_spacings(GTK_TABLE(table), 5);
   477     /* Device models */
   478     GtkListStore *dev_model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING);
   479     maple_build_device_model(dev_model);
   481     GtkListStore *subdev_model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING);
   482     maple_build_subdevice_model(subdev_model);
   483     g_object_set_data( G_OBJECT(table), "subdev_model", subdev_model ); 
   484     register_vmulist_change_hook( maple_vmulist_changed, subdev_model );
   486     int y =0;
   487     for( i=0; i< MAPLE_PORTS; i++ ) {
   488         char buf[16];
   489         GtkWidget *combo, *button;
   490         int active = 0, length = 1;
   491         maple_device_t device = maple_get_device(i,0);
   492         int has_slots = device == NULL ? 0 : MAPLE_SLOTS(device->device_class);
   494         snprintf( buf, sizeof(buf), _("Port %c."), 'A'+i );
   495         gtk_table_attach_defaults( GTK_TABLE(table), gtk_label_new(buf), 0, 1, y, y+1 );
   497         combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(dev_model));
   498         GtkCellRenderer *rend = gtk_cell_renderer_text_new();
   499         gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(combo), rend, TRUE);
   500         gtk_cell_layout_add_attribute( GTK_CELL_LAYOUT(combo), rend, "text", 0 );
   501         maple_set_device_selection(combo,device);
   502         gtk_table_attach_defaults( GTK_TABLE(table), combo, 1, 2, y, y+1 );
   504         button = gtk_button_new_from_stock( GTK_STOCK_PROPERTIES );
   505         gtk_widget_set_sensitive(button, active != 0 && device->get_config != NULL);
   506         gtk_table_attach_defaults( GTK_TABLE(table), button, 2, 3, y, y+1 );
   508         maple_data[MAPLE_DEVID(i,0)].old_device = device;
   509         maple_data[MAPLE_DEVID(i,0)].new_device = device;
   510         maple_data[MAPLE_DEVID(i,0)].combo = combo;
   511         maple_data[MAPLE_DEVID(i,0)].button = button;
   512         maple_data[MAPLE_DEVID(i,0)].primarySlot = TRUE;
   513         g_signal_connect( button, "clicked", 
   514                           G_CALLBACK( maple_properties_activated ), &maple_data[MAPLE_DEVID(i,0)] );
   515         g_signal_connect( combo, "changed", 
   516                           G_CALLBACK( maple_device_changed ), &maple_data[MAPLE_DEVID(i,0)] );
   517         y++;
   519         for( k=0; k< MAPLE_USER_SLOTS; k++ ) {
   520             char tmp[32] = "        ";
   521             device = maple_get_device(i,k+1);
   522             active = 0;
   523             snprintf( tmp+8, sizeof(tmp)-8, _("VMU %d."), (k+1) );
   524             gtk_table_attach_defaults( GTK_TABLE(table), gtk_label_new(tmp), 0, 1, y, y+1 );
   525             combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(subdev_model));
   526             GtkCellRenderer *rend = gtk_cell_renderer_text_new();
   527             gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(combo), rend, TRUE);
   528             gtk_cell_layout_add_attribute( GTK_CELL_LAYOUT(combo), rend, "text", 0 );
   529             maple_set_device_selection(combo, device);
   531             gtk_table_attach_defaults( GTK_TABLE(table), combo, 1, 2, y, y+1 );
   532             button = gtk_button_new_from_stock( GTK_STOCK_PROPERTIES );
   533             gtk_table_attach_defaults( GTK_TABLE(table), button, 2, 3, y, y+1 );
   534             if( k >= has_slots ) {
   535                 gtk_widget_set_sensitive(combo, FALSE);
   536                 gtk_widget_set_sensitive(button, FALSE);
   537             } else {
   538                 gtk_widget_set_sensitive(button, device != NULL && device->get_config != NULL && !MAPLE_IS_VMU(device));
   539             }            
   541             maple_data[MAPLE_DEVID(i,k+1)].old_device = device;
   542             maple_data[MAPLE_DEVID(i,k+1)].new_device = device;
   543             maple_data[MAPLE_DEVID(i,k+1)].combo = combo;
   544             maple_data[MAPLE_DEVID(i,k+1)].button = button;
   545             maple_data[MAPLE_DEVID(i,k+1)].primarySlot = FALSE;
   546             g_signal_connect( button, "clicked", 
   547                               G_CALLBACK( maple_properties_activated ), &maple_data[MAPLE_DEVID(i,k+1)] );
   548             g_signal_connect( combo, "changed", 
   549                               G_CALLBACK( maple_device_changed ), &maple_data[MAPLE_DEVID(i,k+1)] );
   550             y++;
   551         }
   552         gtk_table_set_row_spacing( GTK_TABLE(table), y-1, 10 );
   553     }
   554     return table;
   555 }
   557 void maple_dialog_run( )
   558 {
   559     gtk_gui_run_property_dialog( _("Controller Settings"), maple_panel_new(), maple_dialog_done );
   560 }
.