diff --git a/src/keybindings.c b/src/keybindings.c index 77f868c239..b0c749f1a6 100644 --- a/src/keybindings.c +++ b/src/keybindings.c @@ -1293,25 +1293,40 @@ static guint key_kp_translate(guint key_in) } -/* Check if event keypress matches keybinding combo */ -gboolean keybindings_check_event(GdkEventKey *ev, GeanyKeyBinding *kb) +void keybindings_get_normalised_event(GdkEventKey *ev, guint *state, guint *keyval) { - guint state, keyval; + GdkModifierType consumed; + GdkDisplay *display = gdk_window_get_display(ev->window); + GdkKeymap *keymap = gdk_keymap_get_for_display(display); - if (ev->keyval == 0) - return FALSE; + gdk_keymap_translate_keyboard_state(keymap, ev->hardware_keycode, + ev->state, ev->group, keyval, NULL, NULL, &consumed); - keyval = ev->keyval; - state = keybindings_get_modifiers(ev->state); - /* hack to get around that CTRL+Shift+r results in GDK_R not GDK_r */ - if ((ev->state & GDK_SHIFT_MASK) || (ev->state & GDK_LOCK_MASK)) - if (keyval >= GDK_A && keyval <= GDK_Z) - keyval += GDK_a - GDK_A; + /* Keys such as caps lock are always reported as consumed even when they + * are not pressed - AND with the original state to get the actually + * consumed events */ + consumed &= ev->state; - if (keyval >= GDK_KP_Space && keyval < GDK_KP_Equal) - keyval = key_kp_translate(keyval); + /* Don't consume modifiers when no other key is set */ + if (*keyval == GDK_VoidSymbol) + consumed = 0; - return (keyval == kb->key && state == kb->mods); + *state = keybindings_get_modifiers(ev->state) & ~consumed; + + /* We want to see Ctrl+Shift+r instead of Ctrl+R. When shift is consumed + * and keyval is uppercase, unconsume the shift. Also handle the case when + * both caps lock and shift are pressed which makes keyval lowercase. */ + if ((consumed & GDK_SHIFT_MASK && gdk_keyval_to_lower(*keyval) != *keyval) || + (consumed & GDK_SHIFT_MASK && consumed & GDK_LOCK_MASK && + gdk_keyval_to_upper(*keyval) != *keyval)) + { + *state |= GDK_SHIFT_MASK; + } + if (consumed & (GDK_SHIFT_MASK|GDK_LOCK_MASK)) + *keyval = gdk_keyval_to_lower(*keyval); + + if (*keyval >= GDK_KP_Space && *keyval < GDK_KP_Equal) + *keyval = key_kp_translate(*keyval); } @@ -1342,7 +1357,7 @@ static gboolean run_kb(GeanyKeyBinding *kb, GeanyKeyGroup *group) /* central keypress event handler, almost all keypress events go to this function */ static gboolean on_key_press_event(GtkWidget *widget, GdkEventKey *ev, gpointer user_data) { - guint state, keyval; + guint state, keyval, legacy_state; gsize g, i; GeanyDocument *doc; GeanyKeyGroup *group; @@ -1355,15 +1370,10 @@ static gboolean on_key_press_event(GtkWidget *widget, GdkEventKey *ev, gpointer if (doc) document_check_disk_status(doc, FALSE); - keyval = ev->keyval; - state = keybindings_get_modifiers(ev->state); - /* hack to get around that CTRL+Shift+r results in GDK_R not GDK_r */ - if ((ev->state & GDK_SHIFT_MASK) || (ev->state & GDK_LOCK_MASK)) - if (keyval >= GDK_A && keyval <= GDK_Z) - keyval += GDK_a - GDK_A; - - if (keyval >= GDK_KP_Space && keyval < GDK_KP_Equal) - keyval = key_kp_translate(keyval); + keybindings_get_normalised_event(ev, &state, &keyval); + /* User keybindings of old Geany versions may contain extra modifiers + * such as shift - make sure these still work. */ + legacy_state = keybindings_get_modifiers(ev->state); /*geany_debug("%d (%d) %d (%d)", keyval, ev->keyval, state, ev->state);*/ @@ -1379,7 +1389,7 @@ static gboolean on_key_press_event(GtkWidget *widget, GdkEventKey *ev, gpointer { foreach_ptr_array(kb, i, group->key_items) { - if (keyval == kb->key && state == kb->mods) + if (keyval == kb->key && (state == kb->mods || legacy_state == kb->mods)) { if (run_kb(kb, group)) return TRUE; diff --git a/src/keybindings.h b/src/keybindings.h index fa4e9b11af..f75a1fdee8 100644 --- a/src/keybindings.h +++ b/src/keybindings.h @@ -322,7 +322,7 @@ void keybindings_write_to_file(void); void keybindings_show_shortcuts(void); -gboolean keybindings_check_event(GdkEventKey *ev, GeanyKeyBinding *kb); +void keybindings_get_normalised_event(GdkEventKey *ev, guint *state, guint *keyval); void keybindings_dialog_show_prefs_scroll(const gchar *name); diff --git a/src/prefs.c b/src/prefs.c index 9b60e09aef..619d7ae3b1 100644 --- a/src/prefs.c +++ b/src/prefs.c @@ -1415,16 +1415,16 @@ static void kb_cell_edited_cb(GtkCellRendererText *cellrenderertext, static gboolean kb_grab_key_dialog_key_press_cb(GtkWidget *dialog, GdkEventKey *event, GtkLabel *label) { gchar *str; - guint state; + guint state, keyval; g_return_val_if_fail(GTK_IS_LABEL(label), FALSE); - state = keybindings_get_modifiers(event->state); - if (event->keyval == GDK_Escape) return FALSE; /* close the dialog, don't allow escape when detecting keybindings. */ - str = gtk_accelerator_name(event->keyval, state); + keybindings_get_normalised_event(event, &state, &keyval); + + str = gtk_accelerator_name(keyval, state); gtk_label_set_text(label, str); g_free(str); @@ -1633,8 +1633,14 @@ static gboolean prefs_dialog_key_press_response_cb(GtkWidget *dialog, GdkEventKe gpointer data) { GeanyKeyBinding *kb = keybindings_lookup_item(GEANY_KEY_GROUP_HELP, GEANY_KEYS_HELP_HELP); + guint state, keyval; + + if (event->keyval == 0) + return FALSE; + + keybindings_get_normalised_event(event, &state, &keyval); - if (keybindings_check_event(event, kb)) + if (keyval == kb->key && state == kb->mods) { open_preferences_help(); return TRUE;