]> git.sesse.net Git - vlc/blob - modules/gui/gtk/preferences.c
* ./configure.ac.in: removed -W in favour of -Wtraditional.
[vlc] / modules / gui / gtk / preferences.c
1 /*****************************************************************************
2  * gtk_preferences.c: functions to handle the preferences dialog box.
3  *****************************************************************************
4  * Copyright (C) 2000, 2001 VideoLAN
5  * $Id: preferences.c,v 1.5 2002/12/06 16:34:07 sam 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 <vlc/vlc.h>
38 #include <vlc/intf.h>
39
40 #ifdef MODULE_NAME_IS_gnome
41 #   include <gnome.h>
42 #else
43 #   include <gtk/gtk.h>
44 #endif
45
46 #include <string.h>
47
48 #include "gtk_support.h"
49
50 #include "common.h"
51 #include "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 GtkFloatChanged      ( GtkEditable *, gpointer );
66 static void GtkBoolChanged       ( GtkToggleButton *, gpointer );
67
68 static void GtkFreeHashTable     ( GtkObject *object );
69 static void GtkFreeHashValue     ( gpointer, gpointer, gpointer );
70 static gboolean GtkSaveHashValue ( gpointer, gpointer, gpointer );
71
72 static void GtkModuleConfigure   ( GtkButton *, gpointer );
73 static void GtkModuleSelected    ( GtkButton *, gpointer );
74 static void GtkModuleHighlighted ( GtkCList *, int, int, GdkEventButton *,
75                                    gpointer );
76
77 /****************************************************************************
78  * Callback for menuitems: display configuration interface window
79  ****************************************************************************/
80 void GtkPreferencesShow( GtkMenuItem * menuitem, gpointer user_data )
81 {
82     intf_thread_t * p_intf;
83
84     p_intf = GtkGetIntf( menuitem );
85
86     GtkCreateConfigDialog( "main", p_intf );
87 }
88
89 /****************************************************************************
90  * GtkCreateConfigDialog: dynamically creates the configuration dialog
91  * box from all the configuration data provided by the selected module.
92  ****************************************************************************/
93
94 /* create a new tooltipped area */
95 #define TOOLTIP( text )                                                   \
96     /* create an event box to catch some events */                        \
97     item_event_box = gtk_event_box_new();                                 \
98     /* add a tooltip on mouseover */                                      \
99     gtk_tooltips_set_tip( p_intf->p_sys->p_tooltips,                      \
100                           item_event_box, 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     gtk_table_attach_defaults( GTK_TABLE(category_table), item_align,     \
110                                0, 1, rows - 1, rows );                    \
111     item_align = gtk_alignment_new( 0, .5, .5, 0 );                       \
112     gtk_container_add( GTK_CONTAINER(item_align), widget );               \
113     TOOLTIP(tooltip)                                                      \
114     gtk_container_add( GTK_CONTAINER(item_event_box), item_align );       \
115     gtk_table_attach_defaults( GTK_TABLE(category_table), item_event_box, \
116                                1, 2, rows - 1, rows );
117
118 static void GtkCreateConfigDialog( char *psz_module_name,
119                                    intf_thread_t *p_intf )
120 {
121     module_t **pp_parser;
122     vlc_list_t *p_list;
123     module_config_t *p_item;
124
125     guint rows = 0;
126
127     GHashTable *config_hash_table;
128
129     GtkWidget *item_event_box;
130
131     GtkWidget *config_dialog;
132     GtkWidget *config_dialog_vbox;
133     GtkWidget *config_notebook;
134
135     GtkWidget *category_table = NULL;
136     GtkWidget *category_label = NULL;
137
138 #ifndef MODULE_NAME_IS_gnome
139     GtkWidget *dialog_action_area;
140 #endif
141
142     GtkWidget *ok_button;
143     GtkWidget *apply_button;
144     GtkWidget *save_button;
145     GtkWidget *cancel_button;
146
147     GtkWidget *item_align;
148     GtkWidget *item_frame;
149     GtkWidget *item_hbox;
150     GtkWidget *item_label;
151     GtkWidget *item_vbox;
152     GtkWidget *item_combo;
153     GtkWidget *string_entry;
154     GtkWidget *integer_spinbutton;
155     GtkWidget *float_spinbutton;
156     GtkObject *item_adj;
157     GtkWidget *bool_checkbutton;
158     GtkWidget *module_clist;
159     GtkWidget *module_config_button;
160     GtkWidget *module_select_button;
161
162     gint category_max_height;
163
164     /* Check if the dialog box is already opened because we don't want to
165      * duplicate identical dialog windows. */
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_grab_focus( config_dialog );
172         return;
173     }
174
175
176     /* Look for the selected module */
177     p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE, FIND_ANYWHERE );
178
179     for( pp_parser = (module_t **)p_list->pp_objects ;
180          *pp_parser ;
181          pp_parser++ )
182     {
183
184         if( psz_module_name
185              && !strcmp( psz_module_name, (*pp_parser)->psz_object_name ) )
186         {
187             break;
188         }
189     }
190
191     if( !(*pp_parser) )
192     {
193         vlc_list_release( p_list );
194         return;
195     }
196
197     /* We found it, now we can start building its configuration interface */
198     /* Create the configuration dialog box */
199
200 #ifdef MODULE_NAME_IS_gnome
201     config_dialog = gnome_dialog_new( (*pp_parser)->psz_longname, NULL );
202     config_dialog_vbox = GNOME_DIALOG(config_dialog)->vbox;
203 #else
204     config_dialog = gtk_dialog_new();
205     gtk_window_set_title( GTK_WINDOW(config_dialog),
206                           (*pp_parser)->psz_longname );
207     config_dialog_vbox = GTK_DIALOG(config_dialog)->vbox;
208 #endif
209
210     gtk_object_set_data( GTK_OBJECT(config_dialog), "p_intf", p_intf );
211
212     category_max_height = config_GetInt( p_intf, MODULE_STRING "-prefs-maxh" );
213
214     gtk_window_set_policy( GTK_WINDOW(config_dialog), TRUE, TRUE, FALSE );
215     gtk_container_set_border_width( GTK_CONTAINER(config_dialog_vbox), 0 );
216
217     /* Create our config hash table and associate it with the dialog box */
218     config_hash_table = g_hash_table_new( NULL, NULL );
219     gtk_object_set_data( GTK_OBJECT(config_dialog),
220                          "config_hash_table", config_hash_table );
221
222     /* Create notebook */
223     config_notebook = gtk_notebook_new();
224     gtk_notebook_set_scrollable( GTK_NOTEBOOK(config_notebook), TRUE );
225     gtk_container_add( GTK_CONTAINER(config_dialog_vbox), config_notebook );
226
227     /* Enumerate config options and add corresponding config boxes */
228     p_item = (*pp_parser)->p_config;
229
230     if( p_item ) do
231     {
232         switch( p_item->i_type )
233         {
234
235         case CONFIG_HINT_CATEGORY:
236         case CONFIG_HINT_END:
237
238             /*
239              * Before we start building the interface for the new category, we
240              * must close/finish the previous one we were generating.
241              */
242             if( category_table )
243             {
244                 GtkWidget *_scrolled_window;
245                 GtkWidget *_viewport;
246                 GtkWidget *_vbox;
247                 GtkRequisition _requisition;
248
249                 /* create a vbox to deal with EXPAND/FILL issues in the
250                  * notebook page, and pack it with the previously generated
251                  * category_table */
252                 _vbox = gtk_vbox_new( FALSE, 0 );
253                 gtk_container_set_border_width( GTK_CONTAINER(_vbox), 4 );
254                 gtk_box_pack_start( GTK_BOX(_vbox), category_table,
255                                     FALSE, FALSE, 0 );
256
257                 /* create a new scrolled window that will contain all of the
258                  * above. */
259                 _scrolled_window = gtk_scrolled_window_new( NULL, NULL );
260                 gtk_scrolled_window_set_policy(
261                     GTK_SCROLLED_WINDOW(_scrolled_window), GTK_POLICY_NEVER,
262                     GTK_POLICY_AUTOMATIC );
263                 /* add scrolled window as a notebook page */
264                 gtk_notebook_append_page( GTK_NOTEBOOK(config_notebook),
265                                           _scrolled_window, category_label );
266                 /* pack the vbox into the scrolled window */
267                 _viewport = gtk_viewport_new( NULL, NULL );
268                 gtk_viewport_set_shadow_type( GTK_VIEWPORT(_viewport),
269                                               GTK_SHADOW_NONE );
270                 gtk_container_add( GTK_CONTAINER(_viewport), _vbox );
271                 gtk_container_add( GTK_CONTAINER(_scrolled_window),
272                                    _viewport );
273
274                 /* set the size of the scrolled window to the size of the
275                  * child widget */
276                 gtk_widget_show_all( _vbox );
277                 gtk_widget_size_request( _vbox, &_requisition );
278                 if( _requisition.height > category_max_height )
279                     gtk_widget_set_usize( _scrolled_window, -1,
280                                           category_max_height );
281                 else
282                     gtk_widget_set_usize( _scrolled_window, -1,
283                                           _requisition.height );
284
285             }
286
287             /*
288              * Now we can start taking care of the new category
289              */
290
291             if( p_item->i_type == CONFIG_HINT_CATEGORY )
292             {
293                 /* create a new table for right-left alignment of children */
294                 category_table = gtk_table_new( 0, 0, FALSE );
295                 gtk_table_set_col_spacings( GTK_TABLE(category_table), 4 );
296                 rows = 0;
297
298                 /* create a new category label */
299                 category_label = gtk_label_new( p_item->psz_text );
300             }
301
302             break;
303
304         case CONFIG_ITEM_MODULE:
305
306             item_frame = gtk_frame_new( p_item->psz_text );
307
308             gtk_table_resize( GTK_TABLE(category_table), ++rows, 2 );
309             gtk_table_attach_defaults( GTK_TABLE(category_table), item_frame,
310                                        0, 2, rows - 1, rows );
311
312             item_vbox = gtk_vbox_new( FALSE, 4 );
313             gtk_container_add( GTK_CONTAINER(item_frame), item_vbox );
314
315             /* create a new clist widget */
316             {
317                 gchar * titles[] = { N_("Name"), N_("Description") };
318                 titles[0] = _(titles[0]);
319                 titles[1] = _(titles[1]);
320
321                 module_clist = gtk_clist_new_with_titles( 2, titles );
322             }
323             gtk_object_set_data( GTK_OBJECT(module_clist), "p_intf", p_intf );
324             gtk_clist_column_titles_passive( GTK_CLIST(module_clist) );
325             gtk_clist_set_selection_mode( GTK_CLIST(module_clist),
326                                           GTK_SELECTION_SINGLE);
327             gtk_container_add( GTK_CONTAINER(item_vbox), module_clist );
328
329             /* build a list of available modules */
330             {
331                 gchar * entry[2];
332                 pp_parser = (module_t **)p_list->pp_objects;
333
334                 for( ; *pp_parser ; pp_parser++ )
335                 {
336                     if( !strcmp( (*pp_parser)->psz_capability,
337                                  p_item->psz_type ) )
338                     {
339                         entry[0] = (*pp_parser)->psz_object_name;
340                         entry[1] = (*pp_parser)->psz_longname;
341                         gtk_clist_append( GTK_CLIST(module_clist), entry );
342                     }
343                 }
344             }
345
346             gtk_clist_set_column_auto_resize( GTK_CLIST(module_clist),
347                                               0, TRUE );
348             gtk_clist_set_column_auto_resize( GTK_CLIST(module_clist),
349                                               1, TRUE );
350
351             /* connect signals to the modules list */
352             gtk_signal_connect( GTK_OBJECT(module_clist), "select_row",
353                                 GTK_SIGNAL_FUNC(GtkModuleHighlighted),
354                                 NULL );
355
356             /* hbox holding the "select" and "configure" buttons */
357             item_hbox = gtk_hbox_new( FALSE, 4 );
358             gtk_container_add( GTK_CONTAINER(item_vbox), item_hbox);
359
360             /* add configure button */
361             module_config_button =
362                 gtk_button_new_with_label( _("Configure") );
363             gtk_widget_set_sensitive( module_config_button, FALSE );
364             gtk_container_add( GTK_CONTAINER(item_hbox),
365                                module_config_button );
366             gtk_object_set_data( GTK_OBJECT(module_config_button),
367                                  "p_intf", p_intf );
368             gtk_object_set_data( GTK_OBJECT(module_clist),
369                                  "config_button", module_config_button );
370
371             /* add select button */
372             module_select_button =
373                 gtk_button_new_with_label( _("Select") );
374             gtk_container_add( GTK_CONTAINER(item_hbox),
375                                module_select_button );
376             /* add a tooltip on mouseover */
377             gtk_tooltips_set_tip( p_intf->p_sys->p_tooltips,
378                                   module_select_button,
379                                   p_item->psz_longtext, "" );
380
381             /* hbox holding the "selected" label and text input */
382             item_hbox = gtk_hbox_new( FALSE, 4 );
383             gtk_container_add( GTK_CONTAINER(item_vbox), item_hbox);
384             /* add new label */
385             item_label = gtk_label_new( _("Selected:") );
386             gtk_container_add( GTK_CONTAINER(item_hbox), item_label );
387
388             /* add input box with default value */
389             string_entry = gtk_entry_new();
390             gtk_object_set_data( GTK_OBJECT(module_clist),
391                                  "module_entry", string_entry );
392             gtk_container_add( GTK_CONTAINER(item_hbox), string_entry );
393             vlc_mutex_lock( p_item->p_lock );
394             gtk_entry_set_text( GTK_ENTRY(string_entry),
395                                 p_item->psz_value ? p_item->psz_value : "" );
396             vlc_mutex_unlock( p_item->p_lock );
397             /* add a tooltip on mouseover */
398             gtk_tooltips_set_tip( p_intf->p_sys->p_tooltips,
399                                   string_entry, p_item->psz_longtext, "" );
400
401             /* connect signals to the buttons */
402             gtk_signal_connect( GTK_OBJECT(module_config_button), "clicked",
403                                 GTK_SIGNAL_FUNC(GtkModuleConfigure),
404                                 (gpointer)module_clist );
405             gtk_signal_connect( GTK_OBJECT(module_select_button), "clicked",
406                                 GTK_SIGNAL_FUNC(GtkModuleSelected),
407                                 (gpointer)module_clist );
408
409             /* connect signal to track changes in the text box */
410             gtk_object_set_data( GTK_OBJECT(string_entry), "config_option",
411                                  p_item->psz_name );
412             gtk_signal_connect( GTK_OBJECT(string_entry), "changed",
413                                 GTK_SIGNAL_FUNC(GtkStringChanged),
414                                 (gpointer)config_dialog );
415             break;
416
417         case CONFIG_ITEM_STRING:
418         case CONFIG_ITEM_FILE:
419
420             if( !p_item->ppsz_list )
421             {
422                 /* add input box with default value */
423                 item_combo = string_entry = gtk_entry_new();
424             }
425             else
426             {
427                 /* add combo box with default value */
428                 GList *items = NULL;
429                 int i;
430
431                 for( i=0; p_item->ppsz_list[i]; i++ )
432                     items = g_list_append( items, p_item->ppsz_list[i] );
433
434                 item_combo = gtk_combo_new();
435                 string_entry = GTK_COMBO(item_combo)->entry;
436                 gtk_combo_set_popdown_strings( GTK_COMBO(item_combo),
437                                                items );
438
439             }
440
441             vlc_mutex_lock( p_item->p_lock );
442             gtk_entry_set_text( GTK_ENTRY(string_entry),
443                                 p_item->psz_value ? p_item->psz_value : "" );
444             vlc_mutex_unlock( p_item->p_lock );
445
446             /* connect signal to track changes in the text box */
447             gtk_object_set_data( GTK_OBJECT(string_entry), "config_option",
448                                  p_item->psz_name );
449             gtk_signal_connect( GTK_OBJECT(string_entry), "changed",
450                                 GTK_SIGNAL_FUNC(GtkStringChanged),
451                                 (gpointer)config_dialog );
452
453             LABEL_AND_WIDGET( p_item->psz_text,
454                               item_combo, p_item->psz_longtext );
455             break;
456
457         case CONFIG_ITEM_INTEGER:
458
459             /* add input box with default value */
460             item_adj = gtk_adjustment_new( p_item->i_value,
461                                            -1, 99999, 1, 10, 10 );
462             integer_spinbutton = gtk_spin_button_new( GTK_ADJUSTMENT(item_adj),
463                                                       1, 0 );
464
465             /* connect signal to track changes in the spinbutton value */
466             gtk_object_set_data( GTK_OBJECT(integer_spinbutton),
467                                  "config_option", p_item->psz_name );
468             gtk_signal_connect( GTK_OBJECT(integer_spinbutton), "changed",
469                                 GTK_SIGNAL_FUNC(GtkIntChanged),
470                                 (gpointer)config_dialog );
471
472             LABEL_AND_WIDGET( p_item->psz_text,
473                               integer_spinbutton, p_item->psz_longtext );
474             break;
475
476         case CONFIG_ITEM_FLOAT:
477
478             /* add input box with default value */
479             item_adj = gtk_adjustment_new( p_item->f_value,
480                                            0, 99999, 0.01, 10, 10 );
481             float_spinbutton = gtk_spin_button_new( GTK_ADJUSTMENT(item_adj),
482                                                     0.01, 2 );
483
484             /* connect signal to track changes in the spinbutton value */
485             gtk_object_set_data( GTK_OBJECT(float_spinbutton),
486                                  "config_option", p_item->psz_name );
487             gtk_signal_connect( GTK_OBJECT(float_spinbutton), "changed",
488                                 GTK_SIGNAL_FUNC(GtkFloatChanged),
489                                 (gpointer)config_dialog );
490
491             LABEL_AND_WIDGET( p_item->psz_text,
492                               float_spinbutton, p_item->psz_longtext );
493             break;
494
495         case CONFIG_ITEM_BOOL:
496
497             /* add check button */
498             bool_checkbutton = gtk_check_button_new();
499             gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(bool_checkbutton),
500                                           p_item->i_value );
501
502             /* connect signal to track changes in the button state */
503             gtk_object_set_data( GTK_OBJECT(bool_checkbutton), "config_option",
504                                  p_item->psz_name );
505             gtk_signal_connect( GTK_OBJECT(bool_checkbutton), "toggled",
506                                 GTK_SIGNAL_FUNC(GtkBoolChanged),
507                                 (gpointer)config_dialog );
508
509             LABEL_AND_WIDGET( p_item->psz_text,
510                               bool_checkbutton, p_item->psz_longtext );
511             break;
512
513         }
514
515     }
516     while( p_item->i_type != CONFIG_HINT_END && p_item++ );
517
518     vlc_list_release( p_list );
519
520 #ifndef MODULE_NAME_IS_gnome
521     /* Now let's add the action buttons at the bottom of the page */
522     dialog_action_area = GTK_DIALOG(config_dialog)->action_area;
523     gtk_container_set_border_width( GTK_CONTAINER(dialog_action_area), 4 );
524
525     /* add a new table for the config option */
526     item_hbox = gtk_hbox_new( FALSE, 0 );
527     gtk_box_pack_end( GTK_BOX(dialog_action_area), item_hbox,
528                       TRUE, FALSE, 0 );
529     item_hbox = gtk_hbox_new( FALSE, 0 );
530     gtk_box_pack_end( GTK_BOX(dialog_action_area), item_hbox,
531                       TRUE, FALSE, 0 );
532 #endif
533
534     /* Create the OK button */
535 #ifdef MODULE_NAME_IS_gnome
536     gnome_dialog_append_button( GNOME_DIALOG(config_dialog),
537                                 GNOME_STOCK_BUTTON_OK );
538     ok_button =
539         GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
540
541     gnome_dialog_append_button( GNOME_DIALOG(config_dialog),
542                                 GNOME_STOCK_BUTTON_APPLY );
543     apply_button =
544         GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
545
546     gnome_dialog_append_button_with_pixmap(
547         GNOME_DIALOG(config_dialog), _("Save"), GNOME_STOCK_PIXMAP_SAVE );
548     save_button =
549         GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
550
551     gnome_dialog_append_button( GNOME_DIALOG(config_dialog),
552                                 GNOME_STOCK_BUTTON_CANCEL );
553     cancel_button =
554         GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
555 #else
556     ok_button = gtk_button_new_with_label( _("OK") );
557     gtk_box_pack_start( GTK_BOX(dialog_action_area), ok_button,
558                         TRUE, TRUE, 0 );
559
560     apply_button = gtk_button_new_with_label( _("Apply") );
561     gtk_box_pack_start( GTK_BOX(dialog_action_area), apply_button,
562                         TRUE, TRUE, 0 );
563
564     save_button = gtk_button_new_with_label( _("Save") );
565     gtk_box_pack_start( GTK_BOX(dialog_action_area), save_button,
566                         TRUE, TRUE, 0 );
567
568     cancel_button = gtk_button_new_with_label( _("Cancel") );
569     gtk_box_pack_start( GTK_BOX(dialog_action_area), cancel_button,
570                         TRUE, TRUE, 0 );
571 #endif
572
573     gtk_signal_connect( GTK_OBJECT(ok_button), "clicked",
574                         GTK_SIGNAL_FUNC(GtkConfigOk),
575                         config_dialog );
576     gtk_widget_set_sensitive( apply_button, FALSE );
577     gtk_object_set_data( GTK_OBJECT(config_dialog), "apply_button",
578                          apply_button );
579     gtk_signal_connect( GTK_OBJECT(apply_button), "clicked",
580                         GTK_SIGNAL_FUNC(GtkConfigApply),
581                         config_dialog );
582     gtk_signal_connect( GTK_OBJECT(save_button), "clicked",
583                         GTK_SIGNAL_FUNC(GtkConfigSave),
584                         config_dialog );
585     gtk_signal_connect( GTK_OBJECT(cancel_button), "clicked",
586                         GTK_SIGNAL_FUNC(GtkConfigCancel),
587                         config_dialog );
588
589
590
591     /* Ok, job done successfully. Let's keep a reference to the dialog box */
592     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_window),
593                          psz_module_name, config_dialog );
594     gtk_object_set_data( GTK_OBJECT(config_dialog), "psz_module_name",
595                          psz_module_name );
596
597     /* we want this ref to be destroyed if the object is destroyed */
598     gtk_signal_connect( GTK_OBJECT(config_dialog), "destroy",
599                        GTK_SIGNAL_FUNC(GtkConfigDialogDestroyed),
600                        (gpointer)p_intf );
601
602     gtk_widget_show_all( config_dialog );
603 }
604
605 #undef LABEL_AND_WIDGET
606 #undef TOOLTIP
607
608 /****************************************************************************
609  * GtkConfigApply: store the changes to the config inside the modules
610  * configuration structure and clear the hash table.
611  ****************************************************************************/
612 static void GtkConfigApply( GtkButton * button, gpointer user_data )
613 {
614     intf_thread_t *p_intf;
615     GHashTable *hash_table;
616     GtkWidget *apply_button;
617
618     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
619                                                     "config_hash_table" );
620     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(user_data),
621                                                    "p_intf" );
622     g_hash_table_foreach_remove( hash_table, GtkSaveHashValue, (void*)p_intf );
623
624     /* change the highlight status of the Apply button */
625     apply_button = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
626                                                     "apply_button" );
627     gtk_widget_set_sensitive( apply_button, FALSE );
628 }
629
630 static void GtkConfigOk( GtkButton * button, gpointer user_data )
631 {
632     GtkConfigApply( button, user_data );
633     gtk_widget_destroy( gtk_widget_get_toplevel( GTK_WIDGET (button) ) );
634 }
635
636
637 static void GtkConfigCancel( GtkButton * button, gpointer user_data )
638 {
639     gtk_widget_destroy( gtk_widget_get_toplevel( GTK_WIDGET (button) ) );
640 }
641
642 static void GtkConfigSave( GtkButton * button, gpointer user_data )
643 {
644     intf_thread_t *p_intf;
645
646     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(user_data),
647                                                    "p_intf" );
648     GtkConfigApply( button, user_data );
649     config_SaveConfigFile( p_intf, NULL );
650 }
651
652 /****************************************************************************
653  * GtkModuleHighlighted: display module description when an entry is selected
654  *   in the clist, and activate the configure button if necessary.
655  ****************************************************************************/
656 static void GtkModuleHighlighted( GtkCList *module_clist, int row, int column,
657                                   GdkEventButton *event, gpointer user_data )
658 {
659     intf_thread_t *p_intf;
660     GtkWidget *config_button;
661     module_t **pp_parser;
662     vlc_list_t *p_list;
663     char *psz_name;
664
665     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(module_clist),
666                                                    "p_intf" );
667
668     if( !gtk_clist_get_text( GTK_CLIST(module_clist), row, 0, &psz_name ) )
669     {
670         return;
671     }
672
673     /* look for module 'psz_name' */
674     p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE, FIND_ANYWHERE );
675
676     for( pp_parser = (module_t **)p_list->pp_objects ;
677          *pp_parser ;
678          pp_parser++ )
679     {
680         if( !strcmp( (*pp_parser)->psz_object_name, psz_name ) )
681         {
682             gtk_object_set_data( GTK_OBJECT(module_clist),
683                                  "module_highlighted", (*pp_parser) );
684             config_button = gtk_object_get_data( GTK_OBJECT(module_clist),
685                                                  "config_button" );
686             if( (*pp_parser)->i_config_items )
687                 gtk_widget_set_sensitive( config_button, TRUE );
688             else
689                 gtk_widget_set_sensitive( config_button, FALSE );
690
691             break;
692         }
693     }
694
695     vlc_list_release( p_list );
696 }
697
698 /****************************************************************************
699  * GtkModuleConfigure: display module configuration dialog box.
700  ****************************************************************************/
701 static void GtkModuleConfigure( GtkButton *button, gpointer user_data )
702 {
703     module_t *p_module;
704     intf_thread_t *p_intf;
705
706     p_module = (module_t *)gtk_object_get_data( GTK_OBJECT(user_data),
707                                                 "module_highlighted" );
708
709     if( !p_module ) return;
710     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(button),
711                                                    "p_intf" );
712     GtkCreateConfigDialog( p_module->psz_object_name, (gpointer)p_intf );
713
714 }
715
716 /****************************************************************************
717  * GtkModuleSelected: select module.
718  ****************************************************************************/
719 static void GtkModuleSelected( GtkButton *button, gpointer user_data )
720 {
721     module_t *p_module;
722     GtkWidget *widget;
723
724     p_module = (module_t *)gtk_object_get_data( GTK_OBJECT(user_data),
725                                                 "module_highlighted" );
726     widget = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
727                                                "module_entry" );
728     if( !p_module ) return;
729
730     gtk_entry_set_text( GTK_ENTRY(widget), p_module->psz_object_name );
731
732 }
733
734 /****************************************************************************
735  * GtkStringChanged: signal called when the user changes a string value.
736  ****************************************************************************/
737 static void GtkStringChanged( GtkEditable *editable, gpointer user_data )
738 {
739     intf_thread_t *p_intf;
740     module_config_t *p_config;
741     GHashTable *hash_table;
742     GtkWidget *apply_button;
743
744     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(editable),
745                                                    "p_intf" );
746     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
747                                                     "config_hash_table" );
748     /* free old p_config */
749     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
750                                                        (gpointer)editable );
751     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, (void *)p_intf );
752
753     p_config = malloc( sizeof(module_config_t) );
754     p_config->i_type = CONFIG_ITEM_STRING;
755     p_config->psz_value = gtk_editable_get_chars( editable, 0, -1 );
756     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(editable),
757                                                       "config_option" );
758
759     g_hash_table_insert( hash_table, (gpointer)editable,
760                          (gpointer)p_config );
761
762     /* change the highlight status of the Apply button */
763     apply_button = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
764                                                     "apply_button" );
765     gtk_widget_set_sensitive( apply_button, TRUE );
766 }
767
768 /****************************************************************************
769  * GtkIntChanged: signal called when the user changes an integer value.
770  ****************************************************************************/
771 static void GtkIntChanged( GtkEditable *editable, gpointer user_data )
772 {
773     intf_thread_t *p_intf;
774     module_config_t *p_config;
775     GHashTable *hash_table;
776     GtkWidget *apply_button;
777
778     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(editable),
779                                                    "p_intf" );
780     gtk_spin_button_update( GTK_SPIN_BUTTON(editable) );
781
782     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
783                                                     "config_hash_table" );
784
785     /* free old p_config */
786     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
787                                                        (gpointer)editable );
788     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, (void *)p_intf );
789
790     p_config = malloc( sizeof(module_config_t) );
791     p_config->i_type = CONFIG_ITEM_INTEGER;
792     p_config->i_value = gtk_spin_button_get_value_as_int(
793                             GTK_SPIN_BUTTON(editable) );
794     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(editable),
795                                                       "config_option" );
796
797     g_hash_table_insert( hash_table, (gpointer)editable,
798                          (gpointer)p_config );
799
800     /* change the highlight status of the Apply button */
801     apply_button = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
802                                                     "apply_button" );
803     gtk_widget_set_sensitive( apply_button, TRUE );
804 }
805
806 /****************************************************************************
807  * GtkFloatChanged: signal called when the user changes a float value.
808  ****************************************************************************/
809 static void GtkFloatChanged( GtkEditable *editable, gpointer user_data )
810 {
811     intf_thread_t *p_intf;
812     module_config_t *p_config;
813     GHashTable *hash_table;
814     GtkWidget *apply_button;
815
816     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(editable),
817                                                    "p_intf" );
818     gtk_spin_button_update( GTK_SPIN_BUTTON(editable) );
819
820     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
821                                                     "config_hash_table" );
822
823     /* free old p_config */
824     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
825                                                        (gpointer)editable );
826     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, (void *)p_intf );
827
828     p_config = malloc( sizeof(module_config_t) );
829     p_config->i_type = CONFIG_ITEM_FLOAT;
830     p_config->f_value = gtk_spin_button_get_value_as_float(
831                            GTK_SPIN_BUTTON(editable) );
832     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(editable),
833                                                       "config_option" );
834
835     g_hash_table_insert( hash_table, (gpointer)editable,
836                          (gpointer)p_config );
837
838     /* change the highlight status of the Apply button */
839     apply_button = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
840                                                     "apply_button" );
841     gtk_widget_set_sensitive( apply_button, TRUE );
842 }
843
844 /****************************************************************************
845  * GtkBoolChanged: signal called when the user changes a bool value.
846  ****************************************************************************/
847 static void GtkBoolChanged( GtkToggleButton *button, gpointer user_data )
848 {
849     intf_thread_t *p_intf;
850     module_config_t *p_config;
851     GHashTable *hash_table;
852     GtkWidget *apply_button;
853
854     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(button),
855                                                    "p_intf" );
856     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
857                                                     "config_hash_table" );
858
859     /* free old p_config */
860     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
861                                                        (gpointer)button );
862     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, (void *)p_intf );
863
864     p_config = malloc( sizeof(module_config_t) );
865     p_config->i_type = CONFIG_ITEM_BOOL;
866     p_config->i_value = gtk_toggle_button_get_active( button );
867     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(button),
868                                                       "config_option" );
869
870     g_hash_table_insert( hash_table, (gpointer)button,
871                          (gpointer)p_config );
872
873     /* change the highlight status of the Apply button */
874     apply_button = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
875                                                      "apply_button" );
876     gtk_widget_set_sensitive( apply_button, TRUE );
877 }
878
879 /****************************************************************************
880  * GtkFreeHashTable: signal called when the config hash table is destroyed.
881  ****************************************************************************/
882 static void GtkFreeHashTable( GtkObject *object )
883 {
884     GHashTable *hash_table = (GHashTable *)gtk_object_get_data( object,
885                                                          "config_hash_table" );
886     intf_thread_t *p_intf = (intf_thread_t *)gtk_object_get_data( object,
887                                                                   "p_intf" );
888
889     g_hash_table_foreach( hash_table, GtkFreeHashValue, (void *)p_intf );
890     g_hash_table_destroy( hash_table );
891 }
892
893 /****************************************************************************
894  * GtkFreeHashValue: signal called when an element of the config hash table
895  * is destroyed.
896  ****************************************************************************/
897 static void GtkFreeHashValue( gpointer key, gpointer value, gpointer user_data)
898 {
899     module_config_t * p_config = (module_config_t *)value;
900
901     if( p_config->i_type == CONFIG_ITEM_STRING )
902         if( p_config->psz_value ) g_free( p_config->psz_value );
903     free( p_config );
904 }
905
906 /****************************************************************************
907  * GtkSaveHashValue: callback used when enumerating the hash table in
908  * GtkConfigApply().
909  ****************************************************************************/
910 static gboolean GtkSaveHashValue( gpointer key, gpointer value,
911                                   gpointer user_data )
912 {
913     intf_thread_t *   p_intf   = (intf_thread_t *)user_data;
914     module_config_t * p_config = (module_config_t *)value;
915
916     switch( p_config->i_type )
917     {
918
919     case CONFIG_ITEM_STRING:
920     case CONFIG_ITEM_FILE:
921     case CONFIG_ITEM_MODULE:
922         config_PutPsz( p_intf, p_config->psz_name,
923                        *p_config->psz_value ? p_config->psz_value : NULL );
924         break;
925     case CONFIG_ITEM_INTEGER:
926     case CONFIG_ITEM_BOOL:
927         config_PutInt( p_intf, p_config->psz_name, p_config->i_value );
928         break;
929     case CONFIG_ITEM_FLOAT:
930         config_PutFloat( p_intf, p_config->psz_name, p_config->f_value );
931         break;
932     }
933
934     /* free the hash value we allocated */
935     if( p_config->i_type == CONFIG_ITEM_STRING )
936         g_free( p_config->psz_value );
937     free( p_config );
938
939     /* return TRUE so glib will free the hash entry */
940     return TRUE;
941 }
942
943 /****************************************************************************
944  * GtkConfigDialogDestroyed: callback triggered when the config dialog box is
945  * destroyed.
946  ****************************************************************************/
947 static void GtkConfigDialogDestroyed( GtkObject *object, gpointer user_data )
948 {
949     intf_thread_t *p_intf = (intf_thread_t *)user_data;
950     char *psz_module_name;
951
952     psz_module_name = gtk_object_get_data( object, "psz_module_name" );
953
954     /* remove the ref to the dialog box */
955     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_window),
956                          psz_module_name, NULL );
957
958     GtkFreeHashTable( object );
959 }