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