diff --git a/ChangeLog b/ChangeLog index b82f240ac..f094f41ea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2008-07-28 Bastien Nocera + + * Makefile.am: + * configure.in: Remove libsounds and esound usage, check for + libcanberra instead (Closes: #542979) + Thu Jul 24 15:34:50 2008 Søren Sandmann * configure.in diff --git a/capplets/appearance/ChangeLog b/capplets/appearance/ChangeLog index af5eb5beb..182d18ed7 100644 --- a/capplets/appearance/ChangeLog +++ b/capplets/appearance/ChangeLog @@ -1,3 +1,9 @@ +2008-07-28 Bastien Nocera + + * theme-util.c: + * theme-util.h: Remove the directory deletion helpers, and + move them to the common sub-directory + 2008-07-20 Jens Granseuer * theme-util.c: (theme_delete): don't delete ~/.icons when deleting diff --git a/capplets/appearance/theme-util.c b/capplets/appearance/theme-util.c index 7c137356e..c78d82f63 100644 --- a/capplets/appearance/theme-util.c +++ b/capplets/appearance/theme-util.c @@ -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) { diff --git a/capplets/appearance/theme-util.h b/capplets/appearance/theme-util.h index 87616e603..b6f453bbc 100644 --- a/capplets/appearance/theme-util.h +++ b/capplets/appearance/theme-util.h @@ -61,4 +61,4 @@ gboolean theme_find_in_model (GtkTreeModel *model, const gchar *name, GtkTreeIte void theme_install_file (GtkWindow *parent, const gchar *path); gboolean packagekit_available (void); -gboolean file_delete_recursive (GFile *directory, GError **error); + diff --git a/capplets/common/ChangeLog b/capplets/common/ChangeLog index 7b7aa077c..6a72d14a6 100644 --- a/capplets/common/ChangeLog +++ b/capplets/common/ChangeLog @@ -1,3 +1,11 @@ +2008-07-28 Bastien Nocera + + * 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-15 Matthias Clasen Bug 533611 - add notification themes to the metatheme format @@ -1827,7 +1835,7 @@ Sun Jan 6 02:52:59 2002 Jonathan Blandford (close_cb): Don't call gtk_object_destroy (dialog) (get_control_cb): Rename from create_dialog_cb -2001-07-14 Carlos Perelló Marín +2001-07-14 Carlos Perello Marin * .cvsignore: ssshhhh diff --git a/capplets/common/Makefile.am b/capplets/common/Makefile.am index 6b0d40d16..d191949ce 100644 --- a/capplets/common/Makefile.am +++ b/capplets/common/Makefile.am @@ -11,7 +11,8 @@ INCLUDES = \ $(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_SOURCES = \ 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 @@ gnome_theme_test_LDADD = \ $(GNOMECC_CAPPLETS_LIBS) noinst_PROGRAMS = \ - gnome-theme-test \ No newline at end of file + gnome-theme-test diff --git a/capplets/common/capplet-util.c b/capplets/common/capplet-util.c index a1c6c6c9b..a1d87d803 100644 --- a/capplets/common/capplet-util.c +++ b/capplets/common/capplet-util.c @@ -96,3 +96,73 @@ capplet_set_icon (GtkWidget *window, char const *icon_file_name) 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); +} + diff --git a/capplets/common/capplet-util.h b/capplets/common/capplet-util.h index f28e99299..b94042fd3 100644 --- a/capplets/common/capplet-util.h +++ b/capplets/common/capplet-util.h @@ -24,6 +24,7 @@ #ifndef __CAPPLET_UTIL_H #define __CAPPLET_UTIL_H +#include #include #include #include @@ -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 */ diff --git a/capplets/sound/ChangeLog b/capplets/sound/ChangeLog index ee55b6787..23ac6e3df 100644 --- a/capplets/sound/ChangeLog +++ b/capplets/sound/ChangeLog @@ -1,3 +1,15 @@ +2008-07-28 Bastien Nocera + + * 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) + ==================== 2.23.2 ==================== 2008-04-25 Jens Granseuer diff --git a/capplets/sound/Makefile.am b/capplets/sound/Makefile.am index 674443080..e015d5fe2 100644 --- a/capplets/sound/Makefile.am +++ b/capplets/sound/Makefile.am @@ -4,15 +4,23 @@ cappletname = sound 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)\"" \ diff --git a/capplets/sound/sound-properties-capplet.c b/capplets/sound/sound-properties-capplet.c index 5a40a7f24..90cc38938 100644 --- a/capplets/sound/sound-properties-capplet.c +++ b/capplets/sound/sound-properties-capplet.c @@ -35,7 +35,6 @@ #include "capplet-util.h" #include "gconf-property-editor.h" -#include "libsounds/sound-view.h" #include @@ -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 @@ typedef struct _DeviceChooser 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 @@ typedef struct _DeviceChooser /* 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 @@ CheckXKB (void) 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; + GtkWidget *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); - - 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 @@ create_dialog (void) 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 @@ setup_dialog (GladeXML *dialog, GConfChangeSet *changeset) 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 @@ get_legacy_settings (void) 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)); } diff --git a/capplets/sound/sound-properties.glade b/capplets/sound/sound-properties.glade index 3f1563eb1..cba7a1bc4 100644 --- a/capplets/sound/sound-properties.glade +++ b/capplets/sound/sound-properties.glade @@ -211,7 +211,7 @@ False - + 12 True False @@ -940,32 +940,12 @@ - + 12 True False 6 - - - True - True - E_nable software sound mixing - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - True @@ -987,29 +967,198 @@ - + True - 0 - 0 + 0.5 + 0.5 1 1 - 12 + 0 0 - 0 + 12 0 - + True False 0 - + + True + True + Play sounds when buttons are pressed + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + _Play system beep sound + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + 0 + False + False + + + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 12 + 12 + 12 + 0 + + + + True + False + True + + + + + + + + True + <b>Sound theme</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + False + True + + + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 12 + 0 + 12 + 0 + + + + True + True + GTK_POLICY_NEVER + GTK_POLICY_ALWAYS + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + True + False + False + True + False + False + False + + + + + + + + + + True + <b>System sounds</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + 0 True @@ -1045,189 +1194,6 @@ tab - - - - True - False - 0 - - - - 12 - True - False - 12 - - - - True - gtk-missing-image - 4 - 0 - 0 - 0 - 0 - - - 0 - False - True - - - - - - True - False - 6 - - - - True - True - _Enable system beep - True - GTK_RELIEF_NORMAL - True - True - False - True - - - 0 - False - False - - - - - - True - True - _Visual system beep - True - GTK_RELIEF_NORMAL - True - False - False - True - - - 0 - False - False - - - - - - True - 0 - 0 - 1 - 1 - 0 - 0 - 12 - 0 - - - - True - False - 6 - - - - True - True - Flash _window titlebar - True - GTK_RELIEF_NORMAL - True - False - False - True - - - 0 - False - False - - - - - - True - True - Flash _entire screen - True - GTK_RELIEF_NORMAL - True - False - False - True - bell_flash_window_radio - - - 0 - False - False - - - - - - - 0 - True - True - - - - - 0 - True - True - - - - - 0 - True - True - - - - - False - True - - - - - - True - System Beep - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - 0 diff --git a/capplets/sound/sound-theme-definition.h b/capplets/sound/sound-theme-definition.h new file mode 100644 index 000000000..ce36e86fd --- /dev/null +++ b/capplets/sound/sound-theme-definition.h @@ -0,0 +1,71 @@ +/* -*- mode: c; style: linux -*- */ +/* -*- c-basic-offset: 2 -*- */ + +/* sound-theme-definition.h + * Copyright (C) 2008 Bastien Nocera + * + * 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 } } +}; + diff --git a/capplets/sound/sound-theme-file-utils.c b/capplets/sound/sound-theme-file-utils.c new file mode 100644 index 000000000..8e1b083fd --- /dev/null +++ b/capplets/sound/sound-theme-file-utils.c @@ -0,0 +1,152 @@ +/* -*- mode: c; style: linux -*- */ +/* -*- c-basic-offset: 2 -*- */ + +/* sound-theme-file-utils.c + * Copyright (C) 2008 Bastien Nocera + * + * 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 +#include +#include + +#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); + } +} + diff --git a/capplets/sound/sound-theme-file-utils.h b/capplets/sound/sound-theme-file-utils.h new file mode 100644 index 000000000..bbdf71c6c --- /dev/null +++ b/capplets/sound/sound-theme-file-utils.h @@ -0,0 +1,36 @@ +/* -*- mode: c; style: linux -*- */ +/* -*- c-basic-offset: 2 -*- */ + +/* sound-theme-file-utils.h + * Copyright (C) 2008 Bastien Nocera + * + * 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 + +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__ */ diff --git a/capplets/sound/sound-theme.c b/capplets/sound/sound-theme.c new file mode 100644 index 000000000..b9429658f --- /dev/null +++ b/capplets/sound/sound-theme.c @@ -0,0 +1,1135 @@ +/* -*- mode: c; style: linux -*- */ +/* -*- c-basic-offset: 2 -*- */ + +/* sound-theme.c + * Copyright (C) 2008 Bastien Nocera + * + * 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 +#include +#include +#include +#include +#include +#include + +#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)); +} + diff --git a/capplets/sound/sound-theme.h b/capplets/sound/sound-theme.h new file mode 100644 index 000000000..aed38f5d6 --- /dev/null +++ b/capplets/sound/sound-theme.h @@ -0,0 +1,31 @@ +/* -*- mode: c; style: linux -*- */ +/* -*- c-basic-offset: 2 -*- */ + +/* sound-theme.h + * Copyright (C) 2008 Bastien Nocera + * + * 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 +#include + +void setup_sound_theme (GladeXML *dialog); +void setup_sound_theme_custom (GladeXML *dialog, gboolean have_xkb); + +#endif /* __SOUND_THEME_HH__ */