gnome-media r4128 - in trunk/gnome-volume-control: . src



Author: mccann
Date: Sun Dec 21 20:15:15 2008
New Revision: 4128
URL: http://svn.gnome.org/viewvc/gnome-media?rev=4128&view=rev

Log:
2008-12-21  William Jon McCann  <jmccann redhat com>

	* src/gvc-sound-theme-chooser.c (on_combobox_changed),
	(set_input_feedback_enabled), (update_theme), (on_alert_toggled),
	(play_preview_for_path), (on_treeview_row_activated),
	(on_treeview_selection_changed), (create_alert_treeview),
	(gvc_sound_theme_chooser_constructor),
	(gvc_sound_theme_chooser_init):
	* src/gvc-sound-theme-editor.c (theme_changed_custom_reinit),
	(on_theme_changed), (load_index_theme_name), (sound_theme_in_dir),
	(add_theme_to_store), (set_theme_name), (audible_bell_foreach),
	(set_audible_bell_enabled), (input_feedback_foreach),
	(set_input_feedback_enabled), (get_file_type),
	(theme_changed_custom_init), (update_theme),
	(setup_theme_selector), (play_sound_preview), (get_sound_filename),
	(count_customised_sounds), (save_sounds), (save_custom_theme),
	(dump_theme), (on_setting_column_edited), (fill_custom_model),
	(on_combobox_editing_started), (play_sound_at_path),
	(setting_set_func), (activatable_cell_renderer_pixbuf_activate),
	(activatable_cell_renderer_pixbuf_init),
	(activatable_cell_renderer_pixbuf_class_init),
	(setup_theme_custom_selector),
	(gvc_sound_theme_editor_constructor),
	(gvc_sound_theme_editor_class_init), (on_click_feedback_toggled),
	(on_key_changed), (on_treeview_row_activated),
	(constrain_list_size), (setup_list_size_constraint),
	(gvc_sound_theme_editor_init), (gvc_sound_theme_editor_finalize),
	(gvc_sound_theme_editor_new):
	* src/gvc-sound-theme-editor.h:
	Simplify the theme selection.  Move the ability to
	customize the sound theme into a separate widget.  We
	should add a Customize button to access it.
	This new approach will mirror the way we handle
	wallpapers.  Next thing to do is to add a set
	of standard alert sounds that one can choose from.
	And have the widget customize the selected theme to
	use them.



Added:
   trunk/gnome-volume-control/src/gvc-sound-theme-editor.c
   trunk/gnome-volume-control/src/gvc-sound-theme-editor.h
Modified:
   trunk/gnome-volume-control/ChangeLog
   trunk/gnome-volume-control/src/gvc-sound-theme-chooser.c

Modified: trunk/gnome-volume-control/src/gvc-sound-theme-chooser.c
==============================================================================
--- trunk/gnome-volume-control/src/gvc-sound-theme-chooser.c	(original)
+++ trunk/gnome-volume-control/src/gvc-sound-theme-chooser.c	Sun Dec 21 20:15:15 2008
@@ -52,53 +52,6 @@
 
 G_DEFINE_TYPE (GvcSoundThemeChooser, gvc_sound_theme_chooser, GTK_TYPE_VBOX)
 
-typedef enum {
-        CATEGORY_INVALID,
-        CATEGORY_BELL,
-        CATEGORY_WINDOWS_BUTTONS,
-        CATEGORY_DESKTOP,
-        CATEGORY_ALERTS,
-        NUM_CATEGORIES
-} CategoryType;
-
-typedef enum {
-        SOUND_TYPE_NORMAL,
-        SOUND_TYPE_AUDIO_BELL,
-        SOUND_TYPE_FEEDBACK
-} SoundType;
-
-static struct {
-        CategoryType category;
-        SoundType    type;
-        const char  *display_name;
-        const char  *names[6];
-} sounds[20] = {
-        /* Bell */
-        { CATEGORY_BELL, SOUND_TYPE_AUDIO_BELL, NC_("Sound event", "Alert sound"), { "bell-terminal", "bell-window-system", NULL } },
-        /* Windows and buttons */
-        { CATEGORY_WINDOWS_BUTTONS, -1, NC_("Sound event", "Windows and Buttons"), { NULL } },
-        { CATEGORY_WINDOWS_BUTTONS, SOUND_TYPE_FEEDBACK, NC_("Sound event", "Button clicked"), { "button-pressed", "menu-click", "menu-popup", "menu-popdown", "menu-replace", NULL } },
-        { CATEGORY_WINDOWS_BUTTONS, SOUND_TYPE_FEEDBACK, NC_("Sound event", "Toggle button clicked"), { "button-toggle-off", "button-toggle-on", NULL } },
-        { CATEGORY_WINDOWS_BUTTONS, SOUND_TYPE_FEEDBACK, NC_("Sound event", "Window maximized"), { "window-maximized", NULL } },
-        { CATEGORY_WINDOWS_BUTTONS, SOUND_TYPE_FEEDBACK, NC_("Sound event", "Window unmaximized"), { "window-unmaximized", NULL } },
-        { CATEGORY_WINDOWS_BUTTONS, SOUND_TYPE_FEEDBACK, NC_("Sound event", "Window minimised"), { "window-minimized", NULL } },
-        /* Desktop */
-        { CATEGORY_DESKTOP, -1, NC_("Sound event", "Desktop"), { NULL } },
-        { CATEGORY_DESKTOP, SOUND_TYPE_NORMAL, NC_("Sound event", "Login"), { "desktop-login", NULL } },
-        { CATEGORY_DESKTOP, SOUND_TYPE_NORMAL, NC_("Sound event", "Logout"), { "desktop-logout", NULL } },
-        { CATEGORY_DESKTOP, SOUND_TYPE_NORMAL, NC_("Sound event", "New e-mail"), { "message-new-email", NULL } },
-        { CATEGORY_DESKTOP, SOUND_TYPE_NORMAL, NC_("Sound event", "Empty trash"), { "trash-empty", NULL } },
-        { CATEGORY_DESKTOP, SOUND_TYPE_NORMAL, NC_("Sound event", "Long action completed (download, CD burning, etc.)"), { "complete-copy", "complete-download", "complete-media-burn", "complete-media-rip", "complete-scan", NULL } },
-        /* Alerts? */
-        { CATEGORY_ALERTS, -1, NC_("Sound event", "Alerts"), { NULL } },
-        { CATEGORY_ALERTS, SOUND_TYPE_NORMAL, NC_("Sound event", "Information or question"), { "dialog-information", "dialog-question", NULL } },
-        { CATEGORY_ALERTS, SOUND_TYPE_NORMAL, NC_("Sound event", "Warning"), { "dialog-warning", NULL } },
-        { CATEGORY_ALERTS, SOUND_TYPE_NORMAL, NC_("Sound event", "Error"), { "dialog-error", NULL } },
-        { CATEGORY_ALERTS, SOUND_TYPE_NORMAL, NC_("Sound event", "Battery warning"), { "power-unplug-battery-low", "battery-low", "battery-caution", NULL } },
-        /* Finish off */
-        { -1, -1, NULL, { NULL } }
-};
-
 #define KEY_SOUNDS_DIR             "/desktop/gnome/sound"
 #define EVENT_SOUNDS_KEY           KEY_SOUNDS_DIR "/event_sounds"
 #define INPUT_SOUNDS_KEY           KEY_SOUNDS_DIR "/input_feedback_sounds"
@@ -106,9 +59,9 @@
 #define KEY_METACITY_DIR           "/apps/metacity/general"
 #define AUDIO_BELL_KEY             KEY_METACITY_DIR "/audible_bell"
 
+#define DEFAULT_ALERT_ID        "__default"
 #define CUSTOM_THEME_NAME       "__custom"
 #define NO_SOUNDS_THEME_NAME    "__no_sounds"
-#define PREVIEW_BUTTON_XPAD     5
 
 enum {
         THEME_DISPLAY_COL,
@@ -118,46 +71,19 @@
 };
 
 enum {
-        SOUND_UNSET,
-        SOUND_OFF,
-        SOUND_BUILTIN,
-        SOUND_CUSTOM,
-        SOUND_CUSTOM_OLD
+        ALERT_DISPLAY_COL,
+        ALERT_IDENTIFIER_COL,
+        ALERT_SOUND_TYPE_COL,
+        ALERT_ACTIVE_COL,
+        ALERT_NUM_COLS
 };
 
 enum {
-        DISPLAY_COL,
-        SETTING_COL,
-        TYPE_COL,
-        SENSITIVE_COL,
-        HAS_PREVIEW_COL,
-        FILENAME_COL,
-        SOUND_NAMES_COL,
-        NUM_COLS
+        SOUND_TYPE_DEFAULT_FROM_THEME,
+        SOUND_TYPE_BUILTIN,
+        SOUND_TYPE_CUSTOM
 };
 
-static gboolean
-theme_changed_custom_reinit (GtkTreeModel *model,
-                             GtkTreePath  *path,
-                             GtkTreeIter  *iter,
-                             gpointer      data)
-{
-        int      type;
-        gboolean sensitive;
-
-        gtk_tree_model_get (model,
-                            iter,
-                            TYPE_COL, &type,
-                            SENSITIVE_COL, &sensitive, -1);
-        if (type != -1) {
-                gtk_tree_store_set (GTK_TREE_STORE (model), iter,
-                                    SETTING_COL, SOUND_BUILTIN,
-                                    HAS_PREVIEW_COL, sensitive,
-                                    -1);
-        }
-        return FALSE;
-}
-
 static void
 on_combobox_changed (GtkComboBox          *widget,
                      GvcSoundThemeChooser *chooser)
@@ -190,28 +116,7 @@
         gconf_client_set_string (client, SOUND_THEME_KEY, theme_name, NULL);
         g_object_unref (client);
 
-        /* Don't reinit a custom theme */
-        if (strcmp (theme_name, CUSTOM_THEME_NAME) != 0) {
-                model = gtk_tree_view_get_model (GTK_TREE_VIEW (chooser->priv->treeview));
-                gtk_tree_model_foreach (model, theme_changed_custom_reinit, NULL);
-
-                /* Delete the custom dir */
-                delete_custom_theme_dir ();
-
-                /* And the combo box entry */
-                model = gtk_combo_box_get_model (GTK_COMBO_BOX (chooser->priv->combo_box));
-                gtk_tree_model_get_iter_first (model, &iter);
-                do {
-                        char *parent;
-                        gtk_tree_model_get (model, &iter, THEME_PARENT_ID_COL, &parent, -1);
-                        if (parent != NULL && strcmp (parent, CUSTOM_THEME_NAME) != 0) {
-                                gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
-                                g_free (parent);
-                                break;
-                        }
-                        g_free (parent);
-                } while (gtk_tree_model_iter_next (model, &iter));
-        }
+        /* FIXME: reset alert model */
 }
 
 static char *
@@ -354,132 +259,12 @@
         }
 }
 
-/* Functions to toggle whether the audible bell sound is editable */
-static gboolean
-audible_bell_foreach (GtkTreeModel *model,
-                      GtkTreePath  *path,
-                      GtkTreeIter  *iter,
-                      gpointer      data)
-{
-        int      type;
-        int      setting;
-        gboolean enabled = GPOINTER_TO_INT (data);
-
-        setting = enabled ? SOUND_BUILTIN : SOUND_OFF;
-
-        gtk_tree_model_get (model, iter, TYPE_COL, &type, -1);
-        if (type == SOUND_TYPE_AUDIO_BELL) {
-                gtk_tree_store_set (GTK_TREE_STORE (model),
-                                    iter,
-                                    SETTING_COL, setting,
-                                    HAS_PREVIEW_COL, enabled,
-                                    -1);
-                return TRUE;
-        }
-        return FALSE;
-}
-
-static void
-set_audible_bell_enabled (GvcSoundThemeChooser *chooser,
-                          gboolean              enabled)
-{
-        GtkTreeModel *model;
-
-        model = gtk_tree_view_get_model (GTK_TREE_VIEW (chooser->priv->treeview));
-        gtk_tree_model_foreach (model, audible_bell_foreach, GINT_TO_POINTER (enabled));
-}
-
-/* Functions to toggle whether the Input feedback sounds are editable */
-static gboolean
-input_feedback_foreach (GtkTreeModel *model,
-                        GtkTreePath  *path,
-                        GtkTreeIter  *iter,
-                        gpointer      data)
-{
-        int      type;
-        gboolean enabled = GPOINTER_TO_INT (data);
-
-        gtk_tree_model_get (model, iter, TYPE_COL, &type, -1);
-        if (type == SOUND_TYPE_FEEDBACK) {
-                gtk_tree_store_set (GTK_TREE_STORE (model), iter,
-                                    SENSITIVE_COL, enabled,
-                                    HAS_PREVIEW_COL, enabled,
-                                    -1);
-        }
-        return FALSE;
-}
-
 static void
 set_input_feedback_enabled (GvcSoundThemeChooser *chooser,
                             gboolean              enabled)
 {
-        GtkTreeModel *model;
-
         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (chooser->priv->click_feedback_button),
                                       enabled);
-
-        model = gtk_tree_view_get_model (GTK_TREE_VIEW (chooser->priv->treeview));
-        gtk_tree_model_foreach (model, input_feedback_foreach, GINT_TO_POINTER (enabled));
-}
-
-static int
-get_file_type (const char *sound_name,
-               char      **linked_name)
-{
-        char *name, *filename;
-
-        *linked_name = NULL;
-
-        name = g_strdup_printf ("%s.disabled", sound_name);
-        filename = custom_theme_dir_path (name);
-        g_free (name);
-
-        if (g_file_test (filename, G_FILE_TEST_IS_REGULAR) != FALSE) {
-                g_free (filename);
-                return SOUND_OFF;
-        }
-        g_free (filename);
-
-        /* We only check for .ogg files because those are the
-         * only ones we create */
-        name = g_strdup_printf ("%s.ogg", sound_name);
-        filename = custom_theme_dir_path (name);
-        g_free (name);
-
-        if (g_file_test (filename, G_FILE_TEST_IS_SYMLINK) != FALSE) {
-                *linked_name = g_file_read_link (filename, NULL);
-                g_free (filename);
-                return SOUND_CUSTOM;
-        }
-        g_free (filename);
-
-        return SOUND_BUILTIN;
-}
-
-static gboolean
-theme_changed_custom_init (GtkTreeModel *model,
-                           GtkTreePath *path,
-                           GtkTreeIter *iter,
-                           gpointer data)
-{
-        char **sound_names;
-
-        gtk_tree_model_get (model, iter, SOUND_NAMES_COL, &sound_names, -1);
-        if (sound_names != NULL) {
-                char *filename;
-                int type;
-
-                type = get_file_type (sound_names[0], &filename);
-
-                gtk_tree_store_set (GTK_TREE_STORE (model), iter,
-                                    SETTING_COL, type,
-                                    HAS_PREVIEW_COL, type != SOUND_OFF,
-                                    FILENAME_COL, filename,
-                                    -1);
-                g_strfreev (sound_names);
-                g_free (filename);
-        }
-        return FALSE;
 }
 
 static void
@@ -494,7 +279,7 @@
         client = gconf_client_get_default ();
 
         bell_enabled = gconf_client_get_bool (client, AUDIO_BELL_KEY, NULL);
-        set_audible_bell_enabled (chooser, bell_enabled);
+        //set_audible_bell_enabled (chooser, bell_enabled);
 
         feedback_enabled = gconf_client_get_bool (client, INPUT_SOUNDS_KEY, NULL);
         set_input_feedback_enabled (chooser, feedback_enabled);
@@ -510,14 +295,8 @@
 
         set_combox_for_theme_name (chooser, theme_name);
 
-        /* Setup the default values if we're using the custom theme */
-        if (theme_name != NULL && strcmp (theme_name, CUSTOM_THEME_NAME) == 0) {
-                GtkTreeModel *model;
-                model = gtk_tree_view_get_model (GTK_TREE_VIEW (chooser->priv->treeview));
-                gtk_tree_model_foreach (model,
-                                        theme_changed_custom_init,
-                                        NULL);
-        }
+        /* FIXME: update alerts */
+
         g_free (theme_name);
 
         g_object_unref (client);
@@ -599,643 +378,207 @@
 }
 
 static void
-play_sound_preview (GtkFileChooser *chooser,
-                    gpointer user_data)
+on_alert_toggled (GtkCellRendererToggle *renderer,
+                  char                  *path_str,
+                  GvcSoundThemeChooser  *chooser)
 {
-        char       *filename;
-        ca_context *ctx;
-
-        filename = gtk_file_chooser_get_preview_filename (GTK_FILE_CHOOSER (chooser));
-        if (filename == NULL) {
-                return;
-        }
-
-        ctx = ca_gtk_context_get ();
-        ca_gtk_play_for_widget (GTK_WIDGET (chooser), 0,
-                                CA_PROP_APPLICATION_NAME, _("Sound Preferences"),
-                                CA_PROP_MEDIA_FILENAME, filename,
-                                CA_PROP_EVENT_DESCRIPTION, _("Testing event sound"),
-                                CA_PROP_CANBERRA_CACHE_CONTROL, "never",
-#ifdef CA_PROP_CANBERRA_ENABLE
-                                CA_PROP_CANBERRA_ENABLE, "1",
-#endif
-                                NULL);
-        g_free (filename);
-}
-
-static char *
-get_sound_filename (GvcSoundThemeChooser *chooser)
-{
-        GtkWidget          *file_chooser;
-        GtkWidget          *toplevel;
-        GtkWindow          *parent;
-        int                 response;
-        char               *filename;
-        char               *path;
-        const char * const *data_dirs, *data_dir;
-        GtkFileFilter      *filter;
-        guint               i;
-
-	/* Try to get the parent window of the widget */
-	toplevel = gtk_widget_get_toplevel (GTK_WIDGET (chooser));
-	if (GTK_WIDGET_TOPLEVEL (toplevel) != FALSE)
-		parent = GTK_WINDOW (toplevel);
-	else
-		parent = NULL;
-
-        file_chooser = gtk_file_chooser_dialog_new (_("Select Sound File"),
-                                                    parent,
-                                                    GTK_FILE_CHOOSER_ACTION_OPEN,
-                                                    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-                                                    GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
-                                                    NULL);
-
-        gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (file_chooser), TRUE);
-        gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (file_chooser), FALSE);
-
-        filter = gtk_file_filter_new ();
-        gtk_file_filter_set_name (filter, _("Sound files"));
-        gtk_file_filter_add_mime_type (filter, "audio/x-vorbis+ogg");
-        gtk_file_filter_add_mime_type (filter, "audio/x-wav");
-        gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_chooser), filter);
-        gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (file_chooser), filter);
-
-        g_signal_connect (file_chooser, "update-preview",
-                          G_CALLBACK (play_sound_preview), NULL);
-
-        data_dirs = g_get_system_data_dirs ();
-        for (i = 0; data_dirs[i] != NULL; i++) {
-                path = g_build_filename (data_dirs[i], "sounds", NULL);
-                gtk_file_chooser_add_shortcut_folder (GTK_FILE_CHOOSER (file_chooser), path, NULL);
-                g_free (path);
-        }
-        data_dir = g_get_user_special_dir (G_USER_DIRECTORY_MUSIC);
-        if (data_dir != NULL)
-                gtk_file_chooser_add_shortcut_folder (GTK_FILE_CHOOSER (file_chooser), data_dir, NULL);
-
-        gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (file_chooser), SOUND_DATA_DIR);
-
-        response = gtk_dialog_run (GTK_DIALOG (file_chooser));
-        filename = NULL;
-        if (response == GTK_RESPONSE_ACCEPT)
-                filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (file_chooser));
-
-        gtk_widget_destroy (file_chooser);
-
-        return filename;
-}
-
-
-static gboolean
-count_customised_sounds (GtkTreeModel *model,
-                         GtkTreePath  *path,
-                         GtkTreeIter  *iter,
-                         int          *num_custom)
-{
-        int type;
-        int setting;
-
-        gtk_tree_model_get (model, iter, TYPE_COL, &type, SETTING_COL, &setting, -1);
-        if (setting == SOUND_OFF || setting == SOUND_CUSTOM || setting == SOUND_CUSTOM_OLD) {
-                (*num_custom)++;
-        }
-
-        return FALSE;
-}
-
-static gboolean
-save_sounds (GtkTreeModel *model,
-             GtkTreePath  *path,
-             GtkTreeIter  *iter,
-             gpointer      data)
-{
-        int    type;
-        int    setting;
-        char  *filename;
-        char **sounds;
-
-        gtk_tree_model_get (model, iter,
-                            TYPE_COL, &type,
-                            SETTING_COL, &setting,
-                            FILENAME_COL, &filename,
-                            SOUND_NAMES_COL, &sounds,
-                            -1);
-
-        if (setting == SOUND_BUILTIN) {
-                delete_old_files (sounds);
-                delete_disabled_files (sounds);
-        } else if (setting == SOUND_OFF) {
-                delete_old_files (sounds);
-                add_disabled_file (sounds);
-        } else if (setting == SOUND_CUSTOM || setting == SOUND_CUSTOM_OLD) {
-                delete_old_files (sounds);
-                delete_disabled_files (sounds);
-                add_custom_file (sounds, filename);
-        }
-        g_free (filename);
-        g_strfreev (sounds);
-
-        return FALSE;
-}
-
-static void
-save_custom_theme (GtkTreeModel *model,
-                   const char   *parent)
-{
-        GKeyFile *keyfile;
-        char     *data;
-        char     *path;
-
-        /* Create the custom directory */
-        path = custom_theme_dir_path (NULL);
-        g_mkdir_with_parents (path, 0755);
-        g_free (path);
-
-        /* Save the sounds themselves */
-        gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc) save_sounds, NULL);
-
-        /* Set the data for index.theme */
-        keyfile = g_key_file_new ();
-        g_key_file_set_string (keyfile, "Sound Theme", "Name", _("Custom"));
-        g_key_file_set_string (keyfile, "Sound Theme", "Inherits", parent);
-        g_key_file_set_string (keyfile, "Sound Theme", "Directories", ".");
-        data = g_key_file_to_data (keyfile, NULL, NULL);
-        g_key_file_free (keyfile);
-
-        /* Save the index.theme */
-        path = custom_theme_dir_path ("index.theme");
-        g_file_set_contents (path, data, -1, NULL);
-        g_free (path);
-        g_free (data);
-
-        custom_theme_update_time ();
-}
-
-static void
-dump_theme (GvcSoundThemeChooser *chooser)
-{
-        int           num_custom;
         GtkTreeModel *model;
         GtkTreeIter   iter;
-        char         *parent;
+        GtkTreePath  *path;
+        gboolean      toggled;
+        char         *id;
 
-        num_custom = 0;
         model = gtk_tree_view_get_model (GTK_TREE_VIEW (chooser->priv->treeview));
-        gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc) count_customised_sounds, &num_custom);
 
-        g_debug ("%d customised sounds", num_custom);
-
-        model = gtk_combo_box_get_model (GTK_COMBO_BOX (chooser->priv->combo_box));
-        /* Get the current theme's name, and set the parent */
-        if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (chooser->priv->combo_box), &iter) == FALSE)
-                return;
-
-        if (num_custom == 0) {
-                gtk_tree_model_get (model, &iter, THEME_PARENT_ID_COL, &parent, -1);
-                if (parent != NULL) {
-                        set_combox_for_theme_name (chooser, parent);
-                        g_free (parent);
-                }
-                gtk_tree_model_get_iter_first (model, &iter);
-                do {
-                        gtk_tree_model_get (model, &iter, THEME_PARENT_ID_COL, &parent, -1);
-                        if (parent != NULL && strcmp (parent, CUSTOM_THEME_NAME) != 0) {
-                                gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
-                                break;
-                        }
-                } while (gtk_tree_model_iter_next (model, &iter));
-
-                delete_custom_theme_dir ();
-        } else {
-                gtk_tree_model_get (model, &iter, THEME_IDENTIFIER_COL, &parent, -1);
-                if (strcmp (parent, CUSTOM_THEME_NAME) != 0) {
-                        gtk_list_store_insert_with_values (GTK_LIST_STORE (model), NULL, G_MAXINT,
-                                                           THEME_DISPLAY_COL, _("Custom"),
-                                                           THEME_IDENTIFIER_COL, CUSTOM_THEME_NAME,
-                                                           THEME_PARENT_ID_COL, parent,
-                                                           -1);
-                } else {
-                        g_free (parent);
-                        gtk_tree_model_get (model, &iter, THEME_PARENT_ID_COL, &parent, -1);
-                }
-
-                g_debug ("The parent theme is: %s", parent);
-                model = gtk_tree_view_get_model (GTK_TREE_VIEW (chooser->priv->treeview));
-                save_custom_theme (model, parent);
-                g_free (parent);
-
-                set_combox_for_theme_name (chooser, CUSTOM_THEME_NAME);
-        }
-}
-
-static void
-on_setting_column_edited (GtkCellRendererText  *renderer,
-                          char                 *path,
-                          char                 *new_text,
-                          GvcSoundThemeChooser *chooser)
-{
-        GtkTreeModel *model;
-        GtkTreeModel *tree_model;
-        GtkTreeIter   iter;
-        GtkTreeIter   tree_iter;
-        SoundType     type;
-        char         *text;
-        char         *old_filename;
-        int           setting;
-
-        if (new_text == NULL) {
-                return;
-        }
-
-        g_object_get (renderer,
-                      "model", &model,
-                      NULL);
-
-        tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (chooser->priv->treeview));
-        if (gtk_tree_model_get_iter_from_string (tree_model, &tree_iter, path) == FALSE)
-                return;
+        path = gtk_tree_path_new_from_string (path_str);
+        gtk_tree_model_get_iter (model, &iter, path);
+        gtk_tree_path_free (path);
 
-        gtk_tree_model_get (tree_model, &tree_iter,
-                            TYPE_COL, &type,
-                            FILENAME_COL, &old_filename,
+        id = NULL;
+        gtk_tree_model_get (model, &iter,
+                            ALERT_IDENTIFIER_COL, &id,
+                            ALERT_ACTIVE_COL, &toggled,
                             -1);
 
-        gtk_tree_model_get_iter_first (model, &iter);
-        do {
-                int cmp;
-
-                gtk_tree_model_get (model, &iter,
-                                    0, &text,
-                                    1, &setting,
-                                    -1);
-                cmp = g_utf8_collate (text, new_text);
-                g_free (text);
-
-                if (cmp != 0) {
-                        continue;
-                }
-
-                if (type == SOUND_TYPE_NORMAL
-                    || type == SOUND_TYPE_FEEDBACK
-                    || type == SOUND_TYPE_AUDIO_BELL) {
-
-                        if (setting == SOUND_CUSTOM
-                            || (setting == SOUND_CUSTOM_OLD
-                                && old_filename == NULL)) {
-
-                                char *filename = get_sound_filename (chooser);
-
-                                if (filename == NULL) {
-                                        break;
-                                }
-                                gtk_tree_store_set (GTK_TREE_STORE (tree_model),
-                                                    &tree_iter,
-                                                    SETTING_COL, setting,
-                                                    HAS_PREVIEW_COL, setting != SOUND_OFF,
-                                                    FILENAME_COL, filename,
-                                                    -1);
-                                g_free (filename);
-                        } else if (setting == SOUND_CUSTOM_OLD) {
-                                gtk_tree_store_set (GTK_TREE_STORE (tree_model),
-                                                    &tree_iter,
-                                                    SETTING_COL, setting,
-                                                    HAS_PREVIEW_COL, setting != SOUND_OFF,
-                                                    FILENAME_COL, old_filename,
-                                                    -1);
-                        } else {
-                                gtk_tree_store_set (GTK_TREE_STORE (tree_model),
-                                                    &tree_iter,
-                                                    SETTING_COL, setting,
-                                                    HAS_PREVIEW_COL, setting != SOUND_OFF,
-                                                    -1);
-                        }
-
-                        g_debug ("Something changed, dump theme");
-                        dump_theme (chooser);
-
-                        break;
-                }
-
-                g_assert_not_reached ();
-
-        } while (gtk_tree_model_iter_next (model, &iter));
-
-        g_free (old_filename);
-}
-
-static void
-fill_custom_model (GtkListStore *store,
-                   const char   *prev_filename)
-{
-        GtkTreeIter iter;
-
-        gtk_list_store_clear (store);
-
-        if (prev_filename != NULL) {
-                char *display;
-                display = g_filename_display_basename (prev_filename);
-                gtk_list_store_insert_with_values (store, &iter, G_MAXINT,
-                                                   0, display,
-                                                   1, SOUND_CUSTOM_OLD,
-                                                   -1);
-                g_free (display);
+        toggled ^= 1;
+        if (toggled) {
+                g_debug ("Default input selected: %s", id);
         }
 
-        gtk_list_store_insert_with_values (store, &iter, G_MAXINT,
-                                           0, _("Default"),
-                                           1, SOUND_BUILTIN,
-                                           -1);
-        gtk_list_store_insert_with_values (store, &iter, G_MAXINT,
-                                           0, _("Disabled"),
-                                           1, SOUND_OFF,
-                                           -1);
-        gtk_list_store_insert_with_values (store, &iter, G_MAXINT,
-                                           0, _("Custom..."),
-                                           1, SOUND_CUSTOM, -1);
+        g_free (id);
 }
 
 static void
-on_combobox_editing_started (GtkCellRenderer      *renderer,
-                             GtkCellEditable      *editable,
-                             gchar                *path,
-                             GvcSoundThemeChooser *chooser)
+play_preview_for_path (GvcSoundThemeChooser *chooser,
+                       GtkTreePath          *path)
 {
         GtkTreeModel *model;
-        GtkTreeModel *store;
         GtkTreeIter   iter;
-        SoundType     type;
+        char         *id;
         char         *filename;
-
-        model = gtk_tree_view_get_model (GTK_TREE_VIEW (chooser->priv->treeview));
-        if (gtk_tree_model_get_iter_from_string (model, &iter, path) == FALSE) {
-                return;
-        }
-
-        gtk_tree_model_get (model, &iter, TYPE_COL, &type, FILENAME_COL, &filename, -1);
-        g_object_get (renderer, "model", &store, NULL);
-        fill_custom_model (GTK_LIST_STORE (store), filename);
-        g_free (filename);
-}
-
-static gboolean
-play_sound_at_path (GtkWidget         *tree_view,
-                    GtkTreePath       *path)
-{
-        GtkTreeModel *model;
-        GtkTreeIter   iter;
-        char        **sound_names;
-        gboolean      sensitive;
         ca_context   *ctx;
 
-        model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
+        model = gtk_tree_view_get_model (GTK_TREE_VIEW (chooser->priv->treeview));
         if (gtk_tree_model_get_iter (model, &iter, path) == FALSE) {
-                return FALSE;
+                return;
         }
 
+        id = NULL;
         gtk_tree_model_get (model, &iter,
-                            SOUND_NAMES_COL, &sound_names,
-                            SENSITIVE_COL, &sensitive,
+                            ALERT_IDENTIFIER_COL, &id,
                             -1);
-        if (!sensitive || sound_names == NULL) {
-                return FALSE;
+        if (id == NULL) {
+                return;
         }
 
+        filename = NULL;
+
+        g_debug ("playing: %s", id);
         ctx = ca_gtk_context_get ();
-        ca_gtk_play_for_widget (GTK_WIDGET (tree_view), 0,
-                                CA_PROP_APPLICATION_NAME, _("Sound Preferences"),
-                                CA_PROP_EVENT_ID, sound_names[0],
-                                CA_PROP_EVENT_DESCRIPTION, _("Testing event sound"),
-                                CA_PROP_CANBERRA_CACHE_CONTROL, "never",
+        if (strcmp (id, DEFAULT_ALERT_ID) == 0) {
+                ca_gtk_play_for_widget (GTK_WIDGET (chooser), 0,
+                                        CA_PROP_APPLICATION_NAME, _("Sound Preferences"),
+                                        CA_PROP_EVENT_ID, "bell-window-system",
+                                        CA_PROP_EVENT_DESCRIPTION, _("Testing event sound"),
+                                        CA_PROP_CANBERRA_CACHE_CONTROL, "never",
 #ifdef CA_PROP_CANBERRA_ENABLE
-                                CA_PROP_CANBERRA_ENABLE, "1",
+                                        CA_PROP_CANBERRA_ENABLE, "1",
 #endif
-                                NULL);
+                                        NULL);
+        } else {
+                ca_gtk_play_for_widget (GTK_WIDGET (chooser), 0,
+                                        CA_PROP_APPLICATION_NAME, _("Sound Preferences"),
+                                        CA_PROP_MEDIA_FILENAME, filename,
+                                        CA_PROP_EVENT_DESCRIPTION, _("Testing event sound"),
+                                        CA_PROP_CANBERRA_CACHE_CONTROL, "never",
+#ifdef CA_PROP_CANBERRA_ENABLE
+                                        CA_PROP_CANBERRA_ENABLE, "1",
+#endif
+                                        NULL);
 
-        g_strfreev (sound_names);
+        }
 
-        return TRUE;
+        g_free (id);
 }
 
 static void
-setting_set_func (GtkTreeViewColumn *tree_column,
-                  GtkCellRenderer   *cell,
-                  GtkTreeModel      *model,
-                  GtkTreeIter       *iter,
-                  gpointer           data)
-{
-        int       setting;
-        char     *filename;
-        SoundType type;
-
-        gtk_tree_model_get (model, iter,
-                            SETTING_COL, &setting,
-                            FILENAME_COL, &filename,
-                            TYPE_COL, &type,
-                            -1);
-
-        if (setting == SOUND_UNSET) {
-                g_object_set (cell,
-                              "visible", FALSE,
-                              NULL);
-                g_free (filename);
-                return;
-        }
+on_treeview_row_activated (GtkTreeView          *treeview,
+                           GtkTreePath          *path,
+                           GtkTreeViewColumn    *column,
+                           GvcSoundThemeChooser *chooser)
+{
+        play_preview_for_path (chooser, path);
+}
 
-        if (setting == SOUND_OFF) {
-                g_object_set (cell,
-                              "text", _("Disabled"),
-                              NULL);
-        } else if (setting == SOUND_BUILTIN) {
-                g_object_set (cell,
-                              "text", _("Default"),
-                              NULL);
-        } else if (setting == SOUND_CUSTOM || setting == SOUND_CUSTOM_OLD) {
-                char *display;
+static void
+on_treeview_selection_changed (GtkTreeSelection     *selection,
+                               GvcSoundThemeChooser *chooser)
+{
+        GList        *paths;
+        GtkTreeModel *model;
+        GtkTreePath  *path;
 
-                display = g_filename_display_basename (filename);
-                g_object_set (cell,
-                              "text", display,
-                              NULL);
-                g_free (display);
+        if (chooser->priv->treeview == NULL) {
+                return;
         }
 
-        g_free (filename);
-}
-
-typedef GtkCellRendererPixbuf      ActivatableCellRendererPixbuf;
-typedef GtkCellRendererPixbufClass ActivatableCellRendererPixbufClass;
-
-GType activatable_cell_renderer_pixbuf_get_type (void);
-#define ACTIVATABLE_TYPE_CELL_RENDERER_PIXBUF (activatable_cell_renderer_pixbuf_get_type ())
-G_DEFINE_TYPE (ActivatableCellRendererPixbuf, activatable_cell_renderer_pixbuf, GTK_TYPE_CELL_RENDERER_PIXBUF);
+        model = gtk_tree_view_get_model (GTK_TREE_VIEW (chooser->priv->treeview));
 
-static gboolean
-activatable_cell_renderer_pixbuf_activate (GtkCellRenderer      *cell,
-                                           GdkEvent             *event,
-                                           GtkWidget            *widget,
-                                           const gchar          *path_string,
-                                           GdkRectangle         *background_area,
-                                           GdkRectangle         *cell_area,
-                                           GtkCellRendererState  flags)
-{
-        GtkTreePath *path;
-        gboolean     res;
+        paths = gtk_tree_selection_get_selected_rows (selection, &model);
 
-        g_debug ("Activating pixbuf");
+        path = paths->data;
 
-        path = gtk_tree_path_new_from_string (path_string);
-        res = play_sound_at_path (widget, path);
-        gtk_tree_path_free (path);
+        play_preview_for_path (chooser, path);
 
-        return res;
+        g_list_foreach (paths, (GFunc)gtk_tree_path_free, NULL);
+        g_list_free (paths);
 }
 
-static void
-activatable_cell_renderer_pixbuf_init (ActivatableCellRendererPixbuf *cell)
+static GtkWidget *
+create_alert_treeview (GvcSoundThemeChooser *chooser)
 {
-}
+        GHashTable           *hash;
+        GtkListStore         *store;
+        GtkWidget            *treeview;
+        GtkCellRenderer      *renderer;
+        GtkTreeViewColumn    *column;
+        GtkTreeSelection     *selection;
 
-static void
-activatable_cell_renderer_pixbuf_class_init (ActivatableCellRendererPixbufClass *class)
-{
-        GtkCellRendererClass *cell_class;
+        treeview = gtk_tree_view_new ();
+        g_signal_connect (treeview,
+                          "row-activated",
+                          G_CALLBACK (on_treeview_row_activated),
+                          chooser);
+
+        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
+        gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+        g_signal_connect (selection,
+                          "changed",
+                          G_CALLBACK (on_treeview_selection_changed),
+                          chooser);
 
-        cell_class = GTK_CELL_RENDERER_CLASS (class);
+        /* Add the theme names and their display name to a hash table,
+         * makes it easy to avoid duplicate themes */
+        hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 
-        cell_class->activate = activatable_cell_renderer_pixbuf_activate;
-}
 
-static void
-setup_theme_custom_selector (GvcSoundThemeChooser *chooser,
-                             gboolean              have_xkb )
-{
-        GtkTreeStore      *store;
-        GtkTreeModel      *custom_model;
-        GtkTreeViewColumn *column;
-        GtkCellRenderer   *renderer;
-        GtkTreeIter        iter;
-        GtkTreeIter        parent;
-        GConfClient       *client;
-        CategoryType       type;
-        guint              i;
+        /* FIXME: need to read this from a set of deskop files */
 
-        client = gconf_client_get_default ();
 
-        /* Set up the model for the custom view */
-        store = gtk_tree_store_new (NUM_COLS,
+        /* Setup the tree model, 3 columns:
+         * - display name
+         * - sound id
+         * - sound type
+         */
+        store = gtk_list_store_new (ALERT_NUM_COLS,
+                                    G_TYPE_STRING,
                                     G_TYPE_STRING,
-                                    G_TYPE_INT,
-                                    G_TYPE_INT,
-                                    G_TYPE_BOOLEAN,
-                                    G_TYPE_BOOLEAN,
                                     G_TYPE_STRING,
-                                    G_TYPE_STRV);
+                                    G_TYPE_BOOLEAN);
 
-        /* The first column with the categories/sound names */
-        renderer = gtk_cell_renderer_text_new ();
-        column = gtk_tree_view_column_new_with_attributes ("Display", renderer,
-                                                           "text", DISPLAY_COL,
-                                                           "sensitive", SENSITIVE_COL,
-							   "ellipsize", PANGO_ELLIPSIZE_START,
-							   "ellipsize-set", TRUE,
-							   NULL);
-	g_object_set (G_OBJECT (column), "expand", TRUE, NULL);
+        gtk_list_store_insert_with_values (store,
+                                           NULL,
+                                           G_MAXINT,
+                                           ALERT_IDENTIFIER_COL, DEFAULT_ALERT_ID,
+                                           ALERT_DISPLAY_COL, _("Default"),
+                                           ALERT_SOUND_TYPE_COL, _("From theme"),
+                                           ALERT_ACTIVE_COL, TRUE,
+                                           -1);
 
-        gtk_tree_view_append_column (GTK_TREE_VIEW (chooser->priv->treeview), column);
+        g_hash_table_foreach (hash, (GHFunc) add_theme_to_store, store);
+        g_hash_table_destroy (hash);
 
-        /* The 2nd column with the sound settings */
-        renderer = gtk_cell_renderer_combo_new ();
-        g_signal_connect (renderer,
-                          "edited",
-                          G_CALLBACK (on_setting_column_edited),
-                          chooser);
+        gtk_tree_view_set_model (GTK_TREE_VIEW (treeview),
+                                 GTK_TREE_MODEL (store));
+
+        renderer = gtk_cell_renderer_toggle_new ();
+        gtk_cell_renderer_toggle_set_radio (GTK_CELL_RENDERER_TOGGLE (renderer),
+                                            TRUE);
+        column = gtk_tree_view_column_new_with_attributes (NULL,
+                                                           renderer,
+                                                           "active", ALERT_ACTIVE_COL,
+                                                           NULL);
+        gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
         g_signal_connect (renderer,
-                          "editing-started",
-                          G_CALLBACK (on_combobox_editing_started),
+                          "toggled",
+                          G_CALLBACK (on_alert_toggled),
                           chooser);
-        custom_model = GTK_TREE_MODEL (gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT));
-        fill_custom_model (GTK_LIST_STORE (custom_model), NULL);
 
-        g_object_set (renderer,
-                      "model", custom_model,
-                      "has-entry", FALSE,
-                      "editable", TRUE,
-                      "text-column", 0,
-                      NULL);
-        column = gtk_tree_view_column_new_with_attributes ("Setting", renderer,
-                                                           "editable", SENSITIVE_COL,
-                                                           "sensitive", SENSITIVE_COL,
-                                                           "visible", TRUE,
+        renderer = gtk_cell_renderer_text_new ();
+        column = gtk_tree_view_column_new_with_attributes (_("Name"),
+                                                           renderer,
+                                                           "text", ALERT_DISPLAY_COL,
                                                            NULL);
-        gtk_tree_view_append_column (GTK_TREE_VIEW (chooser->priv->treeview), column);
-        gtk_tree_view_column_set_cell_data_func (column, renderer, setting_set_func, NULL, NULL);
+        gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
 
-        /* The 3rd column with the preview pixbuf */
-        renderer = g_object_new (ACTIVATABLE_TYPE_CELL_RENDERER_PIXBUF, NULL);
-        g_object_set (renderer,
-                      "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE,
-                      "icon-name", "media-playback-start",
-                      "stock-size", GTK_ICON_SIZE_MENU,
-                      NULL);
-        column = gtk_tree_view_column_new_with_attributes ("Preview", renderer,
-                                                           "visible", HAS_PREVIEW_COL,
+        renderer = gtk_cell_renderer_text_new ();
+        column = gtk_tree_view_column_new_with_attributes (_("Type"),
+                                                           renderer,
+                                                           "text", ALERT_SOUND_TYPE_COL,
                                                            NULL);
-        gtk_tree_view_append_column (GTK_TREE_VIEW (chooser->priv->treeview), column);
-        g_object_set_data (G_OBJECT (chooser->priv->treeview), "preview-column", column);
-
-        gtk_tree_view_set_model (GTK_TREE_VIEW (chooser->priv->treeview), GTK_TREE_MODEL (store));
-        gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (chooser->priv->treeview), FALSE);
-
-        /* Fill in the model */
-        type = CATEGORY_INVALID;
-
-        for (i = 0; ; i++) {
-                GtkTreeIter *_parent;
-
-                if (sounds[i].category == -1) {
-                        break;
-                }
-
-                /* Is it a new type of sound? */
-                if (sounds[i].category == type
-                    && type != CATEGORY_INVALID
-                    && type != CATEGORY_BELL) {
-                        _parent = &parent;
-                } else {
-                        _parent = NULL;
-                }
-
-                if (sounds[i].type != -1) {
-                        gtk_tree_store_insert_with_values (store, &iter, _parent, G_MAXINT,
-                                                           DISPLAY_COL, g_dpgettext2 (NULL, "Sound event", sounds[i].display_name),
-                                                           SETTING_COL, SOUND_BUILTIN,
-                                                           TYPE_COL, sounds[i].type,
-                                                           SOUND_NAMES_COL, sounds[i].names,
-                                                           HAS_PREVIEW_COL, TRUE,
-                                                           SENSITIVE_COL, TRUE,
-                                                           -1);
-                } else {
-                        /* Category */
-                        gtk_tree_store_insert_with_values (store, &iter, _parent, G_MAXINT,
-                                                           DISPLAY_COL, g_dpgettext2 (NULL, "Sound event", sounds[i].display_name),
-                                                           SETTING_COL, SOUND_UNSET,
-                                                           TYPE_COL, sounds[i].type,
-                                                           SENSITIVE_COL, TRUE,
-                                                           HAS_PREVIEW_COL, FALSE,
-                                                           -1);
-                }
-
-                /* If we didn't set a parent already, set one in case we need it later */
-                if (_parent == NULL) {
-                        parent = iter;
-                }
-                type = sounds[i].category;
-        }
-
-        gtk_tree_view_expand_all (GTK_TREE_VIEW (chooser->priv->treeview));
+        gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
 
-
-        g_object_unref (client);
+        return treeview;
 }
 
 static GObject *
@@ -1251,7 +594,6 @@
         self = GVC_SOUND_THEME_CHOOSER (object);
 
         setup_theme_selector (self);
-        setup_theme_custom_selector (self, TRUE);
 
         update_theme (self);
 
@@ -1312,16 +654,6 @@
 }
 
 static void
-on_treeview_row_activated (GtkTreeView          *treeview,
-                           GtkTreePath          *path,
-                           GtkTreeViewColumn    *column,
-                           GvcSoundThemeChooser *chooser)
-{
-        g_debug ("row activated");
-        play_sound_at_path (GTK_WIDGET (treeview), path);
-}
-
-static void
 constrain_list_size (GtkWidget      *widget,
                      GtkRequisition *requisition,
                      GtkWidget      *to_size)
@@ -1369,14 +701,12 @@
 
         client = gconf_client_get_default ();
 
-        chooser->priv->selection_box = box = gtk_vbox_new (FALSE, 0);
+        chooser->priv->selection_box = box = gtk_frame_new (_("Choose an alert sound:"));
+        gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE);
+
         gtk_box_pack_start (GTK_BOX (chooser), box, TRUE, TRUE, 0);
 
-        chooser->priv->treeview = gtk_tree_view_new ();
-        g_signal_connect (chooser->priv->treeview,
-                          "row-activated",
-                          G_CALLBACK (on_treeview_row_activated),
-                          chooser);
+        chooser->priv->treeview = create_alert_treeview (chooser);
 
         scrolled_window = gtk_scrolled_window_new (NULL, NULL);
         setup_list_size_constraint (scrolled_window, chooser->priv->treeview);
@@ -1392,7 +722,7 @@
         chooser->priv->click_feedback_button = gtk_check_button_new_with_mnemonic (_("Enable window and button sounds"));
         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (chooser->priv->click_feedback_button),
                                       gconf_client_get_bool (client, INPUT_SOUNDS_KEY, NULL));
-        gtk_box_pack_start (GTK_BOX (box),
+        gtk_box_pack_start (GTK_BOX (chooser),
                             chooser->priv->click_feedback_button,
                             FALSE, FALSE, 0);
         g_signal_connect (chooser->priv->click_feedback_button,

Added: trunk/gnome-volume-control/src/gvc-sound-theme-editor.c
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/gvc-sound-theme-editor.c	Sun Dec 21 20:15:15 2008
@@ -0,0 +1,1396 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Bastien Nocera <hadess hadess net>
+ * Copyright (C) 2008 William Jon McCann
+ *
+ * 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
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <canberra-gtk.h>
+
+#include <gconf/gconf-client.h>
+
+#include "gvc-sound-theme-editor.h"
+#include "sound-theme-file-utils.h"
+
+#define GVC_SOUND_THEME_EDITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_SOUND_THEME_EDITOR, GvcSoundThemeEditorPrivate))
+
+struct GvcSoundThemeEditorPrivate
+{
+        GtkWidget *treeview;
+        GtkWidget *theme_box;
+        GtkWidget *selection_box;
+        GtkWidget *click_feedback_button;
+};
+
+static void     gvc_sound_theme_editor_class_init (GvcSoundThemeEditorClass *klass);
+static void     gvc_sound_theme_editor_init       (GvcSoundThemeEditor      *sound_theme_editor);
+static void     gvc_sound_theme_editor_finalize   (GObject                  *object);
+
+G_DEFINE_TYPE (GvcSoundThemeEditor, gvc_sound_theme_editor, GTK_TYPE_VBOX)
+
+typedef enum {
+        CATEGORY_INVALID,
+        CATEGORY_BELL,
+        CATEGORY_WINDOWS_BUTTONS,
+        CATEGORY_DESKTOP,
+        CATEGORY_ALERTS,
+        NUM_CATEGORIES
+} CategoryType;
+
+typedef enum {
+        SOUND_TYPE_NORMAL,
+        SOUND_TYPE_AUDIO_BELL,
+        SOUND_TYPE_FEEDBACK
+} SoundType;
+
+static struct {
+        CategoryType category;
+        SoundType    type;
+        const char  *display_name;
+        const char  *names[6];
+} sounds[20] = {
+        /* Bell */
+        { CATEGORY_BELL, SOUND_TYPE_AUDIO_BELL, NC_("Sound event", "Alert sound"), { "bell-terminal", "bell-window-system", NULL } },
+        /* Windows and buttons */
+        { CATEGORY_WINDOWS_BUTTONS, -1, NC_("Sound event", "Windows and Buttons"), { NULL } },
+        { CATEGORY_WINDOWS_BUTTONS, SOUND_TYPE_FEEDBACK, NC_("Sound event", "Button clicked"), { "button-pressed", "menu-click", "menu-popup", "menu-popdown", "menu-replace", NULL } },
+        { CATEGORY_WINDOWS_BUTTONS, SOUND_TYPE_FEEDBACK, NC_("Sound event", "Toggle button clicked"), { "button-toggle-off", "button-toggle-on", NULL } },
+        { CATEGORY_WINDOWS_BUTTONS, SOUND_TYPE_FEEDBACK, NC_("Sound event", "Window maximized"), { "window-maximized", NULL } },
+        { CATEGORY_WINDOWS_BUTTONS, SOUND_TYPE_FEEDBACK, NC_("Sound event", "Window unmaximized"), { "window-unmaximized", NULL } },
+        { CATEGORY_WINDOWS_BUTTONS, SOUND_TYPE_FEEDBACK, NC_("Sound event", "Window minimised"), { "window-minimized", NULL } },
+        /* Desktop */
+        { CATEGORY_DESKTOP, -1, NC_("Sound event", "Desktop"), { NULL } },
+        { CATEGORY_DESKTOP, SOUND_TYPE_NORMAL, NC_("Sound event", "Login"), { "desktop-login", NULL } },
+        { CATEGORY_DESKTOP, SOUND_TYPE_NORMAL, NC_("Sound event", "Logout"), { "desktop-logout", NULL } },
+        { CATEGORY_DESKTOP, SOUND_TYPE_NORMAL, NC_("Sound event", "New e-mail"), { "message-new-email", NULL } },
+        { CATEGORY_DESKTOP, SOUND_TYPE_NORMAL, NC_("Sound event", "Empty trash"), { "trash-empty", NULL } },
+        { CATEGORY_DESKTOP, SOUND_TYPE_NORMAL, NC_("Sound event", "Long action completed (download, CD burning, etc.)"), { "complete-copy", "complete-download", "complete-media-burn", "complete-media-rip", "complete-scan", NULL } },
+        /* Alerts? */
+        { CATEGORY_ALERTS, -1, NC_("Sound event", "Alerts"), { NULL } },
+        { CATEGORY_ALERTS, SOUND_TYPE_NORMAL, NC_("Sound event", "Information or question"), { "dialog-information", "dialog-question", NULL } },
+        { CATEGORY_ALERTS, SOUND_TYPE_NORMAL, NC_("Sound event", "Warning"), { "dialog-warning", NULL } },
+        { CATEGORY_ALERTS, SOUND_TYPE_NORMAL, NC_("Sound event", "Error"), { "dialog-error", NULL } },
+        { CATEGORY_ALERTS, SOUND_TYPE_NORMAL, NC_("Sound event", "Battery warning"), { "power-unplug-battery-low", "battery-low", "battery-caution", NULL } },
+        /* Finish off */
+        { -1, -1, NULL, { NULL } }
+};
+
+#define KEY_SOUNDS_DIR             "/desktop/gnome/sound"
+#define EVENT_SOUNDS_KEY           KEY_SOUNDS_DIR "/event_sounds"
+#define INPUT_SOUNDS_KEY           KEY_SOUNDS_DIR "/input_feedback_sounds"
+#define SOUND_THEME_KEY            KEY_SOUNDS_DIR "/theme_name"
+#define KEY_METACITY_DIR           "/apps/metacity/general"
+#define AUDIO_BELL_KEY             KEY_METACITY_DIR "/audible_bell"
+
+#define CUSTOM_THEME_NAME       "__custom"
+#define NO_SOUNDS_THEME_NAME    "__no_sounds"
+#define PREVIEW_BUTTON_XPAD     5
+
+enum {
+        THEME_DISPLAY_COL,
+        THEME_IDENTIFIER_COL,
+        THEME_PARENT_ID_COL,
+        THEME_NUM_COLS
+};
+
+enum {
+        SOUND_UNSET,
+        SOUND_OFF,
+        SOUND_BUILTIN,
+        SOUND_CUSTOM,
+        SOUND_CUSTOM_OLD
+};
+
+enum {
+        DISPLAY_COL,
+        SETTING_COL,
+        TYPE_COL,
+        SENSITIVE_COL,
+        HAS_PREVIEW_COL,
+        FILENAME_COL,
+        SOUND_NAMES_COL,
+        NUM_COLS
+};
+
+static gboolean
+theme_changed_custom_reinit (GtkTreeModel *model,
+                             GtkTreePath  *path,
+                             GtkTreeIter  *iter,
+                             gpointer      data)
+{
+        int      type;
+        gboolean sensitive;
+
+        gtk_tree_model_get (model,
+                            iter,
+                            TYPE_COL, &type,
+                            SENSITIVE_COL, &sensitive, -1);
+        if (type != -1) {
+                gtk_tree_store_set (GTK_TREE_STORE (model), iter,
+                                    SETTING_COL, SOUND_BUILTIN,
+                                    HAS_PREVIEW_COL, sensitive,
+                                    -1);
+        }
+        return FALSE;
+}
+
+static void
+on_theme_changed ()
+{
+        /* Don't reinit a custom theme */
+        if (strcmp (theme_name, CUSTOM_THEME_NAME) != 0) {
+                model = gtk_tree_view_get_model (GTK_TREE_VIEW (editor->priv->treeview));
+                gtk_tree_model_foreach (model, theme_changed_custom_reinit, NULL);
+
+                /* Delete the custom dir */
+                delete_custom_theme_dir ();
+
+                /* And the combo box entry */
+                model = gtk_combo_box_get_model (GTK_COMBO_BOX (editor->priv->combo_box));
+                gtk_tree_model_get_iter_first (model, &iter);
+                do {
+                        char *parent;
+                        gtk_tree_model_get (model, &iter, THEME_PARENT_ID_COL, &parent, -1);
+                        if (parent != NULL && strcmp (parent, CUSTOM_THEME_NAME) != 0) {
+                                gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+                                g_free (parent);
+                                break;
+                        }
+                        g_free (parent);
+                } while (gtk_tree_model_iter_next (model, &iter));
+        }
+}
+
+static char *
+load_index_theme_name (const char *index,
+                       char      **parent)
+{
+        GKeyFile *file;
+        char *indexname = NULL;
+        gboolean hidden;
+
+        file = g_key_file_new ();
+        if (g_key_file_load_from_file (file, index, G_KEY_FILE_KEEP_TRANSLATIONS, NULL) == FALSE) {
+                g_key_file_free (file);
+                return NULL;
+        }
+        /* Don't add hidden themes to the list */
+        hidden = g_key_file_get_boolean (file, "Sound Theme", "Hidden", NULL);
+        if (!hidden) {
+                indexname = g_key_file_get_locale_string (file,
+                                                          "Sound Theme",
+                                                          "Name",
+                                                          NULL,
+                                                          NULL);
+
+                /* Save the parent theme, if there's one */
+                if (parent != NULL) {
+                        *parent = g_key_file_get_string (file,
+                                                         "Sound Theme",
+                                                         "Inherits",
+                                                         NULL);
+                }
+        }
+
+        g_key_file_free (file);
+        return indexname;
+}
+
+static void
+sound_theme_in_dir (GHashTable *hash,
+                    const char *dir)
+{
+        GDir *d;
+        const char *name;
+
+        d = g_dir_open (dir, 0, NULL);
+        if (d == NULL) {
+                return;
+        }
+
+        while ((name = g_dir_read_name (d)) != NULL) {
+                char *dirname, *index, *indexname;
+
+                /* Look for directories */
+                dirname = g_build_filename (dir, name, NULL);
+                if (g_file_test (dirname, G_FILE_TEST_IS_DIR) == FALSE) {
+                        g_free (dirname);
+                        continue;
+                }
+
+                /* Look for index files */
+                index = g_build_filename (dirname, "index.theme", NULL);
+                g_free (dirname);
+
+                /* Check the name of the theme in the index.theme file */
+                indexname = load_index_theme_name (index, NULL);
+                g_free (index);
+                if (indexname == NULL) {
+                        continue;
+                }
+
+                g_hash_table_insert (hash, g_strdup (name), indexname);
+        }
+
+        g_dir_close (d);
+}
+
+static void
+add_theme_to_store (const char   *key,
+                    const char   *value,
+                    GtkListStore *store)
+{
+        char *parent;
+
+        parent = NULL;
+
+        /* Get the parent, if we're checking the custom theme */
+        if (strcmp (key, CUSTOM_THEME_NAME) == 0) {
+                char *name, *path;
+
+                path = custom_theme_dir_path ("index.theme");
+                name = load_index_theme_name (path, &parent);
+                g_free (name);
+                g_free (path);
+        }
+        gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
+                                           THEME_DISPLAY_COL, value,
+                                           THEME_IDENTIFIER_COL, key,
+                                           THEME_PARENT_ID_COL, parent,
+                                           -1);
+        g_free (parent);
+}
+
+static void
+set_theme_name (GvcSoundThemeEditor *editor,
+                const char           *name)
+{
+        GConfClient *client;
+
+        g_debug ("setting theme %s", name ? name : "(null)");
+
+        /* If the name is empty, use "freedesktop" */
+        if (name == NULL || *name == '\0') {
+                name = "freedesktop";
+        }
+
+        client = gconf_client_get_default ();
+        gconf_client_set_string (client, SOUND_THEME_KEY, theme_name, NULL);
+        g_object_unref (client);
+}
+
+/* Functions to toggle whether the audible bell sound is editable */
+static gboolean
+audible_bell_foreach (GtkTreeModel *model,
+                      GtkTreePath  *path,
+                      GtkTreeIter  *iter,
+                      gpointer      data)
+{
+        int      type;
+        int      setting;
+        gboolean enabled = GPOINTER_TO_INT (data);
+
+        setting = enabled ? SOUND_BUILTIN : SOUND_OFF;
+
+        gtk_tree_model_get (model, iter, TYPE_COL, &type, -1);
+        if (type == SOUND_TYPE_AUDIO_BELL) {
+                gtk_tree_store_set (GTK_TREE_STORE (model),
+                                    iter,
+                                    SETTING_COL, setting,
+                                    HAS_PREVIEW_COL, enabled,
+                                    -1);
+                return TRUE;
+        }
+        return FALSE;
+}
+
+static void
+set_audible_bell_enabled (GvcSoundThemeEditor *editor,
+                          gboolean              enabled)
+{
+        GtkTreeModel *model;
+
+        model = gtk_tree_view_get_model (GTK_TREE_VIEW (editor->priv->treeview));
+        gtk_tree_model_foreach (model, audible_bell_foreach, GINT_TO_POINTER (enabled));
+}
+
+/* Functions to toggle whether the Input feedback sounds are editable */
+static gboolean
+input_feedback_foreach (GtkTreeModel *model,
+                        GtkTreePath  *path,
+                        GtkTreeIter  *iter,
+                        gpointer      data)
+{
+        int      type;
+        gboolean enabled = GPOINTER_TO_INT (data);
+
+        gtk_tree_model_get (model, iter, TYPE_COL, &type, -1);
+        if (type == SOUND_TYPE_FEEDBACK) {
+                gtk_tree_store_set (GTK_TREE_STORE (model), iter,
+                                    SENSITIVE_COL, enabled,
+                                    HAS_PREVIEW_COL, enabled,
+                                    -1);
+        }
+        return FALSE;
+}
+
+static void
+set_input_feedback_enabled (GvcSoundThemeEditor *editor,
+                            gboolean              enabled)
+{
+        GtkTreeModel *model;
+
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (editor->priv->click_feedback_button),
+                                      enabled);
+
+        model = gtk_tree_view_get_model (GTK_TREE_VIEW (editor->priv->treeview));
+        gtk_tree_model_foreach (model, input_feedback_foreach, GINT_TO_POINTER (enabled));
+}
+
+static int
+get_file_type (const char *sound_name,
+               char      **linked_name)
+{
+        char *name, *filename;
+
+        *linked_name = NULL;
+
+        name = g_strdup_printf ("%s.disabled", sound_name);
+        filename = custom_theme_dir_path (name);
+        g_free (name);
+
+        if (g_file_test (filename, G_FILE_TEST_IS_REGULAR) != FALSE) {
+                g_free (filename);
+                return SOUND_OFF;
+        }
+        g_free (filename);
+
+        /* We only check for .ogg files because those are the
+         * only ones we create */
+        name = g_strdup_printf ("%s.ogg", sound_name);
+        filename = custom_theme_dir_path (name);
+        g_free (name);
+
+        if (g_file_test (filename, G_FILE_TEST_IS_SYMLINK) != FALSE) {
+                *linked_name = g_file_read_link (filename, NULL);
+                g_free (filename);
+                return SOUND_CUSTOM;
+        }
+        g_free (filename);
+
+        return SOUND_BUILTIN;
+}
+
+static gboolean
+theme_changed_custom_init (GtkTreeModel *model,
+                           GtkTreePath *path,
+                           GtkTreeIter *iter,
+                           gpointer data)
+{
+        char **sound_names;
+
+        gtk_tree_model_get (model, iter, SOUND_NAMES_COL, &sound_names, -1);
+        if (sound_names != NULL) {
+                char *filename;
+                int type;
+
+                type = get_file_type (sound_names[0], &filename);
+
+                gtk_tree_store_set (GTK_TREE_STORE (model), iter,
+                                    SETTING_COL, type,
+                                    HAS_PREVIEW_COL, type != SOUND_OFF,
+                                    FILENAME_COL, filename,
+                                    -1);
+                g_strfreev (sound_names);
+                g_free (filename);
+        }
+        return FALSE;
+}
+
+static void
+update_theme (GvcSoundThemeEditor *editor)
+{
+        char        *theme_name;
+        gboolean     events_enabled;
+        gboolean     bell_enabled;
+        GConfClient *client;
+        gboolean     feedback_enabled;
+
+        client = gconf_client_get_default ();
+
+        bell_enabled = gconf_client_get_bool (client, AUDIO_BELL_KEY, NULL);
+        set_audible_bell_enabled (editor, bell_enabled);
+
+        feedback_enabled = gconf_client_get_bool (client, INPUT_SOUNDS_KEY, NULL);
+        set_input_feedback_enabled (editor, feedback_enabled);
+
+        events_enabled = gconf_client_get_bool (client, EVENT_SOUNDS_KEY, NULL);
+        if (events_enabled) {
+                theme_name = gconf_client_get_string (client, SOUND_THEME_KEY, NULL);
+        } else {
+                theme_name = g_strdup (NO_SOUNDS_THEME_NAME);
+        }
+
+        gtk_widget_set_sensitive (editor->priv->selection_box, events_enabled);
+
+        set_theme_name (editor, theme_name);
+
+        /* Setup the default values if we're using the custom theme */
+        if (theme_name != NULL && strcmp (theme_name, CUSTOM_THEME_NAME) == 0) {
+                GtkTreeModel *model;
+                model = gtk_tree_view_get_model (GTK_TREE_VIEW (editor->priv->treeview));
+                gtk_tree_model_foreach (model,
+                                        theme_changed_custom_init,
+                                        NULL);
+        }
+        g_free (theme_name);
+
+        g_object_unref (client);
+}
+
+static void
+setup_theme_selector (GvcSoundThemeEditor *editor)
+{
+        GHashTable           *hash;
+        GtkListStore         *store;
+        GtkCellRenderer      *renderer;
+        const char * const   *data_dirs;
+        const char           *data_dir;
+        char                 *dir;
+        guint                 i;
+
+        /* Add the theme names and their display name to a hash table,
+         * makes it easy to avoid duplicate themes */
+        hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+        data_dirs = g_get_system_data_dirs ();
+        for (i = 0; data_dirs[i] != NULL; i++) {
+                dir = g_build_filename (data_dirs[i], "sounds", NULL);
+                sound_theme_in_dir (hash, dir);
+                g_free (dir);
+        }
+
+        data_dir = g_get_user_data_dir ();
+        dir = g_build_filename (data_dir, "sounds", NULL);
+        sound_theme_in_dir (hash, dir);
+        g_free (dir);
+
+        /* If there isn't at least one theme, make everything
+         * insensitive, LAME! */
+        if (g_hash_table_size (hash) == 0) {
+                gtk_widget_set_sensitive (GTK_WIDGET (editor), FALSE);
+                g_warning ("Bad setup, install the freedesktop sound theme");
+                g_hash_table_destroy (hash);
+                return;
+        }
+
+        /* Setup the tree model, 3 columns:
+         * - internal theme name/directory
+         * - display theme name
+         * - the internal id for the parent theme, used for the custom theme */
+        store = gtk_list_store_new (THEME_NUM_COLS,
+                                    G_TYPE_STRING,
+                                    G_TYPE_STRING,
+                                    G_TYPE_STRING);
+
+        /* Add the themes to a combobox */
+        gtk_list_store_insert_with_values (store,
+                                           NULL,
+                                           G_MAXINT,
+                                           THEME_DISPLAY_COL, _("No sounds"),
+                                           THEME_IDENTIFIER_COL, "__no_sounds",
+                                           THEME_PARENT_ID_COL, NULL,
+                                           -1);
+        g_hash_table_foreach (hash, (GHFunc) add_theme_to_store, store);
+        g_hash_table_destroy (hash);
+
+        /* Set the display */
+        gtk_combo_box_set_model (GTK_COMBO_BOX (editor->priv->combo_box),
+                                 GTK_TREE_MODEL (store));
+
+        renderer = gtk_cell_renderer_text_new ();
+        gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (editor->priv->combo_box),
+                                    renderer,
+                                    TRUE);
+        gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (editor->priv->combo_box),
+                                        renderer,
+                                        "text", THEME_DISPLAY_COL,
+                                        NULL);
+
+        g_signal_connect (editor->priv->combo_box,
+                          "changed",
+                          G_CALLBACK (on_combobox_changed),
+                          editor);
+}
+
+static void
+play_sound_preview (GtkFileEditor *editor,
+                    gpointer user_data)
+{
+        char       *filename;
+        ca_context *ctx;
+
+        filename = gtk_file_editor_get_preview_filename (GTK_FILE_EDITOR (editor));
+        if (filename == NULL) {
+                return;
+        }
+
+        ctx = ca_gtk_context_get ();
+        ca_gtk_play_for_widget (GTK_WIDGET (editor), 0,
+                                CA_PROP_APPLICATION_NAME, _("Sound Preferences"),
+                                CA_PROP_MEDIA_FILENAME, filename,
+                                CA_PROP_EVENT_DESCRIPTION, _("Testing event sound"),
+                                CA_PROP_CANBERRA_CACHE_CONTROL, "never",
+#ifdef CA_PROP_CANBERRA_ENABLE
+                                CA_PROP_CANBERRA_ENABLE, "1",
+#endif
+                                NULL);
+        g_free (filename);
+}
+
+static char *
+get_sound_filename (GvcSoundThemeEditor *editor)
+{
+        GtkWidget          *file_editor;
+        GtkWidget          *toplevel;
+        GtkWindow          *parent;
+        int                 response;
+        char               *filename;
+        char               *path;
+        const char * const *data_dirs, *data_dir;
+        GtkFileFilter      *filter;
+        guint               i;
+
+	/* Try to get the parent window of the widget */
+	toplevel = gtk_widget_get_toplevel (GTK_WIDGET (editor));
+	if (GTK_WIDGET_TOPLEVEL (toplevel) != FALSE)
+		parent = GTK_WINDOW (toplevel);
+	else
+		parent = NULL;
+
+        file_editor = gtk_file_editor_dialog_new (_("Select Sound File"),
+                                                    parent,
+                                                    GTK_FILE_EDITOR_ACTION_OPEN,
+                                                    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                                    GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+                                                    NULL);
+
+        gtk_file_editor_set_local_only (GTK_FILE_EDITOR (file_editor), TRUE);
+        gtk_file_editor_set_select_multiple (GTK_FILE_EDITOR (file_editor), FALSE);
+
+        filter = gtk_file_filter_new ();
+        gtk_file_filter_set_name (filter, _("Sound files"));
+        gtk_file_filter_add_mime_type (filter, "audio/x-vorbis+ogg");
+        gtk_file_filter_add_mime_type (filter, "audio/x-wav");
+        gtk_file_editor_add_filter (GTK_FILE_EDITOR (file_editor), filter);
+        gtk_file_editor_set_filter (GTK_FILE_EDITOR (file_editor), filter);
+
+        g_signal_connect (file_editor, "update-preview",
+                          G_CALLBACK (play_sound_preview), NULL);
+
+        data_dirs = g_get_system_data_dirs ();
+        for (i = 0; data_dirs[i] != NULL; i++) {
+                path = g_build_filename (data_dirs[i], "sounds", NULL);
+                gtk_file_editor_add_shortcut_folder (GTK_FILE_EDITOR (file_editor), path, NULL);
+                g_free (path);
+        }
+        data_dir = g_get_user_special_dir (G_USER_DIRECTORY_MUSIC);
+        if (data_dir != NULL)
+                gtk_file_editor_add_shortcut_folder (GTK_FILE_EDITOR (file_editor), data_dir, NULL);
+
+        gtk_file_editor_set_current_folder (GTK_FILE_EDITOR (file_editor), SOUND_DATA_DIR);
+
+        response = gtk_dialog_run (GTK_DIALOG (file_editor));
+        filename = NULL;
+        if (response == GTK_RESPONSE_ACCEPT)
+                filename = gtk_file_editor_get_filename (GTK_FILE_EDITOR (file_editor));
+
+        gtk_widget_destroy (file_editor);
+
+        return filename;
+}
+
+
+static gboolean
+count_customised_sounds (GtkTreeModel *model,
+                         GtkTreePath  *path,
+                         GtkTreeIter  *iter,
+                         int          *num_custom)
+{
+        int type;
+        int setting;
+
+        gtk_tree_model_get (model, iter, TYPE_COL, &type, SETTING_COL, &setting, -1);
+        if (setting == SOUND_OFF || setting == SOUND_CUSTOM || setting == SOUND_CUSTOM_OLD) {
+                (*num_custom)++;
+        }
+
+        return FALSE;
+}
+
+static gboolean
+save_sounds (GtkTreeModel *model,
+             GtkTreePath  *path,
+             GtkTreeIter  *iter,
+             gpointer      data)
+{
+        int    type;
+        int    setting;
+        char  *filename;
+        char **sounds;
+
+        gtk_tree_model_get (model, iter,
+                            TYPE_COL, &type,
+                            SETTING_COL, &setting,
+                            FILENAME_COL, &filename,
+                            SOUND_NAMES_COL, &sounds,
+                            -1);
+
+        if (setting == SOUND_BUILTIN) {
+                delete_old_files (sounds);
+                delete_disabled_files (sounds);
+        } else if (setting == SOUND_OFF) {
+                delete_old_files (sounds);
+                add_disabled_file (sounds);
+        } else if (setting == SOUND_CUSTOM || setting == SOUND_CUSTOM_OLD) {
+                delete_old_files (sounds);
+                delete_disabled_files (sounds);
+                add_custom_file (sounds, filename);
+        }
+        g_free (filename);
+        g_strfreev (sounds);
+
+        return FALSE;
+}
+
+static void
+save_custom_theme (GtkTreeModel *model,
+                   const char   *parent)
+{
+        GKeyFile *keyfile;
+        char     *data;
+        char     *path;
+
+        /* Create the custom directory */
+        path = custom_theme_dir_path (NULL);
+        g_mkdir_with_parents (path, 0755);
+        g_free (path);
+
+        /* Save the sounds themselves */
+        gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc) save_sounds, NULL);
+
+        /* Set the data for index.theme */
+        keyfile = g_key_file_new ();
+        g_key_file_set_string (keyfile, "Sound Theme", "Name", _("Custom"));
+        g_key_file_set_string (keyfile, "Sound Theme", "Inherits", parent);
+        g_key_file_set_string (keyfile, "Sound Theme", "Directories", ".");
+        data = g_key_file_to_data (keyfile, NULL, NULL);
+        g_key_file_free (keyfile);
+
+        /* Save the index.theme */
+        path = custom_theme_dir_path ("index.theme");
+        g_file_set_contents (path, data, -1, NULL);
+        g_free (path);
+        g_free (data);
+
+        custom_theme_update_time ();
+}
+
+static void
+dump_theme (GvcSoundThemeEditor *editor)
+{
+        int           num_custom;
+        GtkTreeModel *model;
+        GtkTreeIter   iter;
+        char         *parent;
+
+        num_custom = 0;
+        model = gtk_tree_view_get_model (GTK_TREE_VIEW (editor->priv->treeview));
+        gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc) count_customised_sounds, &num_custom);
+
+        g_debug ("%d customised sounds", num_custom);
+
+        model = gtk_combo_box_get_model (GTK_COMBO_BOX (editor->priv->combo_box));
+        /* Get the current theme's name, and set the parent */
+        if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (editor->priv->combo_box), &iter) == FALSE)
+                return;
+
+        if (num_custom == 0) {
+                gtk_tree_model_get (model, &iter, THEME_PARENT_ID_COL, &parent, -1);
+                if (parent != NULL) {
+                        set_theme_name (editor, parent);
+                        g_free (parent);
+                }
+                gtk_tree_model_get_iter_first (model, &iter);
+                do {
+                        gtk_tree_model_get (model, &iter, THEME_PARENT_ID_COL, &parent, -1);
+                        if (parent != NULL && strcmp (parent, CUSTOM_THEME_NAME) != 0) {
+                                gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+                                break;
+                        }
+                } while (gtk_tree_model_iter_next (model, &iter));
+
+                delete_custom_theme_dir ();
+        } else {
+                gtk_tree_model_get (model, &iter, THEME_IDENTIFIER_COL, &parent, -1);
+                if (strcmp (parent, CUSTOM_THEME_NAME) != 0) {
+                        gtk_list_store_insert_with_values (GTK_LIST_STORE (model), NULL, G_MAXINT,
+                                                           THEME_DISPLAY_COL, _("Custom"),
+                                                           THEME_IDENTIFIER_COL, CUSTOM_THEME_NAME,
+                                                           THEME_PARENT_ID_COL, parent,
+                                                           -1);
+                } else {
+                        g_free (parent);
+                        gtk_tree_model_get (model, &iter, THEME_PARENT_ID_COL, &parent, -1);
+                }
+
+                g_debug ("The parent theme is: %s", parent);
+                model = gtk_tree_view_get_model (GTK_TREE_VIEW (editor->priv->treeview));
+                save_custom_theme (model, parent);
+                g_free (parent);
+
+                set_theme_name (editor, CUSTOM_THEME_NAME);
+        }
+}
+
+static void
+on_setting_column_edited (GtkCellRendererText  *renderer,
+                          char                 *path,
+                          char                 *new_text,
+                          GvcSoundThemeEditor *editor)
+{
+        GtkTreeModel *model;
+        GtkTreeModel *tree_model;
+        GtkTreeIter   iter;
+        GtkTreeIter   tree_iter;
+        SoundType     type;
+        char         *text;
+        char         *old_filename;
+        int           setting;
+
+        if (new_text == NULL) {
+                return;
+        }
+
+        g_object_get (renderer,
+                      "model", &model,
+                      NULL);
+
+        tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (editor->priv->treeview));
+        if (gtk_tree_model_get_iter_from_string (tree_model, &tree_iter, path) == FALSE)
+                return;
+
+        gtk_tree_model_get (tree_model, &tree_iter,
+                            TYPE_COL, &type,
+                            FILENAME_COL, &old_filename,
+                            -1);
+
+        gtk_tree_model_get_iter_first (model, &iter);
+        do {
+                int cmp;
+
+                gtk_tree_model_get (model, &iter,
+                                    0, &text,
+                                    1, &setting,
+                                    -1);
+                cmp = g_utf8_collate (text, new_text);
+                g_free (text);
+
+                if (cmp != 0) {
+                        continue;
+                }
+
+                if (type == SOUND_TYPE_NORMAL
+                    || type == SOUND_TYPE_FEEDBACK
+                    || type == SOUND_TYPE_AUDIO_BELL) {
+
+                        if (setting == SOUND_CUSTOM
+                            || (setting == SOUND_CUSTOM_OLD
+                                && old_filename == NULL)) {
+
+                                char *filename = get_sound_filename (editor);
+
+                                if (filename == NULL) {
+                                        break;
+                                }
+                                gtk_tree_store_set (GTK_TREE_STORE (tree_model),
+                                                    &tree_iter,
+                                                    SETTING_COL, setting,
+                                                    HAS_PREVIEW_COL, setting != SOUND_OFF,
+                                                    FILENAME_COL, filename,
+                                                    -1);
+                                g_free (filename);
+                        } else if (setting == SOUND_CUSTOM_OLD) {
+                                gtk_tree_store_set (GTK_TREE_STORE (tree_model),
+                                                    &tree_iter,
+                                                    SETTING_COL, setting,
+                                                    HAS_PREVIEW_COL, setting != SOUND_OFF,
+                                                    FILENAME_COL, old_filename,
+                                                    -1);
+                        } else {
+                                gtk_tree_store_set (GTK_TREE_STORE (tree_model),
+                                                    &tree_iter,
+                                                    SETTING_COL, setting,
+                                                    HAS_PREVIEW_COL, setting != SOUND_OFF,
+                                                    -1);
+                        }
+
+                        g_debug ("Something changed, dump theme");
+                        dump_theme (editor);
+
+                        break;
+                }
+
+                g_assert_not_reached ();
+
+        } while (gtk_tree_model_iter_next (model, &iter));
+
+        g_free (old_filename);
+}
+
+static void
+fill_custom_model (GtkListStore *store,
+                   const char   *prev_filename)
+{
+        GtkTreeIter iter;
+
+        gtk_list_store_clear (store);
+
+        if (prev_filename != NULL) {
+                char *display;
+                display = g_filename_display_basename (prev_filename);
+                gtk_list_store_insert_with_values (store, &iter, G_MAXINT,
+                                                   0, display,
+                                                   1, SOUND_CUSTOM_OLD,
+                                                   -1);
+                g_free (display);
+        }
+
+        gtk_list_store_insert_with_values (store, &iter, G_MAXINT,
+                                           0, _("Default"),
+                                           1, SOUND_BUILTIN,
+                                           -1);
+        gtk_list_store_insert_with_values (store, &iter, G_MAXINT,
+                                           0, _("Disabled"),
+                                           1, SOUND_OFF,
+                                           -1);
+        gtk_list_store_insert_with_values (store, &iter, G_MAXINT,
+                                           0, _("Custom..."),
+                                           1, SOUND_CUSTOM, -1);
+}
+
+static void
+on_combobox_editing_started (GtkCellRenderer      *renderer,
+                             GtkCellEditable      *editable,
+                             gchar                *path,
+                             GvcSoundThemeEditor *editor)
+{
+        GtkTreeModel *model;
+        GtkTreeModel *store;
+        GtkTreeIter   iter;
+        SoundType     type;
+        char         *filename;
+
+        model = gtk_tree_view_get_model (GTK_TREE_VIEW (editor->priv->treeview));
+        if (gtk_tree_model_get_iter_from_string (model, &iter, path) == FALSE) {
+                return;
+        }
+
+        gtk_tree_model_get (model, &iter, TYPE_COL, &type, FILENAME_COL, &filename, -1);
+        g_object_get (renderer, "model", &store, NULL);
+        fill_custom_model (GTK_LIST_STORE (store), filename);
+        g_free (filename);
+}
+
+static gboolean
+play_sound_at_path (GtkWidget         *tree_view,
+                    GtkTreePath       *path)
+{
+        GtkTreeModel *model;
+        GtkTreeIter   iter;
+        char        **sound_names;
+        gboolean      sensitive;
+        ca_context   *ctx;
+
+        model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
+        if (gtk_tree_model_get_iter (model, &iter, path) == FALSE) {
+                return FALSE;
+        }
+
+        gtk_tree_model_get (model, &iter,
+                            SOUND_NAMES_COL, &sound_names,
+                            SENSITIVE_COL, &sensitive,
+                            -1);
+        if (!sensitive || sound_names == NULL) {
+                return FALSE;
+        }
+
+        ctx = ca_gtk_context_get ();
+        ca_gtk_play_for_widget (GTK_WIDGET (tree_view), 0,
+                                CA_PROP_APPLICATION_NAME, _("Sound Preferences"),
+                                CA_PROP_EVENT_ID, sound_names[0],
+                                CA_PROP_EVENT_DESCRIPTION, _("Testing event sound"),
+                                CA_PROP_CANBERRA_CACHE_CONTROL, "never",
+#ifdef CA_PROP_CANBERRA_ENABLE
+                                CA_PROP_CANBERRA_ENABLE, "1",
+#endif
+                                NULL);
+
+        g_strfreev (sound_names);
+
+        return TRUE;
+}
+
+static void
+setting_set_func (GtkTreeViewColumn *tree_column,
+                  GtkCellRenderer   *cell,
+                  GtkTreeModel      *model,
+                  GtkTreeIter       *iter,
+                  gpointer           data)
+{
+        int       setting;
+        char     *filename;
+        SoundType type;
+
+        gtk_tree_model_get (model, iter,
+                            SETTING_COL, &setting,
+                            FILENAME_COL, &filename,
+                            TYPE_COL, &type,
+                            -1);
+
+        if (setting == SOUND_UNSET) {
+                g_object_set (cell,
+                              "visible", FALSE,
+                              NULL);
+                g_free (filename);
+                return;
+        }
+
+        if (setting == SOUND_OFF) {
+                g_object_set (cell,
+                              "text", _("Disabled"),
+                              NULL);
+        } else if (setting == SOUND_BUILTIN) {
+                g_object_set (cell,
+                              "text", _("Default"),
+                              NULL);
+        } else if (setting == SOUND_CUSTOM || setting == SOUND_CUSTOM_OLD) {
+                char *display;
+
+                display = g_filename_display_basename (filename);
+                g_object_set (cell,
+                              "text", display,
+                              NULL);
+                g_free (display);
+        }
+
+        g_free (filename);
+}
+
+typedef GtkCellRendererPixbuf      ActivatableCellRendererPixbuf;
+typedef GtkCellRendererPixbufClass ActivatableCellRendererPixbufClass;
+
+GType activatable_cell_renderer_pixbuf_get_type (void);
+#define ACTIVATABLE_TYPE_CELL_RENDERER_PIXBUF (activatable_cell_renderer_pixbuf_get_type ())
+G_DEFINE_TYPE (ActivatableCellRendererPixbuf, activatable_cell_renderer_pixbuf, GTK_TYPE_CELL_RENDERER_PIXBUF);
+
+static gboolean
+activatable_cell_renderer_pixbuf_activate (GtkCellRenderer      *cell,
+                                           GdkEvent             *event,
+                                           GtkWidget            *widget,
+                                           const gchar          *path_string,
+                                           GdkRectangle         *background_area,
+                                           GdkRectangle         *cell_area,
+                                           GtkCellRendererState  flags)
+{
+        GtkTreePath *path;
+        gboolean     res;
+
+        g_debug ("Activating pixbuf");
+
+        path = gtk_tree_path_new_from_string (path_string);
+        res = play_sound_at_path (widget, path);
+        gtk_tree_path_free (path);
+
+        return res;
+}
+
+static void
+activatable_cell_renderer_pixbuf_init (ActivatableCellRendererPixbuf *cell)
+{
+}
+
+static void
+activatable_cell_renderer_pixbuf_class_init (ActivatableCellRendererPixbufClass *class)
+{
+        GtkCellRendererClass *cell_class;
+
+        cell_class = GTK_CELL_RENDERER_CLASS (class);
+
+        cell_class->activate = activatable_cell_renderer_pixbuf_activate;
+}
+
+static void
+setup_theme_custom_selector (GvcSoundThemeEditor *editor,
+                             gboolean              have_xkb )
+{
+        GtkTreeStore      *store;
+        GtkTreeModel      *custom_model;
+        GtkTreeViewColumn *column;
+        GtkCellRenderer   *renderer;
+        GtkTreeIter        iter;
+        GtkTreeIter        parent;
+        GConfClient       *client;
+        CategoryType       type;
+        guint              i;
+
+        client = gconf_client_get_default ();
+
+        /* Set up the model for the custom view */
+        store = gtk_tree_store_new (NUM_COLS,
+                                    G_TYPE_STRING,
+                                    G_TYPE_INT,
+                                    G_TYPE_INT,
+                                    G_TYPE_BOOLEAN,
+                                    G_TYPE_BOOLEAN,
+                                    G_TYPE_STRING,
+                                    G_TYPE_STRV);
+
+        /* The first column with the categories/sound names */
+        renderer = gtk_cell_renderer_text_new ();
+        column = gtk_tree_view_column_new_with_attributes ("Display", renderer,
+                                                           "text", DISPLAY_COL,
+                                                           "sensitive", SENSITIVE_COL,
+							   "ellipsize", PANGO_ELLIPSIZE_START,
+							   "ellipsize-set", TRUE,
+							   NULL);
+	g_object_set (G_OBJECT (column), "expand", TRUE, NULL);
+
+        gtk_tree_view_append_column (GTK_TREE_VIEW (editor->priv->treeview), column);
+
+        /* The 2nd column with the sound settings */
+        renderer = gtk_cell_renderer_combo_new ();
+        g_signal_connect (renderer,
+                          "edited",
+                          G_CALLBACK (on_setting_column_edited),
+                          editor);
+        g_signal_connect (renderer,
+                          "editing-started",
+                          G_CALLBACK (on_combobox_editing_started),
+                          editor);
+        custom_model = GTK_TREE_MODEL (gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT));
+        fill_custom_model (GTK_LIST_STORE (custom_model), NULL);
+
+        g_object_set (renderer,
+                      "model", custom_model,
+                      "has-entry", FALSE,
+                      "editable", TRUE,
+                      "text-column", 0,
+                      NULL);
+        column = gtk_tree_view_column_new_with_attributes ("Setting", renderer,
+                                                           "editable", SENSITIVE_COL,
+                                                           "sensitive", SENSITIVE_COL,
+                                                           "visible", TRUE,
+                                                           NULL);
+        gtk_tree_view_append_column (GTK_TREE_VIEW (editor->priv->treeview), column);
+        gtk_tree_view_column_set_cell_data_func (column, renderer, setting_set_func, NULL, NULL);
+
+        /* The 3rd column with the preview pixbuf */
+        renderer = g_object_new (ACTIVATABLE_TYPE_CELL_RENDERER_PIXBUF, NULL);
+        g_object_set (renderer,
+                      "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE,
+                      "icon-name", "media-playback-start",
+                      "stock-size", GTK_ICON_SIZE_MENU,
+                      NULL);
+        column = gtk_tree_view_column_new_with_attributes ("Preview", renderer,
+                                                           "visible", HAS_PREVIEW_COL,
+                                                           NULL);
+        gtk_tree_view_append_column (GTK_TREE_VIEW (editor->priv->treeview), column);
+        g_object_set_data (G_OBJECT (editor->priv->treeview), "preview-column", column);
+
+        gtk_tree_view_set_model (GTK_TREE_VIEW (editor->priv->treeview), GTK_TREE_MODEL (store));
+        gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (editor->priv->treeview), FALSE);
+
+        /* Fill in the model */
+        type = CATEGORY_INVALID;
+
+        for (i = 0; ; i++) {
+                GtkTreeIter *_parent;
+
+                if (sounds[i].category == -1) {
+                        break;
+                }
+
+                /* Is it a new type of sound? */
+                if (sounds[i].category == type
+                    && type != CATEGORY_INVALID
+                    && type != CATEGORY_BELL) {
+                        _parent = &parent;
+                } else {
+                        _parent = NULL;
+                }
+
+                if (sounds[i].type != -1) {
+                        gtk_tree_store_insert_with_values (store, &iter, _parent, G_MAXINT,
+                                                           DISPLAY_COL, g_dpgettext2 (NULL, "Sound event", sounds[i].display_name),
+                                                           SETTING_COL, SOUND_BUILTIN,
+                                                           TYPE_COL, sounds[i].type,
+                                                           SOUND_NAMES_COL, sounds[i].names,
+                                                           HAS_PREVIEW_COL, TRUE,
+                                                           SENSITIVE_COL, TRUE,
+                                                           -1);
+                } else {
+                        /* Category */
+                        gtk_tree_store_insert_with_values (store, &iter, _parent, G_MAXINT,
+                                                           DISPLAY_COL, g_dpgettext2 (NULL, "Sound event", sounds[i].display_name),
+                                                           SETTING_COL, SOUND_UNSET,
+                                                           TYPE_COL, sounds[i].type,
+                                                           SENSITIVE_COL, TRUE,
+                                                           HAS_PREVIEW_COL, FALSE,
+                                                           -1);
+                }
+
+                /* If we didn't set a parent already, set one in case we need it later */
+                if (_parent == NULL) {
+                        parent = iter;
+                }
+                type = sounds[i].category;
+        }
+
+        gtk_tree_view_expand_all (GTK_TREE_VIEW (editor->priv->treeview));
+
+
+        g_object_unref (client);
+}
+
+static GObject *
+gvc_sound_theme_editor_constructor (GType                  type,
+                                     guint                  n_construct_properties,
+                                     GObjectConstructParam *construct_params)
+{
+        GObject              *object;
+        GvcSoundThemeEditor *self;
+
+        object = G_OBJECT_CLASS (gvc_sound_theme_editor_parent_class)->constructor (type, n_construct_properties, construct_params);
+
+        self = GVC_SOUND_THEME_EDITOR (object);
+
+        setup_theme_selector (self);
+        setup_theme_custom_selector (self, TRUE);
+
+        update_theme (self);
+
+        return object;
+}
+
+static void
+gvc_sound_theme_editor_class_init (GvcSoundThemeEditorClass *klass)
+{
+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->constructor = gvc_sound_theme_editor_constructor;
+        object_class->finalize = gvc_sound_theme_editor_finalize;
+
+        g_type_class_add_private (klass, sizeof (GvcSoundThemeEditorPrivate));
+}
+
+static void
+on_click_feedback_toggled (GtkToggleButton      *button,
+                           GvcSoundThemeEditor *editor)
+{
+        GConfClient *client;
+        gboolean     enabled;
+
+        enabled = gtk_toggle_button_get_active (button);
+
+        client = gconf_client_get_default ();
+        gconf_client_set_bool (client, INPUT_SOUNDS_KEY, enabled, NULL);
+        g_object_unref (client);
+}
+
+static void
+on_key_changed (GConfClient          *client,
+                guint                 cnxn_id,
+                GConfEntry           *entry,
+                GvcSoundThemeEditor *editor)
+{
+        const char *key;
+        GConfValue *value;
+
+        key = gconf_entry_get_key (entry);
+
+        if (! g_str_has_prefix (key, KEY_SOUNDS_DIR)
+            && ! g_str_has_prefix (key, KEY_METACITY_DIR)) {
+                return;
+        }
+
+        value = gconf_entry_get_value (entry);
+        if (strcmp (key, EVENT_SOUNDS_KEY) == 0) {
+                update_theme (editor);
+        } else if (strcmp (key, SOUND_THEME_KEY) == 0) {
+                update_theme (editor);
+        } else if (strcmp (key, INPUT_SOUNDS_KEY) == 0) {
+                update_theme (editor);
+        } else if (strcmp (key, AUDIO_BELL_KEY) == 0) {
+                update_theme (editor);
+        }
+}
+
+static void
+on_treeview_row_activated (GtkTreeView          *treeview,
+                           GtkTreePath          *path,
+                           GtkTreeViewColumn    *column,
+                           GvcSoundThemeEditor *editor)
+{
+        g_debug ("row activated");
+        play_sound_at_path (GTK_WIDGET (treeview), path);
+}
+
+static void
+constrain_list_size (GtkWidget      *widget,
+                     GtkRequisition *requisition,
+                     GtkWidget      *to_size)
+{
+        GtkRequisition req;
+        int            max_height;
+
+        /* constrain height to be the tree height up to a max */
+        max_height = (gdk_screen_get_height (gtk_widget_get_screen (widget))) / 4;
+
+        gtk_widget_size_request (to_size, &req);
+
+        requisition->height = MIN (req.height, max_height);
+}
+
+static void
+setup_list_size_constraint (GtkWidget *widget,
+                            GtkWidget *to_size)
+{
+        g_signal_connect (widget,
+                          "size-request",
+                          G_CALLBACK (constrain_list_size),
+                          to_size);
+}
+
+static void
+gvc_sound_theme_editor_init (GvcSoundThemeEditor *editor)
+{
+        GtkWidget   *box;
+        GtkWidget   *label;
+        GtkWidget   *scrolled_window;
+        GConfClient *client;
+
+        editor->priv = GVC_SOUND_THEME_EDITOR_GET_PRIVATE (editor);
+
+        editor->priv->theme_box = gtk_hbox_new (FALSE, 6);
+        gtk_box_pack_start (GTK_BOX (editor),
+                            editor->priv->theme_box, FALSE, FALSE, 0);
+
+        label = gtk_label_new (_("Sound Theme:"));
+        gtk_box_pack_start (GTK_BOX (editor->priv->theme_box), label, FALSE, FALSE, 6);
+        editor->priv->combo_box = gtk_combo_box_new ();
+        gtk_box_pack_start (GTK_BOX (editor->priv->theme_box), editor->priv->combo_box, FALSE, FALSE, 0);
+
+
+        client = gconf_client_get_default ();
+
+        editor->priv->selection_box = box = gtk_vbox_new (FALSE, 0);
+        gtk_box_pack_start (GTK_BOX (editor), box, TRUE, TRUE, 0);
+
+        editor->priv->treeview = gtk_tree_view_new ();
+        g_signal_connect (editor->priv->treeview,
+                          "row-activated",
+                          G_CALLBACK (on_treeview_row_activated),
+                          editor);
+
+        scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+        setup_list_size_constraint (scrolled_window, editor->priv->treeview);
+
+        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+                                        GTK_POLICY_NEVER,
+                                        GTK_POLICY_AUTOMATIC);
+        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
+                                             GTK_SHADOW_IN);
+        gtk_container_add (GTK_CONTAINER (scrolled_window), editor->priv->treeview);
+        gtk_container_add (GTK_CONTAINER (box), scrolled_window);
+
+        editor->priv->click_feedback_button = gtk_check_button_new_with_mnemonic (_("Enable window and button sounds"));
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (editor->priv->click_feedback_button),
+                                      gconf_client_get_bool (client, INPUT_SOUNDS_KEY, NULL));
+        gtk_box_pack_start (GTK_BOX (box),
+                            editor->priv->click_feedback_button,
+                            FALSE, FALSE, 0);
+        g_signal_connect (editor->priv->click_feedback_button,
+                          "toggled",
+                          G_CALLBACK (on_click_feedback_toggled),
+                          editor);
+
+
+        gconf_client_add_dir (client, KEY_SOUNDS_DIR,
+                              GCONF_CLIENT_PRELOAD_ONELEVEL,
+                              NULL);
+        gconf_client_notify_add (client,
+                                 KEY_SOUNDS_DIR,
+                                 (GConfClientNotifyFunc)on_key_changed,
+                                 editor, NULL, NULL);
+        gconf_client_add_dir (client, KEY_METACITY_DIR,
+                              GCONF_CLIENT_PRELOAD_ONELEVEL,
+                              NULL);
+        gconf_client_notify_add (client,
+                                 KEY_METACITY_DIR,
+                                 (GConfClientNotifyFunc)on_key_changed,
+                                 editor, NULL, NULL);
+
+        g_object_unref (client);
+
+        /* FIXME: should accept drag and drop themes.  should also
+           add an "Add Theme..." item to the theme combobox */
+}
+
+static void
+gvc_sound_theme_editor_finalize (GObject *object)
+{
+        GvcSoundThemeEditor *sound_theme_editor;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GVC_IS_SOUND_THEME_EDITOR (object));
+
+        sound_theme_editor = GVC_SOUND_THEME_EDITOR (object);
+
+        g_return_if_fail (sound_theme_editor->priv != NULL);
+
+        G_OBJECT_CLASS (gvc_sound_theme_editor_parent_class)->finalize (object);
+}
+
+GtkWidget *
+gvc_sound_theme_editor_new (void)
+{
+        GObject *editor;
+        editor = g_object_new (GVC_TYPE_SOUND_THEME_EDITOR,
+                                "spacing", 6,
+                                NULL);
+        return GTK_WIDGET (editor);
+}

Added: trunk/gnome-volume-control/src/gvc-sound-theme-editor.h
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/gvc-sound-theme-editor.h	Sun Dec 21 20:15:15 2008
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_SOUND_THEME_EDITOR_H
+#define __GVC_SOUND_THEME_EDITOR_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GVC_TYPE_SOUND_THEME_EDITOR         (gvc_sound_theme_editor_get_type ())
+#define GVC_SOUND_THEME_EDITOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_SOUND_THEME_EDITOR, GvcSoundThemeEditor))
+#define GVC_SOUND_THEME_EDITOR_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_SOUND_THEME_EDITOR, GvcSoundThemeEditorClass))
+#define GVC_IS_SOUND_THEME_EDITOR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_SOUND_THEME_EDITOR))
+#define GVC_IS_SOUND_THEME_EDITOR_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_SOUND_THEME_EDITOR))
+#define GVC_SOUND_THEME_EDITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_SOUND_THEME_EDITOR, GvcSoundThemeEditorClass))
+
+typedef struct GvcSoundThemeEditorPrivate GvcSoundThemeEditorPrivate;
+
+typedef struct
+{
+        GtkVBox                     parent;
+        GvcSoundThemeEditorPrivate *priv;
+} GvcSoundThemeEditor;
+
+typedef struct
+{
+        GtkVBoxClass          parent_class;
+} GvcSoundThemeEditorClass;
+
+GType               gvc_sound_theme_editor_get_type            (void);
+
+GtkWidget *         gvc_sound_theme_editor_new                 (void);
+
+G_END_DECLS
+
+#endif /* __GVC_SOUND_THEME_EDITOR_H */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]