]> git.sesse.net Git - vlc/blob - plugins/gtk/gtk_preferences.c
* ALL: new module API. Makes a few things a lot simpler, and we gain
[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.35 2002/07/31 20:56:51 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->p_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                 char *  psz_capability;
319
320                 for( p_module_bis = p_intf->p_vlc->p_module_bank->first ;
321                      p_module_bis != NULL ;
322                      p_module_bis = p_module_bis->next )
323                 {
324 #if 0 /* FIXME */
325                     for( psz_capability = p_module_bis->pp_capabilities[0] ;
326                          *psz_capability ;
327                          psz_capability++ )
328                     {
329                         if( !strcmp( psz_capability, p_item->psz_type ) )
330                         {
331                             entry[0] = p_module_bis->psz_object_name;
332                             entry[1] = p_module_bis->psz_longname;
333                             gtk_clist_append( GTK_CLIST(module_clist), entry );
334                             break;
335                         }
336                     }
337 #endif
338                 }
339             }
340
341             gtk_clist_set_column_auto_resize( GTK_CLIST(module_clist),
342                                               0, TRUE );
343             gtk_clist_set_column_auto_resize( GTK_CLIST(module_clist),
344                                               1, TRUE );
345
346             /* connect signals to the modules list */
347             gtk_signal_connect( GTK_OBJECT(module_clist), "select_row",
348                                 GTK_SIGNAL_FUNC(GtkModuleHighlighted),
349                                 NULL );
350
351             /* hbox holding the "select" and "configure" buttons */
352             item_hbox = gtk_hbox_new( FALSE, 4 );
353             gtk_container_add( GTK_CONTAINER(item_vbox), item_hbox);
354
355             /* add configure button */
356             module_config_button =
357                 gtk_button_new_with_label( _("Configure") );
358             gtk_widget_set_sensitive( module_config_button, FALSE );
359             gtk_container_add( GTK_CONTAINER(item_hbox),
360                                module_config_button );
361             gtk_object_set_data( GTK_OBJECT(module_config_button),
362                                  "p_intf", p_intf );
363             gtk_object_set_data( GTK_OBJECT(module_clist),
364                                  "config_button", module_config_button );
365
366             /* add select button */
367             module_select_button =
368                 gtk_button_new_with_label( _("Select") );
369             gtk_container_add( GTK_CONTAINER(item_hbox),
370                                module_select_button );
371             /* add a tooltip on mouseover */
372             gtk_tooltips_set_tip( p_intf->p_sys->p_tooltips,
373                                   module_select_button,
374                                   p_item->psz_longtext, "" );
375
376             /* hbox holding the "selected" label and text input */
377             item_hbox = gtk_hbox_new( FALSE, 4 );
378             gtk_container_add( GTK_CONTAINER(item_vbox), item_hbox);
379             /* add new label */
380             item_label = gtk_label_new( _("Selected:") );
381             gtk_container_add( GTK_CONTAINER(item_hbox), item_label );
382
383             /* add input box with default value */
384             string_entry = gtk_entry_new();
385             gtk_object_set_data( GTK_OBJECT(module_clist),
386                                  "module_entry", string_entry );
387             gtk_container_add( GTK_CONTAINER(item_hbox), string_entry );
388             vlc_mutex_lock( p_item->p_lock );
389             gtk_entry_set_text( GTK_ENTRY(string_entry),
390                                 p_item->psz_value ? p_item->psz_value : "" );
391             vlc_mutex_unlock( p_item->p_lock );
392             /* add a tooltip on mouseover */
393             gtk_tooltips_set_tip( p_intf->p_sys->p_tooltips,
394                                   string_entry, p_item->psz_longtext, "" );
395
396             /* connect signals to the buttons */
397             gtk_signal_connect( GTK_OBJECT(module_config_button), "clicked",
398                                 GTK_SIGNAL_FUNC(GtkModuleConfigure),
399                                 (gpointer)module_clist );
400             gtk_signal_connect( GTK_OBJECT(module_select_button), "clicked",
401                                 GTK_SIGNAL_FUNC(GtkModuleSelected),
402                                 (gpointer)module_clist );
403
404             /* connect signal to track changes in the text box */
405             gtk_object_set_data( GTK_OBJECT(string_entry), "config_option",
406                                  p_item->psz_name );
407             gtk_signal_connect( GTK_OBJECT(string_entry), "changed",
408                                 GTK_SIGNAL_FUNC(GtkStringChanged),
409                                 (gpointer)config_dialog );
410             break;
411
412         case CONFIG_ITEM_STRING:
413         case CONFIG_ITEM_FILE:
414
415             if( !p_item->ppsz_list )
416             {
417                 /* add input box with default value */
418                 item_combo = string_entry = gtk_entry_new();
419             }
420             else
421             {
422                 /* add combo box with default value */
423                 GList *items = NULL;
424                 int i;
425
426                 for( i=0; p_item->ppsz_list[i]; i++ )
427                     items = g_list_append( items, p_item->ppsz_list[i] );
428
429                 item_combo = gtk_combo_new();
430                 string_entry = GTK_COMBO(item_combo)->entry;
431                 gtk_combo_set_popdown_strings( GTK_COMBO(item_combo),
432                                                items );
433
434             }
435
436             vlc_mutex_lock( p_item->p_lock );
437             gtk_entry_set_text( GTK_ENTRY(string_entry),
438                                 p_item->psz_value ? p_item->psz_value : "" );
439             vlc_mutex_unlock( p_item->p_lock );
440
441             /* connect signal to track changes in the text box */
442             gtk_object_set_data( GTK_OBJECT(string_entry), "config_option",
443                                  p_item->psz_name );
444             gtk_signal_connect( GTK_OBJECT(string_entry), "changed",
445                                 GTK_SIGNAL_FUNC(GtkStringChanged),
446                                 (gpointer)config_dialog );
447
448             LABEL_AND_WIDGET( p_item->psz_text,
449                               item_combo, p_item->psz_longtext );
450             break;
451
452         case CONFIG_ITEM_INTEGER:
453
454             /* add input box with default value */
455             item_adj = gtk_adjustment_new( p_item->i_value,
456                                            -1, 99999, 1, 10, 10 );
457             integer_spinbutton = gtk_spin_button_new( GTK_ADJUSTMENT(item_adj),
458                                                       1, 0 );
459
460             /* connect signal to track changes in the spinbutton value */
461             gtk_object_set_data( GTK_OBJECT(integer_spinbutton),
462                                  "config_option", p_item->psz_name );
463             gtk_signal_connect( GTK_OBJECT(integer_spinbutton), "changed",
464                                 GTK_SIGNAL_FUNC(GtkIntChanged),
465                                 (gpointer)config_dialog );
466
467             LABEL_AND_WIDGET( p_item->psz_text,
468                               integer_spinbutton, p_item->psz_longtext );
469             break;
470
471         case CONFIG_ITEM_FLOAT:
472
473             /* add input box with default value */
474             item_adj = gtk_adjustment_new( p_item->f_value,
475                                            0, 99999, 0.01, 10, 10 );
476             float_spinbutton = gtk_spin_button_new( GTK_ADJUSTMENT(item_adj),
477                                                     0.01, 2 );
478
479             /* connect signal to track changes in the spinbutton value */
480             gtk_object_set_data( GTK_OBJECT(float_spinbutton),
481                                  "config_option", p_item->psz_name );
482             gtk_signal_connect( GTK_OBJECT(float_spinbutton), "changed",
483                                 GTK_SIGNAL_FUNC(GtkFloatChanged),
484                                 (gpointer)config_dialog );
485
486             LABEL_AND_WIDGET( p_item->psz_text,
487                               float_spinbutton, p_item->psz_longtext );
488             break;
489
490         case CONFIG_ITEM_BOOL:
491
492             /* add check button */
493             bool_checkbutton = gtk_check_button_new();
494             gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(bool_checkbutton),
495                                           p_item->i_value );
496
497             /* connect signal to track changes in the button state */
498             gtk_object_set_data( GTK_OBJECT(bool_checkbutton), "config_option",
499                                  p_item->psz_name );
500             gtk_signal_connect( GTK_OBJECT(bool_checkbutton), "toggled",
501                                 GTK_SIGNAL_FUNC(GtkBoolChanged),
502                                 (gpointer)config_dialog );
503
504             LABEL_AND_WIDGET( p_item->psz_text,
505                               bool_checkbutton, p_item->psz_longtext );
506             break;
507
508         }
509
510     }
511     while( p_item->i_type != CONFIG_HINT_END && p_item++ );
512
513 #ifndef MODULE_NAME_IS_gnome
514     /* Now let's add the action buttons at the bottom of the page */
515     dialog_action_area = GTK_DIALOG(config_dialog)->action_area;
516     gtk_container_set_border_width( GTK_CONTAINER(dialog_action_area), 4 );
517
518     /* add a new table for the config option */
519     item_hbox = gtk_hbox_new( FALSE, 0 );
520     gtk_box_pack_end( GTK_BOX(dialog_action_area), item_hbox,
521                       TRUE, FALSE, 0 );
522     item_hbox = gtk_hbox_new( FALSE, 0 );
523     gtk_box_pack_end( GTK_BOX(dialog_action_area), item_hbox,
524                       TRUE, FALSE, 0 );
525 #endif
526
527     /* Create the OK button */
528 #ifdef MODULE_NAME_IS_gnome
529     gnome_dialog_append_button( GNOME_DIALOG(config_dialog),
530                                 GNOME_STOCK_BUTTON_OK );
531     ok_button =
532         GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
533
534     gnome_dialog_append_button( GNOME_DIALOG(config_dialog),
535                                 GNOME_STOCK_BUTTON_APPLY );
536     apply_button =
537         GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
538
539     gnome_dialog_append_button_with_pixmap(
540         GNOME_DIALOG(config_dialog), _("Save"), GNOME_STOCK_PIXMAP_SAVE );
541     save_button =
542         GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
543
544     gnome_dialog_append_button( GNOME_DIALOG(config_dialog),
545                                 GNOME_STOCK_BUTTON_CANCEL );
546     cancel_button =
547         GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
548 #else
549     ok_button = gtk_button_new_with_label( _("OK") );
550     gtk_box_pack_start( GTK_BOX(dialog_action_area), ok_button,
551                         TRUE, TRUE, 0 );
552
553     apply_button = gtk_button_new_with_label( _("Apply") );
554     gtk_box_pack_start( GTK_BOX(dialog_action_area), apply_button,
555                         TRUE, TRUE, 0 );
556
557     save_button = gtk_button_new_with_label( _("Save") );
558     gtk_box_pack_start( GTK_BOX(dialog_action_area), save_button,
559                         TRUE, TRUE, 0 );
560
561     cancel_button = gtk_button_new_with_label( _("Cancel") );
562     gtk_box_pack_start( GTK_BOX(dialog_action_area), cancel_button,
563                         TRUE, TRUE, 0 );
564 #endif
565
566     gtk_signal_connect( GTK_OBJECT(ok_button), "clicked",
567                         GTK_SIGNAL_FUNC(GtkConfigOk),
568                         config_dialog );
569     gtk_widget_set_sensitive( apply_button, FALSE );
570     gtk_object_set_data( GTK_OBJECT(config_dialog), "apply_button",
571                          apply_button );
572     gtk_signal_connect( GTK_OBJECT(apply_button), "clicked",
573                         GTK_SIGNAL_FUNC(GtkConfigApply),
574                         config_dialog );
575     gtk_signal_connect( GTK_OBJECT(save_button), "clicked",
576                         GTK_SIGNAL_FUNC(GtkConfigSave),
577                         config_dialog );
578     gtk_signal_connect( GTK_OBJECT(cancel_button), "clicked",
579                         GTK_SIGNAL_FUNC(GtkConfigCancel),
580                         config_dialog );
581
582
583
584     /* Ok, job done successfully. Let's keep a reference to the dialog box */
585     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_window),
586                          psz_module_name, config_dialog );
587     gtk_object_set_data( GTK_OBJECT(config_dialog), "psz_module_name",
588                          psz_module_name );
589
590     /* we want this ref to be destroyed if the object is destroyed */
591     gtk_signal_connect( GTK_OBJECT(config_dialog), "destroy",
592                        GTK_SIGNAL_FUNC(GtkConfigDialogDestroyed),
593                        (gpointer)p_intf );
594
595     gtk_widget_show_all( config_dialog );
596 }
597
598 #undef LABEL_AND_WIDGET
599 #undef TOOLTIP
600
601 /****************************************************************************
602  * GtkConfigApply: store the changes to the config inside the modules
603  * configuration structure and clear the hash table.
604  ****************************************************************************/
605 void GtkConfigApply( GtkButton * button, gpointer user_data )
606 {
607     intf_thread_t *p_intf;
608     GHashTable *hash_table;
609     GtkWidget *apply_button;
610
611     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
612                                                     "config_hash_table" );
613     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(user_data),
614                                                    "p_intf" );
615     g_hash_table_foreach_remove( hash_table, GtkSaveHashValue, (void*)p_intf );
616
617     /* change the highlight status of the Apply button */
618     apply_button = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
619                                                     "apply_button" );
620     gtk_widget_set_sensitive( apply_button, FALSE );
621 }
622
623 void GtkConfigOk( GtkButton * button, gpointer user_data )
624 {
625     GtkConfigApply( button, user_data );
626     gtk_widget_destroy( gtk_widget_get_toplevel( GTK_WIDGET (button) ) );
627 }
628
629
630 void GtkConfigCancel( GtkButton * button, gpointer user_data )
631 {
632     gtk_widget_destroy( gtk_widget_get_toplevel( GTK_WIDGET (button) ) );
633 }
634
635 void GtkConfigSave( GtkButton * button, gpointer user_data )
636 {
637     intf_thread_t *p_intf;
638
639     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(user_data),
640                                                    "p_intf" );
641     GtkConfigApply( button, user_data );
642     config_SaveConfigFile( p_intf, NULL );
643 }
644
645 /****************************************************************************
646  * GtkModuleHighlighted: display module description when an entry is selected
647  *   in the clist, and activate the configure button if necessary.
648  ****************************************************************************/
649 void GtkModuleHighlighted( GtkCList *module_clist, int row, int column,
650                            GdkEventButton *event, gpointer user_data )
651 {
652     intf_thread_t *p_intf;
653     GtkWidget *config_button;
654     module_t *p_module;
655     char *psz_name;
656
657     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(module_clist),
658                                                    "p_intf" );
659
660     if( gtk_clist_get_text( GTK_CLIST(module_clist), row, 0, &psz_name ) )
661     {
662         /* look for module 'psz_name' */
663         for( p_module = p_intf->p_vlc->p_module_bank->first ;
664              p_module != NULL ;
665              p_module = p_module->next )
666         {
667           if( !strcmp( p_module->psz_object_name, psz_name ) )
668           {
669               gtk_object_set_data( GTK_OBJECT(module_clist),
670                                    "module_highlighted", p_module );
671               config_button = gtk_object_get_data( GTK_OBJECT(module_clist),
672                                                    "config_button" );
673               if( p_module->i_config_items )
674                   gtk_widget_set_sensitive( config_button, TRUE );
675               else
676                   gtk_widget_set_sensitive( config_button, FALSE );
677
678               break;
679           }
680         }
681
682     }
683 }
684
685 /****************************************************************************
686  * GtkModuleConfigure: display module configuration dialog box.
687  ****************************************************************************/
688 void GtkModuleConfigure( GtkButton *button, gpointer user_data )
689 {
690     module_t *p_module;
691     intf_thread_t *p_intf;
692
693     p_module = (module_t *)gtk_object_get_data( GTK_OBJECT(user_data),
694                                                 "module_highlighted" );
695
696     if( !p_module ) return;
697     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(button),
698                                                    "p_intf" );
699     GtkCreateConfigDialog( p_module->psz_object_name, (gpointer)p_intf );
700
701 }
702
703 /****************************************************************************
704  * GtkModuleSelected: select module.
705  ****************************************************************************/
706 void GtkModuleSelected( GtkButton *button, gpointer user_data )
707 {
708     module_t *p_module;
709     GtkWidget *widget;
710
711     p_module = (module_t *)gtk_object_get_data( GTK_OBJECT(user_data),
712                                                 "module_highlighted" );
713     widget = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
714                                                "module_entry" );
715     if( !p_module ) return;
716
717     gtk_entry_set_text( GTK_ENTRY(widget), p_module->psz_object_name );
718
719 }
720
721 /****************************************************************************
722  * GtkStringChanged: signal called when the user changes a string value.
723  ****************************************************************************/
724 static void GtkStringChanged( GtkEditable *editable, gpointer user_data )
725 {
726     intf_thread_t *p_intf;
727     module_config_t *p_config;
728     GHashTable *hash_table;
729     GtkWidget *apply_button;
730
731     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(editable),
732                                                    "p_intf" );
733     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
734                                                     "config_hash_table" );
735     /* free old p_config */
736     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
737                                                        (gpointer)editable );
738     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, (void *)p_intf );
739
740     p_config = malloc( sizeof(module_config_t) );
741     p_config->i_type = CONFIG_ITEM_STRING;
742     p_config->psz_value = gtk_editable_get_chars( editable, 0, -1 );
743     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(editable),
744                                                       "config_option" );
745
746     g_hash_table_insert( hash_table, (gpointer)editable,
747                          (gpointer)p_config );
748
749     /* change the highlight status of the Apply button */
750     apply_button = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
751                                                     "apply_button" );
752     gtk_widget_set_sensitive( apply_button, TRUE );
753 }
754
755 /****************************************************************************
756  * GtkIntChanged: signal called when the user changes an integer value.
757  ****************************************************************************/
758 static void GtkIntChanged( GtkEditable *editable, gpointer user_data )
759 {
760     intf_thread_t *p_intf;
761     module_config_t *p_config;
762     GHashTable *hash_table;
763     GtkWidget *apply_button;
764
765     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(editable),
766                                                    "p_intf" );
767     gtk_spin_button_update( GTK_SPIN_BUTTON(editable) );
768
769     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
770                                                     "config_hash_table" );
771
772     /* free old p_config */
773     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
774                                                        (gpointer)editable );
775     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, (void *)p_intf );
776
777     p_config = malloc( sizeof(module_config_t) );
778     p_config->i_type = CONFIG_ITEM_INTEGER;
779     p_config->i_value = gtk_spin_button_get_value_as_int(
780                             GTK_SPIN_BUTTON(editable) );
781     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(editable),
782                                                       "config_option" );
783
784     g_hash_table_insert( hash_table, (gpointer)editable,
785                          (gpointer)p_config );
786
787     /* change the highlight status of the Apply button */
788     apply_button = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
789                                                     "apply_button" );
790     gtk_widget_set_sensitive( apply_button, TRUE );
791 }
792
793 /****************************************************************************
794  * GtkFloatChanged: signal called when the user changes a float value.
795  ****************************************************************************/
796 static void GtkFloatChanged( GtkEditable *editable, gpointer user_data )
797 {
798     intf_thread_t *p_intf;
799     module_config_t *p_config;
800     GHashTable *hash_table;
801     GtkWidget *apply_button;
802
803     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(editable),
804                                                    "p_intf" );
805     gtk_spin_button_update( GTK_SPIN_BUTTON(editable) );
806
807     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
808                                                     "config_hash_table" );
809
810     /* free old p_config */
811     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
812                                                        (gpointer)editable );
813     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, (void *)p_intf );
814
815     p_config = malloc( sizeof(module_config_t) );
816     p_config->i_type = CONFIG_ITEM_FLOAT;
817     p_config->f_value = gtk_spin_button_get_value_as_float(
818                            GTK_SPIN_BUTTON(editable) );
819     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(editable),
820                                                       "config_option" );
821
822     g_hash_table_insert( hash_table, (gpointer)editable,
823                          (gpointer)p_config );
824
825     /* change the highlight status of the Apply button */
826     apply_button = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
827                                                     "apply_button" );
828     gtk_widget_set_sensitive( apply_button, TRUE );
829 }
830
831 /****************************************************************************
832  * GtkBoolChanged: signal called when the user changes a bool value.
833  ****************************************************************************/
834 static void GtkBoolChanged( GtkToggleButton *button, gpointer user_data )
835 {
836     intf_thread_t *p_intf;
837     module_config_t *p_config;
838     GHashTable *hash_table;
839     GtkWidget *apply_button;
840
841     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(button),
842                                                    "p_intf" );
843     hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
844                                                     "config_hash_table" );
845
846     /* free old p_config */
847     p_config = (module_config_t *)g_hash_table_lookup( hash_table,
848                                                        (gpointer)button );
849     if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, (void *)p_intf );
850
851     p_config = malloc( sizeof(module_config_t) );
852     p_config->i_type = CONFIG_ITEM_BOOL;
853     p_config->i_value = gtk_toggle_button_get_active( button );
854     p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(button),
855                                                       "config_option" );
856
857     g_hash_table_insert( hash_table, (gpointer)button,
858                          (gpointer)p_config );
859
860     /* change the highlight status of the Apply button */
861     apply_button = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
862                                                      "apply_button" );
863     gtk_widget_set_sensitive( apply_button, TRUE );
864 }
865
866 /****************************************************************************
867  * GtkFreeHashTable: signal called when the config hash table is destroyed.
868  ****************************************************************************/
869 static void GtkFreeHashTable( GtkObject *object )
870 {
871     GHashTable *hash_table = (GHashTable *)gtk_object_get_data( object,
872                                                          "config_hash_table" );
873     intf_thread_t *p_intf = (intf_thread_t *)gtk_object_get_data( object,
874                                                                   "p_intf" );
875
876     g_hash_table_foreach( hash_table, GtkFreeHashValue, (void *)p_intf );
877     g_hash_table_destroy( hash_table );
878 }
879
880 /****************************************************************************
881  * GtkFreeHashValue: signal called when an element of the config hash table
882  * is destroyed.
883  ****************************************************************************/
884 static void GtkFreeHashValue( gpointer key, gpointer value, gpointer user_data)
885 {
886     module_config_t * p_config = (module_config_t *)value;
887
888     if( p_config->i_type == CONFIG_ITEM_STRING )
889         if( p_config->psz_value ) g_free( p_config->psz_value );
890     free( p_config );
891 }
892
893 /****************************************************************************
894  * GtkSaveHashValue: callback used when enumerating the hash table in
895  * GtkConfigApply().
896  ****************************************************************************/
897 static gboolean GtkSaveHashValue( gpointer key, gpointer value,
898                                   gpointer user_data )
899 {
900     intf_thread_t *   p_intf   = (intf_thread_t *)user_data;
901     module_config_t * p_config = (module_config_t *)value;
902
903     switch( p_config->i_type )
904     {
905
906     case CONFIG_ITEM_STRING:
907     case CONFIG_ITEM_FILE:
908     case CONFIG_ITEM_MODULE:
909         config_PutPsz( p_intf, p_config->psz_name,
910                        *p_config->psz_value ? p_config->psz_value : NULL );
911         break;
912     case CONFIG_ITEM_INTEGER:
913     case CONFIG_ITEM_BOOL:
914         config_PutInt( p_intf, p_config->psz_name, p_config->i_value );
915         break;
916     case CONFIG_ITEM_FLOAT:
917         config_PutFloat( p_intf, p_config->psz_name, p_config->f_value );
918         break;
919     }
920
921     /* free the hash value we allocated */
922     if( p_config->i_type == CONFIG_ITEM_STRING )
923         g_free( p_config->psz_value );
924     free( p_config );
925
926     /* return TRUE so glib will free the hash entry */
927     return TRUE;
928 }
929
930 /****************************************************************************
931  * GtkConfigDialogDestroyed: callback triggered when the config dialog box is
932  * destroyed.
933  ****************************************************************************/
934 static void GtkConfigDialogDestroyed( GtkObject *object, gpointer user_data )
935 {
936     intf_thread_t *p_intf = (intf_thread_t *)user_data;
937     char *psz_module_name;
938
939     psz_module_name = gtk_object_get_data( object, "psz_module_name" );
940
941     /* remove the ref to the dialog box */
942     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_window),
943                          psz_module_name, NULL );
944
945     GtkFreeHashTable( object );
946 }