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