]> git.sesse.net Git - vlc/blobdiff - plugins/gtk/gtk_preferences.c
Some dirty ifdefs for a nicer gnome interface (gnome buttons, gnome
[vlc] / plugins / gtk / gtk_preferences.c
index 6268ad13ff2682230b2665d9c877118d8296e691..17d254758cdcf7fc88c1b3f28ccbb2ef062fa125 100644 (file)
@@ -1,12 +1,12 @@
 /*****************************************************************************
- * gtk_control.c : functions to handle stream control buttons.
+ * gtk_preferences.c: functions to handle the preferences dialog box.
  *****************************************************************************
  * Copyright (C) 2000, 2001 VideoLAN
- * $Id: gtk_preferences.c,v 1.7 2001/11/28 15:08:05 massiot Exp $
+ * $Id: gtk_preferences.c,v 1.19 2002/03/27 21:01:29 lool Exp $
+ *
+ * Authors: Gildas Bazin <gbazin@netcourrier.com>
+ *          Loïc Minier <lool@via.ecp.fr>
  *
- * Authors: Samuel Hocevar <sam@zoy.org>
- *          Stéphane Borel <stef@via.ecp.fr>
- *      
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  *****************************************************************************/
 
 /*****************************************************************************
- * Preamble
+ * Preamble: Our main job is to build a nice interface from the modules config
+ *   structure. Once this is done, we need to track each change made by the
+ *   user to the data contained in this interface so that when/if he decides to
+ *   apply his changes we can quickly commit them into the modules config
+ *   structure. (for this last task we use a GHashTable to accumulate the
+ *   changes. To commit them, we then just have to circle through it )
+ *
  *****************************************************************************/
-#include "defs.h"
 #include <sys/types.h>                                              /* off_t */
 #include <stdlib.h>
 
-#define gtk 12
-#define gnome 42
-#if ( MODULE_NAME == gtk )
-#   include <gtk/gtk.h>
-#elif ( MODULE_NAME == gnome )
+#include <videolan/vlc.h>
+
+#ifdef MODULE_NAME_IS_gnome
 #   include <gnome.h>
+#else
+#   include <gtk/gtk.h>
 #endif
-#undef gtk
-#undef gnome
 
 #include <string.h>
 
-#include "config.h"
-#include "common.h"
-#include "intf_msg.h"
-#include "threads.h"
-#include "mtime.h"
-
-#include "stream_control.h"
-#include "input_ext-intf.h"
-
 #include "interface.h"
-#include "intf_playlist.h"
 
-#include "gtk_callbacks.h"
-#include "gtk_interface.h"
 #include "gtk_support.h"
-#include "gtk_playlist.h"
-#include "intf_gtk.h"
+#include "gtk_common.h"
+#include "gtk_preferences.h"
 
-#include "main.h"
+/* local functions */
+static void GtkCreateConfigDialog( char *, intf_thread_t * );
 
-#include "modules_export.h"
+static void GtkConfigOk          ( GtkButton *, gpointer );
+static void GtkConfigApply       ( GtkButton *, gpointer );
+static void GtkConfigCancel      ( GtkButton *, gpointer );
+static void GtkConfigSave        ( GtkButton *, gpointer );
+
+static void GtkConfigDialogDestroyed ( GtkObject *, gpointer );
+
+static void GtkStringChanged     ( GtkEditable *, gpointer );
+static void GtkIntChanged        ( GtkEditable *, gpointer );
+static void GtkBoolChanged       ( GtkToggleButton *, gpointer );
+
+static void GtkFreeHashTable     ( gpointer );
+static void GtkFreeHashValue     ( gpointer, gpointer, gpointer );
+static void GtkSaveHashValue     ( gpointer, gpointer, gpointer );
+
+static void GtkPluginConfigure   ( GtkButton *, gpointer );
+static void GtkPluginSelected    ( GtkButton *, gpointer );
+static void GtkPluginHighlighted ( GtkCList *, int, int, GdkEventButton *,
+                                   gpointer );
 
 /****************************************************************************
- * GtkPreferencesShow: display interface window after initialization
- * if necessary
+ * Callback for menuitems: display configuration interface window
  ****************************************************************************/
+void GtkPreferencesActivate( GtkMenuItem * menuitem, gpointer user_data )
+{
+    intf_thread_t * p_intf;
 
-/* macros to create preference box */
-#define ASSIGN_PSZ_ENTRY( var, default, name )                               \
-    gtk_entry_set_text( GTK_ENTRY( gtk_object_get_data( GTK_OBJECT(          \
-        p_intf->p_sys->p_preferences ), name ) ),                            \
-                        main_GetPszVariable( var, default ) )
-
-#define ASSIGN_INT_VALUE( var, default, name )                                        \
-    gtk_spin_button_set_value( GTK_SPIN_BUTTON( gtk_object_get_data(         \
-        GTK_OBJECT( p_intf->p_sys->p_preferences ), name ) ),                \
-                               main_GetIntVariable( var, default ) )
-
-#define ASSIGN_INT_TOGGLE( var, default, name )                                       \
-    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( gtk_object_get_data(    \
-        GTK_OBJECT( p_intf->p_sys->p_preferences ), name ) ),                \
-                               main_GetIntVariable( var, default ) )
-
-gboolean GtkPreferencesShow( GtkWidget       *widget,
-                             GdkEventButton  *event,
-                             gpointer         user_data )
+    p_intf = GetIntf( GTK_WIDGET(menuitem), (char*)user_data );
+
+    GtkCreateConfigDialog( "main", p_intf );
+}
+
+/****************************************************************************
+ * GtkCreateConfigDialog: dynamically creates the configuration dialog
+ * box from all the configuration data provided by the selected module.
+ ****************************************************************************/
+
+/* create a new tooltipped area */
+#define TOOLTIP( text )                                                   \
+    /* create an event box to catch some events */                        \
+    item_event_box = gtk_event_box_new();                                 \
+    /* add a tooltip on mouseover */                                      \
+    /* FIXME: have a different text for the private text */               \
+    gtk_tooltips_set_tip( p_intf->p_sys->p_tooltips,                      \
+                          item_event_box, text, text );                   \
+    gtk_container_set_border_width( GTK_CONTAINER(item_event_box), 4 );
+
+/* draws a right aligned label in side of a widget */
+#define LABEL_AND_WIDGET( label_text, widget, tooltip )                   \
+    gtk_table_resize( GTK_TABLE(category_table), ++rows, 2 );             \
+    item_align = gtk_alignment_new( 1, .5, 0, 0 );                        \
+    item_label = gtk_label_new( label_text );                             \
+    gtk_container_add( GTK_CONTAINER(item_align), item_label );           \
+    TOOLTIP(tooltip)                                                      \
+    gtk_container_add( GTK_CONTAINER(item_event_box), item_align );       \
+    gtk_table_attach_defaults( GTK_TABLE(category_table), item_event_box, \
+                               0, 1, rows - 1, rows );                    \
+    item_align = gtk_alignment_new( 0, .5, .5, 0 );                       \
+    gtk_container_add( GTK_CONTAINER(item_align), widget );               \
+    TOOLTIP(tooltip)                                                      \
+    gtk_container_add( GTK_CONTAINER(item_event_box), item_align );       \
+    gtk_table_attach_defaults( GTK_TABLE(category_table), item_event_box, \
+                               1, 2, rows - 1, rows );
+
+static void GtkCreateConfigDialog( char *psz_module_name,
+                                   intf_thread_t *p_intf )
 {
-    intf_thread_t *p_intf = GetIntf( GTK_WIDGET(widget), (char*)user_data );
+    module_t *p_module, *p_module_bis;
+    int i;
+
+    guint rows = 0;
+
+    GHashTable *config_hash_table;
+
+    GtkWidget *item_event_box;
+
+    GtkWidget *config_dialog;
+    GtkWidget *config_dialog_vbox;
+    GtkWidget *config_notebook;
+    GtkWidget *category_table = NULL;
+    GtkWidget *category_vbox = NULL;
+
+#ifndef MODULE_NAME_IS_gnome
+    GtkWidget *dialog_action_area;
+#endif
 
-    /* If we have never used the file selector, open it */
-    if( !GTK_IS_WIDGET( p_intf->p_sys->p_preferences ) )
+    GtkWidget *ok_button;
+    GtkWidget *apply_button;
+    GtkWidget *save_button;
+    GtkWidget *cancel_button;
+
+    GtkWidget *item_align;
+    GtkWidget *item_frame;
+    GtkWidget *item_hbox;
+    GtkWidget *item_label;
+    GtkWidget *item_vbox;
+    GtkWidget *string_entry;
+    GtkWidget *integer_spinbutton;
+    GtkObject *item_adj;
+    GtkWidget *bool_checkbutton;
+    GtkWidget *plugin_clist;
+    GtkWidget *plugin_config_button;
+    GtkWidget *plugin_select_button;
+
+
+    /* Check if the dialog box is already opened, if so this will save us
+     * quite a bit of work. (the interface will be destroyed when you actually
+     * close the dialog window, but remember that it is only hidden if you
+     * clicked on the action buttons). This trick also allows us not to
+     * duplicate identical dialog windows. */
+
+    config_dialog = (GtkWidget *)gtk_object_get_data(
+                    GTK_OBJECT(p_intf->p_sys->p_window), psz_module_name );
+    if( config_dialog )
     {
-        p_intf->p_sys->p_preferences = create_intf_preferences();
-        gtk_object_set_data( GTK_OBJECT( p_intf->p_sys->p_preferences ),
-                             "p_intf", p_intf );
-
-        /* Default path */
-        ASSIGN_PSZ_ENTRY( INTF_PATH_VAR, INTF_PATH_DEFAULT,
-                          "preferences_file_path_entry" );
-    
-        /* Default DVD */
-        ASSIGN_PSZ_ENTRY( INPUT_DVD_DEVICE_VAR,INPUT_DVD_DEVICE_DEFAULT,
-                          "preferences_disc_dvd_entry" );
-    
-        /* Default VCD */
-        ASSIGN_PSZ_ENTRY( INPUT_VCD_DEVICE_VAR, INPUT_VCD_DEVICE_DEFAULT,
-                          "preferences_disc_vcd_entry" );
-    
-        /* Default server */
-        ASSIGN_PSZ_ENTRY( INPUT_SERVER_VAR, INPUT_SERVER_DEFAULT,
-                          "preferences_network_server_entry" );
-    
-        /* Default port */
-        ASSIGN_INT_VALUE( INPUT_PORT_VAR, INPUT_PORT_DEFAULT,
-                          "preferences_network_port_spinbutton" );
-    
-        /* Broadcast address */
-        ASSIGN_PSZ_ENTRY( INPUT_BCAST_ADDR_VAR, INPUT_BCAST_ADDR_DEFAULT,
-                          "preferences_network_broadcast_entry" );
-    
-        /* Broadcast stream by default ? */
-        ASSIGN_INT_TOGGLE( INPUT_BROADCAST_VAR, INPUT_BROADCAST_DEFAULT,
-                           "preferences_network_broadcast_checkbutton" );
-    
-        /* XXX Protocol */
-    
-        /* Default interface */
-        ASSIGN_PSZ_ENTRY( INTF_METHOD_VAR, INTF_METHOD_DEFAULT,
-                          "preferences_interface_entry" );
-    
-        /* Default video output */
-        ASSIGN_PSZ_ENTRY( VOUT_METHOD_VAR, VOUT_METHOD_DEFAULT,
-                          "preferences_video_output_entry" );
-    
-        /* Default output width */
-        ASSIGN_INT_VALUE( VOUT_WIDTH_VAR, VOUT_WIDTH_DEFAULT,
-                          "preferences_video_width_spinbutton" );
-    
-        /* Default output height */
-        ASSIGN_INT_VALUE( VOUT_HEIGHT_VAR, VOUT_HEIGHT_DEFAULT,
-                          "preferences_video_height_spinbutton" );
-    
-        /* XXX Default screen depth */
-    
-        /* XXX Default fullscreen depth */
-    
-        /* XXX Default gamma */
-        
-        /* Fullscreen on play */
-        ASSIGN_INT_TOGGLE( VOUT_FULLSCREEN_VAR, VOUT_FULLSCREEN_DEFAULT,
-                           "preferences_video_fullscreen_checkbutton" );
-    
-        /* Grayscale display */
-        ASSIGN_INT_TOGGLE( VOUT_GRAYSCALE_VAR, VOUT_GRAYSCALE_DEFAULT,
-                           "preferences_video_grayscale_checkbutton" );
-    
-        /* Default audio output */
-        ASSIGN_PSZ_ENTRY( AOUT_METHOD_VAR, AOUT_METHOD_DEFAULT,
-                          "preferences_audio_output_entry" );
-    
-        /* Default audio device */
-        ASSIGN_PSZ_ENTRY( AOUT_DSP_VAR, AOUT_DSP_DEFAULT,
-                          "preferences_audio_device_entry" );
-    
-        /* XXX Default frequency */
-    
-        /* XXX Default quality */
-    
-        /* XXX Default number of channels */
-    
-        /* Use spdif output ? */
-        ASSIGN_INT_TOGGLE( AOUT_SPDIF_VAR, AOUT_SPDIF_DEFAULT,
-                           "preferences_audio_spdif_checkbutton" );
-    
-        /* Launch playlist on startup */
-        ASSIGN_INT_TOGGLE( PLAYLIST_STARTUP_VAR, PLAYLIST_STARTUP_DEFAULT,
-                        "preferences_playlist_startup_checkbutton" );
-    
-        /* Enqueue drag'n dropped item as default */
-        ASSIGN_INT_TOGGLE( PLAYLIST_ENQUEUE_VAR, PLAYLIST_ENQUEUE_DEFAULT,
-                           "preferences_playlist_enqueue_checkbutton" );
-    
-        /* Loop on playlist end */
-        ASSIGN_INT_TOGGLE( PLAYLIST_LOOP_VAR, PLAYLIST_LOOP_DEFAULT,
-                           "preferences_playlist_loop_checkbutton" );
-    
-        /* Verbosity of warning messages */
-        ASSIGN_INT_VALUE( INTF_WARNING_VAR, INTF_WARNING_DEFAULT,
-                          "preferences_misc_messages_spinbutton" );
-#undef ASSIGN_PSZ_ENTRY
-#undef ASSIGN_INT_VALUE
-#undef ASSIGN_INT_TOGGLE
+        /* Yeah it was open */
+        gtk_widget_show( config_dialog );
+        gtk_widget_grab_focus( config_dialog );
+        return;
     }
 
-    gtk_widget_show( p_intf->p_sys->p_preferences );
-    gdk_window_raise( p_intf->p_sys->p_preferences->window );
 
-    return TRUE;
+    /* Look for the selected module */
+    for( p_module = p_module_bank->first ; p_module != NULL ;
+         p_module = p_module->next )
+    {
+
+        if( psz_module_name && !strcmp( psz_module_name, p_module->psz_name ) )
+            break;
+    }
+    if( !p_module ) return;
+
+    /* We found it, now we can start building its configuration interface */
+    /* Create the configuration dialog box */
+#ifdef MODULE_NAME_IS_gnome
+    config_dialog = gnome_dialog_new( p_module->psz_longname, NULL );
+    config_dialog_vbox = GNOME_DIALOG(config_dialog)->vbox;
+#else
+    config_dialog = gtk_dialog_new();
+    gtk_window_set_title( GTK_WINDOW(config_dialog), p_module->psz_longname );
+    config_dialog_vbox = GTK_DIALOG(config_dialog)->vbox;
+#endif
+
+    gtk_container_set_border_width( GTK_CONTAINER(config_dialog_vbox), 0 );
+
+    /* Create our config hash table and associate it with the dialog box */
+    config_hash_table = g_hash_table_new( NULL, NULL );
+    gtk_object_set_data_full( GTK_OBJECT(config_dialog),
+                              "config_hash_table", config_hash_table,
+                              (GtkDestroyNotify)GtkFreeHashTable );
+
+    /* Create notebook */
+    config_notebook = gtk_notebook_new();
+    gtk_notebook_set_scrollable( GTK_NOTEBOOK(config_notebook), TRUE );
+    gtk_container_add( GTK_CONTAINER(config_dialog_vbox), config_notebook );
+
+    /* Enumerate config options and add corresponding config boxes */
+    for( i = 0; i < p_module->i_config_lines; i++ )
+    {
+
+        switch( p_module->p_config[i].i_type )
+        {
+        case MODULE_CONFIG_HINT_CATEGORY:
+
+            /* add a new table for right-left alignment */
+            category_table = gtk_table_new( 0, 0, FALSE );
+            gtk_table_set_col_spacings( GTK_TABLE(category_table), 4 );
+            rows = 0;
+            /* the vbox serves only EXPAND / FILL issues in the notebook */
+            category_vbox = gtk_vbox_new( FALSE, 0 );
+            gtk_box_pack_start( GTK_BOX(category_vbox), category_table,
+                                FALSE, FALSE, 0 );
+            gtk_container_set_border_width( GTK_CONTAINER(category_vbox), 4 );
+
+            /* add category vbox as a notebook page */
+            item_label = gtk_label_new( p_module->p_config[i].psz_text );
+            gtk_notebook_append_page( GTK_NOTEBOOK(config_notebook),
+                                      category_vbox, item_label );
+
+
+            break;
+
+        case MODULE_CONFIG_ITEM_PLUGIN:
+
+            item_frame = gtk_frame_new( p_module->p_config[i].psz_text );
+
+            gtk_table_resize( GTK_TABLE(category_table), ++rows, 2 );
+            gtk_table_attach_defaults( GTK_TABLE(category_table), item_frame,
+                                       0, 2, rows - 1, rows );
+
+            TOOLTIP( p_module->p_config[i].psz_longtext )
+            gtk_container_add( GTK_CONTAINER(item_frame), item_event_box );
+
+            item_vbox = gtk_vbox_new( FALSE, 4 );
+            gtk_container_add( GTK_CONTAINER(item_event_box), item_vbox );
+
+            /* create a new clist widget */
+            {
+                gchar * titles[] = { "Name", "Description" };
+
+                plugin_clist =
+                    gtk_clist_new_with_titles( 2, titles );
+            }
+            gtk_clist_column_titles_passive( GTK_CLIST(plugin_clist) );
+            gtk_clist_set_selection_mode( GTK_CLIST(plugin_clist),
+                                          GTK_SELECTION_SINGLE);
+            gtk_container_add( GTK_CONTAINER(item_vbox), plugin_clist );
+
+            /* build a list of available plugins */
+            {
+                gchar * entry[2];
+
+                for( p_module_bis = p_module_bank->first ;
+                     p_module_bis != NULL ;
+                     p_module_bis = p_module_bis->next )
+                {
+                    if( p_module_bis->i_capabilities &
+                        (1 << p_module->p_config[i].i_value) )
+                    {
+                        entry[0] = p_module_bis->psz_name;
+                        entry[1] = p_module_bis->psz_longname;
+                        gtk_clist_append( GTK_CLIST(plugin_clist), entry );
+                    }
+                }
+            }
+
+            gtk_clist_set_column_auto_resize( GTK_CLIST(plugin_clist),
+                                              0, TRUE );
+            gtk_clist_set_column_auto_resize( GTK_CLIST(plugin_clist),
+                                              1, TRUE );
+
+            /* connect signals to the plugins list */
+            gtk_signal_connect( GTK_OBJECT(plugin_clist), "select_row",
+                                GTK_SIGNAL_FUNC(GtkPluginHighlighted),
+                                NULL );
+
+            /* hbox holding the "select" and "configure" buttons */
+            item_hbox = gtk_hbox_new( FALSE, 4 );
+            gtk_container_add( GTK_CONTAINER(item_vbox), item_hbox);
+
+            /* add configure button */
+            plugin_config_button =
+                gtk_button_new_with_label( _("Configure") );
+            gtk_widget_set_sensitive( plugin_config_button, FALSE );
+            gtk_container_add( GTK_CONTAINER(item_hbox),
+                               plugin_config_button );
+            gtk_object_set_data( GTK_OBJECT(plugin_config_button),
+                                 "p_intf", p_intf );
+            gtk_object_set_data( GTK_OBJECT(plugin_clist),
+                                 "config_button", plugin_config_button );
+
+            /* add select button */
+            plugin_select_button =
+                gtk_button_new_with_label( _("Select") );
+            gtk_container_add( GTK_CONTAINER(item_hbox),
+                               plugin_select_button );
+
+            /* hbox holding the "selected" label and text input */
+            item_hbox = gtk_hbox_new( FALSE, 4 );
+            gtk_container_add( GTK_CONTAINER(item_vbox), item_hbox);
+            /* add new label */
+            item_label = gtk_label_new( _("Selected:") );
+            gtk_container_add( GTK_CONTAINER(item_hbox), item_label );
+
+            /* add input box with default value */
+            string_entry = gtk_entry_new();
+            gtk_object_set_data( GTK_OBJECT(plugin_clist),
+                                 "plugin_entry", string_entry );
+            gtk_container_add( GTK_CONTAINER(item_hbox), string_entry );
+            vlc_mutex_lock( p_module->p_config[i].p_lock );
+            gtk_entry_set_text( GTK_ENTRY(string_entry),
+                                p_module->p_config[i].psz_value ?
+                                p_module->p_config[i].psz_value : "" );
+            vlc_mutex_unlock( p_module->p_config[i].p_lock );
+
+            /* connect signals to the buttons */
+            gtk_signal_connect( GTK_OBJECT(plugin_config_button), "clicked",
+                                GTK_SIGNAL_FUNC(GtkPluginConfigure),
+                                (gpointer)plugin_clist );
+            gtk_signal_connect( GTK_OBJECT(plugin_select_button), "clicked",
+                                GTK_SIGNAL_FUNC(GtkPluginSelected),
+                                (gpointer)plugin_clist );
+
+            /* connect signal to track changes in the text box */
+            gtk_object_set_data( GTK_OBJECT(string_entry), "config_option",
+                                 p_module->p_config[i].psz_name );
+            gtk_signal_connect( GTK_OBJECT(string_entry), "changed",
+                                GTK_SIGNAL_FUNC(GtkStringChanged),
+                                (gpointer)config_dialog );
+            break;
+
+        case MODULE_CONFIG_ITEM_STRING:
+        case MODULE_CONFIG_ITEM_FILE:
+
+            /* add input box with default value */
+            string_entry = gtk_entry_new();
+            vlc_mutex_lock( p_module->p_config[i].p_lock );
+            gtk_entry_set_text( GTK_ENTRY(string_entry),
+                                p_module->p_config[i].psz_value ?
+                                p_module->p_config[i].psz_value : "" );
+            vlc_mutex_unlock( p_module->p_config[i].p_lock );
+
+            /* connect signal to track changes in the text box */
+            gtk_object_set_data( GTK_OBJECT(string_entry), "config_option",
+                                 p_module->p_config[i].psz_name );
+            gtk_signal_connect( GTK_OBJECT(string_entry), "changed",
+                                GTK_SIGNAL_FUNC(GtkStringChanged),
+                                (gpointer)config_dialog );
+
+            LABEL_AND_WIDGET( p_module->p_config[i].psz_text,
+                              string_entry,
+                              p_module->p_config[i].psz_longtext );
+            break;
+
+        case MODULE_CONFIG_ITEM_INTEGER:
+
+            /* add input box with default value */
+            item_adj = gtk_adjustment_new( p_module->p_config[i].i_value,
+                                           -1, 99999, 1, 10, 10 );
+            integer_spinbutton = gtk_spin_button_new( GTK_ADJUSTMENT(item_adj),
+                                                      1, 0 );
+
+            /* connect signal to track changes in the spinbutton value */
+            gtk_object_set_data( GTK_OBJECT(integer_spinbutton),
+                                 "config_option",
+                                 p_module->p_config[i].psz_name );
+            gtk_signal_connect( GTK_OBJECT(integer_spinbutton), "changed",
+                                GTK_SIGNAL_FUNC(GtkIntChanged),
+                                (gpointer)config_dialog );
+
+            LABEL_AND_WIDGET( p_module->p_config[i].psz_text,
+                              integer_spinbutton,
+                              p_module->p_config[i].psz_longtext );
+            break;
+
+        case MODULE_CONFIG_ITEM_BOOL:
+
+            /* add check button */
+            bool_checkbutton = gtk_check_button_new();
+            gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(bool_checkbutton),
+                                          p_module->p_config[i].i_value );
+
+            /* connect signal to track changes in the button state */
+            gtk_object_set_data( GTK_OBJECT(bool_checkbutton), "config_option",
+                                 p_module->p_config[i].psz_name );
+            gtk_signal_connect( GTK_OBJECT(bool_checkbutton), "toggled",
+                                GTK_SIGNAL_FUNC(GtkBoolChanged),
+                                (gpointer)config_dialog );
+
+            LABEL_AND_WIDGET( p_module->p_config[i].psz_text,
+                              bool_checkbutton,
+                              p_module->p_config[i].psz_longtext );
+            break;
+        }
+    }
+
+#ifndef MODULE_NAME_IS_gnome
+    /* Now let's add the action buttons at the bottom of the page */
+    dialog_action_area = GTK_DIALOG(config_dialog)->action_area;
+    gtk_container_set_border_width( GTK_CONTAINER(dialog_action_area), 4 );
+
+    /* add a new table for the config option */
+    item_hbox = gtk_hbox_new( FALSE, 0 );
+    gtk_box_pack_end( GTK_BOX(dialog_action_area), item_hbox,
+                      TRUE, FALSE, 0 );
+    item_hbox = gtk_hbox_new( FALSE, 0 );
+    gtk_box_pack_end( GTK_BOX(dialog_action_area), item_hbox,
+                      TRUE, FALSE, 0 );
+#endif
+
+    /* Create the OK button */
+#ifdef MODULE_NAME_IS_gnome
+    gnome_dialog_append_button( GNOME_DIALOG(config_dialog),
+                                GNOME_STOCK_BUTTON_OK );
+    ok_button =
+        GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
+
+    gnome_dialog_append_button( GNOME_DIALOG(config_dialog),
+                                GNOME_STOCK_BUTTON_APPLY );
+    apply_button =
+        GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
+
+    gnome_dialog_append_button_with_pixmap(
+        GNOME_DIALOG(config_dialog), _("Save"), GNOME_STOCK_PIXMAP_SAVE );
+    save_button =
+        GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
+
+    gnome_dialog_append_button( GNOME_DIALOG(config_dialog),
+                                GNOME_STOCK_BUTTON_CANCEL );
+    cancel_button =
+        GTK_WIDGET(g_list_last(GNOME_DIALOG(config_dialog)->buttons)->data);
+#else
+    ok_button = gtk_button_new_with_label( _("Ok") );
+    gtk_box_pack_start( GTK_BOX(dialog_action_area), ok_button,
+                        TRUE, TRUE, 0 );
+
+    apply_button = gtk_button_new_with_label( _("Apply") );
+    gtk_box_pack_start( GTK_BOX(dialog_action_area), apply_button,
+                        TRUE, TRUE, 0 );
+
+    save_button = gtk_button_new_with_label( _("Save") );
+    gtk_box_pack_start( GTK_BOX(dialog_action_area), save_button,
+                        TRUE, TRUE, 0 );
+
+    cancel_button = gtk_button_new_with_label( _("Cancel") );
+    gtk_box_pack_start( GTK_BOX(dialog_action_area), cancel_button,
+                        TRUE, TRUE, 0 );
+#endif
+
+    gtk_signal_connect( GTK_OBJECT(ok_button), "clicked",
+                        GTK_SIGNAL_FUNC(GtkConfigOk),
+                        config_dialog );
+    gtk_signal_connect( GTK_OBJECT(apply_button), "clicked",
+                        GTK_SIGNAL_FUNC(GtkConfigApply),
+                        config_dialog );
+    gtk_signal_connect( GTK_OBJECT(save_button), "clicked",
+                        GTK_SIGNAL_FUNC(GtkConfigSave),
+                        config_dialog );
+    gtk_signal_connect( GTK_OBJECT(cancel_button), "clicked",
+                        GTK_SIGNAL_FUNC(GtkConfigCancel),
+                        config_dialog );
+
+
+
+    /* Ok, job done successfully. Let's keep a reference to the dialog box */
+    gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_window),
+                         psz_module_name, config_dialog );
+    gtk_object_set_data( GTK_OBJECT(config_dialog), "psz_module_name",
+                         psz_module_name );
+
+    /* we want this ref to be destroyed if the object is destroyed */
+    gtk_signal_connect( GTK_OBJECT(config_dialog), "destroy",
+                       GTK_SIGNAL_FUNC(GtkConfigDialogDestroyed),
+                       (gpointer)p_intf );
+
+    gtk_widget_show_all( config_dialog );
 }
 
+#undef FRAME
+#undef SCROLLED_WINDOW
+#undef LABEL
+#undef TOOLTIP
+
 /****************************************************************************
- * GtkPreferencesApply: store the values into the environnement variables
+ * GtkConfigApply: store the changes to the config inside the modules
+ * configuration structure
  ****************************************************************************/
+void GtkConfigApply( GtkButton * button, gpointer user_data )
+{
+    GHashTable *hash_table;
+    hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
+                                                    "config_hash_table" );
+    g_hash_table_foreach( hash_table, GtkSaveHashValue, NULL );
 
-/* macros to read value frfom preference box */
-#define ASSIGN_PSZ_ENTRY( var, name )                                        \
-    main_PutPszVariable( var, gtk_entry_get_text(                            \
-    GTK_ENTRY( gtk_object_get_data( GTK_OBJECT( p_preferences ), name ) ) ) )
+}
 
-#define ASSIGN_INT_VALUE( var, name )                                        \
-    main_PutIntVariable( var, gtk_spin_button_get_value_as_int(              \
-        GTK_SPIN_BUTTON( gtk_object_get_data( GTK_OBJECT( p_preferences ),   \
-        name ) ) ) )
+void GtkConfigOk( GtkButton * button, gpointer user_data )
+{
+    GtkConfigApply( button, user_data );
+    gtk_widget_hide( gtk_widget_get_toplevel( GTK_WIDGET (button) ) );
+}
 
-#define ASSIGN_INT_TOGGLE( var, name )                                       \
-    main_PutIntVariable( var, gtk_toggle_button_get_active(                  \
-        GTK_TOGGLE_BUTTON( gtk_object_get_data( GTK_OBJECT( p_preferences ), \
-        name ) ) ) )
 
-void GtkPreferencesApply( GtkButton * button, gpointer user_data )
+void GtkConfigCancel( GtkButton * button, gpointer user_data )
 {
-    GtkWidget *     p_preferences;
+    gtk_widget_hide( gtk_widget_get_toplevel( GTK_WIDGET (button) ) );
+}
 
-    /* get preferences window */
-    p_preferences = gtk_widget_get_toplevel( GTK_WIDGET( button ) );
+void GtkConfigSave( GtkButton * button, gpointer user_data )
+{
+    GtkConfigApply( button, user_data );
+    config_SaveConfigFile( NULL );
+}
 
-    /* Default path */
-    ASSIGN_PSZ_ENTRY( INTF_PATH_VAR, "preferences_file_path_entry" );
+/****************************************************************************
+ * GtkPluginHighlighted: display plugin description when an entry is selected
+ *   in the clist, and activate the configure button if necessary.
+ ****************************************************************************/
+void GtkPluginHighlighted( GtkCList *plugin_clist, int row, int column,
+                           GdkEventButton *event, gpointer user_data )
+{
+    GtkWidget *config_button;
+    module_t *p_module;
+    char *psz_name;
 
-    /* Default DVD */
-    ASSIGN_PSZ_ENTRY( INPUT_DVD_DEVICE_VAR, "preferences_disc_dvd_entry" );
+    if( gtk_clist_get_text( GTK_CLIST(plugin_clist), row, 0, &psz_name ) )
+    {
+        /* look for plugin 'psz_name' */
+        for( p_module = p_module_bank->first ;
+             p_module != NULL ;
+             p_module = p_module->next )
+        {
+          if( !strcmp( p_module->psz_name, psz_name ) )
+          {
+              gtk_object_set_data( GTK_OBJECT(plugin_clist),
+                                   "plugin_highlighted", p_module );
+              config_button = gtk_object_get_data( GTK_OBJECT(plugin_clist),
+                                                   "config_button" );
+              if( p_module->i_config_items )
+                  gtk_widget_set_sensitive( config_button, TRUE );
+              else
+                  gtk_widget_set_sensitive( config_button, FALSE );
+
+              break;
+          }
+        }
 
-    /* Default VCD */
-    ASSIGN_PSZ_ENTRY( INPUT_VCD_DEVICE_VAR, "preferences_disc_vcd_entry" );
+    }
+}
 
-    /* Default server */
-    ASSIGN_PSZ_ENTRY( INPUT_SERVER_VAR, "preferences_network_server_entry" );
+/****************************************************************************
+ * GtkPluginConfigure: display plugin configuration dialog box.
+ ****************************************************************************/
+void GtkPluginConfigure( GtkButton *button, gpointer user_data )
+{
+    module_t *p_module;
+    intf_thread_t *p_intf;
 
-    /* Default port */
-    ASSIGN_INT_VALUE( INPUT_PORT_VAR, "preferences_network_port_spinbutton" );
+    p_module = (module_t *)gtk_object_get_data( GTK_OBJECT(user_data),
+                                                "plugin_highlighted" );
 
-    /* Broadcast address */
-    ASSIGN_PSZ_ENTRY( INPUT_BCAST_ADDR_VAR,
-                      "preferences_network_broadcast_entry" );
+    if( !p_module ) return;
+    p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT(button),
+                                                   "p_intf" );
+    GtkCreateConfigDialog( p_module->psz_name, (gpointer)p_intf );
 
-    /* Broadcast stream by default ? */
-    ASSIGN_INT_TOGGLE( INPUT_BROADCAST_VAR,
-                       "preferences_network_broadcast_checkbutton" );
+}
 
-    /* XXX Protocol */
+/****************************************************************************
+ * GtkPluginSelected: select plugin.
+ ****************************************************************************/
+void GtkPluginSelected( GtkButton *button, gpointer user_data )
+{
+    module_t *p_module;
+    GtkWidget *widget;
 
-    /* Default interface */
-    ASSIGN_PSZ_ENTRY( INTF_METHOD_VAR, "preferences_interface_entry" );
+    p_module = (module_t *)gtk_object_get_data( GTK_OBJECT(user_data),
+                                                "plugin_highlighted" );
+    widget = (GtkWidget *)gtk_object_get_data( GTK_OBJECT(user_data),
+                                               "plugin_entry" );
+    if( !p_module ) return;
 
-    /* Default video output */
-    ASSIGN_PSZ_ENTRY( VOUT_METHOD_VAR, "preferences_video_output_entry" );
+    gtk_entry_set_text( GTK_ENTRY(widget), p_module->psz_name );
 
-    /* Default output width */
-    ASSIGN_INT_VALUE( VOUT_WIDTH_VAR, "preferences_video_width_spinbutton" );
+}
 
-    /* Default output height */
-    ASSIGN_INT_VALUE( VOUT_HEIGHT_VAR, "preferences_video_height_spinbutton" );
+/****************************************************************************
+ * GtkStringChanged: signal called when the user changes a string value.
+ ****************************************************************************/
+static void GtkStringChanged( GtkEditable *editable, gpointer user_data )
+{
+    module_config_t *p_config;
 
-    /* XXX Default screen depth */
+    GHashTable *hash_table;
 
-    /* XXX Default fullscreen depth */
+    hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
+                                                    "config_hash_table" );
+    /* free old p_config */
+    p_config = (module_config_t *)g_hash_table_lookup( hash_table,
+                                                       (gpointer)editable );
+    if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, NULL );
 
-    /* XXX Default gamma */
-    
-    /* Fullscreen on play */
-    ASSIGN_INT_TOGGLE( VOUT_FULLSCREEN_VAR,
-                       "preferences_video_fullscreen_checkbutton" );
+    p_config = malloc( sizeof(module_config_t) );
+    p_config->i_type = MODULE_CONFIG_ITEM_STRING;
+    p_config->psz_value = gtk_editable_get_chars( editable, 0, -1 );
+    p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(editable),
+                                                      "config_option" );
 
-    /* Grayscale display */
-    ASSIGN_INT_TOGGLE( VOUT_GRAYSCALE_VAR,
-                       "preferences_video_grayscale_checkbutton" );
+    g_hash_table_insert( hash_table, (gpointer)editable,
+                         (gpointer)p_config );
+}
 
-    /* Default audio output */
-    ASSIGN_PSZ_ENTRY( AOUT_METHOD_VAR, "preferences_audio_output_entry" );
+/****************************************************************************
+ * GtkIntChanged: signal called when the user changes a an integer value.
+ ****************************************************************************/
+static void GtkIntChanged( GtkEditable *editable, gpointer user_data )
+{
+    module_config_t *p_config;
 
-    /* Default audio device */
-    ASSIGN_PSZ_ENTRY( AOUT_DSP_VAR, "preferences_audio_device_entry" );
+    GHashTable *hash_table;
 
-    /* XXX Default frequency */
+    hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
+                                                    "config_hash_table" );
 
-    /* XXX Default quality */
+    /* free old p_config */
+    p_config = (module_config_t *)g_hash_table_lookup( hash_table,
+                                                       (gpointer)editable );
+    if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, NULL );
 
-    /* XXX Default number of channels */
+    p_config = malloc( sizeof(module_config_t) );
+    p_config->i_type = MODULE_CONFIG_ITEM_INTEGER;
+    p_config->i_value =  gtk_spin_button_get_value_as_int(
+                             GTK_SPIN_BUTTON(editable) );
+    p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(editable),
+                                                      "config_option" );
 
-    /* Use spdif output ? */
-    ASSIGN_INT_TOGGLE( AOUT_SPDIF_VAR, "preferences_audio_spdif_checkbutton" );
+    g_hash_table_insert( hash_table, (gpointer)editable,
+                         (gpointer)p_config );
+}
 
-    /* Launch playlist on startup */
-    ASSIGN_INT_TOGGLE( PLAYLIST_STARTUP_VAR,
-                       "preferences_playlist_startup_checkbutton" );
+/****************************************************************************
+ * GtkStringChanged: signal called when the user changes a bool value.
+ ****************************************************************************/
+static void GtkBoolChanged( GtkToggleButton *button, gpointer user_data )
+{
+    module_config_t *p_config;
 
-    /* Enqueue drag'n dropped item as default */
-    ASSIGN_INT_TOGGLE( PLAYLIST_ENQUEUE_VAR,
-                       "preferences_playlist_enqueue_checkbutton" );
+    GHashTable *hash_table;
 
-    /* Loop on playlist end */
-    ASSIGN_INT_TOGGLE( PLAYLIST_LOOP_VAR,
-                       "preferences_playlist_loop_checkbutton" );
+    hash_table = (GHashTable *)gtk_object_get_data( GTK_OBJECT(user_data),
+                                                    "config_hash_table" );
 
-    /* Verbosity of warning messages */
-    ASSIGN_INT_VALUE( INTF_WARNING_VAR,
-                      "preferences_misc_messages_spinbutton" );
-}
-#undef ASSIGN_PSZ_ENTRY
-#undef ASSIGN_INT_VALUE
-#undef ASSIGN_INT_TOGGLE
+    /* free old p_config */
+    p_config = (module_config_t *)g_hash_table_lookup( hash_table,
+                                                       (gpointer)button );
+    if( p_config ) GtkFreeHashValue( NULL, (gpointer)p_config, NULL );
+
+    p_config = malloc( sizeof(module_config_t) );
+    p_config->i_type = MODULE_CONFIG_ITEM_BOOL;
+    p_config->i_value = gtk_toggle_button_get_active( button );
+    p_config->psz_name = (char *)gtk_object_get_data( GTK_OBJECT(button),
+                                                      "config_option" );
 
+    g_hash_table_insert( hash_table, (gpointer)button,
+                         (gpointer)p_config );
+}
 
-void GtkPreferencesOk( GtkButton * button, gpointer user_data )
+/****************************************************************************
+ * GtkFreeHashTable: signal called when the config hash table is destroyed.
+ ****************************************************************************/
+static void GtkFreeHashTable( gpointer user_data )
 {
-    GtkPreferencesApply( button, user_data );
-    gtk_widget_hide( gtk_widget_get_toplevel( GTK_WIDGET (button) ) );
+    GHashTable *hash_table = (GHashTable *)user_data;
+
+    g_hash_table_foreach( hash_table, GtkFreeHashValue, NULL );
+    g_hash_table_destroy( hash_table );
 }
 
+/****************************************************************************
+ * GtkFreeHashValue: signal called when an element of the config hash table
+ * is destroyed.
+ ****************************************************************************/
+static void GtkFreeHashValue( gpointer key, gpointer value, gpointer user_data)
+{
+    module_config_t *p_config = (module_config_t *)value;
+
+    if( p_config->i_type == MODULE_CONFIG_ITEM_STRING )
+        g_free( p_config->psz_value );
+    free( p_config );
+}
 
-void GtkPreferencesCancel( GtkButton * button, gpointer user_data )
+/****************************************************************************
+ * GtkSaveHashValue: callback used when enumerating the hash table in
+ * GtkConfigApply().
+ ****************************************************************************/
+static void GtkSaveHashValue( gpointer key, gpointer value, gpointer user_data)
 {
-    gtk_widget_hide( gtk_widget_get_toplevel( GTK_WIDGET (button) ) );
+    module_config_t *p_config = (module_config_t *)value;
+
+    switch( p_config->i_type )
+    {
+
+    case MODULE_CONFIG_ITEM_STRING:
+    case MODULE_CONFIG_ITEM_FILE:
+    case MODULE_CONFIG_ITEM_PLUGIN:
+        config_PutPszVariable( p_config->psz_name,
+            *p_config->psz_value? p_config->psz_value : NULL );
+        break;
+    case MODULE_CONFIG_ITEM_INTEGER:
+    case MODULE_CONFIG_ITEM_BOOL:
+        config_PutIntVariable( p_config->psz_name, p_config->i_value );
+        break;
+    }
 }
 
 /****************************************************************************
- * Callbacks for menuitems
+ * GtkConfigDialogDestroyed: callback triggered when the config dialog box is
+ * destroyed.
  ****************************************************************************/
-void GtkPreferencesActivate( GtkMenuItem * menuitem, gpointer user_data )
+static void GtkConfigDialogDestroyed( GtkObject *object, gpointer user_data )
 {
-    GtkPreferencesShow( GTK_WIDGET( menuitem ), NULL, user_data );
+    intf_thread_t *p_intf = (intf_thread_t *)user_data;
+    char *psz_module_name;
+
+    psz_module_name = gtk_object_get_data( object, "psz_module_name" );
+
+    /* remove the ref to the dialog box */
+    gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_window),
+                         psz_module_name, NULL );
+
 }