]> Pileus Git - ~andy/gtk/blob - gtk/gtkaccelgroup.c
9c30d611e20c511022b3ffbe85b37849568da5ad
[~andy/gtk] / gtk / gtkaccelgroup.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1998, 2001 Tim Janik
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include <config.h>
28 #include <string.h>
29 #include <stdlib.h>
30
31 #include "gtkaccelgroup.h"
32 #include "gtkaccellabel.h" /* For _gtk_accel_label_class_get_accelerator_label */
33 #include "gtkaccelmap.h"
34 #include "gtkintl.h"
35 #include "gtkmain.h"            /* For _gtk_boolean_handled_accumulator */
36 #include "gdk/gdkkeysyms.h"
37 #include "gtkmarshalers.h"
38 #include "gtkalias.h"
39
40
41 /* --- prototypes --- */
42 static void gtk_accel_group_finalize     (GObject    *object);
43 static void gtk_accel_group_get_property (GObject    *object,
44                                           guint       param_id,
45                                           GValue     *value,
46                                           GParamSpec *pspec);
47 static void accel_closure_invalidate     (gpointer    data,
48                                           GClosure   *closure);
49
50
51 /* --- variables --- */
52 static guint             signal_accel_activate = 0;
53 static guint             signal_accel_changed = 0;
54 static guint             quark_acceleratable_groups = 0;
55 static guint             default_accel_mod_mask = (GDK_SHIFT_MASK |
56                                                    GDK_CONTROL_MASK |
57                                                    GDK_MOD1_MASK |
58                                                    GDK_SUPER_MASK |
59                                                    GDK_HYPER_MASK |
60                                                    GDK_META_MASK);
61
62
63 enum {
64   PROP_0,
65   PROP_IS_LOCKED,
66   PROP_MODIFIER_MASK,
67 };
68
69 G_DEFINE_TYPE (GtkAccelGroup, gtk_accel_group, G_TYPE_OBJECT)
70
71 /* --- functions --- */
72 static void
73 gtk_accel_group_class_init (GtkAccelGroupClass *class)
74 {
75   GObjectClass *object_class = G_OBJECT_CLASS (class);
76
77   quark_acceleratable_groups = g_quark_from_static_string ("gtk-acceleratable-accel-groups");
78
79   object_class->finalize = gtk_accel_group_finalize;
80   object_class->get_property = gtk_accel_group_get_property;
81
82   class->accel_changed = NULL;
83
84   g_object_class_install_property (object_class,
85                                    PROP_IS_LOCKED,
86                                    g_param_spec_boolean ("is-locked",
87                                                          "Is locked",
88                                                          "Is the accel group locked",
89                                                          FALSE,
90                                                          G_PARAM_READABLE));
91
92   g_object_class_install_property (object_class,
93                                    PROP_MODIFIER_MASK,
94                                    g_param_spec_enum ("modifier-mask",
95                                                       "Modifier Mask",
96                                                       "Modifier Mask",
97                                                       GDK_TYPE_MODIFIER_TYPE,
98                                                       0,
99                                                       G_PARAM_READABLE));
100
101   /**
102    * GtkAccelGroup::accel-activate:
103    * @accel_group: the #GtkAccelGroup which received the signal
104    * @acceleratable: the object on which the accelerator was activated
105    * @keyval: the accelerator keyval
106    * @modifier: the modifier combination of the accelerator
107    *
108    * The accel-activate signal is an implementation detail of
109    * #GtkAccelGroup and not meant to be used by applications.
110    * 
111    * Returns: %TRUE if the accelerator was activated
112    */
113   signal_accel_activate =
114     g_signal_new (I_("accel_activate"),
115                   G_OBJECT_CLASS_TYPE (class),
116                   G_SIGNAL_DETAILED,
117                   0,
118                   _gtk_boolean_handled_accumulator, NULL,
119                   _gtk_marshal_BOOLEAN__OBJECT_UINT_FLAGS,
120                   G_TYPE_BOOLEAN, 3,
121                   G_TYPE_OBJECT,
122                   G_TYPE_UINT,
123                   GDK_TYPE_MODIFIER_TYPE);
124   /**
125    * GtkAccelGroup::accel-changed:
126    * @accel_group: the #GtkAccelGroup which received the signal
127    * @keyval: the accelerator keyval
128    * @modifier: the modifier combination of the accelerator
129    * @accel_closure: the #GClosure of the accelerator
130    *
131    * The accel-changed signal is emitted when a #GtkAccelGroupEntry
132    * is added to or removed from the accel group. 
133    *
134    * Widgets like #GtkAccelLabel which display an associated 
135    * accelerator should connect to this signal, and rebuild 
136    * their visual representation if the @accel_closure is theirs.
137    */
138   signal_accel_changed =
139     g_signal_new (I_("accel_changed"),
140                   G_OBJECT_CLASS_TYPE (class),
141                   G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
142                   G_STRUCT_OFFSET (GtkAccelGroupClass, accel_changed),
143                   NULL, NULL,
144                   _gtk_marshal_VOID__UINT_FLAGS_BOXED,
145                   G_TYPE_NONE, 3,
146                   G_TYPE_UINT,
147                   GDK_TYPE_MODIFIER_TYPE,
148                   G_TYPE_CLOSURE);
149 }
150
151 static void
152 gtk_accel_group_finalize (GObject *object)
153 {
154   GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (object);
155   guint i;
156   
157   for (i = 0; i < accel_group->n_accels; i++)
158     {
159       GtkAccelGroupEntry *entry = &accel_group->priv_accels[i];
160
161       if (entry->accel_path_quark)
162         {
163           const gchar *accel_path = g_quark_to_string (entry->accel_path_quark);
164
165           _gtk_accel_map_remove_group (accel_path, accel_group);
166         }
167       g_closure_remove_invalidate_notifier (entry->closure, accel_group, accel_closure_invalidate);
168     }
169
170   g_free (accel_group->priv_accels);
171
172   G_OBJECT_CLASS (gtk_accel_group_parent_class)->finalize (object);
173 }
174
175 static void
176 gtk_accel_group_get_property (GObject    *object,
177                               guint       param_id,
178                               GValue     *value,
179                               GParamSpec *pspec)
180 {
181   GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (object);
182
183   switch (param_id)
184     {
185     case PROP_IS_LOCKED:
186       g_value_set_boolean (value, accel_group->lock_count > 0);
187       break;
188     case PROP_MODIFIER_MASK:
189       g_value_set_enum (value, accel_group->modifier_mask);
190       break;
191     default:
192       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
193       break;
194     }
195 }
196
197 static void
198 gtk_accel_group_init (GtkAccelGroup *accel_group)
199 {
200   accel_group->lock_count = 0;
201   accel_group->modifier_mask = gtk_accelerator_get_default_mod_mask ();
202   accel_group->acceleratables = NULL;
203   accel_group->n_accels = 0;
204   accel_group->priv_accels = NULL;
205 }
206
207 /**
208  * gtk_accel_group_new:
209  * @returns: a new #GtkAccelGroup object
210  * 
211  * Creates a new #GtkAccelGroup. 
212  */
213 GtkAccelGroup*
214 gtk_accel_group_new (void)
215 {
216   return g_object_new (GTK_TYPE_ACCEL_GROUP, NULL);
217 }
218
219 /**
220  * gtk_accel_group_get_is_locked:
221  * @accel_group: a #GtkAccelGroup
222  *
223  * @returns: %TRUE if there are 1 or more locks on the @accel_group,
224  * %FALSE otherwise.
225  *
226  * Locks are added and removed using gtk_accel_group_lock() and
227  * gtk_accel_group_unlock().
228  */
229 gboolean
230 gtk_accel_group_get_is_locked (GtkAccelGroup *accel_group)
231 {
232   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
233
234   return accel_group->lock_count > 0;
235 }
236
237 /**
238  * gtk_accel_group_get_modifier_mask:
239  * @accel_group: a #GtkAccelGroup
240  *
241  * @returns: #GdkModifierType representing the mask for this
242  * @accel_group. For example, #GDK_CONTROL_MASK, #GDK_SHIFT_MASK, etc.
243  */
244 GdkModifierType
245 gtk_accel_group_get_modifier_mask (GtkAccelGroup *accel_group)
246 {
247   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), 0);
248
249   return accel_group->modifier_mask;
250 }
251
252 static void
253 accel_group_weak_ref_detach (GSList  *free_list,
254                              GObject *stale_object)
255 {
256   GSList *slist;
257   
258   for (slist = free_list; slist; slist = slist->next)
259     {
260       GtkAccelGroup *accel_group;
261       
262       accel_group = slist->data;
263       accel_group->acceleratables = g_slist_remove (accel_group->acceleratables, stale_object);
264       g_object_unref (accel_group);
265       g_object_notify (G_OBJECT (accel_group), "acceleratables");
266     }
267   g_slist_free (free_list);
268   g_object_set_qdata (stale_object, quark_acceleratable_groups, NULL);
269 }
270
271 void
272 _gtk_accel_group_attach (GtkAccelGroup *accel_group,
273                          GObject       *object)
274 {
275   GSList *slist;
276   
277   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
278   g_return_if_fail (G_IS_OBJECT (object));
279   g_return_if_fail (g_slist_find (accel_group->acceleratables, object) == NULL);
280   
281   g_object_ref (accel_group);
282   accel_group->acceleratables = g_slist_prepend (accel_group->acceleratables, object);
283   slist = g_object_get_qdata (object, quark_acceleratable_groups);
284   if (slist)
285     g_object_weak_unref (object,
286                          (GWeakNotify) accel_group_weak_ref_detach,
287                          slist);
288   slist = g_slist_prepend (slist, accel_group);
289   g_object_set_qdata (object, quark_acceleratable_groups, slist);
290   g_object_weak_ref (object,
291                      (GWeakNotify) accel_group_weak_ref_detach,
292                      slist);
293   g_object_notify (G_OBJECT (accel_group), "acceleratables");
294 }
295
296 void
297 _gtk_accel_group_detach (GtkAccelGroup *accel_group,
298                          GObject       *object)
299 {
300   GSList *slist;
301   
302   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
303   g_return_if_fail (G_IS_OBJECT (object));
304   g_return_if_fail (g_slist_find (accel_group->acceleratables, object) != NULL);
305   
306   accel_group->acceleratables = g_slist_remove (accel_group->acceleratables, object);
307   slist = g_object_get_qdata (object, quark_acceleratable_groups);
308   g_object_weak_unref (object,
309                        (GWeakNotify) accel_group_weak_ref_detach,
310                        slist);
311   slist = g_slist_remove (slist, accel_group);
312   g_object_set_qdata (object, quark_acceleratable_groups, slist);
313   if (slist)
314     g_object_weak_ref (object,
315                        (GWeakNotify) accel_group_weak_ref_detach,
316                        slist);
317   g_object_notify (G_OBJECT (accel_group), "acceleratables");
318   g_object_unref (accel_group);
319 }
320
321 /**
322  * gtk_accel_groups_from_object:
323  * @object:        a #GObject, usually a #GtkWindow 
324  * @returns: a list of all accel groups which are attached to @object
325  *
326  * Gets a list of all accel groups which are attached to @object.
327  */
328 GSList*
329 gtk_accel_groups_from_object (GObject *object)
330 {
331   g_return_val_if_fail (G_IS_OBJECT (object), NULL);
332   
333   return g_object_get_qdata (object, quark_acceleratable_groups);
334 }
335
336 /**
337  * gtk_accel_group_find:
338  * @accel_group: a #GtkAccelGroup
339  * @find_func: a function to filter the entries of @accel_group with
340  * @data: data to pass to @find_func
341  * @returns: the key of the first entry passing @find_func. The key is 
342  * owned by GTK+ and must not be freed.
343  *
344  * Finds the first entry in an accelerator group for which 
345  * @find_func returns %TRUE and returns its #GtkAccelKey.
346  *
347  */
348 GtkAccelKey*
349 gtk_accel_group_find (GtkAccelGroup        *accel_group,
350                       GtkAccelGroupFindFunc find_func,
351                       gpointer              data)
352 {
353   GtkAccelKey *key = NULL;
354   guint i;
355
356   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL);
357   g_return_val_if_fail (find_func != NULL, NULL);
358
359   g_object_ref (accel_group);
360   for (i = 0; i < accel_group->n_accels; i++)
361     if (find_func (&accel_group->priv_accels[i].key,
362                    accel_group->priv_accels[i].closure,
363                    data))
364       {
365         key = &accel_group->priv_accels[i].key;
366         break;
367       }
368   g_object_unref (accel_group);
369
370   return key;
371 }
372
373 /**
374  * gtk_accel_group_lock:
375  * @accel_group: a #GtkAccelGroup
376  * 
377  * Locks the given accelerator group.
378  *
379  * Locking an acelerator group prevents the accelerators contained
380  * within it to be changed during runtime. Refer to
381  * gtk_accel_map_change_entry() about runtime accelerator changes.
382  *
383  * If called more than once, @accel_group remains locked until
384  * gtk_accel_group_unlock() has been called an equivalent number
385  * of times.
386  */
387 void
388 gtk_accel_group_lock (GtkAccelGroup *accel_group)
389 {
390   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
391   
392   accel_group->lock_count += 1;
393
394   g_object_notify (G_OBJECT (accel_group), "lock-count");
395 }
396
397 /**
398  * gtk_accel_group_unlock:
399  * @accel_group: a #GtkAccelGroup
400  * 
401  * Undoes the last call to gtk_accel_group_lock() on this @accel_group.
402  */
403 void
404 gtk_accel_group_unlock (GtkAccelGroup *accel_group)
405 {
406   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
407   g_return_if_fail (accel_group->lock_count > 0);
408
409   accel_group->lock_count -= 1;
410
411   g_object_notify (G_OBJECT (accel_group), "lock-count");
412 }
413
414 static void
415 accel_closure_invalidate (gpointer  data,
416                           GClosure *closure)
417 {
418   GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (data);
419
420   gtk_accel_group_disconnect (accel_group, closure);
421 }
422
423 static int
424 bsearch_compare_accels (const void *d1,
425                         const void *d2)
426 {
427   const GtkAccelGroupEntry *entry1 = d1;
428   const GtkAccelGroupEntry *entry2 = d2;
429
430   if (entry1->key.accel_key == entry2->key.accel_key)
431     return entry1->key.accel_mods < entry2->key.accel_mods ? -1 : entry1->key.accel_mods > entry2->key.accel_mods;
432   else
433     return entry1->key.accel_key < entry2->key.accel_key ? -1 : 1;
434 }
435
436 static void
437 quick_accel_add (GtkAccelGroup  *accel_group,
438                  guint           accel_key,
439                  GdkModifierType accel_mods,
440                  GtkAccelFlags   accel_flags,
441                  GClosure       *closure,
442                  GQuark          path_quark)
443 {
444   guint pos, i = accel_group->n_accels++;
445   GtkAccelGroupEntry key;
446
447   /* find position */
448   key.key.accel_key = accel_key;
449   key.key.accel_mods = accel_mods;
450   for (pos = 0; pos < i; pos++)
451     if (bsearch_compare_accels (&key, accel_group->priv_accels + pos) < 0)
452       break;
453
454   /* insert at position, ref closure */
455   accel_group->priv_accels = g_renew (GtkAccelGroupEntry, accel_group->priv_accels, accel_group->n_accels);
456   g_memmove (accel_group->priv_accels + pos + 1, accel_group->priv_accels + pos,
457              (i - pos) * sizeof (accel_group->priv_accels[0]));
458   accel_group->priv_accels[pos].key.accel_key = accel_key;
459   accel_group->priv_accels[pos].key.accel_mods = accel_mods;
460   accel_group->priv_accels[pos].key.accel_flags = accel_flags;
461   accel_group->priv_accels[pos].closure = g_closure_ref (closure);
462   accel_group->priv_accels[pos].accel_path_quark = path_quark;
463   g_closure_sink (closure);
464   g_object_notify (G_OBJECT (accel_group), "priv-accels");
465   
466   /* handle closure invalidation and reverse lookups */
467   g_closure_add_invalidate_notifier (closure, accel_group, accel_closure_invalidate);
468
469   /* get accel path notification */
470   if (path_quark)
471     _gtk_accel_map_add_group (g_quark_to_string (path_quark), accel_group);
472
473   /* connect and notify changed */
474   if (accel_key)
475     {
476       gchar *accel_name = gtk_accelerator_name (accel_key, accel_mods);
477       GQuark accel_quark = g_quark_from_string (accel_name);
478
479       g_free (accel_name);
480       
481       /* setup handler */
482       g_signal_connect_closure_by_id (accel_group, signal_accel_activate, accel_quark, closure, FALSE);
483       
484       /* and notify */
485       g_signal_emit (accel_group, signal_accel_changed, accel_quark, accel_key, accel_mods, closure);
486     }
487 }
488
489 static void
490 quick_accel_remove (GtkAccelGroup      *accel_group,
491                     guint               pos)
492 {
493   GQuark accel_quark = 0;
494   GtkAccelGroupEntry *entry = accel_group->priv_accels + pos;
495   guint accel_key = entry->key.accel_key;
496   GdkModifierType accel_mods = entry->key.accel_mods;
497   GClosure *closure = entry->closure;
498
499   /* quark for notification */
500   if (accel_key)
501     {
502       gchar *accel_name = gtk_accelerator_name (accel_key, accel_mods);
503
504       accel_quark = g_quark_from_string (accel_name);
505       g_free (accel_name);
506     }
507
508   /* clean up closure invalidate notification and disconnect */
509   g_closure_remove_invalidate_notifier (entry->closure, accel_group, accel_closure_invalidate);
510   if (accel_quark)
511     g_signal_handlers_disconnect_matched (accel_group,
512                                           G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_CLOSURE,
513                                           signal_accel_activate, accel_quark,
514                                           closure, NULL, NULL);
515   /* clean up accel path notification */
516   if (entry->accel_path_quark)
517     _gtk_accel_map_remove_group (g_quark_to_string (entry->accel_path_quark), accel_group);
518
519   /* physically remove */
520   accel_group->n_accels -= 1;
521   g_memmove (entry, entry + 1,
522              (accel_group->n_accels - pos) * sizeof (accel_group->priv_accels[0]));
523
524   /* and notify */
525   if (accel_quark)
526     g_signal_emit (accel_group, signal_accel_changed, accel_quark, accel_key, accel_mods, closure);
527
528   /* remove quick_accel_add() refcount */
529   g_closure_unref (closure);
530 }
531
532 static GtkAccelGroupEntry*
533 quick_accel_find (GtkAccelGroup  *accel_group,
534                   guint           accel_key,
535                   GdkModifierType accel_mods,
536                   guint          *count_p)
537 {
538   GtkAccelGroupEntry *entry;
539   GtkAccelGroupEntry key;
540
541   *count_p = 0;
542
543   if (!accel_group->n_accels)
544     return NULL;
545
546   key.key.accel_key = accel_key;
547   key.key.accel_mods = accel_mods;
548   entry = bsearch (&key, accel_group->priv_accels, accel_group->n_accels,
549                    sizeof (accel_group->priv_accels[0]), bsearch_compare_accels);
550   
551   if (!entry)
552     return NULL;
553
554   /* step back to the first member */
555   for (; entry > accel_group->priv_accels; entry--)
556     if (entry[-1].key.accel_key != accel_key ||
557         entry[-1].key.accel_mods != accel_mods)
558       break;
559   /* count equal members */
560   for (; entry + *count_p < accel_group->priv_accels + accel_group->n_accels; (*count_p)++)
561     if (entry[*count_p].key.accel_key != accel_key ||
562         entry[*count_p].key.accel_mods != accel_mods)
563       break;
564   return entry;
565 }
566
567 /**
568  * gtk_accel_group_connect:
569  * @accel_group:      the accelerator group to install an accelerator in
570  * @accel_key:        key value of the accelerator
571  * @accel_mods:       modifier combination of the accelerator
572  * @accel_flags:      a flag mask to configure this accelerator
573  * @closure:          closure to be executed upon accelerator activation
574  *
575  * Installs an accelerator in this group. When @accel_group is being activated
576  * in response to a call to gtk_accel_groups_activate(), @closure will be
577  * invoked if the @accel_key and @accel_mods from gtk_accel_groups_activate()
578  * match those of this connection.
579  *
580  * The signature used for the @closure is that of #GtkAccelGroupActivate.
581  * 
582  * Note that, due to implementation details, a single closure can only be
583  * connected to one accelerator group.
584  */
585 void
586 gtk_accel_group_connect (GtkAccelGroup  *accel_group,
587                          guint           accel_key,
588                          GdkModifierType accel_mods,
589                          GtkAccelFlags   accel_flags,
590                          GClosure       *closure)
591 {
592   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
593   g_return_if_fail (closure != NULL);
594   g_return_if_fail (accel_key > 0);
595   g_return_if_fail (gtk_accel_group_from_accel_closure (closure) == NULL);
596
597   g_object_ref (accel_group);
598   if (!closure->is_invalid)
599     quick_accel_add (accel_group,
600                      gdk_keyval_to_lower (accel_key),
601                      accel_mods, accel_flags, closure, 0);
602   g_object_unref (accel_group);
603 }
604
605 /**
606  * gtk_accel_group_connect_by_path:
607  * @accel_group:      the accelerator group to install an accelerator in
608  * @accel_path:       path used for determining key and modifiers.
609  * @closure:          closure to be executed upon accelerator activation
610  *
611  * Installs an accelerator in this group, using an accelerator path to look
612  * up the appropriate key and modifiers (see gtk_accel_map_add_entry()).
613  * When @accel_group is being activated in response to a call to
614  * gtk_accel_groups_activate(), @closure will be invoked if the @accel_key and
615  * @accel_mods from gtk_accel_groups_activate() match the key and modifiers
616  * for the path.
617  *
618  * The signature used for the @closure is that of #GtkAccelGroupActivate.
619  * 
620  * Note that @accel_path string will be stored in a #GQuark. Therefore, if you
621  * pass a static string, you can save some memory by interning it first with 
622  * g_intern_static_string().
623  */
624 void
625 gtk_accel_group_connect_by_path (GtkAccelGroup  *accel_group,
626                                  const gchar    *accel_path,
627                                  GClosure       *closure)
628 {
629   guint accel_key = 0;
630   GdkModifierType accel_mods = 0;
631   GtkAccelKey key;
632
633   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
634   g_return_if_fail (closure != NULL);
635   g_return_if_fail (_gtk_accel_path_is_valid (accel_path));
636
637   if (closure->is_invalid)
638     return;
639
640   g_object_ref (accel_group);
641
642   if (gtk_accel_map_lookup_entry (accel_path, &key))
643     {
644       accel_key = gdk_keyval_to_lower (key.accel_key);
645       accel_mods = key.accel_mods;
646     }
647
648   quick_accel_add (accel_group, accel_key, accel_mods, GTK_ACCEL_VISIBLE, closure,
649                    g_quark_from_string (accel_path));
650
651   g_object_unref (accel_group);
652 }
653
654 /**
655  * gtk_accel_group_disconnect:
656  * @accel_group: the accelerator group to remove an accelerator from
657  * @closure:     the closure to remove from this accelerator group
658  * @returns:     %TRUE if the closure was found and got disconnected
659  *
660  * Removes an accelerator previously installed through
661  * gtk_accel_group_connect().
662  */
663 gboolean
664 gtk_accel_group_disconnect (GtkAccelGroup *accel_group,
665                             GClosure      *closure)
666 {
667   guint i;
668
669   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
670
671   for (i = 0; i < accel_group->n_accels; i++)
672     if (accel_group->priv_accels[i].closure == closure)
673       {
674         g_object_ref (accel_group);
675         quick_accel_remove (accel_group, i);
676         g_object_unref (accel_group);
677         return TRUE;
678       }
679   return FALSE;
680 }
681
682 /**
683  * gtk_accel_group_disconnect_key:
684  * @accel_group:      the accelerator group to install an accelerator in
685  * @accel_key:        key value of the accelerator
686  * @accel_mods:       modifier combination of the accelerator
687  * @returns:          %TRUE if there was an accelerator which could be 
688  *                    removed, %FALSE otherwise
689  *
690  * Removes an accelerator previously installed through
691  * gtk_accel_group_connect().
692  */
693 gboolean
694 gtk_accel_group_disconnect_key (GtkAccelGroup  *accel_group,
695                                 guint           accel_key,
696                                 GdkModifierType accel_mods)
697 {
698   GtkAccelGroupEntry *entries;
699   GSList *slist, *clist = NULL;
700   gboolean removed_one = FALSE;
701   guint n;
702
703   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
704
705   g_object_ref (accel_group);
706   
707   accel_key = gdk_keyval_to_lower (accel_key);
708   entries = quick_accel_find (accel_group, accel_key, accel_mods, &n);
709   while (n--)
710     {
711       GClosure *closure = g_closure_ref (entries[n].closure);
712
713       clist = g_slist_prepend (clist, closure);
714     }
715
716   for (slist = clist; slist; slist = slist->next)
717     {
718       GClosure *closure = slist->data;
719
720       removed_one |= gtk_accel_group_disconnect (accel_group, closure);
721       g_closure_unref (closure);
722     }
723   g_slist_free (clist);
724
725   g_object_unref (accel_group);
726
727   return removed_one;
728 }
729
730 void
731 _gtk_accel_group_reconnect (GtkAccelGroup *accel_group,
732                             GQuark         accel_path_quark)
733 {
734   GSList *slist, *clist = NULL;
735   guint i;
736
737   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
738
739   g_object_ref (accel_group);
740
741   for (i = 0; i < accel_group->n_accels; i++)
742     if (accel_group->priv_accels[i].accel_path_quark == accel_path_quark)
743       {
744         GClosure *closure = g_closure_ref (accel_group->priv_accels[i].closure);
745
746         clist = g_slist_prepend (clist, closure);
747       }
748
749   for (slist = clist; slist; slist = slist->next)
750     {
751       GClosure *closure = slist->data;
752
753       gtk_accel_group_disconnect (accel_group, closure);
754       gtk_accel_group_connect_by_path (accel_group, g_quark_to_string (accel_path_quark), closure);
755       g_closure_unref (closure);
756     }
757   g_slist_free (clist);
758
759   g_object_unref (accel_group);
760 }
761
762 /**
763  * gtk_accel_group_query:
764  * @accel_group:      the accelerator group to query
765  * @accel_key:        key value of the accelerator
766  * @accel_mods:       modifier combination of the accelerator
767  * @n_entries:        location to return the number of entries found, or %NULL
768  * @returns:          an array of @n_entries #GtkAccelGroupEntry elements, or %NULL. The array is owned by GTK+ and must not be freed. 
769  *
770  * Queries an accelerator group for all entries matching @accel_key and 
771  * @accel_mods.
772  */
773 GtkAccelGroupEntry*
774 gtk_accel_group_query (GtkAccelGroup  *accel_group,
775                        guint           accel_key,
776                        GdkModifierType accel_mods,
777                        guint          *n_entries)
778 {
779   GtkAccelGroupEntry *entries;
780   guint n;
781
782   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL);
783
784   entries = quick_accel_find (accel_group, gdk_keyval_to_lower (accel_key), accel_mods, &n);
785
786   if (n_entries)
787     *n_entries = entries ? n : 0;
788
789   return entries;
790 }
791
792 /**
793  * gtk_accel_group_from_accel_closure:
794  * @closure: a #GClosure
795  * @returns: the #GtkAccelGroup to which @closure is connected, or %NULL.
796  *
797  * Finds the #GtkAccelGroup to which @closure is connected; 
798  * see gtk_accel_group_connect().
799  */
800 GtkAccelGroup*
801 gtk_accel_group_from_accel_closure (GClosure *closure)
802 {
803   guint i;
804
805   g_return_val_if_fail (closure != NULL, NULL);
806
807   /* a few remarks on what we do here. in general, we need a way to reverse lookup
808    * accel_groups from closures that are being used in accel groups. this could
809    * be done e.g via a hashtable. it is however cheaper (memory wise) to just
810    * use the invalidation notifier on the closure itself (which we need to install
811    * anyway), that contains the accel group as data which, besides needing to peek
812    * a bit at closure internals, works just as good.
813    */
814   for (i = 0; i < G_CLOSURE_N_NOTIFIERS (closure); i++)
815     if (closure->notifiers[i].notify == accel_closure_invalidate)
816       return closure->notifiers[i].data;
817
818   return NULL;
819 }
820
821 /**
822  * gtk_accel_group_activate:
823  * @accel_group:   a #GtkAccelGroup
824  * @accel_quark:   the quark for the accelerator name
825  * @acceleratable: the #GObject, usually a #GtkWindow, on which
826  *                 to activate the accelerator.
827  * @accel_key:     accelerator keyval from a key event
828  * @accel_mods:    keyboard state mask from a key event
829  * @returns:       %TRUE if the accelerator was handled, %FALSE otherwise
830  * 
831  * Finds the first accelerator in @accel_group 
832  * that matches @accel_key and @accel_mods, and
833  * activates it.
834  *
835  * Returns: %TRUE if an accelerator was activated and handled this keypress
836  */
837 gboolean
838 gtk_accel_group_activate (GtkAccelGroup   *accel_group,
839                           GQuark           accel_quark,
840                           GObject         *acceleratable,
841                           guint            accel_key,
842                           GdkModifierType  accel_mods)
843 {
844   gboolean was_handled;
845
846   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
847   g_return_val_if_fail (G_IS_OBJECT (acceleratable), FALSE);
848   
849   was_handled = FALSE;
850   g_signal_emit (accel_group, signal_accel_activate, accel_quark,
851                  acceleratable, accel_key, accel_mods, &was_handled);
852
853   return was_handled;
854 }
855
856 /**
857  * gtk_accel_groups_activate:
858  * @object:        the #GObject, usually a #GtkWindow, on which
859  *                 to activate the accelerator.
860  * @accel_key:     accelerator keyval from a key event
861  * @accel_mods:    keyboard state mask from a key event
862  * @returns:       %TRUE if the accelerator was handled, %FALSE otherwise
863  * 
864  * Finds the first accelerator in any #GtkAccelGroup attached
865  * to @object that matches @accel_key and @accel_mods, and
866  * activates that accelerator.
867  *
868  * Returns: %TRUE if an accelerator was activated and handled this keypress
869  */
870 gboolean
871 gtk_accel_groups_activate (GObject        *object,
872                            guint           accel_key,
873                            GdkModifierType accel_mods)
874 {
875   g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
876   
877   if (gtk_accelerator_valid (accel_key, accel_mods))
878     {
879       gchar *accel_name;
880       GQuark accel_quark;
881       GSList *slist;
882
883       accel_name = gtk_accelerator_name (accel_key, (accel_mods & gtk_accelerator_get_default_mod_mask ()));
884       accel_quark = g_quark_from_string (accel_name);
885       g_free (accel_name);
886       
887       for (slist = gtk_accel_groups_from_object (object); slist; slist = slist->next)
888         if (gtk_accel_group_activate (slist->data, accel_quark, object, accel_key, accel_mods))
889           return TRUE;
890     }
891   
892   return FALSE;
893 }
894
895 /**
896  * gtk_accelerator_valid:
897  * @keyval:    a GDK keyval
898  * @modifiers: modifier mask
899  * @returns:   %TRUE if the accelerator is valid
900  * 
901  * Determines whether a given keyval and modifier mask constitute
902  * a valid keyboard accelerator. For example, the #GDK_a keyval
903  * plus #GDK_CONTROL_MASK is valid - this is a "Ctrl+a" accelerator.
904  * But, you can't, for instance, use the #GDK_Control_L keyval
905  * as an accelerator.
906  */
907 gboolean
908 gtk_accelerator_valid (guint              keyval,
909                        GdkModifierType    modifiers)
910 {
911   static const guint invalid_accelerator_vals[] = {
912     GDK_Shift_L, GDK_Shift_R, GDK_Shift_Lock, GDK_Caps_Lock, GDK_ISO_Lock,
913     GDK_Control_L, GDK_Control_R, GDK_Meta_L, GDK_Meta_R,
914     GDK_Alt_L, GDK_Alt_R, GDK_Super_L, GDK_Super_R, GDK_Hyper_L, GDK_Hyper_R,
915     GDK_ISO_Level3_Shift, GDK_ISO_Next_Group, GDK_ISO_Prev_Group,
916     GDK_ISO_First_Group, GDK_ISO_Last_Group,
917     GDK_Mode_switch, GDK_Num_Lock, GDK_Multi_key,
918     GDK_Scroll_Lock, GDK_Sys_Req, 
919     GDK_Tab, GDK_ISO_Left_Tab, GDK_KP_Tab,
920     GDK_First_Virtual_Screen, GDK_Prev_Virtual_Screen,
921     GDK_Next_Virtual_Screen, GDK_Last_Virtual_Screen,
922     GDK_Terminate_Server, GDK_AudibleBell_Enable,
923     0
924   };
925   static const guint invalid_unmodified_vals[] = {
926     GDK_Up, GDK_Down, GDK_Left, GDK_Right,
927     GDK_KP_Up, GDK_KP_Down, GDK_KP_Left, GDK_KP_Right,
928     0
929   };
930   const guint *ac_val;
931
932   modifiers &= GDK_MODIFIER_MASK;
933     
934   if (keyval <= 0xFF)
935     return keyval >= 0x20;
936
937   ac_val = invalid_accelerator_vals;
938   while (*ac_val)
939     {
940       if (keyval == *ac_val++)
941         return FALSE;
942     }
943
944   if (!modifiers)
945     {
946       ac_val = invalid_unmodified_vals;
947       while (*ac_val)
948         {
949           if (keyval == *ac_val++)
950             return FALSE;
951         }
952     }
953   
954   return TRUE;
955 }
956
957 static inline gboolean
958 is_alt (const gchar *string)
959 {
960   return ((string[0] == '<') &&
961           (string[1] == 'a' || string[1] == 'A') &&
962           (string[2] == 'l' || string[2] == 'L') &&
963           (string[3] == 't' || string[3] == 'T') &&
964           (string[4] == '>'));
965 }
966
967 static inline gboolean
968 is_ctl (const gchar *string)
969 {
970   return ((string[0] == '<') &&
971           (string[1] == 'c' || string[1] == 'C') &&
972           (string[2] == 't' || string[2] == 'T') &&
973           (string[3] == 'l' || string[3] == 'L') &&
974           (string[4] == '>'));
975 }
976
977 static inline gboolean
978 is_modx (const gchar *string)
979 {
980   return ((string[0] == '<') &&
981           (string[1] == 'm' || string[1] == 'M') &&
982           (string[2] == 'o' || string[2] == 'O') &&
983           (string[3] == 'd' || string[3] == 'D') &&
984           (string[4] >= '1' && string[4] <= '5') &&
985           (string[5] == '>'));
986 }
987
988 static inline gboolean
989 is_ctrl (const gchar *string)
990 {
991   return ((string[0] == '<') &&
992           (string[1] == 'c' || string[1] == 'C') &&
993           (string[2] == 't' || string[2] == 'T') &&
994           (string[3] == 'r' || string[3] == 'R') &&
995           (string[4] == 'l' || string[4] == 'L') &&
996           (string[5] == '>'));
997 }
998
999 static inline gboolean
1000 is_shft (const gchar *string)
1001 {
1002   return ((string[0] == '<') &&
1003           (string[1] == 's' || string[1] == 'S') &&
1004           (string[2] == 'h' || string[2] == 'H') &&
1005           (string[3] == 'f' || string[3] == 'F') &&
1006           (string[4] == 't' || string[4] == 'T') &&
1007           (string[5] == '>'));
1008 }
1009
1010 static inline gboolean
1011 is_shift (const gchar *string)
1012 {
1013   return ((string[0] == '<') &&
1014           (string[1] == 's' || string[1] == 'S') &&
1015           (string[2] == 'h' || string[2] == 'H') &&
1016           (string[3] == 'i' || string[3] == 'I') &&
1017           (string[4] == 'f' || string[4] == 'F') &&
1018           (string[5] == 't' || string[5] == 'T') &&
1019           (string[6] == '>'));
1020 }
1021
1022 static inline gboolean
1023 is_control (const gchar *string)
1024 {
1025   return ((string[0] == '<') &&
1026           (string[1] == 'c' || string[1] == 'C') &&
1027           (string[2] == 'o' || string[2] == 'O') &&
1028           (string[3] == 'n' || string[3] == 'N') &&
1029           (string[4] == 't' || string[4] == 'T') &&
1030           (string[5] == 'r' || string[5] == 'R') &&
1031           (string[6] == 'o' || string[6] == 'O') &&
1032           (string[7] == 'l' || string[7] == 'L') &&
1033           (string[8] == '>'));
1034 }
1035
1036 static inline gboolean
1037 is_release (const gchar *string)
1038 {
1039   return ((string[0] == '<') &&
1040           (string[1] == 'r' || string[1] == 'R') &&
1041           (string[2] == 'e' || string[2] == 'E') &&
1042           (string[3] == 'l' || string[3] == 'L') &&
1043           (string[4] == 'e' || string[4] == 'E') &&
1044           (string[5] == 'a' || string[5] == 'A') &&
1045           (string[6] == 's' || string[6] == 'S') &&
1046           (string[7] == 'e' || string[7] == 'E') &&
1047           (string[8] == '>'));
1048 }
1049
1050 static inline gboolean
1051 is_meta (const gchar *string)
1052 {
1053   return ((string[0] == '<') &&
1054           (string[1] == 'm' || string[1] == 'M') &&
1055           (string[2] == 'e' || string[2] == 'E') &&
1056           (string[3] == 't' || string[3] == 'T') &&
1057           (string[4] == 'a' || string[4] == 'A') &&
1058           (string[5] == '>'));
1059 }
1060
1061 static inline gboolean
1062 is_super (const gchar *string)
1063 {
1064   return ((string[0] == '<') &&
1065           (string[1] == 's' || string[1] == 'S') &&
1066           (string[2] == 'u' || string[2] == 'U') &&
1067           (string[3] == 'p' || string[3] == 'P') &&
1068           (string[4] == 'e' || string[4] == 'E') &&
1069           (string[5] == 'r' || string[5] == 'R') &&
1070           (string[6] == '>'));
1071 }
1072
1073 static inline gboolean
1074 is_hyper (const gchar *string)
1075 {
1076   return ((string[0] == '<') &&
1077           (string[1] == 'h' || string[1] == 'H') &&
1078           (string[2] == 'y' || string[2] == 'Y') &&
1079           (string[3] == 'p' || string[3] == 'P') &&
1080           (string[4] == 'e' || string[4] == 'E') &&
1081           (string[5] == 'r' || string[5] == 'R') &&
1082           (string[6] == '>'));
1083 }
1084
1085 /**
1086  * gtk_accelerator_parse:
1087  * @accelerator:      string representing an accelerator
1088  * @accelerator_key:  return location for accelerator keyval
1089  * @accelerator_mods: return location for accelerator modifier mask
1090  *
1091  * Parses a string representing an accelerator. The
1092  * format looks like "&lt;Control&gt;a" or "&lt;Shift&gt;&lt;Alt&gt;F1" or
1093  * "&lt;Release&gt;z" (the last one is for key release).
1094  * The parser is fairly liberal and allows lower or upper case,
1095  * and also abbreviations such as "&lt;Ctl&gt;" and "&lt;Ctrl&gt;".
1096  *
1097  * If the parse fails, @accelerator_key and @accelerator_mods will
1098  * be set to 0 (zero).
1099  */
1100 void
1101 gtk_accelerator_parse (const gchar     *accelerator,
1102                        guint           *accelerator_key,
1103                        GdkModifierType *accelerator_mods)
1104 {
1105   guint keyval;
1106   GdkModifierType mods;
1107   gint len;
1108   
1109   if (accelerator_key)
1110     *accelerator_key = 0;
1111   if (accelerator_mods)
1112     *accelerator_mods = 0;
1113   g_return_if_fail (accelerator != NULL);
1114   
1115   keyval = 0;
1116   mods = 0;
1117   len = strlen (accelerator);
1118   while (len)
1119     {
1120       if (*accelerator == '<')
1121         {
1122           if (len >= 9 && is_release (accelerator))
1123             {
1124               accelerator += 9;
1125               len -= 9;
1126               mods |= GDK_RELEASE_MASK;
1127             }
1128           else if (len >= 9 && is_control (accelerator))
1129             {
1130               accelerator += 9;
1131               len -= 9;
1132               mods |= GDK_CONTROL_MASK;
1133             }
1134           else if (len >= 7 && is_shift (accelerator))
1135             {
1136               accelerator += 7;
1137               len -= 7;
1138               mods |= GDK_SHIFT_MASK;
1139             }
1140           else if (len >= 6 && is_shft (accelerator))
1141             {
1142               accelerator += 6;
1143               len -= 6;
1144               mods |= GDK_SHIFT_MASK;
1145             }
1146           else if (len >= 6 && is_ctrl (accelerator))
1147             {
1148               accelerator += 6;
1149               len -= 6;
1150               mods |= GDK_CONTROL_MASK;
1151             }
1152           else if (len >= 6 && is_modx (accelerator))
1153             {
1154               static const guint mod_vals[] = {
1155                 GDK_MOD1_MASK, GDK_MOD2_MASK, GDK_MOD3_MASK,
1156                 GDK_MOD4_MASK, GDK_MOD5_MASK
1157               };
1158
1159               len -= 6;
1160               accelerator += 4;
1161               mods |= mod_vals[*accelerator - '1'];
1162               accelerator += 2;
1163             }
1164           else if (len >= 5 && is_ctl (accelerator))
1165             {
1166               accelerator += 5;
1167               len -= 5;
1168               mods |= GDK_CONTROL_MASK;
1169             }
1170           else if (len >= 5 && is_alt (accelerator))
1171             {
1172               accelerator += 5;
1173               len -= 5;
1174               mods |= GDK_MOD1_MASK;
1175             }
1176           else if (len >= 6 && is_meta (accelerator))
1177             {
1178               accelerator += 6;
1179               len -= 6;
1180               mods |= GDK_META_MASK;
1181             }
1182           else if (len >= 7 && is_hyper (accelerator))
1183             {
1184               accelerator += 7;
1185               len -= 7;
1186               mods |= GDK_HYPER_MASK;
1187             }
1188           else if (len >= 7 && is_super (accelerator))
1189             {
1190               accelerator += 7;
1191               len -= 7;
1192               mods |= GDK_SUPER_MASK;
1193             }
1194           else
1195             {
1196               gchar last_ch;
1197               
1198               last_ch = *accelerator;
1199               while (last_ch && last_ch != '>')
1200                 {
1201                   last_ch = *accelerator;
1202                   accelerator += 1;
1203                   len -= 1;
1204                 }
1205             }
1206         }
1207       else
1208         {
1209           keyval = gdk_keyval_from_name (accelerator);
1210           accelerator += len;
1211           len -= len;
1212         }
1213     }
1214   
1215   if (accelerator_key)
1216     *accelerator_key = gdk_keyval_to_lower (keyval);
1217   if (accelerator_mods)
1218     *accelerator_mods = mods;
1219 }
1220
1221 /**
1222  * gtk_accelerator_name:
1223  * @accelerator_key:  accelerator keyval
1224  * @accelerator_mods: accelerator modifier mask
1225  * 
1226  * Converts an accelerator keyval and modifier mask
1227  * into a string parseable by gtk_accelerator_parse().
1228  * For example, if you pass in #GDK_q and #GDK_CONTROL_MASK,
1229  * this function returns "&lt;Control&gt;q". 
1230  *
1231  * If you need to display accelerators in the user interface,
1232  * see gtk_accelerator_get_label().
1233  *
1234  * Returns: a newly-allocated accelerator name
1235  */
1236 gchar*
1237 gtk_accelerator_name (guint           accelerator_key,
1238                       GdkModifierType accelerator_mods)
1239 {
1240   static const gchar text_release[] = "<Release>";
1241   static const gchar text_shift[] = "<Shift>";
1242   static const gchar text_control[] = "<Control>";
1243   static const gchar text_mod1[] = "<Alt>";
1244   static const gchar text_mod2[] = "<Mod2>";
1245   static const gchar text_mod3[] = "<Mod3>";
1246   static const gchar text_mod4[] = "<Mod4>";
1247   static const gchar text_mod5[] = "<Mod5>";
1248   static const gchar text_meta[] = "<Meta>";
1249   static const gchar text_super[] = "<Super>";
1250   static const gchar text_hyper[] = "<Hyper>";
1251   guint l;
1252   gchar *keyval_name;
1253   gchar *accelerator;
1254
1255   accelerator_mods &= GDK_MODIFIER_MASK;
1256
1257   keyval_name = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key));
1258   if (!keyval_name)
1259     keyval_name = "";
1260
1261   l = 0;
1262   if (accelerator_mods & GDK_RELEASE_MASK)
1263     l += sizeof (text_release) - 1;
1264   if (accelerator_mods & GDK_SHIFT_MASK)
1265     l += sizeof (text_shift) - 1;
1266   if (accelerator_mods & GDK_CONTROL_MASK)
1267     l += sizeof (text_control) - 1;
1268   if (accelerator_mods & GDK_MOD1_MASK)
1269     l += sizeof (text_mod1) - 1;
1270   if (accelerator_mods & GDK_MOD2_MASK)
1271     l += sizeof (text_mod2) - 1;
1272   if (accelerator_mods & GDK_MOD3_MASK)
1273     l += sizeof (text_mod3) - 1;
1274   if (accelerator_mods & GDK_MOD4_MASK)
1275     l += sizeof (text_mod4) - 1;
1276   if (accelerator_mods & GDK_MOD5_MASK)
1277     l += sizeof (text_mod5) - 1;
1278   l += strlen (keyval_name);
1279   if (accelerator_mods & GDK_META_MASK)
1280     l += sizeof (text_meta) - 1;
1281   if (accelerator_mods & GDK_HYPER_MASK)
1282     l += sizeof (text_hyper) - 1;
1283   if (accelerator_mods & GDK_SUPER_MASK)
1284     l += sizeof (text_super) - 1;
1285
1286   accelerator = g_new (gchar, l + 1);
1287
1288   l = 0;
1289   accelerator[l] = 0;
1290   if (accelerator_mods & GDK_RELEASE_MASK)
1291     {
1292       strcpy (accelerator + l, text_release);
1293       l += sizeof (text_release) - 1;
1294     }
1295   if (accelerator_mods & GDK_SHIFT_MASK)
1296     {
1297       strcpy (accelerator + l, text_shift);
1298       l += sizeof (text_shift) - 1;
1299     }
1300   if (accelerator_mods & GDK_CONTROL_MASK)
1301     {
1302       strcpy (accelerator + l, text_control);
1303       l += sizeof (text_control) - 1;
1304     }
1305   if (accelerator_mods & GDK_MOD1_MASK)
1306     {
1307       strcpy (accelerator + l, text_mod1);
1308       l += sizeof (text_mod1) - 1;
1309     }
1310   if (accelerator_mods & GDK_MOD2_MASK)
1311     {
1312       strcpy (accelerator + l, text_mod2);
1313       l += sizeof (text_mod2) - 1;
1314     }
1315   if (accelerator_mods & GDK_MOD3_MASK)
1316     {
1317       strcpy (accelerator + l, text_mod3);
1318       l += sizeof (text_mod3) - 1;
1319     }
1320   if (accelerator_mods & GDK_MOD4_MASK)
1321     {
1322       strcpy (accelerator + l, text_mod4);
1323       l += sizeof (text_mod4) - 1;
1324     }
1325   if (accelerator_mods & GDK_MOD5_MASK)
1326     {
1327       strcpy (accelerator + l, text_mod5);
1328       l += sizeof (text_mod5) - 1;
1329     }
1330   if (accelerator_mods & GDK_META_MASK)
1331     {
1332       strcpy (accelerator + l, text_meta);
1333       l += sizeof (text_meta) - 1;
1334     }
1335   if (accelerator_mods & GDK_HYPER_MASK)
1336     {
1337       strcpy (accelerator + l, text_hyper);
1338       l += sizeof (text_hyper) - 1;
1339     }
1340   if (accelerator_mods & GDK_SUPER_MASK)
1341     {
1342       strcpy (accelerator + l, text_super);
1343       l += sizeof (text_super) - 1;
1344     }
1345   strcpy (accelerator + l, keyval_name);
1346
1347   return accelerator;
1348 }
1349
1350 /**
1351  * gtk_accelerator_get_label:
1352  * @accelerator_key:  accelerator keyval
1353  * @accelerator_mods: accelerator modifier mask
1354  * 
1355  * Converts an accelerator keyval and modifier mask into a string 
1356  * which can be used to represent the accelerator to the user. 
1357  *
1358  * Returns: a newly-allocated string representing the accelerator.
1359  *
1360  * Since: 2.6
1361  */
1362 gchar*
1363 gtk_accelerator_get_label (guint           accelerator_key,
1364                            GdkModifierType accelerator_mods)
1365 {
1366   GtkAccelLabelClass *klass;
1367   gchar *label;
1368
1369   klass = g_type_class_ref (GTK_TYPE_ACCEL_LABEL);
1370   label = _gtk_accel_label_class_get_accelerator_label (klass, 
1371                                                         accelerator_key, 
1372                                                         accelerator_mods);
1373   g_type_class_unref (klass); /* klass is kept alive since gtk uses static types */
1374
1375   return label;
1376 }  
1377
1378 /**
1379  * gtk_accelerator_set_default_mod_mask:
1380  * @default_mod_mask: accelerator modifier mask
1381  *
1382  * Sets the modifiers that will be considered significant for keyboard
1383  * accelerators. The default mod mask is #GDK_CONTROL_MASK |
1384  * #GDK_SHIFT_MASK | #GDK_MOD1_MASK | #GDK_SUPER_MASK | 
1385  * #GDK_HYPER_MASK | #GDK_META_MASK, that is, Control, Shift, Alt, 
1386  * Super, Hyper and Meta. Other modifiers will by default be ignored 
1387  * by #GtkAccelGroup.
1388  * You must include at least the three modifiers Control, Shift
1389  * and Alt in any value you pass to this function.
1390  *
1391  * The default mod mask should be changed on application startup,
1392  * before using any accelerator groups.
1393  */
1394 void
1395 gtk_accelerator_set_default_mod_mask (GdkModifierType default_mod_mask)
1396 {
1397   default_accel_mod_mask = (default_mod_mask & GDK_MODIFIER_MASK) |
1398     (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK);
1399 }
1400
1401 /**
1402  * gtk_accelerator_get_default_mod_mask:
1403  * @returns: the default accelerator modifier mask
1404  *
1405  * Gets the value set by gtk_accelerator_set_default_mod_mask().
1406  */
1407 guint
1408 gtk_accelerator_get_default_mod_mask (void)
1409 {
1410   return default_accel_mod_mask;
1411 }
1412
1413 #define __GTK_ACCEL_GROUP_C__
1414 #include "gtkaliasdef.c"