]> Pileus Git - ~andy/gtk/blob - gtk/gtkaccelgroup.c
051ea107191d218af86bef845ff40caeede58dfb
[~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   g_free (query);
383
384   /* prematurely abort if the group/entry is already locked
385    */
386   if (accel_group->lock_count > 0)
387     return;
388   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
389   if (entry && entry->accel_flags & GTK_ACCEL_LOCKED)
390     return;
391   
392   /* make sure our structures stay alive
393    */
394   gtk_accel_group_ref (accel_group);
395   gtk_object_ref (object);
396   
397   /* remove an existing entry
398    */
399   if (entry)
400     gtk_signal_emit (entry->object, remove_accelerator_signal_id,
401                      accel_group,
402                      gdk_keyval_to_lower (accel_key),
403                      accel_mods & accel_group->modifier_mask);
404   
405   /* abort if the entry still exists
406    */
407   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
408   if (entry)
409     {
410       gtk_accel_group_unref (accel_group);
411       gtk_object_unref (object);
412       
413       return;
414     }
415   
416   /* collect accel groups and remove existing entries
417    */
418   attach_objects = accel_group->attach_objects;
419   groups = NULL;
420   for (attach_objects = accel_group->attach_objects; attach_objects; attach_objects = attach_objects->next)
421     {
422       GSList *tmp_groups;
423       
424       tmp_groups = gtk_object_get_data_by_id (attach_objects->data, accel_groups_key_id);
425       while (tmp_groups)
426         {
427           groups = g_slist_prepend (groups, tmp_groups->data);
428           gtk_accel_group_ref (tmp_groups->data);
429           tmp_groups = tmp_groups->next;
430         }
431     }
432   for (slist = groups; slist; slist = slist->next)
433     {
434       GtkAccelGroup *tmp_group;
435       
436       tmp_group = slist->data;
437       
438       /* we only remove the accelerator if neccessary
439        */
440       if (tmp_group->lock_count == 0)
441         {
442           entry = gtk_accel_group_lookup (tmp_group, accel_key, accel_mods);
443           if (entry && !(entry->accel_flags & GTK_ACCEL_LOCKED))
444             gtk_signal_emit (entry->object, remove_accelerator_signal_id,
445                              tmp_group,
446                              gdk_keyval_to_lower (accel_key),
447                              accel_mods & tmp_group->modifier_mask);
448         }
449       gtk_accel_group_unref (tmp_group);
450     }
451   g_slist_free (groups);
452   
453   /* now install the new accelerator
454    */
455   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
456   if (!entry)
457     gtk_signal_emit (object, add_accelerator_signal_id,
458                      accel_signal_id,
459                      accel_group,
460                      gdk_keyval_to_lower (accel_key),
461                      accel_mods & accel_group->modifier_mask,
462                      accel_flags & GTK_ACCEL_MASK);
463   
464   /* and release the structures again
465    */
466   gtk_accel_group_unref (accel_group);
467   gtk_object_unref (object);
468 }
469
470 static void
471 gtk_accel_group_delete_entries (GtkObject *object)
472 {
473   GSList *free_slist, *slist;
474   
475   gtk_signal_disconnect_by_func (object,
476                                  GTK_SIGNAL_FUNC (gtk_accel_group_delete_entries),
477                                  NULL);
478
479   /* we remove all entries of this object the hard
480    * way (i.e. without signal emission).
481    */
482   free_slist = gtk_object_get_data_by_id (object, accel_entries_key_id);
483   gtk_object_set_data_by_id (object, accel_entries_key_id, NULL);
484   for (slist = free_slist; slist; slist = slist->next)
485     {
486       GtkAccelEntry *entry;
487       
488       entry = slist->data;
489       
490       g_hash_table_remove (accel_entry_hash_table, entry);
491       gtk_accel_group_unref (entry->accel_group);
492       g_chunk_free (entry, accel_entries_mem_chunk);
493     }
494   g_slist_free (free_slist);
495 }
496
497 void
498 gtk_accel_group_handle_add (GtkObject         *object,
499                             guint              accel_signal_id,
500                             GtkAccelGroup     *accel_group,
501                             guint              accel_key,
502                             GdkModifierType    accel_mods,
503                             GtkAccelFlags      accel_flags)
504 {
505   GtkAccelEntry *entry;
506   
507   g_return_if_fail (object != NULL);
508   g_return_if_fail (GTK_IS_OBJECT (object));
509   g_return_if_fail (accel_group != NULL);
510   g_return_if_fail (accel_signal_id > 0);
511
512   if (!gtk_accelerator_valid (accel_key, accel_mods))
513     return;
514   
515   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
516   if (!entry)
517     {
518       GSList *slist;
519       
520       gtk_accel_group_ref (accel_group);
521       
522       entry = g_chunk_new (GtkAccelEntry, accel_entries_mem_chunk);
523       entry->accel_group = accel_group;
524       entry->accelerator_key = gdk_keyval_to_lower (accel_key);
525       entry->accelerator_mods = accel_mods & accel_group->modifier_mask;
526       entry->accel_flags = accel_flags & GTK_ACCEL_MASK;
527       entry->object = object;
528       entry->signal_id = accel_signal_id;
529       
530       g_hash_table_insert (accel_entry_hash_table, entry, entry);
531       
532       slist = gtk_object_get_data_by_id (object, accel_entries_key_id);
533       if (!slist)
534         gtk_signal_connect (object,
535                             "destroy",
536                             GTK_SIGNAL_FUNC (gtk_accel_group_delete_entries),
537                             NULL);
538       slist = g_slist_prepend (slist, entry);
539       gtk_object_set_data_by_id (object, accel_entries_key_id, slist);
540     }
541 }
542
543 void
544 gtk_accel_group_remove (GtkAccelGroup     *accel_group,
545                         guint              accel_key,
546                         GdkModifierType    accel_mods,
547                         GtkObject         *object)
548 {
549   GtkAccelEntry *entry;
550   guint remove_accelerator_signal_id = 0;
551   
552   g_return_if_fail (accel_group != NULL);
553   g_return_if_fail (object != NULL);
554   g_return_if_fail (GTK_IS_OBJECT (object));
555   
556   /* check for required signals in the objects branch
557    */
558   remove_accelerator_signal_id = gtk_signal_lookup ("remove-accelerator", GTK_OBJECT_TYPE (object));
559   if (!remove_accelerator_signal_id)
560     {
561       g_warning ("gtk_accel_group_remove(): could not find signal \"%s\""
562                  "in the `%s' class ancestry",
563                  "remove-accelerator",
564                  gtk_type_name (GTK_OBJECT_TYPE (object)));
565       return;
566     }
567   
568   /* prematurely abort if the entry is locked
569    */
570   if (accel_group->lock_count > 0)
571     return;
572   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
573   if (!entry ||
574       entry->accel_flags & GTK_ACCEL_LOCKED)
575     return;
576   if (entry->object != object)
577     {
578       g_warning ("gtk_accel_group_remove(): invalid object reference for accel-group entry");
579       return;
580     }
581   
582   /* make sure our structures stay alive
583    */
584   gtk_accel_group_ref (accel_group);
585   gtk_object_ref (object);
586   
587   /* remove the entry
588    */
589   gtk_signal_emit (entry->object, remove_accelerator_signal_id,
590                    accel_group,
591                    gdk_keyval_to_lower (accel_key),
592                    accel_mods & accel_group->modifier_mask);
593   
594   /* and release the structures again
595    */
596   gtk_accel_group_unref (accel_group);
597   gtk_object_unref (object);
598 }
599
600 void
601 gtk_accel_group_handle_remove (GtkObject         *object,
602                                GtkAccelGroup     *accel_group,
603                                guint              accel_key,
604                                GdkModifierType    accel_mods)
605 {
606   GtkAccelEntry *entry;
607   
608   g_return_if_fail (object != NULL);
609   g_return_if_fail (GTK_IS_OBJECT (object));
610   g_return_if_fail (accel_group != NULL);
611   
612   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
613   if (entry)
614     {
615       if (entry->object == object)
616         {
617           GSList *slist;
618           
619           g_hash_table_remove (accel_entry_hash_table, entry);
620           
621           slist = gtk_object_get_data_by_id (object, accel_entries_key_id);
622           if (slist)
623             {
624               slist = g_slist_remove (slist, entry);
625               if (!slist)
626                 gtk_signal_disconnect_by_func (object,
627                                                GTK_SIGNAL_FUNC (gtk_accel_group_delete_entries),
628                                                NULL);
629               gtk_object_set_data_by_id (object, accel_entries_key_id, slist);
630               
631               gtk_accel_group_unref (accel_group);
632               
633               g_chunk_free (entry, accel_entries_mem_chunk);
634             }
635         }
636       else
637         g_warning ("gtk_accel_group_handle_remove(): invalid object reference for accel-group entry");
638     }
639   else
640     g_warning ("gtk_accel_group_handle_remove(): attempt to remove unexisting accel-group entry");
641 }
642
643 guint
644 gtk_accel_group_create_add (GtkType          class_type,
645                             GtkSignalRunType signal_flags,
646                             guint            handler_offset)
647 {
648   g_return_val_if_fail (gtk_type_is_a (class_type, GTK_TYPE_OBJECT), 0);
649
650   return gtk_signal_new ("add-accelerator",
651                          signal_flags,
652                          class_type,
653                          handler_offset,
654                          gtk_marshal_NONE__UINT_POINTER_UINT_UINT_ENUM,
655                          GTK_TYPE_NONE, 5,
656                          GTK_TYPE_UINT,
657                          GTK_TYPE_ACCEL_GROUP,
658                          GTK_TYPE_UINT,
659                          GTK_TYPE_GDK_MODIFIER_TYPE,
660                          GTK_TYPE_ACCEL_FLAGS);
661 }
662
663 guint
664 gtk_accel_group_create_remove (GtkType          class_type,
665                                GtkSignalRunType signal_flags,
666                                guint            handler_offset)
667 {
668   g_return_val_if_fail (gtk_type_is_a (class_type, GTK_TYPE_OBJECT), 0);
669
670   return gtk_signal_new ("remove-accelerator",
671                          signal_flags,
672                          class_type,
673                          handler_offset,
674                          gtk_marshal_NONE__POINTER_UINT_UINT,
675                          GTK_TYPE_NONE, 3,
676                          GTK_TYPE_ACCEL_GROUP,
677                          GTK_TYPE_UINT,
678                          GTK_TYPE_GDK_MODIFIER_TYPE);
679 }
680
681 GSList*
682 gtk_accel_groups_from_object (GtkObject      *object)
683 {
684   g_return_val_if_fail (object != NULL, NULL);
685   g_return_val_if_fail (GTK_IS_OBJECT (object), NULL);
686   
687   return gtk_object_get_data_by_id (object, accel_groups_key_id);
688 }
689
690 GSList*
691 gtk_accel_group_entries_from_object (GtkObject       *object)
692 {
693   g_return_val_if_fail (object != NULL, NULL);
694   g_return_val_if_fail (GTK_IS_OBJECT (object), NULL);
695   
696   return gtk_object_get_data_by_id (object, accel_entries_key_id);
697 }
698
699 gboolean
700 gtk_accelerator_valid (guint              keyval,
701                        GdkModifierType    modifiers)
702 {
703   guint invalid_accelerator_vals[] = {
704     GDK_BackSpace, GDK_Delete, GDK_KP_Delete,
705     GDK_Shift_L, GDK_Shift_R, GDK_Shift_Lock, GDK_Caps_Lock, GDK_ISO_Lock,
706     GDK_Control_L, GDK_Control_R, GDK_Meta_L, GDK_Meta_R,
707     GDK_Super_L, GDK_Super_R, GDK_Hyper_L, GDK_Hyper_R,
708     GDK_Mode_switch, GDK_Num_Lock, GDK_Multi_key,
709     GDK_Scroll_Lock, GDK_Sys_Req, 
710     GDK_Up, GDK_Down, GDK_Left, GDK_Right, GDK_Tab, GDK_ISO_Left_Tab,
711     GDK_KP_Up, GDK_KP_Down, GDK_KP_Left, GDK_KP_Right, GDK_KP_Tab,
712     GDK_First_Virtual_Screen, GDK_Prev_Virtual_Screen,
713     GDK_Next_Virtual_Screen, GDK_Last_Virtual_Screen,
714     GDK_Terminate_Server, GDK_AudibleBell_Enable,
715     0
716   };
717   guint *ac_val;
718
719   modifiers &= GDK_MODIFIER_MASK;
720     
721   if (keyval <= 0xFF)
722     return keyval >= 0x20;
723
724   ac_val = invalid_accelerator_vals;
725   while (*ac_val)
726     {
727       if (keyval == *ac_val++)
728         return FALSE;
729     }
730
731   return TRUE;
732 }
733
734 static inline gboolean
735 is_alt (const gchar *string)
736 {
737   return ((string[0] == '<') &&
738           (string[1] == 'a' || string[1] == 'A') &&
739           (string[2] == 'l' || string[2] == 'L') &&
740           (string[3] == 't' || string[3] == 'T') &&
741           (string[4] == '>'));
742 }
743
744 static inline gboolean
745 is_ctl (const gchar *string)
746 {
747   return ((string[0] == '<') &&
748           (string[1] == 'c' || string[1] == 'C') &&
749           (string[2] == 't' || string[2] == 'T') &&
750           (string[3] == 'l' || string[3] == 'L') &&
751           (string[4] == '>'));
752 }
753
754 static inline gboolean
755 is_modx (const gchar *string)
756 {
757   return ((string[0] == '<') &&
758           (string[1] == 'm' || string[1] == 'M') &&
759           (string[2] == 'o' || string[2] == 'O') &&
760           (string[3] == 'd' || string[3] == 'D') &&
761           (string[4] >= '1' && string[4] <= '5') &&
762           (string[5] == '>'));
763 }
764
765 static inline gboolean
766 is_ctrl (const gchar *string)
767 {
768   return ((string[0] == '<') &&
769           (string[1] == 'c' || string[1] == 'C') &&
770           (string[2] == 't' || string[2] == 'T') &&
771           (string[3] == 'r' || string[3] == 'R') &&
772           (string[4] == 'l' || string[4] == 'L') &&
773           (string[5] == '>'));
774 }
775
776 static inline gboolean
777 is_shft (const gchar *string)
778 {
779   return ((string[0] == '<') &&
780           (string[1] == 's' || string[1] == 'S') &&
781           (string[2] == 'h' || string[2] == 'H') &&
782           (string[3] == 'f' || string[3] == 'F') &&
783           (string[4] == 't' || string[4] == 'T') &&
784           (string[5] == '>'));
785 }
786
787 static inline gboolean
788 is_shift (const gchar *string)
789 {
790   return ((string[0] == '<') &&
791           (string[1] == 's' || string[1] == 'S') &&
792           (string[2] == 'h' || string[2] == 'H') &&
793           (string[3] == 'i' || string[3] == 'I') &&
794           (string[4] == 'f' || string[4] == 'F') &&
795           (string[5] == 't' || string[5] == 'T') &&
796           (string[6] == '>'));
797 }
798
799 static inline gboolean
800 is_control (const gchar *string)
801 {
802   return ((string[0] == '<') &&
803           (string[1] == 'c' || string[1] == 'C') &&
804           (string[2] == 'o' || string[2] == 'O') &&
805           (string[3] == 'n' || string[3] == 'N') &&
806           (string[4] == 't' || string[4] == 'T') &&
807           (string[5] == 'r' || string[5] == 'R') &&
808           (string[6] == 'o' || string[6] == 'O') &&
809           (string[7] == 'l' || string[7] == 'L') &&
810           (string[8] == '>'));
811 }
812
813 static inline gboolean
814 is_release (const gchar *string)
815 {
816   return ((string[0] == '<') &&
817           (string[1] == 'r' || string[1] == 'R') &&
818           (string[2] == 'e' || string[2] == 'E') &&
819           (string[3] == 'l' || string[3] == 'L') &&
820           (string[4] == 'e' || string[4] == 'E') &&
821           (string[5] == 'a' || string[5] == 'A') &&
822           (string[6] == 's' || string[6] == 'S') &&
823           (string[7] == 'e' || string[7] == 'E') &&
824           (string[8] == '>'));
825 }
826
827 void
828 gtk_accelerator_parse (const gchar    *accelerator,
829                        guint          *accelerator_key,
830                        GdkModifierType*accelerator_mods)
831 {
832   guint keyval;
833   GdkModifierType mods;
834   gint len;
835   
836   if (accelerator_key)
837     *accelerator_key = 0;
838   if (accelerator_mods)
839     *accelerator_mods = 0;
840   g_return_if_fail (accelerator != NULL);
841   
842   keyval = 0;
843   mods = 0;
844   len = strlen (accelerator);
845   while (len)
846     {
847       if (*accelerator == '<')
848         {
849           if (len >= 9 && is_release (accelerator))
850             {
851               accelerator += 9;
852               len -= 9;
853               mods |= GDK_RELEASE_MASK;
854             }
855           else if (len >= 9 && is_control (accelerator))
856             {
857               accelerator += 9;
858               len -= 9;
859               mods |= GDK_CONTROL_MASK;
860             }
861           else if (len >= 7 && is_shift (accelerator))
862             {
863               accelerator += 7;
864               len -= 7;
865               mods |= GDK_SHIFT_MASK;
866             }
867           else if (len >= 6 && is_shft (accelerator))
868             {
869               accelerator += 6;
870               len -= 6;
871               mods |= GDK_SHIFT_MASK;
872             }
873           else if (len >= 6 && is_ctrl (accelerator))
874             {
875               accelerator += 6;
876               len -= 6;
877               mods |= GDK_CONTROL_MASK;
878             }
879           else if (len >= 6 && is_modx (accelerator))
880             {
881               guint mod_vals[] = {
882                 GDK_MOD1_MASK, GDK_MOD2_MASK, GDK_MOD3_MASK,
883                 GDK_MOD4_MASK, GDK_MOD5_MASK
884               };
885
886               len -= 6;
887               accelerator += 4;
888               mods |= mod_vals[*accelerator - '1'];
889               accelerator += 2;
890             }
891           else if (len >= 5 && is_ctl (accelerator))
892             {
893               accelerator += 5;
894               len -= 5;
895               mods |= GDK_CONTROL_MASK;
896             }
897           else if (len >= 5 && is_alt (accelerator))
898             {
899               accelerator += 5;
900               len -= 5;
901               mods |= GDK_MOD1_MASK;
902             }
903           else
904             {
905               gchar last_ch;
906               
907               last_ch = *accelerator;
908               while (last_ch && last_ch != '>')
909                 {
910                   last_ch = *accelerator;
911                   accelerator += 1;
912                   len -= 1;
913                 }
914             }
915         }
916       else
917         {
918           keyval = gdk_keyval_from_name (accelerator);
919           accelerator += len;
920           len -= len;
921         }
922     }
923   
924   if (accelerator_key)
925     *accelerator_key = gdk_keyval_to_lower (keyval);
926   if (accelerator_mods)
927     *accelerator_mods = mods;
928 }
929
930 gchar*
931 gtk_accelerator_name (guint           accelerator_key,
932                       GdkModifierType accelerator_mods)
933 {
934   static const gchar text_release[] = "<Release>";
935   static const gchar text_shift[] = "<Shift>";
936   static const gchar text_control[] = "<Control>";
937   static const gchar text_mod1[] = "<Alt>";
938   static const gchar text_mod2[] = "<Mod2>";
939   static const gchar text_mod3[] = "<Mod3>";
940   static const gchar text_mod4[] = "<Mod4>";
941   static const gchar text_mod5[] = "<Mod5>";
942   guint l;
943   gchar *keyval_name;
944   gchar *accelerator;
945
946   accelerator_mods &= GDK_MODIFIER_MASK;
947
948   keyval_name = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key));
949   if (!keyval_name)
950     keyval_name = "";
951
952   l = 0;
953   if (accelerator_mods & GDK_RELEASE_MASK)
954     l += sizeof (text_release) - 1;
955   if (accelerator_mods & GDK_SHIFT_MASK)
956     l += sizeof (text_shift) - 1;
957   if (accelerator_mods & GDK_CONTROL_MASK)
958     l += sizeof (text_control) - 1;
959   if (accelerator_mods & GDK_MOD1_MASK)
960     l += sizeof (text_mod1) - 1;
961   if (accelerator_mods & GDK_MOD2_MASK)
962     l += sizeof (text_mod2) - 1;
963   if (accelerator_mods & GDK_MOD3_MASK)
964     l += sizeof (text_mod3) - 1;
965   if (accelerator_mods & GDK_MOD4_MASK)
966     l += sizeof (text_mod4) - 1;
967   if (accelerator_mods & GDK_MOD5_MASK)
968     l += sizeof (text_mod5) - 1;
969   l += strlen (keyval_name);
970
971   accelerator = g_new (gchar, l + 1);
972
973   l = 0;
974   accelerator[l] = 0;
975   if (accelerator_mods & GDK_RELEASE_MASK)
976     {
977       strcpy (accelerator + l, text_release);
978       l += sizeof (text_release) - 1;
979     }
980   if (accelerator_mods & GDK_SHIFT_MASK)
981     {
982       strcpy (accelerator + l, text_shift);
983       l += sizeof (text_shift) - 1;
984     }
985   if (accelerator_mods & GDK_CONTROL_MASK)
986     {
987       strcpy (accelerator + l, text_control);
988       l += sizeof (text_control) - 1;
989     }
990   if (accelerator_mods & GDK_MOD1_MASK)
991     {
992       strcpy (accelerator + l, text_mod1);
993       l += sizeof (text_mod1) - 1;
994     }
995   if (accelerator_mods & GDK_MOD2_MASK)
996     {
997       strcpy (accelerator + l, text_mod2);
998       l += sizeof (text_mod2) - 1;
999     }
1000   if (accelerator_mods & GDK_MOD3_MASK)
1001     {
1002       strcpy (accelerator + l, text_mod3);
1003       l += sizeof (text_mod3) - 1;
1004     }
1005   if (accelerator_mods & GDK_MOD4_MASK)
1006     {
1007       strcpy (accelerator + l, text_mod4);
1008       l += sizeof (text_mod4) - 1;
1009     }
1010   if (accelerator_mods & GDK_MOD5_MASK)
1011     {
1012       strcpy (accelerator + l, text_mod5);
1013       l += sizeof (text_mod5) - 1;
1014     }
1015   strcpy (accelerator + l, keyval_name);
1016
1017   return accelerator;
1018 }
1019
1020 void
1021 gtk_accelerator_set_default_mod_mask (GdkModifierType default_mod_mask)
1022 {
1023   default_accel_mod_mask = default_mod_mask & GDK_MODIFIER_MASK;
1024 }
1025
1026 guint
1027 gtk_accelerator_get_default_mod_mask (void)
1028 {
1029   return default_accel_mod_mask;
1030 }