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