]> git.sesse.net Git - vlc/blob - plugins/gtk/gtk_preferences.c
* modified the gtk interface to save an empty <string> option as a NULL pointer
[vlc] / plugins / gtk / gtk_preferences.c
1 /*****************************************************************************
2  * gtk_preferences.c: functions to handle the preferences dialog box.
3  *****************************************************************************
4  * Copyright (C) 2000, 2001 VideoLAN
5  * $Id: gtk_preferences.c,v 1.18 2002/03/26 22:30:09 gbazin Exp $
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
8  *          Loïc Minier <lool@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble: Our main job is to build a nice interface from the modules config
27  *   structure. Once this is done, we need to track each change made by the
28  *   user to the data contained in this interface so that when/if he decides to
29  *   apply his changes we can quickly commit them into the modules config
30  *   structure. (for this last task we use a GHashTable to accumulate the
31  *   changes. To commit them, we then just have to circle through it )
32  *
33  *****************************************************************************/
34 #include <sys/types.h>                                              /* off_t */
35 #include <stdlib.h>
36
37 #include <videolan/vlc.h>
38
39 #ifdef MODULE_NAME_IS_gnome
40 #   include <gnome.h>
41 #else
42 #   include <gtk/gtk.h>
43 #endif
44
45 #include <string.h>
46
47 #include "interface.h"
48
49 #include "gtk_support.h"
50 #include "gtk_common.h"
51 #include "gtk_preferences.h"
52
53 /* local functions */
54 static void GtkCreateConfigDialog( char *, intf_thread_t * );
55
56 static void GtkConfigOk          ( GtkButton *, gpointer );
57 static void GtkConfigApply       ( GtkButton *, gpointer );
58 static void GtkConfigCancel      ( GtkButton *, gpointer );
59 static void GtkConfigSave        ( GtkButton *, gpointer );
60
61 static void GtkConfigDialogDestroyed ( GtkObject *, gpointer );
62
63 static void GtkStringChanged     ( GtkEditable *, gpointer );
64 static void GtkIntChanged        ( GtkEditable *, gpointer );
65 static void GtkBoolChanged       ( GtkToggleButton *, gpointer );
66
67 static void GtkFreeHashTable     ( gpointer );
68 static void GtkFreeHashValue     ( gpointer, gpointer, gpointer );
69 static void GtkSaveHashValue     ( gpointer, gpointer, gpointer );
70
71 static void GtkPluginConfigure   ( GtkButton *, gpointer );
72 static void GtkPluginSelected    ( GtkButton *, gpointer );
73 static void GtkPluginHighlighted ( GtkCList *, int, int, GdkEventButton *,
74                                    gpointer );
75
76 /****************************************************************************
77  * Callback for menuitems: display configuration interface window
78  ****************************************************************************/
79 void GtkPreferencesActivate( GtkMenuItem * menuitem, gpointer user_data )
80 {
81     intf_thread_t * p_intf;
82
83     p_intf = GetIntf( GTK_WIDGET(menuitem), (char*)user_data );
84
85     GtkCreateConfigDialog( "main", p_intf );
86 }
87
88 /****************************************************************************
89  * GtkCreateConfigDialog: dynamically creates the configuration dialog
90  * box from all the configuration data provided by the selected module.
91  ****************************************************************************/
92
93 /* create a new tooltipped area */
94 #define TOOLTIP( text )                                                   \
95     /* create an event box to catch some events */                        \
96     item_event_box = gtk_event_box_new();                                 \
97     /* add a tooltip on mouseover */                                      \
98     /* FIXME: have a different text for the private text */               \
99     gtk_tooltips_set_tip( p_intf->p_sys->p_tooltips,                      \
100                           item_event_box, text, text );                   \
101     gtk_container_set_border_width( GTK_CONTAINER(item_event_box), 4 );
102
103 /* draws a right aligned label in side of a widget */
104 #define LABEL_AND_WIDGET( label_text, widget, tooltip )                   \
105     gtk_table_resize( GTK_TABLE(category_table), ++rows, 2 );             \
106     item_align = gtk_alignment_new( 1, .5, 0, 0 );                        \
107     item_label = gtk_label_new( label_text );                             \
108     gtk_container_add( GTK_CONTAINER(item_align), item_label );           \
109     TOOLTIP(tooltip)                                                      \
110     gtk_container_add( GTK_CONTAINER(item_event_box), item_align );       \
111     gtk_table_attach_defaults( GTK_TABLE(category_table), item_event_box, \
112                                0, 1, rows - 1, rows );                    \
113     item_align = gtk_alignment_new( 0, .5, .5, 0 );                       \
114     gtk_container_add( GTK_CONTAINER(item_align), widget );               \
115     TOOLTIP(tooltip)                                                      \
116     gtk_container_add( GTK_CONTAINER(item_event_box), item_align );       \
117     gtk_table_attach_defaults( GTK_TABLE(category_table), item_event_box, \
118                                1, 2, rows - 1, rows );
119
120 static void GtkCreateConfigDialog( char *psz_module_name,
121                                    intf_thread_t *p_intf )
122 {
123     module_t *p_module, *p_module_bis;
124     int i;
125
126     guint rows = 0;
127
128     GHashTable *config_hash_table;
129
130     GtkWidget *item_event_box;
131
132     GtkWidget *config_dialog;
133     GtkWidget *config_dialog_vbox;
134     GtkWidget *config_notebook;
135     GtkWidget *category_table = NULL;
136     GtkWidget *category_vbox = NULL;
137
138     GtkWidget *dialog_action_area;
139     GtkWidget *ok_button;
140     GtkWidget *apply_button;
141     GtkWidget *save_button;
142     GtkWidget *cancel_button;
143
144     GtkWidget *item_align;
145     GtkWidget *item_frame;
146     GtkWidget *item_hbox;
147     GtkWidget *item_label;
148     GtkWidget *item_vbox;
149     GtkWidget *string_entry;
150     GtkWidget *integer_spinbutton;
151     GtkObject *item_adj;
152     GtkWidget *bool_checkbutton;
153     GtkWidget *plugin_clist;
154     GtkWidget *plugin_config_button;
155     GtkWidget *plugin_select_button;
156
157
158     /* Check if the dialog box is already opened, if so this will save us
159      * quite a bit of work. (the interface will be destroyed when you actually
160      * close the dialog window, but remember that it is only hidden if you
161      * clicked on the action buttons). This trick also allows us not to
162      * duplicate identical dialog windows. */
163
164     config_dialog = (GtkWidget *)gtk_object_get_data(
165                     GTK_OBJECT(p_intf->p_sys->p_window), psz_module_name );
166     if( config_dialog )
167     {
168         /* Yeah it was open */
169         gtk_widget_show( config_dialog );
170         gtk_widget_grab_focus( config_dialog );
171         return;
172     }
173
174
175     /* Look for the selected module */
176     for( p_module = p_module_bank->first ; p_module != NULL ;
177          p_module = p_module->next )
178     {
179
180         if( psz_module_name && !strcmp( psz_module_name, p_module->psz_name ) )
181             break;
182     }
183     if( !p_module ) return;
184
185     /* We found it, now we can start building its configuration interface */
186     /* Create the configuration dialog box */
187     config_dialog = gtk_dialog_new();
188     gtk_window_set_title( GTK_WINDOW(config_dialog), p_module->psz_longname );
189
190     config_dialog_vbox = GTK_DIALOG(config_dialog)->vbox;
191     gtk_container_set_border_width( GTK_CONTAINER(config_dialog_vbox), 0 );
192
193     /* Create our config hash table and associate it with the dialog box */
194     config_hash_table = g_hash_table_new( NULL, NULL );
195     gtk_object_set_data_full( GTK_OBJECT(config_dialog),
196                               "config_hash_table", config_hash_table,
197                               (GtkDestroyNotify)GtkFreeHashTable );
198
199     /* Create notebook */
200     config_notebook = gtk_notebook_new();
201     gtk_notebook_set_scrollable( GTK_NOTEBOOK(config_notebook), TRUE );
202     gtk_container_add( GTK_CONTAINER(config_dialog_vbox), config_notebook );
203
204     /* Enumerate config options and add corresponding config boxes */
205     for( i = 0; i < p_module->i_config_lines; i++ )
206     {
207
208         switch( p_module->p_config[i].i_type )
209         {
210         case MODULE_CONFIG_HINT_CATEGORY:
211
212             /* add a new table for right-left alignment */
213             category_table = gtk_table_new( 0, 0, FALSE );
214             gtk_table_set_col_spacings( GTK_TABLE(category_table), 4 );
215             rows = 0;
216             /* the vbox serves only EXPAND / FILL issues in the notebook */
217             category_vbox = gtk_vbox_new( FALSE, 0 );
218             gtk_box_pack_start( GTK_BOX(category_vbox), category_table,
219                                 FALSE, FALSE, 0 );
220             gtk_container_set_border_width( GTK_CONTAINER(category_vbox), 4 );
221
222             /* add category vbox as a notebook page */
223             item_label = gtk_label_new( p_module->p_config[i].psz_text );
224             gtk_notebook_append_page( GTK_NOTEBOOK(config_notebook),
225                                       category_vbox, item_label );
226
227
228             break;
229
230         case MODULE_CONFIG_ITEM_PLUGIN:
231
232             item_frame = gtk_frame_new( p_module->p_config[i].psz_text );
233
234             gtk_table_resize( GTK_TABLE(category_table), ++rows, 2 );
235             gtk_table_attach_defaults( GTK_TABLE(category_table), item_frame,
236                                        0, 2, rows - 1, rows );
237
238             TOOLTIP( p_module->p_config[i].psz_longtext )
239             gtk_container_add( GTK_CONTAINER(item_frame), item_event_box );
240
241             item_vbox = gtk_vbox_new( FALSE, 4 );
242             gtk_container_add( GTK_CONTAINER(item_event_box), item_vbox );
243
244             /* create a new clist widget */
245             {
246                 gchar * titles[] = { "Name", "Description" };
247
248                 plugin_clist =
249                     gtk_clist_new_with_titles( 2, titles );
250             }
251             gtk_clist_column_titles_passive( GTK_CLIST(plugin_clist) );
252             gtk_clist_set_selection_mode( GTK_CLIST(plugin_clist),
253                                           GTK_SELECTION_SINGLE);
254             gtk_container_add( GTK_CONTAINER(item_vbox), plugin_clist );
255
256             /* build a list of available plugins */
257             {
258                 gchar * entry[2];
259
260                 for( p_module_bis = p_module_bank->first ;
261                      p_module_bis != NULL ;
262                      p_module_bis = p_module_bis->next )
263                 {
264                     if( p_module_bis->i_capabilities &
265                         (1 << p_module->p_config[i].i_value) )
266                     {
267                         entry[0] = p_module_bis->psz_name;
268                         entry[1] = p_module_bis->psz_longname;
269                         gtk_clist_append( GTK_CLIST(plugin_clist), entry );
270                     }
271                 }
272             }
273
274             gtk_clist_set_column_auto_resize( GTK_CLIST(plugin_clist),
275                                               0, TRUE );
276             gtk_clist_set_column_auto_resize( GTK_CLIST(plugin_clist),
277                                               1, TRUE );
278
279             /* connect signals to the plugins list */
280             gtk_signal_connect( GTK_OBJECT(plugin_clist), "select_row",
281                                 GTK_SIGNAL_FUNC(GtkPluginHighlighted),
282                                 NULL );
283
284             /* hbox holding the "select" and "configure" buttons */
285             item_hbox = gtk_hbox_new( FALSE, 4 );
286             gtk_container_add( GTK_CONTAINER(item_vbox), item_hbox);
287
288             /* add configure button */
289             plugin_config_button =
290                 gtk_button_new_with_label( _("Configure") );
291             gtk_widget_set_sensitive( plugin_config_button, FALSE );
292             gtk_container_add( GTK_CONTAINER(item_hbox),
293                                plugin_config_button );
294             gtk_object_set_data( GTK_OBJECT(plugin_config_button),
295                                  "p_intf", p_intf );
296             gtk_object_set_data( GTK_OBJECT(plugin_clist),
297                                  "config_button", plugin_config_button );
298
299             /* add select button */
300             plugin_select_button =
301                 gtk_button_new_with_label( _("Select") );
302             gtk_container_add( GTK_CONTAINER(item_hbox),
303                                plugin_select_button );
304
305             /* hbox holding the "selected" label and text input */
306             item_hbox = gtk_hbox_new( FALSE, 4 );
307             gtk_container_add( GTK_CONTAINER(item_vbox), item_hbox);
308             /* add new label */
309             item_label = gtk_label_new( _("Selected:") );
310             gtk_container_add( GTK_CONTAINER(item_hbox), item_label );
311
312             /* add input box with default value */
313             string_entry = gtk_entry_new();
314             gtk_object_set_data( GTK_OBJECT(plugin_clist),
315                                  "plugin_entry", string_entry );
316             gtk_container_add( GTK_CONTAINER(item_hbox), string_entry );
317             vlc_mutex_lock( p_module->p_config[i].p_lock );
318             gtk_entry_set_text( GTK_ENTRY(string_entry),
319                                 p_module->p_config[i].psz_value ?
320                                 p_module->p_config[i].psz_value : "" );
321             vlc_mutex_unlock( p_module->p_config[i].p_lock );
322
323             /* connect signals to the buttons */
324             gtk_signal_connect( GTK_OBJECT(plugin_config_button), "clicked",
325                                 GTK_SIGNAL_FUNC(GtkPluginConfigure),
326                                 (gpointer)plugin_clist );
327             gtk_signal_connect( GTK_OBJECT(plugin_select_button), "clicked",
328                                 GTK_SIGNAL_FUNC(GtkPluginSelected),
329                                 (gpointer)plugin_clist );
330
331             /* connect signal to track changes in the text box */
332             gtk_object_set_data( GTK_OBJECT(string_entry), "config_option",
333                                  p_module->p_config[i].psz_name );
334             gtk_signal_connect( GTK_OBJECT(string_entry), "changed",
335                                 GTK_SIGNAL_FUNC(GtkStringChanged),
336                                 (gpointer)config_dialog );
337             break;
338
339         case MODULE_CONFIG_ITEM_STRING:
340         case MODULE_CONFIG_ITEM_FILE:
341
342             /* add input box with default value */
343             string_entry = gtk_entry_new();
344             vlc_mutex_lock( p_module->p_config[i].p_lock );
345             gtk_entry_set_text( GTK_ENTRY(string_entry),
346                                 p_module->p_config[i].psz_value ?
347                                 p_module->p_config[i].psz_value : "" );
348             vlc_mutex_unlock( p_module->p_config[i].p_lock );
349
350             /* connect signal to track changes in the text box */
351             gtk_object_set_data( GTK_OBJECT(string_entry), "config_option",
352                                  p_module->p_config[i].psz_name );
353             gtk_signal_connect( GTK_OBJECT(string_entry), "changed",
354                                 GTK_SIGNAL_FUNC(GtkStringChanged),
355                                 (gpointer)config_dialog );
356
357             LABEL_AND_WIDGET( p_module->p_config[i].psz_text,
358                               string_entry,
359                               p_module->p_config[i].psz_longtext );
360             break;
361
362         case MODULE_CONFIG_ITEM_INTEGER:
363
364             /* add input box with default value */
365             item_adj = gtk_adjustment_new( p_module->p_config[i].i_value,
366                                            -1, 99999, 1, 10, 10 );
367             integer_spinbutton = gtk_spin_button_new( GTK_ADJUSTMENT(item_adj),
368                                                       1, 0 );
369
370             /* connect signal to track changes in the spinbutton value */
371             gtk_object_set_data( GTK_OBJECT(integer_spinbutton),
372                                  "config_option",
373                                  p_module->p_config[i].psz_name );
374             gtk_signal_connect( GTK_OBJECT(integer_spinbutton), "changed",
375                                 GTK_SIGNAL_FUNC(GtkIntChanged),
376                                 (gpointer)config_dialog );
377
378             LABEL_AND_WIDGET( p_module->p_config[i].psz_text,
379                               integer_spinbutton,
380                               p_module->p_config[i].psz_longtext );
381             break;
382
383         case MODULE_CONFIG_ITEM_BOOL:
384
385             /* add check button */
386             bool_checkbutton = gtk_check_button_new();
387             gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(bool_checkbutton),
388                                           p_module->p_config[i].i_value );
389
390             /* connect signal to track changes in the button state */
391             gtk_object_set_data( GTK_OBJECT(bool_checkbutton), "config_option",
392                                  p_module->p_config[i].psz_name );
393             gtk_signal_connect( GTK_OBJECT(bool_checkbutton), "toggled",
394                                 GTK_SIGNAL_FUNC(GtkBoolChanged),
395                                 (gpointer)config_dialog );
396
397             LABEL_AND_WIDGET( p_module->p_config[i].psz_text,
398                               bool_checkbutton,
399                               p_module->p_config[i].psz_longtext );
400             break;
401         }
402     }
403
404     /* Now let's add the action buttons at the bottom of the page */
405
406     dialog_action_area = GTK_DIALOG(config_dialog)->action_area;
407     gtk_container_set_border_width( GTK_CONTAINER(dialog_action_area), 4 );
408
409     /* add a new table for the config option */
410     item_hbox = gtk_hbox_new( FALSE, 0 );
411     gtk_box_pack_end( GTK_BOX(dialog_action_area), item_hbox,
412                       TRUE, FALSE, 0 );
413     item_hbox = gtk_hbox_new( FALSE, 0 );
414     gtk_box_pack_end( GTK_BOX(dialog_action_area), item_hbox,
415                       TRUE, FALSE, 0 );
416
417     /* Create the OK button */
418     ok_button = gtk_button_new_with_label( _("Ok") );
419     gtk_box_pack_start( GTK_BOX(dialog_action_area), ok_button,
420                         TRUE, TRUE, 0 );
421
422     apply_button = gtk_button_new_with_label( _("Apply") );
423     gtk_box_pack_start( GTK_BOX(dialog_action_area), apply_button,
424                         TRUE, TRUE, 0 );
425
426     save_button = gtk_button_new_with_label( _("Save") );
427     gtk_box_pack_start( GTK_BOX(dialog_action_area), save_button,
428                         TRUE, TRUE, 0 );
429
430     cancel_button = gtk_button_new_with_label( _("Cancel") );
431     gtk_box_pack_start( GTK_BOX(dialog_action_area), cancel_button,
432                         TRUE, TRUE, 0 );
433
434     gtk_signal_connect( GTK_OBJECT(ok_button), "clicked",
435                         GTK_SIGNAL_FUNC(GtkConfigOk),
436                         config_dialog );
437     gtk_signal_connect( GTK_OBJECT(apply_button), "clicked",
438                         GTK_SIGNAL_FUNC(GtkConfigApply),
439                         config_dialog );
440     gtk_signal_connect( GTK_OBJECT(save_button), "clicked",
441                         GTK_SIGNAL_FUNC(GtkConfigSave),
442                         config_dialog );
443     gtk_signal_connect( GTK_OBJECT(cancel_button), "clicked",
444                         GTK_SIGNAL_FUNC(GtkConfigCancel),
445                         config_dialog );
446
447     /* Ok, job done successfully. Let's keep a reference to the dialog box */
448     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_window),
449                          psz_module_name, config_dialog );
450     gtk_object_set_data( GTK_OBJECT(config_dialog), "psz_module_name",
451                          psz_module_name );
452
453     /* we want this ref to be destroyed if the object is destroyed */
454     gtk_signal_connect( GTK_OBJECT(config_dialog), "destroy",
455                        GTK_SIGNAL_FUNC(GtkConfigDialogDestroyed),
456                        (gpointer)p_intf );
457
458     gtk_widget_show_all( config_dialog );
459 }
460
461 #undef FRAME
462 #undef SCROLLED_WINDOW
463 #undef LABEL
464 #undef TOOLTIP
465
466 /****************************************************************************
467  * GtkConfigApply: store the changes to the config inside the modules
468  * configuration structure
469  ****************************************************************************/
470 void GtkConfigApply( GtkButton * button, gpointer user_data )
471 {
472     GHashTable *hash_table;
473     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
474                                                     "config_hash_table" );
475     g_hash_table_foreach( hash_table, GtkSaveHashValue, NULL );
476
477 }
478
479 void GtkConfigOk( GtkButton * button, gpointer user_data )
480 {
481     GtkConfigApply( button, user_data );
482     gtk_widget_hide( gtk_widget_get_toplevel( GTK_WIDGET (button) ) );
483 }
484
485
486 void GtkConfigCancel( GtkButton * button, gpointer user_data )
487 {
488     gtk_widget_hide( gtk_widget_get_toplevel( GTK_WIDGET (button) ) );
489 }
490
491 void GtkConfigSave( GtkButton * button, gpointer user_data )
492 {
493     GtkConfigApply( button, user_data );
494     config_SaveConfigFile( NULL );
495 }
496
497 /****************************************************************************
498  * GtkPluginHighlighted: display plugin description when an entry is selected
499  *   in the clist, and activate the configure button if necessary.
500  ****************************************************************************/
501 void GtkPluginHighlighted( GtkCList *plugin_clist, int row, int column,
502                            GdkEventButton *event, gpointer user_data )
503 {
504     GtkWidget *config_button;
505     module_t *p_module;
506     char *psz_name;
507
508     if( gtk_clist_get_text( GTK_CLIST(plugin_clist), row, 0, &psz_name ) )
509     {
510         /* look for plugin 'psz_name' */
511         for( p_module = p_module_bank->first ;
512              p_module != NULL ;
513              p_module = p_module->next )
514         {
515           if( !strcmp( p_module->psz_name, psz_name ) )
516           {
517               gtk_object_set_data( GTK_OBJECT(plugin_clist),
518                                    "plugin_highlighted", p_module );
519               config_button = gtk_object_get_data( GTK_OBJECT(plugin_clist),
520                                                    "config_button" );
521               if( p_module->i_config_items )
522                   gtk_widget_set_sensitive( config_button, TRUE );
523               else
524                   gtk_widget_set_sensitive( config_button, FALSE );
525
526               break;
527           }
528         }
529
530     }
531 }
532
533 /****************************************************************************
534  * GtkPluginConfigure: display plugin configuration dialog box.
535  ****************************************************************************/
536 void GtkPluginConfigure( GtkButton *button, gpointer user_data )
537 {
538     module_t *p_module;
539     intf_thread_t *p_intf;
540
541     p_module = (module_t *)gtk_object_get_data( GTK_OBJECT(user_data),
542                                                 "plugin_highlighted" );
543
544     if( !p_module ) return;
545     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(button),
546                                                    "p_intf" );
547     GtkCreateConfigDialog( p_module->psz_name, (gpointer)p_intf );
548
549 }
550
551 /****************************************************************************
552  * GtkPluginSelected: select plugin.
553  ****************************************************************************/
554 void GtkPluginSelected( GtkButton *button, gpointer user_data )
555 {
556     module_t *p_module;
557     GtkWidget *widget;
558
559     p_module = (module_t *)gtk_object_get_data( GTK_OBJECT(user_data),
560                                                 "plugin_highlighted" );
561     widget = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
562                                                "plugin_entry" );
563     if( !p_module ) return;
564
565     gtk_entry_set_text( GTK_ENTRY(widget), p_module->psz_name );
566
567 }
568
569 /****************************************************************************
570  * GtkStringChanged: signal called when the user changes a string value.
571  ****************************************************************************/
572 static void GtkStringChanged( GtkEditable *editable, gpointer user_data )
573 {
574     module_config_t *p_config;
575
576     GHashTable *hash_table;
577
578     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
579                                                     "config_hash_table" );
580     /* free old p_config */
581     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
582                                                        (gpointer)editable );
583     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, NULL );
584
585     p_config = malloc( sizeof(module_config_t) );
586     p_config->i_type = MODULE_CONFIG_ITEM_STRING;
587     p_config->psz_value = gtk_editable_get_chars( editable, 0, -1 );
588     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(editable),
589                                                       "config_option" );
590
591     g_hash_table_insert( hash_table, (gpointer)editable,
592                          (gpointer)p_config );
593 }
594
595 /****************************************************************************
596  * GtkIntChanged: signal called when the user changes a an integer value.
597  ****************************************************************************/
598 static void GtkIntChanged( GtkEditable *editable, gpointer user_data )
599 {
600     module_config_t *p_config;
601
602     GHashTable *hash_table;
603
604     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
605                                                     "config_hash_table" );
606
607     /* free old p_config */
608     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
609                                                        (gpointer)editable );
610     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, NULL );
611
612     p_config = malloc( sizeof(module_config_t) );
613     p_config->i_type = MODULE_CONFIG_ITEM_INTEGER;
614     p_config->i_value =  gtk_spin_button_get_value_as_int(
615                              GTK_SPIN_BUTTON(editable) );
616     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(editable),
617                                                       "config_option" );
618
619     g_hash_table_insert( hash_table, (gpointer)editable,
620                          (gpointer)p_config );
621 }
622
623 /****************************************************************************
624  * GtkStringChanged: signal called when the user changes a bool value.
625  ****************************************************************************/
626 static void GtkBoolChanged( GtkToggleButton *button, gpointer user_data )
627 {
628     module_config_t *p_config;
629
630     GHashTable *hash_table;
631
632     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
633                                                     "config_hash_table" );
634
635     /* free old p_config */
636     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
637                                                        (gpointer)button );
638     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, NULL );
639
640     p_config = malloc( sizeof(module_config_t) );
641     p_config->i_type = MODULE_CONFIG_ITEM_BOOL;
642     p_config->i_value = gtk_toggle_button_get_active( button );
643     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(button),
644                                                       "config_option" );
645
646     g_hash_table_insert( hash_table, (gpointer)button,
647                          (gpointer)p_config );
648 }
649
650 /****************************************************************************
651  * GtkFreeHashTable: signal called when the config hash table is destroyed.
652  ****************************************************************************/
653 static void GtkFreeHashTable( gpointer user_data )
654 {
655     GHashTable *hash_table = (GHashTable *)user_data;
656
657     g_hash_table_foreach( hash_table, GtkFreeHashValue, NULL );
658     g_hash_table_destroy( hash_table );
659 }
660
661 /****************************************************************************
662  * GtkFreeHashValue: signal called when an element of the config hash table
663  * is destroyed.
664  ****************************************************************************/
665 static void GtkFreeHashValue( gpointer key, gpointer value, gpointer user_data)
666 {
667     module_config_t *p_config = (module_config_t *)value;
668
669     if( p_config->i_type == MODULE_CONFIG_ITEM_STRING )
670         g_free( p_config->psz_value );
671     free( p_config );
672 }
673
674 /****************************************************************************
675  * GtkSaveHashValue: callback used when enumerating the hash table in
676  * GtkConfigApply().
677  ****************************************************************************/
678 static void GtkSaveHashValue( gpointer key, gpointer value, gpointer user_data)
679 {
680     module_config_t *p_config = (module_config_t *)value;
681
682     switch( p_config->i_type )
683     {
684
685     case MODULE_CONFIG_ITEM_STRING:
686     case MODULE_CONFIG_ITEM_FILE:
687     case MODULE_CONFIG_ITEM_PLUGIN:
688         config_PutPszVariable( p_config->psz_name,
689             *p_config->psz_value? p_config->psz_value : NULL );
690         break;
691     case MODULE_CONFIG_ITEM_INTEGER:
692     case MODULE_CONFIG_ITEM_BOOL:
693         config_PutIntVariable( p_config->psz_name, p_config->i_value );
694         break;
695     }
696 }
697
698 /****************************************************************************
699  * GtkConfigDialogDestroyed: callback triggered when the config dialog box is
700  * destroyed.
701  ****************************************************************************/
702 static void GtkConfigDialogDestroyed( GtkObject *object, gpointer user_data )
703 {
704     intf_thread_t *p_intf = (intf_thread_t *)user_data;
705     char *psz_module_name;
706
707     psz_module_name = gtk_object_get_data( object, "psz_module_name" );
708
709     /* remove the ref to the dialog box */
710     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_window),
711                          psz_module_name, NULL );
712
713 }