]> Pileus Git - ~andy/gtk/blob - gtk/gtkbindings.c
Fix some signed/unsigned warnings. (#85863, David L. Cooper II)
[~andy/gtk] / gtk / gtkbindings.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * GtkBindingSet: Keybinding manager for GtkObjects.
5  * Copyright (C) 1998 Tim Janik
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /*
24  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
25  * file for a list of people on the GTK+ Team.  See the ChangeLog
26  * files for a list of changes.  These files are distributed with
27  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
28  */
29
30 #include <string.h>
31 #include <stdarg.h>
32 #include <gdkkeysyms.h>
33 #include "gtkbindings.h"
34 #include "gtkkeyhash.h"
35 #include "gtksignal.h"
36 #include "gtkwidget.h"
37 #include "gtkrc.h"
38
39
40 /* --- defines --- */
41 #define BINDING_MOD_MASK()      (gtk_accelerator_get_default_mod_mask () | GDK_RELEASE_MASK)
42
43
44 /* --- structures --- */
45 typedef struct {
46   GPatternSpec *pspec;
47   gpointer user_data;
48   guint seq_id;
49 } PatternSpec;
50
51
52 /* --- variables --- */
53 static GHashTable       *binding_entry_hash_table = NULL;
54 static GSList           *binding_key_hashes = NULL;
55 static GSList           *binding_set_list = NULL;
56 static const gchar      *key_class_binding_set = "gtk-class-binding-set";
57 static GQuark            key_id_class_binding_set = 0;
58
59
60 /* --- functions --- */
61 static GtkBindingSignal*
62 binding_signal_new (const gchar *signal_name,
63                     guint        n_args)
64 {
65   GtkBindingSignal *signal;
66   
67   signal = g_new (GtkBindingSignal, 1);
68   signal->next = NULL;
69   signal->signal_name = g_strdup (signal_name);
70   signal->n_args = n_args;
71   signal->args = g_new0 (GtkBindingArg, n_args);
72   
73   return signal;
74 }
75
76 static void
77 binding_signal_free (GtkBindingSignal *sig)
78 {
79   guint i;
80   
81   for (i = 0; i < sig->n_args; i++)
82     {
83       if (GTK_FUNDAMENTAL_TYPE (sig->args[i].arg_type) == GTK_TYPE_STRING)
84         g_free (sig->args[i].d.string_data);
85     }
86   g_free (sig->args);
87   g_free (sig->signal_name);
88   g_free (sig);
89 }
90
91 static guint
92 binding_entry_hash (gconstpointer  key)
93 {
94   register const GtkBindingEntry *e = key;
95   register guint h;
96
97   h = e->keyval;
98   h ^= e->modifiers;
99
100   return h;
101 }
102
103 static gint
104 binding_entries_compare (gconstpointer  a,
105                          gconstpointer  b)
106 {
107   register const GtkBindingEntry *ea = a;
108   register const GtkBindingEntry *eb = b;
109
110   return (ea->keyval == eb->keyval && ea->modifiers == eb->modifiers);
111 }
112
113 static void
114 binding_key_hash_insert_entry (GtkKeyHash      *key_hash,
115                                GtkBindingEntry *entry)
116 {
117   guint keyval = entry->keyval;
118   
119   /* We store lowercased accelerators. To deal with this, if <Shift>
120    * was specified, uppercase.
121    */
122   if (entry->modifiers & GDK_SHIFT_MASK)
123     {
124       if (keyval == GDK_Tab)
125         keyval = GDK_ISO_Left_Tab;
126       else
127         keyval = gdk_keyval_to_upper (keyval);
128     }
129   
130   _gtk_key_hash_add_entry (key_hash, keyval, entry->modifiers & ~GDK_RELEASE_MASK, entry);
131 }
132
133 static void
134 binding_key_hash_destroy (gpointer data)
135 {
136   GtkKeyHash *key_hash = data;
137   
138   binding_key_hashes = g_slist_remove (binding_key_hashes, key_hash);
139   _gtk_key_hash_free (key_hash);
140 }
141
142 static void
143 insert_entries_into_key_hash (gpointer key,
144                               gpointer value,
145                               gpointer data)
146 {
147   GtkKeyHash *key_hash = data;
148   GtkBindingEntry *entry = value;
149
150   for (; entry; entry = entry->hash_next)
151     binding_key_hash_insert_entry (key_hash, entry);
152 }
153
154 static GtkKeyHash *
155 binding_key_hash_for_keymap (GdkKeymap *keymap)
156 {
157   static GQuark key_hash_quark = 0;
158   GtkKeyHash *key_hash;
159
160   if (!key_hash_quark)
161     key_hash_quark = g_quark_from_static_string ("gtk-binding-key-hash");
162   
163   key_hash = g_object_get_qdata (G_OBJECT (keymap), key_hash_quark);
164
165   if (!key_hash)
166     {
167       key_hash = _gtk_key_hash_new (keymap, NULL);
168       g_object_set_qdata_full (G_OBJECT (keymap), key_hash_quark, key_hash, binding_key_hash_destroy);
169
170       if (binding_entry_hash_table)
171         g_hash_table_foreach (binding_entry_hash_table,
172                               insert_entries_into_key_hash,
173                               key_hash);
174
175       binding_key_hashes = g_slist_prepend (binding_key_hashes, key_hash);
176     }
177
178   return key_hash;
179 }
180
181
182 static GtkBindingEntry*
183 binding_entry_new (GtkBindingSet  *binding_set,
184                    guint           keyval,
185                    GdkModifierType modifiers)
186 {
187   GSList *tmp_list;
188   GtkBindingEntry *entry;
189   
190   if (!binding_entry_hash_table)
191     binding_entry_hash_table = g_hash_table_new (binding_entry_hash, binding_entries_compare);
192
193   entry = g_new (GtkBindingEntry, 1);
194   entry->keyval = keyval;
195   entry->modifiers = modifiers;
196   entry->binding_set = binding_set,
197   entry->destroyed = FALSE;
198   entry->in_emission = FALSE;
199   entry->signals = NULL;
200
201   entry->set_next = binding_set->entries;
202   binding_set->entries = entry;
203
204   entry->hash_next = g_hash_table_lookup (binding_entry_hash_table, entry);
205   if (entry->hash_next)
206     g_hash_table_remove (binding_entry_hash_table, entry->hash_next);
207   g_hash_table_insert (binding_entry_hash_table, entry, entry);
208
209   for (tmp_list = binding_key_hashes; tmp_list; tmp_list = tmp_list->next)
210     {
211       GtkKeyHash *key_hash = tmp_list->data;
212       binding_key_hash_insert_entry (key_hash, entry);
213     }
214   
215   return entry;
216 }
217
218 static void
219 binding_entry_free (GtkBindingEntry *entry)
220 {
221   GtkBindingSignal *sig;
222
223   g_assert (entry->set_next == NULL &&
224             entry->hash_next == NULL &&
225             entry->in_emission == FALSE &&
226             entry->destroyed == TRUE);
227
228   entry->destroyed = FALSE;
229   
230   sig = entry->signals;
231   while (sig)
232     {
233       GtkBindingSignal *prev;
234       
235       prev = sig;
236       sig = prev->next;
237       binding_signal_free (prev);
238     }
239   g_free (entry);
240 }
241
242 static void
243 binding_entry_destroy (GtkBindingEntry *entry)
244 {
245   GtkBindingEntry *o_entry;
246   register GtkBindingEntry *tmp;
247   GtkBindingEntry *begin;
248   register GtkBindingEntry *last;
249   GSList *tmp_list;
250
251   /* unlink from binding set
252    */
253   last = NULL;
254   tmp = entry->binding_set->entries;
255   while (tmp)
256     {
257       if (tmp == entry)
258         {
259           if (last)
260             last->set_next = entry->set_next;
261           else
262             entry->binding_set->entries = entry->set_next;
263           break;
264         }
265       last = tmp;
266       tmp = last->set_next;
267     }
268   entry->set_next = NULL;
269   
270   o_entry = g_hash_table_lookup (binding_entry_hash_table, entry);
271   begin = o_entry;
272   last = NULL;
273   tmp = begin;
274   while (tmp)
275     {
276       if (tmp == entry)
277         {
278           if (last)
279             last->hash_next = entry->hash_next;
280           else
281             begin = entry->hash_next;
282           break;
283         }
284       last = tmp;
285       tmp = last->hash_next;
286     }
287   entry->hash_next = NULL;
288   
289   if (!begin)
290     g_hash_table_remove (binding_entry_hash_table, entry);
291   else if (begin != o_entry)
292     {
293       g_hash_table_remove (binding_entry_hash_table, entry);
294       g_hash_table_insert (binding_entry_hash_table, begin, begin);
295     }
296
297   for (tmp_list = binding_key_hashes; tmp_list; tmp_list = tmp_list->next)
298     {
299       GtkKeyHash *key_hash = tmp_list->data;
300       _gtk_key_hash_remove_entry (key_hash, entry);
301     }
302
303   entry->destroyed = TRUE;
304
305   if (!entry->in_emission)
306     binding_entry_free (entry);
307 }
308
309 static GtkBindingEntry*
310 binding_ht_lookup_entry (GtkBindingSet  *set,
311                          guint           keyval,
312                          GdkModifierType modifiers)
313 {
314   GtkBindingEntry lookup_entry = { 0 };
315   GtkBindingEntry *entry;
316   
317   if (!binding_entry_hash_table)
318     return NULL;
319   
320   lookup_entry.keyval = keyval;
321   lookup_entry.modifiers = modifiers;
322   
323   entry = g_hash_table_lookup (binding_entry_hash_table, &lookup_entry);
324   for (; entry; entry = entry->hash_next)
325     if (entry->binding_set == set)
326       return entry;
327
328   return NULL;
329 }
330
331 static gboolean
332 binding_compose_params (GtkObject       *object,
333                         GtkBindingArg   *args,
334                         GSignalQuery    *query,
335                         GValue         **params_p)
336 {
337   GValue *params;
338   const GType *types;
339   guint i;
340   gboolean valid;
341   
342   params = g_new0 (GValue, query->n_params + 1);
343   *params_p = params;
344
345   /* The instance we emit on is the first object in the array
346    */
347   g_value_init (params, G_TYPE_OBJECT);
348   g_value_set_object (params, G_OBJECT (object));
349   params++;
350   
351   types = query->param_types;
352   valid = TRUE;
353   for (i = 1; i < query->n_params + 1 && valid; i++)
354     {
355       GValue tmp_value = { 0, };
356
357       g_value_init (params, *types);
358
359       switch (G_TYPE_FUNDAMENTAL (args->arg_type))
360         {
361         case G_TYPE_DOUBLE:
362           g_value_init (&tmp_value, G_TYPE_DOUBLE);
363           g_value_set_double (&tmp_value, args->d.double_data);
364           break;
365         case  G_TYPE_LONG:
366           g_value_init (&tmp_value, G_TYPE_LONG);
367           g_value_set_long (&tmp_value, args->d.long_data);
368           break;
369         case  G_TYPE_STRING:
370           /* gtk_rc_parse_flags/enum() has fancier parsing for this; we can't call
371            * that since we don't have a GParamSpec, so just do something simple
372            */
373           if (G_TYPE_FUNDAMENTAL (*types) == G_TYPE_ENUM)
374             {
375               GEnumClass *class = G_ENUM_CLASS (g_type_class_ref (*types));
376               
377               valid = FALSE;
378               
379               if (args->arg_type == GTK_TYPE_IDENTIFIER)
380                 {
381                   GEnumValue *enum_value = NULL;
382                   enum_value = g_enum_get_value_by_name (class, args->d.string_data);
383                   if (!enum_value)
384                     enum_value = g_enum_get_value_by_nick (class, args->d.string_data);
385                   if (enum_value)
386                     {
387                       g_value_init (&tmp_value, *types);
388                       g_value_set_enum (&tmp_value, enum_value->value);
389                       valid = TRUE;
390                     }
391                 }
392
393               g_type_class_unref (class);
394             }
395           /* This is just a hack for compatibility with GTK+-1.2 where a string
396            * could be used for a single flag value / without the support for multiple
397            * values in gtk_rc_parse_flags(), this isn't very useful.
398            */
399           else if (G_TYPE_FUNDAMENTAL (*types) == G_TYPE_FLAGS)
400             {
401               GFlagsClass *class = G_FLAGS_CLASS (g_type_class_ref (*types));
402               
403               valid = FALSE;
404               
405               if (args->arg_type == GTK_TYPE_IDENTIFIER)
406                 {
407                   GFlagsValue *flags_value = NULL;
408                   flags_value = g_flags_get_value_by_name (class, args->d.string_data);
409                   if (!flags_value)
410                     flags_value = g_flags_get_value_by_nick (class, args->d.string_data);
411                   if (flags_value)
412                     {
413                       g_value_init (&tmp_value, *types);
414                       g_value_set_flags (&tmp_value, flags_value->value);
415                       valid = TRUE;
416                     }
417                 }
418
419               g_type_class_unref (class);
420             }
421           else
422             {
423               g_value_init (&tmp_value, G_TYPE_STRING);
424               g_value_set_static_string (&tmp_value, args->d.string_data);
425             }
426           break;
427         default:
428           valid = FALSE;
429           break;
430         }
431
432       if (valid)
433         {
434           if (!g_value_transform (&tmp_value, params))
435             valid = FALSE;
436
437           g_value_unset (&tmp_value);
438         }
439       
440       types++;
441       params++;
442       args++;
443     }
444   
445   if (!valid)
446     {
447       guint j;
448
449       for (j = 0; j < i; j++)
450         g_value_unset (&(*params_p)[j]);
451       
452       g_free (*params_p);
453       *params_p = NULL;
454     }
455   
456   return valid;
457 }
458
459 static gboolean
460 gtk_binding_entry_activate (GtkBindingEntry *entry,
461                             GtkObject       *object)
462 {
463   GtkBindingSignal *sig;
464   gboolean old_emission;
465   gboolean handled = FALSE;
466   gint i;
467   
468   old_emission = entry->in_emission;
469   entry->in_emission = TRUE;
470   
471   g_object_ref (object);
472   
473   for (sig = entry->signals; sig; sig = sig->next)
474     {
475       GSignalQuery query;
476       guint signal_id;
477       GValue *params = NULL;
478       GValue return_val = { 0, };
479       gchar *accelerator = NULL;
480       
481       signal_id = g_signal_lookup (sig->signal_name, G_OBJECT_TYPE (object));
482       if (!signal_id)
483         {
484           accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers);
485           g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": "
486                      "could not find signal \"%s\" in the `%s' class ancestry",
487                      entry->binding_set->set_name,
488                      accelerator,
489                      sig->signal_name,
490                      g_type_name (G_OBJECT_TYPE (object)));
491           g_free (accelerator);
492           continue;
493         }
494       
495       g_signal_query (signal_id, &query);
496       if (query.n_params != sig->n_args ||
497           (query.return_type != G_TYPE_NONE && query.return_type != G_TYPE_BOOLEAN) || 
498           !binding_compose_params (object, sig->args, &query, &params))
499         {
500           accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers);
501           g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": "
502                      "signature mismatch for signal \"%s\" in the `%s' class ancestry",
503                      entry->binding_set->set_name,
504                      accelerator,
505                      sig->signal_name,
506                      g_type_name (G_OBJECT_TYPE (object)));
507         }
508       else if (!(query.signal_flags & GTK_RUN_ACTION))
509         {
510           accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers);
511           g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": "
512                      "signal \"%s\" in the `%s' class ancestry cannot be used for action emissions",
513                      entry->binding_set->set_name,
514                      accelerator,
515                      sig->signal_name,
516                      g_type_name (G_OBJECT_TYPE (object)));
517         }
518       g_free (accelerator);
519       if (accelerator)
520         continue;
521
522       if (query.return_type == G_TYPE_BOOLEAN)
523         g_value_init (&return_val, G_TYPE_BOOLEAN);
524       
525       g_signal_emitv (params, signal_id, 0, &return_val);
526
527       if (query.return_type == G_TYPE_BOOLEAN)
528         {
529           if (g_value_get_boolean (&return_val))
530             handled = TRUE;
531           g_value_unset (&return_val);
532         }
533       else
534         handled = TRUE;
535       
536       for (i = 0; i < query.n_params + 1; i++)
537         g_value_unset (&params[i]);
538       g_free (params);
539       
540       if (entry->destroyed)
541         break;
542     }
543   
544   g_object_unref (object);
545
546   entry->in_emission = old_emission;
547   if (entry->destroyed && !entry->in_emission)
548     binding_entry_free (entry);
549
550   return handled;
551 }
552
553 GtkBindingSet*
554 gtk_binding_set_new (const gchar    *set_name)
555 {
556   GtkBindingSet *binding_set;
557   
558   g_return_val_if_fail (set_name != NULL, NULL);
559   
560   binding_set = g_new (GtkBindingSet, 1);
561   binding_set->set_name = g_strdup (set_name);
562   binding_set->widget_path_pspecs = NULL;
563   binding_set->widget_class_pspecs = NULL;
564   binding_set->class_branch_pspecs = NULL;
565   binding_set->entries = NULL;
566   binding_set->current = NULL;
567   
568   binding_set_list = g_slist_prepend (binding_set_list, binding_set);
569   
570   return binding_set;
571 }
572
573 GtkBindingSet*
574 gtk_binding_set_by_class (gpointer object_class)
575 {
576   GtkObjectClass *class = object_class;
577   GtkBindingSet* binding_set;
578
579   g_return_val_if_fail (GTK_IS_OBJECT_CLASS (class), NULL);
580
581   if (!key_id_class_binding_set)
582     key_id_class_binding_set = g_quark_from_static_string (key_class_binding_set);
583
584   binding_set = g_dataset_id_get_data (class, key_id_class_binding_set);
585
586   if (binding_set)
587     return binding_set;
588
589   binding_set = gtk_binding_set_new (gtk_type_name (GTK_CLASS_TYPE (class)));
590   gtk_binding_set_add_path (binding_set,
591                             GTK_PATH_CLASS,
592                             gtk_type_name (GTK_CLASS_TYPE (class)),
593                             GTK_PATH_PRIO_GTK);
594   g_dataset_id_set_data (class, key_id_class_binding_set, binding_set);
595
596   return binding_set;
597 }
598
599 GtkBindingSet*
600 gtk_binding_set_find (const gchar    *set_name)
601 {
602   GSList *slist;
603   
604   g_return_val_if_fail (set_name != NULL, NULL);
605   
606   for (slist = binding_set_list; slist; slist = slist->next)
607     {
608       GtkBindingSet *binding_set;
609       
610       binding_set = slist->data;
611       if (g_str_equal (binding_set->set_name, (gpointer) set_name))
612         return binding_set;
613     }
614   return NULL;
615 }
616
617 gboolean
618 gtk_binding_set_activate (GtkBindingSet  *binding_set,
619                           guint           keyval,
620                           GdkModifierType modifiers,
621                           GtkObject      *object)
622 {
623   GtkBindingEntry *entry;
624   
625   g_return_val_if_fail (binding_set != NULL, FALSE);
626   g_return_val_if_fail (GTK_IS_OBJECT (object), FALSE);
627   
628   keyval = gdk_keyval_to_lower (keyval);
629   modifiers = modifiers & BINDING_MOD_MASK ();
630   
631   entry = binding_ht_lookup_entry (binding_set, keyval, modifiers);
632   if (entry)
633     return gtk_binding_entry_activate (entry, object);
634   
635   return FALSE;
636 }
637
638 void
639 gtk_binding_entry_clear (GtkBindingSet  *binding_set,
640                          guint           keyval,
641                          GdkModifierType modifiers)
642 {
643   GtkBindingEntry *entry;
644   
645   g_return_if_fail (binding_set != NULL);
646   
647   keyval = gdk_keyval_to_lower (keyval);
648   modifiers = modifiers & BINDING_MOD_MASK ();
649   
650   entry = binding_ht_lookup_entry (binding_set, keyval, modifiers);
651   if (entry)
652     binding_entry_destroy (entry);
653
654   entry = binding_entry_new (binding_set, keyval, modifiers);
655 }
656
657 void
658 gtk_binding_entry_remove (GtkBindingSet  *binding_set,
659                           guint           keyval,
660                           GdkModifierType modifiers)
661 {
662   GtkBindingEntry *entry;
663   
664   g_return_if_fail (binding_set != NULL);
665   
666   keyval = gdk_keyval_to_lower (keyval);
667   modifiers = modifiers & BINDING_MOD_MASK ();
668   
669   entry = binding_ht_lookup_entry (binding_set, keyval, modifiers);
670   if (entry)
671     binding_entry_destroy (entry);
672 }
673
674 void
675 gtk_binding_entry_add_signall (GtkBindingSet  *binding_set,
676                                guint           keyval,
677                                GdkModifierType modifiers,
678                                const gchar    *signal_name,
679                                GSList         *binding_args)
680 {
681   GtkBindingEntry *entry;
682   GtkBindingSignal *signal, **signal_p;
683   GSList *slist;
684   guint n = 0;
685   GtkBindingArg *arg;
686   
687   g_return_if_fail (binding_set != NULL);
688   g_return_if_fail (signal_name != NULL);
689   
690   keyval = gdk_keyval_to_lower (keyval);
691   modifiers = modifiers & BINDING_MOD_MASK ();
692   
693   signal = binding_signal_new (signal_name, g_slist_length (binding_args));
694   
695   arg = signal->args;
696   for (slist = binding_args; slist; slist = slist->next)
697     {
698       GtkBindingArg *tmp_arg;
699       
700       tmp_arg = slist->data;
701       if (!tmp_arg)
702         {
703           g_warning ("gtk_binding_entry_add_signall(): arg[%u] is `NULL'", n);
704           binding_signal_free (signal);
705           return;
706         }
707       switch (GTK_FUNDAMENTAL_TYPE (tmp_arg->arg_type))
708         {
709         case  GTK_TYPE_LONG:
710           arg->arg_type = GTK_TYPE_LONG;
711           arg->d.long_data = tmp_arg->d.long_data;
712           break;
713         case  GTK_TYPE_DOUBLE:
714           arg->arg_type = GTK_TYPE_DOUBLE;
715           arg->d.double_data = tmp_arg->d.double_data;
716           break;
717         case  GTK_TYPE_STRING:
718           if (tmp_arg->arg_type != GTK_TYPE_IDENTIFIER)
719             arg->arg_type = GTK_TYPE_STRING;
720           else
721             arg->arg_type = GTK_TYPE_IDENTIFIER;
722           arg->d.string_data = g_strdup (tmp_arg->d.string_data);
723           if (!arg->d.string_data)
724             {
725               g_warning ("gtk_binding_entry_add_signall(): value of `string' arg[%u] is `NULL'", n);
726               binding_signal_free (signal);
727               return;
728             }
729           break;
730         default:
731           g_warning ("gtk_binding_entry_add_signall(): unsupported type `%s' for arg[%u]",
732                      gtk_type_name (arg->arg_type), n);
733           binding_signal_free (signal);
734           return;
735         }
736       arg++;
737       n++;
738     }
739   
740   entry = binding_ht_lookup_entry (binding_set, keyval, modifiers);
741   if (!entry)
742     {
743       gtk_binding_entry_add (binding_set, keyval, modifiers);
744       entry = binding_ht_lookup_entry (binding_set, keyval, modifiers);
745     }
746   signal_p = &entry->signals;
747   while (*signal_p)
748     signal_p = &(*signal_p)->next;
749   *signal_p = signal;
750 }
751
752 void
753 gtk_binding_entry_add_signal (GtkBindingSet  *binding_set,
754                               guint           keyval,
755                               GdkModifierType modifiers,
756                               const gchar    *signal_name,
757                               guint           n_args,
758                               ...)
759 {
760   GSList *slist, *free_slist;
761   va_list args;
762   guint i;
763
764   g_return_if_fail (binding_set != NULL);
765   g_return_if_fail (signal_name != NULL);
766   
767   va_start (args, n_args);
768   slist = NULL;
769   for (i = 0; i < n_args; i++)
770     {
771       GtkBindingArg *arg;
772
773       arg = g_new0 (GtkBindingArg, 1);
774       slist = g_slist_prepend (slist, arg);
775
776       arg->arg_type = va_arg (args, GtkType);
777       switch (GTK_FUNDAMENTAL_TYPE (arg->arg_type))
778         {
779           /* for elaborated commenting about var args collection, take a look
780            * at gtk_arg_collect_value() in gtkargcollector.c
781            */
782         case GTK_TYPE_CHAR:
783         case GTK_TYPE_UCHAR:
784         case GTK_TYPE_INT:
785         case GTK_TYPE_UINT:
786         case GTK_TYPE_BOOL:
787         case GTK_TYPE_ENUM:
788         case GTK_TYPE_FLAGS:
789           arg->arg_type = GTK_TYPE_LONG;
790           arg->d.long_data = va_arg (args, gint);
791           break;
792         case GTK_TYPE_LONG:
793         case GTK_TYPE_ULONG:
794           arg->arg_type = GTK_TYPE_LONG;
795           arg->d.long_data = va_arg (args, glong);
796           break;
797         case GTK_TYPE_FLOAT:
798         case GTK_TYPE_DOUBLE:
799           arg->arg_type = GTK_TYPE_DOUBLE;
800           arg->d.double_data = va_arg (args, gdouble);
801           break;
802         case GTK_TYPE_STRING:
803           if (arg->arg_type != GTK_TYPE_IDENTIFIER)
804             arg->arg_type = GTK_TYPE_STRING;
805           arg->d.string_data = va_arg (args, gchar*);
806           if (!arg->d.string_data)
807             {
808               g_warning ("gtk_binding_entry_add_signal(): type `%s' arg[%u] is `NULL'",
809                          gtk_type_name (arg->arg_type),
810                          i);
811               i += n_args + 1;
812             }
813           break;
814         default:
815           g_warning ("gtk_binding_entry_add_signal(): unsupported type `%s' for arg[%u]",
816                      gtk_type_name (arg->arg_type), i);
817           i += n_args + 1;
818           break;
819         }
820     }
821   va_end (args);
822
823   if (i == n_args || i == 0)
824     {
825       slist = g_slist_reverse (slist);
826       gtk_binding_entry_add_signall (binding_set, keyval, modifiers, signal_name, slist);
827     }
828
829   free_slist = slist;
830   while (slist)
831     {
832       g_free (slist->data);
833       slist = slist->next;
834     }
835   g_slist_free (free_slist);
836 }
837
838 void
839 gtk_binding_set_add_path (GtkBindingSet      *binding_set,
840                           GtkPathType         path_type,
841                           const gchar        *path_pattern,
842                           GtkPathPriorityType priority)
843 {
844   PatternSpec *pspec;
845   GSList **slist_p, *slist;
846   static guint seq_id = 0;
847   
848   g_return_if_fail (binding_set != NULL);
849   g_return_if_fail (path_pattern != NULL);
850   g_return_if_fail (priority <= GTK_PATH_PRIO_MASK);
851
852   priority &= GTK_PATH_PRIO_MASK;
853   
854   switch (path_type)
855     {
856     case  GTK_PATH_WIDGET:
857       slist_p = &binding_set->widget_path_pspecs;
858       break;
859     case  GTK_PATH_WIDGET_CLASS:
860       slist_p = &binding_set->widget_class_pspecs;
861       break;
862     case  GTK_PATH_CLASS:
863       slist_p = &binding_set->class_branch_pspecs;
864       break;
865     default:
866       g_assert_not_reached ();
867       slist_p = NULL;
868       break;
869     }
870   
871   pspec = g_new (PatternSpec, 1);
872   pspec->pspec = g_pattern_spec_new (path_pattern);
873   pspec->seq_id = priority << 28;
874   pspec->user_data = binding_set;
875   
876   slist = *slist_p;
877   while (slist)
878     {
879       PatternSpec *tmp_pspec;
880       
881       tmp_pspec = slist->data;
882       slist = slist->next;
883       
884       if (g_pattern_spec_equal (tmp_pspec->pspec, pspec->pspec))
885         {
886           GtkPathPriorityType lprio = tmp_pspec->seq_id >> 28;
887
888           g_pattern_spec_free (pspec->pspec);
889           g_free (pspec);
890           pspec = NULL;
891           if (lprio < priority)
892             {
893               tmp_pspec->seq_id &= 0x0fffffff;
894               tmp_pspec->seq_id |= priority << 28;
895             }
896           break;
897         }
898     }
899   if (pspec)
900     {
901       pspec->seq_id |= seq_id++ & 0x0fffffff;
902       *slist_p = g_slist_prepend (*slist_p, pspec);
903     }
904 }
905
906 static gboolean
907 binding_match_activate (GSList          *pspec_list,
908                         GtkObject       *object,
909                         guint            path_length,
910                         const gchar     *path,
911                         const gchar     *path_reversed)
912 {
913   GSList *slist;
914
915   for (slist = pspec_list; slist; slist = slist->next)
916     {
917       PatternSpec *pspec;
918
919       pspec = slist->data;
920       if (g_pattern_match (pspec->pspec, path_length, path, path_reversed))
921         {
922           GtkBindingSet *binding_set;
923
924           binding_set = pspec->user_data;
925
926           if (gtk_binding_entry_activate (binding_set->current, object))
927             return TRUE;
928         }
929     }
930
931   return FALSE;
932 }
933
934 static gint
935 gtk_binding_pattern_compare (gconstpointer new_pattern,
936                              gconstpointer existing_pattern)
937 {
938   register const PatternSpec *np  = new_pattern;
939   register const PatternSpec *ep  = existing_pattern;
940
941   /* walk the list as long as the existing patterns have
942    * higher priorities.
943    */
944
945   return np->seq_id < ep->seq_id;
946 }
947
948 static GSList*
949 gtk_binding_entries_sort_patterns (GSList      *entries,
950                                    GtkPathType  path_id,
951                                    gboolean     is_release)
952 {
953   GSList *patterns;
954   GSList *tmp_list;
955
956   patterns = NULL;
957   for (tmp_list = entries; tmp_list; tmp_list = tmp_list->next)
958     {
959       GtkBindingEntry *entry = tmp_list->data;
960       GtkBindingSet *binding_set;
961
962       binding_set = entry->binding_set;
963       binding_set->current = NULL;
964     }
965   
966   for (; entries; entries = entries->next)
967     {
968       GtkBindingEntry *entry = entries->data;
969       GtkBindingSet *binding_set;
970       GSList *slist = NULL;
971
972       if (is_release != ((entry->modifiers & GDK_RELEASE_MASK) != 0))
973         continue;
974
975       binding_set = entry->binding_set;
976
977       if (binding_set->current)
978         continue;
979       binding_set->current = entry;
980
981       switch (path_id)
982         {
983         case GTK_PATH_WIDGET:
984           slist = binding_set->widget_path_pspecs;
985           break;
986         case GTK_PATH_WIDGET_CLASS:
987           slist = binding_set->widget_class_pspecs;
988           break;
989         case GTK_PATH_CLASS:
990           slist = binding_set->class_branch_pspecs;
991           break;
992         }
993
994       for (; slist; slist = slist->next)
995         {
996           PatternSpec *pspec;
997
998           pspec = slist->data;
999           patterns = g_slist_insert_sorted (patterns, pspec, gtk_binding_pattern_compare);
1000         }
1001     }
1002
1003   return patterns;
1004 }
1005
1006 static gboolean
1007 gtk_bindings_activate_list (GtkObject *object,
1008                             GSList    *entries,
1009                             gboolean   is_release)
1010 {
1011   GtkWidget *widget = GTK_WIDGET (object);
1012   gboolean handled = FALSE;
1013
1014   if (!entries)
1015     return FALSE;
1016
1017   if (!handled)
1018     {
1019       guint path_length;
1020       gchar *path, *path_reversed;
1021       GSList *patterns;
1022
1023       gtk_widget_path (widget, &path_length, &path, &path_reversed);
1024       patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_WIDGET, is_release);
1025       handled = binding_match_activate (patterns, object, path_length, path, path_reversed);
1026       g_slist_free (patterns);
1027       g_free (path);
1028       g_free (path_reversed);
1029     }
1030
1031   if (!handled)
1032     {
1033       guint path_length;
1034       gchar *path, *path_reversed;
1035       GSList *patterns;
1036
1037       gtk_widget_class_path (widget, &path_length, &path, &path_reversed);
1038       patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_WIDGET_CLASS, is_release);
1039       handled = binding_match_activate (patterns, object, path_length, path, path_reversed);
1040       g_slist_free (patterns);
1041       g_free (path);
1042       g_free (path_reversed);
1043     }
1044
1045   if (!handled)
1046     {
1047       GSList *patterns;
1048       GtkType class_type;
1049       
1050       patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_CLASS, is_release);
1051       class_type = GTK_OBJECT_TYPE (object);
1052       while (class_type && !handled)
1053         {
1054           guint path_length;
1055           const gchar *path;
1056           gchar *path_reversed;
1057           
1058           path = gtk_type_name (class_type);
1059           path_reversed = g_strdup (path);
1060           g_strreverse (path_reversed);
1061           path_length = strlen (path);
1062           handled = binding_match_activate (patterns, object, path_length, path, path_reversed);
1063           g_free (path_reversed);
1064
1065           class_type = gtk_type_parent (class_type);
1066         }
1067       g_slist_free (patterns);
1068     }
1069
1070   return handled;
1071 }
1072
1073 gboolean
1074 gtk_bindings_activate (GtkObject      *object,
1075                        guint           keyval,
1076                        GdkModifierType modifiers)
1077 {
1078   GSList *entries = NULL;
1079   GdkDisplay *display;
1080   GtkKeyHash *key_hash;
1081   gboolean handled = FALSE;
1082   gboolean is_release;
1083
1084   g_return_val_if_fail (GTK_IS_OBJECT (object), FALSE);
1085
1086   if (!GTK_IS_WIDGET (object))
1087     return FALSE;
1088
1089   is_release = (modifiers & GDK_RELEASE_MASK) != 0;
1090   modifiers = modifiers & BINDING_MOD_MASK () & ~GDK_RELEASE_MASK;
1091
1092   display = gtk_widget_get_display (GTK_WIDGET (object));
1093   key_hash = binding_key_hash_for_keymap (gdk_keymap_get_for_display (display));
1094   
1095   entries = _gtk_key_hash_lookup_keyval (key_hash, keyval, modifiers);
1096
1097   handled = gtk_bindings_activate_list (object, entries, is_release);
1098
1099   g_slist_free (entries);
1100
1101   return handled;
1102 }
1103
1104 /**
1105  * _gtk_bindings_activate_event:
1106  * @object: a #GtkObject (generally must be a widget)
1107  * @event: a #GdkEventKey
1108  * 
1109  * Looks up key bindings for @object to find one matching
1110  * @event, and if one was found, activate it.
1111  * 
1112  * Return value: %TRUE if a matching key binding was found
1113  **/
1114 gboolean
1115 _gtk_bindings_activate_event (GtkObject      *object,
1116                               GdkEventKey    *event)
1117 {
1118   GSList *entries = NULL;
1119   GdkDisplay *display;
1120   GtkKeyHash *key_hash;
1121   gboolean handled = FALSE;
1122
1123   g_return_val_if_fail (GTK_IS_OBJECT (object), FALSE);
1124
1125   if (!GTK_IS_WIDGET (object))
1126     return FALSE;
1127
1128   display = gtk_widget_get_display (GTK_WIDGET (object));
1129   key_hash = binding_key_hash_for_keymap (gdk_keymap_get_for_display (display));
1130
1131   entries = _gtk_key_hash_lookup (key_hash,
1132                                   event->hardware_keycode,
1133                                   event->state & BINDING_MOD_MASK () & ~GDK_RELEASE_MASK,
1134                                   event->group);
1135   
1136   handled = gtk_bindings_activate_list (object, entries,
1137                                         event->type == GDK_KEY_RELEASE);
1138
1139   g_slist_free (entries);
1140
1141   return handled;
1142 }
1143
1144 static guint
1145 gtk_binding_parse_signal (GScanner       *scanner,
1146                           GtkBindingSet  *binding_set,
1147                           guint           keyval,
1148                           GdkModifierType modifiers)
1149 {
1150   gchar *signal;
1151   guint expected_token = 0;
1152   GSList *args;
1153   GSList *slist;
1154   gboolean done;
1155   gboolean negate;
1156   gboolean need_arg;
1157   gboolean seen_comma;
1158
1159   g_return_val_if_fail (scanner != NULL, G_TOKEN_ERROR);
1160   
1161   g_scanner_get_next_token (scanner);
1162   if (scanner->token != G_TOKEN_STRING)
1163     return G_TOKEN_STRING;
1164   g_scanner_peek_next_token (scanner);
1165   if (scanner->next_token != '(')
1166     {
1167       g_scanner_get_next_token (scanner);
1168       return '(';
1169     }
1170   signal = g_strdup (scanner->value.v_string);
1171   g_scanner_get_next_token (scanner);
1172
1173   negate = FALSE;
1174   args = NULL;
1175   done = FALSE;
1176   need_arg = TRUE;
1177   seen_comma = FALSE;
1178   scanner->config->scan_symbols = FALSE;
1179   do
1180     {
1181       if (need_arg)
1182         expected_token = G_TOKEN_INT;
1183       else
1184         expected_token = ')';
1185       g_scanner_get_next_token (scanner);
1186       switch (scanner->token)
1187         {
1188           GtkBindingArg *arg;
1189
1190         case G_TOKEN_FLOAT:
1191           if (need_arg)
1192             {
1193               need_arg = FALSE;
1194               arg = g_new (GtkBindingArg, 1);
1195               arg->arg_type = GTK_TYPE_DOUBLE;
1196               arg->d.double_data = scanner->value.v_float;
1197               if (negate)
1198                 {
1199                   arg->d.double_data = - arg->d.double_data;
1200                   negate = FALSE;
1201                 }
1202               args = g_slist_prepend (args, arg);
1203             }
1204           else
1205             done = TRUE;
1206           break;
1207         case G_TOKEN_INT:
1208           if (need_arg)
1209             {
1210               need_arg = FALSE;
1211               arg = g_new (GtkBindingArg, 1);
1212               arg->arg_type = GTK_TYPE_LONG;
1213               arg->d.long_data = scanner->value.v_int;
1214               if (negate)
1215                 {
1216                   arg->d.long_data = - arg->d.long_data;
1217                   negate = FALSE;
1218                 }
1219               args = g_slist_prepend (args, arg);
1220             }
1221           else
1222             done = TRUE;
1223           break;
1224         case G_TOKEN_STRING:
1225           if (need_arg && !negate)
1226             {
1227               need_arg = FALSE;
1228               arg = g_new (GtkBindingArg, 1);
1229               arg->arg_type = GTK_TYPE_STRING;
1230               arg->d.string_data = g_strdup (scanner->value.v_string);
1231               args = g_slist_prepend (args, arg);
1232             }
1233           else
1234             done = TRUE;
1235           break;
1236         case G_TOKEN_IDENTIFIER:
1237           if (need_arg && !negate)
1238             {
1239               need_arg = FALSE;
1240               arg = g_new (GtkBindingArg, 1);
1241               arg->arg_type = GTK_TYPE_IDENTIFIER;
1242               arg->d.string_data = g_strdup (scanner->value.v_identifier);
1243               args = g_slist_prepend (args, arg);
1244             }
1245           else
1246             done = TRUE;
1247           break;
1248         case '-':
1249           if (!need_arg)
1250             done = TRUE;
1251           else if (negate)
1252             {
1253               expected_token = G_TOKEN_INT;
1254               done = TRUE;
1255             }
1256           else
1257             negate = TRUE;
1258           break;
1259         case ',':
1260           seen_comma = TRUE;
1261           if (need_arg)
1262             done = TRUE;
1263           else
1264             need_arg = TRUE;
1265           break;
1266         case ')':
1267           if (!(need_arg && seen_comma) && !negate)
1268             {
1269               args = g_slist_reverse (args);
1270               gtk_binding_entry_add_signall (binding_set,
1271                                              keyval,
1272                                              modifiers,
1273                                              signal,
1274                                              args);
1275               expected_token = G_TOKEN_NONE;
1276             }
1277           done = TRUE;
1278           break;
1279         default:
1280           done = TRUE;
1281           break;
1282         }
1283     }
1284   while (!done);
1285   scanner->config->scan_symbols = TRUE;
1286   
1287   for (slist = args; slist; slist = slist->next)
1288     {
1289       GtkBindingArg *arg;
1290
1291       arg = slist->data;
1292       if (GTK_FUNDAMENTAL_TYPE (arg->arg_type) == GTK_TYPE_STRING)
1293         g_free (arg->d.string_data);
1294       g_free (arg);
1295     }
1296   g_slist_free (args);
1297   g_free (signal);
1298
1299   return expected_token;
1300 }
1301
1302 static inline guint
1303 gtk_binding_parse_bind (GScanner       *scanner,
1304                         GtkBindingSet  *binding_set)
1305 {
1306   guint keyval = 0;
1307   GdkModifierType modifiers = 0;
1308
1309   g_return_val_if_fail (scanner != NULL, G_TOKEN_ERROR);
1310   
1311   g_scanner_get_next_token (scanner);
1312   if (scanner->token != GTK_RC_TOKEN_BIND)
1313     return GTK_RC_TOKEN_BIND;
1314   g_scanner_get_next_token (scanner);
1315   if (scanner->token != G_TOKEN_STRING)
1316     return G_TOKEN_STRING;
1317   gtk_accelerator_parse (scanner->value.v_string, &keyval, &modifiers);
1318   modifiers &= BINDING_MOD_MASK ();
1319   if (keyval == 0)
1320     return G_TOKEN_STRING;
1321
1322   g_scanner_get_next_token (scanner);
1323   if (scanner->token != '{')
1324     return '{';
1325
1326   gtk_binding_entry_clear (binding_set, keyval, modifiers);
1327   
1328   g_scanner_peek_next_token (scanner);
1329   while (scanner->next_token != '}')
1330     {
1331       switch (scanner->next_token)
1332         {
1333           guint expected_token;
1334
1335         case G_TOKEN_STRING:
1336           expected_token = gtk_binding_parse_signal (scanner,
1337                                                      binding_set,
1338                                                      keyval,
1339                                                      modifiers);
1340           if (expected_token != G_TOKEN_NONE)
1341             return expected_token;
1342           break;
1343         default:
1344           g_scanner_get_next_token (scanner);
1345           return '}';
1346         }
1347       g_scanner_peek_next_token (scanner);
1348     }
1349   g_scanner_get_next_token (scanner);
1350
1351   return G_TOKEN_NONE;
1352 }
1353
1354 guint
1355 gtk_binding_parse_binding (GScanner       *scanner)
1356 {
1357   gchar *name;
1358   GtkBindingSet *binding_set;
1359
1360   g_return_val_if_fail (scanner != NULL, G_TOKEN_ERROR);
1361
1362   g_scanner_get_next_token (scanner);
1363   if (scanner->token != GTK_RC_TOKEN_BINDING)
1364     return GTK_RC_TOKEN_BINDING;
1365   g_scanner_get_next_token (scanner);
1366   if (scanner->token != G_TOKEN_STRING)
1367     return G_TOKEN_STRING;
1368   name = g_strdup (scanner->value.v_string);
1369
1370   g_scanner_get_next_token (scanner);
1371   if (scanner->token != '{')
1372     {
1373       g_free (name);
1374       return G_TOKEN_STRING;
1375     }
1376
1377   binding_set = gtk_binding_set_find (name);
1378   if (!binding_set)
1379     {
1380       binding_set = gtk_binding_set_new (name);
1381       binding_set->parsed = 1;
1382     }
1383   g_free (name);
1384
1385   g_scanner_peek_next_token (scanner);
1386   while (scanner->next_token != '}')
1387     {
1388       switch (scanner->next_token)
1389         {
1390           guint expected_token;
1391
1392         case GTK_RC_TOKEN_BIND:
1393           expected_token = gtk_binding_parse_bind (scanner, binding_set);
1394           if (expected_token != G_TOKEN_NONE)
1395             return expected_token;
1396           break;
1397         default:
1398           g_scanner_get_next_token (scanner);
1399           return '}';
1400         }
1401       g_scanner_peek_next_token (scanner);
1402     }
1403   g_scanner_get_next_token (scanner);
1404
1405   return G_TOKEN_NONE;
1406 }
1407
1408 static void
1409 free_pattern_specs (GSList *pattern_specs)
1410 {
1411   GSList *slist;
1412
1413   for (slist = pattern_specs; slist; slist = slist->next)
1414     {
1415       PatternSpec *pspec;
1416
1417       pspec = slist->data;
1418
1419       g_pattern_spec_free (pspec->pspec);
1420       g_free (pspec);
1421     }
1422
1423   g_slist_free (pattern_specs);
1424 }
1425
1426 static void
1427 binding_set_delete (GtkBindingSet *binding_set)
1428 {
1429   GtkBindingEntry *entry, *next;
1430
1431   entry = binding_set->entries;
1432   while (entry)
1433     {
1434       next = entry->set_next;
1435       binding_entry_destroy (entry);
1436       entry = next;
1437     }
1438   
1439   free_pattern_specs (binding_set->widget_path_pspecs);
1440   free_pattern_specs (binding_set->widget_class_pspecs);
1441   free_pattern_specs (binding_set->class_branch_pspecs);
1442
1443   g_free (binding_set->set_name);
1444   g_free (binding_set);
1445 }
1446
1447 /**
1448  * _gtk_binding_reset_parsed:
1449  * 
1450  * Removing all binding sets that were added by
1451  * gtk_binding_parse_binding()
1452  **/
1453 void
1454 _gtk_binding_reset_parsed (void)
1455 {
1456   GSList *slist, *next;
1457   
1458   slist = binding_set_list;
1459   while (slist)
1460     {
1461       GtkBindingSet *binding_set;
1462
1463       binding_set = slist->data;
1464       next = slist->next;
1465
1466       if (binding_set->parsed)
1467         {
1468           binding_set_list = g_slist_delete_link (binding_set_list, slist);
1469           binding_set_delete (binding_set);
1470         }
1471
1472       slist = next;
1473     }
1474 }