diff --git a/ChangeLog b/ChangeLog index b4e770c100..810d35b375 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2009-01-31 Matthias Clasen + + Bug 162726 – Multiple Latin layouts in XKB break keyboard shortcuts + + * gtk/gtkkeyhash.c (_gtk_key_hash_lookup): Change the handling of + fuzzy matches: As long there are any exact matches, only exact + matches are returned. If there are no exact matches, fuzzy matches + will be returned, as long as they are not shadowing a possible exact + match. This means that fuzzy matches won't be considered if their + keyval is present in the current group. Problem reported by + many people, patch by Simos Xenitellis. + 2009-01-31 Matthias Clasen * NEWS: Updates diff --git a/gtk/gtkkeyhash.c b/gtk/gtkkeyhash.c index 06440994f9..3de8830ebe 100644 --- a/gtk/gtkkeyhash.c +++ b/gtk/gtkkeyhash.c @@ -304,6 +304,54 @@ sort_lookup_results (GSList *slist) return g_slist_sort (slist, lookup_result_compare); } +static gint +lookup_result_compare_by_keyval (gconstpointer a, + gconstpointer b) +{ + const GtkKeyHashEntry *entry_a = a; + const GtkKeyHashEntry *entry_b = b; + + if (entry_a->keyval < entry_b->keyval) + return -1; + else if (entry_a->keyval > entry_b->keyval) + return 1; + else + return 0; +} + +static GSList * +sort_lookup_results_by_keyval (GSList *slist) +{ + return g_slist_sort (slist, lookup_result_compare_by_keyval); +} + +/* Return true if keyval is defined in keyboard group + */ +static gboolean +keyval_in_group (GdkKeymap *keymap, + guint keyval, + gint group) +{ + GtkKeyHashEntry entry; + gint i; + + gdk_keymap_get_entries_for_keyval (keymap, + keyval, + &entry.keys, &entry.n_keys); + + for (i = 0; i < entry.n_keys; i++) + { + if (entry.keys[i].group == group) + { + g_free (entry.keys); + return TRUE; + } + } + + g_free (entry.keys); + return FALSE; +} + /** * _gtk_key_hash_lookup: * @key_hash: a #GtkKeyHash @@ -317,10 +365,15 @@ sort_lookup_results (GSList *slist) * a given event. The results are sorted so that entries with less * modifiers come before entries with more modifiers. * - * 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.) + * The matches returned by this function can be exact (i.e. keycode, level + * and group all match) or fuzzy (i.e. keycode and level match, but group + * does not). As long there are any exact matches, only exact matches + * are returned. If there are no exact matches, fuzzy matches will be + * returned, as long as they are not shadowing a possible exact match. + * This means that fuzzy matches won't be considered if their keyval is + * present in the current group. + * + * Return value: A #GSList of matching entries. **/ GSList * _gtk_key_hash_lookup (GtkKeyHash *key_hash, @@ -409,6 +462,31 @@ _gtk_key_hash_lookup (GtkKeyHash *key_hash, } } + if (!have_exact && results) + { + /* If there are fuzzy matches, check that the current group doesn't also + * define these keyvals; if yes, discard results because a widget up in + * the stack may have an exact match and we don't want to 'steal' it. + */ + gint oldkeyval; + GtkKeyHashEntry *keyhashentry; + + results = sort_lookup_results_by_keyval (results); + for (l = results; l; l = l->next) + { + keyhashentry = l->data; + if (l == results || oldkeyval != keyhashentry->keyval) + { + oldkeyval = keyhashentry->keyval; + if (keyval_in_group (key_hash->keymap, oldkeyval, group)) + { + g_slist_free (results); + return NULL; + } + } + } + } + results = sort_lookup_results (results); for (l = results; l; l = l->next) l->data = ((GtkKeyHashEntry *)l->data)->value; @@ -442,7 +520,7 @@ _gtk_key_hash_lookup_keyval (GtkKeyHash *key_hash, if (!keyval) /* Key without symbol */ return NULL; - /* Find some random keycode for this keycode + /* Find some random keycode for this keyval */ gdk_keymap_get_entries_for_keyval (key_hash->keymap, keyval, &keys, &n_keys);