[gtk/shortcuts-rebased-again: 72/117] shortcut: Add GtkShortcutAction



commit 56ffd978178c1c95622133a8dde13d0164653902
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 b0b0dc8067..c104fdcafb 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -6654,21 +6654,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 4aafe07d6b..6f0ff29bde 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -200,6 +200,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 2f1e6f9b79..75be2849a7 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -1803,7 +1803,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, &params))
-    {
-      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 (&params[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 = { { &GTK_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 (&nothing.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 (&GTK_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 = { { &GTK_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 = { { &GTK_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, &params))
+    {
+      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 (&params[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 (&GTK_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, &parameter_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 (&GTK_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 (&GTK_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 91c8be9f9e..d7dbddb3c6 100644
--- a/gtk/gtktypes.h
+++ b/gtk/gtktypes.h
@@ -45,6 +45,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 8d7be4b8bf..4d3ab3fa74 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -4826,7 +4826,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;
@@ -4876,7 +4876,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 68ddb5b2ff..0867041f56 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 3408f8839b..871435bb11 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -596,7 +596,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);
@@ -1898,7 +1898,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 95f6568ab6..fd163a5aac 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -330,6 +330,7 @@ gtk_public_sources = files([
   'gtkseparatortoolitem.c',
   'gtksettings.c',
   'gtkshortcut.c',
+  'gtkshortcutaction.c',
   'gtkshortcutcontroller.c',
   'gtkshortcutlabel.c',
   'gtkshortcutmanager.c',
@@ -578,6 +579,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 63b4ea5603..c1c4c81936 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]