]> Pileus Git - ~andy/gtk/blob - gtk/gtkbindings.c
deleted most of the argument handling code, since that is now implemented
[~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 #include <ctype.h>
23 #include <strings.h>
24 #include <stdarg.h>
25 #include "gtkbindings.h"
26 #include "gtksignal.h"
27 #include "gtkwidget.h"
28
29
30 /* --- defines --- */
31 #define BINDING_MOD_MASK()      (gtk_accelerator_get_default_mod_mask () | GDK_RELEASE_MASK)
32
33 #define BINDING_TYPE_INT        (GTK_TYPE_INT)
34 #define BINDING_TYPE_LONG       (GTK_TYPE_LONG)
35 #define BINDING_TYPE_FLOAT      (GTK_TYPE_FLOAT)
36 #define BINDING_TYPE_DOUBLE     (GTK_TYPE_DOUBLE)
37 #define BINDING_TYPE_STRING     (GTK_TYPE_STRING)
38 #define BINDING_TYPE_IDENTIFIER (42)
39
40
41 /* --- variables --- */
42 static GHashTable       *binding_entry_hash_table = NULL;
43 static GSList           *binding_set_list = NULL;
44 static const gchar      *key_class_binding_set = "gtk-class-binding-set";
45 static GQuark            key_id_class_binding_set = 0;
46
47
48 /* --- functions --- */
49 static GtkBindingSignal*
50 binding_signal_new (const gchar *signal_name,
51                     guint        n_args)
52 {
53   GtkBindingSignal *signal;
54   
55   signal = g_new (GtkBindingSignal, 1);
56   signal->next = NULL;
57   signal->signal_name = g_strdup (signal_name);
58   signal->n_args = n_args;
59   signal->args = g_new0 (GtkBindingArg, n_args);
60   
61   return signal;
62 }
63
64 static void
65 binding_signal_free (GtkBindingSignal *sig)
66 {
67   guint i;
68   
69   for (i = 0; i < sig->n_args; i++)
70     {
71       if (sig->args[i].arg_type == BINDING_TYPE_STRING ||
72           sig->args[i].arg_type == BINDING_TYPE_IDENTIFIER)
73         g_free (sig->args[i].d.string_data);
74     }
75   g_free (sig->args);
76   g_free (sig->signal_name);
77   g_free (sig);
78 }
79
80 static guint
81 binding_entry_hash (gconstpointer  key)
82 {
83   register const GtkBindingEntry *e = key;
84   register guint h;
85
86   h = e->keyval;
87   h ^= e->modifiers;
88
89   return h;
90 }
91
92 static gint
93 binding_entries_compare (gconstpointer  a,
94                          gconstpointer  b)
95 {
96   register const GtkBindingEntry *ea = a;
97   register const GtkBindingEntry *eb = b;
98
99   return (ea->keyval == eb->keyval && ea->modifiers == eb->modifiers);
100 }
101
102 static GtkBindingEntry*
103 binding_entry_new (GtkBindingSet *binding_set,
104                    guint          keyval,
105                    guint          modifiers)
106 {
107   GtkBindingEntry *entry;
108   
109   if (!binding_entry_hash_table)
110     binding_entry_hash_table = g_hash_table_new (binding_entry_hash, binding_entries_compare);
111
112   entry = g_new (GtkBindingEntry, 1);
113   entry->keyval = keyval;
114   entry->modifiers = modifiers;
115   entry->binding_set = binding_set,
116   entry->destroyed = FALSE;
117   entry->in_emission = FALSE;
118   entry->signals = NULL;
119
120   entry->set_next = binding_set->entries;
121   binding_set->entries = entry;
122
123   entry->hash_next = g_hash_table_lookup (binding_entry_hash_table, entry);
124   g_hash_table_freeze (binding_entry_hash_table);
125   if (entry->hash_next)
126     g_hash_table_remove (binding_entry_hash_table, entry->hash_next);
127   g_hash_table_insert (binding_entry_hash_table, entry, entry);
128   g_hash_table_thaw (binding_entry_hash_table);
129   
130   return entry;
131 }
132
133 static void
134 binding_entry_free (GtkBindingEntry *entry)
135 {
136   GtkBindingSignal *sig;
137
138   g_assert (entry->set_next == NULL &&
139             entry->hash_next == NULL &&
140             entry->in_emission == FALSE &&
141             entry->destroyed == TRUE);
142
143   entry->destroyed = FALSE;
144   
145   sig = entry->signals;
146   while (sig)
147     {
148       GtkBindingSignal *prev;
149       
150       prev = sig;
151       sig = prev->next;
152       binding_signal_free (prev);
153     }
154   g_free (entry);
155 }
156
157 static void
158 binding_entry_destroy (GtkBindingEntry *entry)
159 {
160   GtkBindingEntry *o_entry;
161   register GtkBindingEntry *tmp;
162   GtkBindingEntry *begin;
163   register GtkBindingEntry *last;
164
165   /* unlink from binding set
166    */
167   last = NULL;
168   tmp = entry->binding_set->entries;
169   while (tmp)
170     {
171       if (tmp == entry)
172         {
173           if (last)
174             last->set_next = entry->set_next;
175           else
176             entry->binding_set->entries = entry->set_next;
177           break;
178         }
179       last = tmp;
180       tmp = last->set_next;
181     }
182   entry->set_next = NULL;
183   
184   o_entry = g_hash_table_lookup (binding_entry_hash_table, entry);
185   begin = o_entry;
186   last = NULL;
187   tmp = begin;
188   while (tmp)
189     {
190       if (tmp == entry)
191         {
192           if (last)
193             last->hash_next = entry->hash_next;
194           else
195             begin = entry->hash_next;
196           break;
197         }
198       last = tmp;
199       tmp = last->hash_next;
200     }
201   entry->hash_next = NULL;
202   
203   if (!begin)
204     g_hash_table_remove (binding_entry_hash_table, entry);
205   else if (begin != o_entry)
206     {
207       g_hash_table_freeze (binding_entry_hash_table);
208       g_hash_table_remove (binding_entry_hash_table, entry);
209       g_hash_table_insert (binding_entry_hash_table, begin, begin);
210       g_hash_table_thaw (binding_entry_hash_table);
211     }
212
213   entry->destroyed = TRUE;
214
215   if (!entry->in_emission)
216     binding_entry_free (entry);
217 }
218
219 static GtkBindingEntry*
220 binding_ht_lookup_list (guint keyval,
221                         guint modifiers)
222 {
223   GtkBindingEntry lookup_entry = { 0 };
224   
225   if (!binding_entry_hash_table)
226     return NULL;
227   
228   lookup_entry.keyval = keyval;
229   lookup_entry.modifiers = modifiers;
230   
231   return g_hash_table_lookup (binding_entry_hash_table, &lookup_entry);
232 }
233
234 static GtkBindingEntry*
235 binding_ht_lookup_entry (GtkBindingSet *set,
236                          guint          keyval,
237                          guint          modifiers)
238 {
239   GtkBindingEntry lookup_entry = { 0 };
240   GtkBindingEntry *entry;
241   
242   if (!binding_entry_hash_table)
243     return NULL;
244   
245   lookup_entry.keyval = keyval;
246   lookup_entry.modifiers = modifiers;
247   
248   entry = g_hash_table_lookup (binding_entry_hash_table, &lookup_entry);
249   for (; entry; entry = entry->hash_next)
250     if (entry->binding_set == set)
251       return entry;
252
253   return NULL;
254 }
255
256 static gboolean
257 binding_compose_params (GtkBindingArg   *args,
258                         GtkSignalQuery  *query,
259                         GtkArg          **params_p)
260 {
261   GtkArg *params;
262   const GtkType *types;
263   guint i;
264   gboolean valid;
265   
266   params = g_new0 (GtkArg, query->nparams);
267   *params_p = params;
268   
269   types = query->params;
270   valid = TRUE;
271   for (i = 0; i < query->nparams && valid; i++)
272     {
273       params->type = *types;
274       params->name = NULL;
275       switch (args->arg_type)
276         {
277         case  BINDING_TYPE_DOUBLE:
278           if (params->type == GTK_TYPE_FLOAT)
279             GTK_VALUE_FLOAT (*params) = args->d.double_data;
280           else if (params->type == GTK_TYPE_DOUBLE)
281             GTK_VALUE_DOUBLE (*params) = args->d.double_data;
282           else
283             valid = FALSE;
284           break;
285         case  BINDING_TYPE_LONG:
286           if (params->type == GTK_TYPE_BOOL &&
287               (args->d.long_data == 0 ||
288                args->d.long_data == 1))
289             GTK_VALUE_BOOL (*params) = args->d.long_data;
290           else if (params->type == GTK_TYPE_INT)
291             GTK_VALUE_INT (*params) = args->d.long_data;
292           else if (params->type == GTK_TYPE_UINT &&
293                    args->d.long_data >= 0)
294             GTK_VALUE_UINT (*params) = args->d.long_data;
295           else if (params->type == GTK_TYPE_LONG)
296             GTK_VALUE_LONG (*params) = args->d.long_data;
297           else if (params->type == GTK_TYPE_ULONG &&
298                    args->d.long_data >= 0)
299             GTK_VALUE_ULONG (*params) = args->d.long_data;
300           else if (params->type == GTK_TYPE_FLOAT)
301             GTK_VALUE_FLOAT (*params) = args->d.long_data;
302           else if (params->type == GTK_TYPE_DOUBLE)
303             GTK_VALUE_DOUBLE (*params) = args->d.long_data;
304           else
305             valid = FALSE;
306           break;
307         case  BINDING_TYPE_STRING:
308           if (params->type == GTK_TYPE_STRING)
309             GTK_VALUE_STRING (*params) = args->d.string_data;
310           else
311             valid = FALSE;
312           break;
313         case  BINDING_TYPE_IDENTIFIER:
314         default:
315           valid = FALSE;
316           break;
317         }
318       types++;
319       params++;
320       args++;
321     }
322   
323   if (!valid)
324     {
325       g_free (*params_p);
326       *params_p = NULL;
327     }
328   
329   return valid;
330 }
331
332 static void
333 gtk_binding_entry_activate (GtkBindingEntry     *entry,
334                             GtkObject   *object)
335 {
336   GtkBindingSignal *sig;
337   gboolean old_emission;
338   
339   old_emission = entry->in_emission;
340   entry->in_emission = TRUE;
341   
342   gtk_object_ref (object);
343   
344   for (sig = entry->signals; sig; sig = sig->next)
345     {
346       GtkSignalQuery *query;
347       guint signal_id;
348       GtkArg *params = NULL;
349       gchar *accelerator = NULL;
350       
351       signal_id = gtk_signal_lookup (sig->signal_name, GTK_OBJECT_TYPE (object));
352       if (!signal_id)
353         {
354           accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers);
355           g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": "
356                      "could not find signal \"%s\" in the `%s' class ancestry",
357                      entry->binding_set->set_name,
358                      accelerator,
359                      sig->signal_name,
360                      gtk_type_name (GTK_OBJECT_TYPE (object)));
361           g_free (accelerator);
362           continue;
363         }
364       
365       query = gtk_signal_query (signal_id);
366       if (query->nparams != sig->n_args ||
367           query->return_val != GTK_TYPE_NONE ||
368           !binding_compose_params (sig->args, query, &params))
369         {
370           accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers);
371           g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": "
372                      "signature mismatch for signal \"%s\" in the `%s' class ancestry",
373                      entry->binding_set->set_name,
374                      accelerator,
375                      sig->signal_name,
376                      gtk_type_name (GTK_OBJECT_TYPE (object)));
377         }
378       else if (!(query->signal_flags & GTK_RUN_ACTION))
379         {
380           accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers);
381           g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": "
382                      "signal \"%s\" in the `%s' class ancestry cannot be used for action emissions",
383                      entry->binding_set->set_name,
384                      accelerator,
385                      sig->signal_name,
386                      gtk_type_name (GTK_OBJECT_TYPE (object)));
387         }
388       g_free (accelerator);
389       g_free (query);
390       if (accelerator)
391         continue;
392
393       gtk_signal_emitv (object, signal_id, params);
394       g_free (params);
395       
396       if (GTK_OBJECT_DESTROYED (object) || entry->destroyed)
397         break;
398     }
399   
400   gtk_object_unref (object);
401
402   entry->in_emission = old_emission;
403   if (entry->destroyed && !entry->in_emission)
404     binding_entry_free (entry);
405 }
406
407 GtkBindingSet*
408 gtk_binding_set_new (const gchar    *set_name)
409 {
410   GtkBindingSet *binding_set;
411   
412   g_return_val_if_fail (set_name != NULL, NULL);
413   
414   binding_set = g_new (GtkBindingSet, 1);
415   binding_set->set_name = g_strdup (set_name);
416   binding_set->widget_path_pspecs = NULL;
417   binding_set->widget_class_pspecs = NULL;
418   binding_set->class_branch_pspecs = NULL;
419   binding_set->entries = NULL;
420   binding_set->current = NULL;
421   
422   binding_set_list = g_slist_prepend (NULL, binding_set);
423   
424   return binding_set;
425 }
426
427 GtkBindingSet*
428 gtk_binding_set_by_class (gpointer object_class)
429 {
430   GtkObjectClass *class = object_class;
431   GtkBindingSet* binding_set;
432
433   g_return_val_if_fail (GTK_IS_OBJECT_CLASS (class), NULL);
434
435   if (!key_id_class_binding_set)
436     key_id_class_binding_set = g_quark_from_static_string (key_class_binding_set);
437
438   binding_set = g_dataset_id_get_data (class, key_id_class_binding_set);
439
440   if (binding_set)
441     return binding_set;
442
443   binding_set = gtk_binding_set_new (gtk_type_name (class->type));
444   gtk_binding_set_add_path (binding_set,
445                             GTK_PATH_CLASS,
446                             gtk_type_name (class->type),
447                             GTK_PATH_PRIO_GTK);
448   g_dataset_id_set_data (class, key_id_class_binding_set, binding_set);
449
450   return binding_set;
451 }
452
453 GtkBindingSet*
454 gtk_binding_set_find (const gchar    *set_name)
455 {
456   GSList *slist;
457   
458   g_return_val_if_fail (set_name != NULL, NULL);
459   
460   for (slist = binding_set_list; slist; slist = slist->next)
461     {
462       GtkBindingSet *binding_set;
463       
464       binding_set = slist->data;
465       if (g_str_equal (binding_set->set_name, (gpointer) set_name))
466         return binding_set;
467     }
468   return NULL;
469 }
470
471 gboolean
472 gtk_binding_set_activate (GtkBindingSet  *binding_set,
473                           guint           keyval,
474                           guint           modifiers,
475                           GtkObject      *object)
476 {
477   GtkBindingEntry *entry;
478   
479   g_return_val_if_fail (binding_set != NULL, FALSE);
480   g_return_val_if_fail (object != NULL, FALSE);
481   g_return_val_if_fail (GTK_IS_OBJECT (object), FALSE);
482   
483   keyval = gdk_keyval_to_lower (keyval);
484   modifiers = modifiers & BINDING_MOD_MASK ();
485   
486   if (!GTK_OBJECT_DESTROYED (object))
487     {
488       entry = binding_ht_lookup_entry (binding_set, keyval, modifiers);
489       if (entry)
490         {
491           gtk_binding_entry_activate (entry, object);
492           
493           return TRUE;
494         }
495     }
496   
497   return FALSE;
498 }
499
500 void
501 gtk_binding_entry_clear (GtkBindingSet  *binding_set,
502                          guint           keyval,
503                          guint           modifiers)
504 {
505   GtkBindingEntry *entry;
506   
507   g_return_if_fail (binding_set != NULL);
508   
509   keyval = gdk_keyval_to_lower (keyval);
510   modifiers = modifiers & BINDING_MOD_MASK ();
511   
512   entry = binding_ht_lookup_entry (binding_set, keyval, modifiers);
513   if (entry)
514     binding_entry_destroy (entry);
515
516   entry = binding_entry_new (binding_set, keyval, modifiers);
517 }
518
519 void
520 gtk_binding_entry_remove (GtkBindingSet  *binding_set,
521                           guint           keyval,
522                           guint           modifiers)
523 {
524   GtkBindingEntry *entry;
525   
526   g_return_if_fail (binding_set != NULL);
527   
528   keyval = gdk_keyval_to_lower (keyval);
529   modifiers = modifiers & BINDING_MOD_MASK ();
530   
531   entry = binding_ht_lookup_entry (binding_set, keyval, modifiers);
532   if (entry)
533     binding_entry_destroy (entry);
534 }
535
536 void
537 gtk_binding_entry_add_signall (GtkBindingSet  *binding_set,
538                                guint           keyval,
539                                guint           modifiers,
540                                const gchar    *signal_name,
541                                GSList         *binding_args)
542 {
543   GtkBindingEntry *entry;
544   GtkBindingSignal *signal, **signal_p;
545   GSList *slist;
546   guint n = 0;
547   GtkBindingArg *arg;
548   
549   g_return_if_fail (binding_set != NULL);
550   g_return_if_fail (signal_name != NULL);
551   
552   keyval = gdk_keyval_to_lower (keyval);
553   modifiers = modifiers & BINDING_MOD_MASK ();
554   
555   signal = binding_signal_new (signal_name, g_slist_length (binding_args));
556   
557   arg = signal->args;
558   for (slist = binding_args; slist; slist = slist->next)
559     {
560       GtkBindingArg *tmp_arg;
561       
562       tmp_arg = slist->data;
563       if (!tmp_arg)
564         {
565           g_warning ("gtk_binding_entry_add_signall(): arg[%u] is `NULL'", n);
566           binding_signal_free (signal);
567           return;
568         }
569       arg->arg_type = tmp_arg->arg_type;
570       switch (tmp_arg->arg_type)
571         {
572         case  BINDING_TYPE_INT:
573         case  BINDING_TYPE_LONG:
574           arg->d.long_data = tmp_arg->d.long_data;
575           break;
576         case  BINDING_TYPE_FLOAT:
577         case  BINDING_TYPE_DOUBLE:
578           arg->d.double_data = tmp_arg->d.double_data;
579           break;
580         case  BINDING_TYPE_STRING:
581           if (!tmp_arg->d.string_data)
582             {
583               g_warning ("gtk_binding_entry_add_signall(): value of `string' arg[%u] is `NULL'", n);
584               arg->d.string_data = NULL;
585               binding_signal_free (signal);
586               return;
587             }
588           arg->d.string_data = g_strdup (tmp_arg->d.string_data);
589           break;
590         default:
591           g_warning ("gtk_binding_entry_add_signall(): unsupported type `%s' for arg[%u]",
592                      gtk_type_name (arg->arg_type), n);
593           binding_signal_free (signal);
594           return;
595         }
596       arg++;
597       n++;
598     }
599   
600   entry = binding_ht_lookup_entry (binding_set, keyval, modifiers);
601   if (!entry)
602     {
603       gtk_binding_entry_add (binding_set, keyval, modifiers);
604       entry = binding_ht_lookup_entry (binding_set, keyval, modifiers);
605     }
606   signal_p = &entry->signals;
607   while (*signal_p)
608     signal_p = &(*signal_p)->next;
609   *signal_p = signal;
610 }
611
612 void
613 gtk_binding_entry_add_signal (GtkBindingSet  *binding_set,
614                               guint           keyval,
615                               guint           modifiers,
616                               const gchar    *signal_name,
617                               guint           n_args,
618                               ...)
619 {
620   GSList *slist, *free_slist;
621   va_list args;
622   guint i;
623
624   g_return_if_fail (binding_set != NULL);
625   g_return_if_fail (signal_name != NULL);
626   
627   keyval = gdk_keyval_to_lower (keyval);
628   modifiers = modifiers & BINDING_MOD_MASK ();
629
630   va_start (args, n_args);
631   slist = NULL;
632   for (i = 0; i < n_args; i++)
633     {
634       GtkBindingArg *arg;
635
636       arg = g_new0 (GtkBindingArg, 1);
637       slist = g_slist_prepend (slist, arg);
638
639       arg->arg_type = va_arg (args, GtkType);
640       switch (arg->arg_type)
641         {
642         case  BINDING_TYPE_INT:
643           arg->d.long_data = va_arg (args, gint);
644           break;
645         case  BINDING_TYPE_LONG:
646           arg->d.long_data = va_arg (args, glong);
647           break;
648         case  BINDING_TYPE_FLOAT:
649           arg->d.double_data = va_arg (args, gfloat);
650           break;
651         case  BINDING_TYPE_DOUBLE:
652           arg->d.double_data = va_arg (args, gdouble);
653           break;
654         case  BINDING_TYPE_STRING:
655           arg->d.string_data = va_arg (args, gchar*);
656           if (!arg->d.string_data)
657             {
658               g_warning ("gtk_binding_entry_add_signal(): value of `string' arg[%u] is `NULL'", i);
659               i += n_args + 1;
660             }
661           break;
662         default:
663           g_warning ("gtk_binding_entry_add_signal(): unsupported type `%s' for arg[%u]",
664                      gtk_type_name (arg->arg_type), i);
665           i += n_args + 1;
666           break;
667         }
668     }
669   va_end (args);
670
671   if (i == n_args || i == 0)
672     {
673       slist = g_slist_reverse (slist);
674       gtk_binding_entry_add_signall (binding_set, keyval, modifiers, signal_name, slist);
675     }
676
677   free_slist = slist;
678   while (slist)
679     {
680       g_free (slist->data);
681       slist = slist->next;
682     }
683   g_slist_free (free_slist);
684 }
685
686 void
687 gtk_binding_set_add_path (GtkBindingSet      *binding_set,
688                           GtkPathType         path_type,
689                           const gchar        *path_pattern,
690                           GtkPathPriorityType priority)
691 {
692   GtkPatternSpec *pspec;
693   GSList **slist_p, *slist;
694   static guint seq_id = 0;
695   
696   g_return_if_fail (binding_set != NULL);
697   g_return_if_fail (path_pattern != NULL);
698
699   priority &= GTK_PATH_PRIO_MASK;
700   
701   switch (path_type)
702     {
703     case  GTK_PATH_WIDGET:
704       slist_p = &binding_set->widget_path_pspecs;
705       break;
706     case  GTK_PATH_WIDGET_CLASS:
707       slist_p = &binding_set->widget_class_pspecs;
708       break;
709     case  GTK_PATH_CLASS:
710       slist_p = &binding_set->class_branch_pspecs;
711       break;
712     default:
713       g_assert_not_reached ();
714       slist_p = NULL;
715       break;
716     }
717   
718   pspec = g_new (GtkPatternSpec, 1);
719   gtk_pattern_spec_init (pspec, path_pattern);
720   pspec->seq_id = seq_id++ & 0x0fffffff;
721   pspec->seq_id |= priority << 28;
722   pspec->user_data = binding_set;
723   
724   slist = *slist_p;
725   while (slist)
726     {
727       GtkPatternSpec *tmp_pspec;
728       
729       tmp_pspec = slist->data;
730       slist = slist->next;
731       
732       if (tmp_pspec->pattern_length == pspec->pattern_length &&
733           g_str_equal (tmp_pspec->pattern_reversed, pspec->pattern_reversed))
734         {
735           gtk_pattern_spec_free_segs (pspec);
736           g_free (pspec);
737           pspec = NULL;
738         }
739     }
740   if (pspec)
741     *slist_p = g_slist_prepend (*slist_p, pspec);
742 }
743
744 static inline gboolean
745 binding_match_activate (GSList          *pspec_list,
746                         GtkObject       *object,
747                         guint            path_length,
748                         gchar           *path,
749                         gchar           *path_reversed)
750 {
751   GSList *slist;
752
753   for (slist = pspec_list; slist; slist = slist->next)
754     {
755       GtkPatternSpec *pspec;
756
757       pspec = slist->data;
758       if (gtk_pattern_match (pspec, path_length, path, path_reversed))
759         {
760           GtkBindingSet *binding_set;
761
762           binding_set = pspec->user_data;
763
764           gtk_binding_entry_activate (binding_set->current, object);
765
766           return TRUE;
767         }
768     }
769
770   return FALSE;
771 }
772
773 static gint
774 gtk_binding_pattern_compare (gconstpointer a,
775                              gconstpointer b)
776 {
777   register const GtkPatternSpec *pa  = a;
778   register const GtkPatternSpec *pb  = b;
779
780   return pa->seq_id < pb->seq_id ? -1 : 1;
781 }
782
783 static inline GSList*
784 gtk_binding_entries_sort_patterns (GtkBindingEntry    *entries,
785                                    GtkPathType         path_id)
786 {
787   GSList *patterns;
788
789   patterns = NULL;
790   while (entries)
791     {
792       register GtkBindingSet *binding_set;
793       GSList *slist = NULL;
794
795       binding_set = entries->binding_set;
796       binding_set->current = entries;
797
798       switch (path_id)
799         {
800         case GTK_PATH_WIDGET:
801           slist = binding_set->widget_path_pspecs;
802           break;
803         case GTK_PATH_WIDGET_CLASS:
804           slist = binding_set->widget_class_pspecs;
805           break;
806         case GTK_PATH_CLASS:
807           slist = binding_set->class_branch_pspecs;
808           break;
809         }
810
811       for (; slist; slist = slist->next)
812         {
813           GtkPatternSpec *pspec;
814
815           pspec = slist->data;
816           patterns = g_slist_insert_sorted (patterns, pspec, gtk_binding_pattern_compare);
817         }
818
819       entries = entries->hash_next;
820     }
821
822   return patterns;
823 }
824       
825
826 gboolean
827 gtk_bindings_activate (GtkObject      *object,
828                        guint           keyval,
829                        guint           modifiers)
830 {
831   GtkBindingEntry *entries;
832   GtkWidget *widget;
833   gboolean handled = FALSE;
834
835   g_return_val_if_fail (object != NULL, FALSE);
836   g_return_val_if_fail (GTK_IS_OBJECT (object), FALSE);
837
838   if (!GTK_IS_WIDGET (object) || GTK_OBJECT_DESTROYED (object))
839     return FALSE;
840
841   widget = GTK_WIDGET (object);
842
843   keyval = gdk_keyval_to_lower (keyval);
844   modifiers = modifiers & BINDING_MOD_MASK ();
845
846   entries = binding_ht_lookup_list (keyval, modifiers);
847
848   if (!entries)
849     return FALSE;
850
851   if (!handled)
852     {
853       guint path_length;
854       gchar *path, *path_reversed;
855       GSList *patterns;
856
857       gtk_widget_path (widget, &path_length, &path, &path_reversed);
858       patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_WIDGET);
859       handled = binding_match_activate (patterns, object, path_length, path, path_reversed);
860       g_slist_free (patterns);
861       g_free (path);
862       g_free (path_reversed);
863     }
864
865   if (!handled)
866     {
867       guint path_length;
868       gchar *path, *path_reversed;
869       GSList *patterns;
870
871       gtk_widget_class_path (widget, &path_length, &path, &path_reversed);
872       patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_WIDGET_CLASS);
873       handled = binding_match_activate (patterns, object, path_length, path, path_reversed);
874       g_slist_free (patterns);
875       g_free (path);
876       g_free (path_reversed);
877     }
878
879   if (!handled)
880     {
881       GSList *patterns;
882       GtkType class_type;
883       
884       patterns = gtk_binding_entries_sort_patterns (entries, GTK_PATH_CLASS);
885       class_type = GTK_OBJECT_TYPE (object);
886       while (class_type && !handled)
887         {
888           guint path_length;
889           gchar *path, *path_reversed;
890           
891           path = gtk_type_name (class_type);
892           path_reversed = g_strdup (path);
893           g_strreverse (path_reversed);
894           path_length = strlen (path);
895           handled = binding_match_activate (patterns, object, path_length, path, path_reversed);
896           g_free (path_reversed);
897
898           class_type = gtk_type_parent (class_type);
899         }
900       g_slist_free (patterns);
901     }
902
903   return handled;
904 }
905
906
907
908
909
910
911
912
913
914
915 /* Patterns
916  */
917
918 static inline gboolean
919 gtk_pattern_ph_match (const gchar   *match_pattern,
920                       const gchar   *match_string)
921 {
922   register const gchar *pattern, *string;
923   register gchar ch;
924   
925   pattern = match_pattern;
926   string = match_string;
927   
928   ch = *pattern;
929   pattern++;
930   while (ch)
931     {
932       switch (ch)
933         {
934         case  '?':
935           if (!*string)
936             return FALSE;
937           string++;
938           break;
939           
940         case  '*':
941           do
942             {
943               ch = *pattern;
944               pattern++;
945               if (ch == '?')
946                 {
947                   if (!*string)
948                     return FALSE;
949                   string++;
950                 }
951             }
952           while (ch == '*' || ch == '?');
953           if (!ch)
954             return TRUE;
955           do
956             {
957               while (ch != *string)
958                 {
959                   if (!*string)
960                     return FALSE;
961                   string++;
962                 }
963               string++;
964               if (gtk_pattern_ph_match (pattern, string))
965                 return TRUE;
966             }
967           while (*string);
968           break;
969           
970         default:
971           if (ch == *string)
972             string++;
973           else
974             return FALSE;
975           break;
976         }
977       
978       ch = *pattern;
979       pattern++;
980     }
981   
982   return *string == 0;
983 }
984
985 gboolean
986 gtk_pattern_match (GtkPatternSpec       *pspec,
987                    guint                 string_length,
988                    const gchar          *string,
989                    const gchar          *string_reversed)
990 {
991   g_return_val_if_fail (pspec != NULL, FALSE);
992   g_return_val_if_fail (string != NULL, FALSE);
993   g_return_val_if_fail (string_reversed != NULL, FALSE);
994   
995   switch (pspec->match_type)
996     {
997     case  GTK_MATCH_ALL:
998       return gtk_pattern_ph_match (pspec->pattern, string);
999       
1000     case  GTK_MATCH_ALL_TAIL:
1001       return gtk_pattern_ph_match (pspec->pattern_reversed, string_reversed);
1002       
1003     case  GTK_MATCH_HEAD:
1004       if (pspec->pattern_length > string_length)
1005         return FALSE;
1006       else if (pspec->pattern_length == string_length)
1007         return strcmp (pspec->pattern, string) == 0;
1008       else if (pspec->pattern_length)
1009         return strncmp (pspec->pattern, string, pspec->pattern_length) == 0;
1010       else
1011         return TRUE;
1012       
1013     case  GTK_MATCH_TAIL:
1014       if (pspec->pattern_length > string_length)
1015         return FALSE;
1016       else if (pspec->pattern_length == string_length)
1017         return strcmp (pspec->pattern_reversed, string_reversed) == 0;
1018       else if (pspec->pattern_length)
1019         return strncmp (pspec->pattern_reversed,
1020                         string_reversed,
1021                         pspec->pattern_length) == 0;
1022       else
1023         return TRUE;
1024       
1025     case  GTK_MATCH_EXACT:
1026       if (pspec->pattern_length != string_length)
1027         return FALSE;
1028       else
1029         return strcmp (pspec->pattern_reversed, string_reversed) == 0;
1030       
1031     default:
1032       g_return_val_if_fail (pspec->match_type < GTK_MATCH_LAST, FALSE);
1033       return FALSE;
1034     }
1035 }
1036
1037 void
1038 gtk_pattern_spec_init (GtkPatternSpec         *pspec,
1039                        const gchar            *pattern)
1040 {
1041   gchar *p;
1042   
1043   g_return_if_fail (pspec != NULL);
1044   
1045   pspec->match_type = GTK_MATCH_ALL;
1046   pspec->seq_id = 0;
1047   pspec->user_data = NULL;
1048   
1049   if (!pattern)
1050     pattern = "";
1051   
1052   pspec->pattern = g_strdup (pattern);
1053   pspec->pattern_length = strlen (pspec->pattern);
1054   pspec->pattern_reversed = g_strdup (pspec->pattern);
1055   g_strreverse (pspec->pattern_reversed);
1056   if (pspec->pattern_reversed[0] != '*')
1057     pspec->match_type = GTK_MATCH_ALL_TAIL;
1058   
1059   if (strchr (pspec->pattern, '?'))
1060     return;
1061   
1062   if (!strchr (pspec->pattern, '*'))
1063     {
1064       pspec->match_type = GTK_MATCH_EXACT;
1065       return;
1066     }
1067   
1068   p = pspec->pattern;
1069   while (*p == '*')
1070     p++;
1071   if (p > pspec->pattern &&
1072       !strchr (p, '*'))
1073     {
1074       gchar *t;
1075       
1076       pspec->match_type = GTK_MATCH_TAIL;
1077       t = pspec->pattern;
1078       pspec->pattern = g_strdup (p);
1079       g_free (t);
1080       g_free (pspec->pattern_reversed);
1081       pspec->pattern_reversed = g_strdup (pspec->pattern);
1082       g_strreverse (pspec->pattern_reversed);
1083       pspec->pattern_length = strlen (pspec->pattern);
1084       return;
1085     }
1086   
1087   p = pspec->pattern_reversed;
1088   while (*p == '*')
1089     p++;
1090   if (p > pspec->pattern_reversed &&
1091       !strchr (p, '*'))
1092     {
1093       gchar *t;
1094       
1095       pspec->match_type = GTK_MATCH_HEAD;
1096       t = pspec->pattern_reversed;
1097       pspec->pattern_reversed = g_strdup (p);
1098       g_free (t);
1099       pspec->pattern = g_strdup (pspec->pattern_reversed);
1100       g_strreverse (pspec->pattern);
1101       pspec->pattern_length = strlen (pspec->pattern);
1102     }
1103 }
1104
1105 gboolean
1106 gtk_pattern_match_string (GtkPatternSpec        *pspec,
1107                           const gchar           *string)
1108 {
1109   gchar *string_reversed;
1110   guint length;
1111   gboolean ergo;
1112   
1113   g_return_val_if_fail (pspec != NULL, FALSE);
1114   g_return_val_if_fail (string != NULL, FALSE);
1115   
1116   length = strlen (string);
1117   string_reversed = g_strdup (string);
1118   g_strreverse (string_reversed);
1119   
1120   ergo = gtk_pattern_match (pspec, length, string, string_reversed);
1121   g_free (string_reversed);
1122   
1123   return ergo;
1124 }
1125
1126 gboolean
1127 gtk_pattern_match_simple (const gchar           *pattern,
1128                           const gchar           *string)
1129 {
1130   GtkPatternSpec pspec;
1131   gboolean ergo;
1132   
1133   g_return_val_if_fail (pattern != NULL, FALSE);
1134   g_return_val_if_fail (string != NULL, FALSE);
1135   
1136   gtk_pattern_spec_init (&pspec, pattern);
1137   ergo = gtk_pattern_match_string (&pspec, string);
1138   gtk_pattern_spec_free_segs (&pspec);
1139   
1140   return ergo;
1141 }
1142
1143 void
1144 gtk_pattern_spec_free_segs (GtkPatternSpec      *pspec)
1145 {
1146   g_return_if_fail (pspec != NULL);
1147   
1148   g_free (pspec->pattern);
1149   pspec->pattern = NULL;
1150   g_free (pspec->pattern_reversed);
1151   pspec->pattern_reversed = NULL;
1152 }