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