[gnome-control-center/wip/gbsneto/new-keyboard-panel: 13/27] keyboard: make it a template class
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-control-center/wip/gbsneto/new-keyboard-panel: 13/27] keyboard: make it a template class
- Date: Fri, 29 Jul 2016 16:44:06 +0000 (UTC)
commit d940d7bb5d8f3a7aceb3d4e91d16cbf0bf366c41
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date: Fri Jul 22 15:26:10 2016 -0300
keyboard: make it a template class
To allow a much easier porting to the new layout, the keyboard
panel is now a template class. That has various implications on
the code organization:
- The keyboard-shortcuts.c was responsible for filling the shortcuts.
Because it relied on the GtkBuilder of the panel, most of its code
was moved to the CcKeyboardPanel class.
- The unused code from the keyboard panel class had to be removed in
order to make it work again.
- All the hash tables and widgets are now part of the CcKeyboardPanel
structure.
- The interface elements have a single entry point.
https://bugzilla.gnome.org/show_bug.cgi?id=769063
panels/keyboard/cc-keyboard-item.h | 1 +
panels/keyboard/cc-keyboard-panel.c | 1852 +++++++++++++++++++++++++++++--
panels/keyboard/cc-keyboard-panel.h | 1 +
panels/keyboard/gnome-keyboard-panel.ui | 280 +++---
panels/keyboard/keyboard-shortcuts.c | 1825 +-----------------------------
panels/keyboard/keyboard-shortcuts.h | 14 +-
6 files changed, 1965 insertions(+), 2008 deletions(-)
---
diff --git a/panels/keyboard/cc-keyboard-item.h b/panels/keyboard/cc-keyboard-item.h
index 5d18187..8792ba0 100644
--- a/panels/keyboard/cc-keyboard-item.h
+++ b/panels/keyboard/cc-keyboard-item.h
@@ -21,6 +21,7 @@
#define __CC_KEYBOARD_ITEM_H
#include <glib-object.h>
+#include <gtk/gtk.h>
G_BEGIN_DECLS
diff --git a/panels/keyboard/cc-keyboard-panel.c b/panels/keyboard/cc-keyboard-panel.c
index 38fe4ba..992cfc8 100644
--- a/panels/keyboard/cc-keyboard-panel.c
+++ b/panels/keyboard/cc-keyboard-panel.c
@@ -1,5 +1,6 @@
/*
* 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
@@ -15,21 +16,61 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Thomas Wood <thomas wood intel com>
+ * Georges Basile Stavracas Neto <gbsneto gnome org>
*
*/
+#include <glib/gi18n.h>
+
+#include "cc-keyboard-item.h"
+#include "cc-keyboard-option.h"
#include "cc-keyboard-panel.h"
#include "cc-keyboard-resources.h"
#include "keyboard-shortcuts.h"
+#include "wm-common.h"
+
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+
+#define BINDINGS_SCHEMA "org.gnome.settings-daemon.plugins.media-keys"
+#define CUSTOM_SHORTCUTS_ID "custom"
struct _CcKeyboardPanel
{
CcPanel parent;
- GtkBuilder *builder;
+ /* Treeviews */
+ GtkWidget *section_treeview;
+ GtkWidget *shortcut_treeview;
+
+ /* Toolbar widgets */
+ GtkWidget *add_toolbutton;
+ GtkWidget *remove_toolbutton;
+ GtkWidget *shortcut_toolbar;
+
+ /* Custom shortcut dialog */
+ GtkWidget *custom_shortcut_command_entry;
+ GtkWidget *custom_shortcut_dialog;
+ GtkWidget *custom_shortcut_name_entry;
+ GtkWidget *custom_shortcut_ok_button;
+
+ GHashTable *kb_system_sections;
+ GHashTable *kb_apps_sections;
+ GHashTable *kb_user_sections;
+
+ GSettings *binding_settings;
+
+ GRegex *pictures_regex;
+
+ gpointer wm_changed_id;
+ gchar *section_to_set;
};
+static gboolean cc_keyboard_panel_set_section (CcKeyboardPanel *self,
+ const char *section);
+
CC_PANEL_REGISTER (CcKeyboardPanel, cc_keyboard_panel)
enum {
@@ -37,101 +78,1702 @@ enum {
PROP_PARAMETERS
};
-enum {
- TYPING_PAGE,
- SHORTCUTS_PAGE
-};
+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
-cc_keyboard_panel_set_page (CcKeyboardPanel *panel,
- const gchar *page,
- const gchar *section)
+free_key_array (GPtrArray *keys)
{
- GtkWidget *notebook;
- gint page_num;
+ if (keys != NULL)
+ {
+ gint i;
+
+ for (i = 0; i < keys->len; i++)
+ {
+ CcKeyboardItem *item;
- if (g_strcmp0 (page, "typing") == 0)
- page_num = TYPING_PAGE;
- else if (g_strcmp0 (page, "shortcuts") == 0)
- page_num = SHORTCUTS_PAGE;
- else {
- g_warning ("Could not switch to non-existent page '%s'", page);
+ 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 *sort_model;
+ GtkTreeModel *model, *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;
- }
- notebook = GTK_WIDGET (gtk_builder_get_object (panel->builder, "keyboard_notebook"));
- gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), page_num);
+ sort_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->section_treeview));
+ model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model));
+
+ shortcut_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->shortcut_treeview));
+
+ /* 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);
- if (page_num == SHORTCUTS_PAGE &&
- section != NULL) {
- keyboard_shortcuts_set_section (CC_PANEL (panel), section);
- }
+ 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;
+
+ 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 (model), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+ SECTION_DESCRIPTION_COLUMN, title,
+ SECTION_ID_COLUMN, id,
+ SECTION_GROUP_COLUMN, group,
+ -1);
+ }
}
static void
-cc_keyboard_panel_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
+append_sections_from_file (CcKeyboardPanel *self,
+ const gchar *path,
+ const char *datadir,
+ gchar **wm_keybindings)
{
- CcKeyboardPanel *panel = CC_KEYBOARD_PANEL (object);
+ KeyList *keylist;
+ KeyListEntry *keys;
+ KeyListEntry key = { 0, 0, 0, 0, 0, 0, 0 };
+ const char *title;
+ int group;
+ guint i;
- switch (property_id)
+ 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++)
{
- case PROP_PARAMETERS: {
- GVariant *parameters, *v;
- const gchar *page, *section;
-
- parameters = g_value_get_variant (value);
- if (!parameters)
- break;
- page = section = NULL;
- switch (g_variant_n_children (parameters))
- {
- case 2:
- g_variant_get_child (parameters, 1, "v", &v);
- section = g_variant_get_string (v, NULL);
- g_variant_unref (v);
- /* fall-through */
- case 1:
- g_variant_get_child (parameters, 0, "v", &v);
- page = g_variant_get_string (v, NULL);
- g_variant_unref (v);
- cc_keyboard_panel_set_page (panel, page, section);
- /* fall-through */
- case 0:
- break;
- default:
- g_warning ("Unexpected parameters found, ignore request");
+ 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)
+{
+ GtkTreeSelection *selection;
+ GtkTreeModel *shortcut_model;
+ GtkTreeModel *section_model;
+ GtkTreeModel *sort_model;
+ GtkTreeView *section_treeview;
+ GtkTreeIter iter;
+ GHashTable *loaded_files;
+ GDir *dir;
+ gchar *default_wm_keybindings[] = { "Mutter", "GNOME Shell", NULL };
+ gchar **wm_keybindings;
+ const gchar * const * data_dirs;
+ guint i;
+
+ section_treeview = GTK_TREE_VIEW (self->section_treeview);
+ sort_model = gtk_tree_view_get_model (section_treeview);
+ section_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model));
+
+ shortcut_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->shortcut_treeview));
+ /* FIXME: get current selection and keep it after refreshing */
+
+ /* Clear previous models and hash tables */
+ gtk_list_store_clear (GTK_LIST_STORE (section_model));
+ 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);
+
+ /* Add a separator */
+ gtk_list_store_append (GTK_LIST_STORE (section_model), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (section_model), &iter,
+ SECTION_DESCRIPTION_COLUMN, NULL,
+ SECTION_GROUP_COLUMN, BINDING_GROUP_SEPARATOR,
+ -1);
+
+ /* Load custom keybindings */
+ append_sections_from_gsettings (self);
+
+ /* Select the first item, or the requested section, if any */
+ if (self->section_to_set != NULL)
+ {
+ if (cc_keyboard_panel_set_section (self, self->section_to_set))
+ {
+ g_clear_pointer (&self->section_to_set, g_free);
+ return;
}
- break;
}
+ g_assert (gtk_tree_model_get_iter_first (sort_model, &iter));
+ selection = gtk_tree_view_get_selection (section_treeview);
+ gtk_tree_selection_select_iter (selection, &iter);
+}
+
+static gboolean
+sections_separator_func (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ BindingGroupType type;
+
+ gtk_tree_model_get (model, iter, SECTION_GROUP_COLUMN, &type, -1);
+
+ return type == BINDING_GROUP_SEPARATOR;
+}
+
+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
+section_selection_changed (GtkTreeSelection *selection,
+ CcKeyboardPanel *self)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ BindingGroupType group;
+ GtkTreeModel *shortcut_model;
+ GPtrArray *keys;
+ gchar *id;
+ gint i;
+
+ gtk_tree_model_get (model, &iter,
+ SECTION_ID_COLUMN, &id,
+ SECTION_GROUP_COLUMN, &group, -1);
+
+ keys = g_hash_table_lookup (get_hash_for_group (self, group), id);
+ if (keys == NULL)
+ {
+ g_warning ("Can't find section %s in sections hash table.", id);
+ g_free (id);
+ return;
+ }
+
+ gtk_widget_set_sensitive (self->remove_toolbutton, FALSE);
+
+ /* Fill the shortcut treeview with the keys for the selected section */
+ shortcut_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->shortcut_treeview));
+ gtk_list_store_clear (GTK_LIST_STORE (shortcut_model));
+
+ for (i = 0; i < keys->len; i++)
+ {
+ GtkTreeIter new_row;
+ CcKeyboardItem *item = g_ptr_array_index (keys, i);
+
+ if (!cc_keyboard_item_is_hidden (item))
+ {
+ gtk_list_store_append (GTK_LIST_STORE (shortcut_model), &new_row);
+ gtk_list_store_set (GTK_LIST_STORE (shortcut_model), &new_row,
+ DETAIL_DESCRIPTION_COLUMN, item->description,
+ DETAIL_KEYENTRY_COLUMN, item,
+ DETAIL_TYPE_COLUMN, SHORTCUT_TYPE_KEY_ENTRY,
+ -1);
+ }
+ }
+
+ if (g_str_equal (id, "Typing"))
+ fill_xkb_options_shortcuts (shortcut_model);
+
+ g_free (id);
+ }
+}
+
+static void
+description_set_func (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gchar *description;
+ CcKeyboardItem *item;
+ ShortcutType type;
+
+ gtk_tree_model_get (model, iter,
+ DETAIL_DESCRIPTION_COLUMN, &description,
+ DETAIL_KEYENTRY_COLUMN, &item,
+ DETAIL_TYPE_COLUMN, &type,
+ -1);
+
+ if (type == SHORTCUT_TYPE_XKB_OPTION)
+ {
+ g_object_set (cell, "text", description, NULL);
+ }
+ else
+ {
+ if (item != NULL)
+ g_object_set (cell,
+ "editable", FALSE,
+ "text", item->description != NULL ?
+ item->description : _("<Unknown Action>"),
+ NULL);
+ else
+ g_object_set (cell,
+ "editable", FALSE, NULL);
+ }
+
+ g_free (description);
+}
+
+
+static void
+accel_set_func (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gpointer entry;
+ ShortcutType type;
+ gtk_tree_model_get (model, iter,
+ DETAIL_KEYENTRY_COLUMN, &entry,
+ DETAIL_TYPE_COLUMN, &type,
+ -1);
+
+ gtk_cell_renderer_set_visible (cell, FALSE);
+
+ if (type == SHORTCUT_TYPE_XKB_OPTION &&
+ GTK_IS_CELL_RENDERER_COMBO (cell))
+ {
+ CcKeyboardOption *option = entry;
+
+ gtk_cell_renderer_set_visible (cell, TRUE);
+ g_object_set (cell,
+ "model", cc_keyboard_option_get_store (option),
+ "text", cc_keyboard_option_get_current_value_description (option),
+ NULL);
+ }
+ else if (type == SHORTCUT_TYPE_KEY_ENTRY &&
+ GTK_IS_CELL_RENDERER_TEXT (cell) &&
+ !GTK_IS_CELL_RENDERER_COMBO (cell) &&
+ entry != NULL)
+ {
+ CcKeyboardItem *item = entry;
+
+ gtk_cell_renderer_set_visible (cell, TRUE);
+
+ if (item->editable)
+ g_object_set (cell,
+ "editable", TRUE,
+ "accel-key", item->keyval,
+ "accel-mods", item->mask,
+ "keycode", item->keycode,
+ "style", PANGO_STYLE_NORMAL,
+ NULL);
+ else
+ g_object_set (cell,
+ "editable", FALSE,
+ "accel-key", item->keyval,
+ "accel-mods", item->mask,
+ "keycode", item->keycode,
+ "style", PANGO_STYLE_ITALIC,
+ NULL);
+ }
+}
+
+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
+edit_custom_shortcut (CcKeyboardPanel *self,
+ CcKeyboardItem *item)
+{
+ gint result;
+ gboolean ret;
+ GSettings *settings;
+
+ settings = g_settings_new_with_path (item->schema, item->gsettings_path);
+
+ g_settings_bind (settings, "name",
+ G_OBJECT (self->custom_shortcut_name_entry), "text",
+ G_SETTINGS_BIND_DEFAULT);
+ gtk_widget_grab_focus (self->custom_shortcut_name_entry);
+
+ g_settings_bind (settings, "command",
+ G_OBJECT (self->custom_shortcut_command_entry), "text",
+ G_SETTINGS_BIND_DEFAULT);
+
+ g_settings_delay (settings);
+
+ gtk_widget_set_sensitive (self->custom_shortcut_name_entry, item->desc_editable);
+ gtk_widget_set_sensitive (self->custom_shortcut_command_entry, item->cmd_editable);
+ gtk_window_present (GTK_WINDOW (self->custom_shortcut_dialog));
+
+ result = gtk_dialog_run (GTK_DIALOG (self->custom_shortcut_dialog));
+ switch (result)
+ {
+ case GTK_RESPONSE_OK:
+ g_settings_apply (settings);
+ ret = TRUE;
+ break;
default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ g_settings_revert (settings);
+ ret = FALSE;
+ break;
}
+
+ g_settings_unbind (G_OBJECT (self->custom_shortcut_name_entry), "text");
+ g_settings_unbind (G_OBJECT (self->custom_shortcut_command_entry), "text");
+
+ gtk_widget_hide (self->custom_shortcut_dialog);
+
+ g_object_unref (settings);
+
+ return ret;
}
-static GObject *
-cc_keyboard_panel_constructor (GType gtype,
- guint n_properties,
- GObjectConstructParam *properties)
+static gboolean
+remove_custom_shortcut (CcKeyboardPanel *self,
+ GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ CcKeyboardItem *item;
+ GPtrArray *keys_array;
+ GVariantBuilder builder;
+ char **settings_paths;
+ int i;
+
+ gtk_tree_model_get (model, iter,
+ DETAIL_KEYENTRY_COLUMN, &item,
+ -1);
+
+ /* not a custom shortcut */
+ 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);
+ 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 (CcKeyboardPanel *self,
+ GtkTreeView *tree_view,
+ GtkTreeModel *model)
+{
+ CcKeyboardItem *item;
+ GtkTreePath *path;
+ gchar *settings_path;
+
+ 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 = model;
+
+ if (edit_custom_shortcut (self, item) && item->command && item->command[0])
+ {
+ 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 (GTK_LIST_STORE (model), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (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 (model, &iter);
+ gtk_tree_view_expand_to_path (tree_view, path);
+ gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0, 0);
+ gtk_tree_path_free (path);
+ }
+ else
+ {
+ g_object_unref (item);
+ }
+}
+
+static void
+update_custom_shortcut (CcKeyboardPanel *self,
+ GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ CcKeyboardItem *item;
+
+ gtk_tree_model_get (model, iter,
+ DETAIL_KEYENTRY_COLUMN, &item,
+ -1);
+
+ g_assert (item->type == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH);
+
+ edit_custom_shortcut (self, item);
+
+ if (item->command == NULL || item->command[0] == '\0')
+ {
+ remove_custom_shortcut (self, model, iter);
+ }
+ else
+ {
+ gtk_list_store_set (GTK_LIST_STORE (model), iter,
+ DETAIL_KEYENTRY_COLUMN, item, -1);
+ }
+}
+
+static gboolean
+start_editing_cb (GtkTreeView *treeview,
+ GdkEventButton *event,
+ gpointer user_data)
+{
+ CcKeyboardPanel *self;
+ GtkTreePath *path;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *cell = user_data;
+
+ if (event->window != gtk_tree_view_get_bin_window (treeview))
+ return FALSE;
+
+ self = CC_KEYBOARD_PANEL (gtk_widget_get_ancestor (GTK_WIDGET (treeview), CC_TYPE_KEYBOARD_PANEL));
+
+ if (gtk_tree_view_get_path_at_pos (treeview,
+ (gint) event->x,
+ (gint) event->y,
+ &path,
+ &column,
+ NULL,
+ NULL))
+ {
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ CcKeyboardItem *item;
+ ShortcutType type;
+
+ model = gtk_tree_view_get_model (treeview);
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ DETAIL_KEYENTRY_COLUMN, &item,
+ DETAIL_TYPE_COLUMN, &type,
+ -1);
+
+ if (type == SHORTCUT_TYPE_XKB_OPTION)
+ {
+ gtk_tree_path_free (path);
+ return FALSE;
+ }
+
+ /* if only the accel can be edited on the selected row
+ * always select the accel column */
+ if (item->desc_editable &&
+ column == gtk_tree_view_get_column (treeview, 0))
+ {
+ gtk_widget_grab_focus (GTK_WIDGET (treeview));
+ gtk_tree_view_set_cursor (treeview,
+ path,
+ column,
+ FALSE);
+
+ update_custom_shortcut (self, model, &iter);
+ }
+ else
+ {
+ gtk_widget_grab_focus (GTK_WIDGET (treeview));
+ gtk_tree_view_set_cursor_on_cell (treeview,
+ path,
+ gtk_tree_view_get_column (treeview, 1),
+ cell,
+ TRUE);
+ }
+
+ g_signal_stop_emission_by_name (treeview, "button_press_event");
+ gtk_tree_path_free (path);
+ }
+
+ return TRUE;
+}
+
+static void
+start_editing_kb_cb (GtkTreeView *treeview,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ gpointer user_data)
{
- GObject *obj;
CcKeyboardPanel *self;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ CcKeyboardItem *item;
+ ShortcutType type;
+ GtkCellRenderer *cell = user_data;
+
+ model = gtk_tree_view_get_model (treeview);
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ DETAIL_KEYENTRY_COLUMN, &item,
+ DETAIL_TYPE_COLUMN, &type,
+ -1);
+
+ if (type == SHORTCUT_TYPE_XKB_OPTION)
+ return;
+
+ self = CC_KEYBOARD_PANEL (gtk_widget_get_ancestor (GTK_WIDGET (treeview), CC_TYPE_KEYBOARD_PANEL));
+
+
+ /* if only the accel can be edited on the selected row
+ * always select the accel column */
+ if (item->desc_editable &&
+ column == gtk_tree_view_get_column (treeview, 0))
+ {
+ gtk_widget_grab_focus (GTK_WIDGET (treeview));
+ gtk_tree_view_set_cursor (treeview,
+ path,
+ column,
+ FALSE);
+ update_custom_shortcut (self, model, &iter);
+ }
+ else
+ {
+ gtk_widget_grab_focus (GTK_WIDGET (treeview));
+ gtk_tree_view_set_cursor_on_cell (treeview,
+ path,
+ gtk_tree_view_get_column (treeview, 1),
+ cell,
+ TRUE);
+ }
+}
+
+static gboolean
+compare_keys_for_uniqueness (CcKeyboardItem *element,
+ CcUniquenessData *data)
+{
+ CcKeyboardItem *orig_item;
+
+ orig_item = data->orig_item;
+
+ /* no conflict for : blanks, different modifiers, or ourselves */
+ if (element == NULL ||
+ data->new_mask != element->mask ||
+ cc_keyboard_item_equal (orig_item, element))
+ {
+ return FALSE;
+ }
+
+ if (data->new_keyval != 0)
+ {
+ if (data->new_keyval != element->keyval)
+ return FALSE;
+ }
+ else if (element->keyval != 0 || data->new_keycode != element->keycode)
+ {
+ return FALSE;
+ }
+
+ data->conflict_item = element;
+
+ return TRUE;
+
+}
+
+static gboolean
+cb_check_for_uniqueness (gpointer key,
+ GPtrArray *keys_array,
+ CcUniquenessData *data)
+{
+ guint i;
+
+ for (i = 0; i < keys_array->len; i++)
+ {
+ CcKeyboardItem *item;
+
+ item = keys_array->pdata[i];
+ if (compare_keys_for_uniqueness (item, data))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+static CcKeyboardItem *
+search_for_conflict_item (CcKeyboardPanel *self,
+ CcKeyboardItem *item,
+ guint keyval,
+ GdkModifierType mask,
+ guint keycode)
+{
+ CcUniquenessData data;
+
+ data.orig_item = item;
+ data.new_keyval = keyval;
+ data.new_mask = mask;
+ data.new_keycode = keycode;
+ data.conflict_item = NULL;
+
+ if (keyval != 0 || keycode != 0) /* any number of shortcuts can be disabled */
+ {
+ BindingGroupType i;
+
+ for (i = BINDING_GROUP_SYSTEM; i <= BINDING_GROUP_USER && data.conflict_item == NULL; i++)
+ {
+ GHashTable *table;
+
+ table = get_hash_for_group (self, i);
+ if (!table)
+ continue;
+ g_hash_table_find (table, (GHRFunc) cb_check_for_uniqueness, &data);
+ }
+ }
+
+ return data.conflict_item;
+}
+
+static GtkResponseType
+show_invalid_binding_dialog (CcKeyboardPanel *self,
+ guint keyval,
+ GdkModifierType mask,
+ guint keycode)
+{
+ GtkWidget *dialog;
+ char *name;
+
+ name = binding_name (keyval, keycode, mask, TRUE);
+ dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))),
+ GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_CANCEL,
+ _("The shortcut “%s” cannot be used because it will become impossible to
type using this key.\n"
+ "Please try with a key such as Control, Alt or Shift at the same time."),
+ name);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+
+ g_free (name);
+
+ return GTK_RESPONSE_NONE;
+}
+
+static GtkResponseType
+show_conflict_item_dialog (CcKeyboardPanel *self,
+ CcKeyboardItem *item,
+ CcKeyboardItem *conflict_item,
+ guint keyval,
+ GdkModifierType mask,
+ guint keycode)
+{
+ GtkWidget *dialog;
+ char *name;
+ int response;
+
+ name = binding_name (keyval, keycode, mask, TRUE);
+ dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))),
+ GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_CANCEL,
+ _("The shortcut “%s” is already used for\n“%s”"),
+ name,
+ conflict_item->description);
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ _("If you reassign the shortcut to “%s”, the “%s” shortcut "
+ "will be disabled."),
+ item->description,
+ conflict_item->description);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
+ gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Reassign"), GTK_RESPONSE_ACCEPT);
+
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ gtk_widget_destroy (dialog);
+
+ g_free (name);
+
+ return response;
+}
+
+
+static GtkResponseType
+show_reverse_item_dialog (CcKeyboardPanel *self,
+ CcKeyboardItem *item,
+ CcKeyboardItem *reverse_item,
+ CcKeyboardItem *reverse_conflict_item,
+ guint keyval,
+ GdkModifierType mask,
+ guint keycode)
+{
+ GtkWidget *dialog;
+ char *name;
+ int response;
+
+ name = binding_name (keyval, keycode, mask, TRUE);
+
+ /* translators:
+ * This is the text you get in a dialogue when an action has an associated
+ * "reverse" action, for example Alt+Tab going in the opposite direction to
+ * Alt+Shift+Tab.
+ *
+ * An example text would be:
+ * The "Switch to next input source" shortcut has an associated "Switch to
+ * previous input source" shortcut. Do you want to automatically set it to
+ * "Shift+Ctrl+Alt+Space"? */
+ dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))),
+ GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_CANCEL,
+ _("The “%s” shortcut has an associated “%s” shortcut. "
+ "Do you want to automatically set it to “%s”?"),
+ item->description,
+ reverse_item->description,
+ name);
+
+ if (reverse_conflict_item != NULL)
+ {
+ /* translators:
+ * This is the text you get in a dialogue when you try to use a shortcut
+ * that was already associated with another action, for example:
+ * "Alt+F4" is currently associated with "Close Window", ... */
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ _("“%s” is currently associated with “%s”, this shortcut
will be"
+ " disabled if you move forward."),
+ name,
+ reverse_conflict_item->description);
+ }
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
+ gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Assign"), GTK_RESPONSE_ACCEPT);
+
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+
+ g_free (name);
+
+ return response;
+}
+
+static void
+handle_reverse_item (CcKeyboardItem *item,
+ CcKeyboardItem *reverse_item,
+ guint keyval,
+ GdkModifierType mask,
+ guint keycode,
+ CcKeyboardPanel *self)
+{
+ GtkResponseType response;
+ GdkModifierType reverse_mask;
+
+ reverse_mask = mask ^ GDK_SHIFT_MASK;
+
+ if (!is_valid_binding (keyval, reverse_mask, keycode))
+ return;
+
+ if (reverse_item->keyval != keyval ||
+ reverse_item->keycode != keycode ||
+ reverse_item->mask != reverse_mask)
+ {
+ CcKeyboardItem *reverse_conflict_item;
+ char *binding_str;
+
+ reverse_conflict_item = search_for_conflict_item (self,
+ reverse_item,
+ keyval,
+ reverse_mask,
+ keycode);
+
+ response = show_reverse_item_dialog (self,
+ item,
+ reverse_item,
+ reverse_conflict_item,
+ keyval, reverse_mask,
+ keycode);
+ if (response == GTK_RESPONSE_ACCEPT)
+ {
+ binding_str = binding_name (keyval, keycode, reverse_mask, FALSE);
+
+ g_object_set (G_OBJECT (reverse_item), "binding", binding_str, NULL);
+ g_free (binding_str);
+
+ if (reverse_conflict_item != NULL)
+ g_object_set (G_OBJECT (reverse_conflict_item), "binding", NULL, NULL);
+ }
+ else
+ {
+ /* The existing reverse binding may be conflicting with the binding
+ * we are setting. Other conflicts have already been handled in
+ * accel_edited_callback()
+ */
+ CcKeyboardItem *conflict_item;
+
+ conflict_item = search_for_conflict_item (self, item, keyval, mask, keycode);
+
+ if (conflict_item != NULL)
+ {
+ g_warn_if_fail (conflict_item == reverse_item);
+ g_object_set (G_OBJECT (conflict_item), "binding", NULL, NULL);
+ }
+ }
+ }
+}
+
+static void
+accel_edited_callback (GtkCellRendererText *cell,
+ const char *path_string,
+ guint keyval,
+ GdkModifierType mask,
+ guint keycode,
+ CcKeyboardPanel *self)
+{
+ GtkTreeModel *model;
+ GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
+ GtkTreeIter iter;
+ CcKeyboardItem *item;
+ CcKeyboardItem *conflict_item;
+ CcKeyboardItem *reverse_item;
+ char *str;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->shortcut_treeview));
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_path_free (path);
+ gtk_tree_model_get (model, &iter,
+ DETAIL_KEYENTRY_COLUMN, &item,
+ -1);
+
+ /* sanity check */
+ if (item == NULL)
+ return;
+
+ /* CapsLock isn't supported as a keybinding modifier, so keep it from confusing us */
+ mask &= ~GDK_LOCK_MASK;
+
+ conflict_item = search_for_conflict_item (self, item, keyval, mask, keycode);
+
+ /* Check for unmodified keys */
+ if (!is_valid_binding (keyval, mask, keycode))
+ {
+ show_invalid_binding_dialog (self, keyval, mask, keycode);
+
+ /* set it back to its previous value. */
+ g_object_set (G_OBJECT (cell),
+ "accel-key", item->keyval,
+ "keycode", item->keycode,
+ "accel-mods", item->mask,
+ NULL);
+ return;
+ }
+
+ reverse_item = cc_keyboard_item_get_reverse_item (item);
+
+ /* flag to see if the new accelerator was in use by something */
+ if ((conflict_item != NULL) && (conflict_item != reverse_item))
+ {
+ GtkResponseType response;
+
+ response = show_conflict_item_dialog (self,
+ item,
+ conflict_item,
+ keyval,
+ mask,
+ keycode);
+
+ if (response == GTK_RESPONSE_ACCEPT)
+ {
+ g_object_set (G_OBJECT (conflict_item), "binding", NULL, NULL);
+
+ str = binding_name (keyval, keycode, mask, FALSE);
+ g_object_set (G_OBJECT (item), "binding", str, NULL);
+
+ g_free (str);
+ if (reverse_item == NULL)
+ return;
+ }
+ else
+ {
+ /* set it back to its previous value. */
+ g_object_set (G_OBJECT (cell),
+ "accel-key", item->keyval,
+ "keycode", item->keycode,
+ "accel-mods", item->mask,
+ NULL);
+ return;
+ }
+
+ }
+
+ str = binding_name (keyval, keycode, mask, FALSE);
+ g_object_set (G_OBJECT (item), "binding", str, NULL);
+
+ if (reverse_item != NULL)
+ handle_reverse_item (item, reverse_item, keyval, mask, keycode, self);
+
+ g_free (str);
+}
+
+static void
+accel_cleared_callback (GtkCellRendererText *cell,
+ const char *path_string,
+ gpointer data)
+{
+ GtkTreeView *view = (GtkTreeView *) data;
+ GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
+ CcKeyboardItem *item;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ model = gtk_tree_view_get_model (view);
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_path_free (path);
+ gtk_tree_model_get (model, &iter,
+ DETAIL_KEYENTRY_COLUMN, &item,
+ -1);
+
+ /* sanity check */
+ if (item == NULL)
+ return;
+
+ /* Unset the key */
+ g_object_set (G_OBJECT (item), "binding", NULL, NULL);
+}
+
+static void
+shortcut_entry_changed (GtkEntry *entry,
+ CcKeyboardPanel *self)
+{
+ guint16 name_length;
+ guint16 command_length;
+
+ name_length = gtk_entry_get_text_length (GTK_ENTRY (self->custom_shortcut_name_entry));
+ command_length = gtk_entry_get_text_length (GTK_ENTRY (self->custom_shortcut_command_entry));
+
+ gtk_widget_set_sensitive (self->custom_shortcut_ok_button, name_length > 0 && command_length > 0);
+}
+
+static void
+add_button_clicked (GtkWidget *button,
+ CcKeyboardPanel *self)
+{
+ GtkTreeView *treeview;
+ GtkTreeModel *model;
+ GtkTreeModel *section_model;
+ GtkTreeIter iter;
+ gboolean found, cont;
+
+ treeview = GTK_TREE_VIEW (self->shortcut_treeview);
+ model = gtk_tree_view_get_model (treeview);
+
+ /* Select the Custom Shortcuts section
+ * before adding the shortcut itself */
+ section_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->section_treeview));
+ cont = gtk_tree_model_get_iter_first (section_model, &iter);
+ found = FALSE;
+
+ while (cont)
+ {
+ BindingGroupType group;
+
+ gtk_tree_model_get (section_model, &iter,
+ SECTION_GROUP_COLUMN, &group,
+ -1);
+
+ if (group == BINDING_GROUP_USER)
+ {
+ found = TRUE;
+ break;
+ }
+ cont = gtk_tree_model_iter_next (section_model, &iter);
+ }
+ if (found)
+ {
+ GtkTreeSelection *selection;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->section_treeview));
+ gtk_tree_selection_select_iter (selection, &iter);
+ }
+
+ /* And add the shortcut */
+ add_custom_shortcut (self, treeview, model);
+}
+
+static void
+remove_button_clicked (GtkWidget *button,
+ CcKeyboardPanel *self)
+{
+ GtkTreeView *treeview;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+
+ treeview = GTK_TREE_VIEW (self->shortcut_treeview);
+ model = gtk_tree_view_get_model (treeview);
+ selection = gtk_tree_view_get_selection (treeview);
+
+ if (gtk_tree_selection_get_selected (selection, NULL, &iter))
+ {
+ remove_custom_shortcut (self, model, &iter);
+ }
+}
+
+static void
+xkb_options_combo_changed (GtkCellRendererCombo *combo,
+ gchar *model_path,
+ GtkTreeIter *model_iter,
+ CcKeyboardPanel *self)
+{
+ GtkTreeView *shortcut_treeview;
+ GtkTreeModel *shortcut_model;
+ GtkTreeIter shortcut_iter;
+ GtkTreeSelection *selection;
+ CcKeyboardOption *option;
+ ShortcutType type;
+
+ shortcut_treeview = GTK_TREE_VIEW (self->shortcut_treeview);
+ selection = gtk_tree_view_get_selection (shortcut_treeview);
+
+ if (!gtk_tree_selection_get_selected (selection, &shortcut_model, &shortcut_iter))
+ return;
+
+ gtk_tree_model_get (shortcut_model, &shortcut_iter,
+ DETAIL_KEYENTRY_COLUMN, &option,
+ DETAIL_TYPE_COLUMN, &type,
+ -1);
+
+ if (type != SHORTCUT_TYPE_XKB_OPTION)
+ return;
+
+ cc_keyboard_option_set_selection (option, model_iter);
+}
+
+static void
+setup_tree_views (CcKeyboardPanel *self)
+{
+ GtkTreeViewColumn *column;
+ GtkTreeModelSort *sort_model;
+ GtkCellRenderer *renderer;
+ GtkListStore *model;
GtkWidget *widget;
+ CcShell *shell;
+ GList *focus_chain;
+
+ /* Setup the section treeview */
+ gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (self->section_treeview),
+ sections_separator_func,
+ self,
+ NULL);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Section"),
+ renderer,
+ "text", SECTION_DESCRIPTION_COLUMN,
+ NULL);
+ g_object_set (renderer,
+ "width-chars", 20,
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ NULL);
+
+ gtk_tree_view_append_column (GTK_TREE_VIEW (self->section_treeview), column);
+
+ model = gtk_list_store_new (SECTION_N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
+ sort_model = GTK_TREE_MODEL_SORT (gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (model)));
+ gtk_tree_view_set_model (GTK_TREE_VIEW (self->section_treeview), GTK_TREE_MODEL (sort_model));
+ g_object_unref (model);
+
+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sort_model),
+ SECTION_DESCRIPTION_COLUMN,
+ section_sort_item,
+ self,
+ NULL);
+
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model),
+ SECTION_DESCRIPTION_COLUMN,
+ GTK_SORT_ASCENDING);
+ g_object_unref (sort_model);
+
+ section_selection_changed (gtk_tree_view_get_selection (GTK_TREE_VIEW (self->section_treeview)),
+ self);
+
+ /* Setup the shortcut treeview */
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+
+ column = gtk_tree_view_column_new_with_attributes (NULL, renderer, NULL);
+ gtk_tree_view_column_set_cell_data_func (column, renderer, description_set_func, NULL, NULL);
+ gtk_tree_view_column_set_resizable (column, FALSE);
+ gtk_tree_view_column_set_expand (column, TRUE);
+
+ gtk_tree_view_append_column (GTK_TREE_VIEW (self->shortcut_treeview), column);
+
+ renderer = (GtkCellRenderer *) g_object_new (GTK_TYPE_CELL_RENDERER_ACCEL,
+ "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
+ NULL);
+
+ g_signal_connect (self->shortcut_treeview,
+ "button_press_event",
+ G_CALLBACK (start_editing_cb),
+ renderer);
+ g_signal_connect (self->shortcut_treeview,
+ "row-activated",
+ G_CALLBACK (start_editing_kb_cb),
+ renderer);
+
+ g_signal_connect (renderer,
+ "accel_edited",
+ G_CALLBACK (accel_edited_callback),
+ self);
+ g_signal_connect (renderer,
+ "accel_cleared",
+ G_CALLBACK (accel_cleared_callback),
+ self->shortcut_treeview);
+
+ column = gtk_tree_view_column_new_with_attributes (NULL, renderer, NULL);
+ gtk_tree_view_column_set_cell_data_func (column, renderer, accel_set_func, NULL, NULL);
+ gtk_tree_view_column_set_resizable (column, FALSE);
+ gtk_tree_view_column_set_expand (column, FALSE);
+
+ renderer = (GtkCellRenderer *) g_object_new (GTK_TYPE_CELL_RENDERER_COMBO,
+ "has-entry", FALSE,
+ "text-column", XKB_OPTION_DESCRIPTION_COLUMN,
+ "editable", TRUE,
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ "width-chars", 25,
+ NULL);
+ g_signal_connect (renderer,
+ "changed",
+ G_CALLBACK (xkb_options_combo_changed),
+ self);
- obj = G_OBJECT_CLASS (cc_keyboard_panel_parent_class)->constructor (gtype, n_properties, properties);
+ gtk_tree_view_column_pack_end (column, renderer, FALSE);
- self = CC_KEYBOARD_PANEL (obj);
+ gtk_tree_view_column_set_cell_data_func (column, renderer, accel_set_func, NULL, NULL);
+
+ gtk_tree_view_append_column (GTK_TREE_VIEW (self->shortcut_treeview), column);
+
+ model = gtk_list_store_new (DETAIL_N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (self->shortcut_treeview), GTK_TREE_MODEL (model));
+ g_object_unref (model);
+
+ setup_keyboard_options (model);
+
+ /* set up the focus chain */
+ focus_chain = g_list_append (NULL, self->section_treeview);
+ focus_chain = g_list_append (focus_chain, self->shortcut_treeview);
+ focus_chain = g_list_append (focus_chain, self->shortcut_toolbar);
+
+ gtk_container_set_focus_chain (GTK_CONTAINER (self), focus_chain);
+ g_list_free (focus_chain);
+
+ /* set up the dialog */
+ shell = cc_panel_get_shell (CC_PANEL (self));
+ widget = cc_shell_get_toplevel (shell);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (self->custom_shortcut_dialog), GTK_RESPONSE_OK);
+ gtk_window_set_transient_for (GTK_WINDOW (self->custom_shortcut_dialog), GTK_WINDOW (widget));
+}
+
+static gboolean
+cc_keyboard_panel_set_section (CcKeyboardPanel *self,
+ const char *section)
+{
+ GtkTreeModel *section_model;
+ GtkTreeIter iter;
+ gboolean found, cont;
+
+ section_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->section_treeview));
+ cont = gtk_tree_model_get_iter_first (section_model, &iter);
+ found = FALSE;
+ while (cont)
+ {
+ char *id;
+
+ gtk_tree_model_get (section_model, &iter,
+ SECTION_ID_COLUMN, &id,
+ -1);
+
+ if (g_strcmp0 (id, section) == 0)
+ {
+ found = TRUE;
+ g_free (id);
+ break;
+ }
+ g_free (id);
+ cont = gtk_tree_model_iter_next (section_model, &iter);
+ }
+ if (found)
+ {
+ GtkTreeSelection *selection;
- keyboard_shortcuts_init (CC_PANEL (self), self->builder);
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->section_treeview));
+ gtk_tree_selection_select_iter (selection, &iter);
+ }
+ else
+ {
+ g_warning ("Could not find section '%s' to switch to.", section);
+ }
- widget = (GtkWidget *) gtk_builder_get_object (self->builder, "shortcuts_page");
+ return found;
+}
- gtk_container_add (GTK_CONTAINER (self), widget);
+static void
+cc_keyboard_panel_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id)
+ {
+ case PROP_PARAMETERS:
+ break;
- return obj;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
}
static const char *
@@ -141,56 +1783,88 @@ cc_keyboard_panel_get_help_uri (CcPanel *panel)
}
static void
-cc_keyboard_panel_dispose (GObject *object)
+cc_keyboard_panel_finalize (GObject *object)
{
- keyboard_shortcuts_dispose (CC_PANEL (object));
+ CcKeyboardPanel *self = CC_KEYBOARD_PANEL (object);
- G_OBJECT_CLASS (cc_keyboard_panel_parent_class)->dispose (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->custom_shortcut_dialog);
+ g_clear_object (&self->binding_settings);
+
+ cc_keyboard_option_clear_all ();
+
+ G_OBJECT_CLASS (cc_keyboard_panel_parent_class)->finalize (object);
}
static void
-cc_keyboard_panel_finalize (GObject *object)
+on_window_manager_change (const char *wm_name,
+ CcKeyboardPanel *self)
{
- CcKeyboardPanel *panel = CC_KEYBOARD_PANEL (object);
+ reload_sections (self);
+}
- if (panel->builder)
- g_object_unref (panel->builder);
+static void
+cc_keyboard_panel_constructed (GObject *object)
+{
+ CcKeyboardPanel *self = CC_KEYBOARD_PANEL (object);
- G_OBJECT_CLASS (cc_keyboard_panel_parent_class)->finalize (object);
+ G_OBJECT_CLASS (cc_keyboard_panel_parent_class)->constructed (object);
+
+#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);
}
static void
cc_keyboard_panel_class_init (CcKeyboardPanelClass *klass)
{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
panel_class->get_help_uri = cc_keyboard_panel_get_help_uri;
- object_class->constructor = cc_keyboard_panel_constructor;
object_class->set_property = cc_keyboard_panel_set_property;
- object_class->dispose = cc_keyboard_panel_dispose;
object_class->finalize = cc_keyboard_panel_finalize;
+ object_class->constructed = cc_keyboard_panel_constructed;
g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters");
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/control-center/keyboard/gnome-keyboard-panel.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, add_toolbutton);
+ gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, custom_shortcut_command_entry);
+ gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, custom_shortcut_dialog);
+ gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, custom_shortcut_name_entry);
+ gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, custom_shortcut_ok_button);
+ gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, remove_toolbutton);
+ gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, section_treeview);
+ gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, shortcut_toolbar);
+ gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, shortcut_treeview);
+
+ gtk_widget_class_bind_template_callback (widget_class, add_button_clicked);
+ gtk_widget_class_bind_template_callback (widget_class, remove_button_clicked);
+ gtk_widget_class_bind_template_callback (widget_class, section_selection_changed);
+ gtk_widget_class_bind_template_callback (widget_class, shortcut_entry_changed);
+ gtk_widget_class_bind_template_callback (widget_class, shortcut_selection_changed);
}
static void
cc_keyboard_panel_init (CcKeyboardPanel *self)
{
- GError *error = NULL;
-
g_resources_register (cc_keyboard_get_resource ());
- self->builder = gtk_builder_new ();
+ gtk_widget_init_template (GTK_WIDGET (self));
- if (gtk_builder_add_from_resource (self->builder,
- "/org/gnome/control-center/keyboard/gnome-keyboard-panel.ui",
- &error) == 0)
- {
- g_warning ("Could not load UI: %s", error->message);
- g_clear_error (&error);
- g_object_unref (self->builder);
- self->builder = NULL;
- }
+ self->binding_settings = g_settings_new (BINDINGS_SCHEMA);
}
diff --git a/panels/keyboard/cc-keyboard-panel.h b/panels/keyboard/cc-keyboard-panel.h
index 2276c3f..859a807 100644
--- a/panels/keyboard/cc-keyboard-panel.h
+++ b/panels/keyboard/cc-keyboard-panel.h
@@ -23,6 +23,7 @@
#define _CC_KEYBOARD_PANEL_H
#include <shell/cc-panel.h>
+#include <gtk/gtk.h>
G_BEGIN_DECLS
diff --git a/panels/keyboard/gnome-keyboard-panel.ui b/panels/keyboard/gnome-keyboard-panel.ui
index dd9e50a..cd453db 100644
--- a/panels/keyboard/gnome-keyboard-panel.ui
+++ b/panels/keyboard/gnome-keyboard-panel.ui
@@ -8,10 +8,11 @@
<property name="step_increment">200</property>
<property name="page_increment">200</property>
</object>
- <object class="GtkDialog" id="custom-shortcut-dialog">
+ <object class="GtkDialog" id="custom_shortcut_dialog">
<property name="can_focus">False</property>
<property name="type_hint">dialog</property>
<property name="use_header_bar">1</property>
+ <property name="resizable">False</property>
<child internal-child="headerbar">
<object class="GtkHeaderBar" id="dialog-header-bar">
<property name="visible">True</property>
@@ -19,7 +20,7 @@
<property name="title" translatable="yes">Custom Shortcut</property>
<property name="show_close_button">False</property>
<child>
- <object class="GtkButton" id="custom-shortcut-cancel-button">
+ <object class="GtkButton" id="custom_shortcut_cancel_button">
<property name="label" translatable="yes">_Cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
@@ -36,7 +37,7 @@
</packing>
</child>
<child>
- <object class="GtkButton" id="custom-shortcut-ok-button">
+ <object class="GtkButton" id="custom_shortcut_ok_button">
<property name="label" translatable="yes">_Add</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
@@ -81,7 +82,7 @@
<property name="xalign">0</property>
<property name="label" translatable="yes">_Name:</property>
<property name="use_underline">True</property>
- <property name="mnemonic_widget">custom-shortcut-name-entry</property>
+ <property name="mnemonic_widget">custom_shortcut_name_entry</property>
</object>
<packing>
<property name="left_attach">0</property>
@@ -95,7 +96,7 @@
<property name="xalign">0</property>
<property name="label" translatable="yes">C_ommand:</property>
<property name="use_underline">True</property>
- <property name="mnemonic_widget">custom-shortcut-command-entry</property>
+ <property name="mnemonic_widget">custom_shortcut_command_entry</property>
</object>
<packing>
<property name="left_attach">0</property>
@@ -103,12 +104,13 @@
</packing>
</child>
<child>
- <object class="GtkEntry" id="custom-shortcut-name-entry">
+ <object class="GtkEntry" id="custom_shortcut_name_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="invisible_char">•</property>
<property name="activates_default">True</property>
+ <signal name="changed" handler="shortcut_entry_changed" object="CcKeyboardPanel"
swapped="no" />
</object>
<packing>
<property name="left_attach">1</property>
@@ -116,12 +118,13 @@
</packing>
</child>
<child>
- <object class="GtkEntry" id="custom-shortcut-command-entry">
+ <object class="GtkEntry" id="custom_shortcut_command_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="invisible_char">•</property>
<property name="activates_default">True</property>
+ <signal name="changed" handler="shortcut_entry_changed" object="CcKeyboardPanel"
swapped="no" />
</object>
<packing>
<property name="left_attach">1</property>
@@ -145,161 +148,174 @@
</object>
</child>
<action-widgets>
- <action-widget response="-6">custom-shortcut-cancel-button</action-widget>
- <action-widget response="-5">custom-shortcut-ok-button</action-widget>
+ <action-widget response="-6">custom_shortcut_cancel_button</action-widget>
+ <action-widget response="-5">custom_shortcut_ok_button</action-widget>
</action-widgets>
</object>
- <object class="GtkBox" id="shortcuts_page">
+ <template class="CcKeyboardPanel" parent="CcPanel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">12</property>
+ <child>
+ <object class="GtkBox" id="shortcuts_page">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="border_width">5</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkBox" id="shortcuts_vbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkGrid" id="shortcuts_grid">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <property name="border_width">5</property>
- <property name="spacing">12</property>
+ <property name="column_spacing">5</property>
<child>
- <object class="GtkBox" id="shortcuts_vbox">
+ <object class="GtkScrolledWindow" id="sections_swindow">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
<child>
- <object class="GtkGrid" id="shortcuts_grid">
+ <object class="GtkTreeView" id="section_treeview">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="column_spacing">5</property>
- <child>
- <object class="GtkScrolledWindow" id="sections_swindow">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">never</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkTreeView" id="section_treeview">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="headers_visible">False</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="treeview-selection1"/>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- <property name="height">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkScrolledWindow" id="actions_swindow">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hexpand">True</property>
- <property name="vexpand">True</property>
- <property name="hscrollbar_policy">never</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkTreeView" id="shortcut_treeview">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="headers_visible">False</property>
- <property name="rules_hint">True</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="treeview-selection2"/>
- </child>
- </object>
- </child>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection1">
+ <property name="mode">browse</property>
+ <signal name="changed" handler="section_selection_changed"
object="CcKeyboardPanel" swapped="no" />
</object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">0</property>
- </packing>
</child>
- <child>
- <object class="GtkToolbar" id="shortcut-toolbar">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_size">1</property>
- <property name="toolbar-style">icons</property>
- <style>
- <class name="inline-toolbar"/>
- </style>
- <child>
- <object class="GtkToolButton" id="add-toolbutton">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
- <property name="use_underline">True</property>
- <property name="icon_name">list-add-symbolic</property>
- <property name="label" translatable="yes">Add Shortcut</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolButton" id="remove-toolbutton">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
- <property name="use_underline">True</property>
- <property name="icon_name">list-remove-symbolic</property>
- <property name="label" translatable="yes">Remove Shortcut</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="shortcut_treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">False</property>
+ <property name="rules_hint">True</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection2">
+ <signal name="changed" handler="shortcut_selection_changed"
object="remove_toolbutton" swapped="no" />
</object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">1</property>
- </packing>
</child>
</object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="shortcut_toolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_size">1</property>
+ <property name="toolbar-style">icons</property>
+ <style>
+ <class name="inline-toolbar"/>
+ </style>
+ <child>
+ <object class="GtkToolButton" id="add_toolbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="icon_name">list-add-symbolic</property>
+ <property name="label" translatable="yes">Add Shortcut</property>
+ <signal name="clicked" handler="add_button_clicked" object="CcKeyboardPanel"
swapped="no" />
+ </object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
</packing>
</child>
<child>
- <object class="GtkBox">
+ <object class="GtkToolButton" id="remove_toolbutton">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="border_width">5</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkLabel" id="label12">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">To edit a shortcut, click the row and
hold down the new keys or press Backspace to clear.</property>
- <property name="justify">fill</property>
- <property name="wrap">True</property>
- <property name="max_width_chars">70</property>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="icon_name">list-remove-symbolic</property>
+ <property name="label" translatable="yes">Remove Shortcut</property>
+ <signal name="clicked" handler="remove_button_clicked" object="CcKeyboardPanel"
swapped="no" />
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
+ <property name="homogeneous">True</property>
</packing>
</child>
</object>
<packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">To edit a shortcut, click the row and hold
down the new keys or press Backspace to clear.</property>
+ <property name="justify">fill</property>
+ <property name="wrap">True</property>
+ <property name="max_width_chars">70</property>
+ </object>
+ <packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
-
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
</interface>
diff --git a/panels/keyboard/keyboard-shortcuts.c b/panels/keyboard/keyboard-shortcuts.c
index 3a6ffd0..5b59eba 100644
--- a/panels/keyboard/keyboard-shortcuts.c
+++ b/panels/keyboard/keyboard-shortcuts.c
@@ -28,238 +28,12 @@
#include "cc-keyboard-option.h"
#include "wm-common.h"
-#ifdef GDK_WINDOWING_X11
-#include <gdk/gdkx.h>
-#endif
-
-#define BINDINGS_SCHEMA "org.gnome.settings-daemon.plugins.media-keys"
-#define CUSTOM_KEYS_BASENAME "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings"
-#define CUSTOM_SHORTCUTS_ID "custom"
-#define WID(builder, name) (GTK_WIDGET (gtk_builder_get_object (builder, name)))
-
-static GRegex *pictures_regex = NULL;
-static GSettings *binding_settings = NULL;
-static GtkWidget *custom_shortcut_dialog = NULL;
-static GtkWidget *custom_shortcut_name_entry = NULL;
-static GtkWidget *custom_shortcut_command_entry = NULL;
-static GtkWidget *custom_shortcut_ok_button = NULL;
-static GHashTable *kb_system_sections = NULL;
-static GHashTable *kb_apps_sections = NULL;
-static GHashTable *kb_user_sections = NULL;
-static gpointer wm_changed_id = NULL;
-
-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 (BindingGroupType group)
-{
- GHashTable *hash;
-
- switch (group)
- {
- case BINDING_GROUP_SYSTEM:
- hash = kb_system_sections;
- break;
- case BINDING_GROUP_APPS:
- hash = kb_apps_sections;
- break;
- case BINDING_GROUP_USER:
- hash = kb_user_sections;
- break;
- default:
- hash = NULL;
- }
- return hash;
-}
-
-static gboolean
-have_key_for_group (int group, const gchar *name)
-{
- GHashTableIter iter;
- GPtrArray *keys;
- gint i;
-
- g_hash_table_iter_init (&iter, get_hash_for_group (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 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 (GtkBuilder *builder,
- const gchar *title,
- const gchar *id,
- BindingGroupType group,
- const KeyListEntry *keys_list)
-{
- GPtrArray *keys_array;
- GtkTreeModel *sort_model;
- GtkTreeModel *model, *shortcut_model;
- GtkTreeIter iter;
- gint i;
- GHashTable *hash;
- gboolean is_new;
-
- hash = get_hash_for_group (group);
- if (!hash)
- return;
-
- sort_model = gtk_tree_view_get_model (GTK_TREE_VIEW (gtk_builder_get_object (builder,
"section_treeview")));
- model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model));
-
- shortcut_model = gtk_tree_view_get_model (GTK_TREE_VIEW (gtk_builder_get_object (builder,
"shortcut_treeview")));
-
- /* 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;
- }
-
- GHashTable *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 (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;
-
- 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 (model), &iter);
- gtk_list_store_set (GTK_LIST_STORE (model), &iter,
- SECTION_DESCRIPTION_COLUMN, title,
- SECTION_ID_COLUMN, id,
- SECTION_GROUP_COLUMN, group,
- -1);
- }
-}
+#define CUSTOM_KEYS_BASENAME "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings"
static char *
replace_pictures_folder (const char *description)
{
+ GRegex *pictures_regex;
const char *path;
char *dirname;
char *ret;
@@ -270,11 +44,15 @@ replace_pictures_folder (const char *description)
if (strstr (description, "$PICTURES") == NULL)
return g_strdup (description);
+ pictures_regex = g_regex_new ("\\$PICTURES", 0, 0, NULL);
path = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES);
dirname = g_filename_display_basename (path);
ret = g_regex_replace (pictures_regex, description, -1,
0, dirname, 0, NULL);
+
+ g_regex_unref (pictures_regex);
g_free (dirname);
+
if (ret == NULL)
return g_strdup (description);
@@ -436,412 +214,7 @@ parse_start_tag (GMarkupParseContext *ctx,
g_array_append_val (keylist->entries, key);
}
-static gboolean
-strv_contains (char **strv,
- char *str)
-{
- char **p = strv;
- for (p = strv; *p; p++)
- if (strcmp (*p, str) == 0)
- return TRUE;
-
- return FALSE;
-}
-
-static void
-append_sections_from_file (GtkBuilder *builder, const gchar *path, const char *datadir, gchar
**wm_keybindings)
-{
- GError *err = NULL;
- char *buf;
- gsize buf_len;
- KeyList *keylist;
- KeyListEntry *keys;
- KeyListEntry key = { 0, };
- const char *title;
- int group;
- guint i;
- GMarkupParseContext *ctx;
- GMarkupParser parser = { parse_start_tag, NULL, NULL, NULL, NULL };
-
- /* Parse file */
- if (!g_file_get_contents (path, &buf, &buf_len, &err))
- return;
-
- keylist = g_new0 (KeyList, 1);
- keylist->entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry));
- ctx = g_markup_parse_context_new (&parser, 0, keylist, NULL);
-
- if (!g_markup_parse_context_parse (ctx, buf, buf_len, &err))
- {
- g_warning ("Failed to parse '%s': '%s'", path, err->message);
- g_error_free (err);
- g_free (keylist->name);
- g_free (keylist->package);
- g_free (keylist->wm_name);
- for (i = 0; i < keylist->entries->len; i++)
- g_free (((KeyListEntry *) &(keylist->entries->data[i]))->name);
- g_array_free (keylist->entries, TRUE);
- g_free (keylist);
- keylist = NULL;
- }
- g_markup_parse_context_free (ctx);
- g_free (buf);
-
- if (keylist == NULL)
- return;
-
- /* 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 && !strv_contains (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;
- }
-
- /* 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 (builder, 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 (keys);
-
- g_free (keylist);
-}
-
-static void
-append_sections_from_gsettings (GtkBuilder *builder)
-{
- char **custom_paths;
- GArray *entries;
- KeyListEntry key = { 0, };
- int i;
-
- /* load custom shortcuts from GSettings */
- entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry));
-
- custom_paths = g_settings_get_strv (binding_settings, "custom-keybindings");
- for (i = 0; custom_paths[i]; i++)
- {
- key.name = g_strdup (custom_paths[i]);
- if (!have_key_for_group (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 (builder, _("Custom Shortcuts"), CUSTOM_SHORTCUTS_ID, BINDING_GROUP_USER, keys);
- for (i = 0; i < entries->len; ++i)
- {
- g_free (keys[i].name);
- }
- }
- else
- {
- append_section (builder, _("Custom Shortcuts"), CUSTOM_SHORTCUTS_ID, BINDING_GROUP_USER, NULL);
- }
-
- g_array_free (entries, TRUE);
-}
-
-static void
-reload_sections (CcPanel *panel)
-{
- GtkBuilder *builder;
- gchar **wm_keybindings;
- gchar *default_wm_keybindings[] = { "Mutter", "GNOME Shell", NULL };
- GDir *dir;
- GtkTreeModel *sort_model;
- GtkTreeModel *section_model;
- GtkTreeModel *shortcut_model;
- const gchar * const * data_dirs;
- guint i;
- GtkTreeView *section_treeview;
- GtkTreeSelection *selection;
- GtkTreeIter iter;
- GHashTable *loaded_files;
- const char *section_to_set;
-
- builder = g_object_get_data (G_OBJECT (panel), "builder");
-
- section_treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "section_treeview"));
- sort_model = gtk_tree_view_get_model (section_treeview);
- section_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model));
-
- shortcut_model = gtk_tree_view_get_model (GTK_TREE_VIEW (gtk_builder_get_object (builder,
"shortcut_treeview")));
- /* FIXME: get current selection and keep it after refreshing */
-
- /* Clear previous models and hash tables */
- gtk_list_store_clear (GTK_LIST_STORE (section_model));
- gtk_list_store_clear (GTK_LIST_STORE (shortcut_model));
- if (kb_system_sections != NULL)
- g_hash_table_destroy (kb_system_sections);
- kb_system_sections = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- (GDestroyNotify) free_key_array);
-
- if (kb_apps_sections != NULL)
- g_hash_table_destroy (kb_apps_sections);
- kb_apps_sections = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- (GDestroyNotify) free_key_array);
-
- if (kb_user_sections != NULL)
- g_hash_table_destroy (kb_user_sections);
- 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 ();
-#endif
- else
- 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 (builder, 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);
-
- /* Add a separator */
- gtk_list_store_append (GTK_LIST_STORE (section_model), &iter);
- gtk_list_store_set (GTK_LIST_STORE (section_model), &iter,
- SECTION_DESCRIPTION_COLUMN, NULL,
- SECTION_GROUP_COLUMN, BINDING_GROUP_SEPARATOR,
- -1);
-
- /* Load custom keybindings */
- append_sections_from_gsettings (builder);
-
- /* Select the first item, or the requested section, if any */
- section_to_set = g_object_get_data (G_OBJECT (panel), "section-to-set");
- if (section_to_set != NULL)
- {
- if (keyboard_shortcuts_set_section (panel, section_to_set))
- {
- g_object_set_data (G_OBJECT (panel), "section-to-set", NULL);
- return;
- }
- }
- g_assert (gtk_tree_model_get_iter_first (sort_model, &iter));
- selection = gtk_tree_view_get_selection (section_treeview);
- gtk_tree_selection_select_iter (selection, &iter);
-
- g_object_set_data (G_OBJECT (panel), "section-to-set", NULL);
-}
-
-static void
-accel_set_func (GtkTreeViewColumn *tree_column,
- GtkCellRenderer *cell,
- GtkTreeModel *model,
- GtkTreeIter *iter,
- gpointer data)
-{
- gpointer entry;
- ShortcutType type;
-
- gtk_tree_model_get (model, iter,
- DETAIL_KEYENTRY_COLUMN, &entry,
- DETAIL_TYPE_COLUMN, &type,
- -1);
-
- gtk_cell_renderer_set_visible (cell, FALSE);
-
- if (type == SHORTCUT_TYPE_XKB_OPTION &&
- GTK_IS_CELL_RENDERER_COMBO (cell))
- {
- CcKeyboardOption *option = entry;
-
- gtk_cell_renderer_set_visible (cell, TRUE);
- g_object_set (cell,
- "model", cc_keyboard_option_get_store (option),
- "text", cc_keyboard_option_get_current_value_description (option),
- NULL);
- }
- else if (type == SHORTCUT_TYPE_KEY_ENTRY &&
- GTK_IS_CELL_RENDERER_TEXT (cell) &&
- !GTK_IS_CELL_RENDERER_COMBO (cell) &&
- entry != NULL)
- {
- CcKeyboardItem *item = entry;
-
- gtk_cell_renderer_set_visible (cell, TRUE);
-
- if (item->editable)
- g_object_set (cell,
- "editable", TRUE,
- "accel-key", item->keyval,
- "accel-mods", item->mask,
- "keycode", item->keycode,
- "style", PANGO_STYLE_NORMAL,
- NULL);
- else
- g_object_set (cell,
- "editable", FALSE,
- "accel-key", item->keyval,
- "accel-mods", item->mask,
- "keycode", item->keycode,
- "style", PANGO_STYLE_ITALIC,
- NULL);
- }
-}
-
-static void
-description_set_func (GtkTreeViewColumn *tree_column,
- GtkCellRenderer *cell,
- GtkTreeModel *model,
- GtkTreeIter *iter,
- gpointer data)
-{
- gchar *description;
- CcKeyboardItem *item;
- ShortcutType type;
-
- gtk_tree_model_get (model, iter,
- DETAIL_DESCRIPTION_COLUMN, &description,
- DETAIL_KEYENTRY_COLUMN, &item,
- DETAIL_TYPE_COLUMN, &type,
- -1);
-
- if (type == SHORTCUT_TYPE_XKB_OPTION)
- {
- g_object_set (cell, "text", description, NULL);
- }
- else
- {
- if (item != NULL)
- g_object_set (cell,
- "editable", FALSE,
- "text", item->description != NULL ?
- item->description : _("<Unknown Action>"),
- NULL);
- else
- g_object_set (cell,
- "editable", FALSE, NULL);
- }
-
- g_free (description);
-}
-
-static void
-shortcut_selection_changed (GtkTreeSelection *selection, gpointer data)
-{
- GtkWidget *button = data;
- GtkTreeModel *model;
- GtkTreeIter iter;
- CcKeyboardItem *item;
- gboolean can_remove;
- ShortcutType type;
-
- can_remove = FALSE;
- if (gtk_tree_selection_get_selected (selection, &model, &iter))
- {
- 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 void
+void
fill_xkb_options_shortcuts (GtkTreeModel *model)
{
GList *l;
@@ -860,284 +233,6 @@ fill_xkb_options_shortcuts (GtkTreeModel *model)
}
}
-static void
-section_selection_changed (GtkTreeSelection *selection, gpointer data)
-{
- GtkTreeIter iter;
- GtkTreeModel *model;
- GtkBuilder *builder = GTK_BUILDER (data);
-
- if (gtk_tree_selection_get_selected (selection, &model, &iter))
- {
- GPtrArray *keys;
- GtkWidget *shortcut_treeview;
- GtkTreeModel *shortcut_model;
- gchar *id;
- BindingGroupType group;
- gint i;
-
- gtk_tree_model_get (model, &iter,
- SECTION_ID_COLUMN, &id,
- SECTION_GROUP_COLUMN, &group, -1);
-
- keys = g_hash_table_lookup (get_hash_for_group (group), id);
- if (keys == NULL)
- {
- g_warning ("Can't find section %s in sections hash table.", id);
- g_free (id);
- return;
- }
-
- gtk_widget_set_sensitive (WID (builder, "remove-toolbutton"), FALSE);
-
- /* Fill the shortcut treeview with the keys for the selected section */
- shortcut_treeview = GTK_WIDGET (gtk_builder_get_object (builder, "shortcut_treeview"));
- shortcut_model = gtk_tree_view_get_model (GTK_TREE_VIEW (shortcut_treeview));
- gtk_list_store_clear (GTK_LIST_STORE (shortcut_model));
-
- for (i = 0; i < keys->len; i++)
- {
- GtkTreeIter new_row;
- CcKeyboardItem *item = g_ptr_array_index (keys, i);
-
- if (!cc_keyboard_item_is_hidden (item))
- {
- gtk_list_store_append (GTK_LIST_STORE (shortcut_model), &new_row);
- gtk_list_store_set (GTK_LIST_STORE (shortcut_model), &new_row,
- DETAIL_DESCRIPTION_COLUMN, item->description,
- DETAIL_KEYENTRY_COLUMN, item,
- DETAIL_TYPE_COLUMN, SHORTCUT_TYPE_KEY_ENTRY,
- -1);
- }
- }
-
- if (g_str_equal (id, "Typing"))
- fill_xkb_options_shortcuts (shortcut_model);
-
- g_free (id);
- }
-}
-
-static gboolean
-edit_custom_shortcut (CcKeyboardItem *item)
-{
- gint result;
- gboolean ret;
- GSettings *settings;
-
- settings = g_settings_new_with_path (item->schema, item->gsettings_path);
-
- g_settings_bind (settings, "name",
- G_OBJECT (custom_shortcut_name_entry), "text",
- G_SETTINGS_BIND_DEFAULT);
- gtk_widget_grab_focus (custom_shortcut_name_entry);
-
- g_settings_bind (settings, "command",
- G_OBJECT (custom_shortcut_command_entry), "text",
- G_SETTINGS_BIND_DEFAULT);
-
- g_settings_delay (settings);
-
- gtk_widget_set_sensitive (custom_shortcut_name_entry,
- item->desc_editable);
- gtk_widget_set_sensitive (custom_shortcut_command_entry,
- item->cmd_editable);
- gtk_window_present (GTK_WINDOW (custom_shortcut_dialog));
- result = gtk_dialog_run (GTK_DIALOG (custom_shortcut_dialog));
- switch (result)
- {
- case GTK_RESPONSE_OK:
- g_settings_apply (settings);
- ret = TRUE;
- break;
- default:
- g_settings_revert (settings);
- ret = FALSE;
- break;
- }
-
- g_settings_unbind (G_OBJECT (custom_shortcut_name_entry), "text");
- g_settings_unbind (G_OBJECT (custom_shortcut_command_entry), "text");
-
- g_object_unref (settings);
-
- gtk_widget_hide (custom_shortcut_dialog);
-
- return ret;
-}
-
-static gboolean
-remove_custom_shortcut (GtkTreeModel *model, GtkTreeIter *iter)
-{
- CcKeyboardItem *item;
- GPtrArray *keys_array;
- GVariantBuilder builder;
- char **settings_paths;
- int i;
-
- gtk_tree_model_get (model, iter,
- DETAIL_KEYENTRY_COLUMN, &item,
- -1);
-
- /* not a custom shortcut */
- 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 (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 (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 (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
-update_custom_shortcut (GtkTreeModel *model, GtkTreeIter *iter)
-{
- CcKeyboardItem *item;
-
- gtk_tree_model_get (model, iter,
- DETAIL_KEYENTRY_COLUMN, &item,
- -1);
-
- g_assert (item->type == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH);
-
- edit_custom_shortcut (item);
- if (item->command == NULL || item->command[0] == '\0')
- {
- remove_custom_shortcut (model, iter);
- }
- else
- {
- gtk_list_store_set (GTK_LIST_STORE (model), iter,
- DETAIL_KEYENTRY_COLUMN, item, -1);
- }
-}
-
-static gboolean
-start_editing_cb (GtkTreeView *tree_view,
- GdkEventButton *event,
- gpointer user_data)
-{
- GtkTreePath *path;
- GtkTreeViewColumn *column;
- GtkCellRenderer *cell = user_data;
-
- if (event->window != gtk_tree_view_get_bin_window (tree_view))
- return FALSE;
-
- if (gtk_tree_view_get_path_at_pos (tree_view,
- (gint) event->x,
- (gint) event->y,
- &path, &column,
- NULL, NULL))
- {
- GtkTreeModel *model;
- GtkTreeIter iter;
- CcKeyboardItem *item;
- ShortcutType type;
-
- model = gtk_tree_view_get_model (tree_view);
- gtk_tree_model_get_iter (model, &iter, path);
- gtk_tree_model_get (model, &iter,
- DETAIL_KEYENTRY_COLUMN, &item,
- DETAIL_TYPE_COLUMN, &type,
- -1);
-
- if (type == SHORTCUT_TYPE_XKB_OPTION)
- {
- gtk_tree_path_free (path);
- return FALSE;
- }
-
- /* if only the accel can be edited on the selected row
- * always select the accel column */
- if (item->desc_editable &&
- column == gtk_tree_view_get_column (tree_view, 0))
- {
- gtk_widget_grab_focus (GTK_WIDGET (tree_view));
- gtk_tree_view_set_cursor (tree_view,
- path,
- column,
- FALSE);
- update_custom_shortcut (model, &iter);
- }
- else
- {
- gtk_widget_grab_focus (GTK_WIDGET (tree_view));
- gtk_tree_view_set_cursor_on_cell (tree_view,
- path,
- gtk_tree_view_get_column (tree_view, 1),
- cell,
- TRUE);
- }
- g_signal_stop_emission_by_name (tree_view, "button_press_event");
- gtk_tree_path_free (path);
- }
- return TRUE;
-}
-
-static void
-start_editing_kb_cb (GtkTreeView *treeview,
- GtkTreePath *path,
- GtkTreeViewColumn *column,
- gpointer user_data)
-{
- GtkTreeModel *model;
- GtkTreeIter iter;
- CcKeyboardItem *item;
- ShortcutType type;
- GtkCellRenderer *cell = user_data;
-
- model = gtk_tree_view_get_model (treeview);
- gtk_tree_model_get_iter (model, &iter, path);
- gtk_tree_model_get (model, &iter,
- DETAIL_KEYENTRY_COLUMN, &item,
- DETAIL_TYPE_COLUMN, &type,
- -1);
-
- if (type == SHORTCUT_TYPE_XKB_OPTION)
- return;
-
- /* if only the accel can be edited on the selected row
- * always select the accel column */
- if (item->desc_editable &&
- column == gtk_tree_view_get_column (treeview, 0))
- {
- gtk_widget_grab_focus (GTK_WIDGET (treeview));
- gtk_tree_view_set_cursor (treeview,
- path,
- column,
- FALSE);
- update_custom_shortcut (model, &iter);
- }
- else
- {
- gtk_widget_grab_focus (GTK_WIDGET (treeview));
- gtk_tree_view_set_cursor_on_cell (treeview,
- path,
- gtk_tree_view_get_column (treeview, 1),
- cell,
- TRUE);
- }
-}
-
static const guint forbidden_keyvals[] = {
/* Navigation keys */
GDK_KEY_Home,
@@ -1157,20 +252,6 @@ static const guint forbidden_keyvals[] = {
GDK_KEY_Mode_switch
};
-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
keyval_is_forbidden (guint keyval)
{
@@ -1184,81 +265,7 @@ keyval_is_forbidden (guint keyval)
return FALSE;
}
-static gboolean
-compare_keys_for_uniqueness (CcKeyboardItem *element,
- CcUniquenessData *data)
-{
- CcKeyboardItem *orig_item;
-
- orig_item = data->orig_item;
-
- /* no conflict for : blanks, different modifiers, or ourselves */
- if (element == NULL || data->new_mask != element->mask ||
- cc_keyboard_item_equal (orig_item, element))
- return FALSE;
-
- if (data->new_keyval != 0) {
- if (data->new_keyval != element->keyval)
- return FALSE;
- } else if (element->keyval != 0 || data->new_keycode != element->keycode)
- return FALSE;
-
- data->conflict_item = element;
-
- return TRUE;
-}
-
-static gboolean
-cb_check_for_uniqueness (gpointer key,
- GPtrArray *keys_array,
- CcUniquenessData *data)
-{
- guint i;
-
- for (i = 0; i < keys_array->len; i++)
- {
- CcKeyboardItem *item;
-
- item = keys_array->pdata[i];
- if (compare_keys_for_uniqueness (item, data))
- return TRUE;
- }
- return FALSE;
-}
-
-static CcKeyboardItem *
-search_for_conflict_item (CcKeyboardItem *item,
- guint keyval,
- GdkModifierType mask,
- guint keycode)
-{
- CcUniquenessData data;
-
- data.orig_item = item;
- data.new_keyval = keyval;
- data.new_mask = mask;
- data.new_keycode = keycode;
- data.conflict_item = NULL;
-
- if (keyval != 0 || keycode != 0) /* any number of shortcuts can be disabled */
- {
- BindingGroupType i;
-
- for (i = BINDING_GROUP_SYSTEM; i <= BINDING_GROUP_USER && data.conflict_item == NULL; i++)
- {
- GHashTable *table;
-
- table = get_hash_for_group (i);
- if (!table)
- continue;
- g_hash_table_find (table, (GHRFunc) cb_check_for_uniqueness, &data);
- }
- }
-
- return data.conflict_item;
-}
-
-static gboolean
+gboolean
is_valid_binding (guint keyval,
GdkModifierType mask,
guint keycode)
@@ -1283,316 +290,14 @@ is_valid_binding (guint keyval,
return TRUE;
}
-static GtkResponseType
-show_invalid_binding_dialog (GtkTreeView *view,
- guint keyval,
- GdkModifierType mask,
- guint keycode)
-{
- GtkWidget *dialog;
- char *name;
-
- name = binding_name (keyval, keycode, mask, TRUE);
-
- dialog =
- gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
- GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
- GTK_MESSAGE_WARNING,
- GTK_BUTTONS_CANCEL,
- _("The shortcut \"%s\" cannot be used because it will become impossible to type
using this key.\n"
- "Please try with a key such as Control, Alt or Shift at the same time."),
- name);
-
- g_free (name);
- gtk_dialog_run (GTK_DIALOG (dialog));
- gtk_widget_destroy (dialog);
-
- return GTK_RESPONSE_NONE;
-}
-
-static GtkResponseType
-show_conflict_item_dialog (GtkTreeView *view,
- CcKeyboardItem *item,
- CcKeyboardItem *conflict_item,
- guint keyval,
- GdkModifierType mask,
- guint keycode)
-{
- GtkWidget *dialog;
- char *name;
- int response;
-
- name = binding_name (keyval, keycode, mask, TRUE);
-
- dialog =
- gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
- GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
- GTK_MESSAGE_WARNING,
- GTK_BUTTONS_CANCEL,
- _("The shortcut \"%s\" is already used for\n\"%s\""),
- name, conflict_item->description);
- g_free (name);
-
- gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
- _("If you reassign the shortcut to \"%s\", the \"%s\" shortcut "
- "will be disabled."),
- item->description,
- conflict_item->description);
-
- gtk_dialog_add_button (GTK_DIALOG (dialog),
- _("_Reassign"),
- GTK_RESPONSE_ACCEPT);
-
- gtk_dialog_set_default_response (GTK_DIALOG (dialog),
- GTK_RESPONSE_ACCEPT);
-
- response = gtk_dialog_run (GTK_DIALOG (dialog));
- gtk_widget_destroy (dialog);
-
- return response;
-}
-
-static GtkResponseType
-show_reverse_item_dialog (GtkTreeView *view,
- CcKeyboardItem *item,
- CcKeyboardItem *reverse_item,
- CcKeyboardItem *reverse_conflict_item,
- guint keyval,
- GdkModifierType mask,
- guint keycode)
-{
- GtkWidget *dialog;
- char *name;
- int response;
-
- name = binding_name (keyval, keycode, mask, TRUE);
-
- /* translators:
- * This is the text you get in a dialogue when an action has an associated
- * "reverse" action, for example Alt+Tab going in the opposite direction to
- * Alt+Shift+Tab.
- *
- * An example text would be:
- * The "Switch to next input source" shortcut has an associated "Switch to
- * previous input source" shortcut. Do you want to automatically set it to
- * "Shift+Ctrl+Alt+Space"? */
- dialog =
- gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
- GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
- GTK_MESSAGE_WARNING,
- GTK_BUTTONS_CANCEL,
- _("The \"%s\" shortcut has an associated \"%s\" shortcut. "
- "Do you want to automatically set it to \"%s\"?"),
- item->description, reverse_item->description, name);
-
- if (reverse_conflict_item != NULL) {
- /* translators:
- * This is the text you get in a dialogue when you try to use a shortcut
- * that was already associated with another action, for example:
- * "Alt+F4" is currently associated with "Close Window", ... */
- gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
- _("\"%s\" is currently associated with \"%s\", this shortcut will be"
- " disabled if you move forward."),
- name, reverse_conflict_item->description);
- }
- g_free (name);
-
- gtk_dialog_add_button (GTK_DIALOG (dialog),
- _("_Assign"),
- GTK_RESPONSE_ACCEPT);
-
- gtk_dialog_set_default_response (GTK_DIALOG (dialog),
- GTK_RESPONSE_ACCEPT);
-
- response = gtk_dialog_run (GTK_DIALOG (dialog));
- gtk_widget_destroy (dialog);
-
- return response;
-}
-
-static void
-handle_reverse_item (CcKeyboardItem *item,
- CcKeyboardItem *reverse_item,
- guint keyval,
- GdkModifierType mask,
- guint keycode,
- GtkTreeView *view)
-{
- GtkResponseType response;
- GdkModifierType reverse_mask;
-
- reverse_mask = mask ^ GDK_SHIFT_MASK;
-
- if (!is_valid_binding(keyval, reverse_mask, keycode))
- return;
-
- if ((reverse_item->keyval != keyval)
- || (reverse_item->keycode != keycode)
- || (reverse_item->mask != reverse_mask))
- {
- CcKeyboardItem *reverse_conflict_item;
- char *binding_str;
-
- reverse_conflict_item = search_for_conflict_item (reverse_item, keyval,
- reverse_mask,
- keycode);
-
- response = show_reverse_item_dialog (view, item, reverse_item,
- reverse_conflict_item,
- keyval, reverse_mask,
- keycode);
- if (response == GTK_RESPONSE_ACCEPT)
- {
- binding_str = binding_name (keyval, keycode, reverse_mask, FALSE);
- g_object_set (G_OBJECT (reverse_item), "binding", binding_str, NULL);
- g_free (binding_str);
-
- if (reverse_conflict_item != NULL)
- g_object_set (G_OBJECT (reverse_conflict_item),
- "binding", NULL, NULL);
- }
- else
- {
- /* The existing reverse binding may be conflicting with the binding
- * we are setting. Other conflicts have already been handled in
- * accel_edited_callback()
- */
- CcKeyboardItem *conflict_item;
- conflict_item = search_for_conflict_item (item, keyval,
- mask, keycode);
- if (conflict_item != NULL)
- {
- g_warn_if_fail (conflict_item == reverse_item);
- g_object_set (G_OBJECT (conflict_item),
- "binding", NULL, NULL);
- }
- }
- }
-}
-
-static void
-accel_edited_callback (GtkCellRendererText *cell,
- const char *path_string,
- guint keyval,
- GdkModifierType mask,
- guint keycode,
- GtkTreeView *view)
-{
- GtkTreeModel *model;
- GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
- GtkTreeIter iter;
- CcKeyboardItem *item;
- CcKeyboardItem *conflict_item;
- CcKeyboardItem *reverse_item;
- char *str;
-
- model = gtk_tree_view_get_model (view);
- gtk_tree_model_get_iter (model, &iter, path);
- gtk_tree_path_free (path);
- gtk_tree_model_get (model, &iter,
- DETAIL_KEYENTRY_COLUMN, &item,
- -1);
-
- /* sanity check */
- if (item == NULL)
- return;
-
- /* CapsLock isn't supported as a keybinding modifier, so keep it from confusing us */
- mask &= ~GDK_LOCK_MASK;
-
- conflict_item = search_for_conflict_item (item, keyval, mask, keycode);
-
- /* Check for unmodified keys */
- if (!is_valid_binding (keyval, mask, keycode))
- {
- show_invalid_binding_dialog (view, keyval, mask, keycode);
-
- /* set it back to its previous value. */
- g_object_set (G_OBJECT (cell),
- "accel-key", item->keyval,
- "keycode", item->keycode,
- "accel-mods", item->mask,
- NULL);
- return;
- }
-
- reverse_item = cc_keyboard_item_get_reverse_item (item);
-
- /* flag to see if the new accelerator was in use by something */
- if ((conflict_item != NULL) && (conflict_item != reverse_item))
- {
- GtkResponseType response;
-
- response = show_conflict_item_dialog (view, item, conflict_item,
- keyval, mask, keycode);
-
- if (response == GTK_RESPONSE_ACCEPT)
- {
- g_object_set (G_OBJECT (conflict_item), "binding", NULL, NULL);
-
- str = binding_name (keyval, keycode, mask, FALSE);
- g_object_set (G_OBJECT (item), "binding", str, NULL);
-
- g_free (str);
- if (reverse_item == NULL)
- return;
- }
- else
- {
- /* set it back to its previous value. */
- g_object_set (G_OBJECT (cell),
- "accel-key", item->keyval,
- "keycode", item->keycode,
- "accel-mods", item->mask,
- NULL);
- return;
- }
-
- }
-
- str = binding_name (keyval, keycode, mask, FALSE);
- g_object_set (G_OBJECT (item), "binding", str, NULL);
-
- g_free (str);
-
- if (reverse_item != NULL)
- handle_reverse_item (item, reverse_item, keyval, mask, keycode, view);
-}
-
-static void
-accel_cleared_callback (GtkCellRendererText *cell,
- const char *path_string,
- gpointer data)
-{
- GtkTreeView *view = (GtkTreeView *) data;
- GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
- CcKeyboardItem *item;
- GtkTreeIter iter;
- GtkTreeModel *model;
-
- model = gtk_tree_view_get_model (view);
- gtk_tree_model_get_iter (model, &iter, path);
- gtk_tree_path_free (path);
- gtk_tree_model_get (model, &iter,
- DETAIL_KEYENTRY_COLUMN, &item,
- -1);
-
- /* sanity check */
- if (item == NULL)
- return;
-
- /* Unset the key */
- g_object_set (G_OBJECT (item), "binding", NULL, NULL);
-}
-
-static gchar *
-find_free_settings_path ()
+gchar*
+find_free_settings_path (GSettings *settings)
{
char **used_names;
char *dir = NULL;
int i, num, n_names;
- used_names = g_settings_get_strv (binding_settings, "custom-keybindings");
+ used_names = g_settings_get_strv (settings, "custom-keybindings");
n_names = g_strv_length (used_names);
for (num = 0; dir == NULL; num++)
@@ -1614,219 +319,6 @@ find_free_settings_path ()
return dir;
}
-static void
-add_custom_shortcut (GtkTreeView *tree_view,
- GtkTreeModel *model)
-{
- CcKeyboardItem *item;
- GtkTreePath *path;
- gchar *settings_path;
-
- item = cc_keyboard_item_new (CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH);
-
- settings_path = find_free_settings_path ();
- cc_keyboard_item_load_from_gsettings_path (item, settings_path, TRUE);
- g_free (settings_path);
-
- item->model = model;
-
- if (edit_custom_shortcut (item) &&
- item->command && item->command[0])
- {
- GPtrArray *keys_array;
- GtkTreeIter iter;
- GHashTable *hash;
- GVariantBuilder builder;
- char **settings_paths;
- int i;
-
- hash = get_hash_for_group (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 (GTK_LIST_STORE (model), &iter);
- gtk_list_store_set (GTK_LIST_STORE (model), &iter, DETAIL_KEYENTRY_COLUMN, item, -1);
-
- settings_paths = g_settings_get_strv (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 (binding_settings, "custom-keybindings",
- g_variant_builder_end (&builder));
-
- /* make the new shortcut visible */
- path = gtk_tree_model_get_path (model, &iter);
- gtk_tree_view_expand_to_path (tree_view, path);
- gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0, 0);
- gtk_tree_path_free (path);
- }
- else
- {
- g_object_unref (item);
- }
-}
-
-static void
-shortcut_entry_changed (GtkEntry *entry,
- gpointer user_data)
-{
- guint16 name_length;
- guint16 command_length;
-
- name_length = gtk_entry_get_text_length (GTK_ENTRY (custom_shortcut_name_entry));
- command_length = gtk_entry_get_text_length (GTK_ENTRY (custom_shortcut_command_entry));
-
- gtk_widget_set_sensitive (custom_shortcut_ok_button,
- name_length > 0 && command_length > 0);
-}
-
-static void
-add_button_clicked (GtkWidget *button,
- GtkBuilder *builder)
-{
- GtkTreeView *treeview;
- GtkTreeModel *model;
- GtkTreeModel *section_model;
- GtkTreeIter iter;
- gboolean found, cont;
-
- treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder,
- "shortcut_treeview"));
- model = gtk_tree_view_get_model (treeview);
-
- /* Select the Custom Shortcuts section
- * before adding the shortcut itself */
- section_model = gtk_tree_view_get_model (GTK_TREE_VIEW (WID (builder, "section_treeview")));
- cont = gtk_tree_model_get_iter_first (section_model, &iter);
- found = FALSE;
- while (cont)
- {
- BindingGroupType group;
-
- gtk_tree_model_get (section_model, &iter,
- SECTION_GROUP_COLUMN, &group,
- -1);
-
- if (group == BINDING_GROUP_USER)
- {
- found = TRUE;
- break;
- }
- cont = gtk_tree_model_iter_next (section_model, &iter);
- }
- if (found)
- {
- GtkTreeSelection *selection;
-
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (WID (builder, "section_treeview")));
- gtk_tree_selection_select_iter (selection, &iter);
- }
-
- /* And add the shortcut */
- add_custom_shortcut (treeview, model);
-}
-
-static void
-remove_button_clicked (GtkWidget *button,
- GtkBuilder *builder)
-{
- GtkTreeView *treeview;
- GtkTreeModel *model;
- GtkTreeSelection *selection;
- GtkTreeIter iter;
-
- treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder,
- "shortcut_treeview"));
- model = gtk_tree_view_get_model (treeview);
-
- selection = gtk_tree_view_get_selection (treeview);
- if (gtk_tree_selection_get_selected (selection, NULL, &iter))
- {
- remove_custom_shortcut (model, &iter);
- }
-}
-
-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 gboolean
-sections_separator_func (GtkTreeModel *model,
- GtkTreeIter *iter,
- gpointer data)
-{
- BindingGroupType type;
-
- gtk_tree_model_get (model, iter, SECTION_GROUP_COLUMN, &type, -1);
-
- return type == BINDING_GROUP_SEPARATOR;
-}
-
-static void
-xkb_options_combo_changed (GtkCellRendererCombo *combo,
- gchar *model_path,
- GtkTreeIter *model_iter,
- gpointer data)
-{
- GtkTreeView *shortcut_treeview;
- GtkTreeModel *shortcut_model;
- GtkTreeIter shortcut_iter;
- GtkTreeSelection *selection;
- CcKeyboardOption *option;
- ShortcutType type;
- GtkBuilder *builder = data;
-
- shortcut_treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "shortcut_treeview"));
- selection = gtk_tree_view_get_selection (shortcut_treeview);
- if (!gtk_tree_selection_get_selected (selection, &shortcut_model, &shortcut_iter))
- return;
-
- gtk_tree_model_get (shortcut_model, &shortcut_iter,
- DETAIL_KEYENTRY_COLUMN, &option,
- DETAIL_TYPE_COLUMN, &type,
- -1);
-
- if (type != SHORTCUT_TYPE_XKB_OPTION)
- return;
-
- cc_keyboard_option_set_selection (option, model_iter);
-}
-
static gboolean
poke_xkb_option_row (GtkTreeModel *model,
GtkTreePath *path,
@@ -1855,7 +347,7 @@ xkb_option_changed (CcKeyboardOption *option,
gtk_tree_model_foreach (model, poke_xkb_option_row, option);
}
-static void
+void
setup_keyboard_options (GtkListStore *store)
{
GList *l;
@@ -1865,278 +357,43 @@ setup_keyboard_options (GtkListStore *store)
G_CALLBACK (xkb_option_changed), store);
}
-static void
-setup_dialog (CcPanel *panel, GtkBuilder *builder)
+KeyList*
+parse_keylist_from_file (const gchar *path)
{
- GtkCellRenderer *renderer;
- GtkTreeViewColumn *column;
- GtkWidget *widget;
- GtkTreeView *treeview;
- GtkTreeSelection *selection;
- GList *focus_chain;
- CcShell *shell;
- GtkListStore *model;
- GtkTreeModelSort *sort_model;
- GtkStyleContext *context;
-
- gtk_widget_set_size_request (GTK_WIDGET (panel), -1, 400);
-
- /* Setup the section treeview */
- treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "section_treeview"));
- gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (treeview),
- sections_separator_func,
- panel,
- NULL);
-
- renderer = gtk_cell_renderer_text_new ();
- column = gtk_tree_view_column_new_with_attributes (_("Section"),
- renderer,
- "text", SECTION_DESCRIPTION_COLUMN,
- NULL);
- g_object_set (renderer,
- "width-chars", 20,
- "ellipsize", PANGO_ELLIPSIZE_END,
- NULL);
-
- gtk_tree_view_append_column (treeview, column);
-
- model = gtk_list_store_new (SECTION_N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
- sort_model = GTK_TREE_MODEL_SORT (gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (model)));
- gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (sort_model));
- g_object_unref (model);
-
- gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sort_model),
- SECTION_DESCRIPTION_COLUMN,
- section_sort_item,
- panel,
- NULL);
-
- gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model),
- SECTION_DESCRIPTION_COLUMN,
- GTK_SORT_ASCENDING);
- g_object_unref (sort_model);
-
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
-
- gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
-
- g_signal_connect (selection, "changed",
- G_CALLBACK (section_selection_changed), builder);
- section_selection_changed (selection, builder);
-
- /* Setup the shortcut treeview */
- treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder,
- "shortcut_treeview"));
-
- binding_settings = g_settings_new (BINDINGS_SCHEMA);
-
- renderer = gtk_cell_renderer_text_new ();
- g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-
- column = gtk_tree_view_column_new_with_attributes (NULL, renderer, NULL);
- gtk_tree_view_column_set_cell_data_func (column, renderer, description_set_func, NULL, NULL);
- gtk_tree_view_column_set_resizable (column, FALSE);
- gtk_tree_view_column_set_expand (column, TRUE);
-
- gtk_tree_view_append_column (treeview, column);
-
- renderer = (GtkCellRenderer *) g_object_new (GTK_TYPE_CELL_RENDERER_ACCEL,
- "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
- NULL);
-
- g_signal_connect (treeview, "button_press_event",
- G_CALLBACK (start_editing_cb), renderer);
- g_signal_connect (treeview, "row-activated",
- G_CALLBACK (start_editing_kb_cb), renderer);
-
- g_signal_connect (renderer, "accel_edited",
- G_CALLBACK (accel_edited_callback),
- treeview);
- g_signal_connect (renderer, "accel_cleared",
- G_CALLBACK (accel_cleared_callback),
- treeview);
-
- column = gtk_tree_view_column_new_with_attributes (NULL, renderer, NULL);
- gtk_tree_view_column_set_cell_data_func (column, renderer, accel_set_func, NULL, NULL);
- gtk_tree_view_column_set_resizable (column, FALSE);
- gtk_tree_view_column_set_expand (column, FALSE);
-
- renderer = (GtkCellRenderer *) g_object_new (GTK_TYPE_CELL_RENDERER_COMBO,
- "has-entry", FALSE,
- "text-column", XKB_OPTION_DESCRIPTION_COLUMN,
- "editable", TRUE,
- "ellipsize", PANGO_ELLIPSIZE_END,
- "width-chars", 25,
- NULL);
- g_signal_connect (renderer, "changed",
- G_CALLBACK (xkb_options_combo_changed), builder);
-
- gtk_tree_view_column_pack_end (column, renderer, FALSE);
-
- gtk_tree_view_column_set_cell_data_func (column, renderer, accel_set_func, NULL, NULL);
-
- gtk_tree_view_append_column (treeview, column);
-
- model = gtk_list_store_new (DETAIL_N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT);
- gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (model));
- g_object_unref (model);
-
- setup_keyboard_options (model);
-
- widget = GTK_WIDGET (gtk_builder_get_object (builder, "actions_swindow"));
- context = gtk_widget_get_style_context (widget);
- gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
- widget = GTK_WIDGET (gtk_builder_get_object (builder, "shortcut-toolbar"));
- context = gtk_widget_get_style_context (widget);
- gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
-
- /* set up the focus chain */
- focus_chain = g_list_append (NULL, WID (builder, "sections_swindow"));
- focus_chain = g_list_append (focus_chain, WID (builder, "actions_swindow"));
- focus_chain = g_list_append (focus_chain, WID (builder, "shortcut-toolbar"));
-
- widget = GTK_WIDGET (gtk_builder_get_object (builder, "shortcuts_grid"));
- gtk_container_set_focus_chain (GTK_CONTAINER (widget), focus_chain);
- g_list_free (focus_chain);
-
- /* set up the dialog */
- shell = cc_panel_get_shell (CC_PANEL (panel));
- widget = cc_shell_get_toplevel (shell);
-
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
- g_signal_connect (selection, "changed",
- G_CALLBACK (shortcut_selection_changed),
- WID (builder, "remove-toolbutton"));
-
- /* setup the custom shortcut dialog */
- custom_shortcut_dialog = WID (builder,
- "custom-shortcut-dialog");
- custom_shortcut_ok_button = WID (builder, "custom-shortcut-ok-button");
- custom_shortcut_name_entry = WID (builder,
- "custom-shortcut-name-entry");
- g_signal_connect (custom_shortcut_name_entry, "changed",
- G_CALLBACK (shortcut_entry_changed), NULL);
- custom_shortcut_command_entry = WID (builder,
- "custom-shortcut-command-entry");
- g_signal_connect (custom_shortcut_command_entry, "changed",
- G_CALLBACK (shortcut_entry_changed), NULL);
- g_signal_connect (WID (builder, "add-toolbutton"),
- "clicked", G_CALLBACK (add_button_clicked), builder);
- g_signal_connect (WID (builder, "remove-toolbutton"),
- "clicked", G_CALLBACK (remove_button_clicked), builder);
-
- gtk_dialog_set_default_response (GTK_DIALOG (custom_shortcut_dialog),
- GTK_RESPONSE_OK);
-
- gtk_window_set_transient_for (GTK_WINDOW (custom_shortcut_dialog),
- GTK_WINDOW (widget));
-
- gtk_window_set_resizable (GTK_WINDOW (custom_shortcut_dialog), FALSE);
-}
-
-static void
-on_window_manager_change (const char *wm_name, CcPanel *panel)
-{
- reload_sections (panel);
-}
-
-void
-keyboard_shortcuts_init (CcPanel *panel, GtkBuilder *builder)
-{
- g_object_set_data (G_OBJECT (panel), "builder", builder);
-#ifdef GDK_WINDOWING_X11
- if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
- wm_changed_id = wm_common_register_window_manager_change ((GFunc) on_window_manager_change,
- panel);
-#endif
- pictures_regex = g_regex_new ("\\$PICTURES", 0, 0, NULL);
- setup_dialog (panel, builder);
- reload_sections (panel);
-}
-
-gboolean
-keyboard_shortcuts_set_section (CcPanel *panel, const char *section)
-{
- GtkBuilder *builder;
- GtkTreeModel *section_model;
- GtkTreeIter iter;
- gboolean found, cont;
-
- builder = g_object_get_data (G_OBJECT (panel), "builder");
- if (builder == NULL)
- {
- /* Remember the section name to be set later */
- g_object_set_data_full (G_OBJECT (panel), "section-to-set", g_strdup (section), g_free);
- return TRUE;
- }
- section_model = gtk_tree_view_get_model (GTK_TREE_VIEW (WID (builder, "section_treeview")));
- cont = gtk_tree_model_get_iter_first (section_model, &iter);
- found = FALSE;
- while (cont)
- {
- char *id;
-
- gtk_tree_model_get (section_model, &iter,
- SECTION_ID_COLUMN, &id,
- -1);
+ KeyList *keylist;
+ GError *err = NULL;
+ char *buf;
+ gsize buf_len;
+ guint i;
- if (g_strcmp0 (id, section) == 0)
- {
- found = TRUE;
- g_free (id);
- break;
- }
- g_free (id);
- cont = gtk_tree_model_iter_next (section_model, &iter);
- }
- if (found)
- {
- GtkTreeSelection *selection;
+ GMarkupParseContext *ctx;
+ GMarkupParser parser = { parse_start_tag, NULL, NULL, NULL, NULL };
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (WID (builder, "section_treeview")));
- gtk_tree_selection_select_iter (selection, &iter);
- }
- else
- {
- g_warning ("Could not find section '%s' to switch to.", section);
- }
+ /* Parse file */
+ if (!g_file_get_contents (path, &buf, &buf_len, &err))
+ return NULL;
- return found;
-}
+ keylist = g_new0 (KeyList, 1);
+ keylist->entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry));
+ ctx = g_markup_parse_context_new (&parser, 0, keylist, NULL);
-void
-keyboard_shortcuts_dispose (CcPanel *panel)
-{
- if (kb_system_sections != NULL)
- {
- g_hash_table_destroy (kb_system_sections);
- kb_system_sections = NULL;
- }
- if (kb_apps_sections != NULL)
- {
- g_hash_table_destroy (kb_apps_sections);
- kb_apps_sections = NULL;
- }
- if (kb_user_sections != NULL)
- {
- g_hash_table_destroy (kb_user_sections);
- kb_user_sections = NULL;
- }
- if (pictures_regex != NULL)
+ if (!g_markup_parse_context_parse (ctx, buf, buf_len, &err))
{
- g_regex_unref (pictures_regex);
- pictures_regex = NULL;
- }
-
- g_clear_object (&binding_settings);
+ g_warning ("Failed to parse '%s': '%s'", path, err->message);
+ g_error_free (err);
+ g_free (keylist->name);
+ g_free (keylist->package);
+ g_free (keylist->wm_name);
- g_clear_pointer (&custom_shortcut_dialog, gtk_widget_destroy);
+ for (i = 0; i < keylist->entries->len; i++)
+ g_free (((KeyListEntry *) &(keylist->entries->data[i]))->name);
- if (wm_changed_id)
- {
- wm_common_unregister_window_manager_change (wm_changed_id);
- wm_changed_id = NULL;
+ g_array_free (keylist->entries, TRUE);
+ g_free (keylist);
+ keylist = NULL;
}
+ g_markup_parse_context_free (ctx);
+ g_free (buf);
- cc_keyboard_option_clear_all ();
+ return keylist;
}
diff --git a/panels/keyboard/keyboard-shortcuts.h b/panels/keyboard/keyboard-shortcuts.h
index 02564b1..4631b34 100644
--- a/panels/keyboard/keyboard-shortcuts.h
+++ b/panels/keyboard/keyboard-shortcuts.h
@@ -79,6 +79,14 @@ enum
SECTION_N_COLUMNS
};
-void keyboard_shortcuts_init (CcPanel *panel, GtkBuilder *builder);
-gboolean keyboard_shortcuts_set_section (CcPanel *panel, const char *section);
-void keyboard_shortcuts_dispose (CcPanel *panel);
+gchar* find_free_settings_path (GSettings *settings);
+
+void fill_xkb_options_shortcuts (GtkTreeModel *model);
+
+void setup_keyboard_options (GtkListStore *store);
+
+gboolean is_valid_binding (guint keyval,
+ GdkModifierType mask,
+ guint keycode);
+
+KeyList* parse_keylist_from_file (const gchar *path);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]