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