From 708e1a95749ad61cb0167f729a77e951a30388cf Mon Sep 17 00:00:00 2001 From: Owen Taylor Date: Thu, 21 Feb 2002 17:14:10 +0000 Subject: [PATCH] Implement "fuzzy" key binding lookups; allow matches on key and level but Wed Feb 20 14:26:47 2002 Owen Taylor * gtk/gtkkeyhash.[ch]: Implement "fuzzy" key binding lookups; allow matches on key and level but not group. Also, implement ignoring "consumed modifiers correctly." * gtk/gtkaccelgroup.c gtk/gtkbindings.c: Convert to using GtkKeyHash. * gtk/gtkdebug.h gtk/gtkmain.c: Support GTK_DEBUG=keybindings * gdk/x11/gdkevents-x11.c (gdk_event_translate): Fill in the group for key release events as well as key press events. * gdk/gdkkeys.h gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): Rename unused_modifiers to consumed_modifiers, make the docs and non-Xkb implementation match the Xkb implementation. * gdk/linux-fb/gdkkeyboard-fb.c gdk/win32/gdkkeys-win32.c: Propagate doc and parameter name changes. * gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): XkbTranslateKeyCode doesn't handle LockMask, we need to handle it ourselves. * gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): Force Tab to give GDK_ISO_Left_Tab, since we need consistency to allow dealing with ISO_Left_Tab. * gtk/gtkwindow.c gtk/gtktextview.c gtk/gtkscrolledwindow.c gtk/gtkpaned.c gtk/gtkcombo.c gtk/gtknotebook.c: Remove inappropriate uses of GDK_ISO_Left_Tab. (GDK_ISO_Left_Tab or Tab both are equivalent as a binding specifier.) * gtk/gtkbutton.c (gtk_button_class_init): Make ::activate GTK_RUN_ACTION, so you can bind an accelerator to it. * gtk/gtklabel.c (gtk_label_set_uline_text_internal): Call gdk_unicode_to_keyval on the mnemonic character. * tests/testgtk.c: Add a test for the new fuzzy key binding matching. --- ChangeLog | 42 ++++ ChangeLog.pre-2-0 | 42 ++++ ChangeLog.pre-2-10 | 42 ++++ ChangeLog.pre-2-2 | 42 ++++ ChangeLog.pre-2-4 | 42 ++++ ChangeLog.pre-2-6 | 42 ++++ ChangeLog.pre-2-8 | 42 ++++ docs/reference/ChangeLog | 4 + docs/reference/gdk/tmpl/keys.sgml | 4 +- docs/reference/gtk/Makefile.am | 1 + gdk/gdkkeys.h | 2 +- gdk/linux-fb/gdkkeyboard-fb.c | 22 +- gdk/win32/gdkkeys-win32.c | 10 +- gdk/x11/gdkevents-x11.c | 2 + gdk/x11/gdkkeys-x11.c | 52 ++-- gtk/Makefile.am | 2 + gtk/gtkaccelgroup.c | 2 +- gtk/gtkbindings.c | 219 +++++++++++++---- gtk/gtkbindings.h | 4 +- gtk/gtkbutton.c | 2 +- gtk/gtkcombo.c | 1 - gtk/gtkdebug.h | 22 +- gtk/gtkkeyhash.c | 379 ++++++++++++++++++++++++++++++ gtk/gtkkeyhash.h | 50 ++++ gtk/gtklabel.c | 2 +- gtk/gtkmain.c | 3 +- gtk/gtknotebook.c | 3 - gtk/gtkpaned.c | 3 - gtk/gtkscrolledwindow.c | 3 - gtk/gtktextview.c | 9 +- gtk/gtkwidget.c | 8 +- gtk/gtkwindow.c | 207 +++++++++++++++- gtk/gtkwindow.h | 3 +- tests/testgtk.c | 78 ++++++ 34 files changed, 1254 insertions(+), 137 deletions(-) create mode 100644 gtk/gtkkeyhash.c create mode 100644 gtk/gtkkeyhash.h diff --git a/ChangeLog b/ChangeLog index bd33c02399..856a5c5553 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,45 @@ +Wed Feb 20 14:26:47 2002 Owen Taylor + + * gtk/gtkkeyhash.[ch]: Implement "fuzzy" key binding lookups; + allow matches on key and level but not group. Also, implement + ignoring "consumed modifiers correctly." + + * gtk/gtkaccelgroup.c gtk/gtkbindings.c: Convert to using + GtkKeyHash. + + * gtk/gtkdebug.h gtk/gtkmain.c: Support GTK_DEBUG=keybindings + + * gdk/x11/gdkevents-x11.c (gdk_event_translate): Fill in + the group for key release events as well as key press events. + + * gdk/gdkkeys.h gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): + Rename unused_modifiers to consumed_modifiers, make the docs and + non-Xkb implementation match the Xkb implementation. + + * gdk/linux-fb/gdkkeyboard-fb.c gdk/win32/gdkkeys-win32.c: Propagate + doc and parameter name changes. + + * gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): + XkbTranslateKeyCode doesn't handle LockMask, we need to handle + it ourselves. + + * gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): Force + Tab to give GDK_ISO_Left_Tab, since we need consistency + to allow dealing with ISO_Left_Tab. + + * gtk/gtkwindow.c gtk/gtktextview.c gtk/gtkscrolledwindow.c + gtk/gtkpaned.c gtk/gtkcombo.c gtk/gtknotebook.c: + Remove inappropriate uses of GDK_ISO_Left_Tab. (GDK_ISO_Left_Tab + or Tab both are equivalent as a binding specifier.) + + * gtk/gtkbutton.c (gtk_button_class_init): Make ::activate + GTK_RUN_ACTION, so you can bind an accelerator to it. + + * gtk/gtklabel.c (gtk_label_set_uline_text_internal): Call + gdk_unicode_to_keyval on the mnemonic character. + + * tests/testgtk.c: Add a test for the new fuzzy key binding matching. + 2002-02-21 jacob berkman * gtk/theme-bits/Makefile.am (EXTRA_DIST): inconsitent files are diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index bd33c02399..856a5c5553 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -1,3 +1,45 @@ +Wed Feb 20 14:26:47 2002 Owen Taylor + + * gtk/gtkkeyhash.[ch]: Implement "fuzzy" key binding lookups; + allow matches on key and level but not group. Also, implement + ignoring "consumed modifiers correctly." + + * gtk/gtkaccelgroup.c gtk/gtkbindings.c: Convert to using + GtkKeyHash. + + * gtk/gtkdebug.h gtk/gtkmain.c: Support GTK_DEBUG=keybindings + + * gdk/x11/gdkevents-x11.c (gdk_event_translate): Fill in + the group for key release events as well as key press events. + + * gdk/gdkkeys.h gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): + Rename unused_modifiers to consumed_modifiers, make the docs and + non-Xkb implementation match the Xkb implementation. + + * gdk/linux-fb/gdkkeyboard-fb.c gdk/win32/gdkkeys-win32.c: Propagate + doc and parameter name changes. + + * gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): + XkbTranslateKeyCode doesn't handle LockMask, we need to handle + it ourselves. + + * gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): Force + Tab to give GDK_ISO_Left_Tab, since we need consistency + to allow dealing with ISO_Left_Tab. + + * gtk/gtkwindow.c gtk/gtktextview.c gtk/gtkscrolledwindow.c + gtk/gtkpaned.c gtk/gtkcombo.c gtk/gtknotebook.c: + Remove inappropriate uses of GDK_ISO_Left_Tab. (GDK_ISO_Left_Tab + or Tab both are equivalent as a binding specifier.) + + * gtk/gtkbutton.c (gtk_button_class_init): Make ::activate + GTK_RUN_ACTION, so you can bind an accelerator to it. + + * gtk/gtklabel.c (gtk_label_set_uline_text_internal): Call + gdk_unicode_to_keyval on the mnemonic character. + + * tests/testgtk.c: Add a test for the new fuzzy key binding matching. + 2002-02-21 jacob berkman * gtk/theme-bits/Makefile.am (EXTRA_DIST): inconsitent files are diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index bd33c02399..856a5c5553 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,45 @@ +Wed Feb 20 14:26:47 2002 Owen Taylor + + * gtk/gtkkeyhash.[ch]: Implement "fuzzy" key binding lookups; + allow matches on key and level but not group. Also, implement + ignoring "consumed modifiers correctly." + + * gtk/gtkaccelgroup.c gtk/gtkbindings.c: Convert to using + GtkKeyHash. + + * gtk/gtkdebug.h gtk/gtkmain.c: Support GTK_DEBUG=keybindings + + * gdk/x11/gdkevents-x11.c (gdk_event_translate): Fill in + the group for key release events as well as key press events. + + * gdk/gdkkeys.h gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): + Rename unused_modifiers to consumed_modifiers, make the docs and + non-Xkb implementation match the Xkb implementation. + + * gdk/linux-fb/gdkkeyboard-fb.c gdk/win32/gdkkeys-win32.c: Propagate + doc and parameter name changes. + + * gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): + XkbTranslateKeyCode doesn't handle LockMask, we need to handle + it ourselves. + + * gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): Force + Tab to give GDK_ISO_Left_Tab, since we need consistency + to allow dealing with ISO_Left_Tab. + + * gtk/gtkwindow.c gtk/gtktextview.c gtk/gtkscrolledwindow.c + gtk/gtkpaned.c gtk/gtkcombo.c gtk/gtknotebook.c: + Remove inappropriate uses of GDK_ISO_Left_Tab. (GDK_ISO_Left_Tab + or Tab both are equivalent as a binding specifier.) + + * gtk/gtkbutton.c (gtk_button_class_init): Make ::activate + GTK_RUN_ACTION, so you can bind an accelerator to it. + + * gtk/gtklabel.c (gtk_label_set_uline_text_internal): Call + gdk_unicode_to_keyval on the mnemonic character. + + * tests/testgtk.c: Add a test for the new fuzzy key binding matching. + 2002-02-21 jacob berkman * gtk/theme-bits/Makefile.am (EXTRA_DIST): inconsitent files are diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index bd33c02399..856a5c5553 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -1,3 +1,45 @@ +Wed Feb 20 14:26:47 2002 Owen Taylor + + * gtk/gtkkeyhash.[ch]: Implement "fuzzy" key binding lookups; + allow matches on key and level but not group. Also, implement + ignoring "consumed modifiers correctly." + + * gtk/gtkaccelgroup.c gtk/gtkbindings.c: Convert to using + GtkKeyHash. + + * gtk/gtkdebug.h gtk/gtkmain.c: Support GTK_DEBUG=keybindings + + * gdk/x11/gdkevents-x11.c (gdk_event_translate): Fill in + the group for key release events as well as key press events. + + * gdk/gdkkeys.h gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): + Rename unused_modifiers to consumed_modifiers, make the docs and + non-Xkb implementation match the Xkb implementation. + + * gdk/linux-fb/gdkkeyboard-fb.c gdk/win32/gdkkeys-win32.c: Propagate + doc and parameter name changes. + + * gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): + XkbTranslateKeyCode doesn't handle LockMask, we need to handle + it ourselves. + + * gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): Force + Tab to give GDK_ISO_Left_Tab, since we need consistency + to allow dealing with ISO_Left_Tab. + + * gtk/gtkwindow.c gtk/gtktextview.c gtk/gtkscrolledwindow.c + gtk/gtkpaned.c gtk/gtkcombo.c gtk/gtknotebook.c: + Remove inappropriate uses of GDK_ISO_Left_Tab. (GDK_ISO_Left_Tab + or Tab both are equivalent as a binding specifier.) + + * gtk/gtkbutton.c (gtk_button_class_init): Make ::activate + GTK_RUN_ACTION, so you can bind an accelerator to it. + + * gtk/gtklabel.c (gtk_label_set_uline_text_internal): Call + gdk_unicode_to_keyval on the mnemonic character. + + * tests/testgtk.c: Add a test for the new fuzzy key binding matching. + 2002-02-21 jacob berkman * gtk/theme-bits/Makefile.am (EXTRA_DIST): inconsitent files are diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index bd33c02399..856a5c5553 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,45 @@ +Wed Feb 20 14:26:47 2002 Owen Taylor + + * gtk/gtkkeyhash.[ch]: Implement "fuzzy" key binding lookups; + allow matches on key and level but not group. Also, implement + ignoring "consumed modifiers correctly." + + * gtk/gtkaccelgroup.c gtk/gtkbindings.c: Convert to using + GtkKeyHash. + + * gtk/gtkdebug.h gtk/gtkmain.c: Support GTK_DEBUG=keybindings + + * gdk/x11/gdkevents-x11.c (gdk_event_translate): Fill in + the group for key release events as well as key press events. + + * gdk/gdkkeys.h gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): + Rename unused_modifiers to consumed_modifiers, make the docs and + non-Xkb implementation match the Xkb implementation. + + * gdk/linux-fb/gdkkeyboard-fb.c gdk/win32/gdkkeys-win32.c: Propagate + doc and parameter name changes. + + * gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): + XkbTranslateKeyCode doesn't handle LockMask, we need to handle + it ourselves. + + * gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): Force + Tab to give GDK_ISO_Left_Tab, since we need consistency + to allow dealing with ISO_Left_Tab. + + * gtk/gtkwindow.c gtk/gtktextview.c gtk/gtkscrolledwindow.c + gtk/gtkpaned.c gtk/gtkcombo.c gtk/gtknotebook.c: + Remove inappropriate uses of GDK_ISO_Left_Tab. (GDK_ISO_Left_Tab + or Tab both are equivalent as a binding specifier.) + + * gtk/gtkbutton.c (gtk_button_class_init): Make ::activate + GTK_RUN_ACTION, so you can bind an accelerator to it. + + * gtk/gtklabel.c (gtk_label_set_uline_text_internal): Call + gdk_unicode_to_keyval on the mnemonic character. + + * tests/testgtk.c: Add a test for the new fuzzy key binding matching. + 2002-02-21 jacob berkman * gtk/theme-bits/Makefile.am (EXTRA_DIST): inconsitent files are diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index bd33c02399..856a5c5553 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,45 @@ +Wed Feb 20 14:26:47 2002 Owen Taylor + + * gtk/gtkkeyhash.[ch]: Implement "fuzzy" key binding lookups; + allow matches on key and level but not group. Also, implement + ignoring "consumed modifiers correctly." + + * gtk/gtkaccelgroup.c gtk/gtkbindings.c: Convert to using + GtkKeyHash. + + * gtk/gtkdebug.h gtk/gtkmain.c: Support GTK_DEBUG=keybindings + + * gdk/x11/gdkevents-x11.c (gdk_event_translate): Fill in + the group for key release events as well as key press events. + + * gdk/gdkkeys.h gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): + Rename unused_modifiers to consumed_modifiers, make the docs and + non-Xkb implementation match the Xkb implementation. + + * gdk/linux-fb/gdkkeyboard-fb.c gdk/win32/gdkkeys-win32.c: Propagate + doc and parameter name changes. + + * gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): + XkbTranslateKeyCode doesn't handle LockMask, we need to handle + it ourselves. + + * gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): Force + Tab to give GDK_ISO_Left_Tab, since we need consistency + to allow dealing with ISO_Left_Tab. + + * gtk/gtkwindow.c gtk/gtktextview.c gtk/gtkscrolledwindow.c + gtk/gtkpaned.c gtk/gtkcombo.c gtk/gtknotebook.c: + Remove inappropriate uses of GDK_ISO_Left_Tab. (GDK_ISO_Left_Tab + or Tab both are equivalent as a binding specifier.) + + * gtk/gtkbutton.c (gtk_button_class_init): Make ::activate + GTK_RUN_ACTION, so you can bind an accelerator to it. + + * gtk/gtklabel.c (gtk_label_set_uline_text_internal): Call + gdk_unicode_to_keyval on the mnemonic character. + + * tests/testgtk.c: Add a test for the new fuzzy key binding matching. + 2002-02-21 jacob berkman * gtk/theme-bits/Makefile.am (EXTRA_DIST): inconsitent files are diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index bd33c02399..856a5c5553 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,45 @@ +Wed Feb 20 14:26:47 2002 Owen Taylor + + * gtk/gtkkeyhash.[ch]: Implement "fuzzy" key binding lookups; + allow matches on key and level but not group. Also, implement + ignoring "consumed modifiers correctly." + + * gtk/gtkaccelgroup.c gtk/gtkbindings.c: Convert to using + GtkKeyHash. + + * gtk/gtkdebug.h gtk/gtkmain.c: Support GTK_DEBUG=keybindings + + * gdk/x11/gdkevents-x11.c (gdk_event_translate): Fill in + the group for key release events as well as key press events. + + * gdk/gdkkeys.h gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): + Rename unused_modifiers to consumed_modifiers, make the docs and + non-Xkb implementation match the Xkb implementation. + + * gdk/linux-fb/gdkkeyboard-fb.c gdk/win32/gdkkeys-win32.c: Propagate + doc and parameter name changes. + + * gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): + XkbTranslateKeyCode doesn't handle LockMask, we need to handle + it ourselves. + + * gdk/x11/gdkkeys-x11.c (gdk_keymap_translate_keyboard_state): Force + Tab to give GDK_ISO_Left_Tab, since we need consistency + to allow dealing with ISO_Left_Tab. + + * gtk/gtkwindow.c gtk/gtktextview.c gtk/gtkscrolledwindow.c + gtk/gtkpaned.c gtk/gtkcombo.c gtk/gtknotebook.c: + Remove inappropriate uses of GDK_ISO_Left_Tab. (GDK_ISO_Left_Tab + or Tab both are equivalent as a binding specifier.) + + * gtk/gtkbutton.c (gtk_button_class_init): Make ::activate + GTK_RUN_ACTION, so you can bind an accelerator to it. + + * gtk/gtklabel.c (gtk_label_set_uline_text_internal): Call + gdk_unicode_to_keyval on the mnemonic character. + + * tests/testgtk.c: Add a test for the new fuzzy key binding matching. + 2002-02-21 jacob berkman * gtk/theme-bits/Makefile.am (EXTRA_DIST): inconsitent files are diff --git a/docs/reference/ChangeLog b/docs/reference/ChangeLog index e051435e9f..165481dbcb 100644 --- a/docs/reference/ChangeLog +++ b/docs/reference/ChangeLog @@ -1,3 +1,7 @@ +Thu Feb 21 12:11:42 2002 Owen Taylor + + * gtk/Makefile.am (IGNORE_HFILES): Add gtkkeyhash.h + 2002-02-20 Sven Neumann * gtk/gtk-sections.txt diff --git a/docs/reference/gdk/tmpl/keys.sgml b/docs/reference/gdk/tmpl/keys.sgml index c1f2d482e9..360733587e 100644 --- a/docs/reference/gdk/tmpl/keys.sgml +++ b/docs/reference/gdk/tmpl/keys.sgml @@ -139,8 +139,10 @@ be mapped to a keyval. @keyval: @effective_group: @level: -@unused_modifiers: +@consumed_modifiers: @Returns: + +@unused_modifiers: diff --git a/docs/reference/gtk/Makefile.am b/docs/reference/gtk/Makefile.am index 3c375315aa..6eccf150dc 100644 --- a/docs/reference/gtk/Makefile.am +++ b/docs/reference/gtk/Makefile.am @@ -33,6 +33,7 @@ IGNORE_HFILES= \ gtkhsv.h \ gtkimmodule.h \ gtkintl.h \ + gtkkeyhash.h \ gtkmarshal.h \ gtkprivate.h \ gtktreeprivate.h \ diff --git a/gdk/gdkkeys.h b/gdk/gdkkeys.h index e08af315f8..c884b964f3 100644 --- a/gdk/gdkkeys.h +++ b/gdk/gdkkeys.h @@ -88,7 +88,7 @@ gboolean gdk_keymap_translate_keyboard_state (GdkKeymap *keymap, guint *keyval, gint *effective_group, gint *level, - GdkModifierType *unused_modifiers); + GdkModifierType *consumed_modifiers); gboolean gdk_keymap_get_entries_for_keyval (GdkKeymap *keymap, guint keyval, GdkKeymapKey **keys, diff --git a/gdk/linux-fb/gdkkeyboard-fb.c b/gdk/linux-fb/gdkkeyboard-fb.c index 1f7d75dcbd..625d516c9c 100644 --- a/gdk/linux-fb/gdkkeyboard-fb.c +++ b/gdk/linux-fb/gdkkeyboard-fb.c @@ -61,7 +61,7 @@ struct _GdkFBKeyboardDevice { guint *keyval, gint *effective_group, gint *level, - GdkModifierType *unused_modifiers); + GdkModifierType *consumed_modifiers); gboolean (*get_entries_for_keyval) (GdkFBKeyboard *kb, guint keyval, GdkKeymapKey **keys, @@ -89,7 +89,7 @@ static gboolean xlate_translate (GdkFBKeyboard *kb, guint *keyval, gint *effective_group, gint *level, - GdkModifierType *unused_modifiers); + GdkModifierType *consumed_modifiers); static gboolean xlate_get_for_keyval (GdkFBKeyboard *kb, guint keyval, GdkKeymapKey **keys, @@ -111,7 +111,7 @@ static gboolean raw_translate (GdkFBKeyboard *kb, guint *keyval, gint *effective_group, gint *level, - GdkModifierType *unused_modifiers); + GdkModifierType *consumed_modifiers); static gboolean raw_get_for_keyval (GdkFBKeyboard *kb, guint keyval, GdkKeymapKey **keys, @@ -335,13 +335,13 @@ gdk_keymap_lookup_key (GdkKeymap *keymap, * @keyval: return location for keyval * @effective_group: return location for effective group * @level: return location for level - * @unused_modifiers: return location for modifiers that didn't affect the group or level + * @consumed_modifiers: return location for modifiers that were used to determine the group or level * * * Translates the contents of a #GdkEventKey into a keyval, effective - * group, and level. Modifiers that didn't affect the translation and - * are thus available for application use are returned in - * @unused_modifiers. See gdk_keyval_get_keys() for an explanation of + * group, and level. Modifiers that affected the translation and + * are thus unavailable for application use are returned in + * @consumed_modifiers. See gdk_keyval_get_keys() for an explanation of * groups and levels. The @effective_group is the group that was * actually used for the translation; some keys such as Enter are not * affected by the active keyboard group. The @level is derived from @@ -358,7 +358,7 @@ gdk_keymap_translate_keyboard_state (GdkKeymap *keymap, guint *keyval, gint *effective_group, gint *level, - GdkModifierType *unused_modifiers) + GdkModifierType *consumed_modifiers) { g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE); g_return_val_if_fail (group < 4, FALSE); @@ -370,7 +370,7 @@ gdk_keymap_translate_keyboard_state (GdkKeymap *keymap, keyval, effective_group, level, - unused_modifiers); + consumed_modifiers); } static void @@ -934,7 +934,7 @@ xlate_translate (GdkFBKeyboard *kb, guint *keyval, gint *effective_group, gint *level, - GdkModifierType *unused_modifiers) + GdkModifierType *consumed_modifiers) { g_warning ("xlate_translate() NIY"); return FALSE; @@ -1445,7 +1445,7 @@ raw_translate (GdkFBKeyboard *kb, guint *keyval, gint *effective_group, gint *level, - GdkModifierType *unused_modifiers) + GdkModifierType *consumed_modifiers) { g_warning ("raw_translate() NIY"); return FALSE; diff --git a/gdk/win32/gdkkeys-win32.c b/gdk/win32/gdkkeys-win32.c index 91f73770b3..b28bd7b22b 100644 --- a/gdk/win32/gdkkeys-win32.c +++ b/gdk/win32/gdkkeys-win32.c @@ -261,13 +261,13 @@ gdk_keymap_lookup_key (GdkKeymap *keymap, * @keyval: return location for keyval * @effective_group: return location for effective group * @level: return location for level - * @unused_modifiers: return location for modifiers that didn't affect the group or level + * @consumed_modifiers: return location for modifiers that were used to determine the group or level * * * Translates the contents of a #GdkEventKey into a keyval, effective - * group, and level. Modifiers that didn't affect the translation and - * are thus available for application use are returned in - * @unused_modifiers. See gdk_keyval_get_keys() for an explanation of + * group, and level. Modifiers that affected the translation and + * are thus unavailable for application use are returned in + * @consumed_modifiers. See gdk_keyval_get_keys() for an explanation of * groups and levels. The @effective_group is the group that was * actually used for the translation; some keys such as Enter are not * affected by the active keyboard group. The @level is derived from @@ -284,7 +284,7 @@ gdk_keymap_translate_keyboard_state (GdkKeymap *keymap, guint *keyval, gint *effective_group, gint *level, - GdkModifierType *unused_modifiers) + GdkModifierType *consumed_modifiers) { g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE); g_return_val_if_fail (group < 4, FALSE); diff --git a/gdk/x11/gdkevents-x11.c b/gdk/x11/gdkevents-x11.c index d72b98b5c5..2c66046207 100644 --- a/gdk/x11/gdkevents-x11.c +++ b/gdk/x11/gdkevents-x11.c @@ -679,6 +679,8 @@ gdk_event_translate (GdkEvent *event, event->key.length = 0; event->key.string = NULL; + event->key.group = (xevent->xkey.state & KEYBOARD_GROUP_MASK) >> KEYBOARD_GROUP_SHIFT; + break; case ButtonPress: diff --git a/gdk/x11/gdkkeys-x11.c b/gdk/x11/gdkkeys-x11.c index 139ab9fb7f..e6eb7e125c 100644 --- a/gdk/x11/gdkkeys-x11.c +++ b/gdk/x11/gdkkeys-x11.c @@ -735,13 +735,13 @@ MyEnhancedXkbTranslateKeyCode(register XkbDescPtr xkb, * @keyval: return location for keyval * @effective_group: return location for effective group * @level: return location for level - * @unused_modifiers: return location for modifiers that didn't affect the group or level + * @consumed_modifiers: return location for modifiers that were used to determine the group or level * * * Translates the contents of a #GdkEventKey into a keyval, effective - * group, and level. Modifiers that didn't affect the translation and - * are thus available for application use are returned in - * @unused_modifiers. See gdk_keyval_get_keys() for an explanation of + * group, and level. Modifiers that affected the translation and + * are thus unavailable for application use are returned in + * @consumed_modifiers. See gdk_keyval_get_keys() for an explanation of * groups and levels. The @effective_group is the group that was * actually used for the translation; some keys such as Enter are not * affected by the active keyboard group. The @level is derived from @@ -758,9 +758,10 @@ gdk_keymap_translate_keyboard_state (GdkKeymap *keymap, guint *keyval, gint *effective_group, gint *level, - GdkModifierType *unused_modifiers) + GdkModifierType *consumed_modifiers) { KeySym tmp_keyval = NoSymbol; + guint tmp_modifiers; g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE); g_return_val_if_fail (group < 4, FALSE); @@ -771,8 +772,8 @@ gdk_keymap_translate_keyboard_state (GdkKeymap *keymap, *effective_group = 0; if (level) *level = 0; - if (unused_modifiers) - *unused_modifiers = state; + if (consumed_modifiers) + *consumed_modifiers = 0; update_keyrange (); @@ -792,13 +793,18 @@ gdk_keymap_translate_keyboard_state (GdkKeymap *keymap, MyEnhancedXkbTranslateKeyCode (xkb, hardware_keycode, state, - unused_modifiers, + &tmp_modifiers, &tmp_keyval, effective_group, level); - if (keyval) - *keyval = tmp_keyval; + if (state & ~tmp_modifiers & LockMask) + tmp_keyval = gdk_keyval_to_upper (tmp_keyval); + + /* We need to augment the consumed modifiers with LockMask, since + * we handle that ourselves, and also with the group bits + */ + tmp_modifiers |= LockMask | 1 << 13 | 1 << 14; } else #endif @@ -819,14 +825,7 @@ gdk_keymap_translate_keyboard_state (GdkKeymap *keymap, tmp_keyval = XKeycodeToKeysym (gdk_display, hardware_keycode, group * keysyms_per_keycode + shift_level); - if (keyval) - *keyval = tmp_keyval; - - if (unused_modifiers) - { - *unused_modifiers = state; - *unused_modifiers &= ~(GDK_SHIFT_MASK | GDK_LOCK_MASK | group_switch_mask); - } + tmp_modifiers = GDK_SHIFT_MASK | GDK_LOCK_MASK | group_switch_mask; if (effective_group) *effective_group = (state & group_switch_mask) ? 1 : 0; @@ -835,6 +834,23 @@ gdk_keymap_translate_keyboard_state (GdkKeymap *keymap, *level = shift_level; } + /* GDK_ISO_Left_Tab, as usually configured through XKB, really messes + * up the whole idea of "consumed modifiers" because shift is consumed. + * However, Tab is not _consistently_ GDK_ISO_Left_Tab, so people + * can't bind to GDK_ISO_Left_Tab instead. So, we force consistency here. + */ + if (tmp_keyval == GDK_Tab && (tmp_modifiers & GDK_SHIFT_MASK == 0)) + { + tmp_keyval = GDK_ISO_Left_Tab; + tmp_modifiers |= GDK_SHIFT_MASK; + } + + if (consumed_modifiers) + *consumed_modifiers = tmp_modifiers; + + if (keyval) + *keyval = tmp_keyval; + return tmp_keyval != NoSymbol; } diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 57701300fd..779bea0c9c 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -308,6 +308,8 @@ gtk_c_sources = @STRIP_BEGIN@ \ gtkinvisible.c \ gtkitem.c \ gtkitemfactory.c \ + gtkkeyhash.c \ + gtkkeyhash.h \ gtklabel.c \ gtklayout.c \ gtklist.c \ diff --git a/gtk/gtkaccelgroup.c b/gtk/gtkaccelgroup.c index 8ee2a8ba6b..3f15a9e6e0 100644 --- a/gtk/gtkaccelgroup.c +++ b/gtk/gtkaccelgroup.c @@ -714,7 +714,7 @@ gtk_accel_group_from_accel_closure (GClosure *closure) g_return_val_if_fail (closure != NULL, NULL); - /* a few remarks on wat we do here. in general, we need a way to reverse lookup + /* a few remarks on what we do here. in general, we need a way to reverse lookup * accel_groups from closures that are being used in accel groups. this could * be done e.g via a hashtable. it is however cheaper (memory wise) to just * use the invalidation notifier on the closure itself (which we need to install diff --git a/gtk/gtkbindings.c b/gtk/gtkbindings.c index 676ff3ed8d..95ae875d87 100644 --- a/gtk/gtkbindings.c +++ b/gtk/gtkbindings.c @@ -29,7 +29,9 @@ #include #include +#include #include "gtkbindings.h" +#include "gtkkeyhash.h" #include "gtksignal.h" #include "gtkwidget.h" #include "gtkrc.h" @@ -49,6 +51,7 @@ typedef struct { /* --- variables --- */ static GHashTable *binding_entry_hash_table = NULL; +static GSList *binding_key_hashes = NULL; static GSList *binding_set_list = NULL; static const gchar *key_class_binding_set = "gtk-class-binding-set"; static GQuark key_id_class_binding_set = 0; @@ -107,11 +110,81 @@ binding_entries_compare (gconstpointer a, return (ea->keyval == eb->keyval && ea->modifiers == eb->modifiers); } +static void +binding_key_hash_insert_entry (GtkKeyHash *key_hash, + GtkBindingEntry *entry) +{ + guint keyval = entry->keyval; + + /* We store lowercased accelerators. To deal with this, if + * was specified, uppercase. + */ + if (entry->modifiers & GDK_SHIFT_MASK) + { + if (keyval == GDK_Tab) + keyval = GDK_ISO_Left_Tab; + else + keyval = gdk_keyval_to_upper (keyval); + } + + _gtk_key_hash_add_entry (key_hash, keyval, entry->modifiers & ~GDK_RELEASE_MASK, entry); +} + +static void +binding_key_hash_destroy (gpointer data) +{ + GtkKeyHash *key_hash = data; + + binding_key_hashes = g_slist_remove (binding_key_hashes, key_hash); + _gtk_key_hash_free (key_hash); +} + +static void +insert_entries_into_key_hash (gpointer key, + gpointer value, + gpointer data) +{ + GtkKeyHash *key_hash = data; + GtkBindingEntry *entry = value; + + for (; entry; entry = entry->hash_next) + binding_key_hash_insert_entry (key_hash, entry); +} + +static GtkKeyHash * +binding_key_hash_for_keymap (GdkKeymap *keymap) +{ + static GQuark key_hash_quark = 0; + GtkKeyHash *key_hash; + + if (!key_hash_quark) + key_hash_quark = g_quark_from_static_string ("gtk-binding-key-hash"); + + key_hash = g_object_get_qdata (G_OBJECT (keymap), key_hash_quark); + + if (!key_hash) + { + key_hash = _gtk_key_hash_new (keymap, NULL); + g_object_set_qdata_full (G_OBJECT (keymap), key_hash_quark, key_hash, binding_key_hash_destroy); + + if (binding_entry_hash_table) + g_hash_table_foreach (binding_entry_hash_table, + insert_entries_into_key_hash, + key_hash); + + binding_key_hashes = g_slist_prepend (binding_key_hashes, key_hash); + } + + return key_hash; +} + + static GtkBindingEntry* binding_entry_new (GtkBindingSet *binding_set, guint keyval, GdkModifierType modifiers) { + GSList *tmp_list; GtkBindingEntry *entry; if (!binding_entry_hash_table) @@ -132,6 +205,12 @@ binding_entry_new (GtkBindingSet *binding_set, if (entry->hash_next) g_hash_table_remove (binding_entry_hash_table, entry->hash_next); g_hash_table_insert (binding_entry_hash_table, entry, entry); + + for (tmp_list = binding_key_hashes; tmp_list; tmp_list = tmp_list->next) + { + GtkKeyHash *key_hash = tmp_list->data; + binding_key_hash_insert_entry (key_hash, entry); + } return entry; } @@ -167,6 +246,7 @@ binding_entry_destroy (GtkBindingEntry *entry) register GtkBindingEntry *tmp; GtkBindingEntry *begin; register GtkBindingEntry *last; + GSList *tmp_list; /* unlink from binding set */ @@ -214,27 +294,18 @@ binding_entry_destroy (GtkBindingEntry *entry) g_hash_table_insert (binding_entry_hash_table, begin, begin); } + for (tmp_list = binding_key_hashes; tmp_list; tmp_list = tmp_list->next) + { + GtkKeyHash *key_hash = tmp_list->data; + _gtk_key_hash_remove_entry (key_hash, entry); + } + entry->destroyed = TRUE; if (!entry->in_emission) binding_entry_free (entry); } -static GtkBindingEntry* -binding_ht_lookup_list (guint keyval, - GdkModifierType modifiers) -{ - GtkBindingEntry lookup_entry = { 0 }; - - if (!binding_entry_hash_table) - return NULL; - - lookup_entry.keyval = keyval; - lookup_entry.modifiers = modifiers; - - return g_hash_table_lookup (binding_entry_hash_table, &lookup_entry); -} - static GtkBindingEntry* binding_ht_lookup_entry (GtkBindingSet *set, guint keyval, @@ -835,7 +906,7 @@ gtk_binding_set_add_path (GtkBindingSet *binding_set, } } -static inline gboolean +static gboolean binding_match_activate (GSList *pspec_list, GtkObject *object, guint path_length, @@ -877,20 +948,25 @@ gtk_binding_pattern_compare (gconstpointer new_pattern, return np->seq_id < ep->seq_id; } -static inline GSList* -gtk_binding_entries_sort_patterns (GtkBindingEntry *entries, - GtkPathType path_id) +static GSList* +gtk_binding_entries_sort_patterns (GSList *entries, + GtkPathType path_id, + gboolean is_release) { GSList *patterns; patterns = NULL; - while (entries) + for (; entries; entries = entries->next) { - register GtkBindingSet *binding_set; + GtkBindingEntry *entry = entries->data; + GtkBindingSet *binding_set; GSList *slist = NULL; - binding_set = entries->binding_set; - binding_set->current = entries; + if (is_release != ((entry->modifiers & GDK_RELEASE_MASK) != 0)) + continue; + + binding_set = entry->binding_set; + binding_set->current = entry; switch (path_id) { @@ -912,35 +988,19 @@ gtk_binding_entries_sort_patterns (GtkBindingEntry *entries, pspec = slist->data; patterns = g_slist_insert_sorted (patterns, pspec, gtk_binding_pattern_compare); } - - entries = entries->hash_next; } return patterns; } - -gboolean -gtk_bindings_activate (GtkObject *object, - guint keyval, - GdkModifierType modifiers) +static gboolean +gtk_bindings_activate_list (GtkObject *object, + GSList *entries, + gboolean is_release) { - GtkBindingEntry *entries; - GtkWidget *widget; + GtkWidget *widget = GTK_WIDGET (object); gboolean handled = FALSE; - g_return_val_if_fail (GTK_IS_OBJECT (object), FALSE); - - if (!GTK_IS_WIDGET (object)) - return FALSE; - - widget = GTK_WIDGET (object); - - keyval = gdk_keyval_to_lower (keyval); - modifiers = modifiers & BINDING_MOD_MASK (); - - entries = binding_ht_lookup_list (keyval, modifiers); - if (!entries) return FALSE; @@ -951,7 +1011,7 @@ gtk_bindings_activate (GtkObject *object, GSList *patterns; gtk_widget_path (widget, &path_length, &path, &path_reversed); - patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_WIDGET); + patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_WIDGET, is_release); handled = binding_match_activate (patterns, object, path_length, path, path_reversed); g_slist_free (patterns); g_free (path); @@ -965,7 +1025,7 @@ gtk_bindings_activate (GtkObject *object, GSList *patterns; gtk_widget_class_path (widget, &path_length, &path, &path_reversed); - patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_WIDGET_CLASS); + patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_WIDGET_CLASS, is_release); handled = binding_match_activate (patterns, object, path_length, path, path_reversed); g_slist_free (patterns); g_free (path); @@ -977,7 +1037,7 @@ gtk_bindings_activate (GtkObject *object, GSList *patterns; GtkType class_type; - patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_CLASS); + patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_CLASS, is_release); class_type = GTK_OBJECT_TYPE (object); while (class_type && !handled) { @@ -1000,6 +1060,71 @@ gtk_bindings_activate (GtkObject *object, return handled; } +gboolean +gtk_bindings_activate (GtkObject *object, + guint keyval, + GdkModifierType modifiers) +{ + GSList *entries = NULL; + GtkKeyHash *key_hash; + gboolean handled = FALSE; + gboolean is_release; + + g_return_val_if_fail (GTK_IS_OBJECT (object), FALSE); + + if (!GTK_IS_WIDGET (object)) + return FALSE; + + is_release = (BINDING_MOD_MASK () & GDK_RELEASE_MASK) != 0; + modifiers = modifiers & BINDING_MOD_MASK () & ~GDK_RELEASE_MASK; + + key_hash = binding_key_hash_for_keymap (gdk_keymap_get_default ()); + entries = _gtk_key_hash_lookup_keyval (key_hash, keyval, modifiers); + + handled = gtk_bindings_activate_list (object, entries, is_release); + + g_slist_free (entries); + + return handled; +} + +/** + * _gtk_bindings_activate_event: + * @object: a #GtkObject (generally must be a widget) + * @event: a #GdkEventKey + * + * Looks up key bindings for @object to find one matching + * @event, and if one was found, activate it. + * + * Return value: %TRUE if a matching key binding was found + **/ +gboolean +_gtk_bindings_activate_event (GtkObject *object, + GdkEventKey *event) +{ + GSList *entries = NULL; + GtkKeyHash *key_hash; + gboolean handled = FALSE; + + g_return_val_if_fail (GTK_IS_OBJECT (object), FALSE); + + if (!GTK_IS_WIDGET (object)) + return FALSE; + + key_hash = binding_key_hash_for_keymap (gdk_keymap_get_default ()); + entries = _gtk_key_hash_lookup (key_hash, + event->hardware_keycode, + event->state & BINDING_MOD_MASK () & ~GDK_RELEASE_MASK, + event->group); + + handled = gtk_bindings_activate_list (object, entries, + event->type == GDK_KEY_RELEASE); + + g_slist_free (entries); + + return handled; +} + static guint gtk_binding_parse_signal (GScanner *scanner, GtkBindingSet *binding_set, diff --git a/gtk/gtkbindings.h b/gtk/gtkbindings.h index d574390559..a481f23df6 100644 --- a/gtk/gtkbindings.h +++ b/gtk/gtkbindings.h @@ -136,7 +136,9 @@ void gtk_binding_entry_add_signall (GtkBindingSet *binding_set, guint gtk_binding_parse_binding (GScanner *scanner); -void _gtk_binding_reset_parsed (void); +gboolean _gtk_bindings_activate_event (GtkObject *object, + GdkEventKey *event); +void _gtk_binding_reset_parsed (void); #ifdef __cplusplus } diff --git a/gtk/gtkbutton.c b/gtk/gtkbutton.c index c0d4706874..86701f9fee 100644 --- a/gtk/gtkbutton.c +++ b/gtk/gtkbutton.c @@ -253,7 +253,7 @@ gtk_button_class_init (GtkButtonClass *klass) GTK_TYPE_NONE, 0); button_signals[ACTIVATE] = gtk_signal_new ("activate", - GTK_RUN_FIRST, + GTK_RUN_FIRST | GTK_RUN_ACTION, GTK_CLASS_TYPE (object_class), GTK_SIGNAL_OFFSET (GtkButtonClass, activate), _gtk_marshal_VOID__VOID, diff --git a/gtk/gtkcombo.c b/gtk/gtkcombo.c index d236801b62..a09b572a84 100644 --- a/gtk/gtkcombo.c +++ b/gtk/gtkcombo.c @@ -199,7 +199,6 @@ gtk_combo_entry_key_press (GtkEntry * entry, GdkEventKey * event, GtkCombo * com /* completion */ if ((event->keyval == GDK_Tab || - event->keyval == GDK_ISO_Left_Tab || event->keyval == GDK_KP_Tab) && (event->state & GDK_MOD1_MASK)) { diff --git a/gtk/gtkdebug.h b/gtk/gtkdebug.h index 8c7ddd9423..7aa18463ee 100644 --- a/gtk/gtkdebug.h +++ b/gtk/gtkdebug.h @@ -27,16 +27,17 @@ #ifndef __GTK_DEBUG_H__ #define __GTK_DEBUG_H__ -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ +#include + +G_BEGIN_DECLS typedef enum { - GTK_DEBUG_MISC = 1 << 0, - GTK_DEBUG_PLUGSOCKET = 1 << 1, - GTK_DEBUG_TEXT = 1 << 2, - GTK_DEBUG_TREE = 1 << 3, - GTK_DEBUG_UPDATES = 1 << 4 + GTK_DEBUG_MISC = 1 << 0, + GTK_DEBUG_PLUGSOCKET = 1 << 1, + GTK_DEBUG_TEXT = 1 << 2, + GTK_DEBUG_TREE = 1 << 3, + GTK_DEBUG_UPDATES = 1 << 4, + GTK_DEBUG_KEYBINDINGS = 1 << 5 } GtkDebugFlag; #ifdef G_ENABLE_DEBUG @@ -63,9 +64,6 @@ typedef enum { GTKVAR guint gtk_debug_flags; -#ifdef __cplusplus -} -#endif /* __cplusplus */ - +G_END_DECLS #endif /* __GTK_DEBUG_H__ */ diff --git a/gtk/gtkkeyhash.c b/gtk/gtkkeyhash.c new file mode 100644 index 0000000000..b904c4498c --- /dev/null +++ b/gtk/gtkkeyhash.c @@ -0,0 +1,379 @@ +/* gtkkeyhash.c: Keymap aware matching of key bindings + * + * GTK - The GIMP Toolkit + * Copyright (C) 2002, Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include "gtkdebug.h" +#include "gtkkeyhash.h" + +/* We need to add a ::changed signal to GdkKeyMap to properly deal + * with changes to the key map while we are running. + */ +#undef HAVE_CHANGED_SIGNAL + +typedef struct _GtkKeyHashEntry GtkKeyHashEntry; + +struct _GtkKeyHashEntry +{ + guint keyval; + GdkModifierType modifiers; + GdkKeymapKey *keys; + gint n_keys; + gpointer value; +}; + +struct _GtkKeyHash +{ + GdkKeymap *keymap; + GHashTable *keycode_hash; + GHashTable *reverse_hash; + GDestroyNotify destroy_notify; +}; + +static void +key_hash_clear_keycode (gpointer key, + gpointer value, + gpointer data) +{ + GSList *keys = value; + g_slist_free (keys); +} + +static void +key_hash_insert_entry (GtkKeyHash *key_hash, + GtkKeyHashEntry *entry) +{ + gint i; + + gdk_keymap_get_entries_for_keyval (key_hash->keymap, + entry->keyval, + &entry->keys, &entry->n_keys); + + for (i = 0; i < entry->n_keys; i++) + { + GSList *old_keys = g_hash_table_lookup (key_hash->keycode_hash, + GUINT_TO_POINTER (entry->keys[i].keycode)); + old_keys = g_slist_prepend (old_keys, entry); + g_hash_table_insert (key_hash->keycode_hash, + GUINT_TO_POINTER (entry->keys[i].keycode), + old_keys); + } +} + +#ifdef HAVE_CHANGED_SIGNAL +static void +key_hash_reinsert_entry (gpointer key, + gpointer value, + gpointer data) +{ + GtkKeyHashEntry *entry = value; + GtkKeyHash *key_hash = data; + + g_free (entry->keys); + key_hash_insert_entry (key_hash, entry); +} + +static void +key_hash_keymap_changed (GdkKeymap *keymap, + GtkKeyHash *key_hash) +{ + /* The keymap changed, so we have to clear and reinsert all our entries + */ + g_hash_table_foreach (key_hash->keycode_hash, key_hash_clear_keycode, NULL); + + /* FIXME: Here we reinsert in random order, but I think we actually have to + * insert in the same order as the original order to keep GtkBindingSet happy. + */ + g_hash_table_foreach (key_hash->reverse_hash, key_hash_reinsert_entry, key_hash); +} +#endif /* HAVE_CHANGED_SIGNAL */ + +/** + * _gtk_key_hash_new: + * @keymap: a #GdkKeymap + * @item_destroy_notify: function to be called when items are removed + * from the hash or %NULL. + * + * Create a new key hash object for doing binding resolution. + * + * Return value: the newly created object. Free with _gtk_key_hash_free(). + **/ +GtkKeyHash * +_gtk_key_hash_new (GdkKeymap *keymap, + GDestroyNotify item_destroy_notify) +{ + GtkKeyHash *key_hash = g_new (GtkKeyHash, 1); + + key_hash->keymap = keymap; +#ifdef HAVE_CHANGED_SIGNAL + g_signal_connect (keymap, "changed", + G_CALLBACK (key_hash_keymap_changed), key_hash); +#endif + + key_hash->keycode_hash = g_hash_table_new (g_direct_hash, NULL); + key_hash->reverse_hash = g_hash_table_new (g_direct_hash, NULL); + key_hash->destroy_notify = item_destroy_notify; + + return key_hash; +} + +static void +key_hash_free_entry (GtkKeyHash *key_hash, + GtkKeyHashEntry *entry) +{ + if (key_hash->destroy_notify) + (*key_hash->destroy_notify) (entry->value); + + g_free (entry->keys); + g_free (entry); +} + +static void +key_hash_free_entry_foreach (gpointer key, + gpointer value, + gpointer data) +{ + GtkKeyHashEntry *entry = value; + GtkKeyHash *key_hash = data; + + key_hash_free_entry (key_hash, entry); +} + +/** + * gtk_key_hash_free: + * @key_hash: a #GtkKeyHash + * + * Destroys a key hash created with gtk_key_hash_new() + **/ +void +_gtk_key_hash_free (GtkKeyHash *key_hash) +{ +#if HAVE_CHANGED_SIGNAL + g_signal_handlers_disconnect_by_func (key_hash->keymap, + G_CALLBACK (key_hash_keymap_changed), key_hash); +#endif + + g_hash_table_foreach (key_hash->keycode_hash, key_hash_clear_keycode, NULL); + g_hash_table_foreach (key_hash->reverse_hash, key_hash_free_entry_foreach, key_hash); + g_hash_table_destroy (key_hash->keycode_hash); + g_hash_table_destroy (key_hash->reverse_hash); + + g_free (key_hash); +} + +/** + * _gtk_key_hash_add_entry: + * @key_hash: a #GtkKeyHash + * @keyval: key symbol for this binding + * @modifiers: modifiers for this binding + * @value: value to insert in the key hash + * + * Inserts a pair of key symbol and modifier mask into the key hash. + **/ +void +_gtk_key_hash_add_entry (GtkKeyHash *key_hash, + guint keyval, + GdkModifierType modifiers, + gpointer value) +{ + GtkKeyHashEntry *entry = g_new (GtkKeyHashEntry, 1); + + entry->value = value; + entry->keyval = keyval; + entry->modifiers = modifiers; + + g_hash_table_insert (key_hash->reverse_hash, value, entry); + key_hash_insert_entry (key_hash, entry); +} + +/** + * _gtk_key_hash_remove_entry: + * @key_hash: a #GtkKeyHash + * @value: value previously added with _gtk_key_hash_add_entry() + * + * Removes a value previously added to the key hash with + * _gtk_key_hash_add_entry(). + **/ +void +_gtk_key_hash_remove_entry (GtkKeyHash *key_hash, + gpointer value) +{ + GtkKeyHashEntry *entry = g_hash_table_lookup (key_hash->reverse_hash, value); + if (entry) + { + gint i; + + for (i = 0; i < entry->n_keys; i++) + { + GSList *old_keys = g_hash_table_lookup (key_hash->keycode_hash, + GUINT_TO_POINTER (entry->keys[i].keycode)); + + GSList *new_keys = g_slist_remove (old_keys, entry); + if (old_keys != new_keys) + { + if (old_keys) + g_hash_table_insert (key_hash->keycode_hash, + GUINT_TO_POINTER (entry->keys[i].keycode), + old_keys); + else + g_hash_table_remove (key_hash->keycode_hash, + GUINT_TO_POINTER (entry->keys[i].keycode)); + } + } + + g_hash_table_remove (key_hash->reverse_hash, value); + + key_hash_free_entry (key_hash, entry); + g_free (entry); + } +} + +/** + * _gtk_key_hash_lookup: + * @key_hash: a #GtkKeyHash + * @hardware_keycode: hardware keycode field from a #GdkEventKey + * @state: state field from a #GdkEventKey + * @group: group field from a #GdkEventKey + * + * Looks up the best matching entry or entries in the hash table for + * a given event. + * + * Return value: A #GSList of all matching entries. If there were exact + * matches, they are returned, otherwise all fuzzy matches are + * returned. (A fuzzy match is a match in keycode and level, but not + * in group.) + **/ +GSList * +_gtk_key_hash_lookup (GtkKeyHash *key_hash, + guint16 hardware_keycode, + GdkModifierType state, + gint group) +{ + GSList *keys = g_hash_table_lookup (key_hash->keycode_hash, GUINT_TO_POINTER ((guint)hardware_keycode)); + GSList *results = NULL; + gboolean have_exact = FALSE; + guint keyval; + gint effective_group; + gint level; + GdkModifierType consumed_modifiers; + + gdk_keymap_translate_keyboard_state (key_hash->keymap, + hardware_keycode, state, group, + &keyval, &effective_group, &level, &consumed_modifiers); + + GTK_NOTE (KEYBINDINGS, + g_message ("Looking up keycode = %u, modifiers = 0x%04x,\n" + " keyval = %u, group = %d, level = %d, consumed_modifiers = 0x%04x", + hardware_keycode, state, keyval, effective_group, level, consumed_modifiers)); + + if (keys) + { + GSList *tmp_list = keys; + while (tmp_list) + { + GtkKeyHashEntry *entry = tmp_list->data; + + if ((entry->modifiers & ~consumed_modifiers) == (state & ~consumed_modifiers)) + { + gint i; + + if (keyval == entry->keyval) /* Exact match */ + { + GTK_NOTE (KEYBINDINGS, + g_message (" found exact match, keyval = %u, modifiers = 0x%04x", + entry->keyval, entry->modifiers)); + + if (!have_exact) + { + g_slist_free (results); + results = NULL; + } + + have_exact = TRUE; + results = g_slist_prepend (results, entry->value); + } + + if (!have_exact) + { + for (i = 0; i < entry->n_keys; i++) + { + if (entry->keys[i].keycode == hardware_keycode && + entry->keys[i].level == level) /* Match for all but group */ + { + GTK_NOTE (KEYBINDINGS, + g_message (" found group = %d, level = %d", + entry->keys[i].group, entry->keys[i].level)); + results = g_slist_prepend (results, entry->value); + break; + } + } + } + } + + tmp_list = tmp_list->next; + } + } + + return results; +} + +/** + * _gtk_key_hash_lookup_keyval: + * @key_hash: a #GtkKeyHash + * @event: a #GtkEvent + * + * Looks up the best matching entry or entries in the hash table for a + * given keyval/modifiers pair. It's better to use + * _gtk_key_hash_lookup() if you have the original #GdkEventKey + * available. + * + * Return value: A #GSList of all matching entries. + **/ +GSList * +_gtk_key_hash_lookup_keyval (GtkKeyHash *key_hash, + guint keyval, + GdkModifierType modifiers) +{ + GdkKeymapKey *keys; + gint n_keys; + GSList *results = NULL; + + /* Find some random keycode for this keycode + */ + gdk_keymap_get_entries_for_keyval (key_hash->keymap, keyval, + &keys, &n_keys); + + if (n_keys) + { + GSList *entries = g_hash_table_lookup (key_hash->keycode_hash, GUINT_TO_POINTER (keys[0].keycode)); + + while (entries) + { + GtkKeyHashEntry *entry = entries->data; + + if (entry->keyval == keyval && entry->modifiers == modifiers) + results = g_slist_prepend (results, entry->value); + + entries = entries->next; + } + } + + g_free (keys); + + return results; +} diff --git a/gtk/gtkkeyhash.h b/gtk/gtkkeyhash.h new file mode 100644 index 0000000000..d02c9f5f66 --- /dev/null +++ b/gtk/gtkkeyhash.h @@ -0,0 +1,50 @@ +/* gtkkeyhash.h: Keymap aware matching of key bindings + * + * GTK - The GIMP Toolkit + * Copyright (C) 2002, Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTK_KEY_HASH_H__ +#define __GTK_KEY_HASH_H__ + +#include + +G_BEGIN_DECLS + +typedef struct _GtkKeyHash GtkKeyHash; + +GtkKeyHash *_gtk_key_hash_new (GdkKeymap *keymap, + GDestroyNotify item_destroy_notify); +void _gtk_key_hash_add_entry (GtkKeyHash *key_hash, + guint keyval, + GdkModifierType modifiers, + gpointer value); +void _gtk_key_hash_remove_entry (GtkKeyHash *key_hash, + gpointer value); +GSList * _gtk_key_hash_lookup (GtkKeyHash *key_hash, + guint16 hardware_keycode, + GdkModifierType state, + gint group); +GSList * _gtk_key_hash_lookup_keyval (GtkKeyHash *key_hash, + guint keyval, + GdkModifierType modifiers); +void _gtk_key_hash_free (GtkKeyHash *key_hash); + +G_END_DECLS + +#endif /* __GTK_KEY_HASH_H__ */ diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index b587f953cd..44e259fd43 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -1827,7 +1827,7 @@ gtk_label_set_uline_text_internal (GtkLabel *label, { *pattern_dest++ = '_'; if (accel_key == GDK_VoidSymbol) - accel_key = gdk_keyval_to_lower (c); + accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c)); } while (src < next_src) diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index 2c3f202173..2b659a4677 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -155,7 +155,8 @@ static const GDebugKey gtk_debug_keys[] = { {"plugsocket", GTK_DEBUG_PLUGSOCKET}, {"text", GTK_DEBUG_TEXT}, {"tree", GTK_DEBUG_TREE}, - {"updates", GTK_DEBUG_UPDATES} + {"updates", GTK_DEBUG_UPDATES}, + {"keybindings", GTK_DEBUG_KEYBINDINGS} }; static const guint gtk_ndebug_keys = sizeof (gtk_debug_keys) / sizeof (GDebugKey); diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c index 269d796c09..90aef82284 100644 --- a/gtk/gtknotebook.c +++ b/gtk/gtknotebook.c @@ -294,9 +294,6 @@ add_tab_bindings (GtkBindingSet *binding_set, gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab, modifiers, "move_focus_out", 1, GTK_TYPE_DIRECTION_TYPE, direction); - gtk_binding_entry_add_signal (binding_set, GDK_ISO_Left_Tab, modifiers, - "move_focus_out", 1, - GTK_TYPE_DIRECTION_TYPE, direction); } static void diff --git a/gtk/gtkpaned.c b/gtk/gtkpaned.c index 69141b490f..85098727c9 100644 --- a/gtk/gtkpaned.c +++ b/gtk/gtkpaned.c @@ -145,9 +145,6 @@ add_tab_bindings (GtkBindingSet *binding_set, gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab, modifiers, "cycle_handle_focus", 1, G_TYPE_BOOLEAN, reverse); - gtk_binding_entry_add_signal (binding_set, GDK_ISO_Left_Tab, modifiers, - "cycle_handle_focus", 1, - G_TYPE_BOOLEAN, reverse); } static void diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c index 9f8d4c984c..b22282e687 100644 --- a/gtk/gtkscrolledwindow.c +++ b/gtk/gtkscrolledwindow.c @@ -191,9 +191,6 @@ add_tab_bindings (GtkBindingSet *binding_set, gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab, modifiers, "move_focus_out", 1, GTK_TYPE_DIRECTION_TYPE, direction); - gtk_binding_entry_add_signal (binding_set, GDK_ISO_Left_Tab, modifiers, - "move_focus_out", 1, - GTK_TYPE_DIRECTION_TYPE, direction); } static void diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index 2014aa368a..328a0c44be 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -933,9 +933,6 @@ gtk_text_view_class_init (GtkTextViewClass *klass) gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab, GDK_CONTROL_MASK, "move_focus", 1, GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD); - gtk_binding_entry_add_signal (binding_set, GDK_ISO_Left_Tab, GDK_CONTROL_MASK, - "move_focus", 1, - GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD); gtk_binding_entry_add_signal (binding_set, GDK_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK, "move_focus", 1, @@ -943,9 +940,6 @@ gtk_text_view_class_init (GtkTextViewClass *klass) gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK, "move_focus", 1, GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD); - gtk_binding_entry_add_signal (binding_set, GDK_ISO_Left_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK, - "move_focus", 1, - GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD); } static void @@ -3641,8 +3635,7 @@ gtk_text_view_key_press_event (GtkWidget *widget, GdkEventKey *event) } /* Pass through Tab as literal tab, unless Control is held down */ else if ((event->keyval == GDK_Tab || - event->keyval == GDK_KP_Tab || - event->keyval == GDK_ISO_Left_Tab) && + event->keyval == GDK_KP_Tab) && !(event->state & GDK_CONTROL_MASK)) { /* If the text isn't editable, move the focus instead */ diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 293cd6f625..af9719e714 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -2871,9 +2871,7 @@ gtk_widget_real_key_press_event (GtkWidget *widget, gboolean handled = FALSE; if (!handled) - handled = gtk_bindings_activate (GTK_OBJECT (widget), - event->keyval, - event->state); + handled = _gtk_bindings_activate_event (GTK_OBJECT (widget), event); return handled; } @@ -2885,9 +2883,7 @@ gtk_widget_real_key_release_event (GtkWidget *widget, gboolean handled = FALSE; if (!handled) - handled = gtk_bindings_activate (GTK_OBJECT (widget), - event->keyval, - event->state | GDK_RELEASE_MASK); + handled = _gtk_bindings_activate_event (GTK_OBJECT (widget), event); return handled; } diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 597ca48f4d..a031e8bad3 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -35,6 +35,7 @@ #include "gtkwindow.h" #include "gtkwindow-decorate.h" #include "gtkbindings.h" +#include "gtkkeyhash.h" #include "gtkmain.h" #include "gtkiconfactory.h" #include "gtkintl.h" @@ -174,6 +175,7 @@ static void gtk_window_real_activate_default (GtkWindow *window); static void gtk_window_real_activate_focus (GtkWindow *window); static void gtk_window_move_focus (GtkWindow *window, GtkDirectionType dir); +static void gtk_window_keys_changed (GtkWindow *window); static void gtk_window_read_rcfiles (GtkWidget *widget, GdkEventClient *event); static void gtk_window_paint (GtkWidget *widget, @@ -222,7 +224,12 @@ static void gtk_window_set_default_size_internal (GtkWindow *window, static void gtk_window_realize_icon (GtkWindow *window); static void gtk_window_unrealize_icon (GtkWindow *window); -static void gtk_window_notify_keys_changed (GtkWindow *window); + +static void gtk_window_notify_keys_changed (GtkWindow *window); +static gboolean gtk_window_activate_key (GtkWindow *window, + GdkEventKey *event); +static GtkKeyHash *gtk_window_get_key_hash (GtkWindow *window); +static void gtk_window_free_key_hash (GtkWindow *window); static GSList *toplevel_list = NULL; static GHashTable *mnemonic_hash_table = NULL; @@ -308,9 +315,6 @@ add_tab_bindings (GtkBindingSet *binding_set, gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab, modifiers, "move_focus", 1, GTK_TYPE_DIRECTION_TYPE, direction); - gtk_binding_entry_add_signal (binding_set, GDK_ISO_Left_Tab, modifiers, - "move_focus", 1, - GTK_TYPE_DIRECTION_TYPE, direction); } static void @@ -388,7 +392,7 @@ gtk_window_class_init (GtkWindowClass *klass) klass->activate_default = gtk_window_real_activate_default; klass->activate_focus = gtk_window_real_activate_focus; klass->move_focus = gtk_window_move_focus; - klass->keys_changed = NULL; + klass->keys_changed = gtk_window_keys_changed; /* Construct */ g_object_class_install_property (gobject_class, @@ -2901,7 +2905,9 @@ gtk_window_destroy (GtkObject *object) if (window->group) gtk_window_group_remove_window (window->group, window); - GTK_OBJECT_CLASS (parent_class)->destroy (object); + gtk_window_free_key_hash (window); + + GTK_OBJECT_CLASS (parent_class)->destroy (object); } static gboolean @@ -3525,13 +3531,10 @@ gtk_window_key_press_event (GtkWidget *widget, handled = FALSE; + /* Check for mnemonics and accelerators + */ if (!handled) - handled = gtk_window_mnemonic_activate (window, - event->keyval, - event->state); - - if (!handled) - handled = gtk_accel_groups_activate (G_OBJECT (window), event->keyval, event->state); + handled = gtk_window_activate_key (window, event); if (!handled) { @@ -5676,3 +5679,183 @@ gtk_window_parse_geometry (GtkWindow *window, return result != 0; } + +typedef void (*GtkWindowKeysForeach) (GtkWindow *window, + guint keyval, + GdkModifierType modifiers, + gboolean is_mnemonic, + gpointer data); + +static void +gtk_window_mnemonic_hash_foreach (gpointer key, + gpointer value, + gpointer data) +{ + struct { + GtkWindow *window; + GtkWindowKeysForeach func; + gpointer func_data; + } *info = data; + + GtkWindowMnemonic *mnemonic = value; + + if (mnemonic->window == info->window) + (*info->func) (info->window, mnemonic->keyval, info->window->mnemonic_modifier, TRUE, info->func_data); +} + +static void +gtk_window_keys_foreach (GtkWindow *window, + GtkWindowKeysForeach func, + gpointer func_data) +{ + GSList *groups; + + struct { + GtkWindow *window; + GtkWindowKeysForeach func; + gpointer func_data; + } info; + + info.window = window; + info.func = func; + info.func_data = func_data; + + g_hash_table_foreach (mnemonic_hash_table, + gtk_window_mnemonic_hash_foreach, + &info); + + groups = gtk_accel_groups_from_object (G_OBJECT (window)); + while (groups) + { + GtkAccelGroup *group = groups->data; + gint i; + + for (i = 0; i < group->n_accels; i++) + { + GtkAccelKey *key = &group->priv_accels[i].key; + + if (key->accel_key) + (*func) (window, key->accel_key, key->accel_mods, FALSE, func_data); + } + + groups = groups->next; + } +} + +static void +gtk_window_keys_changed (GtkWindow *window) +{ + gtk_window_free_key_hash (window); + gtk_window_get_key_hash (window); +} + +typedef struct _GtkWindowKeyEntry GtkWindowKeyEntry; + +struct _GtkWindowKeyEntry +{ + guint keyval; + guint modifiers; + gboolean is_mnemonic; +}; + +static void +add_to_key_hash (GtkWindow *window, + guint keyval, + GdkModifierType modifiers, + gboolean is_mnemonic, + gpointer data) +{ + GtkKeyHash *key_hash = data; + + GtkWindowKeyEntry *entry = g_new (GtkWindowKeyEntry, 1); + + entry->keyval = keyval; + entry->modifiers = modifiers; + entry->is_mnemonic = is_mnemonic; + + /* GtkAccelGroup stores lowercased accelerators. To deal + * with this, if was specified, uppercase. + */ + if (modifiers & GDK_SHIFT_MASK) + { + if (keyval == GDK_Tab) + keyval = GDK_ISO_Left_Tab; + else + keyval = gdk_keyval_to_upper (keyval); + } + + _gtk_key_hash_add_entry (key_hash, keyval, entry->modifiers, entry); +} + +static GtkKeyHash * +gtk_window_get_key_hash (GtkWindow *window) +{ + GtkKeyHash *key_hash = g_object_get_data (G_OBJECT (window), "gtk-window-key-hash"); + if (key_hash) + return key_hash; + + key_hash = _gtk_key_hash_new (gdk_keymap_get_default(), (GDestroyNotify)g_free); + gtk_window_keys_foreach (window, add_to_key_hash, key_hash); + g_object_set_data (G_OBJECT (window), "gtk-window-key-hash", key_hash); + + return key_hash; +} + +static void +gtk_window_free_key_hash (GtkWindow *window) +{ + GtkKeyHash *key_hash = g_object_get_data (G_OBJECT (window), "gtk-window-key-hash"); + if (key_hash) + { + _gtk_key_hash_free (key_hash); + g_object_set_data (G_OBJECT (window), "gtk-window-key-hash", NULL); + } +} + +static gboolean +gtk_window_activate_key (GtkWindow *window, + GdkEventKey *event) +{ + GtkKeyHash *key_hash = g_object_get_data (G_OBJECT (window), "gtk-window-key-hash"); + GtkWindowKeyEntry *found_entry = NULL; + + if (!key_hash) + { + gtk_window_keys_changed (window); + key_hash = g_object_get_data (G_OBJECT (window), "gtk-window-key-hash"); + } + + if (key_hash) + { + GSList *entries = _gtk_key_hash_lookup (key_hash, + event->hardware_keycode, + event->state & gtk_accelerator_get_default_mod_mask (), + event->group); + GSList *tmp_list; + + for (tmp_list = entries; tmp_list; tmp_list = tmp_list->next) + { + GtkWindowKeyEntry *entry = tmp_list->data; + if (entry->is_mnemonic) + { + found_entry = entry; + break; + } + } + + if (!found_entry && entries) + found_entry = entries->data; + + g_slist_free (entries); + } + + if (found_entry) + { + if (found_entry->is_mnemonic) + return gtk_window_mnemonic_activate (window, found_entry->keyval, found_entry->modifiers); + else + return gtk_accel_groups_activate (G_OBJECT (window), found_entry->keyval, found_entry->modifiers); + } + else + return FALSE; +} diff --git a/gtk/gtkwindow.h b/gtk/gtkwindow.h index 2ebfaea759..d6c97376dc 100644 --- a/gtk/gtkwindow.h +++ b/gtk/gtkwindow.h @@ -125,7 +125,8 @@ struct _GtkWindowClass void (* activate_focus) (GtkWindow *window); void (* activate_default) (GtkWindow *window); void (* move_focus) (GtkWindow *window, - GtkDirectionType direction); + GtkDirectionType direction); + void (*keys_changed) (GtkWindow *window); }; diff --git a/tests/testgtk.c b/tests/testgtk.c index 7bc2a47101..216548cf40 100644 --- a/tests/testgtk.c +++ b/tests/testgtk.c @@ -3433,6 +3433,83 @@ create_item_factory (void) gtk_widget_destroy (window); } +static GtkWidget * +accel_button_new (GtkAccelGroup *accel_group, + const gchar *text, + const gchar *accel) +{ + guint keyval; + GdkModifierType modifiers; + GtkWidget *button; + GtkWidget *label; + + gtk_accelerator_parse (accel, &keyval, &modifiers); + g_assert (keyval); + + button = gtk_button_new (); + gtk_widget_add_accelerator (button, "activate", accel_group, + keyval, modifiers, GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED); + + label = gtk_accel_label_new (text); + gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label), button); + gtk_widget_show (label); + + gtk_container_add (GTK_CONTAINER (button), label); + + return button; +} + +static void +create_key_lookup (void) +{ + static GtkWidget *window = NULL; + + if (!window) + { + GtkAccelGroup *accel_group = gtk_accel_group_new (); + GtkWidget *button; + + window = gtk_dialog_new_with_buttons ("Key Lookup", NULL, 0, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + NULL); + + /* We have to expand it so the accel labels will draw their labels + */ + gtk_window_set_default_size (GTK_WINDOW (window), 300, -1); + + gtk_window_add_accel_group (GTK_WINDOW (window), accel_group); + + button = gtk_button_new_with_mnemonic ("Button 1 (_a)"); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), button, FALSE, FALSE, 0); + button = gtk_button_new_with_mnemonic ("Button 2 (_A)"); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), button, FALSE, FALSE, 0); + button = gtk_button_new_with_mnemonic ("Button 3 (_ф)"); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), button, FALSE, FALSE, 0); + button = gtk_button_new_with_mnemonic ("Button 4 (_Ф)"); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), button, FALSE, FALSE, 0); + button = gtk_button_new_with_mnemonic ("Button 6 (_b)"); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), button, FALSE, FALSE, 0); + button = accel_button_new (accel_group, "Button 7", "b"); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), button, FALSE, FALSE, 0); + button = accel_button_new (accel_group, "Button 8", "d"); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), button, FALSE, FALSE, 0); + button = accel_button_new (accel_group, "Button 9", "Cyrillic_ve"); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), button, FALSE, FALSE, 0); + button = gtk_button_new_with_mnemonic ("Button 10 (_1)"); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), button, FALSE, FALSE, 0); + button = gtk_button_new_with_mnemonic ("Button 11 (_!)"); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), button, FALSE, FALSE, 0); + + g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window); + g_signal_connect (window, "response", G_CALLBACK (gtk_object_destroy), NULL); + + gtk_widget_show_all (window); + } + else + gtk_widget_destroy (window); +} + + /* create_modal_window */ @@ -11350,6 +11427,7 @@ struct { { "image from drawable", create_get_image }, { "image", create_image }, { "item factory", create_item_factory }, + { "key lookup", create_key_lookup }, { "labels", create_labels }, { "layout", create_layout }, { "list", create_list },