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