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