summaryrefslogtreecommitdiff
path: root/gtk/gtkbindings.c
diff options
context:
space:
mode:
authorTim Janik <timj@gtk.org>1998-06-07 06:48:56 +0000
committerTim Janik <timj@src.gnome.org>1998-06-07 06:48:56 +0000
commita391196ba51da34253593712e8bf4552c04f19e5 (patch)
tree50c2edfc5d066467f3c33c1f82c89dd9923c44c4 /gtk/gtkbindings.c
parentf522f36dd1c484b133ba6b04027667aa9144ad04 (diff)
downloadgtk+-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.c1136
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, &params))
+ {
+ 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;
+}