]> git.sesse.net Git - vlc/blob - modules/gui/gtk/preferences.c
* had accidentally deleted the first line of vout.m
[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.10 2003/06/20 23:13:37 hartman 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         case CONFIG_ITEM_DIRECTORY:
429
430             if( !p_item->ppsz_list )
431             {
432                 /* add input box with default value */
433                 item_combo = string_entry = gtk_entry_new();
434             }
435             else
436             {
437                 /* add combo box with default value */
438                 GList *items = NULL;
439                 int i;
440
441                 for( i=0; p_item->ppsz_list[i]; i++ )
442                     items = g_list_append( items, p_item->ppsz_list[i] );
443
444                 item_combo = gtk_combo_new();
445                 string_entry = GTK_COMBO(item_combo)->entry;
446                 gtk_combo_set_popdown_strings( GTK_COMBO(item_combo),
447                                                items );
448
449             }
450
451             vlc_mutex_lock( p_item->p_lock );
452             gtk_entry_set_text( GTK_ENTRY(string_entry),
453                                 p_item->psz_value ? p_item->psz_value : "" );
454             vlc_mutex_unlock( p_item->p_lock );
455
456             /* connect signal to track changes in the text box */
457             gtk_object_set_data( GTK_OBJECT(string_entry), "config_option",
458                                  p_item->psz_name );
459             gtk_signal_connect( GTK_OBJECT(string_entry), "changed",
460                                 GTK_SIGNAL_FUNC(GtkStringChanged),
461                                 (gpointer)config_dialog );
462
463             LABEL_AND_WIDGET( p_item->psz_text,
464                               item_combo, p_item->psz_longtext );
465             break;
466
467         case CONFIG_ITEM_INTEGER:
468
469             if (( p_item->i_max == 0) && ( p_item->i_min == 0))
470             {
471                 /* add input box with default value */
472                 item_adj = gtk_adjustment_new( p_item->i_value,
473                                                -1, 99999, 1, 10, 10 );
474                 integer_spinbutton = gtk_spin_button_new( GTK_ADJUSTMENT(item_adj), 1, 0 );
475
476                 /* connect signal to track changes in the spinbutton value */
477                 gtk_object_set_data( GTK_OBJECT(integer_spinbutton),
478                                      "config_option", p_item->psz_name );
479                 gtk_signal_connect( GTK_OBJECT(integer_spinbutton), "changed",
480                                     GTK_SIGNAL_FUNC(GtkIntChanged),
481                                     (gpointer)config_dialog );
482
483                 LABEL_AND_WIDGET( p_item->psz_text,
484                                   integer_spinbutton, p_item->psz_longtext );
485             }
486             else /* use i_min and i_max */
487             {
488                 item_adj = gtk_adjustment_new( p_item->i_value, p_item->i_min,
489                                                p_item->i_max, 1, 1, 0 );
490                 integer_slider = gtk_hscale_new( GTK_ADJUSTMENT(item_adj));
491                 gtk_scale_set_digits (GTK_SCALE(integer_slider), 0);
492                                                 
493                 /* connect signal to track changes in the spinbutton value */
494                 gtk_object_set_data( GTK_OBJECT(item_adj),
495                                      "config_option", p_item->psz_name );
496                 gtk_signal_connect( GTK_OBJECT(item_adj), "value-changed",
497                                     GTK_SIGNAL_FUNC(GtkIntRangedChanged),
498                                     (gpointer)config_dialog );
499
500                 LABEL_AND_WIDGET( p_item->psz_text,
501                                   integer_slider, p_item->psz_longtext );
502             }
503             break;
504
505         case CONFIG_ITEM_FLOAT:
506
507             if (( p_item->f_max == 0.0) && ( p_item->f_min == 0.0))
508             {
509                 /* add input box with default value */
510                 item_adj = gtk_adjustment_new( p_item->f_value,
511                                                0, 99999, 0.01, 10, 10 );
512                 float_spinbutton = gtk_spin_button_new( GTK_ADJUSTMENT(item_adj),
513                                                         0.01, 2 );
514
515                 /* connect signal to track changes in the spinbutton value */
516                 gtk_object_set_data( GTK_OBJECT(float_spinbutton),
517                                      "config_option", p_item->psz_name );
518                 gtk_signal_connect( GTK_OBJECT(float_spinbutton), "changed",
519                                     GTK_SIGNAL_FUNC(GtkFloatChanged),
520                                     (gpointer)config_dialog );
521
522                 LABEL_AND_WIDGET( p_item->psz_text,
523                                   float_spinbutton, p_item->psz_longtext );
524             }
525             else /* use f_min and f_max */
526             {
527                 item_adj = gtk_adjustment_new( p_item->f_value, p_item->f_min,
528                                                p_item->f_max, 0.01, 0.01, 0 );
529                 float_slider = gtk_hscale_new( GTK_ADJUSTMENT(item_adj));
530                 gtk_scale_set_digits (GTK_SCALE(float_slider), 2);
531                                                 
532                 /* connect signal to track changes in the spinbutton value */
533                 gtk_object_set_data( GTK_OBJECT(item_adj),
534                                      "config_option", p_item->psz_name );
535                 gtk_signal_connect( GTK_OBJECT(item_adj), "value-changed",
536                                     GTK_SIGNAL_FUNC(GtkFloatRangedChanged),
537                                     (gpointer)config_dialog );
538
539                 LABEL_AND_WIDGET( p_item->psz_text,
540                                   float_slider, p_item->psz_longtext );
541             }
542             break;
543
544
545         case CONFIG_ITEM_BOOL:
546
547             /* add check button */
548             bool_checkbutton = gtk_check_button_new();
549             gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(bool_checkbutton),
550                                           p_item->i_value );
551
552             /* connect signal to track changes in the button state */
553             gtk_object_set_data( GTK_OBJECT(bool_checkbutton), "config_option",
554                                  p_item->psz_name );
555             gtk_signal_connect( GTK_OBJECT(bool_checkbutton), "toggled",
556                                 GTK_SIGNAL_FUNC(GtkBoolChanged),
557                                 (gpointer)config_dialog );
558
559             LABEL_AND_WIDGET( p_item->psz_text,
560                               bool_checkbutton, p_item->psz_longtext );
561             break;
562
563         }
564
565     }
566     while( p_item->i_type != CONFIG_HINT_END && p_item++ );
567
568     vlc_list_release( p_list );
569
570 #ifndef MODULE_NAME_IS_gnome
571     /* Now let's add the action buttons at the bottom of the page */
572     dialog_action_area = GTK_DIALOG(config_dialog)->action_area;
573     gtk_container_set_border_width( GTK_CONTAINER(dialog_action_area), 4 );
574
575     /* add a new table for the config option */
576     item_hbox = gtk_hbox_new( FALSE, 0 );
577     gtk_box_pack_end( GTK_BOX(dialog_action_area), item_hbox,
578                       TRUE, FALSE, 0 );
579     item_hbox = gtk_hbox_new( FALSE, 0 );
580     gtk_box_pack_end( GTK_BOX(dialog_action_area), item_hbox,
581                       TRUE, FALSE, 0 );
582 #endif
583
584     /* Create the OK button */
585 #ifdef MODULE_NAME_IS_gnome
586     gnome_dialog_append_button( GNOME_DIALOG(config_dialog),
587                                 GNOME_STOCK_BUTTON_OK );
588     ok_button =
589         GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
590
591     gnome_dialog_append_button( GNOME_DIALOG(config_dialog),
592                                 GNOME_STOCK_BUTTON_APPLY );
593     apply_button =
594         GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
595
596     gnome_dialog_append_button_with_pixmap(
597         GNOME_DIALOG(config_dialog), _("Save"), GNOME_STOCK_PIXMAP_SAVE );
598     save_button =
599         GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
600
601     gnome_dialog_append_button( GNOME_DIALOG(config_dialog),
602                                 GNOME_STOCK_BUTTON_CANCEL );
603     cancel_button =
604         GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
605 #else
606     ok_button = gtk_button_new_with_label( _("OK") );
607     gtk_box_pack_start( GTK_BOX(dialog_action_area), ok_button,
608                         TRUE, TRUE, 0 );
609
610     apply_button = gtk_button_new_with_label( _("Apply") );
611     gtk_box_pack_start( GTK_BOX(dialog_action_area), apply_button,
612                         TRUE, TRUE, 0 );
613
614     save_button = gtk_button_new_with_label( _("Save") );
615     gtk_box_pack_start( GTK_BOX(dialog_action_area), save_button,
616                         TRUE, TRUE, 0 );
617
618     cancel_button = gtk_button_new_with_label( _("Cancel") );
619     gtk_box_pack_start( GTK_BOX(dialog_action_area), cancel_button,
620                         TRUE, TRUE, 0 );
621 #endif
622
623     gtk_signal_connect( GTK_OBJECT(ok_button), "clicked",
624                         GTK_SIGNAL_FUNC(GtkConfigOk),
625                         config_dialog );
626     gtk_widget_set_sensitive( apply_button, FALSE );
627     gtk_object_set_data( GTK_OBJECT(config_dialog), "apply_button",
628                          apply_button );
629     gtk_signal_connect( GTK_OBJECT(apply_button), "clicked",
630                         GTK_SIGNAL_FUNC(GtkConfigApply),
631                         config_dialog );
632     gtk_signal_connect( GTK_OBJECT(save_button), "clicked",
633                         GTK_SIGNAL_FUNC(GtkConfigSave),
634                         config_dialog );
635     gtk_signal_connect( GTK_OBJECT(cancel_button), "clicked",
636                         GTK_SIGNAL_FUNC(GtkConfigCancel),
637                         config_dialog );
638
639
640
641     /* Ok, job done successfully. Let's keep a reference to the dialog box */
642     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_window),
643                          psz_module_name, config_dialog );
644     gtk_object_set_data( GTK_OBJECT(config_dialog), "psz_module_name",
645                          psz_module_name );
646
647     /* we want this ref to be destroyed if the object is destroyed */
648     gtk_signal_connect( GTK_OBJECT(config_dialog), "destroy",
649                        GTK_SIGNAL_FUNC(GtkConfigDialogDestroyed),
650                        (gpointer)p_intf );
651
652     gtk_widget_show_all( config_dialog );
653 }
654
655 #undef LABEL_AND_WIDGET
656 #undef TOOLTIP
657
658 /****************************************************************************
659  * GtkConfigApply: store the changes to the config inside the modules
660  * configuration structure and clear the hash table.
661  ****************************************************************************/
662 static void GtkConfigApply( GtkButton * button, gpointer user_data )
663 {
664     intf_thread_t *p_intf;
665     GHashTable *hash_table;
666     GtkWidget *apply_button;
667
668     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
669                                                     "config_hash_table" );
670     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(user_data),
671                                                    "p_intf" );
672     g_hash_table_foreach_remove( hash_table, GtkSaveHashValue, (void*)p_intf );
673
674     /* change the highlight status of the Apply button */
675     apply_button = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
676                                                     "apply_button" );
677     gtk_widget_set_sensitive( apply_button, FALSE );
678 }
679
680 static void GtkConfigOk( GtkButton * button, gpointer user_data )
681 {
682     GtkConfigApply( button, user_data );
683     gtk_widget_destroy( gtk_widget_get_toplevel( GTK_WIDGET (button) ) );
684 }
685
686
687 static void GtkConfigCancel( GtkButton * button, gpointer user_data )
688 {
689     gtk_widget_destroy( gtk_widget_get_toplevel( GTK_WIDGET (button) ) );
690 }
691
692 static void GtkConfigSave( GtkButton * button, gpointer user_data )
693 {
694     intf_thread_t *p_intf;
695
696     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(user_data),
697                                                    "p_intf" );
698     GtkConfigApply( button, user_data );
699     config_SaveConfigFile( p_intf, NULL );
700 }
701
702 /****************************************************************************
703  * GtkModuleHighlighted: display module description when an entry is selected
704  *   in the clist, and activate the configure button if necessary.
705  ****************************************************************************/
706 static void GtkModuleHighlighted( GtkCList *module_clist, int row, int column,
707                                   GdkEventButton *event, gpointer user_data )
708 {
709     intf_thread_t *p_intf;
710     GtkWidget *config_button;
711     module_t *p_parser;
712     vlc_list_t *p_list;
713     char *psz_name;
714     int i_index;
715
716     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(module_clist),
717                                                    "p_intf" );
718
719     if( !gtk_clist_get_text( GTK_CLIST(module_clist), row, 0, &psz_name ) )
720     {
721         return;
722     }
723
724     /* look for module 'psz_name' */
725     p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE, FIND_ANYWHERE );
726
727     for( i_index = 0; i_index < p_list->i_count; i_index++ )
728     {
729         p_parser = (module_t *)p_list->p_values[i_index].p_object ;
730
731         if( !strcmp( p_parser->psz_object_name, psz_name ) )
732         {
733             gtk_object_set_data( GTK_OBJECT(module_clist),
734                                  "module_highlighted", p_parser );
735             config_button = gtk_object_get_data( GTK_OBJECT(module_clist),
736                                                  "config_button" );
737             if( p_parser->i_config_items )
738                 gtk_widget_set_sensitive( config_button, TRUE );
739             else
740                 gtk_widget_set_sensitive( config_button, FALSE );
741
742             break;
743         }
744     }
745
746     vlc_list_release( p_list );
747 }
748
749 /****************************************************************************
750  * GtkModuleConfigure: display module configuration dialog box.
751  ****************************************************************************/
752 static void GtkModuleConfigure( GtkButton *button, gpointer user_data )
753 {
754     module_t *p_module;
755     intf_thread_t *p_intf;
756
757     p_module = (module_t *)gtk_object_get_data( GTK_OBJECT(user_data),
758                                                 "module_highlighted" );
759
760     if( !p_module ) return;
761     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(button),
762                                                    "p_intf" );
763     GtkCreateConfigDialog( p_module->psz_object_name, (gpointer)p_intf );
764
765 }
766
767 /****************************************************************************
768  * GtkModuleSelected: select module.
769  ****************************************************************************/
770 static void GtkModuleSelected( GtkButton *button, gpointer user_data )
771 {
772     module_t *p_module;
773     GtkWidget *widget;
774
775     p_module = (module_t *)gtk_object_get_data( GTK_OBJECT(user_data),
776                                                 "module_highlighted" );
777     widget = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
778                                                "module_entry" );
779     if( !p_module ) return;
780
781     gtk_entry_set_text( GTK_ENTRY(widget), p_module->psz_object_name );
782
783 }
784
785 /****************************************************************************
786  * GtkStringChanged: signal called when the user changes a string value.
787  ****************************************************************************/
788 static void GtkStringChanged( GtkEditable *editable, gpointer user_data )
789 {
790     intf_thread_t *p_intf;
791     module_config_t *p_config;
792     GHashTable *hash_table;
793     GtkWidget *apply_button;
794
795     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(editable),
796                                                    "p_intf" );
797     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
798                                                     "config_hash_table" );
799     /* free old p_config */
800     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
801                                                        (gpointer)editable );
802     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, (void *)p_intf );
803
804     p_config = malloc( sizeof(module_config_t) );
805     p_config->i_type = CONFIG_ITEM_STRING;
806     p_config->psz_value = gtk_editable_get_chars( editable, 0, -1 );
807     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(editable),
808                                                       "config_option" );
809
810     g_hash_table_insert( hash_table, (gpointer)editable,
811                          (gpointer)p_config );
812
813     /* change the highlight status of the Apply button */
814     apply_button = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
815                                                     "apply_button" );
816     gtk_widget_set_sensitive( apply_button, TRUE );
817 }
818 /****************************************************************************
819  * GtkIntChanged: signal called when the user changes an integer value.
820  ****************************************************************************/
821 static void GtkIntChanged( GtkEditable *editable, gpointer user_data )
822 {
823     intf_thread_t *p_intf;
824     module_config_t *p_config;
825     GHashTable *hash_table;
826     GtkWidget *apply_button;
827
828     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(editable),
829                                                    "p_intf" );
830     gtk_spin_button_update( GTK_SPIN_BUTTON(editable) );
831
832     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
833                                                     "config_hash_table" );
834
835     /* free old p_config */
836     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
837                                                        (gpointer)editable );
838     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, (void *)p_intf );
839
840     p_config = malloc( sizeof(module_config_t) );
841     p_config->i_type = CONFIG_ITEM_INTEGER;
842     p_config->i_value = gtk_spin_button_get_value_as_int(
843                             GTK_SPIN_BUTTON(editable) );
844     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(editable),
845                                                       "config_option" );
846
847     g_hash_table_insert( hash_table, (gpointer)editable,
848                          (gpointer)p_config );
849
850     /* change the highlight status of the Apply button */
851     apply_button = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
852                                                     "apply_button" );
853     gtk_widget_set_sensitive( apply_button, TRUE );
854 }
855
856
857 /***************************************************************************************
858  * GtkIntRangedChanged: signal called when the user changes an integer with range value.
859  **************************************************************************************/
860 static void GtkIntRangedChanged( GtkEditable *editable, gpointer user_data )
861 {
862     intf_thread_t *p_intf;
863     module_config_t *p_config;
864     GHashTable *hash_table;
865     GtkWidget *apply_button;
866
867     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(editable),
868                                                    "p_intf" );
869
870     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
871                                                     "config_hash_table" );
872
873     /* free old p_config */
874     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
875                                                        (gpointer)editable );
876     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, (void *)p_intf );
877
878     p_config = malloc( sizeof(module_config_t) );
879     p_config->i_type = CONFIG_ITEM_INTEGER;
880     p_config->i_value = ((GTK_ADJUSTMENT(editable))->value);
881     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(editable),
882                                                       "config_option" );
883
884     g_hash_table_insert( hash_table, (gpointer)editable,
885                          (gpointer)p_config );
886
887     /* change the highlight status of the Apply button */
888     apply_button = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
889                                                     "apply_button" );
890     gtk_widget_set_sensitive( apply_button, TRUE );
891 }
892
893 /****************************************************************************
894  * GtkFloatChanged: signal called when the user changes a float value.
895  ****************************************************************************/
896 static void GtkFloatChanged( GtkEditable *editable, gpointer user_data )
897 {
898     intf_thread_t *p_intf;
899     module_config_t *p_config;
900     GHashTable *hash_table;
901     GtkWidget *apply_button;
902
903     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(editable),
904                                                    "p_intf" );
905     gtk_spin_button_update( GTK_SPIN_BUTTON(editable) );
906
907     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
908                                                     "config_hash_table" );
909
910     /* free old p_config */
911     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
912                                                        (gpointer)editable );
913     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, (void *)p_intf );
914
915     p_config = malloc( sizeof(module_config_t) );
916     p_config->i_type = CONFIG_ITEM_FLOAT;
917     p_config->f_value = gtk_spin_button_get_value_as_float(
918                            GTK_SPIN_BUTTON(editable) );
919     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(editable),
920                                                       "config_option" );
921
922     g_hash_table_insert( hash_table, (gpointer)editable,
923                          (gpointer)p_config );
924
925     /* change the highlight status of the Apply button */
926     apply_button = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
927                                                     "apply_button" );
928     gtk_widget_set_sensitive( apply_button, TRUE );
929 }
930
931 /***************************************************************************************
932  * GtkIntRangedChanged: signal called when the user changes an integer with range value.
933  **************************************************************************************/
934 static void GtkFloatRangedChanged( GtkEditable *editable, gpointer user_data )
935 {
936     intf_thread_t *p_intf;
937     module_config_t *p_config;
938     GHashTable *hash_table;
939     GtkWidget *apply_button;
940
941     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(editable),
942                                                    "p_intf" );
943
944     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
945                                                     "config_hash_table" );
946
947     /* free old p_config */
948     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
949                                                        (gpointer)editable );
950     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, (void *)p_intf );
951
952     p_config = malloc( sizeof(module_config_t) );
953     p_config->i_type = CONFIG_ITEM_FLOAT;
954     p_config->f_value = ((GTK_ADJUSTMENT(editable))->value);
955     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(editable),
956                                                       "config_option" );
957
958     g_hash_table_insert( hash_table, (gpointer)editable,
959                          (gpointer)p_config );
960
961     /* change the highlight status of the Apply button */
962     apply_button = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
963                                                     "apply_button" );
964     gtk_widget_set_sensitive( apply_button, TRUE );
965 }
966
967 /****************************************************************************
968  * GtkBoolChanged: signal called when the user changes a bool value.
969  ****************************************************************************/
970 static void GtkBoolChanged( GtkToggleButton *button, gpointer user_data )
971 {
972     intf_thread_t *p_intf;
973     module_config_t *p_config;
974     GHashTable *hash_table;
975     GtkWidget *apply_button;
976
977     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(button),
978                                                    "p_intf" );
979     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
980                                                     "config_hash_table" );
981
982     /* free old p_config */
983     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
984                                                        (gpointer)button );
985     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, (void *)p_intf );
986
987     p_config = malloc( sizeof(module_config_t) );
988     p_config->i_type = CONFIG_ITEM_BOOL;
989     p_config->i_value = gtk_toggle_button_get_active( button );
990     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(button),
991                                                       "config_option" );
992
993     g_hash_table_insert( hash_table, (gpointer)button,
994                          (gpointer)p_config );
995
996     /* change the highlight status of the Apply button */
997     apply_button = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
998                                                      "apply_button" );
999     gtk_widget_set_sensitive( apply_button, TRUE );
1000 }
1001
1002 /****************************************************************************
1003  * GtkFreeHashTable: signal called when the config hash table is destroyed.
1004  ****************************************************************************/
1005 static void GtkFreeHashTable( GtkObject *object )
1006 {
1007     GHashTable *hash_table = (GHashTable *)gtk_object_get_data( object,
1008                                                          "config_hash_table" );
1009     intf_thread_t *p_intf = (intf_thread_t *)gtk_object_get_data( object,
1010                                                                   "p_intf" );
1011
1012     g_hash_table_foreach( hash_table, GtkFreeHashValue, (void *)p_intf );
1013     g_hash_table_destroy( hash_table );
1014 }
1015
1016 /****************************************************************************
1017  * GtkFreeHashValue: signal called when an element of the config hash table
1018  * is destroyed.
1019  ****************************************************************************/
1020 static void GtkFreeHashValue( gpointer key, gpointer value, gpointer user_data)
1021 {
1022     module_config_t * p_config = (module_config_t *)value;
1023
1024     if( p_config->i_type == CONFIG_ITEM_STRING )
1025         if( p_config->psz_value ) g_free( p_config->psz_value );
1026     free( p_config );
1027 }
1028
1029 /****************************************************************************
1030  * GtkSaveHashValue: callback used when enumerating the hash table in
1031  * GtkConfigApply().
1032  ****************************************************************************/
1033 static gboolean GtkSaveHashValue( gpointer key, gpointer value,
1034                                   gpointer user_data )
1035 {
1036     intf_thread_t *   p_intf   = (intf_thread_t *)user_data;
1037     module_config_t * p_config = (module_config_t *)value;
1038
1039     switch( p_config->i_type )
1040     {
1041
1042     case CONFIG_ITEM_STRING:
1043     case CONFIG_ITEM_FILE:
1044     case CONFIG_ITEM_DIRECTORY:
1045     case CONFIG_ITEM_MODULE:
1046         config_PutPsz( p_intf, p_config->psz_name,
1047                        *p_config->psz_value ? p_config->psz_value : NULL );
1048         break;
1049     case CONFIG_ITEM_INTEGER:
1050     case CONFIG_ITEM_BOOL:
1051         config_PutInt( p_intf, p_config->psz_name, p_config->i_value );
1052         break;
1053     case CONFIG_ITEM_FLOAT:
1054         config_PutFloat( p_intf, p_config->psz_name, p_config->f_value );
1055         break;
1056     }
1057
1058     /* free the hash value we allocated */
1059     if( p_config->i_type == CONFIG_ITEM_STRING )
1060         g_free( p_config->psz_value );
1061     free( p_config );
1062
1063     /* return TRUE so glib will free the hash entry */
1064     return TRUE;
1065 }
1066
1067 /****************************************************************************
1068  * GtkConfigDialogDestroyed: callback triggered when the config dialog box is
1069  * destroyed.
1070  ****************************************************************************/
1071 static void GtkConfigDialogDestroyed( GtkObject *object, gpointer user_data )
1072 {
1073     intf_thread_t *p_intf = (intf_thread_t *)user_data;
1074     char *psz_module_name;
1075
1076     psz_module_name = gtk_object_get_data( object, "psz_module_name" );
1077
1078     /* remove the ref to the dialog box */
1079     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_window),
1080                          psz_module_name, NULL );
1081
1082     GtkFreeHashTable( object );
1083 }