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