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 /* --- prototypes --- */
42 static void gtk_accel_group_class_init (GtkAccelGroupClass *class);
43 static void gtk_accel_group_init (GtkAccelGroup *accel_group);
44 static void gtk_accel_group_finalize (GObject *object);
47 /* --- variables --- */
48 static GObjectClass *parent_class = NULL;
49 static guint signal_accel_activate = 0;
50 static guint signal_accel_changed = 0;
51 static guint quark_acceleratable_groups = 0;
52 static guint default_accel_mod_mask = (GDK_SHIFT_MASK |
60 /* --- functions --- */
62 * gtk_accel_group_get_type:
63 * @returns: the type ID for accelerator groups.
66 gtk_accel_group_get_type (void)
68 static GType object_type = 0;
72 static const GTypeInfo object_info = {
73 sizeof (GtkAccelGroupClass),
75 (GBaseFinalizeFunc) NULL,
76 (GClassInitFunc) gtk_accel_group_class_init,
77 NULL, /* class_finalize */
78 NULL, /* class_data */
79 sizeof (GtkAccelGroup),
81 (GInstanceInitFunc) gtk_accel_group_init,
84 object_type = g_type_register_static (G_TYPE_OBJECT, I_("GtkAccelGroup"),
92 gtk_accel_group_class_init (GtkAccelGroupClass *class)
94 GObjectClass *object_class = G_OBJECT_CLASS (class);
96 parent_class = g_type_class_peek_parent (class);
98 quark_acceleratable_groups = g_quark_from_static_string ("gtk-acceleratable-accel-groups");
100 object_class->finalize = gtk_accel_group_finalize;
102 class->accel_changed = NULL;
105 * GtkAccelGroup::accel-activate:
106 * @accel_group: the #GtkAccelGroup which received the signal
107 * @acceleratable: the object on which the accelerator was activated
108 * @keyval: the accelerator keyval
109 * @modifier: the modifier combination of the accelerator
111 * The accel-activate signal is an implementation detail of
112 * #GtkAccelGroup and not meant to be used by applications.
114 * Returns: %TRUE if the accelerator was activated
116 signal_accel_activate =
117 g_signal_new (I_("accel_activate"),
118 G_OBJECT_CLASS_TYPE (class),
121 _gtk_boolean_handled_accumulator, NULL,
122 _gtk_marshal_BOOLEAN__OBJECT_UINT_FLAGS,
126 GDK_TYPE_MODIFIER_TYPE);
128 * GtkAccelGroup::accel-changed:
129 * @accel_group: the #GtkAccelGroup which received the signal
130 * @keyval: the accelerator keyval
131 * @modifier: the modifier combination of the accelerator
132 * @accel_closure: the #GClosure of the accelerator
134 * The accel-changed signal is emitted when a #GtkAccelGroupEntry
135 * is added to or removed from the accel group.
137 * Widgets like #GtkAccelLabel which display an associated
138 * accelerator should connect to this signal, and rebuild
139 * their visual representation if the @accel_closure is theirs.
141 signal_accel_changed =
142 g_signal_new (I_("accel_changed"),
143 G_OBJECT_CLASS_TYPE (class),
144 G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
145 G_STRUCT_OFFSET (GtkAccelGroupClass, accel_changed),
147 _gtk_marshal_VOID__UINT_FLAGS_BOXED,
150 GDK_TYPE_MODIFIER_TYPE,
155 gtk_accel_group_finalize (GObject *object)
157 GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (object);
160 for (i = 0; i < accel_group->n_accels; i++)
162 GtkAccelGroupEntry *entry = &accel_group->priv_accels[i];
164 if (entry->accel_path_quark)
166 const gchar *accel_path = g_quark_to_string (entry[i].accel_path_quark);
168 _gtk_accel_map_remove_group (accel_path, accel_group);
172 g_free (accel_group->priv_accels);
174 G_OBJECT_CLASS (parent_class)->finalize (object);
178 gtk_accel_group_init (GtkAccelGroup *accel_group)
180 accel_group->lock_count = 0;
181 accel_group->modifier_mask = gtk_accelerator_get_default_mod_mask ();
182 accel_group->acceleratables = NULL;
183 accel_group->n_accels = 0;
184 accel_group->priv_accels = NULL;
188 * gtk_accel_group_new:
189 * @returns: a new #GtkAccelGroup object
191 * Creates a new #GtkAccelGroup.
194 gtk_accel_group_new (void)
196 return g_object_new (GTK_TYPE_ACCEL_GROUP, NULL);
200 accel_group_weak_ref_detach (GSList *free_list,
201 GObject *stale_object)
205 for (slist = free_list; slist; slist = slist->next)
207 GtkAccelGroup *accel_group;
209 accel_group = slist->data;
210 accel_group->acceleratables = g_slist_remove (accel_group->acceleratables, stale_object);
211 g_object_unref (accel_group);
213 g_slist_free (free_list);
214 g_object_set_qdata (stale_object, quark_acceleratable_groups, NULL);
218 _gtk_accel_group_attach (GtkAccelGroup *accel_group,
223 g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
224 g_return_if_fail (G_IS_OBJECT (object));
225 g_return_if_fail (g_slist_find (accel_group->acceleratables, object) == NULL);
227 g_object_ref (accel_group);
228 accel_group->acceleratables = g_slist_prepend (accel_group->acceleratables, object);
229 slist = g_object_get_qdata (object, quark_acceleratable_groups);
231 g_object_weak_unref (object,
232 (GWeakNotify) accel_group_weak_ref_detach,
234 slist = g_slist_prepend (slist, accel_group);
235 g_object_set_qdata (object, quark_acceleratable_groups, slist);
236 g_object_weak_ref (object,
237 (GWeakNotify) accel_group_weak_ref_detach,
242 _gtk_accel_group_detach (GtkAccelGroup *accel_group,
247 g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
248 g_return_if_fail (G_IS_OBJECT (object));
249 g_return_if_fail (g_slist_find (accel_group->acceleratables, object) != NULL);
251 accel_group->acceleratables = g_slist_remove (accel_group->acceleratables, object);
252 slist = g_object_get_qdata (object, quark_acceleratable_groups);
253 g_object_weak_unref (object,
254 (GWeakNotify) accel_group_weak_ref_detach,
256 slist = g_slist_remove (slist, accel_group);
257 g_object_set_qdata (object, quark_acceleratable_groups, slist);
259 g_object_weak_ref (object,
260 (GWeakNotify) accel_group_weak_ref_detach,
262 g_object_unref (accel_group);
266 * gtk_accel_groups_from_object:
267 * @object: a #GObject, usually a #GtkWindow
268 * @returns: a list of all accel groups which are attached to @object
270 * Gets a list of all accel groups which are attached to @object.
273 gtk_accel_groups_from_object (GObject *object)
275 g_return_val_if_fail (G_IS_OBJECT (object), NULL);
277 return g_object_get_qdata (object, quark_acceleratable_groups);
281 * gtk_accel_group_find:
282 * @accel_group: a #GtkAccelGroup
283 * @find_func: a function to filter the entries of @accel_group with
284 * @data: data to pass to @find_func
285 * @returns: the key of the first entry passing @find_func. The key is
286 * owned by GTK+ and must not be freed.
288 * Finds the first entry in an accelerator group for which
289 * @find_func returns %TRUE and returns its #GtkAccelKey.
293 gtk_accel_group_find (GtkAccelGroup *accel_group,
294 GtkAccelGroupFindFunc find_func,
297 GtkAccelKey *key = NULL;
300 g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL);
301 g_return_val_if_fail (find_func != NULL, NULL);
303 g_object_ref (accel_group);
304 for (i = 0; i < accel_group->n_accels; i++)
305 if (find_func (&accel_group->priv_accels[i].key,
306 accel_group->priv_accels[i].closure,
309 key = &accel_group->priv_accels[i].key;
312 g_object_unref (accel_group);
318 * gtk_accel_group_lock:
319 * @accel_group: a #GtkAccelGroup
321 * Locks the given accelerator group.
323 * Locking an acelerator group prevents the accelerators contained
324 * within it to be changed during runtime. Refer to
325 * gtk_accel_map_change_entry() about runtime accelerator changes.
327 * If called more than once, @accel_group remains locked until
328 * gtk_accel_group_unlock() has been called an equivalent number
332 gtk_accel_group_lock (GtkAccelGroup *accel_group)
334 g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
336 accel_group->lock_count += 1;
340 * gtk_accel_group_unlock:
341 * @accel_group: a #GtkAccelGroup
343 * Undoes the last call to gtk_accel_group_lock() on this @accel_group.
346 gtk_accel_group_unlock (GtkAccelGroup *accel_group)
348 g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
349 g_return_if_fail (accel_group->lock_count > 0);
351 accel_group->lock_count -= 1;
355 accel_closure_invalidate (gpointer data,
358 GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (data);
360 gtk_accel_group_disconnect (accel_group, closure);
364 bsearch_compare_accels (const void *d1,
367 const GtkAccelGroupEntry *entry1 = d1;
368 const GtkAccelGroupEntry *entry2 = d2;
370 if (entry1->key.accel_key == entry2->key.accel_key)
371 return entry1->key.accel_mods < entry2->key.accel_mods ? -1 : entry1->key.accel_mods > entry2->key.accel_mods;
373 return entry1->key.accel_key < entry2->key.accel_key ? -1 : 1;
377 quick_accel_add (GtkAccelGroup *accel_group,
379 GdkModifierType accel_mods,
380 GtkAccelFlags accel_flags,
384 guint pos, i = accel_group->n_accels++;
385 GtkAccelGroupEntry key;
388 key.key.accel_key = accel_key;
389 key.key.accel_mods = accel_mods;
390 for (pos = 0; pos < i; pos++)
391 if (bsearch_compare_accels (&key, accel_group->priv_accels + pos) < 0)
394 /* insert at position, ref closure */
395 accel_group->priv_accels = g_renew (GtkAccelGroupEntry, accel_group->priv_accels, accel_group->n_accels);
396 g_memmove (accel_group->priv_accels + pos + 1, accel_group->priv_accels + pos,
397 (i - pos) * sizeof (accel_group->priv_accels[0]));
398 accel_group->priv_accels[pos].key.accel_key = accel_key;
399 accel_group->priv_accels[pos].key.accel_mods = accel_mods;
400 accel_group->priv_accels[pos].key.accel_flags = accel_flags;
401 accel_group->priv_accels[pos].closure = g_closure_ref (closure);
402 accel_group->priv_accels[pos].accel_path_quark = path_quark;
403 g_closure_sink (closure);
405 /* handle closure invalidation and reverse lookups */
406 g_closure_add_invalidate_notifier (closure, accel_group, accel_closure_invalidate);
408 /* get accel path notification */
410 _gtk_accel_map_add_group (g_quark_to_string (path_quark), accel_group);
412 /* connect and notify changed */
415 gchar *accel_name = gtk_accelerator_name (accel_key, accel_mods);
416 GQuark accel_quark = g_quark_from_string (accel_name);
421 g_signal_connect_closure_by_id (accel_group, signal_accel_activate, accel_quark, closure, FALSE);
424 g_signal_emit (accel_group, signal_accel_changed, accel_quark, accel_key, accel_mods, closure);
429 quick_accel_remove (GtkAccelGroup *accel_group,
430 GtkAccelGroupEntry *entry)
432 guint pos = entry - accel_group->priv_accels;
433 GQuark accel_quark = 0;
434 guint accel_key = entry->key.accel_key;
435 GdkModifierType accel_mods = entry->key.accel_mods;
436 GClosure *closure = entry->closure;
438 /* quark for notification */
441 gchar *accel_name = gtk_accelerator_name (accel_key, accel_mods);
443 accel_quark = g_quark_from_string (accel_name);
447 /* clean up closure invalidate notification and disconnect */
448 g_closure_remove_invalidate_notifier (entry->closure, accel_group, accel_closure_invalidate);
450 g_signal_handlers_disconnect_matched (accel_group,
451 G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_CLOSURE,
452 signal_accel_activate, accel_quark,
453 closure, NULL, NULL);
454 /* clean up accel path notification */
455 if (entry->accel_path_quark)
456 _gtk_accel_map_remove_group (g_quark_to_string (entry->accel_path_quark), accel_group);
458 /* physically remove */
459 accel_group->n_accels -= 1;
460 g_memmove (entry, entry + 1,
461 (accel_group->n_accels - pos) * sizeof (accel_group->priv_accels[0]));
465 g_signal_emit (accel_group, signal_accel_changed, accel_quark, accel_key, accel_mods, closure);
467 /* remove quick_accel_add() refcount */
468 g_closure_unref (closure);
471 static GtkAccelGroupEntry*
472 quick_accel_find (GtkAccelGroup *accel_group,
474 GdkModifierType accel_mods,
477 GtkAccelGroupEntry *entry;
478 GtkAccelGroupEntry key;
482 if (!accel_group->n_accels)
485 key.key.accel_key = accel_key;
486 key.key.accel_mods = accel_mods;
487 entry = bsearch (&key, accel_group->priv_accels, accel_group->n_accels,
488 sizeof (accel_group->priv_accels[0]), bsearch_compare_accels);
493 /* step back to the first member */
494 for (; entry > accel_group->priv_accels; entry--)
495 if (entry[-1].key.accel_key != accel_key ||
496 entry[-1].key.accel_mods != accel_mods)
498 /* count equal members */
499 for (; entry + *count_p < accel_group->priv_accels + accel_group->n_accels; (*count_p)++)
500 if (entry[*count_p].key.accel_key != accel_key ||
501 entry[*count_p].key.accel_mods != accel_mods)
507 * gtk_accel_group_connect:
508 * @accel_group: the accelerator group to install an accelerator in
509 * @accel_key: key value of the accelerator
510 * @accel_mods: modifier combination of the accelerator
511 * @accel_flags: a flag mask to configure this accelerator
512 * @closure: closure to be executed upon accelerator activation
514 * Installs an accelerator in this group. When @accel_group is being activated
515 * in response to a call to gtk_accel_groups_activate(), @closure will be
516 * invoked if the @accel_key and @accel_mods from gtk_accel_groups_activate()
517 * match those of this connection.
519 * The signature used for the @closure is that of #GtkAccelGroupActivate.
521 * Note that, due to implementation details, a single closure can only be
522 * connected to one accelerator group.
525 gtk_accel_group_connect (GtkAccelGroup *accel_group,
527 GdkModifierType accel_mods,
528 GtkAccelFlags accel_flags,
531 g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
532 g_return_if_fail (closure != NULL);
533 g_return_if_fail (accel_key > 0);
534 g_return_if_fail (gtk_accel_group_from_accel_closure (closure) == NULL);
536 g_object_ref (accel_group);
537 if (!closure->is_invalid)
538 quick_accel_add (accel_group,
539 gdk_keyval_to_lower (accel_key),
540 accel_mods, accel_flags, closure, 0);
541 g_object_unref (accel_group);
545 * gtk_accel_group_connect_by_path:
546 * @accel_group: the accelerator group to install an accelerator in
547 * @accel_path: path used for determining key and modifiers.
548 * @closure: closure to be executed upon accelerator activation
550 * Installs an accelerator in this group, using an accelerator path to look
551 * up the appropriate key and modifiers (see gtk_accel_map_add_entry()).
552 * When @accel_group is being activated in response to a call to
553 * gtk_accel_groups_activate(), @closure will be invoked if the @accel_key and
554 * @accel_mods from gtk_accel_groups_activate() match the key and modifiers
557 * The signature used for the @closure is that of #GtkAccelGroupActivate.
560 gtk_accel_group_connect_by_path (GtkAccelGroup *accel_group,
561 const gchar *accel_path,
565 GdkModifierType accel_mods = 0;
568 g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
569 g_return_if_fail (closure != NULL);
570 g_return_if_fail (_gtk_accel_path_is_valid (accel_path));
572 if (closure->is_invalid)
575 g_object_ref (accel_group);
577 if (gtk_accel_map_lookup_entry (accel_path, &key))
579 accel_key = gdk_keyval_to_lower (key.accel_key);
580 accel_mods = key.accel_mods;
583 quick_accel_add (accel_group, accel_key, accel_mods, GTK_ACCEL_VISIBLE, closure,
584 g_quark_from_string (accel_path));
586 g_object_unref (accel_group);
590 * gtk_accel_group_disconnect:
591 * @accel_group: the accelerator group to remove an accelerator from
592 * @closure: the closure to remove from this accelerator group
593 * @returns: %TRUE if the closure was found and got disconnected
595 * Removes an accelerator previously installed through
596 * gtk_accel_group_connect().
599 gtk_accel_group_disconnect (GtkAccelGroup *accel_group,
604 g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
606 for (i = 0; i < accel_group->n_accels; i++)
607 if (accel_group->priv_accels[i].closure == closure)
609 g_object_ref (accel_group);
610 quick_accel_remove (accel_group, accel_group->priv_accels + i);
611 g_object_unref (accel_group);
618 * gtk_accel_group_disconnect_key:
619 * @accel_group: the accelerator group to install an accelerator in
620 * @accel_key: key value of the accelerator
621 * @accel_mods: modifier combination of the accelerator
622 * @returns: %TRUE if there was an accelerator which could be
623 * removed, %FALSE otherwise
625 * Removes an accelerator previously installed through
626 * gtk_accel_group_connect().
629 gtk_accel_group_disconnect_key (GtkAccelGroup *accel_group,
631 GdkModifierType accel_mods)
633 GtkAccelGroupEntry *entries;
634 GSList *slist, *clist = NULL;
635 gboolean removed_one = FALSE;
638 g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
640 g_object_ref (accel_group);
642 accel_key = gdk_keyval_to_lower (accel_key);
643 entries = quick_accel_find (accel_group, accel_key, accel_mods, &n);
646 GClosure *closure = g_closure_ref (entries[n].closure);
648 clist = g_slist_prepend (clist, closure);
651 for (slist = clist; slist; slist = slist->next)
653 GClosure *closure = slist->data;
655 removed_one |= gtk_accel_group_disconnect (accel_group, closure);
656 g_closure_unref (closure);
658 g_slist_free (clist);
660 g_object_unref (accel_group);
666 _gtk_accel_group_reconnect (GtkAccelGroup *accel_group,
667 GQuark accel_path_quark)
669 GSList *slist, *clist = NULL;
672 g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
674 g_object_ref (accel_group);
676 for (i = 0; i < accel_group->n_accels; i++)
677 if (accel_group->priv_accels[i].accel_path_quark == accel_path_quark)
679 GClosure *closure = g_closure_ref (accel_group->priv_accels[i].closure);
681 clist = g_slist_prepend (clist, closure);
684 for (slist = clist; slist; slist = slist->next)
686 GClosure *closure = slist->data;
688 gtk_accel_group_disconnect (accel_group, closure);
689 gtk_accel_group_connect_by_path (accel_group, g_quark_to_string (accel_path_quark), closure);
690 g_closure_unref (closure);
692 g_slist_free (clist);
694 g_object_unref (accel_group);
698 * gtk_accel_group_query:
699 * @accel_group: the accelerator group to query
700 * @accel_key: key value of the accelerator
701 * @accel_mods: modifier combination of the accelerator
702 * @n_entries: location to return the number of entries found, or %NULL
703 * @returns: an array of @n_entries #GtkAccelGroupEntry elements, or %NULL. The array is owned by GTK+ and must not be freed.
705 * Queries an accelerator group for all entries matching @accel_key and
709 gtk_accel_group_query (GtkAccelGroup *accel_group,
711 GdkModifierType accel_mods,
714 GtkAccelGroupEntry *entries;
717 g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL);
719 entries = quick_accel_find (accel_group, gdk_keyval_to_lower (accel_key), accel_mods, &n);
722 *n_entries = entries ? n : 0;
728 * gtk_accel_group_from_accel_closure:
729 * @closure: a #GClosure
730 * @returns: the #GtkAccelGroup to which @closure is connected, or %NULL.
732 * Finds the #GtkAccelGroup to which @closure is connected;
733 * see gtk_accel_group_connect().
736 gtk_accel_group_from_accel_closure (GClosure *closure)
740 g_return_val_if_fail (closure != NULL, NULL);
742 /* a few remarks on what we do here. in general, we need a way to reverse lookup
743 * accel_groups from closures that are being used in accel groups. this could
744 * be done e.g via a hashtable. it is however cheaper (memory wise) to just
745 * use the invalidation notifier on the closure itself (which we need to install
746 * anyway), that contains the accel group as data which, besides needing to peek
747 * a bit at closure internals, works just as good.
749 for (i = 0; i < G_CLOSURE_N_NOTIFIERS (closure); i++)
750 if (closure->notifiers[i].notify == accel_closure_invalidate)
751 return closure->notifiers[i].data;
757 gtk_accel_group_activate (GtkAccelGroup *accel_group,
759 GObject *acceleratable,
761 GdkModifierType accel_mods)
763 gboolean was_handled;
765 g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
766 g_return_val_if_fail (G_IS_OBJECT (acceleratable), FALSE);
769 g_signal_emit (accel_group, signal_accel_activate, accel_quark,
770 acceleratable, accel_key, accel_mods, &was_handled);
776 * gtk_accel_groups_activate:
777 * @object: the #GObject, usually a #GtkWindow, on which
778 * to activate the accelerator.
779 * @accel_key: accelerator keyval from a key event
780 * @accel_mods: keyboard state mask from a key event
781 * @returns: %TRUE if the accelerator was handled, %FALSE otherwise
783 * Finds the first accelerator in any #GtkAccelGroup attached
784 * to @object that matches @accel_key and @accel_mods, and
785 * activates that accelerator.
786 * If an accelerator was activated and handled this keypress, %TRUE
790 gtk_accel_groups_activate (GObject *object,
792 GdkModifierType accel_mods)
794 g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
796 if (gtk_accelerator_valid (accel_key, accel_mods))
802 accel_name = gtk_accelerator_name (accel_key, (accel_mods & gtk_accelerator_get_default_mod_mask ()));
803 accel_quark = g_quark_from_string (accel_name);
806 for (slist = gtk_accel_groups_from_object (object); slist; slist = slist->next)
807 if (gtk_accel_group_activate (slist->data, accel_quark, object, accel_key, accel_mods))
815 * gtk_accelerator_valid:
816 * @keyval: a GDK keyval
817 * @modifiers: modifier mask
818 * @returns: %TRUE if the accelerator is valid
820 * Determines whether a given keyval and modifier mask constitute
821 * a valid keyboard accelerator. For example, the #GDK_a keyval
822 * plus #GDK_CONTROL_MASK is valid - this is a "Ctrl+a" accelerator.
823 * But, you can't, for instance, use the #GDK_Control_L keyval
827 gtk_accelerator_valid (guint keyval,
828 GdkModifierType modifiers)
830 static const guint invalid_accelerator_vals[] = {
831 GDK_Shift_L, GDK_Shift_R, GDK_Shift_Lock, GDK_Caps_Lock, GDK_ISO_Lock,
832 GDK_Control_L, GDK_Control_R, GDK_Meta_L, GDK_Meta_R,
833 GDK_Alt_L, GDK_Alt_R, GDK_Super_L, GDK_Super_R, GDK_Hyper_L, GDK_Hyper_R,
834 GDK_ISO_Level3_Shift, GDK_ISO_Next_Group, GDK_ISO_Prev_Group,
835 GDK_ISO_First_Group, GDK_ISO_Last_Group,
836 GDK_Mode_switch, GDK_Num_Lock, GDK_Multi_key,
837 GDK_Scroll_Lock, GDK_Sys_Req,
838 GDK_Tab, GDK_ISO_Left_Tab, GDK_KP_Tab,
839 GDK_First_Virtual_Screen, GDK_Prev_Virtual_Screen,
840 GDK_Next_Virtual_Screen, GDK_Last_Virtual_Screen,
841 GDK_Terminate_Server, GDK_AudibleBell_Enable,
844 static const guint invalid_unmodified_vals[] = {
845 GDK_Up, GDK_Down, GDK_Left, GDK_Right,
846 GDK_KP_Up, GDK_KP_Down, GDK_KP_Left, GDK_KP_Right,
851 modifiers &= GDK_MODIFIER_MASK;
854 return keyval >= 0x20;
856 ac_val = invalid_accelerator_vals;
859 if (keyval == *ac_val++)
865 ac_val = invalid_unmodified_vals;
868 if (keyval == *ac_val++)
876 static inline gboolean
877 is_alt (const gchar *string)
879 return ((string[0] == '<') &&
880 (string[1] == 'a' || string[1] == 'A') &&
881 (string[2] == 'l' || string[2] == 'L') &&
882 (string[3] == 't' || string[3] == 'T') &&
886 static inline gboolean
887 is_ctl (const gchar *string)
889 return ((string[0] == '<') &&
890 (string[1] == 'c' || string[1] == 'C') &&
891 (string[2] == 't' || string[2] == 'T') &&
892 (string[3] == 'l' || string[3] == 'L') &&
896 static inline gboolean
897 is_modx (const gchar *string)
899 return ((string[0] == '<') &&
900 (string[1] == 'm' || string[1] == 'M') &&
901 (string[2] == 'o' || string[2] == 'O') &&
902 (string[3] == 'd' || string[3] == 'D') &&
903 (string[4] >= '1' && string[4] <= '5') &&
907 static inline gboolean
908 is_ctrl (const gchar *string)
910 return ((string[0] == '<') &&
911 (string[1] == 'c' || string[1] == 'C') &&
912 (string[2] == 't' || string[2] == 'T') &&
913 (string[3] == 'r' || string[3] == 'R') &&
914 (string[4] == 'l' || string[4] == 'L') &&
918 static inline gboolean
919 is_shft (const gchar *string)
921 return ((string[0] == '<') &&
922 (string[1] == 's' || string[1] == 'S') &&
923 (string[2] == 'h' || string[2] == 'H') &&
924 (string[3] == 'f' || string[3] == 'F') &&
925 (string[4] == 't' || string[4] == 'T') &&
929 static inline gboolean
930 is_shift (const gchar *string)
932 return ((string[0] == '<') &&
933 (string[1] == 's' || string[1] == 'S') &&
934 (string[2] == 'h' || string[2] == 'H') &&
935 (string[3] == 'i' || string[3] == 'I') &&
936 (string[4] == 'f' || string[4] == 'F') &&
937 (string[5] == 't' || string[5] == 'T') &&
941 static inline gboolean
942 is_control (const gchar *string)
944 return ((string[0] == '<') &&
945 (string[1] == 'c' || string[1] == 'C') &&
946 (string[2] == 'o' || string[2] == 'O') &&
947 (string[3] == 'n' || string[3] == 'N') &&
948 (string[4] == 't' || string[4] == 'T') &&
949 (string[5] == 'r' || string[5] == 'R') &&
950 (string[6] == 'o' || string[6] == 'O') &&
951 (string[7] == 'l' || string[7] == 'L') &&
955 static inline gboolean
956 is_release (const gchar *string)
958 return ((string[0] == '<') &&
959 (string[1] == 'r' || string[1] == 'R') &&
960 (string[2] == 'e' || string[2] == 'E') &&
961 (string[3] == 'l' || string[3] == 'L') &&
962 (string[4] == 'e' || string[4] == 'E') &&
963 (string[5] == 'a' || string[5] == 'A') &&
964 (string[6] == 's' || string[6] == 'S') &&
965 (string[7] == 'e' || string[7] == 'E') &&
969 static inline gboolean
970 is_meta (const gchar *string)
972 return ((string[0] == '<') &&
973 (string[1] == 'm' || string[1] == 'M') &&
974 (string[2] == 'e' || string[2] == 'E') &&
975 (string[3] == 't' || string[3] == 'T') &&
976 (string[4] == 'a' || string[4] == 'A') &&
980 static inline gboolean
981 is_super (const gchar *string)
983 return ((string[0] == '<') &&
984 (string[1] == 's' || string[1] == 'S') &&
985 (string[2] == 'u' || string[2] == 'U') &&
986 (string[3] == 'p' || string[3] == 'P') &&
987 (string[4] == 'e' || string[4] == 'E') &&
988 (string[5] == 'r' || string[5] == 'R') &&
992 static inline gboolean
993 is_hyper (const gchar *string)
995 return ((string[0] == '<') &&
996 (string[1] == 'h' || string[1] == 'H') &&
997 (string[2] == 'y' || string[2] == 'Y') &&
998 (string[3] == 'p' || string[3] == 'P') &&
999 (string[4] == 'e' || string[4] == 'E') &&
1000 (string[5] == 'r' || string[5] == 'R') &&
1001 (string[6] == '>'));
1005 * gtk_accelerator_parse:
1006 * @accelerator: string representing an accelerator
1007 * @accelerator_key: return location for accelerator keyval
1008 * @accelerator_mods: return location for accelerator modifier mask
1010 * Parses a string representing an accelerator. The
1011 * format looks like "<Control>a" or "<Shift><Alt>F1" or
1012 * "<Release>z" (the last one is for key release).
1013 * The parser is fairly liberal and allows lower or upper case,
1014 * and also abbreviations such as "<Ctl>" and "<Ctrl>".
1016 * If the parse fails, @accelerator_key and @accelerator_mods will
1017 * be set to 0 (zero).
1020 gtk_accelerator_parse (const gchar *accelerator,
1021 guint *accelerator_key,
1022 GdkModifierType *accelerator_mods)
1025 GdkModifierType mods;
1028 if (accelerator_key)
1029 *accelerator_key = 0;
1030 if (accelerator_mods)
1031 *accelerator_mods = 0;
1032 g_return_if_fail (accelerator != NULL);
1036 len = strlen (accelerator);
1039 if (*accelerator == '<')
1041 if (len >= 9 && is_release (accelerator))
1045 mods |= GDK_RELEASE_MASK;
1047 else if (len >= 9 && is_control (accelerator))
1051 mods |= GDK_CONTROL_MASK;
1053 else if (len >= 7 && is_shift (accelerator))
1057 mods |= GDK_SHIFT_MASK;
1059 else if (len >= 6 && is_shft (accelerator))
1063 mods |= GDK_SHIFT_MASK;
1065 else if (len >= 6 && is_ctrl (accelerator))
1069 mods |= GDK_CONTROL_MASK;
1071 else if (len >= 6 && is_modx (accelerator))
1073 static const guint mod_vals[] = {
1074 GDK_MOD1_MASK, GDK_MOD2_MASK, GDK_MOD3_MASK,
1075 GDK_MOD4_MASK, GDK_MOD5_MASK
1080 mods |= mod_vals[*accelerator - '1'];
1083 else if (len >= 5 && is_ctl (accelerator))
1087 mods |= GDK_CONTROL_MASK;
1089 else if (len >= 5 && is_alt (accelerator))
1093 mods |= GDK_MOD1_MASK;
1095 else if (len >= 6 && is_meta (accelerator))
1099 mods |= GDK_META_MASK;
1101 else if (len >= 7 && is_hyper (accelerator))
1105 mods |= GDK_HYPER_MASK;
1107 else if (len >= 7 && is_super (accelerator))
1111 mods |= GDK_SUPER_MASK;
1117 last_ch = *accelerator;
1118 while (last_ch && last_ch != '>')
1120 last_ch = *accelerator;
1128 keyval = gdk_keyval_from_name (accelerator);
1134 if (accelerator_key)
1135 *accelerator_key = gdk_keyval_to_lower (keyval);
1136 if (accelerator_mods)
1137 *accelerator_mods = mods;
1141 * gtk_accelerator_name:
1142 * @accelerator_key: accelerator keyval
1143 * @accelerator_mods: accelerator modifier mask
1145 * Converts an accelerator keyval and modifier mask
1146 * into a string parseable by gtk_accelerator_parse().
1147 * For example, if you pass in #GDK_q and #GDK_CONTROL_MASK,
1148 * this function returns "<Control>q".
1150 * If you need to display accelerators in the user interface,
1151 * see gtk_accelerator_get_label().
1153 * Returns: a newly-allocated accelerator name
1156 gtk_accelerator_name (guint accelerator_key,
1157 GdkModifierType accelerator_mods)
1159 static const gchar text_release[] = "<Release>";
1160 static const gchar text_shift[] = "<Shift>";
1161 static const gchar text_control[] = "<Control>";
1162 static const gchar text_mod1[] = "<Alt>";
1163 static const gchar text_mod2[] = "<Mod2>";
1164 static const gchar text_mod3[] = "<Mod3>";
1165 static const gchar text_mod4[] = "<Mod4>";
1166 static const gchar text_mod5[] = "<Mod5>";
1167 static const gchar text_meta[] = "<Meta>";
1168 static const gchar text_super[] = "<Super>";
1169 static const gchar text_hyper[] = "<Hyper>";
1174 accelerator_mods &= GDK_MODIFIER_MASK;
1176 keyval_name = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key));
1181 if (accelerator_mods & GDK_RELEASE_MASK)
1182 l += sizeof (text_release) - 1;
1183 if (accelerator_mods & GDK_SHIFT_MASK)
1184 l += sizeof (text_shift) - 1;
1185 if (accelerator_mods & GDK_CONTROL_MASK)
1186 l += sizeof (text_control) - 1;
1187 if (accelerator_mods & GDK_MOD1_MASK)
1188 l += sizeof (text_mod1) - 1;
1189 if (accelerator_mods & GDK_MOD2_MASK)
1190 l += sizeof (text_mod2) - 1;
1191 if (accelerator_mods & GDK_MOD3_MASK)
1192 l += sizeof (text_mod3) - 1;
1193 if (accelerator_mods & GDK_MOD4_MASK)
1194 l += sizeof (text_mod4) - 1;
1195 if (accelerator_mods & GDK_MOD5_MASK)
1196 l += sizeof (text_mod5) - 1;
1197 l += strlen (keyval_name);
1198 if (accelerator_mods & GDK_META_MASK)
1199 l += sizeof (text_meta) - 1;
1200 if (accelerator_mods & GDK_HYPER_MASK)
1201 l += sizeof (text_hyper) - 1;
1202 if (accelerator_mods & GDK_SUPER_MASK)
1203 l += sizeof (text_super) - 1;
1205 accelerator = g_new (gchar, l + 1);
1209 if (accelerator_mods & GDK_RELEASE_MASK)
1211 strcpy (accelerator + l, text_release);
1212 l += sizeof (text_release) - 1;
1214 if (accelerator_mods & GDK_SHIFT_MASK)
1216 strcpy (accelerator + l, text_shift);
1217 l += sizeof (text_shift) - 1;
1219 if (accelerator_mods & GDK_CONTROL_MASK)
1221 strcpy (accelerator + l, text_control);
1222 l += sizeof (text_control) - 1;
1224 if (accelerator_mods & GDK_MOD1_MASK)
1226 strcpy (accelerator + l, text_mod1);
1227 l += sizeof (text_mod1) - 1;
1229 if (accelerator_mods & GDK_MOD2_MASK)
1231 strcpy (accelerator + l, text_mod2);
1232 l += sizeof (text_mod2) - 1;
1234 if (accelerator_mods & GDK_MOD3_MASK)
1236 strcpy (accelerator + l, text_mod3);
1237 l += sizeof (text_mod3) - 1;
1239 if (accelerator_mods & GDK_MOD4_MASK)
1241 strcpy (accelerator + l, text_mod4);
1242 l += sizeof (text_mod4) - 1;
1244 if (accelerator_mods & GDK_MOD5_MASK)
1246 strcpy (accelerator + l, text_mod5);
1247 l += sizeof (text_mod5) - 1;
1249 if (accelerator_mods & GDK_META_MASK)
1251 strcpy (accelerator + l, text_meta);
1252 l += sizeof (text_meta) - 1;
1254 if (accelerator_mods & GDK_HYPER_MASK)
1256 strcpy (accelerator + l, text_hyper);
1257 l += sizeof (text_hyper) - 1;
1259 if (accelerator_mods & GDK_SUPER_MASK)
1261 strcpy (accelerator + l, text_super);
1262 l += sizeof (text_super) - 1;
1264 strcpy (accelerator + l, keyval_name);
1270 * gtk_accelerator_get_label:
1271 * @accelerator_key: accelerator keyval
1272 * @accelerator_mods: accelerator modifier mask
1274 * Converts an accelerator keyval and modifier mask into a string
1275 * which can be used to represent the accelerator to the user.
1277 * Returns: a newly-allocated string representing the accelerator.
1282 gtk_accelerator_get_label (guint accelerator_key,
1283 GdkModifierType accelerator_mods)
1285 GtkAccelLabelClass *klass;
1288 klass = g_type_class_ref (GTK_TYPE_ACCEL_LABEL);
1289 label = _gtk_accel_label_class_get_accelerator_label (klass,
1292 g_type_class_unref (klass); /* klass is kept alive since gtk uses static types */
1298 * gtk_accelerator_set_default_mod_mask:
1299 * @default_mod_mask: accelerator modifier mask
1301 * Sets the modifiers that will be considered significant for keyboard
1302 * accelerators. The default mod mask is #GDK_CONTROL_MASK |
1303 * #GDK_SHIFT_MASK | #GDK_MOD1_MASK | #GDK_SUPER_MASK |
1304 * #GDK_HYPER_MASK | #GDK_META_MASK, that is, Control, Shift, Alt,
1305 * Super, Hyper and Meta. Other modifiers will by default be ignored
1306 * by #GtkAccelGroup.
1307 * You must include at least the three modifiers Control, Shift
1308 * and Alt in any value you pass to this function.
1310 * The default mod mask should be changed on application startup,
1311 * before using any accelerator groups.
1314 gtk_accelerator_set_default_mod_mask (GdkModifierType default_mod_mask)
1316 default_accel_mod_mask = (default_mod_mask & GDK_MODIFIER_MASK) |
1317 (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK);
1321 * gtk_accelerator_get_default_mod_mask:
1322 * @returns: the default accelerator modifier mask
1324 * Gets the value set by gtk_accelerator_set_default_mod_mask().
1327 gtk_accelerator_get_default_mod_mask (void)
1329 return default_accel_mod_mask;
1332 #define __GTK_ACCEL_GROUP_C__
1333 #include "gtkaliasdef.c"