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