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



Author: mccann
Date: Thu Nov  6 07:16:09 2008
New Revision: 4032
URL: http://svn.gnome.org/viewvc/gnome-media?rev=4032&view=rev

Log:
2008-11-06  William Jon McCann  <jmccann redhat com>

	* src/Makefile.am:
	* src/gvc-applet.c (on_menu_activate_open_volume_control),
	(on_status_icon_popup_menu):
	* src/gvc-mixer-dialog.c (on_stream_is_muted_notify), (add_stream),
	(gvc_mixer_dialog_constructor), (gvc_mixer_dialog_new):
	* src/gvc-sound-theme-chooser.c (theme_changed_custom_reinit),
	(on_combobox_changed), (load_index_theme_name),
	(sound_theme_in_dir), (add_theme_to_store),
	(set_combox_for_theme_name), (on_theme_changed),
	(setup_theme_selector), (visual_bell_gconf_to_setting),
	(visual_bell_setting_to_gconf), (play_sound_preview),
	(get_sound_filename), (count_customised_sounds), (save_sounds),
	(save_custom_theme), (dump_theme), (on_setting_column_edited),
	(fill_visual_bell_model), (fill_custom_model),
	(on_combobox_editing_started), (play_sound_at_path),
	(on_treeview_button_press_event), (setting_set_func),
	(activatable_cell_renderer_pixbuf_activate),
	(activatable_cell_renderer_pixbuf_init),
	(activatable_cell_renderer_pixbuf_class_init),
	(input_feedback_foreach), (set_input_feedback_enabled),
	(on_input_feedback_changed), (audible_bell_foreach),
	(set_audible_bell_enabled), (on_audible_bell_changed),
	(get_file_type), (theme_changed_custom_init),
	(setup_theme_custom_selector),
	(gvc_sound_theme_chooser_constructor),
	(gvc_sound_theme_chooser_class_init), (_gtk_label_make_bold),
	(gvc_sound_theme_chooser_init), (gvc_sound_theme_chooser_finalize),
	(gvc_sound_theme_chooser_new):
	* src/gvc-sound-theme-chooser.h:
	* src/sound-theme-file-utils.c (custom_theme_update_time),
	(custom_theme_dir_path), (directory_delete_recursive),
	(capplet_file_delete_recursive), (delete_custom_theme_dir),
	(delete_one_file), (delete_old_files), (delete_disabled_files),
	(create_one_file), (add_disabled_file), (add_custom_file):
	* src/sound-theme-file-utils.h:
	Redesign a bit in order to incorporate the sound properties.



Added:
   trunk/gnome-volume-control/src/gvc-sound-theme-chooser.c
   trunk/gnome-volume-control/src/gvc-sound-theme-chooser.h
   trunk/gnome-volume-control/src/sound-theme-file-utils.c
   trunk/gnome-volume-control/src/sound-theme-file-utils.h
Modified:
   trunk/ChangeLog
   trunk/configure.ac
   trunk/gnome-volume-control/ChangeLog
   trunk/gnome-volume-control/src/Makefile.am
   trunk/gnome-volume-control/src/gvc-applet.c
   trunk/gnome-volume-control/src/gvc-mixer-dialog.c

Modified: trunk/configure.ac
==============================================================================
--- trunk/configure.ac	(original)
+++ trunk/configure.ac	Thu Nov  6 07:16:09 2008
@@ -65,6 +65,7 @@
 LIBGLADE_REQUIRED_VERSION=1.99.2
 GCONF_REQUIRED_VERSION=2.6.1
 PA_REQUIRED_VERSION=0.9.12
+CANBERRA_REQUIRED_VERSION=0.4
 
 PKG_CHECK_MODULES(MEDIA, [
         glib-2.0 >= $GLIB_REQUIRED_VERSION
@@ -90,6 +91,9 @@
         dbus-glib-1 >= $DBUS_GLIB_REQUIRED_VERSION
         gobject-2.0 >= $GLIB_REQUIRED_VERSION
         gtk+-2.0 >= $GTK_REQUIRED_VERSION
+        gio-2.0
+        gconf-2.0 >= $GCONF_REQUIRED_VERSION
+        libcanberra-gtk >= $CANBERRA_REQUIRED_VERSION
 )
 AC_SUBST(VOLUME_CONTROL_CFLAGS)
 AC_SUBST(VOLUME_CONTROL_LIBS)

Modified: trunk/gnome-volume-control/src/Makefile.am
==============================================================================
--- trunk/gnome-volume-control/src/Makefile.am	(original)
+++ trunk/gnome-volume-control/src/Makefile.am	Thu Nov  6 07:16:09 2008
@@ -12,6 +12,7 @@
 	-DLOCALE_DIR=\""$(datadir)/locale"\"	\
 	-DLIBEXECDIR=\"$(libexecdir)\"		\
 	-DGLADEDIR=\""$(pkgdatadir)"\"		\
+	-DSOUND_DATA_DIR="\"$(datadir)/sounds\"" \
 	$(NULL)
 
 gnome_volume_control_applet_LDADD =		\
@@ -55,6 +56,10 @@
 	gvc-mixer-control.c			\
 	gvc-channel-bar.h			\
 	gvc-channel-bar.c			\
+	gvc-sound-theme-chooser.h		\
+	gvc-sound-theme-chooser.c		\
+	sound-theme-file-utils.h		\
+	sound-theme-file-utils.c		\
 	gvc-mixer-dialog.h			\
 	gvc-mixer-dialog.c			\
 	dialog-main.c				\

Modified: trunk/gnome-volume-control/src/gvc-applet.c
==============================================================================
--- trunk/gnome-volume-control/src/gvc-applet.c	(original)
+++ trunk/gnome-volume-control/src/gvc-applet.c	Thu Nov  6 07:16:09 2008
@@ -244,36 +244,7 @@
                                                  0,
                                                  GTK_MESSAGE_ERROR,
                                                  GTK_BUTTONS_CLOSE,
-                                                 _("Failed to start Volume Control: %s"),
-                                                 error->message);
-                g_signal_connect (dialog,
-                                  "response",
-                                  G_CALLBACK (gtk_widget_destroy),
-                                  NULL);
-                gtk_widget_show (dialog);
-                g_error_free (error);
-        }
-}
-
-static void
-on_menu_activate_sounds (GtkMenuItem *item,
-                         GvcApplet   *applet)
-{
-        GError *error;
-
-        error = NULL;
-        gdk_spawn_command_line_on_screen (gtk_widget_get_screen (applet->priv->dock),
-                                          "gnome-sound-properties",
-                                          &error);
-
-        if (error != NULL) {
-                GtkWidget *dialog;
-
-                dialog = gtk_message_dialog_new (NULL,
-                                                 0,
-                                                 GTK_MESSAGE_ERROR,
-                                                 GTK_BUTTONS_CLOSE,
-                                                 _("Failed to start sound properties: %s"),
+                                                 _("Failed to start Sound Preferences: %s"),
                                                  error->message);
                 g_signal_connect (dialog,
                                   "response",
@@ -295,7 +266,7 @@
         GtkWidget *image;
 
         menu = gtk_menu_new ();
-        item = gtk_image_menu_item_new_with_mnemonic (_("_Open Volume Control"));
+        item = gtk_image_menu_item_new_with_mnemonic (_("_Sound Preferences"));
         image = gtk_image_new_from_icon_name ("multimedia-volume-control",
                                               GTK_ICON_SIZE_MENU);
         gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
@@ -305,13 +276,6 @@
                           applet);
         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
-        item = gtk_menu_item_new_with_mnemonic (_("_Sounds"));
-        g_signal_connect (item,
-                          "activate",
-                          G_CALLBACK (on_menu_activate_sounds),
-                          applet);
-        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-
         gtk_widget_show_all (menu);
         gtk_menu_popup (GTK_MENU (menu),
                         NULL,

Modified: trunk/gnome-volume-control/src/gvc-mixer-dialog.c
==============================================================================
--- trunk/gnome-volume-control/src/gvc-mixer-dialog.c	(original)
+++ trunk/gnome-volume-control/src/gvc-mixer-dialog.c	Thu Nov  6 07:16:09 2008
@@ -33,6 +33,7 @@
 #include "gvc-mixer-sink.h"
 #include "gvc-mixer-source.h"
 #include "gvc-mixer-dialog.h"
+#include "gvc-sound-theme-chooser.h"
 
 #define SCALE_SIZE 128
 
@@ -42,10 +43,11 @@
 {
         GvcMixerControl *mixer_control;
         GHashTable      *bars;
-        GtkWidget       *streams_box;
-        GtkWidget       *output_streams_box;
-        GtkWidget       *input_streams_box;
-        GtkWidget       *application_streams_box;
+        GtkWidget       *output_stream_box;
+        GtkWidget       *sound_effects_box;
+        GtkWidget       *input_box;
+        GtkWidget       *output_box;
+        GtkWidget       *applications_box;
 };
 
 enum
@@ -204,7 +206,7 @@
                                       is_muted);
 
         if (stream == gvc_mixer_control_get_default_sink (dialog->priv->mixer_control)) {
-                gtk_widget_set_sensitive (dialog->priv->application_streams_box,
+                gtk_widget_set_sensitive (dialog->priv->applications_box,
                                           !is_muted);
         }
 
@@ -230,36 +232,41 @@
 
         bar = gvc_channel_bar_new ();
         gvc_channel_bar_set_orientation (GVC_CHANNEL_BAR (bar),
-                                         GTK_ORIENTATION_VERTICAL);
+                                         GTK_ORIENTATION_HORIZONTAL);
         gvc_channel_bar_set_show_mute (GVC_CHANNEL_BAR (bar),
                                        TRUE);
         is_muted = gvc_mixer_stream_get_is_muted (stream);
 
         if (stream == gvc_mixer_control_get_default_sink (dialog->priv->mixer_control)) {
                 gvc_channel_bar_set_name (GVC_CHANNEL_BAR (bar),
-                                          _("Speakers"));
-                gtk_widget_set_sensitive (dialog->priv->application_streams_box,
+                                          _("Output volume: "));
+                gtk_widget_set_sensitive (dialog->priv->applications_box,
                                           !is_muted);
         } else if (stream == gvc_mixer_control_get_default_source (dialog->priv->mixer_control)) {
                 gvc_channel_bar_set_name (GVC_CHANNEL_BAR (bar),
-                                          _("Microphone"));
+                                          _("Input volume: "));
+        } else if (stream == gvc_mixer_control_get_event_sink_input (dialog->priv->mixer_control)) {
+                gvc_channel_bar_set_name (GVC_CHANNEL_BAR (bar),
+                                          _("Alert volume: "));
         } else {
                 gvc_channel_bar_set_name (GVC_CHANNEL_BAR (bar),
                                           gvc_mixer_stream_get_name (stream));
+                gvc_channel_bar_set_icon_name (GVC_CHANNEL_BAR (bar),
+                                               gvc_mixer_stream_get_icon_name (stream));
         }
 
-        gvc_channel_bar_set_icon_name (GVC_CHANNEL_BAR (bar),
-                                       gvc_mixer_stream_get_icon_name (stream));
         g_object_set_data (G_OBJECT (bar), "gvc-mixer-dialog-stream", stream);
 
         save_bar_for_stream (dialog, stream, bar);
 
         if (GVC_IS_MIXER_SINK (stream)) {
-                gtk_box_pack_start (GTK_BOX (dialog->priv->output_streams_box), bar, TRUE, FALSE, 0);
+                gtk_box_pack_start (GTK_BOX (dialog->priv->output_stream_box), bar, TRUE, TRUE, 0);
         } else if (GVC_IS_MIXER_SOURCE (stream)) {
-                gtk_box_pack_start (GTK_BOX (dialog->priv->input_streams_box), bar, TRUE, FALSE, 0);
+                gtk_box_pack_end (GTK_BOX (dialog->priv->input_box), bar, FALSE, FALSE, 0);
+        } else if (stream == gvc_mixer_control_get_event_sink_input (dialog->priv->mixer_control)) {
+                gtk_box_pack_end (GTK_BOX (dialog->priv->sound_effects_box), bar, FALSE, FALSE, 0);
         } else {
-                gtk_box_pack_start (GTK_BOX (dialog->priv->application_streams_box), bar, TRUE, FALSE, 0);
+                gtk_box_pack_start (GTK_BOX (dialog->priv->applications_box), bar, FALSE, FALSE, 0);
         }
 
         gvc_channel_bar_set_is_muted (GVC_CHANNEL_BAR (bar), is_muted);
@@ -313,8 +320,10 @@
 {
         GObject        *object;
         GvcMixerDialog *self;
-        GtkWidget      *separator;
         GtkWidget      *main_vbox;
+        GtkWidget      *label;
+        GtkWidget      *box;
+        GtkWidget      *notebook;
         GSList         *streams;
         GSList         *l;
         GvcMixerStream *stream;
@@ -328,30 +337,50 @@
 #else
         main_vbox = GTK_DIALOG (self)->vbox;
 #endif
-        self->priv->streams_box = gtk_hbox_new (FALSE, 12);
-        gtk_container_add (GTK_CONTAINER (main_vbox), self->priv->streams_box);
 
-        self->priv->output_streams_box = gtk_hbox_new (FALSE, 12);
-        gtk_box_pack_start (GTK_BOX (self->priv->streams_box),
-                            self->priv->output_streams_box,
-                            FALSE, FALSE, 6);
+        gtk_container_set_border_width (GTK_CONTAINER (self), 5);
 
-        self->priv->input_streams_box = gtk_hbox_new (FALSE, 12);
-        gtk_box_pack_start (GTK_BOX (self->priv->streams_box),
-                            self->priv->input_streams_box,
+        notebook = gtk_notebook_new ();
+        gtk_box_pack_start (GTK_BOX (main_vbox),
+                            notebook,
+                            TRUE, TRUE, 6);
+
+        self->priv->output_stream_box = gtk_hbox_new (FALSE, 12);
+        gtk_box_pack_start (GTK_BOX (main_vbox),
+                            self->priv->output_stream_box,
                             FALSE, FALSE, 6);
 
-        separator = gtk_vseparator_new ();
-        gtk_box_pack_start (GTK_BOX (self->priv->streams_box),
-                            separator,
-                            FALSE, FALSE, 6);
-
-        self->priv->application_streams_box = gtk_hbox_new (FALSE, 12);
-        gtk_box_pack_start (GTK_BOX (self->priv->streams_box),
-                            self->priv->application_streams_box,
-                            FALSE, FALSE, 6);
+        self->priv->sound_effects_box = gtk_vbox_new (FALSE, 12);
+        gtk_container_set_border_width (GTK_CONTAINER (self->priv->sound_effects_box), 12);
+        label = gtk_label_new (_("Sound Effects"));
+        gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
+                                  self->priv->sound_effects_box,
+                                  label);
+        box = gvc_sound_theme_chooser_new ();
+        gtk_box_pack_start (GTK_BOX (self->priv->sound_effects_box),
+                            box,
+                            TRUE, TRUE, 6);
+
+        self->priv->output_box = gtk_vbox_new (FALSE, 12);
+        gtk_container_set_border_width (GTK_CONTAINER (self->priv->output_box), 12);
+        label = gtk_label_new (_("Output"));
+        gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
+                                  self->priv->output_box,
+                                  label);
+        self->priv->input_box = gtk_vbox_new (FALSE, 12);
+        gtk_container_set_border_width (GTK_CONTAINER (self->priv->input_box), 12);
+        label = gtk_label_new (_("Input"));
+        gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
+                                  self->priv->input_box,
+                                  label);
+        self->priv->applications_box = gtk_vbox_new (FALSE, 12);
+        gtk_container_set_border_width (GTK_CONTAINER (self->priv->applications_box), 12);
+        label = gtk_label_new (_("Applications"));
+        gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
+                                  self->priv->applications_box,
+                                  label);
 
-        gtk_widget_show_all (self->priv->streams_box);
+        gtk_widget_show_all (GTK_WIDGET (self));
 
         g_signal_connect (self->priv->mixer_control,
                           "stream-added",
@@ -455,7 +484,7 @@
         GObject *dialog;
         dialog = g_object_new (GVC_TYPE_MIXER_DIALOG,
                                "icon-name", "multimedia-volume-control",
-                               "title", _("Volume Control"),
+                               "title", _("Sound Preferences"),
                                "has-separator", FALSE,
                                "mixer-control", control,
                                NULL);

Added: trunk/gnome-volume-control/src/gvc-sound-theme-chooser.c
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/gvc-sound-theme-chooser.c	Thu Nov  6 07:16:09 2008
@@ -0,0 +1,1507 @@
+/* -*- 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-chooser.h"
+#include "sound-theme-file-utils.h"
+
+#define GVC_SOUND_THEME_CHOOSER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_SOUND_THEME_CHOOSER, GvcSoundThemeChooserPrivate))
+
+struct GvcSoundThemeChooserPrivate
+{
+        GtkWidget *combo_box;
+        GtkWidget *treeview;
+};
+
+static void     gvc_sound_theme_chooser_class_init (GvcSoundThemeChooserClass *klass);
+static void     gvc_sound_theme_chooser_init       (GvcSoundThemeChooser      *sound_theme_chooser);
+static void     gvc_sound_theme_chooser_finalize   (GObject            *object);
+
+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_VISUAL_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 } },
+        { CATEGORY_BELL, SOUND_TYPE_VISUAL_BELL, NC_("Sound event", "Visual alert"), { 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 SOUND_THEME_KEY         "/desktop/gnome/sound/theme_name"
+#define INPUT_SOUNDS_KEY        "/desktop/gnome/sound/input_feedback_sounds"
+#define VISUAL_BELL_KEY         "/apps/metacity/general/visual_bell"
+#define VISUAL_BELL_TYPE_KEY    "/apps/metacity/general/visual_bell_type"
+#define AUDIO_BELL_KEY          "/apps/metacity/general/audible_bell"
+
+#define CUSTOM_THEME_NAME       "__custom"
+#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
+};
+
+#define SOUND_VISUAL_BELL_TITLEBAR SOUND_BUILTIN
+#define SOUND_VISUAL_BELL_SCREEN SOUND_CUSTOM
+
+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 && type != SOUND_TYPE_VISUAL_BELL) {
+                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)
+{
+        GtkTreeIter   iter;
+        GtkTreeModel *model;
+        GConfClient  *client;
+        char         *theme_name;
+
+        if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (chooser->priv->combo_box), &iter) == FALSE) {
+                return;
+        }
+
+        model = gtk_combo_box_get_model (GTK_COMBO_BOX (chooser->priv->combo_box));
+        gtk_tree_model_get (model, &iter, THEME_IDENTIFIER_COL, &theme_name, -1);
+
+        client = gconf_client_get_default ();
+        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));
+        }
+}
+
+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_combox_for_theme_name (GvcSoundThemeChooser *chooser,
+                           const char           *name)
+{
+        GtkTreeIter   iter;
+        GtkTreeModel *model;
+        gboolean      found;
+
+        g_debug ("setting theme %s", name ? name : "(null)");
+
+        /* If the name is empty, use "freedesktop" */
+        if (name == NULL || *name == '\0') {
+                name = "freedesktop";
+        }
+
+        model = gtk_combo_box_get_model (GTK_COMBO_BOX (chooser->priv->combo_box));
+
+        if (gtk_tree_model_get_iter_first (model, &iter) == FALSE) {
+                return;
+        }
+
+        do {
+                char *value;
+
+                gtk_tree_model_get (model, &iter, THEME_IDENTIFIER_COL, &value, -1);
+                found = (value != NULL && strcmp (value, name) == 0);
+                g_free (value);
+
+        } while (!found && gtk_tree_model_iter_next (model, &iter));
+
+        /* When we can't find the theme we need to set, try to set the default
+         * one "freedesktop" */
+        if (found) {
+                gtk_combo_box_set_active_iter (GTK_COMBO_BOX (chooser->priv->combo_box), &iter);
+        } else if (strcmp (name, "freedesktop") != 0) {
+                g_debug ("not found, falling back to fdo");
+                set_combox_for_theme_name (chooser, "freedesktop");
+        }
+}
+
+static void
+on_theme_changed (GConfClient          *client,
+                  guint                 cnxn_id,
+                  GConfEntry           *entry,
+                  GvcSoundThemeChooser *chooser)
+{
+        char *theme_name;
+
+        theme_name = gconf_client_get_string (client, SOUND_THEME_KEY, NULL);
+        set_combox_for_theme_name (chooser, theme_name);
+        g_free (theme_name);
+}
+
+static void
+setup_theme_selector (GvcSoundThemeChooser *chooser)
+{
+        GHashTable           *hash;
+        GtkListStore         *store;
+        GtkCellRenderer      *renderer;
+        const char * const   *data_dirs;
+        const char           *data_dir;
+        char                 *dir;
+        char                 *theme_name;
+        GConfClient          *client;
+        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 (chooser), 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 */
+        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 (chooser->priv->combo_box),
+                                 GTK_TREE_MODEL (store));
+
+        renderer = gtk_cell_renderer_text_new ();
+        gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (chooser->priv->combo_box),
+                                    renderer,
+                                    TRUE);
+        gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (chooser->priv->combo_box),
+                                        renderer,
+                                        "text", THEME_DISPLAY_COL,
+                                        NULL);
+
+        /* Setup the default as per the GConf setting */
+        client = gconf_client_get_default ();
+        theme_name = gconf_client_get_string (client, SOUND_THEME_KEY, NULL);
+        set_combox_for_theme_name (chooser, theme_name);
+        g_free (theme_name);
+
+        /* Listen for changes in GConf, and on the combobox */
+        gconf_client_notify_add (client,
+                                 SOUND_THEME_KEY,
+                                 (GConfClientNotifyFunc) on_theme_changed,
+                                 chooser,
+                                 NULL,
+                                 NULL);
+        g_object_unref (client);
+
+        g_signal_connect (chooser->priv->combo_box,
+                          "changed",
+                          G_CALLBACK (on_combobox_changed),
+                          chooser);
+
+}
+
+static int
+visual_bell_gconf_to_setting (GConfClient *client)
+{
+        char *value;
+        int retval;
+
+        if (gconf_client_get_bool (client, VISUAL_BELL_KEY, NULL) == FALSE) {
+                return SOUND_OFF;
+        }
+        value = gconf_client_get_string (client, VISUAL_BELL_TYPE_KEY, NULL);
+        if (value == NULL) {
+                return SOUND_VISUAL_BELL_SCREEN;
+        }
+        if (strcmp (value, "fullscreen") == 0) {
+                retval = SOUND_VISUAL_BELL_SCREEN;
+        } else if (strcmp (value, "frame_flash") == 0) {
+                retval = SOUND_VISUAL_BELL_TITLEBAR;
+        } else {
+                retval = SOUND_VISUAL_BELL_SCREEN;
+        }
+
+        g_free (value);
+
+        return retval;
+}
+
+static void
+visual_bell_setting_to_gconf (GConfClient *client,
+                              int          setting)
+{
+        const char *value;
+
+        value = NULL;
+
+        switch (setting) {
+        case SOUND_OFF:
+                break;
+        case SOUND_VISUAL_BELL_SCREEN:
+                value = "fullscreen";
+                break;
+        case SOUND_VISUAL_BELL_TITLEBAR:
+                value = "frame_flash";
+                break;
+        default:
+                g_assert_not_reached ();
+        }
+
+        gconf_client_set_string (client, VISUAL_BELL_TYPE_KEY, value ? value : "fullscreen", NULL);
+        gconf_client_set_bool (client, VISUAL_BELL_KEY, value != NULL, NULL);
+}
+
+static void
+play_sound_preview (GtkFileChooser *chooser,
+                    gpointer user_data)
+{
+        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;
+        int                 response;
+        char               *filename;
+        char               *path;
+        const char * const *data_dirs, *data_dir;
+        GtkFileFilter      *filter;
+        guint               i;
+
+        file_chooser = gtk_file_chooser_dialog_new (_("Select Sound File"),
+                                                    NULL, /* FIXME: */
+                                                    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 (type == SOUND_TYPE_VISUAL_BELL) {
+                return FALSE;
+        }
+
+        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 (type == SOUND_TYPE_VISUAL_BELL) {
+                return FALSE;
+        }
+
+        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;
+
+        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;
+
+        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 {
+                gint 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) {
+                        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;
+                        } else if (type == SOUND_TYPE_VISUAL_BELL) {
+                                GConfClient *client;
+
+                                client = gconf_client_get_default ();
+                                visual_bell_setting_to_gconf (client, setting);
+                                g_object_unref (client);
+                                gtk_tree_store_set (GTK_TREE_STORE (tree_model),
+                                                    &tree_iter,
+                                                    SETTING_COL, setting,
+                                                    -1);
+                                break;
+                        }
+                        g_assert_not_reached ();
+                }
+        } while (gtk_tree_model_iter_next (model, &iter));
+        g_free (old_filename);
+}
+
+static void
+fill_visual_bell_model (GtkListStore *store)
+{
+        GtkTreeIter iter;
+
+        gtk_list_store_clear (store);
+
+        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, _("Flash screen"),
+                                           1, SOUND_VISUAL_BELL_SCREEN,
+                                           -1);
+        gtk_list_store_insert_with_values (store, &iter, G_MAXINT,
+                                           0, _("Flash window"),
+                                           1, SOUND_VISUAL_BELL_TITLEBAR,
+                                           -1);
+}
+
+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,
+                             GvcSoundThemeChooser *chooser)
+{
+        GtkTreeModel *model;
+        GtkTreeModel *store;
+        GtkTreeIter   iter;
+        SoundType     type;
+        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);
+        if (type == SOUND_TYPE_VISUAL_BELL) {
+                fill_visual_bell_model (GTK_LIST_STORE (store));
+        } else {
+                fill_custom_model (GTK_LIST_STORE (store), filename);
+        }
+        g_free (filename);
+}
+
+static gboolean
+play_sound_at_path (GtkWidget         *tree_view,
+                    GtkTreeViewColumn *column,
+                    GtkTreePath       *path)
+{
+        GObject *preview_column;
+
+        preview_column = g_object_get_data (G_OBJECT (tree_view), "preview-column");
+        if (column == (GtkTreeViewColumn *) preview_column) {
+                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;
+        }
+        return FALSE;
+}
+
+static gboolean
+on_treeview_button_press_event (GtkWidget            *tree_view,
+                                GdkEventButton       *event,
+                                GvcSoundThemeChooser *chooser)
+{
+        GtkTreePath       *path;
+        GtkTreeViewColumn *column;
+        GdkEventButton    *button_event = (GdkEventButton *) event;
+        gboolean           res;
+
+        res = FALSE;
+
+        if (event->type != GDK_BUTTON_PRESS) {
+                return TRUE;
+        }
+
+        if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (tree_view),
+                                           button_event->x, button_event->y,
+                                           &path, &column, NULL, NULL)) {
+                res = play_sound_at_path (tree_view, column, path);
+                gtk_tree_path_free (path);
+        }
+
+        return res;
+}
+
+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 (type != SOUND_TYPE_VISUAL_BELL) {
+                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);
+                }
+        } else {
+                if (setting == SOUND_OFF) {
+                        g_object_set (cell,
+                                      "text", _("Disabled"),
+                                      NULL);
+                } else if (setting == SOUND_VISUAL_BELL_SCREEN) {
+                        g_object_set (cell,
+                                      "text", _("Flash screen"),
+                                      NULL);
+                } else if (setting == SOUND_VISUAL_BELL_TITLEBAR) {
+                        g_object_set (cell,
+                                      "text", _("Flash window"),
+                                      NULL);
+                }
+        }
+        g_free (filename);
+}
+
+typedef GtkCellRendererPixbuf      ActivatableCellRendererPixbuf;
+typedef GtkCellRendererPixbufClass ActivatableCellRendererPixbufClass;
+
+#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)
+{
+        GtkTreeViewColumn *preview_column;
+        GtkTreePath *path;
+        gboolean res;
+
+        preview_column = g_object_get_data (G_OBJECT (widget), "preview-column");
+        path = gtk_tree_path_new_from_string (path_string);
+        res = play_sound_at_path (widget, preview_column, 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;
+}
+
+/* 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;
+
+        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 void
+on_input_feedback_changed (GConfClient          *client,
+                           guint                 cnxn_id,
+                           GConfEntry           *entry,
+                           GvcSoundThemeChooser *chooser)
+{
+        gboolean input_feedback_enabled;
+
+        input_feedback_enabled = gconf_client_get_bool (client, INPUT_SOUNDS_KEY, NULL);
+        set_input_feedback_enabled (chooser, input_feedback_enabled);
+}
+/* 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;
+        gboolean enabled = GPOINTER_TO_INT (data);
+
+        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,
+                                    SENSITIVE_COL, enabled,
+                                    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));
+}
+
+static void
+on_audible_bell_changed (GConfClient          *client,
+                         guint                 cnxn_id,
+                         GConfEntry           *entry,
+                         GvcSoundThemeChooser *chooser)
+{
+        gboolean audio_bell_enabled;
+
+        audio_bell_enabled = gconf_client_get_bool (client, AUDIO_BELL_KEY, NULL);
+        set_audible_bell_enabled (chooser, audio_bell_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
+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;
+        gboolean           input_feedback_enabled;
+        gboolean           audible_bell_enabled;
+        int                visual_bell_setting;
+        char              *theme_name;
+        guint              i;
+
+        client = gconf_client_get_default ();
+        visual_bell_setting = visual_bell_gconf_to_setting (client);
+
+        /* 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,
+                                                           NULL);
+        gtk_tree_view_append_column (GTK_TREE_VIEW (chooser->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),
+                          chooser);
+        g_signal_connect (renderer,
+                          "editing-started",
+                          G_CALLBACK (on_combobox_editing_started),
+                          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,
+                                                           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);
+
+        /* 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 (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);
+
+        g_signal_connect (chooser->priv->treeview,
+                          "button-press-event",
+                          G_CALLBACK (on_treeview_button_press_event),
+                          chooser);
+
+        /* Fill in the model */
+        type = CATEGORY_INVALID;
+
+        for (i = 0; ; i++) {
+                GtkTreeIter *_parent;
+
+                if (sounds[i].category == -1) {
+                        break;
+                }
+
+                if (sounds[i].type == SOUND_TYPE_VISUAL_BELL
+                    && have_xkb == FALSE) {
+                        continue;
+                }
+
+                /* 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 == SOUND_TYPE_VISUAL_BELL) {
+                        gtk_tree_store_insert_with_values (store, &iter, _parent, G_MAXINT,
+                                                           DISPLAY_COL, g_dpgettext2 (NULL, "Sound event", sounds[i].display_name),
+                                                           SETTING_COL, visual_bell_setting,
+                                                           TYPE_COL, sounds[i].type,
+                                                           HAS_PREVIEW_COL, FALSE,
+                                                           SENSITIVE_COL, TRUE,
+                                                           -1);
+                } else 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));
+
+        /* Listen to GConf for a bunch of keys */
+        input_feedback_enabled = gconf_client_get_bool (client, INPUT_SOUNDS_KEY, NULL);
+        if (input_feedback_enabled == FALSE) {
+                set_input_feedback_enabled (chooser, FALSE);
+        }
+        gconf_client_notify_add (client,
+                                 INPUT_SOUNDS_KEY,
+                                 (GConfClientNotifyFunc) on_input_feedback_changed,
+                                 chooser,
+                                 NULL,
+                                 NULL);
+
+        audible_bell_enabled = gconf_client_get_bool (client, AUDIO_BELL_KEY, NULL);
+        if (audible_bell_enabled == FALSE) {
+                set_audible_bell_enabled (chooser, FALSE);
+        }
+        gconf_client_notify_add (client,
+                                 AUDIO_BELL_KEY,
+                                 (GConfClientNotifyFunc) on_audible_bell_changed,
+                                 chooser,
+                                 NULL,
+                                 NULL);
+
+        /* Setup the default values if we're using the custom theme */
+        theme_name = gconf_client_get_string (client, SOUND_THEME_KEY, NULL);
+        if (theme_name != NULL && strcmp (theme_name, CUSTOM_THEME_NAME) == 0) {
+                gtk_tree_model_foreach (GTK_TREE_MODEL (store),
+                                        theme_changed_custom_init,
+                                        NULL);
+        }
+        g_free (theme_name);
+
+        g_object_unref (client);
+}
+
+static GObject *
+gvc_sound_theme_chooser_constructor (GType                  type,
+                                     guint                  n_construct_properties,
+                                     GObjectConstructParam *construct_params)
+{
+        GObject              *object;
+        GvcSoundThemeChooser *self;
+
+        object = G_OBJECT_CLASS (gvc_sound_theme_chooser_parent_class)->constructor (type, n_construct_properties, construct_params);
+
+        self = GVC_SOUND_THEME_CHOOSER (object);
+
+        setup_theme_selector (self);
+        setup_theme_custom_selector (self, TRUE);
+
+        return object;
+}
+
+static void
+gvc_sound_theme_chooser_class_init (GvcSoundThemeChooserClass *klass)
+{
+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->constructor = gvc_sound_theme_chooser_constructor;
+        object_class->finalize = gvc_sound_theme_chooser_finalize;
+
+        g_type_class_add_private (klass, sizeof (GvcSoundThemeChooserPrivate));
+}
+
+static void
+_gtk_label_make_bold (GtkLabel *label)
+{
+        PangoFontDescription *font_desc;
+
+        font_desc = pango_font_description_new ();
+
+        pango_font_description_set_weight (font_desc,
+                                           PANGO_WEIGHT_BOLD);
+
+        /* This will only affect the weight of the font, the rest is
+         * from the current state of the widget, which comes from the
+         * theme or user prefs, since the font desc only has the
+         * weight flag turned on.
+         */
+        gtk_widget_modify_font (GTK_WIDGET (label), font_desc);
+
+        pango_font_description_free (font_desc);
+}
+
+static void
+gvc_sound_theme_chooser_init (GvcSoundThemeChooser *chooser)
+{
+        GtkWidget *frame;
+        GtkWidget *box;
+        GtkWidget *label;
+        GtkWidget *alignment;
+
+        chooser->priv = GVC_SOUND_THEME_CHOOSER_GET_PRIVATE (chooser);
+
+        frame = gtk_frame_new (_("Sound Theme"));
+        label = gtk_frame_get_label_widget (GTK_FRAME (frame));
+        _gtk_label_make_bold (GTK_LABEL (label));
+        gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
+        gtk_box_pack_start (GTK_BOX (chooser), frame, FALSE, FALSE, 0);
+
+        alignment = gtk_alignment_new (0, 0, 1, 1);
+        gtk_container_add (GTK_CONTAINER (frame), alignment);
+        gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 12, 0, 12, 0);
+
+        chooser->priv->combo_box = gtk_combo_box_new ();
+        gtk_container_add (GTK_CONTAINER (alignment), chooser->priv->combo_box);
+
+        frame = gtk_frame_new (_("Alerts and Sound Effects"));
+        label = gtk_frame_get_label_widget (GTK_FRAME (frame));
+        _gtk_label_make_bold (GTK_LABEL (label));
+        gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
+        gtk_box_pack_start (GTK_BOX (chooser), frame, TRUE, TRUE, 0);
+
+        alignment = gtk_alignment_new (0, 0, 1, 1);
+        gtk_container_add (GTK_CONTAINER (frame), alignment);
+        gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 12, 0, 12, 0);
+
+        chooser->priv->treeview = gtk_tree_view_new ();
+        box = gtk_scrolled_window_new (NULL, NULL);
+        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (box),
+                                             GTK_SHADOW_IN);
+        gtk_container_add (GTK_CONTAINER (box), chooser->priv->treeview);
+        gtk_container_add (GTK_CONTAINER (alignment), box);
+}
+
+static void
+gvc_sound_theme_chooser_finalize (GObject *object)
+{
+        GvcSoundThemeChooser *sound_theme_chooser;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GVC_IS_SOUND_THEME_CHOOSER (object));
+
+        sound_theme_chooser = GVC_SOUND_THEME_CHOOSER (object);
+
+        g_return_if_fail (sound_theme_chooser->priv != NULL);
+
+        G_OBJECT_CLASS (gvc_sound_theme_chooser_parent_class)->finalize (object);
+}
+
+GtkWidget *
+gvc_sound_theme_chooser_new (void)
+{
+        GObject *chooser;
+        chooser = g_object_new (GVC_TYPE_SOUND_THEME_CHOOSER,
+                                "spacing", 12,
+                                NULL);
+        return GTK_WIDGET (chooser);
+}

Added: trunk/gnome-volume-control/src/gvc-sound-theme-chooser.h
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/gvc-sound-theme-chooser.h	Thu Nov  6 07:16:09 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_CHOOSER_H
+#define __GVC_SOUND_THEME_CHOOSER_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GVC_TYPE_SOUND_THEME_CHOOSER         (gvc_sound_theme_chooser_get_type ())
+#define GVC_SOUND_THEME_CHOOSER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_SOUND_THEME_CHOOSER, GvcSoundThemeChooser))
+#define GVC_SOUND_THEME_CHOOSER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_SOUND_THEME_CHOOSER, GvcSoundThemeChooserClass))
+#define GVC_IS_SOUND_THEME_CHOOSER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_SOUND_THEME_CHOOSER))
+#define GVC_IS_SOUND_THEME_CHOOSER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_SOUND_THEME_CHOOSER))
+#define GVC_SOUND_THEME_CHOOSER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_SOUND_THEME_CHOOSER, GvcSoundThemeChooserClass))
+
+typedef struct GvcSoundThemeChooserPrivate GvcSoundThemeChooserPrivate;
+
+typedef struct
+{
+        GtkVBox                      parent;
+        GvcSoundThemeChooserPrivate *priv;
+} GvcSoundThemeChooser;
+
+typedef struct
+{
+        GtkVBoxClass          parent_class;
+} GvcSoundThemeChooserClass;
+
+GType               gvc_sound_theme_chooser_get_type            (void);
+
+GtkWidget *         gvc_sound_theme_chooser_new                 (void);
+
+G_END_DECLS
+
+#endif /* __GVC_SOUND_THEME_CHOOSER_H */

Added: trunk/gnome-volume-control/src/sound-theme-file-utils.c
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/sound-theme-file-utils.c	Thu Nov  6 07:16:09 2008
@@ -0,0 +1,231 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ * Copyright (C) 2008 Bastien Nocera <hadess hadess net>
+ *
+ * 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, 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 <glib/gstdio.h>
+#include <gio/gio.h>
+#include <utime.h>
+
+#include "sound-theme-file-utils.h"
+
+#define CUSTOM_THEME_NAME       "__custom"
+
+/* This function needs to be called after each individual
+ * changeset to the theme */
+void
+custom_theme_update_time (void)
+{
+        char *path;
+
+        path = custom_theme_dir_path (NULL);
+        utime (path, NULL);
+        g_free (path);
+}
+
+char *
+custom_theme_dir_path (const char *child)
+{
+        static char *dir = NULL;
+        const char *data_dir;
+
+        if (dir == NULL) {
+                data_dir = g_get_user_data_dir ();
+                dir = g_build_filename (data_dir, "sounds", CUSTOM_THEME_NAME, NULL);
+        }
+        if (child == NULL)
+                return g_strdup (dir);
+
+        return g_build_filename (dir, child, NULL);
+}
+
+static gboolean
+directory_delete_recursive (GFile *directory, GError **error)
+{
+        GFileEnumerator *enumerator;
+        GFileInfo *info;
+        gboolean success = TRUE;
+
+        enumerator = g_file_enumerate_children (directory,
+                                                G_FILE_ATTRIBUTE_STANDARD_NAME ","
+                                                G_FILE_ATTRIBUTE_STANDARD_TYPE,
+                                                G_FILE_QUERY_INFO_NONE,
+                                                NULL, error);
+        if (enumerator == NULL)
+                return FALSE;
+
+        while (success &&
+               (info = g_file_enumerator_next_file (enumerator, NULL, NULL))) {
+                GFile *child;
+
+                child = g_file_get_child (directory, g_file_info_get_name (info));
+
+                if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
+                        success = directory_delete_recursive (child, error);
+                }
+                g_object_unref (info);
+
+                if (success)
+                        success = g_file_delete (child, NULL, error);
+        }
+        g_file_enumerator_close (enumerator, NULL, NULL);
+
+        if (success)
+                success = g_file_delete (directory, NULL, error);
+
+        return success;
+}
+
+/**
+ * capplet_file_delete_recursive :
+ * @file :
+ * @error  :
+ *
+ * A utility routine to delete files and/or directories,
+ * including non-empty directories.
+ **/
+gboolean
+capplet_file_delete_recursive (GFile *file, GError **error)
+{
+        GFileInfo *info;
+        GFileType type;
+
+        g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+        info = g_file_query_info (file,
+                                  G_FILE_ATTRIBUTE_STANDARD_TYPE,
+                                  G_FILE_QUERY_INFO_NONE,
+                                  NULL, error);
+        if (info == NULL)
+                return FALSE;
+
+        type = g_file_info_get_file_type (info);
+        g_object_unref (info);
+
+        if (type == G_FILE_TYPE_DIRECTORY)
+                return directory_delete_recursive (file, error);
+        else
+                return g_file_delete (file, NULL, error);
+}
+
+void
+delete_custom_theme_dir (void)
+{
+        char *dir;
+        GFile *file;
+
+        dir = custom_theme_dir_path (NULL);
+        file = g_file_new_for_path (dir);
+        g_free (dir);
+        capplet_file_delete_recursive (file, NULL);
+        g_object_unref (file);
+
+        g_debug ("deleted the custom theme dir");
+}
+
+static void
+delete_one_file (const char *sound_name, const char *pattern)
+{
+        GFile *file;
+        char *name, *filename;
+
+        name = g_strdup_printf (pattern, sound_name);
+        filename = custom_theme_dir_path (name);
+        g_free (name);
+        file = g_file_new_for_path (filename);
+        g_free (filename);
+        capplet_file_delete_recursive (file, NULL);
+        g_object_unref (file);
+}
+
+void
+delete_old_files (char **sounds)
+{
+        guint i;
+
+        for (i = 0; sounds[i] != NULL; i++) {
+                delete_one_file (sounds[i], "%s.ogg");
+        }
+}
+
+void
+delete_disabled_files (char **sounds)
+{
+        guint i;
+
+        for (i = 0; sounds[i] != NULL; i++)
+                delete_one_file (sounds[i], "%s.disabled");
+}
+
+static void
+create_one_file (GFile *file)
+{
+        GFileOutputStream* stream;
+
+        stream = g_file_create (file, G_FILE_CREATE_NONE, NULL, NULL);
+        if (stream != NULL) {
+                g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, NULL);
+                g_object_unref (stream);
+        }
+}
+
+void
+add_disabled_file (char **sounds)
+{
+        guint i;
+
+        for (i = 0; sounds[i] != NULL; i++) {
+                GFile *file;
+                char *name, *filename;
+
+                name = g_strdup_printf ("%s.disabled", sounds[i]);
+                filename = custom_theme_dir_path (name);
+                g_free (name);
+                file = g_file_new_for_path (filename);
+                g_free (filename);
+
+                create_one_file (file);
+                g_object_unref (file);
+        }
+}
+
+void
+add_custom_file (char **sounds, const char *filename)
+{
+        guint i;
+
+        for (i = 0; sounds[i] != NULL; i++) {
+                GFile *file;
+                char *name, *path;
+
+                /* We use *.ogg because it's the first type of file that
+                 * libcanberra looks at */
+                name = g_strdup_printf ("%s.ogg", sounds[i]);
+                path = custom_theme_dir_path (name);
+                g_free (name);
+                /* In case there's already a link there, delete it */
+                g_unlink (path);
+                file = g_file_new_for_path (path);
+                g_free (path);
+
+                /* Create the link */
+                g_file_make_symbolic_link (file, filename, NULL, NULL);
+                g_object_unref (file);
+        }
+}
+

Added: trunk/gnome-volume-control/src/sound-theme-file-utils.h
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/sound-theme-file-utils.h	Thu Nov  6 07:16:09 2008
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ * Copyright (C) 2008 Bastien Nocera <hadess hadess net>
+ *
+ * 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, 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 __SOUND_THEME_FILE_UTILSHH__
+#define __SOUND_THEME_HH__
+
+#include <gio/gio.h>
+
+char *custom_theme_dir_path (const char *child);
+
+void delete_custom_theme_dir (void);
+void delete_old_files (char **sounds);
+void delete_disabled_files (char **sounds);
+
+void add_disabled_file (char **sounds);
+void add_custom_file (char **sounds, const char *filename);
+
+void custom_theme_update_time (void);
+
+#endif /* __SOUND_THEME_FILE_UTILS_HH__ */



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