[glade/composite-templates: 7/9] Revert "Reverting all composite templates support until it lands on GTK"



commit 6552f5f48d02bfab7e7624970c3dd623511dabea
Author: Juan Pablo Ugarte <juanpablougarte gmai com>
Date:   Thu Jan 31 17:15:01 2013 +0100

    Revert "Reverting all composite templates support until it lands on GTK"
    
    This reverts commit e12f3a4ad551591e7de6805c2da13eb7088bc2f3.

 configure.ac                       |    2 +-
 gladeui/Makefile.am                |    2 +
 gladeui/glade-app.c                |    3 +
 gladeui/glade-composite-template.c |  310 ++++++++++++++++++++++++++++++++++++
 gladeui/glade-composite-template.h |   43 +++++
 gladeui/glade-editor-table.c       |  176 +++++++++++++++++----
 gladeui/glade-palette.c            |   65 +++++++-
 gladeui/glade-project.c            |   41 +++++-
 gladeui/glade-project.h            |    1 +
 gladeui/glade-property.c           |    5 +-
 gladeui/glade-widget-adaptor.c     |  273 ++++++++++++++++++++++++++------
 gladeui/glade-widget-adaptor.h     |   22 ++-
 gladeui/glade-widget.c             |   83 +++++++++--
 gladeui/glade-widget.h             |    5 +
 gladeui/glade-xml-utils.h          |    2 +
 gladeui/glade.h                    |    1 +
 plugins/gtk+/glade-gtk.c           |  200 +++++++++++++-----------
 plugins/gtk+/gtk+.xml.in           |   14 ++-
 18 files changed, 1049 insertions(+), 199 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 7811803..3b1bf65 100644
--- a/configure.ac
+++ b/configure.ac
@@ -254,7 +254,7 @@ dnl ================================================================
 dnl Check for extra functions
 dnl ================================================================
 AC_CHECK_FUNCS(gtk_builder_add_from_resource)
-
+AC_CHECK_FUNCS(gtk_container_class_set_template_from_string)
 
 AC_SUBST([GLADE_PREFIX])
 
diff --git a/gladeui/Makefile.am b/gladeui/Makefile.am
index 141aaed..288848d 100644
--- a/gladeui/Makefile.am
+++ b/gladeui/Makefile.am
@@ -53,6 +53,7 @@ libgladeui_2_la_SOURCES = \
 	glade-object-stub.c \
 	glade-xml-utils.c \
 	glade-catalog.c \
+	glade-composite-template.c \
 	glade-widget-adaptor.c \
 	glade-widget.c \
 	glade-property-class.c \
@@ -122,6 +123,7 @@ libgladeuiinclude_HEADERS = \
 	glade-design-view.h \
 	glade-widget.h \
 	glade-widget-adaptor.h \
+	glade-composite-template.h \
 	glade-property.h \
 	glade-property-class.h \
 	glade-utils.h \
diff --git a/gladeui/glade-app.c b/gladeui/glade-app.c
index ccff86a..402c566 100644
--- a/gladeui/glade-app.c
+++ b/gladeui/glade-app.c
@@ -39,6 +39,7 @@
 #include "glade-design-layout.h"
 #include "glade-marshallers.h"
 #include "glade-accumulators.h"
+#include "glade-composite-template.h"
 
 #include <string.h>
 #include <glib.h>
@@ -349,6 +350,8 @@ glade_app_init (GladeApp *app)
 
   /* Load the configuration file */
   priv->config = g_key_file_ref (glade_app_get_config ());
+
+  glade_composite_template_load_directory (g_get_user_special_dir (G_USER_DIRECTORY_TEMPLATES));
 }
 
 static void
diff --git a/gladeui/glade-composite-template.c b/gladeui/glade-composite-template.c
new file mode 100644
index 0000000..81ede79
--- /dev/null
+++ b/gladeui/glade-composite-template.c
@@ -0,0 +1,310 @@
+/*
+ * glade-composite-template.c
+ *
+ * Copyright (C) 2012 Juan Pablo Ugarte
+ *
+ * Author: Juan Pablo Ugarte <juanpablougarte gmail com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+
+#include <glib/gi18n-lib.h>
+#include "glade-composite-template.h"
+#include "glade-app.h"
+#include "glade-utils.h"
+
+typedef struct
+{
+  gboolean right_id;
+  GType parent;
+  const gchar *type_name;
+} ParseData;
+
+static void
+start_element (GMarkupParseContext  *context,
+	       const gchar          *element_name,
+	       const gchar         **attribute_names,
+	       const gchar         **attribute_values,
+	       gpointer              user_data,
+	       GError              **error)
+{
+  ParseData *state = user_data;
+
+  if (g_strcmp0 (element_name, "template") == 0)
+    {
+      gint i;
+
+      for (i = 0; attribute_names[i]; i++)
+        {
+          if (!g_strcmp0 (attribute_names[i], "parent"))
+            state->parent = glade_util_get_type_from_name (attribute_values[i], FALSE);
+          else if (!g_strcmp0 (attribute_names[i], "class"))
+            state->type_name = g_intern_string (attribute_values[i]);
+	  else if (!g_strcmp0 (attribute_names[i], "id"))
+	    state->right_id = (g_strcmp0 (attribute_values[i], "this") == 0);
+        }
+    }
+}
+
+static gboolean
+parse_template (const gchar *template_str, GType *parent, const gchar **type_name)
+{
+  GMarkupParser parser = { start_element };
+  ParseData state = { FALSE, G_TYPE_INVALID, NULL };
+  GMarkupParseContext *context;
+
+  context = g_markup_parse_context_new (&parser,
+                                        G_MARKUP_TREAT_CDATA_AS_TEXT |
+                                        G_MARKUP_PREFIX_ERROR_POSITION,
+                                        &state, NULL);
+
+  g_markup_parse_context_parse (context, template_str, -1, NULL);
+  g_markup_parse_context_end_parse (context, NULL);
+  g_markup_parse_context_free (context);
+  
+  if (!g_type_is_a (state.parent, GTK_TYPE_CONTAINER))
+    {
+      g_warning ("Composite templates should derive from GtkContainer");
+      return FALSE;
+    }
+  
+  if (parent) *parent = state.parent;
+  if (type_name) *type_name = state.type_name;
+
+  return state.right_id;
+}
+
+static void
+composite_template_derived_init (GTypeInstance *instance, gpointer g_class)
+{
+}
+
+#if !HAVE_GTK_CONTAINER_CLASS_SET_TEMPLATE_FROM_STRING
+static GObject *
+composite_template_constructor (GType type,
+                                guint n_construct_properties,
+                                GObjectConstructParam *construct_properties)
+{
+  GladeWidgetAdaptor *adaptor;
+  GObjectClass *parent_class;
+  GObject *obj;
+
+  parent_class = g_type_class_peek (g_type_parent (type));
+  obj = parent_class->constructor (type,
+                                   n_construct_properties,
+                                   construct_properties); 
+/*
+  adaptor = glade_widget_adaptor_get_by_type (type);
+
+
+  glade_widget_adaptor_get_*/
+  return obj;
+}
+#endif
+
+static void
+composite_template_derived_class_init (gpointer g_class, gpointer class_data)
+{
+#if !HAVE_GTK_CONTAINER_CLASS_SET_TEMPLATE_FROM_STRING
+  GObjectClass *object_class = g_class;
+  object_class->constructor = composite_template_constructor;
+#endif
+}
+
+static inline GType
+generate_type (GType parent, const gchar *type_name)
+{
+  GTypeQuery query;
+
+  g_type_query (parent, &query);
+
+  return g_type_register_static_simple (parent, type_name,
+                                        query.class_size,
+                                        composite_template_derived_class_init,
+                                        query.instance_size,
+                                        composite_template_derived_init,
+                                        0);
+}
+
+/* Public API */
+
+/**
+ * glade_composite_template_load_from_string:
+ * @template_xml: a #GtkBuilder UI description string
+ * 
+ * This function will create a new GType from the template UI description defined
+ * by @template_xml and its corresponding #GladeWidgetAdator
+ * 
+ * Returns: A newlly created and registered #GladeWidgetAdptor or NULL if @template_xml is malformed.
+ */
+GladeWidgetAdaptor *
+glade_composite_template_load_from_string (const gchar *template_xml)
+{
+  const gchar *type_name = NULL;
+  GType parent;
+
+  g_return_val_if_fail (template_xml != NULL, NULL);
+
+  if (parse_template (template_xml, &parent, &type_name))
+    {
+      GladeWidgetAdaptor *adaptor;
+      GType template_type;
+
+      /* Generate dummy template type */
+      template_type = generate_type (parent, type_name);
+      
+      /* Create adaptor for template */
+      adaptor = glade_widget_adaptor_from_composite_template (template_type,
+                                                              template_xml,
+                                                              type_name,
+                                                              NULL); /* TODO: generate icon name from parent icon plus some emblem */                        
+      /* Register adaptor */
+      glade_widget_adaptor_register (adaptor);
+
+      return adaptor;
+    }
+  else
+    g_warning ("Could not parse template");
+
+  return NULL;
+}
+
+/**
+ * glade_composite_template_load_from_file:
+ * @path: a filename to load
+ * 
+ * Loads a composite template from a file.
+ * See glade_composite_template_load_from_string() for details.
+ * 
+ * Returns: A newlly created and registered #GladeWidgetAdaptor or NULL.
+ */
+GladeWidgetAdaptor *
+glade_composite_template_load_from_file (const gchar *path)
+{
+  GladeWidgetAdaptor *adaptor;
+  GError *error = NULL;
+  gchar *contents;
+
+  g_return_val_if_fail (path != NULL, NULL);
+  
+  if (!g_file_get_contents (path, &contents, NULL, &error))
+    {
+      g_warning ("Could not load template `%s` %s", path, error->message);
+      g_error_free (error);
+      return NULL;
+    }
+  
+  if ((adaptor = glade_composite_template_load_from_string (contents)))
+    g_object_set (adaptor, "template-path", path, NULL);
+  
+  g_free (contents);
+
+  return adaptor;
+}
+
+/**
+ * glade_composite_template_load_directory:
+ * @directory: a directory path.
+ * 
+ * Loads every .ui composite template found in @directory
+ */
+void
+glade_composite_template_load_directory (const gchar *directory)
+{
+  GError *error = NULL;
+  const gchar *name;
+  GDir *dir;
+
+  g_return_if_fail (directory != NULL);
+
+  if (!(dir = g_dir_open (directory, 0, &error)))
+    {
+      g_warning ("Could not open directory `%s` %s", directory, error->message);
+      g_error_free (error);
+      return;
+    }
+
+  while ((name = g_dir_read_name (dir)))
+    {
+      if (g_str_has_suffix (name, ".ui"))
+	{
+	  gchar *fullname = g_build_filename (directory, name, NULL);
+	  glade_composite_template_load_from_file (fullname);
+	  g_free (fullname);
+	}
+    }
+
+  g_dir_close (dir);
+}
+
+/**
+ * glade_composite_template_save_from_widget:
+ * @gwidget: a #GladeWidget
+ * @template_class: the name of the new composite template class
+ * @filename: a file name to save the template
+ * @replace: True if you want to replace @gwidget with a new widget of type @template_class
+ * 
+ * Saves a copy of @gwidget as a composite template in @filename with @template_class
+ * as the class name
+ */
+void
+glade_composite_template_save_from_widget (GladeWidget *gwidget,
+                                           const gchar *template_class,
+                                           const gchar *filename,
+                                           gboolean replace)
+{
+  GladeProject *project;
+  gchar *template_xml;
+  GladeWidget *dup;
+  
+  g_return_if_fail (GLADE_IS_WIDGET (gwidget));
+  g_return_if_fail (template_class && filename);
+  g_return_if_fail (GTK_IS_CONTAINER (glade_widget_get_object (gwidget)));
+
+  project = glade_project_new ();
+  dup = glade_widget_dup (gwidget, TRUE);
+
+  /* make dupped widget a template */
+  glade_widget_set_name (dup, "this");
+  glade_widget_set_template_class (dup, template_class);
+  
+  glade_project_add_object (project, glade_widget_get_object (dup));
+  template_xml = glade_project_dump_string (project);
+
+  g_file_set_contents (filename, template_xml, -1, NULL);
+
+  if (replace)
+    {
+      GladeProject *project = glade_widget_get_project (gwidget);
+      GladeWidget *parent = glade_widget_get_parent (gwidget);
+      GladeWidgetAdaptor *new_adaptor;
+      GList widgets = {0, };
+      
+      /* Create it at run time */
+      if ((new_adaptor = glade_composite_template_load_from_string (template_xml)))
+        g_object_set (new_adaptor, "template-path", filename, NULL);
+
+      glade_command_push_group (_("Create new composite type %s"), template_class);
+      widgets.data = gwidget;
+      glade_command_cut (&widgets);
+      glade_command_create (new_adaptor, parent, NULL, project);
+      glade_command_pop_group ();
+    }
+  
+  g_free (template_xml);
+  g_object_unref (project);
+}
diff --git a/gladeui/glade-composite-template.h b/gladeui/glade-composite-template.h
new file mode 100644
index 0000000..46a2373
--- /dev/null
+++ b/gladeui/glade-composite-template.h
@@ -0,0 +1,43 @@
+/*
+ * glade-composite-template.h
+ *
+ * Copyright (C) 2012 Juan Pablo Ugarte
+ *
+ * Author: Juan Pablo Ugarte <juanpablougarte gmail com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GLADE_COMPOSITE_TEMPLATE_H__
+#define __GLADE_COMPOSITE_TEMPLATE_H__
+
+#include <gladeui/glade-widget.h>
+
+G_BEGIN_DECLS
+
+GladeWidgetAdaptor *glade_composite_template_load_from_string (const gchar *template_xml);
+
+GladeWidgetAdaptor *glade_composite_template_load_from_file (const gchar *path);
+
+void glade_composite_template_load_directory (const gchar *directory);
+
+void glade_composite_template_save_from_widget (GladeWidget *gwidget,
+                                                const gchar *template_class,
+                                                const gchar *filename,
+                                                gboolean     replace);
+
+G_END_DECLS
+
+#endif /* __GLADE_COMPOSITE_TEMPLATE_H__ */
diff --git a/gladeui/glade-editor-table.c b/gladeui/glade-editor-table.c
index 101bc0a..a82a29c 100644
--- a/gladeui/glade-editor-table.c
+++ b/gladeui/glade-editor-table.c
@@ -49,6 +49,8 @@ struct _GladeEditorTablePrivate
 			  * entry which will not be created from a
 			  * GladeProperty but rather from code.
 			  */
+  GtkWidget *tmpl_label;
+  GtkWidget *tmpl_entry;
 
   GList *properties; /* A list of GladeEditorPropery items.
 		      * For each row in the gtk_table, there is a
@@ -205,58 +207,122 @@ widget_finalized (GladeEditorTable * table, GladeWidget * where_widget_was)
   glade_editable_load (GLADE_EDITABLE (table), NULL);
 }
 
+static void
+glade_editor_table_update_show_template (GladeEditable *editable, gboolean load)
+{
+  GladeEditorTable *table = GLADE_EDITOR_TABLE (editable);
+  GladeEditorTablePrivate *priv = table->priv;
+  GladeWidget *widget = priv->loaded_widget;
+  const gchar *tooltip = NULL;
+  GtkEntry *entry;
+
+  if (!priv->name_entry || !priv->tmpl_entry) return;
+
+  entry = GTK_ENTRY (priv->name_entry);
+
+  if (load) gtk_editable_set_editable (GTK_EDITABLE (priv->name_entry), TRUE);
+  
+  if (widget)
+    {
+      if (!glade_widget_get_parent (widget))
+        {
+          GladeProject *project;
+          if (glade_widget_get_template_class (widget))
+            {
+              if (load) gtk_editable_set_editable (GTK_EDITABLE (priv->name_entry), FALSE);
+              gtk_entry_set_icon_from_stock (entry, GTK_ENTRY_ICON_SECONDARY, "gtk-delete");
+              tooltip = _("Click to disable template class");
+            }
+          else if (!g_strcmp0 (glade_widget_get_name (widget), "this") ||
+                   ((project = glade_widget_get_project (widget)) &&
+                   glade_project_available_widget_name (project, widget, "this")))
+            {
+              gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, "glade");
+              tooltip = _("Click to make this widget a template class (It will be renamed to 'this')");
+            }
+          else
+            gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, NULL);
+        }
+      else
+        gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, NULL);
+    }
+  else
+    {
+      gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, NULL);
+      gtk_widget_hide (priv->tmpl_label);
+      gtk_widget_hide (priv->tmpl_entry);
+    }
+
+  if (gtk_editable_get_editable (GTK_EDITABLE (priv->name_entry)))
+    {
+      gtk_widget_hide (priv->tmpl_label);
+      gtk_widget_hide (priv->tmpl_entry);
+    }
+  else
+    {
+      gtk_widget_show (priv->tmpl_label);
+      gtk_widget_show (priv->tmpl_entry);
+    }
+
+  gtk_entry_set_icon_tooltip_text (entry, GTK_ENTRY_ICON_SECONDARY, tooltip);
+}
 
 static void
 glade_editor_table_load (GladeEditable * editable, GladeWidget * widget)
 {
   GladeEditorTable *table = GLADE_EDITOR_TABLE (editable);
+  GladeEditorTablePrivate *priv = table->priv;
   GladeEditorProperty *property;
   GList *list;
 
   /* abort mission */
-  if (table->priv->loaded_widget == widget)
+  if (priv->loaded_widget == widget)
     return;
 
-  if (table->priv->loaded_widget)
+  if (priv->loaded_widget)
     {
-      g_signal_handlers_disconnect_by_func (G_OBJECT (table->priv->loaded_widget),
+      g_signal_handlers_disconnect_by_func (G_OBJECT (priv->loaded_widget),
                                             G_CALLBACK (widget_name_changed),
                                             table);
 
       /* The widget could die unexpectedly... */
-      g_object_weak_unref (G_OBJECT (table->priv->loaded_widget),
+      g_object_weak_unref (G_OBJECT (priv->loaded_widget),
                            (GWeakNotify) widget_finalized, table);
     }
 
-  table->priv->loaded_widget = widget;
+  priv->loaded_widget = widget;
 
   BLOCK_NAME_ENTRY_CB (table);
 
-  if (table->priv->loaded_widget)
+  if (priv->loaded_widget)
     {
-      g_signal_connect (G_OBJECT (table->priv->loaded_widget), "notify::name",
+      g_signal_connect (G_OBJECT (priv->loaded_widget), "notify::name",
                         G_CALLBACK (widget_name_changed), table);
 
       /* The widget could die unexpectedly... */
-      g_object_weak_ref (G_OBJECT (table->priv->loaded_widget),
+      g_object_weak_ref (G_OBJECT (priv->loaded_widget),
                          (GWeakNotify) widget_finalized, table);
 
-      if (table->priv->name_entry)
-        gtk_entry_set_text (GTK_ENTRY (table->priv->name_entry), 
+      if (priv->name_entry)
+        gtk_entry_set_text (GTK_ENTRY (priv->name_entry), 
 			    glade_widget_get_name (widget));
 
     }
-  else if (table->priv->name_entry)
-    gtk_entry_set_text (GTK_ENTRY (table->priv->name_entry), "");
+  else if (priv->name_entry)
+    gtk_entry_set_text (GTK_ENTRY (priv->name_entry), "");
 
   UNBLOCK_NAME_ENTRY_CB (table);
 
   /* Sync up properties, even if widget is NULL */
-  for (list = table->priv->properties; list; list = list->next)
+  for (list = priv->properties; list; list = g_list_next (list))
     {
       property = list->data;
       glade_editor_property_load_by_widget (property, widget);
     }
+
+  if (priv->tmpl_entry)
+    glade_editor_property_load_by_widget (GLADE_EDITOR_PROPERTY (priv->tmpl_entry), widget);
+  glade_editor_table_update_show_template (editable, TRUE);
 }
 
 static void
@@ -403,32 +469,84 @@ append_items (GladeEditorTable * table,
 }
 
 static void
-append_name_field (GladeEditorTable * table)
+on_name_icon_press (GtkEntry *entry,
+                    GtkEntryIconPosition icon_pos,
+                    GdkEvent  *event,
+                    GladeEditorTable *table)
+{
+  GladeEditorTablePrivate *priv = table->priv;
+  gboolean editable;
+
+  if (!priv->loaded_widget || glade_widget_get_parent (priv->loaded_widget))
+    return;
+
+  if ((editable = gtk_editable_get_editable (GTK_EDITABLE (priv->name_entry))))
+    glade_command_set_name (priv->loaded_widget, "this");
+  else
+    {
+      GladeProperty *property = glade_widget_get_property (priv->loaded_widget, "glade-template-class");
+      glade_command_set_property (property, NULL);
+    }
+
+  gtk_editable_set_editable (GTK_EDITABLE (priv->name_entry), !editable);
+  glade_editor_table_update_show_template (GLADE_EDITABLE (table), FALSE);
+}
+
+static void
+append_name_field (GladeWidgetAdaptor *adaptor, GladeEditorTable *table)
 {
+  GladeEditorTablePrivate *priv = table->priv;
   gchar *text = _("The Object's name");
 
   /* Name */
-  table->priv->name_label = gtk_label_new (_("Name:"));
-  gtk_misc_set_alignment (GTK_MISC (table->priv->name_label), 0.0, 0.5);
-  gtk_widget_show (table->priv->name_label);
-  gtk_widget_set_no_show_all (table->priv->name_label, TRUE);
+  priv->name_label = gtk_label_new (_("Name:"));
+  gtk_misc_set_alignment (GTK_MISC (priv->name_label), 0.0, 0.5);
+  gtk_widget_show (priv->name_label);
+  gtk_widget_set_no_show_all (priv->name_label, TRUE);
 
-  table->priv->name_entry = gtk_entry_new ();
-  gtk_widget_show (table->priv->name_entry);
-  gtk_widget_set_no_show_all (table->priv->name_entry, TRUE);
+  priv->name_entry = gtk_entry_new ();
+  gtk_widget_show (priv->name_entry);
+  gtk_widget_set_no_show_all (priv->name_entry, TRUE);
 
-  gtk_widget_set_tooltip_text (table->priv->name_label, text);
-  gtk_widget_set_tooltip_text (table->priv->name_entry, text);
+  gtk_widget_set_tooltip_text (priv->name_label, text);
+  gtk_widget_set_tooltip_text (priv->name_entry, text);
 
-  g_signal_connect (G_OBJECT (table->priv->name_entry), "activate",
+  g_signal_connect (G_OBJECT (priv->name_entry), "activate",
                     G_CALLBACK (widget_name_edited), table);
-  g_signal_connect (G_OBJECT (table->priv->name_entry), "changed",
+  g_signal_connect (G_OBJECT (priv->name_entry), "changed",
                     G_CALLBACK (widget_name_edited), table);
 
-  glade_editor_table_attach (table, table->priv->name_label, 0, table->priv->rows);
-  glade_editor_table_attach (table, table->priv->name_entry, 1, table->priv->rows);
+  glade_editor_table_attach (table, priv->name_label, 0, priv->rows);
+  glade_editor_table_attach (table, priv->name_entry, 1, priv->rows);
 
-  table->priv->rows++;
+  priv->rows++;
+
+  if (g_type_is_a (glade_widget_adaptor_get_object_type (adaptor), GTK_TYPE_CONTAINER))
+    {
+      gchar *class_text = _("The template class name this widget defines");
+      
+      /* Template class */
+      priv->tmpl_label = gtk_label_new (_("Template Class:"));
+      gtk_misc_set_alignment (GTK_MISC (priv->tmpl_label), 0.0, 0.5);
+      gtk_widget_set_no_show_all (priv->tmpl_label, TRUE);
+
+      priv->tmpl_entry = GTK_WIDGET (glade_widget_adaptor_create_eprop_by_name (adaptor, "glade-template-class", FALSE, TRUE));
+      gtk_widget_hide (priv->tmpl_entry);
+      gtk_widget_set_no_show_all (priv->tmpl_entry, TRUE);
+      g_signal_connect (priv->name_entry, "icon-press", G_CALLBACK (on_name_icon_press), table);
+
+      gtk_widget_set_tooltip_text (priv->tmpl_label, class_text);
+      gtk_widget_set_tooltip_text (priv->tmpl_entry, class_text);
+
+      glade_editor_table_attach (table, priv->tmpl_label, 0, priv->rows);
+      glade_editor_table_attach (table, priv->tmpl_entry, 1, priv->rows);
+
+      priv->rows++;
+    }
+  else
+    {
+      priv->tmpl_label = priv->tmpl_entry = NULL;
+    }
 }
 
 /**
@@ -453,7 +571,7 @@ glade_editor_table_new (GladeWidgetAdaptor * adaptor, GladeEditorPageType type)
   table->priv->type = type;
 
   if (type == GLADE_PAGE_GENERAL)
-    append_name_field (table);
+    append_name_field (adaptor, table);
 
   append_items (table, adaptor, type);
 
diff --git a/gladeui/glade-palette.c b/gladeui/glade-palette.c
index e738a26..0fe5f3f 100644
--- a/gladeui/glade-palette.c
+++ b/gladeui/glade-palette.c
@@ -69,6 +69,8 @@ struct _GladePalettePrivate
 
   GladeWidgetAdaptor *local_selection;
   GHashTable         *button_table;
+
+  GtkWidget *composite_templates;
 };
 
 enum
@@ -336,13 +338,12 @@ glade_palette_new_item (GladePalette * palette, GladeWidgetAdaptor * adaptor)
 }
 
 static GtkWidget *
-glade_palette_new_item_group (GladePalette * palette, GladeWidgetGroup * group)
+tool_item_group_new (const gchar *name)
 {
-  GtkWidget *item_group, *item, *label;
-  GList *l;
+  GtkWidget *item_group, *label;
 
   /* Give the item group a left aligned label */
-  label = gtk_label_new (glade_widget_group_get_title (group));
+  label = gtk_label_new (name);
   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
   gtk_widget_show (label);
 
@@ -354,6 +355,17 @@ glade_palette_new_item_group (GladePalette * palette, GladeWidgetGroup * group)
   gtk_tool_item_group_set_ellipsize (GTK_TOOL_ITEM_GROUP (item_group),
                                      PANGO_ELLIPSIZE_END);
 
+  return item_group;
+}
+
+static GtkWidget *
+glade_palette_new_item_group (GladePalette * palette, GladeWidgetGroup * group)
+{
+  GtkWidget *item_group, *item;
+  GList *l;
+
+  item_group = tool_item_group_new (glade_widget_group_get_title (group));
+
   gtk_widget_set_tooltip_text (item_group,
                                glade_widget_group_get_title (group));
 
@@ -389,12 +401,53 @@ glade_palette_append_item_group (GladePalette * palette,
 }
 
 static void
+glade_palette_update_templates (GladePalette *palette)
+{
+  GList *l, *adaptors = glade_widget_adaptor_list_adaptors ();
+  GladePalettePrivate *priv = palette->priv;
+
+  if (priv->composite_templates == NULL)
+    {
+      priv->composite_templates = tool_item_group_new (_("Composite Templates"));
+      gtk_container_add (GTK_CONTAINER (priv->toolpalette), priv->composite_templates);
+      gtk_widget_show (priv->composite_templates);
+    }
+  else
+    {
+      GtkContainer *container = GTK_CONTAINER (priv->composite_templates);
+      GList *l, *children = gtk_container_get_children (container);
+      
+      for (l = children; l; l = g_list_next (l))
+        gtk_container_remove (container, l->data);
+
+      g_list_free (children);
+    }
+  
+  for (l = adaptors; l; l = g_list_next (l))
+    {
+      GladeWidgetAdaptor *adaptor = l->data;
+
+      /* Create/append new item */
+      if (glade_widget_adaptor_get_template (adaptor))
+        {
+          GtkWidget *item = glade_palette_new_item (palette, adaptor);
+          gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (priv->composite_templates),
+                                      GTK_TOOL_ITEM (item), -1);
+        }
+    }
+
+  g_list_free (adaptors);
+}
+
+static void
 glade_palette_populate (GladePalette * palette)
 {
   GList *l;
 
   g_return_if_fail (GLADE_IS_PALETTE (palette));
 
+  glade_palette_update_templates (palette);
+    
   for (l = (GList *) glade_app_get_catalogs (); l; l = l->next)
     {
       GList *groups = glade_catalog_get_widget_groups (GLADE_CATALOG (l->data));
@@ -666,6 +719,10 @@ glade_palette_init (GladePalette * palette)
   gtk_widget_set_no_show_all (GTK_WIDGET (palette), TRUE);
 
   glade_palette_populate (palette);
+
+  g_signal_connect_swapped (glade_app_get (), "widget-adaptor-registered",
+                            G_CALLBACK (glade_palette_update_templates),
+                            palette);
 }
 
 
diff --git a/gladeui/glade-project.c b/gladeui/glade-project.c
index 7d8bb15..e9aa181 100644
--- a/gladeui/glade-project.c
+++ b/gladeui/glade-project.c
@@ -1442,7 +1442,8 @@ glade_project_count_xml_objects (GladeProject *project,
   for (node = glade_xml_node_get_children (root);
        node; node = glade_xml_node_next (node))
     {
-      if (glade_xml_node_verify_silent (node, GLADE_XML_TAG_WIDGET))
+      if (glade_xml_node_verify_silent (node, GLADE_XML_TAG_WIDGET) ||
+          glade_xml_node_verify_silent (node, GLADE_XML_TAG_TEMPLATE))
         count = glade_project_count_xml_objects (project, node, ++count);
       else if (glade_xml_node_verify_silent (node, GLADE_XML_TAG_CHILD))
         count = glade_project_count_xml_objects (project, node, count);
@@ -1623,7 +1624,8 @@ glade_project_load_internal (GladeProject *project)
        node; node = glade_xml_node_next (node))
     {
       /* Skip "requires" tags */
-      if (!glade_xml_node_verify_silent (node, GLADE_XML_TAG_WIDGET))
+      if (!glade_xml_node_verify_silent (node, GLADE_XML_TAG_WIDGET) &&
+          !glade_xml_node_verify_silent (node, GLADE_XML_TAG_TEMPLATE))
         continue;
 
       if ((widget = glade_widget_read (project, NULL, node, NULL)) != NULL)
@@ -1992,6 +1994,25 @@ glade_project_save (GladeProject *project, const gchar *path, GError **error)
 }
 
 /**
+ * glade_project_dump_string:
+ * @project: a #GladeProject
+ * 
+ * Returns: @project as a newlly allocated string
+ */
+gchar *
+glade_project_dump_string (GladeProject *project)
+{
+  GladeXmlContext *context;
+  gchar *retval;
+
+  context = glade_project_write (project);
+  retval = glade_xml_dump_from_context (context);
+  glade_xml_context_destroy (context);
+
+  return retval;
+}
+
+/**
  * glade_project_preview:
  * @project: a #GladeProject
  * @gwidget: a #GladeWidget
@@ -2120,6 +2141,13 @@ glade_project_verify_property_internal (GladeProject *project,
   adaptor      = glade_widget_adaptor_from_pspec (prop_adaptor, pspec);
 
   g_object_get (adaptor, "catalog", &catalog, NULL);
+
+  /* no need to check if there is no catalog because its a composite widget
+   * automagically loaded by libgladeui
+   */
+  if (catalog == NULL && glade_widget_adaptor_get_template (adaptor))
+    return;
+  
   glade_project_target_version_for_adaptor (project, adaptor,
                                             &target_major, &target_minor);
 
@@ -2205,6 +2233,10 @@ glade_project_verify_signal_internal (GladeWidget *widget,
   adaptor = glade_signal_class_get_adaptor (signal_class);
 
   g_object_get (adaptor, "catalog", &catalog, NULL);
+
+  if (catalog == NULL && glade_widget_adaptor_get_template (adaptor))
+    return;
+
   glade_project_target_version_for_adaptor (glade_widget_get_project (widget),
                                             adaptor,
                                             &target_major, &target_minor);
@@ -2425,8 +2457,11 @@ glade_project_verify_adaptor (GladeProject *project,
   for (adaptor_iter = adaptor; adaptor_iter && support_mask == GLADE_SUPPORT_OK;
        adaptor_iter = glade_widget_adaptor_get_parent_adaptor (adaptor_iter))
     {
-
       g_object_get (adaptor_iter, "catalog", &catalog, NULL);
+
+      if (catalog == NULL && glade_widget_adaptor_get_template (adaptor))
+	continue;
+
       glade_project_target_version_for_adaptor (project, adaptor_iter,
                                                 &target_major, &target_minor);
 
diff --git a/gladeui/glade-project.h b/gladeui/glade-project.h
index cdde53d..0910943 100644
--- a/gladeui/glade-project.h
+++ b/gladeui/glade-project.h
@@ -121,6 +121,7 @@ gboolean            glade_project_load_from_file      (GladeProject        *proj
 gboolean            glade_project_save                (GladeProject        *project, 
                                                        const gchar         *path, 
                                                        GError             **error);
+gchar              *glade_project_dump_string          (GladeProject       *project);
 void                glade_project_push_progress        (GladeProject       *project);
 gboolean            glade_project_load_cancelled       (GladeProject       *project);
 void                glade_project_cancel_load          (GladeProject       *project);
diff --git a/gladeui/glade-property.c b/gladeui/glade-property.c
index 08eb6de..ccdf95f 100644
--- a/gladeui/glade-property.c
+++ b/gladeui/glade-property.c
@@ -1168,9 +1168,10 @@ glade_property_write (GladeProperty * property,
   g_return_if_fail (GLADE_IS_PROPERTY (property));
   g_return_if_fail (node != NULL);
 
-  /* This code should work the same for <packing> and <widget> */
+  /* This code should work the same for <packing>, <widget> and <template> */
   if (!(glade_xml_node_verify_silent (node, GLADE_XML_TAG_PACKING) ||
-        glade_xml_node_verify_silent (node, GLADE_XML_TAG_WIDGET)))
+        glade_xml_node_verify_silent (node, GLADE_XML_TAG_WIDGET) ||
+        glade_xml_node_verify_silent (node, GLADE_XML_TAG_TEMPLATE)))
     return;
 
   /* Skip properties that are default by original pspec default
diff --git a/gladeui/glade-widget-adaptor.c b/gladeui/glade-widget-adaptor.c
index 4078dfb..ccfb61b 100644
--- a/gladeui/glade-widget-adaptor.c
+++ b/gladeui/glade-widget-adaptor.c
@@ -102,6 +102,8 @@ struct _GladeWidgetAdaptorPrivate
 				       * are special children (like notebook tab 
 				       * widgets for example).
 				       */
+  gchar       *template_xml;          /* The GtkBuilder template if this is a composite template class */
+  GFileMonitor *template_monitor;
 };
 
 struct _GladeChildPacking
@@ -135,7 +137,9 @@ enum
   PROP_CATALOG,
   PROP_BOOK,
   PROP_SPECIAL_TYPE,
-  PROP_CURSOR
+  PROP_CURSOR,
+  PROP_TEMPLATE,
+  PROP_TEMPLATE_PATH
 };
 
 typedef struct _GladeChildPacking GladeChildPacking;
@@ -380,7 +384,7 @@ gwa_clone_parent_properties (GladeWidgetAdaptor *adaptor, gboolean is_packing)
           parent_adaptor->priv->packing_props : parent_adaptor->priv->properties;
 
       /* Reset versioning in derived catalogs just once */
-      reset_version = strcmp (adaptor->priv->catalog, parent_adaptor->priv->catalog) != 0;
+      reset_version = g_strcmp0 (adaptor->priv->catalog, parent_adaptor->priv->catalog) != 0;
 
       for (list = proplist; list; list = list->next)
         {
@@ -533,8 +537,8 @@ gwa_inherit_signals (GladeWidgetAdaptor *adaptor)
               parent_signal = node->data;
 
               /* Reset versioning in derived catalogs just once */
-              if (strcmp (adaptor->priv->catalog,
-                          parent_adaptor->priv->catalog))
+              if (g_strcmp0 (adaptor->priv->catalog,
+                             parent_adaptor->priv->catalog))
 		glade_signal_class_set_since (signal, 0, 0);
               else
 		glade_signal_class_set_since (signal, 
@@ -624,7 +628,7 @@ glade_widget_adaptor_constructor (GType type,
 
   /* Reset version numbering if we're in a new catalog just once */
   if (parent_adaptor &&
-      strcmp (adaptor->priv->catalog, parent_adaptor->priv->catalog))
+      g_strcmp0 (adaptor->priv->catalog, parent_adaptor->priv->catalog))
     {
       GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->version_since_major =
           GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->version_since_minor = 0;
@@ -711,59 +715,48 @@ static void
 glade_widget_adaptor_finalize (GObject *object)
 {
   GladeWidgetAdaptor *adaptor = GLADE_WIDGET_ADAPTOR (object);
-
+  GladeWidgetAdaptorPrivate *priv = adaptor->priv;
+    
   /* Free properties and signals */
-  g_list_foreach (adaptor->priv->properties, (GFunc) glade_property_class_free, NULL);
-  g_list_free (adaptor->priv->properties);
+  g_list_foreach (priv->properties, (GFunc) glade_property_class_free, NULL);
+  g_list_free (priv->properties);
 
-  g_list_foreach (adaptor->priv->packing_props, (GFunc) glade_property_class_free,
+  g_list_foreach (priv->packing_props, (GFunc) glade_property_class_free,
                   NULL);
-  g_list_free (adaptor->priv->packing_props);
+  g_list_free (priv->packing_props);
 
   /* Be careful, this list holds GladeSignalClass* not GladeSignal,
    * thus g_free is enough as all members are const */
-  g_list_foreach (adaptor->priv->signals, (GFunc) g_free, NULL);
-  g_list_free (adaptor->priv->signals);
-
+  g_list_foreach (priv->signals, (GFunc) g_free, NULL);
+  g_list_free (priv->signals);
 
   /* Free child packings */
-  g_list_foreach (adaptor->priv->child_packings,
-                  (GFunc) gwa_child_packing_free, NULL);
-  g_list_free (adaptor->priv->child_packings);
-
-  if (adaptor->priv->book)
-    g_free (adaptor->priv->book);
-  if (adaptor->priv->catalog)
-    g_free (adaptor->priv->catalog);
-  if (adaptor->priv->special_child_type)
-    g_free (adaptor->priv->special_child_type);
-
-  if (adaptor->priv->cursor != NULL)
-    g_object_unref (adaptor->priv->cursor);
-
-  if (adaptor->priv->name)
-    g_free (adaptor->priv->name);
-  if (adaptor->priv->generic_name)
-    g_free (adaptor->priv->generic_name);
-  if (adaptor->priv->title)
-    g_free (adaptor->priv->title);
-  if (adaptor->priv->icon_name)
-    g_free (adaptor->priv->icon_name);
-  if (adaptor->priv->missing_icon)
-    g_free (adaptor->priv->missing_icon);
-
-  if (adaptor->priv->actions)
+  g_list_foreach (priv->child_packings, (GFunc) gwa_child_packing_free, NULL);
+  g_list_free (priv->child_packings);
+
+  g_free (priv->book);
+  g_free (priv->catalog);
+  g_free (priv->special_child_type);
+  g_clear_object (&priv->cursor);
+  g_free (priv->name);
+  g_free (priv->generic_name);
+  g_free (priv->title);
+  g_free (priv->icon_name);
+  g_free (priv->missing_icon);
+  g_free (priv->template_xml);
+
+  if (priv->actions)
     {
-      g_list_foreach (adaptor->priv->actions,
+      g_list_foreach (priv->actions,
                       (GFunc) glade_widget_action_class_free, NULL);
-      g_list_free (adaptor->priv->actions);
+      g_list_free (priv->actions);
     }
 
-  if (adaptor->priv->packing_actions)
+  if (priv->packing_actions)
     {
-      g_list_foreach (adaptor->priv->packing_actions,
+      g_list_foreach (priv->packing_actions,
                       (GFunc) glade_widget_action_class_free, NULL);
-      g_list_free (adaptor->priv->packing_actions);
+      g_list_free (priv->packing_actions);
     }
 
   gwa_internal_children_free (adaptor);
@@ -771,6 +764,105 @@ glade_widget_adaptor_finalize (GObject *object)
   G_OBJECT_CLASS (glade_widget_adaptor_parent_class)->finalize (object);
 }
 
+static inline void
+gwa_template_rebuild_objects (GladeWidgetAdaptor *adaptor, GType object_type)
+{
+  GList *l, *rebuild = NULL;
+
+  /* Iterate all projects */
+  for (l = glade_app_get_projects (); l; l = g_list_next (l))
+    {
+      GladeProject *project = l->data;
+      const GList *o;
+
+      /* Iterate all objects in the project */
+      for (o = glade_project_get_objects (project); o; o = g_list_next (o))
+	{
+	  GObject *obj = o->data;
+
+	  /* And rebuild widget if its template just changed */
+	  if (g_type_is_a (G_OBJECT_TYPE (obj), object_type))
+	    rebuild = g_list_prepend (rebuild, glade_widget_get_from_gobject (obj));
+	}
+    }
+
+  for (l = rebuild; l; l = g_list_next (l))
+    glade_widget_rebuild (l->data);
+
+  g_list_free (rebuild);
+}
+
+static inline void
+glade_widget_adaptor_set_template (GladeWidgetAdaptor *adaptor,
+                                   const gchar *template_xml)
+{
+  GladeWidgetAdaptorPrivate *priv = adaptor->priv;
+  GtkContainerClass *klass;
+  GType object_type;
+  
+  if (g_strcmp0 (priv->template_xml, template_xml) == 0)
+    return;
+
+  g_free (priv->template_xml);
+  priv->template_xml = g_strdup (template_xml);
+
+  /* Update container class template */
+  object_type = glade_widget_adaptor_get_object_type (adaptor);
+  klass = g_type_class_peek (object_type);
+#if HAVE_GTK_CONTAINER_CLASS_SET_TEMPLATE_FROM_STRING
+  gtk_container_class_set_template_from_string (klass, priv->template_xml, "this");
+#endif
+  gwa_template_rebuild_objects (adaptor, object_type);
+}
+
+static void
+on_template_file_changed (GFileMonitor       *monitor,
+                          GFile              *file,
+                          GFile              *other_file,
+                          GFileMonitorEvent   event_type,
+                          GladeWidgetAdaptor *adaptor)
+{
+  gchar *contents = NULL;
+  gsize len;
+
+  if (event_type != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
+    return;
+
+  if (g_file_load_contents (file, NULL, &contents, &len, NULL, NULL))
+    {
+      g_object_set (adaptor, "template", contents, NULL);
+      g_free (contents);
+    }
+}
+
+static inline void
+glade_widget_adaptor_set_template_path (GladeWidgetAdaptor *adaptor,
+                                        const gchar *path)
+{
+  GladeWidgetAdaptorPrivate *priv = adaptor->priv;
+  GFile *file = g_file_new_for_path (path);
+  GError *error = NULL;
+
+  g_clear_object (&priv->template_monitor);
+  
+  /* Add watch for file */
+  priv->template_monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error);
+
+  if (priv->template_monitor)
+    {
+      g_signal_connect (priv->template_monitor, "changed",
+                        G_CALLBACK (on_template_file_changed),
+                        adaptor);
+    }
+  else
+    {
+      g_warning ("Unable to monitor path `%s` %s", path, error->message);
+      g_error_free (error);
+    }
+  
+  g_object_unref (file);
+}
+
 static void
 glade_widget_adaptor_real_set_property (GObject *object,
                                         guint prop_id,
@@ -820,6 +912,12 @@ glade_widget_adaptor_real_set_property (GObject *object,
 	g_free (adaptor->priv->special_child_type);
 	adaptor->priv->special_child_type = g_value_dup_string (value);
         break;
+      case PROP_TEMPLATE:
+	glade_widget_adaptor_set_template (adaptor, g_value_get_string (value));
+        break;
+      case PROP_TEMPLATE_PATH:
+	glade_widget_adaptor_set_template_path (adaptor, g_value_get_string (value));
+	break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
         break;
@@ -866,6 +964,9 @@ glade_widget_adaptor_real_get_property (GObject *object,
       case PROP_CURSOR:
         g_value_set_pointer (value, adaptor->priv->cursor);
         break;
+      case PROP_TEMPLATE:
+        g_value_set_string (value, adaptor->priv->template_xml);
+        break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
         break;
@@ -1455,6 +1556,20 @@ glade_widget_adaptor_class_init (GladeWidgetAdaptorClass *adaptor_class)
        ("cursor", _("Cursor"),
         _("A cursor for inserting widgets in the UI"), G_PARAM_READABLE));
 
+  g_object_class_install_property
+      (object_class, PROP_TEMPLATE,
+       g_param_spec_string
+       ("template", _("Template"),
+        _("Builder template of the class"),
+        NULL, G_PARAM_READWRITE));
+
+  g_object_class_install_property
+      (object_class, PROP_TEMPLATE_PATH,
+       g_param_spec_string
+       ("template-path", _("Template path"),
+        _("Builder template file path of the class, if set it will be used to monitor and update template property automatically"),
+        NULL, G_PARAM_WRITABLE));
+
   g_type_class_add_private (adaptor_class, sizeof (GladeWidgetAdaptorPrivate));
 }
 
@@ -1632,9 +1747,14 @@ static void
 gwa_derived_class_init (GladeWidgetAdaptorClass *adaptor_class,
                         GWADerivedClassData *data)
 {
-  GladeXmlNode *node = data->node;
-  GModule *module = data->module;
+  GladeXmlNode *node;
+  GModule *module;
 
+  if (data == NULL) return;
+
+  node = data->node;
+  module = data->module;
+  
   /* Load catalog symbols from module */
   if (module)
     gwa_extend_with_node_load_sym (adaptor_class, node, module);
@@ -1705,7 +1825,6 @@ gwa_derive_adaptor_for_type (GType object_type, GWADerivedClassData *data)
   return derived_type;
 }
 
-
 /*******************************************************************************
                                      API
  *******************************************************************************/
@@ -1781,6 +1900,14 @@ glade_widget_adaptor_get_signals (GladeWidgetAdaptor *adaptor)
   return adaptor->priv->signals;
 }
 
+G_CONST_RETURN gchar *
+glade_widget_adaptor_get_template (GladeWidgetAdaptor *adaptor)
+{
+  g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor), NULL);
+
+  return adaptor->priv->template_xml;
+}
+
 static void
 accum_adaptor (GType *type, GladeWidgetAdaptor *adaptor, GList **list)
 {
@@ -1829,6 +1956,8 @@ glade_widget_adaptor_register (GladeWidgetAdaptor *adaptor)
 
   g_hash_table_insert (adaptor_hash,
                        g_memdup (&adaptor->priv->type, sizeof (GType)), adaptor);
+
+  g_signal_emit_by_name (glade_app_get (), "widget-adaptor-registered", adaptor);
 }
 
 static GladePackingDefault *
@@ -2601,6 +2730,54 @@ generate_deprecated_icon (const gchar *icon_name)
 }
 
 /**
+ * glade_widget_adaptor_from_composite_template:
+ * @template_type: a #GType
+ * @template_xml: composite template ui description
+ * @generic_name: the genereic name of the adaptor
+ * @icon_name: the icon name for the adaptor or NULL to fallback to the parent
+ *
+ * Dynamicaly creates the corresponding adaptor for the template type.
+ */
+GladeWidgetAdaptor *
+glade_widget_adaptor_from_composite_template (GType template_type,
+                                              const gchar *template_xml,
+                                              const gchar *generic_name,
+                                              const gchar *icon_name)
+{
+  gchar *adaptor_icon_name = NULL;
+  GladeWidgetAdaptor *adaptor;
+  GType adaptor_type;
+  const gchar *name;
+
+  g_return_val_if_fail (g_type_is_a (template_type, GTK_TYPE_CONTAINER), NULL);  
+  
+  adaptor_type = gwa_derive_adaptor_for_type (template_type, NULL);
+  name = g_type_name (template_type);
+
+  /* Fallback to parent icon */
+  if (!icon_name)
+    {
+      GladeWidgetAdaptor *parent = glade_widget_adaptor_get_parent_adaptor_by_type (template_type);
+      adaptor_icon_name = g_strdup ((parent && parent->priv->icon_name) ?
+                                    parent->priv->icon_name : DEFAULT_ICON_NAME);
+    }
+
+  adaptor = g_object_new (adaptor_type,
+                          "type", template_type,
+                          "template", template_xml,
+                          "name", name,
+                          "catalog",  NULL, /* yup NULL, it does not have a catalog */
+                          "generic-name", generic_name,
+                          "icon-name", icon_name ? icon_name : adaptor_icon_name,
+                          "title", name,
+                          NULL);
+
+  g_free (adaptor_icon_name);
+  
+  return adaptor;
+}
+
+/**
  * glade_widget_adaptor_from_catalog:
  * @catalog: A #GladeCatalog
  * @class_node: the #GladeXmlNode to load
diff --git a/gladeui/glade-widget-adaptor.h b/gladeui/glade-widget-adaptor.h
index ab33a53..6f07a16 100644
--- a/gladeui/glade-widget-adaptor.h
+++ b/gladeui/glade-widget-adaptor.h
@@ -185,8 +185,8 @@ typedef enum
  * Returns: A newly created #GladeWidget for the said adaptor.
  */
 typedef GladeWidget * (* GladeCreateWidgetFunc) (GladeWidgetAdaptor *adaptor,
-						 const gchar        *first_property_name,
-						 va_list             var_args);
+                                                 const gchar        *first_property_name,
+                                                 va_list             var_args);
 
 /**
  * GladeSetPropertyFunc:
@@ -201,9 +201,9 @@ typedef GladeWidget * (* GladeCreateWidgetFunc) (GladeWidgetAdaptor *adaptor,
  * Sets @value on @object for a given #GladePropertyClass
  */
 typedef void     (* GladeSetPropertyFunc)    (GladeWidgetAdaptor *adaptor,
-					      GObject            *object,
-					      const gchar        *property_name,
-					      const GValue       *value);
+                                              GObject            *object,
+                                              const gchar        *property_name,
+                                              const GValue       *value);
 
 /**
  * GladeGetPropertyFunc:
@@ -215,7 +215,7 @@ typedef void     (* GladeSetPropertyFunc)    (GladeWidgetAdaptor *adaptor,
  * Gets @value on @object for a given #GladePropertyClass
  */
 typedef void     (* GladeGetPropertyFunc)    (GladeWidgetAdaptor *adaptor,
-					      GObject            *object,
+                                              GObject            *object,
 					      const gchar        *property_name,
 					      GValue             *value);
 
@@ -686,12 +686,18 @@ G_CONST_RETURN gchar *glade_widget_adaptor_get_missing_icon (GladeWidgetAdaptor
 G_CONST_RETURN GList *glade_widget_adaptor_get_properties   (GladeWidgetAdaptor   *adaptor);
 G_CONST_RETURN GList *glade_widget_adaptor_get_packing_props(GladeWidgetAdaptor   *adaptor);
 G_CONST_RETURN GList *glade_widget_adaptor_get_signals      (GladeWidgetAdaptor   *adaptor);
+G_CONST_RETURN gchar *glade_widget_adaptor_get_template     (GladeWidgetAdaptor   *adaptor);
 
 GList                *glade_widget_adaptor_list_adaptors    (void);
 
 GladeWidgetAdaptor   *glade_widget_adaptor_from_catalog     (GladeCatalog         *catalog,
-							     GladeXmlNode         *class_node,
-							     GModule              *module);
+                                                             GladeXmlNode         *class_node,
+                                                             GModule              *module);
+
+GladeWidgetAdaptor   *glade_widget_adaptor_from_composite_template (GType template_type,
+                                                                    const gchar *template_xml,
+                                                                    const gchar *generic_name,
+                                                                    const gchar *icon_name);
 
 void                  glade_widget_adaptor_register         (GladeWidgetAdaptor   *adaptor);
  
diff --git a/gladeui/glade-widget.c b/gladeui/glade-widget.c
index ba9c2b3..405856c 100644
--- a/gladeui/glade-widget.c
+++ b/gladeui/glade-widget.c
@@ -79,6 +79,7 @@ struct _GladeWidgetPrivate {
 		* button2. This is a unique name and is the one
 		* used when loading widget with libglade
 		*/
+  gchar *template_class; /* The name of the composite class this widget defines */
 
   gchar *support_warning; /* A warning message for version incompatabilities
 			   * in this widget
@@ -192,6 +193,7 @@ enum
   PROP_TOPLEVEL_HEIGHT,
   PROP_SUPPORT_WARNING,
   PROP_VISIBLE,
+  PROP_TEMPLATE_CLASS,
   N_PROPERTIES
 };
 
@@ -1093,6 +1095,9 @@ glade_widget_set_real_property (GObject * object,
       case PROP_TOPLEVEL_HEIGHT:
         widget->priv->height = g_value_get_int (value);
         break;
+      case PROP_TEMPLATE_CLASS:
+        glade_widget_set_template_class (widget, g_value_get_string (value));
+        break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
         break;
@@ -1149,6 +1154,9 @@ glade_widget_get_real_property (GObject * object,
       case PROP_REASON:
         g_value_set_int (value, widget->priv->construct_reason);
         break;
+      case PROP_TEMPLATE_CLASS:
+        g_value_set_string (value, widget->priv->template_class);
+        break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
         break;
@@ -1304,7 +1312,12 @@ glade_widget_class_init (GladeWidgetClass * klass)
        g_param_spec_boolean ("visible", _("Visible"),
                             _("Wether the widget is visible or not"),
                              FALSE, G_PARAM_READABLE);
-
+  
+  properties[PROP_TEMPLATE_CLASS] =
+       g_param_spec_string ("template-class", _("Template Class"),
+                            _("The class name this template defines"),
+                            NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+  
   /* Install all properties */
   g_object_class_install_properties (object_class, N_PROPERTIES, properties);
 
@@ -2512,7 +2525,7 @@ void
 glade_widget_set_name (GladeWidget * widget, const gchar * name)
 {
   g_return_if_fail (GLADE_IS_WIDGET (widget));
-  if (widget->priv->name != name)
+  if (g_strcmp0 (widget->priv->name, name))
     {
       if (widget->priv->name)
         g_free (widget->priv->name);
@@ -2536,6 +2549,45 @@ glade_widget_get_name (GladeWidget * widget)
 }
 
 /**
+ * glade_widget_set_template_class:
+ * @widget: a #GladeWidget
+ * @name: a string
+ *
+ * Sets the template class name this @widget defines.
+ * @widget has to be toplevel.
+ */
+void
+glade_widget_set_template_class (GladeWidget *widget, const gchar *name)
+{
+  g_return_if_fail (GLADE_IS_WIDGET (widget));
+
+  /* Check toplevelness */
+  if (glade_widget_get_parent (widget)) return;
+
+  if (g_strcmp0 (widget->priv->template_class, name))
+    {
+      if (widget->priv->template_class)
+        g_free (widget->priv->template_class);
+
+      widget->priv->template_class = g_strdup (name);
+      g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_TEMPLATE_CLASS]);
+    }
+}
+
+/**
+ * glade_widget_get_template_class:
+ * @widget: a #GladeWidget
+ *
+ * Returns: a pointer to @widget's template class name.
+ */
+const gchar *
+glade_widget_get_template_class (GladeWidget *widget)
+{
+  g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL);
+  return widget->priv->template_class;
+}
+
+/**
  * glade_widget_set_internal:
  * @widget: A #GladeWidget
  * @internal: The internal name
@@ -3730,23 +3782,24 @@ glade_widget_read (GladeProject * project,
 {
   GladeWidgetAdaptor *adaptor;
   GladeWidget *widget = NULL;
-  gchar *klass, *id;
+  gchar *klass, *id = NULL;
+  gboolean is_tmpl;
 
   if (glade_project_load_cancelled (project))
     return NULL;
 
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
+  is_tmpl = (parent == NULL && glade_xml_node_verify_silent (node, GLADE_XML_TAG_TEMPLATE));
+
+  if (!is_tmpl && !glade_xml_node_verify_silent (node, GLADE_XML_TAG_WIDGET))
     return NULL;
 
   glade_widget_push_superuser ();
 
   if ((klass =
        glade_xml_get_property_string_required
-       (node, GLADE_XML_TAG_CLASS, NULL)) != NULL)
+       (node, is_tmpl ? GLADE_XML_TAG_PARENT : GLADE_XML_TAG_CLASS, NULL)) != NULL)
     {
-      if ((id =
-           glade_xml_get_property_string_required
-           (node, GLADE_XML_TAG_ID, NULL)) != NULL)
+      if ((id = glade_xml_get_property_string_required (node, GLADE_XML_TAG_ID, NULL)))
         {
           GType type;
           /* 
@@ -3780,11 +3833,15 @@ glade_widget_read (GladeProject * project,
                 }
               else
                 {
+                  gchar *tmpl = (is_tmpl) ? glade_xml_get_property_string (node, GLADE_XML_TAG_CLASS) : NULL;
                   widget = glade_widget_adaptor_create_widget
                       (adaptor, FALSE,
                        "name", id,
                        "parent", parent,
-                       "project", project, "reason", GLADE_CREATE_LOAD, NULL);
+                       "project", project,
+                       "reason", GLADE_CREATE_LOAD,
+                       "template-class", tmpl, NULL);
+                  g_free (tmpl);
                 }
 
               glade_widget_adaptor_read_widget (adaptor, widget, node);
@@ -3927,6 +3984,7 @@ void
 glade_widget_write (GladeWidget * widget,
                     GladeXmlContext * context, GladeXmlNode * node)
 {
+  const gchar *tmpl = glade_widget_get_template_class (widget);
   GObject *object = glade_widget_get_object (widget);
   GladeXmlNode *widget_node;
   GList *l, *list;
@@ -3939,13 +3997,16 @@ glade_widget_write (GladeWidget * widget,
       return;
     }
 
-  widget_node = glade_xml_node_new (context, GLADE_XML_TAG_WIDGET);
+  widget_node = glade_xml_node_new (context, (tmpl) ? GLADE_XML_TAG_TEMPLATE : GLADE_XML_TAG_WIDGET);
   glade_xml_node_append_child (node, widget_node);
 
   /* Set class and id */
   glade_xml_node_set_property_string (widget_node,
-                                      GLADE_XML_TAG_CLASS,
+                                      (tmpl) ? GLADE_XML_TAG_PARENT : GLADE_XML_TAG_CLASS,
                                       glade_widget_adaptor_get_name (widget->priv->adaptor));
+  if (tmpl)
+    glade_xml_node_set_property_string (widget_node, GLADE_XML_TAG_CLASS, tmpl);
+
   glade_xml_node_set_property_string (widget_node,
                                       GLADE_XML_TAG_ID, widget->priv->name);
 
diff --git a/gladeui/glade-widget.h b/gladeui/glade-widget.h
index 7762ced..e3983df 100644
--- a/gladeui/glade-widget.h
+++ b/gladeui/glade-widget.h
@@ -337,6 +337,11 @@ void                    glade_widget_set_name		    (GladeWidget      *widget,
  
 G_CONST_RETURN gchar   *glade_widget_get_name               (GladeWidget      *widget);
 
+void                    glade_widget_set_template_class     (GladeWidget      *widget,
+							     const gchar      *name);
+
+G_CONST_RETURN gchar   *glade_widget_get_template_class     (GladeWidget      *widget);
+
 void                    glade_widget_set_internal	    (GladeWidget      *widget,
 							     const gchar      *internal);
  
diff --git a/gladeui/glade-xml-utils.h b/gladeui/glade-xml-utils.h
index c7b116f..ff9b676 100644
--- a/gladeui/glade-xml-utils.h
+++ b/gladeui/glade-xml-utils.h
@@ -36,12 +36,14 @@ typedef struct _GladeProject        GladeProject;
 /* Used for catalog tags and attributes */
 #define GLADE_XML_TAG_PROJECT                     "interface"
 #define GLADE_XML_TAG_WIDGET                      "object"
+#define GLADE_XML_TAG_TEMPLATE                    "template"
 
 #define GLADE_XML_TAG_VERSION                     "version"
 #define GLADE_XML_TAG_REQUIRES                    "requires"
 #define GLADE_XML_TAG_LIB                         "lib"
 #define GLADE_XML_TAG_PROPERTY                    "property"
 #define GLADE_XML_TAG_CLASS                       "class"
+#define GLADE_XML_TAG_PARENT                      "parent"
 #define GLADE_XML_TAG_ID                          "id"
 #define GLADE_XML_TAG_SIGNAL                      "signal"
 #define GLADE_XML_TAG_HANDLER                     "handler"
diff --git a/gladeui/glade.h b/gladeui/glade.h
index ddfb178..04dc406 100644
--- a/gladeui/glade.h
+++ b/gladeui/glade.h
@@ -46,5 +46,6 @@
 #include <gladeui/glade-displayable-values.h>
 #include <gladeui/glade-cell-renderer-icon.h>
 #include <gladeui/glade-cursor.h>
+#include <gladeui/glade-composite-template.h>
 
 #endif /* __GLADE_H__ */
diff --git a/plugins/gtk+/glade-gtk.c b/plugins/gtk+/glade-gtk.c
index c1f787d..9e147aa 100644
--- a/plugins/gtk+/glade-gtk.c
+++ b/plugins/gtk+/glade-gtk.c
@@ -410,9 +410,6 @@ void
 glade_gtk_widget_read_widget (GladeWidgetAdaptor * adaptor,
                               GladeWidget * widget, GladeXmlNode * node)
 {
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (G_TYPE_OBJECT)->read_widget (adaptor, widget, node);
 
@@ -506,7 +503,6 @@ glade_gtk_widget_write_atk_properties (GladeWidget * widget,
 
       g_free (atkname);
     }
-
 }
 
 static void
@@ -686,9 +682,6 @@ glade_gtk_widget_write_widget (GladeWidgetAdaptor * adaptor,
 {
   GObject *obj;
 
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* Make sure use-action-appearance and related-action properties are
    * ordered in a sane way and are only saved if there is an action */
   if ((obj = glade_widget_get_object (widget)) &&
@@ -812,7 +805,6 @@ glade_gtk_widget_set_property (GladeWidgetAdaptor * adaptor,
     {
       id = "tooltip-text";
     }
-
   GWA_GET_CLASS (G_TYPE_OBJECT)->set_property (adaptor, object, id, value);
 }
 
@@ -825,7 +817,6 @@ glade_gtk_widget_get_property (GladeWidgetAdaptor * adaptor,
     {
       id = "tooltip-text";
     }
-
   GWA_GET_CLASS (G_TYPE_OBJECT)->get_property (adaptor, object, id, value);
 }
 
@@ -1135,6 +1126,18 @@ glade_gtk_widget_action_submenu (GladeWidgetAdaptor * adaptor,
 
 /* ----------------------------- GtkContainer ------------------------------ */
 void
+glade_gtk_container_deep_post_create (GladeWidgetAdaptor *adaptor,
+                                      GObject *container,
+                                      GladeCreateReason reason)
+{
+  GladeWidget *widget = glade_widget_get_from_gobject (container);
+
+  if (!glade_widget_get_parent (widget) && glade_widget_get_template_class (widget))
+    glade_widget_property_set (widget, "glade-template-class", 
+                               glade_widget_get_template_class (widget));
+}
+
+void
 glade_gtk_container_post_create (GladeWidgetAdaptor * adaptor,
                                  GObject * container, GladeCreateReason reason)
 {
@@ -1336,6 +1339,103 @@ glade_gtk_container_create_editable (GladeWidgetAdaptor * adaptor,
   return GWA_GET_CLASS (GTK_TYPE_CONTAINER)->create_editable (adaptor, type);
 }
 
+void
+glade_gtk_container_set_property (GladeWidgetAdaptor *adaptor,
+                                  GObject *object,
+                                  const gchar *id,
+                                  const GValue *value)
+{
+  if (!strcmp (id, "glade-template-class"))
+    {
+      GladeWidget *gwidget = glade_widget_get_from_gobject (object);
+      glade_widget_set_template_class (gwidget, g_value_get_string (value));
+    }
+  else GWA_GET_CLASS (G_TYPE_OBJECT)->set_property (adaptor, object, id, value);
+}
+
+void
+glade_gtk_container_get_property (GladeWidgetAdaptor *adaptor,
+                                  GObject *object,
+                                  const gchar *id,
+                                  GValue *value)
+{
+  if (!strcmp (id, "glade-template-class"))
+    {
+      GladeWidget *gwidget = glade_widget_get_from_gobject (object);
+      g_value_set_string (value, glade_widget_get_template_class (gwidget));
+    }
+  else GWA_GET_CLASS (G_TYPE_OBJECT)->get_property (adaptor, object, id, value);
+}
+
+void
+glade_gtk_container_action_activate (GladeWidgetAdaptor *adaptor,
+                                     GObject *object,
+                                     const gchar *action_path)
+{
+  GladeWidget *gwidget = glade_widget_get_from_gobject (object);
+
+  if (strcmp (action_path, "template") == 0)
+    {
+      GtkFileFilter *filter = gtk_file_filter_new ();
+      gchar *template_class, *template_file;
+      GtkWidget *dialog, *help_label;
+      GtkFileChooser *chooser;
+
+      gtk_file_filter_add_pattern (filter, "*.ui");
+
+      dialog = gtk_file_chooser_dialog_new (_("Save Template"),
+                                            GTK_WINDOW (glade_app_get_window ()),
+                                            GTK_FILE_CHOOSER_ACTION_SAVE,
+                                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                            GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+                                            NULL);
+      chooser = GTK_FILE_CHOOSER (dialog);
+      gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE);
+      gtk_file_chooser_set_filter (chooser, filter);
+      help_label = gtk_label_new (_("NOTE: the base name of the file will be used"
+                                    " as the class name so it should be in CamelCase"));
+      gtk_file_chooser_set_extra_widget (chooser, help_label);
+      gtk_widget_show (help_label);
+
+      template_class = g_strconcat ("My", G_OBJECT_TYPE_NAME (object), NULL);
+      template_file = g_strconcat (template_class, ".ui", NULL);
+      
+      gtk_file_chooser_set_current_folder (chooser, g_get_user_special_dir (G_USER_DIRECTORY_TEMPLATES));
+      gtk_file_chooser_set_current_name (chooser, template_file);
+      
+      if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
+	{
+	  gchar *filename = gtk_file_chooser_get_filename (chooser);
+	  gchar *basename = g_path_get_basename (filename);
+	  gchar *dot = g_strrstr (basename, ".ui");
+
+	  /* Strip file extension to get the class name */
+	  if (dot)
+	    {
+	      *dot = '\0';
+	    }
+	  else
+	    {
+	      /* Or add it if it is not present */
+	      gchar *tmp = g_strconcat (filename, ".ui", NULL);
+	      g_free (filename);
+	      filename = tmp;
+	    }
+	  
+	  glade_composite_template_save_from_widget (gwidget, basename, filename, TRUE);
+
+	  g_free (basename);
+	  g_free (filename);
+	}
+
+      g_free (template_class);
+      g_free (template_file);
+      gtk_widget_destroy (dialog);
+    }
+  else
+    GWA_GET_CLASS (GTK_TYPE_WIDGET)->action_activate (adaptor, object, action_path);
+}
+
 /* ----------------------------- GtkBox ------------------------------ */
 
 GladeWidget *
@@ -3393,9 +3493,6 @@ glade_gtk_entry_read_widget (GladeWidgetAdaptor * adaptor,
 {
   GladeProperty *property;
 
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (GTK_TYPE_WIDGET)->read_widget (adaptor, widget, node);
 
@@ -3631,9 +3728,6 @@ void
 glade_gtk_window_read_widget (GladeWidgetAdaptor * adaptor,
                               GladeWidget * widget, GladeXmlNode * node)
 {
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (GTK_TYPE_WIDGET)->read_widget (adaptor, widget, node);
 
@@ -3677,9 +3771,6 @@ glade_gtk_window_write_widget (GladeWidgetAdaptor * adaptor,
                                GladeWidget * widget,
                                GladeXmlContext * context, GladeXmlNode * node)
 {
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (GTK_TYPE_WIDGET)->write_widget (adaptor, widget, context,
                                                  node);
@@ -4262,9 +4353,6 @@ void
 glade_gtk_button_read_widget (GladeWidgetAdaptor * adaptor,
                               GladeWidget * widget, GladeXmlNode * node)
 {
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (GTK_TYPE_CONTAINER)->read_widget (adaptor, widget, node);
 
@@ -4280,9 +4368,6 @@ glade_gtk_button_write_widget (GladeWidgetAdaptor * adaptor,
   gboolean use_stock;
   gchar *stock = NULL;
 
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* Do not save GtkColorButton GtkFontButton and GtkScaleButton label property */
   if (!(GTK_IS_COLOR_BUTTON (glade_widget_get_object (widget)) ||
 	GTK_IS_FONT_BUTTON (glade_widget_get_object (widget)) ||
@@ -4318,9 +4403,6 @@ glade_gtk_image_read_widget (GladeWidgetAdaptor * adaptor,
 {
   GladeProperty *property;
 
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (GTK_TYPE_WIDGET)->read_widget (adaptor, widget, node);
 
@@ -4356,9 +4438,6 @@ glade_gtk_image_write_widget (GladeWidgetAdaptor * adaptor,
   GtkIconSize icon_size;
   gchar *value;
 
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and write all the normal properties (including "use-stock")... */
   GWA_GET_CLASS (GTK_TYPE_WIDGET)->write_widget (adaptor, widget, context,
                                                  node);
@@ -5502,9 +5581,6 @@ glade_gtk_image_menu_item_read_widget (GladeWidgetAdaptor * adaptor,
   gboolean use_stock;
   gchar *label = NULL;
 
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (GTK_TYPE_MENU_ITEM)->read_widget (adaptor, widget, node);
 
@@ -5541,9 +5617,6 @@ glade_gtk_image_menu_item_write_widget (GladeWidgetAdaptor * adaptor,
   gboolean use_stock;
   gchar *stock;
 
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* Make a copy of the GladeProperty, override its value if use-stock is TRUE */
   label_prop = glade_widget_get_property (widget, "label");
   label_prop = glade_property_dup (label_prop, widget);
@@ -6246,8 +6319,6 @@ void
 glade_gtk_tool_item_group_read_widget (GladeWidgetAdaptor * adaptor,
 				       GladeWidget * widget, GladeXmlNode * node)
 {
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
 
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (GTK_TYPE_TOOL_ITEM)->read_widget (adaptor, widget, node);
@@ -6554,9 +6625,6 @@ void
 glade_gtk_tool_button_read_widget (GladeWidgetAdaptor * adaptor,
                                    GladeWidget * widget, GladeXmlNode * node)
 {
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (GTK_TYPE_TOOL_ITEM)->read_widget (adaptor, widget, node);
 
@@ -7040,8 +7108,6 @@ glade_gtk_label_read_widget (GladeWidgetAdaptor * adaptor,
                              GladeWidget * widget, GladeXmlNode * node)
 {
   GladeProperty *prop;
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
 
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (GTK_TYPE_WIDGET)->read_widget (adaptor, widget, node);
@@ -7126,9 +7192,6 @@ glade_gtk_label_write_widget (GladeWidgetAdaptor * adaptor,
 {
   GladeXmlNode *attrs_node;
 
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (GTK_TYPE_WIDGET)->write_widget (adaptor, widget, context,
                                                  node);
@@ -7567,9 +7630,6 @@ void
 glade_gtk_combo_box_text_read_widget (GladeWidgetAdaptor * adaptor,
 				      GladeWidget * widget, GladeXmlNode * node)
 {
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (GTK_TYPE_COMBO_BOX)->read_widget (adaptor, widget, node);
 
@@ -7621,9 +7681,6 @@ glade_gtk_combo_box_text_write_widget (GladeWidgetAdaptor * adaptor,
 {
   GladeXmlNode *attrs_node;
 
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (GTK_TYPE_COMBO_BOX)->write_widget (adaptor, widget, context, node);
 
@@ -8151,9 +8208,6 @@ void
 glade_gtk_size_group_read_widget (GladeWidgetAdaptor * adaptor,
                                   GladeWidget * widget, GladeXmlNode * node)
 {
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (G_TYPE_OBJECT)->read_widget (adaptor, widget, node);
 
@@ -8199,9 +8253,6 @@ glade_gtk_size_group_write_widget (GladeWidgetAdaptor * adaptor,
                                    GladeXmlContext * context,
                                    GladeXmlNode * node)
 {
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (G_TYPE_OBJECT)->write_widget (adaptor, widget, context, node);
 
@@ -8390,9 +8441,6 @@ void
 glade_gtk_icon_factory_read_widget (GladeWidgetAdaptor * adaptor,
                                     GladeWidget * widget, GladeXmlNode * node)
 {
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in any normal properties.. */
   GWA_GET_CLASS (G_TYPE_OBJECT)->read_widget (adaptor, widget, node);
 
@@ -8498,9 +8546,6 @@ glade_gtk_icon_factory_write_widget (GladeWidgetAdaptor * adaptor,
                                      GladeXmlContext * context,
                                      GladeXmlNode * node)
 {
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and write all the normal properties.. */
   GWA_GET_CLASS (G_TYPE_OBJECT)->write_widget (adaptor, widget, context, node);
 
@@ -9178,9 +9223,6 @@ glade_gtk_store_write_widget (GladeWidgetAdaptor * adaptor,
                               GladeWidget * widget,
                               GladeXmlContext * context, GladeXmlNode * node)
 {
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and write all the normal properties.. */
   GWA_GET_CLASS (G_TYPE_OBJECT)->write_widget (adaptor, widget, context, node);
 
@@ -9384,9 +9426,6 @@ void
 glade_gtk_store_read_widget (GladeWidgetAdaptor * adaptor,
                              GladeWidget * widget, GladeXmlNode * node)
 {
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (G_TYPE_OBJECT)->read_widget (adaptor, widget, node);
 
@@ -9618,9 +9657,6 @@ glade_gtk_cell_renderer_write_widget (GladeWidgetAdaptor * adaptor,
                                       GladeXmlContext * context,
                                       GladeXmlNode * node)
 {
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* Write our normal properties, then chain up to write any other normal properties,
    * then attributes 
    */
@@ -9674,9 +9710,6 @@ void
 glade_gtk_cell_renderer_read_widget (GladeWidgetAdaptor * adaptor,
                                      GladeWidget * widget, GladeXmlNode * node)
 {
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in all the properties... */
   GWA_GET_CLASS (G_TYPE_OBJECT)->read_widget (adaptor, widget, node);
 
@@ -10401,9 +10434,6 @@ glade_gtk_adjustment_write_widget (GladeWidgetAdaptor * adaptor,
 {
   GladeProperty *prop;
 
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* Ensure proper order of adjustment properties by writing them here. */
   prop = glade_widget_get_property (widget, "lower");
   glade_property_write (prop, context, node);
@@ -10941,9 +10971,6 @@ glade_gtk_recent_filter_read_widget (GladeWidgetAdaptor *adaptor,
 				     GladeWidget        *widget, 
 				     GladeXmlNode       *node)
 {
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (G_TYPE_OBJECT)->read_widget (adaptor, widget, node);
 
@@ -10960,9 +10987,6 @@ glade_gtk_recent_filter_write_widget (GladeWidgetAdaptor *adaptor,
 {
   GladeXmlNode *strings_node;
 
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (G_TYPE_OBJECT)->write_widget (adaptor, widget, context, node);
 
@@ -10999,9 +11023,6 @@ glade_gtk_file_filter_read_widget (GladeWidgetAdaptor *adaptor,
 				   GladeWidget        *widget, 
 				   GladeXmlNode       *node)
 {
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (G_TYPE_OBJECT)->read_widget (adaptor, widget, node);
 
@@ -11017,9 +11038,6 @@ glade_gtk_file_filter_write_widget (GladeWidgetAdaptor *adaptor,
 {
   GladeXmlNode *strings_node;
 
-  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
-    return;
-
   /* First chain up and read in all the normal properties.. */
   GWA_GET_CLASS (G_TYPE_OBJECT)->write_widget (adaptor, widget, context, node);
 
diff --git a/plugins/gtk+/gtk+.xml.in b/plugins/gtk+/gtk+.xml.in
index df4af2a..f6d20cf 100644
--- a/plugins/gtk+/gtk+.xml.in
+++ b/plugins/gtk+/gtk+.xml.in
@@ -6,7 +6,6 @@
                domain="glade3"
                book="gtk3">
   <glade-widget-classes>
-
     <glade-widget-class name="GtkWidget" _title="Widget" default-width="100" default-height="60">
 
       <deep-post-create-function>glade_gtk_widget_deep_post_create</deep-post-create-function>
@@ -289,6 +288,7 @@ embedded in another object</_tooltip>
 
     <glade-widget-class name="GtkContainer" _title="Container" use-placeholders="True">
       <post-create-function>glade_gtk_container_post_create</post-create-function>
+      <deep-post-create-function>glade_gtk_container_deep_post_create</deep-post-create-function>
       <add-child-verify-function>glade_gtk_container_add_verify</add-child-verify-function>
       <add-child-function>glade_gtk_container_add_child</add-child-function>
       <remove-child-function>glade_gtk_container_remove_child</remove-child-function> 
@@ -296,8 +296,18 @@ embedded in another object</_tooltip>
       <child-set-property-function>glade_gtk_container_set_child_property</child-set-property-function>
       <child-get-property-function>glade_gtk_container_get_child_property</child-get-property-function>
       <get-children-function>glade_gtk_container_get_children</get-children-function>
-
+      <set-property-function>glade_gtk_container_set_property</set-property-function>
+      <get-property-function>glade_gtk_container_get_property</get-property-function>
+      <action-activate-function>glade_gtk_container_action_activate</action-activate-function>
+      <actions>
+	<action id="template" _name="Export as template" stock="gtk-convert" important="True"/>
+      </actions>
       <properties>
+	<property id="glade-template-class" _name="Template Class" save="False" custom-layout="True">
+	  <parameter-spec>
+	    <type>GParamString</type>
+	  </parameter-spec>
+	</property>
         <property id="border-width" common="True" weight="2.5"/>
         <property id="resize-mode" ignore="True" common="True" weight="4.7">
   	  <displayable-values>



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