[gtk/wip/otte/listview: 1028/1040] expression: Allow passing a this object to bind()



commit 1dc089ff227aaf8be4cce1da8a33da23d6e6b642
Author: Benjamin Otte <otte redhat com>
Date:   Thu Nov 28 02:32:12 2019 +0100

    expression: Allow passing a this object to bind()
    
    This gives a bit more control over the arguments passed to expressions.

 demos/gtk-demo/listview_clocks.c |  6 +--
 gtk/gtkbuilder.c                 | 38 ++++++++++++++----
 gtk/gtkbuilderparser.c           |  6 ++-
 gtk/gtkbuilderprivate.h          |  1 +
 gtk/gtkexpression.c              | 83 ++++++++++++++++++++++++----------------
 gtk/gtkexpression.h              |  5 ++-
 6 files changed, 94 insertions(+), 45 deletions(-)
---
diff --git a/demos/gtk-demo/listview_clocks.c b/demos/gtk-demo/listview_clocks.c
index b463839051..ae0ad02538 100644
--- a/demos/gtk-demo/listview_clocks.c
+++ b/demos/gtk-demo/listview_clocks.c
@@ -401,7 +401,7 @@ setup_listitem_cb (GtkListItemFactory *factory,
                                             "location");
   /* Now create the label and bind the expression to it. */
   location_label = gtk_label_new (NULL);
-  gtk_expression_bind (expression, location_label, "label");
+  gtk_expression_bind (expression, location_label, "label", location_label);
   gtk_container_add (GTK_CONTAINER (box), location_label);
 
 
@@ -411,7 +411,7 @@ setup_listitem_cb (GtkListItemFactory *factory,
   expression = gtk_expression_ref (clock_expression);
   /* Now create the widget and bind the expression to it. */
   picture = gtk_picture_new ();
-  gtk_expression_bind (expression, picture, "paintable");
+  gtk_expression_bind (expression, picture, "paintable", picture);
   gtk_container_add (GTK_CONTAINER (box), picture);
 
 
@@ -430,7 +430,7 @@ setup_listitem_cb (GtkListItemFactory *factory,
                                             NULL, NULL);
   /* Now create the label and bind the expression to it. */
   time_label = gtk_label_new (NULL);
-  gtk_expression_bind (expression, time_label, "label");
+  gtk_expression_bind (expression, time_label, "label", time_label);
   gtk_container_add (GTK_CONTAINER (box), time_label);
 
   gtk_expression_unref (clock_expression);
diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c
index 149d124c85..9c6d81938a 100644
--- a/gtk/gtkbuilder.c
+++ b/gtk/gtkbuilder.c
@@ -1055,11 +1055,13 @@ gtk_builder_create_bindings (GtkBuilder  *builder,
           BindingInfo *info = l->data;
           GObject *source;
 
-          source = _gtk_builder_lookup_object (builder, info->source, info->line, info->col);
+          source = gtk_builder_lookup_object (builder, info->source, info->line, info->col, error);
           if (source)
             g_object_bind_property (source, info->source_property,
                                     info->target, info->target_pspec->name,
                                     info->flags);
+          else
+            error = NULL;
 
           _free_binding_info (info, NULL);
         }
@@ -1067,17 +1069,39 @@ gtk_builder_create_bindings (GtkBuilder  *builder,
         {
           BindingExpressionInfo *info = l->data;
           GtkExpression *expression;
+          GObject *object;
 
-          expression = expression_info_construct (builder, info->expr, error);
-          if (expression == NULL)
+          if (info->object_name)
+            {
+              object = gtk_builder_lookup_object (builder, info->object_name, info->line, info->col, error);
+              if (object == NULL)
+                {
+                  error = NULL;
+                  result = FALSE;
+                }
+            }
+          else if (priv->current_object)
             {
-              g_prefix_error (error, "%s:%d:%d: ", priv->filename, info->line, info->col);
-              error = NULL;
-              result = FALSE;
+              object = priv->current_object;
             }
           else
             {
-              gtk_expression_bind (expression, info->target, info->target_pspec->name);
+              object = info->target;
+            }
+
+          if (object)
+            {
+              expression = expression_info_construct (builder, info->expr, error);
+              if (expression == NULL)
+                {
+                  g_prefix_error (error, "%s:%d:%d: ", priv->filename, info->line, info->col);
+                  error = NULL;
+                  result = FALSE;
+                }
+              else
+                {
+                  gtk_expression_bind (expression, info->target, info->target_pspec->name, object);
+                }
             }
 
           free_binding_expression_info (info);
diff --git a/gtk/gtkbuilderparser.c b/gtk/gtkbuilderparser.c
index 959609d5d4..77ebdd0925 100644
--- a/gtk/gtkbuilderparser.c
+++ b/gtk/gtkbuilderparser.c
@@ -955,7 +955,8 @@ parse_binding (ParserData   *data,
                GError      **error)
 {
   BindingExpressionInfo *info;
-  const gchar *name = NULL;
+  const char *name = NULL;
+  const char *object_name = NULL;
   ObjectInfo *object_info;
   GParamSpec *pspec = NULL;
 
@@ -970,6 +971,7 @@ parse_binding (ParserData   *data,
 
   if (!g_markup_collect_attributes (element_name, names, values, error,
                                     G_MARKUP_COLLECT_STRING, "name", &name,
+                                    G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "object", 
&object_name,
                                     G_MARKUP_COLLECT_INVALID))
     {
       _gtk_builder_prefix_error (data->builder, &data->ctx, error);
@@ -1014,6 +1016,7 @@ parse_binding (ParserData   *data,
   info->tag_type = TAG_BINDING_EXPRESSION;
   info->target = NULL;
   info->target_pspec = pspec;
+  info->object_name = g_strdup (object_name);
   gtk_buildable_parse_context_get_position (&data->ctx, &info->line, &info->col);
 
   state_push (data, info);
@@ -1507,6 +1510,7 @@ free_binding_expression_info (BindingExpressionInfo *info)
 {
   if (info->expr)
     free_expression_info (info->expr);
+  g_free (info->object_name);
   g_slice_free (BindingExpressionInfo, info);
 }
 
diff --git a/gtk/gtkbuilderprivate.h b/gtk/gtkbuilderprivate.h
index a5e1f22861..8e690e0c88 100644
--- a/gtk/gtkbuilderprivate.h
+++ b/gtk/gtkbuilderprivate.h
@@ -133,6 +133,7 @@ typedef struct
   guint tag_type;
   GObject *target;
   GParamSpec *target_pspec;
+  char *object_name;
   ExpressionInfo *expr;
   gint line;
   gint col;
diff --git a/gtk/gtkexpression.c b/gtk/gtkexpression.c
index 177b91ac24..1cd2d98ef9 100644
--- a/gtk/gtkexpression.c
+++ b/gtk/gtkexpression.c
@@ -1262,8 +1262,7 @@ gtk_expression_watch_evaluate (GtkExpressionWatch *watch,
 
 typedef struct {
   GtkExpressionWatch *watch;
-  GtkExpression *expression;
-  GObject *object;
+  GObject *target;
   GParamSpec *pspec;
 } GtkExpressionBind;
 
@@ -1278,7 +1277,12 @@ invalidate_binds (gpointer unused,
     {
       GtkExpressionBind *bind = l->data;
 
-      bind->object = NULL;
+      /* This guarantees we neither try to update bindings
+       * (which would wreck havoc because the object is
+       * dispose()ing itself) nor try to destroy bindings
+       * anymore, so destruction can be done in free_binds().
+       */
+      bind->target = NULL;
     }
 }
 
@@ -1291,8 +1295,10 @@ free_binds (gpointer data)
     {
       GtkExpressionBind *bind = l->data;
 
-      bind->object = NULL;
-      gtk_expression_watch_unwatch (bind->watch);
+      g_assert (bind->target == NULL);
+      if (bind->watch)
+        gtk_expression_watch_unwatch (bind->watch);
+      g_slice_free (GtkExpressionBind, bind);
     }
   g_slist_free (data);
 }
@@ -1302,19 +1308,30 @@ gtk_expression_bind_free (gpointer data)
 {
   GtkExpressionBind *bind = data;
 
-  if (bind->object)
+  if (bind->target)
     {
       GSList *binds;
-      binds = g_object_steal_data (bind->object, "gtk-expression-binds");
+      binds = g_object_steal_data (bind->target, "gtk-expression-binds");
       binds = g_slist_remove (binds, bind);
       if (binds)
-        g_object_set_data_full (bind->object, "gtk-expression-binds", binds, free_binds);
+        g_object_set_data_full (bind->target, "gtk-expression-binds", binds, free_binds);
       else
-        g_object_weak_unref (bind->object, invalidate_binds, NULL);
-    }
-  gtk_expression_unref (bind->expression);
+        g_object_weak_unref (bind->target, invalidate_binds, NULL);
 
-  g_slice_free (GtkExpressionBind, bind);
+      g_slice_free (GtkExpressionBind, bind);
+    }
+  else
+    {
+      /* If a bind gets unwatched after invalidate_binds() but
+       * before free_binds(), we end up here. This can happen if
+       * the bind was watching itself or if the target's dispose()
+       * function freed the object that was watched.
+       * We make sure we don't destroy the binding or free_binds() will do
+       * bad stuff, but we clear the watch, so free_binds() won't try to
+       * unwatch() it.
+       */
+      bind->watch = NULL;
+    }
 }
 
 static void
@@ -1323,29 +1340,31 @@ gtk_expression_bind_notify (gpointer data)
   GValue value = G_VALUE_INIT;
   GtkExpressionBind *bind = data;
 
-  if (bind->object == NULL)
+  if (bind->target == NULL)
     return;
 
-  if (!gtk_expression_evaluate (bind->expression, bind->object, &value))
+  if (!gtk_expression_watch_evaluate (bind->watch, &value))
     return;
 
-  g_object_set_property (bind->object, bind->pspec->name, &value);
+  g_object_set_property (bind->target, bind->pspec->name, &value);
   g_value_unset (&value);
 }
 
 /**
  * gtk_expression_bind:
  * @self: (transfer full): a #GtkExpression
- * @object: (transfer none) (type GObject): the object to bind
- * @property: name of the property to bind to
+ * @target: (transfer none) (type GObject): the target object to bind to
+ * @property: name of the property on @target to bind to
+ * @this_: (transfer none) (type GObject): the this argument for
+ *     the evaluation of @self
  *
- * Bind @object's property named @property to @self.
+ * Bind @target's property named @property to @self.
  *
  * The value that @self evaluates to is set via g_object_set() on
- * @object. This is repeated whenever @self changes to ensure that
+ * @target. This is repeated whenever @self changes to ensure that
  * the object's property stays synchronized with @self.
  *
- * If @self's evaluation fails, @object's @property is not updated.
+ * If @self's evaluation fails, @target's @property is not updated.
  * You can ensure that this doesn't happen by using a fallback
  * expression.
  *
@@ -1356,44 +1375,44 @@ gtk_expression_bind_notify (gpointer data)
  **/
 GtkExpressionWatch *
 gtk_expression_bind (GtkExpression *self,
-                     gpointer       object,
-                     const char    *property)
+                     gpointer       target,
+                     const char    *property,
+                     gpointer       this_)
 {
   GtkExpressionBind *bind;
   GParamSpec *pspec;
   GSList *binds;
 
   g_return_val_if_fail (GTK_IS_EXPRESSION (self), NULL);
-  g_return_val_if_fail (G_IS_OBJECT (object), NULL);
+  g_return_val_if_fail (G_IS_OBJECT (target), NULL);
   g_return_val_if_fail (property != NULL, NULL);
-  pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), property);
+  pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (target), property);
   if (G_UNLIKELY (pspec == NULL))
     {
       g_critical ("%s: Class '%s' has no property named '%s'",
-                  G_STRFUNC, G_OBJECT_TYPE_NAME (object), property);
+                  G_STRFUNC, G_OBJECT_TYPE_NAME (target), property);
       return NULL;
     }
   if (G_UNLIKELY ((pspec->flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) != G_PARAM_WRITABLE))
     {
       g_critical ("%s: property '%s' of class '%s' is not writable",
-                 G_STRFUNC, pspec->name, G_OBJECT_TYPE_NAME (object));
+                 G_STRFUNC, pspec->name, G_OBJECT_TYPE_NAME (target));
       return NULL;
     }
 
   bind = g_slice_new0 (GtkExpressionBind);
-  binds = g_object_steal_data (object, "gtk-expression-binds");
+  binds = g_object_steal_data (target, "gtk-expression-binds");
   if (binds == NULL)
-    g_object_weak_ref (object, invalidate_binds, NULL);
-  bind->expression = self;
-  bind->object = object;
+    g_object_weak_ref (target, invalidate_binds, NULL);
+  bind->target = target;
   bind->pspec = pspec;
   bind->watch = gtk_expression_watch (self,
-                                      object,
+                                      this_,
                                       gtk_expression_bind_notify,
                                       bind,
                                       gtk_expression_bind_free);
   binds = g_slist_prepend (binds, bind);
-  g_object_set_data_full (object, "gtk-expression-binds", binds, free_binds);
+  g_object_set_data_full (target, "gtk-expression-binds", binds, free_binds);
 
   gtk_expression_unref (self);
 
diff --git a/gtk/gtkexpression.h b/gtk/gtkexpression.h
index bac7762561..6b642a30fe 100644
--- a/gtk/gtkexpression.h
+++ b/gtk/gtkexpression.h
@@ -58,8 +58,9 @@ GtkExpressionWatch *    gtk_expression_watch                    (GtkExpression
                                                                  GDestroyNotify                  
user_destroy);
 GDK_AVAILABLE_IN_ALL
 GtkExpressionWatch *    gtk_expression_bind                     (GtkExpression                  *self,
-                                                                 gpointer                        object,
-                                                                 const char *                    property);
+                                                                 gpointer                        target,
+                                                                 const char *                    property,
+                                                                 gpointer                        this_);
 
 GDK_AVAILABLE_IN_ALL
 GtkExpressionWatch *    gtk_expression_watch_ref                (GtkExpressionWatch             *watch);


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