[glade/gbinding] Implement reading/writing of <binding> tags



commit 325683ef0deaa39bfb655071feaa974c80643d88
Author: Denis Washington <denisw src gnome org>
Date:   Fri Jun 3 22:26:28 2011 +0200

    Implement reading/writing of <binding> tags
    
    This is the first in a (hopefully) long line of commits to implement
    support for GObject property binding in Glade. It extends Glade's
    GtkBuilder parsing/writing code to recognize property binding definitions
    (which are codified as <binding> tags), create objects of the new
    GladeBinding class to represent them, and write them back to the GtkBuilder
    file when saving.
    
    What this means is that if you now open a file with <binding> tags and
    save them again later, the binding information is not lost.

 gladeui/Makefile.am            |    4 +-
 gladeui/glade-binding.c        |  348 ++++++++++++++++++++++++++++++++++++++++
 gladeui/glade-binding.h        |   63 +++++++
 gladeui/glade-project.c        |    6 +-
 gladeui/glade-property.c       |   31 ++++-
 gladeui/glade-property.h       |    7 +
 gladeui/glade-widget-adaptor.c |   19 ++-
 gladeui/glade-xml-utils.h      |    3 +
 8 files changed, 476 insertions(+), 5 deletions(-)
---
diff --git a/gladeui/Makefile.am b/gladeui/Makefile.am
index 5860d03..29be1b8 100644
--- a/gladeui/Makefile.am
+++ b/gladeui/Makefile.am
@@ -87,7 +87,9 @@ libgladeui_2_la_SOURCES = \
 	glade-displayable-values.c \
 	glade-signal-model.c \
 	glade-cell-renderer-icon.c \
-	glade-preview.c
+	glade-preview.c \
+	glade-binding.c \
+	glade-binding.h
 
 libgladeui_2_la_CPPFLAGS =  \
 	$(common_defines)   \
diff --git a/gladeui/glade-binding.c b/gladeui/glade-binding.c
new file mode 100644
index 0000000..29a6673
--- /dev/null
+++ b/gladeui/glade-binding.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2011 Denis Washington
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Authors:
+ *   Denis Washington <denisw online de>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include "glade-binding.h"
+#include "glade-property.h"
+#include "glade-project.h"
+
+struct _GladeBindingPrivate {
+
+  GladeProperty *target;     /* A pointer to the the binding's target
+                              * GladeProperty
+                              */
+
+  GladeProperty *source;     /* A pointer to the the binding's source
+                              * GladeProperty
+                              */
+
+};
+
+enum {
+  PROP_0,
+  PROP_SOURCE,
+  PROP_TARGET,
+  N_PROPERTIES
+};
+
+static GParamSpec *properties[N_PROPERTIES];
+
+static void glade_binding_class_init    (GladeBindingClass * klass);
+static void glade_binding_init          (GladeBinding      * binding);
+static void glade_binding_finalize      (GObject           * object);
+
+G_DEFINE_TYPE (GladeBinding, glade_binding, G_TYPE_OBJECT)
+
+/*******************************************************************************
+                      GObjectClass & Object Construction
+ *******************************************************************************/
+static void
+glade_binding_init (GladeBinding * binding)
+{
+  binding->priv = G_TYPE_INSTANCE_GET_PRIVATE (binding,
+                                               GLADE_TYPE_BINDING,
+                                               GladeBindingPrivate);
+
+  binding->priv->source = NULL;
+  binding->priv->target = NULL;
+}
+
+static void
+glade_binding_finalize (GObject * object)
+{
+}
+
+static void
+glade_binding_get_property (GObject * object,
+                            guint prop_id,
+                            GValue * value,
+                            GParamSpec * pspec)
+{
+  GladeBinding *binding = GLADE_BINDING (object);
+
+  switch (prop_id)
+    {
+    case PROP_SOURCE:
+      g_value_set_object (value, binding->priv->source);
+      break;
+    case PROP_TARGET:
+      g_value_set_object (value, binding->priv->target);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+glade_binding_set_property (GObject * object,
+                            guint prop_id,
+                            const GValue * value,
+                            GParamSpec * pspec)
+{
+  GladeBinding *binding = GLADE_BINDING (object);
+
+  switch (prop_id)
+    {
+      case PROP_SOURCE:
+        binding->priv->source = g_value_get_object (value);
+        break;
+      case PROP_TARGET:
+        binding->priv->target = g_value_get_object (value);
+        if (binding->priv->target)
+          glade_property_set_binding (binding->priv->target, binding);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+glade_binding_class_init (GladeBindingClass * klass)
+{
+  GObjectClass *object_class;
+  g_return_if_fail (klass != NULL);
+
+  object_class = G_OBJECT_CLASS (klass);
+
+  /* GObjectClass */
+  object_class->get_property = glade_binding_get_property;
+  object_class->set_property = glade_binding_set_property;
+  object_class->finalize = glade_binding_finalize;
+
+  /* Properties */
+  properties[PROP_TARGET] =
+    g_param_spec_object ("target",
+                         _("Target"),
+                         _("The GladeBinding's target property"),
+                         GLADE_TYPE_PROPERTY,
+                         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
+
+  properties[PROP_SOURCE] =
+    g_param_spec_object ("source",
+                         _("Source"),
+                         _("The GladeBinding's source property"),
+                         GLADE_TYPE_PROPERTY,
+                         G_PARAM_READWRITE);
+
+  g_object_class_install_properties (object_class, N_PROPERTIES, properties);
+
+  /* Private */
+  g_type_class_add_private (klass, sizeof (GladeBindingPrivate));
+}
+
+/*******************************************************************************
+                                     API
+ *******************************************************************************/
+/**
+ * glade_binding_new:
+ * @source: The binding source property
+ * @target: The binding target property
+ *
+ * Create a new GladeBinding with the specified @source and
+ * @target property.
+ *
+ * Returns: The newly created #GladeBinding
+ */
+GladeBinding *
+glade_binding_new (GladeProperty * source,
+                   GladeProperty * target)
+{
+  return g_object_new (GLADE_TYPE_BINDING,
+                       "source", source,
+                       "target", target,
+                       NULL);
+}
+
+/**
+ * glade_binding_get_target:
+ * @binding: The #GladeBinding
+ *
+ * Returns: The binding's target property
+ */
+GladeProperty *
+glade_binding_get_target (GladeBinding * binding)
+{
+  g_return_val_if_fail (GLADE_IS_BINDING (binding), NULL);
+
+  return binding->priv->target;
+}
+
+/**
+ * glade_binding_get_target:
+ * @binding: The #GladeBinding
+ *
+ * Returns: The binding's source property
+ */
+GladeProperty *
+glade_binding_get_source (GladeBinding * binding)
+{
+  g_return_val_if_fail (GLADE_IS_BINDING (binding), NULL);
+
+  return binding->priv->source;
+}
+
+/**
+ * glade_binding_read:
+ * @node: The #GladeXmlNode to read from
+ * @widget: The widget to which the target property belongs
+ *
+ * Read the binding information from @node and create a new
+ * #GladeBinding from it, which is returned.
+ *
+ * Note that binding's source property will only be resolved
+ * after the project is completely loaded by calling
+ * glade_binding_complete().
+ *
+ * Returns: The read #GladeBinding
+ */
+GladeBinding *
+glade_binding_read (GladeXmlNode * node,
+                    GladeWidget  * widget)
+{
+  gchar *to, *from, *source;
+  GladeProperty *target;
+  GladeBinding *binding;
+  
+  g_return_val_if_fail (node != NULL, NULL);
+  g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL);
+
+  if (!glade_xml_node_verify (node, GLADE_XML_TAG_BINDING))
+    return NULL;
+
+  if (!(to = glade_xml_get_property_string_required (node, "to", NULL))
+      || !(from = glade_xml_get_property_string_required (node, "from", NULL))
+      || !(source = glade_xml_get_property_string_required (node, "source", NULL)))
+    return NULL;
+
+  target = glade_widget_get_property (widget, to);
+  binding = glade_binding_new (NULL, target);
+
+  /* We need to wait with resolving the source property until the complete
+   * project is loaded, as the object referred to might not have been read
+   * in yet.
+   */
+  g_object_set_data_full (G_OBJECT (binding),
+                          "glade-source-property-name", from,
+                          g_free);
+  g_object_set_data_full (G_OBJECT (binding),
+                          "glade-source-object-name", source,
+                          g_free);
+
+  return binding;
+}
+
+/**
+ * glade_binding_complete:
+ * @binding: The #GladeBinding
+ * @project: The containing #GladeProject
+ *
+ * Resolves the source property of a #GladeBinding read from
+ * a file with glade_binding_read() by looking it up in the
+ * passed #GladeProject (which must be completely loaded).
+ */
+void
+glade_binding_complete (GladeBinding * binding,
+                        GladeProject * project)
+{
+  gchar *source_obj, *source_prop;
+  GladeWidget *widget;
+  
+  g_return_if_fail (GLADE_IS_BINDING (binding));
+  g_return_if_fail (GLADE_IS_PROJECT (project));
+
+  source_obj = g_object_get_data (G_OBJECT (binding),
+                                  "glade-source-object-name");
+  source_prop = g_object_get_data (G_OBJECT (binding),
+                                   "glade-source-property-name");
+
+  /* If the binding has no attached source property data,
+   * there is nothing to do
+   */
+  if (!source_obj)
+    {
+      g_assert (source_prop != NULL);
+      return;
+    }
+
+  widget = glade_project_get_widget_by_name (project, source_obj);
+  if (widget)
+    {
+      GladeProperty *source = glade_widget_get_property (widget, source_prop);
+      if (source)
+        binding->priv->source = source;
+    }
+}
+
+/**
+ * glade_binding_write:
+ * @binding: a #GladeBinding
+ * @context: A #GladeXmlContext
+ * @node: A #GladeXmlNode
+ *
+ * Write @binding to @node.
+ */
+void
+glade_binding_write (GladeBinding    * binding,
+                     GladeXmlContext * context,
+                     GladeXmlNode    * node)
+{
+  GladeXmlNode *binding_node;
+  GladeProperty *target_prop, *source_prop;
+  const gchar *to, *from, *source;
+  GladeWidget *widget;
+  
+  g_return_if_fail (GLADE_IS_BINDING (binding));
+  g_return_if_fail (glade_binding_get_target (binding) != NULL);
+  g_return_if_fail (glade_binding_get_source (binding) != NULL);
+  g_return_if_fail (node != NULL);
+  
+  if (!(glade_xml_node_verify_silent (node, GLADE_XML_TAG_WIDGET)))
+    return;
+
+  binding_node = glade_xml_node_new (context, GLADE_XML_TAG_BINDING);
+  glade_xml_node_append_child (node, binding_node);
+
+  target_prop = glade_binding_get_target (binding);
+  source_prop = glade_binding_get_source (binding);  
+  to = glade_property_class_id (glade_property_get_class (target_prop));
+  from = glade_property_class_id (glade_property_get_class (source_prop));
+
+  widget = glade_property_get_widget (glade_binding_get_source (binding));
+  source = glade_widget_get_name (widget);
+  
+  glade_xml_node_set_property_string (binding_node,
+                                      GLADE_XML_TAG_TO,
+                                      to);
+  glade_xml_node_set_property_string (binding_node,
+                                      GLADE_XML_TAG_FROM,
+                                      from);
+  glade_xml_node_set_property_string (binding_node,
+                                      GLADE_XML_TAG_SOURCE,
+                                      source);
+}
diff --git a/gladeui/glade-binding.h b/gladeui/glade-binding.h
new file mode 100644
index 0000000..d992bd6
--- /dev/null
+++ b/gladeui/glade-binding.h
@@ -0,0 +1,63 @@
+#ifndef __GLADE_BINDING_H__
+#define __GLADE_BINDING_H__
+
+#include <glib-object.h>
+#include "glade-xml-utils.h"
+
+G_BEGIN_DECLS
+
+#define GLADE_TYPE_BINDING            (glade_binding_get_type())
+#define GLADE_BINDING(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_BINDING, GladeBinding))
+#define GLADE_BINDING_KLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_BINDING, GladeBindingClass))
+#define GLADE_IS_BINDING(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_BINDING))
+#define GLADE_IS_BINDING_KLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_BINDING))
+#define GLADE_BINDING_GET_KLASS(o)    (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_BINDING, GladePropertyKlass))
+
+typedef struct _GladeBinding        GladeBinding;
+typedef struct _GladeBindingClass   GladeBindingClass;
+typedef struct _GladeBindingPrivate GladeBindingPrivate;
+
+/* Forward-declare GladeProperty so that
+ * we don't have to include "glade-property.h",
+ * which depends on this header file
+ */
+typedef struct _GladeProperty       GladeProperty;
+
+struct _GladeBinding
+{
+  GObject              parent_instance;
+
+  GladeBindingPrivate *priv;
+};
+
+
+struct _GladeBindingClass
+{
+  GObjectClass  parent_class;
+};
+
+
+GType                  glade_binding_get_type   (void) G_GNUC_CONST;
+
+GladeBinding          *glade_binding_new        (GladeProperty    * source,
+                                                 GladeProperty    * widget);
+
+GladeProperty         *glade_binding_get_target (GladeBinding     * binding);
+
+GladeProperty         *glade_binding_get_source (GladeBinding     * binding);
+
+GladeBinding          *glade_binding_read       (GladeXmlNode     * node,
+                                                 GladeWidget      * widget);
+
+void                   glade_binding_complete   (GladeBinding     * binding,
+                                                 GladeProject     * project);
+
+void                   glade_binding_write       (GladeBinding    * binding,
+                                                  GladeXmlContext * context,
+                                                  GladeXmlNode    * node);
+
+void                   glade_binding_free       (GladeBinding     * binding);
+
+G_END_DECLS
+
+#endif /* __GLADE_BINDING_H__ */
diff --git a/gladeui/glade-project.c b/gladeui/glade-project.c
index 79605a1..a2e9ed2 100644
--- a/gladeui/glade-project.c
+++ b/gladeui/glade-project.c
@@ -1027,7 +1027,8 @@ glade_project_fix_object_props (GladeProject *project)
   GladeWidget *gwidget;
   GladeProperty *property;
   gchar *txt;
-
+  GladeBinding *binding;
+  
   objects = g_list_copy (project->priv->objects);
   for (l = objects; l; l = l->next)
     {
@@ -1057,6 +1058,9 @@ glade_project_fix_object_props (GladeProject *project)
               g_object_set_data (G_OBJECT (property),
                                  "glade-loaded-object", NULL);
             }
+
+          if ((binding = glade_property_get_binding (property)) != NULL)
+            glade_binding_complete (binding, project);
         }
     }
   g_list_free (objects);
diff --git a/gladeui/glade-property.c b/gladeui/glade-property.c
index 8bd9bc8..99d9f79 100644
--- a/gladeui/glade-property.c
+++ b/gladeui/glade-property.c
@@ -104,11 +104,14 @@ struct _GladePropertyPrivate {
 					* or derived widget code).
 					*/
 
+  /* Non-NULL if the property is the target of a binding */
+  GladeBinding       *binding;
+
   /* Used only for translatable strings. */
   guint     i18n_translatable : 1;
   gchar    *i18n_context;
   gchar    *i18n_comment;
-  
+
   gint      syncing;  /* Avoid recursion while synchronizing object with value */
   gint      sync_tolerance;
 };
@@ -1164,7 +1167,8 @@ glade_property_write (GladeProperty * property,
 {
   GladeXmlNode *prop_node;
   gchar *name, *value, *tmp;
-
+  GladeBinding *binding;
+  
   g_return_if_fail (GLADE_IS_PROPERTY (property));
   g_return_if_fail (node != NULL);
 
@@ -1226,6 +1230,11 @@ glade_property_write (GladeProperty * property,
                                             GLADE_TAG_COMMENT,
                                             property->priv->i18n_comment);
     }
+
+  /* Write property's binding if existent */
+  if ((binding = glade_property_get_binding (property)) != NULL)
+    glade_binding_write (binding, context, node);
+
   g_free (name);
   g_free (value);
 }
@@ -1557,6 +1566,24 @@ glade_property_get_state (GladeProperty      *property)
   return property->priv->state;
 }
 
+GladeBinding *
+glade_property_get_binding (GladeProperty    *property)
+{
+  g_return_val_if_fail (GLADE_IS_PROPERTY (property), NULL);
+  
+  return property->priv->binding;
+}
+
+void
+glade_property_set_binding (GladeProperty    *property,
+                            GladeBinding     *binding)
+{
+  g_return_if_fail (GLADE_IS_PROPERTY (property));
+  g_return_if_fail (GLADE_IS_BINDING (binding));
+  g_return_if_fail (glade_binding_get_target (binding) == property);
+  
+  property->priv->binding = binding;
+}
 
 static gint glade_property_su_stack = 0;
 
diff --git a/gladeui/glade-property.h b/gladeui/glade-property.h
index b69c248..9f4aeb6 100644
--- a/gladeui/glade-property.h
+++ b/gladeui/glade-property.h
@@ -2,6 +2,8 @@
 #define __GLADE_PROPERTY_H__
 
 #include <glib-object.h>
+#include "glade-binding.h"
+#include "glade-property-class.h"
 
 G_BEGIN_DECLS
 
@@ -158,6 +160,11 @@ GValue                 *glade_property_inline_value          (GladeProperty
 
 GladePropertyState      glade_property_get_state             (GladeProperty      *property);
 
+GladeBinding *          glade_property_get_binding           (GladeProperty      *property);
+
+void                    glade_property_set_binding           (GladeProperty      *property,
+                                                              GladeBinding       *binding);
+
 void                    glade_property_i18n_set_comment      (GladeProperty      *property, 
 							      const gchar        *str);
 
diff --git a/gladeui/glade-widget-adaptor.c b/gladeui/glade-widget-adaptor.c
index f3191f8..d2cd4e0 100644
--- a/gladeui/glade-widget-adaptor.c
+++ b/gladeui/glade-widget-adaptor.c
@@ -42,6 +42,7 @@
 #include "glade-displayable-values.h"
 #include "glade-editor-table.h"
 #include "glade-cursor.h"
+#include "glade-binding.h"
 
 /* For g_file_exists */
 #include <sys/types.h>
@@ -970,6 +971,7 @@ glade_widget_adaptor_object_read_widget (GladeWidgetAdaptor * adaptor,
   GladeXmlNode *iter_node;
   GladeSignal *signal;
   GladeProperty *property;
+  GladeBinding *binding;
   gchar *name, *prop_name;
   GList *read_properties = NULL, *l;
 
@@ -1008,6 +1010,17 @@ glade_widget_adaptor_object_read_widget (GladeWidgetAdaptor * adaptor,
     }
   g_list_free (read_properties);
 
+  /* Read in property bindings */
+  for (iter_node = glade_xml_node_get_children (node);
+       iter_node; iter_node = glade_xml_node_next (iter_node))
+    {
+      if (!glade_xml_node_verify_silent (iter_node, GLADE_XML_TAG_BINDING))
+        continue;
+
+      if (!(binding = glade_binding_read (iter_node, widget)))
+        continue;
+    }
+
   /* Read in the signals */
   for (iter_node = glade_xml_node_get_children (node);
        iter_node; iter_node = glade_xml_node_next (iter_node))
@@ -1049,10 +1062,14 @@ glade_widget_adaptor_object_write_widget (GladeWidgetAdaptor * adaptor,
     {
       GladeProperty      *property = props->data;
       GladePropertyClass *klass = glade_property_get_class (property);
-
+      GladeBinding       *binding = glade_property_get_binding (property);
+      
       if (glade_property_class_save (klass) && 
 	  glade_property_get_enabled (property))
         glade_property_write (GLADE_PROPERTY (props->data), context, node);
+
+      if (binding)
+        glade_binding_write (binding, context, node);
     }
 }
 
diff --git a/gladeui/glade-xml-utils.h b/gladeui/glade-xml-utils.h
index c07da74..602d616 100644
--- a/gladeui/glade-xml-utils.h
+++ b/gladeui/glade-xml-utils.h
@@ -43,6 +43,9 @@ typedef struct _GladeProject        GladeProject;
 #define GLADE_XML_TAG_PROPERTY                    "property"
 #define GLADE_XML_TAG_CLASS                       "class"
 #define GLADE_XML_TAG_ID                          "id"
+#define GLADE_XML_TAG_BINDING                     "binding"
+#define GLADE_XML_TAG_TO                          "to"
+#define GLADE_XML_TAG_FROM                        "from"
 #define GLADE_XML_TAG_SIGNAL                      "signal"
 #define GLADE_XML_TAG_HANDLER                     "handler"
 #define GLADE_XML_TAG_AFTER                       "after"



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