]> Pileus Git - ~andy/gtk/blob - gtk/gtkaccelgroup.c
Fix 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"
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
43  *     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 #GtkUIManager, GTK+
52  * automatically sets up the accelerators for your menus in the ui
53  * manager's #GtkAccelGroup.
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
366  *     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: (scope call): a function to filter the entries
380  *    of @accel_group with
381  * @data: data to pass to @find_func
382  * @returns: (transfer none): the key of the first entry passing
383  *    @find_func. The key is owned by GTK+ and must not be freed.
384  *
385  * Finds the first entry in an accelerator group for which
386  * @find_func returns %TRUE and returns its #GtkAccelKey.
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
621  * activated in response to a call to gtk_accel_groups_activate(),
622  * @closure will be invoked if the @accel_key and @accel_mods from
623  * gtk_accel_groups_activate() 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
628  * only be 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
657  * to look up the appropriate key and modifiers (see
658  * gtk_accel_map_add_entry()). When @accel_group is being activated
659  * in response to a call to gtk_accel_groups_activate(), @closure will
660  * be invoked if the @accel_key and @accel_mods from
661  * gtk_accel_groups_activate() match the key and modifiers 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,
666  * if you pass a static string, you can save some memory by interning it
667  * first with 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
703  *     group, or %NULL 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
824  *     of entries found, or %NULL
825  * @returns: (transfer none) (array length=n_entries): an array of
826  *     @n_entries #GtkAccelGroupEntry elements, or %NULL. The array
827  *     is owned by GTK+ and must not be freed.
828  *
829  * Queries an accelerator group for all entries matching @accel_key
830  * and @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
868    * reverse lookup accel_groups from closures that are being used in
869    * accel groups. this could be done e.g via a hashtable. it is however
870    * cheaper (memory wise) to just use the invalidation notifier on the
871    * closure itself (which we need to install anyway), that contains the
872    * accel group as data which, besides needing to peek a bit at closure
873    * internals, works just as good.
874    */
875   for (i = 0; i < G_CLOSURE_N_NOTIFIERS (closure); i++)
876     if (closure->notifiers[i].notify == accel_closure_invalidate)
877       return closure->notifiers[i].data;
878
879   return NULL;
880 }
881
882 /**
883  * gtk_accel_group_activate:
884  * @accel_group: a #GtkAccelGroup
885  * @accel_quark: the quark for the accelerator name
886  * @acceleratable: the #GObject, usually a #GtkWindow, on which
887  *    to activate the accelerator
888  * @accel_key: accelerator keyval from a key event
889  * @accel_mods: keyboard state mask from a key event
890  *
891  * Finds the first accelerator in @accel_group that matches
892  * @accel_key and @accel_mods, and activates it.
893  *
894  * Returns: %TRUE if an accelerator was activated and handled
895  *     this keypress
896  */
897 gboolean
898 gtk_accel_group_activate (GtkAccelGroup   *accel_group,
899                           GQuark           accel_quark,
900                           GObject         *acceleratable,
901                           guint            accel_key,
902                           GdkModifierType  accel_mods)
903 {
904   gboolean was_handled;
905
906   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
907   g_return_val_if_fail (G_IS_OBJECT (acceleratable), FALSE);
908
909   was_handled = FALSE;
910   g_signal_emit (accel_group, signal_accel_activate, accel_quark,
911                  acceleratable, accel_key, accel_mods, &was_handled);
912
913   return was_handled;
914 }
915
916 /**
917  * gtk_accel_groups_activate:
918  * @object: the #GObject, usually a #GtkWindow, on which
919  *     to activate the accelerator
920  * @accel_key: accelerator keyval from a key event
921  * @accel_mods: keyboard state mask from a key event
922  *
923  * Finds the first accelerator in any #GtkAccelGroup attached
924  * to @object that matches @accel_key and @accel_mods, and
925  * activates that accelerator.
926  *
927  * Returns: %TRUE if an accelerator was activated and handled
928  *     this keypress
929  */
930 gboolean
931 gtk_accel_groups_activate (GObject         *object,
932                            guint            accel_key,
933                            GdkModifierType  accel_mods)
934 {
935   g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
936
937   if (gtk_accelerator_valid (accel_key, accel_mods))
938     {
939       gchar *accel_name;
940       GQuark accel_quark;
941       GSList *slist;
942
943       accel_name = gtk_accelerator_name (accel_key, (accel_mods & gtk_accelerator_get_default_mod_mask ()));
944       accel_quark = g_quark_from_string (accel_name);
945       g_free (accel_name);
946
947       for (slist = gtk_accel_groups_from_object (object); slist; slist = slist->next)
948         if (gtk_accel_group_activate (slist->data, accel_quark, object, accel_key, accel_mods))
949           return TRUE;
950     }
951
952   return FALSE;
953 }
954
955 /**
956  * gtk_accelerator_valid:
957  * @keyval: a GDK keyval
958  * @modifiers: modifier mask
959  * @returns: %TRUE if the accelerator is valid
960  *
961  * Determines whether a given keyval and modifier mask constitute
962  * a valid keyboard accelerator. For example, the #GDK_KEY_a keyval
963  * plus #GDK_CONTROL_MASK is valid - this is a "Ctrl+a" accelerator.
964  * But, you can't, for instance, use the #GDK_KEY_Control_L keyval
965  * as an accelerator.
966  */
967 gboolean
968 gtk_accelerator_valid (guint           keyval,
969                        GdkModifierType modifiers)
970 {
971   static const guint invalid_accelerator_vals[] = {
972     GDK_KEY_Shift_L, GDK_KEY_Shift_R, GDK_KEY_Shift_Lock,
973     GDK_KEY_Caps_Lock, GDK_KEY_ISO_Lock, GDK_KEY_Control_L,
974     GDK_KEY_Control_R, GDK_KEY_Meta_L, GDK_KEY_Meta_R,
975     GDK_KEY_Alt_L, GDK_KEY_Alt_R, GDK_KEY_Super_L, GDK_KEY_Super_R,
976     GDK_KEY_Hyper_L, GDK_KEY_Hyper_R, GDK_KEY_ISO_Level3_Shift,
977     GDK_KEY_ISO_Next_Group, GDK_KEY_ISO_Prev_Group,
978     GDK_KEY_ISO_First_Group, GDK_KEY_ISO_Last_Group,
979     GDK_KEY_Mode_switch, GDK_KEY_Num_Lock, GDK_KEY_Multi_key,
980     GDK_KEY_Scroll_Lock, GDK_KEY_Sys_Req,
981     GDK_KEY_Tab, GDK_KEY_ISO_Left_Tab, GDK_KEY_KP_Tab,
982     GDK_KEY_First_Virtual_Screen, GDK_KEY_Prev_Virtual_Screen,
983     GDK_KEY_Next_Virtual_Screen, GDK_KEY_Last_Virtual_Screen,
984     GDK_KEY_Terminate_Server, GDK_KEY_AudibleBell_Enable,
985     0
986   };
987   static const guint invalid_unmodified_vals[] = {
988     GDK_KEY_Up, GDK_KEY_Down, GDK_KEY_Left, GDK_KEY_Right,
989     GDK_KEY_KP_Up, GDK_KEY_KP_Down, GDK_KEY_KP_Left, GDK_KEY_KP_Right,
990     0
991   };
992   const guint *ac_val;
993
994   modifiers &= GDK_MODIFIER_MASK;
995
996   if (keyval <= 0xFF)
997     return keyval >= 0x20;
998
999   ac_val = invalid_accelerator_vals;
1000   while (*ac_val)
1001     {
1002       if (keyval == *ac_val++)
1003         return FALSE;
1004     }
1005
1006   if (!modifiers)
1007     {
1008       ac_val = invalid_unmodified_vals;
1009       while (*ac_val)
1010         {
1011           if (keyval == *ac_val++)
1012             return FALSE;
1013         }
1014     }
1015
1016   return TRUE;
1017 }
1018
1019 static inline gboolean
1020 is_alt (const gchar *string)
1021 {
1022   return ((string[0] == '<') &&
1023           (string[1] == 'a' || string[1] == 'A') &&
1024           (string[2] == 'l' || string[2] == 'L') &&
1025           (string[3] == 't' || string[3] == 'T') &&
1026           (string[4] == '>'));
1027 }
1028
1029 static inline gboolean
1030 is_ctl (const gchar *string)
1031 {
1032   return ((string[0] == '<') &&
1033           (string[1] == 'c' || string[1] == 'C') &&
1034           (string[2] == 't' || string[2] == 'T') &&
1035           (string[3] == 'l' || string[3] == 'L') &&
1036           (string[4] == '>'));
1037 }
1038
1039 static inline gboolean
1040 is_modx (const gchar *string)
1041 {
1042   return ((string[0] == '<') &&
1043           (string[1] == 'm' || string[1] == 'M') &&
1044           (string[2] == 'o' || string[2] == 'O') &&
1045           (string[3] == 'd' || string[3] == 'D') &&
1046           (string[4] >= '1' && string[4] <= '5') &&
1047           (string[5] == '>'));
1048 }
1049
1050 static inline gboolean
1051 is_ctrl (const gchar *string)
1052 {
1053   return ((string[0] == '<') &&
1054           (string[1] == 'c' || string[1] == 'C') &&
1055           (string[2] == 't' || string[2] == 'T') &&
1056           (string[3] == 'r' || string[3] == 'R') &&
1057           (string[4] == 'l' || string[4] == 'L') &&
1058           (string[5] == '>'));
1059 }
1060
1061 static inline gboolean
1062 is_shft (const gchar *string)
1063 {
1064   return ((string[0] == '<') &&
1065           (string[1] == 's' || string[1] == 'S') &&
1066           (string[2] == 'h' || string[2] == 'H') &&
1067           (string[3] == 'f' || string[3] == 'F') &&
1068           (string[4] == 't' || string[4] == 'T') &&
1069           (string[5] == '>'));
1070 }
1071
1072 static inline gboolean
1073 is_shift (const gchar *string)
1074 {
1075   return ((string[0] == '<') &&
1076           (string[1] == 's' || string[1] == 'S') &&
1077           (string[2] == 'h' || string[2] == 'H') &&
1078           (string[3] == 'i' || string[3] == 'I') &&
1079           (string[4] == 'f' || string[4] == 'F') &&
1080           (string[5] == 't' || string[5] == 'T') &&
1081           (string[6] == '>'));
1082 }
1083
1084 static inline gboolean
1085 is_control (const gchar *string)
1086 {
1087   return ((string[0] == '<') &&
1088           (string[1] == 'c' || string[1] == 'C') &&
1089           (string[2] == 'o' || string[2] == 'O') &&
1090           (string[3] == 'n' || string[3] == 'N') &&
1091           (string[4] == 't' || string[4] == 'T') &&
1092           (string[5] == 'r' || string[5] == 'R') &&
1093           (string[6] == 'o' || string[6] == 'O') &&
1094           (string[7] == 'l' || string[7] == 'L') &&
1095           (string[8] == '>'));
1096 }
1097
1098 static inline gboolean
1099 is_release (const gchar *string)
1100 {
1101   return ((string[0] == '<') &&
1102           (string[1] == 'r' || string[1] == 'R') &&
1103           (string[2] == 'e' || string[2] == 'E') &&
1104           (string[3] == 'l' || string[3] == 'L') &&
1105           (string[4] == 'e' || string[4] == 'E') &&
1106           (string[5] == 'a' || string[5] == 'A') &&
1107           (string[6] == 's' || string[6] == 'S') &&
1108           (string[7] == 'e' || string[7] == 'E') &&
1109           (string[8] == '>'));
1110 }
1111
1112 static inline gboolean
1113 is_meta (const gchar *string)
1114 {
1115   return ((string[0] == '<') &&
1116           (string[1] == 'm' || string[1] == 'M') &&
1117           (string[2] == 'e' || string[2] == 'E') &&
1118           (string[3] == 't' || string[3] == 'T') &&
1119           (string[4] == 'a' || string[4] == 'A') &&
1120           (string[5] == '>'));
1121 }
1122
1123 static inline gboolean
1124 is_super (const gchar *string)
1125 {
1126   return ((string[0] == '<') &&
1127           (string[1] == 's' || string[1] == 'S') &&
1128           (string[2] == 'u' || string[2] == 'U') &&
1129           (string[3] == 'p' || string[3] == 'P') &&
1130           (string[4] == 'e' || string[4] == 'E') &&
1131           (string[5] == 'r' || string[5] == 'R') &&
1132           (string[6] == '>'));
1133 }
1134
1135 static inline gboolean
1136 is_hyper (const gchar *string)
1137 {
1138   return ((string[0] == '<') &&
1139           (string[1] == 'h' || string[1] == 'H') &&
1140           (string[2] == 'y' || string[2] == 'Y') &&
1141           (string[3] == 'p' || string[3] == 'P') &&
1142           (string[4] == 'e' || string[4] == 'E') &&
1143           (string[5] == 'r' || string[5] == 'R') &&
1144           (string[6] == '>'));
1145 }
1146
1147 /**
1148  * gtk_accelerator_parse:
1149  * @accelerator: string representing an accelerator
1150  * @accelerator_key: (out) (allow-none): return location for accelerator
1151  *     keyval, or %NULL
1152  * @accelerator_mods: (out) (allow-none): return location for accelerator
1153  *     modifier mask, %NULL
1154  *
1155  * Parses a string representing an accelerator. The
1156  * format looks like "&lt;Control&gt;a" or "&lt;Shift&gt;&lt;Alt&gt;F1"
1157  * or "&lt;Release&gt;z" (the last one is for key release).
1158  *
1159  * The parser is fairly liberal and allows lower or upper case,
1160  * and also abbreviations such as "&lt;Ctl&gt;" and "&lt;Ctrl&gt;".
1161  * Key names are parsed using gdk_keyval_from_name(). For character
1162  * keys the name is not the symbol, but the lowercase name, e.g. one
1163  * would use "&lt;Ctrl&gt;minus" instead of "&lt;Ctrl&gt;-".
1164  *
1165  * If the parse fails, @accelerator_key and @accelerator_mods will
1166  * be set to 0 (zero).
1167  */
1168 void
1169 gtk_accelerator_parse (const gchar     *accelerator,
1170                        guint           *accelerator_key,
1171                        GdkModifierType *accelerator_mods)
1172 {
1173   guint keyval;
1174   GdkModifierType mods;
1175   gint len;
1176
1177   if (accelerator_key)
1178     *accelerator_key = 0;
1179   if (accelerator_mods)
1180     *accelerator_mods = 0;
1181   g_return_if_fail (accelerator != NULL);
1182
1183   keyval = 0;
1184   mods = 0;
1185   len = strlen (accelerator);
1186   while (len)
1187     {
1188       if (*accelerator == '<')
1189         {
1190           if (len >= 9 && is_release (accelerator))
1191             {
1192               accelerator += 9;
1193               len -= 9;
1194               mods |= GDK_RELEASE_MASK;
1195             }
1196           else if (len >= 9 && is_control (accelerator))
1197             {
1198               accelerator += 9;
1199               len -= 9;
1200               mods |= GDK_CONTROL_MASK;
1201             }
1202           else if (len >= 7 && is_shift (accelerator))
1203             {
1204               accelerator += 7;
1205               len -= 7;
1206               mods |= GDK_SHIFT_MASK;
1207             }
1208           else if (len >= 6 && is_shft (accelerator))
1209             {
1210               accelerator += 6;
1211               len -= 6;
1212               mods |= GDK_SHIFT_MASK;
1213             }
1214           else if (len >= 6 && is_ctrl (accelerator))
1215             {
1216               accelerator += 6;
1217               len -= 6;
1218               mods |= GDK_CONTROL_MASK;
1219             }
1220           else if (len >= 6 && is_modx (accelerator))
1221             {
1222               static const guint mod_vals[] = {
1223                 GDK_MOD1_MASK, GDK_MOD2_MASK, GDK_MOD3_MASK,
1224                 GDK_MOD4_MASK, GDK_MOD5_MASK
1225               };
1226
1227               len -= 6;
1228               accelerator += 4;
1229               mods |= mod_vals[*accelerator - '1'];
1230               accelerator += 2;
1231             }
1232           else if (len >= 5 && is_ctl (accelerator))
1233             {
1234               accelerator += 5;
1235               len -= 5;
1236               mods |= GDK_CONTROL_MASK;
1237             }
1238           else if (len >= 5 && is_alt (accelerator))
1239             {
1240               accelerator += 5;
1241               len -= 5;
1242               mods |= GDK_MOD1_MASK;
1243             }
1244           else if (len >= 6 && is_meta (accelerator))
1245             {
1246               accelerator += 6;
1247               len -= 6;
1248               mods |= GDK_META_MASK;
1249             }
1250           else if (len >= 7 && is_hyper (accelerator))
1251             {
1252               accelerator += 7;
1253               len -= 7;
1254               mods |= GDK_HYPER_MASK;
1255             }
1256           else if (len >= 7 && is_super (accelerator))
1257             {
1258               accelerator += 7;
1259               len -= 7;
1260               mods |= GDK_SUPER_MASK;
1261             }
1262           else
1263             {
1264               gchar last_ch;
1265
1266               last_ch = *accelerator;
1267               while (last_ch && last_ch != '>')
1268                 {
1269                   last_ch = *accelerator;
1270                   accelerator += 1;
1271                   len -= 1;
1272                 }
1273             }
1274         }
1275       else
1276         {
1277           keyval = gdk_keyval_from_name (accelerator);
1278           accelerator += len;
1279           len -= len;
1280         }
1281     }
1282
1283   if (accelerator_key)
1284     *accelerator_key = gdk_keyval_to_lower (keyval);
1285   if (accelerator_mods)
1286     *accelerator_mods = mods;
1287 }
1288
1289 /**
1290  * gtk_accelerator_name:
1291  * @accelerator_key: accelerator keyval
1292  * @accelerator_mods: accelerator modifier mask
1293  *
1294  * Converts an accelerator keyval and modifier mask
1295  * into a string parseable by gtk_accelerator_parse().
1296  * For example, if you pass in #GDK_KEY_q and #GDK_CONTROL_MASK,
1297  * this function returns "&lt;Control&gt;q".
1298  *
1299  * If you need to display accelerators in the user interface,
1300  * see gtk_accelerator_get_label().
1301  *
1302  * Returns: a newly-allocated accelerator name
1303  */
1304 gchar*
1305 gtk_accelerator_name (guint           accelerator_key,
1306                       GdkModifierType accelerator_mods)
1307 {
1308   static const gchar text_release[] = "<Release>";
1309   static const gchar text_shift[] = "<Shift>";
1310   static const gchar text_control[] = "<Control>";
1311   static const gchar text_mod1[] = "<Alt>";
1312   static const gchar text_mod2[] = "<Mod2>";
1313   static const gchar text_mod3[] = "<Mod3>";
1314   static const gchar text_mod4[] = "<Mod4>";
1315   static const gchar text_mod5[] = "<Mod5>";
1316   static const gchar text_meta[] = "<Meta>";
1317   static const gchar text_super[] = "<Super>";
1318   static const gchar text_hyper[] = "<Hyper>";
1319   guint l;
1320   gchar *keyval_name;
1321   gchar *accelerator;
1322
1323   accelerator_mods &= GDK_MODIFIER_MASK;
1324
1325   keyval_name = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key));
1326   if (!keyval_name)
1327     keyval_name = "";
1328
1329   l = 0;
1330   if (accelerator_mods & GDK_RELEASE_MASK)
1331     l += sizeof (text_release) - 1;
1332   if (accelerator_mods & GDK_SHIFT_MASK)
1333     l += sizeof (text_shift) - 1;
1334   if (accelerator_mods & GDK_CONTROL_MASK)
1335     l += sizeof (text_control) - 1;
1336   if (accelerator_mods & GDK_MOD1_MASK)
1337     l += sizeof (text_mod1) - 1;
1338   if (accelerator_mods & GDK_MOD2_MASK)
1339     l += sizeof (text_mod2) - 1;
1340   if (accelerator_mods & GDK_MOD3_MASK)
1341     l += sizeof (text_mod3) - 1;
1342   if (accelerator_mods & GDK_MOD4_MASK)
1343     l += sizeof (text_mod4) - 1;
1344   if (accelerator_mods & GDK_MOD5_MASK)
1345     l += sizeof (text_mod5) - 1;
1346   l += strlen (keyval_name);
1347   if (accelerator_mods & GDK_META_MASK)
1348     l += sizeof (text_meta) - 1;
1349   if (accelerator_mods & GDK_HYPER_MASK)
1350     l += sizeof (text_hyper) - 1;
1351   if (accelerator_mods & GDK_SUPER_MASK)
1352     l += sizeof (text_super) - 1;
1353
1354   accelerator = g_new (gchar, l + 1);
1355
1356   l = 0;
1357   accelerator[l] = 0;
1358   if (accelerator_mods & GDK_RELEASE_MASK)
1359     {
1360       strcpy (accelerator + l, text_release);
1361       l += sizeof (text_release) - 1;
1362     }
1363   if (accelerator_mods & GDK_SHIFT_MASK)
1364     {
1365       strcpy (accelerator + l, text_shift);
1366       l += sizeof (text_shift) - 1;
1367     }
1368   if (accelerator_mods & GDK_CONTROL_MASK)
1369     {
1370       strcpy (accelerator + l, text_control);
1371       l += sizeof (text_control) - 1;
1372     }
1373   if (accelerator_mods & GDK_MOD1_MASK)
1374     {
1375       strcpy (accelerator + l, text_mod1);
1376       l += sizeof (text_mod1) - 1;
1377     }
1378   if (accelerator_mods & GDK_MOD2_MASK)
1379     {
1380       strcpy (accelerator + l, text_mod2);
1381       l += sizeof (text_mod2) - 1;
1382     }
1383   if (accelerator_mods & GDK_MOD3_MASK)
1384     {
1385       strcpy (accelerator + l, text_mod3);
1386       l += sizeof (text_mod3) - 1;
1387     }
1388   if (accelerator_mods & GDK_MOD4_MASK)
1389     {
1390       strcpy (accelerator + l, text_mod4);
1391       l += sizeof (text_mod4) - 1;
1392     }
1393   if (accelerator_mods & GDK_MOD5_MASK)
1394     {
1395       strcpy (accelerator + l, text_mod5);
1396       l += sizeof (text_mod5) - 1;
1397     }
1398   if (accelerator_mods & GDK_META_MASK)
1399     {
1400       strcpy (accelerator + l, text_meta);
1401       l += sizeof (text_meta) - 1;
1402     }
1403   if (accelerator_mods & GDK_HYPER_MASK)
1404     {
1405       strcpy (accelerator + l, text_hyper);
1406       l += sizeof (text_hyper) - 1;
1407     }
1408   if (accelerator_mods & GDK_SUPER_MASK)
1409     {
1410       strcpy (accelerator + l, text_super);
1411       l += sizeof (text_super) - 1;
1412     }
1413   strcpy (accelerator + l, keyval_name);
1414
1415   return accelerator;
1416 }
1417
1418 /**
1419  * gtk_accelerator_get_label:
1420  * @accelerator_key: accelerator keyval
1421  * @accelerator_mods: accelerator modifier mask
1422  *
1423  * Converts an accelerator keyval and modifier mask into a string
1424  * which can be used to represent the accelerator to the user.
1425  *
1426  * Returns: a newly-allocated string representing the accelerator.
1427  *
1428  * Since: 2.6
1429  */
1430 gchar*
1431 gtk_accelerator_get_label (guint           accelerator_key,
1432                            GdkModifierType accelerator_mods)
1433 {
1434   GtkAccelLabelClass *klass;
1435   gchar *label;
1436
1437   klass = g_type_class_ref (GTK_TYPE_ACCEL_LABEL);
1438   label = _gtk_accel_label_class_get_accelerator_label (klass,
1439                                                         accelerator_key,
1440                                                         accelerator_mods);
1441   g_type_class_unref (klass); /* klass is kept alive since gtk uses static types */
1442
1443   return label;
1444 }
1445
1446 /**
1447  * gtk_accelerator_set_default_mod_mask:
1448  * @default_mod_mask: accelerator modifier mask
1449  *
1450  * Sets the modifiers that will be considered significant for keyboard
1451  * accelerators. The default mod mask is #GDK_CONTROL_MASK |
1452  * #GDK_SHIFT_MASK | #GDK_MOD1_MASK | #GDK_SUPER_MASK |
1453  * #GDK_HYPER_MASK | #GDK_META_MASK, that is, Control, Shift, Alt,
1454  * Super, Hyper and Meta. Other modifiers will by default be ignored
1455  * by #GtkAccelGroup.
1456  * You must include at least the three modifiers Control, Shift
1457  * and Alt in any value you pass to this function.
1458  *
1459  * The default mod mask should be changed on application startup,
1460  * before using any accelerator groups.
1461  */
1462 void
1463 gtk_accelerator_set_default_mod_mask (GdkModifierType default_mod_mask)
1464 {
1465   default_accel_mod_mask = (default_mod_mask & GDK_MODIFIER_MASK) |
1466     (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK);
1467 }
1468
1469 /**
1470  * gtk_accelerator_get_default_mod_mask:
1471  * @returns: the default accelerator modifier mask
1472  *
1473  * Gets the value set by gtk_accelerator_set_default_mod_mask().
1474  */
1475 GdkModifierType
1476 gtk_accelerator_get_default_mod_mask (void)
1477 {
1478   return default_accel_mod_mask;
1479 }