1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1998, 2001 Tim Janik
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.
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.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
31 #include "gtkaccelgroup.h"
32 #include "gtkaccellabel.h" /* For _gtk_accel_label_class_get_accelerator_label */
33 #include "gtkaccelmap.h"
35 #include "gtkmain.h" /* For _gtk_boolean_handled_accumulator */
36 #include "gdk/gdkkeysyms.h"
37 #include "gtkmarshalers.h"
41 * SECTION:gtkaccelgroup
42 * @Short_description: Groups of global keyboard accelerators for an entire GtkWindow
43 * @Title: Accelerator Groups
44 * @See_also:gtk_window_add_accel_group(), gtk_accel_map_change_entry(),
45 * gtk_item_factory_new(), gtk_label_new_with_mnemonic()
47 * A #GtkAccelGroup represents a group of keyboard accelerators,
48 * typically attached to a toplevel #GtkWindow (with
49 * gtk_window_add_accel_group()). Usually you won't need to create a
50 * #GtkAccelGroup directly; instead, when using #GtkItemFactory, GTK+
51 * automatically sets up the accelerators for your menus in the item
52 * factory's #GtkAccelGroup.
55 * Note that <firstterm>accelerators</firstterm> are different from
56 * <firstterm>mnemonics</firstterm>. Accelerators are shortcuts for
57 * activating a menu item; they appear alongside the menu item they're a
58 * shortcut for. For example "Ctrl+Q" might appear alongside the "Quit"
59 * menu item. Mnemonics are shortcuts for GUI elements such as text
60 * entries or buttons; they appear as underlined characters. See
61 * gtk_label_new_with_mnemonic(). Menu items can have both accelerators
62 * and mnemonics, of course.
66 /* --- prototypes --- */
67 static void gtk_accel_group_finalize (GObject *object);
68 static void gtk_accel_group_get_property (GObject *object,
72 static void accel_closure_invalidate (gpointer data,
76 /* --- variables --- */
77 static guint signal_accel_activate = 0;
78 static guint signal_accel_changed = 0;
79 static guint quark_acceleratable_groups = 0;
80 static guint default_accel_mod_mask = (GDK_SHIFT_MASK |
94 G_DEFINE_TYPE (GtkAccelGroup, gtk_accel_group, G_TYPE_OBJECT)
96 /* --- functions --- */
98 gtk_accel_group_class_init (GtkAccelGroupClass *class)
100 GObjectClass *object_class = G_OBJECT_CLASS (class);
102 quark_acceleratable_groups = g_quark_from_static_string ("gtk-acceleratable-accel-groups");
104 object_class->finalize = gtk_accel_group_finalize;
105 object_class->get_property = gtk_accel_group_get_property;
107 class->accel_changed = NULL;
109 g_object_class_install_property (object_class,
111 g_param_spec_boolean ("is-locked",
113 "Is the accel group locked",
117 g_object_class_install_property (object_class,
119 g_param_spec_flags ("modifier-mask",
122 GDK_TYPE_MODIFIER_TYPE,
123 default_accel_mod_mask,
127 * GtkAccelGroup::accel-activate:
128 * @accel_group: the #GtkAccelGroup which received the signal
129 * @acceleratable: the object on which the accelerator was activated
130 * @keyval: the accelerator keyval
131 * @modifier: the modifier combination of the accelerator
133 * The accel-activate signal is an implementation detail of
134 * #GtkAccelGroup and not meant to be used by applications.
136 * Returns: %TRUE if the accelerator was activated
138 signal_accel_activate =
139 g_signal_new (I_("accel-activate"),
140 G_OBJECT_CLASS_TYPE (class),
143 _gtk_boolean_handled_accumulator, NULL,
144 _gtk_marshal_BOOLEAN__OBJECT_UINT_FLAGS,
148 GDK_TYPE_MODIFIER_TYPE);
150 * GtkAccelGroup::accel-changed:
151 * @accel_group: the #GtkAccelGroup which received the signal
152 * @keyval: the accelerator keyval
153 * @modifier: the modifier combination of the accelerator
154 * @accel_closure: the #GClosure of the accelerator
156 * The accel-changed signal is emitted when a #GtkAccelGroupEntry
157 * is added to or removed from the accel group.
159 * Widgets like #GtkAccelLabel which display an associated
160 * accelerator should connect to this signal, and rebuild
161 * their visual representation if the @accel_closure is theirs.
163 signal_accel_changed =
164 g_signal_new (I_("accel-changed"),
165 G_OBJECT_CLASS_TYPE (class),
166 G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
167 G_STRUCT_OFFSET (GtkAccelGroupClass, accel_changed),
169 _gtk_marshal_VOID__UINT_FLAGS_BOXED,
172 GDK_TYPE_MODIFIER_TYPE,
177 gtk_accel_group_finalize (GObject *object)
179 GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (object);
182 for (i = 0; i < accel_group->n_accels; i++)
184 GtkAccelGroupEntry *entry = &accel_group->priv_accels[i];
186 if (entry->accel_path_quark)
188 const gchar *accel_path = g_quark_to_string (entry->accel_path_quark);
190 _gtk_accel_map_remove_group (accel_path, accel_group);
192 g_closure_remove_invalidate_notifier (entry->closure, accel_group, accel_closure_invalidate);
194 /* remove quick_accel_add() refcount */
195 g_closure_unref (entry->closure);
198 g_free (accel_group->priv_accels);
200 G_OBJECT_CLASS (gtk_accel_group_parent_class)->finalize (object);
204 gtk_accel_group_get_property (GObject *object,
209 GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (object);
214 g_value_set_boolean (value, accel_group->lock_count > 0);
216 case PROP_MODIFIER_MASK:
217 g_value_set_flags (value, accel_group->modifier_mask);
220 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
226 gtk_accel_group_init (GtkAccelGroup *accel_group)
228 accel_group->lock_count = 0;
229 accel_group->modifier_mask = gtk_accelerator_get_default_mod_mask ();
230 accel_group->acceleratables = NULL;
231 accel_group->n_accels = 0;
232 accel_group->priv_accels = NULL;
236 * gtk_accel_group_new:
237 * @returns: a new #GtkAccelGroup object
239 * Creates a new #GtkAccelGroup.
242 gtk_accel_group_new (void)
244 return g_object_new (GTK_TYPE_ACCEL_GROUP, NULL);
248 * gtk_accel_group_get_is_locked:
249 * @accel_group: a #GtkAccelGroup
251 * Locks are added and removed using gtk_accel_group_lock() and
252 * gtk_accel_group_unlock().
254 * Returns: %TRUE if there are 1 or more locks on the @accel_group,
260 gtk_accel_group_get_is_locked (GtkAccelGroup *accel_group)
262 g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
264 return accel_group->lock_count > 0;
268 * gtk_accel_group_get_modifier_mask:
269 * @accel_group: a #GtkAccelGroup
271 * Gets a #GdkModifierType representing the mask for this
272 * @accel_group. For example, #GDK_CONTROL_MASK, #GDK_SHIFT_MASK, etc.
274 * Returns: the modifier mask for this accel group.
279 gtk_accel_group_get_modifier_mask (GtkAccelGroup *accel_group)
281 g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), 0);
283 return accel_group->modifier_mask;
287 accel_group_weak_ref_detach (GSList *free_list,
288 GObject *stale_object)
292 for (slist = free_list; slist; slist = slist->next)
294 GtkAccelGroup *accel_group;
296 accel_group = slist->data;
297 accel_group->acceleratables = g_slist_remove (accel_group->acceleratables, stale_object);
298 g_object_unref (accel_group);
300 g_slist_free (free_list);
301 g_object_set_qdata (stale_object, quark_acceleratable_groups, NULL);
305 _gtk_accel_group_attach (GtkAccelGroup *accel_group,
310 g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
311 g_return_if_fail (G_IS_OBJECT (object));
312 g_return_if_fail (g_slist_find (accel_group->acceleratables, object) == NULL);
314 g_object_ref (accel_group);
315 accel_group->acceleratables = g_slist_prepend (accel_group->acceleratables, object);
316 slist = g_object_get_qdata (object, quark_acceleratable_groups);
318 g_object_weak_unref (object,
319 (GWeakNotify) accel_group_weak_ref_detach,
321 slist = g_slist_prepend (slist, accel_group);
322 g_object_set_qdata (object, quark_acceleratable_groups, slist);
323 g_object_weak_ref (object,
324 (GWeakNotify) accel_group_weak_ref_detach,
329 _gtk_accel_group_detach (GtkAccelGroup *accel_group,
334 g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
335 g_return_if_fail (G_IS_OBJECT (object));
336 g_return_if_fail (g_slist_find (accel_group->acceleratables, object) != NULL);
338 accel_group->acceleratables = g_slist_remove (accel_group->acceleratables, object);
339 slist = g_object_get_qdata (object, quark_acceleratable_groups);
340 g_object_weak_unref (object,
341 (GWeakNotify) accel_group_weak_ref_detach,
343 slist = g_slist_remove (slist, accel_group);
344 g_object_set_qdata (object, quark_acceleratable_groups, slist);
346 g_object_weak_ref (object,
347 (GWeakNotify) accel_group_weak_ref_detach,
349 g_object_unref (accel_group);
353 * gtk_accel_groups_from_object:
354 * @object: a #GObject, usually a #GtkWindow
356 * Gets a list of all accel groups which are attached to @object.
358 * Returns: (element-type GtkAccelGroup) (transfer none): a list of all accel groups which are attached to @object
361 gtk_accel_groups_from_object (GObject *object)
363 g_return_val_if_fail (G_IS_OBJECT (object), NULL);
365 return g_object_get_qdata (object, quark_acceleratable_groups);
369 * gtk_accel_group_find:
370 * @accel_group: a #GtkAccelGroup
371 * @find_func: a function to filter the entries of @accel_group with
372 * @data: data to pass to @find_func
373 * @returns: the key of the first entry passing @find_func. The key is
374 * owned by GTK+ and must not be freed.
376 * Finds the first entry in an accelerator group for which
377 * @find_func returns %TRUE and returns its #GtkAccelKey.
381 gtk_accel_group_find (GtkAccelGroup *accel_group,
382 GtkAccelGroupFindFunc find_func,
385 GtkAccelKey *key = NULL;
388 g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL);
389 g_return_val_if_fail (find_func != NULL, NULL);
391 g_object_ref (accel_group);
392 for (i = 0; i < accel_group->n_accels; i++)
393 if (find_func (&accel_group->priv_accels[i].key,
394 accel_group->priv_accels[i].closure,
397 key = &accel_group->priv_accels[i].key;
400 g_object_unref (accel_group);
406 * gtk_accel_group_lock:
407 * @accel_group: a #GtkAccelGroup
409 * Locks the given accelerator group.
411 * Locking an acelerator group prevents the accelerators contained
412 * within it to be changed during runtime. Refer to
413 * gtk_accel_map_change_entry() about runtime accelerator changes.
415 * If called more than once, @accel_group remains locked until
416 * gtk_accel_group_unlock() has been called an equivalent number
420 gtk_accel_group_lock (GtkAccelGroup *accel_group)
422 g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
424 accel_group->lock_count += 1;
426 if (accel_group->lock_count == 1) {
427 /* State change from unlocked to locked */
428 g_object_notify (G_OBJECT (accel_group), "is-locked");
433 * gtk_accel_group_unlock:
434 * @accel_group: a #GtkAccelGroup
436 * Undoes the last call to gtk_accel_group_lock() on this @accel_group.
439 gtk_accel_group_unlock (GtkAccelGroup *accel_group)
441 g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
442 g_return_if_fail (accel_group->lock_count > 0);
444 accel_group->lock_count -= 1;
446 if (accel_group->lock_count < 1) {
447 /* State change from locked to unlocked */
448 g_object_notify (G_OBJECT (accel_group), "is-locked");
453 accel_closure_invalidate (gpointer data,
456 GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (data);
458 gtk_accel_group_disconnect (accel_group, closure);
462 bsearch_compare_accels (const void *d1,
465 const GtkAccelGroupEntry *entry1 = d1;
466 const GtkAccelGroupEntry *entry2 = d2;
468 if (entry1->key.accel_key == entry2->key.accel_key)
469 return entry1->key.accel_mods < entry2->key.accel_mods ? -1 : entry1->key.accel_mods > entry2->key.accel_mods;
471 return entry1->key.accel_key < entry2->key.accel_key ? -1 : 1;
475 quick_accel_add (GtkAccelGroup *accel_group,
477 GdkModifierType accel_mods,
478 GtkAccelFlags accel_flags,
482 guint pos, i = accel_group->n_accels++;
483 GtkAccelGroupEntry key;
486 key.key.accel_key = accel_key;
487 key.key.accel_mods = accel_mods;
488 for (pos = 0; pos < i; pos++)
489 if (bsearch_compare_accels (&key, accel_group->priv_accels + pos) < 0)
492 /* insert at position, ref closure */
493 accel_group->priv_accels = g_renew (GtkAccelGroupEntry, accel_group->priv_accels, accel_group->n_accels);
494 g_memmove (accel_group->priv_accels + pos + 1, accel_group->priv_accels + pos,
495 (i - pos) * sizeof (accel_group->priv_accels[0]));
496 accel_group->priv_accels[pos].key.accel_key = accel_key;
497 accel_group->priv_accels[pos].key.accel_mods = accel_mods;
498 accel_group->priv_accels[pos].key.accel_flags = accel_flags;
499 accel_group->priv_accels[pos].closure = g_closure_ref (closure);
500 accel_group->priv_accels[pos].accel_path_quark = path_quark;
501 g_closure_sink (closure);
503 /* handle closure invalidation and reverse lookups */
504 g_closure_add_invalidate_notifier (closure, accel_group, accel_closure_invalidate);
506 /* get accel path notification */
508 _gtk_accel_map_add_group (g_quark_to_string (path_quark), accel_group);
510 /* connect and notify changed */
513 gchar *accel_name = gtk_accelerator_name (accel_key, accel_mods);
514 GQuark accel_quark = g_quark_from_string (accel_name);
519 g_signal_connect_closure_by_id (accel_group, signal_accel_activate, accel_quark, closure, FALSE);
522 g_signal_emit (accel_group, signal_accel_changed, accel_quark, accel_key, accel_mods, closure);
527 quick_accel_remove (GtkAccelGroup *accel_group,
530 GQuark accel_quark = 0;
531 GtkAccelGroupEntry *entry = accel_group->priv_accels + pos;
532 guint accel_key = entry->key.accel_key;
533 GdkModifierType accel_mods = entry->key.accel_mods;
534 GClosure *closure = entry->closure;
536 /* quark for notification */
539 gchar *accel_name = gtk_accelerator_name (accel_key, accel_mods);
541 accel_quark = g_quark_from_string (accel_name);
545 /* clean up closure invalidate notification and disconnect */
546 g_closure_remove_invalidate_notifier (entry->closure, accel_group, accel_closure_invalidate);
548 g_signal_handlers_disconnect_matched (accel_group,
549 G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_CLOSURE,
550 signal_accel_activate, accel_quark,
551 closure, NULL, NULL);
552 /* clean up accel path notification */
553 if (entry->accel_path_quark)
554 _gtk_accel_map_remove_group (g_quark_to_string (entry->accel_path_quark), accel_group);
556 /* physically remove */
557 accel_group->n_accels -= 1;
558 g_memmove (entry, entry + 1,
559 (accel_group->n_accels - pos) * sizeof (accel_group->priv_accels[0]));
563 g_signal_emit (accel_group, signal_accel_changed, accel_quark, accel_key, accel_mods, closure);
565 /* remove quick_accel_add() refcount */
566 g_closure_unref (closure);
569 static GtkAccelGroupEntry*
570 quick_accel_find (GtkAccelGroup *accel_group,
572 GdkModifierType accel_mods,
575 GtkAccelGroupEntry *entry;
576 GtkAccelGroupEntry key;
580 if (!accel_group->n_accels)
583 key.key.accel_key = accel_key;
584 key.key.accel_mods = accel_mods;
585 entry = bsearch (&key, accel_group->priv_accels, accel_group->n_accels,
586 sizeof (accel_group->priv_accels[0]), bsearch_compare_accels);
591 /* step back to the first member */
592 for (; entry > accel_group->priv_accels; entry--)
593 if (entry[-1].key.accel_key != accel_key ||
594 entry[-1].key.accel_mods != accel_mods)
596 /* count equal members */
597 for (; entry + *count_p < accel_group->priv_accels + accel_group->n_accels; (*count_p)++)
598 if (entry[*count_p].key.accel_key != accel_key ||
599 entry[*count_p].key.accel_mods != accel_mods)
605 * gtk_accel_group_connect:
606 * @accel_group: the accelerator group to install an accelerator in
607 * @accel_key: key value of the accelerator
608 * @accel_mods: modifier combination of the accelerator
609 * @accel_flags: a flag mask to configure this accelerator
610 * @closure: closure to be executed upon accelerator activation
612 * Installs an accelerator in this group. When @accel_group is being activated
613 * in response to a call to gtk_accel_groups_activate(), @closure will be
614 * invoked if the @accel_key and @accel_mods from gtk_accel_groups_activate()
615 * match those of this connection.
617 * The signature used for the @closure is that of #GtkAccelGroupActivate.
619 * Note that, due to implementation details, a single closure can only be
620 * connected to one accelerator group.
623 gtk_accel_group_connect (GtkAccelGroup *accel_group,
625 GdkModifierType accel_mods,
626 GtkAccelFlags accel_flags,
629 g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
630 g_return_if_fail (closure != NULL);
631 g_return_if_fail (accel_key > 0);
632 g_return_if_fail (gtk_accel_group_from_accel_closure (closure) == NULL);
634 g_object_ref (accel_group);
635 if (!closure->is_invalid)
636 quick_accel_add (accel_group,
637 gdk_keyval_to_lower (accel_key),
638 accel_mods, accel_flags, closure, 0);
639 g_object_unref (accel_group);
643 * gtk_accel_group_connect_by_path:
644 * @accel_group: the accelerator group to install an accelerator in
645 * @accel_path: path used for determining key and modifiers.
646 * @closure: closure to be executed upon accelerator activation
648 * Installs an accelerator in this group, using an accelerator path to look
649 * up the appropriate key and modifiers (see gtk_accel_map_add_entry()).
650 * When @accel_group is being activated in response to a call to
651 * gtk_accel_groups_activate(), @closure will be invoked if the @accel_key and
652 * @accel_mods from gtk_accel_groups_activate() match the key and modifiers
655 * The signature used for the @closure is that of #GtkAccelGroupActivate.
657 * Note that @accel_path string will be stored in a #GQuark. Therefore, if you
658 * pass a static string, you can save some memory by interning it first with
659 * g_intern_static_string().
662 gtk_accel_group_connect_by_path (GtkAccelGroup *accel_group,
663 const gchar *accel_path,
667 GdkModifierType accel_mods = 0;
670 g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
671 g_return_if_fail (closure != NULL);
672 g_return_if_fail (_gtk_accel_path_is_valid (accel_path));
674 if (closure->is_invalid)
677 g_object_ref (accel_group);
679 if (gtk_accel_map_lookup_entry (accel_path, &key))
681 accel_key = gdk_keyval_to_lower (key.accel_key);
682 accel_mods = key.accel_mods;
685 quick_accel_add (accel_group, accel_key, accel_mods, GTK_ACCEL_VISIBLE, closure,
686 g_quark_from_string (accel_path));
688 g_object_unref (accel_group);
692 * gtk_accel_group_disconnect:
693 * @accel_group: the accelerator group to remove an accelerator from
694 * @closure: (allow-none): the closure to remove from this accelerator group, or %NULL
695 * to remove all closures
696 * @returns: %TRUE if the closure was found and got disconnected
698 * Removes an accelerator previously installed through
699 * gtk_accel_group_connect().
701 * Since 2.20 @closure can be %NULL.
704 gtk_accel_group_disconnect (GtkAccelGroup *accel_group,
709 g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
711 for (i = 0; i < accel_group->n_accels; i++)
712 if (accel_group->priv_accels[i].closure == closure || !closure)
714 g_object_ref (accel_group);
715 quick_accel_remove (accel_group, i);
716 g_object_unref (accel_group);
723 * gtk_accel_group_disconnect_key:
724 * @accel_group: the accelerator group to install an accelerator in
725 * @accel_key: key value of the accelerator
726 * @accel_mods: modifier combination of the accelerator
727 * @returns: %TRUE if there was an accelerator which could be
728 * removed, %FALSE otherwise
730 * Removes an accelerator previously installed through
731 * gtk_accel_group_connect().
734 gtk_accel_group_disconnect_key (GtkAccelGroup *accel_group,
736 GdkModifierType accel_mods)
738 GtkAccelGroupEntry *entries;
739 GSList *slist, *clist = NULL;
740 gboolean removed_one = FALSE;
743 g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
745 g_object_ref (accel_group);
747 accel_key = gdk_keyval_to_lower (accel_key);
748 entries = quick_accel_find (accel_group, accel_key, accel_mods, &n);
751 GClosure *closure = g_closure_ref (entries[n].closure);
753 clist = g_slist_prepend (clist, closure);
756 for (slist = clist; slist; slist = slist->next)
758 GClosure *closure = slist->data;
760 removed_one |= gtk_accel_group_disconnect (accel_group, closure);
761 g_closure_unref (closure);
763 g_slist_free (clist);
765 g_object_unref (accel_group);
771 _gtk_accel_group_reconnect (GtkAccelGroup *accel_group,
772 GQuark accel_path_quark)
774 GSList *slist, *clist = NULL;
777 g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
779 g_object_ref (accel_group);
781 for (i = 0; i < accel_group->n_accels; i++)
782 if (accel_group->priv_accels[i].accel_path_quark == accel_path_quark)
784 GClosure *closure = g_closure_ref (accel_group->priv_accels[i].closure);
786 clist = g_slist_prepend (clist, closure);
789 for (slist = clist; slist; slist = slist->next)
791 GClosure *closure = slist->data;
793 gtk_accel_group_disconnect (accel_group, closure);
794 gtk_accel_group_connect_by_path (accel_group, g_quark_to_string (accel_path_quark), closure);
795 g_closure_unref (closure);
797 g_slist_free (clist);
799 g_object_unref (accel_group);
803 * gtk_accel_group_query:
804 * @accel_group: the accelerator group to query
805 * @accel_key: key value of the accelerator
806 * @accel_mods: modifier combination of the accelerator
807 * @n_entries: (allow-none): location to return the number of entries found, or %NULL
808 * @returns: (allow-none): an array of @n_entries #GtkAccelGroupEntry elements, or %NULL. The array is owned by GTK+ and must not be freed.
810 * Queries an accelerator group for all entries matching @accel_key and
814 gtk_accel_group_query (GtkAccelGroup *accel_group,
816 GdkModifierType accel_mods,
819 GtkAccelGroupEntry *entries;
822 g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL);
824 entries = quick_accel_find (accel_group, gdk_keyval_to_lower (accel_key), accel_mods, &n);
827 *n_entries = entries ? n : 0;
833 * gtk_accel_group_from_accel_closure:
834 * @closure: a #GClosure
835 * @returns: (allow-none): the #GtkAccelGroup to which @closure is connected, or %NULL.
837 * Finds the #GtkAccelGroup to which @closure is connected;
838 * see gtk_accel_group_connect().
841 gtk_accel_group_from_accel_closure (GClosure *closure)
845 g_return_val_if_fail (closure != NULL, NULL);
847 /* a few remarks on what we do here. in general, we need a way to reverse lookup
848 * accel_groups from closures that are being used in accel groups. this could
849 * be done e.g via a hashtable. it is however cheaper (memory wise) to just
850 * use the invalidation notifier on the closure itself (which we need to install
851 * anyway), that contains the accel group as data which, besides needing to peek
852 * a bit at closure internals, works just as good.
854 for (i = 0; i < G_CLOSURE_N_NOTIFIERS (closure); i++)
855 if (closure->notifiers[i].notify == accel_closure_invalidate)
856 return closure->notifiers[i].data;
862 * gtk_accel_group_activate:
863 * @accel_group: a #GtkAccelGroup
864 * @accel_quark: the quark for the accelerator name
865 * @acceleratable: the #GObject, usually a #GtkWindow, on which
866 * to activate the accelerator.
867 * @accel_key: accelerator keyval from a key event
868 * @accel_mods: keyboard state mask from a key event
870 * Finds the first accelerator in @accel_group
871 * that matches @accel_key and @accel_mods, and
874 * Returns: %TRUE if an accelerator was activated and handled this keypress
877 gtk_accel_group_activate (GtkAccelGroup *accel_group,
879 GObject *acceleratable,
881 GdkModifierType accel_mods)
883 gboolean was_handled;
885 g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
886 g_return_val_if_fail (G_IS_OBJECT (acceleratable), FALSE);
889 g_signal_emit (accel_group, signal_accel_activate, accel_quark,
890 acceleratable, accel_key, accel_mods, &was_handled);
896 * gtk_accel_groups_activate:
897 * @object: the #GObject, usually a #GtkWindow, on which
898 * to activate the accelerator.
899 * @accel_key: accelerator keyval from a key event
900 * @accel_mods: keyboard state mask from a key event
902 * Finds the first accelerator in any #GtkAccelGroup attached
903 * to @object that matches @accel_key and @accel_mods, and
904 * activates that accelerator.
906 * Returns: %TRUE if an accelerator was activated and handled this keypress
909 gtk_accel_groups_activate (GObject *object,
911 GdkModifierType accel_mods)
913 g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
915 if (gtk_accelerator_valid (accel_key, accel_mods))
921 accel_name = gtk_accelerator_name (accel_key, (accel_mods & gtk_accelerator_get_default_mod_mask ()));
922 accel_quark = g_quark_from_string (accel_name);
925 for (slist = gtk_accel_groups_from_object (object); slist; slist = slist->next)
926 if (gtk_accel_group_activate (slist->data, accel_quark, object, accel_key, accel_mods))
934 * gtk_accelerator_valid:
935 * @keyval: a GDK keyval
936 * @modifiers: modifier mask
937 * @returns: %TRUE if the accelerator is valid
939 * Determines whether a given keyval and modifier mask constitute
940 * a valid keyboard accelerator. For example, the #GDK_a keyval
941 * plus #GDK_CONTROL_MASK is valid - this is a "Ctrl+a" accelerator.
942 * But, you can't, for instance, use the #GDK_Control_L keyval
946 gtk_accelerator_valid (guint keyval,
947 GdkModifierType modifiers)
949 static const guint invalid_accelerator_vals[] = {
950 GDK_Shift_L, GDK_Shift_R, GDK_Shift_Lock, GDK_Caps_Lock, GDK_ISO_Lock,
951 GDK_Control_L, GDK_Control_R, GDK_Meta_L, GDK_Meta_R,
952 GDK_Alt_L, GDK_Alt_R, GDK_Super_L, GDK_Super_R, GDK_Hyper_L, GDK_Hyper_R,
953 GDK_ISO_Level3_Shift, GDK_ISO_Next_Group, GDK_ISO_Prev_Group,
954 GDK_ISO_First_Group, GDK_ISO_Last_Group,
955 GDK_Mode_switch, GDK_Num_Lock, GDK_Multi_key,
956 GDK_Scroll_Lock, GDK_Sys_Req,
957 GDK_Tab, GDK_ISO_Left_Tab, GDK_KP_Tab,
958 GDK_First_Virtual_Screen, GDK_Prev_Virtual_Screen,
959 GDK_Next_Virtual_Screen, GDK_Last_Virtual_Screen,
960 GDK_Terminate_Server, GDK_AudibleBell_Enable,
963 static const guint invalid_unmodified_vals[] = {
964 GDK_Up, GDK_Down, GDK_Left, GDK_Right,
965 GDK_KP_Up, GDK_KP_Down, GDK_KP_Left, GDK_KP_Right,
970 modifiers &= GDK_MODIFIER_MASK;
973 return keyval >= 0x20;
975 ac_val = invalid_accelerator_vals;
978 if (keyval == *ac_val++)
984 ac_val = invalid_unmodified_vals;
987 if (keyval == *ac_val++)
995 static inline gboolean
996 is_alt (const gchar *string)
998 return ((string[0] == '<') &&
999 (string[1] == 'a' || string[1] == 'A') &&
1000 (string[2] == 'l' || string[2] == 'L') &&
1001 (string[3] == 't' || string[3] == 'T') &&
1002 (string[4] == '>'));
1005 static inline gboolean
1006 is_ctl (const gchar *string)
1008 return ((string[0] == '<') &&
1009 (string[1] == 'c' || string[1] == 'C') &&
1010 (string[2] == 't' || string[2] == 'T') &&
1011 (string[3] == 'l' || string[3] == 'L') &&
1012 (string[4] == '>'));
1015 static inline gboolean
1016 is_modx (const gchar *string)
1018 return ((string[0] == '<') &&
1019 (string[1] == 'm' || string[1] == 'M') &&
1020 (string[2] == 'o' || string[2] == 'O') &&
1021 (string[3] == 'd' || string[3] == 'D') &&
1022 (string[4] >= '1' && string[4] <= '5') &&
1023 (string[5] == '>'));
1026 static inline gboolean
1027 is_ctrl (const gchar *string)
1029 return ((string[0] == '<') &&
1030 (string[1] == 'c' || string[1] == 'C') &&
1031 (string[2] == 't' || string[2] == 'T') &&
1032 (string[3] == 'r' || string[3] == 'R') &&
1033 (string[4] == 'l' || string[4] == 'L') &&
1034 (string[5] == '>'));
1037 static inline gboolean
1038 is_shft (const gchar *string)
1040 return ((string[0] == '<') &&
1041 (string[1] == 's' || string[1] == 'S') &&
1042 (string[2] == 'h' || string[2] == 'H') &&
1043 (string[3] == 'f' || string[3] == 'F') &&
1044 (string[4] == 't' || string[4] == 'T') &&
1045 (string[5] == '>'));
1048 static inline gboolean
1049 is_shift (const gchar *string)
1051 return ((string[0] == '<') &&
1052 (string[1] == 's' || string[1] == 'S') &&
1053 (string[2] == 'h' || string[2] == 'H') &&
1054 (string[3] == 'i' || string[3] == 'I') &&
1055 (string[4] == 'f' || string[4] == 'F') &&
1056 (string[5] == 't' || string[5] == 'T') &&
1057 (string[6] == '>'));
1060 static inline gboolean
1061 is_control (const gchar *string)
1063 return ((string[0] == '<') &&
1064 (string[1] == 'c' || string[1] == 'C') &&
1065 (string[2] == 'o' || string[2] == 'O') &&
1066 (string[3] == 'n' || string[3] == 'N') &&
1067 (string[4] == 't' || string[4] == 'T') &&
1068 (string[5] == 'r' || string[5] == 'R') &&
1069 (string[6] == 'o' || string[6] == 'O') &&
1070 (string[7] == 'l' || string[7] == 'L') &&
1071 (string[8] == '>'));
1074 static inline gboolean
1075 is_release (const gchar *string)
1077 return ((string[0] == '<') &&
1078 (string[1] == 'r' || string[1] == 'R') &&
1079 (string[2] == 'e' || string[2] == 'E') &&
1080 (string[3] == 'l' || string[3] == 'L') &&
1081 (string[4] == 'e' || string[4] == 'E') &&
1082 (string[5] == 'a' || string[5] == 'A') &&
1083 (string[6] == 's' || string[6] == 'S') &&
1084 (string[7] == 'e' || string[7] == 'E') &&
1085 (string[8] == '>'));
1088 static inline gboolean
1089 is_meta (const gchar *string)
1091 return ((string[0] == '<') &&
1092 (string[1] == 'm' || string[1] == 'M') &&
1093 (string[2] == 'e' || string[2] == 'E') &&
1094 (string[3] == 't' || string[3] == 'T') &&
1095 (string[4] == 'a' || string[4] == 'A') &&
1096 (string[5] == '>'));
1099 static inline gboolean
1100 is_super (const gchar *string)
1102 return ((string[0] == '<') &&
1103 (string[1] == 's' || string[1] == 'S') &&
1104 (string[2] == 'u' || string[2] == 'U') &&
1105 (string[3] == 'p' || string[3] == 'P') &&
1106 (string[4] == 'e' || string[4] == 'E') &&
1107 (string[5] == 'r' || string[5] == 'R') &&
1108 (string[6] == '>'));
1111 static inline gboolean
1112 is_hyper (const gchar *string)
1114 return ((string[0] == '<') &&
1115 (string[1] == 'h' || string[1] == 'H') &&
1116 (string[2] == 'y' || string[2] == 'Y') &&
1117 (string[3] == 'p' || string[3] == 'P') &&
1118 (string[4] == 'e' || string[4] == 'E') &&
1119 (string[5] == 'r' || string[5] == 'R') &&
1120 (string[6] == '>'));
1124 * gtk_accelerator_parse:
1125 * @accelerator: string representing an accelerator
1126 * @accelerator_key: return location for accelerator keyval
1127 * @accelerator_mods: return location for accelerator modifier mask
1129 * Parses a string representing an accelerator. The
1130 * format looks like "<Control>a" or "<Shift><Alt>F1" or
1131 * "<Release>z" (the last one is for key release).
1132 * The parser is fairly liberal and allows lower or upper case,
1133 * and also abbreviations such as "<Ctl>" and "<Ctrl>".
1135 * If the parse fails, @accelerator_key and @accelerator_mods will
1136 * be set to 0 (zero).
1139 gtk_accelerator_parse (const gchar *accelerator,
1140 guint *accelerator_key,
1141 GdkModifierType *accelerator_mods)
1144 GdkModifierType mods;
1147 if (accelerator_key)
1148 *accelerator_key = 0;
1149 if (accelerator_mods)
1150 *accelerator_mods = 0;
1151 g_return_if_fail (accelerator != NULL);
1155 len = strlen (accelerator);
1158 if (*accelerator == '<')
1160 if (len >= 9 && is_release (accelerator))
1164 mods |= GDK_RELEASE_MASK;
1166 else if (len >= 9 && is_control (accelerator))
1170 mods |= GDK_CONTROL_MASK;
1172 else if (len >= 7 && is_shift (accelerator))
1176 mods |= GDK_SHIFT_MASK;
1178 else if (len >= 6 && is_shft (accelerator))
1182 mods |= GDK_SHIFT_MASK;
1184 else if (len >= 6 && is_ctrl (accelerator))
1188 mods |= GDK_CONTROL_MASK;
1190 else if (len >= 6 && is_modx (accelerator))
1192 static const guint mod_vals[] = {
1193 GDK_MOD1_MASK, GDK_MOD2_MASK, GDK_MOD3_MASK,
1194 GDK_MOD4_MASK, GDK_MOD5_MASK
1199 mods |= mod_vals[*accelerator - '1'];
1202 else if (len >= 5 && is_ctl (accelerator))
1206 mods |= GDK_CONTROL_MASK;
1208 else if (len >= 5 && is_alt (accelerator))
1212 mods |= GDK_MOD1_MASK;
1214 else if (len >= 6 && is_meta (accelerator))
1218 mods |= GDK_META_MASK;
1220 else if (len >= 7 && is_hyper (accelerator))
1224 mods |= GDK_HYPER_MASK;
1226 else if (len >= 7 && is_super (accelerator))
1230 mods |= GDK_SUPER_MASK;
1236 last_ch = *accelerator;
1237 while (last_ch && last_ch != '>')
1239 last_ch = *accelerator;
1247 keyval = gdk_keyval_from_name (accelerator);
1253 if (accelerator_key)
1254 *accelerator_key = gdk_keyval_to_lower (keyval);
1255 if (accelerator_mods)
1256 *accelerator_mods = mods;
1260 * gtk_accelerator_name:
1261 * @accelerator_key: accelerator keyval
1262 * @accelerator_mods: accelerator modifier mask
1264 * Converts an accelerator keyval and modifier mask
1265 * into a string parseable by gtk_accelerator_parse().
1266 * For example, if you pass in #GDK_q and #GDK_CONTROL_MASK,
1267 * this function returns "<Control>q".
1269 * If you need to display accelerators in the user interface,
1270 * see gtk_accelerator_get_label().
1272 * Returns: a newly-allocated accelerator name
1275 gtk_accelerator_name (guint accelerator_key,
1276 GdkModifierType accelerator_mods)
1278 static const gchar text_release[] = "<Release>";
1279 static const gchar text_shift[] = "<Shift>";
1280 static const gchar text_control[] = "<Control>";
1281 static const gchar text_mod1[] = "<Alt>";
1282 static const gchar text_mod2[] = "<Mod2>";
1283 static const gchar text_mod3[] = "<Mod3>";
1284 static const gchar text_mod4[] = "<Mod4>";
1285 static const gchar text_mod5[] = "<Mod5>";
1286 static const gchar text_meta[] = "<Meta>";
1287 static const gchar text_super[] = "<Super>";
1288 static const gchar text_hyper[] = "<Hyper>";
1293 accelerator_mods &= GDK_MODIFIER_MASK;
1295 keyval_name = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key));
1300 if (accelerator_mods & GDK_RELEASE_MASK)
1301 l += sizeof (text_release) - 1;
1302 if (accelerator_mods & GDK_SHIFT_MASK)
1303 l += sizeof (text_shift) - 1;
1304 if (accelerator_mods & GDK_CONTROL_MASK)
1305 l += sizeof (text_control) - 1;
1306 if (accelerator_mods & GDK_MOD1_MASK)
1307 l += sizeof (text_mod1) - 1;
1308 if (accelerator_mods & GDK_MOD2_MASK)
1309 l += sizeof (text_mod2) - 1;
1310 if (accelerator_mods & GDK_MOD3_MASK)
1311 l += sizeof (text_mod3) - 1;
1312 if (accelerator_mods & GDK_MOD4_MASK)
1313 l += sizeof (text_mod4) - 1;
1314 if (accelerator_mods & GDK_MOD5_MASK)
1315 l += sizeof (text_mod5) - 1;
1316 l += strlen (keyval_name);
1317 if (accelerator_mods & GDK_META_MASK)
1318 l += sizeof (text_meta) - 1;
1319 if (accelerator_mods & GDK_HYPER_MASK)
1320 l += sizeof (text_hyper) - 1;
1321 if (accelerator_mods & GDK_SUPER_MASK)
1322 l += sizeof (text_super) - 1;
1324 accelerator = g_new (gchar, l + 1);
1328 if (accelerator_mods & GDK_RELEASE_MASK)
1330 strcpy (accelerator + l, text_release);
1331 l += sizeof (text_release) - 1;
1333 if (accelerator_mods & GDK_SHIFT_MASK)
1335 strcpy (accelerator + l, text_shift);
1336 l += sizeof (text_shift) - 1;
1338 if (accelerator_mods & GDK_CONTROL_MASK)
1340 strcpy (accelerator + l, text_control);
1341 l += sizeof (text_control) - 1;
1343 if (accelerator_mods & GDK_MOD1_MASK)
1345 strcpy (accelerator + l, text_mod1);
1346 l += sizeof (text_mod1) - 1;
1348 if (accelerator_mods & GDK_MOD2_MASK)
1350 strcpy (accelerator + l, text_mod2);
1351 l += sizeof (text_mod2) - 1;
1353 if (accelerator_mods & GDK_MOD3_MASK)
1355 strcpy (accelerator + l, text_mod3);
1356 l += sizeof (text_mod3) - 1;
1358 if (accelerator_mods & GDK_MOD4_MASK)
1360 strcpy (accelerator + l, text_mod4);
1361 l += sizeof (text_mod4) - 1;
1363 if (accelerator_mods & GDK_MOD5_MASK)
1365 strcpy (accelerator + l, text_mod5);
1366 l += sizeof (text_mod5) - 1;
1368 if (accelerator_mods & GDK_META_MASK)
1370 strcpy (accelerator + l, text_meta);
1371 l += sizeof (text_meta) - 1;
1373 if (accelerator_mods & GDK_HYPER_MASK)
1375 strcpy (accelerator + l, text_hyper);
1376 l += sizeof (text_hyper) - 1;
1378 if (accelerator_mods & GDK_SUPER_MASK)
1380 strcpy (accelerator + l, text_super);
1381 l += sizeof (text_super) - 1;
1383 strcpy (accelerator + l, keyval_name);
1389 * gtk_accelerator_get_label:
1390 * @accelerator_key: accelerator keyval
1391 * @accelerator_mods: accelerator modifier mask
1393 * Converts an accelerator keyval and modifier mask into a string
1394 * which can be used to represent the accelerator to the user.
1396 * Returns: a newly-allocated string representing the accelerator.
1401 gtk_accelerator_get_label (guint accelerator_key,
1402 GdkModifierType accelerator_mods)
1404 GtkAccelLabelClass *klass;
1407 klass = g_type_class_ref (GTK_TYPE_ACCEL_LABEL);
1408 label = _gtk_accel_label_class_get_accelerator_label (klass,
1411 g_type_class_unref (klass); /* klass is kept alive since gtk uses static types */
1417 * gtk_accelerator_set_default_mod_mask:
1418 * @default_mod_mask: accelerator modifier mask
1420 * Sets the modifiers that will be considered significant for keyboard
1421 * accelerators. The default mod mask is #GDK_CONTROL_MASK |
1422 * #GDK_SHIFT_MASK | #GDK_MOD1_MASK | #GDK_SUPER_MASK |
1423 * #GDK_HYPER_MASK | #GDK_META_MASK, that is, Control, Shift, Alt,
1424 * Super, Hyper and Meta. Other modifiers will by default be ignored
1425 * by #GtkAccelGroup.
1426 * You must include at least the three modifiers Control, Shift
1427 * and Alt in any value you pass to this function.
1429 * The default mod mask should be changed on application startup,
1430 * before using any accelerator groups.
1433 gtk_accelerator_set_default_mod_mask (GdkModifierType default_mod_mask)
1435 default_accel_mod_mask = (default_mod_mask & GDK_MODIFIER_MASK) |
1436 (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK);
1440 * gtk_accelerator_get_default_mod_mask:
1441 * @returns: the default accelerator modifier mask
1443 * Gets the value set by gtk_accelerator_set_default_mod_mask().
1446 gtk_accelerator_get_default_mod_mask (void)
1448 return default_accel_mod_mask;
1451 #define __GTK_ACCEL_GROUP_C__
1452 #include "gtkaliasdef.c"