[gtk/wip/otte/listview: 110/169] bindings: Add gtk_binding_entry_add_action()



commit 19304c1d2c8df4ca1e8ba11337ece80553911761
Author: Benjamin Otte <otte redhat com>
Date:   Mon Oct 14 21:34:40 2019 +0200

    bindings: Add gtk_binding_entry_add_action()
    
    Allows registering bindings for activating widget actions, as an
    alternative to signal emissions.

 docs/reference/gtk/gtk4-sections.txt |   2 +
 gtk/gtkbindings.c                    | 318 ++++++++++++++++++++++++++---------
 gtk/gtkbindings.h                    |  14 ++
 3 files changed, 258 insertions(+), 76 deletions(-)
---
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt
index 6ba53c0c56..2ad5b92fcc 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -5247,6 +5247,8 @@ gtk_binding_set_find
 gtk_bindings_activate
 gtk_bindings_activate_event
 gtk_binding_set_activate
+gtk_binding_entry_add_action
+gtk_binding_entry_add_action_variant
 gtk_binding_entry_add_signal
 gtk_binding_entry_add_signal_from_string
 gtk_binding_entry_skip
diff --git a/gtk/gtkbindings.c b/gtk/gtkbindings.c
index db9d6bd2a7..59c1815772 100644
--- a/gtk/gtkbindings.c
+++ b/gtk/gtkbindings.c
@@ -138,6 +138,12 @@ struct _GtkBindingArg
   } d;
 };
 
+typedef enum 
+{
+  GTK_BINDING_SIGNAL,
+  GTK_BINDING_ACTION
+} GtkBindingActionType;
+
 /**
  * GtkBindingSignal:
  * @next: implementation detail
@@ -151,10 +157,16 @@ struct _GtkBindingArg
  */
 struct _GtkBindingSignal
 {
-  GtkBindingSignal *next;
-  gchar            *signal_name;
-  guint             n_args;
-  GtkBindingArg    *args;
+  GtkBindingSignal     *next;
+  gchar                *signal_name;
+  GtkBindingActionType  action_type;
+  union {
+    struct {
+      guint             n_args;
+      GtkBindingArg    *args;
+    };
+    GVariant           *variant;
+  };
 };
 
 /* --- variables --- */
@@ -168,13 +180,14 @@ static GQuark            key_id_class_binding_set = 0;
 /* --- functions --- */
 
 static GtkBindingSignal*
-binding_signal_new (const gchar *signal_name,
-                    guint        n_args)
+binding_signal_new_signal (const gchar *signal_name,
+                           guint        n_args)
 {
   GtkBindingSignal *signal;
 
   signal = (GtkBindingSignal *) g_slice_alloc0 (sizeof (GtkBindingSignal) + n_args * sizeof (GtkBindingArg));
   signal->next = NULL;
+  signal->action_type = GTK_BINDING_SIGNAL;
   signal->signal_name = (gchar *)g_intern_string (signal_name);
   signal->n_args = n_args;
   signal->args = (GtkBindingArg *)(signal + 1);
@@ -182,17 +195,48 @@ binding_signal_new (const gchar *signal_name,
   return signal;
 }
 
+static GtkBindingSignal*
+binding_signal_new_action (const gchar *signal_name,
+                           GVariant    *variant)
+{
+  GtkBindingSignal *signal;
+
+  signal = g_slice_new0 (GtkBindingSignal);
+  signal->next = NULL;
+  signal->action_type = GTK_BINDING_ACTION;
+  signal->signal_name = (gchar *)g_intern_string (signal_name);
+  signal->variant = variant;
+  if (variant)
+    g_variant_ref_sink (variant);
+
+  return signal;
+}
+
 static void
 binding_signal_free (GtkBindingSignal *sig)
 {
   guint i;
 
-  for (i = 0; i < sig->n_args; i++)
+  switch (sig->action_type)
     {
-      if (G_TYPE_FUNDAMENTAL (sig->args[i].arg_type) == G_TYPE_STRING)
-        g_free (sig->args[i].d.string_data);
+    case GTK_BINDING_SIGNAL:
+      for (i = 0; i < sig->n_args; i++)
+        {
+          if (G_TYPE_FUNDAMENTAL (sig->args[i].arg_type) == G_TYPE_STRING)
+            g_free (sig->args[i].d.string_data);
+        }
+      g_slice_free1 (sizeof (GtkBindingSignal) + sig->n_args * sizeof (GtkBindingArg), sig);
+      break;
+
+    case GTK_BINDING_ACTION:
+      g_clear_pointer (&sig->variant, g_variant_unref);
+      g_slice_free (GtkBindingSignal, sig);
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
     }
-  g_slice_free1 (sizeof (GtkBindingSignal) + sig->n_args * sizeof (GtkBindingArg), sig);
 }
 
 static guint
@@ -559,6 +603,97 @@ binding_compose_params (GObject         *object,
   return valid;
 }
 
+static gboolean
+binding_signal_activate_signal (GtkBindingSignal *sig,
+                                GObject          *object)
+{
+  GSignalQuery query;
+  guint signal_id;
+  GValue *params = NULL;
+  GValue return_val = G_VALUE_INIT;
+  gboolean handled = FALSE;
+
+  signal_id = g_signal_lookup (sig->signal_name, G_OBJECT_TYPE (object));
+  if (!signal_id)
+    {
+      g_warning ("gtk_binding_entry_activate(): "
+                 "could not find signal \"%s\" in the '%s' class ancestry",
+                 sig->signal_name,
+                 g_type_name (G_OBJECT_TYPE (object)));
+      return FALSE;
+    }
+
+  g_signal_query (signal_id, &query);
+  if (query.n_params != sig->n_args ||
+      (query.return_type != G_TYPE_NONE && query.return_type != G_TYPE_BOOLEAN) ||
+      !binding_compose_params (object, sig->args, &query, &params))
+    {
+      g_warning ("gtk_binding_entry_activate(): "
+                 "signature mismatch for signal \"%s\" in the '%s' class ancestry",
+                 sig->signal_name,
+                 g_type_name (G_OBJECT_TYPE (object)));
+      return FALSE;
+    }
+  else if (!(query.signal_flags & G_SIGNAL_ACTION))
+    {
+      g_warning ("gtk_binding_entry_activate(): "
+                 "signal \"%s\" in the '%s' class ancestry cannot be used for action emissions",
+                 sig->signal_name,
+                 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);
+
+  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)
+    {
+      guint i;
+
+      for (i = 0; i < query.n_params + 1; i++)
+        g_value_unset (&params[i]);
+
+      g_free (params);
+    }
+
+  return handled;
+}
+
+static gboolean
+binding_signal_activate_action (GtkBindingSignal *sig,
+                                GObject          *object)
+{
+  if (!GTK_IS_WIDGET (object))
+    {
+      g_warning ("gtk_binding_entry_activate(): "
+                 "actions must be emitted on GtkWidget subtypes, %s is not supported",
+                 G_OBJECT_TYPE_NAME (object));
+      return FALSE;
+    }
+
+  if (!gtk_widget_activate_action_variant (GTK_WIDGET (object), sig->signal_name, sig->variant))
+    {
+      g_warning ("gtk_binding_entry_activate(): "
+                 "action \"%s\" does not exist on class \"%s\"",
+                 sig->signal_name,
+                 G_OBJECT_TYPE_NAME (object));
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
 static gboolean
 gtk_binding_entry_activate (GtkBindingEntry *entry,
                             GObject         *object)
@@ -566,7 +701,6 @@ gtk_binding_entry_activate (GtkBindingEntry *entry,
   GtkBindingSignal *sig;
   gboolean old_emission;
   gboolean handled = FALSE;
-  gint i;
 
   old_emission = entry->in_emission;
   entry->in_emission = TRUE;
@@ -575,73 +709,19 @@ gtk_binding_entry_activate (GtkBindingEntry *entry,
 
   for (sig = entry->signals; sig; sig = sig->next)
     {
-      GSignalQuery query;
-      guint signal_id;
-      GValue *params = NULL;
-      GValue return_val = G_VALUE_INIT;
-      gchar *accelerator = NULL;
-
-      signal_id = g_signal_lookup (sig->signal_name, G_OBJECT_TYPE (object));
-      if (!signal_id)
-        {
-          accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers);
-          g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": "
-                     "could not find signal \"%s\" in the '%s' class ancestry",
-                     entry->binding_set->set_name,
-                     accelerator,
-                     sig->signal_name,
-                     g_type_name (G_OBJECT_TYPE (object)));
-          g_free (accelerator);
-          continue;
-        }
-
-      g_signal_query (signal_id, &query);
-      if (query.n_params != sig->n_args ||
-          (query.return_type != G_TYPE_NONE && query.return_type != G_TYPE_BOOLEAN) ||
-          !binding_compose_params (object, sig->args, &query, &params))
-        {
-          accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers);
-          g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": "
-                     "signature mismatch for signal \"%s\" in the '%s' class ancestry",
-                     entry->binding_set->set_name,
-                     accelerator,
-                     sig->signal_name,
-                     g_type_name (G_OBJECT_TYPE (object)));
-        }
-      else if (!(query.signal_flags & G_SIGNAL_ACTION))
-        {
-          accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers);
-          g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": "
-                     "signal \"%s\" in the '%s' class ancestry cannot be used for action emissions",
-                     entry->binding_set->set_name,
-                     accelerator,
-                     sig->signal_name,
-                     g_type_name (G_OBJECT_TYPE (object)));
-        }
-      g_free (accelerator);
-      if (accelerator)
-        continue;
-
-      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)
+      switch (sig->action_type)
         {
-          if (g_value_get_boolean (&return_val))
-            handled = TRUE;
-          g_value_unset (&return_val);
-        }
-      else
-        handled = TRUE;
+        case GTK_BINDING_SIGNAL:
+          handled = binding_signal_activate_signal (sig, object);
+          break;
 
-      if (params != NULL)
-        {
-          for (i = 0; i < query.n_params + 1; i++)
-            g_value_unset (&params[i]);
+        case GTK_BINDING_ACTION:
+          handled = binding_signal_activate_action (sig, object);
+          break;
 
-          g_free (params);
+        default:
+          g_assert_not_reached ();
+          break;
         }
 
       if (entry->destroyed)
@@ -889,7 +969,7 @@ gtk_binding_entry_add_signall (GtkBindingSet  *binding_set,
   keyval = gdk_keyval_to_lower (keyval);
   modifiers = modifiers & BINDING_MOD_MASK ();
 
-  signal = binding_signal_new (signal_name, g_slist_length (binding_args));
+  signal = binding_signal_new_signal (signal_name, g_slist_length (binding_args));
 
   arg = signal->args;
   for (slist = binding_args; slist; slist = slist->next)
@@ -1060,6 +1140,92 @@ gtk_binding_entry_add_signal (GtkBindingSet  *binding_set,
   g_slist_free (free_slist);
 }
 
+/**
+ * gtk_binding_entry_add_action_variant:
+ * @binding_set: a #GtkBindingSet to install an entry for
+ * @keyval:      key value of binding to install
+ * @modifiers:   key modifier of binding to install
+ * @action_name: signal to execute upon activation
+ * @args:        #GVariant of the arguments or %NULL if none
+ *
+ * Override or install a new key binding for @keyval with @modifiers on
+ * @binding_set. When the binding is activated, @action_name will be
+ * activated on the target widget, with @args used as arguments.
+ */
+void
+gtk_binding_entry_add_action_variant (GtkBindingSet  *binding_set,
+                                      guint           keyval,
+                                      GdkModifierType modifiers,
+                                      const gchar    *action_name,
+                                      GVariant       *args)
+{
+  GtkBindingEntry *entry;
+  GtkBindingSignal *signal, **signal_p;
+
+  g_return_if_fail (binding_set != NULL);
+  g_return_if_fail (action_name != NULL);
+
+  keyval = gdk_keyval_to_lower (keyval);
+  modifiers = modifiers & BINDING_MOD_MASK ();
+
+  signal = binding_signal_new_action (action_name, args);
+
+  entry = binding_ht_lookup_entry (binding_set, keyval, modifiers);
+  if (!entry)
+    {
+      gtk_binding_entry_clear_internal (binding_set, keyval, modifiers);
+      entry = binding_ht_lookup_entry (binding_set, keyval, modifiers);
+    }
+  signal_p = &entry->signals;
+  while (*signal_p)
+    signal_p = &(*signal_p)->next;
+  *signal_p = signal;
+}
+
+/**
+ * gtk_binding_entry_add_action:
+ * @binding_set: a #GtkBindingSet to install an entry for
+ * @keyval:      key value of binding to install
+ * @modifiers:   key modifier of binding to install
+ * @action_name: signal to execute upon activation
+ * @format_string: GVariant format string for arguments or %NULL
+ *    for no arguments
+ * @...: arguments, as given by format string
+ *
+ * Override or install a new key binding for @keyval with @modifiers on
+ * @binding_set. When the binding is activated, @action_name will be
+ * activated on the target widget, with arguments read according to
+ * @format_string.
+ */
+void
+gtk_binding_entry_add_action (GtkBindingSet  *binding_set,
+                              guint           keyval,
+                              GdkModifierType modifiers,
+                              const char     *action_name,
+                              const char     *format_string,
+                              ...)
+{
+  GVariant *parameters = NULL;
+
+  g_return_if_fail (binding_set != NULL);
+  g_return_if_fail (action_name != NULL);
+
+  if (format_string != NULL)
+    {
+      va_list args;
+
+      va_start (args, format_string);
+      parameters = g_variant_new_va (format_string, NULL, &args);
+      va_end (args);
+
+      g_variant_ref_sink (parameters);
+    }
+
+  gtk_binding_entry_add_action_variant (binding_set, keyval, modifiers, action_name, parameters);
+
+  g_clear_pointer (&parameters, g_variant_unref);
+}
+
 static guint
 gtk_binding_parse_signal (GScanner       *scanner,
                           GtkBindingSet  *binding_set,
diff --git a/gtk/gtkbindings.h b/gtk/gtkbindings.h
index 887869f981..5f2b019006 100644
--- a/gtk/gtkbindings.h
+++ b/gtk/gtkbindings.h
@@ -76,6 +76,20 @@ GDK_AVAILABLE_IN_ALL
 GTokenType     gtk_binding_entry_add_signal_from_string
                                              (GtkBindingSet       *binding_set,
                                               const gchar         *signal_desc);
+GDK_AVAILABLE_IN_ALL
+void           gtk_binding_entry_add_action_variant
+                                             (GtkBindingSet       *binding_set,
+                                              guint                keyval,
+                                              GdkModifierType      modifiers,
+                                              const char          *action_name,
+                                              GVariant            *args);
+GDK_AVAILABLE_IN_ALL
+void           gtk_binding_entry_add_action  (GtkBindingSet       *binding_set,
+                                              guint                keyval,
+                                              GdkModifierType      modifiers,
+                                              const char          *action_name,
+                                              const char          *format_string,
+                                              ...);
 
 GDK_AVAILABLE_IN_ALL
 void           gtk_binding_entry_remove      (GtkBindingSet       *binding_set,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]