bonobo-ui-node re-write ...



Hi Guys,

	So ... complaints of UI memory usage and sluggishness finaly got
to me - and so I re-wrote the BonoboUINode implementation, and put some
hooks into it to shrink memory consumption, and accelerate our use of it.

	There's still plenty of room left for optimizations in there, but
at least it's doing integer compares instead of string compares for the
most part, and the thing is a lot smaller.

	_But_ this is a fairly complex piece of code, and I'd like people
to try out the work and review the patch before I go ahead in the stable
branch; I've committed to a branch 'ui-shrink' so to get that do:

	cvs -z3 upd -Pd -r 'ui-shrink'

	And you should get a smaller sweeter version. I'm particularly
interested in reports of problems (or improvements) with nautilus /
evolution. Due to a libxml leak it may well be worth using the latest
libxml on the LIB_XML_1_BRANCH. Still - the memory savings look impressive
for the ( lightweight ) test-ui UI, so I'm hoping this will be quite
helpful overall.

	Please hammer on it and let me know.

	Regards,

		Michael.

Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/bonobo/ChangeLog,v
retrieving revision 1.1102
diff -u -r1.1102 ChangeLog
--- ChangeLog	2001/08/21 23:47:10	1.1102
+++ ChangeLog	2001/08/22 16:14:42
@@ -1,3 +1,104 @@
+2001-08-22  Michael Meeks  <michael ximian com>
+
+	* bonobo/bonobo-ui-component.c
+	(bonobo_ui_component_set_status): update path - since tree
+	no longer has empty child content nodes.
+
+2001-08-21  Michael Meeks  <michael ximian com>
+
+	* bonobo/bonobo-ui-engine.c (state_update_new):
+	efficiency wins.
+	(hide_placeholder_if_empty_or_hidden): ditto.
+
+	* bonobo/bonobo-ui-xml.c (do_insert): optimize.
+	(merge): more efficiency tweaking.
+
+	* bonobo/bonobo-ui-engine.c (move_dirt_cmd_to_widget):
+	accelerate loop, use name_id
+	(update_commands_state): ditto.
+
+	* bonobo/bonobo-ui-xml.c (bonobo_ui_xml_set_dirty, merge):
+	quick compares for placeholder.
+	(find_child, merge): accelerate name lookups.
+
+	* tests/test-ui.c (main): update status check.
+
+	* bonobo/bonobo-ui-node.c (uiEndElement): fix
+	empty content pruning bug.
+	(bonobo_ui_node_replace): swap arg. order; it was wrong.
+
+	* bonobo/bonobo-ui-util.c
+	(bonobo_ui_util_translate_ui): encode strings for
+	libxml transport issues.
+
+	* bonobo/bonobo-ui-node.c
+	(bonobo_ui_node_move_children): impl.
+
+	* bonobo/bonobo-ui-xml.c (override_node_with),
+	(reinstate_old_node): use it.
+	(move_children): kill it - slow.
+
+	* bonobo/bonobo-ui-engine.c (class_init): setup
+	cached ids for "id", "verb", "name"
+	(node_get_id): fix horrible inefficiencies.
+	(cmd_to_node_add_node): upd.
+	(cmd_to_node_remove_node): upd.
+	(impl_emit_verb_on): upd.
+	(cmd_get_node): upd.
+	(impl_emit_event_on): upd.
+	(bonobo_ui_engine_get_cmd_node): upd.
+
+	* bonobo/bonobo-ui-xml.c
+	(bonobo_ui_xml_class_init): setup name_id.
+	(do_set_id): use it.
+	(bonobo_ui_xml_make_path): ditto & optimize.
+
+2001-08-21  Michael Meeks  <michael ximian com>
+
+	* Merged libbonoboui to here:
+
+	* bonobo/bonobo-ui-xml.c (bonobo_ui_xml_dump),
+	(override_node_with): port to new code.
+	(bonobo_ui_xml_merge): ditto.
+
+	* bonobo/bonobo-ui-util.c (bonobo_ui_util_translate_ui):
+	re-implement for new node structures.
+
+	* bonobo/bonobo-ui-node.c
+	(bonobo_ui_node_from_string, bonobo_ui_node_from_file): reimpl.
+	(parse_state_new, parse_state_free): impl.
+	(bonobo_ui_node_str_from_id): impl.
+	(internal_to_string): impl.
+	(bonobo_ui_node_to_string): re-impl.
+
+2001-08-20  Michael Meeks  <michael ximian com>
+
+	* bonobo/bonobo-ui-node.c (bonobo_ui_node_new),
+	(bonobo_ui_node_new_child, bonobo_ui_node_copy): rewrite.
+	(node_free_internal): impl.
+	(bonobo_ui_node_free, bonobo_ui_node_set_data),
+	(bonobo_ui_node_get_data): rewrite.
+	(bonobo_ui_node_get_id, cleanhash): impl.
+	(bonobo_ui_node_set_attr_by_id): impl.
+	(bonobo_ui_node_set_attr): upd.
+	(bonobo_ui_node_remove_attr): upd.
+	(bonobo_ui_node_unlink, bonobo_ui_node_insert_before),
+	(bonobo_ui_node_add_child, bonobo_ui_node_set_content),
+	(bonobo_ui_node_replace): rewrite.
+	(bonobo_ui_node_peek_content): impl
+	(bonobo_ui_node_next): re-impl.
+	(bonobo_ui_node_prev, bonobo_ui_node_children),
+	(bonobo_ui_node_get_name, bonobo_ui_node_parent): rewrite.
+	(bonobo_ui_node_has_name): reimpl.
+	(bonobo_ui_node_has_name_by_id): impl.
+	(bonobo_ui_node_transparent): reimpl.
+	(bonobo_ui_node_copy_attrs): reimpl.
+	(node_free_attrs): impl.
+	(do_strip): kill.
+	(bonobo_ui_node_strip): make a noop.
+
+	* bonobo/bonobo-ui-node-private.h: add.
+
 2001-08-21  Alex Larsson  <alexl redhat com>

 	* bonobo/bonobo-ui-util.c (bonobo_ui_util_set_ui):
Index: NEWS
===================================================================
RCS file: /cvs/gnome/bonobo/NEWS,v
retrieving revision 1.39
diff -u -r1.39 NEWS
--- NEWS	2001/06/27 11:41:24	1.39
+++ NEWS	2001/08/22 16:14:42
@@ -1,3 +1,17 @@
+Bonobo 1.0.8
+
+	* Memory trampling fixage (Morten, Me)
+	* UI translation profiling & acceleration (Alan Cox, Me)
+	* event source re-enterancy fix (Me)
+	* Improved exception dumping (Jacob Berkman)
+	* ReadOnly exception fired on property bag (Jacob Berkman)
+	* error condition fixage (Dirk-Jan Binnema)
+	* Tolerate aliases sanely in property sets (Me)
+	* Library fixage (Richard Hult)
+	* Build / warning cleans (Darin Adler)
+	* stream-memory mem_get/set_info impl (Lutz Muller)
+	* portability fixes (Tim Mooney)
+
 Bonobo 1.0.5

 	* UI handler stuff:
Index: bonobo/Makefile.am
===================================================================
RCS file: /cvs/gnome/bonobo/bonobo/Makefile.am,v
retrieving revision 1.171
diff -u -r1.171 Makefile.am
--- bonobo/Makefile.am	2001/04/16 23:25:54	1.171
+++ bonobo/Makefile.am	2001/08/22 16:14:42
@@ -169,6 +169,7 @@
 	bonobo-activation-context.h		\
 	bonobo-ui-engine-config.h		\
 	bonobo-ui-engine-private.h		\
+	bonobo-ui-node-private.h		\
 	bonobo-ui-sync-menu.h			\
 	bonobo-ui-sync-keys.h			\
 	bonobo-ui-sync-status.h			\
Index: bonobo/bonobo-ui-component.c
===================================================================
RCS file: /cvs/gnome/bonobo/bonobo/bonobo-ui-component.c,v
retrieving revision 1.48
diff -u -r1.48 bonobo-ui-component.c
--- bonobo/bonobo-ui-component.c	2001/08/20 16:19:15	1.48
+++ bonobo/bonobo-ui-component.c	2001/08/22 16:14:42
@@ -1280,7 +1280,7 @@
 {
 	if (text == NULL ||
 	    text [0] == '\0') { /* Remove what was there to reveal other msgs */
-		bonobo_ui_component_rm (component, "/status/main/*", opt_ev);
+		bonobo_ui_component_rm (component, "/status/main", opt_ev);
 	} else {
 		char *str, *encoded;

Index: bonobo/bonobo-ui-container.c
===================================================================
RCS file: /cvs/gnome/bonobo/bonobo/bonobo-ui-container.c,v
retrieving revision 1.24
diff -u -r1.24 bonobo-ui-container.c
--- bonobo/bonobo-ui-container.c	2001/08/20 16:19:15	1.24
+++ bonobo/bonobo-ui-container.c	2001/08/22 16:14:42
@@ -176,7 +176,7 @@
 	if (!engine)
 		return;

-/*	g_warning ("Node remove '%s' for '%s'", path, component_name);*/
+/*	g_warning ("Node remove '%s' for '%s'", path, component_name); */

 	err = bonobo_ui_engine_xml_rm (engine, path, component_name);

Index: bonobo/bonobo-ui-engine.c
===================================================================
RCS file: /cvs/gnome/bonobo/bonobo/bonobo-ui-engine.c,v
retrieving revision 1.17
diff -u -r1.17 bonobo-ui-engine.c
--- bonobo/bonobo-ui-engine.c	2001/06/26 17:35:07	1.17
+++ bonobo/bonobo-ui-engine.c	2001/08/22 16:14:42
@@ -20,6 +20,7 @@
 #include <bonobo/bonobo-ui-engine-config.h>
 #include <bonobo/bonobo-ui-engine-private.h>
 #include <bonobo/bonobo-exception.h>
+#include <bonobo/bonobo-ui-node-private.h>

 /* Various debugging output defines */
 #undef STATE_SYNC_DEBUG
@@ -30,6 +31,12 @@

 static GtkObjectClass *parent_class = NULL;

+static guint32 id_id        = 0;
+static guint32 verb_id      = 0;
+static guint32 name_id      = 0;
+static guint32 hidden_id    = 0;
+static guint32 sensitive_id = 0;
+
 enum {
 	ADD_HINT,
 	REMOVE_HINT,
@@ -84,8 +91,7 @@
 		return ret;
 	}

-	return find_sync_for_node (
-		engine, bonobo_ui_node_parent (node));
+	return find_sync_for_node (engine, node->parent);
 }

 /**
@@ -150,29 +156,22 @@
 	GSList *nodes;
 } CmdToNode;

-static char *
+static const char *
 node_get_id (BonoboUINode *node)
 {
-	char *txt;
-	char *ret;
+	const char *txt;

 	g_return_val_if_fail (node != NULL, NULL);

-	if (!(txt = bonobo_ui_node_get_attr (node, "id"))) {
-		txt = bonobo_ui_node_get_attr (node, "verb");
-		if (txt && txt [0] == '\0') {
-			bonobo_ui_node_free_string (txt);
-			txt = bonobo_ui_node_get_attr (node, "name");
-		}
-	}
+	if (!(txt = bonobo_ui_node_get_attr_by_id (node, id_id))) {

-	if (txt) {
-		ret = g_strdup (txt);
-		bonobo_ui_node_free_string (txt);
-	} else
-		ret = NULL;
+		txt = bonobo_ui_node_get_attr_by_id (node, verb_id);

-	return ret;
+		if (txt && txt [0] == '\0')
+			txt = bonobo_ui_node_get_attr_by_id (node, name_id);
+	}
+
+	return txt;
 }

 static void
@@ -180,14 +179,13 @@
 		      BonoboUINode   *node,
 		      gboolean        recurse)
 {
-	CmdToNode *ctn;
-	char      *name;
+	CmdToNode  *ctn;
+	const char *name;

 	if (recurse) {
 		BonoboUINode *l;

-		for (l = bonobo_ui_node_children (node); l;
-		     l = bonobo_ui_node_next (l))
+		for (l = node->children; l; l = l->next)
 			cmd_to_node_add_node (engine, l, TRUE);
 	}

@@ -196,17 +194,16 @@
 		return;

 	ctn = g_hash_table_lookup (
-		engine->priv->cmd_to_node, name);
+		engine->priv->cmd_to_node, (gpointer) name);

 	if (!ctn) {
 		ctn = g_new (CmdToNode, 1);

-		ctn->name = name;
+		ctn->name = g_strdup (name);
 		ctn->nodes = NULL;
 		g_hash_table_insert (
 			engine->priv->cmd_to_node, ctn->name, ctn);
-	} else
-		g_free (name);
+	}

 /*	fprintf (stderr, "Adding %d'th '%s'\n",
 	g_slist_length (ctn->nodes), ctn->name);*/
@@ -219,14 +216,13 @@
 			 BonoboUINode   *node,
 			 gboolean        recurse)
 {
-	CmdToNode *ctn;
-	char      *name;
+	CmdToNode  *ctn;
+	const char *name;

 	if (recurse) {
 		BonoboUINode *l;

-		for (l = bonobo_ui_node_children (node); l;
-		     l = bonobo_ui_node_next (l))
+		for (l = node->children; l; l = l->next)
 			cmd_to_node_remove_node (engine, l, TRUE);
 	}

@@ -249,7 +245,6 @@
 	 * NB. we leave the CmdToNode structures around
 	 * for future use.
 	 */
-	g_free (name);
 }

 static int
@@ -924,23 +919,22 @@
 		  GtkWidget    *widget,
 		  BonoboUINode *node)
 {
-	char *hidden, *sensitive, *state;
-	StateUpdate   *su;
+	char        *state;
+	const char  *hidden, *sensitive;
+	StateUpdate *su;

 	g_return_val_if_fail (node != NULL, NULL);
 	g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);

-	hidden = bonobo_ui_node_get_attr (node, "hidden");
+	hidden = bonobo_ui_node_get_attr_by_id (node, hidden_id);
 	if (hidden && atoi (hidden))
 		gtk_widget_hide (widget);
 	else
 		gtk_widget_show (widget);
-	bonobo_ui_node_free_string (hidden);

-	sensitive = bonobo_ui_node_get_attr (node, "sensitive");
+	sensitive = bonobo_ui_node_get_attr_by_id (node, sensitive_id);
 	if (sensitive)
 		gtk_widget_set_sensitive (widget, atoi (sensitive));
-	bonobo_ui_node_free_string (sensitive);

 	if ((state = bonobo_ui_node_get_attr (node, "state"))) {
 		su = g_new0 (StateUpdate, 1);
@@ -1383,7 +1377,7 @@
 impl_emit_verb_on (BonoboUIEngine *engine,
 		   BonoboUINode   *node)
 {
-	CORBA_char      *verb;
+	const char      *verb;
 	BonoboUIXmlData *data;

 	g_return_if_fail (node != NULL);
@@ -1405,14 +1399,11 @@
 	else {
 		if (!data->id) {
 			g_warning ("Weird; no ID on verb '%s'", verb);
-			bonobo_ui_node_free_string (verb);
 			return;
 		}

 		real_exec_verb (engine, data->id, verb);
 	}
-
-	g_free (verb);
 }

 static BonoboUINode *
@@ -1421,7 +1412,7 @@
 {
 	char         *path;
 	BonoboUINode *ret;
-	char         *cmd_name;
+	const char   *cmd_name;

 	g_return_val_if_fail (engine != NULL, NULL);

@@ -1457,7 +1448,6 @@
 	}

 	g_free (path);
-	g_free (cmd_name);

 	return ret;
 }
@@ -1623,9 +1613,9 @@
 		    BonoboUINode   *node,
 		    const char     *state)
 {
-	char            *id;
+	const char      *id;
 	BonoboUIXmlData *data;
-	char            *component_id;
+	char            *component_id, *real_id;

 	g_return_if_fail (node != NULL);

@@ -1636,16 +1626,17 @@
 	g_return_if_fail (data != NULL);

 	component_id = g_strdup (data->id);
+	real_id      = g_strdup (id);

 	/* This could invoke a CORBA method that might de-register the component */
 	set_cmd_attr (engine, node, "state", state, TRUE);

-	real_emit_ui_event (engine, component_id, id,
+	real_emit_ui_event (engine, component_id, real_id,
 			    Bonobo_UIComponent_STATE_CHANGED,
 			    state);

+	g_free (real_id);
 	g_free (component_id);
-	g_free (id);
 }

 static void
@@ -1743,6 +1734,12 @@
 				  GTK_TYPE_NONE, 2, GTK_TYPE_POINTER, GTK_TYPE_STRING);

 	gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
+
+	id_id        = bonobo_ui_node_get_id ("id");
+	verb_id      = bonobo_ui_node_get_id ("verb");
+	name_id      = bonobo_ui_node_get_id ("name");
+	hidden_id    = bonobo_ui_node_get_id ("hidden");
+	sensitive_id = bonobo_ui_node_get_id ("sensitive");
 }

 static void
@@ -1900,14 +1897,13 @@
 hide_placeholder_if_empty_or_hidden (BonoboUIEngine *engine,
 				     BonoboUINode   *node)
 {
-	NodeInfo *info;
-	char *txt;
+	NodeInfo   *info;
+	const char *txt;
 	gboolean hide_placeholder_and_contents;
 	gboolean has_visible_separator;

-	txt = bonobo_ui_node_get_attr (node, "hidden");
+	txt = bonobo_ui_node_get_attr_by_id (node, hidden_id);
 	hide_placeholder_and_contents = txt && atoi (txt);
-	bonobo_ui_node_free_string (txt);

 	info = bonobo_ui_xml_get_data (engine->priv->tree, node);
 	has_visible_separator = info && info->widget
@@ -2221,20 +2217,18 @@
 	if (!cmds)
 		return;

-	for (l = bonobo_ui_node_children (cmds); l;
-             l = bonobo_ui_node_next (l)) {
-		BonoboUIXmlData *data = bonobo_ui_xml_get_data (engine->priv->tree, l);
+	for (l = cmds->children; l; l = l->next) {
+		BonoboUIXmlData *data = bonobo_ui_xml_get_data (
+			engine->priv->tree, l);

 		if (data->dirty) {
-			char *cmd_name;
+			const char *cmd_name;

-			cmd_name = bonobo_ui_node_get_attr (l, "name");
+			cmd_name = bonobo_ui_node_get_attr_by_id (l, name_id);
 			if (!cmd_name)
 				g_warning ("Serious error, cmd without name");
 			else
 				dirty_by_cmd (engine, cmd_name);
-
-			bonobo_ui_node_free_string (cmd_name);
 		}
 	}
 }
@@ -2253,13 +2247,12 @@
 	if (!cmds)
 		return;

-	for (l = bonobo_ui_node_children (cmds); l;
-             l = bonobo_ui_node_next (l)) {
+	for (l = cmds->children; l; l = l->next) {
 		BonoboUIXmlData *data = bonobo_ui_xml_get_data (
 			engine->priv->tree, l);
-		char *cmd_name;
+		const char *cmd_name;

-		cmd_name = bonobo_ui_node_get_attr (l, "name");
+		cmd_name = bonobo_ui_node_get_attr_by_id (l, name_id);
 		if (!cmd_name)
 			g_warning ("Internal error; cmd with no id");

@@ -2268,7 +2261,6 @@
 				engine, updates, l, cmd_name);

 		data->dirty = FALSE;
-		bonobo_ui_node_free_string (cmd_name);
 	}

 	execute_state_updates (updates);
@@ -2705,7 +2697,7 @@
 {
 	char         *path;
 	BonoboUINode *ret;
-	char         *cmd_name;
+	const char   *cmd_name;

 	g_return_val_if_fail (engine != NULL, NULL);

@@ -2740,7 +2732,6 @@
 	}

 	g_free (path);
-	g_free (cmd_name);

 	return ret;
 }
Index: bonobo/bonobo-ui-node.c
===================================================================
RCS file: /cvs/gnome/bonobo/bonobo/bonobo-ui-node.c,v
retrieving revision 1.17
diff -u -r1.17 bonobo-ui-node.c
--- bonobo/bonobo-ui-node.c	2001/08/20 18:11:59	1.17
+++ bonobo/bonobo-ui-node.c	2001/08/22 16:14:42
@@ -1,30 +1,71 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
+/**
  * bonobo-ui-node.c: Code to manipulate BonoboUINode objects
  *
- * Author:
+ * Authors:
  *	Havoc Pennington <hp redhat com>
+ *      Michael Meeks    <michael ximian com>
  *
  * Copyright 2000 Red Hat, Inc.
+ *           2001 Ximian, Inc.
  */
-
-#include "config.h"
-#include <bonobo/bonobo-ui-node.h>
-#include <stdlib.h>
+#include <config.h>
 #include <string.h>
+#include <bonobo/bonobo-ui-node.h>
+#include <bonobo/bonobo-ui-node-private.h>

 #include <gnome-xml/parser.h>
 #include <gnome-xml/parserInternals.h>
 #include <gnome-xml/xmlmemory.h>

-/* Having this struct here makes debugging nicer. */
-struct _BonoboUINode {
-	xmlNode real_node;
-};
+#define attr(n,i) g_array_index((n)->attrs, BonoboUIAttr, (i))
+
+static GHashTable *str_to_id_hash = NULL;
+static GHashTable *id_to_str_hash = NULL;
+static guint32     id_idx = 32;
+
+static void
+cleanhash ()
+{
+	if (str_to_id_hash) {
+		g_hash_table_foreach (str_to_id_hash, (GHFunc) g_free, NULL);
+		g_hash_table_destroy (str_to_id_hash);
+		g_hash_table_destroy (id_to_str_hash);
+	}
+}
+
+guint32
+bonobo_ui_node_get_id (const char *str)
+{
+	gpointer p;
+
+	if (!id_to_str_hash) {
+		id_to_str_hash = g_hash_table_new (NULL, NULL);
+		str_to_id_hash = g_hash_table_new (g_str_hash, g_str_equal);
+		g_atexit (cleanhash);
+	}
+
+	if ((p = g_hash_table_lookup (str_to_id_hash, str)))
+		return GPOINTER_TO_UINT (p);

-#define XML_NODE(x) (&(x)->real_node)
-#define BNODE(x) ((BonoboUINode *)(x))
+	else {
+		char *nstr = g_strdup (str);

+		g_hash_table_insert (
+			str_to_id_hash, nstr, GUINT_TO_POINTER (++id_idx));
+		g_hash_table_insert (
+			id_to_str_hash, GUINT_TO_POINTER (id_idx), nstr);
+	}
+
+	return id_idx;
+}
+
+const char *
+bonobo_ui_node_str_from_id (guint32 id)
+{
+	return g_hash_table_lookup (id_to_str_hash, GUINT_TO_POINTER (id));
+}
+
 /**
  * bonobo_ui_node_new:
  * @name: The name for the node
@@ -34,9 +75,16 @@
  * Return value: a new node pointer
  **/
 BonoboUINode*
-bonobo_ui_node_new (const char   *name)
+bonobo_ui_node_new (const char *name)
 {
-        return BNODE (xmlNewNode (NULL, name));
+	BonoboUINode *node = g_new0 (BonoboUINode, 1);
+
+	node->name_id = bonobo_ui_node_get_id (name);
+
+	/* FIXME: we could do this idly */
+	node->attrs = g_array_new (FALSE, FALSE, sizeof (BonoboUIAttr));
+
+	return node;
 }

 /**
@@ -52,7 +100,13 @@
 bonobo_ui_node_new_child (BonoboUINode *parent,
                           const char   *name)
 {
-        return BNODE (xmlNewChild (XML_NODE (parent), NULL, name, NULL));
+	BonoboUINode *node;
+
+	node = bonobo_ui_node_new (name);
+
+	bonobo_ui_node_add_child (parent, node);
+
+	return node;
 }

 /**
@@ -62,16 +116,211 @@
  *
  * Copy an XML node, if @recursive do a deep copy, otherwise just dup the node itself.
  *
- * Return value: a copy of the node
+ * Return value: a copy of the noce
  **/
 BonoboUINode*
 bonobo_ui_node_copy (BonoboUINode *node,
                      gboolean recursive)
 {
-        return BNODE (xmlCopyNode (XML_NODE (node), recursive));
+	BonoboUINode *copy;
+
+	copy = g_new0 (BonoboUINode, 1);
+	copy->name_id = node->name_id;
+
+	if (node->content)
+		copy->content = g_strdup (node->content);
+
+	bonobo_ui_node_copy_attrs (node, copy);
+
+	if (recursive) {
+		BonoboUINode *l, *last = NULL;
+
+		for (l = node->children; l; l = l->next) {
+			BonoboUINode *child;
+
+			child = bonobo_ui_node_copy (l, TRUE);
+
+			if (!last)
+				copy->children = child;
+			else {
+				child->prev = last;
+				last->next  = child;
+			}
+		}
+	}
+
+	return copy;
 }

 /**
+ * bonobo_ui_node_add_child:
+ * @parent: the parent
+ * @child: the new child
+ *
+ * Add a @child node to the @parent node ( after the other children )
+ **/
+void
+bonobo_ui_node_add_child (BonoboUINode *parent,
+			  BonoboUINode *child)
+{
+	BonoboUINode *l, *last = NULL;
+
+	for (l = parent->children; l; l = l->next)
+		last = l;
+
+	if (!last) {
+		parent->children = child;
+		child->prev = NULL;
+		child->next = NULL;
+	} else {
+		last->next  = child;
+		child->prev = last;
+	}
+	child->parent = parent;
+}
+
+void
+bonobo_ui_node_add_after (BonoboUINode *before,
+			  BonoboUINode *new_after)
+{
+	new_after->next = before->next;
+	new_after->prev = before;
+
+	if (new_after->next)
+		new_after->next->prev = new_after;
+
+	before->next = new_after;
+
+	new_after->parent = before->parent;
+}
+
+/**
+ * bonobo_ui_node_insert_before:
+ * @sibling: the node to insert
+ * @prev_sibling: the placeholder for insertion
+ *
+ * Insert a @sibling before @prev_sibling in a node list
+ **/
+void
+bonobo_ui_node_insert_before (BonoboUINode *new_before,
+                              BonoboUINode *after)
+{
+	new_before->prev = after->prev;
+	if (!after->prev) {
+		if (after->parent)
+			after->parent->children = new_before;
+	}
+	new_before->next = after;
+	if (new_before->prev)
+		new_before->prev->next = new_before;
+
+	after->prev = new_before;
+
+	new_before->parent = after->parent;
+}
+
+/**
+ * bonobo_ui_node_unlink:
+ * @node: the node
+ *
+ * Unlink @node from its tree, ie. disassociate it with its parent
+ **/
+void
+bonobo_ui_node_unlink (BonoboUINode *node)
+{
+	if (!node->prev) {
+		if (node->parent)
+			node->parent->children = node->next;
+	} else
+		node->prev->next = node->next;
+
+	if (node->next)
+		node->next->prev = node->prev;
+
+	node->next   = NULL;
+	node->prev   = NULL;
+	node->parent = NULL;
+}
+
+/**
+ * bonobo_ui_node_replace:
+ * @old_node: node to be replaced
+ * @new_node: node to replace with
+ *
+ * Replace @old_node with @new_node in the tree. @old_node is
+ * left unlinked and floating with its children.
+ **/
+void
+bonobo_ui_node_replace (BonoboUINode *old_node,
+			BonoboUINode *new_node)
+{
+	bonobo_ui_node_unlink (new_node);
+
+	new_node->next   = old_node->next;
+	new_node->prev   = old_node->prev;
+	new_node->parent = old_node->parent;
+
+	old_node->next   = NULL;
+	old_node->prev   = NULL;
+	old_node->parent = NULL;
+
+	if (new_node->next)
+		new_node->next->prev = new_node;
+
+	if (new_node->prev)
+		new_node->prev->next = new_node;
+	else {
+		if (new_node->parent)
+			new_node->parent->children = new_node;
+	}
+}
+
+void
+bonobo_ui_node_move_children (BonoboUINode *from, BonoboUINode *to)
+{
+	BonoboUINode *l;
+
+	g_return_if_fail (to != NULL);
+	g_return_if_fail (from != NULL);
+	g_return_if_fail (bonobo_ui_node_children (to) == NULL);
+
+	to->children   = from->children;
+	from->children = NULL;
+
+	for (l = to->children; l; l = l->next)
+		l->parent = to;
+}
+
+static void
+node_free_attrs (BonoboUINode *node)
+{
+	int    i;
+
+	for (i = 0; i < node->attrs->len; i++)
+		if (attr (node, i).value)
+			xmlFree (attr (node, i).value);
+
+	g_array_free (node->attrs, TRUE);
+}
+
+static void
+node_free_internal (BonoboUINode *node)
+{
+	BonoboUINode *l, *next;
+
+	node_free_attrs (node);
+
+	g_free (node->content);
+
+	for (l = node->children; l; l = next) {
+		next = l->next;
+		bonobo_ui_node_free (l);
+	}
+
+	g_free (node);
+}
+
+/**
  * bonobo_ui_node_free:
  * @node: a node.
  *
@@ -80,7 +329,10 @@
 void
 bonobo_ui_node_free (BonoboUINode *node)
 {
-        xmlFreeNode (XML_NODE (node));
+	if (node->parent || node->next)
+		bonobo_ui_node_unlink (node);
+
+	node_free_internal (node);
 }

 /**
@@ -94,7 +346,7 @@
 bonobo_ui_node_set_data (BonoboUINode *node,
                          gpointer      data)
 {
-        XML_NODE (node)->_private = data;
+	node->user_data = data;
 }

 /**
@@ -108,27 +360,66 @@
 gpointer
 bonobo_ui_node_get_data (BonoboUINode *node)
 {
-        return XML_NODE (node)->_private;
+	return node->user_data;
 }

-static xmlAttrPtr
-get_attr (xmlNode *node, const char *name)
+static BonoboUIAttr *
+get_attr (BonoboUINode *node, guint32 id, BonoboUIAttr **opt_space)
 {
-        xmlAttrPtr prop;
+	int i;
+	BonoboUIAttr *a;
+
+	if (opt_space)
+		*opt_space = NULL;

-        if ((node == NULL) || (name == NULL)) return(NULL);
-        /*
-         * Check on the properties attached to the node
-         */
-        prop = node->properties;
-        while (prop != NULL) {
-                if (!xmlStrcmp(prop->name, name))  {
-                        return(prop);
-                }
-                prop = prop->next;
-      }
-
-      return(NULL);
+	for (i = 0; i < node->attrs->len; i++) {
+		a = &attr (node, i);
+
+		if (a->id == id)
+			return a;
+
+		if (a->id == 0 && opt_space)
+			*opt_space = a;
+	}
+
+	return NULL;
+}
+
+void
+bonobo_ui_node_set_attr_by_id (BonoboUINode *node,
+			       guint32       id,
+			       const char   *value)
+{
+	BonoboUIAttr *a, *space;
+
+	g_return_if_fail (node != NULL);
+
+	a = get_attr (node, id, &space);
+
+	if (a) {
+		xmlFree (a->value);
+		a->value = NULL;
+
+		if (!value) /* Unset the attribute */
+			a->id = 0;
+		else
+			a->value = xmlStrdup (value);
+	} else {
+		if (!value)
+			return;
+
+		if (space) {
+			space->id = id;
+			space->value = xmlStrdup (value);
+		} else {
+			BonoboUIAttr na;
+
+			na.id = id;
+			na.value = xmlStrdup (value);
+
+			g_array_append_val (node->attrs, na);
+		}
+	}
 }

 /**
@@ -144,14 +435,23 @@
 bonobo_ui_node_set_attr (BonoboUINode *node,
                          const char   *name,
                          const char   *value)
+{
+	bonobo_ui_node_set_attr_by_id (
+		node, bonobo_ui_node_get_id (name), value);
+}
+
+const char *
+bonobo_ui_node_get_attr_by_id (BonoboUINode *node,
+			       guint32       id)
 {
-        if (value == NULL) {
-                xmlAttrPtr attr = get_attr (XML_NODE (node), name);
-                if (attr)
-                        xmlRemoveProp (attr);
-        } else {
-                xmlSetProp (XML_NODE (node), name, value);
-        }
+	BonoboUIAttr *a;
+
+	if (!node)
+		return NULL;
+
+	a = get_attr (node, id, NULL);
+
+	return a ? a->value : NULL;
 }

 /**
@@ -168,7 +468,12 @@
 bonobo_ui_node_get_attr (BonoboUINode *node,
                          const char   *name)
 {
-        return xmlGetProp (XML_NODE (node), name);
+	const char *val;
+
+	val = bonobo_ui_node_get_attr_by_id (
+		node, bonobo_ui_node_get_id (name));
+
+	return val ? g_strdup (val) : NULL;
 }

 /**
@@ -184,7 +489,8 @@
 bonobo_ui_node_has_attr (BonoboUINode *node,
                          const char   *name)
 {
-        return get_attr (XML_NODE (node), name) != NULL;
+	return bonobo_ui_node_get_attr_by_id (
+		node, bonobo_ui_node_get_id (name)) != NULL;
 }

 /**
@@ -197,70 +503,11 @@
 void
 bonobo_ui_node_remove_attr (BonoboUINode *node,
                             const char   *name)
-{
-        xmlAttrPtr attr = get_attr (XML_NODE (node), name);
-        if (attr)
-                xmlRemoveProp (attr);
-}
-
-/**
- * bonobo_ui_node_add_child:
- * @parent: the parent
- * @child: the new child
- *
- * Add a @child node to the @parent node ( after the other children )
- **/
-void
-bonobo_ui_node_add_child (BonoboUINode *parent,
-			  BonoboUINode *child)
 {
-        xmlAddChild (XML_NODE (parent), XML_NODE (child));
+	bonobo_ui_node_set_attr (node, name, NULL);
 }

 /**
- * bonobo_ui_node_insert_before:
- * @sibling: the node to insert
- * @prev_sibling: the placeholder for insertion
- *
- * Insert a @sibling before @prev_sibling in a node list
- **/
-void
-bonobo_ui_node_insert_before (BonoboUINode *sibling,
-                              BonoboUINode *prev_sibling)
-{
-        xmlAddPrevSibling (XML_NODE (sibling), XML_NODE (prev_sibling));
-}
-
-/**
- * bonobo_ui_node_unlink:
- * @node: the node
- *
- * Unlink @node from its tree, ie. disassociate it with its parent
- **/
-void
-bonobo_ui_node_unlink (BonoboUINode *node)
-{
-	xmlUnlinkNode (XML_NODE (node));
-}
-
-/**
- * bonobo_ui_node_replace:
- * @old_node: node to be replaced
- * @new_node: node to replace with
- *
- * Replace @old_node with @new_node in the tree. @old_node is
- * left unlinked and floating with its children.
- **/
-void
-bonobo_ui_node_replace (BonoboUINode *old_node,
-			BonoboUINode *new_node)
-{
-	/* libxml has these args indisputably backward */
-	xmlReplaceNode (XML_NODE (new_node),
-			XML_NODE (old_node));
-}
-
-/**
  * bonobo_ui_node_set_content:
  * @node: the node
  * @content: the new content
@@ -270,8 +517,15 @@
 void
 bonobo_ui_node_set_content (BonoboUINode *node,
                             const char   *content)
+{
+	g_free (node->content);
+	node->content = g_strdup (content);
+}
+
+const char *
+bonobo_ui_node_peek_content (BonoboUINode *node)
 {
-        xmlNodeSetContent (XML_NODE (node), content);
+	return node->content;
 }

 /**
@@ -285,63 +539,57 @@
 char *
 bonobo_ui_node_get_content (BonoboUINode *node)
 {
-        return xmlNodeGetContent (XML_NODE (node));
+	const char *content = bonobo_ui_node_peek_content (node);
+
+	return content ? xmlStrdup (content) : NULL;
 }

 /**
  * bonobo_ui_node_next:
  * @node: the node
- *
- * accesses the next node.
- *
+ *
  * Return value: the node after @node in the list
  **/
 BonoboUINode*
 bonobo_ui_node_next (BonoboUINode *node)
 {
-        return BNODE (XML_NODE (node)->next);
+	return node->next;
 }

 /**
  * bonobo_ui_node_prev:
  * @node: the node
  *
- * accesses the previous node.
- *
- * Return value: the node before @node in the list
+ * Return value: the node after @node in the list
  **/
 BonoboUINode*
 bonobo_ui_node_prev (BonoboUINode *node)
 {
-        return BNODE (XML_NODE (node)->prev);
+	return node->prev;
 }

 /**
  * bonobo_ui_node_children:
  * @node: the node
  *
- * accesses the node's children.
- *
  * Return value: the first child of @node
  **/
 BonoboUINode*
 bonobo_ui_node_children (BonoboUINode *node)
 {
-        return BNODE (XML_NODE (node)->xmlChildrenNode);
+	return node->children;
 }

 /**
  * bonobo_ui_node_parent:
  * @node: the node
- *
- * accesses the node's parent.
  *
  * Return value: the parent node of @node
  **/
 BonoboUINode*
 bonobo_ui_node_parent (BonoboUINode *node)
 {
-        return BNODE (XML_NODE (node)->parent);
+	return node->parent;
 }

 /**
@@ -352,8 +600,15 @@
  **/
 const char*
 bonobo_ui_node_get_name (BonoboUINode *node)
+{
+	return bonobo_ui_node_str_from_id (node->name_id);
+}
+
+gboolean
+bonobo_ui_node_has_name_by_id (BonoboUINode *node,
+			       guint32       id)
 {
-        return XML_NODE (node)->name;
+	return (node->name_id == id);
 }

 /**
@@ -361,15 +616,14 @@
  * @node: the node
  * @name: a name the node might have
  *
- * accesses the node's name.
- *
  * Return value: TRUE if @node has name == @name
  **/
 gboolean
 bonobo_ui_node_has_name (BonoboUINode *node,
 			 const char   *name)
 {
-        return strcmp (XML_NODE (node)->name, name) == 0;
+        return bonobo_ui_node_has_name_by_id (
+		node, bonobo_ui_node_get_id (name));
 }

 /**
@@ -386,46 +640,255 @@
 }

 /**
- * bonobo_ui_node_to_string:
- * @node: the node tree
- * @recurse: whether to dump its children as well
+ * bonobo_ui_node_transparent:
+ * @node: the node
  *
- * Convert the Node to its XML string representation
- * see also: bonobo_ui_node_free_string
+ * Determines whether @node is transparent. A node is
+ * transparent if it has no content and either no attributes
+ * or a single 'name' attribute.
  *
- * Return value: the string representation or NULL on error
+ * Return value: TRUE if transparent
  **/
-char *
-bonobo_ui_node_to_string (BonoboUINode *node,
-			  gboolean      recurse)
+gboolean
+bonobo_ui_node_transparent (BonoboUINode *node)
 {
-	xmlDoc     *doc;
-	xmlChar    *mem = NULL;
-	int         size;
-
-	doc = xmlNewDoc ("1.0");
-	g_return_val_if_fail (doc != NULL, NULL);
-
-	doc->xmlRootNode = XML_NODE(bonobo_ui_node_copy (node, TRUE));
-	g_return_val_if_fail (doc->xmlRootNode != NULL, NULL);
-
-	if (!recurse && bonobo_ui_node_children (BNODE (doc->xmlRootNode))) {
-		BonoboUINode *tmp;
-		while ((tmp = bonobo_ui_node_children (BNODE (doc->xmlRootNode)))) {
-			xmlUnlinkNode (XML_NODE(tmp));
-			bonobo_ui_node_free (tmp);
+	gboolean ret = FALSE;
+	static guint32 name_id = 0;
+
+	g_return_val_if_fail (node != NULL, TRUE);
+
+	if (!name_id)
+		name_id = bonobo_ui_node_get_id ("name");
+
+	if (node->content)
+		ret = FALSE;
+
+	else if (node->attrs->len == 0)
+		ret = TRUE;
+
+	else if (node->attrs->len == 1 && attr (node, 0).id == name_id)
+		ret = TRUE;
+
+	return ret;
+}
+
+/**
+ * bonobo_ui_node_copy_attrs:
+ * @src: the attr source node
+ * @dest: where to dump the attrs.
+ *
+ * This function copies all the attributes from @src to @dest
+ * effectively cloning the @src node as @dest
+ **/
+void
+bonobo_ui_node_copy_attrs (const BonoboUINode *src,
+			   BonoboUINode       *dest)
+{
+	int i;
+
+	if (dest->attrs)
+		node_free_attrs (dest);
+
+	dest->attrs = g_array_new (FALSE, FALSE, sizeof (BonoboUIAttr));
+	g_array_set_size (dest->attrs, src->attrs->len);
+
+	for (i = 0; i < src->attrs->len; i++) {
+		BonoboUIAttr *as = &attr (src, i);
+		BonoboUIAttr *ad = &attr (dest, i);
+
+		ad->id    = as->id;
+		ad->value = as->value ? xmlStrdup (as->value) : NULL;
+	}
+}
+
+/**
+ * bonobo_ui_node_strip:
+ * @node: a pointer to the node's pointer
+ *
+ * A compat function for legacy reasons.
+ **/
+void
+bonobo_ui_node_strip (BonoboUINode **node)
+{
+}
+
+
+/* SAX Parser */
+
+typedef struct {
+	BonoboUINode *root;
+
+	BonoboUINode *cur;
+
+	GString      *content;
+} ParseState;
+
+static ParseState *
+parse_state_new (void)
+{
+	ParseState *ps = g_new0 (ParseState, 1);
+
+	ps->root = bonobo_ui_node_new ("");
+	ps->cur  = ps->root;
+
+	ps->content = g_string_sized_new (0);
+
+	return ps;
+}
+
+static BonoboUINode *
+parse_state_free (ParseState *ps, gboolean free_root)
+{
+	BonoboUINode *ret = NULL;
+
+	if (ps) {
+		if (!free_root) {
+			ret = ps->root->children;
+			bonobo_ui_node_unlink (ret);
 		}
+
+		bonobo_ui_node_free (ps->root);
+
+		g_string_free (ps->content, TRUE);
+
+		g_free (ps);
 	}
+
+	return ret;
+}

-	xmlDocDumpMemory (doc, &mem, &size);
+static void
+uiStartDocument (ParseState *ps)
+{
+	ps->cur = ps->root;
+}
+
+static void
+uiStartElement (ParseState     *ps,
+		const xmlChar  *name,
+		const xmlChar **attrs)
+{
+	int           i;
+	BonoboUINode *node;
+
+	/* FIXME: we want to keep a stack of list end nodes
+	   here for speed ... */

-	g_return_val_if_fail (mem != NULL, NULL);
+	node = bonobo_ui_node_new_child (ps->cur, name);

-	xmlFreeDoc (doc);
+	ps->cur = node;

-	return mem;
+	for (i = 0; attrs && attrs [i]; i++) {
+		BonoboUIAttr a;
+
+		a.id = bonobo_ui_node_get_id (attrs [i++]);
+		a.value = xmlStrdup (attrs [i]);
+
+		g_array_append_val (node->attrs, a);
+	}
 }

+static void
+uiEndElement (ParseState *ps, const xmlChar *name)
+{
+	if (ps->content->len > 0) {
+		int   i;
+		char *content = ps->content->str;
+
+		for (i = 0; content [i] != '\0'; i++) {
+			if (content [i] != ' ' &&
+			    content [i] != '\t' &&
+			    content [i] != '\n')
+				break;
+		}
+
+		if (content [i] != '\0') {
+			g_free (ps->cur->content);
+			ps->cur->content = content;
+
+			g_string_free (ps->content, FALSE);
+		} else
+			g_string_free (ps->content, TRUE);
+
+		ps->content = g_string_sized_new (0);
+	}
+
+	ps->cur = ps->cur->parent;
+}
+
+static void
+uiCharacters (ParseState *ps, const xmlChar *chars, int len)
+{
+    int i;
+
+    for (i = 0; i < len; i++)
+	    g_string_append_c (ps->content, chars [i]);
+}
+
+static xmlEntityPtr
+uiGetEntity (ParseState *ps, const xmlChar *name)
+{
+	/* FIXME: do we need this ? */
+	return xmlGetPredefinedEntity (name);
+}
+
+static void
+uiWarning (ParseState *ps, const char *msg, ...)
+{
+	va_list args;
+
+	va_start (args, msg);
+	g_logv   ("XML", G_LOG_LEVEL_WARNING, msg, args);
+	va_end   (args);
+}
+
+static void
+uiError (ParseState *ps, const char *msg, ...)
+{
+	va_list args;
+
+	va_start (args, msg);
+	g_logv   ("XML", G_LOG_LEVEL_CRITICAL, msg, args);
+	va_end   (args);
+}
+
+static void
+uiFatalError (ParseState *ps, const char *msg, ...)
+{
+	va_list args;
+
+	va_start (args, msg);
+	g_logv   ("XML", G_LOG_LEVEL_ERROR, msg, args);
+	va_end   (args);
+}
+
+static xmlSAXHandler bonoboSAXParser = {
+	NULL, /* internalSubset */
+	NULL, /* isStandalone */
+	NULL, /* hasInternalSubset */
+	NULL, /* hasExternalSubset */
+	NULL, /* resolveEntity */
+	(getEntitySAXFunc) uiGetEntity, /* getEntity */
+	NULL, /* entityDecl */
+	NULL, /* notationDecl */
+	NULL, /* attributeDecl */
+	NULL, /* elementDecl */
+	NULL, /* unparsedEntityDecl */
+	NULL, /* setDocumentLocator */
+	(startDocumentSAXFunc) uiStartDocument, /* startDocument */
+	(endDocumentSAXFunc) NULL, /* endDocument */
+	(startElementSAXFunc) uiStartElement, /* startElement */
+	(endElementSAXFunc) uiEndElement, /* endElement */
+	NULL, /* reference */
+	(charactersSAXFunc) uiCharacters, /* characters */
+	NULL, /* ignorableWhitespace */
+	NULL, /* processingInstruction */
+	NULL, /* comment */
+	(warningSAXFunc) uiWarning, /* warning */
+	(errorSAXFunc) uiError, /* error */
+	(fatalErrorSAXFunc) uiFatalError, /* fatalError */
+};
+
 /**
  * bonobo_ui_node_from_string:
  * @xml: the xml string
@@ -437,23 +900,24 @@
 BonoboUINode*
 bonobo_ui_node_from_string (const char *xml)
 {
-	/* We have crap error reporting for this function */
-	xmlDoc  *doc;
-	BonoboUINode *node;
-
-	doc = xmlParseDoc ((char *)xml);
-	if (!doc)
+	ParseState *ps;
+	guint32     len;
+
+	g_return_val_if_fail (xml != NULL, NULL);
+
+	len = strlen (xml);
+	if (len < 4)
 		return NULL;

-	node = BNODE (doc->xmlRootNode);
-	bonobo_ui_node_strip (&node);
+	ps = parse_state_new ();

-	xmlUnlinkNode (XML_NODE (node));
-	doc->xmlRootNode = NULL;
-
-	xmlFreeDoc (doc);
+	if (xmlSAXUserParseMemory (&bonoboSAXParser, ps, (char *)xml, len) < 0) {
+		g_warning ("XML not well formed!");
+		parse_state_free (ps, TRUE);
+		return NULL;
+	}

-	return node;
+	return parse_state_free (ps, FALSE);
 }

 /**
@@ -467,165 +931,121 @@
 BonoboUINode*
 bonobo_ui_node_from_file (const char *fname)
 {
-	/* Error reporting blows here too (because it blows
-	 * in libxml)
-	 */
-	xmlDoc  *doc;
-	BonoboUINode *node;
+	ParseState *ps;

 	g_return_val_if_fail (fname != NULL, NULL);

-	doc = xmlParseFile (fname);
-
-	g_return_val_if_fail (doc != NULL, NULL);
-
-	node = BNODE (doc->xmlRootNode);
-	bonobo_ui_node_strip (&node);
+	ps = parse_state_new ();

-	xmlUnlinkNode (XML_NODE (node));
-	doc->xmlRootNode = NULL;
-
-	xmlFreeDoc (doc);
+	if (xmlSAXUserParseFile (&bonoboSAXParser, ps, fname) < 0) {
+		g_warning ("XML not well formed!");
+		parse_state_free (ps, TRUE);
+		return NULL;
+	}

-	return node;
+	return parse_state_free (ps, FALSE);
 }

-/**
- * bonobo_ui_node_transparent:
- * @node: the node
- *
- * Determines whether @node is transparent. A node is
- * transparent if it has no content and either no attributes
- * or a single 'name' attribute.
- *
- * Return value: TRUE if transparent
- **/
-gboolean
-bonobo_ui_node_transparent (BonoboUINode *node)
+static void
+internal_to_string (GString      *str,
+		    BonoboUINode *node,
+		    gboolean      recurse)
 {
-	xmlNode *n = XML_NODE (node);
-	gboolean ret = FALSE;
+	int         i;
+	gboolean    contains;
+	const char *tag_name;

-	g_return_val_if_fail (n != NULL, TRUE);
+	contains = node->content || (node->children && recurse);
+	tag_name = bonobo_ui_node_str_from_id (node->name_id);
+
+	g_string_append_c (str, '<');
+	g_string_append   (str, tag_name);

-	if (n->content) {
-		ret = FALSE;
+	for (i = 0; i < node->attrs->len; i++) {
+		BonoboUIAttr *a = &attr (node, i);

-	} else if (!n->properties) {
-		ret = TRUE;
+		if (!a->id)
+			continue;

-	} else if (!n->properties->next) {
-		if (!strcmp (n->properties->name, "name"))
-			ret = TRUE;
+		g_string_append_c (str, ' ');
+		g_string_append   (str, bonobo_ui_node_str_from_id (a->id));
+		g_string_append_c (str, '=');
+		g_string_append_c (str, '\"');
+		g_string_append   (str, a->value);
+		g_string_append_c (str, '\"');
 	}
-
-	return ret;
-}

-/**
- * bonobo_ui_node_copy_attrs:
- * @src: the attr source node
- * @dest: where to dump the attrs.
- *
- * This function copies all the attributes from @src to @dest
- * effectively cloning the @src node as @dest
- **/
-void
-bonobo_ui_node_copy_attrs (BonoboUINode *src,
-			   BonoboUINode *dest)
-{
-	xmlAttr *attr;
-
-	for (attr = XML_NODE (src)->properties; attr; attr = attr->next) {
-		char *txt = xmlGetProp (XML_NODE (src), attr->name);
-
-		g_assert (txt != NULL);
+	if (contains) {
+		g_string_append_c (str, '>');
+
+		if (recurse && node->children) {
+			BonoboUINode *l;
+
+			g_string_append (str, "\n");
+
+			for (l = node->children; l; l = l->next)
+				internal_to_string (str, l, recurse);
+		}

-		xmlSetProp (XML_NODE (dest), attr->name, txt);
+		if (node->content)
+			g_string_append (str, node->content);

-		xmlFree (txt);
-	}
+		g_string_append   (str, "</");
+		g_string_append   (str, tag_name);
+		g_string_append   (str, ">\n");
+	} else
+		g_string_append   (str, "/>\n");
 }

-static gboolean
-do_strip (xmlNode *node)
+#if 0
+static void
+validate_tree (BonoboUINode *node)
 {
-        xmlNode *l, *next;
-	gboolean suspicious = FALSE;
+	BonoboUINode *l, *last = NULL;

 	if (!node)
-		return FALSE;
+		return;

-	switch (node->type) {
-        case XML_DOCUMENT_FRAG_NODE:
-        case XML_ELEMENT_NODE:
-	case XML_TEXT_NODE:
-        case XML_ENTITY_NODE:
-        case XML_ENTITY_REF_NODE: {
-		xmlAttr *a, *nexta;
-
-		node->nsDef = NULL;
-		node->ns = NULL;
-		node->doc = NULL;
-
-		for (a = node->properties; a; a = nexta) {
-			nexta = a->next;
-			a->ns = NULL;
-			do_strip (a->val);
-		}
+	for (l = node->children; l; l = l->next) {

-		for (l = node->xmlChildrenNode; l; l = next) {
-			next = l->next;
-			do_strip (l);
-		}
-		break;
+		if (l->parent != node)
+			g_warning ("Parent chaining error on '%p' parent %p should be %p",
+				   l, l->parent, node);
+
+		if (l->prev != last)
+			g_warning ("Previous chaining error on '%p' prev %p should be %p",
+				   l, l->prev, last);
+		validate_tree (l);
+
+		last = l;
 	}
-
-	case XML_ATTRIBUTE_NODE: {
-		xmlAttr *attr = (xmlAttr *)node;
-		attr->ns = NULL;
-		do_strip (attr->val);
-		break;
-	}
-
-        case XML_PI_NODE:
-        case XML_COMMENT_NODE:
-        case XML_DOCUMENT_NODE:
-        case XML_HTML_DOCUMENT_NODE:
-        case XML_DOCUMENT_TYPE_NODE:
-        case XML_NOTATION_NODE:
-        case XML_CDATA_SECTION_NODE:
-		suspicious = TRUE;
-		break;
-	}
-
-	if (suspicious) {
-/*		g_warning ("node looks suspicious %d: '%s'",
-			   node->type,
-			   bonobo_ui_node_to_string (BNODE (node), TRUE));*/
-		xmlUnlinkNode (node);
-		bonobo_ui_node_free (BNODE (node));
-		return TRUE;
-	} else
-		return FALSE;
 }
+#endif

 /**
- * bonobo_ui_node_strip:
- * @node: a pointer to the node's pointer
+ * bonobo_ui_node_to_string:
+ * @node: the node tree
+ * @recurse: whether to dump its children as well
+ *
+ * Convert the Node to its XML string representation
+ * see also: bonobo_ui_node_free_string
  *
- *   This function is used to purge unwanted content from
- * a set of nodes, and particularly clean up stray Doc and
- * NS pointers that cause serious trouble later.
+ * Return value: the string representation or NULL on error
  **/
-void
-bonobo_ui_node_strip (BonoboUINode **node)
+char *
+bonobo_ui_node_to_string (BonoboUINode *node,
+			  gboolean      recurse)
 {
-	BonoboUINode *next, *l;
+	char    *ret;
+	GString *str = g_string_sized_new (64);

-	for (l = *node; l; l = next) {
-		next = bonobo_ui_node_next (l);
-		if (l == *node && do_strip (XML_NODE (l)))
-			*node = next;
-	}
+	internal_to_string (str, node, recurse);
+
+/*	fprintf (stderr, "nodes to string: '%s'", str->str); */
+/*	validate_tree (node); */
+
+	ret = str->str;
+	g_string_free (str, FALSE);
+
+	return ret;
 }
Index: bonobo/bonobo-ui-node.h
===================================================================
RCS file: /cvs/gnome/bonobo/bonobo/bonobo-ui-node.h,v
retrieving revision 1.5
diff -u -r1.5 bonobo-ui-node.h
--- bonobo/bonobo-ui-node.h	2001/05/24 08:40:07	1.5
+++ bonobo/bonobo-ui-node.h	2001/08/22 16:14:42
@@ -50,7 +50,7 @@
 gboolean      bonobo_ui_node_has_name    (BonoboUINode *node,
 					  const char   *name);
 gboolean      bonobo_ui_node_transparent (BonoboUINode *node);
-void          bonobo_ui_node_copy_attrs  (BonoboUINode *src,
+void          bonobo_ui_node_copy_attrs  (const BonoboUINode *src,
 					  BonoboUINode *dest);

 /* This blows. libxml2 fixes it I guess. */
Index: bonobo/bonobo-ui-util.c
===================================================================
RCS file: /cvs/gnome/bonobo/bonobo/bonobo-ui-util.c,v
retrieving revision 1.47
diff -u -r1.47 bonobo-ui-util.c
--- bonobo/bonobo-ui-util.c	2001/08/21 23:47:11	1.47
+++ bonobo/bonobo-ui-util.c	2001/08/22 16:14:42
@@ -13,11 +13,8 @@

 #include <bonobo/bonobo-ui-xml.h>
 #include <bonobo/bonobo-ui-util.h>
+#include <bonobo/bonobo-ui-node-private.h>

-#include <gnome-xml/tree.h>
-#include <gnome-xml/parser.h>
-#include <gnome-xml/xmlmemory.h>
-
 static const char write_lut[16] = {
 	'0', '1', '2', '3', '4', '5', '6', '7',
 	'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
@@ -1035,12 +1032,6 @@
 	return NULL;
 }

-
-/* To avoid exporting property iterators on BonoboUINode
- * (not sure those should be public), this hack is used.
- */
-#define XML_NODE(x) ((xmlNode*)(x))
-
 /**
  * bonobo_ui_util_translate_ui:
  * @node: the node to start at.
@@ -1050,51 +1041,37 @@
  * property and removes the leading '_'.
  **/
 void
-bonobo_ui_util_translate_ui (BonoboUINode *bnode)
+bonobo_ui_util_translate_ui (BonoboUINode *node)
 {
         BonoboUINode *l;
-        xmlNode *node;
-	xmlAttr *prop;
+	int           i;

-	if (!bnode)
+	if (!node)
 		return;

-	bonobo_ui_node_strip (&bnode);
-	if (!bnode) {
-		g_warning ("All xml stripped away");
-		return;
-	}
+	for (i = 0; i < node->attrs->len; i++) {
+		BonoboUIAttr *a;
+		const char   *str;

-	node = XML_NODE (bnode);
-	for (prop = node->properties; prop; prop = prop->next) {
+		a = &g_array_index (node->attrs, BonoboUIAttr, i);

-		/* FIXME: with more evilness we can make this yet faster */
+		if (!a->id)
+			continue;

-		/* Find translatable properties */
-		if (prop->name && prop->name [0] == '_') {
+		str = bonobo_ui_node_str_from_id (a->id);
+		if (str [0] == '_') {
 			char *encoded;
-			xmlChar *value;
-			xmlChar *newname;
-
-			value = xmlNodeListGetString (NULL, prop->val, 1);

-			encoded = bonobo_ui_util_encode_str (_(value));
-			if (prop->val)
-				xmlFreeNodeList (prop->val);
+			a->id = bonobo_ui_node_get_id (str + 1);

-			/* We know there are no entities, it's a hex string */
-			prop->val = xmlStringGetNodeList (NULL, encoded);
+			encoded = bonobo_ui_util_encode_str (_(a->value));
+			xmlFree (a->value);
+			a->value = xmlStrdup (encoded);
 			g_free (encoded);
-
-			bonobo_ui_node_free_string (value);
-
-			newname = xmlStrdup (prop->name + 1);
-			xmlFree ((xmlChar *)prop->name);
-			prop->name = newname;
-		}
+		}
 	}

-	for (l = bonobo_ui_node_children (bnode); l; l = bonobo_ui_node_next (l))
+	for (l = node->children; l; l = l->next)
 		bonobo_ui_util_translate_ui (l);
 }

@@ -1280,7 +1257,7 @@
 /**
  * bonobo_ui_util_set_ui:
  * @component: the component
- * @app_datadir: the application datadir eg. /opt/gnome/share
+ * @app_datadir: the application datadir eg. /opt/gnome/share or NULL
  * @file_name: the filename of the file to merge relative to the datadir.
  * @app_name: the application name - for help merging
  *
@@ -1320,7 +1297,7 @@

 	/* FIXME: May want to stat the file to see if it changed? */
 	entry.file_name = (char *)fname;
-	entry.app_datadir = (char *)app_datadir;
+	entry.app_datadir = (char *) app_datadir;
 	entry.app_name = (char *)app_name;

 	cached = g_hash_table_lookup (loaded_node_cache, &entry);
Index: bonobo/bonobo-ui-xml.c
===================================================================
RCS file: /cvs/gnome/bonobo/bonobo/bonobo-ui-xml.c,v
retrieving revision 1.54
diff -u -r1.54 bonobo-ui-xml.c
--- bonobo/bonobo-ui-xml.c	2001/02/23 23:27:59	1.54
+++ bonobo/bonobo-ui-xml.c	2001/08/22 16:14:42
@@ -11,11 +11,8 @@
 #include <string.h>
 #include <gtk/gtksignal.h>
 #include <bonobo/bonobo-ui-xml.h>
+#include <bonobo/bonobo-ui-node-private.h>

-#include <gnome-xml/tree.h>
-#include <gnome-xml/parser.h>
-
-
 #undef UI_XML_DEBUG
 #undef BONOBO_UI_XML_DUMP

@@ -25,17 +22,14 @@
 #	define DUMP_XML(a,b,c)
 #endif

+static guint32 pos_id = 0;
+static guint32 name_id = 0;
+static guint32 placeholder_id = 0;
+
 static void watch_update  (BonoboUIXml  *tree,
 			   BonoboUINode *node);
 static void watch_destroy (gpointer      data);

-/*
- * Change these to not have a cast in order to find all
- * bad hack casts
- */
-#define XML_NODE(x) ((xmlNode*)(x))
-#define BNODE(x)    ((BonoboUINode*)(x))
-
 static GtkObjectClass *bonobo_ui_xml_parent_class;

 enum {
@@ -165,8 +159,7 @@
 		 * live in bonobo-win.c for cleanliness and never in this
 		 * more generic code.
 		 */
-
-		if (!strcmp (bonobo_ui_node_get_name (l), "placeholder"))
+		if (l->name_id == placeholder_id)
 			i--;

 		data = bonobo_ui_xml_get_data (tree, l);
@@ -268,17 +261,15 @@

 	/* Do some basic validation here ? */
 	{
-		char *p, *name;
+		const char *p, *name;

-		if ((name = bonobo_ui_node_get_attr (node, "name"))) {
+		if ((name = bonobo_ui_node_get_attr_by_id (node, name_id))) {
 			/*
 			 *  The consequences of this are so unthinkable
 			 * an assertion is warrented.
 			 */
 			for (p = name; *p; p++)
 				g_assert (*p != '/' && *p != '#');
-
-			bonobo_ui_node_free_string (name);
 		}
 	}

@@ -352,49 +343,20 @@
  **/
 void
 bonobo_ui_xml_dump (BonoboUIXml  *tree,
-		    BonoboUINode *bnode,
+		    BonoboUINode *node,
 		    const char   *descr)
 {
-	/* XML_NODE() hack used here since there's no public "dump node" API */
-	xmlDoc *doc;
-        xmlNode *node = XML_NODE (bnode);
-
-	doc = xmlNewDoc ("1.0");
-	doc->xmlRootNode = node;
-
-	fprintf (stderr, "%s\n", descr);
+	gchar *str;

-	xmlDocDump (stderr, doc);
+	str = bonobo_ui_node_to_string (node, TRUE);
+	fprintf (stderr, "Dump '%s':\n%s\n", descr, str);
+	g_free (str);

-	doc->xmlRootNode = NULL;
-	xmlFreeDoc (doc);
 	fprintf (stderr, "--- Internals ---\n");
-	dump_internals (tree, BNODE (node));
+	dump_internals (tree, node);
 	fprintf (stderr, "---\n");
 }

-/*
- * Re-parenting should be in libxml but isn't
- */
-static void
-move_children (BonoboUINode *from, BonoboUINode *to)
-{
-	BonoboUINode *l, *next;
-
-	g_return_if_fail (to != NULL);
-	g_return_if_fail (from != NULL);
-	g_return_if_fail (bonobo_ui_node_children (to) == NULL);
-
-	for (l = bonobo_ui_node_children (from); l; l = next) {
-		next = bonobo_ui_node_next (l);
-
-		bonobo_ui_node_unlink (l);
-		bonobo_ui_node_add_child (to, l);
-	}
-
-	g_assert (bonobo_ui_node_children (from) == NULL);
-}
-
 static void
 prune_overrides_by_id (BonoboUIXml *tree, BonoboUIXmlData *data, gpointer id)
 {
@@ -456,13 +418,12 @@

 	old_data->overridden = NULL;

- 	/* This code doesn't work right with opaque BonoboUINode object */
  	if (bonobo_ui_node_children (new))
- 		merge (tree, old, (BonoboUINode**)&XML_NODE (new)->xmlChildrenNode);
+ 		merge (tree, old, &new->children);

-	move_children (old, new);
+	bonobo_ui_node_move_children (old, new);

-	xmlReplaceNode (XML_NODE (old), XML_NODE (new));
+	bonobo_ui_node_replace (old, new);

 	g_assert (bonobo_ui_node_children (old) == NULL);

@@ -507,10 +468,10 @@
 		gtk_signal_emit (GTK_OBJECT (tree), signals [REMOVE], node);

 		/* Move children across */
-		move_children (node, old);
+		bonobo_ui_node_move_children (node, old);

 		/* Switch node back into tree */
-		bonobo_ui_node_replace (old, node);
+		bonobo_ui_node_replace (node, old);

 		/* Mark dirty */
 		bonobo_ui_xml_set_dirty (tree, old);
@@ -557,14 +518,12 @@
 	g_return_val_if_fail (name != NULL, NULL);
 	g_return_val_if_fail (node != NULL, NULL);

-	for (l = bonobo_ui_node_children (node); l && !ret; l = bonobo_ui_node_next (l)) {
-		char *txt;
+	for (l = node->children; l && !ret; l = l->next) {
+		const char *txt;

-		if ((txt = bonobo_ui_node_get_attr (l, "name"))) {
+		if ((txt = bonobo_ui_node_get_attr_by_id (l, name_id))) {
 			if (!strcmp (txt, name))
 				ret = l;
-
-			bonobo_ui_node_free_string (txt);
 		}

 		if (!ret && bonobo_ui_node_has_name (l, name))
@@ -715,7 +674,7 @@
 #ifdef UI_XML_DEBUG
 	fprintf (stderr, "Find path '%s'\n", path);
 #endif
-	DUMP_XML (tree, tree->root, "Before find path");
+/*	DUMP_XML (tree, tree->root, "Before find path"); */

 	if (allow_wild)
 		*wildcard = FALSE;
@@ -747,7 +706,7 @@

 	bonobo_ui_xml_path_freev (names);

-	DUMP_XML (tree, tree->root, "After clean find path");
+/*	DUMP_XML (tree, tree->root, "After clean find path"); */

 	return ret;
 }
@@ -796,33 +755,33 @@
  * Return value: the path name, use g_free to release.
  **/
 char *
-bonobo_ui_xml_make_path  (BonoboUINode *node)
+bonobo_ui_xml_make_path (BonoboUINode *node)
 {
-	GString *path;
-	char    *tmp;
+	GString    *path;
+	char       *ret;

 	g_return_val_if_fail (node != NULL, NULL);

 	path = g_string_new ("");
-	while (node && bonobo_ui_node_parent (node)) {
+	while (node && node->parent) {
+		const char *tmp;

-		if ((tmp = bonobo_ui_node_get_attr (node, "name"))) {
+		if ((tmp = bonobo_ui_node_get_attr_by_id (node, name_id))) {
 			g_string_prepend (path, tmp);
 			g_string_prepend (path, "/");
-			bonobo_ui_node_free_string (tmp);
 		} else {
 			g_string_prepend (path, bonobo_ui_node_get_name (node));
 			g_string_prepend (path, "/");
 		}

-		node = bonobo_ui_node_parent (node);
+		node = node->parent;
 	}

-	tmp = path->str;
+	ret = path->str;
 	g_string_free (path, FALSE);

 /*	fprintf (stderr, "Make path: '%s'\n", tmp);*/
-	return tmp;
+	return ret;
 }

 static void
@@ -853,17 +812,16 @@
 	   BonoboUINode *child,
 	   BonoboUINode *insert)
 {
-	char    *pos;
-	gboolean added = FALSE;
+	const char *pos;
+	gboolean    added = FALSE;

-	if ((pos = bonobo_ui_node_get_attr (child, "pos"))) {
+	if ((pos = bonobo_ui_node_get_attr_by_id (child, pos_id))) {
 		if (pos [0] == 't') {
 			bonobo_ui_node_insert_before (
 				bonobo_ui_node_children (parent),
 				child);
 			added = TRUE;
 		}
-		bonobo_ui_node_free_string (pos);
 	}

 	if (!added) {
@@ -880,31 +838,28 @@
 {
 	BonoboUINode *a, *b, *nexta, *nextb, *insert = NULL;

-	for (a = bonobo_ui_node_children (current); a; a = nexta) {
+	for (a = current->children; a; a = nexta) {
 		BonoboUINode *result;
-		xmlChar *a_name;
-		xmlChar *b_name = NULL;
+		const xmlChar *a_name;
+		const xmlChar *b_name = NULL;

-		nexta = bonobo_ui_node_next (a);
+		nexta = a->next;
 		nextb = NULL;

-		a_name = bonobo_ui_node_get_attr (a, "name");
+		a_name = bonobo_ui_node_get_attr_by_id (a, name_id);

 		for (b = *new; b; b = nextb) {
-			nextb = bonobo_ui_node_next (b);
+			nextb = b->next;

-			bonobo_ui_node_free_string (b_name);
 			b_name = NULL;

 /*			printf ("'%s' '%s' with '%s' '%s'\n",
 				a->name, bonobo_ui_node_get_attr (a, "name"),
 				b->name, bonobo_ui_node_get_attr (b, "name"));*/

-			b_name = bonobo_ui_node_get_attr (b, "name");
+			b_name = bonobo_ui_node_get_attr_by_id (b, name_id);

-			if (!a_name && !b_name &&
-                            bonobo_ui_node_has_name (
-				    a, bonobo_ui_node_get_name (b)))
+			if (!a_name && !b_name && a->name_id == b->name_id)
 				break;

 			if (!a_name || !b_name)
@@ -913,7 +868,6 @@
 			if (!strcmp (a_name, b_name))
 				break;
 		}
-		bonobo_ui_node_free_string (b_name);

 		if (b == *new)
 			*new = nextb;
@@ -923,17 +877,14 @@

 		result = b ? b : a;

-		if (!insert && !a_name &&
-		    bonobo_ui_node_has_name (result, "placeholder"))
+		if (!insert && !a_name && result->name_id == placeholder_id)
 			insert = result;
-
-		bonobo_ui_node_free_string (a_name);
 	}

 	for (b = *new; b; b = nextb) {
 		BonoboUIXmlData *data;

-		nextb = bonobo_ui_node_next (b);
+		nextb = b->next;

 /*		fprintf (stderr, "Transfering '%s' '%s' into '%s' '%s'\n",
 			 b->name, bonobo_ui_node_get_attr (b, "name"),
@@ -992,11 +943,11 @@

 	current = bonobo_ui_xml_get_path (tree, path);
 	if (!current) {
-		xmlNode *l, *next;
+		BonoboUINode *l, *next;

-		for (l = XML_NODE (nodes); l; l = next) {
+		for (l = nodes; l; l = next) {
 			next = l->next;
-			node_free (tree, BNODE (l));
+			node_free (tree, l);
 		}

 		return BONOBO_UI_ERROR_INVALID_PATH;
@@ -1035,7 +986,7 @@
 		tree, path, &wildcard);

 /*	fprintf (stderr, "remove stuff from '%s' (%d) -> '%p'\n",
-	path, wildcard, current);*/
+	path, wildcard, current); */

 	if (current)
 		reinstate_node (tree, current, id, !wildcard);
@@ -1072,6 +1023,10 @@
 bonobo_ui_xml_class_init (BonoboUIXmlClass *klass)
 {
 	GtkObjectClass *object_class = (GtkObjectClass *) klass;
+
+	pos_id         = bonobo_ui_node_get_id ("pos");
+	name_id        = bonobo_ui_node_get_id ("name");
+	placeholder_id = bonobo_ui_node_get_id ("placeholder");

 	bonobo_ui_xml_parent_class = gtk_type_class (
 		gtk_object_get_type ());
Index: tests/test-ui.c
===================================================================
RCS file: /cvs/gnome/bonobo/tests/test-ui.c,v
retrieving revision 1.57
diff -u -r1.57 test-ui.c
--- tests/test-ui.c	2001/08/20 16:19:16	1.57
+++ tests/test-ui.c	2001/08/22 16:14:42
@@ -619,10 +619,16 @@

 	{
 		char *txt = bonobo_ui_component_get (componenta, "/status/main", TRUE, NULL);
-		if (!txt || strcmp (txt, "<?xml version=\"1.0\"?>\n<item name=\"main\">576861744136</item>\n")) {
-			g_warning ("Broken merging code '%s'", txt);
+		char *foo = bonobo_ui_util_encode_str ("WhatA6");
+		const char *good = "<item name=\"main\">576861744136</item>\n";
+
+		if (!txt || strcmp (txt, good)) {
+			g_warning ("Broken merging code '%s' should be '%s' 'WhatA6' = '%s'",
+				   txt, good, foo);
+			bonobo_window_dump (win, "on fatal error");
 			g_assert_not_reached ();
 		}
+		g_free (foo);
 	}

 	gtk_main ();

-- 
 mmeeks gnu org  <><, Pseudo Engineer, itinerant idiot





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