source: trunk/third/gtk/gtk/gtkbindings.c @ 14482

Revision 14482, 34.2 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r14481, which included commits to RCS files with non-trunk default branches.
Line 
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 --- */
44static GHashTable       *binding_entry_hash_table = NULL;
45static GSList           *binding_set_list = NULL;
46static const gchar      *key_class_binding_set = "gtk-class-binding-set";
47static GQuark            key_id_class_binding_set = 0;
48
49
50/* --- functions --- */
51static GtkBindingSignal*
52binding_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
66static void
67binding_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
81static guint
82binding_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
93static gint
94binding_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
103static GtkBindingEntry*
104binding_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
134static void
135binding_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
158static void
159binding_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
220static GtkBindingEntry*
221binding_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
235static GtkBindingEntry*
236binding_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
257static gboolean
258binding_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
350static void
351gtk_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
425GtkBindingSet*
426gtk_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
445GtkBindingSet*
446gtk_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
471GtkBindingSet*
472gtk_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
489gboolean
490gtk_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
518void
519gtk_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
537void
538gtk_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
554void
555gtk_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
632void
633gtk_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
721void
722gtk_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
780static inline gboolean
781binding_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
809static gint
810gtk_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
823static inline GSList*
824gtk_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
866gboolean
867gtk_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
958static inline gboolean
959gtk_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
1025gboolean
1026gtk_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
1077void
1078gtk_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      g_free (pspec->pattern);
1140      pspec->pattern = g_strdup (pspec->pattern_reversed);
1141      g_strreverse (pspec->pattern);
1142      pspec->pattern_length = strlen (pspec->pattern);
1143    }
1144}
1145
1146gboolean
1147gtk_pattern_match_string (GtkPatternSpec        *pspec,
1148                          const gchar           *string)
1149{
1150  gchar *string_reversed;
1151  guint length;
1152  gboolean ergo;
1153 
1154  g_return_val_if_fail (pspec != NULL, FALSE);
1155  g_return_val_if_fail (string != NULL, FALSE);
1156 
1157  length = strlen (string);
1158  string_reversed = g_strdup (string);
1159  g_strreverse (string_reversed);
1160 
1161  ergo = gtk_pattern_match (pspec, length, string, string_reversed);
1162  g_free (string_reversed);
1163 
1164  return ergo;
1165}
1166
1167gboolean
1168gtk_pattern_match_simple (const gchar           *pattern,
1169                          const gchar           *string)
1170{
1171  GtkPatternSpec pspec;
1172  gboolean ergo;
1173 
1174  g_return_val_if_fail (pattern != NULL, FALSE);
1175  g_return_val_if_fail (string != NULL, FALSE);
1176 
1177  gtk_pattern_spec_init (&pspec, pattern);
1178  ergo = gtk_pattern_match_string (&pspec, string);
1179  gtk_pattern_spec_free_segs (&pspec);
1180 
1181  return ergo;
1182}
1183
1184void
1185gtk_pattern_spec_free_segs (GtkPatternSpec      *pspec)
1186{
1187  g_return_if_fail (pspec != NULL);
1188 
1189  g_free (pspec->pattern);
1190  pspec->pattern = NULL;
1191  g_free (pspec->pattern_reversed);
1192  pspec->pattern_reversed = NULL;
1193}
1194
1195static guint
1196gtk_binding_parse_signal (GScanner       *scanner,
1197                          GtkBindingSet  *binding_set,
1198                          guint           keyval,
1199                          guint           modifiers)
1200{
1201  gchar *signal;
1202  guint expected_token = 0;
1203  GSList *args;
1204  GSList *slist;
1205  gboolean done;
1206  gboolean negate;
1207  gboolean need_arg;
1208  gboolean seen_comma;
1209
1210  g_return_val_if_fail (scanner != NULL, G_TOKEN_ERROR);
1211 
1212  g_scanner_get_next_token (scanner);
1213  if (scanner->token != G_TOKEN_STRING)
1214    return G_TOKEN_STRING;
1215  g_scanner_peek_next_token (scanner);
1216  if (scanner->next_token != '(')
1217    {
1218      g_scanner_get_next_token (scanner);
1219      return '(';
1220    }
1221  signal = g_strdup (scanner->value.v_string);
1222  g_scanner_get_next_token (scanner);
1223
1224  negate = FALSE;
1225  args = NULL;
1226  done = FALSE;
1227  need_arg = TRUE;
1228  seen_comma = FALSE;
1229  scanner->config->scan_symbols = FALSE;
1230  do
1231    {
1232      if (need_arg)
1233        expected_token = G_TOKEN_INT;
1234      else
1235        expected_token = ')';
1236      g_scanner_get_next_token (scanner);
1237      switch (scanner->token)
1238        {
1239          GtkBindingArg *arg;
1240
1241        case G_TOKEN_FLOAT:
1242          if (need_arg)
1243            {
1244              need_arg = FALSE;
1245              arg = g_new (GtkBindingArg, 1);
1246              arg->arg_type = GTK_TYPE_DOUBLE;
1247              arg->d.double_data = scanner->value.v_float;
1248              if (negate)
1249                {
1250                  arg->d.double_data = - arg->d.double_data;
1251                  negate = FALSE;
1252                }
1253              args = g_slist_prepend (args, arg);
1254            }
1255          else
1256            done = TRUE;
1257          break;
1258        case G_TOKEN_INT:
1259          if (need_arg)
1260            {
1261              need_arg = FALSE;
1262              arg = g_new (GtkBindingArg, 1);
1263              arg->arg_type = GTK_TYPE_LONG;
1264              arg->d.long_data = scanner->value.v_int;
1265              if (negate)
1266                {
1267                  arg->d.long_data = - arg->d.long_data;
1268                  negate = FALSE;
1269                }
1270              args = g_slist_prepend (args, arg);
1271            }
1272          else
1273            done = TRUE;
1274          break;
1275        case G_TOKEN_STRING:
1276          if (need_arg && !negate)
1277            {
1278              need_arg = FALSE;
1279              arg = g_new (GtkBindingArg, 1);
1280              arg->arg_type = GTK_TYPE_STRING;
1281              arg->d.string_data = g_strdup (scanner->value.v_string);
1282              args = g_slist_prepend (args, arg);
1283            }
1284          else
1285            done = TRUE;
1286          break;
1287        case G_TOKEN_IDENTIFIER:
1288          if (need_arg && !negate)
1289            {
1290              need_arg = FALSE;
1291              arg = g_new (GtkBindingArg, 1);
1292              arg->arg_type = GTK_TYPE_IDENTIFIER;
1293              arg->d.string_data = g_strdup (scanner->value.v_identifier);
1294              args = g_slist_prepend (args, arg);
1295            }
1296          else
1297            done = TRUE;
1298          break;
1299        case '-':
1300          if (!need_arg)
1301            done = TRUE;
1302          else if (negate)
1303            {
1304              expected_token = G_TOKEN_INT;
1305              done = TRUE;
1306            }
1307          else
1308            negate = TRUE;
1309          break;
1310        case ',':
1311          seen_comma = TRUE;
1312          if (need_arg)
1313            done = TRUE;
1314          else
1315            need_arg = TRUE;
1316          break;
1317        case ')':
1318          if (!(need_arg && seen_comma) && !negate)
1319            {
1320              args = g_slist_reverse (args);
1321              gtk_binding_entry_add_signall (binding_set,
1322                                             keyval,
1323                                             modifiers,
1324                                             signal,
1325                                             args);
1326              expected_token = G_TOKEN_NONE;
1327            }
1328          done = TRUE;
1329          break;
1330        default:
1331          done = TRUE;
1332          break;
1333        }
1334    }
1335  while (!done);
1336  scanner->config->scan_symbols = TRUE;
1337 
1338  for (slist = args; slist; slist = slist->next)
1339    {
1340      GtkBindingArg *arg;
1341
1342      arg = slist->data;
1343      if (GTK_FUNDAMENTAL_TYPE (arg->arg_type) == GTK_TYPE_STRING)
1344        g_free (arg->d.string_data);
1345      g_free (arg);
1346    }
1347  g_slist_free (args);
1348  g_free (signal);
1349
1350  return expected_token;
1351}
1352
1353static inline guint
1354gtk_binding_parse_bind (GScanner       *scanner,
1355                        GtkBindingSet  *binding_set)
1356{
1357  guint keyval = 0;
1358  guint modifiers = 0;
1359
1360  g_return_val_if_fail (scanner != NULL, G_TOKEN_ERROR);
1361 
1362  g_scanner_get_next_token (scanner);
1363  if (scanner->token != GTK_RC_TOKEN_BIND)
1364    return GTK_RC_TOKEN_BIND;
1365  g_scanner_get_next_token (scanner);
1366  if (scanner->token != G_TOKEN_STRING)
1367    return G_TOKEN_STRING;
1368  gtk_accelerator_parse (scanner->value.v_string, &keyval, &modifiers);
1369  modifiers &= BINDING_MOD_MASK ();
1370  if (keyval == 0)
1371    return G_TOKEN_STRING;
1372
1373  g_scanner_get_next_token (scanner);
1374  if (scanner->token != '{')
1375    return '{';
1376
1377  gtk_binding_entry_clear (binding_set, keyval, modifiers);
1378 
1379  g_scanner_peek_next_token (scanner);
1380  while (scanner->next_token != '}')
1381    {
1382      switch (scanner->next_token)
1383        {
1384          guint expected_token;
1385
1386        case G_TOKEN_STRING:
1387          expected_token = gtk_binding_parse_signal (scanner,
1388                                                     binding_set,
1389                                                     keyval,
1390                                                     modifiers);
1391          if (expected_token != G_TOKEN_NONE)
1392            return expected_token;
1393          break;
1394        default:
1395          g_scanner_get_next_token (scanner);
1396          return '}';
1397        }
1398      g_scanner_peek_next_token (scanner);
1399    }
1400  g_scanner_get_next_token (scanner);
1401
1402  return G_TOKEN_NONE;
1403}
1404
1405guint
1406gtk_binding_parse_binding (GScanner       *scanner)
1407{
1408  gchar *name;
1409  GtkBindingSet *binding_set;
1410
1411  g_return_val_if_fail (scanner != NULL, G_TOKEN_ERROR);
1412
1413  g_scanner_get_next_token (scanner);
1414  if (scanner->token != GTK_RC_TOKEN_BINDING)
1415    return GTK_RC_TOKEN_BINDING;
1416  g_scanner_get_next_token (scanner);
1417  if (scanner->token != G_TOKEN_STRING)
1418    return G_TOKEN_STRING;
1419  name = g_strdup (scanner->value.v_string);
1420
1421  g_scanner_get_next_token (scanner);
1422  if (scanner->token != '{')
1423    {
1424      g_free (name);
1425      return G_TOKEN_STRING;
1426    }
1427
1428  binding_set = gtk_binding_set_find (name);
1429  if (!binding_set)
1430    binding_set = gtk_binding_set_new (name);
1431  g_free (name);
1432
1433  g_scanner_peek_next_token (scanner);
1434  while (scanner->next_token != '}')
1435    {
1436      switch (scanner->next_token)
1437        {
1438          guint expected_token;
1439
1440        case GTK_RC_TOKEN_BIND:
1441          expected_token = gtk_binding_parse_bind (scanner, binding_set);
1442          if (expected_token != G_TOKEN_NONE)
1443            return expected_token;
1444          break;
1445        default:
1446          g_scanner_get_next_token (scanner);
1447          return '}';
1448        }
1449      g_scanner_peek_next_token (scanner);
1450    }
1451  g_scanner_get_next_token (scanner);
1452
1453  return G_TOKEN_NONE;
1454}
1455
Note: See TracBrowser for help on using the repository browser.