]> Pileus Git - ~andy/gtk/blob - gtk/gtkaccelgroup.c
Fix up includes
[~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 "gtkaccelmapprivate.h"
35 #include "gtkintl.h"
36 #include "gtkmarshalers.h"
37 #include "gtkprivate.h"
38
39 /**
40  * SECTION:gtkaccelgroup
41  * @Short_description: Groups of global keyboard accelerators for an
42  *     entire GtkWindow
43  * @Title: Accelerator Groups
44  * @See_also:gtk_window_add_accel_group(), gtk_accel_map_change_entry(),
45  * gtk_item_factory_new(), gtk_label_new_with_mnemonic()
46  *
47  * A #GtkAccelGroup represents a group of keyboard accelerators,
48  * typically attached to a toplevel #GtkWindow (with
49  * gtk_window_add_accel_group()). Usually you won't need to create a
50  * #GtkAccelGroup directly; instead, when using #GtkUIManager, GTK+
51  * automatically sets up the accelerators for your menus in the ui
52  * manager's #GtkAccelGroup.
53  *
54  * Note that <firstterm>accelerators</firstterm> are different from
55  * <firstterm>mnemonics</firstterm>. Accelerators are shortcuts for
56  * activating a menu item; they appear alongside the menu item they're a
57  * shortcut for. For example "Ctrl+Q" might appear alongside the "Quit"
58  * menu item. Mnemonics are shortcuts for GUI elements such as text
59  * entries or buttons; they appear as underlined characters. See
60  * gtk_label_new_with_mnemonic(). Menu items can have both accelerators
61  * and mnemonics, of course.
62  */
63
64 /* --- prototypes --- */
65 static void gtk_accel_group_finalize     (GObject    *object);
66 static void gtk_accel_group_get_property (GObject    *object,
67                                           guint       param_id,
68                                           GValue     *value,
69                                           GParamSpec *pspec);
70 static void accel_closure_invalidate     (gpointer    data,
71                                           GClosure   *closure);
72
73
74 /* --- variables --- */
75 static guint  signal_accel_activate      = 0;
76 static guint  signal_accel_changed       = 0;
77 static guint  quark_acceleratable_groups = 0;
78 static guint  default_accel_mod_mask     = (GDK_SHIFT_MASK   |
79                                             GDK_CONTROL_MASK |
80                                             GDK_MOD1_MASK    |
81                                             GDK_SUPER_MASK   |
82                                             GDK_HYPER_MASK   |
83                                             GDK_META_MASK);
84
85
86 enum {
87   PROP_0,
88   PROP_IS_LOCKED,
89   PROP_MODIFIER_MASK,
90 };
91
92 G_DEFINE_TYPE (GtkAccelGroup, gtk_accel_group, G_TYPE_OBJECT)
93
94 /* --- functions --- */
95 static void
96 gtk_accel_group_class_init (GtkAccelGroupClass *class)
97 {
98   GObjectClass *object_class = G_OBJECT_CLASS (class);
99
100   quark_acceleratable_groups = g_quark_from_static_string ("gtk-acceleratable-accel-groups");
101
102   object_class->finalize = gtk_accel_group_finalize;
103   object_class->get_property = gtk_accel_group_get_property;
104
105   class->accel_changed = NULL;
106
107   g_object_class_install_property (object_class,
108                                    PROP_IS_LOCKED,
109                                    g_param_spec_boolean ("is-locked",
110                                                          "Is locked",
111                                                          "Is the accel group locked",
112                                                          FALSE,
113                                                          G_PARAM_READABLE));
114
115   g_object_class_install_property (object_class,
116                                    PROP_MODIFIER_MASK,
117                                    g_param_spec_flags ("modifier-mask",
118                                                        "Modifier Mask",
119                                                        "Modifier Mask",
120                                                        GDK_TYPE_MODIFIER_TYPE,
121                                                        default_accel_mod_mask,
122                                                        G_PARAM_READABLE));
123
124   /**
125    * GtkAccelGroup::accel-activate:
126    * @accel_group: the #GtkAccelGroup which received the signal
127    * @acceleratable: the object on which the accelerator was activated
128    * @keyval: the accelerator keyval
129    * @modifier: the modifier combination of the accelerator
130    *
131    * The accel-activate signal is an implementation detail of
132    * #GtkAccelGroup and not meant to be used by applications.
133    *
134    * Returns: %TRUE if the accelerator was activated
135    */
136   signal_accel_activate =
137     g_signal_new (I_("accel-activate"),
138                   G_OBJECT_CLASS_TYPE (class),
139                   G_SIGNAL_DETAILED,
140                   0,
141                   _gtk_boolean_handled_accumulator, NULL,
142                   _gtk_marshal_BOOLEAN__OBJECT_UINT_FLAGS,
143                   G_TYPE_BOOLEAN, 3,
144                   G_TYPE_OBJECT,
145                   G_TYPE_UINT,
146                   GDK_TYPE_MODIFIER_TYPE);
147   /**
148    * GtkAccelGroup::accel-changed:
149    * @accel_group: the #GtkAccelGroup which received the signal
150    * @keyval: the accelerator keyval
151    * @modifier: the modifier combination of the accelerator
152    * @accel_closure: the #GClosure of the accelerator
153    *
154    * The accel-changed signal is emitted when a #GtkAccelGroupEntry
155    * is added to or removed from the accel group.
156    *
157    * Widgets like #GtkAccelLabel which display an associated
158    * accelerator should connect to this signal, and rebuild
159    * their visual representation if the @accel_closure is theirs.
160    */
161   signal_accel_changed =
162     g_signal_new (I_("accel-changed"),
163                   G_OBJECT_CLASS_TYPE (class),
164                   G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
165                   G_STRUCT_OFFSET (GtkAccelGroupClass, accel_changed),
166                   NULL, NULL,
167                   _gtk_marshal_VOID__UINT_FLAGS_BOXED,
168                   G_TYPE_NONE, 3,
169                   G_TYPE_UINT,
170                   GDK_TYPE_MODIFIER_TYPE,
171                   G_TYPE_CLOSURE);
172
173   g_type_class_add_private (object_class, sizeof (GtkAccelGroupPrivate));
174 }
175
176 static void
177 gtk_accel_group_finalize (GObject *object)
178 {
179   GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (object);
180   guint i;
181
182   for (i = 0; i < accel_group->priv->n_accels; i++)
183     {
184       GtkAccelGroupEntry *entry = &accel_group->priv->priv_accels[i];
185
186       if (entry->accel_path_quark)
187         {
188           const gchar *accel_path = g_quark_to_string (entry->accel_path_quark);
189
190           _gtk_accel_map_remove_group (accel_path, accel_group);
191         }
192       g_closure_remove_invalidate_notifier (entry->closure, accel_group, accel_closure_invalidate);
193
194       /* remove quick_accel_add() refcount */
195       g_closure_unref (entry->closure);
196     }
197
198   g_free (accel_group->priv->priv_accels);
199
200   G_OBJECT_CLASS (gtk_accel_group_parent_class)->finalize (object);
201 }
202
203 static void
204 gtk_accel_group_get_property (GObject    *object,
205                               guint       param_id,
206                               GValue     *value,
207                               GParamSpec *pspec)
208 {
209   GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (object);
210
211   switch (param_id)
212     {
213     case PROP_IS_LOCKED:
214       g_value_set_boolean (value, accel_group->priv->lock_count > 0);
215       break;
216     case PROP_MODIFIER_MASK:
217       g_value_set_flags (value, accel_group->priv->modifier_mask);
218       break;
219     default:
220       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
221       break;
222     }
223 }
224
225 static void
226 gtk_accel_group_init (GtkAccelGroup *accel_group)
227 {
228   GtkAccelGroupPrivate *priv;
229
230   accel_group->priv = G_TYPE_INSTANCE_GET_PRIVATE (accel_group,
231                                                    GTK_TYPE_ACCEL_GROUP,
232                                                    GtkAccelGroupPrivate);
233   priv = accel_group->priv;
234
235   priv->lock_count = 0;
236   priv->modifier_mask = gtk_accelerator_get_default_mod_mask ();
237   priv->acceleratables = NULL;
238   priv->n_accels = 0;
239   priv->priv_accels = NULL;
240 }
241
242 /**
243  * gtk_accel_group_new:
244  *
245  * Creates a new #GtkAccelGroup.
246  *
247  * Returns: a new #GtkAccelGroup object
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  *
384  * Finds the first entry in an accelerator group for which
385  * @find_func returns %TRUE and returns its #GtkAccelKey.
386  *
387  * Returns: (transfer none): the key of the first entry passing
388  *    @find_func. The key is owned by GTK+ and must not be freed.
389  */
390 GtkAccelKey*
391 gtk_accel_group_find (GtkAccelGroup         *accel_group,
392                       GtkAccelGroupFindFunc  find_func,
393                       gpointer               data)
394 {
395   GtkAccelKey *key = NULL;
396   guint i;
397
398   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL);
399   g_return_val_if_fail (find_func != NULL, NULL);
400
401   g_object_ref (accel_group);
402   for (i = 0; i < accel_group->priv->n_accels; i++)
403     if (find_func (&accel_group->priv->priv_accels[i].key,
404                    accel_group->priv->priv_accels[i].closure,
405                    data))
406       {
407         key = &accel_group->priv->priv_accels[i].key;
408         break;
409       }
410   g_object_unref (accel_group);
411
412   return key;
413 }
414
415 /**
416  * gtk_accel_group_lock:
417  * @accel_group: a #GtkAccelGroup
418  *
419  * Locks the given accelerator group.
420  *
421  * Locking an acelerator group prevents the accelerators contained
422  * within it to be changed during runtime. Refer to
423  * gtk_accel_map_change_entry() about runtime accelerator changes.
424  *
425  * If called more than once, @accel_group remains locked until
426  * gtk_accel_group_unlock() has been called an equivalent number
427  * of times.
428  */
429 void
430 gtk_accel_group_lock (GtkAccelGroup *accel_group)
431 {
432   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
433
434   accel_group->priv->lock_count += 1;
435
436   if (accel_group->priv->lock_count == 1) {
437     /* State change from unlocked to locked */
438     g_object_notify (G_OBJECT (accel_group), "is-locked");
439   }
440 }
441
442 /**
443  * gtk_accel_group_unlock:
444  * @accel_group: a #GtkAccelGroup
445  *
446  * Undoes the last call to gtk_accel_group_lock() on this @accel_group.
447  */
448 void
449 gtk_accel_group_unlock (GtkAccelGroup *accel_group)
450 {
451   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
452   g_return_if_fail (accel_group->priv->lock_count > 0);
453
454   accel_group->priv->lock_count -= 1;
455
456   if (accel_group->priv->lock_count < 1) {
457     /* State change from locked to unlocked */
458     g_object_notify (G_OBJECT (accel_group), "is-locked");
459   }
460 }
461
462 static void
463 accel_closure_invalidate (gpointer  data,
464                           GClosure *closure)
465 {
466   GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (data);
467
468   gtk_accel_group_disconnect (accel_group, closure);
469 }
470
471 static int
472 bsearch_compare_accels (const void *d1,
473                         const void *d2)
474 {
475   const GtkAccelGroupEntry *entry1 = d1;
476   const GtkAccelGroupEntry *entry2 = d2;
477
478   if (entry1->key.accel_key == entry2->key.accel_key)
479     return entry1->key.accel_mods < entry2->key.accel_mods ? -1 : entry1->key.accel_mods > entry2->key.accel_mods;
480   else
481     return entry1->key.accel_key < entry2->key.accel_key ? -1 : 1;
482 }
483
484 static void
485 quick_accel_add (GtkAccelGroup   *accel_group,
486                  guint            accel_key,
487                  GdkModifierType  accel_mods,
488                  GtkAccelFlags    accel_flags,
489                  GClosure        *closure,
490                  GQuark           path_quark)
491 {
492   guint pos, i = accel_group->priv->n_accels++;
493   GtkAccelGroupEntry key;
494
495   /* find position */
496   key.key.accel_key = accel_key;
497   key.key.accel_mods = accel_mods;
498   for (pos = 0; pos < i; pos++)
499     if (bsearch_compare_accels (&key, accel_group->priv->priv_accels + pos) < 0)
500       break;
501
502   /* insert at position, ref closure */
503   accel_group->priv->priv_accels = g_renew (GtkAccelGroupEntry, accel_group->priv->priv_accels, accel_group->priv->n_accels);
504   g_memmove (accel_group->priv->priv_accels + pos + 1, accel_group->priv->priv_accels + pos,
505              (i - pos) * sizeof (accel_group->priv->priv_accels[0]));
506   accel_group->priv->priv_accels[pos].key.accel_key = accel_key;
507   accel_group->priv->priv_accels[pos].key.accel_mods = accel_mods;
508   accel_group->priv->priv_accels[pos].key.accel_flags = accel_flags;
509   accel_group->priv->priv_accels[pos].closure = g_closure_ref (closure);
510   accel_group->priv->priv_accels[pos].accel_path_quark = path_quark;
511   g_closure_sink (closure);
512
513   /* handle closure invalidation and reverse lookups */
514   g_closure_add_invalidate_notifier (closure, accel_group, accel_closure_invalidate);
515
516   /* get accel path notification */
517   if (path_quark)
518     _gtk_accel_map_add_group (g_quark_to_string (path_quark), accel_group);
519
520   /* connect and notify changed */
521   if (accel_key)
522     {
523       gchar *accel_name = gtk_accelerator_name (accel_key, accel_mods);
524       GQuark accel_quark = g_quark_from_string (accel_name);
525
526       g_free (accel_name);
527
528       /* setup handler */
529       g_signal_connect_closure_by_id (accel_group, signal_accel_activate, accel_quark, closure, FALSE);
530
531       /* and notify */
532       g_signal_emit (accel_group, signal_accel_changed, accel_quark, accel_key, accel_mods, closure);
533     }
534 }
535
536 static void
537 quick_accel_remove (GtkAccelGroup *accel_group,
538                     guint          pos)
539 {
540   GQuark accel_quark = 0;
541   GtkAccelGroupEntry *entry = accel_group->priv->priv_accels + pos;
542   guint accel_key = entry->key.accel_key;
543   GdkModifierType accel_mods = entry->key.accel_mods;
544   GClosure *closure = entry->closure;
545
546   /* quark for notification */
547   if (accel_key)
548     {
549       gchar *accel_name = gtk_accelerator_name (accel_key, accel_mods);
550
551       accel_quark = g_quark_from_string (accel_name);
552       g_free (accel_name);
553     }
554
555   /* clean up closure invalidate notification and disconnect */
556   g_closure_remove_invalidate_notifier (entry->closure, accel_group, accel_closure_invalidate);
557   if (accel_quark)
558     g_signal_handlers_disconnect_matched (accel_group,
559                                           G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_CLOSURE,
560                                           signal_accel_activate, accel_quark,
561                                           closure, NULL, NULL);
562   /* clean up accel path notification */
563   if (entry->accel_path_quark)
564     _gtk_accel_map_remove_group (g_quark_to_string (entry->accel_path_quark), accel_group);
565
566   /* physically remove */
567   accel_group->priv->n_accels -= 1;
568   g_memmove (entry, entry + 1,
569              (accel_group->priv->n_accels - pos) * sizeof (accel_group->priv->priv_accels[0]));
570
571   /* and notify */
572   if (accel_quark)
573     g_signal_emit (accel_group, signal_accel_changed, accel_quark, accel_key, accel_mods, closure);
574
575   /* remove quick_accel_add() refcount */
576   g_closure_unref (closure);
577 }
578
579 static GtkAccelGroupEntry*
580 quick_accel_find (GtkAccelGroup   *accel_group,
581                   guint            accel_key,
582                   GdkModifierType  accel_mods,
583                   guint           *count_p)
584 {
585   GtkAccelGroupEntry *entry;
586   GtkAccelGroupEntry key;
587
588   *count_p = 0;
589
590   if (!accel_group->priv->n_accels)
591     return NULL;
592
593   key.key.accel_key = accel_key;
594   key.key.accel_mods = accel_mods;
595   entry = bsearch (&key, accel_group->priv->priv_accels, accel_group->priv->n_accels,
596                    sizeof (accel_group->priv->priv_accels[0]), bsearch_compare_accels);
597
598   if (!entry)
599     return NULL;
600
601   /* step back to the first member */
602   for (; entry > accel_group->priv->priv_accels; entry--)
603     if (entry[-1].key.accel_key != accel_key ||
604         entry[-1].key.accel_mods != accel_mods)
605       break;
606   /* count equal members */
607   for (; entry + *count_p < accel_group->priv->priv_accels + accel_group->priv->n_accels; (*count_p)++)
608     if (entry[*count_p].key.accel_key != accel_key ||
609         entry[*count_p].key.accel_mods != accel_mods)
610       break;
611   return entry;
612 }
613
614 /**
615  * gtk_accel_group_connect:
616  * @accel_group: the accelerator group to install an accelerator in
617  * @accel_key: key value of the accelerator
618  * @accel_mods: modifier combination of the accelerator
619  * @accel_flags: a flag mask to configure this accelerator
620  * @closure: closure to be executed upon accelerator activation
621  *
622  * Installs an accelerator in this group. When @accel_group is being
623  * activated in response to a call to gtk_accel_groups_activate(),
624  * @closure will be invoked if the @accel_key and @accel_mods from
625  * gtk_accel_groups_activate() match those of this connection.
626  *
627  * The signature used for the @closure is that of #GtkAccelGroupActivate.
628  *
629  * Note that, due to implementation details, a single closure can
630  * only be connected to one accelerator group.
631  */
632 void
633 gtk_accel_group_connect (GtkAccelGroup   *accel_group,
634                          guint            accel_key,
635                          GdkModifierType  accel_mods,
636                          GtkAccelFlags    accel_flags,
637                          GClosure        *closure)
638 {
639   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
640   g_return_if_fail (closure != NULL);
641   g_return_if_fail (accel_key > 0);
642   g_return_if_fail (gtk_accel_group_from_accel_closure (closure) == NULL);
643
644   g_object_ref (accel_group);
645   if (!closure->is_invalid)
646     quick_accel_add (accel_group,
647                      gdk_keyval_to_lower (accel_key),
648                      accel_mods, accel_flags, closure, 0);
649   g_object_unref (accel_group);
650 }
651
652 /**
653  * gtk_accel_group_connect_by_path:
654  * @accel_group: the accelerator group to install an accelerator in
655  * @accel_path: path used for determining key and modifiers
656  * @closure: closure to be executed upon accelerator activation
657  *
658  * Installs an accelerator in this group, using an accelerator path
659  * to look up the appropriate key and modifiers (see
660  * gtk_accel_map_add_entry()). When @accel_group is being activated
661  * in response to a call to gtk_accel_groups_activate(), @closure will
662  * be invoked if the @accel_key and @accel_mods from
663  * gtk_accel_groups_activate() match the key and modifiers for the path.
664  *
665  * The signature used for the @closure is that of #GtkAccelGroupActivate.
666  *
667  * Note that @accel_path string will be stored in a #GQuark. Therefore,
668  * if you pass a static string, you can save some memory by interning it
669  * first with g_intern_static_string().
670  */
671 void
672 gtk_accel_group_connect_by_path (GtkAccelGroup *accel_group,
673                                  const gchar   *accel_path,
674                                  GClosure      *closure)
675 {
676   guint accel_key = 0;
677   GdkModifierType accel_mods = 0;
678   GtkAccelKey key;
679
680   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
681   g_return_if_fail (closure != NULL);
682   g_return_if_fail (_gtk_accel_path_is_valid (accel_path));
683
684   if (closure->is_invalid)
685     return;
686
687   g_object_ref (accel_group);
688
689   if (gtk_accel_map_lookup_entry (accel_path, &key))
690     {
691       accel_key = gdk_keyval_to_lower (key.accel_key);
692       accel_mods = key.accel_mods;
693     }
694
695   quick_accel_add (accel_group, accel_key, accel_mods, GTK_ACCEL_VISIBLE, closure,
696                    g_quark_from_string (accel_path));
697
698   g_object_unref (accel_group);
699 }
700
701 /**
702  * gtk_accel_group_disconnect:
703  * @accel_group: the accelerator group to remove an accelerator from
704  * @closure: (allow-none): the closure to remove from this accelerator
705  *     group, or %NULL to remove all closures
706  *
707  * Removes an accelerator previously installed through
708  * gtk_accel_group_connect().
709  *
710  * Since 2.20 @closure can be %NULL.
711  *
712  * Returns: %TRUE if the closure was found and got disconnected
713  */
714 gboolean
715 gtk_accel_group_disconnect (GtkAccelGroup *accel_group,
716                             GClosure      *closure)
717 {
718   guint i;
719
720   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
721
722   for (i = 0; i < accel_group->priv->n_accels; i++)
723     if (accel_group->priv->priv_accels[i].closure == closure)
724       {
725         g_object_ref (accel_group);
726         quick_accel_remove (accel_group, i);
727         g_object_unref (accel_group);
728         return TRUE;
729       }
730   return FALSE;
731 }
732
733 /**
734  * gtk_accel_group_disconnect_key:
735  * @accel_group: the accelerator group to install an accelerator in
736  * @accel_key: key value of the accelerator
737  * @accel_mods: modifier combination of the accelerator
738  *
739  * Removes an accelerator previously installed through
740  * gtk_accel_group_connect().
741  *
742  * Returns: %TRUE if there was an accelerator which could be
743  *     removed, %FALSE otherwise
744  */
745 gboolean
746 gtk_accel_group_disconnect_key (GtkAccelGroup   *accel_group,
747                                 guint            accel_key,
748                                 GdkModifierType  accel_mods)
749 {
750   GtkAccelGroupEntry *entries;
751   GSList *slist, *clist = NULL;
752   gboolean removed_one = FALSE;
753   guint n;
754
755   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
756
757   g_object_ref (accel_group);
758
759   accel_key = gdk_keyval_to_lower (accel_key);
760   entries = quick_accel_find (accel_group, accel_key, accel_mods, &n);
761   while (n--)
762     {
763       GClosure *closure = g_closure_ref (entries[n].closure);
764
765       clist = g_slist_prepend (clist, closure);
766     }
767
768   for (slist = clist; slist; slist = slist->next)
769     {
770       GClosure *closure = slist->data;
771
772       removed_one |= gtk_accel_group_disconnect (accel_group, closure);
773       g_closure_unref (closure);
774     }
775   g_slist_free (clist);
776
777   g_object_unref (accel_group);
778
779   return removed_one;
780 }
781
782 void
783 _gtk_accel_group_reconnect (GtkAccelGroup *accel_group,
784                             GQuark         accel_path_quark)
785 {
786   GSList *slist, *clist = NULL;
787   guint i;
788
789   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
790
791   g_object_ref (accel_group);
792
793   for (i = 0; i < accel_group->priv->n_accels; i++)
794     if (accel_group->priv->priv_accels[i].accel_path_quark == accel_path_quark)
795       {
796         GClosure *closure = g_closure_ref (accel_group->priv->priv_accels[i].closure);
797
798         clist = g_slist_prepend (clist, closure);
799       }
800
801   for (slist = clist; slist; slist = slist->next)
802     {
803       GClosure *closure = slist->data;
804
805       gtk_accel_group_disconnect (accel_group, closure);
806       gtk_accel_group_connect_by_path (accel_group, g_quark_to_string (accel_path_quark), closure);
807       g_closure_unref (closure);
808     }
809   g_slist_free (clist);
810
811   g_object_unref (accel_group);
812 }
813
814 GSList*
815 _gtk_accel_group_get_accelerables (GtkAccelGroup *accel_group)
816 {
817     g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL);
818
819     return accel_group->priv->acceleratables;
820 }
821
822 /**
823  * gtk_accel_group_query:
824  * @accel_group: the accelerator group to query
825  * @accel_key: key value of the accelerator
826  * @accel_mods: modifier combination of the accelerator
827  * @n_entries: (allow-none): location to return the number
828  *     of entries found, or %NULL
829  *
830  * Queries an accelerator group for all entries matching @accel_key
831  * and @accel_mods.
832  *
833  * Returns: (transfer none) (array length=n_entries): an array of
834  *     @n_entries #GtkAccelGroupEntry elements, or %NULL. The array
835  *     is owned by GTK+ and must not be freed.
836  */
837 GtkAccelGroupEntry*
838 gtk_accel_group_query (GtkAccelGroup   *accel_group,
839                        guint            accel_key,
840                        GdkModifierType  accel_mods,
841                        guint           *n_entries)
842 {
843   GtkAccelGroupEntry *entries;
844   guint n;
845
846   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL);
847
848   entries = quick_accel_find (accel_group, gdk_keyval_to_lower (accel_key), accel_mods, &n);
849
850   if (n_entries)
851     *n_entries = entries ? n : 0;
852
853   return entries;
854 }
855
856 /**
857  * gtk_accel_group_from_accel_closure:
858  * @closure: a #GClosure
859  *
860  * Finds the #GtkAccelGroup to which @closure is connected;
861  * see gtk_accel_group_connect().
862  *
863  * Returns: (transfer none): the #GtkAccelGroup to which @closure
864  *     is connected, or %NULL
865  */
866 GtkAccelGroup*
867 gtk_accel_group_from_accel_closure (GClosure *closure)
868 {
869   guint i;
870
871   g_return_val_if_fail (closure != NULL, NULL);
872
873   /* A few remarks on what we do here. in general, we need a way to
874    * reverse lookup accel_groups from closures that are being used in
875    * accel groups. this could be done e.g via a hashtable. it is however
876    * cheaper (memory wise) to just use the invalidation notifier on the
877    * closure itself (which we need to install anyway), that contains the
878    * accel group as data which, besides needing to peek a bit at closure
879    * internals, works just as good.
880    */
881   for (i = 0; i < G_CLOSURE_N_NOTIFIERS (closure); i++)
882     if (closure->notifiers[i].notify == accel_closure_invalidate)
883       return closure->notifiers[i].data;
884
885   return NULL;
886 }
887
888 /**
889  * gtk_accel_group_activate:
890  * @accel_group: a #GtkAccelGroup
891  * @accel_quark: the quark for the accelerator name
892  * @acceleratable: the #GObject, usually a #GtkWindow, on which
893  *    to activate the accelerator
894  * @accel_key: accelerator keyval from a key event
895  * @accel_mods: keyboard state mask from a key event
896  *
897  * Finds the first accelerator in @accel_group that matches
898  * @accel_key and @accel_mods, and activates it.
899  *
900  * Returns: %TRUE if an accelerator was activated and handled
901  *     this keypress
902  */
903 gboolean
904 gtk_accel_group_activate (GtkAccelGroup   *accel_group,
905                           GQuark           accel_quark,
906                           GObject         *acceleratable,
907                           guint            accel_key,
908                           GdkModifierType  accel_mods)
909 {
910   gboolean was_handled;
911
912   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
913   g_return_val_if_fail (G_IS_OBJECT (acceleratable), FALSE);
914
915   was_handled = FALSE;
916   g_signal_emit (accel_group, signal_accel_activate, accel_quark,
917                  acceleratable, accel_key, accel_mods, &was_handled);
918
919   return was_handled;
920 }
921
922 /**
923  * gtk_accel_groups_activate:
924  * @object: the #GObject, usually a #GtkWindow, on which
925  *     to activate the accelerator
926  * @accel_key: accelerator keyval from a key event
927  * @accel_mods: keyboard state mask from a key event
928  *
929  * Finds the first accelerator in any #GtkAccelGroup attached
930  * to @object that matches @accel_key and @accel_mods, and
931  * activates that accelerator.
932  *
933  * Returns: %TRUE if an accelerator was activated and handled
934  *     this keypress
935  */
936 gboolean
937 gtk_accel_groups_activate (GObject         *object,
938                            guint            accel_key,
939                            GdkModifierType  accel_mods)
940 {
941   g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
942
943   if (gtk_accelerator_valid (accel_key, accel_mods))
944     {
945       gchar *accel_name;
946       GQuark accel_quark;
947       GSList *slist;
948
949       accel_name = gtk_accelerator_name (accel_key, (accel_mods & gtk_accelerator_get_default_mod_mask ()));
950       accel_quark = g_quark_from_string (accel_name);
951       g_free (accel_name);
952
953       for (slist = gtk_accel_groups_from_object (object); slist; slist = slist->next)
954         if (gtk_accel_group_activate (slist->data, accel_quark, object, accel_key, accel_mods))
955           return TRUE;
956     }
957
958   return FALSE;
959 }
960
961 /**
962  * gtk_accelerator_valid:
963  * @keyval: a GDK keyval
964  * @modifiers: modifier mask
965  *
966  * Determines whether a given keyval and modifier mask constitute
967  * a valid keyboard accelerator. For example, the #GDK_KEY_a keyval
968  * plus #GDK_CONTROL_MASK is valid - this is a "Ctrl+a" accelerator.
969  * But, you can't, for instance, use the #GDK_KEY_Control_L keyval
970  * as an accelerator.
971  *
972  * Returns: %TRUE if the accelerator is valid
973  */
974 gboolean
975 gtk_accelerator_valid (guint           keyval,
976                        GdkModifierType modifiers)
977 {
978   static const guint invalid_accelerator_vals[] = {
979     GDK_KEY_Shift_L, GDK_KEY_Shift_R, GDK_KEY_Shift_Lock,
980     GDK_KEY_Caps_Lock, GDK_KEY_ISO_Lock, GDK_KEY_Control_L,
981     GDK_KEY_Control_R, GDK_KEY_Meta_L, GDK_KEY_Meta_R,
982     GDK_KEY_Alt_L, GDK_KEY_Alt_R, GDK_KEY_Super_L, GDK_KEY_Super_R,
983     GDK_KEY_Hyper_L, GDK_KEY_Hyper_R, GDK_KEY_ISO_Level3_Shift,
984     GDK_KEY_ISO_Next_Group, GDK_KEY_ISO_Prev_Group,
985     GDK_KEY_ISO_First_Group, GDK_KEY_ISO_Last_Group,
986     GDK_KEY_Mode_switch, GDK_KEY_Num_Lock, GDK_KEY_Multi_key,
987     GDK_KEY_Scroll_Lock, GDK_KEY_Sys_Req,
988     GDK_KEY_Tab, GDK_KEY_ISO_Left_Tab, GDK_KEY_KP_Tab,
989     GDK_KEY_First_Virtual_Screen, GDK_KEY_Prev_Virtual_Screen,
990     GDK_KEY_Next_Virtual_Screen, GDK_KEY_Last_Virtual_Screen,
991     GDK_KEY_Terminate_Server, GDK_KEY_AudibleBell_Enable,
992     0
993   };
994   static const guint invalid_unmodified_vals[] = {
995     GDK_KEY_Up, GDK_KEY_Down, GDK_KEY_Left, GDK_KEY_Right,
996     GDK_KEY_KP_Up, GDK_KEY_KP_Down, GDK_KEY_KP_Left, GDK_KEY_KP_Right,
997     0
998   };
999   const guint *ac_val;
1000
1001   modifiers &= GDK_MODIFIER_MASK;
1002
1003   if (keyval <= 0xFF)
1004     return keyval >= 0x20;
1005
1006   ac_val = invalid_accelerator_vals;
1007   while (*ac_val)
1008     {
1009       if (keyval == *ac_val++)
1010         return FALSE;
1011     }
1012
1013   if (!modifiers)
1014     {
1015       ac_val = invalid_unmodified_vals;
1016       while (*ac_val)
1017         {
1018           if (keyval == *ac_val++)
1019             return FALSE;
1020         }
1021     }
1022
1023   return TRUE;
1024 }
1025
1026 static inline gboolean
1027 is_alt (const gchar *string)
1028 {
1029   return ((string[0] == '<') &&
1030           (string[1] == 'a' || string[1] == 'A') &&
1031           (string[2] == 'l' || string[2] == 'L') &&
1032           (string[3] == 't' || string[3] == 'T') &&
1033           (string[4] == '>'));
1034 }
1035
1036 static inline gboolean
1037 is_ctl (const gchar *string)
1038 {
1039   return ((string[0] == '<') &&
1040           (string[1] == 'c' || string[1] == 'C') &&
1041           (string[2] == 't' || string[2] == 'T') &&
1042           (string[3] == 'l' || string[3] == 'L') &&
1043           (string[4] == '>'));
1044 }
1045
1046 static inline gboolean
1047 is_modx (const gchar *string)
1048 {
1049   return ((string[0] == '<') &&
1050           (string[1] == 'm' || string[1] == 'M') &&
1051           (string[2] == 'o' || string[2] == 'O') &&
1052           (string[3] == 'd' || string[3] == 'D') &&
1053           (string[4] >= '1' && string[4] <= '5') &&
1054           (string[5] == '>'));
1055 }
1056
1057 static inline gboolean
1058 is_ctrl (const gchar *string)
1059 {
1060   return ((string[0] == '<') &&
1061           (string[1] == 'c' || string[1] == 'C') &&
1062           (string[2] == 't' || string[2] == 'T') &&
1063           (string[3] == 'r' || string[3] == 'R') &&
1064           (string[4] == 'l' || string[4] == 'L') &&
1065           (string[5] == '>'));
1066 }
1067
1068 static inline gboolean
1069 is_shft (const gchar *string)
1070 {
1071   return ((string[0] == '<') &&
1072           (string[1] == 's' || string[1] == 'S') &&
1073           (string[2] == 'h' || string[2] == 'H') &&
1074           (string[3] == 'f' || string[3] == 'F') &&
1075           (string[4] == 't' || string[4] == 'T') &&
1076           (string[5] == '>'));
1077 }
1078
1079 static inline gboolean
1080 is_shift (const gchar *string)
1081 {
1082   return ((string[0] == '<') &&
1083           (string[1] == 's' || string[1] == 'S') &&
1084           (string[2] == 'h' || string[2] == 'H') &&
1085           (string[3] == 'i' || string[3] == 'I') &&
1086           (string[4] == 'f' || string[4] == 'F') &&
1087           (string[5] == 't' || string[5] == 'T') &&
1088           (string[6] == '>'));
1089 }
1090
1091 static inline gboolean
1092 is_control (const gchar *string)
1093 {
1094   return ((string[0] == '<') &&
1095           (string[1] == 'c' || string[1] == 'C') &&
1096           (string[2] == 'o' || string[2] == 'O') &&
1097           (string[3] == 'n' || string[3] == 'N') &&
1098           (string[4] == 't' || string[4] == 'T') &&
1099           (string[5] == 'r' || string[5] == 'R') &&
1100           (string[6] == 'o' || string[6] == 'O') &&
1101           (string[7] == 'l' || string[7] == 'L') &&
1102           (string[8] == '>'));
1103 }
1104
1105 static inline gboolean
1106 is_release (const gchar *string)
1107 {
1108   return ((string[0] == '<') &&
1109           (string[1] == 'r' || string[1] == 'R') &&
1110           (string[2] == 'e' || string[2] == 'E') &&
1111           (string[3] == 'l' || string[3] == 'L') &&
1112           (string[4] == 'e' || string[4] == 'E') &&
1113           (string[5] == 'a' || string[5] == 'A') &&
1114           (string[6] == 's' || string[6] == 'S') &&
1115           (string[7] == 'e' || string[7] == 'E') &&
1116           (string[8] == '>'));
1117 }
1118
1119 static inline gboolean
1120 is_meta (const gchar *string)
1121 {
1122   return ((string[0] == '<') &&
1123           (string[1] == 'm' || string[1] == 'M') &&
1124           (string[2] == 'e' || string[2] == 'E') &&
1125           (string[3] == 't' || string[3] == 'T') &&
1126           (string[4] == 'a' || string[4] == 'A') &&
1127           (string[5] == '>'));
1128 }
1129
1130 static inline gboolean
1131 is_super (const gchar *string)
1132 {
1133   return ((string[0] == '<') &&
1134           (string[1] == 's' || string[1] == 'S') &&
1135           (string[2] == 'u' || string[2] == 'U') &&
1136           (string[3] == 'p' || string[3] == 'P') &&
1137           (string[4] == 'e' || string[4] == 'E') &&
1138           (string[5] == 'r' || string[5] == 'R') &&
1139           (string[6] == '>'));
1140 }
1141
1142 static inline gboolean
1143 is_hyper (const gchar *string)
1144 {
1145   return ((string[0] == '<') &&
1146           (string[1] == 'h' || string[1] == 'H') &&
1147           (string[2] == 'y' || string[2] == 'Y') &&
1148           (string[3] == 'p' || string[3] == 'P') &&
1149           (string[4] == 'e' || string[4] == 'E') &&
1150           (string[5] == 'r' || string[5] == 'R') &&
1151           (string[6] == '>'));
1152 }
1153
1154 static inline gboolean
1155 is_primary (const gchar *string)
1156 {
1157   return ((string[0] == '<') &&
1158           (string[1] == 'p' || string[1] == 'P') &&
1159           (string[2] == 'r' || string[2] == 'R') &&
1160           (string[3] == 'i' || string[3] == 'I') &&
1161           (string[4] == 'm' || string[4] == 'M') &&
1162           (string[5] == 'a' || string[5] == 'A') &&
1163           (string[6] == 'r' || string[6] == 'R') &&
1164           (string[7] == 'y' || string[7] == 'Y') &&
1165           (string[8] == '>'));
1166 }
1167
1168 static inline gboolean
1169 is_keycode (const gchar *string)
1170 {
1171   return (string[0] == '0' &&
1172           string[1] == 'x' &&
1173           g_ascii_isxdigit (string[2]) &&
1174           g_ascii_isxdigit (string[3]));
1175 }
1176
1177 /**
1178  * gtk_accelerator_parse_with_keycode:
1179  * @accelerator: string representing an accelerator
1180  * @accelerator_key: (out) (allow-none): return location for accelerator
1181  *     keyval, or %NULL
1182  * @accelerator_codes: (out) (allow-none): return location for accelerator
1183  *     keycodes, or %NULL
1184  * @accelerator_mods: (out) (allow-none): return location for accelerator
1185  *     modifier mask, %NULL
1186  *
1187  * Parses a string representing an accelerator, similarly to
1188  * gtk_accelerator_parse() but handles keycodes as well. This is only
1189  * useful for system-level components, applications should use
1190  * gtk_accelerator_parse() instead.
1191  *
1192  * If a keycode is present in the accelerator and no @accelerator_codes
1193  * is given, the parse will fail.
1194  *
1195  * If the parse fails, @accelerator_key, @accelerator_mods and
1196  * @accelerator_codes will be set to 0 (zero).
1197  *
1198  * Since: 3.4
1199  */
1200 void
1201 gtk_accelerator_parse_with_keycode (const gchar     *accelerator,
1202                                     guint           *accelerator_key,
1203                                     guint          **accelerator_codes,
1204                                     GdkModifierType *accelerator_mods)
1205 {
1206   guint keyval;
1207   GdkModifierType mods;
1208   gint len;
1209   gboolean error;
1210
1211   if (accelerator_key)
1212     *accelerator_key = 0;
1213   if (accelerator_mods)
1214     *accelerator_mods = 0;
1215   if (accelerator_codes)
1216     *accelerator_codes = NULL;
1217   g_return_if_fail (accelerator != NULL);
1218
1219   error = FALSE;
1220   keyval = 0;
1221   mods = 0;
1222   len = strlen (accelerator);
1223   while (len)
1224     {
1225       if (*accelerator == '<')
1226         {
1227           if (len >= 9 && is_release (accelerator))
1228             {
1229               accelerator += 9;
1230               len -= 9;
1231               mods |= GDK_RELEASE_MASK;
1232             }
1233           else if (len >= 9 && is_primary (accelerator))
1234             {
1235               accelerator += 9;
1236               len -= 9;
1237               mods |= _gtk_get_primary_accel_mod ();
1238             }
1239           else if (len >= 9 && is_control (accelerator))
1240             {
1241               accelerator += 9;
1242               len -= 9;
1243               mods |= GDK_CONTROL_MASK;
1244             }
1245           else if (len >= 7 && is_shift (accelerator))
1246             {
1247               accelerator += 7;
1248               len -= 7;
1249               mods |= GDK_SHIFT_MASK;
1250             }
1251           else if (len >= 6 && is_shft (accelerator))
1252             {
1253               accelerator += 6;
1254               len -= 6;
1255               mods |= GDK_SHIFT_MASK;
1256             }
1257           else if (len >= 6 && is_ctrl (accelerator))
1258             {
1259               accelerator += 6;
1260               len -= 6;
1261               mods |= GDK_CONTROL_MASK;
1262             }
1263           else if (len >= 6 && is_modx (accelerator))
1264             {
1265               static const guint mod_vals[] = {
1266                 GDK_MOD1_MASK, GDK_MOD2_MASK, GDK_MOD3_MASK,
1267                 GDK_MOD4_MASK, GDK_MOD5_MASK
1268               };
1269
1270               len -= 6;
1271               accelerator += 4;
1272               mods |= mod_vals[*accelerator - '1'];
1273               accelerator += 2;
1274             }
1275           else if (len >= 5 && is_ctl (accelerator))
1276             {
1277               accelerator += 5;
1278               len -= 5;
1279               mods |= GDK_CONTROL_MASK;
1280             }
1281           else if (len >= 5 && is_alt (accelerator))
1282             {
1283               accelerator += 5;
1284               len -= 5;
1285               mods |= GDK_MOD1_MASK;
1286             }
1287           else if (len >= 6 && is_meta (accelerator))
1288             {
1289               accelerator += 6;
1290               len -= 6;
1291               mods |= GDK_META_MASK;
1292             }
1293           else if (len >= 7 && is_hyper (accelerator))
1294             {
1295               accelerator += 7;
1296               len -= 7;
1297               mods |= GDK_HYPER_MASK;
1298             }
1299           else if (len >= 7 && is_super (accelerator))
1300             {
1301               accelerator += 7;
1302               len -= 7;
1303               mods |= GDK_SUPER_MASK;
1304             }
1305           else
1306             {
1307               gchar last_ch;
1308
1309               last_ch = *accelerator;
1310               while (last_ch && last_ch != '>')
1311                 {
1312                   last_ch = *accelerator;
1313                   accelerator += 1;
1314                   len -= 1;
1315                 }
1316             }
1317         }
1318       else
1319         {
1320           if (len >= 4 && is_keycode (accelerator))
1321             {
1322                char keystring[5];
1323                gchar *endptr;
1324                gint tmp_keycode;
1325
1326                memcpy (keystring, accelerator, 4);
1327                keystring [4] = '\000';
1328
1329                tmp_keycode = strtol (keystring, &endptr, 16);
1330
1331                if (endptr == NULL || *endptr != '\000')
1332                  {
1333                    error = TRUE;
1334                    goto out;
1335                  }
1336                else if (accelerator_codes != NULL)
1337                  {
1338                    /* 0x00 is an invalid keycode too. */
1339                    if (tmp_keycode == 0)
1340                      {
1341                        error = TRUE;
1342                        goto out;
1343                      }
1344                    else
1345                      {
1346                        *accelerator_codes = g_new0 (guint, 2);
1347                        (*accelerator_codes)[0] = tmp_keycode;
1348                      }
1349                  }
1350                else
1351                  {
1352                    /* There was a keycode in the string, but
1353                     * we cannot store it, so we have an error */
1354                    error = TRUE;
1355                    goto out;
1356                  }
1357             }
1358           else
1359             {
1360               keyval = gdk_keyval_from_name (accelerator);
1361               if (keyval == GDK_KEY_VoidSymbol)
1362                 {
1363                   error = TRUE;
1364                   goto out;
1365                 }
1366             }
1367
1368           if (keyval && accelerator_codes != NULL)
1369             {
1370               GdkKeymapKey *keys;
1371               gint n_keys, i, j;
1372
1373               if (!gdk_keymap_get_entries_for_keyval (gdk_keymap_get_default (), keyval, &keys, &n_keys))
1374                 {
1375                   /* Not in keymap */
1376                   error = TRUE;
1377                   goto out;
1378                 }
1379               else
1380                 {
1381                   *accelerator_codes = g_new0 (guint, n_keys + 1);
1382
1383                   /* Prefer level-0 keys to modified keys */
1384                   for (i = 0, j = 0; i < n_keys; ++i)
1385                     {
1386                       if (keys[i].level == 0)
1387                         (*accelerator_codes)[j++] = keys[i].keycode;
1388                     }
1389
1390                   /* No level-0 keys? Find in the whole keymap */
1391                   if (j == 0)
1392                     {
1393                       for (i = 0, j = 0; i < n_keys; ++i)
1394                         (*accelerator_codes)[j++] = keys[i].keycode;
1395                     }
1396
1397                   if (j == 0)
1398                     {
1399                       g_free (*accelerator_codes);
1400                       *accelerator_codes = NULL;
1401                       /* Not in keymap */
1402                       error = TRUE;
1403                       goto out;
1404                     }
1405                   g_free (keys);
1406                 }
1407             }
1408
1409           accelerator += len;
1410           len -= len;
1411         }
1412     }
1413
1414 out:
1415   if (error)
1416     keyval = mods = 0;
1417
1418   if (accelerator_key)
1419     *accelerator_key = gdk_keyval_to_lower (keyval);
1420   if (accelerator_mods)
1421     *accelerator_mods = mods;
1422 }
1423
1424 /**
1425  * gtk_accelerator_parse:
1426  * @accelerator: string representing an accelerator
1427  * @accelerator_key: (out) (allow-none): return location for accelerator
1428  *     keyval, or %NULL
1429  * @accelerator_mods: (out) (allow-none): return location for accelerator
1430  *     modifier mask, %NULL
1431  *
1432  * Parses a string representing an accelerator. The
1433  * format looks like "&lt;Control&gt;a" or "&lt;Shift&gt;&lt;Alt&gt;F1"
1434  * or "&lt;Release&gt;z" (the last one is for key release).
1435  *
1436  * The parser is fairly liberal and allows lower or upper case,
1437  * and also abbreviations such as "&lt;Ctl&gt;" and "&lt;Ctrl&gt;".
1438  * Key names are parsed using gdk_keyval_from_name(). For character
1439  * keys the name is not the symbol, but the lowercase name, e.g. one
1440  * would use "&lt;Ctrl&gt;minus" instead of "&lt;Ctrl&gt;-".
1441  *
1442  * If the parse fails, @accelerator_key and @accelerator_mods will
1443  * be set to 0 (zero).
1444  */
1445 void
1446 gtk_accelerator_parse (const gchar     *accelerator,
1447                        guint           *accelerator_key,
1448                        GdkModifierType *accelerator_mods)
1449 {
1450   gtk_accelerator_parse_with_keycode (accelerator, accelerator_key, NULL, accelerator_mods);
1451 }
1452
1453 /**
1454  * gtk_accelerator_name_with_keycode:
1455  * @display: (allow-none): a #GdkDisplay or %NULL to use the default display
1456  * @accelerator_key: accelerator keyval
1457  * @accelerator_mods: accelerator modifier mask
1458  *
1459  * Converts an accelerator keyval and modifier mask
1460  * into a string parseable by gtk_accelerator_parse_full(),
1461  * similarly to gtk_accelerator_name() but handling keycodes.
1462  * This is only useful for system-level components, applications
1463  * should use gtk_accelerator_parse() instead.
1464  *
1465  * Returns: a newly allocated accelerator name.
1466  *
1467  * Since: 3.4
1468  */
1469 gchar *
1470 gtk_accelerator_name_with_keycode (GdkDisplay      *display,
1471                                    guint            accelerator_key,
1472                                    guint            keycode,
1473                                    GdkModifierType  accelerator_mods)
1474 {
1475   gchar *gtk_name;
1476
1477   if (display == NULL)
1478     display = gdk_display_manager_get_default_display (gdk_display_manager_get ());
1479
1480   gdk_keymap_add_virtual_modifiers (gdk_keymap_get_for_display (display), &accelerator_mods);
1481   gtk_name = gtk_accelerator_name (accelerator_key, accelerator_mods);
1482
1483   if (!accelerator_key)
1484     {
1485       gchar *name;
1486       name = g_strdup_printf ("%s0x%02x", gtk_name, keycode);
1487       g_free (gtk_name);
1488       return name;
1489     }
1490
1491   return gtk_name;
1492 }
1493
1494 /**
1495  * gtk_accelerator_name:
1496  * @accelerator_key: accelerator keyval
1497  * @accelerator_mods: accelerator modifier mask
1498  *
1499  * Converts an accelerator keyval and modifier mask
1500  * into a string parseable by gtk_accelerator_parse().
1501  * For example, if you pass in #GDK_KEY_q and #GDK_CONTROL_MASK,
1502  * this function returns "&lt;Control&gt;q".
1503  *
1504  * If you need to display accelerators in the user interface,
1505  * see gtk_accelerator_get_label().
1506  *
1507  * Returns: a newly-allocated accelerator name
1508  */
1509 gchar*
1510 gtk_accelerator_name (guint           accelerator_key,
1511                       GdkModifierType accelerator_mods)
1512 {
1513   static const gchar text_release[] = "<Release>";
1514   static const gchar text_primary[] = "<Primary>";
1515   static const gchar text_shift[] = "<Shift>";
1516   static const gchar text_control[] = "<Control>";
1517   static const gchar text_mod1[] = "<Alt>";
1518   static const gchar text_mod2[] = "<Mod2>";
1519   static const gchar text_mod3[] = "<Mod3>";
1520   static const gchar text_mod4[] = "<Mod4>";
1521   static const gchar text_mod5[] = "<Mod5>";
1522   static const gchar text_meta[] = "<Meta>";
1523   static const gchar text_super[] = "<Super>";
1524   static const gchar text_hyper[] = "<Hyper>";
1525   GdkModifierType saved_mods;
1526   guint l;
1527   gchar *keyval_name;
1528   gchar *accelerator;
1529
1530   accelerator_mods &= GDK_MODIFIER_MASK;
1531
1532   keyval_name = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key));
1533   if (!keyval_name)
1534     keyval_name = "";
1535
1536   saved_mods = accelerator_mods;
1537   l = 0;
1538   if (accelerator_mods & GDK_RELEASE_MASK)
1539     l += sizeof (text_release) - 1;
1540   if (accelerator_mods & _gtk_get_primary_accel_mod ())
1541     {
1542       l += sizeof (text_primary) - 1;
1543       accelerator_mods &= ~_gtk_get_primary_accel_mod (); /* consume the default accel */
1544     }
1545   if (accelerator_mods & GDK_SHIFT_MASK)
1546     l += sizeof (text_shift) - 1;
1547   if (accelerator_mods & GDK_CONTROL_MASK)
1548     l += sizeof (text_control) - 1;
1549   if (accelerator_mods & GDK_MOD1_MASK)
1550     l += sizeof (text_mod1) - 1;
1551   if (accelerator_mods & GDK_MOD2_MASK)
1552     l += sizeof (text_mod2) - 1;
1553   if (accelerator_mods & GDK_MOD3_MASK)
1554     l += sizeof (text_mod3) - 1;
1555   if (accelerator_mods & GDK_MOD4_MASK)
1556     l += sizeof (text_mod4) - 1;
1557   if (accelerator_mods & GDK_MOD5_MASK)
1558     l += sizeof (text_mod5) - 1;
1559   l += strlen (keyval_name);
1560   if (accelerator_mods & GDK_META_MASK)
1561     l += sizeof (text_meta) - 1;
1562   if (accelerator_mods & GDK_HYPER_MASK)
1563     l += sizeof (text_hyper) - 1;
1564   if (accelerator_mods & GDK_SUPER_MASK)
1565     l += sizeof (text_super) - 1;
1566
1567   accelerator = g_new (gchar, l + 1);
1568
1569   accelerator_mods = saved_mods;
1570   l = 0;
1571   accelerator[l] = 0;
1572   if (accelerator_mods & GDK_RELEASE_MASK)
1573     {
1574       strcpy (accelerator + l, text_release);
1575       l += sizeof (text_release) - 1;
1576     }
1577   if (accelerator_mods & _gtk_get_primary_accel_mod ())
1578     {
1579       strcpy (accelerator + l, text_primary);
1580       l += sizeof (text_primary) - 1;
1581       accelerator_mods &= ~_gtk_get_primary_accel_mod (); /* consume the default accel */
1582     }
1583   if (accelerator_mods & GDK_SHIFT_MASK)
1584     {
1585       strcpy (accelerator + l, text_shift);
1586       l += sizeof (text_shift) - 1;
1587     }
1588   if (accelerator_mods & GDK_CONTROL_MASK)
1589     {
1590       strcpy (accelerator + l, text_control);
1591       l += sizeof (text_control) - 1;
1592     }
1593   if (accelerator_mods & GDK_MOD1_MASK)
1594     {
1595       strcpy (accelerator + l, text_mod1);
1596       l += sizeof (text_mod1) - 1;
1597     }
1598   if (accelerator_mods & GDK_MOD2_MASK)
1599     {
1600       strcpy (accelerator + l, text_mod2);
1601       l += sizeof (text_mod2) - 1;
1602     }
1603   if (accelerator_mods & GDK_MOD3_MASK)
1604     {
1605       strcpy (accelerator + l, text_mod3);
1606       l += sizeof (text_mod3) - 1;
1607     }
1608   if (accelerator_mods & GDK_MOD4_MASK)
1609     {
1610       strcpy (accelerator + l, text_mod4);
1611       l += sizeof (text_mod4) - 1;
1612     }
1613   if (accelerator_mods & GDK_MOD5_MASK)
1614     {
1615       strcpy (accelerator + l, text_mod5);
1616       l += sizeof (text_mod5) - 1;
1617     }
1618   if (accelerator_mods & GDK_META_MASK)
1619     {
1620       strcpy (accelerator + l, text_meta);
1621       l += sizeof (text_meta) - 1;
1622     }
1623   if (accelerator_mods & GDK_HYPER_MASK)
1624     {
1625       strcpy (accelerator + l, text_hyper);
1626       l += sizeof (text_hyper) - 1;
1627     }
1628   if (accelerator_mods & GDK_SUPER_MASK)
1629     {
1630       strcpy (accelerator + l, text_super);
1631       l += sizeof (text_super) - 1;
1632     }
1633   strcpy (accelerator + l, keyval_name);
1634
1635   return accelerator;
1636 }
1637
1638 /**
1639  * gtk_accelerator_get_label_with_keycode:
1640  * @display: (allow-none): a #GdkDisplay or %NULL to use the default display
1641  * @accelerator_key: accelerator keyval
1642  * @accelerator_mods: accelerator modifier mask
1643  *
1644  * Converts an accelerator keyval and modifier mask
1645  * into a (possibly translated) string that can be displayed to
1646  * a user, similarly to gtk_accelerator_get_label(), but handling
1647  * keycodes.
1648  *
1649  * This is only useful for system-level components, applications
1650  * should use gtk_accelerator_parse() instead.
1651  *
1652  * Returns: a newly-allocated string representing the accelerator.
1653  *
1654  * Since: 3.4
1655  */
1656 gchar *
1657 gtk_accelerator_get_label_with_keycode (GdkDisplay      *display,
1658                                         guint            accelerator_key,
1659                                         guint            keycode,
1660                                         GdkModifierType  accelerator_mods)
1661 {
1662   gchar *gtk_label;
1663
1664   if (display == NULL)
1665     display = gdk_display_manager_get_default_display (gdk_display_manager_get ());
1666
1667   gdk_keymap_add_virtual_modifiers (gdk_keymap_get_for_display (display), &accelerator_mods);
1668   gtk_label = gtk_accelerator_get_label (accelerator_key, accelerator_mods);
1669
1670   if (!accelerator_key)
1671     {
1672       gchar *label;
1673       label = g_strdup_printf ("%s0x%02x", gtk_label, keycode);
1674       g_free (gtk_label);
1675       return label;
1676     }
1677
1678   return gtk_label;
1679 }
1680
1681 /**
1682  * gtk_accelerator_get_label:
1683  * @accelerator_key: accelerator keyval
1684  * @accelerator_mods: accelerator modifier mask
1685  *
1686  * Converts an accelerator keyval and modifier mask into a string
1687  * which can be used to represent the accelerator to the user.
1688  *
1689  * Returns: a newly-allocated string representing the accelerator.
1690  *
1691  * Since: 2.6
1692  */
1693 gchar*
1694 gtk_accelerator_get_label (guint           accelerator_key,
1695                            GdkModifierType accelerator_mods)
1696 {
1697   GtkAccelLabelClass *klass;
1698   gchar *label;
1699
1700   klass = g_type_class_ref (GTK_TYPE_ACCEL_LABEL);
1701   label = _gtk_accel_label_class_get_accelerator_label (klass,
1702                                                         accelerator_key,
1703                                                         accelerator_mods);
1704   g_type_class_unref (klass); /* klass is kept alive since gtk uses static types */
1705
1706   return label;
1707 }
1708
1709 /**
1710  * gtk_accelerator_set_default_mod_mask:
1711  * @default_mod_mask: accelerator modifier mask
1712  *
1713  * Sets the modifiers that will be considered significant for keyboard
1714  * accelerators. The default mod mask is #GDK_CONTROL_MASK |
1715  * #GDK_SHIFT_MASK | #GDK_MOD1_MASK | #GDK_SUPER_MASK |
1716  * #GDK_HYPER_MASK | #GDK_META_MASK, that is, Control, Shift, Alt,
1717  * Super, Hyper and Meta. Other modifiers will by default be ignored
1718  * by #GtkAccelGroup.
1719  * You must include at least the three modifiers Control, Shift
1720  * and Alt in any value you pass to this function.
1721  *
1722  * The default mod mask should be changed on application startup,
1723  * before using any accelerator groups.
1724  */
1725 void
1726 gtk_accelerator_set_default_mod_mask (GdkModifierType default_mod_mask)
1727 {
1728   default_accel_mod_mask = (default_mod_mask & GDK_MODIFIER_MASK) |
1729     (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK);
1730 }
1731
1732 /**
1733  * gtk_accelerator_get_default_mod_mask:
1734  *
1735  * Gets the value set by gtk_accelerator_set_default_mod_mask().
1736  *
1737  * Returns: the default accelerator modifier mask
1738  */
1739 GdkModifierType
1740 gtk_accelerator_get_default_mod_mask (void)
1741 {
1742   return default_accel_mod_mask;
1743 }