This commit includes all the changes that seem to be necessary for CcKeyboardItem to be used for dealing with multiple keybindings, without (yet) changing the user interface to expose this. The `primary_combo` and `binding` fields of `CcKeyboardItem` are removed, in favor of the existing `key_combos`. No combination is "primary", since all of them can now be seen and changed equally. We treat `CcKeyboardItem.key_combos` as a set, that a combo can be added to or removed from. Though it continues to be represented as a `GList`, instead of a `GHashTable`, to preserve ordering. A lot of the keyboard panel code relied on the assumption that only one combo can be set for each setting, so this required a variety of miscellaneous changes.
874 lines
23 KiB
C
874 lines
23 KiB
C
/* -*- 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, &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)
|
|
{
|
|
GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_display_get_default ());
|
|
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_keymap_get_entries_for_keycode (keymap, 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++)
|
|
{
|
|
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, 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);
|
|
}
|