diff options
author | Tim Janik <timj@gtk.org> | 1998-06-07 06:48:56 +0000 |
---|---|---|
committer | Tim Janik <timj@src.gnome.org> | 1998-06-07 06:48:56 +0000 |
commit | a391196ba51da34253593712e8bf4552c04f19e5 (patch) | |
tree | 50c2edfc5d066467f3c33c1f82c89dd9923c44c4 /gtk/gtkbindings.c | |
parent | f522f36dd1c484b133ba6b04027667aa9144ad04 (diff) | |
download | gtk+-a391196ba51da34253593712e8bf4552c04f19e5.tar.gz |
fixed an assertment.
Sat Jun 6 06:01:24 1998 Tim Janik <timj@gtk.org>
* gtk/gtksignal.c (gtk_signal_emitv): fixed an assertment.
* gtk/makeenums.awk: a script to generate the GtkEnumValue arrays from,
this should eventually be done by gentypeinfo.el somewhen.
* gtk/gtkenumvalues.c: new generated file to hold GtkEnumValue arrays.
* gtk/gtktypeutils.h: new function gtk_enum_values() to retrive all the
enum values of an enum type.
* gtk/gtk.defs:
* gtk/gtkcurve.h:
* gtk/gtkobject.h:
* gtk/gtkprivate.h:
* gtk/gtkwidget.h:
* gtk/gtkenums.h:
brought enum/flags definitions in sync, added a few more enum
definitions for bindings and pattern matching.
* some more macro and GtkType fixups in various places.
* gdk/gdktypes.h (enum): added a new value GDK_AFTER_MASK, which is used
as a key-release modifier for the binding system.
Fri Jun 5 06:06:06 1998 Tim Janik <timj@gtk.org>
* gtk/gtkmenu.h (struct _GtkMenu): removed GList*children, since it
was a stale list pointer that is already present in GtkMenuShell.
* gtk/gtkmenushell.h (struct _GtkMenuShellClass): added a signal
GtkMenuShell::selection_done which is emitted after the menu shell
poped down again and all possible menu items have been activated.
Thu Jun 4 02:20:42 1998 Tim Janik <timj@gtk.org>
* gtk/gtkmenushell.c (gtk_menu_shell_button_release): flush the x-queue
before activation of the menuitem, so the menu is actually taken off the
screen prior to any menu item activation.
* gtk/gtkctree.c (gtk_ctree_get_row_data): allow function invokation
for NULL nodes.
* gtk/gtkwidget.h:
* gtk/gtkwidget.c: new function gtk_widget_stop_accelerator to stop
the emission of the "add-accelerator" signal on a widget. this is
usefull to prevent accelerator installation on certain widgets.
* gtk/gtknotebook.c (gtk_notebook_menu_item_create): keep the menu
labels left justified, by setting their alignment. stop accelerator
installation for the menu items, since we use dynamic menus.
Wed Jun 3 06:41:22 1998 Tim Janik <timj@gtk.org>
* gtk/gtkmenufactory.c: adaptions to use the new accel groups. people
should *really* use GtkItemFactory. this is only for preserving source
compatibility where possible, use of GtkMenuFactory is deprecated as of
now.
* gtk/gtkobject.h (gtk_object_class_add_user_signal): new function
to create user signals of type GTK_RUN_NO_RECURSE. don't know why i
missed this possibility when i added gtk_object_class_add_user_signal
in late january.
* gtk/gtkmain.c (gtk_init): ignore subsequent function calls.
Sun May 31 07:31:09 1998 Tim Janik <timj@gtk.org>
* gtk/gtkaccelgroup.h:
* gtk/gtkaccelgroup.c: new implementation of the accelerator concept.
* gtk/gtkaccellabel.h:
* gtk/gtkaccellabel.c: new widget derived from GtkLabel whitch features
display of the accelerators associated with a certain widget.
* gtk/gtkitemfactory.h:
* gtk/gtkitemfactory.c: new widget, item factory with automatic rc
parsing and accelerator handling.
* gtk/gtkmenu.c (gtk_menu_reposition): new function to care for
positioning a menu.
(gtk_menu_map): removed the allocation code.
(gtk_menu_size_allocate): care for redrawing of children and resize
our widget->window correctly.
(gtk_menu_key_press): feature the new accelerator groups.
* gtk/gtkmenuitem.c (gtk_menu_item_size_allocate): reposition the
submenu if neccessary.
* gtk/gtkmenuitem.c:
* gtk/gtkcheckmenuitem.c:
* gtk/gtkradiomenuitem.c: use GtkAccelLabel in the *_new_with_label()
function variants.
* gdk/gdk.c:
(gdk_keyval_from_name):
(gdk_keyval_name): new functions for keyval<->key-name associations.
(gdk_keyval_to_upper):
(gdk_keyval_to_lower):
(gdk_keyval_is_upper):
(gdk_keyval_is_lower): new functions to check/translate keyvalues with
regards to their cases.
Wed May 27 00:48:10 1998 Tim Janik <timj@gtk.org>
* gtk/gtkwidget.c (gtk_widget_class_path): new function to calculate a
widget's class path.
(gtk_widget_path): new function to calculate a widget's name path.
* gtk/gtkrc.c: newly introduced GtkPatternSpec structures to speed up
pattern matching, features reversed pattern matches.
Diffstat (limited to 'gtk/gtkbindings.c')
-rw-r--r-- | gtk/gtkbindings.c | 1136 |
1 files changed, 1136 insertions, 0 deletions
diff --git a/gtk/gtkbindings.c b/gtk/gtkbindings.c new file mode 100644 index 0000000000..c9e55181ed --- /dev/null +++ b/gtk/gtkbindings.c @@ -0,0 +1,1136 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * GtkBindingSet: Keybinding manager for GtkObjects. + * 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 + * 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. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include <ctype.h> +#include <strings.h> +#include <stdarg.h> +#include "gtkbindings.h" +#include "gtksignal.h" +#include "gtkwidget.h" + + +/* --- defines --- */ +#define BINDING_MOD_MASK() (gtk_accelerator_get_default_mod_mask () | GDK_AFTER_MASK) + + +/* --- variables --- */ +static GHashTable *binding_entry_hash_table = NULL; +static GSList *binding_set_list = NULL; +static const gchar *key_class_binding_set = "gtk-class-binding-set"; +static guint key_id_class_binding_set = 0; + + +/* --- functions --- */ +static GtkBindingSignal* +binding_signal_new (const gchar *signal_name, + guint n_args) +{ + GtkBindingSignal *signal; + + signal = g_new (GtkBindingSignal, 1); + signal->next = NULL; + signal->signal_name = g_strdup (signal_name); + signal->n_args = n_args; + signal->args = g_new0 (GtkBindingArg, n_args); + + return signal; +} + +static void +binding_signal_free (GtkBindingSignal *sig) +{ + guint i; + + for (i = 0; i < sig->n_args; i++) + { + if (sig->args[i].arg_type == GTK_BINDING_ARG_STRING) + g_free (sig->args[i].d.string_data); + } + g_free (sig->args); + g_free (sig->signal_name); + g_free (sig); +} + +static guint +binding_entry_hash (gpointer key) +{ + register GtkBindingEntry *e = key; + register guint h; + + h = e->keyval; + h ^= e->modifiers; + + return h; +} + +static gint +binding_entries_compare (gpointer a, + gpointer b) +{ + register GtkBindingEntry *ea = a; + register GtkBindingEntry *eb = b; + + return (ea->keyval == eb->keyval && ea->modifiers == eb->modifiers); +} + +static GtkBindingEntry* +binding_entry_new (GtkBindingSet *binding_set, + guint keyval, + guint modifiers) +{ + GtkBindingEntry *entry; + + if (!binding_entry_hash_table) + binding_entry_hash_table = g_hash_table_new (binding_entry_hash, binding_entries_compare); + + entry = g_new (GtkBindingEntry, 1); + entry->keyval = keyval; + entry->modifiers = modifiers; + entry->binding_set = binding_set, + entry->destroyed = FALSE; + entry->in_emission = FALSE; + entry->signals = NULL; + + entry->set_next = binding_set->entries; + binding_set->entries = entry; + + entry->hash_next = g_hash_table_lookup (binding_entry_hash_table, entry); + g_hash_table_freeze (binding_entry_hash_table); + if (entry->hash_next) + g_hash_table_remove (binding_entry_hash_table, entry->hash_next); + g_hash_table_insert (binding_entry_hash_table, entry, entry); + g_hash_table_thaw (binding_entry_hash_table); + + return entry; +} + +static void +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->destroyed = FALSE; + + sig = entry->signals; + while (sig) + { + GtkBindingSignal *prev; + + prev = sig; + sig = prev->next; + binding_signal_free (prev); + } + g_free (entry); +} + +static void +binding_entry_destroy (GtkBindingEntry *entry) +{ + GtkBindingEntry *o_entry; + register GtkBindingEntry *tmp; + GtkBindingEntry *begin; + register GtkBindingEntry *last; + + /* unlink from binding set + */ + last = NULL; + tmp = entry->binding_set->entries; + while (tmp) + { + if (tmp == entry) + { + 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; + tmp = begin; + while (tmp) + { + if (tmp == entry) + { + if (last) + last->hash_next = entry->hash_next; + else + begin = entry->hash_next; + break; + } + last = tmp; + tmp = last->hash_next; + } + entry->hash_next = NULL; + + if (!begin) + g_hash_table_remove (binding_entry_hash_table, entry); + else if (begin != o_entry) + { + g_hash_table_freeze (binding_entry_hash_table); + g_hash_table_remove (binding_entry_hash_table, entry); + g_hash_table_insert (binding_entry_hash_table, begin, begin); + g_hash_table_thaw (binding_entry_hash_table); + } + + entry->destroyed = TRUE; + + if (!entry->in_emission) + binding_entry_free (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) +{ + 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) + return entry; + + return NULL; +} + +static gboolean +binding_compose_params (GtkBindingArg *args, + GtkSignalQuery *query, + GtkArg **params_p) +{ + GtkArg *params; + const GtkType *types; + guint i; + gboolean valid; + + params = g_new0 (GtkArg, query->nparams); + *params_p = params; + + types = query->params; + valid = TRUE; + for (i = 0; i < query->nparams && valid; i++) + { + params->type = *types; + params->name = NULL; + switch (args->arg_type) + { + case GTK_BINDING_ARG_STRING: + if (params->type == GTK_TYPE_STRING) + GTK_VALUE_STRING (*params) = args->d.string_data; + else + valid = FALSE; + break; + case GTK_BINDING_ARG_DOUBLE: + if (params->type == GTK_TYPE_FLOAT) + GTK_VALUE_FLOAT (*params) = args->d.double_data; + else if (params->type == GTK_TYPE_DOUBLE) + GTK_VALUE_DOUBLE (*params) = args->d.double_data; + else + valid = FALSE; + break; + case GTK_BINDING_ARG_LONG: + if (params->type == GTK_TYPE_BOOL && + (args->d.long_data == 0 || + args->d.long_data == 1)) + GTK_VALUE_BOOL (*params) = args->d.long_data; + else if (params->type == GTK_TYPE_INT) + GTK_VALUE_INT (*params) = args->d.long_data; + else if (params->type == GTK_TYPE_UINT && + args->d.long_data >= 0) + GTK_VALUE_UINT (*params) = args->d.long_data; + else if (params->type == GTK_TYPE_LONG) + GTK_VALUE_LONG (*params) = args->d.long_data; + else if (params->type == GTK_TYPE_ULONG && + args->d.long_data >= 0) + GTK_VALUE_ULONG (*params) = args->d.long_data; + else if (params->type == GTK_TYPE_FLOAT) + GTK_VALUE_FLOAT (*params) = args->d.long_data; + else if (params->type == GTK_TYPE_DOUBLE) + GTK_VALUE_DOUBLE (*params) = args->d.long_data; + else + valid = FALSE; + break; + default: + valid = FALSE; + break; + } + types++; + params++; + args++; + } + + if (!valid) + { + g_free (*params_p); + *params_p = NULL; + } + + return valid; +} + +static void +gtk_binding_entry_activate (GtkBindingEntry *entry, + GtkObject *object) +{ + GtkBindingSignal *sig; + gboolean old_emission; + + old_emission = entry->in_emission; + entry->in_emission = TRUE; + + gtk_object_ref (object); + + for (sig = entry->signals; sig; sig = sig->next) + { + GtkSignalQuery *query; + guint signal_id; + GtkArg *params = NULL; + + signal_id = gtk_signal_lookup (sig->signal_name, GTK_OBJECT_TYPE (object)); + if (!signal_id) + { + gchar *accelerator; + + accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers); + g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": " + "could not find signal \"%s\" in the `%s' class ancestry", + entry->binding_set->set_name, + accelerator, + sig->signal_name, + gtk_type_name (GTK_OBJECT_TYPE (object))); + g_free (accelerator); + continue; + } + + query = gtk_signal_query (signal_id); + if (query->nparams != sig->n_args || + query->return_val != GTK_TYPE_NONE || + !binding_compose_params (sig->args, query, ¶ms)) + { + gchar *accelerator; + + accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers); + g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": " + "signature mismatch for signal \"%s\" in the `%s' class ancestry", + entry->binding_set->set_name, + accelerator, + sig->signal_name, + gtk_type_name (GTK_OBJECT_TYPE (object))); + g_free (accelerator); + g_free (query); + continue; + } + g_free (query); + + gtk_signal_emitv (object, signal_id, params); + g_free (params); + + if (GTK_OBJECT_DESTROYED (object) || entry->destroyed) + break; + } + + gtk_object_unref (object); + + entry->in_emission = old_emission; + if (entry->destroyed && !entry->in_emission) + binding_entry_free (entry); +} + +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 = g_strdup (set_name); + binding_set->widget_path_pspecs = NULL; + binding_set->widget_class_pspecs = NULL; + binding_set->class_branch_pspecs = NULL; + binding_set->entries = NULL; + binding_set->current = NULL; + + binding_set_list = g_slist_prepend (NULL, binding_set); + + return binding_set; +} + +GtkBindingSet* +gtk_binding_set_by_class (gpointer object_class) +{ + GtkObjectClass *class = object_class; + GtkBindingSet* binding_set; + + g_return_val_if_fail (GTK_IS_OBJECT_CLASS (class), NULL); + + if (!key_id_class_binding_set) + key_id_class_binding_set = g_dataset_force_id (key_class_binding_set); + + binding_set = g_dataset_id_get_data (class, key_id_class_binding_set); + + if (binding_set) + return binding_set; + + binding_set = gtk_binding_set_new (gtk_type_name (class->type)); + gtk_binding_set_add_path (binding_set, + GTK_PATH_CLASS, + gtk_type_name (class->type), + GTK_PATH_PRIO_GTK); + g_dataset_id_set_data (class, key_id_class_binding_set, binding_set); + + return binding_set; +} + +GtkBindingSet* +gtk_binding_set_find (const gchar *set_name) +{ + 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; +} + +gboolean +gtk_binding_set_activate (GtkBindingSet *binding_set, + guint keyval, + guint 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; + } + } + + return FALSE; +} + +void +gtk_binding_entry_clear (GtkBindingSet *binding_set, + guint keyval, + guint modifiers) +{ + GtkBindingEntry *entry; + + g_return_if_fail (binding_set != NULL); + + keyval = gdk_keyval_to_lower (keyval); + modifiers = modifiers & BINDING_MOD_MASK (); + + entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); + if (entry) + binding_entry_destroy (entry); + + entry = binding_entry_new (binding_set, keyval, modifiers); +} + +void +gtk_binding_entry_remove (GtkBindingSet *binding_set, + guint keyval, + guint 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); +} + +void +gtk_binding_entry_add_signall (GtkBindingSet *binding_set, + guint keyval, + guint modifiers, + const gchar *signal_name, + GSList *binding_args) +{ + GtkBindingEntry *entry; + GtkBindingSignal *signal, **signal_p; + GSList *slist; + guint n = 0; + GtkBindingArg *arg; + + g_return_if_fail (binding_set != NULL); + g_return_if_fail (signal_name != NULL); + + keyval = gdk_keyval_to_lower (keyval); + modifiers = modifiers & BINDING_MOD_MASK (); + + signal = binding_signal_new (signal_name, g_slist_length (binding_args)); + + arg = signal->args; + for (slist = binding_args; slist; slist = slist->next) + { + GtkBindingArg *tmp_arg; + + tmp_arg = slist->data; + if (!tmp_arg) + { + g_warning ("gtk_binding_entry_add_signall(): arg[%u] is `NULL'", n); + binding_signal_free (signal); + return; + } + arg->arg_type = tmp_arg->arg_type; + switch (tmp_arg->arg_type) + { + case GTK_BINDING_ARG_INT: + case GTK_BINDING_ARG_LONG: + arg->d.long_data = tmp_arg->d.long_data; + break; + case GTK_BINDING_ARG_FLOAT: + case GTK_BINDING_ARG_DOUBLE: + arg->d.double_data = tmp_arg->d.double_data; + break; + case GTK_BINDING_ARG_STRING: + if (!tmp_arg->d.string_data) + { + g_warning ("gtk_binding_entry_add_signall(): value of `string' arg[%u] is `NULL'", n); + arg->d.string_data = NULL; + binding_signal_free (signal); + return; + } + arg->d.string_data = g_strdup (tmp_arg->d.string_data); + break; + default: + g_warning ("gtk_binding_entry_add_signall(): unsupported type `%s' for arg[%u]", + gtk_type_name (arg->arg_type), n); + binding_signal_free (signal); + return; + } + arg++; + n++; + } + + entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); + if (!entry) + { + gtk_binding_entry_add (binding_set, keyval, modifiers); + entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); + } + signal_p = &entry->signals; + while (*signal_p) + signal_p = &(*signal_p)->next; + *signal_p = signal; +} + +void +gtk_binding_entry_add_signal (GtkBindingSet *binding_set, + guint keyval, + guint modifiers, + const gchar *signal_name, + guint n_args, + ...) +{ + GSList *slist, *free_slist; + va_list args; + guint i; + + 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++) + { + GtkBindingArg *arg; + + arg = g_new0 (GtkBindingArg, 1); + slist = g_slist_prepend (slist, arg); + + arg->arg_type = va_arg (args, GtkType); + switch (arg->arg_type) + { + case GTK_BINDING_ARG_INT: + arg->d.long_data = va_arg (args, gint); + break; + case GTK_BINDING_ARG_LONG: + arg->d.long_data = va_arg (args, glong); + break; + case GTK_BINDING_ARG_FLOAT: + arg->d.double_data = va_arg (args, gfloat); + break; + case GTK_BINDING_ARG_DOUBLE: + arg->d.double_data = va_arg (args, gdouble); + break; + case GTK_BINDING_ARG_STRING: + arg->d.string_data = va_arg (args, gchar*); + if (!arg->d.string_data) + { + g_warning ("gtk_binding_entry_add_signal(): value of `string' arg[%u] is `NULL'", i); + i = n_args + 2; + } + break; + default: + g_warning ("gtk_binding_entry_add_signal(): unsupported type `%s' for arg[%u]", + gtk_type_name (arg->arg_type), i); + i = n_args + 2; + break; + } + } + va_end (args); + + if (i == n_args + 1 || i == 0) + { + slist = g_slist_reverse (slist); + gtk_binding_entry_add_signall (binding_set, keyval, modifiers, signal_name, slist); + } + + free_slist = slist; + while (slist) + { + g_free (slist->data); + slist = slist->next; + } + g_slist_free (free_slist); +} + +void +gtk_binding_set_add_path (GtkBindingSet *binding_set, + GtkPathType path_type, + const gchar *path_pattern, + GtkPathPriorityType priority) +{ + GtkPatternSpec *pspec; + GSList **slist_p, *slist; + static guint seq_id = 0; + + g_return_if_fail (binding_set != NULL); + g_return_if_fail (path_pattern != NULL); + + 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 (GtkPatternSpec, 1); + gtk_pattern_spec_init (pspec, path_pattern); + pspec->seq_id = seq_id++ & 0x0fffffff; + pspec->seq_id |= priority << 28; + pspec->user_data = binding_set; + + slist = *slist_p; + while (slist) + { + GtkPatternSpec *tmp_pspec; + + tmp_pspec = slist->data; + slist = slist->next; + + if (tmp_pspec->pattern_length == pspec->pattern_length && + g_str_equal (tmp_pspec->pattern_reversed, pspec->pattern_reversed)) + { + gtk_pattern_spec_free_segs (pspec); + g_free (pspec); + pspec = NULL; + } + } + if (pspec) + *slist_p = g_slist_prepend (*slist_p, pspec); +} + +static inline gboolean +binding_match_activate (GSList *pspec_list, + GtkObject *object, + guint path_length, + gchar *path, + gchar *path_reversed) +{ + GSList *slist; + + for (slist = pspec_list; slist; slist = slist->next) + { + GtkPatternSpec *pspec; + + pspec = slist->data; + if (gtk_pattern_match (pspec, path_length, path, path_reversed)) + { + GtkBindingSet *binding_set; + + binding_set = pspec->user_data; + + gtk_binding_entry_activate (binding_set->current, object); + + return TRUE; + } + } + + return FALSE; +} + +static gint +gtk_binding_pattern_compare (gpointer a, + gpointer b) +{ + register GtkPatternSpec *pa = a; + register GtkPatternSpec *pb = b; + + return pa->seq_id < pb->seq_id ? -1 : 1; +} + +static inline GSList* +gtk_binding_entries_sort_patterns (GtkBindingEntry *entries, + GtkPathType path_id) +{ + GSList *patterns; + + patterns = NULL; + while (entries) + { + register GtkBindingSet *binding_set; + GSList *slist = NULL; + + binding_set = entries->binding_set; + binding_set->current = entries; + + switch (path_id) + { + case GTK_PATH_WIDGET: + slist = binding_set->widget_path_pspecs; + break; + case GTK_PATH_WIDGET_CLASS: + slist = binding_set->widget_class_pspecs; + break; + case GTK_PATH_CLASS: + slist = binding_set->class_branch_pspecs; + break; + } + + for (; slist; slist = slist->next) + { + GtkPatternSpec *pspec; + + pspec = slist->data; + patterns = g_slist_insert_sorted (patterns, pspec, gtk_binding_pattern_compare); + } + + entries = entries->hash_next; + } + + return patterns; +} + + +gboolean +gtk_bindings_activate (GtkObject *object, + guint keyval, + guint modifiers) +{ + GtkBindingEntry *entries; + GtkWidget *widget; + 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; + + if (!handled) + { + guint path_length; + gchar *path, *path_reversed; + GSList *patterns; + + gtk_widget_path (widget, &path_length, &path, &path_reversed); + patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_WIDGET); + handled = binding_match_activate (patterns, object, path_length, path, path_reversed); + g_slist_free (patterns); + g_free (path); + g_free (path_reversed); + } + + if (!handled) + { + guint path_length; + gchar *path, *path_reversed; + GSList *patterns; + + gtk_widget_class_path (widget, &path_length, &path, &path_reversed); + patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_WIDGET_CLASS); + handled = binding_match_activate (patterns, object, path_length, path, path_reversed); + g_slist_free (patterns); + g_free (path); + g_free (path_reversed); + } + + if (!handled) + { + GSList *patterns; + GtkType class_type; + + patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_CLASS); + class_type = GTK_OBJECT_TYPE (object); + while (class_type && !handled) + { + guint path_length; + gchar *path, *path_reversed; + + path = gtk_type_name (class_type); + path_reversed = g_strdup (path); + g_strreverse (path_reversed); + path_length = strlen (path); + handled = binding_match_activate (patterns, object, path_length, path, path_reversed); + g_free (path_reversed); + + class_type = gtk_type_parent (class_type); + } + g_slist_free (patterns); + } + + return handled; +} + + + + + + + + + + +/* Patterns + */ + +static inline gboolean +gtk_pattern_ph_match (const gchar *match_pattern, + const gchar *match_string) +{ + register const gchar *pattern, *string; + register gchar ch; + + pattern = match_pattern; + string = match_string; + + ch = *pattern; + pattern++; + while (ch) + { + switch (ch) + { + case '?': + if (!*string) + return FALSE; + string++; + break; + + case '*': + do + { + ch = *pattern; + pattern++; + if (ch == '?') + { + if (!*string) + return FALSE; + string++; + } + } + while (ch == '*' || ch == '?'); + if (!ch) + return TRUE; + do + { + while (ch != *string) + { + if (!*string) + return FALSE; + string++; + } + string++; + if (gtk_pattern_ph_match (pattern, string)) + return TRUE; + } + while (*string); + break; + + default: + if (ch == *string) + string++; + else + return FALSE; + break; + } + + ch = *pattern; + pattern++; + } + + return *string == 0; +} + +gboolean +gtk_pattern_match (GtkPatternSpec *pspec, + guint string_length, + const gchar *string, + const gchar *string_reversed) +{ + g_return_val_if_fail (pspec != NULL, FALSE); + g_return_val_if_fail (string != NULL, FALSE); + g_return_val_if_fail (string_reversed != NULL, FALSE); + + switch (pspec->match_type) + { + case GTK_MATCH_ALL: + return gtk_pattern_ph_match (pspec->pattern, string); + + case GTK_MATCH_ALL_TAIL: + return gtk_pattern_ph_match (pspec->pattern_reversed, string_reversed); + + case GTK_MATCH_HEAD: + if (pspec->pattern_length > string_length) + return FALSE; + else if (pspec->pattern_length == string_length) + return strcmp (pspec->pattern, string) == 0; + else if (pspec->pattern_length) + return strncmp (pspec->pattern, string, pspec->pattern_length) == 0; + else + return TRUE; + + case GTK_MATCH_TAIL: + if (pspec->pattern_length > string_length) + return FALSE; + else if (pspec->pattern_length == string_length) + return strcmp (pspec->pattern_reversed, string_reversed) == 0; + else if (pspec->pattern_length) + return strncmp (pspec->pattern_reversed, + string_reversed, + pspec->pattern_length) == 0; + else + return TRUE; + + case GTK_MATCH_EXACT: + if (pspec->pattern_length != string_length) + return FALSE; + else + return strcmp (pspec->pattern_reversed, string_reversed) == 0; + + default: + g_return_val_if_fail (pspec->match_type < GTK_MATCH_LAST, FALSE); + return FALSE; + } +} + +void +gtk_pattern_spec_init (GtkPatternSpec *pspec, + const gchar *pattern) +{ + gchar *p; + + g_return_if_fail (pspec != NULL); + + pspec->match_type = GTK_MATCH_ALL; + pspec->seq_id = 0; + pspec->user_data = NULL; + + if (!pattern) + pattern = ""; + + pspec->pattern = g_strdup (pattern); + pspec->pattern_length = strlen (pspec->pattern); + pspec->pattern_reversed = g_strdup (pspec->pattern); + g_strreverse (pspec->pattern_reversed); + if (pspec->pattern_reversed[0] != '*') + pspec->match_type = GTK_MATCH_ALL_TAIL; + + if (strchr (pspec->pattern, '?')) + return; + + if (!strchr (pspec->pattern, '*')) + { + pspec->match_type = GTK_MATCH_EXACT; + return; + } + + p = pspec->pattern; + while (*p == '*') + p++; + if (p > pspec->pattern && + !strchr (p, '*')) + { + gchar *t; + + pspec->match_type = GTK_MATCH_TAIL; + t = pspec->pattern; + pspec->pattern = g_strdup (p); + g_free (t); + g_free (pspec->pattern_reversed); + pspec->pattern_reversed = g_strdup (pspec->pattern); + g_strreverse (pspec->pattern_reversed); + pspec->pattern_length = strlen (pspec->pattern); + return; + } + + p = pspec->pattern_reversed; + while (*p == '*') + p++; + if (p > pspec->pattern_reversed && + !strchr (p, '*')) + { + gchar *t; + + pspec->match_type = GTK_MATCH_HEAD; + t = pspec->pattern_reversed; + pspec->pattern_reversed = g_strdup (p); + g_free (t); + pspec->pattern = g_strdup (pspec->pattern_reversed); + g_strreverse (pspec->pattern); + pspec->pattern_length = strlen (pspec->pattern); + } +} + +gboolean +gtk_pattern_match_string (GtkPatternSpec *pspec, + const gchar *string) +{ + gchar *string_reversed; + guint length; + gboolean ergo; + + g_return_val_if_fail (pspec != NULL, FALSE); + g_return_val_if_fail (string != NULL, FALSE); + + length = strlen (string); + string_reversed = g_strdup (string); + g_strreverse (string_reversed); + + ergo = gtk_pattern_match (pspec, length, string, string_reversed); + g_free (string_reversed); + + return ergo; +} + +gboolean +gtk_pattern_match_simple (const gchar *pattern, + const gchar *string) +{ + GtkPatternSpec pspec; + gboolean ergo; + + g_return_val_if_fail (pattern != NULL, FALSE); + g_return_val_if_fail (string != NULL, FALSE); + + gtk_pattern_spec_init (&pspec, pattern); + ergo = gtk_pattern_match_string (&pspec, string); + gtk_pattern_spec_free_segs (&pspec); + + return ergo; +} + +void +gtk_pattern_spec_free_segs (GtkPatternSpec *pspec) +{ + g_return_if_fail (pspec != NULL); + + g_free (pspec->pattern); + pspec->pattern = NULL; + g_free (pspec->pattern_reversed); + pspec->pattern_reversed = NULL; +} |