Mercurial > audlegacy-plugins
view src/evdev-plug/ed_ui.c @ 866:d8b8b36d2709 trunk
[svn] - prevent skin list overlap when it's first populated and user quickly switchs to other tabs in the prefswin
author | giacomo |
---|---|
date | Fri, 16 Mar 2007 18:47:24 -0700 |
parents | d124034ebea3 |
children | 0cc6b35fbeb4 |
line wrap: on
line source
/* * * Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "ed_ui.h" #include "ed_types.h" #include "ed_internals.h" #include "ed_actions.h" #include "ed_bindings_store.h" #include "ed_common.h" #include <stdlib.h> #include <linux/input.h> #include <audacious/i18n.h> #include <gtk/gtk.h> static void cfg_ui_bindings_show ( ed_device_t * , GtkTreeRowReference * ); extern ed_action_t player_actions[]; extern gboolean plugin_is_active; static GtkWidget *cfg_win = NULL; enum { DEVLIST_COL_ISACTIVE = 0, DEVLIST_COL_NAME, DEVLIST_COL_FILENAME, DEVLIST_COL_PHYS, DEVLIST_COL_ISAVAILABLE, DEVLIST_COL_BINDINGS, DEVLIST_NUMCOLS }; enum { DEVLIST_ISAVAILABLE_NOTDET = 0, DEVLIST_ISAVAILABLE_DET, DEVLIST_ISAVAILABLE_CUSTOM }; static void cfg_device_lv_populate( GtkListStore * store ) { /* store is logically divided in three families of devices, depending on the ISAVAILABLE value; DEVLIST_ISAVAILABLE_NOTDET -> not detected devices: these are not-custom devices that are present in configuration file but they do not seem to be plugged at the moment, cause they're not in system_devices_list; DEVLIST_ISAVAILABLE_DET -> detected devices: these are not-custom devices that are currently plugged, no matter whether they are in configuration or not; DEVLIST_ISAVAILABLE_CUSTOM -> custom devices: defined by user in configuration file, these are not checked against system_devices_list; */ GList *system_devices_list = NULL; GList *config_devices_list = NULL; GList *list_iter = NULL; system_devices_list = ed_device_get_list_from_system(); config_devices_list = ed_device_get_list_from_config(); /* first, for each not-custom device in config_devices_list, perform a search in system_devices_list */ list_iter = config_devices_list; while ( list_iter != NULL ) { ed_device_info_t *info_from_cfg = list_iter->data; if ( info_from_cfg->is_custom == 0 ) /* only try to detect not-custom devices */ { /* note: if device is found, filename and phys in info_from_cfg could be updated by the check; the found device will be marked as "found" in system_devices_list too */ if ( ed_device_check( system_devices_list , info_from_cfg->name , &(info_from_cfg->filename) , &(info_from_cfg->phys) ) == ED_DEVCHECK_OK ) { info_from_cfg->reg = 1; /* mark our device from config as "found" */ } } list_iter = g_list_next( list_iter ); } /* at this point, we have three logical groups: #1 - not-custom devices in config_devices_list, marked as "found" -> DEVLIST_ISAVAILABLE_DET #2 - not-custom devices in config_devices_list, not-marked -> DEVLIST_ISAVAILABLE_NOTDET #3 - custom devices in config_devices_list -> DEVLIST_ISAVAILABLE_CUSTOM #1bis - plus, we pick not-marked devices from system_devices_list, which are plugged devices that are not present in config_devices_list -> DEVLIST_ISAVAILABLE_DET */ /* let's start to fill the store with items from group #1 (DEVLIST_ISAVAILABLE_DET) */ list_iter = config_devices_list; while ( list_iter != NULL ) { ed_device_info_t *info_from_cfg = list_iter->data; if (( info_from_cfg->reg == 1 ) && ( info_from_cfg->is_custom == 0 )) { GtkTreeIter iter; gtk_list_store_append( store , &iter ); gtk_list_store_set( store , &iter , DEVLIST_COL_ISACTIVE , info_from_cfg->is_active , DEVLIST_COL_NAME , info_from_cfg->name , DEVLIST_COL_FILENAME , info_from_cfg->filename , DEVLIST_COL_PHYS , info_from_cfg->phys , DEVLIST_COL_ISAVAILABLE , DEVLIST_ISAVAILABLE_DET , DEVLIST_COL_BINDINGS , info_from_cfg->bindings , -1 ); } list_iter = g_list_next( list_iter ); } /* add items from group #1bis (DEVLIST_ISAVAILABLE_DET) */ list_iter = system_devices_list; while ( list_iter != NULL ) { ed_device_info_t *info_from_sys = list_iter->data; if ( info_from_sys->reg == 0 ) { GtkTreeIter iter; gtk_list_store_append( store , &iter ); gtk_list_store_set( store , &iter , DEVLIST_COL_ISACTIVE , info_from_sys->is_active , DEVLIST_COL_NAME , info_from_sys->name , DEVLIST_COL_FILENAME , info_from_sys->filename , DEVLIST_COL_PHYS , info_from_sys->phys , DEVLIST_COL_ISAVAILABLE , DEVLIST_ISAVAILABLE_DET , DEVLIST_COL_BINDINGS , info_from_sys->bindings , -1 ); } list_iter = g_list_next( list_iter ); } /* add items from group #2 (DEVLIST_ISAVAILABLE_NOTDET) */ list_iter = config_devices_list; while ( list_iter != NULL ) { ed_device_info_t *info_from_cfg = list_iter->data; if (( info_from_cfg->reg == 0 ) && ( info_from_cfg->is_custom == 0 )) { GtkTreeIter iter; gtk_list_store_append( store , &iter ); gtk_list_store_set( store , &iter , DEVLIST_COL_ISACTIVE , info_from_cfg->is_active , DEVLIST_COL_NAME , info_from_cfg->name , DEVLIST_COL_FILENAME , info_from_cfg->filename , DEVLIST_COL_PHYS , info_from_cfg->phys , DEVLIST_COL_ISAVAILABLE , DEVLIST_ISAVAILABLE_NOTDET , DEVLIST_COL_BINDINGS , info_from_cfg->bindings , -1 ); } list_iter = g_list_next( list_iter ); } /* add items from group #3 (DEVLIST_ISAVAILABLE_CUSTOM) */ list_iter = config_devices_list; while ( list_iter != NULL ) { ed_device_info_t *info_from_cfg = list_iter->data; if ( info_from_cfg->is_custom == 1 ) { GtkTreeIter iter; gtk_list_store_append( store , &iter ); gtk_list_store_set( store , &iter , DEVLIST_COL_ISACTIVE , info_from_cfg->is_active , DEVLIST_COL_NAME , info_from_cfg->name , DEVLIST_COL_FILENAME , info_from_cfg->filename , DEVLIST_COL_PHYS , info_from_cfg->phys , DEVLIST_COL_ISAVAILABLE , DEVLIST_ISAVAILABLE_CUSTOM , DEVLIST_COL_BINDINGS , info_from_cfg->bindings , -1 ); } list_iter = g_list_next( list_iter ); } /* delete lists; bindings objects are not deleted, they must be accessible from liststore for customization and they'll be deleted when the config win is destroyed */ ed_device_free_list( config_devices_list ); ed_device_free_list( system_devices_list ); return; } static void cfg_device_lv_celldatafunc_isavailable( GtkTreeViewColumn * col , GtkCellRenderer * renderer , GtkTreeModel * model , GtkTreeIter * iter , gpointer data ) { guint is_available = 0; gtk_tree_model_get( model , iter , DEVLIST_COL_ISAVAILABLE , &is_available , -1 ); switch (is_available) { case DEVLIST_ISAVAILABLE_DET: g_object_set( renderer , "text" , "Detected" , "foreground" , "Green" , "foreground-set" , TRUE , "background" , "Black" , "background-set" , TRUE , NULL ); break; case DEVLIST_ISAVAILABLE_CUSTOM: g_object_set( renderer , "text" , "Custom" , "foreground" , "Yellow" , "foreground-set" , TRUE , "background" , "Black" , "background-set" , TRUE , NULL ); break; case DEVLIST_ISAVAILABLE_NOTDET: default: g_object_set( renderer , "text" , "Not Detected" , "foreground" , "Orange" , "foreground-set" , TRUE , "background" , "Black" , "background-set" , TRUE , NULL ); break; } return; } static void cfg_device_lv_changetoggle ( GtkCellRendererToggle * toggle , gchar * path_str , gpointer cfg_device_lv ) { GtkTreeModel *model = gtk_tree_view_get_model( GTK_TREE_VIEW(cfg_device_lv) ); GtkTreeIter iter; GtkTreePath *path = gtk_tree_path_new_from_string( path_str ); gboolean toggled; gtk_tree_model_get_iter( model , &iter , path ); gtk_tree_model_get( model , &iter , DEVLIST_COL_ISACTIVE , &toggled , -1 ); toggled ^= 1; gtk_list_store_set( GTK_LIST_STORE(model), &iter , DEVLIST_COL_ISACTIVE , toggled , -1 ); gtk_tree_path_free( path ); return; } static void cfg_config_cb_bindings_show ( gpointer cfg_device_lv ) { GtkTreeSelection *sel; GtkTreeModel *model; GtkTreeIter iter; /* check if a row is selected */ sel = gtk_tree_view_get_selection( GTK_TREE_VIEW(cfg_device_lv) ); if ( gtk_tree_selection_get_selected( sel , &model , &iter ) == TRUE ) { /* check availability and active status */ guint is_available = 0; gboolean is_active = FALSE; gtk_tree_model_get( model , &iter , DEVLIST_COL_ISACTIVE , &is_active , DEVLIST_COL_ISAVAILABLE , &is_available , -1 ); if ( is_available == DEVLIST_ISAVAILABLE_NOTDET ) { /* do not open bindings window for a not-detected device */ ed_ui_message_show( _("Information") , _("Cannot open bindings window for a not-detected device.\n" "Ensure that the device has been correctly plugged in.") , cfg_win ); return; } else { /* ok, create a ed_device_t item from selected row information */ ed_device_t *dev; gchar *device_name = NULL, *device_file = NULL, *device_phys = NULL; gpointer bindings = NULL; gtk_tree_model_get( model , &iter , DEVLIST_COL_NAME , &device_name , DEVLIST_COL_FILENAME , &device_file , DEVLIST_COL_PHYS , &device_phys , DEVLIST_COL_BINDINGS , &bindings , -1 ); dev = ed_device_new( device_name , device_file , device_phys , ( is_available == DEVLIST_ISAVAILABLE_CUSTOM ? 1 : 0 ) ); g_free( device_name ); g_free( device_file ); g_free( device_phys ); if ( dev != NULL ) { GtkTreePath *path; GtkTreeRowReference *rowref; dev->info->bindings = bindings; path = gtk_tree_model_get_path( model , &iter ); rowref = gtk_tree_row_reference_new( model , path ); gtk_tree_path_free( path ); /* show bindings window for device "dev"; also pass to the bindings window the GtkTreeRowReference (it keeps track of the model too) for selected row; if changes are committed in the bindings window, the row reference will be needed to update the treemodel with the new bindings information */ cfg_ui_bindings_show( dev , rowref ); } else { /* something went wrong */ ed_ui_message_show( _("Error") , _("Unable to open selected device.\n" "Please check read permissions on device file.") , cfg_win ); } } } } static void cfg_config_cb_addcustom_show ( gpointer cfg_device_lv ) { GtkWidget *addc_dlg; GtkWidget *addc_data_frame, *addc_data_vbox; GtkWidget *addc_data_label; GtkWidget *addc_data_table; GtkWidget *addc_data_name_label, *addc_data_name_entry; GtkWidget *addc_data_file_label, *addc_data_file_entry; gint result; gboolean task_done = FALSE; addc_dlg = gtk_dialog_new_with_buttons( _("EvDev-Plug - Add custom device") , GTK_WINDOW(cfg_win) , GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT , GTK_STOCK_CANCEL , GTK_RESPONSE_REJECT , GTK_STOCK_OK , GTK_RESPONSE_ACCEPT , NULL ); addc_data_frame = gtk_frame_new( NULL ); gtk_container_add( GTK_CONTAINER(GTK_DIALOG(addc_dlg)->vbox) , addc_data_frame ); addc_data_vbox = gtk_vbox_new( FALSE , 5 ); gtk_container_set_border_width( GTK_CONTAINER(addc_data_vbox) , 5 ); gtk_container_add( GTK_CONTAINER(addc_data_frame) , addc_data_vbox ); addc_data_label = gtk_label_new( "" ); gtk_label_set_text( GTK_LABEL(addc_data_label) , _("EvDev-Plug tries to automatically detect and update information about\n" "event devices available on the system.\n" "However, if auto-detect doesn't work for your system, or you have event\n" "devices in a non-standard location (currently they're only searched in\n" "/dev/input/ ), you may want to add a custom device, explicitly specifying\n" "name and device file.") ); gtk_box_pack_start( GTK_BOX(addc_data_vbox) , addc_data_label , FALSE , FALSE , 0 ); addc_data_name_label = gtk_label_new( _("Device name:") ); gtk_misc_set_alignment( GTK_MISC(addc_data_name_label) , 0 , 0.5 ); addc_data_name_entry = gtk_entry_new(); addc_data_file_label = gtk_label_new( _("Device file:") ); gtk_misc_set_alignment( GTK_MISC(addc_data_file_label) , 0 , 0.5 ); addc_data_file_entry = gtk_entry_new(); addc_data_table = gtk_table_new( 2 , 2 , FALSE ); gtk_table_set_col_spacings( GTK_TABLE(addc_data_table) , 2 ); gtk_table_set_row_spacings( GTK_TABLE(addc_data_table) , 2 ); gtk_table_attach( GTK_TABLE(addc_data_table) , addc_data_name_label , 0 , 1 , 0 , 1 , GTK_FILL , GTK_EXPAND | GTK_FILL , 0 , 0 ); gtk_table_attach( GTK_TABLE(addc_data_table) , addc_data_name_entry , 1 , 2 , 0 , 1 , GTK_EXPAND | GTK_FILL , GTK_EXPAND | GTK_FILL , 0 , 0 ); gtk_table_attach( GTK_TABLE(addc_data_table) , addc_data_file_label , 0 , 1 , 1 , 2 , GTK_FILL , GTK_EXPAND | GTK_FILL , 0 , 0 ); gtk_table_attach( GTK_TABLE(addc_data_table) , addc_data_file_entry , 1 , 2 , 1 , 2 , GTK_EXPAND | GTK_FILL , GTK_EXPAND | GTK_FILL , 0 , 0 ); gtk_box_pack_start( GTK_BOX(addc_data_vbox) , addc_data_table , TRUE , TRUE , 0 ); gtk_widget_show_all( addc_dlg ); while ( task_done == FALSE ) { result = gtk_dialog_run( GTK_DIALOG(addc_dlg) ); if ( result == GTK_RESPONSE_ACCEPT ) { const gchar *name = gtk_entry_get_text( GTK_ENTRY(addc_data_name_entry) ); const gchar *file = gtk_entry_get_text( GTK_ENTRY(addc_data_file_entry) ); if ( ( strcmp( name , "" ) != 0 ) && ( strcmp( name , "___plugin___" ) != 0 ) && ( strcmp( file , "" ) != 0 ) && ( file[0] == '/' ) ) { GtkTreeModel *model = gtk_tree_view_get_model( GTK_TREE_VIEW(cfg_device_lv) ); GtkTreeIter iter; gtk_list_store_append( GTK_LIST_STORE(model) , &iter ); gtk_list_store_set( GTK_LIST_STORE(model) , &iter , DEVLIST_COL_ISACTIVE , FALSE , DEVLIST_COL_NAME , name , DEVLIST_COL_FILENAME , file , DEVLIST_COL_PHYS , "(custom)" , DEVLIST_COL_ISAVAILABLE , DEVLIST_ISAVAILABLE_CUSTOM , DEVLIST_COL_BINDINGS , NULL , -1 ); task_done = TRUE; } else { ed_ui_message_show( _("Information") , _("Please specify both name and filename.\n" "Filename must be specified with absolute path.") , addc_dlg ); } } else task_done = TRUE; } gtk_widget_destroy( addc_dlg ); } static void cfg_config_cb_remove ( gpointer cfg_device_lv ) { GtkTreeSelection *sel; GtkTreeModel *model; GtkTreeIter iter; /* check if a row is selected */ sel = gtk_tree_view_get_selection( GTK_TREE_VIEW(cfg_device_lv) ); if ( gtk_tree_selection_get_selected( sel , &model , &iter ) == TRUE ) { guint is_available = 0; gtk_tree_model_get( model , &iter , DEVLIST_COL_ISAVAILABLE , &is_available , -1 ); switch ( is_available ) { case DEVLIST_ISAVAILABLE_NOTDET: { /* if remove is called on a not-detected device, it allows to remove existing configuration for it */ GtkWidget *yesno_dlg = gtk_message_dialog_new( GTK_WINDOW(cfg_win) , GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT , GTK_MESSAGE_QUESTION , GTK_BUTTONS_YES_NO , _("Do you want to remove the existing configuration for selected device?\n") ); if ( gtk_dialog_run( GTK_DIALOG(yesno_dlg) ) == GTK_RESPONSE_YES ) { gpointer bindings = NULL; gtk_tree_model_get( model , &iter , DEVLIST_COL_BINDINGS , &bindings , -1 ); if ( bindings != NULL ) ed_bindings_store_delete( bindings ); gtk_list_store_remove( GTK_LIST_STORE(model) , &iter ); } gtk_widget_destroy( yesno_dlg ); break; } case DEVLIST_ISAVAILABLE_CUSTOM: { /* if remove is called on a custom device, it allows to remove it along with its configuration */ GtkWidget *yesno_dlg = gtk_message_dialog_new( GTK_WINDOW(cfg_win) , GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT , GTK_MESSAGE_QUESTION , GTK_BUTTONS_YES_NO , _("Do you want to remove the selected custom device?\n") ); if ( gtk_dialog_run( GTK_DIALOG(yesno_dlg) ) == GTK_RESPONSE_YES ) { gpointer bindings = NULL; gtk_tree_model_get( model , &iter , DEVLIST_COL_BINDINGS , &bindings , -1 ); if ( bindings != NULL ) ed_bindings_store_delete( bindings ); gtk_list_store_remove( GTK_LIST_STORE(model) , &iter ); } gtk_widget_destroy( yesno_dlg ); break; } case DEVLIST_ISAVAILABLE_DET: default: { /* do nothing for detected devices */ break; } } } } static gboolean cfg_config_cb_bindings_commit_foreach ( GtkTreeModel * model , GtkTreePath * path , GtkTreeIter * iter , gpointer config_device_list_p ) { gchar *device_name = NULL; gchar *device_file = NULL; gchar *device_phys = NULL; gboolean is_active = FALSE; guint is_available = 0; gpointer bindings = NULL; ed_device_info_t *info; GList **config_device_list = config_device_list_p; gtk_tree_model_get( model , iter , DEVLIST_COL_ISACTIVE , &is_active , DEVLIST_COL_NAME , &device_name , DEVLIST_COL_FILENAME , &device_file , DEVLIST_COL_PHYS , &device_phys , DEVLIST_COL_ISAVAILABLE , &is_available , DEVLIST_COL_BINDINGS , &bindings , -1 ); info = ed_device_info_new( device_name , device_file , device_phys , ( is_available == DEVLIST_ISAVAILABLE_CUSTOM ? 1 : 0 ) ); info->bindings = bindings; info->is_active = is_active; *config_device_list = g_list_append( *config_device_list , info ); return FALSE; } static gboolean cfg_config_cb_bindings_delbindings_foreach ( GtkTreeModel * model , GtkTreePath * path , GtkTreeIter * iter , gpointer data ) { gpointer bindings = NULL; gtk_tree_model_get( model , iter , DEVLIST_COL_BINDINGS , &bindings , -1 ); if ( bindings != NULL ) ed_bindings_store_delete( bindings ); return FALSE; } static void cfg_config_cb_commit ( gpointer cfg_device_lv ) { GList *config_device_list = NULL; GList *list_iter; GtkTreeModel *model; model = gtk_tree_view_get_model( GTK_TREE_VIEW(cfg_device_lv) ); /* fill config_device_list with information from the treeview */ gtk_tree_model_foreach( model , cfg_config_cb_bindings_commit_foreach , &config_device_list ); /* ok, now we have a list of ed_device_info_t objects */ /* commit changes in configuration file */ ed_config_save_from_list( config_device_list ); /* not needed anymore */ ed_device_free_list( config_device_list ); /* free bindings stored in the liststore */ gtk_tree_model_foreach( model , cfg_config_cb_bindings_delbindings_foreach , NULL ); if ( plugin_is_active == TRUE ) { /* restart device listening according to the (possibly changed) config file */ ed_device_start_listening_from_config(); } gtk_widget_destroy( cfg_win ); } static void cfg_config_cb_cancel ( gpointer cfg_device_lv ) { GtkTreeModel *model; model = gtk_tree_view_get_model( GTK_TREE_VIEW(cfg_device_lv) ); /* free bindings stored in the liststore */ gtk_tree_model_foreach( model , cfg_config_cb_bindings_delbindings_foreach , NULL ); if ( plugin_is_active == TRUE ) { /* restart device listening according to the (unchanged) config file */ ed_device_start_listening_from_config(); } gtk_widget_destroy( cfg_win ); } void ed_ui_config_show( void ) { GtkWidget *cfg_vbox; GtkWidget *cfg_device_lv, *cfg_device_lv_frame, *cfg_device_lv_sw; GtkTreeViewColumn *cfg_device_lv_col_name, *cfg_device_lv_col_filename, *cfg_device_lv_col_phys; GtkTreeViewColumn *cfg_device_lv_col_isactive, *cfg_device_lv_col_isavailable; GtkCellRenderer *cfg_device_lv_rndr_text, *cfg_device_lv_rndr_toggle; GtkCellRenderer *cfg_device_lv_rndr_textphys, *cfg_device_lv_rndr_isavailable; GtkListStore *device_store; GtkWidget *cfg_bbar_hbbox; GtkWidget *cfg_bbar_bt_bind, *cfg_bbar_bt_addc, *cfg_bbar_bt_remc; GtkWidget *cfg_bbar_bt_cancel, *cfg_bbar_bt_ok; GdkGeometry cfg_win_hints; if ( cfg_win != NULL ) { gtk_window_present( GTK_WINDOW(cfg_win) ); return; } /* IMPORTANT: stop listening for all devices when config window is opened */ ed_device_stop_listening_all( TRUE ); cfg_win = gtk_window_new( GTK_WINDOW_TOPLEVEL ); gtk_window_set_type_hint( GTK_WINDOW(cfg_win), GDK_WINDOW_TYPE_HINT_DIALOG ); gtk_window_set_position( GTK_WINDOW(cfg_win), GTK_WIN_POS_CENTER ); gtk_window_set_title( GTK_WINDOW(cfg_win), _("EvDev-Plug - Configuration") ); gtk_container_set_border_width( GTK_CONTAINER(cfg_win), 10 ); g_signal_connect( G_OBJECT(cfg_win) , "destroy" , G_CALLBACK(gtk_widget_destroyed) , &cfg_win ); cfg_win_hints.min_width = -1; cfg_win_hints.min_height = 300; gtk_window_set_geometry_hints( GTK_WINDOW(cfg_win) , GTK_WIDGET(cfg_win) , &cfg_win_hints , GDK_HINT_MIN_SIZE ); cfg_vbox = gtk_vbox_new( FALSE , 0 ); gtk_container_add( GTK_CONTAINER(cfg_win) , cfg_vbox ); /* current liststore model ---------------------------------------------- G_TYPE_BOOLEAN -> device listening on/off G_TYPE_STRING -> device name G_TYPE_STRING -> device filename G_TYPE_STRING -> device physical port G_TYPE_UINT -> device is available G_TYPE_POINTER -> device bindings ---------------------------------------------- */ device_store = gtk_list_store_new( DEVLIST_NUMCOLS , G_TYPE_BOOLEAN , G_TYPE_STRING , G_TYPE_STRING , G_TYPE_STRING , G_TYPE_UINT , G_TYPE_POINTER ); cfg_device_lv_populate( device_store ); cfg_device_lv_frame = gtk_frame_new( NULL ); cfg_device_lv = gtk_tree_view_new_with_model( GTK_TREE_MODEL(device_store) ); g_object_unref( device_store ); cfg_device_lv_rndr_text = gtk_cell_renderer_text_new(); cfg_device_lv_rndr_toggle = gtk_cell_renderer_toggle_new(); cfg_device_lv_rndr_isavailable = gtk_cell_renderer_text_new(); cfg_device_lv_rndr_textphys = gtk_cell_renderer_text_new(); g_object_set( G_OBJECT(cfg_device_lv_rndr_textphys) , "ellipsize-set" , TRUE , "ellipsize" , PANGO_ELLIPSIZE_END , NULL ); cfg_device_lv_col_isactive = gtk_tree_view_column_new_with_attributes( _("Active") , cfg_device_lv_rndr_toggle , "active" , DEVLIST_COL_ISACTIVE , NULL ); gtk_tree_view_column_set_expand( GTK_TREE_VIEW_COLUMN(cfg_device_lv_col_isactive) , FALSE ); gtk_tree_view_append_column( GTK_TREE_VIEW(cfg_device_lv), cfg_device_lv_col_isactive ); cfg_device_lv_col_isavailable = gtk_tree_view_column_new(); gtk_tree_view_column_set_title( cfg_device_lv_col_isavailable , _("Status") ); gtk_tree_view_column_pack_start( cfg_device_lv_col_isavailable , cfg_device_lv_rndr_isavailable , TRUE ); gtk_tree_view_column_set_cell_data_func( cfg_device_lv_col_isavailable , cfg_device_lv_rndr_isavailable , (GtkTreeCellDataFunc)cfg_device_lv_celldatafunc_isavailable , NULL , NULL ); gtk_tree_view_column_set_expand( GTK_TREE_VIEW_COLUMN(cfg_device_lv_col_isavailable) , FALSE ); gtk_tree_view_append_column( GTK_TREE_VIEW(cfg_device_lv), cfg_device_lv_col_isavailable ); cfg_device_lv_col_name = gtk_tree_view_column_new_with_attributes( _("Device Name") , cfg_device_lv_rndr_text , "text" , DEVLIST_COL_NAME , NULL ); gtk_tree_view_column_set_expand( GTK_TREE_VIEW_COLUMN(cfg_device_lv_col_name) , FALSE ); gtk_tree_view_append_column( GTK_TREE_VIEW(cfg_device_lv), cfg_device_lv_col_name ); cfg_device_lv_col_filename = gtk_tree_view_column_new_with_attributes( _("Device File") , cfg_device_lv_rndr_text , "text" , DEVLIST_COL_FILENAME , NULL ); gtk_tree_view_column_set_expand( GTK_TREE_VIEW_COLUMN(cfg_device_lv_col_filename) , FALSE ); gtk_tree_view_append_column( GTK_TREE_VIEW(cfg_device_lv), cfg_device_lv_col_filename ); cfg_device_lv_col_phys = gtk_tree_view_column_new_with_attributes( _("Device Address") , cfg_device_lv_rndr_textphys , "text" , DEVLIST_COL_PHYS , NULL ); gtk_tree_view_column_set_expand( GTK_TREE_VIEW_COLUMN(cfg_device_lv_col_phys) , TRUE ); gtk_tree_view_column_set_resizable( GTK_TREE_VIEW_COLUMN(cfg_device_lv_col_phys) , TRUE ); gtk_tree_view_append_column( GTK_TREE_VIEW(cfg_device_lv), cfg_device_lv_col_phys ); cfg_device_lv_sw = gtk_scrolled_window_new( NULL , NULL ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(cfg_device_lv_sw) , GTK_POLICY_NEVER , GTK_POLICY_ALWAYS ); gtk_container_add( GTK_CONTAINER(cfg_device_lv_sw) , cfg_device_lv ); gtk_container_add( GTK_CONTAINER(cfg_device_lv_frame) , cfg_device_lv_sw ); gtk_box_pack_start( GTK_BOX(cfg_vbox) , cfg_device_lv_frame , TRUE , TRUE , 0 ); gtk_box_pack_start( GTK_BOX(cfg_vbox) , gtk_hseparator_new() , FALSE , FALSE , 4 ); /* button bar */ cfg_bbar_hbbox = gtk_hbutton_box_new(); gtk_button_box_set_layout( GTK_BUTTON_BOX(cfg_bbar_hbbox) , GTK_BUTTONBOX_START ); cfg_bbar_bt_bind = gtk_button_new_with_mnemonic( _("_Bindings") ); gtk_button_set_image( GTK_BUTTON(cfg_bbar_bt_bind) , gtk_image_new_from_stock( GTK_STOCK_PREFERENCES , GTK_ICON_SIZE_BUTTON ) ); cfg_bbar_bt_addc = gtk_button_new_from_stock( GTK_STOCK_ADD ); cfg_bbar_bt_remc = gtk_button_new_from_stock( GTK_STOCK_REMOVE ); /*cfg_bbar_bt_refresh = gtk_button_new_from_stock( GTK_STOCK_REFRESH );*/ cfg_bbar_bt_cancel = gtk_button_new_from_stock( GTK_STOCK_CANCEL ); cfg_bbar_bt_ok = gtk_button_new_from_stock( GTK_STOCK_OK ); gtk_container_add( GTK_CONTAINER(cfg_bbar_hbbox) , cfg_bbar_bt_bind ); gtk_container_add( GTK_CONTAINER(cfg_bbar_hbbox) , cfg_bbar_bt_addc ); gtk_container_add( GTK_CONTAINER(cfg_bbar_hbbox) , cfg_bbar_bt_remc ); /*gtk_container_add( GTK_CONTAINER(cfg_bbar_hbbox) , cfg_bbar_bt_refresh );*/ gtk_container_add( GTK_CONTAINER(cfg_bbar_hbbox) , cfg_bbar_bt_cancel ); gtk_container_add( GTK_CONTAINER(cfg_bbar_hbbox) , cfg_bbar_bt_ok ); gtk_button_box_set_child_secondary( GTK_BUTTON_BOX(cfg_bbar_hbbox) , cfg_bbar_bt_cancel , TRUE ); gtk_button_box_set_child_secondary( GTK_BUTTON_BOX(cfg_bbar_hbbox) , cfg_bbar_bt_ok , TRUE ); gtk_box_pack_start( GTK_BOX(cfg_vbox) , cfg_bbar_hbbox , FALSE , FALSE , 0 ); g_signal_connect( cfg_device_lv_rndr_toggle , "toggled" , G_CALLBACK(cfg_device_lv_changetoggle) , cfg_device_lv ); g_signal_connect_swapped( G_OBJECT(cfg_bbar_bt_bind) , "clicked" , G_CALLBACK(cfg_config_cb_bindings_show) , cfg_device_lv ); g_signal_connect_swapped( G_OBJECT(cfg_bbar_bt_addc) , "clicked" , G_CALLBACK(cfg_config_cb_addcustom_show) , cfg_device_lv ); g_signal_connect_swapped( G_OBJECT(cfg_bbar_bt_remc) , "clicked" , G_CALLBACK(cfg_config_cb_remove) , cfg_device_lv ); g_signal_connect_swapped( G_OBJECT(cfg_bbar_bt_cancel) , "clicked" , G_CALLBACK(cfg_config_cb_cancel) , cfg_device_lv ); g_signal_connect_swapped( G_OBJECT(cfg_bbar_bt_ok) , "clicked" , G_CALLBACK(cfg_config_cb_commit) , cfg_device_lv ); gtk_widget_show_all( cfg_win ); } /* bindings window */ static void cfg_bindbox_new_empty_row( GtkTable * , GtkWidget * , gboolean ); enum { BINDLIST_COL_COMBO_ACTION = 0, BINDLIST_COL_BT_ASSIGN, BINDLIST_COL_LABEL_EVCODE, BINDLIST_COL_BT_DELETE, BINDLIST_NUMCOLS }; static gboolean cfg_bindbox_assign_binding_timeout_func ( gpointer bindings_win ) { GtkWidget *trigger_dlg = g_object_get_data( G_OBJECT(bindings_win) , "trigger-win" ); if ( trigger_dlg != NULL ) gtk_dialog_response( GTK_DIALOG(trigger_dlg) , GTK_RESPONSE_CANCEL ); return FALSE; } static gboolean cfg_bindbox_assign_binding_input_func ( GIOChannel * iochan , GIOCondition cond , gpointer bindings_win ) { switch ( cond ) { case G_IO_IN: { gsize rb = 0; GError *gerr = NULL; struct input_event inputev; g_io_channel_read_chars( iochan , (gchar*)&inputev , sizeof(struct input_event) , &rb , NULL ); if ( rb == sizeof(struct input_event) ) { GtkWidget *trigger_dlg = g_object_get_data( G_OBJECT(bindings_win) , "trigger-win" ); if ( trigger_dlg == NULL ) { /* the trigger-dialog window is not open; ignore input */ return TRUE; } else { /* the trigger-dialog window is open; record input and store it in a container managed by the trigger-dialog itself */ ed_inputevent_t *dinputev = g_malloc(sizeof(ed_inputevent_t*)); dinputev->type = inputev.type; dinputev->code = inputev.code; dinputev->value = inputev.value; g_object_set_data( G_OBJECT(trigger_dlg) , "trigger-data" , dinputev ); gtk_dialog_response( GTK_DIALOG(trigger_dlg) , GTK_RESPONSE_OK ); } } } } return TRUE; } static gboolean cfg_bindbox_assign_binding_checkdups( GtkWidget * table , ed_inputevent_t * inputev ) { /* check if inputev is already assigned in table */ GList *children = GTK_TABLE(table)->children; for ( children ; children != NULL ; children = g_list_next(children) ) { GtkTableChild *child = children->data; if ( child->top_attach + 1 == GTK_TABLE(table)->nrows ) continue; /* skip last empty row */ if ( child->left_attach == BINDLIST_COL_LABEL_EVCODE ) { /* it's a label, pick its inputevent */ ed_inputevent_t * iev = g_object_get_data( G_OBJECT(child->widget) , "inputevent" ); if ( ( iev != NULL ) && ( ed_inputevent_check_equality( inputev , iev ) == TRUE ) ) return TRUE; /* a duplicate was found */ } } return FALSE; } static void cfg_bindbox_assign_binding ( GtkButton * assign_button , gpointer bindings_win ) { GtkWidget *trigger_dlg; GtkWidget *trigger_dlg_frame; GtkWidget *trigger_dlg_label; guint timeout_sid = 0; gint res = 0; /* create a not-decorated window to inform the user about what's going on */ trigger_dlg = gtk_dialog_new(); gtk_widget_set_name( trigger_dlg , "trigger_dlg" ); trigger_dlg_label = gtk_label_new( _("Press a key of your device to bind it;\n" "if no key is pressed in five seconds, this window\n" "will close without binding changes.") ); gtk_widget_hide( GTK_WIDGET(GTK_DIALOG(trigger_dlg)->action_area) ); gtk_misc_set_padding( GTK_MISC(trigger_dlg_label) , 10 , 10 ); trigger_dlg_frame = gtk_frame_new( NULL ); gtk_container_add( GTK_CONTAINER(trigger_dlg_frame) , trigger_dlg_label ); gtk_container_add( GTK_CONTAINER(GTK_DIALOG(trigger_dlg)->vbox) , trigger_dlg_frame ); gtk_window_set_position( GTK_WINDOW(trigger_dlg) , GTK_WIN_POS_CENTER ); gtk_window_set_decorated( GTK_WINDOW(trigger_dlg) , FALSE ); gtk_window_set_transient_for( GTK_WINDOW(trigger_dlg) , GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(assign_button))) ); gtk_dialog_set_has_separator( GTK_DIALOG(trigger_dlg) , FALSE ); gtk_widget_show_all( trigger_dlg ); /* close the trigger-window if no input is received in 5 seconds */ timeout_sid = g_timeout_add( 5000 , cfg_bindbox_assign_binding_timeout_func , bindings_win ); g_object_set_data( G_OBJECT(bindings_win) , "trigger-win" , trigger_dlg ); /* enable trigger-listening */ res = gtk_dialog_run( GTK_DIALOG(trigger_dlg) ); g_object_set_data( G_OBJECT(bindings_win) , "trigger-win" , NULL ); /* stop trigger-listening */ switch ( res ) { case GTK_RESPONSE_OK: { ed_inputevent_t *dinputev = g_object_get_data( G_OBJECT(trigger_dlg) , "trigger-data" ); GtkWidget *label = g_object_get_data( G_OBJECT(assign_button) , "label" ); GtkWidget *table = g_object_get_data( G_OBJECT(bindings_win) , "table" ); gchar *input_str; /* we got a new input event; ensure that this has not been assigned to an action already */ if ( cfg_bindbox_assign_binding_checkdups( table , dinputev ) == TRUE ) { /* this input event has been already used */ g_free( dinputev ); g_source_remove( timeout_sid ); ed_ui_message_show( _("Information") , _("This input event has been already assigned.\n\n" "It's not possible to assign multiple actions to the same input event " "(although it's possible to assign the same action to multiple events).") , bindings_win ); } else { /* if this is the last row ("Assign" button) , create a new empty row */ if ( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(assign_button),"last")) == 1 ) { cfg_bindbox_new_empty_row( GTK_TABLE(table) , bindings_win , TRUE ); /* so, the current row is not the last one anymore; remove its "last" mark and enable its delete button */ g_object_set_data( G_OBJECT(assign_button) , "last" , GINT_TO_POINTER(0) ); gtk_widget_set_sensitive( GTK_WIDGET(g_object_get_data(G_OBJECT(assign_button),"delbt")) , TRUE ); } g_object_set_data_full( G_OBJECT(label) , "inputevent" , dinputev , g_free ); input_str = g_strdup_printf( "%i:%i:%i" , dinputev->type , dinputev->code , dinputev->value ); gtk_label_set_text( GTK_LABEL(label) , input_str ); g_free( input_str ); g_source_remove( timeout_sid ); } break; } default: { break; /* nothing should be changed */ } } gtk_widget_destroy( trigger_dlg ); } static void cfg_bindbox_delete_row( GtkButton * delete_button , gpointer bindings_win ) { GtkTable *oldtable, *newtable; GtkWidget *table_sw, *table_vp; GList *children = NULL; gint drow = 0; /* here comes the drawback of using GtkTable to display a list-like set of widgets :) deletion can be a complex task */ /* first, get the row number of the row that should be deleted (this can't be the last one cause it has the delete button disabled) */ oldtable = g_object_get_data( G_OBJECT(bindings_win) , "table" ); children = oldtable->children; while ( children != NULL ) { GtkTableChild *child = children->data; if ( child->widget == (GtkWidget*)delete_button ) drow = child->top_attach; children = g_list_next( children ); } /* now, a simple trick: create a new GtkTable and move all of the widgets there, excepting those positioned in row "drow" */ newtable = (GtkTable*)gtk_table_new( oldtable->nrows - 1 , oldtable->ncols , FALSE ); children = oldtable->children; while ( children != NULL ) { GtkTableChild *child = children->data; if ( child->top_attach < drow ) { GtkWidget *widget = child->widget; guint widget_row = child->top_attach; guint widget_col = child->left_attach; g_object_ref( widget ); gtk_container_remove( GTK_CONTAINER(oldtable) , widget ); gtk_table_attach( GTK_TABLE(newtable) , widget , widget_col , widget_col + 1 , widget_row , widget_row + 1 , (widget_col == BINDLIST_COL_LABEL_EVCODE ? GTK_EXPAND | GTK_FILL : GTK_FILL ) , GTK_FILL , 0 , 0 ); /* gtk_container_remove() changed the structure used to iterate; restart */ children = oldtable->children; } else if ( child->top_attach > drow ) { GtkWidget *widget = child->widget; guint widget_row = child->top_attach; guint widget_col = child->left_attach; g_object_ref( widget ); gtk_container_remove( GTK_CONTAINER(oldtable) , widget ); gtk_table_attach( GTK_TABLE(newtable) , widget , widget_col , widget_col + 1 , widget_row - 1 , widget_row , (widget_col == BINDLIST_COL_LABEL_EVCODE ? GTK_EXPAND | GTK_FILL : GTK_FILL ) , GTK_FILL , 0 , 0 ); /* gtk_container_remove() changed the structure used to iterate; restart */ children = oldtable->children; } else children = g_list_next(children); } /* now, replace the old GtkTable with the new one */ table_sw = g_object_get_data( G_OBJECT(bindings_win) , "tablesw" ); table_vp = gtk_bin_get_child( GTK_BIN(table_sw) ); /* get the viewport */ gtk_widget_destroy( GTK_WIDGET(oldtable) ); /* destroy old GtkTable */ gtk_container_add( GTK_CONTAINER(table_vp) , GTK_WIDGET(newtable) ); g_object_set_data( G_OBJECT(bindings_win) , "table" , newtable ); /* update table in bindings_win */ gtk_widget_show_all( GTK_WIDGET(newtable) ); return; } /* the three structure types defined below are only used in cfg_bindbox_populate process */ typedef struct { GtkWidget *combobox; gint action_code; } combosas_helper_t; typedef struct { gint action_code; ed_inputevent_t * inputev; } popul_binding_t; typedef struct { gint id; popul_binding_t *bindings; } popul_container_binding_t; static gboolean cfg_bindbox_populate_foreach_combosas ( GtkTreeModel * model , GtkTreePath * path , GtkTreeIter * iter , gpointer combosas_helper_gp ) { gint action_code = 0; combosas_helper_t *combosas_helper = combosas_helper_gp; gtk_tree_model_get( model , iter , 1 , &action_code , -1 ); if ( action_code == combosas_helper->action_code ) { gtk_combo_box_set_active_iter( GTK_COMBO_BOX(combosas_helper->combobox) , iter ); return TRUE; } return FALSE; } static void cfg_bindbox_populate_foreach ( ed_inputevent_t * sinputev , gint action_code , gpointer popul_container_gp , gpointer none ) { ed_inputevent_t *inputev; popul_container_binding_t *popul_container = popul_container_gp; /* make a copy of the ed_inputevent_t object "sinputev" */ inputev = g_malloc(sizeof(ed_inputevent_t)); inputev->type = sinputev->type; inputev->code = sinputev->code; inputev->value = sinputev->value; popul_container->bindings[popul_container->id].inputev = inputev; popul_container->bindings[popul_container->id].action_code = action_code; popul_container->id++; return; } static int cfg_bindbox_populate_qsortfunc( const void * p_binding_a , const void * p_binding_b ) { return ((popul_binding_t*)p_binding_a)->action_code - ((popul_binding_t*)p_binding_b)->action_code; } static void cfg_bindbox_populate( GtkWidget * bind_table , GtkWidget * bindings_win , gpointer bindings ) { if ( bindings != NULL ) { guint bindings_num = ed_bindings_store_size( bindings ); popul_binding_t *popul_binding = (popul_binding_t*)calloc( bindings_num , sizeof(popul_binding_t) ); popul_container_binding_t *popul_container = g_malloc(sizeof(popul_container_binding_t)); gint i = 0; popul_container->bindings = popul_binding; popul_container->id = 0; ed_bindings_store_foreach( bindings , (ed_bindings_store_foreach_func)cfg_bindbox_populate_foreach , popul_container , NULL ); /* ok, now we have a popul_binding array, reorder it using action_codes */ qsort( popul_binding , bindings_num , sizeof(popul_binding_t) , cfg_bindbox_populate_qsortfunc ); /* attach to table each popul_binding */ for ( i = 0 ; i < bindings_num ; i++ ) { GList *children = GTK_TABLE(bind_table)->children; for ( children ; children != NULL ; children = g_list_next(children) ) { GtkTableChild *child = children->data; if ( ( (child->top_attach + 1) == GTK_TABLE(bind_table)->nrows ) && ( child->left_attach == BINDLIST_COL_BT_ASSIGN ) && ( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(child->widget),"last")) == 1 ) ) { /* ok, this child->widget is the last assign button */ GtkWidget *combobox = g_object_get_data(G_OBJECT(child->widget),"combobox"); GtkWidget *label = g_object_get_data(G_OBJECT(child->widget),"label"); GtkWidget *delbt = g_object_get_data(G_OBJECT(child->widget),"delbt"); GtkTreeModel *combomodel; GtkTreeIter comboiter; combosas_helper_t *combosas_helper; gchar *input_str; combomodel = gtk_combo_box_get_model( GTK_COMBO_BOX(combobox) ); combosas_helper = g_malloc(sizeof(combosas_helper_t)); combosas_helper->combobox = combobox; combosas_helper->action_code = popul_binding[i].action_code; gtk_tree_model_foreach( combomodel , cfg_bindbox_populate_foreach_combosas , combosas_helper ); g_free( combosas_helper ); g_object_set_data_full( G_OBJECT(label) , "inputevent" , popul_binding[i].inputev , g_free ); input_str = g_strdup_printf( "%i:%i:%i" , popul_binding[i].inputev->type , popul_binding[i].inputev->code , popul_binding[i].inputev->value ); gtk_label_set_text( GTK_LABEL(label) , input_str ); g_free( input_str ); /* so, the current row wont' be the last one anymore; remove its "last" mark and enable its delete button */ g_object_set_data( G_OBJECT(child->widget) , "last" , GINT_TO_POINTER(0) ); gtk_widget_set_sensitive( delbt , TRUE ); cfg_bindbox_new_empty_row( GTK_TABLE(bind_table) , bindings_win , TRUE ); } } DEBUGMSG( "populating bindings window: code %i -> event %d:%d:%d\n" , popul_binding[i].action_code , popul_binding[i].inputev->type, popul_binding[i].inputev->code, popul_binding[i].inputev->value ); } g_free( popul_binding ); g_free( popul_container ); } return; } static void cfg_bindbox_new_empty_row( GtkTable * bind_table , GtkWidget * bindings_win , gboolean resize_table ) { GtkWidget *action_combobox; GtkCellRenderer *action_combobox_rndr; GtkWidget *assign_button; GtkWidget *assign_label; GtkWidget *delete_button; /* add one row to the table */ if ( resize_table == TRUE ) gtk_table_resize( bind_table , bind_table->nrows + 1 , bind_table->ncols ); /* action combobox */ action_combobox = gtk_combo_box_new_with_model( GTK_TREE_MODEL(g_object_get_data(G_OBJECT(bindings_win),"action_store")) ); action_combobox_rndr = gtk_cell_renderer_text_new(); gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(action_combobox) , action_combobox_rndr , TRUE ); gtk_cell_layout_set_attributes( GTK_CELL_LAYOUT(action_combobox) , action_combobox_rndr , "text" , 0 , NULL ); gtk_combo_box_set_active( GTK_COMBO_BOX(action_combobox) , 0 ); gtk_table_attach( bind_table , action_combobox , BINDLIST_COL_COMBO_ACTION , BINDLIST_COL_COMBO_ACTION + 1 , bind_table->nrows - 1 , bind_table->nrows , GTK_FILL , GTK_FILL , 0 , 0 ); /* assign button */ assign_button = gtk_button_new(); gtk_button_set_image( GTK_BUTTON(assign_button) , gtk_image_new_from_stock( GTK_STOCK_EXECUTE , GTK_ICON_SIZE_BUTTON ) ); g_object_set_data( G_OBJECT(assign_button) , "last" , GINT_TO_POINTER(1) ); g_signal_connect( G_OBJECT(assign_button) , "clicked" , G_CALLBACK(cfg_bindbox_assign_binding) , bindings_win ); gtk_table_attach( bind_table , assign_button , BINDLIST_COL_BT_ASSIGN , BINDLIST_COL_BT_ASSIGN + 1 , bind_table->nrows - 1 , bind_table->nrows , GTK_FILL , GTK_FILL , 0 , 0 ); /* assigned-code label */ assign_label = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC(assign_label) , 0 , 0.5 ); gtk_misc_set_padding( GTK_MISC(assign_label) , 10 , 0 ); g_object_set_data_full( G_OBJECT(assign_label) , "inputevent" , NULL , g_free ); gtk_table_attach( bind_table , assign_label , BINDLIST_COL_LABEL_EVCODE , BINDLIST_COL_LABEL_EVCODE +1 , bind_table->nrows - 1 , bind_table->nrows , GTK_EXPAND | GTK_FILL , GTK_FILL , 0 , 0 ); /* delete button */ delete_button = gtk_button_new(); gtk_button_set_image( GTK_BUTTON(delete_button) , gtk_image_new_from_stock( GTK_STOCK_DELETE , GTK_ICON_SIZE_BUTTON ) ); g_signal_connect( G_OBJECT(delete_button) , "clicked" , G_CALLBACK(cfg_bindbox_delete_row) , bindings_win ); gtk_widget_set_sensitive( delete_button , FALSE ); gtk_table_attach( bind_table , delete_button , BINDLIST_COL_BT_DELETE , BINDLIST_COL_BT_DELETE + 1 , bind_table->nrows - 1 , bind_table->nrows , GTK_FILL , GTK_FILL , 0 , 0 ); /* data for assign button */ g_object_set_data( G_OBJECT(assign_button) , "combobox" , action_combobox ); g_object_set_data( G_OBJECT(assign_button) , "label" , assign_label ); g_object_set_data( G_OBJECT(assign_button) , "delbt" , delete_button ); gtk_widget_show_all( GTK_WIDGET(bind_table) ); return; } static void cfg_bindings_cb_destroy ( GtkObject * bindings_win , gpointer dev_gp ) { ed_device_t *dev = dev_gp; /* bindings window is going to be destroyed; deactivate device listening and free the ed_device_t object */ g_source_remove( dev->iochan_sid ); ed_device_delete( dev ); /* the rowreference is no longer needed as well */ gtk_tree_row_reference_free( (GtkTreeRowReference*)g_object_get_data(G_OBJECT(bindings_win),"rowref") ); return; } static void cfg_bindings_cb_commit ( gpointer bindings_win ) { GtkTreeRowReference *rowref = g_object_get_data(G_OBJECT(bindings_win),"rowref"); if ( gtk_tree_row_reference_valid( rowref ) == TRUE ) { GtkTreeModel *model = gtk_tree_row_reference_get_model( rowref ); GtkTreePath *path = gtk_tree_row_reference_get_path( rowref ); GtkTreeIter iter; GtkTable *table; gpointer new_bindings = NULL, old_bindings = NULL; /****************************************************************************/ table = g_object_get_data( G_OBJECT(bindings_win) , "table" ); if ( table->nrows > 1 ) { /* bindings defined */ GList *children = table->children; gint *array_actioncode; ed_inputevent_t **array_inputevent; gint i = 0; array_actioncode = calloc( table->nrows - 1 , sizeof(gint) ); array_inputevent = calloc( table->nrows - 1 , sizeof(ed_inputevent_t*) ); for ( children ; children != NULL ; children = g_list_next( children ) ) { /* pick information from relevant table cells and put them in arrays */ GtkTableChild *child = children->data; if ( ( child->top_attach + 1 ) == table->nrows ) continue; /* skip last empty row */ if ( child->left_attach == BINDLIST_COL_COMBO_ACTION ) { GtkTreeModel *combomodel = gtk_combo_box_get_model( GTK_COMBO_BOX(child->widget) ); GtkTreeIter comboiter; gint actioncode = 0; gtk_combo_box_get_active_iter( GTK_COMBO_BOX(child->widget) , &comboiter ); gtk_tree_model_get( combomodel , &comboiter , 1 , &actioncode , -1 ); /* pick the action code */ array_actioncode[child->top_attach] = actioncode; } else if ( child->left_attach == BINDLIST_COL_LABEL_EVCODE ) { ed_inputevent_t *inputevent = g_object_get_data( G_OBJECT(child->widget) , "inputevent" ); array_inputevent[child->top_attach] = inputevent; } } /* ok, now copy data from arrays to the bindings store */ new_bindings = ed_bindings_store_new(); /* new bindings store object */ for ( i = 0 ; i < ( table->nrows - 1 ) ; i++ ) ed_bindings_store_insert( new_bindings , array_inputevent[i] , array_actioncode[i] ); g_free( array_actioncode ); g_free( array_inputevent ); /* not needed anymore */ } else { /* no bindings defined */ new_bindings = NULL; } /****************************************************************************/ /* pick old bindings store and delete it */ gtk_tree_model_get_iter( model , &iter , path ); gtk_tree_model_get( model , &iter, DEVLIST_COL_BINDINGS , &old_bindings , -1 ); if ( old_bindings != NULL ) ed_bindings_store_delete( old_bindings ); /* replace it with new one */ gtk_list_store_set( GTK_LIST_STORE(model) , &iter , DEVLIST_COL_BINDINGS , new_bindings , -1 ); /* everything done! */ } gtk_widget_destroy( GTK_WIDGET(bindings_win) ); } #define action_store_add(x) { gtk_list_store_append(action_store,&iter); gtk_list_store_set(action_store,&iter,0,player_actions[x].desc,1,x,-1); } static void cfg_ui_bindings_show ( ed_device_t * dev , GtkTreeRowReference * rowref ) { GtkWidget *bindings_win; GdkGeometry bindings_win_hints; GtkWidget *bindings_vbox; GtkWidget *bindings_info_table, *bindings_info_frame; GtkWidget *bindings_info_label_name_p, *bindings_info_label_name_c; GtkWidget *bindings_info_label_file_p, *bindings_info_label_file_c; GtkWidget *bindings_info_label_phys_p, *bindings_info_label_phys_c; GtkWidget *bindings_bind_frame, *bindings_bind_table, *bindings_bind_table_sw; GtkWidget *bindings_bbar_hbbox, *bindings_bbar_bt_cancel, *bindings_bbar_bt_ok; GtkListStore *action_store; GtkTreeIter iter; static gboolean style_added = FALSE; if ( !style_added ) { /* this is used by trigger dialogs, calling it once is enough */ gtk_rc_parse_string( "style \"noaaborder\" { GtkDialog::action-area-border = 0 }\n" "widget \"trigger_dlg\" style \"noaaborder\"\n" ); style_added = TRUE; } bindings_win = gtk_window_new( GTK_WINDOW_TOPLEVEL ); gtk_window_set_type_hint( GTK_WINDOW(bindings_win), GDK_WINDOW_TYPE_HINT_DIALOG ); gtk_window_set_position( GTK_WINDOW(bindings_win), GTK_WIN_POS_CENTER ); gtk_window_set_transient_for( GTK_WINDOW(bindings_win), GTK_WINDOW(cfg_win) ); gtk_window_set_modal( GTK_WINDOW(bindings_win) , TRUE ); gtk_window_set_title( GTK_WINDOW(bindings_win), _("EvDev-Plug - Bindings Configuration") ); gtk_container_set_border_width( GTK_CONTAINER(bindings_win), 10 ); bindings_win_hints.min_width = 400; bindings_win_hints.min_height = 400; gtk_window_set_geometry_hints( GTK_WINDOW(bindings_win) , GTK_WIDGET(bindings_win) , &bindings_win_hints , GDK_HINT_MIN_SIZE ); g_object_set_data( G_OBJECT(bindings_win) , "rowref" , rowref ); g_object_set_data( G_OBJECT(bindings_win) , "trigger-win" , NULL ); bindings_vbox = gtk_vbox_new( FALSE , 4 ); gtk_container_add( GTK_CONTAINER(bindings_win) , bindings_vbox ); /* action combobox model column 0 -> action desc column 1 -> action code */ action_store = gtk_list_store_new( 2 , G_TYPE_STRING , G_TYPE_INT ); action_store_add( ED_ACTION_PB_PLAY ); action_store_add( ED_ACTION_PB_STOP ); action_store_add( ED_ACTION_PB_PAUSE ); action_store_add( ED_ACTION_PB_PREV ); action_store_add( ED_ACTION_PB_NEXT ); action_store_add( ED_ACTION_PB_EJECT ); action_store_add( ED_ACTION_PL_REPEAT ); action_store_add( ED_ACTION_PL_SHUFFLE ); action_store_add( ED_ACTION_VOL_UP5 ); action_store_add( ED_ACTION_VOL_DOWN5 ); action_store_add( ED_ACTION_VOL_UP10 ); action_store_add( ED_ACTION_VOL_DOWN10 ); action_store_add( ED_ACTION_VOL_MUTE ); action_store_add( ED_ACTION_WIN_MAIN ); action_store_add( ED_ACTION_WIN_PLAYLIST ); action_store_add( ED_ACTION_WIN_EQUALIZER ); action_store_add( ED_ACTION_WIN_JTF ); g_object_set_data_full( G_OBJECT(bindings_win) , "action_store" , action_store , g_object_unref ); /* info table */ bindings_info_table = gtk_table_new( 3 , 2 , FALSE ); gtk_container_set_border_width( GTK_CONTAINER(bindings_info_table), 4 ); bindings_info_label_name_p = gtk_label_new( "" ); gtk_label_set_markup( GTK_LABEL(bindings_info_label_name_p) , _("<b>Name: </b>") ); gtk_misc_set_alignment( GTK_MISC(bindings_info_label_name_p) , 0 , 0.5 ); bindings_info_label_name_c = gtk_label_new( dev->info->name ); gtk_misc_set_alignment( GTK_MISC(bindings_info_label_name_c) , 0 , 0.5 ); gtk_table_attach( GTK_TABLE(bindings_info_table) , bindings_info_label_name_p , 0 , 1 , 0 , 1 , GTK_FILL , GTK_FILL | GTK_EXPAND , 0 , 0 ); gtk_table_attach( GTK_TABLE(bindings_info_table) , bindings_info_label_name_c , 1 , 2 , 0 , 1 , GTK_FILL , GTK_FILL | GTK_EXPAND , 0 , 0 ); bindings_info_label_file_p = gtk_label_new( "" ); gtk_label_set_markup( GTK_LABEL(bindings_info_label_file_p) , _("<b>Filename: </b>") ); gtk_misc_set_alignment( GTK_MISC(bindings_info_label_file_p) , 0 , 0.5 ); bindings_info_label_file_c = gtk_label_new( dev->info->filename ); gtk_misc_set_alignment( GTK_MISC(bindings_info_label_file_c) , 0 , 0.5 ); gtk_table_attach( GTK_TABLE(bindings_info_table) , bindings_info_label_file_p , 0 , 1 , 1 , 2 , GTK_FILL , GTK_FILL | GTK_EXPAND , 0 , 0 ); gtk_table_attach( GTK_TABLE(bindings_info_table) , bindings_info_label_file_c , 1 , 2 , 1 , 2 , GTK_FILL , GTK_FILL | GTK_EXPAND , 0 , 0 ); bindings_info_label_phys_p = gtk_label_new( "" ); gtk_label_set_markup( GTK_LABEL(bindings_info_label_phys_p) , _("<b>Phys.Address: </b>") ); gtk_misc_set_alignment( GTK_MISC(bindings_info_label_phys_p) , 0 , 0.5 ); bindings_info_label_phys_c = gtk_label_new( dev->info->phys ); gtk_misc_set_alignment( GTK_MISC(bindings_info_label_phys_c) , 0 , 0.5 ); gtk_table_attach( GTK_TABLE(bindings_info_table) , bindings_info_label_phys_p , 0 , 1 , 2 , 3 , GTK_FILL , GTK_FILL | GTK_EXPAND , 0 , 0 ); gtk_table_attach( GTK_TABLE(bindings_info_table) , bindings_info_label_phys_c , 1 , 2 , 2 , 3 , GTK_FILL , GTK_FILL | GTK_EXPAND , 0 , 0 ); bindings_info_frame = gtk_frame_new( NULL ); gtk_container_add( GTK_CONTAINER(bindings_info_frame) , bindings_info_table ); gtk_box_pack_start( GTK_BOX(bindings_vbox) , bindings_info_frame , FALSE , FALSE , 0 ); /* bindings boxlist */ bindings_bind_table = gtk_table_new( 1 , BINDLIST_NUMCOLS , FALSE ); cfg_bindbox_new_empty_row( GTK_TABLE(bindings_bind_table) , bindings_win , FALSE ); cfg_bindbox_populate( bindings_bind_table , bindings_win , dev->info->bindings ); bindings_bind_table_sw = gtk_scrolled_window_new( NULL , NULL ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(bindings_bind_table_sw) , GTK_POLICY_NEVER , GTK_POLICY_ALWAYS ); bindings_bind_frame = gtk_frame_new( NULL ); gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW(bindings_bind_table_sw) , bindings_bind_table ); gtk_container_add( GTK_CONTAINER(bindings_bind_frame) , bindings_bind_table_sw ); gtk_box_pack_start( GTK_BOX(bindings_vbox) , bindings_bind_frame , TRUE , TRUE , 0 ); g_object_set_data( G_OBJECT(bindings_win) , "table" , bindings_bind_table ); g_object_set_data( G_OBJECT(bindings_win) , "tablesw" , bindings_bind_table_sw ); gtk_box_pack_start( GTK_BOX(bindings_vbox) , gtk_hseparator_new() , FALSE , FALSE , 0 ); /* button bar */ bindings_bbar_hbbox = gtk_hbutton_box_new(); gtk_button_box_set_layout( GTK_BUTTON_BOX(bindings_bbar_hbbox) , GTK_BUTTONBOX_START ); bindings_bbar_bt_cancel = gtk_button_new_from_stock( GTK_STOCK_CANCEL ); bindings_bbar_bt_ok = gtk_button_new_from_stock( GTK_STOCK_OK ); gtk_container_add( GTK_CONTAINER(bindings_bbar_hbbox) , bindings_bbar_bt_cancel ); gtk_container_add( GTK_CONTAINER(bindings_bbar_hbbox) , bindings_bbar_bt_ok ); gtk_button_box_set_child_secondary( GTK_BUTTON_BOX(bindings_bbar_hbbox) , bindings_bbar_bt_cancel , TRUE ); gtk_button_box_set_child_secondary( GTK_BUTTON_BOX(bindings_bbar_hbbox) , bindings_bbar_bt_ok , TRUE ); gtk_box_pack_start( GTK_BOX(bindings_vbox) , bindings_bbar_hbbox , FALSE , FALSE , 0 ); /* activate device listening */ dev->iochan_sid = g_io_add_watch( dev->iochan , G_IO_IN , (GIOFunc)cfg_bindbox_assign_binding_input_func , bindings_win ); /* signals */ g_signal_connect( G_OBJECT(bindings_win) , "destroy" , G_CALLBACK(cfg_bindings_cb_destroy) , dev ); g_signal_connect_swapped( G_OBJECT(bindings_bbar_bt_cancel) , "clicked" , G_CALLBACK(gtk_widget_destroy) , bindings_win ); g_signal_connect_swapped( G_OBJECT(bindings_bbar_bt_ok) , "clicked" , G_CALLBACK(cfg_bindings_cb_commit) , bindings_win ); gtk_widget_show_all( bindings_win ); } /* about box */ void ed_ui_about_show( void ) { static GtkWidget *about_win = NULL; GtkWidget *about_vbox; GtkWidget *logoandinfo_vbox; GtkWidget *info_tv, *info_tv_sw, *info_tv_frame; GtkWidget *bbar_bbox, *bbar_bt_ok; GtkTextBuffer *info_tb; GdkGeometry abount_win_hints; if ( about_win != NULL ) { gtk_window_present( GTK_WINDOW(about_win) ); return; } about_win = gtk_window_new( GTK_WINDOW_TOPLEVEL ); gtk_window_set_type_hint( GTK_WINDOW(about_win), GDK_WINDOW_TYPE_HINT_DIALOG ); gtk_window_set_position( GTK_WINDOW(about_win), GTK_WIN_POS_CENTER ); gtk_window_set_title( GTK_WINDOW(about_win), _("EvDev-Plug - about") ); abount_win_hints.min_width = 420; abount_win_hints.min_height = 200; gtk_window_set_geometry_hints( GTK_WINDOW(about_win) , GTK_WIDGET(about_win) , &abount_win_hints , GDK_HINT_MIN_SIZE ); /* gtk_window_set_resizable( GTK_WINDOW(about_win) , FALSE ); */ gtk_container_set_border_width( GTK_CONTAINER(about_win) , 10 ); g_signal_connect( G_OBJECT(about_win) , "destroy" , G_CALLBACK(gtk_widget_destroyed) , &about_win ); about_vbox = gtk_vbox_new( FALSE , 0 ); gtk_container_add( GTK_CONTAINER(about_win) , about_vbox ); logoandinfo_vbox = gtk_vbox_new( TRUE , 2 ); /* TODO make a logo or make someone do it! :) logo_pixbuf = gdk_pixbuf_new_from_xpm_data( (const gchar **)evdev_plug_logo_xpm ); logo_image = gtk_image_new_from_pixbuf( logo_pixbuf ); g_object_unref( logo_pixbuf ); logo_frame = gtk_frame_new( NULL ); gtk_container_add( GTK_CONTAINER(logo_frame) , logo_image ); gtk_box_pack_start( GTK_BOX(logoandinfo_vbox) , logo_frame , TRUE , TRUE , 0 ); */ info_tv = gtk_text_view_new(); info_tb = gtk_text_view_get_buffer( GTK_TEXT_VIEW(info_tv) ); gtk_text_view_set_editable( GTK_TEXT_VIEW(info_tv) , FALSE ); gtk_text_view_set_cursor_visible( GTK_TEXT_VIEW(info_tv) , FALSE ); gtk_text_view_set_justification( GTK_TEXT_VIEW(info_tv) , GTK_JUSTIFY_LEFT ); gtk_text_view_set_left_margin( GTK_TEXT_VIEW(info_tv) , 10 ); gtk_text_buffer_set_text( info_tb , _("\nEvDev-Plug " ED_VERSION_PLUGIN "\nplayer remote control via event devices\n" "http://www.develia.org/projects.php?p=evdevplug\n\n" "written by Giacomo Lozito\n" "< james@develia.org >\n\n") , -1 ); info_tv_sw = gtk_scrolled_window_new( NULL , NULL ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(info_tv_sw) , GTK_POLICY_NEVER , GTK_POLICY_ALWAYS ); gtk_container_add( GTK_CONTAINER(info_tv_sw) , info_tv ); info_tv_frame = gtk_frame_new( NULL ); gtk_container_add( GTK_CONTAINER(info_tv_frame) , info_tv_sw ); gtk_box_pack_start( GTK_BOX(logoandinfo_vbox) , info_tv_frame , TRUE , TRUE , 0 ); gtk_box_pack_start( GTK_BOX(about_vbox) , logoandinfo_vbox , TRUE , TRUE , 0 ); /* horizontal separator and buttons */ gtk_box_pack_start( GTK_BOX(about_vbox) , gtk_hseparator_new() , FALSE , FALSE , 4 ); bbar_bbox = gtk_hbutton_box_new(); gtk_button_box_set_layout( GTK_BUTTON_BOX(bbar_bbox) , GTK_BUTTONBOX_END ); bbar_bt_ok = gtk_button_new_from_stock( GTK_STOCK_OK ); g_signal_connect_swapped( G_OBJECT(bbar_bt_ok) , "clicked" , G_CALLBACK(gtk_widget_destroy) , about_win ); gtk_container_add( GTK_CONTAINER(bbar_bbox) , bbar_bt_ok ); gtk_box_pack_start( GTK_BOX(about_vbox) , bbar_bbox , FALSE , FALSE , 0 ); gtk_widget_show_all( about_win ); } /* message box */ void ed_ui_message_show ( gchar * title , gchar * message , gpointer parent_win_gp ) { GtkWidget *message_win; GtkWindow *parent_win = NULL; if (( parent_win_gp != NULL ) && ( GTK_WIDGET_TOPLEVEL(GTK_WIDGET(parent_win_gp)) )) parent_win = GTK_WINDOW(parent_win_gp); message_win = gtk_message_dialog_new( parent_win , ( parent_win != NULL ? GTK_DIALOG_DESTROY_WITH_PARENT : 0 ) , GTK_MESSAGE_INFO , GTK_BUTTONS_CLOSE , message ); gtk_window_set_title( GTK_WINDOW(message_win) , title ); gtk_dialog_run( GTK_DIALOG(message_win) ); gtk_widget_destroy( message_win ); }