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