X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkbindings.c;h=efb4ea08c58c8f1a80b827e403f079f8afd9fe4c;hb=1a1b91e840f4aca03b9da09802a505d50ac29608;hp=50222147d30e048816f6a90e2d11bf3942709682;hpb=347efc33b8007f5d2fe625d7c0e6a51fbb3a3438;p=~andy%2Fgtk diff --git a/gtk/gtkbindings.c b/gtk/gtkbindings.c index 50222147d..efb4ea08c 100644 --- a/gtk/gtkbindings.c +++ b/gtk/gtkbindings.c @@ -5,41 +5,52 @@ * 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ -#include -#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 #include +#include #include "gtkbindings.h" -#include "gtksignal.h" +#include "gtkkeyhash.h" #include "gtkwidget.h" +#include "gtkrc.h" /* --- defines --- */ #define BINDING_MOD_MASK() (gtk_accelerator_get_default_mod_mask () | GDK_RELEASE_MASK) -#define BINDING_TYPE_INT (GTK_TYPE_INT) -#define BINDING_TYPE_LONG (GTK_TYPE_LONG) -#define BINDING_TYPE_FLOAT (GTK_TYPE_FLOAT) -#define BINDING_TYPE_DOUBLE (GTK_TYPE_DOUBLE) -#define BINDING_TYPE_STRING (GTK_TYPE_STRING) -#define BINDING_TYPE_IDENTIFIER (42) + +/* --- structures --- */ +typedef struct { + GPatternSpec *pspec; + gpointer user_data; + guint seq_id; +} PatternSpec; /* --- variables --- */ static GHashTable *binding_entry_hash_table = NULL; +static GSList *binding_key_hashes = NULL; static GSList *binding_set_list = NULL; static const gchar *key_class_binding_set = "gtk-class-binding-set"; static GQuark key_id_class_binding_set = 0; @@ -68,8 +79,7 @@ binding_signal_free (GtkBindingSignal *sig) for (i = 0; i < sig->n_args; i++) { - if (sig->args[i].arg_type == BINDING_TYPE_STRING || - sig->args[i].arg_type == BINDING_TYPE_IDENTIFIER) + if (G_TYPE_FUNDAMENTAL (sig->args[i].arg_type) == G_TYPE_STRING) g_free (sig->args[i].d.string_data); } g_free (sig->args); @@ -99,11 +109,81 @@ binding_entries_compare (gconstpointer a, return (ea->keyval == eb->keyval && ea->modifiers == eb->modifiers); } +static void +binding_key_hash_insert_entry (GtkKeyHash *key_hash, + GtkBindingEntry *entry) +{ + guint keyval = entry->keyval; + + /* We store lowercased accelerators. To deal with this, if + * was specified, uppercase. + */ + if (entry->modifiers & GDK_SHIFT_MASK) + { + if (keyval == GDK_Tab) + keyval = GDK_ISO_Left_Tab; + else + keyval = gdk_keyval_to_upper (keyval); + } + + _gtk_key_hash_add_entry (key_hash, keyval, entry->modifiers & ~GDK_RELEASE_MASK, entry); +} + +static void +binding_key_hash_destroy (gpointer data) +{ + GtkKeyHash *key_hash = data; + + binding_key_hashes = g_slist_remove (binding_key_hashes, key_hash); + _gtk_key_hash_free (key_hash); +} + +static void +insert_entries_into_key_hash (gpointer key, + gpointer value, + gpointer data) +{ + GtkKeyHash *key_hash = data; + GtkBindingEntry *entry = value; + + for (; entry; entry = entry->hash_next) + binding_key_hash_insert_entry (key_hash, entry); +} + +static GtkKeyHash * +binding_key_hash_for_keymap (GdkKeymap *keymap) +{ + static GQuark key_hash_quark = 0; + GtkKeyHash *key_hash; + + if (!key_hash_quark) + key_hash_quark = g_quark_from_static_string ("gtk-binding-key-hash"); + + key_hash = g_object_get_qdata (G_OBJECT (keymap), key_hash_quark); + + if (!key_hash) + { + key_hash = _gtk_key_hash_new (keymap, NULL); + g_object_set_qdata_full (G_OBJECT (keymap), key_hash_quark, key_hash, binding_key_hash_destroy); + + if (binding_entry_hash_table) + g_hash_table_foreach (binding_entry_hash_table, + insert_entries_into_key_hash, + key_hash); + + binding_key_hashes = g_slist_prepend (binding_key_hashes, key_hash); + } + + return key_hash; +} + + static GtkBindingEntry* -binding_entry_new (GtkBindingSet *binding_set, - guint keyval, - guint modifiers) +binding_entry_new (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers) { + GSList *tmp_list; GtkBindingEntry *entry; if (!binding_entry_hash_table) @@ -121,11 +201,15 @@ binding_entry_new (GtkBindingSet *binding_set, 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; } @@ -161,6 +245,7 @@ binding_entry_destroy (GtkBindingEntry *entry) register GtkBindingEntry *tmp; GtkBindingEntry *begin; register GtkBindingEntry *last; + GSList *tmp_list; /* unlink from binding set */ @@ -204,10 +289,14 @@ binding_entry_destroy (GtkBindingEntry *entry) 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; @@ -217,24 +306,9 @@ 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; @@ -254,67 +328,114 @@ binding_ht_lookup_entry (GtkBindingSet *set, } static gboolean -binding_compose_params (GtkBindingArg *args, - GtkSignalQuery *query, - GtkArg **params_p) +binding_compose_params (GtkObject *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; + + /* 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->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) + GValue tmp_value = { 0, }; + + g_value_init (params, *types); + + switch (G_TYPE_FUNDAMENTAL (args->arg_type)) { - case BINDING_TYPE_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; + case G_TYPE_DOUBLE: + g_value_init (&tmp_value, G_TYPE_DOUBLE); + g_value_set_double (&tmp_value, args->d.double_data); break; - case BINDING_TYPE_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; + case G_TYPE_LONG: + g_value_init (&tmp_value, G_TYPE_LONG); + g_value_set_long (&tmp_value, args->d.long_data); break; - case BINDING_TYPE_STRING: - if (params->type == GTK_TYPE_STRING) - GTK_VALUE_STRING (*params) = args->d.string_data; + 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 - valid = FALSE; + { + g_value_init (&tmp_value, G_TYPE_STRING); + g_value_set_static_string (&tmp_value, args->d.string_data); + } break; - case BINDING_TYPE_IDENTIFIER: default: valid = FALSE; break; } + + if (valid) + { + if (!g_value_transform (&tmp_value, params)) + valid = FALSE; + + g_value_unset (&tmp_value); + } + types++; params++; args++; @@ -322,6 +443,11 @@ binding_compose_params (GtkBindingArg *args, if (!valid) { + guint j; + + for (j = 0; j < i; j++) + g_value_unset (&(*params_p)[j]); + g_free (*params_p); *params_p = NULL; } @@ -329,26 +455,29 @@ binding_compose_params (GtkBindingArg *args, return valid; } -static void -gtk_binding_entry_activate (GtkBindingEntry *entry, - GtkObject *object) +static gboolean +gtk_binding_entry_activate (GtkBindingEntry *entry, + GtkObject *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 = { 0, }; 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); @@ -357,15 +486,15 @@ gtk_binding_entry_activate (GtkBindingEntry *entry, entry->binding_set->set_name, accelerator, sig->signal_name, - gtk_type_name (GTK_OBJECT_TYPE (object))); + g_type_name (G_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)) + 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\": " @@ -373,9 +502,9 @@ gtk_binding_entry_activate (GtkBindingEntry *entry, entry->binding_set->set_name, accelerator, sig->signal_name, - gtk_type_name (GTK_OBJECT_TYPE (object))); + g_type_name (G_OBJECT_TYPE (object))); } - else if (!(query->signal_flags & GTK_RUN_ACTION)) + 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\": " @@ -383,25 +512,41 @@ gtk_binding_entry_activate (GtkBindingEntry *entry, entry->binding_set->set_name, accelerator, sig->signal_name, - gtk_type_name (GTK_OBJECT_TYPE (object))); + g_type_name (G_OBJECT_TYPE (object))); } g_free (accelerator); - g_free (query); if (accelerator) continue; - gtk_signal_emitv (object, signal_id, params); + if (query.return_type == 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); + } + 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) + 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; } GtkBindingSet* @@ -418,8 +563,9 @@ gtk_binding_set_new (const gchar *set_name) binding_set->class_branch_pspecs = NULL; binding_set->entries = NULL; binding_set->current = NULL; + binding_set->parsed = FALSE; - binding_set_list = g_slist_prepend (NULL, binding_set); + binding_set_list = g_slist_prepend (binding_set_list, binding_set); return binding_set; } @@ -440,10 +586,10 @@ gtk_binding_set_by_class (gpointer object_class) if (binding_set) return binding_set; - binding_set = gtk_binding_set_new (gtk_type_name (class->type)); + binding_set = gtk_binding_set_new (g_type_name (G_OBJECT_CLASS_TYPE (class))); gtk_binding_set_add_path (binding_set, GTK_PATH_CLASS, - gtk_type_name (class->type), + 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); @@ -471,28 +617,20 @@ gtk_binding_set_find (const gchar *set_name) gboolean gtk_binding_set_activate (GtkBindingSet *binding_set, guint keyval, - guint modifiers, + GdkModifierType modifiers, GtkObject *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); 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; } @@ -500,7 +638,7 @@ gtk_binding_set_activate (GtkBindingSet *binding_set, void gtk_binding_entry_clear (GtkBindingSet *binding_set, guint keyval, - guint modifiers) + GdkModifierType modifiers) { GtkBindingEntry *entry; @@ -519,7 +657,7 @@ gtk_binding_entry_clear (GtkBindingSet *binding_set, void gtk_binding_entry_remove (GtkBindingSet *binding_set, guint keyval, - guint modifiers) + GdkModifierType modifiers) { GtkBindingEntry *entry; @@ -536,7 +674,7 @@ gtk_binding_entry_remove (GtkBindingSet *binding_set, void gtk_binding_entry_add_signall (GtkBindingSet *binding_set, guint keyval, - guint modifiers, + GdkModifierType modifiers, const gchar *signal_name, GSList *binding_args) { @@ -566,30 +704,32 @@ gtk_binding_entry_add_signall (GtkBindingSet *binding_set, binding_signal_free (signal); return; } - arg->arg_type = tmp_arg->arg_type; - switch (tmp_arg->arg_type) + switch (G_TYPE_FUNDAMENTAL (tmp_arg->arg_type)) { - case BINDING_TYPE_INT: - case BINDING_TYPE_LONG: + case G_TYPE_LONG: + arg->arg_type = G_TYPE_LONG; arg->d.long_data = tmp_arg->d.long_data; break; - case BINDING_TYPE_FLOAT: - case BINDING_TYPE_DOUBLE: + case G_TYPE_DOUBLE: + arg->arg_type = G_TYPE_DOUBLE; arg->d.double_data = tmp_arg->d.double_data; break; - case BINDING_TYPE_STRING: - if (!tmp_arg->d.string_data) + 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); - 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); + g_type_name (arg->arg_type), n); binding_signal_free (signal); return; } @@ -612,7 +752,7 @@ gtk_binding_entry_add_signall (GtkBindingSet *binding_set, void gtk_binding_entry_add_signal (GtkBindingSet *binding_set, guint keyval, - guint modifiers, + GdkModifierType modifiers, const gchar *signal_name, guint n_args, ...) @@ -624,9 +764,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; for (i = 0; i < n_args; i++) @@ -637,31 +774,46 @@ gtk_binding_entry_add_signal (GtkBindingSet *binding_set, slist = g_slist_prepend (slist, arg); arg->arg_type = va_arg (args, GtkType); - switch (arg->arg_type) + switch (G_TYPE_FUNDAMENTAL (arg->arg_type)) { - case BINDING_TYPE_INT: + /* for elaborated commenting about var args collection, take a look + * at gtk_arg_collect_value() in gtkargcollector.c + */ + 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 BINDING_TYPE_LONG: + case G_TYPE_LONG: + case G_TYPE_ULONG: + arg->arg_type = G_TYPE_LONG; arg->d.long_data = va_arg (args, glong); break; - case BINDING_TYPE_FLOAT: - arg->d.double_data = va_arg (args, gfloat); - break; - case BINDING_TYPE_DOUBLE: + case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: + arg->arg_type = G_TYPE_DOUBLE; arg->d.double_data = va_arg (args, gdouble); break; - case BINDING_TYPE_STRING: + 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(): value of `string' arg[%u] is `NULL'", i); + 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]", - gtk_type_name (arg->arg_type), i); + g_type_name (arg->arg_type), i); i += n_args + 1; break; } @@ -689,12 +841,13 @@ gtk_binding_set_add_path (GtkBindingSet *binding_set, 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; @@ -715,55 +868,63 @@ gtk_binding_set_add_path (GtkBindingSet *binding_set, 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->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)) + if (g_pattern_spec_equal (tmp_pspec->pspec, pspec->pspec)) { - gtk_pattern_spec_free_segs (pspec); + GtkPathPriorityType lprio = tmp_pspec->seq_id >> 28; + + g_pattern_spec_free (pspec->pspec); g_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); + { + pspec->seq_id |= seq_id++ & 0x0fffffff; + *slist_p = g_slist_prepend (*slist_p, pspec); + } } -static inline gboolean +static gboolean binding_match_activate (GSList *pspec_list, GtkObject *object, guint path_length, - gchar *path, - gchar *path_reversed) + const gchar *path, + const gchar *path_reversed) { GSList *slist; for (slist = pspec_list; slist; slist = slist->next) { - GtkPatternSpec *pspec; + PatternSpec *pspec; pspec = slist->data; - if (gtk_pattern_match (pspec, path_length, path, path_reversed)) + if (g_pattern_match (pspec->pspec, path_length, path, path_reversed)) { GtkBindingSet *binding_set; binding_set = pspec->user_data; - gtk_binding_entry_activate (binding_set->current, object); - - return TRUE; + if (gtk_binding_entry_activate (binding_set->current, object)) + return TRUE; } } @@ -771,29 +932,51 @@ binding_match_activate (GSList *pspec_list, } static gint -gtk_binding_pattern_compare (gconstpointer a, - gconstpointer b) +gtk_binding_pattern_compare (gconstpointer new_pattern, + gconstpointer existing_pattern) { - register const GtkPatternSpec *pa = a; - register const GtkPatternSpec *pb = b; + register const PatternSpec *np = new_pattern; + register const PatternSpec *ep = existing_pattern; + + /* walk the list as long as the existing patterns have + * higher priorities. + */ - return pa->seq_id < pb->seq_id ? -1 : 1; + return np->seq_id < ep->seq_id; } -static inline GSList* -gtk_binding_entries_sort_patterns (GtkBindingEntry *entries, - GtkPathType path_id) +static GSList* +gtk_binding_entries_sort_patterns (GSList *entries, + GtkPathType path_id, + gboolean is_release) { GSList *patterns; + GSList *tmp_list; patterns = NULL; - while (entries) + for (tmp_list = entries; tmp_list; tmp_list = tmp_list->next) + { + GtkBindingEntry *entry = tmp_list->data; + GtkBindingSet *binding_set; + + binding_set = entry->binding_set; + binding_set->current = NULL; + } + + for (; entries; entries = entries->next) { - register GtkBindingSet *binding_set; + GtkBindingEntry *entry = entries->data; + GtkBindingSet *binding_set; GSList *slist = NULL; - binding_set = entries->binding_set; - binding_set->current = entries; + if (is_release != ((entry->modifiers & GDK_RELEASE_MASK) != 0)) + continue; + + binding_set = entry->binding_set; + + if (binding_set->current) + continue; + binding_set->current = entry; switch (path_id) { @@ -810,41 +993,24 @@ gtk_binding_entries_sort_patterns (GtkBindingEntry *entries, for (; slist; slist = slist->next) { - GtkPatternSpec *pspec; + PatternSpec *pspec; pspec = slist->data; patterns = g_slist_insert_sorted (patterns, pspec, gtk_binding_pattern_compare); } - - entries = entries->hash_next; } return patterns; } - -gboolean -gtk_bindings_activate (GtkObject *object, - guint keyval, - guint modifiers) +static gboolean +gtk_bindings_activate_list (GtkObject *object, + GSList *entries, + gboolean is_release) { - GtkBindingEntry *entries; - GtkWidget *widget; + GtkWidget *widget = GTK_WIDGET (object); gboolean handled = FALSE; - g_return_val_if_fail (object != NULL, FALSE); - g_return_val_if_fail (GTK_IS_OBJECT (object), FALSE); - - if (!GTK_IS_WIDGET (object) || GTK_OBJECT_DESTROYED (object)) - return FALSE; - - widget = GTK_WIDGET (object); - - keyval = gdk_keyval_to_lower (keyval); - modifiers = modifiers & BINDING_MOD_MASK (); - - entries = binding_ht_lookup_list (keyval, modifiers); - if (!entries) return FALSE; @@ -855,7 +1021,7 @@ gtk_bindings_activate (GtkObject *object, GSList *patterns; gtk_widget_path (widget, &path_length, &path, &path_reversed); - patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_WIDGET); + patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_WIDGET, is_release); handled = binding_match_activate (patterns, object, path_length, path, path_reversed); g_slist_free (patterns); g_free (path); @@ -869,7 +1035,7 @@ gtk_bindings_activate (GtkObject *object, GSList *patterns; gtk_widget_class_path (widget, &path_length, &path, &path_reversed); - patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_WIDGET_CLASS); + patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_WIDGET_CLASS, is_release); handled = binding_match_activate (patterns, object, path_length, path, path_reversed); g_slist_free (patterns); g_free (path); @@ -879,23 +1045,24 @@ gtk_bindings_activate (GtkObject *object, if (!handled) { GSList *patterns; - GtkType class_type; + GType class_type; - patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_CLASS); - class_type = GTK_OBJECT_TYPE (object); + 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, *path_reversed; + const gchar *path; + gchar *path_reversed; - path = gtk_type_name (class_type); + path = 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); g_free (path_reversed); - class_type = gtk_type_parent (class_type); + class_type = g_type_parent (class_type); } g_slist_free (patterns); } @@ -903,250 +1070,434 @@ gtk_bindings_activate (GtkObject *object, return handled; } +gboolean +gtk_bindings_activate (GtkObject *object, + guint keyval, + GdkModifierType modifiers) +{ + GSList *entries = NULL; + GdkDisplay *display; + GtkKeyHash *key_hash; + gboolean handled = FALSE; + gboolean is_release; + g_return_val_if_fail (GTK_IS_OBJECT (object), FALSE); + if (!GTK_IS_WIDGET (object)) + return FALSE; + is_release = (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); + g_slist_free (entries); + return handled; +} +/** + * _gtk_bindings_activate_event: + * @object: a #GtkObject (generally must be a widget) + * @event: a #GdkEventKey + * + * Looks up key bindings for @object to find one matching + * @event, and if one was found, activate it. + * + * Return value: %TRUE if a matching key binding was found + **/ +gboolean +_gtk_bindings_activate_event (GtkObject *object, + GdkEventKey *event) +{ + GSList *entries = NULL; + GdkDisplay *display; + GtkKeyHash *key_hash; + gboolean handled = FALSE; -/* Patterns - */ + g_return_val_if_fail (GTK_IS_OBJECT (object), FALSE); -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; + if (!GTK_IS_WIDGET (object)) + return FALSE; + + 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 (key_hash, + event->hardware_keycode, + event->state & BINDING_MOD_MASK () & ~GDK_RELEASE_MASK, + event->group); - ch = *pattern; - pattern++; - while (ch) + handled = gtk_bindings_activate_list (object, entries, + event->type == GDK_KEY_RELEASE); + + g_slist_free (entries); + + return handled; +} + +static guint +gtk_binding_parse_signal (GScanner *scanner, + GtkBindingSet *binding_set, + 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 { - switch (ch) + if (need_arg) + expected_token = G_TOKEN_INT; + else + expected_token = ')'; + g_scanner_get_next_token (scanner); + switch (scanner->token) { - case '?': - if (!*string) - return FALSE; - string++; - break; - - case '*': - do + GtkBindingArg *arg; + + case G_TOKEN_FLOAT: + if (need_arg) { - ch = *pattern; - pattern++; - if (ch == '?') + need_arg = FALSE; + arg = g_new (GtkBindingArg, 1); + arg->arg_type = G_TYPE_DOUBLE; + arg->d.double_data = scanner->value.v_float; + if (negate) { - if (!*string) - return FALSE; - string++; + arg->d.double_data = - arg->d.double_data; + negate = FALSE; } + args = g_slist_prepend (args, arg); } - while (ch == '*' || ch == '?'); - if (!ch) - return TRUE; - do + else + done = TRUE; + break; + case G_TOKEN_INT: + if (need_arg) { - while (ch != *string) + need_arg = FALSE; + arg = g_new (GtkBindingArg, 1); + arg->arg_type = G_TYPE_LONG; + arg->d.long_data = scanner->value.v_int; + if (negate) { - if (!*string) - return FALSE; - string++; + arg->d.long_data = - arg->d.long_data; + negate = FALSE; } - string++; - if (gtk_pattern_ph_match (pattern, string)) - return TRUE; + args = g_slist_prepend (args, arg); } - while (*string); + else + done = TRUE; break; - - default: - if (ch == *string) - string++; + 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 - return FALSE; + 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; } - - ch = *pattern; - pattern++; } + while (!done); + scanner->config->scan_symbols = TRUE; - return *string == 0; + 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; } -gboolean -gtk_pattern_match (GtkPatternSpec *pspec, - guint string_length, - const gchar *string, - const gchar *string_reversed) +static inline guint +gtk_binding_parse_bind (GScanner *scanner, + GtkBindingSet *binding_set) { - 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) + guint keyval = 0; + GdkModifierType modifiers = 0; + + g_return_val_if_fail (scanner != NULL, G_TOKEN_ERROR); + + g_scanner_get_next_token (scanner); + if (scanner->token != GTK_RC_TOKEN_BIND) + return GTK_RC_TOKEN_BIND; + 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; + + g_scanner_get_next_token (scanner); + if (scanner->token != '{') + return '{'; + + gtk_binding_entry_clear (binding_set, keyval, modifiers); + + g_scanner_peek_next_token (scanner); + while (scanner->next_token != '}') { - 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; + 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; } -void -gtk_pattern_spec_init (GtkPatternSpec *pspec, - const gchar *pattern) +guint +gtk_binding_parse_binding (GScanner *scanner) { - 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, '*')) + 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 != '{') { - pspec->match_type = GTK_MATCH_EXACT; - return; + g_free (name); + return G_TOKEN_STRING; } - - p = pspec->pattern; - while (*p == '*') - p++; - if (p > pspec->pattern && - !strchr (p, '*')) + + binding_set = gtk_binding_set_find (name); + if (!binding_set) { - 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; + binding_set = gtk_binding_set_new (name); + binding_set->parsed = 1; } - - p = pspec->pattern_reversed; - while (*p == '*') - p++; - if (p > pspec->pattern_reversed && - !strchr (p, '*')) + g_free (name); + + g_scanner_peek_next_token (scanner); + while (scanner->next_token != '}') { - 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); + switch (scanner->next_token) + { + guint expected_token; + + case GTK_RC_TOKEN_BIND: + 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; } -gboolean -gtk_pattern_match_string (GtkPatternSpec *pspec, - const gchar *string) +static void +free_pattern_specs (GSList *pattern_specs) { - 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; + GSList *slist; + + for (slist = pattern_specs; slist; slist = slist->next) + { + PatternSpec *pspec; + + pspec = slist->data; + + g_pattern_spec_free (pspec->pspec); + g_free (pspec); + } + + g_slist_free (pattern_specs); } -gboolean -gtk_pattern_match_simple (const gchar *pattern, - const gchar *string) +static void +binding_set_delete (GtkBindingSet *binding_set) { - 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); + GtkBindingEntry *entry, *next; + + entry = binding_set->entries; + while (entry) + { + next = entry->set_next; + binding_entry_destroy (entry); + entry = next; + } - return ergo; + 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->set_name); + g_free (binding_set); } +/** + * _gtk_binding_reset_parsed: + * + * Removing all binding sets that were added by + * gtk_binding_parse_binding() + **/ void -gtk_pattern_spec_free_segs (GtkPatternSpec *pspec) +_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; + } +} + +guint +_gtk_binding_signal_new (const gchar *signal_name, + GType itype, + GSignalFlags signal_flags, + GCallback handler, + GSignalAccumulator accumulator, + gpointer accu_data, + GSignalCMarshaller c_marshaller, + GType return_type, + guint n_params, + ...) { - g_return_if_fail (pspec != NULL); + va_list args; + guint signal_id; + + g_return_val_if_fail (signal_name != NULL, 0); - g_free (pspec->pattern); - pspec->pattern = NULL; - g_free (pspec->pattern_reversed); - pspec->pattern_reversed = NULL; + va_start (args, n_params); + + signal_id = g_signal_new_valist (signal_name, itype, signal_flags, + g_cclosure_new (handler, NULL, NULL), + accumulator, accu_data, c_marshaller, + return_type, n_params, args); + + va_end (args); + + return signal_id; }