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