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