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