]> Pileus Git - ~andy/gtk/blob - gtk/gtkaccelgroup.c
a0360055e4d6439db612b44c30ba33ae67780e6a
[~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"
34 #include "gtkaccelmap.h"
35 #include "gtkintl.h"
36 #include "gtkmainprivate.h"
37 #include "gtkmarshalers.h"
38
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 #define GTK_ACCEL_GROUP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_ACCEL_GROUP, GtkAccelGroupPrivate))
66
67 /* --- prototypes --- */
68 static void gtk_accel_group_finalize     (GObject    *object);
69 static void gtk_accel_group_get_property (GObject    *object,
70                                           guint       param_id,
71                                           GValue     *value,
72                                           GParamSpec *pspec);
73 static void accel_closure_invalidate     (gpointer    data,
74                                           GClosure   *closure);
75
76
77 /* --- variables --- */
78 static guint  signal_accel_activate      = 0;
79 static guint  signal_accel_changed       = 0;
80 static guint  quark_acceleratable_groups = 0;
81 static guint  default_accel_mod_mask     = (GDK_SHIFT_MASK   |
82                                             GDK_CONTROL_MASK |
83                                             GDK_MOD1_MASK    |
84                                             GDK_SUPER_MASK   |
85                                             GDK_HYPER_MASK   |
86                                             GDK_META_MASK);
87
88
89 enum {
90   PROP_0,
91   PROP_IS_LOCKED,
92   PROP_MODIFIER_MASK,
93 };
94
95 G_DEFINE_TYPE (GtkAccelGroup, gtk_accel_group, G_TYPE_OBJECT)
96
97 /* --- functions --- */
98 static void
99 gtk_accel_group_class_init (GtkAccelGroupClass *class)
100 {
101   GObjectClass *object_class = G_OBJECT_CLASS (class);
102
103   quark_acceleratable_groups = g_quark_from_static_string ("gtk-acceleratable-accel-groups");
104
105   object_class->finalize = gtk_accel_group_finalize;
106   object_class->get_property = gtk_accel_group_get_property;
107
108   class->accel_changed = NULL;
109
110   g_object_class_install_property (object_class,
111                                    PROP_IS_LOCKED,
112                                    g_param_spec_boolean ("is-locked",
113                                                          "Is locked",
114                                                          "Is the accel group locked",
115                                                          FALSE,
116                                                          G_PARAM_READABLE));
117
118   g_object_class_install_property (object_class,
119                                    PROP_MODIFIER_MASK,
120                                    g_param_spec_flags ("modifier-mask",
121                                                        "Modifier Mask",
122                                                        "Modifier Mask",
123                                                        GDK_TYPE_MODIFIER_TYPE,
124                                                        default_accel_mod_mask,
125                                                        G_PARAM_READABLE));
126
127   /**
128    * GtkAccelGroup::accel-activate:
129    * @accel_group: the #GtkAccelGroup which received the signal
130    * @acceleratable: the object on which the accelerator was activated
131    * @keyval: the accelerator keyval
132    * @modifier: the modifier combination of the accelerator
133    *
134    * The accel-activate signal is an implementation detail of
135    * #GtkAccelGroup and not meant to be used by applications.
136    * 
137    * Returns: %TRUE if the accelerator was activated
138    */
139   signal_accel_activate =
140     g_signal_new (I_("accel-activate"),
141                   G_OBJECT_CLASS_TYPE (class),
142                   G_SIGNAL_DETAILED,
143                   0,
144                   _gtk_boolean_handled_accumulator, NULL,
145                   _gtk_marshal_BOOLEAN__OBJECT_UINT_FLAGS,
146                   G_TYPE_BOOLEAN, 3,
147                   G_TYPE_OBJECT,
148                   G_TYPE_UINT,
149                   GDK_TYPE_MODIFIER_TYPE);
150   /**
151    * GtkAccelGroup::accel-changed:
152    * @accel_group: the #GtkAccelGroup which received the signal
153    * @keyval: the accelerator keyval
154    * @modifier: the modifier combination of the accelerator
155    * @accel_closure: the #GClosure of the accelerator
156    *
157    * The accel-changed signal is emitted when a #GtkAccelGroupEntry
158    * is added to or removed from the accel group. 
159    *
160    * Widgets like #GtkAccelLabel which display an associated 
161    * accelerator should connect to this signal, and rebuild 
162    * their visual representation if the @accel_closure is theirs.
163    */
164   signal_accel_changed =
165     g_signal_new (I_("accel-changed"),
166                   G_OBJECT_CLASS_TYPE (class),
167                   G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
168                   G_STRUCT_OFFSET (GtkAccelGroupClass, accel_changed),
169                   NULL, NULL,
170                   _gtk_marshal_VOID__UINT_FLAGS_BOXED,
171                   G_TYPE_NONE, 3,
172                   G_TYPE_UINT,
173                   GDK_TYPE_MODIFIER_TYPE,
174                   G_TYPE_CLOSURE);
175
176   g_type_class_add_private (object_class, sizeof (GtkAccelGroupPrivate));
177 }
178
179 static void
180 gtk_accel_group_finalize (GObject *object)
181 {
182   GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (object);
183   guint i;
184   
185   for (i = 0; i < accel_group->priv->n_accels; i++)
186     {
187       GtkAccelGroupEntry *entry = &accel_group->priv->priv_accels[i];
188
189       if (entry->accel_path_quark)
190         {
191           const gchar *accel_path = g_quark_to_string (entry->accel_path_quark);
192
193           _gtk_accel_map_remove_group (accel_path, accel_group);
194         }
195       g_closure_remove_invalidate_notifier (entry->closure, accel_group, accel_closure_invalidate);
196
197       /* remove quick_accel_add() refcount */
198       g_closure_unref (entry->closure);
199     }
200
201   g_free (accel_group->priv->priv_accels);
202
203   G_OBJECT_CLASS (gtk_accel_group_parent_class)->finalize (object);
204 }
205
206 static void
207 gtk_accel_group_get_property (GObject    *object,
208                               guint       param_id,
209                               GValue     *value,
210                               GParamSpec *pspec)
211 {
212   GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (object);
213
214   switch (param_id)
215     {
216     case PROP_IS_LOCKED:
217       g_value_set_boolean (value, accel_group->priv->lock_count > 0);
218       break;
219     case PROP_MODIFIER_MASK:
220       g_value_set_flags (value, accel_group->priv->modifier_mask);
221       break;
222     default:
223       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
224       break;
225     }
226 }
227
228 static void
229 gtk_accel_group_init (GtkAccelGroup *accel_group)
230 {
231   GtkAccelGroupPrivate *priv = GTK_ACCEL_GROUP_GET_PRIVATE (accel_group);
232
233   priv->lock_count = 0;
234   priv->modifier_mask = gtk_accelerator_get_default_mod_mask ();
235   priv->acceleratables = NULL;
236   priv->n_accels = 0;
237   priv->priv_accels = NULL;
238
239   accel_group->priv = priv;
240 }
241
242 /**
243  * gtk_accel_group_new:
244  * @returns: a new #GtkAccelGroup object
245  * 
246  * Creates a new #GtkAccelGroup. 
247  */
248 GtkAccelGroup*
249 gtk_accel_group_new (void)
250 {
251   return g_object_new (GTK_TYPE_ACCEL_GROUP, NULL);
252 }
253
254 /**
255  * gtk_accel_group_get_is_locked:
256  * @accel_group: a #GtkAccelGroup
257  *
258  * Locks are added and removed using gtk_accel_group_lock() and
259  * gtk_accel_group_unlock().
260  *
261  * Returns: %TRUE if there are 1 or more locks on the @accel_group,
262  * %FALSE otherwise.
263  *
264  * Since: 2.14
265  */
266 gboolean
267 gtk_accel_group_get_is_locked (GtkAccelGroup *accel_group)
268 {
269   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
270
271   return accel_group->priv->lock_count > 0;
272 }
273
274 /**
275  * gtk_accel_group_get_modifier_mask:
276  * @accel_group: a #GtkAccelGroup
277  *
278  * Gets a #GdkModifierType representing the mask for this
279  * @accel_group. For example, #GDK_CONTROL_MASK, #GDK_SHIFT_MASK, etc.
280  *
281  * Returns: the modifier mask for this accel group.
282  *
283  * Since: 2.14
284  */
285 GdkModifierType
286 gtk_accel_group_get_modifier_mask (GtkAccelGroup *accel_group)
287 {
288   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), 0);
289
290   return accel_group->priv->modifier_mask;
291 }
292
293 static void
294 accel_group_weak_ref_detach (GSList  *free_list,
295                              GObject *stale_object)
296 {
297   GSList *slist;
298   
299   for (slist = free_list; slist; slist = slist->next)
300     {
301       GtkAccelGroup *accel_group;
302       
303       accel_group = slist->data;
304       accel_group->priv->acceleratables = g_slist_remove (accel_group->priv->acceleratables, stale_object);
305       g_object_unref (accel_group);
306     }
307   g_slist_free (free_list);
308   g_object_set_qdata (stale_object, quark_acceleratable_groups, NULL);
309 }
310
311 void
312 _gtk_accel_group_attach (GtkAccelGroup *accel_group,
313                          GObject       *object)
314 {
315   GSList *slist;
316   
317   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
318   g_return_if_fail (G_IS_OBJECT (object));
319   g_return_if_fail (g_slist_find (accel_group->priv->acceleratables, object) == NULL);
320   
321   g_object_ref (accel_group);
322   accel_group->priv->acceleratables = g_slist_prepend (accel_group->priv->acceleratables, object);
323   slist = g_object_get_qdata (object, quark_acceleratable_groups);
324   if (slist)
325     g_object_weak_unref (object,
326                          (GWeakNotify) accel_group_weak_ref_detach,
327                          slist);
328   slist = g_slist_prepend (slist, accel_group);
329   g_object_set_qdata (object, quark_acceleratable_groups, slist);
330   g_object_weak_ref (object,
331                      (GWeakNotify) accel_group_weak_ref_detach,
332                      slist);
333 }
334
335 void
336 _gtk_accel_group_detach (GtkAccelGroup *accel_group,
337                          GObject       *object)
338 {
339   GSList *slist;
340   
341   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
342   g_return_if_fail (G_IS_OBJECT (object));
343   g_return_if_fail (g_slist_find (accel_group->priv->acceleratables, object) != NULL);
344   
345   accel_group->priv->acceleratables = g_slist_remove (accel_group->priv->acceleratables, object);
346   slist = g_object_get_qdata (object, quark_acceleratable_groups);
347   g_object_weak_unref (object,
348                        (GWeakNotify) accel_group_weak_ref_detach,
349                        slist);
350   slist = g_slist_remove (slist, accel_group);
351   g_object_set_qdata (object, quark_acceleratable_groups, slist);
352   if (slist)
353     g_object_weak_ref (object,
354                        (GWeakNotify) accel_group_weak_ref_detach,
355                        slist);
356   g_object_unref (accel_group);
357 }
358
359 /**
360  * gtk_accel_groups_from_object:
361  * @object:        a #GObject, usually a #GtkWindow
362  *
363  * Gets a list of all accel groups which are attached to @object.
364  *
365  * Returns: (element-type GtkAccelGroup) (transfer none): a list of all accel groups which are attached to @object
366  */
367 GSList*
368 gtk_accel_groups_from_object (GObject *object)
369 {
370   g_return_val_if_fail (G_IS_OBJECT (object), NULL);
371   
372   return g_object_get_qdata (object, quark_acceleratable_groups);
373 }
374
375 /**
376  * gtk_accel_group_find:
377  * @accel_group: a #GtkAccelGroup
378  * @find_func: (scope call): a function to filter the entries
379  *    of @accel_group with
380  * @data: data to pass to @find_func
381  * @returns: (transfer none): the key of the first entry passing
382  *    @find_func. The key is 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,
824  *     or %NULL
825  * @returns: (transfer none) (array length=n_entries): an array of
826  *     @n_entries #GtkAccelGroupEntry elements, or %NULL. The array is
827  *     owned by GTK+ and must not be freed.
828  *
829  * Queries an accelerator group for all entries matching @accel_key and 
830  * @accel_mods.
831  */
832 GtkAccelGroupEntry*
833 gtk_accel_group_query (GtkAccelGroup  *accel_group,
834                        guint           accel_key,
835                        GdkModifierType accel_mods,
836                        guint          *n_entries)
837 {
838   GtkAccelGroupEntry *entries;
839   guint n;
840
841   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL);
842
843   entries = quick_accel_find (accel_group, gdk_keyval_to_lower (accel_key), accel_mods, &n);
844
845   if (n_entries)
846     *n_entries = entries ? n : 0;
847
848   return entries;
849 }
850
851 /**
852  * gtk_accel_group_from_accel_closure:
853  * @closure: a #GClosure
854  * @returns: (transfer none): the #GtkAccelGroup to which @closure
855  *     is connected, or %NULL.
856  *
857  * Finds the #GtkAccelGroup to which @closure is connected; 
858  * see gtk_accel_group_connect().
859  */
860 GtkAccelGroup*
861 gtk_accel_group_from_accel_closure (GClosure *closure)
862 {
863   guint i;
864
865   g_return_val_if_fail (closure != NULL, NULL);
866
867   /* a few remarks on what we do here. in general, we need a way to reverse lookup
868    * accel_groups from closures that are being used in accel groups. this could
869    * be done e.g via a hashtable. it is however cheaper (memory wise) to just
870    * use the invalidation notifier on the closure itself (which we need to install
871    * anyway), that contains the accel group as data which, besides needing to peek
872    * a bit at closure internals, works just as good.
873    */
874   for (i = 0; i < G_CLOSURE_N_NOTIFIERS (closure); i++)
875     if (closure->notifiers[i].notify == accel_closure_invalidate)
876       return closure->notifiers[i].data;
877
878   return NULL;
879 }
880
881 /**
882  * gtk_accel_group_activate:
883  * @accel_group:   a #GtkAccelGroup
884  * @accel_quark:   the quark for the accelerator name
885  * @acceleratable: the #GObject, usually a #GtkWindow, on which
886  *                 to activate the accelerator.
887  * @accel_key:     accelerator keyval from a key event
888  * @accel_mods:    keyboard state mask from a key event
889  * 
890  * Finds the first accelerator in @accel_group 
891  * that matches @accel_key and @accel_mods, and
892  * activates it.
893  *
894  * Returns: %TRUE if an accelerator was activated and handled this keypress
895  */
896 gboolean
897 gtk_accel_group_activate (GtkAccelGroup   *accel_group,
898                           GQuark           accel_quark,
899                           GObject         *acceleratable,
900                           guint            accel_key,
901                           GdkModifierType  accel_mods)
902 {
903   gboolean was_handled;
904
905   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
906   g_return_val_if_fail (G_IS_OBJECT (acceleratable), FALSE);
907   
908   was_handled = FALSE;
909   g_signal_emit (accel_group, signal_accel_activate, accel_quark,
910                  acceleratable, accel_key, accel_mods, &was_handled);
911
912   return was_handled;
913 }
914
915 /**
916  * gtk_accel_groups_activate:
917  * @object:        the #GObject, usually a #GtkWindow, on which
918  *                 to activate the accelerator.
919  * @accel_key:     accelerator keyval from a key event
920  * @accel_mods:    keyboard state mask from a key event
921  * 
922  * Finds the first accelerator in any #GtkAccelGroup attached
923  * to @object that matches @accel_key and @accel_mods, and
924  * activates that accelerator.
925  *
926  * Returns: %TRUE if an accelerator was activated and handled this keypress
927  */
928 gboolean
929 gtk_accel_groups_activate (GObject        *object,
930                            guint           accel_key,
931                            GdkModifierType accel_mods)
932 {
933   g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
934   
935   if (gtk_accelerator_valid (accel_key, accel_mods))
936     {
937       gchar *accel_name;
938       GQuark accel_quark;
939       GSList *slist;
940
941       accel_name = gtk_accelerator_name (accel_key, (accel_mods & gtk_accelerator_get_default_mod_mask ()));
942       accel_quark = g_quark_from_string (accel_name);
943       g_free (accel_name);
944       
945       for (slist = gtk_accel_groups_from_object (object); slist; slist = slist->next)
946         if (gtk_accel_group_activate (slist->data, accel_quark, object, accel_key, accel_mods))
947           return TRUE;
948     }
949   
950   return FALSE;
951 }
952
953 /**
954  * gtk_accelerator_valid:
955  * @keyval:    a GDK keyval
956  * @modifiers: modifier mask
957  * @returns:   %TRUE if the accelerator is valid
958  *
959  * Determines whether a given keyval and modifier mask constitute
960  * a valid keyboard accelerator. For example, the #GDK_KEY_a keyval
961  * plus #GDK_CONTROL_MASK is valid - this is a "Ctrl+a" accelerator.
962  * But, you can't, for instance, use the #GDK_KEY_Control_L keyval
963  * as an accelerator.
964  */
965 gboolean
966 gtk_accelerator_valid (guint              keyval,
967                        GdkModifierType    modifiers)
968 {
969   static const guint invalid_accelerator_vals[] = {
970     GDK_KEY_Shift_L, GDK_KEY_Shift_R, GDK_KEY_Shift_Lock, GDK_KEY_Caps_Lock, GDK_KEY_ISO_Lock,
971     GDK_KEY_Control_L, GDK_KEY_Control_R, GDK_KEY_Meta_L, GDK_KEY_Meta_R,
972     GDK_KEY_Alt_L, GDK_KEY_Alt_R, GDK_KEY_Super_L, GDK_KEY_Super_R, GDK_KEY_Hyper_L, GDK_KEY_Hyper_R,
973     GDK_KEY_ISO_Level3_Shift, GDK_KEY_ISO_Next_Group, GDK_KEY_ISO_Prev_Group,
974     GDK_KEY_ISO_First_Group, GDK_KEY_ISO_Last_Group,
975     GDK_KEY_Mode_switch, GDK_KEY_Num_Lock, GDK_KEY_Multi_key,
976     GDK_KEY_Scroll_Lock, GDK_KEY_Sys_Req, 
977     GDK_KEY_Tab, GDK_KEY_ISO_Left_Tab, GDK_KEY_KP_Tab,
978     GDK_KEY_First_Virtual_Screen, GDK_KEY_Prev_Virtual_Screen,
979     GDK_KEY_Next_Virtual_Screen, GDK_KEY_Last_Virtual_Screen,
980     GDK_KEY_Terminate_Server, GDK_KEY_AudibleBell_Enable,
981     0
982   };
983   static const guint invalid_unmodified_vals[] = {
984     GDK_KEY_Up, GDK_KEY_Down, GDK_KEY_Left, GDK_KEY_Right,
985     GDK_KEY_KP_Up, GDK_KEY_KP_Down, GDK_KEY_KP_Left, GDK_KEY_KP_Right,
986     0
987   };
988   const guint *ac_val;
989
990   modifiers &= GDK_MODIFIER_MASK;
991     
992   if (keyval <= 0xFF)
993     return keyval >= 0x20;
994
995   ac_val = invalid_accelerator_vals;
996   while (*ac_val)
997     {
998       if (keyval == *ac_val++)
999         return FALSE;
1000     }
1001
1002   if (!modifiers)
1003     {
1004       ac_val = invalid_unmodified_vals;
1005       while (*ac_val)
1006         {
1007           if (keyval == *ac_val++)
1008             return FALSE;
1009         }
1010     }
1011   
1012   return TRUE;
1013 }
1014
1015 static inline gboolean
1016 is_alt (const gchar *string)
1017 {
1018   return ((string[0] == '<') &&
1019           (string[1] == 'a' || string[1] == 'A') &&
1020           (string[2] == 'l' || string[2] == 'L') &&
1021           (string[3] == 't' || string[3] == 'T') &&
1022           (string[4] == '>'));
1023 }
1024
1025 static inline gboolean
1026 is_ctl (const gchar *string)
1027 {
1028   return ((string[0] == '<') &&
1029           (string[1] == 'c' || string[1] == 'C') &&
1030           (string[2] == 't' || string[2] == 'T') &&
1031           (string[3] == 'l' || string[3] == 'L') &&
1032           (string[4] == '>'));
1033 }
1034
1035 static inline gboolean
1036 is_modx (const gchar *string)
1037 {
1038   return ((string[0] == '<') &&
1039           (string[1] == 'm' || string[1] == 'M') &&
1040           (string[2] == 'o' || string[2] == 'O') &&
1041           (string[3] == 'd' || string[3] == 'D') &&
1042           (string[4] >= '1' && string[4] <= '5') &&
1043           (string[5] == '>'));
1044 }
1045
1046 static inline gboolean
1047 is_ctrl (const gchar *string)
1048 {
1049   return ((string[0] == '<') &&
1050           (string[1] == 'c' || string[1] == 'C') &&
1051           (string[2] == 't' || string[2] == 'T') &&
1052           (string[3] == 'r' || string[3] == 'R') &&
1053           (string[4] == 'l' || string[4] == 'L') &&
1054           (string[5] == '>'));
1055 }
1056
1057 static inline gboolean
1058 is_shft (const gchar *string)
1059 {
1060   return ((string[0] == '<') &&
1061           (string[1] == 's' || string[1] == 'S') &&
1062           (string[2] == 'h' || string[2] == 'H') &&
1063           (string[3] == 'f' || string[3] == 'F') &&
1064           (string[4] == 't' || string[4] == 'T') &&
1065           (string[5] == '>'));
1066 }
1067
1068 static inline gboolean
1069 is_shift (const gchar *string)
1070 {
1071   return ((string[0] == '<') &&
1072           (string[1] == 's' || string[1] == 'S') &&
1073           (string[2] == 'h' || string[2] == 'H') &&
1074           (string[3] == 'i' || string[3] == 'I') &&
1075           (string[4] == 'f' || string[4] == 'F') &&
1076           (string[5] == 't' || string[5] == 'T') &&
1077           (string[6] == '>'));
1078 }
1079
1080 static inline gboolean
1081 is_control (const gchar *string)
1082 {
1083   return ((string[0] == '<') &&
1084           (string[1] == 'c' || string[1] == 'C') &&
1085           (string[2] == 'o' || string[2] == 'O') &&
1086           (string[3] == 'n' || string[3] == 'N') &&
1087           (string[4] == 't' || string[4] == 'T') &&
1088           (string[5] == 'r' || string[5] == 'R') &&
1089           (string[6] == 'o' || string[6] == 'O') &&
1090           (string[7] == 'l' || string[7] == 'L') &&
1091           (string[8] == '>'));
1092 }
1093
1094 static inline gboolean
1095 is_release (const gchar *string)
1096 {
1097   return ((string[0] == '<') &&
1098           (string[1] == 'r' || string[1] == 'R') &&
1099           (string[2] == 'e' || string[2] == 'E') &&
1100           (string[3] == 'l' || string[3] == 'L') &&
1101           (string[4] == 'e' || string[4] == 'E') &&
1102           (string[5] == 'a' || string[5] == 'A') &&
1103           (string[6] == 's' || string[6] == 'S') &&
1104           (string[7] == 'e' || string[7] == 'E') &&
1105           (string[8] == '>'));
1106 }
1107
1108 static inline gboolean
1109 is_meta (const gchar *string)
1110 {
1111   return ((string[0] == '<') &&
1112           (string[1] == 'm' || string[1] == 'M') &&
1113           (string[2] == 'e' || string[2] == 'E') &&
1114           (string[3] == 't' || string[3] == 'T') &&
1115           (string[4] == 'a' || string[4] == 'A') &&
1116           (string[5] == '>'));
1117 }
1118
1119 static inline gboolean
1120 is_super (const gchar *string)
1121 {
1122   return ((string[0] == '<') &&
1123           (string[1] == 's' || string[1] == 'S') &&
1124           (string[2] == 'u' || string[2] == 'U') &&
1125           (string[3] == 'p' || string[3] == 'P') &&
1126           (string[4] == 'e' || string[4] == 'E') &&
1127           (string[5] == 'r' || string[5] == 'R') &&
1128           (string[6] == '>'));
1129 }
1130
1131 static inline gboolean
1132 is_hyper (const gchar *string)
1133 {
1134   return ((string[0] == '<') &&
1135           (string[1] == 'h' || string[1] == 'H') &&
1136           (string[2] == 'y' || string[2] == 'Y') &&
1137           (string[3] == 'p' || string[3] == 'P') &&
1138           (string[4] == 'e' || string[4] == 'E') &&
1139           (string[5] == 'r' || string[5] == 'R') &&
1140           (string[6] == '>'));
1141 }
1142
1143 /**
1144  * gtk_accelerator_parse:
1145  * @accelerator:      string representing an accelerator
1146  * @accelerator_key: (out):  return location for accelerator keyval
1147  * @accelerator_mods: (out): return location for accelerator modifier mask
1148  *
1149  * Parses a string representing an accelerator. The
1150  * format looks like "&lt;Control&gt;a" or "&lt;Shift&gt;&lt;Alt&gt;F1" or
1151  * "&lt;Release&gt;z" (the last one is for key release).
1152  * The parser is fairly liberal and allows lower or upper case,
1153  * and also abbreviations such as "&lt;Ctl&gt;" and "&lt;Ctrl&gt;".
1154  * Key names are parsed using gdk_keyval_from_name(). For character keys the
1155  * name is not the symbol, but the lowercase name, e.g. one would use
1156  * "&lt;Ctrl&gt;minus" instead of "&lt;Ctrl&gt;-".
1157  *
1158  * If the parse fails, @accelerator_key and @accelerator_mods will
1159  * be set to 0 (zero).
1160  */
1161 void
1162 gtk_accelerator_parse (const gchar     *accelerator,
1163                        guint           *accelerator_key,
1164                        GdkModifierType *accelerator_mods)
1165 {
1166   guint keyval;
1167   GdkModifierType mods;
1168   gint len;
1169   
1170   if (accelerator_key)
1171     *accelerator_key = 0;
1172   if (accelerator_mods)
1173     *accelerator_mods = 0;
1174   g_return_if_fail (accelerator != NULL);
1175   
1176   keyval = 0;
1177   mods = 0;
1178   len = strlen (accelerator);
1179   while (len)
1180     {
1181       if (*accelerator == '<')
1182         {
1183           if (len >= 9 && is_release (accelerator))
1184             {
1185               accelerator += 9;
1186               len -= 9;
1187               mods |= GDK_RELEASE_MASK;
1188             }
1189           else if (len >= 9 && is_control (accelerator))
1190             {
1191               accelerator += 9;
1192               len -= 9;
1193               mods |= GDK_CONTROL_MASK;
1194             }
1195           else if (len >= 7 && is_shift (accelerator))
1196             {
1197               accelerator += 7;
1198               len -= 7;
1199               mods |= GDK_SHIFT_MASK;
1200             }
1201           else if (len >= 6 && is_shft (accelerator))
1202             {
1203               accelerator += 6;
1204               len -= 6;
1205               mods |= GDK_SHIFT_MASK;
1206             }
1207           else if (len >= 6 && is_ctrl (accelerator))
1208             {
1209               accelerator += 6;
1210               len -= 6;
1211               mods |= GDK_CONTROL_MASK;
1212             }
1213           else if (len >= 6 && is_modx (accelerator))
1214             {
1215               static const guint mod_vals[] = {
1216                 GDK_MOD1_MASK, GDK_MOD2_MASK, GDK_MOD3_MASK,
1217                 GDK_MOD4_MASK, GDK_MOD5_MASK
1218               };
1219
1220               len -= 6;
1221               accelerator += 4;
1222               mods |= mod_vals[*accelerator - '1'];
1223               accelerator += 2;
1224             }
1225           else if (len >= 5 && is_ctl (accelerator))
1226             {
1227               accelerator += 5;
1228               len -= 5;
1229               mods |= GDK_CONTROL_MASK;
1230             }
1231           else if (len >= 5 && is_alt (accelerator))
1232             {
1233               accelerator += 5;
1234               len -= 5;
1235               mods |= GDK_MOD1_MASK;
1236             }
1237           else if (len >= 6 && is_meta (accelerator))
1238             {
1239               accelerator += 6;
1240               len -= 6;
1241               mods |= GDK_META_MASK;
1242             }
1243           else if (len >= 7 && is_hyper (accelerator))
1244             {
1245               accelerator += 7;
1246               len -= 7;
1247               mods |= GDK_HYPER_MASK;
1248             }
1249           else if (len >= 7 && is_super (accelerator))
1250             {
1251               accelerator += 7;
1252               len -= 7;
1253               mods |= GDK_SUPER_MASK;
1254             }
1255           else
1256             {
1257               gchar last_ch;
1258               
1259               last_ch = *accelerator;
1260               while (last_ch && last_ch != '>')
1261                 {
1262                   last_ch = *accelerator;
1263                   accelerator += 1;
1264                   len -= 1;
1265                 }
1266             }
1267         }
1268       else
1269         {
1270           keyval = gdk_keyval_from_name (accelerator);
1271           accelerator += len;
1272           len -= len;
1273         }
1274     }
1275   
1276   if (accelerator_key)
1277     *accelerator_key = gdk_keyval_to_lower (keyval);
1278   if (accelerator_mods)
1279     *accelerator_mods = mods;
1280 }
1281
1282 /**
1283  * gtk_accelerator_name:
1284  * @accelerator_key:  accelerator keyval
1285  * @accelerator_mods: accelerator modifier mask
1286  *
1287  * Converts an accelerator keyval and modifier mask
1288  * into a string parseable by gtk_accelerator_parse().
1289  * For example, if you pass in #GDK_KEY_q and #GDK_CONTROL_MASK,
1290  * this function returns "&lt;Control&gt;q".
1291  *
1292  * If you need to display accelerators in the user interface,
1293  * see gtk_accelerator_get_label().
1294  *
1295  * Returns: a newly-allocated accelerator name
1296  */
1297 gchar*
1298 gtk_accelerator_name (guint           accelerator_key,
1299                       GdkModifierType accelerator_mods)
1300 {
1301   static const gchar text_release[] = "<Release>";
1302   static const gchar text_shift[] = "<Shift>";
1303   static const gchar text_control[] = "<Control>";
1304   static const gchar text_mod1[] = "<Alt>";
1305   static const gchar text_mod2[] = "<Mod2>";
1306   static const gchar text_mod3[] = "<Mod3>";
1307   static const gchar text_mod4[] = "<Mod4>";
1308   static const gchar text_mod5[] = "<Mod5>";
1309   static const gchar text_meta[] = "<Meta>";
1310   static const gchar text_super[] = "<Super>";
1311   static const gchar text_hyper[] = "<Hyper>";
1312   guint l;
1313   gchar *keyval_name;
1314   gchar *accelerator;
1315
1316   accelerator_mods &= GDK_MODIFIER_MASK;
1317
1318   keyval_name = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key));
1319   if (!keyval_name)
1320     keyval_name = "";
1321
1322   l = 0;
1323   if (accelerator_mods & GDK_RELEASE_MASK)
1324     l += sizeof (text_release) - 1;
1325   if (accelerator_mods & GDK_SHIFT_MASK)
1326     l += sizeof (text_shift) - 1;
1327   if (accelerator_mods & GDK_CONTROL_MASK)
1328     l += sizeof (text_control) - 1;
1329   if (accelerator_mods & GDK_MOD1_MASK)
1330     l += sizeof (text_mod1) - 1;
1331   if (accelerator_mods & GDK_MOD2_MASK)
1332     l += sizeof (text_mod2) - 1;
1333   if (accelerator_mods & GDK_MOD3_MASK)
1334     l += sizeof (text_mod3) - 1;
1335   if (accelerator_mods & GDK_MOD4_MASK)
1336     l += sizeof (text_mod4) - 1;
1337   if (accelerator_mods & GDK_MOD5_MASK)
1338     l += sizeof (text_mod5) - 1;
1339   l += strlen (keyval_name);
1340   if (accelerator_mods & GDK_META_MASK)
1341     l += sizeof (text_meta) - 1;
1342   if (accelerator_mods & GDK_HYPER_MASK)
1343     l += sizeof (text_hyper) - 1;
1344   if (accelerator_mods & GDK_SUPER_MASK)
1345     l += sizeof (text_super) - 1;
1346
1347   accelerator = g_new (gchar, l + 1);
1348
1349   l = 0;
1350   accelerator[l] = 0;
1351   if (accelerator_mods & GDK_RELEASE_MASK)
1352     {
1353       strcpy (accelerator + l, text_release);
1354       l += sizeof (text_release) - 1;
1355     }
1356   if (accelerator_mods & GDK_SHIFT_MASK)
1357     {
1358       strcpy (accelerator + l, text_shift);
1359       l += sizeof (text_shift) - 1;
1360     }
1361   if (accelerator_mods & GDK_CONTROL_MASK)
1362     {
1363       strcpy (accelerator + l, text_control);
1364       l += sizeof (text_control) - 1;
1365     }
1366   if (accelerator_mods & GDK_MOD1_MASK)
1367     {
1368       strcpy (accelerator + l, text_mod1);
1369       l += sizeof (text_mod1) - 1;
1370     }
1371   if (accelerator_mods & GDK_MOD2_MASK)
1372     {
1373       strcpy (accelerator + l, text_mod2);
1374       l += sizeof (text_mod2) - 1;
1375     }
1376   if (accelerator_mods & GDK_MOD3_MASK)
1377     {
1378       strcpy (accelerator + l, text_mod3);
1379       l += sizeof (text_mod3) - 1;
1380     }
1381   if (accelerator_mods & GDK_MOD4_MASK)
1382     {
1383       strcpy (accelerator + l, text_mod4);
1384       l += sizeof (text_mod4) - 1;
1385     }
1386   if (accelerator_mods & GDK_MOD5_MASK)
1387     {
1388       strcpy (accelerator + l, text_mod5);
1389       l += sizeof (text_mod5) - 1;
1390     }
1391   if (accelerator_mods & GDK_META_MASK)
1392     {
1393       strcpy (accelerator + l, text_meta);
1394       l += sizeof (text_meta) - 1;
1395     }
1396   if (accelerator_mods & GDK_HYPER_MASK)
1397     {
1398       strcpy (accelerator + l, text_hyper);
1399       l += sizeof (text_hyper) - 1;
1400     }
1401   if (accelerator_mods & GDK_SUPER_MASK)
1402     {
1403       strcpy (accelerator + l, text_super);
1404       l += sizeof (text_super) - 1;
1405     }
1406   strcpy (accelerator + l, keyval_name);
1407
1408   return accelerator;
1409 }
1410
1411 /**
1412  * gtk_accelerator_get_label:
1413  * @accelerator_key:  accelerator keyval
1414  * @accelerator_mods: accelerator modifier mask
1415  * 
1416  * Converts an accelerator keyval and modifier mask into a string 
1417  * which can be used to represent the accelerator to the user. 
1418  *
1419  * Returns: a newly-allocated string representing the accelerator.
1420  *
1421  * Since: 2.6
1422  */
1423 gchar*
1424 gtk_accelerator_get_label (guint           accelerator_key,
1425                            GdkModifierType accelerator_mods)
1426 {
1427   GtkAccelLabelClass *klass;
1428   gchar *label;
1429
1430   klass = g_type_class_ref (GTK_TYPE_ACCEL_LABEL);
1431   label = _gtk_accel_label_class_get_accelerator_label (klass, 
1432                                                         accelerator_key, 
1433                                                         accelerator_mods);
1434   g_type_class_unref (klass); /* klass is kept alive since gtk uses static types */
1435
1436   return label;
1437 }  
1438
1439 /**
1440  * gtk_accelerator_set_default_mod_mask:
1441  * @default_mod_mask: accelerator modifier mask
1442  *
1443  * Sets the modifiers that will be considered significant for keyboard
1444  * accelerators. The default mod mask is #GDK_CONTROL_MASK |
1445  * #GDK_SHIFT_MASK | #GDK_MOD1_MASK | #GDK_SUPER_MASK | 
1446  * #GDK_HYPER_MASK | #GDK_META_MASK, that is, Control, Shift, Alt, 
1447  * Super, Hyper and Meta. Other modifiers will by default be ignored 
1448  * by #GtkAccelGroup.
1449  * You must include at least the three modifiers Control, Shift
1450  * and Alt in any value you pass to this function.
1451  *
1452  * The default mod mask should be changed on application startup,
1453  * before using any accelerator groups.
1454  */
1455 void
1456 gtk_accelerator_set_default_mod_mask (GdkModifierType default_mod_mask)
1457 {
1458   default_accel_mod_mask = (default_mod_mask & GDK_MODIFIER_MASK) |
1459     (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK);
1460 }
1461
1462 /**
1463  * gtk_accelerator_get_default_mod_mask:
1464  * @returns: the default accelerator modifier mask
1465  *
1466  * Gets the value set by gtk_accelerator_set_default_mod_mask().
1467  */
1468 GdkModifierType
1469 gtk_accelerator_get_default_mod_mask (void)
1470 {
1471   return default_accel_mod_mask;
1472 }