summaryrefslogtreecommitdiff
path: root/panels
diff options
context:
space:
mode:
Diffstat (limited to 'panels')
-rw-r--r--panels/keyboard/cc-keyboard-manager.c122
-rw-r--r--panels/keyboard/cc-keyboard-manager.h9
-rw-r--r--panels/keyboard/cc-keyboard-shortcut-editor.c93
-rw-r--r--panels/keyboard/shortcut-editor.ui13
4 files changed, 237 insertions, 0 deletions
diff --git a/panels/keyboard/cc-keyboard-manager.c b/panels/keyboard/cc-keyboard-manager.c
index a98baeb8f..a9809a91c 100644
--- a/panels/keyboard/cc-keyboard-manager.c
+++ b/panels/keyboard/cc-keyboard-manager.c
@@ -84,6 +84,62 @@ free_key_array (GPtrArray *keys)
}
}
+static gboolean
+compare_keys_for_uniqueness (CcKeyboardItem *current_item,
+ CcUniquenessData *data)
+{
+ CcKeyboardItem *item;
+
+ item = data->orig_item;
+
+ /* No conflict for: blanks, different modifiers or ourselves */
+ if (!current_item ||
+ item == current_item ||
+ data->new_mask != current_item->mask)
+ {
+ return FALSE;
+ }
+
+ if (item && cc_keyboard_item_equal (item, current_item))
+ goto out;
+
+ if (data->new_keyval != 0)
+ {
+ if (data->new_keyval != current_item->keyval)
+ return FALSE;
+ }
+ else if (current_item->keyval != 0 || data->new_keycode != current_item->keycode)
+ {
+ return FALSE;
+ }
+
+out:
+ data->conflict_item = current_item;
+
+ return TRUE;
+}
+
+static gboolean
+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 GHashTable*
get_hash_for_group (CcKeyboardManager *self,
BindingGroupType group)
@@ -815,3 +871,69 @@ cc_keyboard_manager_remove_custom_shortcut (CcKeyboardManager *self,
g_signal_emit (self, signals[SHORTCUT_REMOVED], 0, item);
}
+
+/**
+ * cc_keyboard_manager_get_collision:
+ * @self: a #CcKeyboardManager
+ * @item: (nullable): a keyboard shortcut
+ * @keyval: the key value
+ * @mask: a mask for the key sequence
+ * @keycode: the code of the key.
+ *
+ * Retrieves the collision item for the given shortcut.
+ *
+ * Returns: (transfer none)(nullable): the collisioned shortcut
+ */
+CcKeyboardItem*
+cc_keyboard_manager_get_collision (CcKeyboardManager *self,
+ CcKeyboardItem *item,
+ gint keyval,
+ GdkModifierType mask,
+ gint keycode)
+{
+ CcUniquenessData data;
+
+ g_return_val_if_fail (CC_IS_KEYBOARD_MANAGER (self), NULL);
+
+ data.orig_item = item;
+ data.new_keyval = keyval;
+ data.new_mask = mask;
+ data.new_keycode = keycode;
+ data.conflict_item = NULL;
+
+ /* Any number of shortcuts can be disabled */
+ if (keyval != 0 || keycode != 0)
+ {
+ BindingGroupType i;
+
+ for (i = BINDING_GROUP_SYSTEM; i <= BINDING_GROUP_USER && !data.conflict_item; i++)
+ {
+ GHashTable *table;
+
+ table = get_hash_for_group (self, i);
+
+ if (!table)
+ continue;
+
+ g_hash_table_find (table, (GHRFunc) check_for_uniqueness, &data);
+ }
+ }
+
+ return data.conflict_item;
+}
+
+/**
+ * cc_keyboard_manager_disable_shortcut:
+ * @self: a #CcKeyboardManager
+ * @item: a @CcKeyboardItem
+ *
+ * Disables the given keyboard shortcut.
+ */
+void
+cc_keyboard_manager_disable_shortcut (CcKeyboardManager *self,
+ CcKeyboardItem *item)
+{
+ g_return_if_fail (CC_IS_KEYBOARD_MANAGER (self));
+
+ g_object_set (item, "binding", NULL, NULL);
+}
diff --git a/panels/keyboard/cc-keyboard-manager.h b/panels/keyboard/cc-keyboard-manager.h
index ab14d4a4a..b63ffa582 100644
--- a/panels/keyboard/cc-keyboard-manager.h
+++ b/panels/keyboard/cc-keyboard-manager.h
@@ -45,6 +45,15 @@ void cc_keyboard_manager_add_custom_shortcut (CcKeyboardMana
void cc_keyboard_manager_remove_custom_shortcut (CcKeyboardManager *self,
CcKeyboardItem *item);
+CcKeyboardItem* cc_keyboard_manager_get_collision (CcKeyboardManager *self,
+ CcKeyboardItem *item,
+ gint keyval,
+ GdkModifierType mask,
+ gint keycode);
+
+void cc_keyboard_manager_disable_shortcut (CcKeyboardManager *self,
+ CcKeyboardItem *item);
+
G_END_DECLS
#endif /* CC_KEYBOARD_MANAGER_H */
diff --git a/panels/keyboard/cc-keyboard-shortcut-editor.c b/panels/keyboard/cc-keyboard-shortcut-editor.c
index eef7a2872..962eb8681 100644
--- a/panels/keyboard/cc-keyboard-shortcut-editor.c
+++ b/panels/keyboard/cc-keyboard-shortcut-editor.c
@@ -40,9 +40,11 @@ struct _CcKeyboardShortcutEditor
GtkWidget *edit_button;
GtkWidget *headerbar;
GtkWidget *name_entry;
+ GtkWidget *new_shortcut_conflict_label;
GtkWidget *remove_button;
GtkWidget *replace_button;
GtkWidget *shortcut_accel_label;
+ GtkWidget *shortcut_conflict_label;
GtkWidget *stack;
GtkWidget *top_info_label;
@@ -53,6 +55,8 @@ struct _CcKeyboardShortcutEditor
CcKeyboardManager *manager;
CcKeyboardItem *item;
+ CcKeyboardItem *collision_item;
+
/* Custom shortcuts */
GdkDevice *grab_pointer;
@@ -122,6 +126,8 @@ clear_custom_entries (CcKeyboardShortcutEditor *self)
gtk_entry_set_text (GTK_ENTRY (self->command_entry), "");
gtk_shortcut_label_set_accelerator (GTK_SHORTCUT_LABEL (self->custom_shortcut_accel_label), "");
+ gtk_label_set_label (GTK_LABEL (self->new_shortcut_conflict_label), "");
+ gtk_label_set_label (GTK_LABEL (self->shortcut_conflict_label), "");
self->custom_keycode = 0;
self->custom_keyval = 0;
@@ -129,6 +135,8 @@ clear_custom_entries (CcKeyboardShortcutEditor *self)
self->custom_is_modifier = TRUE;
self->edited = FALSE;
+ self->collision_item = NULL;
+
g_signal_handlers_unblock_by_func (self->command_entry, command_entry_changed_cb, self);
g_signal_handlers_unblock_by_func (self->name_entry, name_entry_changed_cb, self);
}
@@ -200,6 +208,10 @@ update_shortcut (CcKeyboardShortcutEditor *self)
/* Setup the binding */
apply_custom_item_fields (self, self->item);
+ /* Eventually disable the conflict shortcut */
+ if (self->collision_item)
+ cc_keyboard_manager_disable_shortcut (self->manager, self->collision_item);
+
/* Cleanup whatever was set before */
clear_custom_entries (self);
@@ -216,9 +228,20 @@ get_current_shortcut_label (CcKeyboardShortcutEditor *self)
}
static void
+set_collision_headerbar (CcKeyboardShortcutEditor *self,
+ gboolean has_collision)
+{
+ gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (self->headerbar), !has_collision);
+
+ gtk_widget_set_visible (self->cancel_button, has_collision);
+ gtk_widget_set_visible (self->replace_button, has_collision);
+}
+
+static void
setup_custom_shortcut (CcKeyboardShortcutEditor *self)
{
GtkShortcutLabel *shortcut_label;
+ CcKeyboardItem *collision_item;
gboolean valid;
gchar *accel;
@@ -240,8 +263,16 @@ setup_custom_shortcut (CcKeyboardShortcutEditor *self)
return;
shortcut_label = get_current_shortcut_label (self);
+
+ collision_item = cc_keyboard_manager_get_collision (self->manager,
+ self->item,
+ self->custom_keyval,
+ self->custom_mask,
+ self->custom_keycode);
+
accel = gtk_accelerator_name (self->custom_keyval, self->custom_mask);
+
/* Setup the accelerator label */
gtk_shortcut_label_set_accelerator (shortcut_label, accel);
@@ -255,6 +286,48 @@ setup_custom_shortcut (CcKeyboardShortcutEditor *self)
release_grab (self);
+ /*
+ * Oops! Looks like the accelerator is already being used, so we
+ * must warn the user and let it be very clear that adding this
+ * shortcut will disable the other.
+ */
+ gtk_widget_set_visible (self->new_shortcut_conflict_label, collision_item != NULL);
+
+ if (collision_item)
+ {
+ GtkWidget *label;
+ gchar *friendly_accelerator;
+ gchar *collision_text;
+
+ friendly_accelerator = convert_keysym_state_to_string (self->custom_keyval,
+ self->custom_mask,
+ self->custom_keycode);
+
+ collision_text = g_strdup_printf (_("%s is already being used for <b>%s</b>. If you "
+ "replace it, %s will be disabled"),
+ friendly_accelerator,
+ collision_item->description,
+ collision_item->description);
+
+ label = is_custom_shortcut (self) ? self->new_shortcut_conflict_label : self->shortcut_conflict_label;
+
+ gtk_label_set_markup (GTK_LABEL (label), collision_text);
+
+ g_free (friendly_accelerator);
+ g_free (collision_text);
+ }
+
+ /*
+ * When there is a collision between the current shortcut and another shortcut,
+ * and we're editing an existing shortcut (rather than creating a new one), setup
+ * the headerbar to display "Cancel" and "Replace". Otherwise, make sure to set
+ * only the close button again.
+ */
+ if (self->mode == CC_SHORTCUT_EDITOR_EDIT)
+ set_collision_headerbar (self, collision_item != NULL);
+
+ self->collision_item = collision_item;
+
g_free (accel);
}
@@ -268,6 +341,10 @@ add_button_clicked_cb (CcKeyboardShortcutEditor *self)
/* Apply the custom shortcut setup at the new item */
apply_custom_item_fields (self, item);
+ /* Eventually disable the conflict shortcut */
+ if (self->collision_item)
+ cc_keyboard_manager_disable_shortcut (self->manager, self->collision_item);
+
/* Cleanup everything once we're done */
clear_custom_entries (self);
@@ -318,6 +395,14 @@ remove_button_clicked_cb (CcKeyboardShortcutEditor *self)
}
static void
+replace_button_clicked_cb (CcKeyboardShortcutEditor *self)
+{
+ update_shortcut (self);
+
+ gtk_widget_hide (GTK_WIDGET (self));
+}
+
+static void
setup_keyboard_item (CcKeyboardShortcutEditor *self,
CcKeyboardItem *item)
{
@@ -572,9 +657,11 @@ cc_keyboard_shortcut_editor_class_init (CcKeyboardShortcutEditorClass *klass)
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, edit_button);
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, headerbar);
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, name_entry);
+ gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, new_shortcut_conflict_label);
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, remove_button);
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, replace_button);
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, shortcut_accel_label);
+ gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, shortcut_conflict_label);
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, stack);
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, top_info_label);
@@ -584,6 +671,7 @@ cc_keyboard_shortcut_editor_class_init (CcKeyboardShortcutEditorClass *klass)
gtk_widget_class_bind_template_callback (widget_class, edit_custom_shortcut_button_toggled_cb);
gtk_widget_class_bind_template_callback (widget_class, name_entry_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, remove_button_clicked_cb);
+ gtk_widget_class_bind_template_callback (widget_class, replace_button_clicked_cb);
}
static void
@@ -660,12 +748,17 @@ void
cc_keyboard_shortcut_editor_set_mode (CcKeyboardShortcutEditor *self,
CcShortcutEditorMode mode)
{
+ gboolean is_create_mode;
+
g_return_if_fail (CC_IS_KEYBOARD_SHORTCUT_EDITOR (self));
if (self->mode == mode)
return;
self->mode = mode;
+ is_create_mode = mode == CC_SHORTCUT_EDITOR_CREATE;
+
+ gtk_widget_set_visible (self->new_shortcut_conflict_label, is_create_mode);
if (mode == CC_SHORTCUT_EDITOR_CREATE)
{
diff --git a/panels/keyboard/shortcut-editor.ui b/panels/keyboard/shortcut-editor.ui
index 07b7cabac..65dc2e370 100644
--- a/panels/keyboard/shortcut-editor.ui
+++ b/panels/keyboard/shortcut-editor.ui
@@ -46,6 +46,18 @@
</object>
</child>
<child>
+ <object class="GtkLabel" id="shortcut_conflict_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="wrap">True</property>
+ <property name="wrap_mode">word-char</property>
+ <property name="width_chars">15</property>
+ <property name="max_width_chars">20</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ <child>
<object class="GtkButton" id="reset_button">
<property name="label" translatable="yes">Reset</property>
<property name="visible">True</property>
@@ -242,6 +254,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
+ <signal name="clicked" handler="replace_button_clicked_cb" object="CcKeyboardShortcutEditor" swapped="yes" />
</object>
<packing>
<property name="pack_type">end</property>