X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkbindings.c;h=4cbbeaf6aadbb67cd9e0c9981f00b256363ef54a;hb=1708ae3f1a962d3bbfcc0e673c5fa9f629d16eae;hp=648e572144b204deb88f893839f8d549ccfa3093;hpb=913cdf3be750a1e74c09b20edf55a57f9a919fcc;p=~andy%2Fgtk diff --git a/gtk/gtkbindings.c b/gtk/gtkbindings.c index 648e57214..4cbbeaf6a 100644 --- a/gtk/gtkbindings.c +++ b/gtk/gtkbindings.c @@ -1,7 +1,7 @@ /* 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 @@ -11,60 +11,175 @@ * * 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 + * 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. + * License along with this library. If not, see . */ /* * 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/. + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ #include "config.h" #include #include -#include -#include "gtkbindings.h" +#include "gtkbindingsprivate.h" #include "gtkkeyhash.h" +#include "gtkstylecontext.h" #include "gtkwidget.h" -#include "gtkrc.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 () | GDK_RELEASE_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; - GSList *path; 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 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; +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) { - _gtk_rc_free_widget_class_path (pspec->path); if (pspec->pspec) g_pattern_spec_free (pspec->pspec); g_free (pspec); @@ -72,7 +187,7 @@ pattern_spec_free (PatternSpec *pspec) static GtkBindingSignal* binding_signal_new (const gchar *signal_name, - guint n_args) + guint n_args) { GtkBindingSignal *signal; @@ -81,7 +196,7 @@ binding_signal_new (const gchar *signal_name, signal->signal_name = (gchar *)g_intern_string (signal_name); signal->n_args = n_args; signal->args = (GtkBindingArg *)(signal + 1); - + return signal; } @@ -89,11 +204,11 @@ static void binding_signal_free (GtkBindingSignal *sig) { guint i; - + for (i = 0; i < sig->n_args; i++) { if (G_TYPE_FUNDAMENTAL (sig->args[i].arg_type) == G_TYPE_STRING) - g_free (sig->args[i].d.string_data); + g_free (sig->args[i].d.string_data); } g_slice_free1 (sizeof (GtkBindingSignal) + sig->n_args * sizeof (GtkBindingArg), sig); } @@ -112,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; @@ -122,21 +237,21 @@ binding_entries_compare (gconstpointer a, static void binding_key_hash_insert_entry (GtkKeyHash *key_hash, - GtkBindingEntry *entry) + 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; + keyval = GDK_KEY_ISO_Left_Tab; else - keyval = gdk_keyval_to_upper (keyval); + keyval = gdk_keyval_to_upper (keyval); } - + _gtk_key_hash_add_entry (key_hash, keyval, entry->modifiers & ~GDK_RELEASE_MASK, entry); } @@ -144,15 +259,15 @@ 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) + gpointer value, + gpointer data) { GtkKeyHash *key_hash = data; GtkBindingEntry *entry = value; @@ -169,7 +284,7 @@ binding_key_hash_for_keymap (GdkKeymap *keymap) 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) @@ -178,9 +293,9 @@ binding_key_hash_for_keymap (GdkKeymap *keymap) 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); + 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); } @@ -191,12 +306,12 @@ binding_key_hash_for_keymap (GdkKeymap *keymap) static GtkBindingEntry* binding_entry_new (GtkBindingSet *binding_set, - guint keyval, - GdkModifierType modifiers) + 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); @@ -222,7 +337,7 @@ binding_entry_new (GtkBindingSet *binding_set, GtkKeyHash *key_hash = tmp_list->data; binding_key_hash_insert_entry (key_hash, entry); } - + return entry; } @@ -232,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); @@ -266,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; @@ -285,18 +400,18 @@ 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) @@ -319,18 +434,18 @@ binding_entry_destroy (GtkBindingEntry *entry) static GtkBindingEntry* binding_ht_lookup_entry (GtkBindingSet *set, - guint keyval, - GdkModifierType modifiers) + 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) @@ -340,16 +455,16 @@ binding_ht_lookup_entry (GtkBindingSet *set, } static gboolean -binding_compose_params (GtkObject *object, - GtkBindingArg *args, - GSignalQuery *query, - GValue **params_p) +binding_compose_params (GObject *object, + GtkBindingArg *args, + GSignalQuery *query, + GValue **params_p) { GValue *params; const GType *types; guint i; gboolean valid; - + params = g_new0 (GValue, query->n_params + 1); *params_p = params; @@ -358,200 +473,200 @@ binding_compose_params (GtkObject *object, g_value_init (params, G_TYPE_OBJECT); g_value_set_object (params, G_OBJECT (object)); params++; - + types = query->param_types; valid = TRUE; for (i = 1; i < query->n_params + 1 && valid; i++) { - GValue tmp_value = { 0, }; + 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; - } + { + 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; + { + if (!g_value_transform (&tmp_value, params)) + valid = FALSE; + + g_value_unset (&tmp_value); + } - 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_value_unset (&(*params_p)[j]); + g_free (*params_p); *params_p = NULL; } - + return valid; } static gboolean gtk_binding_entry_activate (GtkBindingEntry *entry, - GtkObject *object) + GObject *object) { GtkBindingSignal *sig; gboolean old_emission; gboolean handled = FALSE; gint i; - + old_emission = entry->in_emission; entry->in_emission = TRUE; - + g_object_ref (object); - + for (sig = entry->signals; sig; sig = sig->next) { GSignalQuery query; guint signal_id; GValue *params = NULL; - GValue return_val = { 0, }; + GValue return_val = G_VALUE_INIT; gchar *accelerator = NULL; - + 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, - g_type_name (G_OBJECT_TYPE (object))); - g_free (accelerator); - continue; - } - + { + 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))); - } + (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))); - } + { + 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); if (accelerator) - continue; + continue; if (query.return_type == G_TYPE_BOOLEAN) - g_value_init (&return_val, G_TYPE_BOOLEAN); - + g_value_init (&return_val, G_TYPE_BOOLEAN); + g_signal_emitv (params, signal_id, 0, &return_val); if (query.return_type == G_TYPE_BOOLEAN) - { - if (g_value_get_boolean (&return_val)) - handled = TRUE; - g_value_unset (&return_val); - } + { + if (g_value_get_boolean (&return_val)) + handled = TRUE; + g_value_unset (&return_val); + } else - handled = TRUE; - + handled = TRUE; + for (i = 0; i < query.n_params + 1; i++) - g_value_unset (¶ms[i]); + g_value_unset (¶ms[i]); g_free (params); - + if (entry->destroyed) - break; + break; } - + g_object_unref (object); entry->in_emission = old_emission; @@ -562,21 +677,21 @@ gtk_binding_entry_activate (GtkBindingEntry *entry, } /** - * gtk_binding_set_new: + * 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: new binding set + * Return value: (transfer full): new binding set */ GtkBindingSet* 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 = (gchar *) g_intern_string (set_name); binding_set->widget_path_pspecs = NULL; @@ -585,29 +700,30 @@ gtk_binding_set_new (const gchar *set_name) binding_set->entries = NULL; binding_set->current = NULL; binding_set->parsed = FALSE; - + binding_set_list = g_slist_prepend (binding_set_list, binding_set); - + return binding_set; } /** - * gtk_binding_set_by_class: - * @object_class: a valid #GtkObject class + * 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: the binding set corresponding to @object_class + * 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_quark_from_static_string (key_class_binding_set); @@ -618,41 +734,45 @@ gtk_binding_set_by_class (gpointer object_class) return binding_set; binding_set = gtk_binding_set_new (g_type_name (G_OBJECT_CLASS_TYPE (class))); - gtk_binding_set_add_path (binding_set, - GTK_PATH_CLASS, - g_type_name (G_OBJECT_CLASS_TYPE (class)), - GTK_PATH_PRIO_GTK); g_dataset_id_set_data (class, key_id_class_binding_set, binding_set); return binding_set; } +static GtkBindingSet* +gtk_binding_set_find_interned (const gchar *set_name) +{ + GSList *slist; + + for (slist = binding_set_list; slist; slist = slist->next) + { + GtkBindingSet *binding_set; + + binding_set = slist->data; + 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(). + * 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: %NULL or the specified binding set + * Return value: (transfer none): %NULL or the specified binding set */ GtkBindingSet* gtk_binding_set_find (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; - } - return NULL; + + return gtk_binding_set_find_interned (g_intern_string (set_name)); } /** @@ -668,23 +788,23 @@ gtk_binding_set_find (const gchar *set_name) * Return value: %TRUE if a binding was found and activated */ gboolean -gtk_binding_set_activate (GtkBindingSet *binding_set, - guint keyval, - GdkModifierType 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 (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 (); - + entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); if (entry) return gtk_binding_entry_activate (entry, object); - + return FALSE; } @@ -747,17 +867,17 @@ gtk_binding_entry_skip (GtkBindingSet *binding_set, * gtk_binding_entry_add_signal() on @binding_set. */ void -gtk_binding_entry_remove (GtkBindingSet *binding_set, - guint keyval, - GdkModifierType 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); @@ -769,17 +889,18 @@ gtk_binding_entry_remove (GtkBindingSet *binding_set, * @keyval: key value * @modifiers: key modifier * @signal_name: signal name to be bound - * @binding_args: list of #GtkBindingArg signal arguments + * @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 keyval, GdkModifierType modifiers, const gchar *signal_name, - GSList *binding_args) + GSList *binding_args) { _gtk_binding_entry_add_signall (binding_set, keyval, modifiers, @@ -788,70 +909,70 @@ gtk_binding_entry_add_signall (GtkBindingSet *binding_set, void _gtk_binding_entry_add_signall (GtkBindingSet *binding_set, - guint keyval, + guint keyval, GdkModifierType modifiers, const gchar *signal_name, - GSList *binding_args) + 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; - } + { + 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: + { + 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->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) { @@ -871,7 +992,7 @@ _gtk_binding_entry_add_signall (GtkBindingSet *binding_set, * @modifiers: key modifier of binding to install * @signal_name: signal to execute upon activation * @n_args: number of arguments to @signal_name - * @Varargs: 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 @@ -880,11 +1001,11 @@ _gtk_binding_entry_add_signall (GtkBindingSet *binding_set, */ void gtk_binding_entry_add_signal (GtkBindingSet *binding_set, - guint keyval, - GdkModifierType 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; @@ -892,7 +1013,7 @@ gtk_binding_entry_add_signal (GtkBindingSet *binding_set, g_return_if_fail (binding_set != NULL); g_return_if_fail (signal_name != NULL); - + va_start (args, n_args); slist = NULL; for (i = 0; i < n_args; i++) @@ -904,45 +1025,45 @@ gtk_binding_entry_add_signal (GtkBindingSet *binding_set, 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; - } + { + 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); @@ -961,255 +1082,486 @@ gtk_binding_entry_add_signal (GtkBindingSet *binding_set, g_slist_free (free_slist); } -/** - * 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 is used internally by the GtkRC parsing mechanism to - * assign match patterns to #GtkBindingSet structures. - */ -void -gtk_binding_set_add_path (GtkBindingSet *binding_set, - GtkPathType path_type, - const gchar *path_pattern, - GtkPathPriorityType priority) +static guint +gtk_binding_parse_signal (GScanner *scanner, + GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers) { - 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); + gchar *signal; + guint expected_token = 0; + GSList *args; + GSList *slist; + gboolean done; + gboolean negate; + gboolean need_arg; + gboolean seen_comma; - priority &= GTK_PATH_PRIO_MASK; - - switch (path_type) - { - case GTK_PATH_WIDGET: - slist_p = &binding_set->widget_path_pspecs; - break; - case GTK_PATH_WIDGET_CLASS: - slist_p = &binding_set->widget_class_pspecs; - break; - case GTK_PATH_CLASS: - slist_p = &binding_set->class_branch_pspecs; - break; - default: - g_assert_not_reached (); - slist_p = NULL; - break; - } - - pspec = g_new (PatternSpec, 1); - pspec->type = path_type; - if (path_type == GTK_PATH_WIDGET_CLASS) - { - pspec->pspec = NULL; - pspec->path = _gtk_rc_parse_widget_class_path (path_pattern); - } - else - { - pspec->pspec = g_pattern_spec_new (path_pattern); - pspec->path = NULL; - } - - pspec->seq_id = priority << 28; - pspec->user_data = binding_set; - - slist = *slist_p; - while (slist) - { - PatternSpec *tmp_pspec; - - tmp_pspec = slist->data; - slist = slist->next; - - 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) + 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 != '(') { - pspec->seq_id |= seq_id++ & 0x0fffffff; - *slist_p = g_slist_prepend (*slist_p, pspec); + g_scanner_get_next_token (scanner); + return '('; } -} -static gboolean -binding_match_activate (GSList *pspec_list, - GtkObject *object, - guint path_length, - gchar *path, - gchar *path_reversed, - gboolean *unbound) -{ - GSList *slist; + signal = g_strdup (scanner->value.v_string); + g_scanner_get_next_token (scanner); - *unbound = FALSE; + negate = FALSE; + args = NULL; + done = FALSE; + need_arg = TRUE; + seen_comma = FALSE; + scanner->config->scan_symbols = FALSE; - for (slist = pspec_list; slist; slist = slist->next) + do { - PatternSpec *pspec; - GtkBindingSet *binding_set; + GtkBindingArg *arg; - binding_set = NULL; - pspec = slist->data; - - if (pspec->type == GTK_PATH_WIDGET_CLASS) - { - if (_gtk_rc_match_widget_class (pspec->path, path_length, path, path_reversed)) - binding_set = pspec->user_data; - } + if (need_arg) + expected_token = G_TOKEN_INT; else - { - if (g_pattern_match (pspec->pspec, path_length, path, path_reversed)) - binding_set = pspec->user_data; - } + expected_token = ')'; + + g_scanner_get_next_token (scanner); - if (binding_set) + switch ((guint) scanner->token) { - if (binding_set->current->marks_unbound) + case G_TOKEN_FLOAT: + if (need_arg) { - *unbound = TRUE; - return FALSE; + 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; - if (gtk_binding_entry_activate (binding_set->current, object)) - return 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; - return FALSE; -} + 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; -static gint -gtk_binding_pattern_compare (gconstpointer new_pattern, - gconstpointer existing_pattern) -{ - register const PatternSpec *np = new_pattern; - register const PatternSpec *ep = existing_pattern; + break; + case '-': + if (!need_arg) + done = TRUE; + else if (negate) + { + expected_token = G_TOKEN_INT; + done = TRUE; + } + else + negate = TRUE; - /* walk the list as long as the existing patterns have - * higher priorities. - */ + break; + case ',': + seen_comma = TRUE; + if (need_arg) + done = TRUE; + else + need_arg = TRUE; - return np->seq_id < ep->seq_id; -} + 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; + } -static GSList* -gtk_binding_entries_sort_patterns (GSList *entries, - GtkPathType path_id, - gboolean is_release) -{ - GSList *patterns; - GSList *tmp_list; + done = TRUE; + break; + default: + done = TRUE; + break; + } + } + while (!done); - patterns = NULL; - for (tmp_list = entries; tmp_list; tmp_list = tmp_list->next) - { - GtkBindingEntry *entry = tmp_list->data; - GtkBindingSet *binding_set; + scanner->config->scan_symbols = TRUE; - binding_set = entry->binding_set; - binding_set->current = NULL; - } - - for (; entries; entries = entries->next) + for (slist = args; slist; slist = slist->next) { - GtkBindingEntry *entry = entries->data; - GtkBindingSet *binding_set; - GSList *slist = NULL; - - if (is_release != ((entry->modifiers & GDK_RELEASE_MASK) != 0)) - continue; - - binding_set = entry->binding_set; - - if (binding_set->current) - continue; - binding_set->current = entry; - - 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) - { - PatternSpec *pspec; - - pspec = slist->data; - patterns = g_slist_insert_sorted (patterns, pspec, gtk_binding_pattern_compare); - } - } + GtkBindingArg *arg; - return patterns; -} + 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) +{ + 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: + slist_p = &binding_set->widget_path_pspecs; + break; + case GTK_PATH_WIDGET_CLASS: + slist_p = &binding_set->widget_class_pspecs; + break; + case GTK_PATH_CLASS: + slist_p = &binding_set->class_branch_pspecs; + break; + default: + g_assert_not_reached (); + slist_p = NULL; + break; + } + + 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) + { + PatternSpec *tmp_pspec; + + tmp_pspec = slist->data; + slist = slist->next; + + 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) + { + pspec->seq_id |= seq_id++ & 0x0fffffff; + *slist_p = g_slist_prepend (*slist_p, pspec); + } +} + +static gint +find_entry_with_binding (GtkBindingEntry *entry, + GtkBindingSet *binding_set) +{ + return (entry->binding_set == binding_set) ? 0 : 1; +} static gboolean -gtk_bindings_activate_list (GtkObject *object, - GSList *entries, - gboolean is_release) +binding_activate (GtkBindingSet *binding_set, + GSList *entries, + GObject *object, + gboolean is_release, + gboolean *unbound) { - GtkWidget *widget = GTK_WIDGET (object); + GtkBindingEntry *entry; + GSList *elem; + + elem = g_slist_find_custom (entries, binding_set, + (GCompareFunc) find_entry_with_binding); + + if (!elem) + return FALSE; + + entry = elem->data; + + if (is_release != ((entry->modifiers & GDK_RELEASE_MASK) != 0)) + return FALSE; + + if (entry->marks_unbound) + { + *unbound = TRUE; + return FALSE; + } + + if (gtk_binding_entry_activate (entry, object)) + return TRUE; + + 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) + context = gtk_widget_get_style_context (GTK_WIDGET (object)); + state = gtk_widget_get_state_flags (GTK_WIDGET (object)); + + gtk_style_context_get (context, state, + "gtk-key-bindings", &array, + NULL); + if (array) { - guint path_length; - gchar *path, *path_reversed; - GSList *patterns; - gboolean unbound; - - gtk_widget_path (widget, &path_length, &path, &path_reversed); - patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_WIDGET, is_release); - handled = binding_match_activate (patterns, object, path_length, path, path_reversed, &unbound); - g_slist_free (patterns); - g_free (path); - g_free (path_reversed); + gint i; - if (unbound) - return FALSE; - } + 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; + } - if (!handled) - { - guint path_length; - gchar *path, *path_reversed; - GSList *patterns; - gboolean unbound; - - gtk_widget_class_path (widget, &path_length, &path, &path_reversed); - patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_WIDGET_CLASS, is_release); - handled = binding_match_activate (patterns, object, path_length, path, path_reversed, &unbound); - g_slist_free (patterns); - g_free (path); - g_free (path_reversed); + g_ptr_array_unref (array); if (unbound) return FALSE; @@ -1217,32 +1569,22 @@ gtk_bindings_activate_list (GtkObject *object, if (!handled) { - GSList *patterns; GType class_type; - gboolean unbound = FALSE; - patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_CLASS, is_release); class_type = G_TYPE_FROM_INSTANCE (object); + while (class_type && !handled) - { - guint path_length; - gchar *path; - gchar *path_reversed; - - path = g_strdup (g_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, &unbound); - g_free (path); - g_free (path_reversed); - - if (unbound) - break; + { + binding_set = gtk_binding_set_find_interned (g_type_name (class_type)); + class_type = g_type_parent (class_type); - class_type = g_type_parent (class_type); - } - g_slist_free (patterns); + if (!binding_set) + continue; + + handled = binding_activate (binding_set, entries, + object, is_release, + &unbound); + } if (unbound) return FALSE; @@ -1263,9 +1605,9 @@ gtk_bindings_activate_list (GtkObject *object, * Return value: %TRUE if a binding was found and activated */ gboolean -gtk_bindings_activate (GtkObject *object, - guint keyval, - GdkModifierType modifiers) +gtk_bindings_activate (GObject *object, + guint keyval, + GdkModifierType modifiers) { GSList *entries = NULL; GdkDisplay *display; @@ -1273,8 +1615,6 @@ gtk_bindings_activate (GtkObject *object, gboolean handled = FALSE; gboolean is_release; - g_return_val_if_fail (GTK_IS_OBJECT (object), FALSE); - if (!GTK_IS_WIDGET (object)) return FALSE; @@ -1283,7 +1623,7 @@ gtk_bindings_activate (GtkObject *object, 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); @@ -1295,18 +1635,18 @@ gtk_bindings_activate (GtkObject *object, /** * gtk_bindings_activate_event: - * @object: a #GtkObject (generally must be a widget) + * @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_bindings_activate_event (GtkObject *object, +gtk_bindings_activate_event (GObject *object, GdkEventKey *event) { GSList *entries = NULL; @@ -1314,8 +1654,6 @@ gtk_bindings_activate_event (GtkObject *object, GtkKeyHash *key_hash; gboolean handled = FALSE; - g_return_val_if_fail (GTK_IS_OBJECT (object), FALSE); - if (!GTK_IS_WIDGET (object)) return FALSE; @@ -1323,355 +1661,15 @@ gtk_bindings_activate_event (GtkObject *object, key_hash = binding_key_hash_for_keymap (gdk_keymap_get_for_display (display)); entries = _gtk_key_hash_lookup (key_hash, - event->hardware_keycode, - event->state, - BINDING_MOD_MASK () & ~GDK_RELEASE_MASK, - event->group); - + 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); + event->type == GDK_KEY_RELEASE); g_slist_free (entries); return handled; } - -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 - { - if (need_arg) - expected_token = G_TOKEN_INT; - else - expected_token = ')'; - g_scanner_get_next_token (scanner); - switch (scanner->token) - { - GtkBindingArg *arg; - - 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 != GTK_RC_TOKEN_BIND && - scanner->token != GTK_RC_TOKEN_UNBIND) - return GTK_RC_TOKEN_BIND; - unbind = scanner->token == GTK_RC_TOKEN_UNBIND; - g_scanner_get_next_token (scanner); - if (scanner->token != 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 != '}') - { - switch (scanner->next_token) - { - guint expected_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; -} - -guint -_gtk_binding_parse_binding (GScanner *scanner) -{ - gchar *name; - GtkBindingSet *binding_set; - - g_return_val_if_fail (scanner != NULL, G_TOKEN_ERROR); - - g_scanner_get_next_token (scanner); - if (scanner->token != GTK_RC_TOKEN_BINDING) - return GTK_RC_TOKEN_BINDING; - g_scanner_get_next_token (scanner); - if (scanner->token != G_TOKEN_STRING) - return G_TOKEN_STRING; - name = g_strdup (scanner->value.v_string); - - g_scanner_get_next_token (scanner); - if (scanner->token != '{') - { - g_free (name); - return G_TOKEN_STRING; - } - - binding_set = gtk_binding_set_find (name); - if (!binding_set) - { - binding_set = gtk_binding_set_new (name); - binding_set->parsed = 1; - } - g_free (name); - - g_scanner_peek_next_token (scanner); - while (scanner->next_token != '}') - { - switch (scanner->next_token) - { - guint expected_token; - - case GTK_RC_TOKEN_BIND: - case GTK_RC_TOKEN_UNBIND: - expected_token = gtk_binding_parse_bind (scanner, binding_set); - 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 void -free_pattern_specs (GSList *pattern_specs) -{ - GSList *slist; - - for (slist = pattern_specs; slist; slist = slist->next) - { - PatternSpec *pspec; - - pspec = slist->data; - - pattern_spec_free (pspec); - } - - g_slist_free (pattern_specs); -} - -static void -binding_set_delete (GtkBindingSet *binding_set) -{ - GtkBindingEntry *entry, *next; - - entry = binding_set->entries; - while (entry) - { - next = entry->set_next; - binding_entry_destroy (entry); - entry = next; - } - - free_pattern_specs (binding_set->widget_path_pspecs); - free_pattern_specs (binding_set->widget_class_pspecs); - free_pattern_specs (binding_set->class_branch_pspecs); - - g_free (binding_set); -} - -/** - * _gtk_binding_reset_parsed: - * - * Remove all binding sets that were added by gtk_binding_parse_binding(). - */ -void -_gtk_binding_reset_parsed (void) -{ - GSList *slist, *next; - - slist = binding_set_list; - while (slist) - { - GtkBindingSet *binding_set; - - binding_set = slist->data; - next = slist->next; - - if (binding_set->parsed) - { - binding_set_list = g_slist_delete_link (binding_set_list, slist); - binding_set_delete (binding_set); - } - - slist = next; - } -}