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 1036:af7b0c5905dd
prev1034:7044e01148f0
next1043:ec716f2b8ffb
author nkeynes
date Thu Jun 25 21:21:18 2009 +0000 (12 years ago)
permissions -rw-r--r--
last change Add quick state bits to the menus
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;
   296         has_slots = data->new_device == NULL ? 0 : MAPLE_SLOTS(devclz);
   297         if( MAPLE_IS_VMU(data->new_device) ) {
   298             MAPLE_SET_VMU_NAME(data->new_device,vmu_filename);
   299         }
   301         if( set_selection ) {
   302             maple_set_device_selection(data->combo, data->new_device);
   303         }
   304     } else {
   305         if( data->new_device != NULL && data->new_device != data->old_device ) {
   306             data->new_device->destroy(data->new_device);
   307         }
   308         data->new_device = NULL;
   309     }
   310     gtk_widget_set_sensitive(data->button, has_config);
   312     if( data->primarySlot ) {
   313         for( i=0; i<MAPLE_USER_SLOTS; i++ ) {
   314             /* This is a little morally dubious... */
   315             maple_slot_data_t subdata = data + MAPLE_DEVID(0,(i+1));
   316             gtk_widget_set_sensitive(subdata->combo, i < has_slots );
   317             gtk_widget_set_sensitive(subdata->button, i < has_slots && subdata->new_device != NULL && subdata->new_device->get_config != NULL );
   318         }
   319     }
   320     return TRUE;
   321 }
   323 static void maple_build_device_model( GtkListStore *dev_model )
   324 {
   325     const struct maple_device_class **devices = maple_get_device_classes();
   326     int i;
   328     gtk_list_store_clear(dev_model);
   329     gtk_list_store_insert_with_values( dev_model, NULL, 0, 0, _("<empty>"), 1, NULL, 2, NULL, -1 );
   330     for( i=0; devices[i] != NULL; i++ ) {
   331         if( devices[i]->flags & MAPLE_TYPE_PRIMARY ) {
   332             gtk_list_store_insert_with_values( dev_model, NULL, i+1, 0, devices[i]->name, 1, devices[i], 2, NULL, -1 );
   333         }
   334     }
   336 }
   338 /**
   339  * (Re)build the subdevice combo-box model. 
   340  */
   341 static void maple_build_subdevice_model( GtkListStore *subdev_model )
   342 {
   343     int i, j;
   344     const struct maple_device_class **devices = maple_get_device_classes();
   346     gtk_list_store_clear(subdev_model);
   347     gtk_list_store_insert_with_values( subdev_model, NULL, 0, 0, _("<empty>"), 1, NULL, 2, NULL, -1 );
   348     for( i=0; devices[i] != NULL; i++ ) {
   349         if( !(devices[i]->flags & MAPLE_TYPE_PRIMARY) && !MAPLE_IS_VMU_CLASS(devices[i]) ) {
   350             gtk_list_store_insert_with_values( subdev_model, NULL, i+1, 0, devices[i]->name, 1, devices[i], 2, NULL, -1 );
   351         }
   352     }
   353     for( j=0; j < vmulist_get_size(); j++ ) {
   354         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 );
   355     }
   356     gtk_list_store_insert_with_values( subdev_model, NULL, i+j+1, 0, _("Load VMU..."), 1, LOAD_VMU_TAG, 2, NULL, -1 );
   357     gtk_list_store_insert_with_values( subdev_model, NULL, i+j+2, 0, _("Create VMU..."), 1, CREATE_VMU_TAG, 2, NULL, -1 );
   358 }
   360 static gboolean maple_vmulist_changed( vmulist_change_type_t type, int idx, void *data )
   361 {
   362     GtkListStore *list = (GtkListStore *)data;
   363     GtkTreeIter iter;
   364     int i,j;
   366     /* Search for the row and update accordingly. There's probably better ways
   367      * to do this 
   368      */
   370     gboolean valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list), &iter);
   371     while( valid ) {
   372         gchar *vmu_filename;
   373         gtk_tree_model_get(GTK_TREE_MODEL(list), &iter, 2, &vmu_filename, -1 );
   374         if( vmu_filename != NULL )
   375             break;
   376         valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(list), &iter);
   377     }
   378     if( valid ) {
   379         for( i=0; i<idx && valid; i++ ) {
   380             valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(list), &iter);
   381         }
   382     }
   383     if( valid ) {
   384         if( type == VMU_ADDED ) {
   385             GtkTreeIter newiter;
   386             gtk_list_store_insert_before(list, &newiter, &iter);
   387             gtk_list_store_set(list, &newiter, 0, vmulist_get_name(idx), 1, NULL, 2, vmulist_get_filename(idx), -1 );
   388         } else if( type == VMU_REMOVED ) {
   389             gtk_list_store_remove(list, &iter );
   390         }
   391     }
   392     return TRUE;
   393 }
   395 /**
   396  * Set the device popup selection based on the device (works for both primary
   397  * and secondary devices)
   398  */
   399 static void maple_set_device_selection( GtkWidget *combo, maple_device_t device )
   400 {
   401     GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
   402     GtkTreeIter iter;
   404     if( device == NULL ) {
   405         gtk_combo_box_set_active( GTK_COMBO_BOX(combo), 0 );
   406         return;
   407     }
   409     gboolean valid = gtk_tree_model_get_iter_first(model, &iter);
   410     while( valid ) {
   411         const struct maple_device_class *clz;
   412         const gchar *vmu_filename;
   414         gtk_tree_model_get(model, &iter, 1, &clz, 2, &vmu_filename, -1 );
   416         if( device->device_class == clz ) {
   417             gtk_combo_box_set_active_iter( GTK_COMBO_BOX(combo), &iter );
   418             return;
   419         } else if( vmu_filename != NULL && MAPLE_IS_VMU(device) && 
   420                 MAPLE_VMU_HAS_NAME(device, vmu_filename) ) {
   421             gtk_combo_box_set_active_iter( GTK_COMBO_BOX(combo), &iter );
   422             return;
   423         }
   425         valid = gtk_tree_model_iter_next(model, &iter);
   426     }
   427     gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
   428 }
   430 static void maple_dialog_done( GtkWidget *panel, gboolean isOK )
   431 {
   432     void *p = g_object_get_data( G_OBJECT(panel), "subdev_model" );
   433     unregister_vmulist_change_hook( maple_vmulist_changed, p );
   435     if( isOK ) {
   436         int i;
   437         for( i=0; i<MAPLE_MAX_DEVICES; i++ ) {
   438             if( maple_data[i].new_device != maple_data[i].old_device ) {
   439                 if( maple_data[i].old_device != NULL ) {
   440                     maple_detach_device(MAPLE_DEVID_PORT(i),MAPLE_DEVID_SLOT(i));
   441                 }
   442                 if( maple_data[i].new_device != NULL ) {
   443                     maple_attach_device(maple_data[i].new_device, MAPLE_DEVID_PORT(i), MAPLE_DEVID_SLOT(i) );
   444                 }
   445             }
   446         }
   447         lxdream_save_config();
   448     } else {
   449         int i;
   450         for( i=0; i<MAPLE_MAX_DEVICES; i++ ) {
   451             if( maple_data[i].new_device != NULL && 
   452                     maple_data[i].new_device != maple_data[i].old_device ) {
   453                 maple_data[i].new_device->destroy(maple_data[i].new_device);
   454             }
   455         }
   456     }
   458 }
   460 static GtkWidget *maple_panel_new()
   461 {
   462     GtkWidget *table = gtk_table_new( MAPLE_PORTS * (MAPLE_USER_SLOTS+1), 3, TRUE);
   463     int i,j,k;
   464     const struct maple_device_class **devices = maple_get_device_classes();
   466     gtk_table_set_row_spacings(GTK_TABLE(table), 3);
   467     gtk_table_set_col_spacings(GTK_TABLE(table), 5);
   469     /* Device models */
   470     GtkListStore *dev_model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING);
   471     maple_build_device_model(dev_model);
   473     GtkListStore *subdev_model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING);
   474     maple_build_subdevice_model(subdev_model);
   475     g_object_set_data( G_OBJECT(table), "subdev_model", subdev_model ); 
   476     register_vmulist_change_hook( maple_vmulist_changed, subdev_model );
   478     int y =0;
   479     for( i=0; i< MAPLE_PORTS; i++ ) {
   480         char buf[16];
   481         GtkWidget *combo, *button;
   482         int active = 0, length = 1;
   483         maple_device_t device = maple_get_device(i,0);
   484         int has_slots = device == NULL ? 0 : MAPLE_SLOTS(device->device_class);
   486         snprintf( buf, sizeof(buf), _("Port %c."), 'A'+i );
   487         gtk_table_attach_defaults( GTK_TABLE(table), gtk_label_new(buf), 0, 1, y, y+1 );
   489         combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(dev_model));
   490         GtkCellRenderer *rend = gtk_cell_renderer_text_new();
   491         gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(combo), rend, TRUE);
   492         gtk_cell_layout_add_attribute( GTK_CELL_LAYOUT(combo), rend, "text", 0 );
   493         maple_set_device_selection(combo,device);
   494         gtk_table_attach_defaults( GTK_TABLE(table), combo, 1, 2, y, y+1 );
   496         button = gtk_button_new_from_stock( GTK_STOCK_PROPERTIES );
   497         gtk_widget_set_sensitive(button, active != 0 && device->get_config != NULL);
   498         gtk_table_attach_defaults( GTK_TABLE(table), button, 2, 3, y, y+1 );
   500         maple_data[MAPLE_DEVID(i,0)].old_device = device;
   501         maple_data[MAPLE_DEVID(i,0)].new_device = device;
   502         maple_data[MAPLE_DEVID(i,0)].combo = combo;
   503         maple_data[MAPLE_DEVID(i,0)].button = button;
   504         maple_data[MAPLE_DEVID(i,0)].primarySlot = TRUE;
   505         g_signal_connect( button, "clicked", 
   506                           G_CALLBACK( maple_properties_activated ), &maple_data[MAPLE_DEVID(i,0)] );
   507         g_signal_connect( combo, "changed", 
   508                           G_CALLBACK( maple_device_changed ), &maple_data[MAPLE_DEVID(i,0)] );
   509         y++;
   511         for( k=0; k< MAPLE_USER_SLOTS; k++ ) {
   512             char tmp[32] = "        ";
   513             device = maple_get_device(i,k+1);
   514             active = 0;
   515             snprintf( tmp+8, sizeof(tmp)-8, _("VMU %d."), (k+1) );
   516             gtk_table_attach_defaults( GTK_TABLE(table), gtk_label_new(tmp), 0, 1, y, y+1 );
   517             combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(subdev_model));
   518             GtkCellRenderer *rend = gtk_cell_renderer_text_new();
   519             gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(combo), rend, TRUE);
   520             gtk_cell_layout_add_attribute( GTK_CELL_LAYOUT(combo), rend, "text", 0 );
   521             maple_set_device_selection(combo, device);
   523             gtk_table_attach_defaults( GTK_TABLE(table), combo, 1, 2, y, y+1 );
   524             button = gtk_button_new_from_stock( GTK_STOCK_PROPERTIES );
   525             gtk_table_attach_defaults( GTK_TABLE(table), button, 2, 3, y, y+1 );
   526             if( k >= has_slots ) {
   527                 gtk_widget_set_sensitive(combo, FALSE);
   528                 gtk_widget_set_sensitive(button, FALSE);
   529             } else {
   530                 gtk_widget_set_sensitive(button, device != NULL && device->get_config != NULL && !MAPLE_IS_VMU(device));
   531             }            
   533             maple_data[MAPLE_DEVID(i,k+1)].old_device = device;
   534             maple_data[MAPLE_DEVID(i,k+1)].new_device = device;
   535             maple_data[MAPLE_DEVID(i,k+1)].combo = combo;
   536             maple_data[MAPLE_DEVID(i,k+1)].button = button;
   537             maple_data[MAPLE_DEVID(i,k+1)].primarySlot = FALSE;
   538             g_signal_connect( button, "clicked", 
   539                               G_CALLBACK( maple_properties_activated ), &maple_data[MAPLE_DEVID(i,k+1)] );
   540             g_signal_connect( combo, "changed", 
   541                               G_CALLBACK( maple_device_changed ), &maple_data[MAPLE_DEVID(i,k+1)] );
   542             y++;
   543         }
   544         gtk_table_set_row_spacing( GTK_TABLE(table), y-1, 10 );
   545     }
   546     return table;
   547 }
   549 void maple_dialog_run( )
   550 {
   551     gtk_gui_run_property_dialog( _("Controller Settings"), maple_panel_new(), maple_dialog_done );
   552 }
.