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