diff options
Diffstat (limited to '')
-rw-r--r-- | panels/sound/cc-alert-chooser.c | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/panels/sound/cc-alert-chooser.c b/panels/sound/cc-alert-chooser.c new file mode 100644 index 0000000..686d8c6 --- /dev/null +++ b/panels/sound/cc-alert-chooser.c @@ -0,0 +1,306 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2018 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib/gi18n.h> +#include <gsound.h> + +#include "config.h" +#include "cc-alert-chooser.h" +#include "cc-sound-resources.h" + +#define KEY_SOUNDS_SCHEMA "org.gnome.desktop.sound" + +struct _CcAlertChooser +{ + GtkBox parent_instance; + + GtkToggleButton *click_button; + GtkToggleButton *hum_button; + GtkToggleButton *string_button; + GtkToggleButton *swing_button; + + GSoundContext *context; + GSettings *sound_settings; +}; + +static void clicked_cb (CcAlertChooser *self, + GtkToggleButton *button); + +G_DEFINE_TYPE (CcAlertChooser, cc_alert_chooser, GTK_TYPE_BOX) + +#define CUSTOM_THEME_NAME "__custom" + +static gchar * +get_theme_dir (void) +{ + return g_build_filename (g_get_user_data_dir (), "sounds", CUSTOM_THEME_NAME, NULL); +} + +static gchar * +get_sound_path (const gchar *name) +{ + g_autofree gchar *filename = NULL; + + filename = g_strdup_printf ("%s.ogg", name); + return g_build_filename (SOUND_DATA_DIR, "gnome", "default", "alerts", filename, NULL); +} + +static gchar * +get_alert_name (void) +{ + g_autofree gchar *dir = NULL; + g_autofree gchar *path = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GFileInfo) info = NULL; + const gchar *target; + g_autofree gchar *basename = NULL; + g_autoptr(GError) error = NULL; + + dir = get_theme_dir (); + path = g_build_filename (dir, "bell-terminal.ogg", NULL); + file = g_file_new_for_path (path); + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET, + G_FILE_QUERY_INFO_NONE, + NULL, + &error); + if (info == NULL) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + g_warning ("Failed to get sound theme symlink %s: %s", path, error->message); + return NULL; + } + target = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET); + if (target == NULL) + return NULL; + + basename = g_path_get_basename (target); + if (g_str_has_suffix (basename, ".ogg")) + basename[strlen (basename) - 4] = '\0'; + + return g_steal_pointer (&basename); +} + +static void +set_sound_symlink (const gchar *alert_name, + const gchar *name) +{ + g_autofree gchar *dir = NULL; + g_autofree gchar *source_filename = NULL; + g_autofree gchar *source_path = NULL; + g_autofree gchar *target_path = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GError) error = NULL; + + dir = get_theme_dir (); + source_filename = g_strdup_printf ("%s.ogg", alert_name); + source_path = g_build_filename (dir, source_filename, NULL); + target_path = get_sound_path (name); + + file = g_file_new_for_path (source_path); + if (!g_file_delete (file, NULL, &error)) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + g_warning ("Failed to remove existing sound symbolic link %s: %s", source_path, error->message); + } + if (!g_file_make_symbolic_link (file, target_path, NULL, &error)) + g_warning ("Failed to make sound theme symbolic link %s->%s: %s", source_path, target_path, error->message); +} + +static void +set_custom_theme (CcAlertChooser *self, + const gchar *name) +{ + g_autofree gchar *dir_path = NULL; + g_autofree gchar *theme_path = NULL; + g_autoptr(GDateTime) now = NULL; + g_autoptr(GFile) dir = NULL; + g_autoptr(GKeyFile) theme_file = NULL; + g_autoptr(GVariant) default_theme = NULL; + g_autoptr(GError) load_error = NULL; + g_autoptr(GError) save_error = NULL; + g_autoptr(GError) mtime_error = NULL; + + dir_path = get_theme_dir (); + g_mkdir_with_parents (dir_path, USER_DIR_MODE); + + theme_path = g_build_filename (dir_path, "index.theme", NULL); + + default_theme = g_settings_get_default_value (self->sound_settings, "theme-name"); + + theme_file = g_key_file_new (); + if (!g_key_file_load_from_file (theme_file, theme_path, G_KEY_FILE_KEEP_COMMENTS, &load_error)) + { + if (!g_error_matches (load_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) + g_printerr ("Failed to load theme file %s: %s", theme_path, load_error->message); + } + g_key_file_set_string (theme_file, "Sound Theme", "Name", _("Custom")); + if (default_theme != NULL) + g_key_file_set_string (theme_file, "Sound Theme", "Inherits", g_variant_get_string (default_theme, NULL)); + g_key_file_set_string (theme_file, "Sound Theme", "Directories", "."); + + if (!g_key_file_save_to_file (theme_file, theme_path, &save_error)) + { + g_warning ("Failed to save theme file %s: %s", theme_path, save_error->message); + } + + set_sound_symlink ("bell-terminal", name); + set_sound_symlink ("bell-window-system", name); + + /* Ensure the g-s-d sound plugin which does non-recursive monitoring + * notices the change even if the theme directory already existed. + */ + now = g_date_time_new_now_utc (); + dir = g_file_new_for_path (dir_path); + if (!g_file_set_attribute_uint64 (dir, + G_FILE_ATTRIBUTE_TIME_MODIFIED, + g_date_time_to_unix (now), + G_FILE_QUERY_INFO_NONE, + NULL, + &mtime_error)) + { + g_warning ("Failed to update theme directory modification time for %s: %s", + dir_path, mtime_error->message); + } + + g_settings_set_boolean (self->sound_settings, "event-sounds", TRUE); + g_settings_set_string (self->sound_settings, "theme-name", CUSTOM_THEME_NAME); +} + +static void +select_sound (CcAlertChooser *self, + const gchar *name) +{ + g_autofree gchar *path = NULL; + g_autoptr(GError) error = NULL; + + path = get_sound_path (name); + if (!gsound_context_play_simple (self->context, NULL, &error, + GSOUND_ATTR_MEDIA_FILENAME, path, + NULL)) + { + g_warning ("Failed to play alert sound %s: %s", path, error->message); + } + + set_custom_theme (self, name); +} + +static void +set_button (CcAlertChooser *self, + GtkToggleButton *button, + gboolean active) +{ + g_signal_handlers_block_by_func (button, clicked_cb, self); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active); + g_signal_handlers_unblock_by_func (button, clicked_cb, self); +} + +static void +clicked_cb (CcAlertChooser *self, + GtkToggleButton *button) +{ + if (button == self->click_button) + select_sound (self, "click"); + else if (button == self->hum_button) + select_sound (self, "hum"); + else if (button == self->string_button) + select_sound (self, "string"); + else if (button == self->swing_button) + select_sound (self, "swing"); + + set_button (self, button, TRUE); + if (button != self->click_button) + set_button (self, self->click_button, FALSE); + if (button != self->hum_button) + set_button (self, self->hum_button, FALSE); + if (button != self->string_button) + set_button (self, self->string_button, FALSE); + if (button != self->swing_button) + set_button (self, self->swing_button, FALSE); +} + +static void +cc_alert_chooser_dispose (GObject *object) +{ + CcAlertChooser *self = CC_ALERT_CHOOSER (object); + + g_clear_object (&self->context); + g_clear_object (&self->sound_settings); + + G_OBJECT_CLASS (cc_alert_chooser_parent_class)->dispose (object); +} + +void +cc_alert_chooser_class_init (CcAlertChooserClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = cc_alert_chooser_dispose; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/sound/cc-alert-chooser.ui"); + + gtk_widget_class_bind_template_child (widget_class, CcAlertChooser, click_button); + gtk_widget_class_bind_template_child (widget_class, CcAlertChooser, hum_button); + gtk_widget_class_bind_template_child (widget_class, CcAlertChooser, string_button); + gtk_widget_class_bind_template_child (widget_class, CcAlertChooser, swing_button); + + gtk_widget_class_bind_template_callback (widget_class, clicked_cb); +} + +void +cc_alert_chooser_init (CcAlertChooser *self) +{ + g_autofree gchar *alert_name = NULL; + g_autoptr(GError) error = NULL; + + g_resources_register (cc_sound_get_resource ()); + + gtk_widget_init_template (GTK_WIDGET (self)); + + self->context = gsound_context_new (NULL, &error); + if (self->context == NULL) + g_error ("Failed to make sound context: %s", error->message); + + self->sound_settings = g_settings_new (KEY_SOUNDS_SCHEMA); + + alert_name = get_alert_name (); + + /* If user has selected an old sound alert, migrate them to click. */ + if (g_strcmp0 (alert_name, "click") != 0 && + g_strcmp0 (alert_name, "hum") != 0 && + g_strcmp0 (alert_name, "string") != 0 && + g_strcmp0 (alert_name, "swing") != 0) + { + set_custom_theme (self, "click"); + g_free (alert_name); + alert_name = g_strdup ("click"); + } + + if (g_strcmp0 (alert_name, "click") == 0) + set_button (self, self->click_button, TRUE); + else if (g_strcmp0 (alert_name, "hum") == 0) + set_button (self, self->hum_button, TRUE); + else if (g_strcmp0 (alert_name, "string") == 0) + set_button (self, self->string_button, TRUE); + else if (g_strcmp0 (alert_name, "swing") == 0) + set_button (self, self->swing_button, TRUE); + else if (alert_name != NULL) + g_warning ("Current alert sound has unknown name %s", alert_name); +} |