]> Pileus Git - ~andy/gtk/blob - gtk/gtkaccelgroup.c
temporary implementation of action signal "debug_msg"
[~andy/gtk] / gtk / gtkaccelgroup.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * GtkAccelGroup: Accelerator 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 "gtkaccelgroup.h"
25 #include "gdk/gdkkeysyms.h"
26 #include "gtksignal.h"
27 #include "gtkwidget.h"
28
29
30 /* --- signals --- */
31 typedef void (*GtkSignalAddAccelerator)    (GtkObject       *object,
32                                             guint            accel_signal_id,
33                                             GtkAccelGroup   *accel_group,
34                                             guint            accel_key,
35                                             GdkModifierType  accel_mods,
36                                             GtkAccelFlags    accel_flags,
37                                             gpointer         func_data);
38 typedef void (*GtkSignalRemoveAccelerator) (GtkObject       *object,
39                                             GtkAccelGroup   *accel_group,
40                                             guint            accel_key,
41                                             GdkModifierType  accel_mods,
42                                             gpointer         func_data);
43
44 /* --- variables --- */
45 static GtkAccelGroup    *default_accel_group = NULL;
46 static guint             default_accel_mod_mask = (GDK_SHIFT_MASK |
47                                                    GDK_CONTROL_MASK |
48                                                    GDK_MOD1_MASK);
49 static const gchar      *accel_groups_key = "gtk-accel-groups";
50 static guint             accel_groups_key_id = 0;
51 static const gchar      *accel_entries_key = "gtk-accel-entries";
52 static guint             accel_entries_key_id = 0;
53 static GHashTable       *accel_entry_hash_table = NULL;
54 static GMemChunk        *accel_tables_mem_chunk = NULL;
55 static GMemChunk        *accel_entries_mem_chunk = NULL;
56
57
58 /* --- functions --- */
59 static gint
60 gtk_accel_entries_equal (gconstpointer a,
61                          gconstpointer b)
62 {
63   const GtkAccelEntry *e1;
64   const GtkAccelEntry *e2;
65   
66   e1 = a;
67   e2 = b;
68   
69   return ((e1->accel_group == e2->accel_group) &&
70           (e1->accelerator_key == e2->accelerator_key) &&
71           (e1->accelerator_mods == e2->accelerator_mods));
72 }
73
74 static guint
75 gtk_accel_entries_hash (gconstpointer a)
76 {
77   const GtkAccelEntry *e;
78   guint h;
79   
80   e = a;
81   
82   h = (gulong) e->accel_group;
83   h ^= e->accelerator_key << 16;
84   h ^= e->accelerator_key >> 16;
85   h ^= e->accelerator_mods;
86   
87   return h;
88 }
89
90 GtkAccelGroup*
91 gtk_accel_group_new (void)
92 {
93   GtkAccelGroup *accel_group;
94   
95   if (!accel_groups_key_id)
96     {
97       accel_groups_key_id = g_quark_from_static_string (accel_groups_key);
98       accel_entries_key_id = g_quark_from_static_string (accel_entries_key);
99       
100       accel_entry_hash_table = g_hash_table_new (gtk_accel_entries_hash,
101                                                  gtk_accel_entries_equal);
102       
103       accel_tables_mem_chunk = g_mem_chunk_create (GtkAccelGroup, 8, G_ALLOC_AND_FREE);
104       accel_entries_mem_chunk = g_mem_chunk_create (GtkAccelEntry, 64, G_ALLOC_AND_FREE);
105     }
106   
107   accel_group = g_chunk_new (GtkAccelGroup, accel_tables_mem_chunk);
108   
109   accel_group->ref_count = 1;
110   accel_group->lock_count = 0;
111   accel_group->modifier_mask = gtk_accelerator_get_default_mod_mask ();
112   accel_group->attach_objects = NULL;
113   
114   return accel_group;
115 }
116
117 GtkAccelGroup*
118 gtk_accel_group_get_default (void)
119 {
120   if (!default_accel_group)
121     default_accel_group = gtk_accel_group_new ();
122   
123   return default_accel_group;
124 }
125
126 GtkAccelGroup*
127 gtk_accel_group_ref (GtkAccelGroup      *accel_group)
128 {
129   g_return_val_if_fail (accel_group != NULL, NULL);
130   
131   accel_group->ref_count += 1;
132   
133   return accel_group;
134 }
135
136 void
137 gtk_accel_group_unref (GtkAccelGroup  *accel_group)
138 {
139   g_return_if_fail (accel_group != NULL);
140   g_return_if_fail (accel_group->ref_count > 0);
141   
142   accel_group->ref_count -= 1;
143   if (accel_group->ref_count == 0)
144     {
145       g_return_if_fail (accel_group != default_accel_group);
146       g_return_if_fail (accel_group->attach_objects == NULL);
147       
148       g_chunk_free (accel_group, accel_tables_mem_chunk);
149     }
150 }
151
152 static void
153 gtk_accel_group_object_destroy (GtkObject *object)
154 {
155   GSList *free_list, *slist;
156   
157   free_list = gtk_object_get_data_by_id (object, accel_groups_key_id);
158   gtk_object_set_data_by_id (object, accel_groups_key_id, NULL);
159   
160   for (slist = free_list; slist; slist = slist->next)
161     {
162       GtkAccelGroup *accel_group;
163       
164       accel_group = slist->data;
165       accel_group->attach_objects = g_slist_remove (accel_group->attach_objects, object);
166       gtk_accel_group_unref (accel_group);
167     }
168   g_slist_free (free_list);
169 }
170
171 void
172 gtk_accel_group_attach (GtkAccelGroup   *accel_group,
173                         GtkObject       *object)
174 {
175   GSList *slist;
176   
177   g_return_if_fail (accel_group != NULL);
178   g_return_if_fail (object != NULL);
179   g_return_if_fail (GTK_IS_OBJECT (object));
180   g_return_if_fail (g_slist_find (accel_group->attach_objects, object) == NULL);
181   
182   accel_group->attach_objects = g_slist_prepend (accel_group->attach_objects, object);
183   gtk_accel_group_ref (accel_group);
184   slist = gtk_object_get_data_by_id (object, accel_groups_key_id);
185   if (!slist)
186     gtk_signal_connect (object,
187                         "destroy",
188                         GTK_SIGNAL_FUNC (gtk_accel_group_object_destroy),
189                         NULL);
190   slist = g_slist_prepend (slist, accel_group);
191   gtk_object_set_data_by_id (object, accel_groups_key_id, slist);
192 }
193
194 void
195 gtk_accel_group_detach (GtkAccelGroup   *accel_group,
196                         GtkObject       *object)
197 {
198   GSList *slist;
199   
200   g_return_if_fail (accel_group != NULL);
201   g_return_if_fail (object != NULL);
202   g_return_if_fail (GTK_IS_OBJECT (object));
203   g_return_if_fail (g_slist_find (accel_group->attach_objects, object) != NULL);
204   
205   accel_group->attach_objects = g_slist_remove (accel_group->attach_objects, object);
206   gtk_accel_group_unref (accel_group);
207   slist = gtk_object_get_data_by_id (object, accel_groups_key_id);
208   slist = g_slist_remove (slist, accel_group);
209   if (!slist)
210     gtk_signal_disconnect_by_func (object,
211                                    GTK_SIGNAL_FUNC (gtk_accel_group_object_destroy),
212                                    NULL);
213   gtk_object_set_data_by_id (object, accel_groups_key_id, slist);
214 }
215
216 void
217 gtk_accel_group_lock (GtkAccelGroup      *accel_group)
218 {
219   g_return_if_fail (accel_group != NULL);
220   
221   accel_group->lock_count += 1;
222 }
223
224 void
225 gtk_accel_group_unlock (GtkAccelGroup  *accel_group)
226 {
227   g_return_if_fail (accel_group != NULL);
228   
229   if (accel_group->lock_count)
230     accel_group->lock_count -= 1;
231 }
232
233 static GtkAccelEntry*
234 gtk_accel_group_lookup (GtkAccelGroup   *accel_group,
235                         guint            accel_key,
236                         GdkModifierType  accel_mods)
237 {
238   GtkAccelEntry key_entry = { 0 };
239   
240   key_entry.accel_group = accel_group;
241   key_entry.accelerator_key = gdk_keyval_to_lower (accel_key);
242   key_entry.accelerator_mods = accel_mods & accel_group->modifier_mask;
243   
244   return g_hash_table_lookup (accel_entry_hash_table, &key_entry);
245 }
246
247 gboolean
248 gtk_accel_group_activate (GtkAccelGroup  *accel_group,
249                           guint           accel_key,
250                           GdkModifierType accel_mods)
251 {
252   GtkAccelEntry *entry;
253   
254   g_return_val_if_fail (accel_group != NULL, FALSE);
255   
256   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
257   if (entry && entry->signal_id)
258     {
259       gtk_signal_emit (entry->object, entry->signal_id);
260       return TRUE;
261     }
262   return FALSE;
263 }
264
265 gboolean
266 gtk_accel_groups_activate (GtkObject        *object,
267                            guint             accel_key,
268                            GdkModifierType   accel_mods)
269 {
270   g_return_val_if_fail (object != NULL, FALSE);
271   g_return_val_if_fail (GTK_IS_OBJECT (object), FALSE);
272   
273   if (gtk_accelerator_valid (accel_key, accel_mods))
274     {
275       GSList *slist;
276       
277       for (slist = gtk_accel_groups_from_object (object); slist; slist = slist->next)
278         if (gtk_accel_group_activate (slist->data, accel_key, accel_mods))
279           return TRUE;
280       return gtk_accel_group_activate (gtk_accel_group_get_default (), accel_key, accel_mods);
281     }
282   
283   return FALSE;
284 }
285
286 void
287 gtk_accel_group_lock_entry (GtkAccelGroup        *accel_group,
288                             guint                 accel_key,
289                             GdkModifierType       accel_mods)
290 {
291   GtkAccelEntry *entry;
292   
293   g_return_if_fail (accel_group != NULL);
294   
295   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
296   if (entry)
297     entry->accel_flags |= GTK_ACCEL_LOCKED;
298 }
299
300 void
301 gtk_accel_group_unlock_entry (GtkAccelGroup     *accel_group,
302                               guint              accel_key,
303                               GdkModifierType    accel_mods)
304 {
305   GtkAccelEntry *entry;
306   
307   g_return_if_fail (accel_group != NULL);
308   
309   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
310   if (entry)
311     entry->accel_flags &= ~GTK_ACCEL_LOCKED;
312 }
313
314 GtkAccelEntry*
315 gtk_accel_group_get_entry (GtkAccelGroup    *accel_group,
316                            guint             accel_key,
317                            GdkModifierType   accel_mods)
318 {
319   g_return_val_if_fail (accel_group != NULL, 0);
320   
321   return gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
322 }
323
324 void
325 gtk_accel_group_add (GtkAccelGroup      *accel_group,
326                      guint               accel_key,
327                      GdkModifierType     accel_mods,
328                      GtkAccelFlags       accel_flags,
329                      GtkObject          *object,
330                      const gchar        *accel_signal)
331 {
332   guint accel_signal_id = 0;
333   guint add_accelerator_signal_id = 0;
334   guint remove_accelerator_signal_id = 0;
335   gchar *signal;
336   GtkSignalQuery *query;
337   GSList *slist;
338   GSList *groups;
339   GSList *attach_objects;
340   GtkAccelEntry *entry;
341   
342   g_return_if_fail (accel_group != NULL);
343   g_return_if_fail (object != NULL);
344   g_return_if_fail (GTK_IS_OBJECT (object));
345   g_return_if_fail (accel_signal != NULL);
346   
347   /* check for required signals in the objects branch
348    */
349   signal = (gchar*) accel_signal;
350   accel_signal_id = gtk_signal_lookup (signal, GTK_OBJECT_TYPE (object));
351   if (accel_signal_id)
352     {
353       signal = "add-accelerator";
354       add_accelerator_signal_id = gtk_signal_lookup (signal, GTK_OBJECT_TYPE (object));
355     }
356   if (add_accelerator_signal_id)
357     {
358       signal = "remove-accelerator";
359       remove_accelerator_signal_id = gtk_signal_lookup (signal, GTK_OBJECT_TYPE (object));
360     }
361   if (!accel_signal_id ||
362       !add_accelerator_signal_id ||
363       !remove_accelerator_signal_id)
364     {
365       g_warning ("gtk_accel_group_add(): could not find signal \"%s\""
366                  "in the `%s' class ancestry",
367                  signal,
368                  gtk_type_name (GTK_OBJECT_TYPE (object)));
369       return;
370     }
371   query = gtk_signal_query (accel_signal_id);
372   if (!query ||
373       query->nparams > 0)
374     {
375       g_warning ("gtk_accel_group_add(): signal \"%s\" in the `%s' class ancestry"
376                  "cannot be used as accelerator signal",
377                  accel_signal,
378                  gtk_type_name (GTK_OBJECT_TYPE (object)));
379       return;
380     }
381   
382   /* prematurely abort if the group/entry is already locked
383    */
384   if (accel_group->lock_count > 0)
385     return;
386   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
387   if (entry && entry->accel_flags & GTK_ACCEL_LOCKED)
388     return;
389   
390   /* make sure our structures stay alive
391    */
392   gtk_accel_group_ref (accel_group);
393   gtk_object_ref (object);
394   
395   /* remove an existing entry
396    */
397   if (entry)
398     gtk_signal_emit (entry->object, remove_accelerator_signal_id,
399                      accel_group,
400                      gdk_keyval_to_lower (accel_key),
401                      accel_mods & accel_group->modifier_mask);
402   
403   /* abort if the entry still exists
404    */
405   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
406   if (entry)
407     {
408       gtk_accel_group_unref (accel_group);
409       gtk_object_unref (object);
410       
411       return;
412     }
413   
414   /* collect accel groups and remove existing entries
415    */
416   attach_objects = accel_group->attach_objects;
417   groups = NULL;
418   for (attach_objects = accel_group->attach_objects; attach_objects; attach_objects = attach_objects->next)
419     {
420       GSList *tmp_groups;
421       
422       tmp_groups = gtk_object_get_data_by_id (attach_objects->data, accel_groups_key_id);
423       while (tmp_groups)
424         {
425           groups = g_slist_prepend (groups, tmp_groups->data);
426           gtk_accel_group_ref (tmp_groups->data);
427           tmp_groups = tmp_groups->next;
428         }
429     }
430   for (slist = groups; slist; slist = slist->next)
431     {
432       GtkAccelGroup *tmp_group;
433       
434       tmp_group = slist->data;
435       
436       /* we only remove the accelerator if neccessary
437        */
438       if (tmp_group->lock_count == 0)
439         {
440           entry = gtk_accel_group_lookup (tmp_group, accel_key, accel_mods);
441           if (entry && !(entry->accel_flags & GTK_ACCEL_LOCKED))
442             gtk_signal_emit (entry->object, remove_accelerator_signal_id,
443                              tmp_group,
444                              gdk_keyval_to_lower (accel_key),
445                              accel_mods & tmp_group->modifier_mask);
446         }
447       gtk_accel_group_unref (tmp_group);
448     }
449   g_slist_free (groups);
450   
451   /* now install the new accelerator
452    */
453   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
454   if (!entry)
455     gtk_signal_emit (object, add_accelerator_signal_id,
456                      accel_signal_id,
457                      accel_group,
458                      gdk_keyval_to_lower (accel_key),
459                      accel_mods & accel_group->modifier_mask,
460                      accel_flags & GTK_ACCEL_MASK);
461   
462   /* and release the structures again
463    */
464   gtk_accel_group_unref (accel_group);
465   gtk_object_unref (object);
466 }
467
468 static void
469 gtk_accel_group_delete_entries (GtkObject *object)
470 {
471   GSList *free_slist, *slist;
472   
473   gtk_signal_disconnect_by_func (object,
474                                  GTK_SIGNAL_FUNC (gtk_accel_group_delete_entries),
475                                  NULL);
476
477   /* we remove all entries of this object the hard
478    * way (i.e. without signal emission).
479    */
480   free_slist = gtk_object_get_data_by_id (object, accel_entries_key_id);
481   gtk_object_set_data_by_id (object, accel_entries_key_id, NULL);
482   for (slist = free_slist; slist; slist = slist->next)
483     {
484       GtkAccelEntry *entry;
485       
486       entry = slist->data;
487       
488       g_hash_table_remove (accel_entry_hash_table, entry);
489       gtk_accel_group_unref (entry->accel_group);
490       g_chunk_free (entry, accel_entries_mem_chunk);
491     }
492   g_slist_free (free_slist);
493 }
494
495 void
496 gtk_accel_group_handle_add (GtkObject         *object,
497                             guint              accel_signal_id,
498                             GtkAccelGroup     *accel_group,
499                             guint              accel_key,
500                             GdkModifierType    accel_mods,
501                             GtkAccelFlags      accel_flags)
502 {
503   GtkAccelEntry *entry;
504   
505   g_return_if_fail (object != NULL);
506   g_return_if_fail (GTK_IS_OBJECT (object));
507   g_return_if_fail (accel_group != NULL);
508   g_return_if_fail (accel_signal_id > 0);
509
510   if (!gtk_accelerator_valid (accel_key, accel_mods))
511     return;
512   
513   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
514   if (!entry)
515     {
516       GSList *slist;
517       
518       gtk_accel_group_ref (accel_group);
519       
520       entry = g_chunk_new (GtkAccelEntry, accel_entries_mem_chunk);
521       entry->accel_group = accel_group;
522       entry->accelerator_key = gdk_keyval_to_lower (accel_key);
523       entry->accelerator_mods = accel_mods & accel_group->modifier_mask;
524       entry->accel_flags = accel_flags & GTK_ACCEL_MASK;
525       entry->object = object;
526       entry->signal_id = accel_signal_id;
527       
528       g_hash_table_insert (accel_entry_hash_table, entry, entry);
529       
530       slist = gtk_object_get_data_by_id (object, accel_entries_key_id);
531       if (!slist)
532         gtk_signal_connect (object,
533                             "destroy",
534                             GTK_SIGNAL_FUNC (gtk_accel_group_delete_entries),
535                             NULL);
536       slist = g_slist_prepend (slist, entry);
537       gtk_object_set_data_by_id (object, accel_entries_key_id, slist);
538     }
539 }
540
541 void
542 gtk_accel_group_remove (GtkAccelGroup     *accel_group,
543                         guint              accel_key,
544                         GdkModifierType    accel_mods,
545                         GtkObject         *object)
546 {
547   GtkAccelEntry *entry;
548   guint remove_accelerator_signal_id = 0;
549   
550   g_return_if_fail (accel_group != NULL);
551   g_return_if_fail (object != NULL);
552   g_return_if_fail (GTK_IS_OBJECT (object));
553   
554   /* check for required signals in the objects branch
555    */
556   remove_accelerator_signal_id = gtk_signal_lookup ("remove-accelerator", GTK_OBJECT_TYPE (object));
557   if (!remove_accelerator_signal_id)
558     {
559       g_warning ("gtk_accel_group_remove(): could not find signal \"%s\""
560                  "in the `%s' class ancestry",
561                  "remove-accelerator",
562                  gtk_type_name (GTK_OBJECT_TYPE (object)));
563       return;
564     }
565   
566   /* prematurely abort if the entry is locked
567    */
568   if (accel_group->lock_count > 0)
569     return;
570   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
571   if (!entry ||
572       entry->accel_flags & GTK_ACCEL_LOCKED)
573     return;
574   if (entry->object != object)
575     {
576       g_warning ("gtk_accel_group_remove(): invalid object reference for accel-group entry");
577       return;
578     }
579   
580   /* make sure our structures stay alive
581    */
582   gtk_accel_group_ref (accel_group);
583   gtk_object_ref (object);
584   
585   /* remove the entry
586    */
587   gtk_signal_emit (entry->object, remove_accelerator_signal_id,
588                    accel_group,
589                    gdk_keyval_to_lower (accel_key),
590                    accel_mods & accel_group->modifier_mask);
591   
592   /* and release the structures again
593    */
594   gtk_accel_group_unref (accel_group);
595   gtk_object_unref (object);
596 }
597
598 void
599 gtk_accel_group_handle_remove (GtkObject         *object,
600                                GtkAccelGroup     *accel_group,
601                                guint              accel_key,
602                                GdkModifierType    accel_mods)
603 {
604   GtkAccelEntry *entry;
605   
606   g_return_if_fail (object != NULL);
607   g_return_if_fail (GTK_IS_OBJECT (object));
608   g_return_if_fail (accel_group != NULL);
609   
610   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
611   if (entry)
612     {
613       if (entry->object == object)
614         {
615           GSList *slist;
616           
617           g_hash_table_remove (accel_entry_hash_table, entry);
618           
619           slist = gtk_object_get_data_by_id (object, accel_entries_key_id);
620           if (slist)
621             {
622               slist = g_slist_remove (slist, entry);
623               if (!slist)
624                 gtk_signal_disconnect_by_func (object,
625                                                GTK_SIGNAL_FUNC (gtk_accel_group_delete_entries),
626                                                NULL);
627               gtk_object_set_data_by_id (object, accel_entries_key_id, slist);
628               
629               gtk_accel_group_unref (accel_group);
630               
631               g_chunk_free (entry, accel_entries_mem_chunk);
632             }
633         }
634       else
635         g_warning ("gtk_accel_group_handle_remove(): invalid object reference for accel-group entry");
636     }
637   else
638     g_warning ("gtk_accel_group_handle_remove(): attempt to remove unexisting accel-group entry");
639 }
640
641 guint
642 gtk_accel_group_create_add (GtkType          class_type,
643                             GtkSignalRunType signal_flags,
644                             guint            handler_offset)
645 {
646   g_return_val_if_fail (gtk_type_is_a (class_type, GTK_TYPE_OBJECT), 0);
647
648   return gtk_signal_new ("add-accelerator",
649                          signal_flags,
650                          class_type,
651                          handler_offset,
652                          gtk_accel_group_marshal_add,
653                          GTK_TYPE_NONE, 5,
654                          GTK_TYPE_UINT,
655                          GTK_TYPE_ACCEL_GROUP,
656                          GTK_TYPE_UINT,
657                          GTK_TYPE_GDK_MODIFIER_TYPE,
658                          GTK_TYPE_ACCEL_FLAGS);
659 }
660
661 guint
662 gtk_accel_group_create_remove (GtkType          class_type,
663                                GtkSignalRunType signal_flags,
664                                guint            handler_offset)
665 {
666   g_return_val_if_fail (gtk_type_is_a (class_type, GTK_TYPE_OBJECT), 0);
667
668   return gtk_signal_new ("remove-accelerator",
669                          signal_flags,
670                          class_type,
671                          handler_offset,
672                          gtk_accel_group_marshal_remove,
673                          GTK_TYPE_NONE, 3,
674                          GTK_TYPE_ACCEL_GROUP,
675                          GTK_TYPE_UINT,
676                          GTK_TYPE_GDK_MODIFIER_TYPE);
677 }
678
679 void
680 gtk_accel_group_marshal_add (GtkObject      *object,
681                              GtkSignalFunc   func,
682                              gpointer        func_data,
683                              GtkArg         *args)
684 {
685   GtkSignalAddAccelerator signal_func;
686   
687   signal_func = (GtkSignalAddAccelerator) func;
688   
689   signal_func (object,
690                GTK_VALUE_UINT (args[0]),
691                GTK_VALUE_BOXED (args[1]),
692                GTK_VALUE_UINT (args[2]),
693                GTK_VALUE_UINT (args[3]),
694                GTK_VALUE_ENUM (args[4]),
695                func_data);
696 }
697
698 void
699 gtk_accel_group_marshal_remove (GtkObject       *object,
700                                 GtkSignalFunc    func,
701                                 gpointer         func_data,
702                                 GtkArg          *args)
703 {
704   GtkSignalRemoveAccelerator signal_func;
705   
706   signal_func = (GtkSignalRemoveAccelerator) func;
707   
708   signal_func (object,
709                GTK_VALUE_BOXED (args[0]),
710                GTK_VALUE_UINT (args[1]),
711                GTK_VALUE_UINT (args[2]),
712                func_data);
713 }
714
715 GSList*
716 gtk_accel_groups_from_object (GtkObject      *object)
717 {
718   g_return_val_if_fail (object != NULL, NULL);
719   g_return_val_if_fail (GTK_IS_OBJECT (object), NULL);
720   
721   return gtk_object_get_data_by_id (object, accel_groups_key_id);
722 }
723
724 GSList*
725 gtk_accel_group_entries_from_object (GtkObject       *object)
726 {
727   g_return_val_if_fail (object != NULL, NULL);
728   g_return_val_if_fail (GTK_IS_OBJECT (object), NULL);
729   
730   return gtk_object_get_data_by_id (object, accel_entries_key_id);
731 }
732
733 gboolean
734 gtk_accelerator_valid (guint              keyval,
735                        guint              modifiers)
736 {
737   guint invalid_accelerator_vals[] = {
738     GDK_BackSpace, GDK_Delete, GDK_KP_Delete,
739     GDK_Shift_L, GDK_Shift_R, GDK_Shift_Lock, GDK_Caps_Lock, GDK_ISO_Lock,
740     GDK_Control_L, GDK_Control_R, GDK_Meta_L, GDK_Meta_R,
741     GDK_Super_L, GDK_Super_R, GDK_Hyper_L, GDK_Hyper_R,
742     GDK_Mode_switch, GDK_Num_Lock, GDK_Multi_key,
743     GDK_Scroll_Lock, GDK_Sys_Req, 
744     GDK_Up, GDK_Down, GDK_Left, GDK_Right, GDK_Tab, GDK_ISO_Left_Tab,
745     GDK_KP_Up, GDK_KP_Down, GDK_KP_Left, GDK_KP_Right, GDK_KP_Tab,
746     GDK_First_Virtual_Screen, GDK_Prev_Virtual_Screen,
747     GDK_Next_Virtual_Screen, GDK_Last_Virtual_Screen,
748     GDK_Terminate_Server, GDK_AudibleBell_Enable,
749     0
750   };
751   guint *ac_val;
752
753   modifiers &= GDK_MODIFIER_MASK;
754     
755   if (keyval <= 0xFF)
756     return keyval >= 0x20;
757
758   ac_val = invalid_accelerator_vals;
759   while (*ac_val)
760     {
761       if (keyval == *ac_val++)
762         return FALSE;
763     }
764
765   return TRUE;
766 }
767
768 static inline gboolean
769 is_alt (const gchar *string)
770 {
771   return ((string[0] == '<') &&
772           (string[1] == 'a' || string[1] == 'A') &&
773           (string[2] == 'l' || string[2] == 'L') &&
774           (string[3] == 't' || string[3] == 'T') &&
775           (string[4] == '>'));
776 }
777
778 static inline gboolean
779 is_ctl (const gchar *string)
780 {
781   return ((string[0] == '<') &&
782           (string[1] == 'c' || string[1] == 'C') &&
783           (string[2] == 't' || string[2] == 'T') &&
784           (string[3] == 'l' || string[3] == 'L') &&
785           (string[4] == '>'));
786 }
787
788 static inline gboolean
789 is_modx (const gchar *string)
790 {
791   return ((string[0] == '<') &&
792           (string[1] == 'm' || string[1] == 'M') &&
793           (string[2] == 'o' || string[2] == 'O') &&
794           (string[3] == 'd' || string[3] == 'D') &&
795           (string[4] >= '1' && string[4] <= '5') &&
796           (string[5] == '>'));
797 }
798
799 static inline gboolean
800 is_ctrl (const gchar *string)
801 {
802   return ((string[0] == '<') &&
803           (string[1] == 'c' || string[1] == 'C') &&
804           (string[2] == 't' || string[2] == 'T') &&
805           (string[3] == 'r' || string[3] == 'R') &&
806           (string[4] == 'l' || string[4] == 'L') &&
807           (string[5] == '>'));
808 }
809
810 static inline gboolean
811 is_shft (const gchar *string)
812 {
813   return ((string[0] == '<') &&
814           (string[1] == 's' || string[1] == 'S') &&
815           (string[2] == 'h' || string[2] == 'H') &&
816           (string[3] == 'f' || string[3] == 'F') &&
817           (string[4] == 't' || string[4] == 'T') &&
818           (string[5] == '>'));
819 }
820
821 static inline gboolean
822 is_shift (const gchar *string)
823 {
824   return ((string[0] == '<') &&
825           (string[1] == 's' || string[1] == 'S') &&
826           (string[2] == 'h' || string[2] == 'H') &&
827           (string[3] == 'i' || string[3] == 'I') &&
828           (string[4] == 'f' || string[4] == 'F') &&
829           (string[5] == 't' || string[5] == 'T') &&
830           (string[6] == '>'));
831 }
832
833 static inline gboolean
834 is_control (const gchar *string)
835 {
836   return ((string[0] == '<') &&
837           (string[1] == 'c' || string[1] == 'C') &&
838           (string[2] == 'o' || string[2] == 'O') &&
839           (string[3] == 'n' || string[3] == 'N') &&
840           (string[4] == 't' || string[4] == 'T') &&
841           (string[5] == 'r' || string[5] == 'R') &&
842           (string[6] == 'o' || string[6] == 'O') &&
843           (string[7] == 'l' || string[7] == 'L') &&
844           (string[8] == '>'));
845 }
846
847 static inline gboolean
848 is_release (const gchar *string)
849 {
850   return ((string[0] == '<') &&
851           (string[1] == 'r' || string[1] == 'R') &&
852           (string[2] == 'e' || string[2] == 'E') &&
853           (string[3] == 'l' || string[3] == 'L') &&
854           (string[4] == 'e' || string[4] == 'E') &&
855           (string[5] == 'a' || string[5] == 'A') &&
856           (string[6] == 's' || string[6] == 'S') &&
857           (string[7] == 'e' || string[7] == 'E') &&
858           (string[8] == '>'));
859 }
860
861 void
862 gtk_accelerator_parse (const gchar    *accelerator,
863                        guint          *accelerator_key,
864                        GdkModifierType*accelerator_mods)
865 {
866   guint keyval;
867   GdkModifierType mods;
868   gint len;
869   
870   if (accelerator_key)
871     *accelerator_key = 0;
872   if (accelerator_mods)
873     *accelerator_mods = 0;
874   g_return_if_fail (accelerator != NULL);
875   
876   keyval = 0;
877   mods = 0;
878   len = strlen (accelerator);
879   while (len)
880     {
881       if (*accelerator == '<')
882         {
883           if (len >= 9 && is_release (accelerator))
884             {
885               accelerator += 9;
886               len -= 9;
887               mods |= GDK_RELEASE_MASK;
888             }
889           else if (len >= 9 && is_control (accelerator))
890             {
891               accelerator += 9;
892               len -= 9;
893               mods |= GDK_CONTROL_MASK;
894             }
895           else if (len >= 7 && is_shift (accelerator))
896             {
897               accelerator += 7;
898               len -= 7;
899               mods |= GDK_SHIFT_MASK;
900             }
901           else if (len >= 6 && is_shft (accelerator))
902             {
903               accelerator += 6;
904               len -= 6;
905               mods |= GDK_SHIFT_MASK;
906             }
907           else if (len >= 6 && is_ctrl (accelerator))
908             {
909               accelerator += 6;
910               len -= 6;
911               mods |= GDK_CONTROL_MASK;
912             }
913           else if (len >= 6 && is_modx (accelerator))
914             {
915               guint mod_vals[] = {
916                 GDK_MOD1_MASK, GDK_MOD2_MASK, GDK_MOD3_MASK,
917                 GDK_MOD4_MASK, GDK_MOD5_MASK
918               };
919
920               len -= 6;
921               accelerator += 4;
922               mods |= mod_vals[*accelerator - '1'];
923               accelerator += 2;
924             }
925           else if (len >= 5 && is_ctl (accelerator))
926             {
927               accelerator += 5;
928               len -= 5;
929               mods |= GDK_CONTROL_MASK;
930             }
931           else if (len >= 5 && is_alt (accelerator))
932             {
933               accelerator += 5;
934               len -= 5;
935               mods |= GDK_MOD1_MASK;
936             }
937           else
938             {
939               gchar last_ch;
940               
941               last_ch = *accelerator;
942               while (last_ch && last_ch != '>')
943                 {
944                   last_ch = *accelerator;
945                   accelerator += 1;
946                   len -= 1;
947                 }
948             }
949         }
950       else
951         {
952           keyval = gdk_keyval_from_name (accelerator);
953           accelerator += len;
954           len -= len;
955         }
956     }
957   
958   if (accelerator_key)
959     *accelerator_key = gdk_keyval_to_lower (keyval);
960   if (accelerator_mods)
961     *accelerator_mods = mods;
962 }
963
964 gchar*
965 gtk_accelerator_name (guint           accelerator_key,
966                       GdkModifierType accelerator_mods)
967 {
968   static const gchar text_release[] = "<Release>";
969   static const gchar text_shift[] = "<Shift>";
970   static const gchar text_control[] = "<Control>";
971   static const gchar text_mod1[] = "<Alt>";
972   static const gchar text_mod2[] = "<Mod2>";
973   static const gchar text_mod3[] = "<Mod3>";
974   static const gchar text_mod4[] = "<Mod4>";
975   static const gchar text_mod5[] = "<Mod5>";
976   guint l;
977   gchar *keyval_name;
978   gchar *accelerator;
979
980   accelerator_mods &= GDK_MODIFIER_MASK;
981
982   keyval_name = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key));
983   if (!keyval_name)
984     keyval_name = "";
985
986   l = 0;
987   if (accelerator_mods & GDK_RELEASE_MASK)
988     l += sizeof (text_release) - 1;
989   if (accelerator_mods & GDK_SHIFT_MASK)
990     l += sizeof (text_shift) - 1;
991   if (accelerator_mods & GDK_CONTROL_MASK)
992     l += sizeof (text_control) - 1;
993   if (accelerator_mods & GDK_MOD1_MASK)
994     l += sizeof (text_mod1) - 1;
995   if (accelerator_mods & GDK_MOD2_MASK)
996     l += sizeof (text_mod2) - 1;
997   if (accelerator_mods & GDK_MOD3_MASK)
998     l += sizeof (text_mod3) - 1;
999   if (accelerator_mods & GDK_MOD4_MASK)
1000     l += sizeof (text_mod4) - 1;
1001   if (accelerator_mods & GDK_MOD5_MASK)
1002     l += sizeof (text_mod5) - 1;
1003   l += strlen (keyval_name);
1004
1005   accelerator = g_new (gchar, l + 1);
1006
1007   l = 0;
1008   accelerator[l] = 0;
1009   if (accelerator_mods & GDK_RELEASE_MASK)
1010     {
1011       strcpy (accelerator + l, text_release);
1012       l += sizeof (text_release) - 1;
1013     }
1014   if (accelerator_mods & GDK_SHIFT_MASK)
1015     {
1016       strcpy (accelerator + l, text_shift);
1017       l += sizeof (text_shift) - 1;
1018     }
1019   if (accelerator_mods & GDK_CONTROL_MASK)
1020     {
1021       strcpy (accelerator + l, text_control);
1022       l += sizeof (text_control) - 1;
1023     }
1024   if (accelerator_mods & GDK_MOD1_MASK)
1025     {
1026       strcpy (accelerator + l, text_mod1);
1027       l += sizeof (text_mod1) - 1;
1028     }
1029   if (accelerator_mods & GDK_MOD2_MASK)
1030     {
1031       strcpy (accelerator + l, text_mod2);
1032       l += sizeof (text_mod2) - 1;
1033     }
1034   if (accelerator_mods & GDK_MOD3_MASK)
1035     {
1036       strcpy (accelerator + l, text_mod3);
1037       l += sizeof (text_mod3) - 1;
1038     }
1039   if (accelerator_mods & GDK_MOD4_MASK)
1040     {
1041       strcpy (accelerator + l, text_mod4);
1042       l += sizeof (text_mod4) - 1;
1043     }
1044   if (accelerator_mods & GDK_MOD5_MASK)
1045     {
1046       strcpy (accelerator + l, text_mod5);
1047       l += sizeof (text_mod5) - 1;
1048     }
1049   strcpy (accelerator + l, keyval_name);
1050
1051   return accelerator;
1052 }
1053
1054 void
1055 gtk_accelerator_set_default_mod_mask (guint           default_mod_mask)
1056 {
1057   default_accel_mod_mask = default_mod_mask & GDK_MODIFIER_MASK;
1058 }
1059
1060 guint
1061 gtk_accelerator_get_default_mod_mask (void)
1062 {
1063   return default_accel_mod_mask;
1064 }