summaryrefslogtreecommitdiff
path: root/gtk/gtkaccelgroup.c
diff options
context:
space:
mode:
Diffstat (limited to 'gtk/gtkaccelgroup.c')
-rw-r--r--gtk/gtkaccelgroup.c1257
1 files changed, 925 insertions, 332 deletions
diff --git a/gtk/gtkaccelgroup.c b/gtk/gtkaccelgroup.c
index 553ec84ce3..dbf4e261af 100644
--- a/gtk/gtkaccelgroup.c
+++ b/gtk/gtkaccelgroup.c
@@ -29,6 +29,7 @@
#include "gtkaccelgroup.h"
#include "gtkaccelgroupprivate.h"
#include "gtkaccellabelprivate.h"
+#include "gtkaccelmapprivate.h"
#include "gtkintl.h"
#include "gtkmarshalers.h"
#include "gtkprivate.h"
@@ -55,11 +56,893 @@
* and mnemonics, of course.
*/
+/* --- prototypes --- */
+static void gtk_accel_group_finalize (GObject *object);
+static void gtk_accel_group_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void accel_closure_invalidate (gpointer data,
+ GClosure *closure);
+
+
/* --- variables --- */
+static guint signal_accel_activate = 0;
+static guint signal_accel_changed = 0;
+static guint quark_acceleratable_groups = 0;
static guint default_accel_mod_mask = 0;
+enum {
+ PROP_0,
+ PROP_IS_LOCKED,
+ PROP_MODIFIER_MASK,
+ N_PROPERTIES
+};
+
+static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
+
+G_DEFINE_TYPE_WITH_PRIVATE (GtkAccelGroup, gtk_accel_group, G_TYPE_OBJECT)
+
/* --- functions --- */
+static void
+gtk_accel_group_class_init (GtkAccelGroupClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ quark_acceleratable_groups = g_quark_from_static_string ("gtk-acceleratable-accel-groups");
+
+ object_class->finalize = gtk_accel_group_finalize;
+ object_class->get_property = gtk_accel_group_get_property;
+
+ class->accel_changed = NULL;
+
+ obj_properties [PROP_IS_LOCKED] =
+ g_param_spec_boolean ("is-locked",
+ "Is locked",
+ "Is the accel group locked",
+ FALSE,
+ G_PARAM_READABLE);
+
+ obj_properties [PROP_MODIFIER_MASK] =
+ g_param_spec_flags ("modifier-mask",
+ "Modifier Mask",
+ "Modifier Mask",
+ GDK_TYPE_MODIFIER_TYPE,
+ default_accel_mod_mask,
+ G_PARAM_READABLE);
+
+ g_object_class_install_properties (object_class,
+ N_PROPERTIES,
+ obj_properties);
+
+ /**
+ * GtkAccelGroup::accel-activate:
+ * @accel_group: the #GtkAccelGroup which received the signal
+ * @acceleratable: the object on which the accelerator was activated
+ * @keyval: the accelerator keyval
+ * @modifier: the modifier combination of the accelerator
+ *
+ * The accel-activate signal is an implementation detail of
+ * #GtkAccelGroup and not meant to be used by applications.
+ *
+ * Returns: %TRUE if the accelerator was activated
+ */
+ signal_accel_activate =
+ g_signal_new (I_("accel-activate"),
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_DETAILED,
+ 0,
+ _gtk_boolean_handled_accumulator, NULL,
+ _gtk_marshal_BOOLEAN__OBJECT_UINT_FLAGS,
+ G_TYPE_BOOLEAN, 3,
+ G_TYPE_OBJECT,
+ G_TYPE_UINT,
+ GDK_TYPE_MODIFIER_TYPE);
+ /**
+ * GtkAccelGroup::accel-changed:
+ * @accel_group: the #GtkAccelGroup which received the signal
+ * @keyval: the accelerator keyval
+ * @modifier: the modifier combination of the accelerator
+ * @accel_closure: the #GClosure of the accelerator
+ *
+ * The accel-changed signal is emitted when an entry
+ * is added to or removed from the accel group.
+ *
+ * Widgets like #GtkAccelLabel which display an associated
+ * accelerator should connect to this signal, and rebuild
+ * their visual representation if the @accel_closure is theirs.
+ */
+ signal_accel_changed =
+ g_signal_new (I_("accel-changed"),
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
+ G_STRUCT_OFFSET (GtkAccelGroupClass, accel_changed),
+ NULL, NULL,
+ _gtk_marshal_VOID__UINT_FLAGS_BOXED,
+ G_TYPE_NONE, 3,
+ G_TYPE_UINT,
+ GDK_TYPE_MODIFIER_TYPE,
+ G_TYPE_CLOSURE);
+}
+
+static void
+gtk_accel_group_finalize (GObject *object)
+{
+ GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (object);
+ guint i;
+
+ for (i = 0; i < accel_group->priv->n_accels; i++)
+ {
+ GtkAccelGroupEntry *entry = &accel_group->priv->priv_accels[i];
+
+ if (entry->accel_path_quark)
+ {
+ const gchar *accel_path = g_quark_to_string (entry->accel_path_quark);
+
+ _gtk_accel_map_remove_group (accel_path, accel_group);
+ }
+ g_closure_remove_invalidate_notifier (entry->closure, accel_group, accel_closure_invalidate);
+
+ /* remove quick_accel_add() refcount */
+ g_closure_unref (entry->closure);
+ }
+
+ g_free (accel_group->priv->priv_accels);
+
+ G_OBJECT_CLASS (gtk_accel_group_parent_class)->finalize (object);
+}
+
+static void
+gtk_accel_group_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (object);
+
+ switch (param_id)
+ {
+ case PROP_IS_LOCKED:
+ g_value_set_boolean (value, accel_group->priv->lock_count > 0);
+ break;
+ case PROP_MODIFIER_MASK:
+ g_value_set_flags (value, accel_group->priv->modifier_mask);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_accel_group_init (GtkAccelGroup *accel_group)
+{
+ GtkAccelGroupPrivate *priv;
+
+ accel_group->priv = gtk_accel_group_get_instance_private (accel_group);
+ priv = accel_group->priv;
+
+ priv->lock_count = 0;
+ priv->modifier_mask = gtk_accelerator_get_default_mod_mask ();
+ priv->acceleratables = NULL;
+ priv->n_accels = 0;
+ priv->priv_accels = NULL;
+}
+
+/**
+ * gtk_accel_group_new:
+ *
+ * Creates a new #GtkAccelGroup.
+ *
+ * Returns: a new #GtkAccelGroup object
+ */
+GtkAccelGroup*
+gtk_accel_group_new (void)
+{
+ return g_object_new (GTK_TYPE_ACCEL_GROUP, NULL);
+}
+
+/**
+ * gtk_accel_group_get_is_locked:
+ * @accel_group: a #GtkAccelGroup
+ *
+ * Locks are added and removed using gtk_accel_group_lock() and
+ * gtk_accel_group_unlock().
+ *
+ * Returns: %TRUE if there are 1 or more locks on the @accel_group,
+ * %FALSE otherwise.
+ */
+gboolean
+gtk_accel_group_get_is_locked (GtkAccelGroup *accel_group)
+{
+ g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
+
+ return accel_group->priv->lock_count > 0;
+}
+
+/**
+ * gtk_accel_group_get_modifier_mask:
+ * @accel_group: a #GtkAccelGroup
+ *
+ * Gets a #GdkModifierType representing the mask for this
+ * @accel_group. For example, #GDK_CONTROL_MASK, #GDK_SHIFT_MASK, etc.
+ *
+ * Returns: the modifier mask for this accel group.
+ */
+GdkModifierType
+gtk_accel_group_get_modifier_mask (GtkAccelGroup *accel_group)
+{
+ g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), 0);
+
+ return accel_group->priv->modifier_mask;
+}
+
+static void
+accel_group_weak_ref_detach (GSList *free_list,
+ GObject *stale_object)
+{
+ GSList *slist;
+
+ for (slist = free_list; slist; slist = slist->next)
+ {
+ GtkAccelGroup *accel_group;
+
+ accel_group = slist->data;
+ accel_group->priv->acceleratables = g_slist_remove (accel_group->priv->acceleratables, stale_object);
+ g_object_unref (accel_group);
+ }
+ g_slist_free (free_list);
+ g_object_set_qdata (stale_object, quark_acceleratable_groups, NULL);
+}
+
+void
+_gtk_accel_group_attach (GtkAccelGroup *accel_group,
+ GObject *object)
+{
+ GSList *slist;
+
+ g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
+ g_return_if_fail (G_IS_OBJECT (object));
+ g_return_if_fail (g_slist_find (accel_group->priv->acceleratables, object) == NULL);
+
+ g_object_ref (accel_group);
+ accel_group->priv->acceleratables = g_slist_prepend (accel_group->priv->acceleratables, object);
+ slist = g_object_get_qdata (object, quark_acceleratable_groups);
+ if (slist)
+ g_object_weak_unref (object,
+ (GWeakNotify) accel_group_weak_ref_detach,
+ slist);
+ slist = g_slist_prepend (slist, accel_group);
+ g_object_set_qdata (object, quark_acceleratable_groups, slist);
+ g_object_weak_ref (object,
+ (GWeakNotify) accel_group_weak_ref_detach,
+ slist);
+}
+
+void
+_gtk_accel_group_detach (GtkAccelGroup *accel_group,
+ GObject *object)
+{
+ GSList *slist;
+
+ g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
+ g_return_if_fail (G_IS_OBJECT (object));
+ g_return_if_fail (g_slist_find (accel_group->priv->acceleratables, object) != NULL);
+
+ accel_group->priv->acceleratables = g_slist_remove (accel_group->priv->acceleratables, object);
+ slist = g_object_get_qdata (object, quark_acceleratable_groups);
+ g_object_weak_unref (object,
+ (GWeakNotify) accel_group_weak_ref_detach,
+ slist);
+ slist = g_slist_remove (slist, accel_group);
+ g_object_set_qdata (object, quark_acceleratable_groups, slist);
+ if (slist)
+ g_object_weak_ref (object,
+ (GWeakNotify) accel_group_weak_ref_detach,
+ slist);
+ g_object_unref (accel_group);
+}
+
+/**
+ * gtk_accel_groups_from_object:
+ * @object: a #GObject, usually a #GtkWindow
+ *
+ * Gets a list of all accel groups which are attached to @object.
+ *
+ * Returns: (element-type GtkAccelGroup) (transfer none): a list of
+ * all accel groups which are attached to @object
+ */
+GSList*
+gtk_accel_groups_from_object (GObject *object)
+{
+ g_return_val_if_fail (G_IS_OBJECT (object), NULL);
+
+ return g_object_get_qdata (object, quark_acceleratable_groups);
+}
+
+/**
+ * gtk_accel_group_find:
+ * @accel_group: a #GtkAccelGroup
+ * @find_func: (scope call): a function to filter the entries
+ * of @accel_group with
+ * @data: data to pass to @find_func
+ *
+ * Finds the first entry in an accelerator group for which
+ * @find_func returns %TRUE and returns its #GtkAccelKey.
+ *
+ * Returns: (transfer none): the key of the first entry passing
+ * @find_func. The key is owned by GTK+ and must not be freed.
+ */
+GtkAccelKey*
+gtk_accel_group_find (GtkAccelGroup *accel_group,
+ GtkAccelGroupFindFunc find_func,
+ gpointer data)
+{
+ GtkAccelKey *key = NULL;
+ guint i;
+
+ g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL);
+ g_return_val_if_fail (find_func != NULL, NULL);
+
+ g_object_ref (accel_group);
+ for (i = 0; i < accel_group->priv->n_accels; i++)
+ if (find_func (&accel_group->priv->priv_accels[i].key,
+ accel_group->priv->priv_accels[i].closure,
+ data))
+ {
+ key = &accel_group->priv->priv_accels[i].key;
+ break;
+ }
+ g_object_unref (accel_group);
+
+ return key;
+}
+
+/**
+ * gtk_accel_group_lock:
+ * @accel_group: a #GtkAccelGroup
+ *
+ * Locks the given accelerator group.
+ *
+ * Locking an acelerator group prevents the accelerators contained
+ * within it to be changed during runtime. Refer to
+ * gtk_accel_map_change_entry() about runtime accelerator changes.
+ *
+ * If called more than once, @accel_group remains locked until
+ * gtk_accel_group_unlock() has been called an equivalent number
+ * of times.
+ */
+void
+gtk_accel_group_lock (GtkAccelGroup *accel_group)
+{
+ g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
+
+ accel_group->priv->lock_count += 1;
+
+ if (accel_group->priv->lock_count == 1) {
+ /* State change from unlocked to locked */
+ g_object_notify_by_pspec (G_OBJECT (accel_group), obj_properties[PROP_IS_LOCKED]);
+ }
+}
+
+/**
+ * gtk_accel_group_unlock:
+ * @accel_group: a #GtkAccelGroup
+ *
+ * Undoes the last call to gtk_accel_group_lock() on this @accel_group.
+ */
+void
+gtk_accel_group_unlock (GtkAccelGroup *accel_group)
+{
+ g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
+ g_return_if_fail (accel_group->priv->lock_count > 0);
+
+ accel_group->priv->lock_count -= 1;
+
+ if (accel_group->priv->lock_count < 1) {
+ /* State change from locked to unlocked */
+ g_object_notify_by_pspec (G_OBJECT (accel_group), obj_properties[PROP_IS_LOCKED]);
+ }
+}
+
+static void
+accel_closure_invalidate (gpointer data,
+ GClosure *closure)
+{
+ GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (data);
+
+ gtk_accel_group_disconnect (accel_group, closure);
+}
+
+static int
+bsearch_compare_accels (const void *d1,
+ const void *d2)
+{
+ const GtkAccelGroupEntry *entry1 = d1;
+ const GtkAccelGroupEntry *entry2 = d2;
+
+ if (entry1->key.accel_key == entry2->key.accel_key)
+ return entry1->key.accel_mods < entry2->key.accel_mods ? -1 : entry1->key.accel_mods > entry2->key.accel_mods;
+ else
+ return entry1->key.accel_key < entry2->key.accel_key ? -1 : 1;
+}
+
+static void
+quick_accel_add (GtkAccelGroup *accel_group,
+ guint accel_key,
+ GdkModifierType accel_mods,
+ GtkAccelFlags accel_flags,
+ GClosure *closure,
+ GQuark path_quark)
+{
+ guint pos, i = accel_group->priv->n_accels++;
+ GtkAccelGroupEntry key;
+
+ /* find position */
+ key.key.accel_key = accel_key;
+ key.key.accel_mods = accel_mods;
+ for (pos = 0; pos < i; pos++)
+ if (bsearch_compare_accels (&key, accel_group->priv->priv_accels + pos) < 0)
+ break;
+
+ /* insert at position, ref closure */
+ accel_group->priv->priv_accels = g_renew (GtkAccelGroupEntry, accel_group->priv->priv_accels, accel_group->priv->n_accels);
+ memmove (accel_group->priv->priv_accels + pos + 1, accel_group->priv->priv_accels + pos,
+ (i - pos) * sizeof (accel_group->priv->priv_accels[0]));
+ accel_group->priv->priv_accels[pos].key.accel_key = accel_key;
+ accel_group->priv->priv_accels[pos].key.accel_mods = accel_mods;
+ accel_group->priv->priv_accels[pos].key.accel_flags = accel_flags;
+ accel_group->priv->priv_accels[pos].closure = g_closure_ref (closure);
+ accel_group->priv->priv_accels[pos].accel_path_quark = path_quark;
+ g_closure_sink (closure);
+
+ /* handle closure invalidation and reverse lookups */
+ g_closure_add_invalidate_notifier (closure, accel_group, accel_closure_invalidate);
+
+ /* get accel path notification */
+ if (path_quark)
+ _gtk_accel_map_add_group (g_quark_to_string (path_quark), accel_group);
+
+ /* connect and notify changed */
+ if (accel_key)
+ {
+ gchar *accel_name = gtk_accelerator_name (accel_key, accel_mods);
+ GQuark accel_quark = g_quark_from_string (accel_name);
+
+ g_free (accel_name);
+
+ /* setup handler */
+ g_signal_connect_closure_by_id (accel_group, signal_accel_activate, accel_quark, closure, FALSE);
+
+ /* and notify */
+ g_signal_emit (accel_group, signal_accel_changed, accel_quark, accel_key, accel_mods, closure);
+ }
+}
+
+static void
+quick_accel_remove (GtkAccelGroup *accel_group,
+ guint pos)
+{
+ GQuark accel_quark = 0;
+ GtkAccelGroupEntry *entry = accel_group->priv->priv_accels + pos;
+ guint accel_key = entry->key.accel_key;
+ GdkModifierType accel_mods = entry->key.accel_mods;
+ GClosure *closure = entry->closure;
+
+ /* quark for notification */
+ if (accel_key)
+ {
+ gchar *accel_name = gtk_accelerator_name (accel_key, accel_mods);
+
+ accel_quark = g_quark_from_string (accel_name);
+ g_free (accel_name);
+ }
+
+ /* clean up closure invalidate notification and disconnect */
+ g_closure_remove_invalidate_notifier (entry->closure, accel_group, accel_closure_invalidate);
+ if (accel_quark)
+ g_signal_handlers_disconnect_matched (accel_group,
+ G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_CLOSURE,
+ signal_accel_activate, accel_quark,
+ closure, NULL, NULL);
+ /* clean up accel path notification */
+ if (entry->accel_path_quark)
+ _gtk_accel_map_remove_group (g_quark_to_string (entry->accel_path_quark), accel_group);
+
+ /* physically remove */
+ accel_group->priv->n_accels -= 1;
+ memmove (entry, entry + 1,
+ (accel_group->priv->n_accels - pos) * sizeof (accel_group->priv->priv_accels[0]));
+
+ /* and notify */
+ if (accel_quark)
+ g_signal_emit (accel_group, signal_accel_changed, accel_quark, accel_key, accel_mods, closure);
+
+ /* remove quick_accel_add() refcount */
+ g_closure_unref (closure);
+}
+
+static GtkAccelGroupEntry*
+quick_accel_find (GtkAccelGroup *accel_group,
+ guint accel_key,
+ GdkModifierType accel_mods,
+ guint *count_p)
+{
+ GtkAccelGroupEntry *entry;
+ GtkAccelGroupEntry key;
+
+ *count_p = 0;
+
+ if (!accel_group->priv->n_accels)
+ return NULL;
+
+ key.key.accel_key = accel_key;
+ key.key.accel_mods = accel_mods;
+ entry = bsearch (&key, accel_group->priv->priv_accels, accel_group->priv->n_accels,
+ sizeof (accel_group->priv->priv_accels[0]), bsearch_compare_accels);
+
+ if (!entry)
+ return NULL;
+
+ /* step back to the first member */
+ for (; entry > accel_group->priv->priv_accels; entry--)
+ if (entry[-1].key.accel_key != accel_key ||
+ entry[-1].key.accel_mods != accel_mods)
+ break;
+ /* count equal members */
+ for (; entry + *count_p < accel_group->priv->priv_accels + accel_group->priv->n_accels; (*count_p)++)
+ if (entry[*count_p].key.accel_key != accel_key ||
+ entry[*count_p].key.accel_mods != accel_mods)
+ break;
+ return entry;
+}
+
+/**
+ * gtk_accel_group_connect:
+ * @accel_group: the accelerator group to install an accelerator in
+ * @accel_key: key value of the accelerator
+ * @accel_mods: modifier combination of the accelerator
+ * @accel_flags: a flag mask to configure this accelerator
+ * @closure: closure to be executed upon accelerator activation
+ *
+ * Installs an accelerator in this group. When @accel_group is being
+ * activated in response to a call to gtk_accel_groups_activate(),
+ * @closure will be invoked if the @accel_key and @accel_mods from
+ * gtk_accel_groups_activate() match those of this connection.
+ *
+ * The signature used for the @closure is that of #GtkAccelGroupActivate.
+ *
+ * Note that, due to implementation details, a single closure can
+ * only be connected to one accelerator group.
+ */
+void
+gtk_accel_group_connect (GtkAccelGroup *accel_group,
+ guint accel_key,
+ GdkModifierType accel_mods,
+ GtkAccelFlags accel_flags,
+ GClosure *closure)
+{
+ g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
+ g_return_if_fail (closure != NULL);
+ g_return_if_fail (accel_key > 0);
+ g_return_if_fail (gtk_accel_group_from_accel_closure (closure) == NULL);
+
+ g_object_ref (accel_group);
+ if (!closure->is_invalid)
+ quick_accel_add (accel_group,
+ gdk_keyval_to_lower (accel_key),
+ accel_mods, accel_flags, closure, 0);
+ g_object_unref (accel_group);
+}
+
+/**
+ * gtk_accel_group_connect_by_path:
+ * @accel_group: the accelerator group to install an accelerator in
+ * @accel_path: path used for determining key and modifiers
+ * @closure: closure to be executed upon accelerator activation
+ *
+ * Installs an accelerator in this group, using an accelerator path
+ * to look up the appropriate key and modifiers (see
+ * gtk_accel_map_add_entry()). When @accel_group is being activated
+ * in response to a call to gtk_accel_groups_activate(), @closure will
+ * be invoked if the @accel_key and @accel_mods from
+ * gtk_accel_groups_activate() match the key and modifiers for the path.
+ *
+ * The signature used for the @closure is that of #GtkAccelGroupActivate.
+ *
+ * Note that @accel_path string will be stored in a #GQuark. Therefore,
+ * if you pass a static string, you can save some memory by interning it
+ * first with g_intern_static_string().
+ */
+void
+gtk_accel_group_connect_by_path (GtkAccelGroup *accel_group,
+ const gchar *accel_path,
+ GClosure *closure)
+{
+ guint accel_key = 0;
+ GdkModifierType accel_mods = 0;
+ GtkAccelKey key;
+
+ g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
+ g_return_if_fail (closure != NULL);
+ g_return_if_fail (_gtk_accel_path_is_valid (accel_path));
+
+ if (closure->is_invalid)
+ return;
+
+ g_object_ref (accel_group);
+
+ if (gtk_accel_map_lookup_entry (accel_path, &key))
+ {
+ accel_key = gdk_keyval_to_lower (key.accel_key);
+ accel_mods = key.accel_mods;
+ }
+
+ quick_accel_add (accel_group, accel_key, accel_mods, GTK_ACCEL_VISIBLE, closure,
+ g_quark_from_string (accel_path));
+
+ g_object_unref (accel_group);
+}
+
+/**
+ * gtk_accel_group_disconnect:
+ * @accel_group: the accelerator group to remove an accelerator from
+ * @closure: (allow-none): the closure to remove from this accelerator
+ * group, or %NULL to remove all closures
+ *
+ * Removes an accelerator previously installed through
+ * gtk_accel_group_connect().
+ *
+ * Returns: %TRUE if the closure was found and got disconnected
+ */
+gboolean
+gtk_accel_group_disconnect (GtkAccelGroup *accel_group,
+ GClosure *closure)
+{
+ guint i;
+
+ g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
+
+ for (i = 0; i < accel_group->priv->n_accels; i++)
+ if (accel_group->priv->priv_accels[i].closure == closure)
+ {
+ g_object_ref (accel_group);
+ quick_accel_remove (accel_group, i);
+ g_object_unref (accel_group);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * gtk_accel_group_disconnect_key:
+ * @accel_group: the accelerator group to install an accelerator in
+ * @accel_key: key value of the accelerator
+ * @accel_mods: modifier combination of the accelerator
+ *
+ * Removes an accelerator previously installed through
+ * gtk_accel_group_connect().
+ *
+ * Returns: %TRUE if there was an accelerator which could be
+ * removed, %FALSE otherwise
+ */
+gboolean
+gtk_accel_group_disconnect_key (GtkAccelGroup *accel_group,
+ guint accel_key,
+ GdkModifierType accel_mods)
+{
+ GtkAccelGroupEntry *entries;
+ GSList *slist, *clist = NULL;
+ gboolean removed_one = FALSE;
+ guint n;
+
+ g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
+
+ g_object_ref (accel_group);
+
+ accel_key = gdk_keyval_to_lower (accel_key);
+ entries = quick_accel_find (accel_group, accel_key, accel_mods, &n);
+ while (n--)
+ {
+ GClosure *closure = g_closure_ref (entries[n].closure);
+
+ clist = g_slist_prepend (clist, closure);
+ }
+
+ for (slist = clist; slist; slist = slist->next)
+ {
+ GClosure *closure = slist->data;
+
+ removed_one |= gtk_accel_group_disconnect (accel_group, closure);
+ g_closure_unref (closure);
+ }
+ g_slist_free (clist);
+
+ g_object_unref (accel_group);
+
+ return removed_one;
+}
+
+void
+_gtk_accel_group_reconnect (GtkAccelGroup *accel_group,
+ GQuark accel_path_quark)
+{
+ GSList *slist, *clist = NULL;
+ guint i;
+
+ g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
+
+ g_object_ref (accel_group);
+
+ for (i = 0; i < accel_group->priv->n_accels; i++)
+ if (accel_group->priv->priv_accels[i].accel_path_quark == accel_path_quark)
+ {
+ GClosure *closure = g_closure_ref (accel_group->priv->priv_accels[i].closure);
+
+ clist = g_slist_prepend (clist, closure);
+ }
+
+ for (slist = clist; slist; slist = slist->next)
+ {
+ GClosure *closure = slist->data;
+
+ gtk_accel_group_disconnect (accel_group, closure);
+ gtk_accel_group_connect_by_path (accel_group, g_quark_to_string (accel_path_quark), closure);
+ g_closure_unref (closure);
+ }
+ g_slist_free (clist);
+
+ g_object_unref (accel_group);
+}
+
+GSList*
+_gtk_accel_group_get_accelerables (GtkAccelGroup *accel_group)
+{
+ g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL);
+
+ return accel_group->priv->acceleratables;
+}
+
+/**
+ * gtk_accel_group_query:
+ * @accel_group: the accelerator group to query
+ * @accel_key: key value of the accelerator
+ * @accel_mods: modifier combination of the accelerator
+ * @n_entries: (out) (optional): location to return the number
+ * of entries found, or %NULL
+ *
+ * Queries an accelerator group for all entries matching @accel_key
+ * and @accel_mods.
+ *
+ * Returns: (nullable) (transfer none) (array length=n_entries): an array of
+ * @n_entries #GtkAccelGroupEntry elements, or %NULL. The array
+ * is owned by GTK+ and must not be freed.
+ */
+GtkAccelGroupEntry*
+gtk_accel_group_query (GtkAccelGroup *accel_group,
+ guint accel_key,
+ GdkModifierType accel_mods,
+ guint *n_entries)
+{
+ GtkAccelGroupEntry *entries;
+ guint n;
+
+ g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL);
+
+ entries = quick_accel_find (accel_group, gdk_keyval_to_lower (accel_key), accel_mods, &n);
+
+ if (n_entries)
+ *n_entries = entries ? n : 0;
+
+ return entries;
+}
+
+/**
+ * gtk_accel_group_from_accel_closure:
+ * @closure: a #GClosure
+ *
+ * Finds the #GtkAccelGroup to which @closure is connected;
+ * see gtk_accel_group_connect().
+ *
+ * Returns: (nullable) (transfer none): the #GtkAccelGroup to which @closure
+ * is connected, or %NULL
+ */
+GtkAccelGroup*
+gtk_accel_group_from_accel_closure (GClosure *closure)
+{
+ guint i;
+
+ g_return_val_if_fail (closure != NULL, NULL);
+
+ /* A few remarks on what we do here. in general, we need a way to
+ * reverse lookup accel_groups from closures that are being used in
+ * accel groups. this could be done e.g via a hashtable. it is however
+ * cheaper (memory wise) to just use the invalidation notifier on the
+ * closure itself (which we need to install anyway), that contains the
+ * accel group as data which, besides needing to peek a bit at closure
+ * internals, works just as good.
+ */
+ for (i = 0; i < G_CLOSURE_N_NOTIFIERS (closure); i++)
+ if (closure->notifiers[i].notify == accel_closure_invalidate)
+ return closure->notifiers[i].data;
+
+ return NULL;
+}
+
+/**
+ * gtk_accel_group_activate:
+ * @accel_group: a #GtkAccelGroup
+ * @accel_quark: the quark for the accelerator name
+ * @acceleratable: the #GObject, usually a #GtkWindow, on which
+ * to activate the accelerator
+ * @accel_key: accelerator keyval from a key event
+ * @accel_mods: keyboard state mask from a key event
+ *
+ * Finds the first accelerator in @accel_group that matches
+ * @accel_key and @accel_mods, and activates it.
+ *
+ * Returns: %TRUE if an accelerator was activated and handled
+ * this keypress
+ */
+gboolean
+gtk_accel_group_activate (GtkAccelGroup *accel_group,
+ GQuark accel_quark,
+ GObject *acceleratable,
+ guint accel_key,
+ GdkModifierType accel_mods)
+{
+ gboolean was_handled;
+
+ g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
+ g_return_val_if_fail (G_IS_OBJECT (acceleratable), FALSE);
+
+ was_handled = FALSE;
+ g_signal_emit (accel_group, signal_accel_activate, accel_quark,
+ acceleratable, accel_key, accel_mods, &was_handled);
+
+ return was_handled;
+}
+
+/**
+ * gtk_accel_groups_activate:
+ * @object: the #GObject, usually a #GtkWindow, on which
+ * to activate the accelerator
+ * @accel_key: accelerator keyval from a key event
+ * @accel_mods: keyboard state mask from a key event
+ *
+ * Finds the first accelerator in any #GtkAccelGroup attached
+ * to @object that matches @accel_key and @accel_mods, and
+ * activates that accelerator.
+ *
+ * Returns: %TRUE if an accelerator was activated and handled
+ * this keypress
+ */
+gboolean
+gtk_accel_groups_activate (GObject *object,
+ guint accel_key,
+ GdkModifierType accel_mods)
+{
+ g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
+
+ if (gtk_accelerator_valid (accel_key, accel_mods))
+ {
+ gchar *accel_name;
+ GQuark accel_quark;
+ GSList *slist;
+
+ accel_name = gtk_accelerator_name (accel_key, (accel_mods & gtk_accelerator_get_default_mod_mask ()));
+ accel_quark = g_quark_from_string (accel_name);
+ g_free (accel_name);
+
+ for (slist = gtk_accel_groups_from_object (object); slist; slist = slist->next)
+ if (gtk_accel_group_activate (slist->data, accel_quark, object, accel_key, accel_mods))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
/**
* gtk_accelerator_valid:
* @keyval: a GDK keyval
@@ -205,6 +1088,20 @@ is_control (const gchar *string)
}
static inline gboolean
+is_release (const gchar *string)
+{
+ return ((string[0] == '<') &&
+ (string[1] == 'r' || string[1] == 'R') &&
+ (string[2] == 'e' || string[2] == 'E') &&
+ (string[3] == 'l' || string[3] == 'L') &&
+ (string[4] == 'e' || string[4] == 'E') &&
+ (string[5] == 'a' || string[5] == 'A') &&
+ (string[6] == 's' || string[6] == 'S') &&
+ (string[7] == 'e' || string[7] == 'E') &&
+ (string[8] == '>'));
+}
+
+static inline gboolean
is_meta (const gchar *string)
{
return ((string[0] == '<') &&
@@ -265,7 +1162,6 @@ is_keycode (const gchar *string)
/**
* gtk_accelerator_parse_with_keycode:
* @accelerator: string representing an accelerator
- * @display: (allow-none): the #GdkDisplay to look up @accelerator_codes in
* @accelerator_key: (out) (allow-none): return location for accelerator
* keyval, or %NULL
* @accelerator_codes: (out) (array zero-terminated=1) (transfer full) (allow-none):
@@ -286,12 +1182,9 @@ is_keycode (const gchar *string)
*
* If the parse fails, @accelerator_key, @accelerator_mods and
* @accelerator_codes will be set to 0 (zero).
- *
- * Returns: %TRUE if parsing succeeded
*/
-gboolean
+void
gtk_accelerator_parse_with_keycode (const gchar *accelerator,
- GdkDisplay *display,
guint *accelerator_key,
guint **accelerator_codes,
GdkModifierType *accelerator_mods)
@@ -307,8 +1200,7 @@ gtk_accelerator_parse_with_keycode (const gchar *accelerator,
*accelerator_mods = 0;
if (accelerator_codes)
*accelerator_codes = NULL;
-
- g_return_val_if_fail (accelerator != NULL, FALSE);
+ g_return_if_fail (accelerator != NULL);
error = FALSE;
keyval = 0;
@@ -318,7 +1210,13 @@ gtk_accelerator_parse_with_keycode (const gchar *accelerator,
{
if (*accelerator == '<')
{
- if (len >= 9 && is_primary (accelerator))
+ if (len >= 9 && is_release (accelerator))
+ {
+ accelerator += 9;
+ len -= 9;
+ mods |= GDK_RELEASE_MASK;
+ }
+ else if (len >= 9 && is_primary (accelerator))
{
accelerator += 9;
len -= 9;
@@ -455,7 +1353,7 @@ gtk_accelerator_parse_with_keycode (const gchar *accelerator,
if (keyval && accelerator_codes != NULL)
{
- GdkKeymap *keymap = gdk_display_get_keymap (display ? display : gdk_display_get_default ());
+ GdkKeymap *keymap = gdk_display_get_keymap (gdk_display_get_default ());
GdkKeymapKey *keys;
gint n_keys, i, j;
@@ -518,8 +1416,6 @@ out:
*accelerator_key = gdk_keyval_to_lower (keyval);
if (accelerator_mods)
*accelerator_mods = mods;
-
- return !error;
}
/**
@@ -543,12 +1439,12 @@ out:
* If the parse fails, @accelerator_key and @accelerator_mods will
* be set to 0 (zero).
*/
-gboolean
+void
gtk_accelerator_parse (const gchar *accelerator,
guint *accelerator_key,
GdkModifierType *accelerator_mods)
{
- return gtk_accelerator_parse_with_keycode (accelerator, NULL, accelerator_key, NULL, accelerator_mods);
+ gtk_accelerator_parse_with_keycode (accelerator, accelerator_key, NULL, accelerator_mods);
}
/**
@@ -609,6 +1505,7 @@ gchar*
gtk_accelerator_name (guint accelerator_key,
GdkModifierType accelerator_mods)
{
+ static const gchar text_release[] = "<Release>";
static const gchar text_primary[] = "<Primary>";
static const gchar text_shift[] = "<Shift>";
static const gchar text_control[] = "<Control>";
@@ -633,6 +1530,8 @@ gtk_accelerator_name (guint accelerator_key,
saved_mods = accelerator_mods;
l = 0;
+ if (accelerator_mods & GDK_RELEASE_MASK)
+ l += sizeof (text_release) - 1;
if (accelerator_mods & _gtk_get_primary_accel_mod ())
{
l += sizeof (text_primary) - 1;
@@ -665,6 +1564,11 @@ gtk_accelerator_name (guint accelerator_key,
accelerator_mods = saved_mods;
l = 0;
accelerator[l] = 0;
+ if (accelerator_mods & GDK_RELEASE_MASK)
+ {
+ strcpy (accelerator + l, text_release);
+ l += sizeof (text_release) - 1;
+ }
if (accelerator_mods & _gtk_get_primary_accel_mod ())
{
strcpy (accelerator + l, text_primary);
@@ -768,131 +1672,6 @@ gtk_accelerator_get_label_with_keycode (GdkDisplay *display,
return gtk_label;
}
-/* Underscores in key names are better displayed as spaces
- * E.g., Page_Up should be “Page Up”.
- *
- * Some keynames also have prefixes that are not suitable
- * for display, e.g XF86AudioMute, so strip those out, too.
- *
- * This function is only called on untranslated keynames,
- * so no need to be UTF-8 safe.
- */
-static void
-append_without_underscores (GString *s,
- const char *str)
-{
- const char *p;
-
- if (g_str_has_prefix (str, "XF86"))
- p = str + 4;
- else if (g_str_has_prefix (str, "ISO_"))
- p = str + 4;
- else
- p = str;
-
- for ( ; *p; p++)
- {
- if (*p == '_')
- g_string_append_c (s, ' ');
- else
- g_string_append_c (s, *p);
- }
-}
-
-/* On Mac, if the key has symbolic representation (e.g. arrow keys),
- * append it to gstring and return TRUE; otherwise return FALSE.
- * See http://docs.info.apple.com/article.html?path=Mac/10.5/en/cdb_symbs.html
- * for the list of special keys. */
-static gboolean
-append_keyval_symbol (guint accelerator_key,
- GString *gstring)
-{
-#ifdef GDK_WINDOWING_QUARTZ
- switch (accelerator_key)
- {
- case GDK_KEY_Return:
- /* U+21A9 LEFTWARDS ARROW WITH HOOK */
- g_string_append (gstring, "\xe2\x86\xa9");
- return TRUE;
-
- case GDK_KEY_ISO_Enter:
- /* U+2324 UP ARROWHEAD BETWEEN TWO HORIZONTAL BARS */
- g_string_append (gstring, "\xe2\x8c\xa4");
- return TRUE;
-
- case GDK_KEY_Left:
- /* U+2190 LEFTWARDS ARROW */
- g_string_append (gstring, "\xe2\x86\x90");
- return TRUE;
-
- case GDK_KEY_Up:
- /* U+2191 UPWARDS ARROW */
- g_string_append (gstring, "\xe2\x86\x91");
- return TRUE;
-
- case GDK_KEY_Right:
- /* U+2192 RIGHTWARDS ARROW */
- g_string_append (gstring, "\xe2\x86\x92");
- return TRUE;
-
- case GDK_KEY_Down:
- /* U+2193 DOWNWARDS ARROW */
- g_string_append (gstring, "\xe2\x86\x93");
- return TRUE;
-
- case GDK_KEY_Page_Up:
- /* U+21DE UPWARDS ARROW WITH DOUBLE STROKE */
- g_string_append (gstring, "\xe2\x87\x9e");
- return TRUE;
-
- case GDK_KEY_Page_Down:
- /* U+21DF DOWNWARDS ARROW WITH DOUBLE STROKE */
- g_string_append (gstring, "\xe2\x87\x9f");
- return TRUE;
-
- case GDK_KEY_Home:
- /* U+2196 NORTH WEST ARROW */
- g_string_append (gstring, "\xe2\x86\x96");
- return TRUE;
-
- case GDK_KEY_End:
- /* U+2198 SOUTH EAST ARROW */
- g_string_append (gstring, "\xe2\x86\x98");
- return TRUE;
-
- case GDK_KEY_Escape:
- /* U+238B BROKEN CIRCLE WITH NORTHWEST ARROW */
- g_string_append (gstring, "\xe2\x8e\x8b");
- return TRUE;
-
- case GDK_KEY_BackSpace:
- /* U+232B ERASE TO THE LEFT */
- g_string_append (gstring, "\xe2\x8c\xab");
- return TRUE;
-
- case GDK_KEY_Delete:
- /* U+2326 ERASE TO THE RIGHT */
- g_string_append (gstring, "\xe2\x8c\xa6");
- return TRUE;
-
- default:
- return FALSE;
- }
-#else /* !GDK_WINDOWING_QUARTZ */
- return FALSE;
-#endif
-}
-
-static void
-append_separator (GString *string)
-{
-#ifndef GDK_WINDOWING_QUARTZ
- g_string_append (string, "+");
-#else
- /* no separator on quartz */
-#endif
-}
-
/**
* gtk_accelerator_get_label:
* @accelerator_key: accelerator keyval
@@ -907,202 +1686,16 @@ gchar*
gtk_accelerator_get_label (guint accelerator_key,
GdkModifierType accelerator_mods)
{
- GString *gstring;
-
- gstring = g_string_new (NULL);
-
- gtk_accelerator_print_label (gstring, accelerator_key, accelerator_mods);
-
- return g_string_free (gstring, FALSE);
-}
-
-void
-gtk_accelerator_print_label (GString *gstring,
- guint accelerator_key,
- GdkModifierType accelerator_mods)
-{
- gboolean seen_mod = FALSE;
- gunichar ch;
-
- if (accelerator_mods & GDK_SHIFT_MASK)
- {
-#ifndef GDK_WINDOWING_QUARTZ
- /* This is the text that should appear next to menu accelerators
- * that use the shift key. If the text on this key isn't typically
- * translated on keyboards used for your language, don't translate
- * this.
- */
- g_string_append (gstring, C_("keyboard label", "Shift"));
-#else
- /* U+21E7 UPWARDS WHITE ARROW */
- g_string_append (gstring, "\xe2\x87\xa7");
-#endif
- seen_mod = TRUE;
- }
-
- if (accelerator_mods & GDK_CONTROL_MASK)
- {
- if (seen_mod)
- append_separator (gstring);
-
-#ifndef GDK_WINDOWING_QUARTZ
- /* This is the text that should appear next to menu accelerators
- * that use the control key. If the text on this key isn't typically
- * translated on keyboards used for your language, don't translate
- * this.
- */
- g_string_append (gstring, C_("keyboard label", "Ctrl"));
-#else
- /* U+2303 UP ARROWHEAD */
- g_string_append (gstring, "\xe2\x8c\x83");
-#endif
- seen_mod = TRUE;
- }
-
- if (accelerator_mods & GDK_MOD1_MASK)
- {
- if (seen_mod)
- append_separator (gstring);
-
-#ifndef GDK_WINDOWING_QUARTZ
- /* This is the text that should appear next to menu accelerators
- * that use the alt key. If the text on this key isn't typically
- * translated on keyboards used for your language, don't translate
- * this.
- */
- g_string_append (gstring, C_("keyboard label", "Alt"));
-#else
- /* U+2325 OPTION KEY */
- g_string_append (gstring, "\xe2\x8c\xa5");
-#endif
- seen_mod = TRUE;
- }
-
- if (accelerator_mods & GDK_MOD2_MASK)
- {
- if (seen_mod)
- append_separator (gstring);
-
- g_string_append (gstring, "Mod2");
- seen_mod = TRUE;
- }
+ GtkAccelLabelClass *klass;
+ gchar *label;
- if (accelerator_mods & GDK_MOD3_MASK)
- {
- if (seen_mod)
- append_separator (gstring);
+ klass = g_type_class_ref (GTK_TYPE_ACCEL_LABEL);
+ label = _gtk_accel_label_class_get_accelerator_label (klass,
+ accelerator_key,
+ accelerator_mods);
+ g_type_class_unref (klass); /* klass is kept alive since gtk uses static types */
- g_string_append (gstring, "Mod3");
- seen_mod = TRUE;
- }
-
- if (accelerator_mods & GDK_MOD4_MASK)
- {
- if (seen_mod)
- append_separator (gstring);
-
- g_string_append (gstring, "Mod4");
- seen_mod = TRUE;
- }
-
- if (accelerator_mods & GDK_MOD5_MASK)
- {
- if (seen_mod)
- append_separator (gstring);
-
- g_string_append (gstring, "Mod5");
- seen_mod = TRUE;
- }
-
- if (accelerator_mods & GDK_SUPER_MASK)
- {
- if (seen_mod)
- append_separator (gstring);
-
- /* This is the text that should appear next to menu accelerators
- * that use the super key. If the text on this key isn't typically
- * translated on keyboards used for your language, don't translate
- * this.
- */
- g_string_append (gstring, C_("keyboard label", "Super"));
- seen_mod = TRUE;
- }
-
- if (accelerator_mods & GDK_HYPER_MASK)
- {
- if (seen_mod)
- append_separator (gstring);
-
- /* This is the text that should appear next to menu accelerators
- * that use the hyper key. If the text on this key isn't typically
- * translated on keyboards used for your language, don't translate
- * this.
- */
- g_string_append (gstring, C_("keyboard label", "Hyper"));
- seen_mod = TRUE;
- }
-
- if (accelerator_mods & GDK_META_MASK)
- {
- if (seen_mod)
- append_separator (gstring);
-
-#ifndef GDK_WINDOWING_QUARTZ
- /* This is the text that should appear next to menu accelerators
- * that use the meta key. If the text on this key isn't typically
- * translated on keyboards used for your language, don't translate
- * this.
- */
- g_string_append (gstring, C_("keyboard label", "Meta"));
-#else
- /* Command key symbol U+2318 PLACE OF INTEREST SIGN */
- g_string_append (gstring, "\xe2\x8c\x98");
-#endif
- seen_mod = TRUE;
- }
-
- ch = gdk_keyval_to_unicode (accelerator_key);
- if (ch && (ch == ' ' || g_unichar_isgraph (ch)))
- {
- if (seen_mod)
- append_separator (gstring);
-
- switch (ch)
- {
- case ' ':
- g_string_append (gstring, C_("keyboard label", "Space"));
- break;
- case '\\':
- g_string_append (gstring, C_("keyboard label", "Backslash"));
- break;
- default:
- g_string_append_unichar (gstring, g_unichar_toupper (ch));
- break;
- }
- }
- else if (!append_keyval_symbol (accelerator_key, gstring))
- {
- const char *tmp;
-
- tmp = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key));
- if (tmp != NULL)
- {
- if (seen_mod)
- append_separator (gstring);
-
- if (tmp[0] != 0 && tmp[1] == 0)
- g_string_append_c (gstring, g_ascii_toupper (tmp[0]));
- else
- {
- const char *str;
- str = g_dpgettext2 (GETTEXT_PACKAGE, "keyboard label", tmp);
- if (str == tmp)
- append_without_underscores (gstring, tmp);
- else
- g_string_append (gstring, str);
- }
- }
- }
+ return label;
}
/**