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