diff --git a/panels/keyboard/Makefile.am b/panels/keyboard/Makefile.am index fdd6ff177..ad05b8903 100644 --- a/panels/keyboard/Makefile.am +++ b/panels/keyboard/Makefile.am @@ -9,6 +9,8 @@ BUILT_SOURCES = \ libkeyboard_la_SOURCES = \ $(BUILT_SOURCES) \ + cc-keyboard-manager.c \ + cc-keyboard-manager.h \ cc-keyboard-panel.c \ cc-keyboard-panel.h \ cc-keyboard-item.c \ diff --git a/panels/keyboard/cc-keyboard-manager.c b/panels/keyboard/cc-keyboard-manager.c new file mode 100644 index 000000000..a98baeb8f --- /dev/null +++ b/panels/keyboard/cc-keyboard-manager.c @@ -0,0 +1,817 @@ +/* + * Copyright (C) 2010 Intel, Inc + * Copyright (C) 2016 Endless, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Author: Thomas Wood + * Georges Basile Stavracas Neto + * + */ + +#include + +#include "cc-keyboard-manager.h" +#include "keyboard-shortcuts.h" +#include "wm-common.h" + +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#define BINDINGS_SCHEMA "org.gnome.settings-daemon.plugins.media-keys" +#define CUSTOM_SHORTCUTS_ID "custom" + +struct _CcKeyboardManager +{ + GObject parent; + + GtkListStore *shortcuts_model; + GtkListStore *sections_store; + + GHashTable *kb_system_sections; + GHashTable *kb_apps_sections; + GHashTable *kb_user_sections; + + GSettings *binding_settings; + + gpointer wm_changed_id; +}; + +G_DEFINE_TYPE (CcKeyboardManager, cc_keyboard_manager, G_TYPE_OBJECT) + +enum +{ + SHORTCUT_ADDED, + SHORTCUT_CHANGED, + SHORTCUT_REMOVED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0, }; + +/* + * Auxiliary methos + */ +static void +free_key_array (GPtrArray *keys) +{ + if (keys != NULL) + { + gint i; + + for (i = 0; i < keys->len; i++) + { + CcKeyboardItem *item; + + item = g_ptr_array_index (keys, i); + + g_object_unref (item); + } + + g_ptr_array_free (keys, TRUE); + } +} + +static GHashTable* +get_hash_for_group (CcKeyboardManager *self, + BindingGroupType group) +{ + GHashTable *hash; + + switch (group) + { + case BINDING_GROUP_SYSTEM: + hash = self->kb_system_sections; + break; + case BINDING_GROUP_APPS: + hash = self->kb_apps_sections; + break; + case BINDING_GROUP_USER: + hash = self->kb_user_sections; + break; + default: + hash = NULL; + } + + return hash; +} + +static gboolean +have_key_for_group (CcKeyboardManager *self, + int group, + const gchar *name) +{ + GHashTableIter iter; + GPtrArray *keys; + gint i; + + g_hash_table_iter_init (&iter, get_hash_for_group (self, group)); + while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &keys)) + { + for (i = 0; i < keys->len; i++) + { + CcKeyboardItem *item = g_ptr_array_index (keys, i); + + if (item->type == CC_KEYBOARD_ITEM_TYPE_GSETTINGS && + g_strcmp0 (name, item->key) == 0) + { + return TRUE; + } + + return FALSE; + } + } + + return FALSE; +} + +static void +add_shortcuts (CcKeyboardManager *self) +{ + GtkTreeModel *sections_model; + GtkTreeIter sections_iter; + gboolean can_continue; + + sections_model = GTK_TREE_MODEL (self->sections_store); + can_continue = gtk_tree_model_get_iter_first (sections_model, §ions_iter); + + while (can_continue) + { + BindingGroupType group; + GPtrArray *keys; + gchar *id, *title; + gint i; + + gtk_tree_model_get (sections_model, + §ions_iter, + SECTION_DESCRIPTION_COLUMN, &title, + SECTION_GROUP_COLUMN, &group, + SECTION_ID_COLUMN, &id, + -1); + + /* Ignore separators */ + if (group == BINDING_GROUP_SEPARATOR) + { + can_continue = gtk_tree_model_iter_next (sections_model, §ions_iter); + continue; + } + + keys = g_hash_table_lookup (get_hash_for_group (self, group), id); + + for (i = 0; i < keys->len; i++) + { + CcKeyboardItem *item = g_ptr_array_index (keys, i); + + if (!cc_keyboard_item_is_hidden (item)) + { + GtkTreeIter new_row; + + gtk_list_store_append (self->shortcuts_model, &new_row); + gtk_list_store_set (self->shortcuts_model, + &new_row, + DETAIL_DESCRIPTION_COLUMN, item->description, + DETAIL_KEYENTRY_COLUMN, item, + DETAIL_TYPE_COLUMN, SHORTCUT_TYPE_KEY_ENTRY, + -1); + + g_signal_emit (self, signals[SHORTCUT_ADDED], + 0, + item, + id, + title); + } + } + + can_continue = gtk_tree_model_iter_next (sections_model, §ions_iter); + + g_free (title); + g_free (id); + } +} + +static void +append_section (CcKeyboardManager *self, + const gchar *title, + const gchar *id, + BindingGroupType group, + const KeyListEntry *keys_list) +{ + GtkTreeModel *shortcut_model; + GtkTreeIter iter; + GHashTable *reverse_items; + GHashTable *hash; + GPtrArray *keys_array; + gboolean is_new; + gint i; + + hash = get_hash_for_group (self, group); + + if (!hash) + return; + + shortcut_model = GTK_TREE_MODEL (self->shortcuts_model); + + /* Add all CcKeyboardItems for this section */ + is_new = FALSE; + keys_array = g_hash_table_lookup (hash, id); + if (keys_array == NULL) + { + keys_array = g_ptr_array_new (); + is_new = TRUE; + } + + reverse_items = g_hash_table_new (g_str_hash, g_str_equal); + + for (i = 0; keys_list != NULL && keys_list[i].name != NULL; i++) + { + CcKeyboardItem *item; + gboolean ret; + + if (have_key_for_group (self, group, keys_list[i].name)) + continue; + + item = cc_keyboard_item_new (keys_list[i].type); + + switch (keys_list[i].type) + { + case CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH: + ret = cc_keyboard_item_load_from_gsettings_path (item, keys_list[i].name, FALSE); + break; + + case CC_KEYBOARD_ITEM_TYPE_GSETTINGS: + ret = cc_keyboard_item_load_from_gsettings (item, + keys_list[i].description, + keys_list[i].schema, + keys_list[i].name); + if (ret && keys_list[i].reverse_entry != NULL) + { + CcKeyboardItem *reverse_item; + reverse_item = g_hash_table_lookup (reverse_items, + keys_list[i].reverse_entry); + if (reverse_item != NULL) + { + cc_keyboard_item_add_reverse_item (item, + reverse_item, + keys_list[i].is_reversed); + } + else + { + g_hash_table_insert (reverse_items, + keys_list[i].name, + item); + } + } + break; + + default: + g_assert_not_reached (); + } + + if (ret == FALSE) + { + /* We don't actually want to popup a dialog - just skip this one */ + g_object_unref (item); + continue; + } + + cc_keyboard_item_set_hidden (item, keys_list[i].hidden); + item->model = shortcut_model; + item->group = group; + + g_ptr_array_add (keys_array, item); + } + + g_hash_table_destroy (reverse_items); + + /* Add the keys to the hash table */ + if (is_new) + { + g_hash_table_insert (hash, g_strdup (id), keys_array); + + /* Append the section to the left tree view */ + gtk_list_store_append (GTK_LIST_STORE (self->sections_store), &iter); + gtk_list_store_set (GTK_LIST_STORE (self->sections_store), + &iter, + SECTION_DESCRIPTION_COLUMN, title, + SECTION_ID_COLUMN, id, + SECTION_GROUP_COLUMN, group, + -1); + } +} + +static void +append_sections_from_file (CcKeyboardManager *self, + const gchar *path, + const char *datadir, + gchar **wm_keybindings) +{ + KeyList *keylist; + KeyListEntry *keys; + KeyListEntry key = { 0, 0, 0, 0, 0, 0, 0 }; + const char *title; + int group; + guint i; + + keylist = parse_keylist_from_file (path); + + if (keylist == NULL) + return; + +#define const_strv(s) ((const gchar* const*) s) + + /* If there's no keys to add, or the settings apply to a window manager + * that's not the one we're running */ + if (keylist->entries->len == 0 || + (keylist->wm_name != NULL && !g_strv_contains (const_strv (wm_keybindings), keylist->wm_name)) || + keylist->name == NULL) + { + g_free (keylist->name); + g_free (keylist->package); + g_free (keylist->wm_name); + g_array_free (keylist->entries, TRUE); + g_free (keylist); + return; + } + +#undef const_strv + + /* Empty KeyListEntry to end the array */ + key.name = NULL; + g_array_append_val (keylist->entries, key); + + keys = (KeyListEntry *) g_array_free (keylist->entries, FALSE); + if (keylist->package) + { + char *localedir; + + localedir = g_build_filename (datadir, "locale", NULL); + bindtextdomain (keylist->package, localedir); + g_free (localedir); + + title = dgettext (keylist->package, keylist->name); + } else { + title = _(keylist->name); + } + + if (keylist->group && strcmp (keylist->group, "system") == 0) + group = BINDING_GROUP_SYSTEM; + else + group = BINDING_GROUP_APPS; + + append_section (self, title, keylist->name, group, keys); + + g_free (keylist->name); + g_free (keylist->package); + g_free (keylist->wm_name); + g_free (keylist->schema); + g_free (keylist->group); + + for (i = 0; keys[i].name != NULL; i++) + { + KeyListEntry *entry = &keys[i]; + g_free (entry->schema); + g_free (entry->description); + g_free (entry->name); + g_free (entry->reverse_entry); + } + + g_free (keylist); + g_free (keys); +} + +static void +append_sections_from_gsettings (CcKeyboardManager *self) +{ + char **custom_paths; + GArray *entries; + KeyListEntry key = { 0, 0, 0, 0, 0, 0, 0 }; + int i; + + /* load custom shortcuts from GSettings */ + entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry)); + + custom_paths = g_settings_get_strv (self->binding_settings, "custom-keybindings"); + for (i = 0; custom_paths[i]; i++) + { + key.name = g_strdup (custom_paths[i]); + if (!have_key_for_group (self, BINDING_GROUP_USER, key.name)) + { + key.type = CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH; + g_array_append_val (entries, key); + } + else + g_free (key.name); + } + g_strfreev (custom_paths); + + if (entries->len > 0) + { + KeyListEntry *keys; + int i; + + /* Empty KeyListEntry to end the array */ + key.name = NULL; + g_array_append_val (entries, key); + + keys = (KeyListEntry *) entries->data; + append_section (self, _("Custom Shortcuts"), CUSTOM_SHORTCUTS_ID, BINDING_GROUP_USER, keys); + for (i = 0; i < entries->len; ++i) + { + g_free (keys[i].name); + } + } + else + { + append_section (self, _("Custom Shortcuts"), CUSTOM_SHORTCUTS_ID, BINDING_GROUP_USER, NULL); + } + + g_array_free (entries, TRUE); +} + +static void +reload_sections (CcKeyboardManager *self) +{ + GtkTreeModel *shortcut_model; + GHashTable *loaded_files; + GDir *dir; + gchar *default_wm_keybindings[] = { "Mutter", "GNOME Shell", NULL }; + gchar **wm_keybindings; + const gchar * const * data_dirs; + guint i; + + shortcut_model = GTK_TREE_MODEL (self->shortcuts_model); + + /* Clear previous models and hash tables */ + gtk_list_store_clear (GTK_LIST_STORE (self->sections_store)); + gtk_list_store_clear (GTK_LIST_STORE (shortcut_model)); + + g_clear_pointer (&self->kb_system_sections, g_hash_table_destroy); + self->kb_system_sections = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) free_key_array); + + g_clear_pointer (&self->kb_apps_sections, g_hash_table_destroy); + self->kb_apps_sections = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) free_key_array); + + g_clear_pointer (&self->kb_user_sections, g_hash_table_destroy); + self->kb_user_sections = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) free_key_array); + + /* Load WM keybindings */ +#ifdef GDK_WINDOWING_X11 + if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) + wm_keybindings = wm_common_get_current_keybindings (); + else +#endif + wm_keybindings = g_strdupv (default_wm_keybindings); + + loaded_files = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + data_dirs = g_get_system_data_dirs (); + for (i = 0; data_dirs[i] != NULL; i++) + { + char *dir_path; + const gchar *name; + + dir_path = g_build_filename (data_dirs[i], "gnome-control-center", "keybindings", NULL); + + dir = g_dir_open (dir_path, 0, NULL); + if (!dir) + { + g_free (dir_path); + continue; + } + + for (name = g_dir_read_name (dir) ; name ; name = g_dir_read_name (dir)) + { + gchar *path; + + if (g_str_has_suffix (name, ".xml") == FALSE) + continue; + + if (g_hash_table_lookup (loaded_files, name) != NULL) + { + g_debug ("Not loading %s, it was already loaded from another directory", name); + continue; + } + + g_hash_table_insert (loaded_files, g_strdup (name), GINT_TO_POINTER (1)); + path = g_build_filename (dir_path, name, NULL); + append_sections_from_file (self, path, data_dirs[i], wm_keybindings); + g_free (path); + } + + g_free (dir_path); + g_dir_close (dir); + } + + g_hash_table_destroy (loaded_files); + g_strfreev (wm_keybindings); + + /* Load custom keybindings */ + append_sections_from_gsettings (self); +} + +/* + * Callbacks + */ +static void +on_window_manager_change (const char *wm_name, + CcKeyboardManager *self) +{ + reload_sections (self); +} + +static void +cc_keyboard_manager_finalize (GObject *object) +{ + CcKeyboardManager *self = (CcKeyboardManager *)object; + + g_clear_pointer (&self->kb_system_sections, g_hash_table_destroy); + g_clear_pointer (&self->kb_apps_sections, g_hash_table_destroy); + g_clear_pointer (&self->kb_user_sections, g_hash_table_destroy); + g_clear_object (&self->binding_settings); + + g_clear_pointer (&self->wm_changed_id, wm_common_unregister_window_manager_change); + + G_OBJECT_CLASS (cc_keyboard_manager_parent_class)->finalize (object); +} + +static void +cc_keyboard_manager_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +} + +static void +cc_keyboard_manager_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +} + +static void +cc_keyboard_manager_class_init (CcKeyboardManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = cc_keyboard_manager_finalize; + object_class->get_property = cc_keyboard_manager_get_property; + object_class->set_property = cc_keyboard_manager_set_property; + + /** + * CcKeyboardManager:shortcut-added: + * + * Emited when a shortcut is added. + */ + signals[SHORTCUT_ADDED] = g_signal_new ("shortcut-added", + CC_TYPE_KEYBOARD_MANAGER, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, + 3, + CC_TYPE_KEYBOARD_ITEM, + G_TYPE_STRING, + G_TYPE_STRING); + + /** + * CcKeyboardManager:shortcut-changed: + * + * Emited when a shortcut is added. + */ + signals[SHORTCUT_CHANGED] = g_signal_new ("shortcut-changed", + CC_TYPE_KEYBOARD_MANAGER, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, + 1, + CC_TYPE_KEYBOARD_ITEM); + + + /** + * CcKeyboardManager:shortcut-removed: + * + * Emited when a shortcut is removed. + */ + signals[SHORTCUT_REMOVED] = g_signal_new ("shortcut-removed", + CC_TYPE_KEYBOARD_MANAGER, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, + 1, + CC_TYPE_KEYBOARD_ITEM); +} + +static void +cc_keyboard_manager_init (CcKeyboardManager *self) +{ + /* Bindings */ + self->binding_settings = g_settings_new (BINDINGS_SCHEMA); + + /* Setup the section models */ + self->sections_store = gtk_list_store_new (SECTION_N_COLUMNS, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_INT); + + self->shortcuts_model = gtk_list_store_new (DETAIL_N_COLUMNS, + G_TYPE_STRING, + G_TYPE_POINTER, + G_TYPE_INT); + +#ifdef GDK_WINDOWING_X11 + if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) + self->wm_changed_id = wm_common_register_window_manager_change ((GFunc) on_window_manager_change, + self); +#endif +} + + +CcKeyboardManager * +cc_keyboard_manager_new (void) +{ + return g_object_new (CC_TYPE_KEYBOARD_MANAGER, NULL); +} + +void +cc_keyboard_manager_load_shortcuts (CcKeyboardManager *self) +{ + g_return_if_fail (CC_IS_KEYBOARD_MANAGER (self)); + + reload_sections (self); + add_shortcuts (self); +} + +/** + * cc_keyboard_manager_create_custom_shortcut: + * @self: a #CcKeyboardPanel + * + * Creates a new temporary keyboard shortcut. + * + * Returns: (transfer full): a #CcKeyboardItem + */ +CcKeyboardItem* +cc_keyboard_manager_create_custom_shortcut (CcKeyboardManager *self) +{ + CcKeyboardItem *item; + gchar *settings_path; + + g_return_val_if_fail (CC_IS_KEYBOARD_MANAGER (self), NULL); + + item = cc_keyboard_item_new (CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH); + + settings_path = find_free_settings_path (self->binding_settings); + cc_keyboard_item_load_from_gsettings_path (item, settings_path, TRUE); + g_free (settings_path); + + item->model = GTK_TREE_MODEL (self->shortcuts_model); + item->group = BINDING_GROUP_USER; + + return item; +} + +/** + * cc_keyboard_manager_add_custom_shortcut: + * @self: a #CcKeyboardPanel + * @item: the #CcKeyboardItem to be added + * + * Effectively adds the custom shortcut. + */ +void +cc_keyboard_manager_add_custom_shortcut (CcKeyboardManager *self, + CcKeyboardItem *item) +{ + GPtrArray *keys_array; + GtkTreeIter iter; + GHashTable *hash; + GVariantBuilder builder; + char **settings_paths; + int i; + + g_return_if_fail (CC_IS_KEYBOARD_MANAGER (self)); + + hash = get_hash_for_group (self, BINDING_GROUP_USER); + keys_array = g_hash_table_lookup (hash, CUSTOM_SHORTCUTS_ID); + + if (keys_array == NULL) + { + keys_array = g_ptr_array_new (); + g_hash_table_insert (hash, g_strdup (CUSTOM_SHORTCUTS_ID), keys_array); + } + + g_ptr_array_add (keys_array, item); + + gtk_list_store_append (self->shortcuts_model, &iter); + gtk_list_store_set (self->shortcuts_model, &iter, DETAIL_KEYENTRY_COLUMN, item, -1); + + settings_paths = g_settings_get_strv (self->binding_settings, "custom-keybindings"); + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("as")); + + for (i = 0; settings_paths[i]; i++) + g_variant_builder_add (&builder, "s", settings_paths[i]); + + g_variant_builder_add (&builder, "s", item->gsettings_path); + + g_settings_set_value (self->binding_settings, "custom-keybindings", g_variant_builder_end (&builder)); + + g_signal_emit (self, signals[SHORTCUT_ADDED], + 0, + item, + CUSTOM_SHORTCUTS_ID, + _("Custom Shortcuts")); +} + +/** + * cc_keyboard_manager_remove_custom_shortcut: + * @self: a #CcKeyboardPanel + * @item: the #CcKeyboardItem to be added + * + * Removed the custom shortcut. + */ +void +cc_keyboard_manager_remove_custom_shortcut (CcKeyboardManager *self, + CcKeyboardItem *item) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GPtrArray *keys_array; + GVariantBuilder builder; + gboolean valid; + char **settings_paths; + int i; + + g_return_if_fail (CC_IS_KEYBOARD_MANAGER (self)); + + model = GTK_TREE_MODEL (self->shortcuts_model); + valid = gtk_tree_model_get_iter_first (model, &iter); + + /* Search for the iter */ + while (valid) + { + CcKeyboardItem *current_item; + + gtk_tree_model_get (model, &iter, + DETAIL_KEYENTRY_COLUMN, ¤t_item, + -1); + + if (current_item == item) + break; + + valid = gtk_tree_model_iter_next (model, &iter); + } + + /* Shortcut not found or not a custom shortcut */ + g_assert (valid); + g_assert (item->type == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH); + + g_settings_delay (item->settings); + g_settings_reset (item->settings, "name"); + g_settings_reset (item->settings, "command"); + g_settings_reset (item->settings, "binding"); + g_settings_apply (item->settings); + g_settings_sync (); + + settings_paths = g_settings_get_strv (self->binding_settings, "custom-keybindings"); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("as")); + + for (i = 0; settings_paths[i]; i++) + if (strcmp (settings_paths[i], item->gsettings_path) != 0) + g_variant_builder_add (&builder, "s", settings_paths[i]); + + g_settings_set_value (self->binding_settings, + "custom-keybindings", + g_variant_builder_end (&builder)); + + g_strfreev (settings_paths); + + keys_array = g_hash_table_lookup (get_hash_for_group (self, BINDING_GROUP_USER), CUSTOM_SHORTCUTS_ID); + g_ptr_array_remove (keys_array, item); + + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); + + g_signal_emit (self, signals[SHORTCUT_REMOVED], 0, item); +} diff --git a/panels/keyboard/cc-keyboard-manager.h b/panels/keyboard/cc-keyboard-manager.h new file mode 100644 index 000000000..ab14d4a4a --- /dev/null +++ b/panels/keyboard/cc-keyboard-manager.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 Intel, Inc + * Copyright (C) 2016 Endless, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Author: Thomas Wood + * Georges Basile Stavracas Neto + * + */ + +#ifndef CC_KEYBOARD_MANAGER_H +#define CC_KEYBOARD_MANAGER_H + +#include + +#include "cc-keyboard-item.h" + +G_BEGIN_DECLS + +#define CC_TYPE_KEYBOARD_MANAGER (cc_keyboard_manager_get_type()) + +G_DECLARE_FINAL_TYPE (CcKeyboardManager, cc_keyboard_manager, CC, KEYBOARD_MANAGER, GObject) + +CcKeyboardManager* cc_keyboard_manager_new (void); + +void cc_keyboard_manager_load_shortcuts (CcKeyboardManager *self); + +CcKeyboardItem* cc_keyboard_manager_create_custom_shortcut (CcKeyboardManager *self); + +void cc_keyboard_manager_add_custom_shortcut (CcKeyboardManager *self, + CcKeyboardItem *item); + +void cc_keyboard_manager_remove_custom_shortcut (CcKeyboardManager *self, + CcKeyboardItem *item); + +G_END_DECLS + +#endif /* CC_KEYBOARD_MANAGER_H */ + diff --git a/panels/keyboard/cc-keyboard-panel.c b/panels/keyboard/cc-keyboard-panel.c index 4af9530c7..aca093184 100644 --- a/panels/keyboard/cc-keyboard-panel.c +++ b/panels/keyboard/cc-keyboard-panel.c @@ -23,20 +23,13 @@ #include #include "cc-keyboard-item.h" +#include "cc-keyboard-manager.h" #include "cc-keyboard-option.h" #include "cc-keyboard-panel.h" #include "cc-keyboard-resources.h" #include "cc-keyboard-shortcut-editor.h" #include "keyboard-shortcuts.h" -#include "wm-common.h" - -#ifdef GDK_WINDOWING_X11 -#include -#endif - -#define BINDINGS_SCHEMA "org.gnome.settings-daemon.plugins.media-keys" -#define CUSTOM_SHORTCUTS_ID "custom" typedef struct { CcKeyboardItem *item; @@ -49,9 +42,6 @@ struct _CcKeyboardPanel CcPanel parent; /* Shortcut models */ - GtkListStore *shortcuts_model; - GtkListStore *sections_store; - GtkTreeModel *sections_model; GtkWidget *listbox; GtkListBoxRow *add_shortcut_row; GtkSizeGroup *accelerator_sizegroup; @@ -59,15 +49,9 @@ struct _CcKeyboardPanel /* Custom shortcut dialog */ GtkWidget *shortcut_editor; - GHashTable *kb_system_sections; - GHashTable *kb_apps_sections; - GHashTable *kb_user_sections; - - GSettings *binding_settings; - GRegex *pictures_regex; - gpointer wm_changed_id; + CcKeyboardManager *manager; }; CC_PANEL_REGISTER (CcKeyboardPanel, cc_keyboard_panel) @@ -307,694 +291,6 @@ header_function (GtkListBoxRow *row, } } -static GHashTable* -get_hash_for_group (CcKeyboardPanel *self, - BindingGroupType group) -{ - GHashTable *hash; - - switch (group) - { - case BINDING_GROUP_SYSTEM: - hash = self->kb_system_sections; - break; - case BINDING_GROUP_APPS: - hash = self->kb_apps_sections; - break; - case BINDING_GROUP_USER: - hash = self->kb_user_sections; - break; - default: - hash = NULL; - } - - return hash; -} - -static gboolean -have_key_for_group (CcKeyboardPanel *self, - int group, - const gchar *name) -{ - GHashTableIter iter; - GPtrArray *keys; - gint i; - - g_hash_table_iter_init (&iter, get_hash_for_group (self, group)); - while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &keys)) - { - for (i = 0; i < keys->len; i++) - { - CcKeyboardItem *item = g_ptr_array_index (keys, i); - - if (item->type == CC_KEYBOARD_ITEM_TYPE_GSETTINGS && - g_strcmp0 (name, item->key) == 0) - { - return TRUE; - } - - return FALSE; - } - } - - return FALSE; -} - -static void -free_key_array (GPtrArray *keys) -{ - if (keys != NULL) - { - gint i; - - for (i = 0; i < keys->len; i++) - { - CcKeyboardItem *item; - - item = g_ptr_array_index (keys, i); - - g_object_unref (item); - } - - g_ptr_array_free (keys, TRUE); - } -} - -static char* -binding_name (guint keyval, - guint keycode, - GdkModifierType mask, - gboolean translate) -{ - if (keyval != 0 || keycode != 0) - { - return translate ? gtk_accelerator_get_label_with_keycode (NULL, keyval, keycode, mask) : - gtk_accelerator_name_with_keycode (NULL, keyval, keycode, mask); - } - else - { - return g_strdup (translate ? _("Disabled") : NULL); - } -} - - -static gboolean -keybinding_key_changed_foreach (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - CcKeyboardItem *item) -{ - CcKeyboardItem *tmp_item; - - gtk_tree_model_get (item->model, - iter, - DETAIL_KEYENTRY_COLUMN, &tmp_item, - -1); - - if (item == tmp_item) - { - gtk_tree_model_row_changed (item->model, path, iter); - return TRUE; - } - - return FALSE; -} - -static void -item_changed (CcKeyboardItem *item, - GParamSpec *pspec, - gpointer user_data) -{ - /* update the model */ - gtk_tree_model_foreach (item->model, - (GtkTreeModelForeachFunc) keybinding_key_changed_foreach, - item); -} - -static void -append_section (CcKeyboardPanel *self, - const gchar *title, - const gchar *id, - BindingGroupType group, - const KeyListEntry *keys_list) -{ - GtkTreeModel *shortcut_model; - GtkTreeIter iter; - GHashTable *reverse_items; - GHashTable *hash; - GPtrArray *keys_array; - gboolean is_new; - gint i; - - hash = get_hash_for_group (self, group); - - if (!hash) - return; - - shortcut_model = GTK_TREE_MODEL (self->shortcuts_model); - - /* Add all CcKeyboardItems for this section */ - is_new = FALSE; - keys_array = g_hash_table_lookup (hash, id); - if (keys_array == NULL) - { - keys_array = g_ptr_array_new (); - is_new = TRUE; - } - - reverse_items = g_hash_table_new (g_str_hash, g_str_equal); - - for (i = 0; keys_list != NULL && keys_list[i].name != NULL; i++) - { - CcKeyboardItem *item; - gboolean ret; - - if (have_key_for_group (self, group, keys_list[i].name)) - continue; - - item = cc_keyboard_item_new (keys_list[i].type); - switch (keys_list[i].type) - { - case CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH: - ret = cc_keyboard_item_load_from_gsettings_path (item, keys_list[i].name, FALSE); - break; - - case CC_KEYBOARD_ITEM_TYPE_GSETTINGS: - ret = cc_keyboard_item_load_from_gsettings (item, - keys_list[i].description, - keys_list[i].schema, - keys_list[i].name); - if (ret && keys_list[i].reverse_entry != NULL) - { - CcKeyboardItem *reverse_item; - reverse_item = g_hash_table_lookup (reverse_items, - keys_list[i].reverse_entry); - if (reverse_item != NULL) - { - cc_keyboard_item_add_reverse_item (item, - reverse_item, - keys_list[i].is_reversed); - } - else - { - g_hash_table_insert (reverse_items, - keys_list[i].name, - item); - } - } - break; - - default: - g_assert_not_reached (); - } - - if (ret == FALSE) - { - /* We don't actually want to popup a dialog - just skip this one */ - g_object_unref (item); - continue; - } - - cc_keyboard_item_set_hidden (item, keys_list[i].hidden); - item->model = shortcut_model; - item->group = group; - - g_signal_connect (G_OBJECT (item), - "notify", - G_CALLBACK (item_changed), - NULL); - - g_ptr_array_add (keys_array, item); - } - - g_hash_table_destroy (reverse_items); - - /* Add the keys to the hash table */ - if (is_new) - { - g_hash_table_insert (hash, g_strdup (id), keys_array); - - /* Append the section to the left tree view */ - gtk_list_store_append (GTK_LIST_STORE (self->sections_store), &iter); - gtk_list_store_set (GTK_LIST_STORE (self->sections_store), - &iter, - SECTION_DESCRIPTION_COLUMN, title, - SECTION_ID_COLUMN, id, - SECTION_GROUP_COLUMN, group, - -1); - } -} - -static void -append_sections_from_file (CcKeyboardPanel *self, - const gchar *path, - const char *datadir, - gchar **wm_keybindings) -{ - KeyList *keylist; - KeyListEntry *keys; - KeyListEntry key = { 0, 0, 0, 0, 0, 0, 0 }; - const char *title; - int group; - guint i; - - keylist = parse_keylist_from_file (path); - - if (keylist == NULL) - return; - -#define const_strv(s) ((const gchar* const*) s) - - /* If there's no keys to add, or the settings apply to a window manager - * that's not the one we're running */ - if (keylist->entries->len == 0 || - (keylist->wm_name != NULL && !g_strv_contains (const_strv (wm_keybindings), keylist->wm_name)) || - keylist->name == NULL) - { - g_free (keylist->name); - g_free (keylist->package); - g_free (keylist->wm_name); - g_array_free (keylist->entries, TRUE); - g_free (keylist); - return; - } - -#undef const_strv - - /* Empty KeyListEntry to end the array */ - key.name = NULL; - g_array_append_val (keylist->entries, key); - - keys = (KeyListEntry *) g_array_free (keylist->entries, FALSE); - if (keylist->package) - { - char *localedir; - - localedir = g_build_filename (datadir, "locale", NULL); - bindtextdomain (keylist->package, localedir); - g_free (localedir); - - title = dgettext (keylist->package, keylist->name); - } else { - title = _(keylist->name); - } - if (keylist->group && strcmp (keylist->group, "system") == 0) - group = BINDING_GROUP_SYSTEM; - else - group = BINDING_GROUP_APPS; - - append_section (self, title, keylist->name, group, keys); - g_free (keylist->name); - g_free (keylist->package); - g_free (keylist->wm_name); - g_free (keylist->schema); - g_free (keylist->group); - - for (i = 0; keys[i].name != NULL; i++) - { - KeyListEntry *entry = &keys[i]; - g_free (entry->schema); - g_free (entry->description); - g_free (entry->name); - g_free (entry->reverse_entry); - } - - g_free (keylist); - g_free (keys); -} - -static void -append_sections_from_gsettings (CcKeyboardPanel *self) -{ - char **custom_paths; - GArray *entries; - KeyListEntry key = { 0, 0, 0, 0, 0, 0, 0 }; - int i; - - /* load custom shortcuts from GSettings */ - entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry)); - - custom_paths = g_settings_get_strv (self->binding_settings, "custom-keybindings"); - for (i = 0; custom_paths[i]; i++) - { - key.name = g_strdup (custom_paths[i]); - if (!have_key_for_group (self, BINDING_GROUP_USER, key.name)) - { - key.type = CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH; - g_array_append_val (entries, key); - } - else - g_free (key.name); - } - g_strfreev (custom_paths); - - if (entries->len > 0) - { - KeyListEntry *keys; - int i; - - /* Empty KeyListEntry to end the array */ - key.name = NULL; - g_array_append_val (entries, key); - - keys = (KeyListEntry *) entries->data; - append_section (self, _("Custom Shortcuts"), CUSTOM_SHORTCUTS_ID, BINDING_GROUP_USER, keys); - for (i = 0; i < entries->len; ++i) - { - g_free (keys[i].name); - } - } - else - { - append_section (self, _("Custom Shortcuts"), CUSTOM_SHORTCUTS_ID, BINDING_GROUP_USER, NULL); - } - - g_array_free (entries, TRUE); -} - -static void -reload_sections (CcKeyboardPanel *self) -{ - GtkTreeModel *shortcut_model; - GHashTable *loaded_files; - GDir *dir; - gchar *default_wm_keybindings[] = { "Mutter", "GNOME Shell", NULL }; - gchar **wm_keybindings; - const gchar * const * data_dirs; - guint i; - - shortcut_model = GTK_TREE_MODEL (self->shortcuts_model); - /* FIXME: get current selection and keep it after refreshing */ - - /* Clear previous models and hash tables */ - gtk_list_store_clear (GTK_LIST_STORE (self->sections_store)); - gtk_list_store_clear (GTK_LIST_STORE (shortcut_model)); - - g_clear_pointer (&self->kb_system_sections, g_hash_table_destroy); - self->kb_system_sections = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - (GDestroyNotify) free_key_array); - - g_clear_pointer (&self->kb_apps_sections, g_hash_table_destroy); - self->kb_apps_sections = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - (GDestroyNotify) free_key_array); - - g_clear_pointer (&self->kb_user_sections, g_hash_table_destroy); - self->kb_user_sections = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - (GDestroyNotify) free_key_array); - - /* Load WM keybindings */ -#ifdef GDK_WINDOWING_X11 - if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) - wm_keybindings = wm_common_get_current_keybindings (); - else -#endif - wm_keybindings = g_strdupv (default_wm_keybindings); - - loaded_files = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - - data_dirs = g_get_system_data_dirs (); - for (i = 0; data_dirs[i] != NULL; i++) - { - char *dir_path; - const gchar *name; - - dir_path = g_build_filename (data_dirs[i], "gnome-control-center", "keybindings", NULL); - - dir = g_dir_open (dir_path, 0, NULL); - if (!dir) - { - g_free (dir_path); - continue; - } - - for (name = g_dir_read_name (dir) ; name ; name = g_dir_read_name (dir)) - { - gchar *path; - - if (g_str_has_suffix (name, ".xml") == FALSE) - continue; - - if (g_hash_table_lookup (loaded_files, name) != NULL) - { - g_debug ("Not loading %s, it was already loaded from another directory", name); - continue; - } - - g_hash_table_insert (loaded_files, g_strdup (name), GINT_TO_POINTER (1)); - path = g_build_filename (dir_path, name, NULL); - append_sections_from_file (self, path, data_dirs[i], wm_keybindings); - g_free (path); - } - g_free (dir_path); - g_dir_close (dir); - } - - g_hash_table_destroy (loaded_files); - g_strfreev (wm_keybindings); - - /* Load custom keybindings */ - append_sections_from_gsettings (self); -} - -static int -section_sort_item (GtkTreeModel *model, - GtkTreeIter *a, - GtkTreeIter *b, - gpointer data) -{ - char *a_desc; - int a_group; - char *b_desc; - int b_group; - int ret; - - gtk_tree_model_get (model, a, - SECTION_DESCRIPTION_COLUMN, &a_desc, - SECTION_GROUP_COLUMN, &a_group, - -1); - gtk_tree_model_get (model, b, - SECTION_DESCRIPTION_COLUMN, &b_desc, - SECTION_GROUP_COLUMN, &b_group, - -1); - - if (a_group == b_group && a_desc && b_desc) - ret = g_utf8_collate (a_desc, b_desc); - else - ret = a_group - b_group; - - g_free (a_desc); - g_free (b_desc); - - return ret; -} - -static void -add_shortcuts (CcKeyboardPanel *self) -{ - GtkTreeIter sections_iter; - gboolean can_continue; - - can_continue = gtk_tree_model_get_iter_first (self->sections_model, §ions_iter); - - while (can_continue) - { - BindingGroupType group; - GPtrArray *keys; - gchar *id, *title; - gint i; - - gtk_tree_model_get (self->sections_model, - §ions_iter, - SECTION_DESCRIPTION_COLUMN, &title, - SECTION_GROUP_COLUMN, &group, - SECTION_ID_COLUMN, &id, - -1); - - /* Ignore separators */ - if (group == BINDING_GROUP_SEPARATOR) - { - can_continue = gtk_tree_model_iter_next (self->sections_model, §ions_iter); - continue; - } - - keys = g_hash_table_lookup (get_hash_for_group (self, group), id); - - for (i = 0; i < keys->len; i++) - { - CcKeyboardItem *item = g_ptr_array_index (keys, i); - - if (!cc_keyboard_item_is_hidden (item)) - { - GtkTreeIter new_row; - - gtk_list_store_append (self->shortcuts_model, &new_row); - gtk_list_store_set (self->shortcuts_model, - &new_row, - DETAIL_DESCRIPTION_COLUMN, item->description, - DETAIL_KEYENTRY_COLUMN, item, - DETAIL_TYPE_COLUMN, SHORTCUT_TYPE_KEY_ENTRY, - -1); - - add_item (self, item, id, title); - } - } - - can_continue = gtk_tree_model_iter_next (self->sections_model, §ions_iter); - - g_free (title); - g_free (id); - } -} - -static void -shortcut_selection_changed (GtkTreeSelection *selection, - GtkWidget *button) -{ - GtkTreeModel *model; - GtkTreeIter iter; - gboolean can_remove; - - can_remove = FALSE; - - if (gtk_tree_selection_get_selected (selection, &model, &iter)) - { - CcKeyboardItem *item; - ShortcutType type; - - gtk_tree_model_get (model, &iter, - DETAIL_KEYENTRY_COLUMN, &item, - DETAIL_TYPE_COLUMN, &type, - -1); - - if (type == SHORTCUT_TYPE_KEY_ENTRY && - item && - item->command != NULL && - item->editable) - { - can_remove = TRUE; - } - } - - gtk_widget_set_sensitive (button, can_remove); -} - - -static gboolean -remove_custom_shortcut (CcKeyboardShortcutEditor *editor, - CcKeyboardItem *item, - CcKeyboardPanel *self) -{ - GtkTreeModel *model; - GtkTreeIter iter; - GPtrArray *keys_array; - GVariantBuilder builder; - gboolean valid; - char **settings_paths; - int i; - - model = GTK_TREE_MODEL (self->shortcuts_model); - valid = gtk_tree_model_get_iter_first (model, &iter); - - /* Search for the iter */ - while (valid) - { - CcKeyboardItem *current_item; - - gtk_tree_model_get (model, &iter, - DETAIL_KEYENTRY_COLUMN, ¤t_item, - -1); - - if (current_item == item) - break; - - valid = gtk_tree_model_iter_next (model, &iter); - - g_clear_object (¤t_item); - } - - if (!valid) - g_error ("Tried to remove a non-existant shortcut"); - - g_assert (item->type == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH); - - remove_item (self, item); - - g_settings_delay (item->settings); - g_settings_reset (item->settings, "name"); - g_settings_reset (item->settings, "command"); - g_settings_reset (item->settings, "binding"); - g_settings_apply (item->settings); - g_settings_sync (); - - settings_paths = g_settings_get_strv (self->binding_settings, "custom-keybindings"); - g_variant_builder_init (&builder, G_VARIANT_TYPE ("as")); - - for (i = 0; settings_paths[i]; i++) - if (strcmp (settings_paths[i], item->gsettings_path) != 0) - g_variant_builder_add (&builder, "s", settings_paths[i]); - - g_settings_set_value (self->binding_settings, - "custom-keybindings", - g_variant_builder_end (&builder)); - - g_strfreev (settings_paths); - g_object_unref (item); - - keys_array = g_hash_table_lookup (get_hash_for_group (self, BINDING_GROUP_USER), CUSTOM_SHORTCUTS_ID); - g_ptr_array_remove (keys_array, item); - - gtk_list_store_remove (GTK_LIST_STORE (model), &iter); - - return TRUE; -} - -static void -add_custom_shortcut (CcKeyboardShortcutEditor *editor, - CcKeyboardItem *item, - CcKeyboardPanel *self) -{ - GtkTreePath *path; - GPtrArray *keys_array; - GtkTreeIter iter; - GHashTable *hash; - GVariantBuilder builder; - char **settings_paths; - int i; - - hash = get_hash_for_group (self, BINDING_GROUP_USER); - keys_array = g_hash_table_lookup (hash, CUSTOM_SHORTCUTS_ID); - if (keys_array == NULL) - { - keys_array = g_ptr_array_new (); - g_hash_table_insert (hash, g_strdup (CUSTOM_SHORTCUTS_ID), keys_array); - } - - g_ptr_array_add (keys_array, item); - - gtk_list_store_append (self->shortcuts_model, &iter); - gtk_list_store_set (self->shortcuts_model, &iter, DETAIL_KEYENTRY_COLUMN, item, -1); - - settings_paths = g_settings_get_strv (self->binding_settings, "custom-keybindings"); - g_variant_builder_init (&builder, G_VARIANT_TYPE ("as")); - for (i = 0; settings_paths[i]; i++) - g_variant_builder_add (&builder, "s", settings_paths[i]); - g_variant_builder_add (&builder, "s", item->gsettings_path); - g_settings_set_value (self->binding_settings, "custom-keybindings", - g_variant_builder_end (&builder)); - - /* make the new shortcut visible */ - path = gtk_tree_model_get_path (GTK_TREE_MODEL (self->shortcuts_model), &iter); - gtk_tree_path_free (path); - - add_item (self, item, CUSTOM_SHORTCUTS_ID, _("Custom Shortcuts")); -} - static void shortcut_row_activated (GtkWidget *button, GtkListBoxRow *row, @@ -1020,34 +316,6 @@ shortcut_row_activated (GtkWidget *button, gtk_widget_show (self->shortcut_editor); } -static void -setup_tree_views (CcKeyboardPanel *self) -{ - /* Setup the section treeview */ - self->sections_store = gtk_list_store_new (SECTION_N_COLUMNS, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_INT); - self->sections_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (self->sections_store)); - - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (self->sections_model), - SECTION_DESCRIPTION_COLUMN, - section_sort_item, - self, - NULL); - - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (self->sections_model), - SECTION_DESCRIPTION_COLUMN, - GTK_SORT_ASCENDING); - - self->shortcuts_model = gtk_list_store_new (DETAIL_N_COLUMNS, - G_TYPE_STRING, - G_TYPE_POINTER, - G_TYPE_INT); - - setup_keyboard_options (self->shortcuts_model); -} - static void cc_keyboard_panel_set_property (GObject *object, guint property_id, @@ -1075,30 +343,14 @@ cc_keyboard_panel_finalize (GObject *object) { CcKeyboardPanel *self = CC_KEYBOARD_PANEL (object); - g_clear_pointer (&self->kb_system_sections, g_hash_table_destroy); - g_clear_pointer (&self->kb_apps_sections, g_hash_table_destroy); - g_clear_pointer (&self->kb_user_sections, g_hash_table_destroy); g_clear_pointer (&self->pictures_regex, g_regex_unref); - g_clear_pointer (&self->wm_changed_id, wm_common_unregister_window_manager_change); - g_clear_object (&self->accelerator_sizegroup); - g_clear_object (&self->binding_settings); - g_clear_object (&self->shortcuts_model); - g_clear_object (&self->sections_store); - g_clear_object (&self->sections_model); cc_keyboard_option_clear_all (); G_OBJECT_CLASS (cc_keyboard_panel_parent_class)->finalize (object); } -static void -on_window_manager_change (const char *wm_name, - CcKeyboardPanel *self) -{ - reload_sections (self); -} - static void cc_keyboard_panel_constructed (GObject *object) { @@ -1110,17 +362,6 @@ cc_keyboard_panel_constructed (GObject *object) /* Setup the dialog's transient parent */ toplevel = GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self)))); gtk_window_set_transient_for (GTK_WINDOW (self->shortcut_editor), toplevel); - -#ifdef GDK_WINDOWING_X11 - if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) - self->wm_changed_id = wm_common_register_window_manager_change ((GFunc) on_window_manager_change, - self); -#endif - - setup_tree_views (self); - reload_sections (self); - - add_shortcuts (self); } static void @@ -1144,7 +385,6 @@ cc_keyboard_panel_class_init (CcKeyboardPanelClass *klass) gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, listbox); gtk_widget_class_bind_template_callback (widget_class, shortcut_row_activated); - gtk_widget_class_bind_template_callback (widget_class, shortcut_selection_changed); } static void @@ -1154,23 +394,28 @@ cc_keyboard_panel_init (CcKeyboardPanel *self) gtk_widget_init_template (GTK_WIDGET (self)); - self->binding_settings = g_settings_new (BINDINGS_SCHEMA); + self->manager = cc_keyboard_manager_new (); /* Use a sizegroup to make the accelerator labels the same width */ self->accelerator_sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); /* Shortcut editor dialog */ - self->shortcut_editor = cc_keyboard_shortcut_editor_new (self); + self->shortcut_editor = cc_keyboard_shortcut_editor_new (self->manager); - g_signal_connect (self->shortcut_editor, - "add-custom-shortcut", - G_CALLBACK (add_custom_shortcut), - self); + g_signal_connect_swapped (self->manager, + "shortcut-added", + G_CALLBACK (add_item), + self); - g_signal_connect (self->shortcut_editor, - "remove-custom-shortcut", - G_CALLBACK (remove_custom_shortcut), - self); + g_signal_connect_swapped (self->manager, + "shortcut-removed", + G_CALLBACK (remove_item), + self); + + cc_keyboard_manager_load_shortcuts (self->manager); + + /* Shortcut editor dialog */ + self->shortcut_editor = cc_keyboard_shortcut_editor_new (self->manager); /* Setup the shortcuts listbox */ gtk_list_box_set_sort_func (GTK_LIST_BOX (self->listbox), @@ -1184,31 +429,3 @@ cc_keyboard_panel_init (CcKeyboardPanel *self) NULL); } -/** - * cc_keyboard_panel_create_custom_item: - * @self: a #CcKeyboardPanel - * - * Creates a new temporary keyboard shortcut. - * - * Returns: (transfer full): a #CcKeyboardItem - */ -CcKeyboardItem* -cc_keyboard_panel_create_custom_item (CcKeyboardPanel *self) -{ - CcKeyboardItem *item; - gchar *settings_path; - - g_return_val_if_fail (CC_IS_KEYBOARD_PANEL (self), NULL); - - item = cc_keyboard_item_new (CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH); - - settings_path = find_free_settings_path (self->binding_settings); - cc_keyboard_item_load_from_gsettings_path (item, settings_path, TRUE); - g_free (settings_path); - - item->model = GTK_TREE_MODEL (self->shortcuts_model); - item->group = BINDING_GROUP_USER; - - return item; -} - diff --git a/panels/keyboard/cc-keyboard-shortcut-editor.c b/panels/keyboard/cc-keyboard-shortcut-editor.c index f9b4c4aa6..eef7a2872 100644 --- a/panels/keyboard/cc-keyboard-shortcut-editor.c +++ b/panels/keyboard/cc-keyboard-shortcut-editor.c @@ -50,7 +50,7 @@ struct _CcKeyboardShortcutEditor GdkDevice *grab_device; - CcKeyboardPanel *panel; + CcKeyboardManager *manager; CcKeyboardItem *item; /* Custom shortcuts */ @@ -72,19 +72,11 @@ enum { PROP_0, PROP_KEYBOARD_ITEM, - PROP_PANEL, + PROP_MANAGER, N_PROPS }; -enum -{ - ADD_CUSTOM_SHORTCUT, - REMOVE_CUSTOM_SHORTCUT, - LAST_SIGNAL -}; - static GParamSpec *properties [N_PROPS] = { NULL, }; -static guint signals[LAST_SIGNAL] = { 0, }; static void apply_custom_item_fields (CcKeyboardShortcutEditor *self, @@ -271,7 +263,7 @@ add_button_clicked_cb (CcKeyboardShortcutEditor *self) { CcKeyboardItem *item; - item = cc_keyboard_panel_create_custom_item (self->panel); + item = cc_keyboard_manager_create_custom_shortcut (self->manager); /* Apply the custom shortcut setup at the new item */ apply_custom_item_fields (self, item); @@ -279,7 +271,7 @@ add_button_clicked_cb (CcKeyboardShortcutEditor *self) /* Cleanup everything once we're done */ clear_custom_entries (self); - g_signal_emit (self, signals[ADD_CUSTOM_SHORTCUT], 0, item); + cc_keyboard_manager_add_custom_shortcut (self->manager, item); gtk_widget_hide (GTK_WIDGET (self)); } @@ -322,7 +314,7 @@ remove_button_clicked_cb (CcKeyboardShortcutEditor *self) { gtk_widget_hide (GTK_WIDGET (self)); - g_signal_emit (self, signals[REMOVE_CUSTOM_SHORTCUT], 0, self->item); + cc_keyboard_manager_remove_custom_shortcut (self->manager, self->item); } static void @@ -389,6 +381,7 @@ cc_keyboard_shortcut_editor_finalize (GObject *object) CcKeyboardShortcutEditor *self = (CcKeyboardShortcutEditor *)object; g_clear_object (&self->item); + g_clear_object (&self->manager); G_OBJECT_CLASS (cc_keyboard_shortcut_editor_parent_class)->finalize (object); } @@ -407,8 +400,8 @@ cc_keyboard_shortcut_editor_get_property (GObject *object, g_value_set_object (value, self->item); break; - case PROP_PANEL: - g_value_set_pointer (value, self->panel); + case PROP_MANAGER: + g_value_set_object (value, self->manager); break; default: @@ -430,8 +423,8 @@ cc_keyboard_shortcut_editor_set_property (GObject *object, cc_keyboard_shortcut_editor_set_item (self, g_value_get_object (value)); break; - case PROP_PANEL: - self->panel = g_value_get_pointer (value); + case PROP_MANAGER: + g_set_object (&self->manager, g_value_get_object (value)); break; default: @@ -562,36 +555,11 @@ cc_keyboard_shortcut_editor_class_init (CcKeyboardShortcutEditorClass *klass) * * The current keyboard panel. */ - properties[PROP_PANEL] = g_param_spec_pointer ("panel", - "Keyboard panel", - "The keyboard panel being edited", - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); - - /** - * CcKeyboardShortcutEditor:add-custom-shortcut: - * - * Emited when the user asks to add a custom shortcut. - */ - signals[ADD_CUSTOM_SHORTCUT] = g_signal_new ("add-custom-shortcut", - CC_TYPE_KEYBOARD_SHORTCUT_EDITOR, - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, - 1, - CC_TYPE_KEYBOARD_ITEM); - - /** - * CcKeyboardShortcutEditor:remove-custom-shortcut: - * - * Emited when the user asks to remove a custom shortcut. - */ - signals[REMOVE_CUSTOM_SHORTCUT] = g_signal_new ("remove-custom-shortcut", - CC_TYPE_KEYBOARD_SHORTCUT_EDITOR, - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, - 1, - CC_TYPE_KEYBOARD_ITEM); + properties[PROP_MANAGER] = g_param_spec_object ("manager", + "Keyboard manager", + "The keyboard manager", + CC_TYPE_KEYBOARD_MANAGER, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (object_class, N_PROPS, properties); @@ -635,10 +603,10 @@ cc_keyboard_shortcut_editor_init (CcKeyboardShortcutEditor *self) * Returns: (transfer full): a newly created #CcKeyboardShortcutEditor. */ GtkWidget* -cc_keyboard_shortcut_editor_new (CcKeyboardPanel *panel) +cc_keyboard_shortcut_editor_new (CcKeyboardManager *manager) { return g_object_new (CC_TYPE_KEYBOARD_SHORTCUT_EDITOR, - "panel", panel, + "manager", manager, "use-header-bar", 1, NULL); } diff --git a/panels/keyboard/cc-keyboard-shortcut-editor.h b/panels/keyboard/cc-keyboard-shortcut-editor.h index c0fe67550..a9455a8e1 100644 --- a/panels/keyboard/cc-keyboard-shortcut-editor.h +++ b/panels/keyboard/cc-keyboard-shortcut-editor.h @@ -24,7 +24,7 @@ #include #include "cc-keyboard-item.h" -#include "cc-keyboard-panel.h" +#include "cc-keyboard-manager.h" G_BEGIN_DECLS @@ -38,7 +38,7 @@ typedef enum G_DECLARE_FINAL_TYPE (CcKeyboardShortcutEditor, cc_keyboard_shortcut_editor, CC, KEYBOARD_SHORTCUT_EDITOR, GtkDialog) -GtkWidget* cc_keyboard_shortcut_editor_new (CcKeyboardPanel *panel); +GtkWidget* cc_keyboard_shortcut_editor_new (CcKeyboardManager *manager); CcKeyboardItem* cc_keyboard_shortcut_editor_get_item (CcKeyboardShortcutEditor *self);