]> git.sesse.net Git - vlc/blob - plugins/gtk/gtk_preferences.c
* allow the user to resize the configuration windows
[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.21 2002/03/31 22:59:01 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     gtk_tooltips_set_tip( p_intf->p_sys->p_tooltips,                      \
99                           item_event_box, text, "" );                     \
100     gtk_container_set_border_width( GTK_CONTAINER(item_event_box), 4 );
101
102 /* draws a right aligned label in side of a widget */
103 #define LABEL_AND_WIDGET( label_text, widget, tooltip )                   \
104     gtk_table_resize( GTK_TABLE(category_table), ++rows, 2 );             \
105     item_align = gtk_alignment_new( 1, .5, 0, 0 );                        \
106     item_label = gtk_label_new( label_text );                             \
107     gtk_container_add( GTK_CONTAINER(item_align), item_label );           \
108     gtk_table_attach_defaults( GTK_TABLE(category_table), item_align,     \
109                                0, 1, rows - 1, rows );                    \
110     item_align = gtk_alignment_new( 0, .5, .5, 0 );                       \
111     gtk_container_add( GTK_CONTAINER(item_align), widget );               \
112     TOOLTIP(tooltip)                                                      \
113     gtk_container_add( GTK_CONTAINER(item_event_box), item_align );       \
114     gtk_table_attach_defaults( GTK_TABLE(category_table), item_event_box, \
115                                1, 2, rows - 1, rows );
116
117 static void GtkCreateConfigDialog( char *psz_module_name,
118                                    intf_thread_t *p_intf )
119 {
120     module_t *p_module, *p_module_bis;
121     int i;
122
123     guint rows = 0;
124
125     GHashTable *config_hash_table;
126
127     GtkWidget *item_event_box;
128
129     GtkWidget *config_dialog;
130     GtkWidget *config_dialog_vbox;
131     GtkWidget *config_notebook;
132
133     GtkWidget *category_table = NULL;
134     GtkWidget *category_label = NULL;
135
136 #ifndef MODULE_NAME_IS_gnome
137     GtkWidget *dialog_action_area;
138 #endif
139
140     GtkWidget *ok_button;
141     GtkWidget *apply_button;
142     GtkWidget *save_button;
143     GtkWidget *cancel_button;
144
145     GtkWidget *item_align;
146     GtkWidget *item_frame;
147     GtkWidget *item_hbox;
148     GtkWidget *item_label;
149     GtkWidget *item_vbox;
150     GtkWidget *string_entry;
151     GtkWidget *integer_spinbutton;
152     GtkObject *item_adj;
153     GtkWidget *bool_checkbutton;
154     GtkWidget *plugin_clist;
155     GtkWidget *plugin_config_button;
156     GtkWidget *plugin_select_button;
157
158     gint category_max_height;
159
160     /* Check if the dialog box is already opened, if so this will save us
161      * quite a bit of work. (the interface will be destroyed when you actually
162      * close the dialog window, but remember that it is only hidden if you
163      * clicked on the action buttons). This trick also allows us not to
164      * duplicate identical dialog windows. */
165
166     config_dialog = (GtkWidget *)gtk_object_get_data(
167                     GTK_OBJECT(p_intf->p_sys->p_window), psz_module_name );
168     if( config_dialog )
169     {
170         /* Yeah it was open */
171         gtk_widget_show( config_dialog );
172         gtk_widget_grab_focus( config_dialog );
173         return;
174     }
175
176
177     /* Look for the selected module */
178     for( p_module = p_module_bank->first ; p_module != NULL ;
179          p_module = p_module->next )
180     {
181
182         if( psz_module_name && !strcmp( psz_module_name, p_module->psz_name ) )
183             break;
184     }
185     if( !p_module ) return;
186
187     /* We found it, now we can start building its configuration interface */
188     /* Create the configuration dialog box */
189
190 #ifdef MODULE_NAME_IS_gnome
191     config_dialog = gnome_dialog_new( p_module->psz_longname, NULL );
192     config_dialog_vbox = GNOME_DIALOG(config_dialog)->vbox;
193     category_max_height = config_GetIntVariable( "gnome_prefs_maxh" );
194 #else
195     config_dialog = gtk_dialog_new();
196     gtk_window_set_title( GTK_WINDOW(config_dialog), p_module->psz_longname );
197     config_dialog_vbox = GTK_DIALOG(config_dialog)->vbox;
198     category_max_height = config_GetIntVariable( "gtk_prefs_maxh" );
199 #endif
200
201     gtk_window_set_policy( GTK_WINDOW(config_dialog), TRUE, TRUE, FALSE );
202     gtk_container_set_border_width( GTK_CONTAINER(config_dialog_vbox), 0 );
203
204     /* Create our config hash table and associate it with the dialog box */
205     config_hash_table = g_hash_table_new( NULL, NULL );
206     gtk_object_set_data_full( GTK_OBJECT(config_dialog),
207                               "config_hash_table", config_hash_table,
208                               (GtkDestroyNotify)GtkFreeHashTable );
209
210     /* Create notebook */
211     config_notebook = gtk_notebook_new();
212     gtk_notebook_set_scrollable( GTK_NOTEBOOK(config_notebook), TRUE );
213     gtk_container_add( GTK_CONTAINER(config_dialog_vbox), config_notebook );
214
215     /* Enumerate config options and add corresponding config boxes */
216     for( i = 0; i < p_module->i_config_lines; i++ )
217     {
218
219         switch( p_module->p_config[i].i_type )
220         {
221
222         case MODULE_CONFIG_HINT_CATEGORY:
223         case MODULE_CONFIG_HINT_END:
224
225             /*
226              * Before we start building the interface for the new category, we
227              * must close/finish the previous one we were generating.
228              */
229             if( category_table )
230             {
231                 GtkWidget *_scrolled_window;
232                 GtkWidget *_viewport;
233                 GtkWidget *_vbox;
234                 GtkRequisition _requisition;
235
236                 /* create a vbox to deal with EXPAND/FILL issues in the
237                  * notebook page, and pack it with the previously generated
238                  * category_table */
239                 _vbox = gtk_vbox_new( FALSE, 0 );
240                 gtk_container_set_border_width( GTK_CONTAINER(_vbox), 4 );
241                 gtk_box_pack_start( GTK_BOX(_vbox), category_table,
242                                     FALSE, FALSE, 0 );
243
244                 /* create a new scrolled window that will contain all of the
245                  * above. */
246                 _scrolled_window = gtk_scrolled_window_new( NULL, NULL );
247                 gtk_scrolled_window_set_policy(
248                     GTK_SCROLLED_WINDOW(_scrolled_window), GTK_POLICY_NEVER,
249                     GTK_POLICY_AUTOMATIC );
250                 /* add scrolled window as a notebook page */
251                 gtk_notebook_append_page( GTK_NOTEBOOK(config_notebook),
252                                           _scrolled_window, category_label );
253                 /* pack the vbox into the scrolled window */
254                 _viewport = gtk_viewport_new( NULL, NULL );
255                 gtk_viewport_set_shadow_type( GTK_VIEWPORT(_viewport),
256                                               GTK_SHADOW_NONE );
257                 gtk_container_add( GTK_CONTAINER(_viewport), _vbox );
258                 gtk_container_add( GTK_CONTAINER(_scrolled_window),
259                                    _viewport );
260
261                 /* set the size of the scrolled window to the size of the
262                  * child widget */
263                 gtk_widget_show_all( _vbox );
264                 gtk_widget_size_request( _vbox, &_requisition );
265                 if( _requisition.height > category_max_height )
266                     gtk_widget_set_usize( _scrolled_window, -1,
267                                           category_max_height );
268                 else
269                     gtk_widget_set_usize( _scrolled_window, -1,
270                                           _requisition.height );
271
272             }
273             if( p_module->p_config[i].i_type == MODULE_CONFIG_HINT_END ) break;
274
275
276             /*
277              * Now we can start taking care of the new category
278              */
279
280             /* create a new table for right-left alignment of children */
281             category_table = gtk_table_new( 0, 0, FALSE );
282             gtk_table_set_col_spacings( GTK_TABLE(category_table), 4 );
283             rows = 0;
284
285             /* create a new category label */
286             category_label = gtk_label_new( p_module->p_config[i].psz_text );
287
288             break;
289
290         case MODULE_CONFIG_ITEM_PLUGIN:
291
292             item_frame = gtk_frame_new( p_module->p_config[i].psz_text );
293
294             gtk_table_resize( GTK_TABLE(category_table), ++rows, 2 );
295             gtk_table_attach_defaults( GTK_TABLE(category_table), item_frame,
296                                        0, 2, rows - 1, rows );
297
298             item_vbox = gtk_vbox_new( FALSE, 4 );
299             gtk_container_add( GTK_CONTAINER(item_frame), item_vbox );
300
301             /* create a new clist widget */
302             {
303                 gchar * titles[] = { "Name", "Description" };
304
305                 plugin_clist =
306                     gtk_clist_new_with_titles( 2, titles );
307             }
308             gtk_clist_column_titles_passive( GTK_CLIST(plugin_clist) );
309             gtk_clist_set_selection_mode( GTK_CLIST(plugin_clist),
310                                           GTK_SELECTION_SINGLE);
311             gtk_container_add( GTK_CONTAINER(item_vbox), plugin_clist );
312
313             /* build a list of available plugins */
314             {
315                 gchar * entry[2];
316
317                 for( p_module_bis = p_module_bank->first ;
318                      p_module_bis != NULL ;
319                      p_module_bis = p_module_bis->next )
320                 {
321                     if( p_module_bis->i_capabilities &
322                         (1 << p_module->p_config[i].i_value) )
323                     {
324                         entry[0] = p_module_bis->psz_name;
325                         entry[1] = p_module_bis->psz_longname;
326                         gtk_clist_append( GTK_CLIST(plugin_clist), entry );
327                     }
328                 }
329             }
330
331             gtk_clist_set_column_auto_resize( GTK_CLIST(plugin_clist),
332                                               0, TRUE );
333             gtk_clist_set_column_auto_resize( GTK_CLIST(plugin_clist),
334                                               1, TRUE );
335
336             /* connect signals to the plugins list */
337             gtk_signal_connect( GTK_OBJECT(plugin_clist), "select_row",
338                                 GTK_SIGNAL_FUNC(GtkPluginHighlighted),
339                                 NULL );
340
341             /* hbox holding the "select" and "configure" buttons */
342             item_hbox = gtk_hbox_new( FALSE, 4 );
343             gtk_container_add( GTK_CONTAINER(item_vbox), item_hbox);
344
345             /* add configure button */
346             plugin_config_button =
347                 gtk_button_new_with_label( _("Configure") );
348             gtk_widget_set_sensitive( plugin_config_button, FALSE );
349             gtk_container_add( GTK_CONTAINER(item_hbox),
350                                plugin_config_button );
351             gtk_object_set_data( GTK_OBJECT(plugin_config_button),
352                                  "p_intf", p_intf );
353             gtk_object_set_data( GTK_OBJECT(plugin_clist),
354                                  "config_button", plugin_config_button );
355
356             /* add select button */
357             plugin_select_button =
358                 gtk_button_new_with_label( _("Select") );
359             gtk_container_add( GTK_CONTAINER(item_hbox),
360                                plugin_select_button );
361             /* add a tooltip on mouseover */
362             gtk_tooltips_set_tip( p_intf->p_sys->p_tooltips,
363                                   plugin_select_button,
364                                   p_module->p_config[i].psz_longtext, "" );
365
366             /* hbox holding the "selected" label and text input */
367             item_hbox = gtk_hbox_new( FALSE, 4 );
368             gtk_container_add( GTK_CONTAINER(item_vbox), item_hbox);
369             /* add new label */
370             item_label = gtk_label_new( _("Selected:") );
371             gtk_container_add( GTK_CONTAINER(item_hbox), item_label );
372
373             /* add input box with default value */
374             string_entry = gtk_entry_new();
375             gtk_object_set_data( GTK_OBJECT(plugin_clist),
376                                  "plugin_entry", string_entry );
377             gtk_container_add( GTK_CONTAINER(item_hbox), string_entry );
378             vlc_mutex_lock( p_module->p_config[i].p_lock );
379             gtk_entry_set_text( GTK_ENTRY(string_entry),
380                                 p_module->p_config[i].psz_value ?
381                                 p_module->p_config[i].psz_value : "" );
382             vlc_mutex_unlock( p_module->p_config[i].p_lock );
383             /* add a tooltip on mouseover */
384             gtk_tooltips_set_tip( p_intf->p_sys->p_tooltips,
385                                   string_entry,
386                                   p_module->p_config[i].psz_longtext, "" );
387
388             /* connect signals to the buttons */
389             gtk_signal_connect( GTK_OBJECT(plugin_config_button), "clicked",
390                                 GTK_SIGNAL_FUNC(GtkPluginConfigure),
391                                 (gpointer)plugin_clist );
392             gtk_signal_connect( GTK_OBJECT(plugin_select_button), "clicked",
393                                 GTK_SIGNAL_FUNC(GtkPluginSelected),
394                                 (gpointer)plugin_clist );
395
396             /* connect signal to track changes in the text box */
397             gtk_object_set_data( GTK_OBJECT(string_entry), "config_option",
398                                  p_module->p_config[i].psz_name );
399             gtk_signal_connect( GTK_OBJECT(string_entry), "changed",
400                                 GTK_SIGNAL_FUNC(GtkStringChanged),
401                                 (gpointer)config_dialog );
402             break;
403
404         case MODULE_CONFIG_ITEM_STRING:
405         case MODULE_CONFIG_ITEM_FILE:
406
407             /* add input box with default value */
408             string_entry = gtk_entry_new();
409             vlc_mutex_lock( p_module->p_config[i].p_lock );
410             gtk_entry_set_text( GTK_ENTRY(string_entry),
411                                 p_module->p_config[i].psz_value ?
412                                 p_module->p_config[i].psz_value : "" );
413             vlc_mutex_unlock( p_module->p_config[i].p_lock );
414
415             /* connect signal to track changes in the text box */
416             gtk_object_set_data( GTK_OBJECT(string_entry), "config_option",
417                                  p_module->p_config[i].psz_name );
418             gtk_signal_connect( GTK_OBJECT(string_entry), "changed",
419                                 GTK_SIGNAL_FUNC(GtkStringChanged),
420                                 (gpointer)config_dialog );
421
422             LABEL_AND_WIDGET( p_module->p_config[i].psz_text,
423                               string_entry,
424                               p_module->p_config[i].psz_longtext );
425             break;
426
427         case MODULE_CONFIG_ITEM_INTEGER:
428
429             /* add input box with default value */
430             item_adj = gtk_adjustment_new( p_module->p_config[i].i_value,
431                                            -1, 99999, 1, 10, 10 );
432             integer_spinbutton = gtk_spin_button_new( GTK_ADJUSTMENT(item_adj),
433                                                       1, 0 );
434
435             /* connect signal to track changes in the spinbutton value */
436             gtk_object_set_data( GTK_OBJECT(integer_spinbutton),
437                                  "config_option",
438                                  p_module->p_config[i].psz_name );
439             gtk_signal_connect( GTK_OBJECT(integer_spinbutton), "changed",
440                                 GTK_SIGNAL_FUNC(GtkIntChanged),
441                                 (gpointer)config_dialog );
442
443             LABEL_AND_WIDGET( p_module->p_config[i].psz_text,
444                               integer_spinbutton,
445                               p_module->p_config[i].psz_longtext );
446             break;
447
448         case MODULE_CONFIG_ITEM_BOOL:
449
450             /* add check button */
451             bool_checkbutton = gtk_check_button_new();
452             gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(bool_checkbutton),
453                                           p_module->p_config[i].i_value );
454
455             /* connect signal to track changes in the button state */
456             gtk_object_set_data( GTK_OBJECT(bool_checkbutton), "config_option",
457                                  p_module->p_config[i].psz_name );
458             gtk_signal_connect( GTK_OBJECT(bool_checkbutton), "toggled",
459                                 GTK_SIGNAL_FUNC(GtkBoolChanged),
460                                 (gpointer)config_dialog );
461
462             LABEL_AND_WIDGET( p_module->p_config[i].psz_text,
463                               bool_checkbutton,
464                               p_module->p_config[i].psz_longtext );
465             break;
466
467         }
468     }
469
470 #ifndef MODULE_NAME_IS_gnome
471     /* Now let's add the action buttons at the bottom of the page */
472     dialog_action_area = GTK_DIALOG(config_dialog)->action_area;
473     gtk_container_set_border_width( GTK_CONTAINER(dialog_action_area), 4 );
474
475     /* add a new table for the config option */
476     item_hbox = gtk_hbox_new( FALSE, 0 );
477     gtk_box_pack_end( GTK_BOX(dialog_action_area), item_hbox,
478                       TRUE, FALSE, 0 );
479     item_hbox = gtk_hbox_new( FALSE, 0 );
480     gtk_box_pack_end( GTK_BOX(dialog_action_area), item_hbox,
481                       TRUE, FALSE, 0 );
482 #endif
483
484     /* Create the OK button */
485 #ifdef MODULE_NAME_IS_gnome
486     gnome_dialog_append_button( GNOME_DIALOG(config_dialog),
487                                 GNOME_STOCK_BUTTON_OK );
488     ok_button =
489         GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
490
491     gnome_dialog_append_button( GNOME_DIALOG(config_dialog),
492                                 GNOME_STOCK_BUTTON_APPLY );
493     apply_button =
494         GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
495
496     gnome_dialog_append_button_with_pixmap(
497         GNOME_DIALOG(config_dialog), _("Save"), GNOME_STOCK_PIXMAP_SAVE );
498     save_button =
499         GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
500
501     gnome_dialog_append_button( GNOME_DIALOG(config_dialog),
502                                 GNOME_STOCK_BUTTON_CANCEL );
503     cancel_button =
504         GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
505 #else
506     ok_button = gtk_button_new_with_label( _("Ok") );
507     gtk_box_pack_start( GTK_BOX(dialog_action_area), ok_button,
508                         TRUE, TRUE, 0 );
509
510     apply_button = gtk_button_new_with_label( _("Apply") );
511     gtk_box_pack_start( GTK_BOX(dialog_action_area), apply_button,
512                         TRUE, TRUE, 0 );
513
514     save_button = gtk_button_new_with_label( _("Save") );
515     gtk_box_pack_start( GTK_BOX(dialog_action_area), save_button,
516                         TRUE, TRUE, 0 );
517
518     cancel_button = gtk_button_new_with_label( _("Cancel") );
519     gtk_box_pack_start( GTK_BOX(dialog_action_area), cancel_button,
520                         TRUE, TRUE, 0 );
521 #endif
522
523     gtk_signal_connect( GTK_OBJECT(ok_button), "clicked",
524                         GTK_SIGNAL_FUNC(GtkConfigOk),
525                         config_dialog );
526     gtk_signal_connect( GTK_OBJECT(apply_button), "clicked",
527                         GTK_SIGNAL_FUNC(GtkConfigApply),
528                         config_dialog );
529     gtk_signal_connect( GTK_OBJECT(save_button), "clicked",
530                         GTK_SIGNAL_FUNC(GtkConfigSave),
531                         config_dialog );
532     gtk_signal_connect( GTK_OBJECT(cancel_button), "clicked",
533                         GTK_SIGNAL_FUNC(GtkConfigCancel),
534                         config_dialog );
535
536
537
538     /* Ok, job done successfully. Let's keep a reference to the dialog box */
539     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_window),
540                          psz_module_name, config_dialog );
541     gtk_object_set_data( GTK_OBJECT(config_dialog), "psz_module_name",
542                          psz_module_name );
543
544     /* we want this ref to be destroyed if the object is destroyed */
545     gtk_signal_connect( GTK_OBJECT(config_dialog), "destroy",
546                        GTK_SIGNAL_FUNC(GtkConfigDialogDestroyed),
547                        (gpointer)p_intf );
548
549     gtk_widget_show_all( config_dialog );
550 }
551
552 #undef LABEL_AND_WIDGET
553 #undef TOOLTIP
554
555 /****************************************************************************
556  * GtkConfigApply: store the changes to the config inside the modules
557  * configuration structure
558  ****************************************************************************/
559 void GtkConfigApply( GtkButton * button, gpointer user_data )
560 {
561     GHashTable *hash_table;
562     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
563                                                     "config_hash_table" );
564     g_hash_table_foreach( hash_table, GtkSaveHashValue, NULL );
565
566 }
567
568 void GtkConfigOk( GtkButton * button, gpointer user_data )
569 {
570     GtkConfigApply( button, user_data );
571     gtk_widget_hide( gtk_widget_get_toplevel( GTK_WIDGET (button) ) );
572 }
573
574
575 void GtkConfigCancel( GtkButton * button, gpointer user_data )
576 {
577     gtk_widget_hide( gtk_widget_get_toplevel( GTK_WIDGET (button) ) );
578 }
579
580 void GtkConfigSave( GtkButton * button, gpointer user_data )
581 {
582     GtkConfigApply( button, user_data );
583     config_SaveConfigFile( NULL );
584 }
585
586 /****************************************************************************
587  * GtkPluginHighlighted: display plugin description when an entry is selected
588  *   in the clist, and activate the configure button if necessary.
589  ****************************************************************************/
590 void GtkPluginHighlighted( GtkCList *plugin_clist, int row, int column,
591                            GdkEventButton *event, gpointer user_data )
592 {
593     GtkWidget *config_button;
594     module_t *p_module;
595     char *psz_name;
596
597     if( gtk_clist_get_text( GTK_CLIST(plugin_clist), row, 0, &psz_name ) )
598     {
599         /* look for plugin 'psz_name' */
600         for( p_module = p_module_bank->first ;
601              p_module != NULL ;
602              p_module = p_module->next )
603         {
604           if( !strcmp( p_module->psz_name, psz_name ) )
605           {
606               gtk_object_set_data( GTK_OBJECT(plugin_clist),
607                                    "plugin_highlighted", p_module );
608               config_button = gtk_object_get_data( GTK_OBJECT(plugin_clist),
609                                                    "config_button" );
610               if( p_module->i_config_items )
611                   gtk_widget_set_sensitive( config_button, TRUE );
612               else
613                   gtk_widget_set_sensitive( config_button, FALSE );
614
615               break;
616           }
617         }
618
619     }
620 }
621
622 /****************************************************************************
623  * GtkPluginConfigure: display plugin configuration dialog box.
624  ****************************************************************************/
625 void GtkPluginConfigure( GtkButton *button, gpointer user_data )
626 {
627     module_t *p_module;
628     intf_thread_t *p_intf;
629
630     p_module = (module_t *)gtk_object_get_data( GTK_OBJECT(user_data),
631                                                 "plugin_highlighted" );
632
633     if( !p_module ) return;
634     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(button),
635                                                    "p_intf" );
636     GtkCreateConfigDialog( p_module->psz_name, (gpointer)p_intf );
637
638 }
639
640 /****************************************************************************
641  * GtkPluginSelected: select plugin.
642  ****************************************************************************/
643 void GtkPluginSelected( GtkButton *button, gpointer user_data )
644 {
645     module_t *p_module;
646     GtkWidget *widget;
647
648     p_module = (module_t *)gtk_object_get_data( GTK_OBJECT(user_data),
649                                                 "plugin_highlighted" );
650     widget = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
651                                                "plugin_entry" );
652     if( !p_module ) return;
653
654     gtk_entry_set_text( GTK_ENTRY(widget), p_module->psz_name );
655
656 }
657
658 /****************************************************************************
659  * GtkStringChanged: signal called when the user changes a string value.
660  ****************************************************************************/
661 static void GtkStringChanged( GtkEditable *editable, gpointer user_data )
662 {
663     module_config_t *p_config;
664
665     GHashTable *hash_table;
666
667     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
668                                                     "config_hash_table" );
669     /* free old p_config */
670     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
671                                                        (gpointer)editable );
672     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, NULL );
673
674     p_config = malloc( sizeof(module_config_t) );
675     p_config->i_type = MODULE_CONFIG_ITEM_STRING;
676     p_config->psz_value = gtk_editable_get_chars( editable, 0, -1 );
677     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(editable),
678                                                       "config_option" );
679
680     g_hash_table_insert( hash_table, (gpointer)editable,
681                          (gpointer)p_config );
682 }
683
684 /****************************************************************************
685  * GtkIntChanged: signal called when the user changes a an integer value.
686  ****************************************************************************/
687 static void GtkIntChanged( GtkEditable *editable, gpointer user_data )
688 {
689     module_config_t *p_config;
690
691     GHashTable *hash_table;
692
693     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
694                                                     "config_hash_table" );
695
696     /* free old p_config */
697     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
698                                                        (gpointer)editable );
699     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, NULL );
700
701     p_config = malloc( sizeof(module_config_t) );
702     p_config->i_type = MODULE_CONFIG_ITEM_INTEGER;
703     p_config->i_value =  gtk_spin_button_get_value_as_int(
704                              GTK_SPIN_BUTTON(editable) );
705     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(editable),
706                                                       "config_option" );
707
708     g_hash_table_insert( hash_table, (gpointer)editable,
709                          (gpointer)p_config );
710 }
711
712 /****************************************************************************
713  * GtkStringChanged: signal called when the user changes a bool value.
714  ****************************************************************************/
715 static void GtkBoolChanged( GtkToggleButton *button, gpointer user_data )
716 {
717     module_config_t *p_config;
718
719     GHashTable *hash_table;
720
721     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
722                                                     "config_hash_table" );
723
724     /* free old p_config */
725     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
726                                                        (gpointer)button );
727     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, NULL );
728
729     p_config = malloc( sizeof(module_config_t) );
730     p_config->i_type = MODULE_CONFIG_ITEM_BOOL;
731     p_config->i_value = gtk_toggle_button_get_active( button );
732     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(button),
733                                                       "config_option" );
734
735     g_hash_table_insert( hash_table, (gpointer)button,
736                          (gpointer)p_config );
737 }
738
739 /****************************************************************************
740  * GtkFreeHashTable: signal called when the config hash table is destroyed.
741  ****************************************************************************/
742 static void GtkFreeHashTable( gpointer user_data )
743 {
744     GHashTable *hash_table = (GHashTable *)user_data;
745
746     g_hash_table_foreach( hash_table, GtkFreeHashValue, NULL );
747     g_hash_table_destroy( hash_table );
748 }
749
750 /****************************************************************************
751  * GtkFreeHashValue: signal called when an element of the config hash table
752  * is destroyed.
753  ****************************************************************************/
754 static void GtkFreeHashValue( gpointer key, gpointer value, gpointer user_data)
755 {
756     module_config_t *p_config = (module_config_t *)value;
757
758     if( p_config->i_type == MODULE_CONFIG_ITEM_STRING )
759         g_free( p_config->psz_value );
760     free( p_config );
761 }
762
763 /****************************************************************************
764  * GtkSaveHashValue: callback used when enumerating the hash table in
765  * GtkConfigApply().
766  ****************************************************************************/
767 static void GtkSaveHashValue( gpointer key, gpointer value, gpointer user_data)
768 {
769     module_config_t *p_config = (module_config_t *)value;
770
771     switch( p_config->i_type )
772     {
773
774     case MODULE_CONFIG_ITEM_STRING:
775     case MODULE_CONFIG_ITEM_FILE:
776     case MODULE_CONFIG_ITEM_PLUGIN:
777         config_PutPszVariable( p_config->psz_name,
778             *p_config->psz_value? p_config->psz_value : NULL );
779         break;
780     case MODULE_CONFIG_ITEM_INTEGER:
781     case MODULE_CONFIG_ITEM_BOOL:
782         config_PutIntVariable( p_config->psz_name, p_config->i_value );
783         break;
784     }
785 }
786
787 /****************************************************************************
788  * GtkConfigDialogDestroyed: callback triggered when the config dialog box is
789  * destroyed.
790  ****************************************************************************/
791 static void GtkConfigDialogDestroyed( GtkObject *object, gpointer user_data )
792 {
793     intf_thread_t *p_intf = (intf_thread_t *)user_data;
794     char *psz_module_name;
795
796     psz_module_name = gtk_object_get_data( object, "psz_module_name" );
797
798     /* remove the ref to the dialog box */
799     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_window),
800                          psz_module_name, NULL );
801
802 }