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



commit ec15fd3ab3730946008a30100ff24c33a3f8cadb
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/gtk.gresource.xml   |    2 +
 gtk/gtkbuilder.c        |  275 +++++++++++++++++++++++++++++++++++----------
 gtk/gtkbuilder.h        |   38 +++++--
 gtk/gtkbuilderparser.c  |  219 +++++++++++++++++++++++++++++++-----
 gtk/gtkbuilderprivate.h |   13 ++-
 gtk/gtkcontainer.c      |  285 +++++++++++++++++++++++++++++++++++++++++++++++
 gtk/gtkcontainer.h      |   18 +++-
 gtk/gtkdialog.c         |  145 +++++++++++-------------
 gtk/gtkdialog.ui        |   24 ++++
 gtk/gtkmessagedialog.c  |  256 +++++++++++++++++-------------------------
 gtk/gtkmessagedialog.ui |   76 +++++++++++++
 12 files changed, 1020 insertions(+), 334 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/gtk.gresource.xml b/gtk/gtk.gresource.xml
index 939d35f..d3deedb 100644
--- a/gtk/gtk.gresource.xml
+++ b/gtk/gtk.gresource.xml
@@ -11,5 +11,7 @@
     <file alias="cursor/dnd-none.png">cursor_dnd_none.png</file>
     <file alias="cursor/dnd-move.png">cursor_dnd_move.png</file>
     <file alias="cursor/dnd-copy.png">cursor_dnd_copy.png</file>
+    <file preprocess="xml-stripblanks">gtkdialog.ui</file>
+    <file preprocess="xml-stripblanks">gtkmessagedialog.ui</file>
   </gresource>
 </gresources>
diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c
index bf75922..f90cdae 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,6 +1069,31 @@ 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;
@@ -1033,7 +1126,7 @@ gtk_builder_add_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, parent, filename_for_errors,
                                     g_bytes_get_data (data, NULL), g_bytes_get_size (data),
                                     NULL,
                                     &tmp_error);
@@ -1114,7 +1207,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 +1246,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 +1287,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 +1346,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 +1461,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 +1524,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 +1633,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__ */
diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c
index be68602..e5dc22b 100644
--- a/gtk/gtkcontainer.c
+++ b/gtk/gtkcontainer.c
@@ -230,6 +230,28 @@
  * </refsect2>
  */
 
+typedef struct
+{
+  gchar *name;
+  GType type;
+  guint offset;
+  gboolean private;
+} InternalChildData;
+
+typedef struct
+{
+  gchar *name;
+  GObject *object;
+} InternalChild;
+
+struct _GtkContainerClassPrivate
+{
+  GSList *tmpl_classes;
+
+  const gchar *tmpl;
+  GtkBuilderConnectFunc connect_func;
+  GList *internal_children; /* InternalChildData list */
+};
 
 struct _GtkContainerPrivate
 {
@@ -243,6 +265,8 @@ struct _GtkContainerPrivate
   guint restyle_pending    : 1;
   guint resize_mode        : 2;
   guint request_mode       : 2;
+
+  GArray *internal_children; /* InternalChild array */
 };
 
 enum {
@@ -270,6 +294,9 @@ static void     gtk_container_base_class_finalize  (GtkContainerClass *klass);
 static void     gtk_container_class_init           (GtkContainerClass *klass);
 static void     gtk_container_init                 (GtkContainer      *container);
 static void     gtk_container_destroy              (GtkWidget         *widget);
+static GObject *gtk_container_constructor          (GType                  type,
+                                                    guint                  n_construct_properties,
+                                                    GObjectConstructParam *construct_properties);
 static void     gtk_container_set_property         (GObject         *object,
                                                     guint            prop_id,
                                                     const GValue    *value,
@@ -389,6 +416,8 @@ gtk_container_get_type (void)
                                    GTK_TYPE_BUILDABLE,
                                    &buildable_info);
 
+      g_type_add_class_private (container_type, sizeof (GtkContainerClassPrivate));
+
     }
 
   return container_type;
@@ -397,7 +426,18 @@ gtk_container_get_type (void)
 static void
 gtk_container_base_class_init (GtkContainerClass *class)
 {
+  GtkContainerClassPrivate *priv;
+
   /* reset instance specifc class fields that don't get inherited */
+  class->priv = priv =  G_TYPE_CLASS_GET_PRIVATE (class,
+                                                  GTK_TYPE_CONTAINER,
+                                                  GtkContainerClassPrivate);
+
+  priv->tmpl = NULL;
+  priv->tmpl_classes = NULL;
+  priv->connect_func = NULL;
+  priv->internal_children = NULL;
+  
   class->set_child_property = NULL;
   class->get_child_property = NULL;
 }
@@ -405,6 +445,7 @@ gtk_container_base_class_init (GtkContainerClass *class)
 static void
 gtk_container_base_class_finalize (GtkContainerClass *class)
 {
+  GtkContainerClassPrivate *priv = class->priv;
   GList *list, *node;
 
   list = g_param_spec_pool_list_owned (_gtk_widget_child_property_pool, G_OBJECT_CLASS_TYPE (class));
@@ -417,6 +458,8 @@ gtk_container_base_class_finalize (GtkContainerClass *class)
       g_param_spec_unref (pspec);
     }
   g_list_free (list);
+
+  g_slist_free (priv->tmpl_classes);
 }
 
 static void
@@ -430,6 +473,7 @@ gtk_container_class_init (GtkContainerClass *class)
   vadjustment_key_id = g_quark_from_static_string (vadjustment_key);
   hadjustment_key_id = g_quark_from_static_string (hadjustment_key);
 
+  gobject_class->constructor  = gtk_container_constructor;
   gobject_class->set_property = gtk_container_set_property;
   gobject_class->get_property = gtk_container_get_property;
 
@@ -519,10 +563,37 @@ gtk_container_class_init (GtkContainerClass *class)
   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_CONTAINER_ACCESSIBLE);
 }
 
+static GObject *
+gtk_container_buildable_get_internal_child (GtkBuildable *buildable,
+                                            GtkBuilder   *builder,
+                                            const gchar  *childname)
+{
+  GArray *internal_children;
+
+  g_return_val_if_fail (childname && childname[0], NULL);
+
+  if ((internal_children = GTK_CONTAINER (buildable)->priv->internal_children))
+    {
+      gint i, len;
+      
+      for (i = 0, len = internal_children->len; i < len; i++)
+        {
+          InternalChild *data = &g_array_index (internal_children, InternalChild, i);
+
+          if (g_strcmp0 (data->name, childname) == 0) return data->object;
+        }
+    }
+  
+  return parent_buildable_iface->get_internal_child (buildable,
+                                                     builder,
+                                                     childname);
+}
+
 static void
 gtk_container_buildable_init (GtkBuildableIface *iface)
 {
   parent_buildable_iface = g_type_interface_peek_parent (iface);
+  iface->get_internal_child = gtk_container_buildable_get_internal_child;
   iface->add_child = gtk_container_buildable_add_child;
   iface->custom_tag_start = gtk_container_buildable_custom_tag_start;
   iface->custom_tag_end = gtk_container_buildable_custom_tag_end;
@@ -1318,6 +1389,105 @@ gtk_container_class_list_child_properties (GObjectClass *cclass,
   return pspecs;
 }
 
+/**
+ * gtk_container_class_set_template_from_resource:
+ * @container_class: a #GtkContainerClass
+ * @resource_path: the #GtkBuilder xml resource path
+ *
+ * This is used when implementing new composite widget types
+ * to specify a UI template for instances of this type.
+ *
+ * Templates are in the <link linkend="BUILDER-UI">GtkBuilder UI description</link>
+ * format and are used to implement composite widget types in
+ * an automated way. 
+ *
+ * Instances with an assigned template will have thier children
+ * built at object construct time.
+ *
+ * The provided xml is expected to have a <template> tag instead of <object> 
+ * with id="this" and an extra 'parent' property specifying from with type 
+ * the new class derives from.
+ * 
+ * Since: 3.0
+ */
+void
+gtk_container_class_set_template_from_resource (GtkContainerClass *container_class,
+                                                const gchar       *resource_path)
+{
+  GtkContainerClassPrivate *priv;
+  GObjectClass  *oclass;
+  
+  g_return_if_fail (GTK_IS_CONTAINER_CLASS(container_class));
+  g_return_if_fail (resource_path && resource_path[0]);
+
+  priv = container_class->priv;
+
+  priv->tmpl = resource_path;
+
+  /* Collect an ordered list of class which have templates to build */
+  for (oclass = G_OBJECT_CLASS (container_class);
+       GTK_IS_CONTAINER_CLASS (oclass);
+       oclass = g_type_class_peek_parent (oclass))
+    {
+      GtkContainerClassPrivate *cpriv = GTK_CONTAINER_CLASS (oclass)->priv;
+
+      if (cpriv->tmpl)
+        priv->tmpl_classes = g_slist_prepend (priv->tmpl_classes, oclass);
+    }
+}
+
+/**
+ * gtk_container_class_set_connect_func:
+ * @container_class: a #GtkContainerClass
+ * @connect_func: the #GtkBuilderConnectFunc to use when connecting signals internally.
+ *
+ * Sets the function to be used when automatically connecting signals
+ * defined by this class's GtkBuilder template.
+ */
+void
+gtk_container_class_set_connect_func (GtkContainerClass *container_class,
+                                      GtkBuilderConnectFunc connect_func)
+{
+  g_return_if_fail (GTK_IS_CONTAINER_CLASS(container_class));
+  g_return_if_fail (connect_func != NULL);
+
+  container_class->priv->connect_func = connect_func;
+}
+
+/**
+ * gtk_container_class_declare_internal_child:
+ * @container_class: a #GtkContainerClass
+ * @use_private: True if struct_offset refers to the instance private struct
+ * @struct_offset: offset where to save composite children pointer
+ * @name: the name of the composite children to declare
+ *
+ * Declare a child defined in the template as an internal children.
+ * Use #G_STRUCT_OFFSET to pass in the struct_offset of the pointer that will be set automatically on construction.
+ * If you do not need to keep a pointer set use_private to FALSE and struct_offset to 0.
+ */
+void
+gtk_container_class_declare_internal_child (GtkContainerClass *container_class,
+                                            gboolean use_private,
+                                            guint struct_offset,
+                                            const gchar *name)
+{
+  GtkContainerClassPrivate *priv;
+  InternalChildData *child;
+
+  g_return_if_fail (GTK_IS_CONTAINER_CLASS (container_class));
+  g_return_if_fail (name);
+
+  priv = container_class->priv;
+
+  child = g_new0 (InternalChildData, 1);
+  child->name = g_strdup (name);
+  child->private = use_private;
+  child->type = G_TYPE_FROM_CLASS (container_class);
+  child->offset = struct_offset;
+
+  priv->internal_children = g_list_prepend (priv->internal_children, child);
+}
+
 static void
 gtk_container_add_unimplemented (GtkContainer     *container,
                                  GtkWidget        *widget)
@@ -1346,6 +1516,7 @@ gtk_container_init (GtkContainer *container)
   priv->border_width = 0;
   priv->resize_mode = GTK_RESIZE_PARENT;
   priv->reallocate_redraws = FALSE;
+  priv->internal_children = NULL;
 }
 
 static void
@@ -1374,12 +1545,126 @@ gtk_container_destroy (GtkWidget *widget)
   if (priv->has_focus_chain)
     gtk_container_unset_focus_chain (container);
 
+  if (priv->internal_children)
+    {
+      GArray *internal_children = priv->internal_children;
+      gint i, len = internal_children->len;
+
+      for (i = 0; i < len; i++)
+        {
+          InternalChild *data = &g_array_index (internal_children, InternalChild, i);
+          g_object_unref (data->object);
+        }
+      g_array_unref (internal_children);
+      priv->internal_children = NULL;
+    }
+
   gtk_container_foreach (container, (GtkCallback) gtk_widget_destroy, NULL);
 
   GTK_WIDGET_CLASS (parent_class)->destroy (widget);
 }
 
 static void
+gtk_container_child_set_internal (GtkContainer *container,
+                                  InternalChildData *child,
+                                  GObject *internal)
+{
+  GtkContainerPrivate *priv = container->priv;
+  InternalChild data;
+  GObject **retval;
+
+  if (!priv->internal_children)
+    priv->internal_children = g_array_new (FALSE, FALSE, sizeof (InternalChild));
+
+  if (GTK_IS_WIDGET (internal))
+    gtk_widget_set_composite_name (GTK_WIDGET (internal), child->name);
+
+  data.name = child->name;
+  data.object = g_object_ref (internal);
+  g_array_append_val (priv->internal_children, data);
+
+  if (child->private)
+    {
+      gpointer pstruct = G_TYPE_INSTANCE_GET_PRIVATE (container, child->type, gpointer);
+      retval = G_STRUCT_MEMBER_P (pstruct, child->offset);
+    }
+  else
+    retval = G_STRUCT_MEMBER_P (container, child->offset);
+
+  *retval = internal;
+}
+
+static GObject *
+gtk_container_constructor (GType                  type,
+                           guint                  n_construct_properties,
+                           GObjectConstructParam *construct_properties)
+{
+  GtkContainerClassPrivate *priv;
+  GtkContainer *container;
+  GError *error = NULL;
+  GtkBuilder *builder;
+  GObject *object;
+  GSList *l;
+
+  object = G_OBJECT_CLASS (parent_class)->constructor (type,
+                                                       n_construct_properties,
+                                                       construct_properties);
+
+  priv = GTK_CONTAINER_CLASS (G_OBJECT_GET_CLASS (object))->priv;
+  container = GTK_CONTAINER (object);
+
+  gtk_widget_push_composite_child ();
+
+  /* Build the templates for each class starting with the superclass descending */
+  for (l = priv->tmpl_classes; l; l = g_slist_next (l))
+    {
+      GtkContainerClassPrivate *cpriv = GTK_CONTAINER_CLASS (l->data)->priv;
+      GList *children;
+      guint ret;
+        
+      builder = gtk_builder_new ();
+      gtk_builder_expose_object (builder, "this", object);
+      ret = gtk_builder_add_to_parent_from_resource (builder, object, cpriv->tmpl, &error);
+
+      if (ret)
+        {
+          /* Setup internal children */
+          for (children = cpriv->internal_children; children; children = g_list_next (children))
+            {
+              InternalChildData *child = children->data;
+              GObject *internal;
+
+              if ((internal = gtk_builder_get_object (builder, child->name)))
+                gtk_container_child_set_internal (container, child, internal);
+              else
+                {
+                  g_warning ("Unable to setup internal child %s while building GtkContainer class %s",
+                             g_type_name (type), child->name);
+                  continue;
+                }
+            }
+
+          if (cpriv->connect_func)
+            gtk_builder_connect_signals_full (builder, cpriv->connect_func, container);
+          else
+            gtk_builder_connect_signals (builder, container);
+        }
+      else
+	{
+	  g_critical ("Unable to build GtkContainer class %s from template: %s", 
+		      g_type_name (type), error->message);
+	  g_error_free (error);
+	}
+
+      g_object_unref (builder);
+    }
+
+  gtk_widget_pop_composite_child ();
+  
+  return object;
+}
+
+static void
 gtk_container_set_property (GObject         *object,
                             guint            prop_id,
                             const GValue    *value,
diff --git a/gtk/gtkcontainer.h b/gtk/gtkcontainer.h
index 2db2488..774e3ea 100644
--- a/gtk/gtkcontainer.h
+++ b/gtk/gtkcontainer.h
@@ -31,6 +31,8 @@
 
 
 #include <gtk/gtkwidget.h>
+#include <gtk/gtkadjustment.h>
+#include <gtk/gtkbuilder.h>
 
 
 G_BEGIN_DECLS
@@ -42,10 +44,10 @@ G_BEGIN_DECLS
 #define GTK_IS_CONTAINER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CONTAINER))
 #define GTK_CONTAINER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CONTAINER, GtkContainerClass))
 
-
 typedef struct _GtkContainer              GtkContainer;
 typedef struct _GtkContainerPrivate       GtkContainerPrivate;
 typedef struct _GtkContainerClass         GtkContainerClass;
+typedef struct _GtkContainerClassPrivate  GtkContainerClassPrivate;
 
 struct _GtkContainer
 {
@@ -88,9 +90,10 @@ struct _GtkContainerClass
 
 
   /*< private >*/
-
   unsigned int _handle_border_width : 1;
 
+  GtkContainerClassPrivate *priv;
+  
   /* Padding for future expansion */
   void (*_gtk_reserved1) (void);
   void (*_gtk_reserved2) (void);
@@ -99,7 +102,6 @@ struct _GtkContainerClass
   void (*_gtk_reserved5) (void);
   void (*_gtk_reserved6) (void);
   void (*_gtk_reserved7) (void);
-  void (*_gtk_reserved8) (void);
 };
 
 
@@ -219,6 +221,16 @@ void    gtk_container_forall		     (GtkContainer *container,
 
 void    gtk_container_class_handle_border_width (GtkContainerClass *klass);
 
+/* Class-level functions */
+void    gtk_container_class_set_template_from_resource (GtkContainerClass *container_class,
+                                                         const gchar       *resource_path);
+void    gtk_container_class_set_connect_func           (GtkContainerClass *container_class,
+                                                        GtkBuilderConnectFunc connect_func);
+void    gtk_container_class_declare_internal_child     (GtkContainerClass *container_class,
+                                                        gboolean use_private,
+                                                        guint struct_offset,
+                                                        const gchar *name);
+
 GtkWidgetPath * gtk_container_get_path_for_child (GtkContainer      *container,
                                                   GtkWidget         *child);
 
diff --git a/gtk/gtkdialog.c b/gtk/gtkdialog.c
index b88ca5f..ecb87d5 100644
--- a/gtk/gtkdialog.c
+++ b/gtk/gtkdialog.c
@@ -195,9 +195,6 @@ static ResponseData * get_response_data          (GtkWidget    *widget,
                                                   gboolean      create);
 
 static void      gtk_dialog_buildable_interface_init     (GtkBuildableIface *iface);
-static GObject * gtk_dialog_buildable_get_internal_child (GtkBuildable  *buildable,
-                                                          GtkBuilder    *builder,
-                                                          const gchar   *childname);
 static gboolean  gtk_dialog_buildable_custom_tag_start   (GtkBuildable  *buildable,
                                                           GtkBuilder    *builder,
                                                           GObject       *child,
@@ -210,12 +207,6 @@ static void      gtk_dialog_buildable_custom_finished    (GtkBuildable  *buildab
                                                           const gchar   *tagname,
                                                           gpointer       user_data);
 
-
-enum {
-  PROP_0,
-  PROP_HAS_SEPARATOR
-};
-
 enum {
   RESPONSE,
   CLOSE,
@@ -227,17 +218,68 @@ static guint dialog_signals[LAST_SIGNAL];
 G_DEFINE_TYPE_WITH_CODE (GtkDialog, gtk_dialog, GTK_TYPE_WINDOW,
 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
 						gtk_dialog_buildable_interface_init))
+static void
+update_spacings (GtkDialog *dialog)
+{
+  GtkDialogPrivate *priv = dialog->priv;
+  gint content_area_border;
+  gint content_area_spacing;
+  gint button_spacing;
+  gint action_area_border;
+
+  gtk_widget_style_get (GTK_WIDGET (dialog),
+                        "content-area-border", &content_area_border,
+                        "content-area-spacing", &content_area_spacing,
+                        "button-spacing", &button_spacing,
+                        "action-area-border", &action_area_border,
+                        NULL);
+
+  
+  gtk_container_set_border_width (GTK_CONTAINER (priv->vbox),
+                                  content_area_border);
+  if (!_gtk_box_get_spacing_set (GTK_BOX (priv->vbox)))
+    {
+      gtk_box_set_spacing (GTK_BOX (priv->vbox), content_area_spacing);
+      _gtk_box_set_spacing_set (GTK_BOX (priv->vbox), FALSE);
+    }
+  gtk_box_set_spacing (GTK_BOX (priv->action_area), button_spacing);
+  gtk_container_set_border_width (GTK_CONTAINER (priv->action_area),
+                                  action_area_border);
+}
+
+static GObject *
+gtk_dialog_constructor (GType                  type,
+                        guint                  n_construct_properties,
+                        GObjectConstructParam *construct_properties)
+{
+  GObject *obj;
+  
+  obj = G_OBJECT_CLASS (gtk_dialog_parent_class)->constructor (type,
+                                                               n_construct_properties,
+                                                               construct_properties);
+
+  update_spacings (GTK_DIALOG (obj));
+
+  gtk_window_set_type_hint (GTK_WINDOW (obj), GDK_WINDOW_TYPE_HINT_DIALOG);
+  gtk_window_set_position (GTK_WINDOW (obj), GTK_WIN_POS_CENTER_ON_PARENT);
+
+  return obj;
+}
 
 static void
 gtk_dialog_class_init (GtkDialogClass *class)
 {
+  GtkContainerClass *container_class;
   GObjectClass *gobject_class;
   GtkWidgetClass *widget_class;
   GtkBindingSet *binding_set;
 
   gobject_class = G_OBJECT_CLASS (class);
   widget_class = GTK_WIDGET_CLASS (class);
+  container_class = GTK_CONTAINER_CLASS (class);
 
+  gobject_class->constructor = gtk_dialog_constructor;
+  
   widget_class->map = gtk_dialog_map;
   widget_class->style_updated = gtk_dialog_style_updated;
 
@@ -333,48 +375,30 @@ gtk_dialog_class_init (GtkDialogClass *class)
   binding_set = gtk_binding_set_by_class (class);
 
   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "close", 0);
-}
 
-static void
-update_spacings (GtkDialog *dialog)
-{
-  GtkDialogPrivate *priv = dialog->priv;
-  gint content_area_border;
-  gint content_area_spacing;
-  gint button_spacing;
-  gint action_area_border;
+  gtk_container_class_set_template_from_resource (container_class,
+                                                  "/org/gtk/libgtk/gtkdialog.ui");
 
-  gtk_widget_style_get (GTK_WIDGET (dialog),
-                        "content-area-border", &content_area_border,
-                        "content-area-spacing", &content_area_spacing,
-                        "button-spacing", &button_spacing,
-                        "action-area-border", &action_area_border,
-                        NULL);
-
-  
-  gtk_container_set_border_width (GTK_CONTAINER (priv->vbox),
-                                  content_area_border);
-  if (!_gtk_box_get_spacing_set (GTK_BOX (priv->vbox)))
-    {
-      gtk_box_set_spacing (GTK_BOX (priv->vbox), content_area_spacing);
-      _gtk_box_set_spacing_set (GTK_BOX (priv->vbox), FALSE);
-    }
-  gtk_box_set_spacing (GTK_BOX (priv->action_area),
-                       button_spacing);
-  gtk_container_set_border_width (GTK_CONTAINER (priv->action_area),
-                                  action_area_border);
+  gtk_container_class_declare_internal_child (container_class, TRUE,
+                                              G_STRUCT_OFFSET (GtkDialogPrivate, vbox),
+                                              "vbox");
+  gtk_container_class_declare_internal_child (container_class, TRUE,
+                                              G_STRUCT_OFFSET (GtkDialogPrivate, action_area),
+                                              "action_area");
 }
 
 static void
 gtk_dialog_init (GtkDialog *dialog)
 {
   GtkDialogPrivate *priv;
+  
+  dialog->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (dialog,
+                                                     GTK_TYPE_DIALOG,
+                                                     GtkDialogPrivate);
 
-  dialog->priv = G_TYPE_INSTANCE_GET_PRIVATE (dialog,
-                                              GTK_TYPE_DIALOG,
-                                              GtkDialogPrivate);
-  priv = dialog->priv;
-
+  priv->vbox = NULL;
+  priv->action_area = NULL;
+  
   /* To avoid breaking old code that prevents destroy on delete event
    * by connecting a handler, we have to have the FIRST signal
    * connection on the dialog.
@@ -383,25 +407,6 @@ gtk_dialog_init (GtkDialog *dialog)
                     "delete-event",
                     G_CALLBACK (gtk_dialog_delete_event_handler),
                     NULL);
-
-  priv->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
-  gtk_container_add (GTK_CONTAINER (dialog), priv->vbox);
-  gtk_widget_show (priv->vbox);
-
-  priv->action_area = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
-
-  gtk_button_box_set_layout (GTK_BUTTON_BOX (priv->action_area),
-                             GTK_BUTTONBOX_END);
-
-  gtk_box_pack_end (GTK_BOX (priv->vbox), priv->action_area,
-                    FALSE, TRUE, 0);
-  gtk_widget_show (priv->action_area);
-
-  gtk_window_set_type_hint (GTK_WINDOW (dialog),
-			    GDK_WINDOW_TYPE_HINT_DIALOG);
-  gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT);
-
-  update_spacings (dialog);
 }
 
 static GtkBuildableIface *parent_buildable_iface;
@@ -410,28 +415,10 @@ static void
 gtk_dialog_buildable_interface_init (GtkBuildableIface *iface)
 {
   parent_buildable_iface = g_type_interface_peek_parent (iface);
-  iface->get_internal_child = gtk_dialog_buildable_get_internal_child;
   iface->custom_tag_start = gtk_dialog_buildable_custom_tag_start;
   iface->custom_finished = gtk_dialog_buildable_custom_finished;
 }
 
-static GObject *
-gtk_dialog_buildable_get_internal_child (GtkBuildable *buildable,
-					 GtkBuilder   *builder,
-					 const gchar  *childname)
-{
-  GtkDialogPrivate *priv = GTK_DIALOG (buildable)->priv;
-
-  if (strcmp (childname, "vbox") == 0)
-    return G_OBJECT (priv->vbox);
-  else if (strcmp (childname, "action_area") == 0)
-    return G_OBJECT (priv->action_area);
-
-  return parent_buildable_iface->get_internal_child (buildable,
-                                                     builder,
-                                                     childname);
-}
-
 static gboolean
 gtk_dialog_delete_event_handler (GtkWidget   *widget,
                                  GdkEventAny *event,
diff --git a/gtk/gtkdialog.ui b/gtk/gtkdialog.ui
new file mode 100644
index 0000000..b078f5c
--- /dev/null
+++ b/gtk/gtkdialog.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <template parent="GtkWindow" class="GtkDialog" id="this">
+    <child>
+      <object class="GtkBox" id="vbox">
+        <property name="visible">True</property>
+        <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
+        <child>
+          <object class="GtkButtonBox" id="action_area">
+            <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/gtk/gtkmessagedialog.c b/gtk/gtkmessagedialog.c
index 7934280..dc19269 100644
--- a/gtk/gtkmessagedialog.c
+++ b/gtk/gtkmessagedialog.c
@@ -100,33 +100,29 @@
 
 struct _GtkMessageDialogPrivate
 {
-  GtkWidget     *image;
-  GtkWidget     *label;
-  GtkWidget     *message_area; /* vbox for the primary and secondary labels, and any extra content from the caller */
-  GtkWidget     *secondary_label;
-
-  guint          has_primary_markup : 1;
-  guint          has_secondary_text : 1;
-  guint          message_type       : 3;
+  GtkWidget *label;
+  GtkWidget *secondary_label;
+  GtkWidget *image;
+  GtkWidget *message_area;
+  guint message_type : 3;
+  guint buttons_type : 3;
+  guint has_primary_markup : 1;
+  guint has_secondary_text : 1;
 };
 
-static void gtk_message_dialog_style_updated (GtkWidget             *widget);
-
-static void gtk_message_dialog_set_property (GObject          *object,
-					     guint             prop_id,
-					     const GValue     *value,
-					     GParamSpec       *pspec);
-static void gtk_message_dialog_get_property (GObject          *object,
-					     guint             prop_id,
-					     GValue           *value,
-					     GParamSpec       *pspec);
-static void gtk_message_dialog_add_buttons  (GtkMessageDialog *message_dialog,
-					     GtkButtonsType    buttons);
-static void      gtk_message_dialog_buildable_interface_init     (GtkBuildableIface *iface);
-static GObject * gtk_message_dialog_buildable_get_internal_child (GtkBuildable  *buildable,
-                                                                  GtkBuilder    *builder,
-                                                                  const gchar   *childname);
-
+static GObject *gtk_message_dialog_constructor  (GType                  type,
+						 guint                  n_construct_properties,
+						 GObjectConstructParam *construct_params);
+static void     gtk_message_dialog_set_property (GObject          *object,
+						 guint             prop_id,
+						 const GValue     *value,
+						 GParamSpec       *pspec);
+static void     gtk_message_dialog_get_property (GObject          *object,
+						 guint             prop_id,
+						 GValue           *value,
+						 GParamSpec       *pspec);
+static void     gtk_message_dialog_add_buttons  (GtkMessageDialog *message_dialog,
+						 GtkButtonsType    buttons);
 
 enum {
   PROP_0,
@@ -140,46 +136,45 @@ enum {
   PROP_MESSAGE_AREA
 };
 
-G_DEFINE_TYPE_WITH_CODE (GtkMessageDialog, gtk_message_dialog, GTK_TYPE_DIALOG,
-                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
-                                                gtk_message_dialog_buildable_interface_init))
-
-static GtkBuildableIface *parent_buildable_iface;
+G_DEFINE_TYPE (GtkMessageDialog, gtk_message_dialog, GTK_TYPE_DIALOG)
 
 static void
-gtk_message_dialog_buildable_interface_init (GtkBuildableIface *iface)
+gtk_message_dialog_style_updated (GtkWidget *widget)
 {
-  parent_buildable_iface = g_type_interface_peek_parent (iface);
-  iface->get_internal_child = gtk_message_dialog_buildable_get_internal_child;
-  iface->custom_tag_start = parent_buildable_iface->custom_tag_start;
-  iface->custom_finished = parent_buildable_iface->custom_finished;
-}
+  GtkMessageDialog *dialog = GTK_MESSAGE_DIALOG (widget);
+  GtkWidget *parent;
+  gint border_width;
 
-static GObject *
-gtk_message_dialog_buildable_get_internal_child (GtkBuildable *buildable,
-                                                 GtkBuilder   *builder,
-                                                 const gchar  *childname)
-{
-  if (strcmp (childname, "message_area") == 0)
-    return G_OBJECT (gtk_message_dialog_get_message_area (GTK_MESSAGE_DIALOG (buildable)));
+  parent = gtk_widget_get_parent (gtk_message_dialog_get_image (dialog));
 
-  return parent_buildable_iface->get_internal_child (buildable, builder, childname);
-}
+  if (parent)
+    {
+      gtk_widget_style_get (widget, "message-border",
+                            &border_width, NULL);
 
+      gtk_container_set_border_width (GTK_CONTAINER (parent),
+                                      MAX (0, border_width - 7));
+    }
+
+  GTK_WIDGET_CLASS (gtk_message_dialog_parent_class)->style_updated (widget);
+}
 
 static void
 gtk_message_dialog_class_init (GtkMessageDialogClass *class)
 {
+  GtkContainerClass *container_class;
   GtkWidgetClass *widget_class;
   GObjectClass *gobject_class;
 
-  widget_class = GTK_WIDGET_CLASS (class);
   gobject_class = G_OBJECT_CLASS (class);
-  
+  widget_class = GTK_WIDGET_CLASS (class);
+  container_class = GTK_CONTAINER_CLASS (class);
+
   widget_class->style_updated = gtk_message_dialog_style_updated;
 
   gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_ALERT);
 
+  gobject_class->constructor  = gtk_message_dialog_constructor;
   gobject_class->set_property = gtk_message_dialog_set_property;
   gobject_class->get_property = gtk_message_dialog_get_property;
   
@@ -206,7 +201,7 @@ gtk_message_dialog_class_init (GtkMessageDialogClass *class)
 						      P_("The type of message"),
 						      GTK_TYPE_MESSAGE_TYPE,
                                                       GTK_MESSAGE_INFO,
-                                                      GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+                                                      GTK_PARAM_READWRITE));
   g_object_class_install_property (gobject_class,
                                    PROP_BUTTONS,
                                    g_param_spec_enum ("buttons",
@@ -294,32 +289,28 @@ gtk_message_dialog_class_init (GtkMessageDialogClass *class)
                                                         GTK_TYPE_WIDGET,
                                                         GTK_PARAM_READWRITE));
 
-  /**
-   * GtkMessageDialog:message-area:
-   *
-   * The #GtkVBox that corresponds to the message area of this dialog.  See
-   * gtk_message_dialog_get_message_area() for a detailed description of this
-   * area.
-   *
-   * Since: 2.22
-   */
-  g_object_class_install_property (gobject_class,
-				   PROP_MESSAGE_AREA,
-				   g_param_spec_object ("message-area",
-							P_("Message area"),
-							P_("GtkVBox that holds the dialog's primary and secondary labels"),
-							GTK_TYPE_WIDGET,
-							GTK_PARAM_READABLE));
-
   g_type_class_add_private (gobject_class, sizeof (GtkMessageDialogPrivate));
+
+  gtk_container_class_set_template_from_resource (container_class,
+                                                  "/org/gtk/libgtk/gtkmessagedialog.ui");
+
+  gtk_container_class_declare_internal_child (container_class, TRUE,
+                                              G_STRUCT_OFFSET (GtkMessageDialogPrivate, label),
+                                              "label");
+  gtk_container_class_declare_internal_child (container_class, TRUE,
+                                              G_STRUCT_OFFSET (GtkMessageDialogPrivate, secondary_label),
+                                              "secondary_label");
+  gtk_container_class_declare_internal_child (container_class, TRUE,
+                                              G_STRUCT_OFFSET (GtkMessageDialogPrivate, image),
+                                              "image");
+  gtk_container_class_declare_internal_child (container_class, TRUE,
+                                              G_STRUCT_OFFSET (GtkMessageDialogPrivate, message_area),
+                                              "action_area");
 }
 
 static void
 gtk_message_dialog_init (GtkMessageDialog *dialog)
 {
-  GtkWidget *hbox;
-  GtkDialog *message_dialog = GTK_DIALOG (dialog);
-  GtkWidget *action_area, *content_area;
   GtkMessageDialogPrivate *priv;
 
   dialog->priv = G_TYPE_INSTANCE_GET_PRIVATE (dialog,
@@ -327,64 +318,14 @@ gtk_message_dialog_init (GtkMessageDialog *dialog)
                                               GtkMessageDialogPrivate);
   priv = dialog->priv;
 
-  content_area = gtk_dialog_get_content_area (message_dialog);
-  action_area = gtk_dialog_get_action_area (message_dialog);
-
   gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
   gtk_window_set_title (GTK_WINDOW (dialog), "");
   gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), TRUE);
 
   priv->has_primary_markup = FALSE;
   priv->has_secondary_text = FALSE;
-  priv->secondary_label = gtk_label_new (NULL);
-  gtk_widget_set_no_show_all (priv->secondary_label, TRUE);
-
-  priv->label = gtk_label_new (NULL);
-  priv->image = gtk_image_new_from_stock (NULL, GTK_ICON_SIZE_DIALOG);
-  gtk_widget_set_halign (priv->image, GTK_ALIGN_CENTER);
-  gtk_widget_set_valign (priv->image, GTK_ALIGN_START);
-
-  gtk_label_set_line_wrap  (GTK_LABEL (priv->label), TRUE);
-  gtk_label_set_selectable (GTK_LABEL (priv->label), TRUE);
-  gtk_widget_set_halign (priv->label, GTK_ALIGN_START);
-  gtk_widget_set_valign (priv->label, GTK_ALIGN_START);
-
-  gtk_label_set_line_wrap  (GTK_LABEL (priv->secondary_label), TRUE);
-  gtk_label_set_selectable (GTK_LABEL (priv->secondary_label), TRUE);
-  gtk_widget_set_halign (priv->secondary_label, GTK_ALIGN_START);
-  gtk_widget_set_valign (priv->secondary_label, GTK_ALIGN_START);
-
-  gtk_misc_set_alignment (GTK_MISC (priv->label), 0.0, 0.0);
-  gtk_misc_set_alignment (GTK_MISC (priv->secondary_label), 0.0, 0.0);
-
-  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
-  priv->message_area = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
-
-  gtk_box_pack_start (GTK_BOX (priv->message_area), priv->label,
-                      FALSE, FALSE, 0);
-
-  gtk_box_pack_start (GTK_BOX (priv->message_area), priv->secondary_label,
-                      TRUE, TRUE, 0);
-
-  gtk_box_pack_start (GTK_BOX (hbox), priv->image,
-                      FALSE, FALSE, 0);
-
-  gtk_box_pack_start (GTK_BOX (hbox), priv->message_area,
-                      TRUE, TRUE, 0);
-
-  gtk_box_pack_start (GTK_BOX (content_area),
-                      hbox,
-                      FALSE, FALSE, 0);
 
   gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
-  gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
-  gtk_box_set_spacing (GTK_BOX (content_area), 14); /* 14 + 2 * 5 = 24 */
-  gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
-  gtk_box_set_spacing (GTK_BOX (action_area), 6);
-
-  gtk_message_dialog_style_updated (GTK_WIDGET (dialog));
-
-  gtk_widget_show_all (hbox);
 }
 
 static void
@@ -469,6 +410,30 @@ setup_type (GtkMessageDialog *dialog,
     }
 }
 
+static GObject *
+gtk_message_dialog_constructor (GType                  type,
+				guint                  n_construct_properties,
+				GObjectConstructParam *construct_params)
+{
+  GObject *object;
+  GtkMessageDialog *dialog;
+  GtkMessageDialogPrivate *priv;
+
+  object = 
+    G_OBJECT_CLASS (gtk_message_dialog_parent_class)->constructor (type, 
+								   n_construct_properties, 
+								   construct_params);
+
+  dialog = GTK_MESSAGE_DIALOG (object);
+  priv = dialog->priv;
+
+  /* Setup widget's now that they've been built */
+  setup_type (dialog, priv->message_type);
+  gtk_message_dialog_add_buttons (dialog, priv->buttons_type);
+
+  return object;
+}
+
 static void 
 gtk_message_dialog_set_property (GObject      *object,
 				 guint         prop_id,
@@ -481,10 +446,17 @@ gtk_message_dialog_set_property (GObject      *object,
   switch (prop_id)
     {
     case PROP_MESSAGE_TYPE:
-      setup_type (dialog, g_value_get_enum (value));
+
+      if (priv->image)
+	setup_type (dialog, g_value_get_enum (value));
+      else
+	/* Deffer assignment at construct time until image is built */
+	priv->message_type = g_value_get_enum (value);
+
       break;
+
     case PROP_BUTTONS:
-      gtk_message_dialog_add_buttons (dialog, g_value_get_enum (value));
+      priv->buttons_type = g_value_get_enum (value);
       break;
     case PROP_TEXT:
       if (priv->has_primary_markup)
@@ -529,7 +501,6 @@ gtk_message_dialog_set_property (GObject      *object,
     case PROP_IMAGE:
       gtk_message_dialog_set_image (dialog, g_value_get_object (value));
       break;
-
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -573,9 +544,6 @@ gtk_message_dialog_get_property (GObject     *object,
     case PROP_IMAGE:
       g_value_set_object (value, priv->image);
       break;
-    case PROP_MESSAGE_AREA:
-      g_value_set_object (value, priv->message_area);
-      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -742,12 +710,19 @@ gtk_message_dialog_set_image (GtkMessageDialog *dialog,
       gtk_widget_set_valign (image, GTK_ALIGN_START);
     }
 
-  priv->message_type = GTK_MESSAGE_OTHER;
-
-  parent = gtk_widget_get_parent (priv->image);
-  gtk_container_add (GTK_CONTAINER (parent), image);
-  gtk_container_remove (GTK_CONTAINER (parent), priv->image);
-  gtk_box_reorder_child (GTK_BOX (parent), image, 0);
+  /* Only change the message type and the image if
+   * the image is not already in the dialog (which is
+   * the case when GtkBuilder builds the image).
+   */
+  if (priv->image && priv->image != image)
+    {
+      priv->message_type = GTK_MESSAGE_OTHER;
+  
+      parent = gtk_widget_get_parent (priv->image);
+      gtk_container_add (GTK_CONTAINER (parent), image);
+      gtk_container_remove (GTK_CONTAINER (parent), priv->image);
+      gtk_box_reorder_child (GTK_BOX (parent), image, 0);
+    }
 
   priv->image = image;
 
@@ -997,24 +972,3 @@ gtk_message_dialog_add_buttons (GtkMessageDialog* message_dialog,
 
   g_object_notify (G_OBJECT (message_dialog), "buttons");
 }
-
-static void
-gtk_message_dialog_style_updated (GtkWidget *widget)
-{
-  GtkMessageDialog *dialog = GTK_MESSAGE_DIALOG (widget);
-  GtkWidget *parent;
-  gint border_width;
-
-  parent = gtk_widget_get_parent (gtk_message_dialog_get_image (dialog));
-
-  if (parent)
-    {
-      gtk_widget_style_get (widget, "message-border",
-                            &border_width, NULL);
-
-      gtk_container_set_border_width (GTK_CONTAINER (parent),
-                                      MAX (0, border_width - 7));
-    }
-
-  GTK_WIDGET_CLASS (gtk_message_dialog_parent_class)->style_updated (widget);
-}
diff --git a/gtk/gtkmessagedialog.ui b/gtk/gtkmessagedialog.ui
new file mode 100644
index 0000000..8f2332f
--- /dev/null
+++ b/gtk/gtkmessagedialog.ui
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <template parent="GtkDialog" class="GtkMessageDialog" id="this">
+    <child internal-child="vbox">
+      <object class="GtkBox" id="vbox">
+        <property name="spacing">14</property>
+        <child>
+          <object class="GtkBox" id="hbox1">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+            <property name="spacing">12</property>
+            <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
+            <child>
+              <object class="GtkImage" id="image">
+                <property name="visible">True</property>
+                <property name="yalign">0</property>
+                <property name="icon-size">6</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="vbox1">
+                <property name="visible">True</property>
+                <property name="spacing">12</property>
+                <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
+                <child>
+                  <object class="GtkLabel" id="label">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="yalign">0</property>
+                    <property name="label" translatable="yes">primary message</property>
+                    <property name="wrap">True</property>
+                    <property name="selectable">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="secondary_label">
+                    <property name="visible">False</property>
+                    <property name="no_show_all">True</property>
+                    <property name="xalign">0</property>
+                    <property name="yalign">0</property>
+                    <property name="label" translatable="yes">secondary message</property>
+                    <property name="wrap">True</property>
+                    <property name="selectable">True</property>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="action_area">
+            <property name="border_width">5</property>
+            <property name="spacing">6</property>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>



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