summaryrefslogtreecommitdiffstats
path: root/panels/sound/cc-alert-chooser.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--panels/sound/cc-alert-chooser.c306
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);
+}