diff --git a/demos/gtk-demo/listview_clocks.c b/demos/gtk-demo/listview_clocks.c index 3254428d7b..95d45dadd7 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_box_append (GTK_BOX (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_box_append (GTK_BOX (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_box_append (GTK_BOX (box), time_label); gtk_expression_unref (clock_expression); diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c index 6121d2ed8e..385b78db65 100644 --- a/gtk/gtkbuilder.c +++ b/gtk/gtkbuilder.c @@ -1064,11 +1064,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); } @@ -1076,17 +1078,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) { - g_prefix_error (error, "%s:%d:%d: ", priv->filename, info->line, info->col); - error = NULL; - result = FALSE; + 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) + { + 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 d6e4b87e95..5014b161f4 100644 --- a/gtk/gtkbuilderparser.c +++ b/gtk/gtkbuilderparser.c @@ -954,7 +954,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; @@ -969,6 +970,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); @@ -1013,6 +1015,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); @@ -1538,6 +1541,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 506a082887..1bee1bda81 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 c698f8038e..0689b0a504 100644 --- a/gtk/gtkexpression.c +++ b/gtk/gtkexpression.c @@ -1287,8 +1287,7 @@ gtk_expression_watch_evaluate (GtkExpressionWatch *watch, typedef struct { GtkExpressionWatch *watch; - GtkExpression *expression; - GObject *object; + GObject *target; GParamSpec *pspec; } GtkExpressionBind; @@ -1303,7 +1302,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; } } @@ -1316,8 +1320,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); } @@ -1327,19 +1333,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 @@ -1348,29 +1365,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. * @@ -1381,44 +1400,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 0727580c45..9705dae18f 100644 --- a/gtk/gtkexpression.h +++ b/gtk/gtkexpression.h @@ -65,8 +65,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);