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