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