gnome-control-center r8802 - in trunk: . capplets/appearance capplets/common capplets/sound



Author: hadess
Date: Sun Jul 27 23:32:35 2008
New Revision: 8802
URL: http://svn.gnome.org/viewvc/gnome-control-center?rev=8802&view=rev

Log:
2008-07-28  Bastien Nocera  <hadess hadess net>

	* Makefile.am:
	* configure.in: Remove libsounds and esound usage, check for
	libcanberra instead (Closes: #542979)

2008-07-28  Bastien Nocera  <hadess hadess net>

	* theme-util.c:
	* theme-util.h: Remove the directory deletion helpers, and
	move them to the common sub-directory

2008-07-28  Bastien Nocera  <hadess hadess net>

	* Makefile.am:
	* capplet-util.c (directory_delete_recursive),
	(capplet_file_delete_recursive):
	* capplet-util.h: Move directory deletion helper function
	from the appearance capplet into a common directory

2008-07-28  Bastien Nocera  <hadess hadess net>

	* Makefile.am:
	* sound-properties-capplet.c (create_dialog), (setup_dialog),
	(get_legacy_settings):
	* sound-properties.glade:
	* sound-theme-definition.h:
	* sound-theme-file-utils.[ch]:
	* sound-theme.[ch]: Remove separate bell settings tab, remove
	libsounds dependency, add freedesktop sound theme support through
	libcanberra (Closes: #542979)



Added:
   trunk/capplets/sound/sound-theme-definition.h
   trunk/capplets/sound/sound-theme-file-utils.c
   trunk/capplets/sound/sound-theme-file-utils.h
   trunk/capplets/sound/sound-theme.c
   trunk/capplets/sound/sound-theme.h
Modified:
   trunk/ChangeLog
   trunk/capplets/appearance/ChangeLog
   trunk/capplets/appearance/theme-util.c
   trunk/capplets/appearance/theme-util.h
   trunk/capplets/common/ChangeLog
   trunk/capplets/common/Makefile.am
   trunk/capplets/common/capplet-util.c
   trunk/capplets/common/capplet-util.h
   trunk/capplets/sound/ChangeLog
   trunk/capplets/sound/Makefile.am
   trunk/capplets/sound/sound-properties-capplet.c
   trunk/capplets/sound/sound-properties.glade

Modified: trunk/capplets/appearance/theme-util.c
==============================================================================
--- trunk/capplets/appearance/theme-util.c	(original)
+++ trunk/capplets/appearance/theme-util.c	Sun Jul 27 23:32:35 2008
@@ -28,67 +28,6 @@
 
 #include "theme-util.h"
 
-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;
-}
-
-gboolean
-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);
-}
-
 gboolean
 theme_is_writable (const gpointer theme)
 {

Modified: trunk/capplets/appearance/theme-util.h
==============================================================================
--- trunk/capplets/appearance/theme-util.h	(original)
+++ trunk/capplets/appearance/theme-util.h	Sun Jul 27 23:32:35 2008
@@ -61,4 +61,4 @@
 
 void     theme_install_file (GtkWindow *parent, const gchar *path);
 gboolean packagekit_available (void);
-gboolean file_delete_recursive (GFile *directory, GError **error);
+

Modified: trunk/capplets/common/Makefile.am
==============================================================================
--- trunk/capplets/common/Makefile.am	(original)
+++ trunk/capplets/common/Makefile.am	Sun Jul 27 23:32:35 2008
@@ -11,7 +11,8 @@
 	$(DBUS_CFLAGS)							\
 	$(GNOME_DESKTOP_CFLAGS)						\
 	$(METACITY_CFLAGS)						\
-	$(GSD_DBUS_CFLAGS)
+	$(GSD_DBUS_CFLAGS)						\
+	$(GIO_CFLAGS)
 
 
 noinst_LTLIBRARIES = libcommon.la
@@ -42,9 +43,10 @@
 
 libcommon_la_LIBADD =							\
 	$(top_builddir)/libwindow-settings/libgnome-window-settings.la  \
-	$(METACITY_LIBS)							\
+	$(METACITY_LIBS)						\
 	$(DBUS_LIBS)							\
-	$(GNOME_DESKTOP_LIBS)
+	$(GNOME_DESKTOP_LIBS)						\
+	$(GIO_LIBS)
 
 gnome_theme_test_SOURCES = \
 	gnome-theme-test.c
@@ -54,4 +56,4 @@
 	$(GNOMECC_CAPPLETS_LIBS)
 
 noinst_PROGRAMS = \
-	gnome-theme-test
\ No newline at end of file
+	gnome-theme-test

Modified: trunk/capplets/common/capplet-util.c
==============================================================================
--- trunk/capplets/common/capplet-util.c	(original)
+++ trunk/capplets/common/capplet-util.c	Sun Jul 27 23:32:35 2008
@@ -96,3 +96,73 @@
 	gtk_window_set_default_icon_name (icon_file_name);
 	gtk_window_set_icon_name (GTK_WINDOW (window), icon_file_name);
 }
+
+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);
+}
+

Modified: trunk/capplets/common/capplet-util.h
==============================================================================
--- trunk/capplets/common/capplet-util.h	(original)
+++ trunk/capplets/common/capplet-util.h	Sun Jul 27 23:32:35 2008
@@ -24,6 +24,7 @@
 #ifndef __CAPPLET_UTIL_H
 #define __CAPPLET_UTIL_H
 
+#include <gio/gio.h>
 #include <gnome.h>
 #include <gconf/gconf.h>
 #include <gconf/gconf-changeset.h>
@@ -46,5 +47,6 @@
 
 void capplet_help (GtkWindow *parent, char const *helpfile, char const *section);
 void capplet_set_icon (GtkWidget *window, char const *icon_file_name);
+gboolean capplet_file_delete_recursive (GFile *directory, GError **error);
 
 #endif /* __CAPPLET_UTIL_H */

Modified: trunk/capplets/sound/Makefile.am
==============================================================================
--- trunk/capplets/sound/Makefile.am	(original)
+++ trunk/capplets/sound/Makefile.am	Sun Jul 27 23:32:35 2008
@@ -4,15 +4,23 @@
 bin_PROGRAMS = gnome-sound-properties
 
 gnome_sound_properties_LDADD = \
-	$(top_builddir)/libsounds/libsounds.la \
 	$(GNOMECC_CAPPLETS_LIBS) \
-	$(SOUND_CAPPLET_LIBS) \
+	$(CANBERRA_LIBS) $(SOUND_CAPPLET_LIBS) \
 	$(HAL_LIBS)
-gnome_sound_properties_SOURCES = sound-properties-capplet.c pipeline-tests.h pipeline-tests.c mixer-support.h mixer-support.c
+gnome_sound_properties_SOURCES =				\
+	sound-properties-capplet.c				\
+	pipeline-tests.h					\
+	pipeline-tests.c					\
+	mixer-support.h						\
+	mixer-support.c						\
+	sound-theme.c						\
+	sound-theme.h						\
+	sound-theme-file-utils.c				\
+	sound-theme-file-utils.h
 
 INCLUDES = \
 	$(GNOMECC_CAPPLETS_CFLAGS) \
-	$(SOUND_CAPPLET_CFLAGS) \
+	$(CANBERRA_CFLAGS) $(SOUND_CAPPLET_CFLAGS) \
 	$(HAL_CFLAGS) \
 	-DGNOMELOCALEDIR="\"$(datadir)/locale\"" \
 	-DGNOMECC_DATA_DIR="\"$(pkgdatadir)\"" \

Modified: trunk/capplets/sound/sound-properties-capplet.c
==============================================================================
--- trunk/capplets/sound/sound-properties-capplet.c	(original)
+++ trunk/capplets/sound/sound-properties-capplet.c	Sun Jul 27 23:32:35 2008
@@ -35,7 +35,6 @@
 
 #include "capplet-util.h"
 #include "gconf-property-editor.h"
-#include "libsounds/sound-view.h"
 
 #include <glade/glade.h>
 
@@ -53,8 +52,8 @@
 
 #include "activate-settings-daemon.h"
 #include "pipeline-tests.h"
-
 #include "mixer-support.h"
+#include "sound-theme.h"
 
 typedef enum {
 	AUDIO_PLAYBACK,
@@ -72,13 +71,11 @@
 	const gchar *test_pipeline;
 } DeviceChooser;
 
-#define ENABLE_ESD_KEY             "/desktop/gnome/sound/enable_esd"
 #define EVENT_SOUNDS_KEY           "/desktop/gnome/sound/event_sounds"
+#define INPUT_SOUNDS_KEY           "/desktop/gnome/sound/input_feedback_sounds"
 #define DEFAULT_MIXER_DEVICE_KEY   "/desktop/gnome/sound/default_mixer_device"
 #define DEFAULT_MIXER_TRACKS_KEY   "/desktop/gnome/sound/default_mixer_tracks"
-#define VISUAL_BELL_KEY            "/apps/metacity/general/visual_bell"
 #define AUDIO_BELL_KEY             "/apps/metacity/general/audible_bell"
-#define VISUAL_BELL_TYPE_KEY       "/apps/metacity/general/visual_bell_type"
 #define GST_GCONF_DIR              "/system/gstreamer/0.10"
 
 #define AUDIO_TEST_SOURCE          "audiotestsrc wave=sine freq=512"
@@ -88,7 +85,6 @@
 
 /* Capplet-specific prototypes */
 
-static SoundProperties *props = NULL;
 static GConfClient     *gconf_client = NULL;
 static GtkWidget       *dialog_win = NULL;
 static GList           *device_choosers = NULL;
@@ -111,78 +107,18 @@
 	return have_xkb;
 }
 
-static void
-props_changed_cb (SoundProperties *p, SoundEvent *event, gpointer data)
-{
-	sound_properties_user_save (p);
-}
-
-
-
-static GConfEnumStringPair bell_flash_enums[] = {
-	{ 0, "frame_flash" },
-	{ 1, "fullscreen" },
-	{ -1, NULL }
-};
-
-static GConfValue *
-bell_flash_from_widget (GConfPropertyEditor *peditor, const GConfValue *value)
-{
-	GConfValue *new_value;
-
-	new_value = gconf_value_new (GCONF_VALUE_STRING);
-	gconf_value_set_string (new_value,
-				gconf_enum_to_string (bell_flash_enums, gconf_value_get_int (value)));
-
-	return new_value;
-}
-
-static GConfValue *
-bell_flash_to_widget (GConfPropertyEditor *peditor, const GConfValue *value)
-{
-	GConfValue *new_value;
-	const gchar *str;
-	gint val = 2;
-
-	str = (value && (value->type == GCONF_VALUE_STRING)) ? gconf_value_get_string (value) : NULL;
-
-	new_value = gconf_value_new (GCONF_VALUE_INT);
-	if (value->type == GCONF_VALUE_STRING) {
-		gconf_string_to_enum (bell_flash_enums,
-				      str,
-				      &val);
-	}
-	gconf_value_set_int (new_value, val);
-
-	return new_value;
-}
-
 static GladeXML *
 create_dialog (void)
 {
 	GladeXML *dialog;
-	GtkWidget *widget, *box, *view, *image;
-
-	dialog = glade_xml_new (GNOMECC_GLADE_DIR "/sound-properties.glade", "sound_prefs_dialog", NULL);
-	if (dialog == NULL)
-	  return NULL;
-
-	widget = glade_xml_get_widget (dialog, "sound_prefs_dialog");
-
-	props = sound_properties_new ();
-	sound_properties_add_defaults (props, NULL);
-	g_signal_connect (G_OBJECT (props), "event_changed",
-			  (GCallback) props_changed_cb, NULL);
-	view = sound_view_new (props);
-	box = glade_xml_get_widget (dialog, "events_vbox");
-	gtk_box_pack_start (GTK_BOX (box), view, TRUE, TRUE, 0);
-	gtk_widget_show_all (view);
-
-	g_signal_connect_swapped (G_OBJECT (widget), "destroy",
-				  (GCallback) gtk_object_destroy, props);
+	GtkWidget *image;
 
-	gtk_image_set_from_file (GTK_IMAGE (WID ("bell_image")),
-				 GNOMECC_DATA_DIR "/pixmaps/visual-bell.png");
+	dialog = glade_xml_new ("sound-properties.glade", "sound_prefs_dialog", NULL);
+	if (dialog == NULL) {
+		dialog = glade_xml_new (GNOMECC_GLADE_DIR "/sound-properties.glade", "sound_prefs_dialog", NULL);
+		if (dialog == NULL)
+			return NULL;
+	}
 
 	image = gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON);
 	gtk_button_set_image (GTK_BUTTON (WID ("sounds_playback_test")), image);
@@ -196,10 +132,6 @@
 	image = gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON);
 	gtk_button_set_image (GTK_BUTTON (WID ("chat_audio_capture_test")), image);
 
-	if (!CheckXKB()) {
-		gtk_widget_set_sensitive (WID ("bell_flash_alignment"), FALSE);
-	}
-
 	return dialog;
 }
 
@@ -1082,29 +1014,16 @@
 			      WID ("chat_audio_capture_test"),
 			      "gconfaudiosrc" AUDIO_TEST_IN_BETWEEN "gconfaudiosink profile=chat");
 
-#ifdef HAVE_ESD
-	peditor = gconf_peditor_new_boolean (NULL, ENABLE_ESD_KEY, WID ("enable_toggle"), NULL);
-	gconf_peditor_widget_set_guard (GCONF_PROPERTY_EDITOR (peditor), WID ("events_toggle"));
-	gconf_peditor_widget_set_guard (GCONF_PROPERTY_EDITOR (peditor), WID ("events_vbox"));
-#else
-	gtk_widget_hide (WID ("enable_toggle"));
-#endif
-	gconf_peditor_new_boolean (NULL, EVENT_SOUNDS_KEY, WID ("events_toggle"), NULL);
-
+	peditor = gconf_peditor_new_boolean (NULL, EVENT_SOUNDS_KEY, WID ("events_toggle"), NULL);
+	gconf_peditor_widget_set_guard (GCONF_PROPERTY_EDITOR (peditor), WID ("input_feedback_toggle"));
+	gconf_peditor_widget_set_guard (GCONF_PROPERTY_EDITOR (peditor), WID ("sound_theme_combobox"));
+	gconf_peditor_widget_set_guard (GCONF_PROPERTY_EDITOR (peditor), WID ("sounds_treeview"));
+	gconf_peditor_new_boolean (NULL, INPUT_SOUNDS_KEY, WID ("input_feedback_toggle"), NULL);
 	gconf_peditor_new_boolean (NULL, AUDIO_BELL_KEY, WID ("bell_audible_toggle"), NULL);
 
-	peditor = gconf_peditor_new_boolean (NULL, VISUAL_BELL_KEY, WID ("bell_visual_toggle"), NULL);
-	gconf_peditor_widget_set_guard (GCONF_PROPERTY_EDITOR (peditor), WID ("bell_flash_vbox"));
-
-	/* peditor not so convenient for the radiobuttons */
-	gconf_peditor_new_select_radio (NULL,
-					VISUAL_BELL_TYPE_KEY,
-					gtk_radio_button_get_group (GTK_RADIO_BUTTON (WID ("bell_flash_window_radio"))),
-					"conv-to-widget-cb", bell_flash_to_widget,
-					"conv-from-widget-cb", bell_flash_from_widget,
-					NULL);
-
 	setup_default_mixer (dialog);
+	setup_sound_theme (dialog);
+	setup_sound_theme_custom (dialog, CheckXKB());
 }
 
 /* get_legacy_settings
@@ -1123,7 +1042,6 @@
 	gboolean val_bool, def;
 
 	client = gconf_client_get_default ();
-	COPY_FROM_LEGACY (bool, "/desktop/gnome/sound/enable_esd", "/sound/system/settings/start_esd=false");
 	COPY_FROM_LEGACY (bool, "/desktop/gnome/sound/event_sounds", "/sound/system/settings/event_sounds=false");
 	g_object_unref (G_OBJECT (client));
 }

Modified: trunk/capplets/sound/sound-properties.glade
==============================================================================
--- trunk/capplets/sound/sound-properties.glade	(original)
+++ trunk/capplets/sound/sound-properties.glade	Sun Jul 27 23:32:35 2008
@@ -211,7 +211,7 @@
 	  <property name="enable_popup">False</property>
 
 	  <child>
-	    <widget class="GtkVBox" id="vbox13">
+	    <widget class="GtkVBox" id="devices_vbox">
 	      <property name="border_width">12</property>
 	      <property name="visible">True</property>
 	      <property name="homogeneous">False</property>
@@ -940,33 +940,13 @@
 	  </child>
 
 	  <child>
-	    <widget class="GtkVBox" id="vbox1">
+	    <widget class="GtkVBox" id="sounds_vbox">
 	      <property name="border_width">12</property>
 	      <property name="visible">True</property>
 	      <property name="homogeneous">False</property>
 	      <property name="spacing">6</property>
 
 	      <child>
-		<widget class="GtkCheckButton" id="enable_toggle">
-		  <property name="visible">True</property>
-		  <property name="can_focus">True</property>
-		  <property name="label" translatable="yes">E_nable software sound mixing</property>
-		  <property name="use_underline">True</property>
-		  <property name="relief">GTK_RELIEF_NORMAL</property>
-		  <property name="focus_on_click">True</property>
-		  <property name="active">False</property>
-		  <property name="inconsistent">False</property>
-		  <property name="draw_indicator">True</property>
-		  <signal name="toggled" handler="enable_toggled_cb"/>
-		</widget>
-		<packing>
-		  <property name="padding">0</property>
-		  <property name="expand">False</property>
-		  <property name="fill">False</property>
-		</packing>
-	      </child>
-
-	      <child>
 		<widget class="GtkCheckButton" id="events_toggle">
 		  <property name="visible">True</property>
 		  <property name="can_focus">True</property>
@@ -987,110 +967,32 @@
 	      </child>
 
 	      <child>
-		<widget class="GtkAlignment" id="alignment4">
+		<widget class="GtkAlignment" id="alignment17">
 		  <property name="visible">True</property>
-		  <property name="xalign">0</property>
-		  <property name="yalign">0</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
 		  <property name="xscale">1</property>
 		  <property name="yscale">1</property>
-		  <property name="top_padding">12</property>
+		  <property name="top_padding">0</property>
 		  <property name="bottom_padding">0</property>
-		  <property name="left_padding">0</property>
+		  <property name="left_padding">12</property>
 		  <property name="right_padding">0</property>
 
 		  <child>
-		    <widget class="GtkVBox" id="events_vbox">
+		    <widget class="GtkVBox" id="vbox18">
 		      <property name="visible">True</property>
 		      <property name="homogeneous">False</property>
 		      <property name="spacing">0</property>
 
 		      <child>
-			<placeholder/>
-		      </child>
-		    </widget>
-		  </child>
-		</widget>
-		<packing>
-		  <property name="padding">0</property>
-		  <property name="expand">True</property>
-		  <property name="fill">True</property>
-		</packing>
-	      </child>
-	    </widget>
-	    <packing>
-	      <property name="tab_expand">False</property>
-	      <property name="tab_fill">True</property>
-	    </packing>
-	  </child>
-
-	  <child>
-	    <widget class="GtkLabel" id="label1">
-	      <property name="visible">True</property>
-	      <property name="label" translatable="yes">Sounds</property>
-	      <property name="use_underline">True</property>
-	      <property name="use_markup">False</property>
-	      <property name="justify">GTK_JUSTIFY_CENTER</property>
-	      <property name="wrap">False</property>
-	      <property name="selectable">False</property>
-	      <property name="xalign">0</property>
-	      <property name="yalign">0</property>
-	      <property name="xpad">0</property>
-	      <property name="ypad">0</property>
-	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
-	      <property name="width_chars">-1</property>
-	      <property name="single_line_mode">False</property>
-	      <property name="angle">0</property>
-	    </widget>
-	    <packing>
-	      <property name="type">tab</property>
-	    </packing>
-	  </child>
-
-	  <child>
-	    <widget class="GtkVBox" id="sound_events_vbox">
-	      <property name="visible">True</property>
-	      <property name="homogeneous">False</property>
-	      <property name="spacing">0</property>
-
-	      <child>
-		<widget class="GtkHBox" id="hbox2">
-		  <property name="border_width">12</property>
-		  <property name="visible">True</property>
-		  <property name="homogeneous">False</property>
-		  <property name="spacing">12</property>
-
-		  <child>
-		    <widget class="GtkImage" id="bell_image">
-		      <property name="visible">True</property>
-		      <property name="stock">gtk-missing-image</property>
-		      <property name="icon_size">4</property>
-		      <property name="xalign">0</property>
-		      <property name="yalign">0</property>
-		      <property name="xpad">0</property>
-		      <property name="ypad">0</property>
-		    </widget>
-		    <packing>
-		      <property name="padding">0</property>
-		      <property name="expand">False</property>
-		      <property name="fill">True</property>
-		    </packing>
-		  </child>
-
-		  <child>
-		    <widget class="GtkVBox" id="bell_box">
-		      <property name="visible">True</property>
-		      <property name="homogeneous">False</property>
-		      <property name="spacing">6</property>
-
-		      <child>
-			<widget class="GtkCheckButton" id="bell_audible_toggle">
+			<widget class="GtkCheckButton" id="input_feedback_toggle">
 			  <property name="visible">True</property>
 			  <property name="can_focus">True</property>
-			  <property name="label" translatable="yes">_Enable system beep</property>
+			  <property name="label" translatable="yes">Play sounds when buttons are pressed</property>
 			  <property name="use_underline">True</property>
 			  <property name="relief">GTK_RELIEF_NORMAL</property>
 			  <property name="focus_on_click">True</property>
-			  <property name="active">True</property>
+			  <property name="active">False</property>
 			  <property name="inconsistent">False</property>
 			  <property name="draw_indicator">True</property>
 			</widget>
@@ -1102,10 +1004,10 @@
 		      </child>
 
 		      <child>
-			<widget class="GtkCheckButton" id="bell_visual_toggle">
+			<widget class="GtkCheckButton" id="bell_audible_toggle">
 			  <property name="visible">True</property>
 			  <property name="can_focus">True</property>
-			  <property name="label" translatable="yes">_Visual system beep</property>
+			  <property name="label" translatable="yes">_Play system beep sound</property>
 			  <property name="use_underline">True</property>
 			  <property name="relief">GTK_RELIEF_NORMAL</property>
 			  <property name="focus_on_click">True</property>
@@ -1119,77 +1021,141 @@
 			  <property name="fill">False</property>
 			</packing>
 		      </child>
+		    </widget>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">False</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkFrame" id="frame1">
+		  <property name="visible">True</property>
+		  <property name="label_xalign">0</property>
+		  <property name="label_yalign">0.5</property>
+		  <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+		  <child>
+		    <widget class="GtkAlignment" id="alignment15">
+		      <property name="visible">True</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xscale">1</property>
+		      <property name="yscale">1</property>
+		      <property name="top_padding">12</property>
+		      <property name="bottom_padding">12</property>
+		      <property name="left_padding">12</property>
+		      <property name="right_padding">0</property>
 
 		      <child>
-			<widget class="GtkAlignment" id="bell_flash_alignment">
+			<widget class="GtkComboBox" id="sound_theme_combobox">
 			  <property name="visible">True</property>
-			  <property name="xalign">0</property>
-			  <property name="yalign">0</property>
-			  <property name="xscale">1</property>
-			  <property name="yscale">1</property>
-			  <property name="top_padding">0</property>
-			  <property name="bottom_padding">0</property>
-			  <property name="left_padding">12</property>
-			  <property name="right_padding">0</property>
+			  <property name="add_tearoffs">False</property>
+			  <property name="focus_on_click">True</property>
+			</widget>
+		      </child>
+		    </widget>
+		  </child>
 
-			  <child>
-			    <widget class="GtkVBox" id="bell_flash_vbox">
-			      <property name="visible">True</property>
-			      <property name="homogeneous">False</property>
-			      <property name="spacing">6</property>
+		  <child>
+		    <widget class="GtkLabel" id="label17">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">&lt;b&gt;Sound theme&lt;/b&gt;</property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">True</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="type">label_item</property>
+		    </packing>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">True</property>
+		</packing>
+	      </child>
 
-			      <child>
-				<widget class="GtkRadioButton" id="bell_flash_window_radio">
-				  <property name="visible">True</property>
-				  <property name="can_focus">True</property>
-				  <property name="label" translatable="yes">Flash _window titlebar</property>
-				  <property name="use_underline">True</property>
-				  <property name="relief">GTK_RELIEF_NORMAL</property>
-				  <property name="focus_on_click">True</property>
-				  <property name="active">False</property>
-				  <property name="inconsistent">False</property>
-				  <property name="draw_indicator">True</property>
-				</widget>
-				<packing>
-				  <property name="padding">0</property>
-				  <property name="expand">False</property>
-				  <property name="fill">False</property>
-				</packing>
-			      </child>
+	      <child>
+		<widget class="GtkFrame" id="frame2">
+		  <property name="visible">True</property>
+		  <property name="label_xalign">0</property>
+		  <property name="label_yalign">0.5</property>
+		  <property name="shadow_type">GTK_SHADOW_NONE</property>
 
-			      <child>
-				<widget class="GtkRadioButton" id="bell_flash_screen_radio">
-				  <property name="visible">True</property>
-				  <property name="can_focus">True</property>
-				  <property name="label" translatable="yes">Flash _entire screen</property>
-				  <property name="use_underline">True</property>
-				  <property name="relief">GTK_RELIEF_NORMAL</property>
-				  <property name="focus_on_click">True</property>
-				  <property name="active">False</property>
-				  <property name="inconsistent">False</property>
-				  <property name="draw_indicator">True</property>
-				  <property name="group">bell_flash_window_radio</property>
-				</widget>
-				<packing>
-				  <property name="padding">0</property>
-				  <property name="expand">False</property>
-				  <property name="fill">False</property>
-				</packing>
-			      </child>
+		  <child>
+		    <widget class="GtkAlignment" id="alignment16">
+		      <property name="visible">True</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xscale">1</property>
+		      <property name="yscale">1</property>
+		      <property name="top_padding">12</property>
+		      <property name="bottom_padding">0</property>
+		      <property name="left_padding">12</property>
+		      <property name="right_padding">0</property>
+
+		      <child>
+			<widget class="GtkScrolledWindow" id="scrolledwindow2">
+			  <property name="visible">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+			  <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+			  <property name="shadow_type">GTK_SHADOW_IN</property>
+			  <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+			  <child>
+			    <widget class="GtkTreeView" id="sounds_treeview">
+			      <property name="visible">True</property>
+			      <property name="can_focus">True</property>
+			      <property name="headers_visible">True</property>
+			      <property name="rules_hint">False</property>
+			      <property name="reorderable">False</property>
+			      <property name="enable_search">True</property>
+			      <property name="fixed_height_mode">False</property>
+			      <property name="hover_selection">False</property>
+			      <property name="hover_expand">False</property>
 			    </widget>
 			  </child>
 			</widget>
-			<packing>
-			  <property name="padding">0</property>
-			  <property name="expand">True</property>
-			  <property name="fill">True</property>
-			</packing>
 		      </child>
 		    </widget>
+		  </child>
+
+		  <child>
+		    <widget class="GtkLabel" id="label18">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">&lt;b&gt;System sounds&lt;/b&gt;</property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">True</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
 		    <packing>
-		      <property name="padding">0</property>
-		      <property name="expand">True</property>
-		      <property name="fill">True</property>
+		      <property name="type">label_item</property>
 		    </packing>
 		  </child>
 		</widget>
@@ -1207,12 +1173,12 @@
 	  </child>
 
 	  <child>
-	    <widget class="GtkLabel" id="label4">
+	    <widget class="GtkLabel" id="label1">
 	      <property name="visible">True</property>
-	      <property name="label" translatable="yes">System Beep</property>
-	      <property name="use_underline">False</property>
+	      <property name="label" translatable="yes">Sounds</property>
+	      <property name="use_underline">True</property>
 	      <property name="use_markup">False</property>
-	      <property name="justify">GTK_JUSTIFY_LEFT</property>
+	      <property name="justify">GTK_JUSTIFY_CENTER</property>
 	      <property name="wrap">False</property>
 	      <property name="selectable">False</property>
 	      <property name="xalign">0</property>

Added: trunk/capplets/sound/sound-theme-definition.h
==============================================================================
--- (empty file)
+++ trunk/capplets/sound/sound-theme-definition.h	Sun Jul 27 23:32:35 2008
@@ -0,0 +1,71 @@
+/* -*- mode: c; style: linux -*- */
+/* -*- c-basic-offset: 2 -*- */
+
+/* sound-theme-definition.h
+ * 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.
+ */
+
+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, N_("Audible bell"), { "bell-terminal", "bell-window-system", NULL } },
+	{ CATEGORY_BELL, SOUND_TYPE_VISUAL_BELL, N_("Visual bell"), { NULL } },
+	/* Windows and buttons */
+	{ CATEGORY_WINDOWS_BUTTONS, -1, N_("Windows and Buttons"), { NULL } },
+	{ CATEGORY_WINDOWS_BUTTONS, SOUND_TYPE_FEEDBACK, N_("Button clicked"), { "button-pressed", "menu-click", "menu-popup", "menu-popdown", "menu-replace", NULL } },
+	{ CATEGORY_WINDOWS_BUTTONS, SOUND_TYPE_FEEDBACK, N_("Button toggled"), { "button-toggle-off", "button-toggle-on", NULL } },
+	{ CATEGORY_WINDOWS_BUTTONS, SOUND_TYPE_FEEDBACK, N_("Window maximized"), { "window-maximized", NULL } },
+	{ CATEGORY_WINDOWS_BUTTONS, SOUND_TYPE_FEEDBACK, N_("Window unmaximized"), { "window-unmaximized", NULL } },
+	{ CATEGORY_WINDOWS_BUTTONS, SOUND_TYPE_FEEDBACK, N_("Window minimised"), { "window-minimized", NULL } },
+	/* Desktop */
+	{ CATEGORY_DESKTOP, -1, N_("Desktop"), { NULL } },
+	{ CATEGORY_DESKTOP, SOUND_TYPE_NORMAL, N_("Login"), { "desktop-login", NULL } },
+	{ CATEGORY_DESKTOP, SOUND_TYPE_NORMAL, N_("Logout"), { "desktop-logout", NULL } },
+	{ CATEGORY_DESKTOP, SOUND_TYPE_NORMAL, N_("New e-mail"), { "message-new-email", NULL } },
+	{ CATEGORY_DESKTOP, SOUND_TYPE_NORMAL, N_("Empty Trash"), { "trash-empty", NULL } },
+	{ CATEGORY_DESKTOP, SOUND_TYPE_NORMAL, N_("Long action completed (download, CD burning, etc.)"), { "complete-copy", "complete-download", "complete-media-burn", "complete-media-rip", "complete-scan", NULL } },
+	/* Alerts? */
+	{ CATEGORY_ALERTS, -1, N_("Alerts"), { NULL } },
+	{ CATEGORY_ALERTS, SOUND_TYPE_NORMAL, N_("Information or Question"), { "dialog-information", "dialog-question", NULL } },
+	{ CATEGORY_ALERTS, SOUND_TYPE_NORMAL, N_("Warning"), { "dialog-warning", NULL } },
+	{ CATEGORY_ALERTS, SOUND_TYPE_NORMAL, N_("Error"), { "dialog-error", NULL } },
+	{ CATEGORY_ALERTS, SOUND_TYPE_NORMAL, N_("Battery warning"), { "power-unplug-battery-low", "battery-low", "battery-caution", NULL } },
+	/* Finish off */
+	{ -1, -1, NULL, { NULL } }
+};
+

Added: trunk/capplets/sound/sound-theme-file-utils.c
==============================================================================
--- (empty file)
+++ trunk/capplets/sound/sound-theme-file-utils.c	Sun Jul 27 23:32:35 2008
@@ -0,0 +1,152 @@
+/* -*- mode: c; style: linux -*- */
+/* -*- c-basic-offset: 2 -*- */
+
+/* sound-theme-file-utils.c
+ * 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 "capplet-util.h"
+
+#define CUSTOM_THEME_NAME	"__custom"
+
+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);
+}
+
+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/capplets/sound/sound-theme-file-utils.h
==============================================================================
--- (empty file)
+++ trunk/capplets/sound/sound-theme-file-utils.h	Sun Jul 27 23:32:35 2008
@@ -0,0 +1,36 @@
+/* -*- mode: c; style: linux -*- */
+/* -*- c-basic-offset: 2 -*- */
+
+/* sound-theme-file-utils.h
+ * 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);
+
+#endif /* __SOUND_THEME_FILE_UTILS_HH__ */

Added: trunk/capplets/sound/sound-theme.c
==============================================================================
--- (empty file)
+++ trunk/capplets/sound/sound-theme.c	Sun Jul 27 23:32:35 2008
@@ -0,0 +1,1135 @@
+/* -*- mode: c; style: linux -*- */
+/* -*- c-basic-offset: 2 -*- */
+
+/* sound-theme.c
+ * 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 <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <glade/glade.h>
+#include <gconf/gconf-client.h>
+#include <canberra-gtk.h>
+
+#include "sound-theme.h"
+#include "capplet-util.h"
+#include "sound-theme-definition.h"
+#include "sound-theme-file-utils.h"
+
+#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
+};
+
+#define SOUND_VISUAL_BELL_TITLEBAR SOUND_BUILTIN
+#define SOUND_VISUAL_BELL_SCREEN SOUND_CUSTOM
+
+static void set_combox_for_theme_name (GladeXML *dialog, const char *name);
+static gboolean theme_changed_custom_reinit (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data);
+static void dump_theme (GladeXML *dialog);
+
+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
+theme_changed_cb (GConfClient *client,
+		  guint cnxn_id,
+		  GConfEntry *entry,
+		  GladeXML *dialog)
+{
+	char *theme_name;
+
+	theme_name = gconf_client_get_string (client, SOUND_THEME_KEY, NULL);
+	set_combox_for_theme_name (dialog, theme_name);
+	g_free (theme_name);
+}
+
+static char *
+load_index_theme_name (const char *index, char **parent)
+{
+	GKeyFile *file;
+	char *indexname;
+	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 != FALSE)
+		return NULL;
+
+	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
+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
+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
+set_combox_for_theme_name (GladeXML *dialog, const char *name)
+{
+	GtkTreeIter iter;
+	GtkTreeModel *model;
+	gboolean found;
+
+	g_debug ("setting theme %s", name);
+
+	/* If the name is empty, use "freedesktop" */
+	if (name == NULL || *name == '\0')
+		name = "freedesktop";
+
+	found = FALSE;
+	model = gtk_combo_box_get_model (GTK_COMBO_BOX (WID ("sound_theme_combobox")));
+
+	if (gtk_tree_model_get_iter_first (model, &iter) == FALSE) {
+		return;
+	}
+
+	while (1) {
+		char *value;
+
+		gtk_tree_model_get (model, &iter, THEME_IDENTIFIER_COL, &value, -1);
+		if (value != NULL && strcmp (value, name) == 0) {
+			g_free (value);
+			found = TRUE;
+			break;
+		}
+		if (gtk_tree_model_iter_next (model, &iter) == FALSE)
+			break;
+	}
+
+	/* When we can't find the theme we need to set, try to set the default
+	 * one "freedesktop" */
+	if (found == FALSE && strcmp (name, "freedesktop") != 0) {
+		g_debug ("not found, falling back to fdo");
+		set_combox_for_theme_name (dialog, "freedesktop");
+	}
+	else if (found != FALSE)
+		gtk_combo_box_set_active_iter (GTK_COMBO_BOX (WID ("sound_theme_combobox")), &iter);
+}
+
+static void
+theme_combobox_changed (GtkComboBox *widget,
+			GladeXML *dialog)
+{
+	GtkTreeIter iter;
+	GtkTreeModel *model;
+	GConfClient *client;
+	char *theme_name;
+
+	if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (WID ("sound_theme_combobox")), &iter) == FALSE)
+		return;
+
+	model = gtk_combo_box_get_model (GTK_COMBO_BOX (WID ("sound_theme_combobox")));
+	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 (G_OBJECT (client));
+
+	/* Don't reinit a custom theme */
+	if (strcmp (theme_name, CUSTOM_THEME_NAME) != 0) {
+		model = gtk_tree_view_get_model (GTK_TREE_VIEW (WID ("sounds_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 (WID ("sound_theme_combobox")));
+		gtk_tree_model_get_iter_first (model, &iter);
+		while (1) {
+			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);
+			if (gtk_tree_model_iter_next (model, &iter) == FALSE)
+				break;
+		}
+	}
+}
+
+void
+setup_sound_theme (GladeXML *dialog)
+{
+	GHashTable *hash;
+	GtkListStore *store;
+	GtkCellRenderer *renderer;
+	const char * const *data_dirs;
+	const char *data_dir;
+	char *dir, *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 (WID ("sounds_vbox"), FALSE);
+		g_warning ("Bad setup, install the freedesktop sound theme");
+		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 (WID ("sound_theme_combobox")),
+				 GTK_TREE_MODEL (store));
+
+	renderer = gtk_cell_renderer_text_new ();
+	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (WID ("sound_theme_combobox")),
+				    renderer, TRUE);
+	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (WID ("sound_theme_combobox")),
+					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 (dialog, theme_name);
+	g_free (theme_name);
+
+	/* Listen for changes in GConf, and on the combobox */
+	gconf_client_notify_add (client, SOUND_THEME_KEY,
+				 (GConfClientNotifyFunc) theme_changed_cb, dialog, NULL, NULL);
+	g_object_unref (G_OBJECT (client));
+
+	g_signal_connect (G_OBJECT (WID ("sound_theme_combobox")), "changed",
+			  G_CALLBACK (theme_combobox_changed), dialog);
+}
+
+enum {
+	DISPLAY_COL,
+	SETTING_COL,
+	TYPE_COL,
+	SENSITIVE_COL,
+	HAS_PREVIEW_COL,
+	FILENAME_COL,
+	SOUND_NAMES_COL,
+	NUM_COLS
+};
+
+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) {
+			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);
+}
+
+static char *
+get_sound_filename (GladeXML *dialog)
+{
+	GtkWidget *chooser;
+	int response;
+	char *filename, *path;
+	const char * const *data_dirs, *data_dir;
+	GtkFileFilter *filter;
+	guint i;
+
+	chooser = gtk_file_chooser_dialog_new (_("Select Sound File"),
+					       GTK_WINDOW (WID("sound_prefs_dialog")),
+					       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 (chooser), TRUE);
+	gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (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 (chooser), filter);
+	gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (chooser), filter);
+
+	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 (chooser), path, NULL);
+		g_free (path);
+	}
+	data_dir = g_get_user_special_dir (G_USER_DIRECTORY_MUSIC);
+	gtk_file_chooser_add_shortcut_folder (GTK_FILE_CHOOSER (chooser), data_dir, NULL);
+
+	response = gtk_dialog_run (GTK_DIALOG (chooser));
+	filename = NULL;
+	if (response == GTK_RESPONSE_ACCEPT)
+		filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
+
+	gtk_widget_destroy (chooser);
+
+	return 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,
+						   -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
+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
+combobox_editing_started (GtkCellRenderer *renderer,
+			  GtkCellEditable *editable,
+			  gchar           *path,
+			  GladeXML        *dialog)
+{
+	GtkTreeModel *model, *store;
+	GtkTreeIter iter;
+	SoundType type;
+	char *filename;
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (WID ("sounds_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
+save_sounds (GtkTreeModel *model,
+	     GtkTreePath *path,
+	     GtkTreeIter *iter,
+	     gpointer data)
+{
+	int type, setting;
+	char *filename, **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) {
+		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, *path;
+
+	/* Create the custom directory */
+	path = custom_theme_dir_path (NULL);
+	g_mkdir_with_parents (path, 0644);
+	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);
+}
+
+static gboolean
+count_customised_sounds (GtkTreeModel *model,
+			 GtkTreePath *path,
+			 GtkTreeIter *iter,
+			 int *num_custom)
+{
+	int type, 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)
+		(*num_custom)++;
+
+	return FALSE;
+}
+
+static void
+dump_theme (GladeXML *dialog)
+{
+	int num_custom;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	char *parent;
+
+	num_custom = 0;
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (WID ("sounds_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 (WID ("sound_theme_combobox")));
+	/* Get the current theme's name, and set the parent */
+	if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (WID ("sound_theme_combobox")), &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 (dialog, parent);
+			g_free (parent);
+		}
+		gtk_tree_model_get_iter_first (model, &iter);
+		while (1) {
+			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;
+			}
+			if (gtk_tree_model_iter_next (model, &iter) == FALSE)
+				break;
+		}
+
+		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 (WID ("sounds_treeview")));
+		save_custom_theme (model, parent);
+		g_free (parent);
+
+		set_combox_for_theme_name (dialog, CUSTOM_THEME_NAME);
+	}
+}
+
+static void
+setting_column_edited (GtkCellRendererText *renderer,
+		       gchar               *path,
+		       gchar               *new_text,
+		       GladeXML            *dialog)
+{
+	GtkTreeModel *model, *tree_model;
+	GtkTreeIter iter, tree_iter;
+	SoundType type;
+	char *text;
+	int setting;
+
+	if (new_text == NULL)
+		return;
+
+	g_object_get (G_OBJECT (renderer),
+		      "model", &model,
+		      NULL);
+
+	tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (WID ("sounds_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, -1);
+
+	gtk_tree_model_get_iter_first (model, &iter);
+	while (1) {
+		gtk_tree_model_get (model, &iter, 0, &text, 1, &setting, -1);
+		if (g_utf8_collate (text, new_text) == 0) {
+			if (type == SOUND_TYPE_NORMAL || type == SOUND_TYPE_FEEDBACK || type == SOUND_TYPE_AUDIO_BELL) {
+				char *filename;
+
+				if (setting == SOUND_CUSTOM) {
+					filename = get_sound_filename (dialog);
+					if (filename == NULL)
+						break;
+				} else {
+					filename = NULL;
+				}
+
+				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_debug ("Something changed, dump theme");
+				dump_theme (dialog);
+
+				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 ();
+		}
+
+		if (gtk_tree_model_iter_next (model, &iter) == FALSE)
+			break;
+	}
+}
+
+/* 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 (GladeXML *dialog, gboolean enabled)
+{
+	GtkTreeModel *model;
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (WID ("sounds_treeview")));
+	gtk_tree_model_foreach (model, input_feedback_foreach, GINT_TO_POINTER (enabled));
+}
+
+static void
+input_feedback_changed_cb (GConfClient *client,
+			   guint cnxn_id,
+			   GConfEntry *entry,
+			   GladeXML *dialog)
+{
+	gboolean input_feedback_enabled;
+
+	input_feedback_enabled = gconf_client_get_bool (client, INPUT_SOUNDS_KEY, NULL);
+	set_input_feedback_enabled (dialog, 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 (GladeXML *dialog, gboolean enabled)
+{
+	GtkTreeModel *model;
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (WID ("sounds_treeview")));
+	gtk_tree_model_foreach (model, audible_bell_foreach, GINT_TO_POINTER (enabled));
+}
+
+static void
+audible_bell_changed_cb (GConfClient *client,
+			 guint cnxn_id,
+			 GConfEntry *entry,
+			 GladeXML *dialog)
+{
+	gboolean audio_bell_enabled;
+
+	audio_bell_enabled = gconf_client_get_bool (client, AUDIO_BELL_KEY, NULL);
+	set_audible_bell_enabled (dialog, audio_bell_enabled);
+}
+
+static gboolean
+theme_changed_custom_reinit (GtkTreeModel *model,
+			     GtkTreePath *path,
+			     GtkTreeIter *iter,
+			     gpointer data)
+{
+	int type;
+
+	gtk_tree_model_get (model, iter, TYPE_COL, &type, -1);
+	if (type != -1 && type != SOUND_TYPE_VISUAL_BELL) {
+		gtk_tree_store_set (GTK_TREE_STORE (model), iter,
+				    SETTING_COL, SOUND_BUILTIN,
+				    -1);
+	}
+	return FALSE;
+}
+
+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 gboolean
+custom_treeview_button_press_event_cb (GtkWidget *tree_view,
+				       GdkEventButton *event,
+				       GladeXML *dialog)
+{
+	GtkTreePath *path;
+	GtkTreeViewColumn *column;
+	GdkEventButton *button_event = (GdkEventButton *) event;
+
+	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)) {
+		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;
+			ca_context *ctx;
+
+			model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
+			if (gtk_tree_model_get_iter (model, &iter, path) == FALSE) {
+				gtk_tree_path_free (path);
+				return FALSE;
+			}
+			gtk_tree_path_free (path);
+
+			gtk_tree_model_get (model, &iter, SOUND_NAMES_COL, &sound_names, -1);
+			if (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"),
+						NULL);
+
+			g_strfreev (sound_names);
+
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+void
+setup_sound_theme_custom (GladeXML *dialog, gboolean have_xkb)
+{
+	GtkTreeStore *store;
+	GtkTreeModel *custom_model;
+	GtkTreeViewColumn *column;
+	GtkCellRenderer *renderer;
+	GtkTreeIter iter, parent;
+	GConfClient *client;
+	CategoryType type;
+	gboolean input_feedback_enabled, 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 (WID ("sounds_treeview")), column);
+
+	/* The 2nd column with the sound settings */
+	renderer = gtk_cell_renderer_combo_new();
+	g_signal_connect (renderer, "edited", G_CALLBACK (setting_column_edited), dialog);
+	g_signal_connect (renderer, "editing-started", G_CALLBACK (combobox_editing_started), dialog);
+	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 (WID ("sounds_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 = gtk_cell_renderer_pixbuf_new ();
+	g_object_set (renderer,
+		      "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE,
+		      "icon-name", "media-playback-start",
+		      "stock-size", GTK_ICON_SIZE_MENU,
+		      "xpad", PREVIEW_BUTTON_XPAD,
+		      NULL);
+	column = gtk_tree_view_column_new_with_attributes ("Preview", renderer,
+							   "visible", HAS_PREVIEW_COL,
+							   NULL);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (WID ("sounds_treeview")), column);
+	g_object_set_data (G_OBJECT (WID ("sounds_treeview")), "preview-column", column);
+
+	gtk_tree_view_set_model (GTK_TREE_VIEW (WID ("sounds_treeview")), GTK_TREE_MODEL (store));
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (WID ("sounds_treeview")), FALSE);
+
+	g_signal_connect (WID ("sounds_treeview"), "button-press-event",
+			  G_CALLBACK (custom_treeview_button_press_event_cb), dialog);
+
+	/* 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, _(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, _(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, _(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 (WID ("sounds_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 (dialog, FALSE);
+	gconf_client_notify_add (client, INPUT_SOUNDS_KEY,
+				 (GConfClientNotifyFunc) input_feedback_changed_cb, dialog, NULL, NULL);
+
+	audible_bell_enabled = gconf_client_get_bool (client, AUDIO_BELL_KEY, NULL);
+	if (audible_bell_enabled == FALSE)
+		set_audible_bell_enabled (dialog, FALSE);
+	gconf_client_notify_add (client, AUDIO_BELL_KEY,
+				 (GConfClientNotifyFunc) audible_bell_changed_cb, dialog, 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 (G_OBJECT (client));
+}
+

Added: trunk/capplets/sound/sound-theme.h
==============================================================================
--- (empty file)
+++ trunk/capplets/sound/sound-theme.h	Sun Jul 27 23:32:35 2008
@@ -0,0 +1,31 @@
+/* -*- mode: c; style: linux -*- */
+/* -*- c-basic-offset: 2 -*- */
+
+/* sound-theme.h
+ * 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_HH__
+#define __SOUND_THEME_HH__
+
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+
+void setup_sound_theme (GladeXML *dialog);
+void setup_sound_theme_custom (GladeXML *dialog, gboolean have_xkb);
+
+#endif /* __SOUND_THEME_HH__ */



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