]> Pileus Git - ~andy/gtk/blob - gtk/gtkaccelgroup.c
3d1d7860179b358ed050d34939496bfc4f66b379
[~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 gboolean
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 (GTK_IS_OBJECT (object));
187   g_return_if_fail (g_slist_find (accel_group->attach_objects, object) == NULL);
188   
189   accel_group->attach_objects = g_slist_prepend (accel_group->attach_objects, object);
190   gtk_accel_group_ref (accel_group);
191   slist = gtk_object_get_data_by_id (object, accel_groups_key_id);
192   if (!slist)
193     gtk_signal_connect (object,
194                         "destroy",
195                         GTK_SIGNAL_FUNC (gtk_accel_group_object_destroy),
196                         NULL);
197   slist = g_slist_prepend (slist, accel_group);
198   gtk_object_set_data_by_id (object, accel_groups_key_id, slist);
199 }
200
201 void
202 gtk_accel_group_detach (GtkAccelGroup   *accel_group,
203                         GtkObject       *object)
204 {
205   GSList *slist;
206   
207   g_return_if_fail (accel_group != NULL);
208   g_return_if_fail (GTK_IS_OBJECT (object));
209   g_return_if_fail (g_slist_find (accel_group->attach_objects, object) != NULL);
210   
211   accel_group->attach_objects = g_slist_remove (accel_group->attach_objects, object);
212   gtk_accel_group_unref (accel_group);
213   slist = gtk_object_get_data_by_id (object, accel_groups_key_id);
214   slist = g_slist_remove (slist, accel_group);
215   if (!slist)
216     gtk_signal_disconnect_by_func (object,
217                                    GTK_SIGNAL_FUNC (gtk_accel_group_object_destroy),
218                                    NULL);
219   gtk_object_set_data_by_id (object, accel_groups_key_id, slist);
220 }
221
222 void
223 gtk_accel_group_lock (GtkAccelGroup      *accel_group)
224 {
225   g_return_if_fail (accel_group != NULL);
226   
227   accel_group->lock_count += 1;
228 }
229
230 void
231 gtk_accel_group_unlock (GtkAccelGroup  *accel_group)
232 {
233   g_return_if_fail (accel_group != NULL);
234   
235   if (accel_group->lock_count)
236     accel_group->lock_count -= 1;
237 }
238
239 static GtkAccelEntry*
240 gtk_accel_group_lookup (GtkAccelGroup   *accel_group,
241                         guint            accel_key,
242                         GdkModifierType  accel_mods)
243 {
244   GtkAccelEntry key_entry = { 0 };
245   
246   key_entry.accel_group = accel_group;
247   key_entry.accelerator_key = gdk_keyval_to_lower (accel_key);
248   key_entry.accelerator_mods = accel_mods & accel_group->modifier_mask;
249   
250   return g_hash_table_lookup (accel_entry_hash_table, &key_entry);
251 }
252
253 gboolean
254 gtk_accel_group_activate (GtkAccelGroup  *accel_group,
255                           guint           accel_key,
256                           GdkModifierType accel_mods)
257 {
258   GtkAccelEntry *entry;
259   
260   g_return_val_if_fail (accel_group != NULL, FALSE);
261   
262   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
263   if (entry && entry->signal_id &&
264       (!GTK_IS_WIDGET (entry->object) || GTK_WIDGET_IS_SENSITIVE (entry->object)))
265     {
266       gtk_signal_emit (entry->object, entry->signal_id);
267       return TRUE;
268     }
269   return FALSE;
270 }
271
272 gboolean
273 gtk_accel_groups_activate (GtkObject        *object,
274                            guint             accel_key,
275                            GdkModifierType   accel_mods)
276 {
277   g_return_val_if_fail (GTK_IS_OBJECT (object), FALSE);
278   
279   if (gtk_accelerator_valid (accel_key, accel_mods))
280     {
281       GSList *slist;
282       
283       for (slist = gtk_accel_groups_from_object (object); slist; slist = slist->next)
284         if (gtk_accel_group_activate (slist->data, accel_key, accel_mods))
285           return TRUE;
286       return gtk_accel_group_activate (gtk_accel_group_get_default (), accel_key, accel_mods);
287     }
288   
289   return FALSE;
290 }
291
292 void
293 gtk_accel_group_lock_entry (GtkAccelGroup        *accel_group,
294                             guint                 accel_key,
295                             GdkModifierType       accel_mods)
296 {
297   GtkAccelEntry *entry;
298   
299   g_return_if_fail (accel_group != NULL);
300   
301   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
302   if (entry)
303     entry->accel_flags |= GTK_ACCEL_LOCKED;
304 }
305
306 void
307 gtk_accel_group_unlock_entry (GtkAccelGroup     *accel_group,
308                               guint              accel_key,
309                               GdkModifierType    accel_mods)
310 {
311   GtkAccelEntry *entry;
312   
313   g_return_if_fail (accel_group != NULL);
314   
315   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
316   if (entry)
317     entry->accel_flags &= ~GTK_ACCEL_LOCKED;
318 }
319
320 GtkAccelEntry*
321 gtk_accel_group_get_entry (GtkAccelGroup    *accel_group,
322                            guint             accel_key,
323                            GdkModifierType   accel_mods)
324 {
325   g_return_val_if_fail (accel_group != NULL, 0);
326   
327   return gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
328 }
329
330 void
331 gtk_accel_group_add (GtkAccelGroup      *accel_group,
332                      guint               accel_key,
333                      GdkModifierType     accel_mods,
334                      GtkAccelFlags       accel_flags,
335                      GtkObject          *object,
336                      const gchar        *accel_signal)
337 {
338   guint accel_signal_id = 0;
339   guint add_accelerator_signal_id = 0;
340   guint remove_accelerator_signal_id = 0;
341   gchar *signal;
342   GSignalQuery query;
343   GSList *slist;
344   GSList *groups;
345   GSList *attach_objects;
346   GtkAccelEntry *entry;
347   
348   g_return_if_fail (accel_group != NULL);
349   g_return_if_fail (GTK_IS_OBJECT (object));
350   g_return_if_fail (accel_signal != NULL);
351   
352   /* check for required signals in the objects branch
353    */
354   signal = (gchar*) accel_signal;
355   accel_signal_id = gtk_signal_lookup (signal, GTK_OBJECT_TYPE (object));
356   if (accel_signal_id)
357     {
358       signal = "add-accelerator";
359       add_accelerator_signal_id = gtk_signal_lookup (signal, GTK_OBJECT_TYPE (object));
360     }
361   if (add_accelerator_signal_id)
362     {
363       signal = "remove-accelerator";
364       remove_accelerator_signal_id = gtk_signal_lookup (signal, GTK_OBJECT_TYPE (object));
365     }
366   if (!accel_signal_id ||
367       !add_accelerator_signal_id ||
368       !remove_accelerator_signal_id)
369     {
370       g_warning ("gtk_accel_group_add(): could not find signal \"%s\""
371                  "in the `%s' class ancestry",
372                  signal,
373                  gtk_type_name (GTK_OBJECT_TYPE (object)));
374       return;
375     }
376   g_signal_query (accel_signal_id, &query);
377   if (!query.signal_id || query.n_params > 0)
378     {
379       g_warning ("gtk_accel_group_add(): signal \"%s\" in the `%s' class ancestry"
380                  "cannot be used as accelerator signal %s",
381                  accel_signal,
382                  gtk_type_name (GTK_OBJECT_TYPE (object)),
383                  query.n_params > 0 ? "(extraneous parameters are not supported)" : "");
384       return;
385     }
386
387   /* prematurely abort if the group/entry is already locked
388    */
389   if (accel_group->lock_count > 0)
390     return;
391   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
392   if (entry && entry->accel_flags & GTK_ACCEL_LOCKED)
393     return;
394   
395   /* make sure our structures stay alive
396    */
397   gtk_accel_group_ref (accel_group);
398   gtk_object_ref (object);
399   
400   /* remove an existing entry
401    */
402   if (entry)
403     gtk_signal_emit (entry->object, remove_accelerator_signal_id,
404                      accel_group,
405                      gdk_keyval_to_lower (accel_key),
406                      accel_mods & accel_group->modifier_mask);
407   
408   /* abort if the entry still exists
409    */
410   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
411   if (entry)
412     {
413       gtk_accel_group_unref (accel_group);
414       gtk_object_unref (object);
415       
416       return;
417     }
418   
419   /* collect accel groups and remove existing entries
420    */
421   attach_objects = accel_group->attach_objects;
422   groups = NULL;
423   for (attach_objects = accel_group->attach_objects; attach_objects; attach_objects = attach_objects->next)
424     {
425       GSList *tmp_groups;
426       
427       tmp_groups = gtk_object_get_data_by_id (attach_objects->data, accel_groups_key_id);
428       while (tmp_groups)
429         {
430           groups = g_slist_prepend (groups, tmp_groups->data);
431           gtk_accel_group_ref (tmp_groups->data);
432           tmp_groups = tmp_groups->next;
433         }
434     }
435   for (slist = groups; slist; slist = slist->next)
436     {
437       GtkAccelGroup *tmp_group;
438       
439       tmp_group = slist->data;
440       
441       /* we only remove the accelerator if neccessary
442        */
443       if (tmp_group->lock_count == 0)
444         {
445           entry = gtk_accel_group_lookup (tmp_group, accel_key, accel_mods);
446           if (entry && !(entry->accel_flags & GTK_ACCEL_LOCKED))
447             gtk_signal_emit (entry->object, remove_accelerator_signal_id,
448                              tmp_group,
449                              gdk_keyval_to_lower (accel_key),
450                              accel_mods & tmp_group->modifier_mask);
451         }
452       gtk_accel_group_unref (tmp_group);
453     }
454   g_slist_free (groups);
455   
456   /* now install the new accelerator
457    */
458   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
459   if (!entry)
460     gtk_signal_emit (object, add_accelerator_signal_id,
461                      accel_signal_id,
462                      accel_group,
463                      gdk_keyval_to_lower (accel_key),
464                      accel_mods & accel_group->modifier_mask,
465                      accel_flags & GTK_ACCEL_MASK);
466   
467   /* and release the structures again
468    */
469   gtk_accel_group_unref (accel_group);
470   gtk_object_unref (object);
471 }
472
473 static void
474 gtk_accel_group_delete_entries (GtkObject *object)
475 {
476   GSList *free_slist, *slist;
477   
478   gtk_signal_disconnect_by_func (object,
479                                  GTK_SIGNAL_FUNC (gtk_accel_group_delete_entries),
480                                  NULL);
481
482   /* we remove all entries of this object the hard
483    * way (i.e. without signal emission).
484    */
485   free_slist = gtk_object_get_data_by_id (object, accel_entries_key_id);
486   gtk_object_set_data_by_id (object, accel_entries_key_id, NULL);
487   for (slist = free_slist; slist; slist = slist->next)
488     {
489       GtkAccelEntry *entry;
490       
491       entry = slist->data;
492       
493       g_hash_table_remove (accel_entry_hash_table, entry);
494       gtk_accel_group_unref (entry->accel_group);
495       g_chunk_free (entry, accel_entries_mem_chunk);
496     }
497   g_slist_free (free_slist);
498 }
499
500 void
501 gtk_accel_group_handle_add (GtkObject         *object,
502                             guint              accel_signal_id,
503                             GtkAccelGroup     *accel_group,
504                             guint              accel_key,
505                             GdkModifierType    accel_mods,
506                             GtkAccelFlags      accel_flags)
507 {
508   GtkAccelEntry *entry;
509   
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 (GTK_IS_OBJECT (object));
556   
557   /* check for required signals in the objects branch
558    */
559   remove_accelerator_signal_id = gtk_signal_lookup ("remove-accelerator", GTK_OBJECT_TYPE (object));
560   if (!remove_accelerator_signal_id)
561     {
562       g_warning ("gtk_accel_group_remove(): could not find signal \"%s\""
563                  "in the `%s' class ancestry",
564                  "remove-accelerator",
565                  gtk_type_name (GTK_OBJECT_TYPE (object)));
566       return;
567     }
568   
569   /* prematurely abort if the entry is locked
570    */
571   if (accel_group->lock_count > 0)
572     return;
573   entry = gtk_accel_group_lookup (accel_group, accel_key, accel_mods);
574   if (!entry ||
575       entry->accel_flags & GTK_ACCEL_LOCKED)
576     return;
577   if (entry->object != object)
578     {
579       g_warning ("gtk_accel_group_remove(): invalid object reference for accel-group entry");
580       return;
581     }
582   
583   /* make sure our structures stay alive
584    */
585   gtk_accel_group_ref (accel_group);
586   gtk_object_ref (object);
587   
588   /* remove the entry
589    */
590   gtk_signal_emit (entry->object, remove_accelerator_signal_id,
591                    accel_group,
592                    gdk_keyval_to_lower (accel_key),
593                    accel_mods & accel_group->modifier_mask);
594   
595   /* and release the structures again
596    */
597   gtk_accel_group_unref (accel_group);
598   gtk_object_unref (object);
599 }
600
601 void
602 gtk_accel_group_handle_remove (GtkObject         *object,
603                                GtkAccelGroup     *accel_group,
604                                guint              accel_key,
605                                GdkModifierType    accel_mods)
606 {
607   GtkAccelEntry *entry;
608   
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_OBJECT (class_type), 0);
649
650   return gtk_signal_new ("add-accelerator",
651                          signal_flags,
652                          class_type,
653                          handler_offset,
654                          gtk_marshal_VOID__UINT_BOXED_UINT_FLAGS_FLAGS,
655                          GTK_TYPE_NONE, 5,
656                          GTK_TYPE_UINT,
657                          GTK_TYPE_ACCEL_GROUP,
658                          GTK_TYPE_UINT,
659                          GDK_TYPE_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_OBJECT (class_type), 0);
669
670   return gtk_signal_new ("remove-accelerator",
671                          signal_flags,
672                          class_type,
673                          handler_offset,
674                          gtk_marshal_VOID__BOXED_UINT_FLAGS,
675                          GTK_TYPE_NONE, 3,
676                          GTK_TYPE_ACCEL_GROUP,
677                          GTK_TYPE_UINT,
678                          GDK_TYPE_MODIFIER_TYPE);
679 }
680
681 GSList*
682 gtk_accel_groups_from_object (GtkObject      *object)
683 {
684   g_return_val_if_fail (GTK_IS_OBJECT (object), NULL);
685   
686   return gtk_object_get_data_by_id (object, accel_groups_key_id);
687 }
688
689 GSList*
690 gtk_accel_group_entries_from_object (GtkObject       *object)
691 {
692   g_return_val_if_fail (GTK_IS_OBJECT (object), NULL);
693   
694   return gtk_object_get_data_by_id (object, accel_entries_key_id);
695 }
696
697 gboolean
698 gtk_accelerator_valid (guint              keyval,
699                        GdkModifierType    modifiers)
700 {
701   static const guint invalid_accelerator_vals[] = {
702     GDK_BackSpace, GDK_Delete, GDK_KP_Delete,
703     GDK_Shift_L, GDK_Shift_R, GDK_Shift_Lock, GDK_Caps_Lock, GDK_ISO_Lock,
704     GDK_Control_L, GDK_Control_R, GDK_Meta_L, GDK_Meta_R,
705     GDK_Alt_L, GDK_Alt_R, GDK_Super_L, GDK_Super_R, GDK_Hyper_L, GDK_Hyper_R,
706     GDK_Mode_switch, GDK_Num_Lock, GDK_Multi_key,
707     GDK_Scroll_Lock, GDK_Sys_Req, 
708     GDK_Up, GDK_Down, GDK_Left, GDK_Right, GDK_Tab, GDK_ISO_Left_Tab,
709     GDK_KP_Up, GDK_KP_Down, GDK_KP_Left, GDK_KP_Right, GDK_KP_Tab,
710     GDK_First_Virtual_Screen, GDK_Prev_Virtual_Screen,
711     GDK_Next_Virtual_Screen, GDK_Last_Virtual_Screen,
712     GDK_Terminate_Server, GDK_AudibleBell_Enable,
713     0
714   };
715   const guint *ac_val;
716
717   modifiers &= GDK_MODIFIER_MASK;
718     
719   if (keyval <= 0xFF)
720     return keyval >= 0x20;
721
722   ac_val = invalid_accelerator_vals;
723   while (*ac_val)
724     {
725       if (keyval == *ac_val++)
726         return FALSE;
727     }
728
729   return TRUE;
730 }
731
732 static inline gboolean
733 is_alt (const gchar *string)
734 {
735   return ((string[0] == '<') &&
736           (string[1] == 'a' || string[1] == 'A') &&
737           (string[2] == 'l' || string[2] == 'L') &&
738           (string[3] == 't' || string[3] == 'T') &&
739           (string[4] == '>'));
740 }
741
742 static inline gboolean
743 is_ctl (const gchar *string)
744 {
745   return ((string[0] == '<') &&
746           (string[1] == 'c' || string[1] == 'C') &&
747           (string[2] == 't' || string[2] == 'T') &&
748           (string[3] == 'l' || string[3] == 'L') &&
749           (string[4] == '>'));
750 }
751
752 static inline gboolean
753 is_modx (const gchar *string)
754 {
755   return ((string[0] == '<') &&
756           (string[1] == 'm' || string[1] == 'M') &&
757           (string[2] == 'o' || string[2] == 'O') &&
758           (string[3] == 'd' || string[3] == 'D') &&
759           (string[4] >= '1' && string[4] <= '5') &&
760           (string[5] == '>'));
761 }
762
763 static inline gboolean
764 is_ctrl (const gchar *string)
765 {
766   return ((string[0] == '<') &&
767           (string[1] == 'c' || string[1] == 'C') &&
768           (string[2] == 't' || string[2] == 'T') &&
769           (string[3] == 'r' || string[3] == 'R') &&
770           (string[4] == 'l' || string[4] == 'L') &&
771           (string[5] == '>'));
772 }
773
774 static inline gboolean
775 is_shft (const gchar *string)
776 {
777   return ((string[0] == '<') &&
778           (string[1] == 's' || string[1] == 'S') &&
779           (string[2] == 'h' || string[2] == 'H') &&
780           (string[3] == 'f' || string[3] == 'F') &&
781           (string[4] == 't' || string[4] == 'T') &&
782           (string[5] == '>'));
783 }
784
785 static inline gboolean
786 is_shift (const gchar *string)
787 {
788   return ((string[0] == '<') &&
789           (string[1] == 's' || string[1] == 'S') &&
790           (string[2] == 'h' || string[2] == 'H') &&
791           (string[3] == 'i' || string[3] == 'I') &&
792           (string[4] == 'f' || string[4] == 'F') &&
793           (string[5] == 't' || string[5] == 'T') &&
794           (string[6] == '>'));
795 }
796
797 static inline gboolean
798 is_control (const gchar *string)
799 {
800   return ((string[0] == '<') &&
801           (string[1] == 'c' || string[1] == 'C') &&
802           (string[2] == 'o' || string[2] == 'O') &&
803           (string[3] == 'n' || string[3] == 'N') &&
804           (string[4] == 't' || string[4] == 'T') &&
805           (string[5] == 'r' || string[5] == 'R') &&
806           (string[6] == 'o' || string[6] == 'O') &&
807           (string[7] == 'l' || string[7] == 'L') &&
808           (string[8] == '>'));
809 }
810
811 static inline gboolean
812 is_release (const gchar *string)
813 {
814   return ((string[0] == '<') &&
815           (string[1] == 'r' || string[1] == 'R') &&
816           (string[2] == 'e' || string[2] == 'E') &&
817           (string[3] == 'l' || string[3] == 'L') &&
818           (string[4] == 'e' || string[4] == 'E') &&
819           (string[5] == 'a' || string[5] == 'A') &&
820           (string[6] == 's' || string[6] == 'S') &&
821           (string[7] == 'e' || string[7] == 'E') &&
822           (string[8] == '>'));
823 }
824
825 void
826 gtk_accelerator_parse (const gchar    *accelerator,
827                        guint          *accelerator_key,
828                        GdkModifierType*accelerator_mods)
829 {
830   guint keyval;
831   GdkModifierType mods;
832   gint len;
833   
834   if (accelerator_key)
835     *accelerator_key = 0;
836   if (accelerator_mods)
837     *accelerator_mods = 0;
838   g_return_if_fail (accelerator != NULL);
839   
840   keyval = 0;
841   mods = 0;
842   len = strlen (accelerator);
843   while (len)
844     {
845       if (*accelerator == '<')
846         {
847           if (len >= 9 && is_release (accelerator))
848             {
849               accelerator += 9;
850               len -= 9;
851               mods |= GDK_RELEASE_MASK;
852             }
853           else if (len >= 9 && is_control (accelerator))
854             {
855               accelerator += 9;
856               len -= 9;
857               mods |= GDK_CONTROL_MASK;
858             }
859           else if (len >= 7 && is_shift (accelerator))
860             {
861               accelerator += 7;
862               len -= 7;
863               mods |= GDK_SHIFT_MASK;
864             }
865           else if (len >= 6 && is_shft (accelerator))
866             {
867               accelerator += 6;
868               len -= 6;
869               mods |= GDK_SHIFT_MASK;
870             }
871           else if (len >= 6 && is_ctrl (accelerator))
872             {
873               accelerator += 6;
874               len -= 6;
875               mods |= GDK_CONTROL_MASK;
876             }
877           else if (len >= 6 && is_modx (accelerator))
878             {
879               static const guint mod_vals[] = {
880                 GDK_MOD1_MASK, GDK_MOD2_MASK, GDK_MOD3_MASK,
881                 GDK_MOD4_MASK, GDK_MOD5_MASK
882               };
883
884               len -= 6;
885               accelerator += 4;
886               mods |= mod_vals[*accelerator - '1'];
887               accelerator += 2;
888             }
889           else if (len >= 5 && is_ctl (accelerator))
890             {
891               accelerator += 5;
892               len -= 5;
893               mods |= GDK_CONTROL_MASK;
894             }
895           else if (len >= 5 && is_alt (accelerator))
896             {
897               accelerator += 5;
898               len -= 5;
899               mods |= GDK_MOD1_MASK;
900             }
901           else
902             {
903               gchar last_ch;
904               
905               last_ch = *accelerator;
906               while (last_ch && last_ch != '>')
907                 {
908                   last_ch = *accelerator;
909                   accelerator += 1;
910                   len -= 1;
911                 }
912             }
913         }
914       else
915         {
916           keyval = gdk_keyval_from_name (accelerator);
917           accelerator += len;
918           len -= len;
919         }
920     }
921   
922   if (accelerator_key)
923     *accelerator_key = gdk_keyval_to_lower (keyval);
924   if (accelerator_mods)
925     *accelerator_mods = mods;
926 }
927
928 gchar*
929 gtk_accelerator_name (guint           accelerator_key,
930                       GdkModifierType accelerator_mods)
931 {
932   static const gchar text_release[] = "<Release>";
933   static const gchar text_shift[] = "<Shift>";
934   static const gchar text_control[] = "<Control>";
935   static const gchar text_mod1[] = "<Alt>";
936   static const gchar text_mod2[] = "<Mod2>";
937   static const gchar text_mod3[] = "<Mod3>";
938   static const gchar text_mod4[] = "<Mod4>";
939   static const gchar text_mod5[] = "<Mod5>";
940   guint l;
941   gchar *keyval_name;
942   gchar *accelerator;
943
944   accelerator_mods &= GDK_MODIFIER_MASK;
945
946   keyval_name = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key));
947   if (!keyval_name)
948     keyval_name = "";
949
950   l = 0;
951   if (accelerator_mods & GDK_RELEASE_MASK)
952     l += sizeof (text_release) - 1;
953   if (accelerator_mods & GDK_SHIFT_MASK)
954     l += sizeof (text_shift) - 1;
955   if (accelerator_mods & GDK_CONTROL_MASK)
956     l += sizeof (text_control) - 1;
957   if (accelerator_mods & GDK_MOD1_MASK)
958     l += sizeof (text_mod1) - 1;
959   if (accelerator_mods & GDK_MOD2_MASK)
960     l += sizeof (text_mod2) - 1;
961   if (accelerator_mods & GDK_MOD3_MASK)
962     l += sizeof (text_mod3) - 1;
963   if (accelerator_mods & GDK_MOD4_MASK)
964     l += sizeof (text_mod4) - 1;
965   if (accelerator_mods & GDK_MOD5_MASK)
966     l += sizeof (text_mod5) - 1;
967   l += strlen (keyval_name);
968
969   accelerator = g_new (gchar, l + 1);
970
971   l = 0;
972   accelerator[l] = 0;
973   if (accelerator_mods & GDK_RELEASE_MASK)
974     {
975       strcpy (accelerator + l, text_release);
976       l += sizeof (text_release) - 1;
977     }
978   if (accelerator_mods & GDK_SHIFT_MASK)
979     {
980       strcpy (accelerator + l, text_shift);
981       l += sizeof (text_shift) - 1;
982     }
983   if (accelerator_mods & GDK_CONTROL_MASK)
984     {
985       strcpy (accelerator + l, text_control);
986       l += sizeof (text_control) - 1;
987     }
988   if (accelerator_mods & GDK_MOD1_MASK)
989     {
990       strcpy (accelerator + l, text_mod1);
991       l += sizeof (text_mod1) - 1;
992     }
993   if (accelerator_mods & GDK_MOD2_MASK)
994     {
995       strcpy (accelerator + l, text_mod2);
996       l += sizeof (text_mod2) - 1;
997     }
998   if (accelerator_mods & GDK_MOD3_MASK)
999     {
1000       strcpy (accelerator + l, text_mod3);
1001       l += sizeof (text_mod3) - 1;
1002     }
1003   if (accelerator_mods & GDK_MOD4_MASK)
1004     {
1005       strcpy (accelerator + l, text_mod4);
1006       l += sizeof (text_mod4) - 1;
1007     }
1008   if (accelerator_mods & GDK_MOD5_MASK)
1009     {
1010       strcpy (accelerator + l, text_mod5);
1011       l += sizeof (text_mod5) - 1;
1012     }
1013   strcpy (accelerator + l, keyval_name);
1014
1015   return accelerator;
1016 }
1017
1018 void
1019 gtk_accelerator_set_default_mod_mask (GdkModifierType default_mod_mask)
1020 {
1021   default_accel_mod_mask = default_mod_mask & GDK_MODIFIER_MASK;
1022 }
1023
1024 guint
1025 gtk_accelerator_get_default_mod_mask (void)
1026 {
1027   return default_accel_mod_mask;
1028 }