[gtk+/composite-templates: 1/3] Added gtk_builder_expose_object(), gtk_builder_add_to_parent_from_file() and gtk_builder_add_to_pare



commit c1760edb3dfdede9bc4cd2ab7dc75c0d277cf82c
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date:   Mon Jun 7 14:30:28 2010 -0400

    Added gtk_builder_expose_object(), gtk_builder_add_to_parent_from_file() and gtk_builder_add_to_parent_from_string().
    
    Adding the needed GtkBuilder framework for composite containers. First the
    expose_object() api is based on the work Marco Diego AurÃlio Mesquita did
    in bug 447972 with some bugs fixed and things cleared up. Next the
    gtk_builder_add_to_parent_from_[file/string]() variants are used to extend
    containers inline by adding themselves and letting the builder add the
    children defined in a template file.
    
    Added semantics to build composite children automatically from an assigned template.
    
    This patch adds gtk_container_class_set_template[_file](),
    gtk_container_class_set_connect_func() and the GtkParamSpecComposite
    type. Setting the class template will result in GtkContainer building
    the instance's children from the template at construct time, composite
    properties will be assigned to their composite children by matching
    the property names with the child GtkBuildable ids. The exposed connect
    function is there to allow language bindings to automatically assign
    methods for the interface callbacks when they assign templates to classes.
    
    Made GtkDialog and GtkMessageDialog composite widgets using gtk_container_class_set_template().
    
    Enhanced documentation for gtk_container_class_set_template()
    
    Fixing dialog separator property to be construct-only
    
    Implemented gtk_container_buildable_get_internal_child() and removed GSEAL macros
    
    Added missing parameter to _gtk_builder_parser_parse_buffer() calls
    
    Fixed composite implementation
    
    Added gtk_builder_add_to_parent_from_resource()
    Used gtk_builder_add_to_parent_from_* as gtk_builder_add_from_* implementation.
    
    Removed GtkParamSpecComposite
    Added GtkContainerClassPrivate
    Added gtk_container_class_declare_internal_child() funtion to make internal children declaration explicit
    Added GtkContainerTemplateType type parameter to gtk_container_class_set_template()
    
    Updated GtkDialog and GtkMessageDialog implementation to new GtkContainer template API
    
    Reworked GtkDialog and GtkMessageDialog xml template into a GResource
    
    Fixed property setting and signal connection for parent objects (ie: gtk_builder_add_to_parent_*())
    Use intern strings for properties and signal names and a GType instead of a class name in GtkBuilder
    Added new private function _gtk_builder_object_get_name()
    
    Removed gtk_container_get_composite_child()
    Get a reference to every internal object declared with gtk_container_class_declare_internal_child() which gets dropped on GtkContainer::destroy signal
    Implemented "template" tag, this allow us to also build anarchist objects
    
    Replaced GtkDialog and GtkMessageDialog template file extension with .ui
    
    Implemented id tag for template and added cheack for id to be 'this'
    
    Cleaned up template API, we only provided a function to set a resource as a template
    
    Fixed identation

 gtk/Makefile.am         |    3 +-
 gtk/gtkbuilder.c        |  289 ++++++++++++++++++++++++++++++++++++-----------
 gtk/gtkbuilder.h        |   38 +++++--
 gtk/gtkbuilderparser.c  |  219 +++++++++++++++++++++++++++++++-----
 gtk/gtkbuilderprivate.h |   13 ++-
 5 files changed, 453 insertions(+), 109 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 397fa4f..b335c0f 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -1112,7 +1112,8 @@ gtktypebuiltins.c: @REBUILD@ $(gtk_public_h_sources) $(deprecated_h_sources) gtk
 gtkresources.h: gtk.gresource.xml
 	$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) $(srcdir)/gtk.gresource.xml \
 		--target=$@ --sourcedir=$(srcdir) --c-name _gtk --generate-header --manual-register
-gtkresources.c: gtk.gresource.xml gtk-default.css gtk-win32.css gtk-win32-xp.css gtk-win32-base.css gtk-win32-classic.css $(DND_CURSORS)
+gtkresources.c: gtk.gresource.xml \
+         $(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies gtk.gresource.xml)
 	$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) $(srcdir)/gtk.gresource.xml \
 		--target=$@ --sourcedir=$(srcdir) --c-name _gtk --generate-source --manual-register
 
diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c
index bf75922..f306d75 100644
--- a/gtk/gtkbuilder.c
+++ b/gtk/gtkbuilder.c
@@ -123,22 +123,27 @@
  * (can be specified by their name, nick or integer value), flags (can be
  * specified by their name, nick, integer value, optionally combined with "|",
  * e.g. "GTK_VISIBLE|GTK_REALIZED")  and colors (in a format understood by
- * gdk_color_parse()). Objects can be referred to by their name. Pixbufs can be
- * specified as a filename of an image file to load. In general, GtkBuilder
- * allows forward references to objects &mdash; an object doesn't have to be
- * constructed before it can be referred to. The exception to this rule is that
- * an object has to be constructed before it can be used as the value of a
- * construct-only property.
+ * gdk_color_parse()). Pixbufs can be specified as a filename of an image file to load. 
+ * Objects can be referred to by their name and by default refer to objects declared
+ * in the local xml fragment, however external objects exposed via gtk_builder_expose_object()
+ * can be referred to by specifying the "external-object" attribute.
+ * 
+ * In general, GtkBuilder allows forward references to objects &mdash declared
+ * in the local xml; an object doesn't have to be constructed before it can be referred to. 
+ * The exception to this rule is that an object has to be constructed before 
+ * it can be used as the value of a construct-only property.
  *
  * Signal handlers are set up with the &lt;signal&gt; element. The "name"
  * attribute specifies the name of the signal, and the "handler" attribute
  * specifies the function to connect to the signal. By default, GTK+ tries to
  * find the handler using g_module_symbol(), but this can be changed by passing
  * a custom #GtkBuilderConnectFunc to gtk_builder_connect_signals_full(). The
- * remaining attributes, "after", "swapped" and "object", have the same meaning
+ * attributes "after", "swapped" and "object", have the same meaning
  * as the corresponding parameters of the g_signal_connect_object() or
- * g_signal_connect_data() functions. A "last_modification_time" attribute
- * is also allowed, but it does not have a meaning to the builder.
+ * g_signal_connect_data() functions.  Extenral objects can also be referred 
+ * to by specifying the "external-object" attribute in the same way as described 
+ * with the &lt;property&gt; element. A "last_modification_time" attribute is also 
+ * allowed, but it does not have a meaning to the builder.
  *
  * Sometimes it is necessary to refer to widgets which have implicitly been
  * constructed by GTK+ as part of a composite widget, to set properties on them
@@ -268,6 +273,7 @@ struct _GtkBuilderPrivate
 {
   gchar *domain;
   GHashTable *objects;
+  GHashTable *external_objects;
   GSList *delayed_properties;
   GSList *signals;
   gchar *filename;
@@ -318,6 +324,7 @@ gtk_builder_init (GtkBuilder *builder)
   builder->priv->domain = NULL;
   builder->priv->objects = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                   g_free, g_object_unref);
+  builder->priv->external_objects = NULL;
 }
 
 
@@ -335,6 +342,8 @@ gtk_builder_finalize (GObject *object)
   g_free (priv->resource_prefix);
   
   g_hash_table_destroy (priv->objects);
+  if (priv->external_objects)
+    g_hash_table_destroy (priv->external_objects);
 
   g_slist_foreach (priv->signals, (GFunc) _free_signal_info, NULL);
   g_slist_free (priv->signals);
@@ -464,7 +473,7 @@ gtk_builder_get_parameters (GtkBuilder  *builder,
   GObjectClass *oclass;
   DelayedProperty *property;
   GError *error = NULL;
-  
+
   oclass = g_type_class_ref (object_type);
   g_assert (oclass != NULL);
 
@@ -490,22 +499,39 @@ gtk_builder_get_parameters (GtkBuilder  *builder,
       if (G_IS_PARAM_SPEC_OBJECT (pspec) &&
           (G_PARAM_SPEC_VALUE_TYPE (pspec) != GDK_TYPE_PIXBUF))
         {
-          GObject *object = gtk_builder_get_object (builder, prop->data);
+          GObject *object;
 
-          if (object)
+          if (prop->external)
             {
+              object = g_hash_table_lookup (builder->priv->external_objects, prop->data);
+
+              if (!object)
+                {
+                  g_warning ("Failed to get external object property "
+                             "%s of %s with value `%s'",
+                             prop->name, object_name, prop->data);
+                  continue;
+                }
+
               g_value_init (&parameter.value, G_OBJECT_TYPE (object));
               g_value_set_object (&parameter.value, object);
             }
-          else 
+          else if (pspec->flags & G_PARAM_CONSTRUCT_ONLY)
             {
-              if (pspec->flags & G_PARAM_CONSTRUCT_ONLY)
+              object = gtk_builder_get_object (builder, prop->data);
+
+              if (!object)
                 {
                   g_warning ("Failed to get constuct only property "
                              "%s of %s with value `%s'",
                              prop->name, object_name, prop->data);
                   continue;
                 }
+              g_value_init (&parameter.value, G_OBJECT_TYPE (object));
+              g_value_set_object (&parameter.value, object);
+            }
+          else
+            {
               /* Delay setting property */
               property = g_slice_new (DelayedProperty);
               property->object = g_strdup (object_name);
@@ -517,13 +543,13 @@ gtk_builder_get_parameters (GtkBuilder  *builder,
             }
         }
       else if (!gtk_builder_value_from_string (builder, pspec,
-					       prop->data, &parameter.value, &error))
+                                               prop->data, &parameter.value, &error))
         {
           g_warning ("Failed to set property %s.%s to %s: %s",
                      g_type_name (object_type), prop->name, prop->data,
-		     error->message);
-	  g_error_free (error);
-	  error = NULL;
+                     error->message);
+          g_error_free (error);
+          error = NULL;
           continue;
         }
 
@@ -540,7 +566,7 @@ static GObject *
 gtk_builder_get_internal_child (GtkBuilder  *builder,
                                 ObjectInfo  *info,
                                 const gchar *childname,
-				GError      **error)
+                                GError      **error)
 {
   GObject *obj = NULL;
 
@@ -562,7 +588,7 @@ gtk_builder_get_internal_child (GtkBuilder  *builder,
           obj = gtk_buildable_get_internal_child (GTK_BUILDABLE (info->object),
                                                   builder,
                                                   childname);
-    };
+    }
 
   if (!obj)
     {
@@ -574,6 +600,33 @@ gtk_builder_get_internal_child (GtkBuilder  *builder,
   return obj;
 }
 
+static inline void
+object_set_name (GObject *object, const gchar *name)
+{
+  if (GTK_IS_BUILDABLE (object))
+    gtk_buildable_set_name (GTK_BUILDABLE (object), name);
+  else
+    g_object_set_data_full (object, "gtk-builder-name", g_strdup (name), g_free);
+}
+
+void
+_gtk_builder_add_object (GtkBuilder  *builder,
+                         const gchar *id,
+                         GObject     *object)
+{
+  object_set_name (object, id);
+  g_hash_table_insert (builder->priv->objects, g_strdup (id), g_object_ref (object));
+}
+
+const gchar *
+_gtk_builder_object_get_name (GObject *object)
+{
+  if (GTK_IS_BUILDABLE (object))
+    return gtk_buildable_get_name (GTK_BUILDABLE (object));
+  else
+    return g_object_get_data (object, "gtk-builder-name");
+}
+
 GObject *
 _gtk_builder_construct (GtkBuilder *builder,
                         ObjectInfo *info,
@@ -587,25 +640,28 @@ _gtk_builder_construct (GtkBuilder *builder,
   gboolean custom_set_property;
   GtkBuildable *buildable;
 
-  g_assert (info->class_name != NULL);
-  object_type = gtk_builder_get_type_from_name (builder, info->class_name);
-  if (object_type == G_TYPE_INVALID)
+  if ((object_type = info->object_type) == G_TYPE_INVALID)
     {
       g_set_error (error,
 		   GTK_BUILDER_ERROR,
 		   GTK_BUILDER_ERROR_INVALID_VALUE,
 		   "Invalid object type `%s'",
-		   info->class_name);
+		   g_type_name (object_type));
       return NULL;
     }
 
-  gtk_builder_get_parameters (builder, object_type,
+  gtk_builder_get_parameters (builder, info->object_type,
                               info->id,
                               info->properties,
                               &parameters,
                               &construct_parameters);
 
-  if (info->constructor)
+  if (info->object)
+    {
+      /* template_object */
+      obj = g_object_ref (info->object);
+    }
+  else if (info->constructor)
     {
       GObject *constructor;
 
@@ -662,7 +718,7 @@ _gtk_builder_construct (GtkBuilder *builder,
         g_object_ref_sink (obj);
 
       GTK_NOTE (BUILDER,
-                g_print ("created %s of type %s\n", info->id, info->class_name));
+                g_print ("created %s of type %s\n", info->id, g_type_name (object_type)));
 
       for (i = 0; i < construct_parameters->len; i++)
         {
@@ -703,30 +759,17 @@ _gtk_builder_construct (GtkBuilder *builder,
       g_value_unset (&param->value);
     }
   g_array_free (parameters, TRUE);
-  
-  if (GTK_IS_BUILDABLE (obj))
-    gtk_buildable_set_name (buildable, info->id);
-  else
-    g_object_set_data_full (obj,
-                            "gtk-builder-name",
-                            g_strdup (info->id),
-                            g_free);
 
-  /* we already own a reference to obj.  put it in the hash table. */
-  g_hash_table_insert (builder->priv->objects, g_strdup (info->id), obj);
+  /* put it in the hash table. */
+  _gtk_builder_add_object (builder, info->id, obj);
+  
+  /* we already own a reference to obj. */ 
+  g_object_unref (obj);
   
   return obj;
 }
 
 void
-_gtk_builder_add_object (GtkBuilder  *builder,
-                         const gchar *id,
-                         GObject     *object)
-{
-  g_hash_table_insert (builder->priv->objects, g_strdup (id), g_object_ref (object));
-}
-
-void
 _gtk_builder_add (GtkBuilder *builder,
                   ChildInfo  *child_info)
 {
@@ -873,6 +916,31 @@ gtk_builder_add_from_file (GtkBuilder   *builder,
                            const gchar  *filename,
                            GError      **error)
 {
+  return gtk_builder_add_to_parent_from_file (builder, NULL, filename, error);
+}
+
+/**
+ * gtk_builder_add_to_parent_from_file:
+ * @builder: a #GtkBuilder
+ * @parent: the parent object to be assumed in context while parsing the file
+ * @filename: the name of the file to parse
+ * @error: (allow-none): return location for an error, or %NULL
+ *
+ * Like gtk_builder_add_from_file() except the format will expect
+ * <child> instead of <object> as its first elements and expose
+ * @parent in the build context, children defined in the UI fragment
+ * will be added to @parent.
+ * 
+ * Returns: A positive value on success, 0 if an error occurred
+ *
+ * Since: ...
+ **/
+guint
+gtk_builder_add_to_parent_from_file (GtkBuilder   *builder,
+                                     GObject      *parent,
+                                     const gchar  *filename,
+                                     GError      **error)
+{
   gchar *buffer;
   gsize length;
   GError *tmp_error;
@@ -894,7 +962,7 @@ gtk_builder_add_from_file (GtkBuilder   *builder,
   builder->priv->filename = g_strdup (filename);
   builder->priv->resource_prefix = NULL;
 
-  _gtk_builder_parser_parse_buffer (builder, filename,
+  _gtk_builder_parser_parse_buffer (builder, parent, filename,
                                     buffer, length,
                                     NULL,
                                     &tmp_error);
@@ -963,7 +1031,7 @@ gtk_builder_add_objects_from_file (GtkBuilder   *builder,
   builder->priv->filename = g_strdup (filename);
   builder->priv->resource_prefix = NULL;
 
-  _gtk_builder_parser_parse_buffer (builder, filename,
+  _gtk_builder_parser_parse_buffer (builder, NULL, filename,
                                     buffer, length,
                                     object_ids,
                                     &tmp_error);
@@ -1001,18 +1069,43 @@ gtk_builder_add_from_resource (GtkBuilder   *builder,
 			       const gchar  *resource_path,
 			       GError      **error)
 {
+  return gtk_builder_add_to_parent_from_resource (builder, NULL, resource_path, error);
+}
+
+/**
+ * gtk_builder_add_to_parent_from_resource:
+ * @builder: a #GtkBuilder
+ * @parent: the parent object to be assumed in context while parsing the file
+ * @path: the resource path to parse
+ * @error: (allow-none): return location for an error, or %NULL
+ *
+ * Like gtk_builder_add_from_file() except the format will expect
+ * <child> instead of <object> as its first elements and expose
+ * @parent in the build context, children defined in the UI fragment
+ * will be added to @parent.
+ * 
+ * Returns: A positive value on success, 0 if an error occurred
+ *
+ * Since: ...
+ **/
+guint
+gtk_builder_add_to_parent_from_resource (GtkBuilder   *builder,
+                                         GObject      *parent,
+                                         const gchar  *path,
+                                         GError      **error)
+{
   GError *tmp_error;
   GBytes *data;
   char *filename_for_errors;
   char *slash;
 
   g_return_val_if_fail (GTK_IS_BUILDER (builder), 0);
-  g_return_val_if_fail (resource_path != NULL, 0);
+  g_return_val_if_fail (path != NULL, 0);
   g_return_val_if_fail (error == NULL || *error == NULL, 0);
 
   tmp_error = NULL;
 
-  data = g_resources_lookup_data (resource_path, 0, &tmp_error);
+  data = g_resources_lookup_data (path, 0, &tmp_error);
   if (data == NULL)
     {
       g_propagate_error (error, tmp_error);
@@ -1023,17 +1116,15 @@ gtk_builder_add_from_resource (GtkBuilder   *builder,
   g_free (builder->priv->resource_prefix);
   builder->priv->filename = g_strdup (".");
 
-  slash = strrchr (resource_path, '/');
+  slash = strrchr (path, '/');
   if (slash != NULL)
-    builder->priv->resource_prefix =
-      g_strndup (resource_path, slash - resource_path + 1);
+    builder->priv->resource_prefix = g_strndup (path, slash - path + 1);
   else
-    builder->priv->resource_prefix =
-      g_strdup ("/");
+    builder->priv->resource_prefix = g_strdup ("/");
 
-  filename_for_errors = g_strconcat ("<resource>", resource_path, NULL);
+  filename_for_errors = g_strconcat ("<resource>", path, NULL);
 
-  _gtk_builder_parser_parse_buffer (builder, filename_for_errors,
+  _gtk_builder_parser_parse_buffer (builder, parent, filename_for_errors,
                                     g_bytes_get_data (data, NULL), g_bytes_get_size (data),
                                     NULL,
                                     &tmp_error);
@@ -1114,7 +1205,7 @@ gtk_builder_add_objects_from_resource (GtkBuilder   *builder,
 
   filename_for_errors = g_strconcat ("<resource>", resource_path, NULL);
 
-  _gtk_builder_parser_parse_buffer (builder, filename_for_errors,
+  _gtk_builder_parser_parse_buffer (builder, NULL, filename_for_errors,
                                     g_bytes_get_data (data, NULL), g_bytes_get_size (data),
                                     object_ids,
                                     &tmp_error);
@@ -1153,6 +1244,34 @@ gtk_builder_add_from_string (GtkBuilder   *builder,
                              gsize         length,
                              GError      **error)
 {
+  return gtk_builder_add_to_parent_from_string (builder, NULL, buffer, length, error);
+}
+
+
+/**
+ * gtk_builder_add_to_parent_from_string:
+ * @builder: a #GtkBuilder
+ * @parent: the parent object to be assumed in context while parsing
+ * @buffer: the string to parse
+ * @length: the length of @buffer (may be -1 if @buffer is nul-terminated)
+ * @error: (allow-none): return location for an error, or %NULL
+ *
+ * Like gtk_builder_add_from_string() except the format will expect
+ * <child> instead of <object> as its first elements and expose
+ * @parent in the build context, children defined in the UI fragment
+ * will be added to @parent.
+ * 
+ * Returns: A positive value on success, 0 if an error occurred
+ *
+ * Since: ...
+ **/
+guint
+gtk_builder_add_to_parent_from_string (GtkBuilder   *builder,
+                                       GObject      *parent,
+                                       const gchar  *buffer,
+                                       gsize         length,
+                                       GError      **error)
+{
   GError *tmp_error;
 
   g_return_val_if_fail (GTK_IS_BUILDER (builder), 0);
@@ -1166,7 +1285,7 @@ gtk_builder_add_from_string (GtkBuilder   *builder,
   builder->priv->filename = g_strdup (".");
   builder->priv->resource_prefix = NULL;
 
-  _gtk_builder_parser_parse_buffer (builder, "<input>",
+  _gtk_builder_parser_parse_buffer (builder, parent, "<input>",
                                     buffer, length,
                                     NULL,
                                     &tmp_error);
@@ -1225,7 +1344,7 @@ gtk_builder_add_objects_from_string (GtkBuilder   *builder,
   builder->priv->filename = g_strdup (".");
   builder->priv->resource_prefix = NULL;
 
-  _gtk_builder_parser_parse_buffer (builder, "<input>",
+  _gtk_builder_parser_parse_buffer (builder, NULL, "<input>",
                                     buffer, length,
                                     object_ids,
                                     &tmp_error);
@@ -1340,6 +1459,40 @@ gtk_builder_get_translation_domain (GtkBuilder *builder)
   return builder->priv->domain;
 }
 
+/**
+ * gtk_builder_expose_object:
+ * @builder: a #GtkBuilder
+ * @name: the name of the object exposed to the builder
+ * @object: the object to expose
+ *
+ * Adds @object to a pool of objects external to the
+ * objects built by builder. Objects exposed in the pool
+ * can be referred to by xml fragments in the builder.
+ */
+void         
+gtk_builder_expose_object (GtkBuilder    *builder,
+                           const gchar   *name,
+                           GObject       *object)
+{
+  GtkBuilderPrivate *priv;
+
+  g_return_if_fail (GTK_IS_BUILDER (builder));
+  g_return_if_fail (name && name[0]);
+  g_return_if_fail (G_IS_OBJECT (object));
+
+  priv = builder->priv;
+
+  /* We do not create the table in _init() because this is not used very much */
+  if (priv->external_objects == NULL)
+    priv->external_objects = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                    g_free, g_object_unref);
+  
+  object_set_name (object, name);
+  g_hash_table_insert (priv->external_objects, 
+		       g_strdup (name), g_object_ref (object));
+}
+
+
 typedef struct {
   GModule *module;
   gpointer data;
@@ -1369,7 +1522,6 @@ gtk_builder_connect_signals_default (GtkBuilder    *builder,
     g_signal_connect_data (object, signal_name, func, args->data, NULL, flags);
 }
 
-
 /**
  * gtk_builder_connect_signals:
  * @builder: a #GtkBuilder
@@ -1479,14 +1631,19 @@ gtk_builder_connect_signals_full (GtkBuilder            *builder,
       
       if (signal->connect_object_name)
 	{
-	  connect_object = g_hash_table_lookup (builder->priv->objects,
-						signal->connect_object_name);
+          if (signal->external)
+	    connect_object = g_hash_table_lookup (builder->priv->external_objects,
+						  signal->connect_object_name);
+	  else
+	    connect_object = g_hash_table_lookup (builder->priv->objects,
+						  signal->connect_object_name);
+
 	  if (!connect_object)
-	      g_warning ("Could not lookup object %s on signal %s of object %s",
-			 signal->connect_object_name, signal->name,
-			 signal->object_name);
+	    g_warning ("Could not lookup object %s on signal %s of object %s.",
+		       signal->connect_object_name, signal->name,
+		       signal->object_name);
 	}
-						  
+      
       func (builder, object, signal->name, signal->handler, 
 	    connect_object, signal->flags, user_data);
     }
diff --git a/gtk/gtkbuilder.h b/gtk/gtkbuilder.h
index f4950eb..3588a39 100644
--- a/gtk/gtkbuilder.h
+++ b/gtk/gtkbuilder.h
@@ -59,6 +59,7 @@ typedef struct _GtkBuilderPrivate GtkBuilderPrivate;
  * @GTK_BUILDER_ERROR_VERSION_MISMATCH: The input file requires a newer version
  *  of GTK+.
  * @GTK_BUILDER_ERROR_DUPLICATE_ID: An object id occurred twice.
+ * @GTK_BUILDER_ERROR_TEMPLATE_CLASS_MISMATCH: The Class template is designed for a different class.
  *
  * Error codes that identify various errors that can occur while using
  * #GtkBuilder.
@@ -73,7 +74,8 @@ typedef enum
   GTK_BUILDER_ERROR_MISSING_PROPERTY_VALUE,
   GTK_BUILDER_ERROR_INVALID_VALUE,
   GTK_BUILDER_ERROR_VERSION_MISMATCH,
-  GTK_BUILDER_ERROR_DUPLICATE_ID
+  GTK_BUILDER_ERROR_DUPLICATE_ID,
+  GTK_BUILDER_ERROR_TEMPLATE_CLASS_MISMATCH
 } GtkBuilderError;
 
 GQuark gtk_builder_error_quark (void);
@@ -129,22 +131,38 @@ guint        gtk_builder_add_objects_from_file   (GtkBuilder    *builder,
                                                   gchar        **object_ids,
                                                   GError       **error);
 guint        gtk_builder_add_objects_from_resource(GtkBuilder    *builder,
-                                                  const gchar   *resource_path,
-                                                  gchar        **object_ids,
-                                                  GError       **error);
+                                                   const gchar   *resource_path,
+                                                   gchar        **object_ids,
+                                                   GError       **error);
 guint        gtk_builder_add_objects_from_string (GtkBuilder    *builder,
                                                   const gchar   *buffer,
                                                   gsize          length,
                                                   gchar        **object_ids,
                                                   GError       **error);
+guint        gtk_builder_add_to_parent_from_file     (GtkBuilder   *builder,
+                                                      GObject      *parent,
+                                                      const gchar  *filename,
+                                                      GError      **error);
+guint        gtk_builder_add_to_parent_from_string   (GtkBuilder   *builder,
+                                                      GObject      *parent,
+                                                      const gchar  *buffer,
+                                                      gsize         length,
+                                                      GError      **error);
+guint        gtk_builder_add_to_parent_from_resource (GtkBuilder   *builder,
+                                                      GObject      *parent,
+                                                      const gchar  *path,
+                                                      GError      **error);
 GObject*     gtk_builder_get_object              (GtkBuilder    *builder,
                                                   const gchar   *name);
 GSList*      gtk_builder_get_objects             (GtkBuilder    *builder);
+void         gtk_builder_expose_object           (GtkBuilder    *builder,
+                                                  const gchar   *name,
+                                                  GObject       *object);
 void         gtk_builder_connect_signals         (GtkBuilder    *builder,
-						  gpointer       user_data);
+                                                  gpointer       user_data);
 void         gtk_builder_connect_signals_full    (GtkBuilder    *builder,
                                                   GtkBuilderConnectFunc func,
-						  gpointer       user_data);
+                                                  gpointer       user_data);
 void         gtk_builder_set_translation_domain  (GtkBuilder   	*builder,
                                                   const gchar  	*domain);
 const gchar* gtk_builder_get_translation_domain  (GtkBuilder   	*builder);
@@ -152,15 +170,15 @@ GType        gtk_builder_get_type_from_name      (GtkBuilder   	*builder,
                                                   const char   	*type_name);
 
 gboolean     gtk_builder_value_from_string       (GtkBuilder    *builder,
-						  GParamSpec   	*pspec,
+                                                  GParamSpec   	*pspec,
                                                   const gchar  	*string,
                                                   GValue       	*value,
-						  GError       **error);
+                                                  GError       **error);
 gboolean     gtk_builder_value_from_string_type  (GtkBuilder    *builder,
-						  GType        	 type,
+                                                  GType        	 type,
                                                   const gchar  	*string,
                                                   GValue       	*value,
-						  GError       **error);
+                                                  GError       **error);
 
 /**
  * GTK_BUILDER_WARN_INVALID_CHILD_TYPE:
diff --git a/gtk/gtkbuilderparser.c b/gtk/gtkbuilderparser.c
index 59c0721..ae3e8af 100644
--- a/gtk/gtkbuilderparser.c
+++ b/gtk/gtkbuilderparser.c
@@ -65,6 +65,32 @@ state_pop (ParserData *data)
 #define state_pop_info(data, st) ((st*)state_pop(data))
 
 static void
+error_generic (GError **error,
+               GtkBuilderError code,
+               ParserData *data,               
+               const gchar *tag,               
+               const gchar *format,
+               ...)
+{
+  gint line_number, char_number;
+  gchar *message;
+  va_list args;
+  
+  g_markup_parse_context_get_position (data->ctx,
+                                       &line_number,
+                                       &char_number);
+
+  va_start (args, format);
+  message = g_strdup_vprintf (format, args);
+  va_end (args);
+
+  g_set_error (error, GTK_BUILDER_ERROR, code, "%s:%d:%d <%s> %s",
+               data->filename, line_number, char_number, tag, message);
+
+  g_free (message);
+}
+
+static void
 error_missing_attribute (ParserData *data,
                          const gchar *tag,
                          const gchar *attribute,
@@ -203,24 +229,19 @@ builder_construct (ParserData  *data,
   return object;
 }
 
-static gchar *
+static GType
 _get_type_by_symbol (const gchar* symbol)
 {
   static GModule *module = NULL;
   GTypeGetFunc func;
-  GType type;
   
   if (!module)
     module = g_module_open (NULL, 0);
 
   if (!g_module_symbol (module, symbol, (gpointer)&func))
-    return NULL;
+    return G_TYPE_INVALID;
   
-  type = func ();
-  if (type == G_TYPE_INVALID)
-    return NULL;
-
-  return g_strdup (g_type_name (type));
+  return func ();
 }
 
 static void
@@ -303,13 +324,12 @@ parse_object (GMarkupParseContext  *context,
               const gchar         **values,
               GError              **error)
 {
+  GType object_type = G_TYPE_INVALID;
   ObjectInfo *object_info;
-  ChildInfo* child_info;
-  int i;
-  gchar *object_class = NULL;
+  ChildInfo *child_info;
   gchar *object_id = NULL;
   gchar *constructor = NULL;
-  gint line, line2;
+  gint i, line, line2;
 
   child_info = state_peek_info (data, ChildInfo);
   if (child_info && strcmp (child_info->tag.name, "object") == 0)
@@ -321,7 +341,8 @@ parse_object (GMarkupParseContext  *context,
   for (i = 0; names[i] != NULL; i++)
     {
       if (strcmp (names[i], "class") == 0)
-        object_class = g_strdup (values[i]);
+        /* Make sure the class is initialized so we have intern string available */
+        object_type = gtk_builder_get_type_from_name (data->builder, values[i]);
       else if (strcmp (names[i], "id") == 0)
         object_id = g_strdup (values[i]);
       else if (strcmp (names[i], "constructor") == 0)
@@ -332,8 +353,8 @@ parse_object (GMarkupParseContext  *context,
 	   * it's guaranteed afterwards that g_type_from_name on the name
 	   * will return our GType
 	   */
-          object_class = _get_type_by_symbol (values[i]);
-          if (!object_class)
+          object_type = _get_type_by_symbol (values[i]);
+          if (object_type == G_TYPE_INVALID)
             {
               g_markup_parse_context_get_position (context, &line, NULL);
               g_set_error (error, GTK_BUILDER_ERROR,
@@ -350,7 +371,7 @@ parse_object (GMarkupParseContext  *context,
 	}
     }
 
-  if (!object_class)
+  if (object_type == G_TYPE_INVALID)
     {
       error_missing_attribute (data, element_name, "class", error);
       return;
@@ -379,7 +400,6 @@ parse_object (GMarkupParseContext  *context,
         }
       else
         {
-          g_free (object_class);
           g_free (object_id);
           g_free (constructor);
           return;
@@ -387,7 +407,7 @@ parse_object (GMarkupParseContext  *context,
     }
 
   object_info = g_slice_new0 (ObjectInfo);
-  object_info->class_name = object_class;
+  object_info->object_type = object_type;
   object_info->id = object_id;
   object_info->constructor = constructor;
   state_push (data, object_info);
@@ -420,7 +440,6 @@ free_object_info (ObjectInfo *info)
                    (GFunc)free_property_info, NULL);
   g_slist_free (info->properties);
   g_free (info->constructor);
-  g_free (info->class_name);
   g_free (info->id);
   g_slice_free (ObjectInfo, info);
 }
@@ -443,6 +462,7 @@ parse_child (ParserData   *data,
 {
   ObjectInfo* object_info;
   ChildInfo *child_info;
+  GObject  *object;
   guint i;
 
   object_info = state_peek_info (data, ObjectInfo);
@@ -451,6 +471,9 @@ parse_child (ParserData   *data,
       error_invalid_tag (data, element_name, NULL, error);
       return;
     }
+
+  GTK_NOTE (BUILDER, g_print ("parsing child of parent type %s\n", 
+			      object_info->object ? G_OBJECT_TYPE_NAME (object_info->object) : "(none)"));
   
   child_info = g_slice_new0 (ChildInfo);
   state_push (data, child_info);
@@ -467,7 +490,9 @@ parse_child (ParserData   *data,
 
   child_info->parent = (CommonInfo*)object_info;
 
-  object_info->object = builder_construct (data, object_info, error);
+  object = builder_construct (data, object_info, error);
+  object_info->object = object;
+
 }
 
 static void
@@ -489,6 +514,7 @@ parse_property (ParserData   *data,
   gchar *name = NULL;
   gchar *context = NULL;
   gboolean translatable = FALSE;
+  gboolean external = FALSE;
   ObjectInfo *object_info;
   int i;
 
@@ -517,6 +543,11 @@ parse_property (ParserData   *data,
         {
           context = g_strdup (values[i]);
         }
+      else if (strcmp (names[i], "external-object") == 0)
+	{
+	  if (!_gtk_builder_boolean_from_string (values[i], &external, error))
+	    return;
+	}
       else
 	{
 	  error_invalid_attribute (data, element_name, names[i], error);
@@ -531,20 +562,22 @@ parse_property (ParserData   *data,
     }
 
   info = g_slice_new0 (PropertyInfo);
-  info->name = name;
+  info->name         = g_intern_string (name);
   info->translatable = translatable;
-  info->context = context;
-  info->text = g_string_new ("");
+  info->context      = context;
+  info->text         = g_string_new ("");
+  info->external     = external;
   state_push (data, info);
 
   info->tag.name = element_name;
+
+  g_free (name);
 }
 
 static void
 free_property_info (PropertyInfo *info)
 {
   g_free (info->data);
-  g_free (info->name);
   g_slice_free (PropertyInfo, info);
 }
 
@@ -556,12 +589,13 @@ parse_signal (ParserData   *data,
               GError      **error)
 {
   SignalInfo *info;
-  gchar *name = NULL;
+  const gchar *name = NULL;
   gchar *handler = NULL;
   gchar *object = NULL;
   gboolean after = FALSE;
   gboolean swapped = FALSE;
   gboolean swapped_set = FALSE;
+  gboolean external = FALSE;
   ObjectInfo *object_info;
   int i;
 
@@ -575,7 +609,7 @@ parse_signal (ParserData   *data,
   for (i = 0; names[i] != NULL; i++)
     {
       if (strcmp (names[i], "name") == 0)
-        name = g_strdup (values[i]);
+        name = values[i];
       else if (strcmp (names[i], "handler") == 0)
         handler = g_strdup (values[i]);
       else if (strcmp (names[i], "after") == 0)
@@ -589,6 +623,11 @@ parse_signal (ParserData   *data,
 	    return;
 	  swapped_set = TRUE;
 	}
+      else if (strcmp (names[i], "external-object") == 0)
+	{
+	  if (!_gtk_builder_boolean_from_string (values[i], &external, error))
+	    return;
+	}
       else if (strcmp (names[i], "object") == 0)
         object = g_strdup (values[i]);
       else if (strcmp (names[i], "last_modification_time") == 0)
@@ -617,24 +656,120 @@ parse_signal (ParserData   *data,
     swapped = TRUE;
   
   info = g_slice_new0 (SignalInfo);
-  info->name = name;
+  info->name = g_intern_string (name);
   info->handler = handler;
   if (after)
     info->flags |= G_CONNECT_AFTER;
   if (swapped)
     info->flags |= G_CONNECT_SWAPPED;
+  info->external = external;
   info->connect_object_name = object;
   state_push (data, info);
 
   info->tag.name = element_name;
 }
 
+static void
+parse_template (ParserData   *data,
+                const gchar  *element_name,
+                const gchar **names,
+                const gchar **values,
+                GError      **error)
+{
+  GObject *parent = data->template_object;
+  const gchar *parent_class = NULL;
+  const gchar *class_name = NULL;
+  const gchar *id = NULL;
+  GType parent_type, class_type;
+  ObjectInfo *object_info;
+  gint i;
+
+  if (parent == NULL)
+    {
+      error_invalid_tag (data, element_name, NULL, error);
+      return;
+    }
+
+  for (i = 0; names[i] != NULL; i++)
+    {
+      if (strcmp (names[i], "class") == 0)
+        class_name = values[i];
+      else if (strcmp (names[i], "parent") == 0)
+        parent_class = values[i];
+      else if (strcmp (names[i], "id") == 0)
+        id = values[i];
+      else
+        {
+          error_invalid_attribute (data, element_name, names[i], error);
+          return;
+        }
+    }
+
+  if (!class_name)
+    {
+      error_missing_attribute (data, element_name, "class", error);
+      return;
+    }
+
+  if (!parent_class)
+    {
+      error_missing_attribute (data, element_name, "parent", error);
+      return;
+    }
+
+  if (!id)
+    {
+      error_missing_attribute (data, element_name, "id", error);
+      return;
+    }
+
+  if (strcmp (id, "this"))
+    {
+      error_generic (error, GTK_BUILDER_ERROR_INVALID_VALUE, data,
+                     element_name, "%s template should be named 'this' not %s '%s'",
+                     class_name, id);
+      return;
+    }
+
+  if ((class_type = g_type_from_name (class_name)) == G_TYPE_INVALID)
+    {
+      error_generic (error, GTK_BUILDER_ERROR_TEMPLATE_CLASS_MISMATCH, data,
+                     element_name, "invalid class type found '%s'", class_name);
+      return;
+    }
+
+  parent_type = G_OBJECT_TYPE (parent);
+  
+  if (!g_type_is_a (parent_type, class_type))
+    {
+      error_generic (error, GTK_BUILDER_ERROR_TEMPLATE_CLASS_MISMATCH, data,
+                     element_name, "this template is for a class type %s not for %s",
+                     class_name, G_OBJECT_TYPE_NAME (parent));
+      return;
+    }
+
+  if (!g_type_is_a (class_type, g_type_from_name (parent_class)))
+    {
+      error_generic (error, GTK_BUILDER_ERROR_TEMPLATE_CLASS_MISMATCH, data,
+                     element_name, "class %s should derive from parent %s",
+                     class_name, parent_class);
+      return;
+    }
+    
+  object_info = g_slice_new0 (ObjectInfo);
+  object_info->object = parent;
+  object_info->object_type = parent_type;
+  object_info->id = g_strdup (_gtk_builder_object_get_name (parent));
+  object_info->tag.name = "object";
+
+  state_push (data, object_info);
+}
+
 /* Called by GtkBuilder */
 void
 _free_signal_info (SignalInfo *info,
                    gpointer user_data)
 {
-  g_free (info->name);
   g_free (info->handler);
   g_free (info->connect_object_name);
   g_free (info->object_name);
@@ -892,6 +1027,8 @@ start_element (GMarkupParseContext *context,
     parse_interface (data, element_name, names, values, error);
   else if (strcmp (element_name, "menu") == 0)
     _gtk_builder_menu_start (data, element_name, names, values, error);
+  else if (strcmp (element_name, "template") == 0)
+    parse_template (data, element_name, names, values, error);
   else if (strcmp (element_name, "placeholder") == 0)
     {
       /* placeholder has no special treatmeant, but it needs an
@@ -1054,6 +1191,25 @@ end_element (GMarkupParseContext *context,
   else if (strcmp (element_name, "placeholder") == 0)
     {
     }
+  else if (strcmp (element_name, "template") == 0)
+    {
+      if (data->template_object)
+        {
+          ObjectInfo *object_info = state_pop_info (data, ObjectInfo);
+
+          object_info->properties = g_slist_reverse (object_info->properties);
+
+          /* This is just to apply properties to the external object */
+          _gtk_builder_construct (data->builder, object_info, error);
+
+          if (object_info->signals)
+            _gtk_builder_add_signals (data->builder, object_info->signals);
+
+          free_object_info (object_info);
+
+          data->template_object = NULL;
+        }
+    }
   else
     {
       g_assert_not_reached ();
@@ -1126,6 +1282,7 @@ static const GMarkupParser parser = {
 
 void
 _gtk_builder_parser_parse_buffer (GtkBuilder   *builder,
+                                  GObject      *parent,
                                   const gchar  *filename,
                                   const gchar  *buffer,
                                   gsize         length,
@@ -1150,6 +1307,12 @@ _gtk_builder_parser_parse_buffer (GtkBuilder   *builder,
   data->object_ids = g_hash_table_new_full (g_str_hash, g_str_equal,
 					    (GDestroyNotify)g_free, NULL);
 
+  if (parent)
+    {
+      GTK_NOTE (BUILDER, g_print ("parsing with contextual parent %s ptr %p\n", G_OBJECT_TYPE_NAME (parent), parent));
+      data->template_object = parent;
+    }
+
   data->requested_objects = NULL;
   if (requested_objs)
     {
diff --git a/gtk/gtkbuilderprivate.h b/gtk/gtkbuilderprivate.h
index 3667ed5..9e4b292 100644
--- a/gtk/gtkbuilderprivate.h
+++ b/gtk/gtkbuilderprivate.h
@@ -31,7 +31,7 @@ typedef struct {
 
 typedef struct {
   TagInfo tag;
-  gchar *class_name;
+  GType object_type;
   gchar *id;
   gchar *constructor;
   GSList *properties;
@@ -58,18 +58,20 @@ typedef struct {
 
 typedef struct {
   TagInfo tag;
-  gchar *name;
+  const gchar *name; /* Intern string */
   GString *text;
   gchar *data;
-  gboolean translatable;
   gchar *context;
+  guint8 translatable : 1;
+  guint8 external : 1;
 } PropertyInfo;
 
 typedef struct {
   TagInfo tag;
   gchar *object_name;
-  gchar *name;
+  const gchar *name; /* Intern string */
   gchar *handler;
+  gboolean external;
   GConnectFlags flags;
   gchar *connect_object_name;
 } SignalInfo;
@@ -107,12 +109,14 @@ typedef struct {
   gint cur_object_level;
 
   GHashTable *object_ids;
+  GObject *template_object;
 } ParserData;
 
 typedef GType (*GTypeGetFunc) (void);
 
 /* Things only GtkBuilder should use */
 void _gtk_builder_parser_parse_buffer (GtkBuilder *builder,
+                                       GObject     *parent,
                                        const gchar *filename,
                                        const gchar *buffer,
                                        gsize length,
@@ -159,5 +163,6 @@ void      _gtk_builder_menu_start (ParserData   *parser_data,
                                    GError      **error);
 void      _gtk_builder_menu_end   (ParserData  *parser_data);
 
+const gchar * _gtk_builder_object_get_name (GObject *object);
 
 #endif /* __GTK_BUILDER_PRIVATE_H__ */



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