summaryrefslogtreecommitdiffstats
path: root/panels/keyboard/cc-keyboard-item.c
diff options
context:
space:
mode:
Diffstat (limited to 'panels/keyboard/cc-keyboard-item.c')
-rw-r--r--panels/keyboard/cc-keyboard-item.c881
1 files changed, 881 insertions, 0 deletions
diff --git a/panels/keyboard/cc-keyboard-item.c b/panels/keyboard/cc-keyboard-item.c
new file mode 100644
index 0000000..31b134a
--- /dev/null
+++ b/panels/keyboard/cc-keyboard-item.c
@@ -0,0 +1,881 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2011, 2014 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+
+#include "cc-keyboard-item.h"
+
+#define CUSTOM_KEYS_SCHEMA "org.gnome.settings-daemon.plugins.media-keys.custom-keybinding"
+
+struct _CcKeyboardItem
+{
+ GObject parent_instance;
+
+ CcKeyboardItem *reverse_item;
+ gboolean is_reversed;
+ gboolean hidden;
+
+ CcKeyboardItemType type;
+
+ BindingGroupType group;
+ char *description;
+ gboolean editable;
+ GList *key_combos;
+ GList *default_combos;
+ gboolean can_set_multiple;
+
+ /* GSettings path */
+ char *gsettings_path;
+ gboolean desc_editable;
+ char *command;
+ gboolean cmd_editable;
+
+ /* GSettings */
+ char *schema;
+ char *key;
+ GSettings *settings;
+};
+
+enum
+{
+ PROP_0,
+ PROP_DESCRIPTION,
+ PROP_EDITABLE,
+ PROP_TYPE,
+ PROP_IS_VALUE_DEFAULT,
+ PROP_COMMAND,
+ PROP_KEY_COMBOS
+};
+
+static void cc_keyboard_item_class_init (CcKeyboardItemClass *klass);
+static void cc_keyboard_item_init (CcKeyboardItem *keyboard_item);
+static void cc_keyboard_item_finalize (GObject *object);
+
+G_DEFINE_TYPE (CcKeyboardItem, cc_keyboard_item, G_TYPE_OBJECT)
+
+static const CcKeyCombo EMPTY_COMBO = { 0, 0, 0 };
+
+static gboolean
+combo_equal (CcKeyCombo *a, CcKeyCombo *b)
+{
+ return (a->keyval == b->keyval
+ && a->keycode == b->keycode
+ && a->mask == b->mask);
+}
+
+static gboolean
+combos_contains (GList *combos, CcKeyCombo *needle)
+{
+ for (GList *l = combos; l != NULL; l = l->next)
+ {
+ if (combo_equal (l->data, needle))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+combos_equal (GList *a, GList *b)
+{
+ // Should be efficient enough for any sane number of bindings
+
+ for (GList *l = a; l != NULL; l = l->next)
+ {
+ if (!combos_contains (b, l->data))
+ return FALSE;
+ }
+
+ for (GList *l = b; l != NULL; l = l->next)
+ {
+ if (!combos_contains (a, l->data))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+binding_from_string (const char *str,
+ CcKeyCombo *combo)
+{
+ g_return_val_if_fail (combo != NULL, FALSE);
+ g_autofree guint *keycodes = NULL;
+
+ if (str == NULL || strcmp (str, "disabled") == 0)
+ {
+ memset (combo, 0, sizeof(CcKeyCombo));
+ return TRUE;
+ }
+
+ gtk_accelerator_parse_with_keycode (str,
+ gdk_display_get_default (),
+ &combo->keyval,
+ &keycodes,
+ &combo->mask);
+
+ combo->keycode = (keycodes ? keycodes[0] : 0);
+
+ if (combo->keyval == 0)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+static void
+_set_description (CcKeyboardItem *item,
+ const char *value)
+{
+ g_free (item->description);
+ item->description = g_strdup (value);
+}
+
+const char *
+cc_keyboard_item_get_description (CcKeyboardItem *item)
+{
+ g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), NULL);
+
+ return item->description;
+}
+
+gboolean
+cc_keyboard_item_get_desc_editable (CcKeyboardItem *item)
+{
+ g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), FALSE);
+
+ return item->desc_editable;
+}
+
+static void
+_set_type (CcKeyboardItem *item,
+ gint value)
+{
+ item->type = value;
+}
+
+static void
+_set_command (CcKeyboardItem *item,
+ const char *value)
+{
+ g_free (item->command);
+ item->command = g_strdup (value);
+}
+
+const char *
+cc_keyboard_item_get_command (CcKeyboardItem *item)
+{
+ g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), NULL);
+
+ return item->command;
+}
+
+gboolean
+cc_keyboard_item_get_cmd_editable (CcKeyboardItem *item)
+{
+ g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), FALSE);
+
+ return item->cmd_editable;
+}
+
+static void
+cc_keyboard_item_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CcKeyboardItem *self;
+
+ self = CC_KEYBOARD_ITEM (object);
+
+ switch (prop_id) {
+ case PROP_DESCRIPTION:
+ _set_description (self, g_value_get_string (value));
+ break;
+ case PROP_COMMAND:
+ _set_command (self, g_value_get_string (value));
+ break;
+ case PROP_TYPE:
+ _set_type (self, g_value_get_int (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+cc_keyboard_item_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CcKeyboardItem *self;
+
+ self = CC_KEYBOARD_ITEM (object);
+
+ switch (prop_id) {
+ case PROP_DESCRIPTION:
+ g_value_set_string (value, self->description);
+ break;
+ case PROP_EDITABLE:
+ g_value_set_boolean (value, self->editable);
+ break;
+ case PROP_COMMAND:
+ g_value_set_string (value, self->command);
+ break;
+ case PROP_IS_VALUE_DEFAULT:
+ g_value_set_boolean (value, cc_keyboard_item_is_value_default (self));
+ break;
+ case PROP_KEY_COMBOS:
+ g_value_set_pointer (value, self->key_combos);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+cc_keyboard_item_class_init (CcKeyboardItemClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = cc_keyboard_item_get_property;
+ object_class->set_property = cc_keyboard_item_set_property;
+ object_class->finalize = cc_keyboard_item_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_DESCRIPTION,
+ g_param_spec_string ("description",
+ "description",
+ "description",
+ NULL,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_EDITABLE,
+ g_param_spec_boolean ("editable",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (object_class,
+ PROP_TYPE,
+ g_param_spec_int ("type",
+ NULL,
+ NULL,
+ CC_KEYBOARD_ITEM_TYPE_NONE,
+ CC_KEYBOARD_ITEM_TYPE_GSETTINGS,
+ CC_KEYBOARD_ITEM_TYPE_NONE,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
+
+ g_object_class_install_property (object_class,
+ PROP_COMMAND,
+ g_param_spec_string ("command",
+ "command",
+ "command",
+ NULL,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_IS_VALUE_DEFAULT,
+ g_param_spec_boolean ("is-value-default",
+ "is value default",
+ "is value default",
+ TRUE,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (object_class,
+ PROP_KEY_COMBOS,
+ g_param_spec_pointer ("key-combos",
+ "key combos",
+ "key combos",
+ G_PARAM_READABLE));
+}
+
+static void
+cc_keyboard_item_init (CcKeyboardItem *item)
+{
+}
+
+static void
+cc_keyboard_item_finalize (GObject *object)
+{
+ CcKeyboardItem *item;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CC_IS_KEYBOARD_ITEM (object));
+
+ item = CC_KEYBOARD_ITEM (object);
+
+ if (item->settings != NULL)
+ g_object_unref (item->settings);
+
+ /* Free memory */
+ g_free (item->gsettings_path);
+ g_free (item->description);
+ g_free (item->command);
+ g_free (item->schema);
+ g_free (item->key);
+ g_list_free_full (item->key_combos, g_free);
+ g_list_free_full (item->default_combos, g_free);
+
+ G_OBJECT_CLASS (cc_keyboard_item_parent_class)->finalize (object);
+}
+
+CcKeyboardItem *
+cc_keyboard_item_new (CcKeyboardItemType type)
+{
+ GObject *object;
+
+ object = g_object_new (CC_TYPE_KEYBOARD_ITEM,
+ "type", type,
+ NULL);
+
+ return CC_KEYBOARD_ITEM (object);
+}
+
+static guint *
+get_above_tab_keysyms (void)
+{
+ guint keycode = 0x29 /* KEY_GRAVE */ + 8;
+ g_autofree guint *keyvals = NULL;
+ GArray *keysyms;
+ int n_entries, i, j;
+
+ keysyms = g_array_new (TRUE, FALSE, sizeof (guint));
+
+ if (!gdk_display_map_keycode (gdk_display_get_default (),
+ keycode,
+ NULL,
+ &keyvals,
+ &n_entries))
+ goto out;
+
+ for (i = 0; i < n_entries; i++)
+ {
+ gboolean found = FALSE;
+
+ for (j = 0; j < keysyms->len; j++)
+ if (g_array_index (keysyms, guint, j) == keyvals[i])
+ {
+ found = TRUE;
+ break;
+ }
+
+ if (!found)
+ g_array_append_val (keysyms, keyvals[i]);
+ }
+
+out:
+ return (guint *)g_array_free (keysyms, FALSE);
+}
+
+/*
+ * translate_above_tab:
+ *
+ * @original_bindings: A list of accelerator strings
+ * @new_bindings: (out): Translated bindings if translation is needed
+ *
+ * Translate accelerator strings that contain the Above_Tab fake keysym
+ * used by mutter to strings that use the real keysyms that correspond
+ * to the key that is located physically above the tab key.
+ *
+ * Returns: %TRUE if strings were translated, %FALSE if @original_bindings
+ * can be used unmodified
+ */
+static gboolean
+translate_above_tab (char **original_bindings,
+ char ***new_bindings)
+{
+ GPtrArray *replaced_bindings;
+ g_autofree guint *above_tab_keysyms = NULL;
+ gboolean needs_translation = FALSE;
+ char **str;
+
+ for (str = original_bindings; *str && !needs_translation; str++)
+ needs_translation = strstr (*str, "Above_Tab") != NULL;
+
+ if (!needs_translation)
+ return FALSE;
+
+ above_tab_keysyms = get_above_tab_keysyms ();
+
+ replaced_bindings = g_ptr_array_new ();
+
+ for (str = original_bindings; *str; str++)
+ {
+ if (strstr (*str, "Above_Tab") == NULL)
+ {
+ g_ptr_array_add (replaced_bindings, g_strdup (*str));
+ }
+ else
+ {
+ g_auto (GStrv) split_str = g_strsplit (*str, "Above_Tab", -1);
+ int i;
+
+ for (i = 0; above_tab_keysyms[i]; i++)
+ {
+ g_autofree char *sym = NULL;
+
+ sym = gtk_accelerator_name (above_tab_keysyms[i], 0);
+ g_ptr_array_add (replaced_bindings, g_strjoinv (sym, split_str));
+ }
+ }
+ g_ptr_array_add (replaced_bindings, NULL);
+ }
+
+ *new_bindings = (char **)g_ptr_array_free (replaced_bindings, FALSE);
+ return TRUE;
+}
+
+static GList *
+variant_get_key_combos (GVariant *variant)
+{
+ GList *combos = NULL;
+ char **translated_bindings, **str;
+ g_auto(GStrv) bindings = NULL;
+
+ if (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING))
+ {
+ bindings = g_malloc0_n (2, sizeof(char *));
+ if (g_strcmp0 (g_variant_get_string (variant, NULL), "") != 0)
+ bindings[0] = g_variant_dup_string (variant, NULL);
+ }
+ else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING_ARRAY))
+ {
+ bindings = g_variant_dup_strv (variant, NULL);
+ }
+
+ if (translate_above_tab (bindings, &translated_bindings))
+ {
+ g_strfreev (bindings);
+ bindings = translated_bindings;
+ }
+
+ for (str = bindings; *str; str++)
+ {
+ g_autofree CcKeyCombo *combo = g_new (CcKeyCombo, 1);
+
+ binding_from_string (*str, combo);
+
+ if (combo->keyval != 0 || combo->keycode != 0 || combo->mask != 0)
+ combos = g_list_prepend (combos, g_steal_pointer (&combo));
+ }
+
+ return g_list_reverse (combos);
+}
+
+static GList *
+settings_get_key_combos (GSettings *settings,
+ const char *key,
+ gboolean use_default)
+{
+ GList *key_combos;
+ g_autoptr(GVariant) variant = NULL;
+
+ if (use_default)
+ variant = g_settings_get_default_value (settings, key);
+ else
+ variant = g_settings_get_value (settings, key);
+ key_combos = variant_get_key_combos (variant);
+
+ return key_combos;
+}
+
+static void
+binding_changed (CcKeyboardItem *item,
+ const char *key)
+{
+ g_list_free_full (item->key_combos, g_free);
+ item->key_combos = settings_get_key_combos (item->settings, item->key, FALSE);
+
+ item->editable = g_settings_is_writable (item->settings, item->key);
+
+ g_object_notify (G_OBJECT (item), "key-combos");
+}
+
+gboolean
+cc_keyboard_item_load_from_gsettings_path (CcKeyboardItem *item,
+ const char *path,
+ gboolean reset)
+{
+ g_autoptr(GVariant) variant = NULL;
+
+ item->schema = g_strdup (CUSTOM_KEYS_SCHEMA);
+ item->gsettings_path = g_strdup (path);
+ item->key = g_strdup ("binding");
+ item->settings = g_settings_new_with_path (item->schema, path);
+ item->editable = g_settings_is_writable (item->settings, item->key);
+ item->desc_editable = g_settings_is_writable (item->settings, "name");
+ item->cmd_editable = g_settings_is_writable (item->settings, "command");
+
+ variant = g_settings_get_value (item->settings, item->key);
+ item->can_set_multiple = g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING_ARRAY);
+
+ if (reset)
+ {
+ g_settings_reset (item->settings, "name");
+ g_settings_reset (item->settings, "command");
+ g_settings_reset (item->settings, "binding");
+ }
+
+ g_settings_bind (item->settings, "name",
+ G_OBJECT (item), "description", G_SETTINGS_BIND_DEFAULT);
+ g_settings_bind (item->settings, "command",
+ G_OBJECT (item), "command", G_SETTINGS_BIND_DEFAULT);
+
+ g_list_free_full (item->key_combos, g_free);
+ item->key_combos = settings_get_key_combos (item->settings, item->key, FALSE);
+
+ g_signal_connect_object (G_OBJECT (item->settings), "changed::binding",
+ G_CALLBACK (binding_changed), item, G_CONNECT_SWAPPED);
+
+ return TRUE;
+}
+
+gboolean
+cc_keyboard_item_load_from_gsettings (CcKeyboardItem *item,
+ const char *description,
+ const char *schema,
+ const char *key)
+{
+ g_autofree char *signal_name = NULL;
+ g_autoptr(GVariant) variant = NULL;
+
+ item->schema = g_strdup (schema);
+ item->key = g_strdup (key);
+ item->description = g_strdup (description);
+
+ item->settings = g_settings_new (item->schema);
+ item->editable = g_settings_is_writable (item->settings, item->key);
+
+ g_list_free_full (item->key_combos, g_free);
+ item->key_combos = settings_get_key_combos (item->settings, item->key, FALSE);
+
+ g_list_free_full (item->default_combos, g_free);
+ item->default_combos = settings_get_key_combos (item->settings, item->key, TRUE);
+
+ variant = g_settings_get_value (item->settings, item->key);
+ item->can_set_multiple = g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING_ARRAY);
+
+ signal_name = g_strdup_printf ("changed::%s", item->key);
+ g_signal_connect_object (G_OBJECT (item->settings), signal_name,
+ G_CALLBACK (binding_changed), item, G_CONNECT_SWAPPED);
+
+ return TRUE;
+}
+
+gboolean
+cc_keyboard_item_equal (CcKeyboardItem *a,
+ CcKeyboardItem *b)
+{
+ if (a->type != b->type)
+ return FALSE;
+ switch (a->type)
+ {
+ case CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH:
+ return g_str_equal (a->gsettings_path, b->gsettings_path);
+ case CC_KEYBOARD_ITEM_TYPE_GSETTINGS:
+ return (g_str_equal (a->schema, b->schema) &&
+ g_str_equal (a->key, b->key));
+ default:
+ g_assert_not_reached ();
+ }
+
+}
+
+void
+cc_keyboard_item_add_reverse_item (CcKeyboardItem *item,
+ CcKeyboardItem *reverse_item,
+ gboolean is_reversed)
+{
+ g_return_if_fail (item->key != NULL);
+
+ item->reverse_item = reverse_item;
+ if (reverse_item->reverse_item == NULL)
+ {
+ reverse_item->reverse_item = item;
+ reverse_item->is_reversed = !is_reversed;
+ }
+ else
+ g_warn_if_fail (reverse_item->is_reversed == !!is_reversed);
+
+ item->is_reversed = !!is_reversed;
+}
+
+CcKeyboardItem *
+cc_keyboard_item_get_reverse_item (CcKeyboardItem *item)
+{
+ return item->reverse_item;
+}
+
+
+void
+cc_keyboard_item_set_hidden (CcKeyboardItem *item, gboolean hidden)
+{
+ item->hidden = !!hidden;
+}
+
+
+gboolean
+cc_keyboard_item_is_hidden (CcKeyboardItem *item)
+{
+ return item->hidden;
+}
+
+/**
+ * cc_keyboard_item_is_value_default:
+ * @self: a #CcKeyboardItem
+ *
+ * Retrieves whether the shortcut is the default value or not.
+ *
+ * Returns: %TRUE if the shortcut is the default value, %FALSE otherwise.
+ */
+gboolean
+cc_keyboard_item_is_value_default (CcKeyboardItem *self)
+{
+ g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (self), FALSE);
+
+ /*
+ * When the shortcut is custom, we don't treat it as modified
+ * since we don't know what would be its default value.
+ */
+ if (self->type == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH)
+ return TRUE;
+
+ return combos_equal (self->default_combos, self->key_combos);
+}
+
+/**
+ * cc_keyboard_item_reset:
+ * @self: a #CcKeyboardItem
+ *
+ * Reset the keyboard binding to the default value.
+ */
+void
+cc_keyboard_item_reset (CcKeyboardItem *self)
+{
+ CcKeyboardItem *reverse;
+
+ g_return_if_fail (CC_IS_KEYBOARD_ITEM (self));
+
+ reverse = self->reverse_item;
+
+ g_settings_reset (self->settings, self->key);
+ g_object_notify (G_OBJECT (self), "is-value-default");
+
+ /* Also reset the reverse item */
+ if (reverse)
+ {
+ g_settings_reset (reverse->settings, reverse->key);
+ g_object_notify (G_OBJECT (reverse), "is-value-default");
+ }
+}
+
+GList *
+cc_keyboard_item_get_key_combos (CcKeyboardItem *item)
+{
+ g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), NULL);
+ return item->key_combos;
+}
+
+GList *
+cc_keyboard_item_get_default_combos (CcKeyboardItem *item)
+{
+ g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), NULL);
+ return item->default_combos;
+}
+
+CcKeyCombo
+cc_keyboard_item_get_primary_combo (CcKeyboardItem *item)
+{
+ g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), EMPTY_COMBO);
+ return (item->key_combos) ? *((CcKeyCombo*)item->key_combos->data) : EMPTY_COMBO;
+}
+
+const gchar *
+cc_keyboard_item_get_key (CcKeyboardItem *item)
+{
+ g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), NULL);
+ return item->key;
+}
+
+CcKeyboardItemType
+cc_keyboard_item_get_item_type (CcKeyboardItem *item)
+{
+ g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), CC_KEYBOARD_ITEM_TYPE_NONE);
+ return item->type;
+}
+
+const gchar *
+cc_keyboard_item_get_gsettings_path (CcKeyboardItem *item)
+{
+ g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), NULL);
+ return item->gsettings_path;
+}
+
+GSettings *
+cc_keyboard_item_get_settings (CcKeyboardItem *item)
+{
+ g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), NULL);
+ return item->settings;
+}
+
+gboolean
+cc_keyboard_item_can_set_multiple (CcKeyboardItem *item)
+{
+ return item->can_set_multiple;
+}
+
+static gchar*
+combo_get_accelerator (CcKeyCombo *combo)
+{
+ return gtk_accelerator_name_with_keycode (NULL,
+ combo->keyval,
+ combo->keycode,
+ combo->mask);
+}
+
+static void
+cc_keyboard_item_add_key_combo_inner (CcKeyboardItem *self,
+ CcKeyCombo *combo)
+{
+ g_auto(GStrv) strv = NULL;
+ int i;
+
+ if (!self->can_set_multiple)
+ {
+ g_settings_set_string (self->settings, self->key, combo_get_accelerator (combo));
+ }
+ else
+ {
+ strv = g_new0 (gchar*, g_list_length (self->key_combos) + 2);
+
+ i = 0;
+ for (GList *l = self->key_combos; l != NULL; l = l->next, i++)
+ {
+ if (combo_equal (l->data, combo))
+ // This combo is already in the list
+ return;
+ strv[i] = combo_get_accelerator (l->data);
+ }
+ strv[i] = combo_get_accelerator (combo);
+
+ g_settings_set_strv (self->settings, self->key, (const gchar **)strv);
+ }
+
+ binding_changed (self, self->key);
+}
+
+void
+cc_keyboard_item_add_key_combo (CcKeyboardItem *self,
+ CcKeyCombo *combo)
+{
+ CcKeyCombo reverse_combo;
+
+ if (self->reverse_item)
+ {
+ reverse_combo.keyval = combo->keyval;
+ reverse_combo.keycode = combo->keycode;
+ reverse_combo.mask = combo->mask ^ GDK_SHIFT_MASK;
+ cc_keyboard_item_add_key_combo_inner (self->reverse_item, &reverse_combo);
+ }
+
+ cc_keyboard_item_add_key_combo_inner (self, combo);
+}
+
+static void
+cc_keyboard_item_remove_key_combo_inner (CcKeyboardItem *self,
+ CcKeyCombo *combo)
+{
+ g_auto(GStrv) strv = NULL;
+ gboolean found;
+ int i;
+
+ strv = g_new0 (gchar*, g_list_length (self->key_combos) + 1);
+
+ found = FALSE;
+ i = 0;
+ for (GList *l = self->key_combos; l != NULL; l = l->next, i++)
+ {
+ if (combo_equal (l->data, combo))
+ {
+ i--;
+ found = TRUE;
+ }
+ else
+ {
+ strv[i] = combo_get_accelerator (l->data);
+ }
+ }
+
+ if (found)
+ {
+ if (self->can_set_multiple)
+ g_settings_set_strv (self->settings, self->key, (const gchar **)strv);
+ else
+ g_settings_set_string (self->settings, self->key, "");
+ }
+
+ binding_changed (self, self->key);
+}
+
+void
+cc_keyboard_item_remove_key_combo (CcKeyboardItem *self,
+ CcKeyCombo *combo)
+{
+ CcKeyCombo reverse_combo;
+
+ if (self->reverse_item)
+ {
+ reverse_combo.keyval = combo->keyval;
+ reverse_combo.keycode = combo->keycode;
+ reverse_combo.mask = combo->mask ^ GDK_SHIFT_MASK;
+ cc_keyboard_item_remove_key_combo_inner (self->reverse_item, &reverse_combo);
+ }
+
+ cc_keyboard_item_remove_key_combo_inner (self, combo);
+}
+
+void cc_keyboard_item_disable (CcKeyboardItem *self)
+{
+ if (!self->can_set_multiple)
+ {
+ g_settings_set_string (self->settings, self->key, "");
+ if (self->reverse_item)
+ g_settings_set_string (self->reverse_item->settings, self->reverse_item->key, "");
+ }
+ else
+ {
+ g_settings_set_strv (self->settings, self->key, NULL);
+ if (self->reverse_item)
+ g_settings_set_strv (self->reverse_item->settings, self->reverse_item->key, NULL);
+ }
+
+ binding_changed (self, self->key);
+}