diff --git a/gtk/gtkshortcuttrigger.c b/gtk/gtkshortcuttrigger.c index 828a8486d2..15ec8ebd91 100644 --- a/gtk/gtkshortcuttrigger.c +++ b/gtk/gtkshortcuttrigger.c @@ -40,6 +40,7 @@ #include "gtkshortcuttrigger.h" #include "gtkaccelgroupprivate.h" +#include "gtkprivate.h" typedef struct _GtkShortcutTriggerClass GtkShortcutTriggerClass; @@ -499,25 +500,99 @@ gtk_keyval_trigger_trigger (GtkShortcutTrigger *trigger, gboolean enable_mnemonics) { GtkKeyvalTrigger *self = (GtkKeyvalTrigger *) trigger; - GdkModifierType modifiers; + guint keycode; + GdkModifierType state; + GdkModifierType mask; + int group; + GdkKeymap *keymap; guint keyval; + int effective_group; + int level; + GdkModifierType consumed_modifiers; + GdkModifierType shift_group_mask; + gboolean group_mod_is_accel_mod = FALSE; + const GdkModifierType xmods = GDK_MOD2_MASK|GDK_MOD3_MASK|GDK_MOD4_MASK|GDK_MOD5_MASK; + const GdkModifierType vmods = GDK_SUPER_MASK|GDK_HYPER_MASK|GDK_META_MASK; + GdkModifierType modifiers; if (gdk_event_get_event_type (event) != GDK_KEY_PRESS) return GTK_SHORTCUT_TRIGGER_MATCH_NONE; - /* XXX: This needs to deal with groups */ - modifiers = gdk_event_get_modifier_state (event); - keyval = gdk_key_event_get_keyval (event); + mask = gtk_accelerator_get_default_mod_mask (); - if (keyval == GDK_KEY_ISO_Left_Tab) - keyval = GDK_KEY_Tab; - else - keyval = gdk_keyval_to_lower (keyval); + keycode = gdk_key_event_get_keycode (event); + state = gdk_event_get_modifier_state (event); + group = gdk_key_event_get_group (event); + keymap = gdk_display_get_keymap (gdk_event_get_display (event)); - if (keyval != self->keyval || modifiers != self->modifiers) - return GTK_SHORTCUT_TRIGGER_MATCH_NONE; + /* We don't want Caps_Lock to affect keybinding lookups. + */ + state &= ~GDK_LOCK_MASK; - return GTK_SHORTCUT_TRIGGER_MATCH_EXACT; + _gtk_translate_keyboard_accel_state (keymap, + keycode, state, mask, group, + &keyval, + &effective_group, &level, + &consumed_modifiers); + + /* if the group-toggling modifier is part of the default accel mod + * mask, and it is active, disable it for matching + */ + shift_group_mask = gdk_keymap_get_modifier_mask (keymap, + GDK_MODIFIER_INTENT_SHIFT_GROUP); + if (mask & shift_group_mask) + group_mod_is_accel_mod = TRUE; + + gdk_keymap_map_virtual_modifiers (keymap, &mask); + gdk_keymap_add_virtual_modifiers (keymap, &state); + + modifiers = self->modifiers; + if (gdk_keymap_map_virtual_modifiers (keymap, &modifiers) && + ((modifiers & ~consumed_modifiers & mask & ~vmods) == (state & ~consumed_modifiers & mask & ~vmods) || + (modifiers & ~consumed_modifiers & mask & ~xmods) == (state & ~consumed_modifiers & mask & ~xmods))) + { + /* modifier match */ + GdkKeymapKey *keys; + int n_keys; + int i; + guint key; + + /* Shift gets consumed and applied for the event, + * so apply it to our keyval to match + */ + key = self->keyval; + if (self->modifiers & GDK_SHIFT_MASK) + key = gdk_keyval_to_upper (key); + + if (keyval == key && /* exact match */ + (!group_mod_is_accel_mod || + (state & shift_group_mask) == (self->modifiers & shift_group_mask))) + return GTK_SHORTCUT_TRIGGER_MATCH_EXACT; + + gdk_keymap_get_entries_for_keyval (keymap, + self->keyval, + &keys, &n_keys); + + for (i = 0; i < n_keys; i++) + { + if (keys[i].keycode == keycode && + keys[i].level == level && + /* Only match for group if it's an accel mod */ + (!group_mod_is_accel_mod || + keys[i].group == effective_group)) + { + /* partial match */ + g_free (keys); + + return GTK_SHORTCUT_TRIGGER_MATCH_PARTIAL; + } + } + + g_free (keys); + } + + + return GTK_SHORTCUT_TRIGGER_MATCH_NONE; } static guint diff --git a/testsuite/gtk/shortcuts.c b/testsuite/gtk/shortcuts.c index e06b1814e9..ebf47ae21d 100644 --- a/testsuite/gtk/shortcuts.c +++ b/testsuite/gtk/shortcuts.c @@ -186,7 +186,7 @@ test_trigger_parse (void) static void test_trigger_trigger (void) { - GtkShortcutTrigger *trigger1, *trigger2, *trigger3, *trigger4; + GtkShortcutTrigger *trigger[4]; GdkDisplay *display; GdkSurface *surface; GdkDevice *device; @@ -204,13 +204,13 @@ test_trigger_trigger (void) { GDK_KEY_u, GDK_SHIFT_MASK, FALSE, { GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_NONE } }, { GDK_KEY_u, GDK_SHIFT_MASK, TRUE, { GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_EXACT, GTK_SHORTCUT_TRIGGER_MATCH_EXACT } }, }; - int i; + int i, j; - trigger1 = gtk_shortcut_trigger_ref (gtk_never_trigger_get ()); - trigger2 = gtk_keyval_trigger_new (GDK_KEY_a, GDK_CONTROL_MASK); - trigger3 = gtk_mnemonic_trigger_new (GDK_KEY_u); - trigger4 = gtk_alternative_trigger_new (gtk_shortcut_trigger_ref (trigger2), - gtk_shortcut_trigger_ref (trigger3)); + trigger[0] = gtk_shortcut_trigger_ref (gtk_never_trigger_get ()); + trigger[1] = gtk_keyval_trigger_new (GDK_KEY_a, GDK_CONTROL_MASK); + trigger[2] = gtk_mnemonic_trigger_new (GDK_KEY_u); + trigger[3] = gtk_alternative_trigger_new (gtk_shortcut_trigger_ref (trigger[1]), + gtk_shortcut_trigger_ref (trigger[2])); display = gdk_display_get_default (); device = gdk_seat_get_keyboard (gdk_display_get_default_seat (display)); @@ -218,6 +218,13 @@ test_trigger_trigger (void) for (i = 0; i < G_N_ELEMENTS (tests); i++) { + GdkKeymapKey *keys; + int n_keys; + + if (!gdk_keymap_get_entries_for_keyval (gdk_display_get_keymap (display), + tests[i].keyval, &keys, &n_keys)) + continue; + event = gdk_event_key_new (GDK_KEY_PRESS, surface, device, @@ -225,24 +232,22 @@ test_trigger_trigger (void) GDK_CURRENT_TIME, tests[i].state, tests[i].keyval, - 0, - 0, - 0, + keys[0].keycode, + keys[0].keycode, + keys[0].group, FALSE); - g_assert_cmpint (gtk_shortcut_trigger_trigger (trigger1, event, tests[i].mnemonic), ==, tests[i].result[0]); - g_assert_cmpint (gtk_shortcut_trigger_trigger (trigger2, event, tests[i].mnemonic), ==, tests[i].result[1]); - g_assert_cmpint (gtk_shortcut_trigger_trigger (trigger3, event, tests[i].mnemonic), ==, tests[i].result[2]); - g_assert_cmpint (gtk_shortcut_trigger_trigger (trigger4, event, tests[i].mnemonic), ==, tests[i].result[3]); + for (j = 0; j < 4; j++) + g_assert_cmpint (gtk_shortcut_trigger_trigger (trigger[j], event, tests[i].mnemonic), ==, tests[i].result[j]); gdk_event_unref (event); } g_object_unref (surface); - gtk_shortcut_trigger_unref (trigger1); - gtk_shortcut_trigger_unref (trigger2); - gtk_shortcut_trigger_unref (trigger3); - gtk_shortcut_trigger_unref (trigger4); + gtk_shortcut_trigger_unref (trigger[0]); + gtk_shortcut_trigger_unref (trigger[1]); + gtk_shortcut_trigger_unref (trigger[2]); + gtk_shortcut_trigger_unref (trigger[3]); } static int callback_count;