Search
lxdream.org :: lxdream/src/gtkui/gtk_cfg.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/gtkui/gtk_cfg.c
changeset 1072:d82e04e6d497
author nkeynes
date Tue Jul 21 20:33:21 2009 +1000 (12 years ago)
permissions -rw-r--r--
last change Heavy configuration management refactor
- Configuration groups now take both an on_change event handler and a
default keybinding handler, making most keybinding tasks quite simple
- GUI configuration all merged into a unified model, drastically reducing
the amount of GUI config code.

Bonuses
- OSX now has a hotkey preference pane
- GTK keybinding editor is much more usable
view annotate diff log raw
     1 /**
     2  * $Id$
     3  *
     4  * Configuration pane to display a configuration group
     5  * TODO:
     6  *
     7  * Copyright (c) 2009 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 "config.h"
    28 #include "lxpaths.h"
    29 #include "display.h"
    30 #include "gtkui/gtkui.h"
    32 struct config_data {
    33     lxdream_config_group_t config;
    34     GtkWidget *fields[CONFIG_MAX_KEYS][2];
    35 };
    37 /**
    38  * Update the configuration data for the current value of the given field.
    39  */
    40 static gboolean config_text_changed( GtkWidget *field, gpointer p )
    41 {
    42     GtkWidget *panel = field->parent;
    43     struct config_data *data= (struct config_data *)gtk_object_get_data( GTK_OBJECT(panel), "config_data" );
    44     int tag = GPOINTER_TO_INT( g_object_get_data( G_OBJECT(field), "tag" ) );
    46     char buf[64];
    47     GtkWidget *entry1, *entry2;
    48     const gchar *key1 = NULL, *key2 = NULL;
    50     if( data->fields[tag][0] != NULL ) {
    51         key1 = gtk_entry_get_text(GTK_ENTRY(data->fields[tag][0]));
    52     }
    53     if( data->fields[tag][1] != NULL ) {
    54         key2 = gtk_entry_get_text(GTK_ENTRY(data->fields[tag][1]));
    55     }
    57     if( key1 == NULL || key1[0] == '\0') {
    58         lxdream_set_config_value( data->config, tag, key2 );
    59     } else if( key2 == NULL || key2[0] == '\0') {
    60         lxdream_set_config_value( data->config, tag, key1 );
    61     } else {
    62         char buf[strlen(key1) + strlen(key2) + 3];
    63         snprintf( buf, sizeof(buf), "%s, %s", key1, key2 );
    64         lxdream_set_config_value( data->config, tag, buf );
    65     }
    66     return TRUE;
    67 }
    69 /**
    70  * Reset the fields (identified by one of the widgets in the field) back to it's
    71  * value in the config group.
    72  */
    73 static void config_text_reset( GtkWidget *field )
    74 {
    75     GtkWidget *panel = field->parent;
    76     struct config_data *data= (struct config_data *)gtk_object_get_data( GTK_OBJECT(panel), "config_data" );
    77     int tag = GPOINTER_TO_INT( g_object_get_data( G_OBJECT(field), "tag" ) );
    79     const gchar *value = data->config->params[tag].value;
    80     if( value == NULL ) {
    81         value = "";
    82     }
    84     if( data->fields[tag][0] == NULL ) {
    85         if( data->fields[tag][1] != NULL ) {
    86             gtk_entry_set_text( GTK_ENTRY(data->fields[tag][1]), value );
    87         }
    88     } else if( data->fields[tag][1] == NULL ) {
    89         gtk_entry_set_text( GTK_ENTRY(data->fields[tag][0]), value );
    90     } else { /* Split between two fields */
    91         gchar *v1 = "", *v2 = "";
    92         gchar **parts = g_strsplit(value,",",3);
    93         if( parts[0] != NULL ) {
    94             v1 = parts[0];
    95             if( parts[1] != NULL ) {
    96                 v2 = parts[1];
    97             }
    99         }
   100         gtk_entry_set_text( GTK_ENTRY(data->fields[tag][0]), v1 );
   101         gtk_entry_set_text( GTK_ENTRY(data->fields[tag][1]), v2 );
   102         g_strfreev(parts);
   103     }
   104 }
   106 static void config_set_field( void *p, const gchar *keysym )
   107 {
   108     GtkWidget *field = GTK_WIDGET(p);
   109     GtkWidget *panel = field->parent;
   111     gtk_entry_set_text( GTK_ENTRY(field), keysym );
   112     g_object_set_data( G_OBJECT(field), "keypress_mode", GINT_TO_POINTER(FALSE) );
   113     input_set_keysym_hook(NULL, NULL);
   114     config_text_changed( field, NULL );
   115 }
   117 static gboolean config_key_buttonpress( GtkWidget *widget, GdkEventButton *event, gpointer user_data )
   118 {
   119     gboolean keypress_mode = GPOINTER_TO_INT(g_object_get_data( G_OBJECT(widget), "keypress_mode"));
   120     if( keypress_mode ) {
   121         gchar *keysym = input_keycode_to_keysym( &system_mouse_driver, event->button);
   122         if( keysym != NULL ) {
   123             config_set_field( widget, keysym );
   124             g_free(keysym);
   125         }
   126         return TRUE;
   127     } else {
   128         gtk_entry_set_text( GTK_ENTRY(widget), _("<press key>") );
   129         g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(TRUE) );
   130         input_set_keysym_hook( (display_keysym_callback_t)config_set_field, widget);
   131         gtk_widget_grab_focus( widget );
   132     }
   133     return FALSE;
   134 }
   136 static gboolean config_key_keypress( GtkWidget *widget, GdkEventKey *event, gpointer user_data )
   137 {
   138     gboolean keypress_mode = GPOINTER_TO_INT(g_object_get_data( G_OBJECT(widget), "keypress_mode"));
   139     if( keypress_mode ) {
   140         if( event->keyval == GDK_Escape ) {
   141             config_text_reset( widget );
   142             return TRUE;
   143         }
   144         GdkKeymap *keymap = gdk_keymap_get_default();
   145         guint keyval;
   147         gdk_keymap_translate_keyboard_state( keymap, event->hardware_keycode, 0, 0, &keyval,
   148                                              NULL, NULL, NULL );
   149         config_set_field( widget, gdk_keyval_name(keyval) );
   150         return TRUE;
   151     } else {
   152         switch( event->keyval ) {
   153         case GDK_Return:
   154         case GDK_KP_Enter:
   155             gtk_entry_set_text( GTK_ENTRY(widget), _("<press key>") );
   156             g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(TRUE) );
   157             input_set_keysym_hook((display_keysym_callback_t)config_set_field, widget);
   158             return TRUE;
   159         case GDK_BackSpace:
   160         case GDK_Delete:
   161             config_set_field( widget, "" );
   162             return TRUE;
   163         }
   164         return FALSE;
   165     }
   166 }
   168 static gboolean config_key_unfocus( GtkWidget *widget, gpointer user_data )
   169 {
   170     gboolean keypress_mode = GPOINTER_TO_INT(g_object_get_data( G_OBJECT(widget), "keypress_mode"));
   171     if( keypress_mode ) {
   172         /* We've lost focus while waiting for a key binding - restore the old value */
   173         config_text_reset(widget);
   174         g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(FALSE) );
   175         input_set_keysym_hook(NULL,NULL);
   176     }
   177     return TRUE;
   178 }
   180 static gboolean path_file_button_clicked( GtkWidget *button, gpointer user_data )
   181 {
   182     GtkWidget *entry = GTK_WIDGET(user_data);
   183     GtkWidget *file = gtk_file_chooser_dialog_new( _("Select file"), NULL,
   184             GTK_FILE_CHOOSER_ACTION_OPEN,
   185             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
   186             GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
   187             NULL );
   188     gchar *filename = get_expanded_path(gtk_entry_get_text(GTK_ENTRY(entry)));
   189     gtk_file_chooser_set_filename( GTK_FILE_CHOOSER(file), filename );
   190     gtk_window_set_modal( GTK_WINDOW(file), TRUE );
   191     gtk_widget_show_all( file );
   192     gint result = gtk_dialog_run(GTK_DIALOG(file));
   193     g_free(filename);
   194     if( result == GTK_RESPONSE_ACCEPT ) {
   195         filename = get_escaped_path(gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(file) ));
   196         config_set_field( entry, filename );
   197         g_free(filename);
   198     }
   199     gtk_widget_destroy(file);
   200     return TRUE;
   201 }
   203 static gboolean path_dir_button_clicked( GtkWidget *button, gpointer user_data )
   204 {
   205     GtkWidget *entry = GTK_WIDGET(user_data);
   206     GtkWidget *file = gtk_file_chooser_dialog_new( _("Select file"), NULL,
   207             GTK_FILE_CHOOSER_ACTION_OPEN,
   208             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
   209             GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
   210             NULL );
   211     gchar *filename = get_expanded_path(gtk_entry_get_text(GTK_ENTRY(entry)));
   212     gtk_file_chooser_set_action( GTK_FILE_CHOOSER(file), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER );
   213     gtk_file_chooser_set_filename( GTK_FILE_CHOOSER(file), filename );
   214     gtk_window_set_modal( GTK_WINDOW(file), TRUE );
   215     gtk_widget_show_all( file );
   216     gint result = gtk_dialog_run(GTK_DIALOG(file));
   217     g_free(filename);
   218     if( result == GTK_RESPONSE_ACCEPT ) {
   219         filename = get_escaped_path(gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(file) ));
   220         config_set_field( entry, filename );
   221         g_free(filename);
   222     }
   223     gtk_widget_destroy(file);
   224     return TRUE;
   225 }
   228 static void lxdream_configuration_panel_destroy( GtkWidget *panel, gpointer data )
   229 {
   230     input_set_keysym_hook(NULL, NULL);
   231 }
   233 static GtkWidget *gtk_configuration_panel_new( lxdream_config_group_t conf )
   234 {
   235     int count, i;
   236     for( count=0; conf->params[count].label != NULL; count++ );
   238     GtkWidget *table = gtk_table_new( count, 5, FALSE );
   239     struct config_data *data = g_malloc0( sizeof(struct config_data) );
   240     data->config = conf;
   241     GList *focus_chain = NULL;
   242     gtk_object_set_data_full( GTK_OBJECT(table), "config_data", data, g_free );
   243     g_signal_connect( table, "destroy_event", G_CALLBACK(lxdream_configuration_panel_destroy), NULL );
   244     for( i=0; conf->params[i].label != NULL; i++ ) {
   245         GtkWidget *text, *text2, *button;
   246         int x=0;
   247         int y=i;
   248         gtk_table_attach( GTK_TABLE(table), gtk_label_new(Q_(conf->params[i].label)), x, x+1, y, y+1,
   249                           GTK_SHRINK, GTK_SHRINK, 0, 0 );
   250         switch( conf->params[i].type ) {
   251         case CONFIG_TYPE_KEY:
   252             data->fields[i][0] = text = gtk_entry_new();
   253             gtk_entry_set_width_chars( GTK_ENTRY(text), 11 );
   254             gtk_entry_set_editable( GTK_ENTRY(text), FALSE );
   255             g_signal_connect( text, "key_press_event",
   256                               G_CALLBACK(config_key_keypress), NULL );
   257             g_signal_connect( text, "button_press_event",
   258                               G_CALLBACK(config_key_buttonpress), NULL );
   259             g_signal_connect( text, "focus_out_event",
   260                               G_CALLBACK(config_key_unfocus), NULL);
   261             g_object_set_data( G_OBJECT(text), "keypress_mode", GINT_TO_POINTER(FALSE) );
   262             g_object_set_data( G_OBJECT(text), "tag", GINT_TO_POINTER(i) );
   263             gtk_table_attach_defaults( GTK_TABLE(table), text, x+1, x+2, y, y+1);
   264             focus_chain = g_list_append( focus_chain, text );
   266             data->fields[i][1] = text2 = gtk_entry_new();
   267             gtk_entry_set_width_chars( GTK_ENTRY(text2), 11 );
   268             gtk_entry_set_editable( GTK_ENTRY(text2), FALSE );
   269             g_signal_connect( text2, "key_press_event",
   270                               G_CALLBACK(config_key_keypress), NULL );
   271             g_signal_connect( text2, "button_press_event",
   272                               G_CALLBACK(config_key_buttonpress), NULL );
   273             g_signal_connect( text2, "focus_out_event",
   274                               G_CALLBACK(config_key_unfocus), NULL);
   275             g_object_set_data( G_OBJECT(text2), "keypress_mode", GINT_TO_POINTER(FALSE) );
   276             g_object_set_data( G_OBJECT(text2), "tag", GINT_TO_POINTER(i) );
   277             gtk_table_attach_defaults( GTK_TABLE(table), text2, x+2, x+3, y, y+1);
   278             focus_chain = g_list_append( focus_chain, text2 );
   280             if( conf->params[i].value != NULL ) {
   281                 gchar **parts = g_strsplit(conf->params[i].value,",",3);
   282                 if( parts[0] != NULL ) {
   283                     gtk_entry_set_text( GTK_ENTRY(text), g_strstrip(parts[0]) );
   284                     if( parts[1] != NULL ) {
   285                         gtk_entry_set_text( GTK_ENTRY(text2), g_strstrip(parts[1]) );
   286                     }
   287                 }
   288                 g_strfreev(parts);
   289             }
   290             break;
   291         case CONFIG_TYPE_FILE:
   292         case CONFIG_TYPE_PATH:
   293             data->fields[i][0] = text = gtk_entry_new();
   294             data->fields[i][1] = NULL;
   295             button = gtk_button_new();
   296             gtk_entry_set_text( GTK_ENTRY(text), conf->params[i].value );
   297             gtk_entry_set_width_chars( GTK_ENTRY(text), 48 );
   298             g_object_set_data( G_OBJECT(text), "tag", GINT_TO_POINTER(i) );
   299             gtk_table_attach_defaults( GTK_TABLE(table), text, 1, 2, y, y+1 );
   300             gtk_table_attach( GTK_TABLE(table), button, 2, 3, y, y+1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
   301             g_signal_connect( text, "changed", G_CALLBACK(config_text_changed), NULL );
   302             if( conf->params[i].type == CONFIG_TYPE_FILE ) {
   303                 GtkWidget *image = gtk_image_new_from_stock(GTK_STOCK_FILE, GTK_ICON_SIZE_MENU);
   304                 gtk_button_set_image( GTK_BUTTON(button), image );
   305                 g_signal_connect( button, "clicked", G_CALLBACK(path_file_button_clicked), text );
   306             } else {
   307                 GtkWidget *image = gtk_image_new_from_stock(GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU);
   308                 gtk_button_set_image( GTK_BUTTON(button), image );
   309                 g_signal_connect( button, "clicked", G_CALLBACK(path_dir_button_clicked), text );
   310             }
   311             break;
   312         }
   313     }
   314     gtk_container_set_focus_chain( GTK_CONTAINER(table), focus_chain );
   315 //    gtk_gui_run_property_dialog( _("Controller Configuration"), table, controller_config_done );
   316     return table;
   317 }
   319 int gtk_configuration_panel_run( const gchar *title, lxdream_config_group_t group )
   320 {
   321     struct lxdream_config_group tmp;
   322     lxdream_clone_config_group( &tmp, group );
   323     GtkWidget *panel = gtk_configuration_panel_new( &tmp );
   324     int result = gtk_gui_run_property_dialog( title, panel, NULL );
   325     if( result == GTK_RESPONSE_ACCEPT ) {
   326         lxdream_copy_config_group( group, &tmp );
   327         lxdream_save_config();
   328     }
   329     return result;
   330 }
.