* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
-#include <ctype.h>
#include <string.h>
#include <stdarg.h>
+#include <gdkkeysyms.h>
#include "gtkbindings.h"
-#include "gtksignal.h"
+#include "gtkkeyhash.h"
#include "gtkwidget.h"
#include "gtkrc.h"
/* --- 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;
for (i = 0; i < sig->n_args; i++)
{
- if (GTK_FUNDAMENTAL_TYPE (sig->args[i].arg_type) == GTK_TYPE_STRING)
+ if (G_TYPE_FUNDAMENTAL (sig->args[i].arg_type) == G_TYPE_STRING)
g_free (sig->args[i].d.string_data);
}
g_free (sig->args);
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 <Shift>
+ * 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)
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);
+
+ 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;
}
register GtkBindingEntry *tmp;
GtkBindingEntry *begin;
register GtkBindingEntry *last;
+ GSList *tmp_list;
/* unlink from binding set
*/
g_hash_table_insert (binding_entry_hash_table, begin, begin);
}
+ 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;
if (!entry->in_emission)
}
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;
g_value_init (&tmp_value, G_TYPE_DOUBLE);
g_value_set_double (&tmp_value, args->d.double_data);
break;
- case G_TYPE_LONG:
+ case G_TYPE_LONG:
g_value_init (&tmp_value, G_TYPE_LONG);
g_value_set_long (&tmp_value, args->d.long_data);
break;
- case G_TYPE_STRING:
+ 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
*/
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;
}
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;
}
else
{
g_value_init (&tmp_value, G_TYPE_STRING);
- g_value_set_static_string (params, args->d.string_data);
+ g_value_set_static_string (&tmp_value, args->d.string_data);
}
break;
default:
break;
}
- if (valid && !g_value_transform (&tmp_value, params))
- valid = FALSE;
+ if (valid)
+ {
+ if (!g_value_transform (&tmp_value, params))
+ valid = FALSE;
- g_value_unset (&tmp_value);
+ g_value_unset (&tmp_value);
+ }
types++;
params++;
sig->signal_name,
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\": "
binding_set->class_branch_pspecs = NULL;
binding_set->entries = NULL;
binding_set->current = NULL;
+ binding_set->parsed = FALSE;
binding_set_list = g_slist_prepend (binding_set_list, binding_set);
if (binding_set)
return binding_set;
- binding_set = gtk_binding_set_new (gtk_type_name (GTK_CLASS_TYPE (class)));
+ 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 (GTK_CLASS_TYPE (class)),
+ g_type_name (G_OBJECT_CLASS_TYPE (class)),
GTK_PATH_PRIO_GTK);
g_dataset_id_set_data (class, key_id_class_binding_set, binding_set);
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);
void
gtk_binding_entry_clear (GtkBindingSet *binding_set,
guint keyval,
- guint modifiers)
+ GdkModifierType modifiers)
{
GtkBindingEntry *entry;
void
gtk_binding_entry_remove (GtkBindingSet *binding_set,
guint keyval,
- guint modifiers)
+ GdkModifierType modifiers)
{
GtkBindingEntry *entry;
void
gtk_binding_entry_add_signall (GtkBindingSet *binding_set,
guint keyval,
- guint modifiers,
+ GdkModifierType modifiers,
const gchar *signal_name,
GSList *binding_args)
{
binding_signal_free (signal);
return;
}
- switch (GTK_FUNDAMENTAL_TYPE (tmp_arg->arg_type))
+ switch (G_TYPE_FUNDAMENTAL (tmp_arg->arg_type))
{
- case GTK_TYPE_LONG:
- arg->arg_type = GTK_TYPE_LONG;
+ case G_TYPE_LONG:
+ arg->arg_type = G_TYPE_LONG;
arg->d.long_data = tmp_arg->d.long_data;
break;
- case GTK_TYPE_DOUBLE:
- arg->arg_type = GTK_TYPE_DOUBLE;
+ case G_TYPE_DOUBLE:
+ arg->arg_type = G_TYPE_DOUBLE;
arg->d.double_data = tmp_arg->d.double_data;
break;
- case GTK_TYPE_STRING:
+ case G_TYPE_STRING:
if (tmp_arg->arg_type != GTK_TYPE_IDENTIFIER)
- arg->arg_type = GTK_TYPE_STRING;
+ arg->arg_type = G_TYPE_STRING;
else
arg->arg_type = GTK_TYPE_IDENTIFIER;
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;
}
void
gtk_binding_entry_add_signal (GtkBindingSet *binding_set,
guint keyval,
- guint modifiers,
+ GdkModifierType modifiers,
const gchar *signal_name,
guint n_args,
...)
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++)
slist = g_slist_prepend (slist, arg);
arg->arg_type = va_arg (args, GtkType);
- switch (GTK_FUNDAMENTAL_TYPE (arg->arg_type))
+ switch (G_TYPE_FUNDAMENTAL (arg->arg_type))
{
/* for elaborated commenting about var args collection, take a look
* at gtk_arg_collect_value() in gtkargcollector.c
*/
- case GTK_TYPE_CHAR:
- case GTK_TYPE_UCHAR:
- case GTK_TYPE_INT:
- case GTK_TYPE_UINT:
- case GTK_TYPE_BOOL:
- case GTK_TYPE_ENUM:
- case GTK_TYPE_FLAGS:
- arg->arg_type = GTK_TYPE_LONG;
+ 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 GTK_TYPE_LONG:
- case GTK_TYPE_ULONG:
- arg->arg_type = GTK_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 GTK_TYPE_FLOAT:
- case GTK_TYPE_DOUBLE:
- arg->arg_type = GTK_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 GTK_TYPE_STRING:
+ case G_TYPE_STRING:
if (arg->arg_type != GTK_TYPE_IDENTIFIER)
- arg->arg_type = GTK_TYPE_STRING;
+ arg->arg_type = G_TYPE_STRING;
arg->d.string_data = va_arg (args, gchar*);
if (!arg->d.string_data)
{
g_warning ("gtk_binding_entry_add_signal(): type `%s' arg[%u] is `NULL'",
- gtk_type_name (arg->arg_type),
+ 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;
}
}
}
-static inline gboolean
+static gboolean
binding_match_activate (GSList *pspec_list,
GtkObject *object,
guint path_length,
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)
{
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))
- 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;
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);
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);
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;
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);
}
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;
+
+ g_return_val_if_fail (GTK_IS_OBJECT (object), FALSE);
+
+ 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);
+
+ 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,
- guint modifiers)
+ GdkModifierType modifiers)
{
gchar *signal;
guint expected_token = 0;
{
need_arg = FALSE;
arg = g_new (GtkBindingArg, 1);
- arg->arg_type = GTK_TYPE_DOUBLE;
+ arg->arg_type = G_TYPE_DOUBLE;
arg->d.double_data = scanner->value.v_float;
if (negate)
{
{
need_arg = FALSE;
arg = g_new (GtkBindingArg, 1);
- arg->arg_type = GTK_TYPE_LONG;
+ arg->arg_type = G_TYPE_LONG;
arg->d.long_data = scanner->value.v_int;
if (negate)
{
{
need_arg = FALSE;
arg = g_new (GtkBindingArg, 1);
- arg->arg_type = GTK_TYPE_STRING;
+ arg->arg_type = G_TYPE_STRING;
arg->d.string_data = g_strdup (scanner->value.v_string);
args = g_slist_prepend (args, arg);
}
GtkBindingArg *arg;
arg = slist->data;
- if (GTK_FUNDAMENTAL_TYPE (arg->arg_type) == GTK_TYPE_STRING)
+ if (G_TYPE_FUNDAMENTAL (arg->arg_type) == G_TYPE_STRING)
g_free (arg->d.string_data);
g_free (arg);
}
GtkBindingSet *binding_set)
{
guint keyval = 0;
- guint modifiers = 0;
+ GdkModifierType modifiers = 0;
g_return_val_if_fail (scanner != NULL, G_TOKEN_ERROR);
binding_set = gtk_binding_set_find (name);
if (!binding_set)
- binding_set = gtk_binding_set_new (name);
+ {
+ binding_set = gtk_binding_set_new (name);
+ binding_set->parsed = 1;
+ }
g_free (name);
g_scanner_peek_next_token (scanner);
return G_TOKEN_NONE;
}
+
+static void
+free_pattern_specs (GSList *pattern_specs)
+{
+ GSList *slist;
+
+ for (slist = pattern_specs; slist; slist = slist->next)
+ {
+ PatternSpec *pspec;
+
+ pspec = slist->data;
+
+ g_pattern_spec_free (pspec->pspec);
+ g_free (pspec);
+ }
+
+ g_slist_free (pattern_specs);
+}
+
+static void
+binding_set_delete (GtkBindingSet *binding_set)
+{
+ GtkBindingEntry *entry, *next;
+
+ entry = binding_set->entries;
+ while (entry)
+ {
+ next = entry->set_next;
+ binding_entry_destroy (entry);
+ entry = next;
+ }
+
+ free_pattern_specs (binding_set->widget_path_pspecs);
+ free_pattern_specs (binding_set->widget_class_pspecs);
+ free_pattern_specs (binding_set->class_branch_pspecs);
+
+ g_free (binding_set->set_name);
+ g_free (binding_set);
+}
+
+/**
+ * _gtk_binding_reset_parsed:
+ *
+ * Removing all binding sets that were added by
+ * gtk_binding_parse_binding()
+ **/
+void
+_gtk_binding_reset_parsed (void)
+{
+ GSList *slist, *next;
+
+ slist = binding_set_list;
+ while (slist)
+ {
+ GtkBindingSet *binding_set;
+
+ binding_set = slist->data;
+ next = slist->next;
+
+ if (binding_set->parsed)
+ {
+ binding_set_list = g_slist_delete_link (binding_set_list, slist);
+ binding_set_delete (binding_set);
+ }
+
+ slist = next;
+ }
+}