X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkbindings.c;h=4cbbeaf6aadbb67cd9e0c9981f00b256363ef54a;hb=67f5e595a796a8321d6dc7737c58476564998c07;hp=ad75a0228ba6af7449e5e06de577bdb6d721a187;hpb=b8368a54a2b395617912586d52bba5899c4d2669;p=~andy%2Fgtk diff --git a/gtk/gtkbindings.c b/gtk/gtkbindings.c index ad75a0228..4cbbeaf6a 100644 --- a/gtk/gtkbindings.c +++ b/gtk/gtkbindings.c @@ -1,56 +1,202 @@ /* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * - * GtkBindingSet: Keybinding manager for GtkObjects. + * GtkBindingSet: Keybinding manager for GObjects. * Copyright (C) 1998 Tim Janik * * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public + * 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 - * Library General Public License for more details. + * 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 Library 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. + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . */ -#include -#include + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "config.h" +#include #include -#include "gtkbindings.h" -#include "gtksignal.h" + +#include "gtkbindingsprivate.h" +#include "gtkkeyhash.h" +#include "gtkstylecontext.h" #include "gtkwidget.h" +#include "gtkintl.h" +/** + * SECTION:gtkbindings + * @Title: Bindings + * @Short_description: Key bindings for individual widgets + * @See_also: Keyboard Accelerators, Mnemonics, #GtkCssProvider + * + * #GtkBindingSet provides a mechanism for configuring GTK+ key bindings + * through CSS files. This eases key binding adjustments for application + * developers as well as users and provides GTK+ users or administrators + * with high key binding configurability which requires no application + * or toolkit side changes. + * + * + * Installing a key binding + * + * A CSS file binding consists of a 'binding-set' definition and a match + * statement to apply the binding set to specific widget types. Details + * on the matching mechanism are described under + * Selectors + * in the #GtkCssProvider documentation. Inside the binding set definition, + * key combinations are bound to one or more specific signal emissions on + * the target widget. Key combinations are strings consisting of an optional + * #GdkModifierType name and key names + * such as those defined in <gdk/gdkkeysyms.h> + * or returned from gdk_keyval_name(), they have to be parsable by + * gtk_accelerator_parse(). Specifications of signal emissions consist + * of a string identifying the signal name, and a list of signal specific + * arguments in parenthesis. + * + * + * For example for binding Control and the left or right cursor keys + * of a #GtkEntry widget to the #GtkEntry::move-cursor signal (so movement + * occurs in 3-character steps), the following binding can be used: + * + * @binding-set MoveCursor3 + * { + * bind "<Control>Right" { "move-cursor" (visual-positions, 3, 0) }; + * bind "<Control>Left" { "move-cursor" (visual-positions, -3, 0) }; + * }; + * GtkEntry + * { + * gtk-key-bindings: MoveCursor3 + * } + * + * + * + * + * Unbinding existing key bindings + * + * GTK+ already defines a number of useful bindings for the widgets + * it provides. Because custom bindings set up in CSS files take + * precedence over the default bindings shipped with GTK+, overriding + * existing bindings as demonstrated in + * Installing a key binding + * works as expected. The same mechanism can not be used to "unbind" + * existing bindings, however. + * + * @binding-set MoveCursor3 + * { + * bind "<Control>Right" { }; + * bind "<Control>Left" { }; + * }; + * GtkEntry + * { + * gtk-key-bindings: MoveCursor3 + * } + * + * The above example will not have the desired effect of causing + * "<Control>Right" and "<Control>Left" key presses to + * be ignored by GTK+. Instead, it just causes any existing bindings + * from the bindings set "MoveCursor3" to be deleted, so when + * "<Control>Right" or "<Control>Left" are pressed, no + * binding for these keys is found in binding set "MoveCursor3". + * GTK+ will thus continue to search for matching key bindings, and will + * eventually lookup and find the default GTK+ bindings for entries which + * implement word movement. To keep GTK+ from activating its default + * bindings, the "unbind" keyword can be used like this: + * + * @binding-set MoveCursor3 + * { + * unbind "<Control>Right"; + * unbind "<Control>Left"; + * }; + * GtkEntry + * { + * gtk-key-bindings: MoveCursor3 + * } + * + * Now, GTK+ will find a match when looking up "<Control>Right" + * and "<Control>Left" key presses before it resorts to its default + * bindings, and the match instructs it to abort ("unbind") the search, + * so the key presses are not consumed by this widget. As usual, further + * processing of the key presses, e.g. by an entry's parent widget, is + * now possible. + * + * + */ /* --- defines --- */ -#define BINDING_MOD_MASK() (gtk_accelerator_get_default_mod_mask ()) +#define BINDING_MOD_MASK() (gtk_accelerator_get_default_mod_mask () | GDK_RELEASE_MASK) + + +#define GTK_TYPE_IDENTIFIER (gtk_identifier_get_type ()) +GType gtk_identifier_get_type (void) G_GNUC_CONST; +/* --- structures --- */ +typedef struct { + GtkPathType type; + GPatternSpec *pspec; + gpointer user_data; + guint seq_id; +} PatternSpec; + +typedef enum { + GTK_BINDING_TOKEN_BIND, + GTK_BINDING_TOKEN_UNBIND +} GtkBindingTokens; + /* --- variables --- */ -static GHashTable *binding_entry_hash_table = NULL; -static GSList *binding_set_list = NULL; -static const gchar *key_class_binding_set = "gtk-class-binding-set"; -static guint key_id_class_binding_set = 0; +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; /* --- functions --- */ +GType +gtk_identifier_get_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + { + GTypeInfo tinfo = { 0, }; + our_type = g_type_register_static (G_TYPE_STRING, I_("GtkIdentifier"), &tinfo, 0); + } + + return our_type; +} + +static void +pattern_spec_free (PatternSpec *pspec) +{ + if (pspec->pspec) + g_pattern_spec_free (pspec->pspec); + g_free (pspec); +} + static GtkBindingSignal* binding_signal_new (const gchar *signal_name, - guint n_args) + guint n_args) { GtkBindingSignal *signal; - - signal = g_new (GtkBindingSignal, 1); + + signal = (GtkBindingSignal *) g_slice_alloc0 (sizeof (GtkBindingSignal) + n_args * sizeof (GtkBindingArg)); signal->next = NULL; - signal->signal_name = g_strdup (signal_name); + signal->signal_name = (gchar *)g_intern_string (signal_name); signal->n_args = n_args; - signal->args = g_new0 (GtkBindingArg, n_args); - + signal->args = (GtkBindingArg *)(signal + 1); + return signal; } @@ -58,15 +204,13 @@ static void binding_signal_free (GtkBindingSignal *sig) { guint i; - + for (i = 0; i < sig->n_args; i++) { - if (sig->args[i].arg_type == GTK_BINDING_ARG_STRING) - g_free (sig->args[i].d.string_data); + if (G_TYPE_FUNDAMENTAL (sig->args[i].arg_type) == G_TYPE_STRING) + g_free (sig->args[i].d.string_data); } - g_free (sig->args); - g_free (sig->signal_name); - g_free (sig); + g_slice_free1 (sizeof (GtkBindingSignal) + sig->n_args * sizeof (GtkBindingArg), sig); } static guint @@ -83,7 +227,7 @@ binding_entry_hash (gconstpointer key) static gint binding_entries_compare (gconstpointer a, - gconstpointer b) + gconstpointer b) { register const GtkBindingEntry *ea = a; register const GtkBindingEntry *eb = b; @@ -91,13 +235,83 @@ 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_KEY_Tab) + keyval = GDK_KEY_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, - guint modifiers) +binding_entry_new (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers) { + GSList *tmp_list; GtkBindingEntry *entry; - + if (!binding_entry_hash_table) binding_entry_hash_table = g_hash_table_new (binding_entry_hash, binding_entries_compare); @@ -107,18 +321,23 @@ binding_entry_new (GtkBindingSet *binding_set, entry->binding_set = binding_set, entry->destroyed = FALSE; entry->in_emission = FALSE; + entry->marks_unbound = FALSE; entry->signals = NULL; entry->set_next = binding_set->entries; binding_set->entries = entry; entry->hash_next = g_hash_table_lookup (binding_entry_hash_table, entry); - g_hash_table_freeze (binding_entry_hash_table); 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); - g_hash_table_thaw (binding_entry_hash_table); - + + 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; } @@ -128,17 +347,17 @@ binding_entry_free (GtkBindingEntry *entry) GtkBindingSignal *sig; g_assert (entry->set_next == NULL && - entry->hash_next == NULL && - entry->in_emission == FALSE && - entry->destroyed == TRUE); + entry->hash_next == NULL && + entry->in_emission == FALSE && + entry->destroyed == TRUE); entry->destroyed = FALSE; - + sig = entry->signals; while (sig) { GtkBindingSignal *prev; - + prev = sig; sig = prev->next; binding_signal_free (prev); @@ -153,6 +372,7 @@ binding_entry_destroy (GtkBindingEntry *entry) register GtkBindingEntry *tmp; GtkBindingEntry *begin; register GtkBindingEntry *last; + GSList *tmp_list; /* unlink from binding set */ @@ -161,18 +381,18 @@ binding_entry_destroy (GtkBindingEntry *entry) while (tmp) { if (tmp == entry) - { - if (last) - last->set_next = entry->set_next; - else - entry->binding_set->entries = entry->set_next; - break; - } + { + if (last) + last->set_next = entry->set_next; + else + entry->binding_set->entries = entry->set_next; + break; + } last = tmp; tmp = last->set_next; } entry->set_next = NULL; - + o_entry = g_hash_table_lookup (binding_entry_hash_table, entry); begin = o_entry; last = NULL; @@ -180,26 +400,30 @@ binding_entry_destroy (GtkBindingEntry *entry) while (tmp) { if (tmp == entry) - { - if (last) - last->hash_next = entry->hash_next; - else - begin = entry->hash_next; - break; - } + { + if (last) + last->hash_next = entry->hash_next; + else + begin = entry->hash_next; + break; + } last = tmp; tmp = last->hash_next; } entry->hash_next = NULL; - + if (!begin) g_hash_table_remove (binding_entry_hash_table, entry); else if (begin != o_entry) { - g_hash_table_freeze (binding_entry_hash_table); g_hash_table_remove (binding_entry_hash_table, entry); g_hash_table_insert (binding_entry_hash_table, begin, begin); - g_hash_table_thaw (binding_entry_hash_table); + } + + 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; @@ -209,34 +433,19 @@ binding_entry_destroy (GtkBindingEntry *entry) } static GtkBindingEntry* -binding_ht_lookup_list (guint keyval, - guint 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, - guint modifiers) +binding_ht_lookup_entry (GtkBindingSet *set, + guint keyval, + GdkModifierType modifiers) { GtkBindingEntry lookup_entry = { 0 }; GtkBindingEntry *entry; - + if (!binding_entry_hash_table) return NULL; - + lookup_entry.keyval = keyval; lookup_entry.modifiers = modifiers; - + entry = g_hash_table_lookup (binding_entry_hash_table, &lookup_entry); for (; entry; entry = entry->hash_next) if (entry->binding_set == set) @@ -246,352 +455,528 @@ binding_ht_lookup_entry (GtkBindingSet *set, } static gboolean -binding_compose_params (GtkBindingArg *args, - GtkSignalQuery *query, - GtkArg **params_p) +binding_compose_params (GObject *object, + GtkBindingArg *args, + GSignalQuery *query, + GValue **params_p) { - GtkArg *params; - const GtkType *types; + GValue *params; + const GType *types; guint i; gboolean valid; - - params = g_new0 (GtkArg, query->nparams); + + params = g_new0 (GValue, query->n_params + 1); *params_p = params; - - types = query->params; + + /* The instance we emit on is the first object in the array + */ + g_value_init (params, G_TYPE_OBJECT); + g_value_set_object (params, G_OBJECT (object)); + params++; + + types = query->param_types; valid = TRUE; - for (i = 0; i < query->nparams && valid; i++) + for (i = 1; i < query->n_params + 1 && valid; i++) { - params->type = *types; - params->name = NULL; - switch (args->arg_type) - { - case GTK_BINDING_ARG_STRING: - if (params->type == GTK_TYPE_STRING) - GTK_VALUE_STRING (*params) = args->d.string_data; - else - valid = FALSE; - break; - case GTK_BINDING_ARG_DOUBLE: - if (params->type == GTK_TYPE_FLOAT) - GTK_VALUE_FLOAT (*params) = args->d.double_data; - else if (params->type == GTK_TYPE_DOUBLE) - GTK_VALUE_DOUBLE (*params) = args->d.double_data; - else - valid = FALSE; - break; - case GTK_BINDING_ARG_LONG: - if (params->type == GTK_TYPE_BOOL && - (args->d.long_data == 0 || - args->d.long_data == 1)) - GTK_VALUE_BOOL (*params) = args->d.long_data; - else if (params->type == GTK_TYPE_INT) - GTK_VALUE_INT (*params) = args->d.long_data; - else if (params->type == GTK_TYPE_UINT && - args->d.long_data >= 0) - GTK_VALUE_UINT (*params) = args->d.long_data; - else if (params->type == GTK_TYPE_LONG) - GTK_VALUE_LONG (*params) = args->d.long_data; - else if (params->type == GTK_TYPE_ULONG && - args->d.long_data >= 0) - GTK_VALUE_ULONG (*params) = args->d.long_data; - else if (params->type == GTK_TYPE_FLOAT) - GTK_VALUE_FLOAT (*params) = args->d.long_data; - else if (params->type == GTK_TYPE_DOUBLE) - GTK_VALUE_DOUBLE (*params) = args->d.long_data; - else - valid = FALSE; - break; - default: - valid = FALSE; - break; - } + GValue tmp_value = G_VALUE_INIT; + + g_value_init (params, *types); + + switch (G_TYPE_FUNDAMENTAL (args->arg_type)) + { + case G_TYPE_DOUBLE: + g_value_init (&tmp_value, G_TYPE_DOUBLE); + g_value_set_double (&tmp_value, args->d.double_data); + break; + case G_TYPE_LONG: + g_value_init (&tmp_value, G_TYPE_LONG); + g_value_set_long (&tmp_value, args->d.long_data); + break; + case G_TYPE_STRING: + /* gtk_rc_parse_flags/enum() has fancier parsing for this; we can't call + * that since we don't have a GParamSpec, so just do something simple + */ + if (G_TYPE_FUNDAMENTAL (*types) == G_TYPE_ENUM) + { + GEnumClass *class = G_ENUM_CLASS (g_type_class_ref (*types)); + + valid = FALSE; + + if (args->arg_type == GTK_TYPE_IDENTIFIER) + { + GEnumValue *enum_value = NULL; + enum_value = g_enum_get_value_by_name (class, args->d.string_data); + if (!enum_value) + enum_value = g_enum_get_value_by_nick (class, args->d.string_data); + if (enum_value) + { + g_value_init (&tmp_value, *types); + g_value_set_enum (&tmp_value, enum_value->value); + valid = TRUE; + } + } + + g_type_class_unref (class); + } + /* This is just a hack for compatibility with GTK+-1.2 where a string + * could be used for a single flag value / without the support for multiple + * values in gtk_rc_parse_flags(), this isn't very useful. + */ + else if (G_TYPE_FUNDAMENTAL (*types) == G_TYPE_FLAGS) + { + GFlagsClass *class = G_FLAGS_CLASS (g_type_class_ref (*types)); + + valid = FALSE; + + if (args->arg_type == GTK_TYPE_IDENTIFIER) + { + GFlagsValue *flags_value = NULL; + flags_value = g_flags_get_value_by_name (class, args->d.string_data); + if (!flags_value) + flags_value = g_flags_get_value_by_nick (class, args->d.string_data); + if (flags_value) + { + g_value_init (&tmp_value, *types); + g_value_set_flags (&tmp_value, flags_value->value); + valid = TRUE; + } + } + + g_type_class_unref (class); + } + else + { + g_value_init (&tmp_value, G_TYPE_STRING); + g_value_set_static_string (&tmp_value, args->d.string_data); + } + break; + default: + valid = FALSE; + break; + } + + if (valid) + { + if (!g_value_transform (&tmp_value, params)) + valid = FALSE; + + g_value_unset (&tmp_value); + } + types++; params++; args++; } - + if (!valid) { + guint j; + + for (j = 0; j < i; j++) + g_value_unset (&(*params_p)[j]); + g_free (*params_p); *params_p = NULL; } - + return valid; } -static void -gtk_binding_entry_activate (GtkBindingEntry *entry, - GtkObject *object) +static gboolean +gtk_binding_entry_activate (GtkBindingEntry *entry, + GObject *object) { GtkBindingSignal *sig; gboolean old_emission; - + gboolean handled = FALSE; + gint i; + old_emission = entry->in_emission; entry->in_emission = TRUE; - - gtk_object_ref (object); - + + g_object_ref (object); + for (sig = entry->signals; sig; sig = sig->next) { - GtkSignalQuery *query; + GSignalQuery query; guint signal_id; - GtkArg *params = NULL; + GValue *params = NULL; + GValue return_val = G_VALUE_INIT; gchar *accelerator = NULL; - - signal_id = gtk_signal_lookup (sig->signal_name, GTK_OBJECT_TYPE (object)); + + signal_id = g_signal_lookup (sig->signal_name, G_OBJECT_TYPE (object)); if (!signal_id) - { - accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers); - g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": " - "could not find signal \"%s\" in the `%s' class ancestry", - entry->binding_set->set_name, - accelerator, - sig->signal_name, - gtk_type_name (GTK_OBJECT_TYPE (object))); - g_free (accelerator); - continue; - } - - query = gtk_signal_query (signal_id); - if (query->nparams != sig->n_args || - query->return_val != GTK_TYPE_NONE || - !binding_compose_params (sig->args, query, ¶ms)) - { - accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers); - g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": " - "signature mismatch for signal \"%s\" in the `%s' class ancestry", - entry->binding_set->set_name, - accelerator, - sig->signal_name, - gtk_type_name (GTK_OBJECT_TYPE (object))); - } - else if (!(query->signal_flags & GTK_RUN_ACTION)) - { - accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers); - g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": " - "signal \"%s\" in the `%s' class ancestry cannot be used for action emissions", - entry->binding_set->set_name, - accelerator, - sig->signal_name, - gtk_type_name (GTK_OBJECT_TYPE (object))); - } + { + accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers); + g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": " + "could not find signal \"%s\" in the `%s' class ancestry", + entry->binding_set->set_name, + accelerator, + sig->signal_name, + g_type_name (G_OBJECT_TYPE (object))); + g_free (accelerator); + continue; + } + + g_signal_query (signal_id, &query); + if (query.n_params != sig->n_args || + (query.return_type != G_TYPE_NONE && query.return_type != G_TYPE_BOOLEAN) || + !binding_compose_params (object, sig->args, &query, ¶ms)) + { + accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers); + g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": " + "signature mismatch for signal \"%s\" in the `%s' class ancestry", + entry->binding_set->set_name, + accelerator, + sig->signal_name, + g_type_name (G_OBJECT_TYPE (object))); + } + else if (!(query.signal_flags & G_SIGNAL_ACTION)) + { + accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers); + g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": " + "signal \"%s\" in the `%s' class ancestry cannot be used for action emissions", + entry->binding_set->set_name, + accelerator, + sig->signal_name, + g_type_name (G_OBJECT_TYPE (object))); + } g_free (accelerator); - g_free (query); if (accelerator) - continue; + continue; + + if (query.return_type == G_TYPE_BOOLEAN) + g_value_init (&return_val, G_TYPE_BOOLEAN); + + g_signal_emitv (params, signal_id, 0, &return_val); - gtk_signal_emitv (object, signal_id, params); + if (query.return_type == G_TYPE_BOOLEAN) + { + if (g_value_get_boolean (&return_val)) + handled = TRUE; + g_value_unset (&return_val); + } + else + handled = TRUE; + + for (i = 0; i < query.n_params + 1; i++) + g_value_unset (¶ms[i]); g_free (params); - - if (GTK_OBJECT_DESTROYED (object) || entry->destroyed) - break; + + if (entry->destroyed) + break; } - - gtk_object_unref (object); + + g_object_unref (object); entry->in_emission = old_emission; if (entry->destroyed && !entry->in_emission) binding_entry_free (entry); + + return handled; } +/** + * gtk_binding_set_new: (skip) + * @set_name: unique name of this binding set + * + * GTK+ maintains a global list of binding sets. Each binding set has + * a unique name which needs to be specified upon creation. + * + * Return value: (transfer full): new binding set + */ GtkBindingSet* -gtk_binding_set_new (const gchar *set_name) +gtk_binding_set_new (const gchar *set_name) { GtkBindingSet *binding_set; - + g_return_val_if_fail (set_name != NULL, NULL); - + binding_set = g_new (GtkBindingSet, 1); - binding_set->set_name = g_strdup (set_name); + binding_set->set_name = (gchar *) g_intern_string (set_name); binding_set->widget_path_pspecs = NULL; binding_set->widget_class_pspecs = NULL; binding_set->class_branch_pspecs = NULL; binding_set->entries = NULL; binding_set->current = NULL; - - binding_set_list = g_slist_prepend (NULL, binding_set); - + binding_set->parsed = FALSE; + + binding_set_list = g_slist_prepend (binding_set_list, binding_set); + return binding_set; } +/** + * gtk_binding_set_by_class: (skip) + * @object_class: a valid #GObject class + * + * This function returns the binding set named after the type name of + * the passed in class structure. New binding sets are created on + * demand by this function. + * + * Return value: (transfer full): the binding set corresponding to + * @object_class + */ GtkBindingSet* gtk_binding_set_by_class (gpointer object_class) { - GtkObjectClass *class = object_class; + GObjectClass *class = object_class; GtkBindingSet* binding_set; - g_return_val_if_fail (GTK_IS_OBJECT_CLASS (class), NULL); + g_return_val_if_fail (G_IS_OBJECT_CLASS (class), NULL); if (!key_id_class_binding_set) - key_id_class_binding_set = g_dataset_force_id (key_class_binding_set); + key_id_class_binding_set = g_quark_from_static_string (key_class_binding_set); binding_set = g_dataset_id_get_data (class, key_id_class_binding_set); if (binding_set) return binding_set; - binding_set = gtk_binding_set_new (gtk_type_name (class->type)); - gtk_binding_set_add_path (binding_set, - GTK_PATH_CLASS, - gtk_type_name (class->type), - GTK_PATH_PRIO_GTK); + binding_set = gtk_binding_set_new (g_type_name (G_OBJECT_CLASS_TYPE (class))); g_dataset_id_set_data (class, key_id_class_binding_set, binding_set); return binding_set; } -GtkBindingSet* -gtk_binding_set_find (const gchar *set_name) +static GtkBindingSet* +gtk_binding_set_find_interned (const gchar *set_name) { GSList *slist; - - g_return_val_if_fail (set_name != NULL, NULL); - + for (slist = binding_set_list; slist; slist = slist->next) { GtkBindingSet *binding_set; - + binding_set = slist->data; - if (g_str_equal (binding_set->set_name, (gpointer) set_name)) - return binding_set; + if (binding_set->set_name == set_name) + return binding_set; } + return NULL; } +/** + * gtk_binding_set_find: + * @set_name: unique binding set name + * + * Find a binding set by its globally unique name. + * + * The @set_name can either be a name used for gtk_binding_set_new() + * or the type name of a class used in gtk_binding_set_by_class(). + * + * Return value: (transfer none): %NULL or the specified binding set + */ +GtkBindingSet* +gtk_binding_set_find (const gchar *set_name) +{ + g_return_val_if_fail (set_name != NULL, NULL); + + return gtk_binding_set_find_interned (g_intern_string (set_name)); +} + +/** + * gtk_binding_set_activate: + * @binding_set: a #GtkBindingSet set to activate + * @keyval: key value of the binding + * @modifiers: key modifier of the binding + * @object: object to activate when binding found + * + * Find a key binding matching @keyval and @modifiers within + * @binding_set and activate the binding on @object. + * + * Return value: %TRUE if a binding was found and activated + */ gboolean -gtk_binding_set_activate (GtkBindingSet *binding_set, - guint keyval, - guint modifiers, - GtkObject *object) +gtk_binding_set_activate (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers, + GObject *object) { GtkBindingEntry *entry; - + g_return_val_if_fail (binding_set != NULL, FALSE); - g_return_val_if_fail (object != NULL, FALSE); - g_return_val_if_fail (GTK_IS_OBJECT (object), FALSE); - + g_return_val_if_fail (G_IS_OBJECT (object), FALSE); + keyval = gdk_keyval_to_lower (keyval); modifiers = modifiers & BINDING_MOD_MASK (); - - if (!GTK_OBJECT_DESTROYED (object)) - { - entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); - if (entry) - { - gtk_binding_entry_activate (entry, object); - - return TRUE; - } - } - + + entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); + if (entry) + return gtk_binding_entry_activate (entry, object); + return FALSE; } +static void +gtk_binding_entry_clear_internal (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers) +{ + GtkBindingEntry *entry; + + keyval = gdk_keyval_to_lower (keyval); + modifiers = modifiers & BINDING_MOD_MASK (); + + entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); + if (entry) + binding_entry_destroy (entry); + + entry = binding_entry_new (binding_set, keyval, modifiers); +} + +/** + * gtk_binding_entry_skip: + * @binding_set: a #GtkBindingSet to skip an entry of + * @keyval: key value of binding to skip + * @modifiers: key modifier of binding to skip + * + * Install a binding on @binding_set which causes key lookups + * to be aborted, to prevent bindings from lower priority sets + * to be activated. + * + * Since: 2.12 + */ void -gtk_binding_entry_clear (GtkBindingSet *binding_set, - guint keyval, - guint modifiers) +gtk_binding_entry_skip (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers) { GtkBindingEntry *entry; - + g_return_if_fail (binding_set != NULL); - + keyval = gdk_keyval_to_lower (keyval); modifiers = modifiers & BINDING_MOD_MASK (); - + entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); if (entry) binding_entry_destroy (entry); entry = binding_entry_new (binding_set, keyval, modifiers); + entry->marks_unbound = TRUE; } +/** + * gtk_binding_entry_remove: + * @binding_set: a #GtkBindingSet to remove an entry of + * @keyval: key value of binding to remove + * @modifiers: key modifier of binding to remove + * + * Remove a binding previously installed via + * gtk_binding_entry_add_signal() on @binding_set. + */ void -gtk_binding_entry_remove (GtkBindingSet *binding_set, - guint keyval, - guint modifiers) +gtk_binding_entry_remove (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers) { GtkBindingEntry *entry; - + g_return_if_fail (binding_set != NULL); - + keyval = gdk_keyval_to_lower (keyval); modifiers = modifiers & BINDING_MOD_MASK (); - + entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); if (entry) binding_entry_destroy (entry); } +/** + * gtk_binding_entry_add_signall: + * @binding_set: a #GtkBindingSet to add a signal to + * @keyval: key value + * @modifiers: key modifier + * @signal_name: signal name to be bound + * @binding_args: (transfer none) (element-type GtkBindingArg): + * list of #GtkBindingArg signal arguments + * + * Override or install a new key binding for @keyval with @modifiers on + * @binding_set. + */ void gtk_binding_entry_add_signall (GtkBindingSet *binding_set, - guint keyval, - guint modifiers, - const gchar *signal_name, - GSList *binding_args) + guint keyval, + GdkModifierType modifiers, + const gchar *signal_name, + GSList *binding_args) +{ + _gtk_binding_entry_add_signall (binding_set, + keyval, modifiers, + signal_name, binding_args); +} + +void +_gtk_binding_entry_add_signall (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers, + const gchar *signal_name, + GSList *binding_args) { GtkBindingEntry *entry; GtkBindingSignal *signal, **signal_p; GSList *slist; guint n = 0; GtkBindingArg *arg; - + g_return_if_fail (binding_set != NULL); g_return_if_fail (signal_name != NULL); - + keyval = gdk_keyval_to_lower (keyval); modifiers = modifiers & BINDING_MOD_MASK (); - + signal = binding_signal_new (signal_name, g_slist_length (binding_args)); - + arg = signal->args; for (slist = binding_args; slist; slist = slist->next) { GtkBindingArg *tmp_arg; - + tmp_arg = slist->data; if (!tmp_arg) - { - g_warning ("gtk_binding_entry_add_signall(): arg[%u] is `NULL'", n); - binding_signal_free (signal); - return; - } - arg->arg_type = tmp_arg->arg_type; - switch (tmp_arg->arg_type) - { - case GTK_BINDING_ARG_INT: - case GTK_BINDING_ARG_LONG: - arg->d.long_data = tmp_arg->d.long_data; - break; - case GTK_BINDING_ARG_FLOAT: - case GTK_BINDING_ARG_DOUBLE: - arg->d.double_data = tmp_arg->d.double_data; - break; - case GTK_BINDING_ARG_STRING: - if (!tmp_arg->d.string_data) - { - g_warning ("gtk_binding_entry_add_signall(): value of `string' arg[%u] is `NULL'", n); - arg->d.string_data = NULL; - binding_signal_free (signal); - return; - } - arg->d.string_data = g_strdup (tmp_arg->d.string_data); - break; - default: - g_warning ("gtk_binding_entry_add_signall(): unsupported type `%s' for arg[%u]", - gtk_type_name (arg->arg_type), n); - binding_signal_free (signal); - return; - } + { + g_warning ("gtk_binding_entry_add_signall(): arg[%u] is `NULL'", n); + binding_signal_free (signal); + return; + } + switch (G_TYPE_FUNDAMENTAL (tmp_arg->arg_type)) + { + case G_TYPE_LONG: + arg->arg_type = G_TYPE_LONG; + arg->d.long_data = tmp_arg->d.long_data; + break; + case G_TYPE_DOUBLE: + arg->arg_type = G_TYPE_DOUBLE; + arg->d.double_data = tmp_arg->d.double_data; + break; + case G_TYPE_STRING: + if (tmp_arg->arg_type != GTK_TYPE_IDENTIFIER) + arg->arg_type = G_TYPE_STRING; + else + arg->arg_type = GTK_TYPE_IDENTIFIER; + arg->d.string_data = g_strdup (tmp_arg->d.string_data); + if (!arg->d.string_data) + { + g_warning ("gtk_binding_entry_add_signall(): value of `string' arg[%u] is `NULL'", n); + binding_signal_free (signal); + return; + } + break; + default: + g_warning ("gtk_binding_entry_add_signall(): unsupported type `%s' for arg[%u]", + g_type_name (arg->arg_type), n); + binding_signal_free (signal); + return; + } arg++; n++; } - + entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); if (!entry) { - gtk_binding_entry_add (binding_set, keyval, modifiers); + gtk_binding_entry_clear_internal (binding_set, keyval, modifiers); entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); } signal_p = &entry->signals; @@ -600,13 +985,27 @@ gtk_binding_entry_add_signall (GtkBindingSet *binding_set, *signal_p = signal; } +/** + * gtk_binding_entry_add_signal: + * @binding_set: a #GtkBindingSet to install an entry for + * @keyval: key value of binding to install + * @modifiers: key modifier of binding to install + * @signal_name: signal to execute upon activation + * @n_args: number of arguments to @signal_name + * @...: arguments to @signal_name + * + * Override or install a new key binding for @keyval with @modifiers on + * @binding_set. When the binding is activated, @signal_name will be + * emitted on the target widget, with @n_args @Varargs used as + * arguments. + */ void gtk_binding_entry_add_signal (GtkBindingSet *binding_set, - guint keyval, - guint modifiers, - const gchar *signal_name, - guint n_args, - ...) + guint keyval, + GdkModifierType modifiers, + const gchar *signal_name, + guint n_args, + ...) { GSList *slist, *free_slist; va_list args; @@ -614,9 +1013,6 @@ gtk_binding_entry_add_signal (GtkBindingSet *binding_set, g_return_if_fail (binding_set != NULL); g_return_if_fail (signal_name != NULL); - - keyval = gdk_keyval_to_lower (keyval); - modifiers = modifiers & BINDING_MOD_MASK (); va_start (args, n_args); slist = NULL; @@ -624,71 +1020,416 @@ gtk_binding_entry_add_signal (GtkBindingSet *binding_set, { GtkBindingArg *arg; - arg = g_new0 (GtkBindingArg, 1); + arg = g_slice_new0 (GtkBindingArg); slist = g_slist_prepend (slist, arg); - arg->arg_type = va_arg (args, GtkType); - switch (arg->arg_type) - { - case GTK_BINDING_ARG_INT: - arg->d.long_data = va_arg (args, gint); - break; - case GTK_BINDING_ARG_LONG: - arg->d.long_data = va_arg (args, glong); - break; - case GTK_BINDING_ARG_FLOAT: - arg->d.double_data = va_arg (args, gfloat); - break; - case GTK_BINDING_ARG_DOUBLE: - arg->d.double_data = va_arg (args, gdouble); - break; - case GTK_BINDING_ARG_STRING: - arg->d.string_data = va_arg (args, gchar*); - if (!arg->d.string_data) - { - g_warning ("gtk_binding_entry_add_signal(): value of `string' arg[%u] is `NULL'", i); - i = n_args + 2; - } - break; - default: - g_warning ("gtk_binding_entry_add_signal(): unsupported type `%s' for arg[%u]", - gtk_type_name (arg->arg_type), i); - i = n_args + 2; - break; - } + arg->arg_type = va_arg (args, GType); + switch (G_TYPE_FUNDAMENTAL (arg->arg_type)) + { + case G_TYPE_CHAR: + case G_TYPE_UCHAR: + case G_TYPE_INT: + case G_TYPE_UINT: + case G_TYPE_BOOLEAN: + case G_TYPE_ENUM: + case G_TYPE_FLAGS: + arg->arg_type = G_TYPE_LONG; + arg->d.long_data = va_arg (args, gint); + break; + case G_TYPE_LONG: + case G_TYPE_ULONG: + arg->arg_type = G_TYPE_LONG; + arg->d.long_data = va_arg (args, glong); + break; + case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: + arg->arg_type = G_TYPE_DOUBLE; + arg->d.double_data = va_arg (args, gdouble); + break; + case G_TYPE_STRING: + if (arg->arg_type != GTK_TYPE_IDENTIFIER) + arg->arg_type = G_TYPE_STRING; + arg->d.string_data = va_arg (args, gchar*); + if (!arg->d.string_data) + { + g_warning ("gtk_binding_entry_add_signal(): type `%s' arg[%u] is `NULL'", + g_type_name (arg->arg_type), + i); + i += n_args + 1; + } + break; + default: + g_warning ("gtk_binding_entry_add_signal(): unsupported type `%s' for arg[%u]", + g_type_name (arg->arg_type), i); + i += n_args + 1; + break; + } } va_end (args); - if (i == n_args + 1 || i == 0) + if (i == n_args || i == 0) { slist = g_slist_reverse (slist); - gtk_binding_entry_add_signall (binding_set, keyval, modifiers, signal_name, slist); + _gtk_binding_entry_add_signall (binding_set, keyval, modifiers, signal_name, slist); } free_slist = slist; while (slist) { - g_free (slist->data); + g_slice_free (GtkBindingArg, slist->data); slist = slist->next; } g_slist_free (free_slist); } +static guint +gtk_binding_parse_signal (GScanner *scanner, + GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers) +{ + gchar *signal; + guint expected_token = 0; + GSList *args; + GSList *slist; + gboolean done; + gboolean negate; + gboolean need_arg; + gboolean seen_comma; + + g_return_val_if_fail (scanner != NULL, G_TOKEN_ERROR); + + g_scanner_get_next_token (scanner); + + if (scanner->token != G_TOKEN_STRING) + return G_TOKEN_STRING; + + g_scanner_peek_next_token (scanner); + + if (scanner->next_token != '(') + { + g_scanner_get_next_token (scanner); + return '('; + } + + signal = g_strdup (scanner->value.v_string); + g_scanner_get_next_token (scanner); + + negate = FALSE; + args = NULL; + done = FALSE; + need_arg = TRUE; + seen_comma = FALSE; + scanner->config->scan_symbols = FALSE; + + do + { + GtkBindingArg *arg; + + if (need_arg) + expected_token = G_TOKEN_INT; + else + expected_token = ')'; + + g_scanner_get_next_token (scanner); + + switch ((guint) scanner->token) + { + case G_TOKEN_FLOAT: + if (need_arg) + { + need_arg = FALSE; + arg = g_new (GtkBindingArg, 1); + arg->arg_type = G_TYPE_DOUBLE; + arg->d.double_data = scanner->value.v_float; + + if (negate) + { + arg->d.double_data = - arg->d.double_data; + negate = FALSE; + } + args = g_slist_prepend (args, arg); + } + else + done = TRUE; + + break; + case G_TOKEN_INT: + if (need_arg) + { + need_arg = FALSE; + arg = g_new (GtkBindingArg, 1); + arg->arg_type = G_TYPE_LONG; + arg->d.long_data = scanner->value.v_int; + + if (negate) + { + arg->d.long_data = - arg->d.long_data; + negate = FALSE; + } + args = g_slist_prepend (args, arg); + } + else + done = TRUE; + break; + case G_TOKEN_STRING: + if (need_arg && !negate) + { + need_arg = FALSE; + arg = g_new (GtkBindingArg, 1); + arg->arg_type = G_TYPE_STRING; + arg->d.string_data = g_strdup (scanner->value.v_string); + args = g_slist_prepend (args, arg); + } + else + done = TRUE; + + break; + case G_TOKEN_IDENTIFIER: + if (need_arg && !negate) + { + need_arg = FALSE; + arg = g_new (GtkBindingArg, 1); + arg->arg_type = GTK_TYPE_IDENTIFIER; + arg->d.string_data = g_strdup (scanner->value.v_identifier); + args = g_slist_prepend (args, arg); + } + else + done = TRUE; + + break; + case '-': + if (!need_arg) + done = TRUE; + else if (negate) + { + expected_token = G_TOKEN_INT; + done = TRUE; + } + else + negate = TRUE; + + break; + case ',': + seen_comma = TRUE; + if (need_arg) + done = TRUE; + else + need_arg = TRUE; + + break; + case ')': + if (!(need_arg && seen_comma) && !negate) + { + args = g_slist_reverse (args); + _gtk_binding_entry_add_signall (binding_set, + keyval, + modifiers, + signal, + args); + expected_token = G_TOKEN_NONE; + } + + done = TRUE; + break; + default: + done = TRUE; + break; + } + } + while (!done); + + scanner->config->scan_symbols = TRUE; + + for (slist = args; slist; slist = slist->next) + { + GtkBindingArg *arg; + + arg = slist->data; + + if (G_TYPE_FUNDAMENTAL (arg->arg_type) == G_TYPE_STRING) + g_free (arg->d.string_data); + g_free (arg); + } + + g_slist_free (args); + g_free (signal); + + return expected_token; +} + +static inline guint +gtk_binding_parse_bind (GScanner *scanner, + GtkBindingSet *binding_set) +{ + guint keyval = 0; + GdkModifierType modifiers = 0; + gboolean unbind = FALSE; + + g_return_val_if_fail (scanner != NULL, G_TOKEN_ERROR); + + g_scanner_get_next_token (scanner); + + if (scanner->token != G_TOKEN_SYMBOL) + return G_TOKEN_SYMBOL; + + if (scanner->value.v_symbol != GUINT_TO_POINTER (GTK_BINDING_TOKEN_BIND) && + scanner->value.v_symbol != GUINT_TO_POINTER (GTK_BINDING_TOKEN_UNBIND)) + return G_TOKEN_SYMBOL; + + unbind = (scanner->value.v_symbol == GUINT_TO_POINTER (GTK_BINDING_TOKEN_UNBIND)); + g_scanner_get_next_token (scanner); + + if (scanner->token != (guint) G_TOKEN_STRING) + return G_TOKEN_STRING; + + gtk_accelerator_parse (scanner->value.v_string, &keyval, &modifiers); + modifiers &= BINDING_MOD_MASK (); + + if (keyval == 0) + return G_TOKEN_STRING; + + if (unbind) + { + gtk_binding_entry_skip (binding_set, keyval, modifiers); + return G_TOKEN_NONE; + } + + g_scanner_get_next_token (scanner); + + if (scanner->token != '{') + return '{'; + + gtk_binding_entry_clear_internal (binding_set, keyval, modifiers); + g_scanner_peek_next_token (scanner); + + while (scanner->next_token != '}') + { + guint expected_token; + + switch (scanner->next_token) + { + case G_TOKEN_STRING: + expected_token = gtk_binding_parse_signal (scanner, + binding_set, + keyval, + modifiers); + if (expected_token != G_TOKEN_NONE) + return expected_token; + break; + default: + g_scanner_get_next_token (scanner); + return '}'; + } + + g_scanner_peek_next_token (scanner); + } + + g_scanner_get_next_token (scanner); + + return G_TOKEN_NONE; +} + +static GScanner * +create_signal_scanner (void) +{ + GScanner *scanner; + + scanner = g_scanner_new (NULL); + scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "-_"; + + g_scanner_scope_add_symbol (scanner, 0, "bind", GUINT_TO_POINTER (GTK_BINDING_TOKEN_BIND)); + g_scanner_scope_add_symbol (scanner, 0, "unbind", GUINT_TO_POINTER (GTK_BINDING_TOKEN_UNBIND)); + + g_scanner_set_scope (scanner, 0); + + return scanner; +} + +/** + * gtk_binding_entry_add_signal_from_string: + * @binding_set: a #GtkBindingSet + * @signal_desc: a signal description + * + * Parses a signal description from @signal_desc and incorporates + * it into @binding_set. + * + * Signal descriptions may either bind a key combination to + * one or more signals: + * + * bind "key" { + * "signalname" (param, ...) + * ... + * } + * + * + * Or they may also unbind a key combination: + * + * unbind "key" + * + * + * Key combinations must be in a format that can be parsed by + * gtk_accelerator_parse(). + * + * Returns: %G_TOKEN_NONE if the signal was successfully parsed and added, + * the expected token otherwise + * + * Since: 3.0 + */ +GTokenType +gtk_binding_entry_add_signal_from_string (GtkBindingSet *binding_set, + const gchar *signal_desc) +{ + static GScanner *scanner = NULL; + GTokenType ret; + + g_return_val_if_fail (binding_set != NULL, G_TOKEN_NONE); + g_return_val_if_fail (signal_desc != NULL, G_TOKEN_NONE); + + if (G_UNLIKELY (!scanner)) + scanner = create_signal_scanner (); + + g_scanner_input_text (scanner, signal_desc, + (guint) strlen (signal_desc)); + + ret = gtk_binding_parse_bind (scanner, binding_set); + + /* Reset for next use */ + g_scanner_set_scope (scanner, 0); + + return ret; +} + +/** + * gtk_binding_set_add_path: + * @binding_set: a #GtkBindingSet to add a path to + * @path_type: path type the pattern applies to + * @path_pattern: the actual match pattern + * @priority: binding priority + * + * This function was used internally by the GtkRC parsing mechanism + * to assign match patterns to #GtkBindingSet structures. + * + * In GTK+ 3, these match patterns are unused. + * + * Deprecated: 3.0 + */ void -gtk_binding_set_add_path (GtkBindingSet *binding_set, - GtkPathType path_type, - const gchar *path_pattern, - GtkPathPriorityType priority) +gtk_binding_set_add_path (GtkBindingSet *binding_set, + GtkPathType path_type, + const gchar *path_pattern, + GtkPathPriorityType priority) { - GtkPatternSpec *pspec; + PatternSpec *pspec; GSList **slist_p, *slist; static guint seq_id = 0; - + g_return_if_fail (binding_set != NULL); g_return_if_fail (path_pattern != NULL); + g_return_if_fail (priority <= GTK_PATH_PRIO_MASK); priority &= GTK_PATH_PRIO_MASK; - + switch (path_type) { case GTK_PATH_WIDGET: @@ -705,439 +1446,230 @@ gtk_binding_set_add_path (GtkBindingSet *binding_set, slist_p = NULL; break; } - - pspec = g_new (GtkPatternSpec, 1); - gtk_pattern_spec_init (pspec, path_pattern); - pspec->seq_id = seq_id++ & 0x0fffffff; - pspec->seq_id |= priority << 28; + + pspec = g_new (PatternSpec, 1); + pspec->type = path_type; + if (path_type == GTK_PATH_WIDGET_CLASS) + pspec->pspec = NULL; + else + pspec->pspec = g_pattern_spec_new (path_pattern); + + pspec->seq_id = priority << 28; pspec->user_data = binding_set; - + slist = *slist_p; while (slist) { - GtkPatternSpec *tmp_pspec; - + PatternSpec *tmp_pspec; + tmp_pspec = slist->data; slist = slist->next; - - if (tmp_pspec->pattern_length == pspec->pattern_length && - g_str_equal (tmp_pspec->pattern_reversed, pspec->pattern_reversed)) - { - gtk_pattern_spec_free_segs (pspec); - g_free (pspec); - pspec = NULL; - } + + if (g_pattern_spec_equal (tmp_pspec->pspec, pspec->pspec)) + { + GtkPathPriorityType lprio = tmp_pspec->seq_id >> 28; + + pattern_spec_free (pspec); + pspec = NULL; + if (lprio < priority) + { + tmp_pspec->seq_id &= 0x0fffffff; + tmp_pspec->seq_id |= priority << 28; + } + break; + } } if (pspec) - *slist_p = g_slist_prepend (*slist_p, pspec); -} - -static inline gboolean -binding_match_activate (GSList *pspec_list, - GtkObject *object, - guint path_length, - gchar *path, - gchar *path_reversed) -{ - GSList *slist; - - for (slist = pspec_list; slist; slist = slist->next) { - GtkPatternSpec *pspec; - - pspec = slist->data; - if (gtk_pattern_match (pspec, path_length, path, path_reversed)) - { - GtkBindingSet *binding_set; - - binding_set = pspec->user_data; - - gtk_binding_entry_activate (binding_set->current, object); - - return TRUE; - } + pspec->seq_id |= seq_id++ & 0x0fffffff; + *slist_p = g_slist_prepend (*slist_p, pspec); } - - return FALSE; } static gint -gtk_binding_pattern_compare (gconstpointer a, - gconstpointer b) +find_entry_with_binding (GtkBindingEntry *entry, + GtkBindingSet *binding_set) { - register const GtkPatternSpec *pa = a; - register const GtkPatternSpec *pb = b; - - return pa->seq_id < pb->seq_id ? -1 : 1; + return (entry->binding_set == binding_set) ? 0 : 1; } -static inline GSList* -gtk_binding_entries_sort_patterns (GtkBindingEntry *entries, - GtkPathType path_id) +static gboolean +binding_activate (GtkBindingSet *binding_set, + GSList *entries, + GObject *object, + gboolean is_release, + gboolean *unbound) { - GSList *patterns; - - patterns = NULL; - while (entries) - { - register GtkBindingSet *binding_set; - GSList *slist = NULL; - - binding_set = entries->binding_set; - binding_set->current = entries; - - switch (path_id) - { - case GTK_PATH_WIDGET: - slist = binding_set->widget_path_pspecs; - break; - case GTK_PATH_WIDGET_CLASS: - slist = binding_set->widget_class_pspecs; - break; - case GTK_PATH_CLASS: - slist = binding_set->class_branch_pspecs; - break; - } - - for (; slist; slist = slist->next) - { - GtkPatternSpec *pspec; - - pspec = slist->data; - patterns = g_slist_insert_sorted (patterns, pspec, gtk_binding_pattern_compare); - } - - entries = entries->hash_next; - } + GtkBindingEntry *entry; + GSList *elem; - return patterns; -} - + elem = g_slist_find_custom (entries, binding_set, + (GCompareFunc) find_entry_with_binding); -gboolean -gtk_bindings_activate (GtkObject *object, - guint keyval, - guint modifiers) -{ - GtkBindingEntry *entries; - GtkWidget *widget; - gboolean handled = FALSE; + if (!elem) + return FALSE; - g_return_val_if_fail (object != NULL, FALSE); - g_return_val_if_fail (GTK_IS_OBJECT (object), FALSE); + entry = elem->data; - if (!GTK_IS_WIDGET (object) || GTK_OBJECT_DESTROYED (object)) + if (is_release != ((entry->modifiers & GDK_RELEASE_MASK) != 0)) return FALSE; - widget = GTK_WIDGET (object); + if (entry->marks_unbound) + { + *unbound = TRUE; + return FALSE; + } - keyval = gdk_keyval_to_lower (keyval); - modifiers = modifiers & BINDING_MOD_MASK (); + if (gtk_binding_entry_activate (entry, object)) + return TRUE; - entries = binding_ht_lookup_list (keyval, modifiers); + return FALSE; +} + +static gboolean +gtk_bindings_activate_list (GObject *object, + GSList *entries, + gboolean is_release) +{ + GtkStyleContext *context; + GtkBindingSet *binding_set; + GtkStateFlags state; + gboolean handled = FALSE; + gboolean unbound = FALSE; + GPtrArray *array; if (!entries) return FALSE; - if (!handled) - { - guint path_length; - gchar *path, *path_reversed; - GSList *patterns; - - gtk_widget_path (widget, &path_length, &path, &path_reversed); - patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_WIDGET); - handled = binding_match_activate (patterns, object, path_length, path, path_reversed); - g_slist_free (patterns); - g_free (path); - g_free (path_reversed); - } + context = gtk_widget_get_style_context (GTK_WIDGET (object)); + state = gtk_widget_get_state_flags (GTK_WIDGET (object)); - if (!handled) + gtk_style_context_get (context, state, + "gtk-key-bindings", &array, + NULL); + if (array) { - guint path_length; - gchar *path, *path_reversed; - GSList *patterns; - - gtk_widget_class_path (widget, &path_length, &path, &path_reversed); - patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_WIDGET_CLASS); - handled = binding_match_activate (patterns, object, path_length, path, path_reversed); - g_slist_free (patterns); - g_free (path); - g_free (path_reversed); + gint i; + + for (i = 0; i < array->len; i++) + { + binding_set = g_ptr_array_index (array, i); + handled = binding_activate (binding_set, entries, + object, is_release, + &unbound); + if (handled) + break; + } + + g_ptr_array_unref (array); + + if (unbound) + return FALSE; } if (!handled) { - GSList *patterns; - GtkType class_type; - - patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_CLASS); - class_type = GTK_OBJECT_TYPE (object); + GType class_type; + + class_type = G_TYPE_FROM_INSTANCE (object); + while (class_type && !handled) - { - guint path_length; - gchar *path, *path_reversed; - - path = gtk_type_name (class_type); - path_reversed = g_strdup (path); - g_strreverse (path_reversed); - path_length = strlen (path); - handled = binding_match_activate (patterns, object, path_length, path, path_reversed); - g_free (path_reversed); - - class_type = gtk_type_parent (class_type); - } - g_slist_free (patterns); - } + { + binding_set = gtk_binding_set_find_interned (g_type_name (class_type)); + class_type = g_type_parent (class_type); - return handled; -} + if (!binding_set) + continue; + handled = binding_activate (binding_set, entries, + object, is_release, + &unbound); + } + if (unbound) + return FALSE; + } + return handled; +} +/** + * gtk_bindings_activate: + * @object: object to activate when binding found + * @keyval: key value of the binding + * @modifiers: key modifier of the binding + * + * Find a key binding matching @keyval and @modifiers and activate the + * binding on @object. + * + * Return value: %TRUE if a binding was found and activated + */ +gboolean +gtk_bindings_activate (GObject *object, + guint keyval, + GdkModifierType modifiers) +{ + GSList *entries = NULL; + GdkDisplay *display; + GtkKeyHash *key_hash; + gboolean handled = FALSE; + gboolean is_release; + if (!GTK_IS_WIDGET (object)) + return FALSE; + is_release = (modifiers & GDK_RELEASE_MASK) != 0; + modifiers = modifiers & BINDING_MOD_MASK () & ~GDK_RELEASE_MASK; + display = gtk_widget_get_display (GTK_WIDGET (object)); + key_hash = binding_key_hash_for_keymap (gdk_keymap_get_for_display (display)); + entries = _gtk_key_hash_lookup_keyval (key_hash, keyval, modifiers); + handled = gtk_bindings_activate_list (object, entries, is_release); -/* Patterns - */ + g_slist_free (entries); -static inline gboolean -gtk_pattern_ph_match (const gchar *match_pattern, - const gchar *match_string) -{ - register const gchar *pattern, *string; - register gchar ch; - - pattern = match_pattern; - string = match_string; - - ch = *pattern; - pattern++; - while (ch) - { - switch (ch) - { - case '?': - if (!*string) - return FALSE; - string++; - break; - - case '*': - do - { - ch = *pattern; - pattern++; - if (ch == '?') - { - if (!*string) - return FALSE; - string++; - } - } - while (ch == '*' || ch == '?'); - if (!ch) - return TRUE; - do - { - while (ch != *string) - { - if (!*string) - return FALSE; - string++; - } - string++; - if (gtk_pattern_ph_match (pattern, string)) - return TRUE; - } - while (*string); - break; - - default: - if (ch == *string) - string++; - else - return FALSE; - break; - } - - ch = *pattern; - pattern++; - } - - return *string == 0; + return handled; } +/** + * gtk_bindings_activate_event: + * @object: a #GObject (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 + * + * Since: 2.4 + */ gboolean -gtk_pattern_match (GtkPatternSpec *pspec, - guint string_length, - const gchar *string, - const gchar *string_reversed) +gtk_bindings_activate_event (GObject *object, + GdkEventKey *event) { - g_return_val_if_fail (pspec != NULL, FALSE); - g_return_val_if_fail (string != NULL, FALSE); - g_return_val_if_fail (string_reversed != NULL, FALSE); - - switch (pspec->match_type) - { - case GTK_MATCH_ALL: - return gtk_pattern_ph_match (pspec->pattern, string); - - case GTK_MATCH_ALL_TAIL: - return gtk_pattern_ph_match (pspec->pattern_reversed, string_reversed); - - case GTK_MATCH_HEAD: - if (pspec->pattern_length > string_length) - return FALSE; - else if (pspec->pattern_length == string_length) - return strcmp (pspec->pattern, string) == 0; - else if (pspec->pattern_length) - return strncmp (pspec->pattern, string, pspec->pattern_length) == 0; - else - return TRUE; - - case GTK_MATCH_TAIL: - if (pspec->pattern_length > string_length) - return FALSE; - else if (pspec->pattern_length == string_length) - return strcmp (pspec->pattern_reversed, string_reversed) == 0; - else if (pspec->pattern_length) - return strncmp (pspec->pattern_reversed, - string_reversed, - pspec->pattern_length) == 0; - else - return TRUE; - - case GTK_MATCH_EXACT: - if (pspec->pattern_length != string_length) - return FALSE; - else - return strcmp (pspec->pattern_reversed, string_reversed) == 0; - - default: - g_return_val_if_fail (pspec->match_type < GTK_MATCH_LAST, FALSE); - return FALSE; - } -} + GSList *entries = NULL; + GdkDisplay *display; + GtkKeyHash *key_hash; + gboolean handled = FALSE; -void -gtk_pattern_spec_init (GtkPatternSpec *pspec, - const gchar *pattern) -{ - gchar *p; - - g_return_if_fail (pspec != NULL); - - pspec->match_type = GTK_MATCH_ALL; - pspec->seq_id = 0; - pspec->user_data = NULL; - - if (!pattern) - pattern = ""; - - pspec->pattern = g_strdup (pattern); - pspec->pattern_length = strlen (pspec->pattern); - pspec->pattern_reversed = g_strdup (pspec->pattern); - g_strreverse (pspec->pattern_reversed); - if (pspec->pattern_reversed[0] != '*') - pspec->match_type = GTK_MATCH_ALL_TAIL; - - if (strchr (pspec->pattern, '?')) - return; - - if (!strchr (pspec->pattern, '*')) - { - pspec->match_type = GTK_MATCH_EXACT; - return; - } - - p = pspec->pattern; - while (*p == '*') - p++; - if (p > pspec->pattern && - !strchr (p, '*')) - { - gchar *t; - - pspec->match_type = GTK_MATCH_TAIL; - t = pspec->pattern; - pspec->pattern = g_strdup (p); - g_free (t); - g_free (pspec->pattern_reversed); - pspec->pattern_reversed = g_strdup (pspec->pattern); - g_strreverse (pspec->pattern_reversed); - pspec->pattern_length = strlen (pspec->pattern); - return; - } - - p = pspec->pattern_reversed; - while (*p == '*') - p++; - if (p > pspec->pattern_reversed && - !strchr (p, '*')) - { - gchar *t; - - pspec->match_type = GTK_MATCH_HEAD; - t = pspec->pattern_reversed; - pspec->pattern_reversed = g_strdup (p); - g_free (t); - pspec->pattern = g_strdup (pspec->pattern_reversed); - g_strreverse (pspec->pattern); - pspec->pattern_length = strlen (pspec->pattern); - } -} + if (!GTK_IS_WIDGET (object)) + return FALSE; -gboolean -gtk_pattern_match_string (GtkPatternSpec *pspec, - const gchar *string) -{ - gchar *string_reversed; - guint length; - gboolean ergo; - - g_return_val_if_fail (pspec != NULL, FALSE); - g_return_val_if_fail (string != NULL, FALSE); - - length = strlen (string); - string_reversed = g_strdup (string); - g_strreverse (string_reversed); - - ergo = gtk_pattern_match (pspec, length, string, string_reversed); - g_free (string_reversed); - - return ergo; -} + display = gtk_widget_get_display (GTK_WIDGET (object)); + key_hash = binding_key_hash_for_keymap (gdk_keymap_get_for_display (display)); -gboolean -gtk_pattern_match_simple (const gchar *pattern, - const gchar *string) -{ - GtkPatternSpec pspec; - gboolean ergo; - - g_return_val_if_fail (pattern != NULL, FALSE); - g_return_val_if_fail (string != NULL, FALSE); - - gtk_pattern_spec_init (&pspec, pattern); - ergo = gtk_pattern_match_string (&pspec, string); - gtk_pattern_spec_free_segs (&pspec); - - return ergo; -} + entries = _gtk_key_hash_lookup (key_hash, + event->hardware_keycode, + event->state, + BINDING_MOD_MASK () & ~GDK_RELEASE_MASK, + event->group); -void -gtk_pattern_spec_free_segs (GtkPatternSpec *pspec) -{ - g_return_if_fail (pspec != NULL); - - g_free (pspec->pattern); - pspec->pattern = NULL; - g_free (pspec->pattern_reversed); - pspec->pattern_reversed = NULL; + handled = gtk_bindings_activate_list (object, entries, + event->type == GDK_KEY_RELEASE); + + g_slist_free (entries); + + return handled; }