? tests/testbuilder
Index: docs/gtkbuilder.dtd
===================================================================
RCS file: docs/gtkbuilder.dtd
diff -N docs/gtkbuilder.dtd
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ docs/gtkbuilder.dtd	6 Apr 2005 19:26:39 -0000
@@ -0,0 +1,89 @@
+<!-- proposed DTD for new glade format -->
+
+<!ELEMENT glade-interface (requires*, object*) >
+<!ATTLIST glade-interface
+  xmlns CDATA #FIXED 'http://glade.gnome.org/glade-2.0.dtd' >
+
+<!ELEMENT requires EMPTY >
+<!ATTLIST requires
+  lib CDATA #REQUIRED >
+
+<!ELEMENT object (property*, accessibility?, signal*, accelerator*, child*) >
+<!ATTLIST object
+  class CDATA #REQUIRED
+  id ID #REQUIRED
+  constructor ID #IMPLIED >
+
+<!ELEMENT property (#PCDATA) >
+<!-- type is an optional tag, and should be the string name of the
+     GType for the property -->
+<!-- translatable specifies whether the property should be translated
+     before use. -->
+<!-- context indicates that the value has a |-separated 
+     context which must be stripped before use, look up g_strip_context() 
+     in the GLib API documentation for details.-->
+<!ATTLIST property
+  name CDATA #REQUIRED
+  type CDATA #IMPLIED
+  translatable (yes|no) 'no'
+  context (yes|no) 'no'
+  comments CDATA #IMPLIED
+  agent CDATA #IMPLIED >
+
+<!ELEMENT atkproperty (#PCDATA | accessibility)* >
+<!ATTLIST atkproperty
+  name CDATA #REQUIRED
+  type CDATA #IMPLIED
+  translatable (yes|no) 'no'
+  context (yes|no) 'no'
+  comments CDATA #IMPLIED >
+
+<!ELEMENT atkrelation EMPTY >
+<!ATTLIST atkrelation
+  target CDATA #REQUIRED
+  type CDATA #REQUIRED >
+
+<!-- description is assumed to be a translatable string -->
+<!ELEMENT atkaction EMPTY >
+<!ATTLIST atkaction
+  action_name CDATA #REQUIRED
+  description CDATA #IMPLIED >
+
+<!ELEMENT accessibility (atkrelation | atkaction | atkproperty)* >
+
+<!ELEMENT signal (property*) >
+<!ATTLIST signal
+  name CDATA #REQUIRED
+  handler CDATA #REQUIRED
+  after (yes|no) 'no'
+  object IDREF #IMPLIED
+  last_modification_time CDATA #IMPLIED >
+
+<!ELEMENT accelerator EMPTY >
+<!ATTLIST accelerator
+  key CDATA #REQUIRED
+  modifiers CDATA #REQUIRED
+  signal CDATA #REQUIRED >
+
+<!ELEMENT child ((object|placeholder), packing?, layout?) >
+<!-- internal children should not have any properties set on them.
+     (Internal children are things like the scrollbars in a
+     GtkScrolledWindow, or the vbox in a GtkDialog). -->
+<!ATTLIST child
+  internal-child CDATA #IMPLIED >
+
+<!ELEMENT packing (property+) >
+
+<!ELEMENT placeholder EMPTY >
+
+<!-- only for GtkCellRenderers -->
+<!ELEMENT layout (propety+) >
+
+<!-- only for GtkUIManager -->
+<!ELEMENT ui-state CDATA matches EMPTY>
+<!ATTRLIST ui-state
+  id       ID    #REQUIRED
+  filename ID    #IMPLIED
+  merge    ID    (yes|no) 'yes'
+  data     CDATA #IMPLIED>
+  
Index: gtk/Makefile.am
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/Makefile.am,v
retrieving revision 1.269
diff -u -r1.269 Makefile.am
--- gtk/Makefile.am	20 Mar 2005 07:01:21 -0000	1.269
+++ gtk/Makefile.am	6 Apr 2005 19:26:42 -0000
@@ -129,6 +129,7 @@
 	gtkbin.h		\
 	gtkbindings.h		\
 	gtkbox.h		\
+	gtkbuilder.h		\
 	gtkbutton.h		\
 	gtkcalendar.h		\
 	gtkcelleditable.h	\
@@ -296,6 +297,8 @@
 # GTK+ header files that don't get installed
 gtk_private_h_sources =		\
 	gtkentryprivate.h	\
+	gtkbuilderparser.h	\
+	gtkbuilderprivate.h	\
 	gtkfilechooserembed.h	\
 	gtkfilechooserentry.h	\
 	gtkfilechooserdefault.h	\
@@ -339,6 +342,9 @@
 	gtkbin.c		\
 	gtkbindings.c		\
 	gtkbox.c		\
+	gtkbuilder.c		\
+	gtkbuilderobjects.c	\
+	gtkbuilderparser.c	\
 	gtkbutton.c		\
 	gtkcalendar.c		\
 	gtkcelleditable.c	\
Index: gtk/gtk.symbols
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtk.symbols,v
retrieving revision 1.36
diff -u -r1.36 gtk.symbols
--- gtk/gtk.symbols	4 Apr 2005 20:37:42 -0000	1.36
+++ gtk/gtk.symbols	6 Apr 2005 19:26:46 -0000
@@ -355,6 +355,29 @@
 #endif
 #endif
 
+#if IN_HEADER(__GTK_BUILDER_H__)
+#if IN_FILE(__GTK_BUILDER_C__)
+gtk_builder_get_type G_GNUC_CONST
+gtk_builder_new
+gtk_builder_new_from_buffer
+gtk_builder_signal_connect
+gtk_builder_signal_connect_data
+gtk_builder_signal_autoconnect
+gtk_builder_signal_connect_full
+gtk_builder_signal_autoconnect_full
+gtk_builder_get_object
+gtk_builder_get_objects
+gtk_builder_relative_file
+gtk_builder_get_ui_manager
+gtk_builder_merge_ui_from_name
+gtk_builder_remove_ui_from_name
+gtk_builder_add_ui_merge_id
+gtk_builder_get_object_name
+gtk_builder_get_object_tree
+gtk_builder_set_custom_handler
+#endif
+#endif
+
 #if IN_HEADER(__GTK_BUTTON_BOX_H__)
 #if IN_FILE(__GTK_BUTTON_BOX_C__)
 #ifndef GTK_DISABLE_DEPRECATED
Index: gtk/gtkbuilder.c
===================================================================
RCS file: gtk/gtkbuilder.c
diff -N gtk/gtkbuilder.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gtk/gtkbuilder.c	6 Apr 2005 19:26:46 -0000
@@ -0,0 +1,2465 @@
+/* -*- Mode: C; c-file-style: "gnu" -*- */
+/*
+ * gtkbuilder - a library for building interfaces from XML files at runtime
+ * Copyright (C) 1998-2005  James Henstridge <james@daa.com.au>
+ * Copyright (C) 2005       Johan Dahlin <johan@gnome.org>
+ *
+ * gtkbuilder.c: implementation of core public interface functions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA  02111-1307, USA.
+ */
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifdef ENABLE_NLS
+#  include <libintl.h>
+#endif /* ENABLE_NLS */
+
+#include <stdlib.h>
+#include <string.h>
+#include <gmodule.h>
+#include <gtk/gtkcontainer.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkuimanager.h>
+#include <atk/atk.h>
+
+#include "gtkbuilder.h"
+#include "gtkbuilderprivate.h"
+
+static const gchar *gtk_builder_tree_key     = "GtkBuilder::tree";
+static GQuark       gtk_builder_tree_id      = 0;
+static const gchar *gtk_builder_name_key     = "GtkBuilder::name";
+static GQuark       gtk_builder_name_id      = 0;
+static const gchar *gtk_builder_tooltips_key = "GtkBuilder::tooltips";
+static GQuark       gtk_builder_tooltips_id  = 0;
+
+static const gchar *gtk_builder_data_key   = "GtkBuilder::build-data";
+static GQuark       gtk_builder_data_id = 0;
+/* used to store a GArray of custom property handlers */
+static const gchar *gtk_builder_custom_props_key = "GtkBuilder::custom-props";
+static GQuark       gtk_builder_custom_props_id  = 0;
+/* used to store a cache of the property handlers (ie. this type's
+ * properties + parent properties) */
+static const gchar *gtk_builder_custom_props_cache_key = "GtkBuilder::custom-props-cache";
+static GQuark       gtk_builder_custom_props_cache_id  = 0;
+
+/* a small struct to hold info about a custom prop until we apply it */
+typedef struct _CustomPropData CustomPropData;
+struct _CustomPropData
+{
+  GtkApplyCustomPropFunc  apply_prop;
+  GtkBuilderProperty     *prop;
+};
+
+typedef struct
+{
+  GtkBuilderConnectFunc func;
+  gpointer              user_data;
+} connect_struct;
+
+typedef struct {
+  gint              merge_id;
+  GtkUIManager     *ui_manager;
+  GtkBuilderUIInfo *ui;
+} merge_data;
+
+typedef struct
+{
+  GCallback func;
+  gpointer  user_data;
+} connect_data_data;
+
+typedef struct _GtkObjectBuildData GtkObjectBuildData;
+struct _GtkObjectBuildData
+{
+  GtkBuildChildrenFunc     build_children;
+  GtkFindInternalChildFunc find_internal_child;
+};
+
+typedef struct _CustomPropInfo CustomPropInfo;
+struct _CustomPropInfo
+{
+  GQuark                 name_quark;
+  GtkApplyCustomPropFunc apply_prop;
+};
+
+static GObjectClass *parent_class;
+static void  gtk_builder_init                 (GtkBuilder       *builder);
+static void  gtk_builder_class_init           (GtkBuilderClass  *class);
+
+static void  gtk_builder_finalize             (GObject        *object);
+
+static void  gtk_builder_build_interface      (GtkBuilder       *builder,
+					       GtkBuilderInterface *iface,
+					       const char     *root);
+
+static GType gtk_builder_real_lookup_type     (GtkBuilder       *builder,
+					       const char     *gtypename);
+
+static void  gtk_builder_child_set_properties (GtkBuilder        *builder,
+					       GObject         *child,
+					       GtkBuilderObjectInfo *info);
+
+extern void _gtk_builder_init_types         (void);
+
+/**
+ * gtk_builder_get_type:
+ *
+ * Creates the typecode for the GtkBuilder object type.
+ *
+ * Returns: the typecode for the GtkBuilder object type.
+ */
+GType
+gtk_builder_get_type (void)
+{
+  static GType builder_type = 0;
+
+  if (!builder_type) {
+    static const GTypeInfo builder_info =
+      {
+	sizeof (GtkBuilderClass),
+	(GBaseInitFunc) NULL,
+	(GBaseFinalizeFunc) NULL,
+	(GClassInitFunc) gtk_builder_class_init,
+	(GClassFinalizeFunc) NULL,
+	NULL,
+      
+	sizeof (GtkBuilder),
+	0, /* n_preallocs */
+	(GInstanceInitFunc) gtk_builder_init,
+      };
+
+    builder_type = g_type_register_static (G_TYPE_OBJECT, "GtkBuilder",
+					   &builder_info, 0);
+  }
+  
+  return builder_type;
+}
+
+static void
+gtk_builder_class_init (GtkBuilderClass *class)
+{
+  parent_class = g_type_class_peek_parent (class);
+
+  G_OBJECT_CLASS(class)->finalize = gtk_builder_finalize;
+  
+  class->lookup_type = gtk_builder_real_lookup_type;
+  
+  gtk_builder_tree_id = g_quark_from_static_string (gtk_builder_tree_key);
+  gtk_builder_name_id = g_quark_from_static_string (gtk_builder_name_key);
+  gtk_builder_tooltips_id = g_quark_from_static_string (gtk_builder_tooltips_key);
+  
+  /* make sure all types are loaded */
+  _gtk_builder_init_types ();
+}
+
+static void
+gtk_builder_init (GtkBuilder *builder)
+{
+  GtkBuilderPrivate *priv;
+	
+  builder->priv = priv = g_new0 (GtkBuilderPrivate, 1);
+  builder->filename = NULL;
+  
+  priv->tree = NULL;
+  priv->tooltips = gtk_tooltips_new ();
+  gtk_tooltips_enable (priv->tooltips);
+  g_object_ref (priv->tooltips);
+  gtk_object_sink (GTK_OBJECT (priv->tooltips));
+  priv->name_hash = g_hash_table_new (g_str_hash, g_str_equal);
+  priv->signals = g_hash_table_new (g_str_hash, g_str_equal);
+  priv->ui_hash  = g_hash_table_new (g_str_hash, g_str_equal);
+  priv->toplevel = NULL;
+  priv->accel_group = NULL;
+  priv->default_widget = NULL;
+  priv->focus_widget = NULL;
+  priv->deferred_props = NULL;
+}
+
+static void
+gtk_builder_destroy_signals (char *key, GList *signal_datas)
+{
+  GList *tmp;
+  
+  for (tmp = signal_datas; tmp; tmp = tmp->next)
+    {
+      GtkBuilderSignalData *data = tmp->data;
+      g_free(data);
+    }
+  
+  g_list_free(signal_datas);
+}
+
+static void
+remove_data_func (gpointer key, gpointer value, gpointer user_data)
+{
+  GObject *object = value;
+  GtkBuilder *builder = GTK_BUILDER (user_data);
+  int i;
+  
+  g_object_set_qdata (object, gtk_builder_tree_id, NULL);
+  g_object_set_qdata (object, gtk_builder_name_id, NULL);
+
+  /* If its not a GtkObject (which has a ::destroy) and is a toplevel
+   * unref it here. This applies to GtkUIMangers, GtkTextBuffers etc
+   */
+  if (!GTK_IS_OBJECT (object))
+    for (i = 0; i < builder->priv->tree->n_toplevels; i++)
+      if (!strcmp (key, builder->priv->tree->toplevels[i]->name))
+	{
+	  g_object_unref (object);
+	}
+}
+
+static void
+remove_ui_func (gpointer key, gpointer value, gpointer user_data)
+{
+  merge_data *data = value;
+  g_free(data);
+}
+
+static void
+gtk_builder_finalize (GObject *object)
+{
+  GtkBuilder *builder = GTK_BUILDER (object);
+  GtkBuilderPrivate *priv = builder->priv;
+  
+  g_free (builder->filename);
+  builder->filename = NULL;
+
+  if (priv)
+    {
+      /* remove data items from all the widgets that are still
+       * live. */
+      g_hash_table_foreach (priv->name_hash, remove_data_func, builder);
+      /* strings are owned in the GtkBuildInterface structure */
+      g_hash_table_destroy (priv->name_hash);
+
+      g_hash_table_foreach (priv->ui_hash, remove_ui_func, builder);
+      g_hash_table_destroy (priv->ui_hash);
+
+      g_hash_table_foreach (priv->signals,
+			    (GHFunc) gtk_builder_destroy_signals, NULL);
+      g_hash_table_destroy (priv->signals);
+
+      if (priv->tooltips)
+	g_object_unref (priv->tooltips);
+
+      if (priv->accel_group)
+	g_object_unref (priv->accel_group);
+
+      if (priv->tree)
+	gtk_builder_interface_destroy (priv->tree);
+      
+      g_free (builder->priv);
+    }
+    
+  builder->priv = NULL;
+  if (parent_class->finalize)
+    (* parent_class->finalize) (object);
+}
+
+/**
+ * gtk_builder_real_lookup_type
+ *
+ * Default implementation of the lookup_type() virtual function.
+ */
+static GType
+gtk_builder_real_lookup_type (GtkBuilder *builder, const char *gtypename)
+{
+  return g_type_from_name (gtypename);
+}
+
+/**
+ * gtk_builder_new:
+ * @fname: the Builder file name.
+ * @root: the object node in @fname to start building from (or %NULL)
+ * @domain: the translation domain for the Builder file (or %NULL for default)
+ *
+ * Creates a new GtkBuilder object (and the corresponding objects) from the
+ * Builder file @fname.  Optionally it will only build the interface from the
+ * object node @root (if it is not %NULL).  This feature is useful if you
+ * only want to build say a toolbar or menu from the Builder file, but not the
+ * window it is embedded in.  Note also that the Builder parse tree is cached
+ * to speed up creating another GtkBuilder object for the same file
+ *
+ * Returns: the newly created GtkBuilder object, or NULL on failure.
+ */
+
+GtkBuilder *
+gtk_builder_new (const char *fname, const char *root, const char *domain)
+{
+  GtkBuilder *builder;
+  GtkBuilderInterface *iface;
+
+  g_return_val_if_fail (fname != NULL, FALSE);
+  
+  iface = gtk_builder_parser_parse_file (fname, domain);
+  if (!iface)
+    return FALSE;
+  
+  builder = g_object_new (GTK_TYPE_BUILDER, NULL);
+  
+  if (builder->filename)
+    g_free (builder->filename);
+  builder->filename = g_strdup (fname);
+  builder->priv->tree = iface;
+    
+  gtk_builder_build_interface (builder, iface, root);
+  
+  return builder;
+}
+
+/**
+ * gtk_builder_new_from_buffer:
+ * @buffer: the memory buffer containing the Builder document.
+ * @size: the size of the buffer.
+ * @root: the object node in @buffer to start building from (or %NULL)
+ * @domain: the translation domain to use for this interface (or %NULL)
+ *
+ * Creates a new GtkBuilder object (and the corresponding objects) from the
+ * buffer @buffer.  Optionally it will only build the interface from the
+ * object node @root (if it is not %NULL).  This feature is useful if you
+ * only want to build say a toolbar or menu from the Builder document, but not the
+ * window it is embedded in.
+ *
+ * Returns: the newly created GtkBuilder object, or NULL on failure.
+ */
+GtkBuilder *
+gtk_builder_new_from_buffer (const char *buffer, int size, const char *root,
+			     const char *domain)
+{
+  GtkBuilder *builder;
+  GtkBuilderInterface *iface;
+
+  iface = gtk_builder_parser_parse_buffer (buffer, size, domain);
+  if (!iface)
+    return NULL;
+  
+  builder = g_object_new (GTK_TYPE_BUILDER, NULL);
+  builder->priv->tree = iface;
+  builder->filename = NULL;
+  gtk_builder_build_interface (builder, iface, root);
+  
+  return builder;
+}
+
+/**
+ * gtk_builder_signal_connect:
+ * @builder: the GtkBuilder object
+ * @handlername: the signal handler name
+ * @func: the signal handler function
+ *
+ * In the gtk interface descriptions, signal handlers are specified for
+ * objects by name.  This function allows you to connect a C function to
+ * all signals in the GtkBuilder file with the given signal handler name.
+ */
+void
+gtk_builder_signal_connect (GtkBuilder *builder, const char *handlername,
+			    GCallback func)
+{
+  GList *signals;
+
+  g_return_if_fail(builder != NULL);
+  g_return_if_fail(handlername != NULL);
+  g_return_if_fail(func != NULL);
+
+  signals = g_hash_table_lookup (builder->priv->signals, handlername);
+  for (; signals != NULL; signals = signals->next)
+    {
+      GtkBuilderSignalData *data = signals->data;
+      
+      if (data->connect_object)
+	{
+	  GObject *other = g_hash_table_lookup (builder->priv->name_hash,
+						data->connect_object);
+	  
+	  g_signal_connect_object (data->signal_object, data->signal_name,
+				   func, other, (data->signal_after ? G_CONNECT_AFTER : 0)
+				   | G_CONNECT_SWAPPED);
+	}
+      else
+	{
+	  /* the signal_data argument is just a string, but may be
+	   * helpful for someone */
+	  if (data->signal_after)
+	    g_signal_connect_after (data->signal_object, data->signal_name,
+				    func, NULL);
+	  else
+	    g_signal_connect (data->signal_object, data->signal_name,
+			      func, NULL);
+	}
+    }
+}
+
+static void
+autoconnect_foreach (const char *signal_handler,
+		     GList *signals,
+		     GModule *allsymbols)
+{
+  GCallback func;
+  
+  if (!g_module_symbol (allsymbols, signal_handler, (gpointer)&func))
+    g_warning("could not find signal handler '%s'.", signal_handler);
+  else
+    for (; signals != NULL; signals = signals->next)
+      {
+	GtkBuilderSignalData *data = signals->data;
+	if (data->connect_object)
+	  {
+	    GtkBuilder *builder = gtk_builder_get_object_tree (G_OBJECT (data->signal_object));
+	    GObject *other = g_hash_table_lookup (builder->priv->name_hash,
+						  data->connect_object);
+	    
+	    g_signal_connect_object (data->signal_object, data->signal_name,
+				     func, other, (data->signal_after ? G_CONNECT_AFTER : 0)
+				     | G_CONNECT_SWAPPED);
+	  }
+	else
+	  {
+	    /* the signal_data argument is just a string, but may
+	     * be helpful for someone */
+	    if (data->signal_after)
+	      g_signal_connect_after (data->signal_object,
+				      data->signal_name,
+				      func, NULL);
+	    else
+	      g_signal_connect (data->signal_object,
+				data->signal_name,
+				func, NULL);
+	  }
+      }
+}
+
+/**
+ * gtk_builder_signal_autoconnect:
+ * @builder: the GtkBuilder object.
+ *
+ * This function is a variation of gtk_builder_signal_connect.  It uses
+ * gmodule's introspective features (by openning the module %NULL) to
+ * look at the application's symbol table.  From here it tries to match
+ * the signal handler names given in the interface description with
+ * symbols in the application and connects the signals.
+ * 
+ * Note that this function will not work correctly if gmodule is not
+ * supported on the platform.
+ */
+void
+gtk_builder_signal_autoconnect (GtkBuilder *builder)
+{
+  GModule *allsymbols;
+  
+  g_return_if_fail (builder != NULL);
+  if (!g_module_supported ())
+    g_error ("gtk_builder_signal_autoconnect requires working gmodule");
+
+  /* get a handle on the main executable -- use this to find symbols */
+  allsymbols = g_module_open (NULL, 0);
+  g_hash_table_foreach (builder->priv->signals,
+			(GHFunc) autoconnect_foreach,
+			allsymbols);
+}
+
+static void
+autoconnect_full_foreach (const char *signal_handler,
+			  GList *signals,
+			  connect_struct *conn)
+{
+  GtkBuilder *builder = NULL;
+  
+  for (; signals != NULL; signals = signals->next)
+    {
+      GtkBuilderSignalData *data = signals->data;
+      GObject *connect_object = NULL;
+      
+      if (data->connect_object)
+	{
+	  if (!builder)
+	    builder = gtk_builder_get_object_tree (G_OBJECT (data->signal_object));
+	  connect_object = g_hash_table_lookup (builder->priv->name_hash,
+						data->connect_object);
+	}
+
+      (* conn->func) (signal_handler, data->signal_object,
+		      data->signal_name, NULL,
+		      connect_object, data->signal_after,
+		      conn->user_data);
+    }
+}
+
+/**
+ * GtkBuilderConnectFunc:
+ * @handler_name: the name of the handler function to connect.
+ * @object: the object to connect the signal to.
+ * @signal_name: the name of the signal.
+ * @signal_data: the string value of the signal data given in the Builder file.
+ * @connect_object: non NULL if g_signal_connect_object should be used.
+ * @after: TRUE if the connection should be made with g_signal_connect_after.
+ * @user_data: the user data argument.
+ *
+ * This is the signature of a function used to connect signals.  It is used
+ * by the gtk_builder_signal_connect_full and gtk_builder_signal_autoconnect_full
+ * functions.  It is mainly intented for interpreted language bindings, but
+ * could be useful where the programmer wants more control over the signal
+ * connection process.
+ */
+
+/**
+ * gtk_builder_signal_connect_full:
+ * @builder: the GtkBuilder object.
+ * @handler_name: the name of the signal handler that we want to connect.
+ * @func: the function to use to connect the signals.
+ * @user_data: arbitrary data to pass to the connect function.
+ *
+ * This function is similar to gtk_builder_signal_connect, except that it
+ * allows you to give an arbitrary function that will be used for actually
+ * connecting the signals.  This is mainly useful for writers of interpreted
+ * language bindings, or applications where you need more control over the
+ * signal connection process.
+ */
+void
+gtk_builder_signal_connect_full (GtkBuilder *builder,
+				 const gchar *handler_name,
+				 GtkBuilderConnectFunc func,
+				 gpointer user_data)
+{
+  connect_struct conn;
+  GList *signals;
+  
+  g_return_if_fail (builder != NULL);
+  g_return_if_fail (handler_name != NULL);
+  g_return_if_fail (func != NULL);
+  
+  /* rather than rewriting the code from the autoconnect_full
+   * version, just reuse its helper function */
+  conn.func = func;
+  conn.user_data = user_data;
+  signals = g_hash_table_lookup (builder->priv->signals, handler_name);
+  autoconnect_full_foreach (handler_name, signals, &conn);
+}
+
+/**
+ * gtk_builder_signal_autoconnect_full:
+ * @builder: the GtkBuilder object.
+ * @func: the function used to connect the signals.
+ * @user_data: arbitrary data that will be passed to the connection function.
+ *
+ * This function is similar to gtk_builder_signal_connect_full, except that it
+ * will try to connect all signals in the interface, not just a single
+ * named handler.  It can be thought of the interpeted language binding
+ * version of gtk_builder_signal_autoconnect, except that it does not
+ * require gmodule to function correctly.
+ */
+void
+gtk_builder_signal_autoconnect_full (GtkBuilder *builder,
+				     GtkBuilderConnectFunc func,
+				     gpointer user_data)
+{
+  connect_struct conn;
+  
+  g_return_if_fail (builder != NULL);
+  g_return_if_fail (func != NULL);
+  
+  conn.func = func;
+  conn.user_data = user_data;
+  g_hash_table_foreach (builder->priv->signals,
+			(GHFunc) autoconnect_full_foreach, &conn);
+}
+
+/**
+ * gtk_builder_signal_connect_data:
+ * @builder: the GtkBuilder object
+ * @handlername: the signal handler name
+ * @func: the signal handler function
+ * @user_data: the signal handler data
+ *
+ * In the gtk interface descriptions, signal handlers are specified for
+ * objects by name.  This function allows you to connect a C function to
+ * all signals in the GtkBuilder file with the given signal handler name.
+ *
+ * It differs from gtk_builder_signal_connect since it allows you to
+ * specify the data parameter for the signal handler.  It is also a small
+ * demonstration of how to use gtk_builder_signal_connect_full.
+ */
+static void
+connect_data_connect_func (const gchar *handler_name,
+			   GObject *object,
+			   const gchar *signal_name,
+			   const gchar *signal_data,
+			   GObject *connect_object,
+			   gboolean after,
+			   gpointer user_data)
+{
+  connect_data_data *data = (connect_data_data *)user_data;
+  
+  if (connect_object)
+    {
+      g_signal_connect_object (object, signal_name, data->func,
+			       connect_object, (after ? G_CONNECT_AFTER : 0) | G_CONNECT_SWAPPED);
+    }
+  else
+    {
+      if (after)
+	g_signal_connect_after (object, signal_name,
+				data->func, data->user_data);
+      else
+	g_signal_connect (object, signal_name,
+			  data->func, data->user_data);
+    }
+}
+
+void
+gtk_builder_signal_connect_data (GtkBuilder *builder,
+				 const char *handlername,
+				 GCallback func,
+				 gpointer user_data)
+{
+  connect_data_data data;
+  
+  data.func = func;
+  data.user_data = user_data;
+  
+  gtk_builder_signal_connect_full (builder, handlername,
+				   connect_data_connect_func, &data);
+}
+
+/**
+ * gtk_builder_get_object:
+ * @builder: the GtkBuilder object.
+ * @name: the name of the object.
+ *
+ * This function is used to get a pointer to the GObject corresponding to
+ * @name in the interface description.  You would use this if you have to do
+ * anything to the object after loading.
+ *
+ * Returns: the object matching @name, or %NULL if none exists.
+ */
+GObject *
+gtk_builder_get_object (GtkBuilder *builder, const char *name)
+{
+  g_return_val_if_fail (builder != NULL, NULL);
+  g_return_val_if_fail (name != NULL, NULL);
+
+  return g_hash_table_lookup (builder->priv->name_hash, name);
+}
+
+
+/**
+ * gtk_builder_get_object:
+ * @builder: the GtkBuilder object.
+ * @name: the name of the object.
+ *
+ * This function is used to get a list of pointers to the GObject(s)
+ * with names that start with the string @name in the interface description.
+ * You would use this if you have to do something  to all of these objects
+ * after loading.
+ *
+ * Returns: A list of the widget that match @name as the start of their
+ * name, or %NULL if none exists.
+ */
+static void
+object_prefix_add_to_list (gchar *name, gpointer value,
+			   GList **list)
+{
+  *list = g_list_prepend (*list, value);
+}
+
+GList *
+gtk_builder_get_objects (GtkBuilder *builder)
+{
+  GList *list = NULL;
+  
+  g_hash_table_foreach (builder->priv->name_hash,
+			(GHFunc) object_prefix_add_to_list, &list);
+  list = g_list_reverse (list);
+  
+  return list;
+}
+
+/**
+ * gtk_builder_relative_file
+ * @builder: the GtkBuilder object.
+ * @filename: the filename.
+ *
+ * This function resolves a relative pathname, using the directory of the
+ * Builder file as a base.  If the pathname is absolute, then the original
+ * filename is returned.
+ *
+ * Returns: the filename.  The result must be g_free'd.
+ */
+gchar *
+gtk_builder_relative_file (GtkBuilder *builder, const gchar *filename)
+{
+  gchar *dirname, *tmp;
+  
+  g_return_val_if_fail (builder != NULL, NULL);
+  g_return_val_if_fail (filename != NULL, NULL);
+
+  if (g_path_is_absolute (filename)) /* an absolute pathname */
+    return g_strdup (filename);
+  
+  /* prepend Builder file's dir onto filename */
+  dirname = g_path_get_dirname (builder->filename);	
+  tmp = g_strconcat (dirname, G_DIR_SEPARATOR_S, filename, NULL);
+  g_free (dirname);
+  return tmp;
+}
+
+/**
+ * gtk_builder_get_object_name:
+ * @object: the object
+ *
+ * Used to get the name of a object that was generated by a GtkBuilder object.
+ *
+ * Returns: the name of the object.
+ */
+const char *
+gtk_builder_get_object_name (GObject *object)
+{
+  g_return_val_if_fail (object != NULL, NULL);
+
+  return (const char *) g_object_get_qdata (G_OBJECT (object),
+					    gtk_builder_name_id);
+}
+
+/**
+ * gtk_get_object_tree:
+ * @object: the object
+ *
+ * Description:
+ * This function is used to get the GtkBuilder object that built this object.
+ *
+ * Returns: the GtkBuilder object that built this object.
+ */
+GtkBuilder *
+gtk_builder_get_object_tree (GObject *object)
+{
+  g_return_val_if_fail (object != NULL, NULL);
+    
+  return g_object_get_qdata (G_OBJECT (object), gtk_builder_tree_id);
+}
+
+/* ------------------------------------------- */
+
+
+/**
+ * gtk_builder_set_toplevel:
+ * @builder: the GtkBuilder object.
+ * @window: the toplevel.
+ *
+ * This is used while the tree is being built to set the toplevel window that
+ * is currently being built.  It is mainly used to enable GtkAccelGroup's to
+ * be bound to the correct window, but could have other uses.
+ */
+void
+gtk_builder_set_toplevel (GtkBuilder *builder, GtkWindow *window)
+{
+  if (builder->priv->focus_widget)
+    gtk_widget_grab_focus (builder->priv->focus_widget);
+  if (builder->priv->default_widget)
+    gtk_widget_grab_default (builder->priv->default_widget);
+  
+  builder->priv->focus_widget = NULL;
+  builder->priv->default_widget = NULL;
+  builder->priv->toplevel = window;
+  /* new toplevel needs new accel group */
+  if (builder->priv->accel_group)
+    g_object_unref (builder->priv->accel_group);
+  builder->priv->accel_group = NULL;
+
+  if (GTK_IS_WINDOW (window)) {
+    /* the window should hold a reference to the tooltips object */
+    g_object_ref (builder->priv->tooltips);
+    g_object_set_qdata_full (G_OBJECT (window),
+			     gtk_builder_tooltips_id,
+			     builder->priv->tooltips,
+			     (GDestroyNotify) g_object_unref);
+  }
+}
+
+/**
+ * gtk_builder_handle_object_prop
+ * @builder: the GtkBuilder object
+ * @object: the property the object widgetto set the property on.
+ * @prop_name: the name of the property.
+ * @value_name: the name of the object used as the value for the property.
+ *
+ * Some objects have properties of type GtkWidget.  These are
+ * represented as the widget name in the gtk file.  When
+ * constructing the interface, the widget specified as the value for a
+ * property may not exist yet.
+ *
+ * Rather than setting the property directly, this function should be
+ * used.  It will perform the name to GtkWidget conversion, and if the
+ * widget is yet to be constructed, defer setting the property until
+ * the widget is constructed.
+ */
+void
+gtk_builder_handle_object_prop (GtkBuilder *builder,
+				GObject *object,
+				const gchar *prop_name,
+				const gchar *value_name)
+{
+  GtkWidget *value_widget;
+  
+  g_return_if_fail (GTK_IS_BUILDER (builder));
+
+  value_widget = g_hash_table_lookup (builder->priv->name_hash, value_name);
+  if (value_widget) {
+    g_object_set (G_OBJECT (object), prop_name, value_widget, NULL);
+  } else {
+    GtkBuilderDeferredProperty *dprop = g_new (GtkBuilderDeferredProperty, 1);
+
+    dprop->target_name = value_name;
+    dprop->type = DEFERRED_PROP;
+    dprop->d.prop.object = G_OBJECT (object);
+    dprop->d.prop.prop_name = prop_name;
+    
+    builder->priv->deferred_props = g_list_prepend (builder->priv->deferred_props,
+						    dprop);
+  }
+}
+
+/**
+ * gtk_builder_ensure_accel:
+ * @builder: the GtkBuilder object.
+ *
+ * This function is used to get the current GtkAccelGroup.  If there isn't
+ * one, a new one is created and bound to the current toplevel window (if
+ * a toplevel has been set).
+ * Returns: the current GtkAccelGroup.
+ */
+GtkAccelGroup *
+gtk_builder_ensure_accel (GtkBuilder *builder)
+{
+  if (!builder->priv->accel_group)
+    builder->priv->accel_group = gtk_accel_group_new ();
+  
+  if (builder->priv->toplevel)
+    gtk_window_add_accel_group (builder->priv->toplevel,
+				builder->priv->accel_group);
+  
+  return builder->priv->accel_group;
+}
+
+/* this is a private function */
+static void
+gtk_builder_add_signals (GtkBuilder *builder,
+			 GObject *object,
+			 GtkBuilderObjectInfo *info)
+{
+  gint i;
+
+  for (i = 0; i < info->n_signals; i++)
+    {
+      GtkBuilderSignalInfo *sig = &info->signals[i];
+      GtkBuilderSignalData *data = g_new0 (GtkBuilderSignalData, 1);
+      GList *list;
+
+      data->signal_object = G_OBJECT (object);
+      data->signal_name = sig->name;
+      data->connect_object = sig->object;
+      data->signal_after = sig->after;
+
+      list = g_hash_table_lookup (builder->priv->signals, sig->handler);
+      list = g_list_prepend (list, data);
+      g_hash_table_insert (builder->priv->signals, sig->handler, list);
+    }
+}
+
+/* this is a private function */
+static void
+gtk_builder_add_atk_actions (GtkBuilder           *builder,
+			     GtkWidget            *w,
+			     GtkBuilderObjectInfo *info)
+{
+  gint i, n_actions;
+  AtkObject *accessible;
+  AtkAction *action;
+  
+  if (info->n_atk_actions == 0)
+    return;
+  
+  accessible = gtk_widget_get_accessible (w);
+  
+  if (!ATK_IS_ACTION (accessible))
+    {
+      g_warning("widgets of type %s don't have actions, but one is specified",
+		G_OBJECT_TYPE_NAME (w));
+      return;
+    }
+  
+  action = ATK_ACTION (accessible);
+  n_actions = atk_action_get_n_actions (action);    
+    
+  for (i = 0; i < info->n_atk_actions; i++)
+    {
+      GtkBuilderAtkActionInfo *action_info = &info->atk_actions[i];
+      int j;
+      
+      for (j = 0; j < n_actions; j++)
+	{
+	  if (strcmp (atk_action_get_name (action, j),
+		      action_info->action_name) == 0)
+	    {
+	      break;
+	    }
+	}
+      
+      if (j < n_actions)
+	{
+	  atk_action_set_description (action, j, action_info->description);
+	}
+      else
+	{
+	  /* don't show a warning -- the action names might not
+	   * be registered if libgail hasn't been loaded */
+	}
+    }
+}
+
+/* this is a private function */
+static void
+gtk_builder_add_atk_relations (GtkBuilder           *builder,
+			       GtkWidget            *w,
+			       GtkBuilderObjectInfo *info)
+{
+  gint i;
+  AtkObject *accessible;
+  AtkRelationSet *relations;
+  
+  if (info->n_relations == 0)
+    return;
+  
+  accessible = gtk_widget_get_accessible (w);
+  relations = atk_object_ref_relation_set (accessible);
+    
+  for (i = 0; i < info->n_relations; i++)
+    {
+      GtkBuilderAtkRelationInfo *rinfo = &info->relations[i];
+      GObject *target_widget = gtk_builder_get_object (builder, rinfo->target);
+      AtkRelationType relation_type;
+      
+      relation_type = atk_relation_type_for_name (rinfo->type);
+      if (target_widget)
+	{
+	  AtkObject *target_accessible;
+	  
+	  target_accessible = gtk_widget_get_accessible (GTK_WIDGET (target_widget));
+	  
+	  atk_relation_set_add_relation_by_type (relations, relation_type,
+						 target_accessible);
+	}
+      else
+	{
+	  GtkBuilderDeferredProperty *dprop = g_new (GtkBuilderDeferredProperty, 1);
+	  
+	  dprop->target_name = rinfo->target;
+	  dprop->type = DEFERRED_REL;
+	  dprop->d.rel.relation_set = g_object_ref (relations);
+	  dprop->d.rel.relation_type = relation_type;
+	  
+	  builder->priv->deferred_props =
+	    g_list_prepend (builder->priv->deferred_props, dprop);
+	}
+    }
+  g_object_unref (relations);
+}
+
+static void
+gtk_builder_add_accels (GtkBuilder *builder, GtkWidget *w, GtkBuilderObjectInfo *info)
+{
+  gint i;
+  
+  for (i = 0; i < info->n_accels; i++)
+    {
+      GtkBuilderAccelInfo *accel = &info->accels[i];
+      
+      GTK_NOTE (BUILDER,
+		g_message ("New Accel: key=%d,mod=%d -> %s:%s",
+			   accel->key, accel->modifiers,
+			   gtk_widget_get_name (w), accel->signal));
+      gtk_widget_add_accelerator (w, accel->signal,
+				  gtk_builder_ensure_accel (builder),
+				  accel->key, accel->modifiers,
+				  GTK_ACCEL_VISIBLE);
+    }
+}
+
+static void
+gtk_builder_add_accessibility_info (GtkBuilder           *builder,
+				    GtkWidget            *w,
+				    GtkBuilderObjectInfo *info)
+{
+  gint i;
+  AtkObject *accessible = gtk_widget_get_accessible (w);
+    
+  for (i = 0; i < info->n_atk_props; i++)
+    {
+      GParamSpec *pspec;
+      GValue value = { 0 };
+      
+      pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (accessible),
+					    info->atk_props[i].name);
+      if (!pspec)
+	{
+	  g_warning ("unknown property `%s' for class `%s'",
+		     info->atk_props[i].name, G_OBJECT_TYPE_NAME (accessible));
+	  continue;
+	}
+      else if (gtk_builder_set_value_from_string (builder, pspec,
+						  info->atk_props[i].value,
+						  &value))
+	{
+	  g_object_set_property (G_OBJECT (accessible),
+				 info->atk_props[i].name, &value);
+	  g_value_unset (&value);
+        }
+
+      GTK_NOTE (BUILDER,
+		g_message ("Adding accessibility property %s:%s",
+			   info->atk_props[i].name,
+			   info->atk_props[i].value));
+    }
+
+  /* Hang on, we're not done yet ;-) */
+  gtk_builder_add_atk_actions(builder, w, info);
+  gtk_builder_add_atk_relations(builder, w, info);
+  
+  /*
+    hmm, might have to defer some relations,
+    if the widgets they refer to haven't
+    been constructed yet (will look at the other prop-as-widget
+    stuff for ideas)
+  */
+}
+
+/**
+ * GtkBuilderCustomWidgetHandler:
+ * @builder: the GtkBuilder object.
+ * @func_name: the function name.
+ * @name: the name of the widget to be created.
+ * @string1: the string1 property.
+ * @string2: the string2 property.
+ * @int1: the int1 property.
+ * @int2: the int2 property.
+ * @user_data: the data passed to gtk_builder_set_custom_handler()
+ *
+ * This prototype is for a function that creates custom widgets.
+ * Returns: the GtkWidget.
+ */
+static GObject *
+default_custom_handler (GtkBuilder *builder, gchar *func_name, gchar *name,
+			gchar *string1, gchar *string2, gint int1, gint int2,
+			gpointer user_data)
+{
+  typedef GObject *(* create_func) (gchar *name,
+				    gchar *string1, gchar *string2,
+				    gint int1, gint int2);
+  GModule *allsymbols;
+  create_func func;
+
+  if (!g_module_supported ())
+    {
+      g_error("custom_new requires gmodule to work correctly");
+      return NULL;
+    }
+  
+  allsymbols = g_module_open(NULL, 0);
+  if (g_module_symbol (allsymbols, func_name, (gpointer) &func))
+    return (* func) (name, string1, string2, int1, int2);
+  
+  g_warning ("could not find widget creation function");
+  return NULL;
+}
+
+static GtkBuilderCustomObjectHandler custom_handler = default_custom_handler;
+static gpointer custom_user_data = NULL;
+
+/**
+ * gtk_builder_set_custom_handler:
+ * @handler: the custom widget handler
+ * @user_data: user data passed to the custom handler
+ *
+ * Calling this function allows you to override the default behaviour
+ * when a Custom widget is found in an interface.  This could be used by
+ * a language binding to call some other function, or to limit what
+ * functions can be called to create custom widgets.
+ */
+void
+gtk_builder_set_custom_handler (GtkBuilderCustomObjectHandler handler,
+				gpointer user_data)
+{
+  custom_handler = handler;
+  custom_user_data = user_data;
+}
+
+static GObject *
+gtk_create_custom (GtkBuilder *builder, gchar *func_name, gchar *name,
+		   gchar *string1, gchar *string2, gint int1, gint int2)
+{
+  return (* custom_handler) (builder, func_name, name, string1, string2,
+			     int1, int2, custom_user_data);
+}
+
+static GObject *
+custom_new (GtkBuilder *builder, GtkBuilderObjectInfo *info)
+{
+  GObject *obj = NULL;
+  gchar *func_name = NULL, *string1 = NULL, *string2 = NULL;
+  gint int1 = 0, int2 = 0;
+  int i;
+  
+  for (i = 0; i < info->n_properties; i++)
+    {
+      char *name  = info->properties[i].name;
+      char *value = info->properties[i].value;
+      
+      if (!strcmp (name, "creation_function"))
+	func_name = value;
+      else if (!strcmp (name, "string1"))
+	string1 = value;
+      else if (!strcmp (name, "string2"))
+	string2 = value;
+      else if (!strcmp (name, "int1"))
+	int1 = strtol (value, NULL, 0);
+      else if (!strcmp (name, "int2"))
+	int2 = strtol (value, NULL, 0);
+    }
+  
+  obj = gtk_create_custom (builder, func_name, info->name, string1,
+			   string2, int1, int2);
+  /* fallback to prevent segfault */
+  if (obj == NULL)
+    obj = G_OBJECT (gtk_label_new ("[custom widget creation failed]"));
+  return obj;
+}
+
+/**
+ * gtk_builder_enum_from_string
+ * @type: the GType for this enum type.
+ * @string: the string representation of the enum value.
+ *
+ * This helper routine is designed to be used by widget build routines to
+ * convert the string representations of enumeration values found in the
+ * Builder descriptions to the integer values that can be used to configure
+ * the widget.
+ *
+ * Returns: the integer value for this enumeration, or 0 if it couldn't be
+ * found.
+ */
+gint
+gtk_builder_enum_from_string (GType type, const char *string)
+{
+  GEnumClass *eclass;
+  GEnumValue *ev;
+  gchar *endptr;
+  gint ret = 0;
+
+  ret = strtoul (string, &endptr, 0);
+  if (endptr != string) /* parsed a number */
+    return ret;
+
+  eclass = g_type_class_ref (type);
+  ev = g_enum_get_value_by_name (eclass, string);
+  if (!ev) ev = g_enum_get_value_by_nick (eclass, string);
+  if (ev)  ret = ev->value;
+  
+  g_type_class_unref (eclass);
+  
+  return ret;
+}
+
+/**
+ * gtk_flags_from_string
+ * @type: the GType for this flags type.
+ * @string: the string representation of the flags value.
+ *
+ * This helper routine is designed to be used by widget build routines
+ * to convert the string representations of flags values found in the
+ * Builder descriptions to the integer values that can be used to
+ * configure the widget.  The string is composed of string names or
+ * nicknames for various flags separated by '|'.
+ *
+ * Returns: the integer value for this flags string
+ */
+guint
+gtk_builder_flags_from_string (GType type, const char *string)
+{
+  GFlagsClass *fclass;
+  gchar *endptr, *prevptr;
+  guint i, j, ret = 0;
+  char *flagstr;
+  
+  ret = strtoul (string, &endptr, 0);
+  if (endptr != string) /* parsed a number */
+    return ret;
+  
+  fclass = g_type_class_ref (type);
+  
+  flagstr = g_strdup (string);
+  for (ret = i = j = 0; ; i++)
+    {
+      gboolean eos;
+      
+      eos = flagstr [i] == '\0';
+      
+      if (eos || flagstr [i] == '|')
+	{
+	  GFlagsValue *fv;
+	  const char  *flag;
+	  gunichar ch;
+	  
+	  flag = &flagstr [j];
+	  endptr = &flagstr [i];
+	  
+	  if (!eos)
+	    {
+	      flagstr [i++] = '\0';
+	      j = i;
+	    }
+	  
+	  /* trim spaces */
+	  for (;;)
+	    {
+	      ch = g_utf8_get_char (flag);
+	      if (!g_unichar_isspace (ch))
+		break;
+	      flag = g_utf8_next_char (flag);
+	    }
+	  
+	  while (endptr > flag)
+	    {
+	      prevptr = g_utf8_prev_char (endptr);
+	      ch = g_utf8_get_char (prevptr);
+	      if (!g_unichar_isspace (ch))
+		break;
+	      endptr = prevptr;
+	    }
+	  
+	  if (endptr > flag)
+	    {
+	      *endptr = '\0';
+	      fv = g_flags_get_value_by_name (fclass, flag);
+	      
+	      if (!fv)
+		fv = g_flags_get_value_by_nick (fclass, flag);
+	      
+	      if (fv)
+		ret |= fv->value;
+	      else
+		g_warning ("Unknown flag: '%s'", flag);
+	    }
+	  
+	  if (eos)
+	    break;
+	}
+    }
+  
+  g_free (flagstr);
+  
+  g_type_class_unref (fclass);
+  
+  return ret;
+}
+
+static void
+gtk_builder_build_interface (GtkBuilder *builder,
+			     GtkBuilderInterface *iface,
+			     const char *root)
+{
+  gint i;
+  GtkBuilderObjectInfo *obj;
+  GObject *o;
+
+  /* TODO: require tag */
+#if 0
+  for (i = 0; i < iface->n_requires; i++)
+    {
+      g_print ("XXX: require %s", iface->requires[i]);
+    }
+#endif
+  
+  if (root)
+    {
+      obj = g_hash_table_lookup (iface->names, root);
+      g_return_if_fail (obj != NULL);
+      o = gtk_builder_build_object (builder, obj);
+    }
+  else
+    {
+      /* build all toplevel nodes */
+      for (i = 0; i < iface->n_toplevels; i++)
+	{
+	  o = gtk_builder_build_object (builder, iface->toplevels[i]);
+	}
+    }
+}
+
+/* below are functions from gtk-build.h */
+
+/**
+ * gtk_register_object:
+ * @type: the GType of the widget.
+ * @new_func: the function used to construct instances of the widget.
+ * @build_children: function used to construct children (or NULL).
+ * @find_internal_child: function to find internal children (or NULL).
+ *
+ * This function is used to register new construction functions for a
+ * widget type.  The child building routine would call
+ * gtk_builder_build_object() on each child node to create the child
+ * before packing it.
+ *
+ * This function is mainly useful for addon widget modules for libgtk
+ * (it would get called from the gtk_init_module() function).
+ */
+void
+gtk_builder_register_type (GType type,
+			   GtkBuildChildrenFunc build_children,
+			   GtkFindInternalChildFunc find_internal_child)
+{
+  GtkObjectBuildData *data;
+  gpointer old_data;
+
+  g_return_if_fail (g_type_is_a (type, G_TYPE_OBJECT));
+
+  if (gtk_builder_data_id == 0)
+    gtk_builder_data_id = g_quark_from_static_string (gtk_builder_data_key);
+
+  /* Containers defaults to gtk_standard_build_children */
+  if (!build_children && g_type_is_a (type, GTK_TYPE_CONTAINER))
+    build_children = gtk_builder_standard_build_children;
+
+  data = g_new (GtkObjectBuildData, 1);
+  data->build_children = build_children;
+  data->find_internal_child = find_internal_child;
+
+  old_data = g_type_get_qdata (type, gtk_builder_data_id);
+  if (old_data)
+    g_free (old_data);
+
+  g_type_set_qdata (type, gtk_builder_data_id, data);
+}
+
+/* helper function for getting the build data for a type */
+static const GtkObjectBuildData *
+get_build_data (GType type)
+{
+  static const GtkObjectBuildData widget_build_data =
+    {
+      NULL,
+      NULL
+    };
+  static const GtkObjectBuildData container_build_data =
+    {
+      gtk_builder_standard_build_children,
+      NULL
+    };
+  const GtkObjectBuildData *build_data;
+
+  if (gtk_builder_data_id == 0)
+    gtk_builder_data_id = g_quark_from_static_string (gtk_builder_data_key);
+
+  build_data = g_type_get_qdata (type, gtk_builder_data_id);
+  
+  if (build_data)
+    return build_data;  /* specific BuildData found */
+  
+  /* gtkmm derives it's own types, but they aren't registed with gtk_register_widget(),
+   * so look at the base type.
+   * If 3rd party custom widgets haven't used gtk_register_widget() then this might mean that
+   * libgtk mistakenly uses the base type's functions. */
+  build_data = g_type_get_qdata (g_type_parent (type), gtk_builder_data_id);
+
+  if (build_data)
+    return build_data;  /* specific BuildData found */
+  
+  /* No specifc BuildData found - use something generic: */
+  if (g_type_is_a (type, GTK_TYPE_CONTAINER))
+    return &container_build_data;
+  else
+    return &widget_build_data;
+}
+
+
+/* utility function for invalidating the cached property key */
+static void
+invalidate_custom_prop_cache (GType type)
+{
+  gpointer data;
+  GType *children;
+  guint i, n_children = 0;
+  
+  data = g_type_get_qdata (type, gtk_builder_custom_props_cache_id);
+  /* if there is no cached custom prop data, then no children will
+   * have cached data. */
+  if (!data)
+    return;
+
+  g_type_set_qdata (type, gtk_builder_custom_props_cache_id, NULL);
+  g_free (data);
+  children = g_type_children (type, &n_children);
+  for (i = 0; i < n_children; i++)
+    invalidate_custom_prop_cache (children[i]);
+  g_free (children);
+}
+
+/**
+ * gtk_builder_register_custom_prop:
+ * @type: the GType of the widget.
+ * @prop_name: the name for the custom widget.
+ * @apply_prop: the function to be called when the custom property is found.
+ *
+ * Some properties are not (yet) handled through the GObject property
+ * code, so can not be handled by the generic code.  This function
+ * provides a way to register handlers for these properties.  Such
+ * handlers will apply for the GType @type and all its descendants.
+ */
+/**
+ * GtkApplyCustomPropFunc:
+ * @builder: the GtkBuilder object.
+ * @widget: the widget to apply the property to.
+ * @propname: the name of the property.
+ * @value: the value of the property.
+ *
+ * This prototype is used for custom property handlers registered with
+ * gtk_builder_register_custom_prop().
+ */
+void
+gtk_builder_register_custom_prop (GType type,
+				  const gchar *prop_name,
+				  GtkApplyCustomPropFunc apply_prop)
+{
+  GArray *array;
+  CustomPropInfo prop_info;
+    
+  /* set up quarks ... */
+  if (gtk_builder_custom_props_id == 0)
+    {
+      gtk_builder_custom_props_id = g_quark_from_static_string (gtk_builder_custom_props_key);
+      gtk_builder_custom_props_cache_id = g_quark_from_static_string (gtk_builder_custom_props_cache_key);
+    }
+  
+  /* get the GArray of prop info */
+  array = g_type_get_qdata (type, gtk_builder_custom_props_id);
+  if (!array) {
+    array = g_array_new (TRUE, FALSE, sizeof (CustomPropInfo));
+    g_type_set_qdata (type, gtk_builder_custom_props_id, array);
+  }
+  
+  /* append the extra prop */
+  prop_info.name_quark = g_quark_from_string (prop_name);
+  prop_info.apply_prop = apply_prop;
+  g_array_append_val (array, prop_info);
+
+  /* invalidate cached info, if any */
+  invalidate_custom_prop_cache (type);
+}
+
+static CustomPropInfo *
+get_custom_prop_info (GType type)
+{
+  CustomPropInfo *prop_info, *parent_info = NULL;
+  GType parent;
+  GArray *array;
+  gint length;
+  
+  /* if gtk_register_custom_prop hasn't been called ... */
+  if (gtk_builder_custom_props_id == 0)
+    return NULL;
+  
+  /* has the prop info been calculated/cached already? */
+  prop_info = g_type_get_qdata (type, gtk_builder_custom_props_cache_id);
+  if (prop_info)
+    return prop_info;
+  
+  array = g_type_get_qdata (type, gtk_builder_custom_props_id);
+  parent = g_type_parent (type);
+  prop_info = NULL;
+  length = 0;
+  /* hit the top */
+  if (parent == G_TYPE_INVALID)
+    {
+      if (array)
+	{
+	  length = array->len;
+	  prop_info = g_memdup (array->data,
+				sizeof (CustomPropInfo) * (length + 1));
+	}
+    } else { /* merge parent info with our info */
+      gint count = 0;
+      
+      parent_info = get_custom_prop_info (parent);
+      if (parent_info)
+	while (parent_info[count].name_quark != 0)
+	  count++;
+      length = count;
+      if (array) length += array->len;
+      prop_info = g_new (CustomPropInfo, length + 1);
+      /* concatenate info */
+      if (count > 0)
+	memcpy (prop_info, parent_info,
+		sizeof (CustomPropInfo) * count);
+      if (array)
+	memcpy (&prop_info[count], array->data,
+		sizeof (CustomPropInfo) * array->len);
+    }
+  /* make sure it is null terminated */
+  if (prop_info != NULL) {
+    prop_info[length].name_quark = 0;
+    prop_info[length].apply_prop = NULL;
+  }
+  
+  g_type_set_qdata (type, gtk_builder_custom_props_cache_id, prop_info);
+  return prop_info;
+}
+
+
+/**
+ * gtk_builder_set_value_from_string
+ * @builder: the GtkBuilder object.
+ * @pspec: the GParamSpec for the property
+ * @string: the string representation of the value.
+ * @value: the GValue to store the result in.
+ *
+ * This function demarshals a value from a string.  This function
+ * calls g_value_init() on the @value argument, so it need not be
+ * initialised beforehand.
+ *
+ * This function can handle char, uchar, boolean, int, uint, long,
+ * ulong, enum, flags, float, double, string, GdkColor and
+ * GtkAdjustment type values.  Support for GtkWidget type values is
+ * still to come.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean
+gtk_builder_set_value_from_string (GtkBuilder *builder,
+				   GParamSpec *pspec,
+				   const gchar *string,
+				   GValue *value)
+{
+  GType prop_type;
+  gboolean ret = TRUE, showerr = TRUE;
+  
+  prop_type = G_PARAM_SPEC_VALUE_TYPE(pspec);
+  GTK_NOTE (BUILDER,
+	    g_message ("%s = %s (%s)\n", pspec->name, string,
+		       g_type_name (G_TYPE_FUNDAMENTAL(prop_type))));
+  
+  g_value_init (value, prop_type);
+  switch (G_TYPE_FUNDAMENTAL (prop_type)) {
+  case G_TYPE_CHAR:
+    g_value_set_char(value, string[0]);
+    break;
+  case G_TYPE_UCHAR:
+    g_value_set_uchar (value, (guchar)string[0]);
+    break;
+  case G_TYPE_BOOLEAN:
+    g_value_set_boolean (value, 
+			 g_ascii_tolower (string[0]) == 't'
+			 || g_ascii_tolower (string[0]) == 'y'
+			 || strtol (string, NULL, 0));
+    break;
+  case G_TYPE_INT:
+    g_value_set_int (value, strtol (string, NULL, 0));
+    break;
+  case G_TYPE_UINT:
+    if (G_IS_PARAM_SPEC_UNICHAR (pspec))
+      g_value_set_uint(value, (guint)string[0]);
+    else
+      g_value_set_uint(value, strtoul(string, NULL, 0));
+    break;
+  case G_TYPE_LONG:
+    g_value_set_long (value, strtol (string, NULL, 0));
+    break;
+  case G_TYPE_ULONG:
+    g_value_set_ulong (value, strtoul (string, NULL, 0));
+    break; 
+  case G_TYPE_ENUM:
+    g_value_set_enum (value, gtk_builder_enum_from_string (prop_type, string));
+    break;
+  case G_TYPE_FLAGS:
+    g_value_set_flags (value, gtk_builder_flags_from_string (prop_type, string));
+    break;
+  case G_TYPE_FLOAT:
+    g_value_set_float (value, g_strtod (string, NULL));
+    break;
+  case G_TYPE_DOUBLE:
+    g_value_set_double (value, g_strtod (string, NULL));
+    break;
+  case G_TYPE_STRING:
+    g_value_set_string (value, string);
+    break;
+  case G_TYPE_BOXED:
+    if (G_VALUE_HOLDS (value, GDK_TYPE_COLOR))
+      {
+	GdkColor colour = { 0, };
+	
+	if (gdk_color_parse (string, &colour) &&
+	    gdk_colormap_alloc_color (gtk_widget_get_default_colormap (),
+				      &colour, FALSE, TRUE))
+	  {
+	    g_value_set_boxed (value, &colour);
+	  }
+	else
+	  {
+	    g_warning ("could not parse colour name `%s'", string);
+	    ret = FALSE;
+	  }
+      }
+    else if (G_VALUE_HOLDS (value, G_TYPE_STRV))
+      {
+	char **vector = g_strsplit (string, "\n", 0);
+	g_value_take_boxed (value, vector);
+      } else
+	ret = FALSE;
+    break;
+  case G_TYPE_INTERFACE:
+    {
+      GObject *object = g_hash_table_lookup (builder->priv->name_hash,
+					     string);
+      if (object)
+	{
+	  g_value_set_object (value, object);
+	} else
+	  {
+	    ret = FALSE;
+	    showerr = FALSE;
+	  }
+      break;
+    }
+  case G_TYPE_OBJECT:
+    if (G_VALUE_HOLDS (value, GTK_TYPE_ADJUSTMENT))
+      {
+	GtkAdjustment *adj =
+	  GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, 100, 1, 10, 10));
+	gchar *ptr = (gchar *)string;
+	
+	adj->value = g_strtod (ptr, &ptr);
+	adj->lower = g_strtod (ptr, &ptr);
+	adj->upper = g_strtod (ptr, &ptr);
+	adj->step_increment = g_strtod (ptr, &ptr);
+	adj->page_increment = g_strtod (ptr, &ptr);
+	adj->page_size = g_strtod (ptr, &ptr);
+	
+	g_value_set_object (value, adj);
+	gtk_object_sink (GTK_OBJECT (adj));
+      }
+    else if (G_VALUE_HOLDS (value, GDK_TYPE_PIXBUF))
+      {
+	gchar *filename;
+	GError *error = NULL;
+	GdkPixbuf *pixbuf;
+	
+	filename = gtk_builder_relative_file (builder, string);
+	pixbuf = gdk_pixbuf_new_from_file (filename, &error);
+	if (pixbuf)
+	  {
+	    g_value_set_object (value, pixbuf);
+	    g_object_unref (G_OBJECT (pixbuf));
+	  }
+	else
+	  {
+	    g_warning ("Error loading image: %s", error->message);
+	    g_error_free (error);
+	    ret = FALSE;
+	  }
+	g_free (filename);
+      }
+    else if (g_type_is_a (G_TYPE_OBJECT, prop_type) ||
+	     g_type_is_a (prop_type, G_TYPE_OBJECT))
+      {
+	GObject *object = g_hash_table_lookup (builder->priv->name_hash,
+					       string);
+	if (object)
+	  {
+	    g_value_set_object (value, object);
+	  }
+	else
+	  {
+	    ret = FALSE;
+	    showerr = FALSE;
+	  }
+      } else
+	ret = FALSE;
+    break;
+  default:
+    ret = FALSE;
+    break;
+  }
+  
+  if (!ret)
+    {
+      if (showerr)
+	g_warning ("could not convert string to type "
+		   "`%s' for property `%s'",
+		   g_type_name (prop_type), pspec->name);
+      g_value_unset (value);
+    }
+  
+  return ret;
+}
+
+/**
+ * gtk_builder_standard_build_object
+ * @builder: the GtkBuilder object.
+ * @type: the GType of the object.
+ * @info: the GtkBuilderObjectInfo structure.
+ *
+ * This is the standard widget building function.  It processes all
+ * the widget properties using the standard object properties
+ * interfaces.  This function will be sufficient for most widget
+ * types, thus reducing the ammount of work needed to wrap a library.
+ *
+ * Returns: the constructed widget.
+ */
+GObject *
+gtk_builder_standard_build_object (GtkBuilder *builder,
+				   GType                 type,
+				   GtkBuilderObjectInfo *info)
+{
+  static GArray *props_array = NULL;
+  static GArray *custom_props_array = NULL;
+  GObjectClass *oclass;
+  CustomPropInfo *custom_props;
+  GObject *object;
+  GList *deferred_props = NULL, *tmp;
+  guint i;
+  
+  if (!props_array)
+    {
+      props_array = g_array_new (FALSE, FALSE, sizeof (GParameter));
+      custom_props_array = g_array_new (FALSE, FALSE, sizeof (CustomPropData));
+    }
+
+  /* we ref the class here as a slight optimisation */
+  oclass = g_type_class_ref (type);
+
+  custom_props = get_custom_prop_info (type);
+
+  /* collect properties */
+  for (i = 0; i < info->n_properties; i++)
+    {
+      GQuark name_quark;
+      GParameter param = { NULL };
+      GParamSpec *pspec;
+      
+      if (custom_props &&
+	  (name_quark = g_quark_try_string (info->properties[i].name)) != 0)
+	{
+	  gint j = 0;
+	  
+	  while (custom_props[j].name_quark)
+	    {
+	      if (custom_props[j].name_quark == name_quark)
+		{
+		  CustomPropData prop_data;
+		  
+		  prop_data.apply_prop = custom_props[j].apply_prop;
+		  prop_data.prop = &info->properties[i];
+		  g_array_append_val (custom_props_array, prop_data);
+		  break;
+		}
+	      j++;
+	    }
+	  if (custom_props[j].name_quark != 0) /* a prop was matched */
+	    continue;
+	}
+      pspec = g_object_class_find_property (oclass, info->properties[i].name);
+      if (!pspec)
+	{
+	  g_warning ("unknown property `%s' for class `%s'",
+		     info->properties[i].name, g_type_name (type));
+	  continue;
+	}
+      
+      if (gtk_builder_set_value_from_string (builder, pspec,
+					     info->properties[i].value,
+					     &param.value))
+	{
+	  param.name = info->properties[i].name;
+	  g_array_append_val(props_array, param);
+	}
+      else if (g_type_is_a (G_TYPE_OBJECT, G_PARAM_SPEC_VALUE_TYPE (pspec)) ||
+	       g_type_is_a (G_TYPE_INTERFACE, G_PARAM_SPEC_VALUE_TYPE (pspec)) ||
+	       g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (pspec), G_TYPE_OBJECT))
+	{
+	  /* if the pspec could hold a widget, then try to handle it
+	   * later */
+	  deferred_props = g_list_prepend (deferred_props,
+					   &info->properties[i]);
+	}
+    }
+  object = g_object_newv (type, props_array->len,
+			  (GParameter *)props_array->data);
+  
+  /* clean up props_array */
+  for (i = 0; i < props_array->len; i++)
+    {
+      g_array_index (props_array, GParameter, i).name = NULL;
+      g_value_unset (&g_array_index (props_array, GParameter, i).value);
+    }
+  
+  /* do custom props */
+  for (i = 0; i < custom_props_array->len; i++)
+    {
+      CustomPropData *data;
+      
+      data = &g_array_index (custom_props_array, CustomPropData, i);
+      if (data->apply_prop)
+	(* data->apply_prop) (builder, object,
+			      data->prop->name,
+			      data->prop->value);
+    }
+  
+  /* handle deferred properties */
+  for (tmp = deferred_props; tmp; tmp = tmp->next)
+    {
+      GtkBuilderProperty *prop = tmp->data;
+      
+      gtk_builder_handle_object_prop (builder, object,
+				      prop->name, prop->value);
+    }
+  
+  g_list_free (deferred_props);
+
+  g_array_set_size (props_array, 0);
+  g_array_set_size (custom_props_array, 0);
+  g_type_class_unref (oclass);
+  
+  return object;
+}
+
+/**
+ * gtk_builder_set_packing_property:
+ * @builder: the GtkBuilder object.
+ * @parent: the container widget.
+ * @child: the contained child
+ * @name: the name of the property
+ * @value: it's stringified value
+ * 
+ * This sets the packing property on container @parent of widget
+ * @child with @name to @value
+ **/
+void
+gtk_builder_set_packing_property (GtkBuilder   *builder,
+				  GtkWidget  *parent,
+				  GtkWidget  *child,
+				  const char *name,
+				  const char *value)
+{
+  GValue gvalue = { 0 };
+  GParamSpec *pspec;
+  
+  pspec = gtk_container_class_find_child_property (
+						   G_OBJECT_GET_CLASS (parent), name);
+  if (!pspec)
+    g_warning ("unknown child property `%s' for container `%s'",
+	       name, G_OBJECT_TYPE_NAME (parent));
+  else if (gtk_builder_set_value_from_string (builder, pspec, value, &gvalue))
+    {
+      gtk_container_child_set_property (GTK_CONTAINER (parent), child,
+					name, &gvalue);
+      g_value_unset (&gvalue);
+    }
+}
+
+/**
+ * gtk_standard_build_children
+ * @builder: the GtkBuilder object.
+ * @parent: the container widget.
+ * @info: the GtkBuilderObjectInfo structure.
+ *
+ * This is the standard child building function.  It simply calls
+ * gtk_container_add on each child to add them to the parent, and
+ * process any packing properties using the generic container packing
+ * properties interfaces.
+ *
+ * This function will be sufficient for most container widgets
+ * provided that they implement the GtkContainer child packing
+ * properties interfaces.
+ */
+void
+gtk_builder_standard_build_children (GtkBuilder *builder,
+				     GObject *parent,
+				     GtkBuilderObjectInfo *info)
+{
+  gint i, j;
+
+  g_object_ref (G_OBJECT (parent));
+  for (i = 0; i < info->n_children; i++)
+    {
+      GtkBuilderObjectInfo *childinfo = info->children[i].child;
+      GObject *child;
+
+      /* handle any internal children */
+      if (info->children[i].internal_child)
+	{
+	  gtk_builder_handle_internal_child (builder, parent, &info->children[i]);
+	  continue;
+	}
+
+      child = gtk_builder_build_object (builder, childinfo);
+      
+      g_object_ref (G_OBJECT (child));
+      if (GTK_IS_WIDGET (parent) && GTK_IS_WIDGET (child))
+	{
+	  gtk_widget_freeze_child_notify (GTK_WIDGET (child));
+	  gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (child));
+
+	  for (j = 0; j < info->children[i].n_properties; j++)
+	    gtk_builder_set_packing_property (builder,
+					      GTK_WIDGET(parent),
+					      GTK_WIDGET(child),
+					      info->children[i].properties[j].name,
+					      info->children[i].properties[j].value);
+	
+	  gtk_widget_thaw_child_notify (GTK_WIDGET (child));
+	}
+      
+      g_object_unref (G_OBJECT (child));
+    }
+
+  g_object_unref (G_OBJECT (parent));
+}
+
+
+void
+gtk_builder_merge_ui (GtkBuilder *builder,
+		      GtkUIManager *ui_manager,
+		      GtkBuilderUIInfo *ui,
+		      gboolean merge)
+{
+  
+  gchar *filename;
+  merge_data *data, *current_data;
+  int merge_id;
+  GError *error = NULL;
+
+  if (merge)
+    {
+      if (ui->filename)
+	{
+	  filename = gtk_builder_relative_file (builder, ui->filename);
+	  
+	  merge_id = gtk_ui_manager_add_ui_from_file (ui_manager, filename,
+						      &error);
+	  g_free(filename);
+	}
+      else if (ui->data)
+	merge_id = gtk_ui_manager_add_ui_from_string (ui_manager, ui->data,
+						      ui->data_len, &error);
+      else
+	{
+	  g_warning("No ui specified");
+	  return;
+	}
+      
+      
+    } else {
+      merge_id = -1;
+    }
+  
+  if (error)
+    {
+      g_warning ("Error: %s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  current_data = g_hash_table_lookup (builder->priv->ui_hash, ui->name);
+  if (current_data != NULL) {
+    current_data->merge_id = merge_id;
+  } else {
+    data = g_new0 (merge_data, 1);
+    data->ui_manager = ui_manager;
+    data->merge_id = merge_id;
+    data->ui = ui;
+    g_hash_table_insert (builder->priv->ui_hash,
+			 (gpointer)ui->name,
+			 data);
+  }
+}
+
+gint
+gtk_builder_merge_ui_from_name (GtkBuilder  *builder,
+				const gchar *uiname)
+{
+  merge_data *data;
+  gint merge_id = -1;
+  
+  g_return_val_if_fail (GTK_IS_BUILDER (builder), -1);
+  g_return_val_if_fail (uiname != NULL, -1);
+
+  data = g_hash_table_lookup (builder->priv->ui_hash, uiname);
+  if (data && data->merge_id == -1)
+    {
+      gtk_builder_merge_ui (builder, data->ui_manager, data->ui, TRUE);
+      /* gtk_builder_merge_ui updates data->merge_id */
+      merge_id = data->merge_id;
+    }
+  
+  return merge_id;
+}
+
+gboolean
+gtk_builder_remove_ui_from_name (GtkBuilder  *builder,
+				 const gchar *uiname)
+{
+  merge_data *data;
+  
+  g_return_val_if_fail (GTK_IS_BUILDER (builder), FALSE);
+  g_return_val_if_fail (uiname != NULL, FALSE);
+
+  data = g_hash_table_lookup (builder->priv->ui_hash, uiname);
+  if (!data)
+    return FALSE;
+  
+  gtk_ui_manager_remove_ui (data->ui_manager, data->merge_id);
+
+  data->merge_id = -1;
+  return TRUE;
+}
+
+/**
+ * gtk_builder_ui_manager_construct
+ * @builder: the GtkBuilder object.
+ * @constructor: name of the constructor, at the moment
+ *               the name of a GtkUIManager
+ * @name: name of the widget to fetch from the constructor
+ * 
+ * Returns: the newly created object.
+ */
+GObject*
+gtk_builder_ui_manager_construct (GtkBuilder *builder,
+				  const char *constructor_name,
+				  const char *name)
+{
+  gchar *tmp;
+  GObject *object;
+  GtkBuilderObjectInfo *info;
+  GObject *constructor;
+  
+  constructor = gtk_builder_get_object (builder, constructor_name);
+  if (!constructor)
+    {
+      g_warning ("No constructor called '%s'", constructor_name);
+      return NULL;
+    }
+
+  if (GTK_IS_UI_MANAGER (constructor))
+    {
+      GtkWidget *widget;
+      
+      tmp = g_strdup_printf ("ui/%s", name);
+      widget = gtk_ui_manager_get_widget (GTK_UI_MANAGER (constructor), tmp);
+      g_free (tmp);
+      
+      g_return_val_if_fail (widget != NULL, NULL); 
+      object = G_OBJECT (widget);
+      
+      builder->priv->accel_group = gtk_ui_manager_get_accel_group (GTK_UI_MANAGER (constructor));
+    }
+  else
+    {
+      g_warning ("Unknown constructor of type %s", g_type_name (G_OBJECT_TYPE (constructor)));
+      return NULL;
+    }
+
+  info = g_hash_table_lookup (builder->priv->tree->names, name);
+  g_return_val_if_fail (info != NULL, NULL);
+  
+  gtk_builder_child_set_properties (builder, object, info);
+  
+  return object;
+}
+
+/**
+ * GtkNewFunc
+ * @builder: The GtkBuilder object.
+ * @type: the GType code of the widget.
+ * @info: the GtkBuilderObjectInfo structure for this widget.
+ *
+ * This function signature should be used by functions that build particular
+ * widget types.  The function should create the new widget and set any non
+ * standard widget parameters (ie. don't set visibility, size, etc), as
+ * this is handled by gtk_builder_build_object, which calls these functions.
+ *
+ * Returns: the new widget.
+ */
+/**
+ * GtkBuildChildrenFunc
+ * @builder: the GtkBuilder object.
+ * @parent: the parent.
+ * @info: the GtkBuilderObjectInfo structure for the parent.
+ *
+ * This function signature should be used by functions that are responsible
+ * for adding children to a container widget.  To create each child widget,
+ * gtk_builder_build_object should be called.
+ */
+/**
+ * GtkFindInternalChildFunc
+ * @builder: the GtkBuilder object.
+ * @parent: the parent widget.
+ * @childname: the name of the internal child
+ *
+ * When some composite widgets are created, a number of children are
+ * added at the same time (for example, the vbox in a GtkDialog).
+ * These widgets are identified in the Builder interface file by the
+ * internal-child attribute on their &lt;child&gt; element.
+ *
+ * When libgtk encounters an internal child, rather than creating a
+ * new widget instance, libgtk walks up the tree until it finds the
+ * first non internal-child parent.  It then calls the
+ * find_internal_child callback for that parent's class.
+ *
+ * That callback should return the internal child corresponding to the
+ * name passed in as the third argument.
+ *
+ * Returns: the named internal child.
+ */
+/**
+ * gtk_builder_build_object:
+ * @builder: the GtkBuilder object.
+ * @info: the GtkBuilderObjectInfo structure for the widget.
+ *
+ * This function is not intended for people who just use libgtk.  Instead
+ * it is for people extending it (it is designed to be called in the child
+ * build routine defined for the parent widget).  It first checks the type
+ * of the widget from the class tag, then calls the corresponding widget
+ * creation routine.  This routine sets up all the settings specific to that
+ * type of widget.  Then general widget settings are performed on the widget.
+ * Then it sets up accelerators for the widget, and extracts any signal
+ * information for the widget.  Then it checks to see if there are any
+ * child widget nodes for this widget, and if so calls the widget's
+ * build routine, which will create the children with this function and add
+ * them to the widget in the appropriate way.  Finally it returns the widget.
+ * 
+ * Returns: the newly created widget.
+ */
+GObject *
+gtk_builder_build_object (GtkBuilder *builder, GtkBuilderObjectInfo *info)
+{
+  GType type = G_TYPE_INVALID;
+  GObject *ret;
+
+  GTK_NOTE (BUILDER, g_message ("Widget class: %s\tname: %s",
+				info->classname, info->name));
+  
+  if (!strcmp (info->classname, "Custom"))
+    {
+      ret = custom_new (builder, info);
+    }
+  else if (info->constructor != NULL)
+    {
+      ret = gtk_builder_ui_manager_construct (builder, info->constructor, info->name);
+      if (!ret)
+	{
+	  g_warning ("unknown widget name '%s'", info->name);
+	  return NULL;
+	} 
+    }
+  else
+    {
+      /* Call GtkBuilder's lookup_type() virtual function to get the gtype: */
+      type = (* GTK_BUILDER_GET_CLASS(builder)->lookup_type) (builder, info->classname);
+      
+      if (type == G_TYPE_INVALID)
+	{
+	  char buf[50];
+	  g_warning ("unknown widget class '%s'", info->classname);
+	  g_snprintf (buf, 49, "[a %s]", info->classname);
+	  ret = G_OBJECT (gtk_label_new(buf));
+	}
+      else
+	{
+	  ret = gtk_builder_standard_build_object (builder, type, info);
+	}
+    }
+  
+  if (GTK_IS_WINDOW (ret))
+    gtk_builder_set_toplevel (builder, GTK_WINDOW (ret));
+
+  gtk_builder_set_common_params (builder, ret, info);
+  
+  if (GTK_IS_WINDOW (ret))
+    {
+      gtk_builder_ensure_accel (builder);
+      gtk_builder_set_toplevel (builder, NULL);
+    }
+  
+  return ret;
+}
+
+/**
+ * gtk_builder_handle_internal_child
+ * @builder: the GtkBuilder object.
+ * @parent: the parent widget.
+ * @child_info: the GtkObjectChildInfo structure for the child.
+ *
+ * This function is intended to be called by the build_children
+ * callback for container widgets.  If the build_children callback
+ * encounters a child with the internal-child attribute set, then it
+ * should call this function to handle it and then continue on to the
+ * next child.
+ */
+void
+gtk_builder_handle_internal_child (GtkBuilder *builder,
+				   GObject *parent,
+				   GtkBuilderChildInfo *child_info)
+{
+  const GtkObjectBuildData *parent_build_data = NULL;
+  GObject *child;
+
+  /* walk up the widget heirachy until we find a parent with a
+   * find_internal_child handler */
+  if (GTK_IS_WIDGET (parent))
+    {
+      while (parent_build_data == NULL && parent != NULL) {
+	parent_build_data = get_build_data (G_OBJECT_TYPE (parent));
+	
+	if (parent_build_data->find_internal_child != NULL)
+	  break;
+	
+	parent_build_data = NULL; /* set to NULL if no find_internal_child */
+	parent = G_OBJECT (GTK_WIDGET (parent)->parent);
+      }
+    }
+
+  if (!parent_build_data || !parent_build_data->find_internal_child) {
+    g_warning ("could not find a parent that handles internal"
+	       " children for `%s'", child_info->internal_child);
+    return;
+  }
+
+  child = parent_build_data->find_internal_child (builder, parent,
+						  child_info->internal_child);
+
+  if (!child) {
+    g_warning("could not find internal child `%s' in parent of type `%s'",
+	      child_info->internal_child, G_OBJECT_TYPE_NAME (parent));
+    return;
+  }
+  
+  gtk_builder_child_set_properties (builder, child, child_info->child);
+}
+
+
+static void
+gtk_builder_child_set_properties (GtkBuilder *builder, GObject *child, GtkBuilderObjectInfo *info)
+{
+  GObjectClass *oclass;
+  CustomPropInfo *custom_props;
+  guint i;
+    
+  oclass = G_OBJECT_GET_CLASS (child);
+  custom_props = get_custom_prop_info (G_OBJECT_TYPE (child));
+  for (i = 0; i < info->n_properties; i++)
+    {
+      GQuark name_quark;
+      GValue value = { 0, };
+      GParamSpec *pspec;
+      
+      if (custom_props &&
+	  (name_quark = g_quark_try_string (info->properties[i].name)) != 0)
+	{
+	  gint j = 0;
+
+	  while (custom_props[j].name_quark)
+	    {
+	      if (custom_props[j].name_quark == name_quark)
+		{
+		  (* custom_props[j].apply_prop) (builder, child,
+						  info->properties[i].name,
+						  info->properties[i].value);
+		  break;
+		}
+	      j++;
+	    }
+	  if (custom_props[j].name_quark != 0) /* a prop was matched */
+	    continue;
+	}
+      pspec = g_object_class_find_property (oclass, info->properties[i].name);
+      if (!pspec)
+	{
+	  g_warning("unknown property `%s' for class `%s'",
+		    info->properties[i].name, G_OBJECT_TYPE_NAME (child));
+	  continue;
+	}
+      
+      /* this should catch all properties wanting a GtkWidget
+       * subclass.  We also look for types that could hold a *
+       * GtkWidget in order to catch things like the *
+       * GtkAccelLabel::accel_object property.  Since we don't do
+       * any * handling of GObject or GtkObject directly in *
+       * gtk_builder_set_value_from_string, this shouldn't be a *
+       * problem. */
+      if (g_type_is_a (G_TYPE_OBJECT, G_PARAM_SPEC_VALUE_TYPE (pspec)) ||
+	  g_type_is_a(G_PARAM_SPEC_VALUE_TYPE (pspec), G_TYPE_OBJECT)) {
+	gtk_builder_handle_object_prop (builder, child,
+					pspec->name,
+					info->properties[i].value);
+	continue;
+      }
+      
+      if (gtk_builder_set_value_from_string (builder, pspec,
+					     info->properties[i].value,
+					     &value))
+	{
+	  g_object_set_property (G_OBJECT(child), pspec->name, &value);
+	  g_value_unset (&value);
+	}
+    }
+  
+  gtk_builder_set_common_params (builder, child, info);
+}
+
+
+static void
+gtk_builder_object_destroy (GtkObject *object, GtkBuilder *builder)
+{
+  gchar *name;
+  
+  g_return_if_fail (GTK_IS_OBJECT (object));
+  g_return_if_fail (GTK_IS_BUILDER (builder));
+  
+  name = g_object_get_qdata (G_OBJECT (object), gtk_builder_name_id);
+  if (!name)
+    return;
+  
+  g_hash_table_remove (builder->priv->name_hash, name);
+  g_object_set_qdata (G_OBJECT (object), gtk_builder_tree_id, NULL);
+  g_object_set_qdata (G_OBJECT (object), gtk_builder_name_id, NULL);
+}
+
+/**
+ * gtk_builder_set_common_params
+ * @builder: the GtkBuilder widget.
+ * @widget: the widget to set parameters on.
+ * @info: the GtkBuilderObjectInfo structure for the widget.
+ *
+ * This function sets the common parameters on a widget, and is responsible
+ * for inserting it into the GtkBuilder object's internal structures.  It will
+ * also add the children to this widget.  Usually this function is only called
+ * by gtk_builder_build_object, but is exposed for difficult cases, such as
+ * setting up toolbar buttons and the like.
+ */
+void
+gtk_builder_set_common_params (GtkBuilder *builder, GObject *object,
+			       GtkBuilderObjectInfo *info)
+{
+  GList *tmp;
+  const GtkObjectBuildData *data;
+  static GQuark visible_id = 0;
+  
+  /* get the build data */
+  data = get_build_data (G_OBJECT_TYPE (object));
+  gtk_builder_add_signals (builder, object, info);
+  if (GTK_IS_WIDGET (object))
+    {
+      gtk_widget_set_name (GTK_WIDGET (object), info->name);
+      gtk_builder_add_accels (builder, GTK_WIDGET (object), info);
+      gtk_builder_add_accessibility_info (builder, GTK_WIDGET (object), info);
+    } 
+  
+  /* store this information as data of the object. */
+  g_object_set_qdata (object, gtk_builder_tree_id, builder);
+  g_object_set_qdata (object, gtk_builder_name_id, info->name);
+  /* store objects in hash table, for easy lookup */
+  g_hash_table_insert (builder->priv->name_hash, info->name, object);
+
+  /* set up function to remove object from GtkBuilder object's
+   * name_hash on destruction. Use connect_object so the handler is
+   * automatically removed on finalization of the GtkBuilder
+   * object. */
+  if (GTK_IS_OBJECT (object))
+    g_signal_connect_object (object, "destroy",
+			     G_CALLBACK (gtk_builder_object_destroy),
+			     G_OBJECT (builder), 0);
+
+  /* handle any deferred properties using this object */
+  tmp = builder->priv->deferred_props;
+  while (tmp)
+    {
+      GtkBuilderDeferredProperty *dprop = tmp->data;
+      
+      if (!strcmp (info->name, dprop->target_name))
+	{
+	  tmp = tmp->next;
+	  builder->priv->deferred_props =
+	    g_list_remove (builder->priv->deferred_props, dprop);
+	  
+	  switch (dprop->type)
+	    {
+	    case DEFERRED_PROP:
+	      g_object_set (G_OBJECT (dprop->d.prop.object),
+			    dprop->d.prop.prop_name, G_OBJECT (object),
+			    NULL);
+	      break;
+	    case DEFERRED_REL:
+	      {
+		AtkObject *target = gtk_widget_get_accessible (GTK_WIDGET (object));
+		
+		atk_relation_set_add_relation_by_type
+		  (dprop->d.rel.relation_set,
+		   dprop->d.rel.relation_type, target);
+		g_object_unref (dprop->d.rel.relation_set);
+	      }
+	      break;
+	    default:
+	      g_warning ("unknown deferred property type");
+	    }
+	  g_free (dprop);
+	}
+      else
+	{
+	  tmp = tmp->next;
+	}
+    }
+  
+  if (data && data->build_children && info->children)
+    {
+      data->build_children (builder, object, info);
+    }
+
+  if (visible_id == 0)
+    visible_id = g_quark_from_static_string ("GtkBuilder::visible");
+  
+  if (g_object_get_qdata (object, visible_id))
+    gtk_widget_show (GTK_WIDGET (object));
+}
+
+/**
+ * gtk_builder_get_merge_id
+ * @builder: the GtkBuilder object
+ * @uiname: name of widget 
+ *
+ * Returns: 
+ */
+gint
+gtk_builder_get_merge_id (GtkBuilder *builder, const gchar *uiname)
+{
+  gpointer data;
+    
+  data = g_hash_table_lookup (builder->priv->ui_hash, uiname);
+  if (!data)
+    return -1;
+  
+  return GPOINTER_TO_INT (data);
+}
+
Index: gtk/gtkbuilder.h
===================================================================
RCS file: gtk/gtkbuilder.h
diff -N gtk/gtkbuilder.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gtk/gtkbuilder.h	6 Apr 2005 19:26:50 -0000
@@ -0,0 +1,207 @@
+/* -*- Mode: C; c-file-style: "gnu" -*- */
+/* gtkbuilder - class for building interfaces from XML files at runtime
+ * Copyright (C) 1998-2005  James Henstridge <james@daa.com.au>
+ * Copyright (C) 2005       Johan Dahlin <johan@gnome.org>
+ *
+ * gtkbuilder.h: public interface of libgtk library
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA  02111-1307, USA.
+ */
+
+#ifndef __GTK_BUILDER_H__
+#define __GTK_BUILDER_H__
+
+#include <glib.h>
+#include <gtk/gtkwidget.h>
+#include <gtk/gtktooltips.h>
+#include <gtk/gtkbuilderparser.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_BUILDER            (gtk_builder_get_type())
+#define GTK_BUILDER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_BUILDER, GtkBuilder))
+#define GTK_BUILDER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_BUILDER, GtkBuilderClass))
+#define GTK_IS_BUILDER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_BUILDER))
+#define GTK_IS_BUILDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), GTK_TYPE_BUILDER))
+#define GTK_BUILDER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_BUILDER, GtkBuilderClass))
+
+typedef struct _GtkBuilder GtkBuilder;
+typedef struct _GtkBuilderClass GtkBuilderClass;
+typedef struct _GtkBuilderPrivate GtkBuilderPrivate;
+
+struct _GtkBuilder
+{
+  /* <public> */
+  GObject parent;
+  
+  char *filename;
+
+  /* <private> */
+  GtkBuilderPrivate *priv;
+};
+
+struct _GtkBuilderClass
+{
+  GObjectClass parent_class;
+  
+  /* Virtual function: gets the appropriate gtype for the typename.*/
+  GType (* lookup_type) (GtkBuilder *builder,
+			 const char *gtypename);
+};
+
+/* if the gtk_signal_connect_object behaviour is required, connect_object
+ * will point to the object, otherwise it will be NULL.
+ */
+typedef void    (*GtkBuilderConnectFunc)          (const gchar *handler_name,
+						   GObject     *object,
+						   const gchar *signal_name,
+						   const gchar *signal_data,
+						   GObject     *connect_object,
+						   gboolean     after,
+						   gpointer     user_data);
+
+/* interface for changing the custom widget handling */
+typedef GObject *(* GtkBuilderCustomObjectHandler) (GtkBuilder *builder,
+						    gchar      *func_name,
+						    gchar      *name,
+						    gchar      *string1,
+						    gchar      *string2,
+						    gint        int1,
+						    gint        int2,
+						    gpointer    user_data);
+
+G_GNUC_CONST
+GType         gtk_builder_get_type                (void);
+GtkBuilder   *gtk_builder_new                     (const char    *fname,
+						   const char    *root,
+						   const char    *domain);
+GtkBuilder   *gtk_builder_new_from_buffer         (const char    *buffer,
+						   int            size,
+						   const char    *root,
+						   const char    *domain);
+void          gtk_builder_signal_connect          (GtkBuilder    *builder,
+						   const char    *handlername,
+						   GCallback      func);
+void          gtk_builder_signal_connect_data     (GtkBuilder    *builder,
+						   const char    *handlername,
+						   GCallback      func,
+						   gpointer       user_data);
+void          gtk_builder_signal_autoconnect      (GtkBuilder    *builder);
+void          gtk_builder_signal_connect_full     (GtkBuilder   *builder,
+						   const gchar   *handler_name,
+						   GtkBuilderConnectFunc func,
+						   gpointer       user_data);
+void          gtk_builder_signal_autoconnect_full (GtkBuilder    *builder,
+						   GtkBuilderConnectFunc func,
+						   gpointer       user_data);
+GObject      *gtk_builder_get_object              (GtkBuilder    *builder,
+						   const char    *name);
+GList        *gtk_builder_get_objects             (GtkBuilder    *builder);
+gchar        *gtk_builder_relative_file           (GtkBuilder    *builder,
+						   const gchar   *filename);
+GtkUIManager *gtk_builder_get_ui_manager          (GtkBuilder    *builder);
+gint          gtk_builder_merge_ui_from_name      (GtkBuilder    *builder,
+						   const gchar   *definition);
+gboolean      gtk_builder_remove_ui_from_name     (GtkBuilder    *builder,
+						   const gchar   *definition);
+void          gtk_builder_add_ui_merge_id         (GtkBuilder    *builder,
+						   const gchar   *name,
+						   int            merge_id);
+
+/* don't free the results of these two ... */
+const char   *gtk_builder_get_object_name      (GObject *object);
+GtkBuilder   *gtk_builder_get_object_tree      (GObject *object);
+
+void          gtk_builder_set_custom_handler   (GtkBuilderCustomObjectHandler handler,
+						gpointer                      user_data);
+
+
+/* Functions for extending GtkBuilder, to add support for widgets in
+ * external libraries (or language bindings)
+ */
+
+/* call gtk_builder_build_widget on each child node, and pack in builder */
+typedef void       (* GtkBuildChildrenFunc)     (GtkBuilder           *builder,
+						 GObject              *parent,
+						 GtkBuilderObjectInfo *info);
+typedef GObject   *(* GtkFindInternalChildFunc) (GtkBuilder           *builder,
+						 GObject              *parent,
+						 const gchar          *childname);
+
+typedef void       (* GtkApplyCustomPropFunc)   (GtkBuilder           *builder,
+						 GObject              *object,
+						 const gchar          *propname,
+						 const gchar          *value);
+
+
+/* register handlers for a object */
+void        gtk_builder_register_type          (GType                    type,
+						GtkBuildChildrenFunc     build_children,
+						GtkFindInternalChildFunc find_internal_child);
+
+/* register a custom handler for a property (that may not have an
+ * associated gobject property.  Works in conjunction with
+ * gtk_standard_build_object. */
+void       gtk_builder_register_custom_prop    (GType                   type,
+						const gchar            *prop_name,
+						GtkApplyCustomPropFunc  apply_prop);
+
+/* this function is used to get a pointer to the internal child of a
+ * container object.  It would generally be called by the
+ * build_children callback for any children with the internal_child
+ * name set. */
+/* this function is called to build the interface by GtkBuilder */
+GObject *  gtk_builder_build_object            (GtkBuilder           *builder,
+						GtkBuilderObjectInfo *info);
+
+GObject *  gtk_builder_standard_build_object   (GtkBuilder           *builder,
+						GType                 object_type,
+						GtkBuilderObjectInfo *info);
+
+/* A standard child building routine that can be used in object builders */
+void       gtk_builder_standard_build_children (GtkBuilder           *builder,
+						GObject              *parent,
+						GtkBuilderObjectInfo *info);
+
+void       gtk_builder_handle_internal_child   (GtkBuilder           *builder,
+						GObject              *parent,
+						GtkBuilderChildInfo  *child_info);
+
+/* This function performs half of what gtk_builder_build_object does.  It is
+ * useful when the object has already been created.  Usually it would not
+ * have any use at all. */
+void       gtk_builder_set_common_params       (GtkBuilder           *builder,
+						GObject              *object,
+						GtkBuilderObjectInfo *info);
+
+/* this is a wrapper for gtk_type_enum_find_value, that just returns the
+ * integer value for the enum */
+gint       gtk_builder_enum_from_string        (GType                 type,
+						const char           *string);
+guint      gtk_builder_flags_from_string       (GType                 type,
+						const char           *string);
+
+void       gtk_builder_merge_ui                (GtkBuilder           *builder,
+						GtkUIManager         *ui_manager,
+						GtkBuilderUIInfo     *ui,
+						gboolean              merge);
+
+
+
+G_END_DECLS
+	
+#endif /* __GTK_BUILDER_H__ */
+
Index: gtk/gtkbuilderobjects.c
===================================================================
RCS file: gtk/gtkbuilderobjects.c
diff -N gtk/gtkbuilderobjects.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gtk/gtkbuilderobjects.c	6 Apr 2005 19:26:50 -0000
@@ -0,0 +1,1384 @@
+/* -*- Mode: C; c-file-style: "gnu" -*- */
+/* gtkbuilder - a library for building interfaces from XML files at runtime
+ * Copyright (C) 1998-2005  James Henstridge <james@daa.com.au>
+ * Copyright (C) 2005 Johan Dahlin <johan@gnome.org>
+ *
+ * gtkbuilderobjects.c: support for GTK+ widgets in libglade
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA  02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <gtk/gtk.h>
+#include <gtk/gtkcalendar.h>
+#include "gtkbuilderprivate.h"
+#include "gtkbuilder.h"
+
+#define INT(s)   (strtol ((s), NULL, 0))
+#define BOOL(s)  (g_ascii_tolower (*(s)) == 't' || g_ascii_tolower (*(s)) == 'y' || INT (s))
+#define FLOAT(s) (g_strtod ((s), NULL))
+
+static void
+noop (GtkBuilder *builder,
+      GObject *obj,
+      const gchar *prop_name,
+      const gchar *prop_value)
+{
+}
+
+static void
+set_visible (GtkBuilder *builder,
+	     GObject *obj,
+	     const gchar *prop_name,
+	     const gchar *prop_value)
+{
+  static GQuark visible_id = 0;
+
+  if (visible_id == 0)
+    visible_id = g_quark_from_static_string ("GtkBuilder::visible");
+
+  if (BOOL (prop_value))
+    g_object_set_qdata (obj, visible_id, GINT_TO_POINTER (TRUE));
+}
+
+static void
+set_tooltip (GtkBuilder *builder,
+	     GObject *obj,
+	     const gchar *prop_name,
+	     const gchar *prop_value)
+{
+  if (GTK_IS_TOOL_ITEM (obj))
+    gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (obj), builder->priv->tooltips,
+			       prop_value, NULL);
+  else
+    gtk_tooltips_set_tip (builder->priv->tooltips,
+			  GTK_WIDGET (obj), prop_value, NULL);
+}
+
+static void
+set_has_default(GtkBuilder *builder, GObject *obj,
+		const gchar *prop_name, const gchar *prop_value)
+{
+  if (!GTK_WIDGET_CAN_DEFAULT (obj))
+    return;
+  
+  if (BOOL (prop_value))
+    builder->priv->default_widget = GTK_WIDGET (obj);
+}
+
+static void
+set_has_focus (GtkBuilder *builder, GObject *obj,
+	       const gchar *prop_name, const gchar *prop_value)
+{
+  if (BOOL (prop_value))
+    builder->priv->focus_widget = GTK_WIDGET (obj);
+}
+
+static void
+set_sizegroup(GtkBuilder *builder, GObject *obj,
+	      const gchar *prop_name, const gchar *prop_value)
+{
+  GObject *sizegroup;
+
+  sizegroup = gtk_builder_get_object (builder, prop_value);
+
+  gtk_size_group_add_widget (GTK_SIZE_GROUP (sizegroup),
+			     GTK_WIDGET (obj));
+}
+
+static void
+calendar_set_display_options (GtkBuilder *builder, GObject *obj,
+			      const char *name, const char *value)
+{
+  int flags;
+  
+  flags = gtk_builder_flags_from_string (GTK_TYPE_CALENDAR_DISPLAY_OPTIONS,
+					 value); 
+  gtk_calendar_set_display_options (GTK_CALENDAR (obj), flags);
+}
+
+static GType *
+parse_types(const char *value, int *len)
+{
+  gchar **typenames;
+  int i;
+  GType *types;
+    
+  typenames = g_strsplit (value, " ", -1);
+  *len = g_strv_length (typenames);
+  types = g_new0 (GType, *len);
+  for (i = 0; i < *len; i++)
+    {
+      GType type = g_type_from_name (typenames[i]);
+      if (type == G_TYPE_INVALID)
+	{
+	  g_warning ("invalid type '%s'", typenames[i]);
+	  continue;
+	}
+      types[i] = type;
+    }
+  g_strfreev (typenames);
+
+  return types;
+}
+
+static void
+list_store_set_types (GtkBuilder *builder, GObject *obj,
+		      const char *name, const char *value)
+{
+  GType *types;
+  int len;
+
+  types = parse_types (value, &len);
+
+  gtk_list_store_set_column_types (GTK_LIST_STORE (obj), len, types);
+
+  g_free (types);
+}
+
+static void
+tree_store_set_types (GtkBuilder *builder, GObject *obj,
+		      const char *name, const char *value)
+{
+  GType *types;
+  int len;
+    
+  types = parse_types (value, &len);
+  gtk_tree_store_set_column_types (GTK_TREE_STORE (obj), len, types);
+
+  g_free (types);
+}
+
+static void
+menu_item_set_label (GtkBuilder *builder, GObject *obj,
+		     const char *name, const char *value)
+{
+  GtkWidget *child = GTK_BIN (obj)->child;
+
+  if (!child)
+    {
+      child = gtk_accel_label_new ("");
+      gtk_misc_set_alignment (GTK_MISC(child), 0.0, 0.5);
+      gtk_container_add (GTK_CONTAINER(obj), child);
+      gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (child),
+					GTK_WIDGET (obj));
+      gtk_widget_show (child);
+    }
+
+  if (GTK_IS_LABEL (child))
+    gtk_label_set_text (GTK_LABEL (child), value);
+}
+
+static void
+menu_item_set_use_underline (GtkBuilder *builder, GObject *obj,
+			     const char *name, const char *value)
+{
+  GtkWidget *child = GTK_BIN (obj)->child;
+
+  if (!child)
+    {
+      child = gtk_accel_label_new ("");
+      gtk_misc_set_alignment (GTK_MISC(child), 0.0, 0.5);
+      gtk_container_add (GTK_CONTAINER(obj), child);
+      gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (child),
+					GTK_WIDGET (obj));
+      gtk_widget_show (child);
+    }
+
+  if (GTK_IS_LABEL (child))
+    gtk_label_set_use_underline (GTK_LABEL (child), BOOL (value));
+}
+
+static void
+menu_item_set_use_stock (GtkBuilder *builder, GObject *obj,
+			 const gchar *name, const gchar *value)
+{
+  GtkWidget *child = GTK_BIN (obj)->child;
+
+  if (!child)
+    {
+      child = gtk_accel_label_new ("");
+      gtk_misc_set_alignment (GTK_MISC(child), 0.0, 0.5);
+      gtk_container_add (GTK_CONTAINER(obj), child);
+      gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (child),
+					GTK_WIDGET (obj));
+      gtk_widget_show (child);
+    }
+
+  if (GTK_IS_LABEL (child) && BOOL (value))
+    {
+      const gchar *stock_id = gtk_label_get_label (GTK_LABEL (child));
+      GtkStockItem stock_item;
+
+      if (gtk_stock_lookup (stock_id, &stock_item))
+	{
+	  /* put in the stock image next to the text.  Done before
+	   * messing with the label child, so that stock_id doesn't
+	   * become invalid. */
+	  if (GTK_IS_IMAGE_MENU_ITEM (obj))
+	    {
+	      GtkWidget *image =
+		gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU);
+
+	      gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (obj), image);
+	      gtk_widget_show (image);
+	    }
+
+	  gtk_label_set_text (GTK_LABEL (child), stock_item.label);
+	  gtk_label_set_use_underline (GTK_LABEL (child), TRUE);
+
+	  if (stock_item.keyval)
+	    gtk_widget_add_accelerator (GTK_WIDGET (obj),
+					"activate",
+					gtk_builder_ensure_accel (builder),
+					stock_item.keyval,
+					stock_item.modifier,
+					GTK_ACCEL_VISIBLE);
+	}
+      else
+	{
+	  g_warning ("could not look up stock id '%s'", stock_id);
+	}
+    }
+}
+
+static void
+button_set_response_id (GtkBuilder *builder, GObject *obj,
+			const gchar *name, const gchar *value)
+{
+  g_object_set_data (G_OBJECT (obj), "response_id",
+		     GINT_TO_POINTER (INT (value)));
+}
+
+static void
+tool_button_set_icon (GtkBuilder *builder, GObject *obj,
+		      const char *name, const char *value)
+{
+  GdkPixbuf *pb;
+  GtkWidget *image;
+  char *file;
+
+  file = gtk_builder_relative_file (builder, value);
+  pb = gdk_pixbuf_new_from_file (file, NULL);
+  g_free (file);
+
+  if (!pb)
+    {
+      g_warning ("Couldn't find image file: %s", value);
+      return;
+    }
+
+  image = gtk_image_new_from_pixbuf (pb);
+  g_object_unref (pb);
+
+  gtk_widget_show (image);
+  gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (obj), image);
+}
+
+static void
+combo_box_set_items (GtkBuilder *builder, GObject *obj,
+		     const char *name, const char *value)
+{
+  GtkListStore *store;
+  GtkCellRenderer *cell;
+  gchar *items, *items_end, *item_start, *item_end;
+  GtkTreeIter iter;
+
+  /* If the "items" property is set, we create a simple model with just
+     one column of text. */
+  store = gtk_list_store_new (1, G_TYPE_STRING);
+  gtk_combo_box_set_model (GTK_COMBO_BOX (obj), GTK_TREE_MODEL (store));
+
+  /* GtkComboBoxEntry creates the cell renderer itself, but we have to set
+     the column containing the text. */
+  if (GTK_IS_COMBO_BOX_ENTRY (obj))
+    {
+      gtk_combo_box_entry_set_text_column (GTK_COMBO_BOX_ENTRY (obj), 0);
+    }
+  else
+    {
+      cell = gtk_cell_renderer_text_new ();
+      gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (obj), cell, TRUE);
+      gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (obj), cell,
+				      "text", 0,
+				      NULL);
+    }
+
+  /* Now add the items one at a time. */
+  items = g_strdup (value);
+  items_end = &items[strlen (items)];
+  item_start = items;
+  while (item_start < items_end)
+    {
+      item_end = strchr (item_start, '\n');
+      if (item_end == NULL)
+	item_end = items_end;
+      *item_end = '\0';
+
+      gtk_list_store_append (store, &iter);
+      gtk_list_store_set (store, &iter, 0, item_start, -1);
+
+      item_start = item_end + 1;
+    }
+  g_free (items);
+}
+
+static void
+uimanager_build_children(GtkBuilder *builder,
+			 GObject *obj,
+			 GtkBuilderObjectInfo *info)
+{
+  gint i;
+
+  if (info->n_children == 0 || info->n_uis == 0)
+    return;
+    
+  g_object_ref (obj);
+
+  for (i = 0; i < info->n_children; i++)
+    {
+      GObject *child;
+      GtkBuilderObjectInfo *childinfo = info->children[i].child;
+
+      child = gtk_builder_build_object (builder, childinfo);
+      gtk_ui_manager_insert_action_group (GTK_UI_MANAGER (obj),
+					  GTK_ACTION_GROUP (child), i);
+    }
+
+  for (i = 0; i < info->n_uis; i++)
+    gtk_builder_merge_ui (builder, GTK_UI_MANAGER (obj),
+			  &info->uis[i],
+			  info->uis[i].merge);
+  
+  g_object_unref (obj);
+}
+
+static void
+action_group_build_children(GtkBuilder *builder, GObject *obj,
+			    GtkBuilderObjectInfo *info)
+{
+  gint i, j;
+  GObject *child;
+  GtkBuilderObjectInfo *childinfo;
+  gchar *accelerator = NULL;
+  char *accel_path = NULL;
+  guint  accel_key = 0;
+  GdkModifierType accel_mods;
+  gchar *stock_id = NULL;
+  GtkStockItem stock_item;
+    
+  if (info->n_children == 0)
+    return;
+
+  g_object_ref (obj);
+  for (i = 0; i < info->n_children; i++)
+    {
+      childinfo = info->children[i].child;
+	
+      child = gtk_builder_build_object(builder, childinfo);
+      for (j = 0; j < childinfo->n_properties; j++)
+	if (!strcmp (childinfo->properties[j].name, "accelerator"))
+	  {
+	    accelerator = childinfo->properties[j].value;
+	    break;
+	  }
+
+      g_object_get (child, "stock-id", &stock_id, NULL);
+
+      if (accelerator)
+	{
+	  gtk_accelerator_parse (accelerator, &accel_key, &accel_mods);
+	  accelerator = NULL;
+	}
+      else if (stock_id && gtk_stock_lookup (stock_id, &stock_item))
+	{
+	  accel_key = stock_item.keyval;
+	  accel_mods = stock_item.modifier;
+	}
+
+      if (accel_key)
+	{
+	  accel_path = g_strconcat ("<Actions>/", info->name, "/", childinfo->name, NULL);
+	  gtk_accel_map_add_entry (accel_path, accel_key, accel_mods);
+	  accel_key = 0;
+	    
+	  gtk_action_set_accel_path (GTK_ACTION(child), accel_path);
+	  g_free (accel_path);
+	}
+
+      if (stock_id)
+	{
+	  g_free(stock_id);
+	  stock_id = NULL;
+	}
+	
+      gtk_action_group_add_action (GTK_ACTION_GROUP (obj), GTK_ACTION (child));
+    }
+
+  g_object_unref (obj);
+}
+
+static void
+menuitem_build_children (GtkBuilder *builder, GObject *obj,
+			 GtkBuilderObjectInfo *info)
+{
+  gint i;
+
+  if (info->n_children == 0)
+    return;
+
+  g_object_ref (obj);
+  for (i = 0; i < info->n_children; i++)
+    {
+      GtkWidget       *child;
+      GtkBuilderObjectInfo *childinfo = info->children[i].child;
+
+      if (info->children[i].internal_child)
+	{
+	  gtk_builder_handle_internal_child (builder, obj, &info->children[i]);
+	  continue;
+	}
+
+      child = GTK_WIDGET(gtk_builder_build_object (builder, childinfo));
+
+      if (GTK_IS_MENU (child))
+	gtk_menu_item_set_submenu (GTK_MENU_ITEM (obj), child);
+      else
+	gtk_container_add (GTK_CONTAINER (obj), child);
+    }
+  g_object_unref (obj);
+}
+
+static void
+gtk_dialog_build_children (GtkBuilder *builder, GObject *obj,
+			   GtkBuilderObjectInfo *info)
+
+{
+  GtkDialog *dialog = GTK_DIALOG (obj);
+  GList *children, *list;
+
+  gtk_builder_standard_build_children (builder, obj, info);
+
+  if (dialog->action_area == NULL)
+    return;
+
+  /* repack children of action_area */
+  children = gtk_container_get_children (GTK_CONTAINER (dialog->action_area));
+  for (list = children; list; list = list->next)
+    {
+      GtkWidget *child = GTK_WIDGET (list->data);
+
+      g_object_ref (child);
+      gtk_container_remove (GTK_CONTAINER (dialog->action_area), child);
+    }
+  for (list = children; list; list = list->next)
+    {
+      GtkWidget *child = GTK_WIDGET (list->data);
+      gint response_id;
+
+      response_id = GPOINTER_TO_INT (g_object_steal_data (G_OBJECT(child),
+							  "response_id"));
+      gtk_dialog_add_action_widget (dialog, child, response_id);
+      g_object_unref (child);
+
+    }
+  g_list_free (children);
+}
+
+
+static void
+frame_build_children (GtkBuilder *builder, GObject *parent,
+		      GtkBuilderObjectInfo *info)
+{
+  gint i, j;
+  enum {
+    FRAME_ITEM,
+    LABEL_ITEM
+  } type;
+
+  if (info->n_children == 0)
+    return;
+
+  g_object_ref (parent);
+  for (i = 0; i < info->n_children; i++)
+    {
+      GtkBuilderObjectInfo *childinfo = info->children[i].child;
+      GtkWidget *child = GTK_WIDGET (gtk_builder_build_object (builder, childinfo));
+
+      type = FRAME_ITEM;
+      /* there should really only be 2 children */
+      for (j = 0; j < info->children[i].n_properties; j++)
+	{
+	  if (!strcmp (info->children[i].properties[j].name, "type"))
+	    {
+	      const char *value = info->children[i].properties[j].value;
+
+	      if (!strcmp (value, "label_item"))
+		type = LABEL_ITEM;
+	      break;
+	    }
+	}
+
+      if (type == LABEL_ITEM)
+	{
+	  gtk_frame_set_label_widget (GTK_FRAME (parent), child);
+	}
+      else
+	{
+	  gtk_container_add (GTK_CONTAINER (parent), child);
+	}
+    }
+  g_object_unref (parent);
+}
+
+static void
+expander_build_children (GtkBuilder        *builder,
+			 GObject         *parent,
+			 GtkBuilderObjectInfo *info)
+{
+  int i;
+
+  if (info->n_children == 0)
+    return;
+
+  g_object_ref (parent);
+  for (i = 0; i < info->n_children; i++)
+    {
+      GtkBuilderObjectInfo *childinfo = info->children [i].child;
+      GtkWidget       *child;
+      gboolean         label_item = FALSE;
+      int              j;
+	
+      child = GTK_WIDGET (gtk_builder_build_object (builder, childinfo));
+
+      /* there should really only be 2 children */
+      for (j = 0; j < info->children [i].n_properties; j++)
+	{
+	  if (!strcmp (info->children [i].properties[j].name, "type"))
+	    {
+	      const char *value = info->children [i].properties [j].value;
+
+	      if (!strcmp (value, "label_item"))
+		label_item = TRUE;
+	      break;
+	    }
+	}
+
+      if (label_item)
+	gtk_expander_set_label_widget (GTK_EXPANDER (parent), child);
+      else
+	gtk_container_add (GTK_CONTAINER (parent), child);
+    }
+  g_object_unref (parent);
+}
+
+static void
+notebook_build_children (GtkBuilder *builder, GObject *parent,
+			 GtkBuilderObjectInfo *info)
+{
+  gint i, j, tab = 0;
+  enum {
+    PANE_ITEM,
+    TAB_ITEM,
+    MENU_ITEM
+  } type;
+
+  if (info->n_children == 0)
+    return;
+
+  g_object_ref (parent);
+  for (i = 0; i < info->n_children; i++)
+    {
+      GtkBuilderObjectInfo *childinfo = info->children[i].child;
+      GtkWidget *child = GTK_WIDGET (gtk_builder_build_object (builder, childinfo));
+
+      type = PANE_ITEM;
+      for (j = 0; j < info->children[i].n_properties; j++)
+	{
+	  if (!strcmp (info->children[i].properties[j].name, "type"))
+	    {
+	      const char *value = info->children[i].properties[j].value;
+
+	      if (!strcmp (value, "tab"))
+		type = TAB_ITEM;
+	      break;
+	    }
+	}
+
+      if (type == TAB_ITEM) { /* The GtkNotebook API blows */
+	GtkWidget *body;
+
+	body = gtk_notebook_get_nth_page (GTK_NOTEBOOK (parent), (tab - 1));
+	gtk_notebook_set_tab_label (GTK_NOTEBOOK (parent), body, child);
+      }
+      else
+	{
+	  gtk_notebook_append_page (GTK_NOTEBOOK (parent), child, NULL);
+	  tab++;
+	}
+    }
+  g_object_unref (parent);
+}
+
+static void
+tree_view_build_children (GtkBuilder *builder, GObject *obj, GtkBuilderObjectInfo *info)
+{
+  int i;
+    
+  if (info->n_children == 0)
+    return;
+
+  g_object_ref (obj);
+  for (i = 0; i < info->n_children; i++)
+    {
+      GObject *child;
+      GtkBuilderObjectInfo *childinfo = info->children[i].child;
+
+      child = gtk_builder_build_object (builder, childinfo);
+      /* TODO: packing properties */
+      gtk_tree_view_insert_column (GTK_TREE_VIEW (obj),
+				   GTK_TREE_VIEW_COLUMN (child),
+				   i);
+    }
+  g_object_unref (obj);
+
+}
+
+static void
+tree_view_column_build_children (GtkBuilder *builder, GObject *obj, GtkBuilderObjectInfo *info)
+{
+  int i, j;
+  int expand = TRUE;
+  int pack_start = TRUE;
+    
+  if (info->n_children == 0)
+    return;
+
+  g_object_ref(obj);
+  for (i = 0; i < info->n_children; i++)
+    {
+      GObject *child;
+      GtkBuilderObjectInfo *childinfo = info->children[i].child;
+
+      child = gtk_builder_build_object (builder, childinfo);
+
+	
+      for (j = 0; j < info->children[i].n_properties; j++)
+	{
+	  const gchar *name = info->children[i].properties[j].name;
+	  const gchar *value = info->children[i].properties[j].value;
+	    
+	  if (!strcmp (name, "expand"))
+	    expand = BOOL (value);
+	  else if (!strcmp (name, "pack_start"))
+	    pack_start = BOOL (value);
+	  else
+	    g_warning ("unknown child packing property %s for GtkTreeViewColumn",
+		       name);
+	}
+
+      if (pack_start)
+	gtk_tree_view_column_pack_start (GTK_TREE_VIEW_COLUMN (obj),
+					 GTK_CELL_RENDERER (child),
+					 expand);
+      else
+	gtk_tree_view_column_pack_end (GTK_TREE_VIEW_COLUMN (obj),
+				       GTK_CELL_RENDERER (child),
+				       expand);
+	    
+	
+      for (j = 0; j < childinfo->n_layout_props; j++)
+	{
+	  int value;
+
+	  value = atoi (childinfo->layout_props[j].value);
+	  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (obj),
+					  GTK_CELL_RENDERER (child),
+					  childinfo->layout_props[j].name,
+					  value,
+					  NULL);
+	}
+
+    }
+  g_object_unref (obj);
+
+}
+
+static void
+paned_build_children (GtkBuilder *builder, GObject *obj, GtkBuilderObjectInfo *info)
+{
+  int i;
+  GtkWidget *child;
+  gboolean resize, shrink;
+  GtkBuilderChildInfo *cinfo;
+
+  if (info->n_children == 0)
+    return;
+    
+  cinfo = &info->children[0];
+  child = GTK_WIDGET(gtk_builder_build_object(builder, cinfo->child));
+    
+  resize = FALSE; shrink = TRUE;
+  for (i = 0; i < cinfo->n_properties; i++)
+    {
+      const char *name  = cinfo->properties[i].name;
+      const char *value = cinfo->properties[i].value;
+
+      if (!strcmp (name, "resize"))
+	resize = BOOL (value);
+      else if (!strcmp (name, "shrink"))
+	shrink = BOOL (value);
+      else
+	g_warning ("Unknown GtkPaned child property: %s", name);
+    }
+
+  gtk_paned_pack1 (GTK_PANED(obj), child, resize, shrink);
+    
+  if (info->n_children == 1)
+    return;
+
+  cinfo = &info->children[1];
+  child = GTK_WIDGET (gtk_builder_build_object (builder, cinfo->child));
+  resize = TRUE; shrink = TRUE;
+
+  for (i = 0; i < cinfo->n_properties; i++)
+    {
+      const char *name  = cinfo->properties[i].name;
+      const char *value = cinfo->properties[i].value;
+
+      if (!strcmp (name, "resize"))
+	resize = BOOL (value);
+      else if (!strcmp (name, "shrink"))
+	shrink = BOOL (value);
+      else
+	g_warning ("Unknown GtkPaned child property: %s", name);
+    }
+	
+  gtk_paned_pack2 (GTK_PANED(obj), child, resize, shrink);
+}
+
+static void
+layout_build_children (GtkBuilder *builder, GObject *obj, GtkBuilderObjectInfo *info)
+{
+  gint i;
+
+  if (info->n_children == 0)
+    return;
+
+  for (i = 0; i < info->n_children; i++)
+    {
+      GtkBuilderObjectInfo *childinfo = info->children[i].child;
+      GtkWidget *child = NULL;
+      gint j, x = 0, y = 0;
+
+      if (info->children[i].internal_child)
+	{
+	  gtk_builder_handle_internal_child (builder, obj, &info->children[i]);
+	  continue;
+	}
+
+      child = GTK_WIDGET (gtk_builder_build_object (builder, childinfo));
+      for (j = 0; j < info->children[i].n_properties; j++)
+	{
+	  const gchar *name = info->children[i].properties[j].name;
+	  const gchar *value = info->children[i].properties[j].value;
+
+	  if (name[0] == 'x' && name[1] == '\0')
+	    x = INT (value);
+	  else if (name[0] == 'y' && name[1] == '\0')
+	    y = INT (value);
+	  else
+	    g_warning("unknown child packing property %s for GtkLayout",
+		      name);
+	}
+
+      gtk_layout_put (GTK_LAYOUT (obj), child, x, y);
+    }
+}
+
+static void
+toolbar_build_children (GtkBuilder *builder,
+			GObject *parent,
+                        GtkBuilderObjectInfo *info)
+{
+  int i;
+
+  g_object_ref (parent);
+
+  for (i = 0; i < info->n_children; i++)
+    {
+      GtkBuilderChildInfo *childinfo;
+      GObject *child = NULL;
+
+      childinfo = &info->children[i];
+
+      if (!strcmp (childinfo->child->classname, "toggle") ||
+	  !strcmp (childinfo->child->classname, "radio") ||
+	  !strcmp (childinfo->child->classname, "button"))
+	{
+	  const char *label = NULL, *stock = NULL, *group_name = NULL;
+	  const char *tooltip = NULL;
+	  char *icon = NULL;
+	  gboolean use_stock = FALSE, active = FALSE, new_group = FALSE;
+	  gboolean use_underline = FALSE;
+	  GtkWidget *iconw = NULL;
+	  int j;
+
+	  for (j = 0; j < childinfo->child->n_properties; j++)
+	    {
+	      const char *name  = childinfo->child->properties[j].name;
+	      const char *value = childinfo->child->properties[j].value;
+
+	      if (!strcmp (name, "label"))
+		{
+		  label = value;
+                }
+	      else if (!strcmp (name, "use_stock"))
+		{
+		  use_stock = TRUE;
+                }
+	      else if (!strcmp (name, "icon"))
+		{
+		  g_free (icon);
+		  stock = NULL;
+		  icon = gtk_builder_relative_file (builder, value);
+                }
+	      else if (!strcmp (name, "stock_pixmap"))
+		{
+		  g_free (icon);
+		  icon = NULL;
+		  stock = value;
+                }
+	      else if (!strcmp (name, "active"))
+		{
+		  active = BOOL (value);
+                }
+	      else  if (!strcmp (name, "group"))
+		{
+		  group_name = value;
+                }
+	      else if (!strcmp (name, "new_group"))
+		{
+		  new_group = BOOL (value);
+                }
+	      else if (!strcmp (name, "visible"))
+		{
+		  /* ignore for now */
+                }
+	      else if (!strcmp (name, "tooltip"))
+		{
+		  tooltip = value;
+                }
+	      else if (!strcmp (name, "use_underline"))
+		{
+		  use_underline = BOOL (value);
+                }
+	      else if (!strcmp (name, "inconsistent"))
+		{
+		  /* ignore for now */
+                }
+	      else {
+		g_warning ("Unknown GtkToolbar child property: %s", name);
+	      }
+            }
+
+	  /* For stock items, we create the stock icon and get the label
+	     here, partly because GTK+ doesn't have direct support for stock
+	     toggle & radio items. */
+	  if (use_stock)
+	    {
+	      stock = label;
+	      label = NULL;
+            }
+
+	  if (stock)
+	    {
+	      iconw = gtk_image_new_from_stock
+		(stock, GTK_TOOLBAR (parent)->icon_size);
+	      if (!iconw)
+		g_warning ("Could not create stock item: %s", stock);
+            }
+	  else if (icon)
+	    {
+	      iconw = gtk_image_new_from_file (icon);
+	      g_free (icon);
+            }
+
+	  if (iconw)
+	    gtk_widget_show (iconw);
+
+#if 0
+	  if (new_group)
+	    gtk_toolbar_append_space (GTK_TOOLBAR (parent));
+
+	  if (!strcmp (childinfo->child->classname, "toggle"))
+	    {
+	      child = gtk_toolbar_append_element
+		(GTK_TOOLBAR (parent),
+		 GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL,
+		 label, tooltip, NULL, iconw, NULL, NULL);
+	      gtk_toggle_button_set_active
+		(GTK_TOGGLE_BUTTON (child), active);
+            }
+	  else if (!strcmp (childinfo->child->classname, "radio"))
+	    {
+	      child = gtk_toolbar_append_element
+		(GTK_TOOLBAR (parent),
+		 GTK_TOOLBAR_CHILD_RADIOBUTTON, NULL,
+		 label, tooltip, NULL, iconw, NULL, NULL);
+
+	      if (group_name)
+		{
+		  g_object_set (G_OBJECT (child),
+				"group", gtk_builder_get_widget (builder, group_name),
+				NULL);
+                }
+	      gtk_toggle_button_set_active
+		(GTK_TOGGLE_BUTTON (child), active);
+            }
+	  else
+	    child = gtk_toolbar_append_item (
+					     GTK_TOOLBAR (parent),
+					     label, tooltip, NULL, iconw, NULL, NULL);
+		
+	  /* GTK+ doesn't support use_underline directly, so we have to hack
+	     it. */
+	  if (use_underline)
+	    {
+	      GList *elem = g_list_last (GTK_TOOLBAR (parent)->children);
+	      GtkToolbarChild *toolbar_child = elem->data;
+	      gtk_label_set_use_underline (GTK_LABEL (toolbar_child->label),
+					   TRUE);
+            }
+#endif
+
+	  gtk_builder_set_common_params (builder, child, childinfo->child);
+        }
+      else
+	{
+	  child = gtk_builder_build_object (builder, childinfo->child);
+
+	  if (GTK_IS_TOOL_ITEM (child))
+	    gtk_toolbar_insert (GTK_TOOLBAR (parent), GTK_TOOL_ITEM (child), -1);
+#if 0
+	  else
+	    gtk_toolbar_append_widget (GTK_TOOLBAR (parent), child, NULL, NULL);
+#endif		
+        }
+    }
+}
+
+#ifndef GTK_DISABLE_DEPRECATED
+
+static void
+option_menu_build_children(GtkBuilder           *builder,
+			   GObject              *parent,
+			   GtkBuilderObjectInfo *info)
+{
+  int i, history = 0;
+
+  for (i = 0; i < info->n_children; i++)
+    {
+      GObject *child;
+      GtkBuilderObjectInfo *childinfo = info->children[i].child;
+
+      if (strcmp(childinfo->classname, "GtkMenu") != 0)
+	{
+	  g_warning("the child of the option menu '%s' was not a GtkMenu",
+		    info->name);
+	  continue;
+	}
+      
+      child = gtk_builder_build_object (builder, childinfo);
+
+      gtk_option_menu_set_menu (GTK_OPTION_MENU (parent), GTK_WIDGET (child));
+    }
+
+  for (i = 0; i < info->n_properties; i++)
+    {
+      const char *name  = info->properties[i].name;
+      const char *value = info->properties[i].value;
+
+      if (strcmp (name, "history"))
+	continue;
+
+      history = INT (value);
+      break;
+    }
+
+  /* we have to set the history *after* building the child menu */
+  gtk_option_menu_set_history (GTK_OPTION_MENU (parent), history);
+}
+
+static void
+clist_build_children(GtkBuilder           *builder,
+		     GObject              *parent,
+		     GtkBuilderObjectInfo *info)
+{
+  int i;
+
+  g_object_ref (parent);
+  for (i = 0; i < info->n_children; i++)
+    {
+      GtkBuilderObjectInfo*childinfo;
+      GObject *child = NULL;
+
+      childinfo = info->children[i].child;
+
+      /* treat GtkLabels specially */
+      if (!strcmp (childinfo->classname, "GtkLabel"))
+	{
+	  int j;
+	  const char *label = NULL;
+
+	  for (j = 0; j < childinfo->n_properties; j++)
+	    {
+	      if (!strcmp (childinfo->properties[j].name, "label"))
+		{
+		  label = childinfo->properties[j].value;
+		  break;
+		}
+	      else
+		{
+		  g_warning ("Unknown GtkCList child property: %s", childinfo->properties[j].name);
+		}
+	    }
+
+	  if (label)
+	    {
+	      /* FIXME: translate ? */
+	      gtk_clist_set_column_title (GTK_CLIST (parent), i, label);
+	      child = G_OBJECT (gtk_clist_get_column_widget (GTK_CLIST (parent), i));
+	      child = G_OBJECT (GTK_BIN (child)->child);
+	      gtk_builder_set_common_params (builder, child, childinfo);
+	    }
+	}
+
+      if (!child)
+	{
+	  child = gtk_builder_get_object (builder, childinfo->name);
+	  gtk_clist_set_column_widget (GTK_CLIST (parent), i, GTK_WIDGET (child));
+	}
+    }
+  
+  g_object_unref (parent);
+}
+#endif
+
+static GObject *
+scrolled_window_find_internal_child(GtkBuilder *builder, GObject *parent,
+				    const gchar *childname)
+{
+  if (!strcmp (childname, "vscrollbar"))
+    return G_OBJECT (GTK_SCROLLED_WINDOW (parent)->vscrollbar);
+  if (!strcmp (childname, "hscrollbar"))
+    return G_OBJECT (GTK_SCROLLED_WINDOW (parent)->hscrollbar);
+  return NULL;
+}
+
+static GObject *
+filesel_find_internal_child(GtkBuilder *builder, GObject *parent,
+			    const gchar *childname)
+{
+  if (!strcmp (childname, "vbox"))
+    return G_OBJECT (GTK_DIALOG (parent)->vbox);
+  if (!strcmp (childname, "action_area"))
+    return G_OBJECT (GTK_DIALOG (parent)->action_area);
+  if (!strcmp (childname, "ok_button"))
+    return G_OBJECT (GTK_FILE_SELECTION (parent)->ok_button);
+  if (!strcmp (childname, "cancel_button"))
+    return G_OBJECT (GTK_FILE_SELECTION (parent)->cancel_button);
+  if (!strcmp (childname, "help_button"))
+    return G_OBJECT (GTK_FILE_SELECTION (parent)->help_button);
+  return NULL;
+}
+
+static GObject *
+colorseldlg_find_internal_child(GtkBuilder *builder, GObject *parent,
+				const gchar *childname)
+{
+  if (!strcmp (childname, "vbox"))
+    return G_OBJECT (GTK_DIALOG (parent)->vbox);
+  if (!strcmp (childname, "action_area"))
+    return G_OBJECT (GTK_DIALOG (parent)->action_area);
+  if (!strcmp (childname, "ok_button"))
+    return G_OBJECT (GTK_COLOR_SELECTION_DIALOG (parent)->ok_button);
+  if (!strcmp (childname, "cancel_button"))
+    return G_OBJECT (GTK_COLOR_SELECTION_DIALOG (parent)->cancel_button);
+  if (!strcmp (childname, "help_button"))
+    return G_OBJECT (GTK_COLOR_SELECTION_DIALOG (parent)->help_button);
+  if (!strcmp (childname, "color_selection"))
+    return G_OBJECT (GTK_COLOR_SELECTION_DIALOG (parent)->colorsel);
+  return NULL;
+}
+
+static GObject *
+fontseldlg_find_internal_child(GtkBuilder *builder, GObject *parent,
+			       const gchar *childname)
+{
+  if (!strcmp (childname, "vbox"))
+    return G_OBJECT (GTK_DIALOG (parent)->vbox);
+  if (!strcmp (childname, "action_area"))
+    return G_OBJECT (GTK_DIALOG (parent)->action_area);
+  if (!strcmp (childname, "ok_button"))
+    return G_OBJECT (GTK_FONT_SELECTION_DIALOG (parent)->ok_button);
+  if (!strcmp (childname, "cancel_button"))
+    return G_OBJECT (GTK_FONT_SELECTION_DIALOG (parent)->cancel_button);
+  if (!strcmp (childname, "apply_button"))
+    return G_OBJECT (GTK_FONT_SELECTION_DIALOG (parent)->apply_button);
+  if (!strcmp (childname, "font_selection"))
+    return G_OBJECT (GTK_FONT_SELECTION_DIALOG (parent)->fontsel);
+  return NULL;
+}
+
+static GObject *
+combo_box_entry_find_internal_child(GtkBuilder *builder, GObject *parent,
+				    const gchar *childname)
+{
+  if (!strcmp (childname, "entry"))
+    return G_OBJECT (gtk_bin_get_child(GTK_BIN (parent)));
+  return NULL;
+}
+
+static GObject *
+dialog_find_internal_child(GtkBuilder *builder, GObject *parent,
+			   const gchar *childname)
+{
+  if (!strcmp (childname, "vbox"))
+    return G_OBJECT (GTK_DIALOG (parent)->vbox);
+  if (!strcmp (childname, "action_area"))
+    return G_OBJECT (GTK_DIALOG (parent)->action_area);
+
+  return NULL;
+}
+
+static GObject *
+image_menu_find_internal_child(GtkBuilder *builder, GObject *parent,
+			       const gchar *childname)
+{
+  if (!strcmp (childname, "image")) {
+    GtkWidget *pl;
+
+    pl = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (parent));
+    if (!pl) {
+      pl = gtk_image_new ();
+
+      gtk_image_menu_item_set_image
+	(GTK_IMAGE_MENU_ITEM (parent), pl);
+    }
+
+    return G_OBJECT (pl);
+  }
+  return NULL;
+}
+
+#ifndef GTK_DISABLE_DEPRECATED
+static GObject *
+combo_find_internal_child(GtkBuilder *builder,
+			  GObject *parent,
+			  const gchar *childname)
+{
+    if (!strcmp (childname, "entry"))
+	return G_OBJECT (GTK_COMBO (parent)->entry);
+    if (!strcmp (childname, "button"))
+	return G_OBJECT (GTK_COMBO (parent)->button);
+    if (!strcmp (childname, "popup"))
+	return G_OBJECT (GTK_COMBO (parent)->popup);
+    if (!strcmp (childname, "popwin"))
+	return G_OBJECT (GTK_COMBO (parent)->popwin);
+    if (!strcmp (childname, "list"))
+	return G_OBJECT (GTK_COMBO (parent)->list);
+    return NULL;
+}
+#endif
+
+void
+_gtk_builder_init_types (void)
+{
+  /* http://bugzilla.gnome.org/show_bug.cgi?id=172536 */
+  gtk_builder_register_custom_prop (GTK_TYPE_ACTION, "accelerator", noop); 
+  gtk_builder_register_custom_prop (GTK_TYPE_BUTTON, "response_id", button_set_response_id);
+  /* This one redundant, remove? */
+  gtk_builder_register_custom_prop (GTK_TYPE_CALENDAR, "display_options", calendar_set_display_options);
+  gtk_builder_register_custom_prop (GTK_TYPE_COMBO_BOX, "items", combo_box_set_items);
+  gtk_builder_register_custom_prop (GTK_TYPE_LIST_STORE, "types", list_store_set_types);
+  gtk_builder_register_custom_prop (GTK_TYPE_MENU_ITEM, "label", menu_item_set_label);
+  gtk_builder_register_custom_prop (GTK_TYPE_MENU_ITEM, "use_underline", menu_item_set_use_underline);
+  gtk_builder_register_custom_prop (GTK_TYPE_MENU_ITEM, "use_stock", menu_item_set_use_stock);
+  gtk_builder_register_custom_prop (GTK_TYPE_TOOL_BUTTON, "icon", tool_button_set_icon);
+  gtk_builder_register_custom_prop (GTK_TYPE_TREE_STORE, "types", tree_store_set_types);
+
+#if 0
+  /* Deprecated */
+  gtk_builder_register_custom_prop (GTK_TYPE_PIXMAP, "build_insensitive", pixmap_set_build_insensitive);
+  gtk_builder_register_custom_prop (GTK_TYPE_PIXMAP, "filename", pixmap_set_filename);
+  gtk_builder_register_custom_prop (GTK_TYPE_PROGRESS, "format", progress_set_format);
+  gtk_builder_register_custom_prop (GTK_TYPE_OPTION_MENU, "history", custom_noop);
+  gtk_builder_register_custom_prop (GTK_TYPE_CLIST, "column_widths", clist_set_column_widths);
+  gtk_builder_register_custom_prop (GTK_TYPE_CLIST, "selection_mode", clist_set_selection_mode);
+  gtk_builder_register_custom_prop (GTK_TYPE_CLIST, "shadow_type", clist_set_shadow_type);
+  gtk_builder_register_custom_prop (GTK_TYPE_CLIST, "show_titles", clist_set_show_titles);
+  gtk_builder_register_custom_prop (GTK_TYPE_TOOLBAR, "tooltips", toolbar_set_tooltips);
+  gtk_builder_register_custom_prop (GTK_TYPE_TOGGLE_TOOL_BUTTON, "active", toggle_tool_button_set_active);
+  gtk_builder_register_custom_prop (GTK_TYPE_TOOL_BUTTON, "icon", tool_button_set_icon);
+#endif
+
+  /* special properties */
+  gtk_builder_register_custom_prop (GTK_TYPE_WIDGET, "visible", set_visible);
+  gtk_builder_register_custom_prop (GTK_TYPE_WIDGET, "tooltip", set_tooltip);
+  gtk_builder_register_custom_prop (GTK_TYPE_WIDGET, "has_default", set_has_default);
+  gtk_builder_register_custom_prop (GTK_TYPE_WIDGET, "has_focus", set_has_focus);
+  gtk_builder_register_custom_prop (GTK_TYPE_WIDGET, "sizegroup", set_sizegroup);
+
+  gtk_builder_register_type (GDK_TYPE_PIXBUF, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_ABOUT_DIALOG, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_ACCEL_LABEL, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_ACTION, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_ALIGNMENT, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_ARROW, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_BUTTON, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_CALENDAR, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_CELL_RENDERER_TEXT, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_CELL_RENDERER_PIXBUF, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_CHECK_BUTTON, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_COLOR_BUTTON, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_COLOR_SELECTION, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_COMBO_BOX, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_CURVE, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_DRAWING_AREA, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_ENTRY, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_EVENT_BOX, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_FILE_CHOOSER, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_FILE_CHOOSER_BUTTON, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_FIXED, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_FONT_BUTTON, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_FONT_SELECTION, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_GAMMA_CURVE, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_HANDLE_BOX, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_HBOX, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_HBUTTON_BOX, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_HRULER, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_HSCALE, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_HSCROLLBAR, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_HSEPARATOR, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_ICON_VIEW, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_IMAGE, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_INPUT_DIALOG, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_LABEL, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_LIST_STORE, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_MENU_TOOL_BUTTON, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_MENU, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_MENU_BAR, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_MESSAGE_DIALOG, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_PROGRESS_BAR, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_RADIO_ACTION, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_RADIO_BUTTON, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_RADIO_TOOL_BUTTON, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_SEPARATOR_MENU_ITEM, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_SEPARATOR_TOOL_ITEM, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_SIZE_GROUP, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_SPIN_BUTTON, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_STATUSBAR, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_TABLE, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_TEAROFF_MENU_ITEM, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_TEXT_BUFFER, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_TEXT_VIEW, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_TOGGLE_ACTION, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_TOGGLE_BUTTON, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_TOGGLE_TOOL_BUTTON, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_TOOL_BUTTON, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_TOOL_ITEM, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_TREE_STORE, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_TREE_MODEL_SORT, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_VBOX, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_VBUTTON_BOX, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_VIEWPORT, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_VRULER, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_VSCALE, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_VSCROLLBAR, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_VSEPARATOR, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_WINDOW, NULL, NULL);
+
+  /* New, added over libglade */
+  gtk_builder_register_type (GTK_TYPE_ACTION_GROUP, action_group_build_children, NULL);
+  gtk_builder_register_type (GTK_TYPE_TREE_VIEW, tree_view_build_children, NULL);
+  gtk_builder_register_type (GTK_TYPE_TREE_VIEW_COLUMN,
+		       tree_view_column_build_children, NULL);
+  gtk_builder_register_type (GTK_TYPE_UI_MANAGER, uimanager_build_children, NULL);
+
+  /* Dialogs */
+  gtk_builder_register_type (GTK_TYPE_DIALOG, gtk_dialog_build_children,
+		       dialog_find_internal_child);
+  gtk_builder_register_type (GTK_TYPE_FILE_CHOOSER_DIALOG, gtk_dialog_build_children,
+		       dialog_find_internal_child);
+
+  /* Frames */
+  gtk_builder_register_type (GTK_TYPE_ASPECT_FRAME, frame_build_children, NULL);
+  gtk_builder_register_type (GTK_TYPE_FRAME, frame_build_children, NULL);
+
+  /* Menuitems */
+  gtk_builder_register_type (GTK_TYPE_IMAGE_MENU_ITEM, menuitem_build_children,
+		       image_menu_find_internal_child);
+  gtk_builder_register_type (GTK_TYPE_CHECK_MENU_ITEM, menuitem_build_children, NULL);
+  gtk_builder_register_type (GTK_TYPE_MENU_ITEM, menuitem_build_children, NULL);
+  gtk_builder_register_type (GTK_TYPE_RADIO_MENU_ITEM,  menuitem_build_children, NULL);
+
+  /* Paneds */
+  gtk_builder_register_type (GTK_TYPE_HPANED, paned_build_children, NULL);
+  gtk_builder_register_type (GTK_TYPE_VPANED, paned_build_children, NULL);
+
+  /* Misc */
+  gtk_builder_register_type (GTK_TYPE_EXPANDER, expander_build_children, NULL);
+  gtk_builder_register_type (GTK_TYPE_LAYOUT, layout_build_children, NULL);
+  gtk_builder_register_type (GTK_TYPE_NOTEBOOK, notebook_build_children, NULL);
+  gtk_builder_register_type (GTK_TYPE_TOOLBAR, toolbar_build_children, NULL);
+    
+  /* Only internal child, replace with introspection */
+  gtk_builder_register_type (GTK_TYPE_COLOR_SELECTION_DIALOG, NULL,
+		       colorseldlg_find_internal_child);
+  gtk_builder_register_type (GTK_TYPE_COMBO_BOX_ENTRY, NULL,
+		       combo_box_entry_find_internal_child);
+  gtk_builder_register_type (GTK_TYPE_FONT_SELECTION_DIALOG, NULL,
+		       fontseldlg_find_internal_child);
+  gtk_builder_register_type (GTK_TYPE_FILE_SELECTION, NULL,
+		       filesel_find_internal_child);
+  gtk_builder_register_type (GTK_TYPE_SCROLLED_WINDOW, NULL,
+		       scrolled_window_find_internal_child);
+
+#ifdef HAVE_GTK_PLUG
+  gtk_builder_register_type (GTK_TYPE_PLUG, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_SOCKET, NULL, NULL);
+#endif
+
+#ifndef GTK_DISABLE_DEPRECATED
+  /* Deprecated */
+  gtk_builder_register_type (GTK_TYPE_CLIST, clist_build_children, NULL);
+  gtk_builder_register_type (GTK_TYPE_COMBO, NULL, combo_find_internal_child);
+  gtk_builder_register_type (GTK_TYPE_CTREE, clist_build_children, NULL);
+  gtk_builder_register_type (GTK_TYPE_OPTION_MENU, option_menu_build_children, NULL);
+  gtk_builder_register_type (GTK_TYPE_PIXMAP, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_PROGRESS, NULL, NULL);
+  gtk_builder_register_type (GTK_TYPE_TIPS_QUERY, NULL, NULL);
+#endif
+    
+}
+
Index: gtk/gtkbuilderparser.c
===================================================================
RCS file: gtk/gtkbuilderparser.c
diff -N gtk/gtkbuilderparser.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gtk/gtkbuilderparser.c	6 Apr 2005 19:26:50 -0000
@@ -0,0 +1,1641 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * gtkbuilder - a library for building interfaces from XML files at runtime
+ * Copyright (C) 1998-2005  James Henstridge <james@daa.com.au>
+ * Copyright (C) 2005       Johan Dahlin <johan@gnome.org>
+ *
+ * gtkbuilderparser.c: functions for parsing glade-2.0 files
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA  02111-1307, USA.
+ */
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef ENABLE_NLS
+#  include <libintl.h>
+#else
+#  define textdomain(String) (String)
+#  define dgettext(Domain, String) (String)
+#endif
+
+#include <glib/gmarkup.h>
+#include <gdk/gdkkeys.h>
+#include <gtk/gtkdebug.h>
+
+#include "gtkbuilderparser.h"
+
+typedef enum
+{
+    PARSER_START,
+    PARSER_GLADE_INTERFACE,
+    PARSER_REQUIRES,
+    PARSER_OBJECT,
+    PARSER_OBJECT_PROPERTY,
+    PARSER_OBJECT_ATK,
+    PARSER_OBJECT_ATK_PROPERTY,
+    PARSER_OBJECT_ATK_ACTION,
+    PARSER_OBJECT_ATK_RELATION,
+    PARSER_OBJECT_AFTER_ATK,
+    PARSER_OBJECT_SIGNAL,
+    PARSER_OBJECT_AFTER_SIGNAL,
+    PARSER_OBJECT_ACCEL,
+    PARSER_OBJECT_AFTER_ACCEL,
+    PARSER_OBJECT_CHILD,
+    PARSER_OBJECT_CHILD_AFTER_OBJECT,
+    PARSER_OBJECT_CHILD_PACKING,
+    PARSER_OBJECT_CHILD_PACKING_PROPERTY,
+    PARSER_OBJECT_CHILD_AFTER_PACKING,
+    PARSER_OBJECT_CHILD_PLACEHOLDER,
+    PARSER_OBJECT_CHILD_AFTER_PLACEHOLDER,
+    PARSER_OBJECT_LAYOUT,
+    PARSER_OBJECT_LAYOUT_PROPERTY,
+    PARSER_OBJECT_AFTER_LAYOUT,
+    PARSER_OBJECT_UI_STATE,
+    PARSER_FINISH,
+    PARSER_UNKNOWN
+} ParserState;
+
+#ifdef G_ENABLE_DEBUG
+static const gchar *state_names[] =
+{
+    "START",
+    "GLADE_INTERFACE",
+    "REQUIRES",
+    "OBJECT",
+    "OBJECT_PROPERTY",
+    "OBJECT_ATK",
+    "OBJECT_ATK_PROPERTY",
+    "OBJECT_ATK_ACTION",
+    "OBJECT_ATK_RELATION",
+    "OBJECT_AFTER_ATK",
+    "OBJECT_SIGNAL",
+    "OBJECT_AFTER_SIGNAL",
+    "OBJECT_ACCEL",
+    "OBJECT_AFTER_ACCEL",
+    "OBJECT_CHILD",
+    "OBJECT_CHILD_AFTER_OBJECT",
+    "OBJECT_CHILD_PACKING",
+    "OBJECT_CHILD_PACKING_PROPERTY",
+    "OBJECT_CHILD_AFTER_PACKING",
+    "OBJECT_CHILD_PLACEHOLDER",
+    "OBJECT_CHILD_AFTER_PLACEHOLDER",
+    "OBJECT_LAYOUT",
+    "OBJECT_LAYOUT_PROPERTY",
+    "OBJECT_AFTER_LAYOUT",
+    "OBJECT_UI_STATE",
+    "FINISH",
+    "UNKNOWN",
+};
+#endif
+
+typedef struct _GtkBuilderParseState GtkBuilderParseState;
+struct _GtkBuilderParseState
+{
+    ParserState      state;
+
+    const gchar     *domain;
+
+    guint            unknown_depth;    /* handle recursive unrecognised tags */
+    ParserState      prev_state; /* the last `known' state we were in */
+
+    guint            object_depth;
+    GString         *content;
+
+    GtkBuilderInterface  *interface;
+    GtkBuilderObjectInfo *object;
+
+    enum {
+	PROP_NONE,
+	PROP_OBJECT,
+	PROP_ATK,
+	PROP_CHILD,
+	PROP_LAYOUT
+    } prop_type;
+    gchar           *prop_name;
+    gboolean         translate_prop;
+    gboolean         context_prop;
+    GArray          *props;
+
+    GArray          *signals;
+    GArray          *atk_actions;
+    GArray          *relations;
+    GArray          *accels;
+    GArray          *uis;
+    gchar           *ui;
+};
+
+static gchar *
+alloc_string (GtkBuilderInterface *interface, const gchar *string)
+{
+    gchar *s;
+
+    s = g_hash_table_lookup (interface->strings, string);
+    if (!s)
+    {
+	s = g_strdup (string);
+	g_hash_table_insert (interface->strings, s, s);
+    }
+    return s;
+}
+
+static gchar *
+alloc_propname (GtkBuilderInterface *interface, const gchar *string)
+{
+    static GString *norm_str;
+    gint i;
+
+    if (!norm_str)
+	norm_str = g_string_new_len (NULL, 64);
+
+    /* assign the string to norm_str */
+    g_string_assign (norm_str, string);
+    /* convert all dashes to underscores */
+    for (i = 0; i < norm_str->len; i++)
+	if (norm_str->str[i] == '-')
+	    norm_str->str[i] = '_';
+
+    return alloc_string (interface, norm_str->str);
+}
+
+static GtkBuilderObjectInfo *
+create_object_info (GtkBuilderInterface *interface,
+		    const gchar   **names,
+		    const gchar   **values)
+{
+    GtkBuilderObjectInfo *info = g_new0 (GtkBuilderObjectInfo, 1);
+    int i;
+
+    for (i = 0; names && names[i] != NULL; i++)
+    {
+	if (!strcmp (names[i], "class"))
+	    info->classname = alloc_string (interface, values[i]);
+	else if (!strcmp (names[i], "id"))
+	    info->name = alloc_string (interface, values[i]);
+	else if (!strcmp (names[i], "constructor"))
+	    info->constructor = alloc_string (interface, values[i]);
+	else
+	    g_warning ("unknown attribute `%s' for <object>.", names[i]);
+    }
+    if (info->classname == NULL || info->name == NULL)
+	g_warning ("<object> element missing required attributes!");
+    g_hash_table_insert (interface->names, info->name, info);
+    return info;
+}
+
+static inline void
+flush_properties (GtkBuilderParseState *state)
+
+{
+    if (state->props == NULL)
+	return;
+    switch (state->prop_type)
+    {
+    case PROP_NONE:
+	break;
+    case PROP_OBJECT:
+	if (state->object->properties)
+	    g_warning ("we already read all the props for this key.  Leaking");
+	state->object->properties = (GtkBuilderProperty *) state->props->data;
+	state->object->n_properties = state->props->len;
+	g_array_free (state->props, FALSE);
+	break;
+    case PROP_ATK:
+	if (state->object->atk_props)
+	    g_warning ("we already read all the ATK props for this key.  Leaking");
+	state->object->atk_props = (GtkBuilderProperty *) state->props->data;
+	state->object->n_atk_props = state->props->len;
+	g_array_free (state->props, FALSE);
+	break;
+    case PROP_CHILD:
+	if (state->object->n_children == 0)
+	{
+	    g_warning ("no children, but have child properties!");
+	    g_array_free (state->props, TRUE);
+	}
+	else
+	{
+	    GtkBuilderChildInfo *info = &state->object->children[
+		state->object->n_children-1];
+	    if (info->properties)
+		g_warning ("we already read all the child props for this key.  Leaking");
+	    info->properties = (GtkBuilderProperty *) state->props->data;
+	    info->n_properties = state->props->len;
+	    g_array_free (state->props, FALSE);
+	}
+	break;
+    case PROP_LAYOUT:
+	if (state->object->layout_props)
+	    g_warning ("we already read all the layout props for this key.  Leaking");
+	state->object->layout_props = (GtkBuilderProperty *) state->props->data;
+	state->object->n_layout_props = state->props->len;
+	g_array_free (state->props, FALSE);
+	break;
+    }
+    state->prop_type = PROP_NONE;
+    state->prop_name = NULL;
+    state->props = NULL;
+}
+
+static inline void
+flush_signals (GtkBuilderParseState *state)
+{
+    if (state->signals)
+    {
+	state->object->signals = (GtkBuilderSignalInfo *) state->signals->data;
+	state->object->n_signals = state->signals->len;
+	g_array_free (state->signals, FALSE);
+    }
+    state->signals = NULL;
+}
+
+static inline void
+flush_actions (GtkBuilderParseState *state)
+{
+    if (state->atk_actions)
+    {
+	state->object->atk_actions = (GtkBuilderAtkActionInfo *) state->atk_actions->data;
+	state->object->n_atk_actions = state->atk_actions->len;
+	g_array_free (state->atk_actions, FALSE);
+    }
+    state->atk_actions = NULL;
+}
+
+static inline void
+flush_relations (GtkBuilderParseState *state)
+{
+    if (state->relations)
+    {
+	state->object->relations = (GtkBuilderAtkRelationInfo *) state->relations->data;
+	state->object->n_relations = state->relations->len;
+	g_array_free (state->relations, FALSE);
+    }
+    state->relations = NULL;
+}
+
+static inline void
+flush_accels (GtkBuilderParseState *state)
+{
+    if (state->accels)
+    {
+	state->object->accels = (GtkBuilderAccelInfo *) state->accels->data;
+	state->object->n_accels = state->accels->len;
+	g_array_free (state->accels, FALSE);
+    }
+    state->accels = NULL;
+}
+
+static inline void
+flush_uis (GtkBuilderParseState *state)
+{
+    if (state->uis)
+    {
+	state->object->uis = (GtkBuilderUIInfo *) state->uis->data;
+	state->object->n_uis = state->uis->len;
+	g_array_free (state->uis, FALSE);
+    }
+    state->uis = NULL;
+}
+static inline void
+handle_atk_action (GtkBuilderParseState *state,
+		   const gchar    **names,
+		   const gchar    **values)
+{
+    gint i;
+    GtkBuilderAtkActionInfo info = { NULL };
+
+    flush_properties (state);
+
+    for (i = 0; names && names[i] != NULL; i++)
+    {
+	if (!strcmp (names[i], "action_name"))
+	    info.action_name = alloc_string (state->interface, values[i]);
+	else if (!strcmp (names[i], "description"))
+	    info.description = alloc_string (state->interface,
+					     dgettext (state->domain, values[i]));
+	else
+	    g_warning ("unknown attribute `%s' for <action>.", names[i]);
+    }
+    if (info.action_name == NULL)
+    {
+	g_warning ("required <atkaction> attribute 'action_name' missing!!!");
+	return;
+    }
+    if (!state->atk_actions)
+	state->atk_actions = g_array_new (FALSE, FALSE,
+					  sizeof (GtkBuilderAtkActionInfo));
+    g_array_append_val (state->atk_actions, info);
+}
+
+static inline void
+handle_atk_relation (GtkBuilderParseState *state,
+		     const gchar      **names,
+		     const gchar      **values)
+{
+    gint i;
+    GtkBuilderAtkRelationInfo info = { NULL };
+
+    flush_properties (state);
+
+    for (i = 0; names && names[i] != NULL; i++)
+    {
+	if (!strcmp (names[i], "target"))
+	    info.target = alloc_string (state->interface, values[i]);
+	else if (!strcmp (names[i], "type"))
+	    info.type = alloc_string (state->interface, values[i]);
+	else
+	    g_warning ("unknown attribute `%s' for <signal>.", names[i]);
+    }
+    if (info.target == NULL || info.type == NULL)
+    {
+	g_warning ("required <atkrelation> attributes ('target' and/or 'type') missing!!!");
+	return;
+    }
+    if (!state->relations)
+	state->relations = g_array_new (FALSE, FALSE,
+					sizeof (GtkBuilderAtkRelationInfo));
+    g_array_append_val (state->relations, info);
+}
+
+static inline void
+handle_signal (GtkBuilderParseState *state, 
+	       const gchar    **names,
+	       const gchar    **values)
+{
+    GtkBuilderSignalInfo info = { NULL };
+    gint i;
+
+    flush_properties (state);
+
+    info.after = FALSE;
+    for (i = 0; names && names[i] != NULL; i++)
+    {
+	if (!strcmp (names[i], "name"))
+	    info.name = alloc_string (state->interface, values[i]);
+	else if (!strcmp (names[i], "handler"))
+	    info.handler = alloc_string (state->interface, values[i]);
+	else if (!strcmp (names[i], "after"))
+	    info.after = values[i][0] == 'y';
+	else if (!strcmp (names[i], "object"))
+	    info.object = alloc_string (state->interface, values[i]);
+	else if (!strcmp (names[i], "last_modification_time"))
+	    /* Do nothing. */;
+	else
+	    g_warning ("unknown attribute `%s' for <signal>.", names[i]);
+    }
+    if (info.name == NULL || info.handler == NULL)
+    {
+	g_warning ("required <signal> attributes missing!!!");
+	return;
+    }
+    if (!state->signals)
+	state->signals = g_array_new (FALSE, FALSE,
+				      sizeof (GtkBuilderSignalInfo));
+    g_array_append_val (state->signals, info);
+}
+
+static inline void
+handle_accel (GtkBuilderParseState *state,
+	      const gchar    **names,
+	      const gchar    **values)
+{
+    GtkBuilderAccelInfo info = { 0 };
+    gint i;
+
+    flush_properties (state);
+    flush_signals (state);
+    flush_actions (state);
+    flush_relations (state);
+    flush_uis (state);
+    
+    for (i = 0; names && names[i] != NULL; i++)
+    {
+	if  (!strcmp(names[i], "key"))
+	    info.key = gdk_keyval_from_name (values[i]);
+	else if (!strcmp (names[i], "modifiers"))
+	{
+	    const gchar *pos = values[i];
+
+	    info.modifiers = 0;
+	    while (pos[0])
+		if (!strncmp (pos, "GDK_", 4))
+		{
+		    pos += 4;
+		    if (!strncmp (pos, "SHIFT_MASK", 10))
+		    {
+			info.modifiers |= GDK_SHIFT_MASK;
+			pos += 10;
+		    }
+		    else if (!strncmp (pos, "LOCK_MASK", 9))
+		    {
+			info.modifiers |= GDK_LOCK_MASK;
+			pos += 9;
+		    }
+		    else if (!strncmp (pos, "CONTROL_MASK", 12))
+		    {
+			info.modifiers |= GDK_CONTROL_MASK;
+			pos += 12;
+		    }
+		    else if (!strncmp (pos, "MOD", 3) &&
+			     !strncmp (pos+4, "_MASK", 5))
+		    {
+			switch (pos[3])
+			{
+			case '1':
+			    info.modifiers |= GDK_MOD1_MASK; break;
+			case '2':
+			    info.modifiers |= GDK_MOD2_MASK; break;
+			case '3':
+			    info.modifiers |= GDK_MOD3_MASK; break;
+			case '4':
+			    info.modifiers |= GDK_MOD4_MASK; break;
+			case '5':
+			    info.modifiers |= GDK_MOD5_MASK; break;
+			}
+			pos += 9;
+		    }
+		    else if (!strncmp (pos, "BUTTON", 6) &&
+			     !strncmp (pos+7, "_MASK", 5))
+		    {
+			switch (pos[6])
+			{
+			case '1':
+			    info.modifiers |= GDK_BUTTON1_MASK; break;
+			case '2':
+			    info.modifiers |= GDK_BUTTON2_MASK; break;
+			case '3':
+			    info.modifiers |= GDK_BUTTON3_MASK; break;
+			case '4':
+			    info.modifiers |= GDK_BUTTON4_MASK; break;
+			case '5':
+			    info.modifiers |= GDK_BUTTON5_MASK; break;
+			}
+			pos += 12;
+		    }
+		    else if (!strncmp (pos, "RELEASE_MASK", 12))
+		    {
+			info.modifiers |= GDK_RELEASE_MASK;
+			pos += 12;
+		    }
+		    else
+			pos++;
+		}
+		else
+		    pos++;
+	}
+	else if (!strcmp (names[i], "signal"))
+	    info.signal = alloc_string (state->interface, values[i]);
+	else
+	    g_warning ("unknown attribute `%s' for <accelerator>.", names[i]);
+    }
+    if (info.key == 0 || info.signal == NULL)
+    {
+	g_warning ("required <accelerator> attributes missing!!!");
+	return;
+    }
+    if (!state->accels)
+	state->accels = g_array_new (FALSE, FALSE,
+				     sizeof (GtkBuilderAccelInfo));
+    g_array_append_val (state->accels, info);
+}
+
+static inline void
+handle_uis (GtkBuilderParseState *state, 
+	    const gchar    **names,
+	    const gchar    **values)
+{
+    GtkBuilderUIInfo info = { NULL };
+    gint i;
+
+    info.merge = TRUE;
+
+    for (i = 0; names && names[i] != NULL; i++)
+    {
+	if (!strcmp (names[i], "name"))
+	    info.name = alloc_string (state->interface, values[i]);
+	else if (!strcmp (names[i], "filename"))
+	    info.filename = alloc_string (state->interface, values[i]);
+	else if (!strcmp (names[i], "merge")) {
+	    if (!strcmp (values[i], "no"))
+		info.merge = FALSE;
+	    else if (strcmp (values[i], "yes"))
+		g_warning ("unknown value `%s' for merge attribute.",
+			   names[i]);
+
+	} else
+	    g_warning ("unknown attribute `%s' for <property>.",
+		       names[i]);
+    }
+
+    if (!state->uis)
+	state->uis = g_array_new (FALSE, FALSE,
+				  sizeof (GtkBuilderUIInfo));
+    g_array_append_val (state->uis, info);
+}
+
+static inline void
+handle_child (GtkBuilderParseState *state, 
+	      const gchar    **names,
+	      const gchar    **values)
+{
+    GtkBuilderChildInfo *info;
+    gint i;
+
+    /* make sure all of these are flushed */
+    flush_properties (state);
+    flush_signals (state);
+    flush_actions (state);
+    flush_relations (state);
+    flush_accels (state);
+
+    state->object->n_children++;
+    state->object->children = g_renew (GtkBuilderChildInfo, state->object->children,
+				       state->object->n_children);
+    info = &state->object->children[state->object->n_children-1];
+    info->internal_child = NULL;
+    info->properties = NULL;
+    info->n_properties = 0;
+    info->child = NULL;
+
+    for (i = 0; names && names[i] != NULL; i++)
+    {
+	if (!strcmp (names[i], "internal-child"))
+	    info->internal_child = alloc_string (state->interface, values[i]);
+	else
+	    g_warning ("unknown attribute `%s' for <child>.", names[i]);
+    }
+}
+
+static void
+gtk_builder_parser_start_document (GtkBuilderParseState *state)
+{
+    state->state = PARSER_START;
+
+    state->unknown_depth = 0;
+    state->prev_state = PARSER_UNKNOWN;
+
+    state->object_depth = 0;
+    state->content = g_string_sized_new (128);
+
+    state->interface = g_new0 (GtkBuilderInterface, 1);
+    state->interface->names = g_hash_table_new (g_str_hash, g_str_equal);
+    state->interface->strings = g_hash_table_new_full (g_str_hash,
+						       g_str_equal,
+						       (GDestroyNotify)g_free,
+						       NULL);
+    state->object = NULL;
+
+    state->prop_type = PROP_NONE;
+    state->prop_name = NULL;
+    state->translate_prop = FALSE;
+    state->props = NULL;
+
+    state->signals = NULL;
+    state->accels = NULL;
+}
+
+static void
+gtk_builder_parser_end_document (GtkBuilderParseState *state)
+{
+    g_string_free (state->content, TRUE);
+
+    if (state->unknown_depth != 0)
+	g_warning ("unknown_depth != 0 (%d)", state->unknown_depth);
+    if (state->object_depth != 0)
+	g_warning ("object_depth != 0 (%d)", state->object_depth);
+}
+
+static void
+gtk_builder_parser_start_element  (GMarkupParseContext *context,
+				   const gchar         *name,
+				   const gchar        **names,
+				   const gchar        **values,
+				   GtkBuilderParseState    *state,
+				   GError             **error)
+{     
+    int i;
+#ifdef G_ENABLE_DEBUG
+    int line, col;
+    g_markup_parse_context_get_position (context, &line, &col);
+    GTK_NOTE (BUILDER,
+	      g_message ("%d <%s:%d:%d> in state %s",
+			 state->object_depth,
+			 name, line, col, state_names[state->state]));
+#endif
+    
+    switch (state->state)
+    {
+    case PARSER_START:
+	if (!strcmp (name, "glade-interface"))
+	{
+	    state->state = PARSER_GLADE_INTERFACE;
+#if 0
+	    /* check for correct XML namespace */
+	    for (i = 0; names && names[i] != NULL; i ++)
+	    {
+		if (!strcmp (names[i], "xmlns") &&
+		    !strcmp (values[i], "...")) {
+		    g_warning ("bad XML namespace `%s'.", values[i]);
+		} else
+		    g_warning ("unknown attribute `%s' for <glade-interface>",
+			       names[i]);
+	    }
+#endif
+	}
+	else
+	{
+	    g_warning ("Expected <glade-interface>.  Got <%s>.", name);
+	    state->prev_state = state->state;
+	    state->state = PARSER_UNKNOWN;
+	    state->unknown_depth++;
+	}
+	break;
+    case PARSER_GLADE_INTERFACE:
+	if (!strcmp (name, "requires"))
+	{
+	    for (i = 0; names && names[i] != NULL; i++) {
+		if (!strcmp (names[i], "lib")) {
+		    GtkBuilderInterface *iface = state->interface;
+
+		    /* add to the list of requirements for this module */
+		    iface->n_requires++;
+		    iface->requires = g_renew (gchar *, iface->requires,
+					       iface->n_requires);
+		    iface->requires[iface->n_requires-1] =
+			alloc_string (iface, values[i]);
+		}
+		else
+		    g_warning ("unknown attribute `%s' for <requires>.",
+			       names[i]);
+	    }
+	    state->state = PARSER_REQUIRES;
+	}
+	else if (!strcmp (name, "object") || !strcmp (name, "widget"))
+	{
+	    GtkBuilderInterface *iface = state->interface;
+
+	    iface->n_toplevels++;
+	    iface->toplevels = g_renew (GtkBuilderObjectInfo *, iface->toplevels,
+					iface->n_toplevels);
+	    state->object = create_object_info (iface, names, values);
+	    iface->toplevels[iface->n_toplevels-1] = state->object;
+
+	    state->object_depth++;
+	    state->prop_type = PROP_NONE;
+	    state->prop_name = NULL;
+	    state->props = NULL;
+	    state->signals = NULL;
+	    state->accels = NULL;
+
+	    state->state = PARSER_OBJECT;
+	}
+	else
+	{
+	    g_warning ("Unexpected element <%s> inside <glade-interface>.",
+		       name);
+	    state->prev_state = state->state;
+	    state->state = PARSER_UNKNOWN;
+	    state->unknown_depth++;
+	}
+	break;
+    case PARSER_REQUIRES:
+	g_warning ("<requires> element should be empty.  Found <%s>.", name);
+	state->prev_state = state->state;
+	state->state = PARSER_UNKNOWN;
+	state->unknown_depth++;
+	break;
+    case PARSER_OBJECT:
+	if (!strcmp (name, "property"))
+	{
+	    gboolean bad_agent = FALSE;
+
+	    if (state->prop_type != PROP_NONE &&
+		state->prop_type != PROP_OBJECT)
+		g_warning ("non object properties defined here (oh no!)");
+	    state->translate_prop = FALSE;
+	    for (i = 0; names && names[i] != NULL; i++)
+	    {
+		if (!strcmp (names[i], "name"))
+		    state->prop_name = alloc_propname (state->interface, values[i]);
+		else if (!strcmp (names[i], "translatable"))
+		    state->translate_prop = !strcmp (values[i], "yes");
+		else if (!strcmp (names[i], "context")) 
+		    state->context_prop = !strcmp (values[i], "yes");
+		else if (!strcmp (names[i], "agent"))
+		    bad_agent = strcmp (names[i], "libglade") != 0;
+		else if (!strcmp (names[i], "comments"))
+		    /* Do nothing. */;
+		else
+		    g_warning ("unknown attribute `%s' for <property>.", names[i]);
+	    }
+	    if (bad_agent)
+	    {
+		/* ignore the property ... */
+		state->prev_state = state->state;
+		state->state = PARSER_UNKNOWN;
+		state->unknown_depth++;
+	    }
+	    else
+	    {
+		state->prop_type = PROP_OBJECT;
+		state->state = PARSER_OBJECT_PROPERTY;
+	    }
+	}
+	else if (!strcmp (name, "accessibility"))
+	{
+	    flush_properties (state);
+
+	    if (names != NULL && names[0] != NULL)
+		g_warning ("<accessibility> element should have no attributes");
+	    state->state = PARSER_OBJECT_ATK;
+	}
+	else if (!strcmp (name, "signal"))
+	{
+	    handle_signal (state, names, values);
+	    state->state = PARSER_OBJECT_SIGNAL;
+	}
+	else if (!strcmp (name, "accelerator"))
+	{
+	    handle_accel (state, names, values);
+	    state->state = PARSER_OBJECT_ACCEL;
+	}
+	else if (!strcmp (name, "child"))
+	{
+	    handle_child (state, names, values);
+	    state->state = PARSER_OBJECT_CHILD;
+	}
+	else if (!strcmp (name, "layout"))
+	{
+	    flush_properties (state);
+	    state->state = PARSER_OBJECT_LAYOUT;
+	}
+	else if (!strcmp (name, "ui-state"))
+	{
+	    handle_uis (state, names, values);
+	    state->state = PARSER_OBJECT_UI_STATE;
+	}
+	else
+	{
+	    g_warning ("Unexpected element <%s> inside <object>.", name);
+	    state->prev_state = state->state;
+	    state->state = PARSER_UNKNOWN;
+	    state->unknown_depth++;
+	}
+	break;
+    case PARSER_OBJECT_PROPERTY:
+	g_warning ("<property> element should be empty.  Found <%s>.", name);
+	state->prev_state = state->state;
+	state->state = PARSER_UNKNOWN;
+	state->unknown_depth++;
+	break;
+    case PARSER_OBJECT_ATK:
+	if (!strcmp (name, "atkproperty"))
+	{
+	    if (state->prop_type != PROP_NONE &&
+		state->prop_type != PROP_ATK)
+		g_warning ("non atk properties defined here (oh no!)");
+	    state->prop_type = PROP_ATK;
+	    state->translate_prop = FALSE;
+	    for (i = 0; names && names[i] != NULL; i++)
+	    {
+		if (!strcmp (names[i], "name"))
+		    state->prop_name = alloc_propname (state->interface,
+						       values[i]);
+		else if (!strcmp (names[i], "translatable"))
+		    state->translate_prop = !strcmp (values[i], "yes");
+		else if (!strcmp (names[i], "context"))
+		    state->context_prop = !strcmp (values[i], "yes");
+		else if (!strcmp (names[i], "comments"))
+		    /* Do nothing. */;
+		else
+		    g_warning ("unknown attribute `%s' for <atkproperty>.",
+			       names[i]);
+	    }
+	    state->state = PARSER_OBJECT_ATK_PROPERTY;
+	}
+	else if (!strcmp (name, "atkaction"))
+	{
+	    handle_atk_action (state, names, values);
+	    state->state = PARSER_OBJECT_ATK_ACTION;
+	}
+	else if (!strcmp (name, "atkrelation"))
+	{
+	    handle_atk_relation (state, names, values);
+	    state->state = PARSER_OBJECT_ATK_RELATION;
+	}
+	else
+	{
+	    g_warning ("Unexpected element <%s> inside <accessibility>.", name);
+	    state->prev_state = state->state;
+	    state->state = PARSER_UNKNOWN;
+	    state->unknown_depth++;
+	}
+	break;
+    case PARSER_OBJECT_ATK_PROPERTY:
+	if (!strcmp (name, "accessibility"))
+	{
+	    state->state = PARSER_OBJECT_ATK;
+	}
+	else
+	{
+	    g_warning ("Unexpected element <%s> inside <atkproperty>.", name);
+	    state->prev_state = state->state;
+	    state->state = PARSER_UNKNOWN;
+	    state->unknown_depth++;
+	}
+	break;
+    case PARSER_OBJECT_ATK_ACTION:
+	g_warning ("<atkaction> element should be empty.  Found <%s>.", name);
+	state->prev_state = state->state;
+	state->state = PARSER_UNKNOWN;
+	state->unknown_depth++;
+	break;
+    case PARSER_OBJECT_ATK_RELATION:
+	g_warning ("<atkrelation> element should be empty.  Found <%s>.", name);
+	state->prev_state = state->state;
+	state->state = PARSER_UNKNOWN;
+	state->unknown_depth++;
+	break;
+    case PARSER_OBJECT_AFTER_ATK:
+	if (!strcmp (name, "signal"))
+	{
+	    handle_signal (state, names, values);
+	    state->state = PARSER_OBJECT_SIGNAL;
+	}
+	else if (!strcmp (name, "accelerator"))
+	{
+	    handle_accel (state, names, values);
+	    state->state = PARSER_OBJECT_ACCEL;
+	}
+	else if (!strcmp (name, "child"))
+	{
+	    handle_child (state, names, values);
+	    state->state = PARSER_OBJECT_CHILD;
+	}
+	else if (!strcmp (name, "layout"))
+	{
+	    state->state = PARSER_OBJECT_LAYOUT;
+	}
+	else {
+	    g_warning ("Unexpected element <%s> inside <object>.", name);
+	    state->prev_state = state->state;
+	    state->state = PARSER_UNKNOWN;
+	    state->unknown_depth++;
+	}
+	break;
+    case PARSER_OBJECT_SIGNAL:
+	g_warning ("<signal> element should be empty.  Found <%s>.", name);
+	state->prev_state = state->state;
+	state->state = PARSER_UNKNOWN;
+	state->unknown_depth++;
+	break;
+    case PARSER_OBJECT_AFTER_SIGNAL:
+	if (!strcmp (name, "accelerator"))
+	{
+	    handle_accel (state, names, values);
+	    state->state = PARSER_OBJECT_ACCEL;
+	}
+	else if (!strcmp (name, "child"))
+	{
+	    handle_child (state, names, values);
+	    state->state = PARSER_OBJECT_CHILD;
+	}
+	else if (!strcmp (name, "ui-state"))
+	{
+	    handle_uis (state, names, values);
+	    state->state = PARSER_OBJECT_UI_STATE;
+	}
+	else if (!strcmp (name, "layout"))
+	{
+	    state->state = PARSER_OBJECT_LAYOUT;
+	}
+	else {
+	    g_warning ("Unexpected element <%s> inside <object>.", name);
+	    state->prev_state = state->state;
+	    state->state = PARSER_UNKNOWN;
+	    state->unknown_depth++;
+	}
+	break;
+    case PARSER_OBJECT_ACCEL:
+	g_warning ("<accelerator> element should be empty.  Found <%s>.", name);
+	state->prev_state = state->state;
+	state->state = PARSER_UNKNOWN;
+	state->unknown_depth++;
+	break;
+    case PARSER_OBJECT_AFTER_ACCEL:
+	if (!strcmp (name, "child"))
+	{
+	    handle_child (state, names, values);
+	    state->state = PARSER_OBJECT_CHILD;
+	}
+	else if (!strcmp (name, "ui-state"))
+	{
+	    handle_uis (state, names, values);
+	    state->state = PARSER_OBJECT_UI_STATE;
+	}
+	else if (!strcmp (name, "layout"))
+	{
+	    state->state = PARSER_OBJECT_LAYOUT;
+	}
+	else {
+	    g_warning ("Unexpected element <%s> inside <object>.", name);
+	    state->prev_state = state->state;
+	    state->state = PARSER_UNKNOWN;
+	    state->unknown_depth++;
+	}
+	break;
+    case PARSER_OBJECT_CHILD:
+	if (!strcmp (name, "object") || !strcmp (name, "widget"))
+	{
+	    GtkBuilderObjectInfo *parent = state->object;
+	    GtkBuilderChildInfo *info = &parent->children[parent->n_children-1];
+
+	    if (info->child)
+		g_warning ("object pointer already set!! not good");
+
+	    state->object = create_object_info (state->interface, names, values);
+	    info->child = state->object;
+	    info->child->parent = parent;
+
+	    state->object_depth++;
+	    state->prop_type = PROP_NONE;
+	    state->prop_name = NULL;
+	    state->props = NULL;
+	    state->signals = NULL;
+	    state->accels = NULL;
+
+	    state->state = PARSER_OBJECT;
+	}
+	else if (!strcmp (name, "ui-state"))
+	{
+	    handle_uis (state, names, values);
+	    state->state = PARSER_OBJECT_UI_STATE;
+	}
+	else if (!strcmp (name, "placeholder"))
+	{
+	    /* this isn't a real child, so knock off  the last ChildInfo */
+	    state->object->n_children--;
+	    state->state = PARSER_OBJECT_CHILD_PLACEHOLDER;
+	}
+	else
+	{
+	    g_warning ("Unexpected element <%s> inside <child>.", name);
+	    state->prev_state = state->state;
+	    state->state = PARSER_UNKNOWN;
+	    state->unknown_depth++;
+	}
+	break;
+    case PARSER_OBJECT_CHILD_AFTER_OBJECT:
+	if  (!strcmp (name, "packing"))
+	{
+	    state->state = PARSER_OBJECT_CHILD_PACKING;
+	}
+	else
+	{
+	    g_warning ("Unexpected element <%s> inside <child>.", name);
+	    state->prev_state = state->state;
+	    state->state = PARSER_UNKNOWN;
+	    state->unknown_depth++;
+	}
+	break;
+    case PARSER_OBJECT_CHILD_PACKING:
+	if (!strcmp (name, "property"))
+	{
+	    gboolean bad_agent = FALSE;
+
+	    if (state->prop_type != PROP_NONE &&
+		state->prop_type != PROP_CHILD)
+		g_warning ("non child properties defined here (oh no!)");
+	    state->translate_prop = FALSE;
+	    for (i = 0; names && names[i] != NULL; i++)
+	    {
+		if (!strcmp (names[i], "name"))
+		    state->prop_name = alloc_propname (state->interface,
+						       values[i]);
+		else if (!strcmp (names[i], "translatable"))
+		    state->translate_prop = !strcmp (values[i], "yes");
+		else if (!strcmp (names[i], "context"))
+		    state->context_prop = !strcmp (values[i], "yes");
+		else if (!strcmp (names[i], "agent"))
+		    bad_agent = strcmp (values[i], "libglade") != 0;
+		else if (!strcmp (names[i], "comments"))
+		    /* Do nothing. */;
+		else
+		    g_warning ("unknown attribute `%s' for <property>.",
+			       names[i]);
+	    }
+	    if (bad_agent)
+	    {
+		/* ignore the property ... */
+		state->prev_state = state->state;
+		state->state = PARSER_UNKNOWN;
+		state->unknown_depth++;
+	    }
+	    else
+	    {
+		state->prop_type = PROP_CHILD;
+		state->state = PARSER_OBJECT_CHILD_PACKING_PROPERTY;
+	    }
+	}
+	else
+	{
+	    g_warning ("Unexpected element <%s> inside <child>.", name);
+	    state->prev_state = state->state;
+	    state->state = PARSER_UNKNOWN;
+	    state->unknown_depth++;
+	}
+	break;
+    case PARSER_OBJECT_CHILD_PACKING_PROPERTY:
+	g_warning ("<property> element should be empty.  Found <%s>.", name);
+	state->prev_state = state->state;
+	state->state = PARSER_UNKNOWN;
+	state->unknown_depth++;
+	break;
+    case PARSER_OBJECT_CHILD_AFTER_PACKING:
+	g_warning ("<child> should have no elements after <packing>.  Found <%s>.", name);
+	state->prev_state = state->state;
+	state->state = PARSER_UNKNOWN;
+	state->unknown_depth++;
+	break;
+    case PARSER_OBJECT_CHILD_PLACEHOLDER:
+	g_warning ("<placeholder> should be empty.  Found <%s>.", name);
+	state->prev_state = state->state;
+	state->state = PARSER_UNKNOWN;
+	state->unknown_depth++;
+	break;
+    case PARSER_OBJECT_CHILD_AFTER_PLACEHOLDER:
+	/* this is a placeholder <child> element -- ignore extra elements */
+	state->prev_state = state->state;
+	state->state = PARSER_UNKNOWN;
+	state->unknown_depth++;
+	break;
+    case PARSER_OBJECT_LAYOUT:
+	if (!strcmp (name, "property"))
+	{
+	    gboolean bad_agent = FALSE;
+
+	    if (state->prop_type != PROP_NONE &&
+		state->prop_type != PROP_LAYOUT)
+		g_warning ("non layout properties defined here (oh no!)");
+	    state->translate_prop = FALSE;
+	    for (i = 0; names && names[i] != NULL; i++)
+	    {
+		if (!strcmp (names[i], "name"))
+		    state->prop_name = alloc_propname (state->interface,
+						       values[i]);
+		else if (!strcmp (names[i], "translatable"))
+		    state->translate_prop = !strcmp (values[i], "yes");
+		else if (!strcmp (names[i], "context"))
+		    state->context_prop = !strcmp (values[i], "yes");
+		else if (!strcmp (names[i], "agent"))
+		    bad_agent = strcmp (values[i], "libglade") != 0;
+		else if (!strcmp (names[i], "comments"))
+		    /* Do nothing. */;
+		else
+		    g_warning ("unknown attribute `%s' for <property>.",
+			       names[i]);
+	    }
+	    if (bad_agent)
+	    {
+		/* ignore the property ... */
+		state->prev_state = state->state;
+		state->state = PARSER_UNKNOWN;
+		state->unknown_depth++;
+	    }
+	    else
+	    {
+		state->prop_type = PROP_LAYOUT;
+		state->state = PARSER_OBJECT_LAYOUT_PROPERTY;
+	    }
+	}
+	else
+	{
+	    g_warning ("Unexpected element <%s> inside <layout>.", name);
+	    state->prev_state = state->state;
+	    state->state = PARSER_UNKNOWN;
+	    state->unknown_depth++;
+	}
+	break;
+    case PARSER_OBJECT_LAYOUT_PROPERTY:
+	g_warning ("<property> element should be empty.  Found <%s>.", name);
+	state->prev_state = state->state;
+	state->state = PARSER_UNKNOWN;
+	state->unknown_depth++;
+	break;
+    case PARSER_OBJECT_AFTER_LAYOUT:
+	state->prev_state = state->state;
+	state->state = PARSER_UNKNOWN;
+	state->unknown_depth++;
+	break;
+    case PARSER_OBJECT_UI_STATE:
+	state->prev_state = state->state;
+	state->state = PARSER_UNKNOWN;
+	state->unknown_depth++;
+	break;
+    case PARSER_FINISH:
+	g_warning ("There should be no elements here.  Found <%s>.", name);
+	state->prev_state = state->state;
+	state->state = PARSER_UNKNOWN;
+	state->unknown_depth++;
+	break;
+    case PARSER_UNKNOWN:
+	state->unknown_depth++;
+	break;
+    }
+    /* truncate the content string ... */
+    g_string_truncate (state->content, 0);
+}
+
+static void
+gtk_builder_parser_end_element (GMarkupParseContext *context,
+				const gchar         *name,
+				GtkBuilderParseState     *state,
+				GError             **error)
+{
+    GtkBuilderProperty prop;
+#ifdef G_ENABLE_DEBUG
+    int line, col;
+    g_markup_parse_context_get_position (context, &line, &col);
+    GTK_NOTE (BUILDER,
+	      g_message ("%d </%s:%d:%d> in state %s",
+			 state->object_depth,
+			 name, line, col, state_names[state->state]));
+#endif
+    
+    switch (state->state) {
+    case PARSER_START:
+	g_warning ("should not be closing any elements in this state");
+	break;
+    case PARSER_GLADE_INTERFACE:
+	if (strcmp (name, "glade-interface") != 0)
+	    g_warning ("should find </glade-interface> here.  Found </%s>",
+		       name);
+	state->state = PARSER_FINISH;
+	break;
+    case PARSER_REQUIRES:
+	if (strcmp (name, "requires") != 0)
+	    g_warning ("should find </requires> here.  Found </%s>", name);
+	state->state = PARSER_GLADE_INTERFACE;
+	break;
+    case PARSER_OBJECT:
+    case PARSER_OBJECT_AFTER_ATK:
+    case PARSER_OBJECT_AFTER_SIGNAL:
+    case PARSER_OBJECT_AFTER_ACCEL:
+	if (strcmp (name, "object") != 0  && strcmp (name, "widget") != 0)
+	    g_warning ("should find </object> here.  Found </%s>", name);
+	flush_properties (state);
+	flush_signals (state);
+	flush_actions (state);
+	flush_relations (state);
+	flush_accels (state);
+	flush_uis (state);
+	state->object = state->object->parent;
+	state->object_depth--;
+
+	if (state->object_depth == 0)
+	    state->state = PARSER_GLADE_INTERFACE;
+	else
+	    state->state = PARSER_OBJECT_CHILD_AFTER_OBJECT;
+	break;
+    case PARSER_OBJECT_PROPERTY:
+	if (strcmp (name, "property") != 0)
+	    g_warning ("should find </property> here.  Found </%s>", name);
+	if (!state->props)
+	    state->props = g_array_new (FALSE, FALSE, sizeof (GtkBuilderProperty));
+	prop.name = state->prop_name;
+ 	if (state->translate_prop && state->content->str[0] != '\0')
+	{
+	    if (state->context_prop) 
+		prop.value = alloc_string (state->interface,
+					   g_strip_context (state->content->str,
+							    dgettext (state->domain, state->content->str)));
+	    else
+		prop.value = alloc_string (state->interface,
+					   dgettext (state->domain, state->content->str));
+ 	}
+	else
+	{
+ 	    prop.value = alloc_string (state->interface, state->content->str);
+  	}
+	g_array_append_val (state->props, prop);
+	state->prop_name = NULL;
+	state->state = PARSER_OBJECT;
+	break;
+    case PARSER_OBJECT_ATK:
+	if (strcmp (name, "accessibility") != 0)
+	    g_warning ("should find </accessibility> here.  Found </%s>", name);
+	flush_properties (state); /* flush the ATK properties */
+	state->state = PARSER_OBJECT_AFTER_ATK;
+	break;
+    case PARSER_OBJECT_ATK_PROPERTY:
+	if (strcmp (name, "atkproperty") != 0)
+	    g_warning ("should find </atkproperty> here.  Found </%s>", name);
+	if (!state->props)
+	    state->props = g_array_new (FALSE, FALSE, sizeof (GtkBuilderProperty));
+	prop.name = state->prop_name;
+ 	if (state->translate_prop && state->content->str[0] != '\0')
+	{
+	    if (state->context_prop) 
+		prop.value = alloc_string (state->interface,
+					   g_strip_context (state->content->str,
+							    dgettext (state->domain, state->content->str)));
+	    else
+		prop.value = alloc_string (state->interface,
+					   dgettext (state->domain, state->content->str));
+ 	}
+	else
+	{
+ 	    prop.value = alloc_string (state->interface, state->content->str);
+  	}
+	g_array_append_val (state->props, prop);
+	state->prop_name = NULL;
+	state->state = PARSER_OBJECT_ATK;
+	break;
+    case PARSER_OBJECT_ATK_ACTION:
+	if (strcmp (name, "atkaction") != 0)
+	    g_warning ("should find </atkaction> here.  Found </%s>", name);
+        state->prop_name = NULL;
+        state->state = PARSER_OBJECT_ATK;
+        break;
+    case PARSER_OBJECT_ATK_RELATION:
+	if (strcmp (name, "atkrelation") != 0)
+	    g_warning ("should find </atkrelation> here.  Found </%s>", name);
+        state->prop_name = NULL;
+        state->state = PARSER_OBJECT_ATK;
+        break;
+    case PARSER_OBJECT_SIGNAL:
+	if (strcmp (name, "signal") != 0)
+	    g_warning ("should find </signal> here.  Found </%s>", name);
+	state->state = PARSER_OBJECT_AFTER_ATK;
+	break;
+    case PARSER_OBJECT_ACCEL:
+	if (strcmp (name, "accelerator") != 0)
+	    g_warning ("should find </accelerator> here.  Found </%s>", name);
+	state->state = PARSER_OBJECT_AFTER_SIGNAL;
+	break;
+    case PARSER_OBJECT_CHILD:
+	if (strcmp (name, "child") != 0)
+	    g_warning ("should find </child> here.  Found </%s>", name);
+	/* if we are ending the element in this state, then there
+	 * hasn't been a <object> element inside this <child>
+	 * element. (If there was, then we would be in
+	 * PARSER_OBJECT_CHILD_AFTER_OBJECT state. */
+	g_warning ("no <object> element found inside <child>.  Discarding");
+	g_free (state->object->children[
+		    state->object->n_children-1].properties);
+	state->object->n_children--;
+	state->state = PARSER_OBJECT_AFTER_ACCEL;
+	break;
+    case PARSER_OBJECT_CHILD_AFTER_OBJECT:
+	if (strcmp (name, "child") != 0)
+	    g_warning ("should find </child> here.  Found </%s>", name);
+	state->state = PARSER_OBJECT_AFTER_ACCEL;
+	break;
+    case PARSER_OBJECT_CHILD_PACKING:
+	if (strcmp (name, "packing") != 0)
+	    g_warning ("should find </packing> here.  Found </%s>", name);
+	state->state = PARSER_OBJECT_CHILD_AFTER_PACKING;
+	flush_properties (state); /* flush the properties. */
+	break;
+    case PARSER_OBJECT_CHILD_PACKING_PROPERTY:
+	if (strcmp (name, "property") != 0)
+	    g_warning ("should find </property> here.  Found </%s>", name);
+	if (!state->props)
+	    state->props = g_array_new (FALSE, FALSE, sizeof (GtkBuilderProperty));
+	prop.name = state->prop_name;
+ 	if (state->translate_prop && state->content->str[0] != '\0')
+	{
+	    if (state->context_prop) 
+		prop.value = alloc_string (state->interface,
+					   g_strip_context (state->content->str,
+							    dgettext (state->domain, state->content->str)));
+	    else
+		prop.value = alloc_string (state->interface,
+					   dgettext (state->domain, state->content->str));
+ 	}
+	else
+	{
+ 	    prop.value = alloc_string (state->interface, state->content->str);
+  	}
+	g_array_append_val (state->props, prop);
+	state->prop_name = NULL;
+	state->state = PARSER_OBJECT_CHILD_PACKING;
+	break;
+    case PARSER_OBJECT_CHILD_AFTER_PACKING:
+	if (strcmp (name, "child") != 0)
+	    g_warning ("should find </child> here.  Found </%s>", name);
+	state->state = PARSER_OBJECT_AFTER_ACCEL;
+	break;
+    case PARSER_OBJECT_CHILD_PLACEHOLDER:
+	if (strcmp (name, "placeholder") != 0)
+	    g_warning ("should find </placeholder> here.  Found </%s>", name);
+	state->state = PARSER_OBJECT_CHILD_AFTER_PLACEHOLDER;
+	break;
+    case PARSER_OBJECT_CHILD_AFTER_PLACEHOLDER:
+	if (strcmp (name, "child") != 0)
+	    g_warning ("should find </child> here.  Found </%s>", name);
+	state->state = PARSER_OBJECT_AFTER_ACCEL;
+	break;
+    case PARSER_OBJECT_LAYOUT:
+	if (strcmp (name, "layout") != 0)
+	    g_warning ("should find </layout> here.  Found </%s>", name);
+	state->state = PARSER_OBJECT_AFTER_ACCEL;
+	flush_properties (state); /* flush the properties. */
+	break;
+    case PARSER_OBJECT_LAYOUT_PROPERTY:
+	if (strcmp (name, "property") != 0)
+	    g_warning ("should find </property> here.  Found </%s>", name);
+	if (!state->props)
+	    state->props = g_array_new (FALSE, FALSE, sizeof (GtkBuilderProperty));
+	prop.name = state->prop_name;
+ 	if (state->translate_prop && state->content->str[0] != '\0')
+	{
+	    if (state->context_prop) 
+		prop.value = alloc_string (state->interface,
+					   g_strip_context (state->content->str,
+							    dgettext (state->domain, state->content->str)));
+	    else
+		prop.value = alloc_string (state->interface,
+					   dgettext (state->domain, state->content->str));
+ 	}
+	else
+	{
+ 	    prop.value = alloc_string (state->interface, state->content->str);
+  	}
+	g_array_append_val (state->props, prop);
+	state->prop_name = NULL;
+	state->state = PARSER_OBJECT_AFTER_LAYOUT;
+	break;
+    case PARSER_OBJECT_AFTER_LAYOUT:
+	if (strcmp (name, "layout") != 0)
+	    g_warning ("should find </layout> here.  Found </%s>", name);
+	state->state = PARSER_OBJECT_AFTER_ACCEL;
+	flush_properties (state); /* flush the properties. */
+	break;
+    case PARSER_OBJECT_UI_STATE:
+    {
+	GtkBuilderUIInfo *info;
+	info = &g_array_index (state->uis, GtkBuilderUIInfo, state->uis->len-1);
+	info->data = alloc_string (state->interface, state->content->str);
+	info->data_len = state->content->len;
+
+	state->state = PARSER_OBJECT_AFTER_ACCEL;
+    }
+    break;
+    case PARSER_FINISH:
+	g_warning ("should not be closing any elements in this state");
+	break;
+    case PARSER_UNKNOWN:
+	state->unknown_depth--;
+	if (state->unknown_depth == 0)
+	    state->state = state->prev_state;
+	break;
+    }
+}
+
+static void
+gtk_builder_parser_text (GMarkupParseContext *context,
+			 const gchar         *chars,
+			 gsize                len,
+			 GtkBuilderParseState     *state,
+			 GError             **error)
+{
+    switch (state->state)
+    {
+    case PARSER_OBJECT_PROPERTY:
+    case PARSER_OBJECT_ATK_PROPERTY:
+    case PARSER_OBJECT_CHILD_PACKING_PROPERTY:
+    case PARSER_OBJECT_LAYOUT_PROPERTY:
+	g_string_append_len (state->content, chars, len);
+	break;
+    default:
+	/* don't care about content in any other states */
+	break;
+    }
+}
+
+static void 
+passthrough_handler (GMarkupParseContext *context,
+		     const gchar         *text,
+		     gsize                text_len,  
+		     GtkBuilderParseState     *state,
+		     GError             **error)
+{
+    
+    switch (state->state)
+    {
+    case PARSER_OBJECT_UI_STATE:
+	if (strncmp (text, "<![CDATA[", 9) == 0)
+	{
+	    g_string_append_len (state->content, text + 9, text_len - 12);	    
+	}
+    default:
+	/* don't care about content in any other states */
+	break;
+    }
+}
+
+static void
+gtk_builder_parser_error(GMarkupParseContext *context,
+			 GError              *error,
+			 GtkBuilderParseState     *state)
+{
+    g_log ("GMarkup", G_LOG_LEVEL_CRITICAL, error->message);
+}
+  
+static GMarkupParser gtk_builder_parser =
+{
+    (void*) gtk_builder_parser_start_element, /* startElement */
+    (void*) gtk_builder_parser_end_element, /* endElement */
+    (void*) gtk_builder_parser_text,  /* characters */
+    (void*) passthrough_handler,  /* CDATA etc */
+    (void*) gtk_builder_parser_error, /* error */
+};
+
+static void
+object_info_free (GtkBuilderObjectInfo *info)
+{
+    gint i;
+
+    g_return_if_fail (info != NULL);
+
+    g_free (info->properties);
+    g_free (info->atk_props);
+    g_free (info->layout_props);
+    g_free (info->signals);
+    g_free (info->atk_actions);
+    g_free (info->relations);
+    g_free (info->accels);
+    g_free (info->uis);
+
+    for (i = 0; i < info->n_children; i++)
+    {
+	g_free (info->children[i].properties);
+	object_info_free (info->children[i].child);
+    }
+    g_free (info->children);
+    g_free (info);
+}
+
+/**
+ * gtk_builder_interface_destroy
+ * @interface: the GtkBuilderInterface structure.
+ *
+ * Frees a GtkBuilderInterface structure.
+ */
+void
+gtk_builder_interface_destroy (GtkBuilderInterface *interface)
+{
+    gint i;
+
+    g_return_if_fail (interface != NULL);
+
+    /* free requirements */
+    g_free (interface->requires);
+
+    for (i = 0; i < interface->n_toplevels; i++)
+	object_info_free (interface->toplevels[i]);
+    g_free (interface->toplevels);
+
+    g_hash_table_destroy (interface->names);
+
+    /* free the strings hash table.  The destroy notify will take care
+     * of the strings. */
+    g_hash_table_destroy (interface->strings);
+
+    g_free (interface);
+}
+
+/**
+ * gtk_builder_parser_parse_file
+ * @file: the filename of the gtk_builder XML file.
+ * @domain: the translation domain for the XML file.
+ *
+ * This function parses a GtkBuilder XML interface file to a GtkBuilderInterface
+ * object (which is GtkBuilder's internal representation of the
+ * interface data).
+ *
+ * Generally, user code won't need to call this function.  Instead, it
+ * should go through the GtkBuilder interfaces.
+ *
+ * Returns: the GtkBuilderInterface structure for the XML file.
+ */
+GtkBuilderInterface *
+gtk_builder_parser_parse_file (const gchar *file, const gchar *domain)
+{
+    GtkBuilderParseState state = { 0 };
+    GMarkupParseContext *context;
+    gchar *buffer;
+    gsize len;
+    
+    if (!g_file_test (file, G_FILE_TEST_IS_REGULAR))
+    {
+	g_warning ("could not open file '%s'", file);
+	return NULL;
+    }
+
+    state.interface = NULL;
+    if (domain)
+	state.domain = domain;
+    else
+	state.domain = textdomain (NULL);
+
+
+    context = g_markup_parse_context_new (&gtk_builder_parser, 0, &state, NULL);
+    
+    if (g_file_get_contents (file, &buffer, &len, NULL))
+    {
+	gtk_builder_parser_start_document (&state);
+	g_markup_parse_context_parse (context, buffer, len, NULL);
+	gtk_builder_parser_end_document (&state);
+    }
+
+    g_markup_parse_context_free (context);
+    g_free (buffer);
+    
+    if (state.state != PARSER_FINISH)
+    {
+	g_warning ("did not finish in PARSER_FINISH state");
+	if (state.interface)
+	    gtk_builder_interface_destroy (state.interface);
+	return NULL;
+    }
+
+    return state.interface;
+}
+
+/**
+ * gtk_builder_parser_parse_buffer
+ * @buffer: a buffer in memory containing XML data.
+ * @len: the length of @buffer.
+ * @domain: the translation domain for the XML file.
+ *
+ * This function is similar to gtk_builder_parser_parse_file, except that it
+ * parses XML data from a buffer in memory.  This could be used to
+ * embed an interface into the executable, for instance.
+ *
+ * Generally, user code won't need to call this function.  Instead, it
+ * should go through the GtkBuilder interfaces.
+ *
+ * Returns: the GtkBuilderInterface structure for the XML buffer.
+ */
+GtkBuilderInterface *
+gtk_builder_parser_parse_buffer (const gchar *buffer, gint len, const gchar *domain)
+{
+    GtkBuilderParseState state = { 0 };
+    GMarkupParseContext *context;
+
+    state.interface = NULL;
+    if (domain)
+	state.domain = domain;
+    else
+	state.domain = textdomain (NULL);
+
+    context = g_markup_parse_context_new (&gtk_builder_parser, 0, &state, NULL);
+    
+    gtk_builder_parser_start_document (&state);
+    g_markup_parse_context_parse (context, buffer, len, NULL);
+    gtk_builder_parser_end_document (&state);
+
+    g_markup_parse_context_free (context);
+    
+    if (state.state != PARSER_FINISH)
+    {
+	g_warning ("did not finish in PARSER_FINISH state!");
+	if (state.interface)
+	    gtk_builder_interface_destroy (state.interface);
+	return NULL;
+    }
+    return state.interface;
+}
+
+
+/**
+ * gtk_builder_interface_dump
+ * @interface: the GtkBuilderInterface
+ * @filename: the filename to write the interface data to.
+ *
+ * This function dumps the contents of a GtkBuilderInterface into a file as
+ * XML.  It is intended mainly as a debugging tool.
+ */
+void
+gtk_builder_interface_dump (GtkBuilderInterface *interface,
+			    const gchar         *filename)
+{
+}
Index: gtk/gtkbuilderparser.h
===================================================================
RCS file: gtk/gtkbuilderparser.h
diff -N gtk/gtkbuilderparser.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gtk/gtkbuilderparser.h	6 Apr 2005 19:26:50 -0000
@@ -0,0 +1,153 @@
+/* -*- Mode: C; c-file-style: "gnu" -*- */
+/* libglade - a library for building interfaces from XML files at runtime
+ * Copyright (C) 1998-2005  James Henstridge <james@daa.com.au>
+ * Copyright (C) 2005  Johan Dahlin <johan@gnome.org>
+ *
+ * gtk-parser.h: functions for parsing glade-2.0 files
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA  02111-1307, USA.
+ */
+
+#ifndef __GTK_BUILDER_PARSER_H__
+#define __GTK_BUILDER_PARSER_H__
+
+#include <glib.h>
+#include <gdk/gdktypes.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GtkBuilderProperty GtkBuilderProperty;
+struct _GtkBuilderProperty
+{
+  gchar *name;
+  gchar *value;
+};
+
+typedef struct _GtkBuilderSignalInfo GtkBuilderSignalInfo;
+struct _GtkBuilderSignalInfo
+{
+  gchar *name;
+  gchar *handler;
+  gchar *object; /* NULL if this isn't a connect_object signal */
+  guint  after : 1;
+};
+
+typedef struct _GtkBuilderAtkActionInfo GtkBuilderAtkActionInfo;
+struct _GtkBuilderAtkActionInfo
+{
+  gchar *action_name;
+  gchar *description;
+};
+
+typedef struct _GtkBuilderAtkRelationInfo GtkBuilderAtkRelationInfo;
+struct _GtkBuilderAtkRelationInfo
+{
+  gchar *target;
+  gchar *type;
+};
+
+typedef struct _GtkBuilderAccelInfo GtkBuilderAccelInfo;
+struct _GtkBuilderAccelInfo
+{
+  guint            key;
+  GdkModifierType  modifiers;
+  gchar           *signal;
+};
+
+typedef struct _GtkBuilderUIInfo GtkBuilderUIInfo;
+struct _GtkBuilderUIInfo
+{
+  gchar *name;
+  gchar *filename;
+  gchar *data;
+  guint   data_len;
+  guint  merge : 1;
+};
+
+typedef struct _GtkBuilderObjectInfo GtkBuilderObjectInfo;
+typedef struct _GtkBuilderChildInfo GtkBuilderChildInfo;
+
+struct _GtkBuilderObjectInfo
+{
+  GtkBuilderObjectInfo      *parent;
+  
+  gchar                     *classname;
+  gchar                     *name;
+  gchar                     *constructor;
+  
+  GtkBuilderProperty        *properties;
+  guint                      n_properties;
+  
+  GtkBuilderProperty        *atk_props;
+  guint                      n_atk_props;
+  
+  GtkBuilderSignalInfo      *signals;
+  guint                      n_signals;
+  
+  GtkBuilderAtkActionInfo   *atk_actions;
+  guint                      n_atk_actions;
+  
+  GtkBuilderAtkRelationInfo *relations;
+  guint                      n_relations;
+  
+  GtkBuilderAccelInfo       *accels;
+  guint                      n_accels;
+  
+  GtkBuilderProperty        *layout_props;
+  guint                      n_layout_props;
+  
+  GtkBuilderUIInfo          *uis;
+  guint                      n_uis;
+
+  GtkBuilderChildInfo       *children;
+  guint                     n_children;
+};
+
+struct _GtkBuilderChildInfo
+{
+  GtkBuilderProperty   *properties;
+  guint                 n_properties;
+  
+  GtkBuilderObjectInfo *child;
+  gchar                *internal_child;
+};
+
+typedef struct _GtkBuilderInterface GtkBuilderInterface;
+struct _GtkBuilderInterface
+{
+  gchar                **requires;
+  guint                  n_requires;
+  
+  GtkBuilderObjectInfo **toplevels;
+  guint                  n_toplevels;
+  
+  GHashTable            *names;
+  GHashTable            *strings;
+};
+
+/* the actual functions ... */
+GtkBuilderInterface *gtk_builder_parser_parse_file   (const gchar         *file,
+						      const gchar         *domain);
+GtkBuilderInterface *gtk_builder_parser_parse_buffer (const gchar         *buffer,
+						      gint                 len,
+						      const gchar         *domain);
+void                 gtk_builder_interface_destroy   (GtkBuilderInterface *iface);
+void                 gtk_builder_interface_dump      (GtkBuilderInterface *iface,
+						      const gchar         *filename);
+
+G_END_DECLS
+
+#endif /* __GTK_BUILDER_PARSER_H__ */
Index: gtk/gtkbuilderprivate.h
===================================================================
RCS file: gtk/gtkbuilderprivate.h
diff -N gtk/gtkbuilderprivate.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gtk/gtkbuilderprivate.h	6 Apr 2005 19:26:50 -0000
@@ -0,0 +1,143 @@
+/* -*- Mode: C; c-file-style: "gnu" -*- */
+/* libglade - a library for building interfaces from XML files at runtime
+ * Copyright (C) 1998-2005  James Henstridge <james@daa.com.au>
+ * Copyright (C) 2005 Johan Dahlin <johan@gnome.org>
+ *
+ * gtkprivate.h: private data for gtk
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA  02111-1307, USA.
+ */
+
+#ifndef __GTK_BUILDER_PRIVATE_H__
+#define __GTK_BUILDER_PRIVATE_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gmodule.h>
+#include <gtk/gtkwidget.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtkaccelgroup.h>
+#include "gtkbuilder.h"
+#include "gtkbuilderparser.h"
+
+G_BEGIN_DECLS
+	
+typedef enum
+{
+  GTK_DEBUG_PARSER = 1 << 0,
+  GTK_DEBUG_BUILD  = 1 << 1
+} GtkBuilderDebugFlag;
+
+extern guint _gtk_debug_flags;
+
+/* the module dynamic loading interface ... */
+typedef struct _GtkBuilderSignalData GtkBuilderSignalData;
+struct _GtkBuilderSignalData
+{
+  GObject  *signal_object;
+  char     *signal_name;
+  char     *connect_object; /* or NULL if there is none */
+  gboolean  signal_after;
+};
+
+typedef struct _GtkBuilderDeferredProperty GtkBuilderDeferredProperty;
+struct _GtkBuilderDeferredProperty
+{
+  const gchar *target_name;
+  
+  enum
+    {
+      DEFERRED_PROP,
+      DEFERRED_REL
+    } type;
+  union
+  {
+    struct
+    {
+      GObject     *object;
+      const gchar *prop_name;
+    } prop;
+    struct
+    {
+      AtkRelationSet *relation_set;
+      AtkRelationType relation_type;
+    } rel;
+  } d;
+};
+
+struct _GtkBuilderPrivate {
+  /* the tree for this GtkBuilder */
+  GtkBuilderInterface *tree; 
+
+  /* if not NULL, holds all tooltip info */
+  GtkTooltips    *tooltips; 
+
+  /* hash tables of objects.  The keys are stored as object data,
+   * and get freed with those widgets. */
+  GHashTable     *name_hash;
+    
+  /* hash tables of ui manager ids. */
+  GHashTable     *ui_hash;
+
+  /* hash table of signals.  The Data is a GList of GtkSignalData
+   * structures which get freed when the GtkBuilder object is
+   * destroyed */
+  GHashTable     *signals;
+
+  /* the current toplevel being built */
+  GtkWindow      *toplevel;
+
+  /* the accel group to add accelerators to (not mnemonics) */
+  GtkAccelGroup  *accel_group;
+
+  /* these hold the focus and default widgets for a window until they
+   * get packed into the window -- we can't call gtk_widget_grab_focus
+   * or grab_default until this occurs */
+  GtkWidget      *focus_widget;
+  GtkWidget      *default_widget;
+
+  /* list of GObject properties waiting to be set.  (they couldn't
+   * be set earlier because the value object hadn't been created
+   * yet). */ 
+  GList          *deferred_props;
+};
+
+/* set the current toplevel object while building (use NULL to unset) */
+void       gtk_builder_set_toplevel(GtkBuilder  *builder,
+				    GtkWindow *window);
+
+/* make sure that builder->priv->accel_group is a valid AccelGroup */
+GtkAccelGroup *gtk_builder_ensure_accel(GtkBuilder *builder);
+
+void gtk_builder_handle_object_prop(GtkBuilder    *self,
+				    GObject     *object,
+				    const gchar *prop_name,
+				    const gchar *value_name);
+
+void gtk_builder_set_packing_property(GtkBuilder   *self,
+				      GtkWidget  *parent,
+				      GtkWidget  *child,
+				      const char *name,
+				      const char *value);
+
+gboolean gtk_builder_set_value_from_string(GtkBuilder    *builder,
+					   GParamSpec  *pspec,
+					   const gchar *string,
+					   GValue      *value);
+
+G_END_DECLS
+	
+#endif /* __GTK_BUILDER_PRIVATE_H__ */
Index: gtk/gtkdebug.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkdebug.h,v
retrieving revision 1.23
diff -u -r1.23 gtkdebug.h
--- gtk/gtkdebug.h	19 Oct 2004 18:45:41 -0000	1.23
+++ gtk/gtkdebug.h	6 Apr 2005 19:26:50 -0000
@@ -41,7 +41,8 @@
   GTK_DEBUG_MULTIHEAD   = 1 << 6,
   GTK_DEBUG_MODULES     = 1 << 7,
   GTK_DEBUG_GEOMETRY    = 1 << 8,
-  GTK_DEBUG_ICONTHEME   = 1 << 9
+  GTK_DEBUG_ICONTHEME   = 1 << 9,
+  GTK_DEBUG_BUILDER     = 1 << 10
 } GtkDebugFlag;
 
 #ifdef G_ENABLE_DEBUG
Index: gtk/gtkmain.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmain.c,v
retrieving revision 1.256
diff -u -r1.256 gtkmain.c
--- gtk/gtkmain.c	20 Mar 2005 07:01:22 -0000	1.256
+++ gtk/gtkmain.c	6 Apr 2005 19:26:52 -0000
@@ -159,7 +159,8 @@
   {"multihead", GTK_DEBUG_MULTIHEAD},
   {"modules", GTK_DEBUG_MODULES},
   {"geometry", GTK_DEBUG_GEOMETRY},
-  {"icontheme", GTK_DEBUG_ICONTHEME}
+  {"icontheme", GTK_DEBUG_ICONTHEME},
+  {"builder", GTK_DEBUG_BUILDER}
 };
 
 static const guint gtk_ndebug_keys = sizeof (gtk_debug_keys) / sizeof (GDebugKey);
Index: tests/Makefile.am
===================================================================
RCS file: /cvs/gnome/gtk+/tests/Makefile.am,v
retrieving revision 1.47
diff -u -r1.47 Makefile.am
--- tests/Makefile.am	11 Feb 2005 07:15:10 -0000	1.47
+++ tests/Makefile.am	6 Apr 2005 19:26:52 -0000
@@ -28,6 +28,7 @@
 
 noinst_PROGRAMS =			\
 	simple				\
+	testbuilder			\
 	testcairo			\
 	testcalendar			\
 	testcombo			\
@@ -108,6 +109,7 @@
 testxinerama_DEPENDENCIES = $(TEST_DEPS)
 testmerge_DEPENDENCIES = $(TEST_DEPS)
 testactions_DEPENDENCIES = $(TEST_DEPS)
+testbuilder_DEPENDENCIES = $(TEST_DEPS)
 
 simple_LDADD = $(LDADDS)
 testcairo_LDADD = $(LDADDS)
@@ -153,6 +155,9 @@
 pixbuf_threads_LDADD = $(LDADDS) $(GLIB_LIBS)
 testmerge_LDADD = $(LDADDS)
 testactions_LDADD = $(LDADDS)
+testbuilder_LDADD = $(LDADDS) 
+
+testbuilder_LDFLAGS = -export-dynamic 
 
 testfilechooser_SOURCES = 	\
 	prop-editor.c		\
@@ -205,6 +210,9 @@
 	testiconview.c		\
 	prop-editor.c
 
+testbuilder_SOURCES = 		\
+	testbuilder.c
+
 EXTRA_DIST = 			\
 	prop-editor.h		\
 	testgtk.1 		\
@@ -224,6 +232,7 @@
 	merge-1.ui		\
 	merge-2.ui		\
 	merge-3.ui		\
+	test.glade		\
 	gnome-textfile.png	\
 	makefile.msc
 
Index: tests/test.glade
===================================================================
RCS file: tests/test.glade
diff -N tests/test.glade
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/test.glade	6 Apr 2005 19:26:52 -0000
@@ -0,0 +1,212 @@
+<?xml version="1.0" ?>
+<glade-interface>
+    <object class="GtkUIManager" id="uimanager1">
+        <child>
+             <object class="GtkActionGroup" id="DefaultActionGroup">
+                  <child>
+                      <object class="GtkAction" id="file">
+                          <property name="name">file</property>
+                          <property name="label">_File</property>
+                          <property name="short-label">_File</property>
+                      </object>
+                  </child>
+                  <child>
+                      <object class="GtkAction" id="new">
+                          <property name="name">new</property>
+                          <property name="label">_New</property>
+                          <property name="short-label">_New</property>
+                          <property name="tooltip">New file</property>
+                          <property name="stock-id">gtk-new</property>
+                      </object>
+                  </child>
+                  <child>
+                      <object class="GtkAction" id="open">
+                          <property name="name">open</property>
+                          <property name="label">_Open</property>
+                          <property name="short-label">_Open</property>
+                          <property name="stock-id">gtk-open</property>
+                      </object>
+                  </child>
+                  <child>
+                      <object class="GtkAction" id="save">
+                          <property name="name">save</property>
+                          <property name="label">_Save</property>
+                          <property name="short-label">_Save</property>
+                          <property name="tooltip">Save your files!</property>
+                          <property name="stock-id">gtk-save</property>
+                      </object>
+                  </child>
+                  <child>
+                      <object class="GtkAction" id="quit">
+                          <property name="name">quit</property>
+                          <property name="label">_Quit</property>
+                          <property name="short-label">_Quit</property>
+                          <property name="stock-id">gtk-quit</property>
+                          <signal name="activate" handler="on_quit_activate"/>
+                      </object>
+                  </child>
+                  <child>
+                      <object class="GtkAction" id="edit">
+                          <property name="name">edit</property>
+                           <property name="label">_Edit</property>
+                           <property name="short-label">_Edit</property>
+                      </object>
+                  </child>
+                  <child>
+                      <object class="GtkAction" id="submenu1">
+                          <property name="name">submenu1</property>
+                          <property name="label">Submenu 1</property>
+                          <property name="short-label">Submenu 1</property>
+                      </object>
+                  </child>
+                  <child>
+                      <object class="GtkAction" id="submenu2">
+                          <property name="name">submenu2</property>
+                          <property name="label">Submenu 2</property>
+                          <property name="short-label">Submenu 2</property> 
+                          <property name="accelerator">&lt;control&gt;2</property> 
+                      </object>
+                  </child>
+                  <child>
+                      <object class="GtkAction" id="copy">
+                          <property name="name">copy</property>
+                          <property name="label">_Copy</property>
+                          <property name="short-label">_Copy</property>
+                          <property name="stock-id">gtk-copy</property>
+                      </object>
+                  </child>
+                  <child>
+                      <object class="GtkAction" id="cut">
+                          <property name="name">cut</property>
+                          <property name="label">_Cut</property>
+                          <property name="short-label">_Cut</property>
+                          <property name="stock-id">gtk-cut</property>
+                      </object>
+                  </child>
+                  <child>
+                      <object class="GtkAction" id="paste">
+                          <property name="name">paste</property>
+                          <property name="label">_Paste</property>
+                          <property name="short-label">_Paste</property>
+                          <property name="stock-id">gtk-paste</property>
+                      </object>
+                  </child>
+                  <child>
+                      <object class="GtkToggleAction" id="merge">
+                          <property name="name">merge</property>
+                          <property name="label">_Merge UI</property>
+                          <property name="short-label">_Merge</property>
+                          <property name="stock-id">gtk-convert</property>
+                          <property name="accelerator">&lt;control&gt;m</property> 
+                          <signal name="activate" handler="on_merge_activate"/>
+                      </object>
+                  </child>
+             </object>
+        </child>
+	<ui-state name="initial"><![CDATA[
+          <ui>
+            <menubar name="menubar1">
+              <menu action="file" name="file">
+                <menuitem action="new" name="new"/>
+                <menuitem action="save" name="save"/>
+                <menuitem action="open" name="open"/>
+              </menu>
+              <menu action="edit" name="edit">
+                <menu action="cut" name="cut">
+                  <menuitem action="submenu1" name="submenu1"/>
+                  <menuitem action="submenu2" name="submenu2"/>
+                </menu>
+                <menuitem action="copy" name="copy"/>
+                <menuitem action="paste" name="paste"/>
+                <separator/>
+                <menuitem action="merge" name="merge"/>
+              </menu>
+            </menubar>
+            <toolbar name="toolbar1">
+              <toolitem action="merge" name="merge"/>
+              <toolitem action="new" name="new"/>
+              <toolitem action="open" name="open"/>
+              <toolitem action="save" name="save"/>
+            </toolbar>
+          </ui>
+        ]]>
+        </ui-state>
+	<ui-state name="additional" merge="no"><![CDATA[
+          <menubar name="menubar1">
+            <menu action="file" name="file">
+              <separator/>
+              <menuitem action="quit" name="quit"/>
+            </menu>
+          </menubar>
+          <toolbar name="toolbar1">
+            <separator/>
+            <toolitem action="copy" name="copy"/>
+            <toolitem action="cut" name="cut"/>
+            <toolitem action="paste" name="paste"/>
+          </toolbar>
+        ]]>
+        </ui-state>
+    </object>
+
+    <object class="GtkTextBuffer" id="buffer1">
+        <property name="text">Hello World!</property>
+    </object>
+
+    <object class="GtkWindow" id="window1">
+        <property name="visible">True</property>
+        <property name="events">0</property>
+        <property name="title">UIManager</property>
+        <property name="default-width">400</property>
+        <property name="default-height">200</property>
+        <child>
+            <object class="GtkVBox" id="vbox1">
+                <property name="visible">True</property>
+                <property name="events">0</property>
+                <child>
+                    <object class="GtkMenuBar" id="menubar1" constructor="uimanager1">
+                        <property name="visible">False</property>
+                    </object>
+                    <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                    </packing>
+                </child>
+                <child>
+                    <object class="GtkToolbar" id="toolbar1" constructor="uimanager1">
+                      <property name="tooltips">False</property>
+                    </object>
+                    <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                    </packing>
+                </child>
+                <child>
+                    <object class="GtkTextView" id="textview1">
+                        <property name="buffer">buffer1</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="events">0</property>
+                        <child>
+                            <placeholder/>
+                        </child>
+                    </object>
+                    <packing>
+                        <property name="position">2</property>
+                    </packing>
+                </child>
+                <child>
+                    <object class="GtkStatusbar" id="statusbar1">
+                        <property name="visible">True</property>
+                        <property name="has_resize_grip">True</property>
+                    </object>
+                    <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">3</property>
+                    </packing>
+                </child>
+            </object>
+        </child>
+    </object>
+</glade-interface>
Index: tests/testbuilder.c
===================================================================
RCS file: tests/testbuilder.c
diff -N tests/testbuilder.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/testbuilder.c	6 Apr 2005 19:26:52 -0000
@@ -0,0 +1,188 @@
+/* -*- Mode: C; c-file-style: "gnu" -*- */
+/* Copyright (C) 1998-2001  James Henstridge <james@daa.com.au>
+ * Copyright (C) 2005       Johan Dahlin <johan@gnome.org>
+ * GNOME option parsing code by Miguel de Icaza.
+ *
+ * test-builder.c: a test program for the GtkBuilder object
+ *
+ *   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., 59 Temple Place, Suite 330, Boston, MA
+ *   02111-1307 USA
+ *
+ *
+ * At your option, you may use this alternative X style licence:
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or sell
+ *   copies of the Software, and to permit persons to whom the
+ *   Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall be
+ *   included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ *   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ *   CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ *   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ *   SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+#include <gtk/gtk.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include <gtk/gtkbuilder.h>
+
+
+GtkBuilder *builder;
+
+void
+on_merge_activate (GtkToggleAction *action)
+{
+  if (gtk_toggle_action_get_active (action))
+    gtk_builder_merge_ui_from_name (builder, "additional");
+  else
+    gtk_builder_remove_ui_from_name (builder, "additional");
+}
+
+void
+on_quit_activate (GtkAction *action)
+{
+  gtk_main_quit ();
+}
+
+int
+main (int argc, char **argv)
+{
+  const char *filename = NULL;
+  const char *rootnode = NULL;
+  gboolean no_connect = FALSE;
+  int i;
+  GList *l;
+    
+  /* initialise libraries */
+  gtk_init (&argc, &argv);
+
+  /* argument parsing */
+  for (i = 1; i < argc; i++)
+    {
+      if (!strcmp (argv[i], "--no-connect"))
+	no_connect = TRUE;
+      else if (filename == NULL)
+	filename = argv[i];
+      else if (rootnode == NULL)
+	rootnode = argv[i];
+      else
+	{
+	  g_print("Usage: %s [--no-connect] filename [rootnode]\n", argv[0]);
+	  return 1;
+	}
+    }
+  
+  if (filename == NULL)
+    {
+      g_print("Usage: %s [--no-connect] filename [rootnode]\n", argv[0]);
+      return 1;
+    }
+
+  gdk_pixbuf_get_type ();
+    
+  /* construct the interface */
+  builder = gtk_builder_new (filename, rootnode, NULL);
+  if (!builder)
+    {
+      g_warning("something bad happened while creating the interface");
+      return 1;
+    }
+
+  /* if you pass the rootnode argument to GtkBuilder, it will only
+   * construct widgets below that point. This shows how you might
+   * want to integrate the constructed interface in your program.
+   */
+  if (rootnode)
+    {
+      GObject *wid = gtk_builder_get_object (builder, rootnode);
+      if (!GTK_IS_WINDOW (wid))
+	{
+	  GtkWidget *win, *frame;
+	  
+	  win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+	  g_signal_connect (win, "destroy",
+			    G_CALLBACK (gtk_main_quit), NULL);
+	  gtk_container_set_border_width (GTK_CONTAINER (win), 5);
+	  frame = gtk_frame_new (NULL);
+	  gtk_container_set_border_width (GTK_CONTAINER (frame), 5);
+	  gtk_container_add (GTK_CONTAINER (win), frame);
+	  gtk_widget_show (frame);
+	  gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET (wid));
+	  gtk_widget_show (win);
+	}
+    }
+  
+  /* make sure delete-event quits the program, connect it on all toplevels */
+  for (l = gtk_builder_get_objects (builder); l; l = l->next)
+    {
+      GtkWidget *widget = l->data;
+      if (GTK_IS_WINDOW (widget))
+	{
+	  g_signal_connect (widget, "delete-event",
+			    G_CALLBACK (gtk_main_quit), NULL);
+	}
+    }
+
+    if (gtk_builder_get_object (builder, "liststore1"))
+      {
+	GObject *list_store;
+	GtkTreeIter iter;
+	
+	list_store = gtk_builder_get_object (builder, "liststore1");
+	gtk_list_store_append (GTK_LIST_STORE (list_store), &iter);
+	gtk_list_store_set (GTK_LIST_STORE (list_store), &iter,
+			    0, "stringie",
+			    1, 1,
+			    -1);
+      }
+
+    /* look up handler names in the symbol table and automatically
+     * connect the signals. This requires that your program export
+     * dynamic symbols.  If you link using libtool, passing the
+     * -export-dynamic link flag will do this in a cross platform
+     * manner.  For GNU ld, the --export-dynamic argument should
+     * work.
+     *
+     * If you don't want to do symbol lookups like this, the
+     * gtk_builder_signal_connect() function allows you to connect
+     * individual handlers up. */
+    
+    if (!no_connect)
+	gtk_builder_signal_autoconnect (builder);
+
+    /* start the event loop */
+    gtk_main ();
+
+    g_object_unref (builder);
+
+    return 0;
+}
