[gtk/gbsneto/shortcuts-rebased: 76/102] shortcut: Add GtkShortcutAction
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/gbsneto/shortcuts-rebased: 76/102] shortcut: Add GtkShortcutAction
- Date: Tue, 14 May 2019 20:21:19 +0000 (UTC)
commit 8b3c767df3c2b6f30c5a4097d4d351ccf0972f8e
Author: Benjamin Otte <otte redhat com>
Date: Sat Aug 18 07:32:11 2018 +0200
shortcut: Add GtkShortcutAction
Similar to GtkShortcutTrigger, GtkShortCutAction provides all the
different ways to activate a shortcut.
So far, these different ways are supported:
- do nothing
- Call a user-provided callback
- Call gtk_widget_activate()
- Call gtk_widget_mnemonic_activate()
- Emit an action signal
- Activate an action from the widget's action muxer
- Activate a GAction
demos/gtk-demo/shortcut_triggers.c | 2 +-
docs/reference/gtk/gtk4-sections.txt | 46 +-
gtk/gtk.h | 1 +
gtk/gtklabel.c | 2 +-
gtk/gtkshortcut.c | 518 +++----------------
gtk/gtkshortcut.h | 38 +-
gtk/gtkshortcutaction.c | 943 +++++++++++++++++++++++++++++++++++
gtk/gtkshortcutaction.h | 130 +++++
gtk/gtkshortcutcontroller.c | 5 +-
gtk/gtktypes.h | 1 +
gtk/gtkwidget.c | 4 +-
gtk/gtkwidget.h | 1 +
gtk/gtkwindow.c | 4 +-
gtk/meson.build | 2 +
testsuite/gtk/defaultvalue.c | 3 +-
15 files changed, 1199 insertions(+), 501 deletions(-)
---
diff --git a/demos/gtk-demo/shortcut_triggers.c b/demos/gtk-demo/shortcut_triggers.c
index 79c3b8bc55..3050c08d3e 100644
--- a/demos/gtk-demo/shortcut_triggers.c
+++ b/demos/gtk-demo/shortcut_triggers.c
@@ -78,7 +78,7 @@ do_shortcut_triggers (GtkWidget *do_widget)
shortcut = gtk_shortcut_new ();
gtk_shortcut_set_trigger (shortcut, shortcuts[i].create_trigger_func());
- gtk_shortcut_set_callback (shortcut, shortcut_activated, row, NULL);
+ gtk_shortcut_set_action (shortcut, gtk_callback_action_new (shortcut_activated, row, NULL));
gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut);
}
}
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt
index 62de63e304..2232d18126 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -6616,21 +6616,55 @@ gtk_mnemonic_trigger_get_keyval
gtk_shortcut_trigger_get_type
</SECTION>
+<SECTION>
+<FILE>gtkshortcutaction</FILE>
+<TITLE>GtkShortcutAction</TITLE>
+GtkShortcutAction
+gtk_shortcut_action_ref
+gtk_shortcut_action_unref
+GtkShortcutActionType
+gtk_shortcut_action_get_action_type
+gtk_shortcut_action_activate
+
+<SUBSECTION>
+gtk_nothing_action_new
+
+<SUBSECTION>
+gtk_callback_action_new
+
+<SUBSECTION>
+gtk_mnemonic_action_new
+
+<SUBSECTION>
+gtk_activate_action_new
+
+<SUBSECTION>
+gtk_signal_action_new
+gtk_signal_action_get_signal_name
+
+<SUBSECTION>
+gtk_action_action_new
+gtk_action_action_get_name
+
+<SUBSECTION>
+gtk_gaction_action_new
+gtk_gaction_action_get_gaction
+
+<SUBSECTION Private>
+gtk_shortcut_action_get_type
+</SECTION>
+
<SECTION>
<FILE>gtkshortcut</FILE>
<TITLE>GtkShortcut</TITLE>
GtkShortcut
gtk_shortcut_new
-gtk_shortcut_set_keyval
-gtk_shortcut_activate
gtk_shortcut_get_trigger
gtk_shortcut_set_trigger
+gtk_shortcut_get_action
+gtk_shortcut_set_action
gtk_shortcut_get_arguments
gtk_shortcut_set_arguments
-gtk_shortcut_get_signal
-gtk_shortcut_set_signal
-gtk_shortcut_has_callback
-gtk_shortcut_set_callback
<SUBSECTION Standard>
GTK_TYPE_SHORTCUT
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 6c7c371a58..1666f39999 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -198,6 +198,7 @@
#include <gtk/gtkseparatortoolitem.h>
#include <gtk/gtksettings.h>
#include <gtk/gtkshortcut.h>
+#include <gtk/gtkshortcutaction.h>
#include <gtk/gtkshortcutcontroller.h>
#include <gtk/gtkshortcutlabel.h>
#include <gtk/gtkshortcutmanager.h>
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index ee6ed4ac24..f4cc8cdf88 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -1744,7 +1744,7 @@ gtk_label_setup_mnemonic (GtkLabel *label)
shortcut = gtk_shortcut_new ();
gtk_shortcut_set_trigger (shortcut, gtk_mnemonic_trigger_new (priv->mnemonic_keyval));
- gtk_shortcut_set_mnemonic_activate (shortcut, TRUE);
+ gtk_shortcut_set_action (shortcut, gtk_mnemonic_action_new ());
gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (priv->mnemonic_controller), shortcut);
gtk_widget_add_controller (GTK_WIDGET (label), priv->mnemonic_controller);
g_object_unref (shortcut);
diff --git a/gtk/gtkshortcut.c b/gtk/gtkshortcut.c
index c1224fa43d..f5657fbc43 100644
--- a/gtk/gtkshortcut.c
+++ b/gtk/gtkshortcut.c
@@ -22,6 +22,7 @@
#include "gtkshortcut.h"
#include "gtkintl.h"
+#include "gtkshortcutaction.h"
#include "gtkshortcuttrigger.h"
#include "gtkwidget.h"
@@ -29,18 +30,19 @@
* SECTION:gtkshortcut
* @title: GtkShortcut
* @short_description: A widget for displaying shortcut
- * @see_also: #GtkShortcutController, #GtkShortcutTrigger
+ * @see_also: #GtkShortcutController, #GtkShortcutAction,
+ * #GtkShortcutTrigger
*
* GtkShortcut is the low level object used for managing keyboard
* shortcuts.
*
* It contains a description of how to trigger the shortcut via a
* #GtkShortcutTrigger and a way to activate the shortcut on a widget
- * with gtk_shortcut_activate().
+ * via #GtkShortcutAction.
*
* The actual work is usually done via #GtkShortcutController, which
* decides if and when to activate a shortcut. Using that controller
- * directly however is rarely necessary as Various higher level
+ * directly however is rarely necessary as various higher level
* convenience APIs exist on #GtkWidgets that make it easier to use
* shortcuts in GTK.
*
@@ -53,23 +55,16 @@ struct _GtkShortcut
{
GObject parent_instance;
+ GtkShortcutAction *action;
GtkShortcutTrigger *trigger;
- char *signal;
- GtkShortcutFunc callback;
- gpointer user_data;
- GDestroyNotify destroy_notify;
GVariant *args;
-
- guint mnemonic_activate : 1;
};
enum
{
PROP_0,
+ PROP_ACTION,
PROP_ARGUMENTS,
- PROP_CALLBACK,
- PROP_MNEMONIC_ACTIVATE,
- PROP_SIGNAL,
PROP_TRIGGER,
N_PROPS
@@ -84,18 +79,9 @@ gtk_shortcut_dispose (GObject *object)
{
GtkShortcut *self = GTK_SHORTCUT (object);
+ g_clear_pointer (&self->action, gtk_shortcut_action_unref);
g_clear_pointer (&self->trigger, gtk_shortcut_trigger_unref);
- g_clear_pointer (&self->signal, g_free);
g_clear_pointer (&self->args, g_variant_unref);
- if (self->callback)
- {
- if (self->destroy_notify)
- self->destroy_notify (self->user_data);
-
- self->callback = NULL;
- self->user_data = NULL;
- self->destroy_notify = NULL;
- }
G_OBJECT_CLASS (gtk_shortcut_parent_class)->dispose (object);
}
@@ -110,20 +96,12 @@ gtk_shortcut_get_property (GObject *object,
switch (property_id)
{
- case PROP_ARGUMENTS:
- g_value_set_variant (value, self->args);
- break;
-
- case PROP_CALLBACK:
- g_value_set_boolean (value, self->callback != NULL);
- break;
-
- case PROP_MNEMONIC_ACTIVATE:
- g_value_set_boolean (value, self->mnemonic_activate);
+ case PROP_ACTION:
+ g_value_set_boxed (value, self->action);
break;
- case PROP_SIGNAL:
- g_value_set_string (value, self->signal);
+ case PROP_ARGUMENTS:
+ g_value_set_variant (value, self->args);
break;
case PROP_TRIGGER:
@@ -146,16 +124,12 @@ gtk_shortcut_set_property (GObject *object,
switch (property_id)
{
- case PROP_ARGUMENTS:
- gtk_shortcut_set_arguments (self, g_value_get_variant (value));
- break;
-
- case PROP_MNEMONIC_ACTIVATE:
- gtk_shortcut_set_mnemonic_activate (self, g_value_get_boolean (value));
+ case PROP_ACTION:
+ gtk_shortcut_set_action (self, g_value_dup_boxed (value));
break;
- case PROP_SIGNAL:
- gtk_shortcut_set_signal (self, g_value_get_string (value));
+ case PROP_ARGUMENTS:
+ gtk_shortcut_set_arguments (self, g_value_get_variant (value));
break;
case PROP_TRIGGER:
@@ -177,6 +151,18 @@ gtk_shortcut_class_init (GtkShortcutClass *klass)
gobject_class->get_property = gtk_shortcut_get_property;
gobject_class->set_property = gtk_shortcut_set_property;
+ /**
+ * GtkShortcut:action:
+ *
+ * The action that gets activated by this shortcut.
+ */
+ properties[PROP_ACTION] =
+ g_param_spec_boxed ("action",
+ P_("Action"),
+ P_("The action activated by this shortcut"),
+ GTK_TYPE_SHORTCUT_ACTION,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
/**
* GtkShortcut:arguments:
*
@@ -190,42 +176,6 @@ gtk_shortcut_class_init (GtkShortcutClass *klass)
NULL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
- /**
- * GtkShortcut:callback:
- *
- * Whether a callback is used for shortcut activation
- */
- properties[PROP_CALLBACK] =
- g_param_spec_boolean ("callback",
- P_("Callback"),
- P_("Whether a callback is used for shortcut activation"),
- FALSE,
- G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
-
- /**
- * GtkShortcut:mnemonic-activate:
- *
- * %TRUE if this shortcut should call gtk_widget_mnemonic_activate().
- */
- properties[PROP_MNEMONIC_ACTIVATE] =
- g_param_spec_boolean ("mnemonic-activate",
- P_("Mnemonic activate"),
- P_("Call gtk_widget_mnemonic_activate()"),
- FALSE,
- G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
-
- /**
- * GtkShortcut:signal:
- *
- * The action signal to emit on the widget upon activation.
- */
- properties[PROP_SIGNAL] =
- g_param_spec_string ("signal",
- P_("Signal"),
- P_("The action signal to emit"),
- NULL,
- G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
-
/**
* GtkShortcut:trigger:
*
@@ -244,6 +194,7 @@ gtk_shortcut_class_init (GtkShortcutClass *klass)
static void
gtk_shortcut_init (GtkShortcut *self)
{
+ self->action = gtk_nothing_action_new ();
self->trigger = gtk_shortcut_trigger_ref (gtk_never_trigger_get ());
}
@@ -260,262 +211,49 @@ gtk_shortcut_new (void)
return g_object_new (GTK_TYPE_SHORTCUT, NULL);
}
-static gboolean
-binding_compose_params (GObject *object,
- GVariantIter *args,
- GSignalQuery *query,
- GValue **params_p)
+/**
+ * gtk_shortcut_get_action:
+ * @self: a #GtkShortcut
+ *
+ * Gets the action that is activated by this shortcut.
+ *
+ * Returns: (transfer none): the action
+ **/
+GtkShortcutAction *
+gtk_shortcut_get_action (GtkShortcut *self)
{
- GValue *params;
- const GType *types;
- guint i;
- gboolean valid;
-
- params = g_new0 (GValue, query->n_params + 1);
- *params_p = params;
-
- /* The instance we emit on is the first object in the array
- */
- g_value_init (params, G_TYPE_OBJECT);
- g_value_set_object (params, G_OBJECT (object));
- params++;
-
- types = query->param_types;
- valid = TRUE;
- for (i = 1; i < query->n_params + 1 && valid; i++)
- {
- GValue tmp_value = G_VALUE_INIT;
- GVariant *tmp_variant;
-
- g_value_init (params, *types);
- tmp_variant = g_variant_iter_next_value (args);
-
- switch ((guint) g_variant_classify (tmp_variant))
- {
- case G_VARIANT_CLASS_BOOLEAN:
- g_value_init (&tmp_value, G_TYPE_BOOLEAN);
- g_value_set_boolean (&tmp_value, g_variant_get_boolean (tmp_variant));
- break;
- case G_VARIANT_CLASS_DOUBLE:
- g_value_init (&tmp_value, G_TYPE_DOUBLE);
- g_value_set_double (&tmp_value, g_variant_get_double (tmp_variant));
- break;
- case G_VARIANT_CLASS_INT32:
- g_value_init (&tmp_value, G_TYPE_LONG);
- g_value_set_long (&tmp_value, g_variant_get_int32 (tmp_variant));
- break;
- case G_VARIANT_CLASS_UINT32:
- g_value_init (&tmp_value, G_TYPE_LONG);
- g_value_set_long (&tmp_value, g_variant_get_uint32 (tmp_variant));
- break;
- case G_VARIANT_CLASS_INT64:
- g_value_init (&tmp_value, G_TYPE_LONG);
- g_value_set_long (&tmp_value, g_variant_get_int64 (tmp_variant));
- break;
- case G_VARIANT_CLASS_STRING:
- /* gtk_rc_parse_flags/enum() has fancier parsing for this; we can't call
- * that since we don't have a GParamSpec, so just do something simple
- */
- if (G_TYPE_FUNDAMENTAL (*types) == G_TYPE_ENUM)
- {
- GEnumClass *class = G_ENUM_CLASS (g_type_class_ref (*types));
- GEnumValue *enum_value;
- const char *s = g_variant_get_string (tmp_variant, NULL);
-
- valid = FALSE;
-
- enum_value = g_enum_get_value_by_name (class, s);
- if (!enum_value)
- enum_value = g_enum_get_value_by_nick (class, s);
-
- if (enum_value)
- {
- g_value_init (&tmp_value, *types);
- g_value_set_enum (&tmp_value, enum_value->value);
- valid = TRUE;
- }
-
- g_type_class_unref (class);
- }
- /* This is just a hack for compatibility with GTK+-1.2 where a string
- * could be used for a single flag value / without the support for multiple
- * values in gtk_rc_parse_flags(), this isn't very useful.
- */
- else if (G_TYPE_FUNDAMENTAL (*types) == G_TYPE_FLAGS)
- {
- GFlagsClass *class = G_FLAGS_CLASS (g_type_class_ref (*types));
- GFlagsValue *flags_value;
- const char *s = g_variant_get_string (tmp_variant, NULL);
-
- valid = FALSE;
-
- flags_value = g_flags_get_value_by_name (class, s);
- if (!flags_value)
- flags_value = g_flags_get_value_by_nick (class, s);
- if (flags_value)
- {
- g_value_init (&tmp_value, *types);
- g_value_set_flags (&tmp_value, flags_value->value);
- valid = TRUE;
- }
-
- g_type_class_unref (class);
- }
- else
- {
- g_value_init (&tmp_value, G_TYPE_STRING);
- g_value_set_static_string (&tmp_value, g_variant_get_string (tmp_variant, NULL));
- }
- break;
- default:
- valid = FALSE;
- break;
- }
-
- if (valid)
- {
- if (!g_value_transform (&tmp_value, params))
- valid = FALSE;
-
- g_value_unset (&tmp_value);
- }
-
- g_variant_unref (tmp_variant);
- types++;
- params++;
- }
-
- if (!valid)
- {
- guint j;
-
- for (j = 0; j < i; j++)
- g_value_unset (&(*params_p)[j]);
-
- g_free (*params_p);
- *params_p = NULL;
- }
+ g_return_val_if_fail (GTK_IS_SHORTCUT (self), NULL);
- return valid;
+ return self->action;
}
-static gboolean
-gtk_shortcut_emit_signal (GObject *object,
- const char *signal,
- GVariant *args,
- gboolean *handled,
- GError **error)
+/**
+ * gtk_shortcut_set_action:
+ * @self: a #GtkShortcut
+ * @action: (transfer full) (nullable): The new action.
+ * If the @action is %NULL, the nothing action will be used.
+ *
+ * Sets the new action for @self to be @action.
+ **/
+void
+gtk_shortcut_set_action (GtkShortcut *self,
+ GtkShortcutAction *action)
{
- GSignalQuery query;
- guint signal_id;
- GValue *params = NULL;
- GValue return_val = G_VALUE_INIT;
- GVariantIter args_iter;
- gsize n_args;
- guint i;
-
- *handled = FALSE;
-
- signal_id = g_signal_lookup (signal, G_OBJECT_TYPE (object));
- if (!signal_id)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Could not find signal \"%s\" in the '%s' class ancestry",
- signal,
- g_type_name (G_OBJECT_TYPE (object)));
- return FALSE;
- }
-
- g_signal_query (signal_id, &query);
- if (args)
- n_args = g_variant_iter_init (&args_iter, args);
- else
- n_args = 0;
- if (query.n_params != n_args ||
- (query.return_type != G_TYPE_NONE && query.return_type != G_TYPE_BOOLEAN) ||
- !binding_compose_params (object, &args_iter, &query, ¶ms))
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "signature mismatch for signal \"%s\" in the '%s' class ancestry",
- signal,
- g_type_name (G_OBJECT_TYPE (object)));
- return FALSE;
- }
- else if (!(query.signal_flags & G_SIGNAL_ACTION))
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "signal \"%s\" in the '%s' class ancestry cannot be used for action emissions",
- signal,
- g_type_name (G_OBJECT_TYPE (object)));
- return FALSE;
- }
-
- if (query.return_type == G_TYPE_BOOLEAN)
- g_value_init (&return_val, G_TYPE_BOOLEAN);
-
- g_signal_emitv (params, signal_id, 0, &return_val);
+ g_return_if_fail (GTK_IS_SHORTCUT (self));
- if (query.return_type == G_TYPE_BOOLEAN)
- {
- if (g_value_get_boolean (&return_val))
- *handled = TRUE;
- g_value_unset (&return_val);
- }
- else
- *handled = TRUE;
+ if (action == NULL)
+ action = gtk_nothing_action_new ();
- if (params != NULL)
+ if (self->action == action)
{
- for (i = 0; i < query.n_params + 1; i++)
- g_value_unset (¶ms[i]);
-
- g_free (params);
+ gtk_shortcut_action_unref (action);
+ return;
}
+
+ gtk_shortcut_action_unref (self->action);
+ self->action = action;
- return TRUE;
-}
-
-gboolean
-gtk_shortcut_activate (GtkShortcut *self,
- GtkWidget *widget)
-{
- g_return_val_if_fail (GTK_IS_SHORTCUT (self), FALSE);
- g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
-
- if (self->callback)
- {
- return self->callback (widget, self->args, self->user_data);
- }
- else if (self->signal)
- {
- GError *error = NULL;
- gboolean handled;
-
- if (!gtk_shortcut_emit_signal (G_OBJECT (widget),
- self->signal,
- self->args,
- &handled,
- &error))
- {
- char *accelerator = gtk_shortcut_trigger_to_string (self->trigger);
- g_warning ("gtk_shortcut_activate(): \":%s\": %s",
- accelerator,
- error->message);
- g_clear_error (&error);
- return FALSE;
- }
-
- return handled;
- }
- else if (self->mnemonic_activate)
- {
- return gtk_widget_mnemonic_activate (widget, FALSE);
- }
- else
- {
- /* shortcut is a dud */
- return FALSE;
- }
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTION]);
}
/**
@@ -587,135 +325,3 @@ gtk_shortcut_set_arguments (GtkShortcut *self,
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ARGUMENTS]);
}
-static void
-gtk_shortcut_clear_activation (GtkShortcut *self)
-{
- if (self->signal)
- {
- g_clear_pointer (&self->signal, g_free);
- g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SIGNAL]);
- }
-
- if (self->callback)
- {
- if (self->destroy_notify)
- self->destroy_notify (self->user_data);
-
- self->callback = NULL;
- self->user_data = NULL;
- self->destroy_notify = NULL;
-
- g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CALLBACK]);
- }
-
- if (self->mnemonic_activate)
- {
- self->mnemonic_activate = FALSE;
- g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MNEMONIC_ACTIVATE]);
- }
-}
-
-const char *
-gtk_shortcut_get_signal (GtkShortcut *self)
-{
- g_return_val_if_fail (GTK_IS_SHORTCUT (self), NULL);
-
- return self->signal;
-}
-
-void
-gtk_shortcut_set_signal (GtkShortcut *self,
- const gchar *signal)
-{
- g_return_if_fail (GTK_IS_SHORTCUT (self));
-
- if (g_strcmp0 (self->signal, signal) == 0)
- return;
-
- g_object_freeze_notify (G_OBJECT (self));
-
- gtk_shortcut_clear_activation (self);
- self->signal = g_strdup (signal);
-
- g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SIGNAL]);
-
- g_object_thaw_notify (G_OBJECT (self));
-}
-
-gboolean
-gtk_shortcut_has_callback (GtkShortcut *self)
-{
- g_return_val_if_fail (GTK_IS_SHORTCUT (self), FALSE);
-
- return self->callback != NULL;
-}
-
-void
-gtk_shortcut_set_callback (GtkShortcut *self,
- GtkShortcutFunc callback,
- gpointer data,
- GDestroyNotify destroy)
-{
- g_return_if_fail (GTK_IS_SHORTCUT (self));
-
- g_object_freeze_notify (G_OBJECT (self));
-
- gtk_shortcut_clear_activation (self);
-
- self->callback = callback;
- self->user_data = data;
- self->destroy_notify = destroy;
-
- g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CALLBACK]);
-
- g_object_thaw_notify (G_OBJECT (self));
-}
-
-/**
- * gtk_shortcut_get_mnemonic_activate:
- * @self: a #GtkShortcut
- *
- * Checks if this shortcut calls gtk_widget_mnemonic_activate() upon
- * activation.
- *
- * Returns: %TRUE if it does.
- **/
-gboolean
-gtk_shortcut_get_mnemonic_activate (GtkShortcut *self)
-{
- g_return_val_if_fail (GTK_IS_SHORTCUT (self), FALSE);
-
- return self->mnemonic_activate;
-}
-
-/**
- * gtk_shortcut_set_mnemonic_activate:
- * @self: a #GtkShortcut
- * @mnemonic_activate: %TRUE to call gtk_widget_mnemonic_activate()
- * upon activation
- *
- * If @mnemonic_activate is %TRUE, this shortcut will call
- * gtk_widget_mnemonic_activate() whenever it is activated. All
- * previous activations will be unset.
- *
- * If @mnemonic_activate is %FALSE, it will stop this shortcut from
- * calling gtk_widget_mnemonic_activate() if it did so before.
- **/
-void
-gtk_shortcut_set_mnemonic_activate (GtkShortcut *self,
- gboolean mnemonic_activate)
-{
- g_return_if_fail (GTK_IS_SHORTCUT (self));
-
- if (self->mnemonic_activate == mnemonic_activate)
- return;
-
- g_object_freeze_notify (G_OBJECT (self));
-
- gtk_shortcut_clear_activation (self);
- self->mnemonic_activate = mnemonic_activate;
-
- g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MNEMONIC_ACTIVATE]);
- g_object_thaw_notify (G_OBJECT (self));
-}
-
diff --git a/gtk/gtkshortcut.h b/gtk/gtkshortcut.h
index 8a5e42e69f..78bc871bf3 100644
--- a/gtk/gtkshortcut.h
+++ b/gtk/gtkshortcut.h
@@ -24,10 +24,6 @@
G_BEGIN_DECLS
-typedef gboolean (* GtkShortcutFunc) (GtkWidget *widget,
- GVariant *args,
- gpointer user_data);
-
#define GTK_TYPE_SHORTCUT (gtk_shortcut_get_type ())
GDK_AVAILABLE_IN_ALL
@@ -36,44 +32,24 @@ G_DECLARE_FINAL_TYPE (GtkShortcut, gtk_shortcut, GTK, SHORTCUT, GObject)
GDK_AVAILABLE_IN_ALL
GtkShortcut * gtk_shortcut_new (void);
+GDK_AVAILABLE_IN_ALL
+GtkShortcutTrigger *
+ gtk_shortcut_get_trigger (GtkShortcut *self);
GDK_AVAILABLE_IN_ALL
void gtk_shortcut_set_trigger (GtkShortcut *self,
GtkShortcutTrigger *trigger);
GDK_AVAILABLE_IN_ALL
-GtkShortcutTrigger *
- gtk_shortcut_get_trigger (GtkShortcut *self);
-
+GtkShortcutAction *
+ gtk_shortcut_get_action (GtkShortcut *self);
GDK_AVAILABLE_IN_ALL
-gboolean gtk_shortcut_activate (GtkShortcut *self,
- GtkWidget *widget);
+void gtk_shortcut_set_action (GtkShortcut *self,
+ GtkShortcutAction *action);
GDK_AVAILABLE_IN_ALL
GVariant * gtk_shortcut_get_arguments (GtkShortcut *self);
GDK_AVAILABLE_IN_ALL
void gtk_shortcut_set_arguments (GtkShortcut *self,
GVariant *args);
-GDK_AVAILABLE_IN_ALL
-const char * gtk_shortcut_get_signal (GtkShortcut *self);
-GDK_AVAILABLE_IN_ALL
-void gtk_shortcut_set_signal (GtkShortcut *self,
- const gchar *signal);
-GDK_AVAILABLE_IN_ALL
-gboolean gtk_shortcut_has_callback (GtkShortcut *self);
-GDK_AVAILABLE_IN_ALL
-void gtk_shortcut_set_callback (GtkShortcut *self,
- GtkShortcutFunc callback,
- gpointer data,
- GDestroyNotify destroy);
-GDK_AVAILABLE_IN_ALL
-gboolean gtk_shortcut_get_mnemonic_activate (GtkShortcut *self);
-GDK_AVAILABLE_IN_ALL
-void gtk_shortcut_set_mnemonic_activate (GtkShortcut *self,
- gboolean mnemonic_activate);
-GDK_AVAILABLE_IN_ALL
-gboolean gtk_shortcut_get_activate (GtkShortcut *self);
-GDK_AVAILABLE_IN_ALL
-void gtk_shortcut_set_activate (GtkShortcut *self,
- gboolean activate);
G_END_DECLS
diff --git a/gtk/gtkshortcutaction.c b/gtk/gtkshortcutaction.c
new file mode 100644
index 0000000000..d204027073
--- /dev/null
+++ b/gtk/gtkshortcutaction.c
@@ -0,0 +1,943 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+/**
+ * SECTION:GtkShortcutAction
+ * @Title: GtkShortcutAction
+ * @Short_description: Actions to track if shortcuts should be activated
+ * @See_also: #GtkShortcut
+ *
+ * #GtkShortcutAction is the object used to track if a #GtkShortcut should be
+ * activated. For this purpose, gtk_shortcut_action_action() can be called
+ * on a #GdkEvent.
+ *
+ * #GtkShortcutActions contain functions that allow easy presentation to end
+ * users as well as being printed for debugging.
+ *
+ * All #GtkShortcutActions are immutable, you can only specify their properties
+ * during construction. If you want to change a action, you have to replace it
+ * with a new one.
+ */
+
+#include "config.h"
+
+#include "gtkshortcutaction.h"
+
+#include "gtkwidgetprivate.h"
+
+typedef struct _GtkShortcutActionClass GtkShortcutActionClass;
+
+#define GTK_IS_SHORTCUT_ACTION_TYPE(action,type) (GTK_IS_SHORTCUT_ACTION (action) &&
(action)->action_class->action_type == (type))
+
+struct _GtkShortcutAction
+{
+ const GtkShortcutActionClass *action_class;
+
+ volatile int ref_count;
+};
+
+struct _GtkShortcutActionClass
+{
+ GtkShortcutActionType action_type;
+ gsize struct_size;
+ const char *type_name;
+
+ void (* finalize) (GtkShortcutAction *action);
+ gboolean (* activate) (GtkShortcutAction *action,
+ GtkShortcutActionFlags flags,
+ GtkWidget *widget,
+ GVariant *args);
+};
+
+G_DEFINE_BOXED_TYPE (GtkShortcutAction, gtk_shortcut_action,
+ gtk_shortcut_action_ref,
+ gtk_shortcut_action_unref)
+
+static void
+gtk_shortcut_action_finalize (GtkShortcutAction *self)
+{
+ self->action_class->finalize (self);
+
+ g_free (self);
+}
+
+/*< private >
+ * gtk_shortcut_action_new:
+ * @action_class: class structure for this action
+ *
+ * Returns: (transfer full): the newly created #GtkShortcutAction
+ */
+static GtkShortcutAction *
+gtk_shortcut_action_new (const GtkShortcutActionClass *action_class)
+{
+ GtkShortcutAction *self;
+
+ g_return_val_if_fail (action_class != NULL, NULL);
+
+ self = g_malloc0 (action_class->struct_size);
+
+ self->action_class = action_class;
+
+ self->ref_count = 1;
+
+ return self;
+}
+
+/**
+ * gtk_shortcut_action_ref:
+ * @action: a #GtkShortcutAction
+ *
+ * Acquires a reference on the given #GtkShortcutAction.
+ *
+ * Returns: (transfer full): the #GtkShortcutAction with an additional reference
+ */
+GtkShortcutAction *
+gtk_shortcut_action_ref (GtkShortcutAction *action)
+{
+ g_return_val_if_fail (GTK_IS_SHORTCUT_ACTION (action), NULL);
+
+ g_atomic_int_inc (&action->ref_count);
+
+ return action;
+}
+
+/**
+ * gtk_shortcut_action_unref:
+ * @action: (transfer full): a #GtkShortcutAction
+ *
+ * Releases a reference on the given #GtkShortcutAction.
+ *
+ * If the reference was the last, the resources associated to the @action are
+ * freed.
+ */
+void
+gtk_shortcut_action_unref (GtkShortcutAction *action)
+{
+ g_return_if_fail (GTK_IS_SHORTCUT_ACTION (action));
+
+ if (g_atomic_int_dec_and_test (&action->ref_count))
+ gtk_shortcut_action_finalize (action);
+}
+
+/**
+ * gtk_shortcut_action_get_action_type:
+ * @self: a #GtkShortcutAction
+ *
+ * Returns the type of the @action.
+ *
+ * Returns: the type of the #GtkShortcutAction
+ */
+GtkShortcutActionType
+gtk_shortcut_action_get_action_type (GtkShortcutAction *self)
+{
+ g_return_val_if_fail (GTK_IS_SHORTCUT_ACTION (self), GTK_SHORTCUT_ACTION_NOTHING);
+
+ return self->action_class->action_type;
+}
+
+/**
+ * gtk_shortcut_action_activate:
+ * @self: a #GtkShortcutAction
+ * @flags: flags to activate with
+ * @widget: Target of the activation
+ * @args: (allow-none): arguments to pass
+ *
+ * Activates the action on the @widget with the given @args.
+ *
+ * Note that some actions do ignore the passed in @flags, @widget or
+ * @args.
+ *
+ * Activation of an action can fail for various reasons. If the action
+ * is not supported by the @widget, if the @args don't match the action
+ * or if the activation otherwise had no effect, %FALSE will be returned.
+ *
+ * Returns: %TRUE if this action was activated successfully
+ **/
+gboolean
+gtk_shortcut_action_activate (GtkShortcutAction *self,
+ GtkShortcutActionFlags flags,
+ GtkWidget *widget,
+ GVariant *args)
+{
+ g_return_val_if_fail (GTK_IS_SHORTCUT_ACTION (self), FALSE);
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+
+ return self->action_class->activate (self, flags, widget, args);
+}
+
+/*** GTK_SHORTCUT_ACTION_NOTHING ***/
+
+typedef struct _GtkNothingAction GtkNothingAction;
+
+struct _GtkNothingAction
+{
+ GtkShortcutAction action;
+};
+
+static void
+gtk_nothing_action_finalize (GtkShortcutAction *action)
+{
+ g_assert_not_reached ();
+}
+
+static gboolean
+gtk_nothing_action_activate (GtkShortcutAction *action,
+ GtkShortcutActionFlags flags,
+ GtkWidget *widget,
+ GVariant *args)
+{
+ return FALSE;
+}
+
+static const GtkShortcutActionClass GTK_NOTHING_ACTION_CLASS = {
+ GTK_SHORTCUT_ACTION_NOTHING,
+ sizeof (GtkNothingAction),
+ "GtkNothingAction",
+ gtk_nothing_action_finalize,
+ gtk_nothing_action_activate
+};
+
+static GtkNothingAction nothing = { { >K_NOTHING_ACTION_CLASS, 1 } };
+
+/**
+ * gtk_nothing_action_new:
+ *
+ * Gets the nothing action. This is an action that does nothing and where
+ * activating it always fails.
+ *
+ * Returns: The nothing action
+ */
+GtkShortcutAction *
+gtk_nothing_action_new (void)
+{
+ return gtk_shortcut_action_ref (¬hing.action);
+}
+
+/*** GTK_SHORTCUT_ACTION_CALLBACK ***/
+
+typedef struct _GtkCallbackAction GtkCallbackAction;
+
+struct _GtkCallbackAction
+{
+ GtkShortcutAction action;
+
+ GtkShortcutFunc callback;
+ gpointer user_data;
+ GDestroyNotify destroy_notify;
+};
+
+static void
+gtk_callback_action_finalize (GtkShortcutAction *action)
+{
+ GtkCallbackAction *self = (GtkCallbackAction *) action;
+
+ if (self->destroy_notify)
+ self->destroy_notify (self->user_data);
+}
+
+static gboolean
+gtk_callback_action_activate (GtkShortcutAction *action,
+ GtkShortcutActionFlags flags,
+ GtkWidget *widget,
+ GVariant *args)
+{
+ GtkCallbackAction *self = (GtkCallbackAction *) action;
+
+ return self->callback (widget, args, self->user_data);
+}
+
+static const GtkShortcutActionClass GTK_CALLBACK_ACTION_CLASS = {
+ GTK_SHORTCUT_ACTION_CALLBACK,
+ sizeof (GtkCallbackAction),
+ "GtkCallbackAction",
+ gtk_callback_action_finalize,
+ gtk_callback_action_activate
+};
+
+/**
+ * gtk_callback_action_new:
+ * @callback: the callback to call
+ * @data:
+ * @destroy:
+ *
+ * Create a custom action that calls the given @callback when
+ * activated.
+ *
+ * Returns: A new shortcut action
+ **/
+GtkShortcutAction *
+gtk_callback_action_new (GtkShortcutFunc callback,
+ gpointer data,
+ GDestroyNotify destroy)
+{
+ GtkCallbackAction *self;
+
+ g_return_val_if_fail (callback != NULL, NULL);
+
+ self = (GtkCallbackAction *) gtk_shortcut_action_new (>K_CALLBACK_ACTION_CLASS);
+
+ self->callback = callback;
+ self->user_data = data;
+ self->destroy_notify = destroy;
+
+ return &self->action;
+}
+
+/*** GTK_SHORTCUT_ACTION_ACTIVATE ***/
+
+typedef struct _GtkActivateAction GtkActivateAction;
+
+struct _GtkActivateAction
+{
+ GtkShortcutAction action;
+};
+
+static void
+gtk_activate_action_finalize (GtkShortcutAction *action)
+{
+ g_assert_not_reached ();
+}
+
+static gboolean
+gtk_activate_action_activate (GtkShortcutAction *action,
+ GtkShortcutActionFlags flags,
+ GtkWidget *widget,
+ GVariant *args)
+{
+ return gtk_widget_activate (widget);
+}
+
+static const GtkShortcutActionClass GTK_ACTIVATE_ACTION_CLASS = {
+ GTK_SHORTCUT_ACTION_ACTIVATE,
+ sizeof (GtkActivateAction),
+ "GtkActivateAction",
+ gtk_activate_action_finalize,
+ gtk_activate_action_activate
+};
+
+static GtkActivateAction activate = { { >K_ACTIVATE_ACTION_CLASS, 1 } };
+
+/**
+ * gtk_activate_action_new:
+ *
+ * Gets the activate action. This is an action that calls gtk_widget_activate()
+ * on the given widget upon activation.
+ *
+ * Returns: The activate action
+ */
+GtkShortcutAction *
+gtk_activate_action_new (void)
+{
+ return gtk_shortcut_action_ref (&activate.action);
+}
+
+/*** GTK_SHORTCUT_ACTION_MNEMONIC ***/
+
+typedef struct _GtkMnemonicAction GtkMnemonicAction;
+
+struct _GtkMnemonicAction
+{
+ GtkShortcutAction action;
+};
+
+static void
+gtk_mnemonic_action_finalize (GtkShortcutAction *action)
+{
+ g_assert_not_reached ();
+}
+
+static gboolean
+gtk_mnemonic_action_activate (GtkShortcutAction *action,
+ GtkShortcutActionFlags flags,
+ GtkWidget *widget,
+ GVariant *args)
+{
+ return gtk_widget_mnemonic_activate (widget, flags & GTK_SHORTCUT_ACTION_EXCLUSIVE ? FALSE : TRUE);
+}
+
+static const GtkShortcutActionClass GTK_MNEMONIC_ACTION_CLASS = {
+ GTK_SHORTCUT_ACTION_MNEMONIC,
+ sizeof (GtkMnemonicAction),
+ "GtkMnemonicAction",
+ gtk_mnemonic_action_finalize,
+ gtk_mnemonic_action_activate
+};
+
+static GtkMnemonicAction mnemonic = { { >K_MNEMONIC_ACTION_CLASS, 1 } };
+
+/**
+ * gtk_mnemonic_action_new:
+ *
+ * Gets the mnemonic action. This is an action that calls
+ * gtk_widget_mnemonic_activate() on the given widget upon activation.
+ *
+ * Returns: The mnemonic action
+ */
+GtkShortcutAction *
+gtk_mnemonic_action_new (void)
+{
+ return gtk_shortcut_action_ref (&mnemonic.action);
+}
+
+/*** GTK_SHORTCUT_ACTION_SIGNAL ***/
+
+typedef struct _GtkSignalAction GtkSignalAction;
+
+struct _GtkSignalAction
+{
+ GtkShortcutAction action;
+
+ char *name;
+};
+
+static void
+gtk_signal_action_finalize (GtkShortcutAction *action)
+{
+ GtkSignalAction *self = (GtkSignalAction *) action;
+
+ g_free (self->name);
+}
+
+static gboolean
+binding_compose_params (GtkWidget *widget,
+ GVariantIter *args,
+ GSignalQuery *query,
+ GValue **params_p)
+{
+ GValue *params;
+ const GType *types;
+ guint i;
+ gboolean valid;
+
+ params = g_new0 (GValue, query->n_params + 1);
+ *params_p = params;
+
+ /* The instance we emit on is the first object in the array
+ */
+ g_value_init (params, G_TYPE_OBJECT);
+ g_value_set_object (params, G_OBJECT (widget));
+ params++;
+
+ types = query->param_types;
+ valid = TRUE;
+ for (i = 1; i < query->n_params + 1 && valid; i++)
+ {
+ GValue tmp_value = G_VALUE_INIT;
+ GVariant *tmp_variant;
+
+ g_value_init (params, *types);
+ tmp_variant = g_variant_iter_next_value (args);
+
+ switch ((guint) g_variant_classify (tmp_variant))
+ {
+ case G_VARIANT_CLASS_BOOLEAN:
+ g_value_init (&tmp_value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&tmp_value, g_variant_get_boolean (tmp_variant));
+ break;
+ case G_VARIANT_CLASS_DOUBLE:
+ g_value_init (&tmp_value, G_TYPE_DOUBLE);
+ g_value_set_double (&tmp_value, g_variant_get_double (tmp_variant));
+ break;
+ case G_VARIANT_CLASS_INT32:
+ g_value_init (&tmp_value, G_TYPE_LONG);
+ g_value_set_long (&tmp_value, g_variant_get_int32 (tmp_variant));
+ break;
+ case G_VARIANT_CLASS_UINT32:
+ g_value_init (&tmp_value, G_TYPE_LONG);
+ g_value_set_long (&tmp_value, g_variant_get_uint32 (tmp_variant));
+ break;
+ case G_VARIANT_CLASS_INT64:
+ g_value_init (&tmp_value, G_TYPE_LONG);
+ g_value_set_long (&tmp_value, g_variant_get_int64 (tmp_variant));
+ break;
+ case G_VARIANT_CLASS_STRING:
+ /* gtk_rc_parse_flags/enum() has fancier parsing for this; we can't call
+ * that since we don't have a GParamSpec, so just do something simple
+ */
+ if (G_TYPE_FUNDAMENTAL (*types) == G_TYPE_ENUM)
+ {
+ GEnumClass *class = G_ENUM_CLASS (g_type_class_ref (*types));
+ GEnumValue *enum_value;
+ const char *s = g_variant_get_string (tmp_variant, NULL);
+
+ valid = FALSE;
+
+ enum_value = g_enum_get_value_by_name (class, s);
+ if (!enum_value)
+ enum_value = g_enum_get_value_by_nick (class, s);
+
+ if (enum_value)
+ {
+ g_value_init (&tmp_value, *types);
+ g_value_set_enum (&tmp_value, enum_value->value);
+ valid = TRUE;
+ }
+
+ g_type_class_unref (class);
+ }
+ /* This is just a hack for compatibility with GTK+-1.2 where a string
+ * could be used for a single flag value / without the support for multiple
+ * values in gtk_rc_parse_flags(), this isn't very useful.
+ */
+ else if (G_TYPE_FUNDAMENTAL (*types) == G_TYPE_FLAGS)
+ {
+ GFlagsClass *class = G_FLAGS_CLASS (g_type_class_ref (*types));
+ GFlagsValue *flags_value;
+ const char *s = g_variant_get_string (tmp_variant, NULL);
+
+ valid = FALSE;
+
+ flags_value = g_flags_get_value_by_name (class, s);
+ if (!flags_value)
+ flags_value = g_flags_get_value_by_nick (class, s);
+ if (flags_value)
+ {
+ g_value_init (&tmp_value, *types);
+ g_value_set_flags (&tmp_value, flags_value->value);
+ valid = TRUE;
+ }
+
+ g_type_class_unref (class);
+ }
+ else
+ {
+ g_value_init (&tmp_value, G_TYPE_STRING);
+ g_value_set_static_string (&tmp_value, g_variant_get_string (tmp_variant, NULL));
+ }
+ break;
+ default:
+ valid = FALSE;
+ break;
+ }
+
+ if (valid)
+ {
+ if (!g_value_transform (&tmp_value, params))
+ valid = FALSE;
+
+ g_value_unset (&tmp_value);
+ }
+
+ g_variant_unref (tmp_variant);
+ types++;
+ params++;
+ }
+
+ if (!valid)
+ {
+ guint j;
+
+ for (j = 0; j < i; j++)
+ g_value_unset (&(*params_p)[j]);
+
+ g_free (*params_p);
+ *params_p = NULL;
+ }
+
+ return valid;
+}
+
+static gboolean
+gtk_signal_action_emit_signal (GtkWidget *widget,
+ const char *signal,
+ GVariant *args,
+ gboolean *handled,
+ GError **error)
+{
+ GSignalQuery query;
+ guint signal_id;
+ GValue *params = NULL;
+ GValue return_val = G_VALUE_INIT;
+ GVariantIter args_iter;
+ gsize n_args;
+ guint i;
+
+ *handled = FALSE;
+
+ signal_id = g_signal_lookup (signal, G_OBJECT_TYPE (widget));
+ if (!signal_id)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Could not find signal \"%s\" in the '%s' class ancestry",
+ signal,
+ g_type_name (G_OBJECT_TYPE (widget)));
+ return FALSE;
+ }
+
+ g_signal_query (signal_id, &query);
+ if (args == NULL)
+ n_args = 0;
+ else if (g_variant_is_of_type (args, G_VARIANT_TYPE_TUPLE))
+ n_args = g_variant_iter_init (&args_iter, args);
+ else
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "argument GVariant is not a tuple");
+ return FALSE;
+ }
+ if (query.n_params != n_args ||
+ (query.return_type != G_TYPE_NONE && query.return_type != G_TYPE_BOOLEAN) ||
+ !binding_compose_params (widget, &args_iter, &query, ¶ms))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "signature mismatch for signal \"%s\" in the '%s' class ancestry",
+ signal,
+ g_type_name (G_OBJECT_TYPE (widget)));
+ return FALSE;
+ }
+ else if (!(query.signal_flags & G_SIGNAL_ACTION))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "signal \"%s\" in the '%s' class ancestry cannot be used for action emissions",
+ signal,
+ g_type_name (G_OBJECT_TYPE (widget)));
+ return FALSE;
+ }
+
+ if (query.return_type == G_TYPE_BOOLEAN)
+ g_value_init (&return_val, G_TYPE_BOOLEAN);
+
+ g_signal_emitv (params, signal_id, 0, &return_val);
+
+ if (query.return_type == G_TYPE_BOOLEAN)
+ {
+ if (g_value_get_boolean (&return_val))
+ *handled = TRUE;
+ g_value_unset (&return_val);
+ }
+ else
+ *handled = TRUE;
+
+ if (params != NULL)
+ {
+ for (i = 0; i < query.n_params + 1; i++)
+ g_value_unset (¶ms[i]);
+
+ g_free (params);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gtk_signal_action_activate (GtkShortcutAction *action,
+ GtkShortcutActionFlags flags,
+ GtkWidget *widget,
+ GVariant *args)
+{
+ GtkSignalAction *self = (GtkSignalAction *) action;
+ GError *error = NULL;
+ gboolean handled;
+
+ if (!gtk_signal_action_emit_signal (widget,
+ self->name,
+ args,
+ &handled,
+ &error))
+ {
+ g_warning ("gtk_signal_action_activate(): %s",
+ error->message);
+ g_clear_error (&error);
+ return FALSE;
+ }
+
+ return handled;
+}
+
+static const GtkShortcutActionClass GTK_SIGNAL_ACTION_CLASS = {
+ GTK_SHORTCUT_ACTION_SIGNAL,
+ sizeof (GtkSignalAction),
+ "GtkSignalAction",
+ gtk_signal_action_finalize,
+ gtk_signal_action_activate
+};
+
+/**
+ * gtk_signal_action_new:
+ * @signal_name: name of the signal to emit
+ *
+ * Creates an action that when activated, emits the given action signal
+ * on the provided widget unpacking the given args into arguments passed
+ * to the signal.
+ *
+ * Returns: a new #GtkShortcutAction
+ **/
+GtkShortcutAction *
+gtk_signal_action_new (const char *signal_name)
+{
+ GtkSignalAction *self;
+
+ g_return_val_if_fail (signal_name != NULL, NULL);
+
+ self = (GtkSignalAction *) gtk_shortcut_action_new (>K_SIGNAL_ACTION_CLASS);
+
+ self->name = g_strdup (signal_name);
+
+ return &self->action;
+}
+
+/**
+ * gtk_signal_action_get_signal_name:
+ * @action: a signal action
+ *
+ * Returns the name of the signal that will be emitted.
+ *
+ * Returns: the name of the signal to emit
+ **/
+const char *
+gtk_signal_action_get_signal_name (GtkShortcutAction *action)
+{
+ GtkSignalAction *self = (GtkSignalAction *) action;
+
+ g_return_val_if_fail (GTK_IS_SHORTCUT_ACTION_TYPE (action, GTK_SHORTCUT_ACTION_SIGNAL), NULL);
+
+ return self->name;
+}
+
+/*** GTK_SHORTCUT_ACTION_ACTION ***/
+
+typedef struct _GtkActionAction GtkActionAction;
+
+struct _GtkActionAction
+{
+ GtkShortcutAction action;
+
+ char *name;
+};
+
+static void
+gtk_action_action_finalize (GtkShortcutAction *action)
+{
+ GtkSignalAction *self = (GtkSignalAction *) action;
+
+ g_free (self->name);
+}
+
+static gboolean
+gtk_shortcut_trigger_check_parameter_type (GVariant *args,
+ const GVariantType *parameter_type)
+{
+ if (args)
+ {
+ if (parameter_type == NULL)
+ {
+ g_warning ("Trying to invoke action with arguments, but action has no parameter");
+ return FALSE;
+ }
+
+ if (!g_variant_is_of_type (args, parameter_type))
+ {
+ gchar *typestr = g_variant_type_dup_string (parameter_type);
+ gchar *targetstr = g_variant_print (args, TRUE);
+ g_warning ("Trying to invoke action with target '%s',"
+ " but action expects parameter with type '%s'", targetstr, typestr);
+ g_free (targetstr);
+ g_free (typestr);
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (parameter_type != NULL)
+ {
+ gchar *typestr = g_variant_type_dup_string (parameter_type);
+ g_warning ("Trying to invoke action without arguments,"
+ " but action expects parameter with type '%s'", typestr);
+ g_free (typestr);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gtk_action_action_activate (GtkShortcutAction *action,
+ GtkShortcutActionFlags flags,
+ GtkWidget *widget,
+ GVariant *args)
+{
+ GtkSignalAction *self = (GtkSignalAction *) action;
+ GActionGroup *action_group;
+ const GVariantType *parameter_type;
+ gboolean enabled;
+
+ action_group = G_ACTION_GROUP (_gtk_widget_get_action_muxer (widget, FALSE));
+ if (action_group == NULL)
+ return FALSE;
+
+ if (!g_action_group_query_action (action_group, self->name, &enabled, ¶meter_type, NULL, NULL, NULL))
+ return FALSE;
+
+ if (!enabled)
+ return FALSE;
+
+ /* We found an action with the correct name and it's enabled.
+ * This is the action that we are going to try to invoke.
+ *
+ * There is still the possibility that the args don't
+ * match the expected parameter type. In that case, we will print
+ * a warning.
+ */
+ if (!gtk_shortcut_trigger_check_parameter_type (args, parameter_type))
+ return FALSE;
+
+ g_action_group_activate_action (action_group, self->name, args);
+
+ return TRUE;
+}
+
+static const GtkShortcutActionClass GTK_ACTION_ACTION_CLASS = {
+ GTK_SHORTCUT_ACTION_ACTION,
+ sizeof (GtkActionAction),
+ "GtkActionAction",
+ gtk_action_action_finalize,
+ gtk_action_action_activate
+};
+
+/**
+ * gtk_action_action_new:
+ * @name: the detailed name of the action
+ *
+ * Creates an action that when activated, activates the action given by
+ * the detailed @name on the widget passing the given arguments to it.
+ *
+ * See gtk_widget_insert_action_group() for how to add actions to widgets.
+ *
+ * Returns: a new #GtkShortcutAction
+ **/
+GtkShortcutAction *
+gtk_action_action_new (const char *name)
+{
+ GtkActionAction *self;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ self = (GtkActionAction *) gtk_shortcut_action_new (>K_ACTION_ACTION_CLASS);
+
+ self->name = g_strdup (name);
+
+ return &self->action;
+}
+
+/**
+ * gtk_action_action_get_name:
+ * @action: an action action
+ *
+ * Returns the name of the action that will be activated.
+ *
+ * Returns: the name of the action to activate
+ **/
+const char *
+gtk_action_action_get_name (GtkShortcutAction *action)
+{
+ GtkActionAction *self = (GtkActionAction *) action;
+
+ g_return_val_if_fail (GTK_IS_SHORTCUT_ACTION_TYPE (action, GTK_SHORTCUT_ACTION_ACTION), NULL);
+
+ return self->name;
+}
+
+/*** GTK_SHORTCUT_ACTION_GACTION ***/
+
+typedef struct _GtkGActionAction GtkGActionAction;
+
+struct _GtkGActionAction
+{
+ GtkShortcutAction action;
+
+ GAction *gaction;
+};
+
+static void
+gtk_gaction_action_finalize (GtkShortcutAction *action)
+{
+ GtkGActionAction *self = (GtkGActionAction *) action;
+
+ g_object_unref (self->gaction);
+}
+
+static gboolean
+gtk_gaction_action_activate (GtkShortcutAction *action,
+ GtkShortcutActionFlags flags,
+ GtkWidget *widget,
+ GVariant *args)
+{
+ GtkGActionAction *self = (GtkGActionAction *) action;
+
+ if (!gtk_shortcut_trigger_check_parameter_type (args, g_action_get_parameter_type (self->gaction)))
+ return FALSE;
+
+ if (!g_action_get_enabled (self->gaction))
+ return FALSE;
+
+ g_action_activate (self->gaction, args);
+
+ return TRUE;
+}
+
+static const GtkShortcutActionClass GTK_GACTION_ACTION_CLASS = {
+ GTK_SHORTCUT_ACTION_GACTION,
+ sizeof (GtkGActionAction),
+ "GtkGActionAction",
+ gtk_gaction_action_finalize,
+ gtk_gaction_action_activate
+};
+
+/**
+ * gtk_gaction_action_new:
+ * @action: a #GAction
+ *
+ * Creates a new action that will activate the given @gaction when activated
+ * with the passed in arguments.
+ *
+ * Returns: a new #GtkShortcutAction
+ **/
+GtkShortcutAction *
+gtk_gaction_action_new (GAction *action)
+{
+ GtkGActionAction *self;
+
+ g_return_val_if_fail (G_IS_ACTION (action), NULL);
+
+ self = (GtkGActionAction *) gtk_shortcut_action_new (>K_GACTION_ACTION_CLASS);
+
+ self->gaction = g_object_ref (action);
+
+ return &self->action;
+}
+
+/**
+ * gtk_gaction_action_get_gaction:
+ * @action: a gaction action
+ *
+ * Queries the #GAction that will be activated when this action is activated.
+ *
+ * Returns: (transfer none): The #GAction that will be activated
+ **/
+GAction *
+gtk_gaction_action_get_gaction (GtkShortcutAction *action)
+{
+ GtkGActionAction *self = (GtkGActionAction *) action;
+
+ g_return_val_if_fail (GTK_IS_SHORTCUT_ACTION_TYPE (action, GTK_SHORTCUT_ACTION_GACTION), NULL);
+
+ return self->gaction;
+}
+
diff --git a/gtk/gtkshortcutaction.h b/gtk/gtkshortcutaction.h
new file mode 100644
index 0000000000..a99eccefd6
--- /dev/null
+++ b/gtk/gtkshortcutaction.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#ifndef __GTK_SHORTCUT_ACTION_H__
+#define __GTK_SHORTCUT_ACTION_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtktypes.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SHORTCUT_ACTION (gtk_shortcut_action_get_type ())
+
+#define GTK_IS_SHORTCUT_ACTION(obj) ((obj) != NULL)
+
+/**
+ * GtkShortcutFunc:
+ * @widget: The widget passed to the activation
+ * @args: The arguments passed to the activation
+ * @user_data: The user data provided when activating the action
+ *
+ * Prototype for shortcuts based on user callbacks.
+ */
+typedef gboolean (* GtkShortcutFunc) (GtkWidget *widget,
+ GVariant *args,
+ gpointer user_data);
+
+/**
+ * GtkShortcutActionFlags:
+ * @GTK_SHORTCUT_ACTION_EXCLUSIVE: The action is the only
+ * action that can be activated. If this flag is not set,
+ * a future activation may select a different action.
+ *
+ * List of flags that can be passed to action activation.
+ * More flags may be added in the future.
+ **/
+typedef enum {
+ GTK_SHORTCUT_ACTION_EXCLUSIVE = 1 << 0
+} GtkShortcutActionFlags;
+
+/**
+ * GtkShortcutActionType:
+ * @GTK_SHORTCUT_ACTION_NOTHING: Don't ever activate
+ * @GTK_SHORTCUT_ACTION_CALLBACK: Call a custom user-provided callback
+ * @GTK_SHORTCUT_ACTION_ACTIVATE: Call gtk_widget_activate() on the widget
+ * @GTK_SHORTCUT_ACTION_MNEMONIC: Call gtk_widget_mnemonic_activate()
+ * on the widget
+ * @GTK_SHORTCUT_ACTION_SIGNAL: Emit the given action signal on the widget
+ * @GTK_SHORTCUT_ACTION_ACTION: Call the provided action on the widget
+ * @GTK_SHORTCUT_ACTION_GACTION: Activate a GAction
+ *
+ * The type of a action determines what the action does when activated.
+ **/
+typedef enum {
+ GTK_SHORTCUT_ACTION_NOTHING,
+ GTK_SHORTCUT_ACTION_CALLBACK,
+ GTK_SHORTCUT_ACTION_ACTIVATE,
+ GTK_SHORTCUT_ACTION_MNEMONIC,
+ GTK_SHORTCUT_ACTION_SIGNAL,
+ GTK_SHORTCUT_ACTION_ACTION,
+ GTK_SHORTCUT_ACTION_GACTION
+} GtkShortcutActionType;
+
+GDK_AVAILABLE_IN_ALL
+GType gtk_shortcut_action_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_ALL
+GtkShortcutAction * gtk_shortcut_action_ref (GtkShortcutAction *self);
+GDK_AVAILABLE_IN_ALL
+void gtk_shortcut_action_unref (GtkShortcutAction *self);
+
+GDK_AVAILABLE_IN_ALL
+GtkShortcutActionType gtk_shortcut_action_get_action_type (GtkShortcutAction *self);
+
+GDK_AVAILABLE_IN_ALL
+gboolean gtk_shortcut_action_activate (GtkShortcutAction *self,
+ GtkShortcutActionFlags flags,
+ GtkWidget *widget,
+ GVariant *args);
+
+GDK_AVAILABLE_IN_ALL
+GtkShortcutAction * gtk_nothing_action_new (void);
+
+GDK_AVAILABLE_IN_ALL
+GtkShortcutAction * gtk_callback_action_new (GtkShortcutFunc callback,
+ gpointer data,
+ GDestroyNotify destroy);
+
+GDK_AVAILABLE_IN_ALL
+GtkShortcutAction * gtk_mnemonic_action_new (void);
+GDK_AVAILABLE_IN_ALL
+GtkShortcutAction * gtk_activate_action_new (void);
+
+GDK_AVAILABLE_IN_ALL
+GtkShortcutAction * gtk_signal_action_new (const char *signal_name);
+GDK_AVAILABLE_IN_ALL
+const char * gtk_signal_action_get_signal_name (GtkShortcutAction *action);
+
+GDK_AVAILABLE_IN_ALL
+GtkShortcutAction * gtk_action_action_new (const char *name);
+GDK_AVAILABLE_IN_ALL
+const char * gtk_action_action_get_name (GtkShortcutAction *action);
+
+GDK_AVAILABLE_IN_ALL
+GtkShortcutAction * gtk_gaction_action_new (GAction *action);
+GDK_AVAILABLE_IN_ALL
+GAction * gtk_gaction_action_get_gaction (GtkShortcutAction *action);
+
+G_END_DECLS
+
+#endif /* __GTK_SHORTCUT_ACTION_H__ */
diff --git a/gtk/gtkshortcutcontroller.c b/gtk/gtkshortcutcontroller.c
index 4e4e882fc7..e26ed63179 100644
--- a/gtk/gtkshortcutcontroller.c
+++ b/gtk/gtkshortcutcontroller.c
@@ -148,7 +148,10 @@ gtk_shortcut_controller_trigger_shortcut (GtkShortcutController *self,
if (!gtk_shortcut_trigger_trigger (gtk_shortcut_get_trigger (shortcut), event, enable_mnemonics))
return FALSE;
- return gtk_shortcut_activate (shortcut, gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)));
+ return gtk_shortcut_action_activate (gtk_shortcut_get_action (shortcut),
+ GTK_SHORTCUT_ACTION_EXCLUSIVE, /* FIXME */
+ gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)),
+ gtk_shortcut_get_arguments (shortcut));
}
static gboolean
diff --git a/gtk/gtktypes.h b/gtk/gtktypes.h
index f3e11bfdf5..64dcd3af30 100644
--- a/gtk/gtktypes.h
+++ b/gtk/gtktypes.h
@@ -44,6 +44,7 @@ typedef struct _GtkRoot GtkRoot;
typedef struct _GtkSelectionData GtkSelectionData;
typedef struct _GtkSettings GtkSettings;
typedef struct _GtkShortcut GtkShortcut;
+typedef struct _GtkShortcutAction GtkShortcutAction;
typedef struct _GtkShortcutTrigger GtkShortcutTrigger;
typedef GdkSnapshot GtkSnapshot;
typedef struct _GtkStyleContext GtkStyleContext;
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 8908f5d9c9..2e9b3c74b0 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -4837,7 +4837,7 @@ gtk_widget_class_add_binding (GtkWidgetClass *widget_class,
shortcut = gtk_shortcut_new ();
gtk_shortcut_set_trigger (shortcut, gtk_keyval_trigger_new (keyval, mods));
- gtk_shortcut_set_callback (shortcut, func, NULL, NULL);
+ gtk_shortcut_set_action (shortcut, gtk_callback_action_new (func, NULL, NULL));
if (format_string)
{
va_list args;
@@ -4887,7 +4887,7 @@ gtk_widget_class_add_binding_signal (GtkWidgetClass *widget_class,
shortcut = gtk_shortcut_new ();
gtk_shortcut_set_trigger (shortcut, gtk_keyval_trigger_new (keyval, mods));
- gtk_shortcut_set_signal (shortcut, signal);
+ gtk_shortcut_set_action (shortcut, gtk_signal_action_new (signal));
if (format_string)
{
va_list args;
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index c8995bd359..90ff398195 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -34,6 +34,7 @@
#include <gtk/gtkaccelgroup.h>
#include <gtk/gtkborder.h>
#include <gtk/gtkshortcut.h>
+#include <gtk/gtkshortcutaction.h>
#include <gtk/gtktypes.h>
#include <atk/atk.h>
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index c3ef5390fa..30c56ef493 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -587,7 +587,7 @@ add_tab_bindings (GtkWidgetClass *widget_class,
gtk_shortcut_set_trigger (shortcut,
gtk_alternative_trigger_new (gtk_keyval_trigger_new (GDK_KEY_Tab, modifiers),
gtk_keyval_trigger_new (GDK_KEY_KP_Tab,
modifiers)));
- gtk_shortcut_set_signal (shortcut, "move-focus");
+ gtk_shortcut_set_action (shortcut, gtk_signal_action_new ("move-focus"));
gtk_shortcut_set_arguments (shortcut, g_variant_new_tuple ((GVariant*[1]) { g_variant_new_int32
(direction) }, 1));
gtk_widget_class_add_shortcut (widget_class, shortcut);
@@ -1910,7 +1910,7 @@ gtk_window_init (GtkWindow *window)
shortcut = gtk_shortcut_new ();
gtk_shortcut_set_trigger (shortcut, gtk_keyval_trigger_new (MENU_BAR_ACCEL, 0));
- gtk_shortcut_set_callback (shortcut, gtk_window_activate_menubar, NULL, NULL);
+ gtk_shortcut_set_action (shortcut, gtk_callback_action_new (gtk_window_activate_menubar, NULL, NULL));
gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut);
gtk_widget_add_controller (widget, controller);
diff --git a/gtk/meson.build b/gtk/meson.build
index 06f210caaf..e0218dfb48 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -328,6 +328,7 @@ gtk_public_sources = files([
'gtkseparatortoolitem.c',
'gtksettings.c',
'gtkshortcut.c',
+ 'gtkshortcutaction.c',
'gtkshortcutcontroller.c',
'gtkshortcutlabel.c',
'gtkshortcutmanager.c',
@@ -574,6 +575,7 @@ gtk_public_headers = files([
'gtkseparatortoolitem.h',
'gtksettings.h',
'gtkshortcut.h',
+ 'gtkshortcutaction.h',
'gtkshortcutcontroller.h',
'gtkshortcutlabel.h',
'gtkshortcutmanager.h',
diff --git a/testsuite/gtk/defaultvalue.c b/testsuite/gtk/defaultvalue.c
index 103a9425b7..14c8f63fdf 100644
--- a/testsuite/gtk/defaultvalue.c
+++ b/testsuite/gtk/defaultvalue.c
@@ -316,7 +316,8 @@ G_GNUC_END_IGNORE_DEPRECATIONS
continue;
if (g_type_is_a (type, GTK_TYPE_SHORTCUT) &&
- strcmp (pspec->name, "trigger") == 0)
+ (strcmp (pspec->name, "action") == 0 ||
+ strcmp (pspec->name, "trigger") == 0))
continue;
if (g_type_is_a (type, GTK_TYPE_SPIN_BUTTON) &&
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]