← Back to team overview

ayatana-commits team mailing list archive

[Branch ~dbusmenu-team/dbusmenu/trunk] Rev 74: Adds a menuitem proxy object to glib and applies various fixes through that this uncovered.

 

Merge authors:
  Ted Gould (ted)
Related merge proposals:
  https://code.launchpad.net/~ted/dbusmenu/menuitem-proxy/+merge/19258
  proposed by: Ted Gould (ted)
  review: Approve - David Barth (dbarth)
------------------------------------------------------------
revno: 74 [merge]
committer: Ted Gould <ted@xxxxxxxx>
branch nick: trunk
timestamp: Thu 2010-02-18 10:27:53 -0600
message:
  Adds a menuitem proxy object to glib and applies various fixes through that this uncovered.
added:
  libdbusmenu-glib/menuitem-proxy.c
  libdbusmenu-glib/menuitem-proxy.h
  tests/test-glib-proxy-client.c
  tests/test-glib-proxy-proxy.c
  tests/test-glib-proxy-server.c
  tests/test-glib-proxy.h
modified:
  .bzrignore
  libdbusmenu-glib/Makefile.am
  libdbusmenu-glib/client.c
  libdbusmenu-glib/server.c
  tests/Makefile.am


--
lp:dbusmenu
https://code.launchpad.net/~dbusmenu-team/dbusmenu/trunk

Your team ayatana-commits is subscribed to branch lp:dbusmenu.
To unsubscribe from this branch go to https://code.launchpad.net/~dbusmenu-team/dbusmenu/trunk/+edit-subscription.
=== modified file '.bzrignore'
--- .bzrignore	2010-02-05 05:35:32 +0000
+++ .bzrignore	2010-02-09 21:22:31 +0000
@@ -59,3 +59,8 @@
 tests/test-glib-objects.xml
 tools/testapp/dbusmenu-testapp
 libdbusmenu-glib/libdbusmenu_glib_la-client-menuitem.lo
+libdbusmenu-glib/libdbusmenu_glib_la-menuitem-proxy.lo
+tests/test-glib-proxy-client
+tests/test-glib-proxy-server
+tests/test-glib-proxy-proxy
+tests/test-glib-proxy

=== modified file 'libdbusmenu-glib/Makefile.am'
--- libdbusmenu-glib/Makefile.am	2010-02-04 02:00:36 +0000
+++ libdbusmenu-glib/Makefile.am	2010-02-09 16:22:30 +0000
@@ -12,6 +12,7 @@
 
 libdbusmenu_glibinclude_HEADERS = \
 	menuitem.h \
+	menuitem-proxy.h \
 	server.h \
 	client.h
 
@@ -23,6 +24,8 @@
 	menuitem-marshal.h \
 	menuitem-marshal.c \
 	menuitem-private.h \
+	menuitem-proxy.h \
+	menuitem-proxy.c \
 	server.h \
 	server.c \
 	server-marshal.h \

=== modified file 'libdbusmenu-glib/client.c'
--- libdbusmenu-glib/client.c	2010-02-09 17:13:51 +0000
+++ libdbusmenu-glib/client.c	2010-02-18 16:27:53 +0000
@@ -75,6 +75,8 @@
 	DBusGProxy * dbusproxy;
 
 	GHashTable * type_handlers;
+
+	GArray * delayed_properties;
 };
 
 typedef struct _newItemPropData newItemPropData;
@@ -85,6 +87,21 @@
 	DbusmenuMenuitem * parent;
 };
 
+typedef struct _propertyDelay propertyDelay;
+struct _propertyDelay
+{
+	guint revision;
+	GArray * entries;
+};
+
+typedef struct _propertyDelayValue propertyDelayValue;
+struct _propertyDelayValue
+{
+	gint id;
+	gchar * name;
+	GValue value;
+};
+
 #define DBUSMENU_CLIENT_GET_PRIVATE(o) \
 (G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_CLIENT, DbusmenuClientPrivate))
 
@@ -208,6 +225,8 @@
 	priv->type_handlers = g_hash_table_new_full(g_str_hash, g_str_equal,
 	                                            g_free, NULL);
 
+	priv->delayed_properties = g_array_new(FALSE, TRUE, sizeof(propertyDelay));
+
 	return;
 }
 
@@ -255,6 +274,23 @@
 		g_hash_table_destroy(priv->type_handlers);
 	}
 
+	if (priv->delayed_properties) {
+		gint i;
+		for (i = 0; i < priv->delayed_properties->len; i++) {
+			propertyDelay * delay = &g_array_index(priv->delayed_properties, propertyDelay, i);
+			gint j;
+			for (j = 0; j < delay->entries->len; j++) {
+				propertyDelayValue * value = &g_array_index(delay->entries, propertyDelayValue, j);
+				g_free(value->name);
+				g_value_unset(&value->value);
+			}
+			g_array_free(delay->entries, TRUE);
+			delay->entries = NULL;
+		}
+		g_array_free(priv->delayed_properties, TRUE);
+		priv->delayed_properties = NULL;
+	}
+
 	G_OBJECT_CLASS (dbusmenu_client_parent_class)->finalize (object);
 	return;
 }
@@ -319,6 +355,49 @@
 	return;
 }
 
+/* Add an entry to the set of entries that are delayed until the
+   layout has been updated to this revision */
+static void
+delay_prop_update (guint revision, GArray * delayarray, gint id, gchar * prop, GValue * value)
+{
+	propertyDelay * delay = NULL;
+	gint i;
+
+	/* First look for something with this revision number.  This
+	   array should be really short, probably not more than an entry or
+	   two so there is no reason to optimize this. */
+	for (i = 0; i < delayarray->len; i++) {
+		propertyDelay * localdelay = &g_array_index(delayarray, propertyDelay, i);
+		if (localdelay->revision == revision) {
+			delay = localdelay;
+			break;
+		}
+	}
+
+	/* If we don't have any entires for this revision number then we
+	   need to create a new one with it's own array of entires. */
+	if (delay == NULL) {
+		propertyDelay localdelay = {0};
+		localdelay.revision = revision;
+		localdelay.entries = g_array_new(FALSE, TRUE, sizeof(propertyDelayValue));
+
+		g_array_append_val(delayarray, localdelay);
+		delay = &g_array_index(delayarray, propertyDelay, delayarray->len - 1);
+	}
+
+	/* Build the actual entry and tack it on the end of the array
+	   of entries */
+	propertyDelayValue delayvalue = {0};
+	delayvalue.id = id;
+	delayvalue.name = g_strdup(prop);
+
+	g_value_init(&delayvalue.value, G_VALUE_TYPE(value));
+	g_value_copy(value, &delayvalue.value);
+
+	g_array_append_val(delay->entries, delayvalue);
+	return;
+}
+
 /* Signal from the server that a property has changed
    on one of our menuitems */
 static void
@@ -333,7 +412,16 @@
 	#endif
 
 	DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
-	g_return_if_fail(priv->root != NULL);
+
+	/* If we're not on the right revision, we need to cache the property
+	   changes as it could be that the menuitems don't exist yet. */
+	if (priv->root == NULL || priv->my_revision != priv->current_revision) {
+		#ifdef MASSIVEDEBUGGING
+		g_debug("Delaying prop update until rev %d for id %d property %s", priv->current_revision, id, property);
+		#endif
+		delay_prop_update(priv->current_revision, priv->delayed_properties, id, property, value);
+		return;
+	}
 
 	DbusmenuMenuitem * menuitem = dbusmenu_menuitem_find_id(priv->root, id);
 	g_return_if_fail(menuitem != NULL);
@@ -810,16 +898,19 @@
 	DbusmenuClient * client = DBUSMENU_CLIENT(data);
 	DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
 
+	/* Check to make sure this isn't an issue */
 	if (error != NULL) {
 		g_warning("Getting layout failed on client %s object %s: %s", priv->dbus_name, priv->dbus_object, error->message);
 		return;
 	}
 
+	/* Try to take in the layout that we got */
 	if (!parse_layout(client, xml)) {
 		g_warning("Unable to parse layout!");
 		return;
 	}
 
+	/* Success, so we need to update our local variables */
 	priv->my_revision = rev;
 	/* g_debug("Root is now: 0x%X", (unsigned int)priv->root); */
 	priv->layoutcall = NULL;
@@ -828,6 +919,44 @@
 	#endif 
 	g_signal_emit(G_OBJECT(client), signals[LAYOUT_UPDATED], 0, TRUE);
 
+	/* Apply the delayed properties that were queued up while
+	   we were waiting on this layout update. */
+	if (G_LIKELY(priv->delayed_properties != NULL)) {
+		gint i;
+		for (i = 0; i < priv->delayed_properties->len; i++) {
+			propertyDelay * delay = &g_array_index(priv->delayed_properties, propertyDelay, i);
+			if (delay->revision > priv->my_revision) {
+				/* Check to see if this is for future revisions, which
+				   is possible if there is a ton of updates. */
+				break;
+			}
+
+			gint j;
+			for (j = 0; j < delay->entries->len; j++) {
+				propertyDelayValue * value = &g_array_index(delay->entries, propertyDelayValue, j);
+				DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, value->id);
+				if (mi != NULL) {
+					#ifdef MASSIVEDEBUGGING
+					g_debug("Applying delayed property id %d property %s", value->id, value->name);
+					#endif
+					dbusmenu_menuitem_property_set_value(mi, value->name, &value->value);
+				}
+				g_free(value->name);
+				g_value_unset(&value->value);
+			}
+			g_array_free(delay->entries, TRUE);
+
+			/* We're removing the entry and moving the index down one
+			   to ensure that we adjust for the shift in the array.  The
+			   reality is that i is always 0.  You understood this loop
+			   until you got here, didn't you :)  */
+			g_array_remove_index(priv->delayed_properties, i);
+			i--;
+		}
+	}
+
+	/* Check to see if we got another update in the time this
+	   one was issued. */
 	if (priv->my_revision < priv->current_revision) {
 		update_layout(client);
 	}

=== added file 'libdbusmenu-glib/menuitem-proxy.c'
--- libdbusmenu-glib/menuitem-proxy.c	1970-01-01 00:00:00 +0000
+++ libdbusmenu-glib/menuitem-proxy.c	2010-02-10 23:08:47 +0000
@@ -0,0 +1,362 @@
+/*
+An object to ferry over properties and signals between two different
+dbusmenu instances.  Useful for services.
+
+Copyright 2010 Canonical Ltd.
+
+Authors:
+    Ted Gould <ted@xxxxxxxxxxxxx>
+
+This program is free software: you can redistribute it and/or modify it 
+under the terms of either or both of the following licenses:
+
+1) the GNU Lesser General Public License version 3, as published by the 
+Free Software Foundation; and/or
+2) the GNU Lesser General Public License version 2.1, as published by 
+the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but 
+WITHOUT ANY WARRANTY; without even the implied warranties of 
+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR 
+PURPOSE.  See the applicable version of the GNU Lesser General Public 
+License for more details.
+
+You should have received a copy of both the GNU Lesser General Public 
+License version 3 and version 2.1 along with this program.  If not, see 
+<http://www.gnu.org/licenses/>
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "menuitem-proxy.h"
+
+typedef struct _DbusmenuMenuitemProxyPrivate DbusmenuMenuitemProxyPrivate;
+struct _DbusmenuMenuitemProxyPrivate {
+	DbusmenuMenuitem * mi;
+	gulong sig_property_changed;
+	gulong sig_child_added;
+	gulong sig_child_removed;
+	gulong sig_child_moved;
+};
+
+/* Properties */
+enum {
+	PROP_0,
+	PROP_MENU_ITEM
+};
+
+#define PROP_MENU_ITEM_S   "menu-item"
+
+#define DBUSMENU_MENUITEM_PROXY_GET_PRIVATE(o) \
+(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_MENUITEM_PROXY, DbusmenuMenuitemProxyPrivate))
+
+static void dbusmenu_menuitem_proxy_class_init (DbusmenuMenuitemProxyClass *klass);
+static void dbusmenu_menuitem_proxy_init       (DbusmenuMenuitemProxy *self);
+static void dbusmenu_menuitem_proxy_dispose    (GObject *object);
+static void dbusmenu_menuitem_proxy_finalize   (GObject *object);
+static void set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec);
+static void get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec);
+static void handle_event (DbusmenuMenuitem * mi, const gchar * name, const GValue * value, guint timestamp);
+static void add_menuitem (DbusmenuMenuitemProxy * pmi, DbusmenuMenuitem * mi);
+static void remove_menuitem (DbusmenuMenuitemProxy * pmi);
+
+G_DEFINE_TYPE (DbusmenuMenuitemProxy, dbusmenu_menuitem_proxy, DBUSMENU_TYPE_MENUITEM);
+
+static void
+dbusmenu_menuitem_proxy_class_init (DbusmenuMenuitemProxyClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (DbusmenuMenuitemProxyPrivate));
+
+	object_class->dispose = dbusmenu_menuitem_proxy_dispose;
+	object_class->finalize = dbusmenu_menuitem_proxy_finalize;
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+
+	DbusmenuMenuitemClass * miclass = DBUSMENU_MENUITEM_CLASS(klass);
+
+	miclass->handle_event = handle_event;
+
+	g_object_class_install_property (object_class, PROP_MENU_ITEM,
+	                                 g_param_spec_object(PROP_MENU_ITEM_S, "The Menuitem we're proxying",
+	                                                     "An instance of the DbusmenuMenuitem class that this menuitem will mimic.",
+	                                                     DBUSMENU_TYPE_MENUITEM,
+	                                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+	return;
+}
+
+static void
+dbusmenu_menuitem_proxy_init (DbusmenuMenuitemProxy *self)
+{
+	DbusmenuMenuitemProxyPrivate * priv = DBUSMENU_MENUITEM_PROXY_GET_PRIVATE(self);
+
+	priv->mi = NULL;
+
+	priv->sig_property_changed = 0;
+	priv->sig_child_added = 0;
+	priv->sig_child_removed = 0;
+	priv->sig_child_moved = 0;
+
+	return;
+}
+
+/* Remove references to objects */
+static void
+dbusmenu_menuitem_proxy_dispose (GObject *object)
+{
+	remove_menuitem(DBUSMENU_MENUITEM_PROXY(object));
+
+	G_OBJECT_CLASS (dbusmenu_menuitem_proxy_parent_class)->dispose (object);
+	return;
+}
+
+/* Free any memory that we've allocated */
+static void
+dbusmenu_menuitem_proxy_finalize (GObject *object)
+{
+
+	G_OBJECT_CLASS (dbusmenu_menuitem_proxy_parent_class)->finalize (object);
+	return;
+}
+
+/* Set a property using the generic GObject interface */
+static void
+set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
+{
+	switch (id) {
+	case PROP_MENU_ITEM: {
+		GObject * lobj = g_value_get_object(value);
+		add_menuitem(DBUSMENU_MENUITEM_PROXY(obj), DBUSMENU_MENUITEM(lobj));
+		break;
+	}
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, id, pspec);
+		break;
+	}
+
+	return;
+}
+
+/* Get a property using the generic GObject interface */
+static void
+get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
+{
+	DbusmenuMenuitemProxyPrivate * priv = DBUSMENU_MENUITEM_PROXY_GET_PRIVATE(obj);
+
+	switch (id) {
+	case PROP_MENU_ITEM:
+		g_value_set_object(value, priv->mi);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, id, pspec);
+		break;
+	}
+
+	return;
+}
+
+/* Takes the event and passes it along to the item that we're
+   playing proxy for. */
+static void
+handle_event (DbusmenuMenuitem * mi, const gchar * name, const GValue * value, guint timestamp)
+{
+	g_return_if_fail(DBUSMENU_IS_MENUITEM_PROXY(mi));
+	DbusmenuMenuitemProxyPrivate * priv = DBUSMENU_MENUITEM_PROXY_GET_PRIVATE(mi);
+	g_return_if_fail(priv->mi != NULL);
+	return dbusmenu_menuitem_handle_event(priv->mi, name, value, timestamp);
+}
+
+/* Watches a property change and makes sure to put that value
+   into our property list. */
+static void
+proxy_item_property_changed (DbusmenuMenuitem * mi, gchar * property, GValue * value, gpointer user_data)
+{
+	DbusmenuMenuitemProxy * pmi = DBUSMENU_MENUITEM_PROXY(user_data);
+	dbusmenu_menuitem_property_set_value(DBUSMENU_MENUITEM(pmi), property, value);
+	return;
+}
+
+/* Looks for a child getting added and wraps it and places it
+   in our list of children. */
+static void
+proxy_item_child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint position, gpointer user_data)
+{
+	DbusmenuMenuitemProxy * pmi = DBUSMENU_MENUITEM_PROXY(user_data);
+	DbusmenuMenuitemProxy * child_pmi = dbusmenu_menuitem_proxy_new(child);
+	dbusmenu_menuitem_child_add_position(DBUSMENU_MENUITEM(pmi), DBUSMENU_MENUITEM(child_pmi), position);
+	return;
+}
+
+/* Find the wrapper for this child and remove it as well. */
+static void 
+proxy_item_child_removed (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, gpointer user_data)
+{
+	DbusmenuMenuitemProxy * pmi = DBUSMENU_MENUITEM_PROXY(user_data);
+	GList * children = dbusmenu_menuitem_get_children(DBUSMENU_MENUITEM(pmi));
+	DbusmenuMenuitemProxy * finalpmi = NULL;
+	GList * childitem;
+
+	for (childitem = children; childitem != NULL; childitem = g_list_next(childitem)) {
+		DbusmenuMenuitemProxy * childpmi = (DbusmenuMenuitemProxy *)childitem->data;
+		DbusmenuMenuitem * childmi = dbusmenu_menuitem_proxy_get_wrapped(childpmi);
+		if (childmi == child) {
+			finalpmi = childpmi;
+			break;
+		}
+	}
+
+	if (finalpmi != NULL) {
+		dbusmenu_menuitem_child_delete(DBUSMENU_MENUITEM(pmi), DBUSMENU_MENUITEM(finalpmi));
+	}
+
+	return;
+}
+
+/* Find the wrapper for the item and move it in our child list */
+static void 
+proxy_item_child_moved (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint newpos, guint oldpos, gpointer user_data)
+{
+	DbusmenuMenuitemProxy * pmi = DBUSMENU_MENUITEM_PROXY(user_data);
+	GList * children = dbusmenu_menuitem_get_children(DBUSMENU_MENUITEM(pmi));
+	DbusmenuMenuitemProxy * finalpmi = NULL;
+	GList * childitem;
+
+	for (childitem = children; childitem != NULL; childitem = g_list_next(childitem)) {
+		DbusmenuMenuitemProxy * childpmi = (DbusmenuMenuitemProxy *)childitem->data;
+		DbusmenuMenuitem * childmi = dbusmenu_menuitem_proxy_get_wrapped(childpmi);
+		if (childmi == child) {
+			finalpmi = childpmi;
+			break;
+		}
+	}
+
+	if (finalpmi != NULL) {
+		dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(pmi), DBUSMENU_MENUITEM(finalpmi), newpos);
+	}
+
+	return;
+}
+
+/* Making g_object_unref into a GFunc */
+static void
+func_g_object_unref (gpointer data, gpointer user_data)
+{
+	return g_object_unref(G_OBJECT(data));
+}
+
+/* References all of the things we need for talking to this menuitem
+   including signals and other data.  If the menuitem already has
+   properties we need to signal that they've changed for us.  */
+static void
+add_menuitem (DbusmenuMenuitemProxy * pmi, DbusmenuMenuitem * mi)
+{
+	/* Put it in private */
+	DbusmenuMenuitemProxyPrivate * priv = DBUSMENU_MENUITEM_PROXY_GET_PRIVATE(pmi);
+	if (priv->mi != NULL) {
+		remove_menuitem(pmi);
+	}
+	priv->mi = mi;
+	g_object_ref(G_OBJECT(priv->mi));
+
+	/* Attach signals */
+	priv->sig_property_changed = g_signal_connect(G_OBJECT(priv->mi), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(proxy_item_property_changed), pmi);
+	priv->sig_child_added =      g_signal_connect(G_OBJECT(priv->mi), DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED,      G_CALLBACK(proxy_item_child_added),      pmi);
+	priv->sig_child_removed =    g_signal_connect(G_OBJECT(priv->mi), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED,    G_CALLBACK(proxy_item_child_removed),    pmi);
+	priv->sig_child_moved =      g_signal_connect(G_OBJECT(priv->mi), DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED,      G_CALLBACK(proxy_item_child_moved),      pmi);
+
+	/* Grab (cache) Properties */
+	GList * props = dbusmenu_menuitem_properties_list(priv->mi);
+	GList * prop;
+	for (prop = props; prop != NULL; prop = g_list_next(prop)) {
+		gchar * prop_name = (gchar *)prop->data;
+		dbusmenu_menuitem_property_set_value(DBUSMENU_MENUITEM(pmi), prop_name, dbusmenu_menuitem_property_get_value(priv->mi, prop_name));
+	}
+	g_list_free(props);
+
+	/* Go through children and wrap them */
+	GList * children = dbusmenu_menuitem_get_children(priv->mi);
+	GList * child;
+	for (child = children; child != NULL; child = g_list_next(child)) {
+		DbusmenuMenuitemProxy * child_pmi = dbusmenu_menuitem_proxy_new(DBUSMENU_MENUITEM(child->data));
+		dbusmenu_menuitem_child_append(DBUSMENU_MENUITEM(pmi), DBUSMENU_MENUITEM(child_pmi));
+	}
+
+	return;
+}
+
+/* Removes the menuitem from being our proxy.  Typically this isn't
+   done until this object is destroyed, but who knows?!? */
+static void
+remove_menuitem (DbusmenuMenuitemProxy * pmi)
+{
+	DbusmenuMenuitemProxyPrivate * priv = DBUSMENU_MENUITEM_PROXY_GET_PRIVATE(pmi);
+	if (priv->mi == NULL) {
+		return;
+	}
+
+	/* Remove signals */
+	if (priv->sig_property_changed != 0) {
+		g_signal_handler_disconnect(G_OBJECT(priv->mi), priv->sig_property_changed);
+	}
+	if (priv->sig_child_added != 0) {
+		g_signal_handler_disconnect(G_OBJECT(priv->mi), priv->sig_child_added);
+	}
+	if (priv->sig_child_removed != 0) {
+		g_signal_handler_disconnect(G_OBJECT(priv->mi), priv->sig_child_removed);
+	}
+	if (priv->sig_child_moved != 0) {
+		g_signal_handler_disconnect(G_OBJECT(priv->mi), priv->sig_child_moved);
+	}
+
+	/* Unref */
+	g_object_unref(G_OBJECT(priv->mi));
+	priv->mi = NULL;
+
+	/* Remove our own children */
+	GList * children = dbusmenu_menuitem_take_children(DBUSMENU_MENUITEM(pmi));
+	g_list_foreach(children, func_g_object_unref, NULL);
+	g_list_free(children);
+
+	return;
+}
+
+/**
+	dbusmenu_menuitem_proxy_new:
+	@mi: The #DbusmenuMenuitem to proxy
+
+	Builds a new #DbusmenuMenuitemProxy object that proxies
+	all of the values for @mi.
+
+	Return value: A new #DbusmenuMenuitemProxy object.
+*/
+DbusmenuMenuitemProxy *
+dbusmenu_menuitem_proxy_new (DbusmenuMenuitem * mi)
+{
+	DbusmenuMenuitemProxy * pmi = g_object_new(DBUSMENU_TYPE_MENUITEM_PROXY,
+	                                           PROP_MENU_ITEM_S, mi,
+	                                           NULL);
+
+	return pmi;
+}
+
+/**
+	dbusmenu_menuitem_proxy_get_wrapped:
+	@pmi: #DbusmenuMenuitemProxy to look into
+
+	Accesses the private variable of which #DbusmenuMenuitem
+	we are doing the proxying for.
+
+	Return value: A #DbusmenuMenuitem object or a #NULL if we
+		don't have one or there is an error.
+*/
+DbusmenuMenuitem *
+dbusmenu_menuitem_proxy_get_wrapped (DbusmenuMenuitemProxy * pmi)
+{
+	g_return_val_if_fail(DBUSMENU_MENUITEM_PROXY(pmi), NULL);
+	DbusmenuMenuitemProxyPrivate * priv = DBUSMENU_MENUITEM_PROXY_GET_PRIVATE(pmi);
+	return priv->mi;
+}

=== added file 'libdbusmenu-glib/menuitem-proxy.h'
--- libdbusmenu-glib/menuitem-proxy.h	1970-01-01 00:00:00 +0000
+++ libdbusmenu-glib/menuitem-proxy.h	2010-02-10 22:59:39 +0000
@@ -0,0 +1,74 @@
+/*
+An object to ferry over properties and signals between two different
+dbusmenu instances.  Useful for services.
+
+Copyright 2010 Canonical Ltd.
+
+Authors:
+    Ted Gould <ted@xxxxxxxxxxxxx>
+
+This program is free software: you can redistribute it and/or modify it 
+under the terms of either or both of the following licenses:
+
+1) the GNU Lesser General Public License version 3, as published by the 
+Free Software Foundation; and/or
+2) the GNU Lesser General Public License version 2.1, as published by 
+the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but 
+WITHOUT ANY WARRANTY; without even the implied warranties of 
+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR 
+PURPOSE.  See the applicable version of the GNU Lesser General Public 
+License for more details.
+
+You should have received a copy of both the GNU Lesser General Public 
+License version 3 and version 2.1 along with this program.  If not, see 
+<http://www.gnu.org/licenses/>
+*/
+
+#ifndef __DBUSMENU_MENUITEM_PROXY_H__
+#define __DBUSMENU_MENUITEM_PROXY_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include "menuitem.h"
+
+G_BEGIN_DECLS
+
+#define DBUSMENU_TYPE_MENUITEM_PROXY            (dbusmenu_menuitem_proxy_get_type ())
+#define DBUSMENU_MENUITEM_PROXY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), DBUSMENU_TYPE_MENUITEM_PROXY, DbusmenuMenuitemProxy))
+#define DBUSMENU_MENUITEM_PROXY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), DBUSMENU_TYPE_MENUITEM_PROXY, DbusmenuMenuitemProxyClass))
+#define DBUSMENU_IS_MENUITEM_PROXY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DBUSMENU_TYPE_MENUITEM_PROXY))
+#define DBUSMENU_IS_MENUITEM_PROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_TYPE_MENUITEM_PROXY))
+#define DBUSMENU_MENUITEM_PROXY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_MENUITEM_PROXY, DbusmenuMenuitemProxyClass))
+
+typedef struct _DbusmenuMenuitemProxy      DbusmenuMenuitemProxy;
+typedef struct _DbusmenuMenuitemProxyClass DbusmenuMenuitemProxyClass;
+
+/**
+	DbusmenuMenuitemProxyClass:
+	@parent_class: The Class of #DbusmeneMenuitem
+
+	Functions and signal slots for #DbusmenuMenuitemProxy.
+*/
+struct _DbusmenuMenuitemProxyClass {
+	DbusmenuMenuitemClass parent_class;
+};
+
+/**
+	DbusmeneMenuitemProxy:
+	@parent: The instance of #DbusmenuMenuitem
+
+	Public instance data for a #DbusmenuMenuitemProxy.
+*/
+struct _DbusmenuMenuitemProxy {
+	DbusmenuMenuitem parent;
+};
+
+GType dbusmenu_menuitem_proxy_get_type (void);
+DbusmenuMenuitemProxy * dbusmenu_menuitem_proxy_new (DbusmenuMenuitem * mi);
+DbusmenuMenuitem * dbusmenu_menuitem_proxy_get_wrapped (DbusmenuMenuitemProxy * pmi);
+
+G_END_DECLS
+
+#endif

=== modified file 'libdbusmenu-glib/server.c'
--- libdbusmenu-glib/server.c	2010-02-12 20:54:50 +0000
+++ libdbusmenu-glib/server.c	2010-02-18 16:27:53 +0000
@@ -301,10 +301,29 @@
 	return;
 }
 
+/* Adds the signals for this entry to the list and looks at
+   the children of this entry to add the signals we need
+   as well.  We like signals. */
+static void
+added_check_children (gpointer data, gpointer user_data)
+{
+	DbusmenuMenuitem * mi = (DbusmenuMenuitem *)data;
+	DbusmenuServer * server = (DbusmenuServer *)user_data;
+
+	menuitem_signals_create(mi, server);
+	g_list_foreach(dbusmenu_menuitem_get_children(mi), added_check_children, server);
+
+	return;
+}
+
+/* Callback for when a child is added.  We need to connect everything
+   up and signal that the layout has changed. */
 static void
 menuitem_child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint pos, DbusmenuServer * server)
 {
 	menuitem_signals_create(child, server);
+	g_list_foreach(dbusmenu_menuitem_get_children(child), added_check_children, server);
+
 	/* TODO: We probably need to group the layout update signals to make the number more reasonble. */
 	DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
 	priv->layout_revision++;

=== modified file 'tests/Makefile.am'
--- tests/Makefile.am	2010-02-05 18:35:58 +0000
+++ tests/Makefile.am	2010-02-10 22:55:26 +0000
@@ -5,6 +5,7 @@
 	test-glib-objects-test \
 	test-glib-layout \
 	test-glib-properties \
+	test-glib-proxy \
 	test-glib-simple-items \
 	test-gtk-label \
 	test-gtk-reorder
@@ -16,6 +17,9 @@
 	test-glib-layout-server \
 	test-glib-properties-client \
 	test-glib-properties-server \
+	test-glib-proxy-client \
+	test-glib-proxy-server \
+	test-glib-proxy-proxy \
 	test-gtk-label-client \
 	test-gtk-label-server \
 	test-glib-simple-items \
@@ -127,6 +131,56 @@
 	../libdbusmenu-glib/libdbusmenu-glib.la \
 	$(DBUSMENUGLIB_LIBS)
 
+######################
+# Test Glib Proxy
+######################
+
+test-glib-proxy: test-glib-proxy-client test-glib-proxy-server test-glib-proxy-proxy Makefile.am
+	@echo "#!/bin/bash" > $@
+	@echo $(DBUS_RUNNER) --task ./test-glib-proxy-client --task-name Client --task ./test-glib-proxy-server --task-name Server --ignore-return \\ >> $@
+	@echo --task ./test-glib-proxy-proxy --parameter test.proxy.first_proxy --parameter test.proxy.second_proxy --task-name Proxy01 --ignore-return \\ >> $@
+	@echo --task ./test-glib-proxy-proxy --parameter test.proxy.second_proxy --parameter test.proxy.third_proxy --task-name Proxy02 --ignore-return \\ >> $@
+	@echo --task ./test-glib-proxy-proxy --parameter test.proxy.third_proxy --parameter test.proxy.fourth_proxy --task-name Proxy03 --ignore-return \\ >> $@
+	@echo --task ./test-glib-proxy-proxy --parameter test.proxy.fourth_proxy --parameter test.proxy.last_proxy --task-name Proxy04 --ignore-return \\ >> $@
+	@echo --task ./test-glib-proxy-proxy --parameter test.proxy.last_proxy --parameter test.proxy.server --task-name Proxy05 --ignore-return >> $@
+	@chmod +x $@
+
+test_glib_proxy_server_SOURCES = \
+	test-glib-proxy.h \
+	test-glib-proxy-server.c
+
+test_glib_proxy_server_CFLAGS = \
+	-I $(srcdir)/.. \
+	$(DBUSMENUGLIB_CFLAGS) -Wall -Werror
+
+test_glib_proxy_server_LDADD = \
+	../libdbusmenu-glib/libdbusmenu-glib.la \
+	$(DBUSMENUGLIB_LIBS)
+
+test_glib_proxy_client_SOURCES = \
+	test-glib-proxy.h \
+	test-glib-proxy-client.c
+
+test_glib_proxy_client_CFLAGS = \
+	-I $(srcdir)/.. \
+	$(DBUSMENUGLIB_CFLAGS) -Wall -Werror
+
+test_glib_proxy_client_LDADD = \
+	../libdbusmenu-glib/libdbusmenu-glib.la \
+	$(DBUSMENUGLIB_LIBS)
+
+test_glib_proxy_proxy_SOURCES = \
+	test-glib-proxy.h \
+	test-glib-proxy-proxy.c
+
+test_glib_proxy_proxy_CFLAGS = \
+	-I $(srcdir)/.. \
+	$(DBUSMENUGLIB_CFLAGS) -Wall -Werror
+
+test_glib_proxy_proxy_LDADD = \
+	../libdbusmenu-glib/libdbusmenu-glib.la \
+	$(DBUSMENUGLIB_LIBS)
+
 #########################
 # Test Glib Simple Items
 #########################

=== added file 'tests/test-glib-proxy-client.c'
--- tests/test-glib-proxy-client.c	1970-01-01 00:00:00 +0000
+++ tests/test-glib-proxy-client.c	2010-02-13 03:43:28 +0000
@@ -0,0 +1,171 @@
+/*
+A test for libdbusmenu to ensure its quality.
+
+Copyright 2009 Canonical Ltd.
+
+Authors:
+    Ted Gould <ted@xxxxxxxxxxxxx>
+
+This program is free software: you can redistribute it and/or modify it 
+under the terms of the GNU General Public License version 3, as published 
+by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but 
+WITHOUT ANY WARRANTY; without even the implied warranties of 
+MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <glib.h>
+
+#include <libdbusmenu-glib/client.h>
+#include <libdbusmenu-glib/menuitem.h>
+
+#include "test-glib-proxy.h"
+
+static guint layouton = -1;
+static GMainLoop * mainloop = NULL;
+static gboolean passed = TRUE;
+static guint death_timer = 0;
+
+static gboolean
+verify_props (DbusmenuMenuitem * mi, gchar ** properties)
+{
+	if (properties == NULL) {
+		return TRUE;
+	}
+
+	/* Verify they're all there and correct */
+	guint i;
+	for (i = 0; properties[i] != NULL; i += 2) {
+		const gchar * value = dbusmenu_menuitem_property_get(mi, properties[i]);
+		if (g_strcmp0(value, properties[i + 1])) {
+			g_debug("\tFailed as property '%s' should be '%s' and is '%s'", properties[i], properties[i+1], value);
+			return FALSE;
+		}
+	}
+
+	/* Verify that we don't have any extras */
+	// GList * props = dbusmenu_menuitem_properties_list(mi);
+
+	return TRUE;
+}
+
+static gboolean
+verify_root_to_layout(DbusmenuMenuitem * mi, proplayout_t * layout)
+{
+	g_debug("Verifying ID: %d", layout->id);
+
+	if (!verify_props(mi, layout->properties)) {
+		g_debug("\tFailed as unable to verify properties.");
+		return FALSE;
+	}
+
+	GList * children = dbusmenu_menuitem_get_children(mi);
+
+	if (children == NULL && layout->submenu == NULL) {
+		g_debug("\tPassed: %d", layout->id);
+		return TRUE;
+	}
+	if (children == NULL || layout->submenu == NULL) {
+		if (children == NULL) {
+			g_debug("\tFailed as there are no children but we have submenus");
+		} else {
+			g_debug("\tFailed as we have children but no submenu");
+		}
+		return FALSE;
+	}
+
+	guint i = 0;
+	for (i = 0; children != NULL && layout->submenu[i].id != -1; children = g_list_next(children), i++) {
+		if (!verify_root_to_layout(DBUSMENU_MENUITEM(children->data), &layout->submenu[i])) {
+			return FALSE;
+		}
+	}
+
+	if (children == NULL && layout->submenu[i].id == -1) {
+		g_debug("\tPassed: %d", layout->id);
+		return TRUE;
+	}
+
+	if (children != NULL) {
+		g_debug("\tFailed as there are still children but no submenus.  (ID: %d)", layout->id);
+	} else {
+		g_debug("\tFailed as there are still submenus but no children.  (ID: %d)", layout->id);
+	}
+	return FALSE;
+}
+
+static gboolean
+timer_func (gpointer data)
+{
+	g_debug("Death timer.  Oops.  Got to: %d", layouton);
+	passed = FALSE;
+	g_main_loop_quit(mainloop);
+	return FALSE;
+}
+
+static gboolean layout_verify_timer (gpointer data);
+
+static void
+layout_updated (DbusmenuClient * client, gpointer data)
+{
+	g_debug("Layout Updated");
+	if (dbusmenu_client_get_root(client) == NULL) {
+		g_debug("\tIgnored, no root");
+		return;
+	}
+	layouton++;
+	g_timeout_add (1500, layout_verify_timer, client);
+	return;
+}
+
+static gboolean
+layout_verify_timer (gpointer data)
+{
+	g_debug("Verifing Layout: %d", layouton);
+	DbusmenuMenuitem * menuroot = dbusmenu_client_get_root(DBUSMENU_CLIENT(data));
+	proplayout_t * layout = &layouts[layouton];
+	
+	if (!verify_root_to_layout(menuroot, layout)) {
+		g_debug("FAILED LAYOUT: %d", layouton);
+		passed = FALSE;
+	} else {
+		/* Extend our death */
+		g_source_remove(death_timer);
+		death_timer = g_timeout_add_seconds(10, timer_func, data);
+	}
+
+	if (layouts[layouton+1].id == -1) {
+		g_main_loop_quit(mainloop);
+	}
+
+	return FALSE;
+}
+
+int
+main (int argc, char ** argv)
+{
+	g_type_init();
+
+	DbusmenuClient * client = dbusmenu_client_new("test.proxy.first_proxy", "/org/test");
+	g_signal_connect(G_OBJECT(client), DBUSMENU_CLIENT_SIGNAL_LAYOUT_UPDATED, G_CALLBACK(layout_updated), NULL);
+
+	death_timer = g_timeout_add_seconds(10, timer_func, client);
+
+	mainloop = g_main_loop_new(NULL, FALSE);
+	g_main_loop_run(mainloop);
+
+	g_object_unref(G_OBJECT(client));
+
+	if (passed) {
+		g_debug("Quiting");
+		return 0;
+	} else {
+		g_debug("Quiting as we're a failure");
+		return 1;
+	}
+}

=== added file 'tests/test-glib-proxy-proxy.c'
--- tests/test-glib-proxy-proxy.c	1970-01-01 00:00:00 +0000
+++ tests/test-glib-proxy-proxy.c	2010-02-10 21:09:50 +0000
@@ -0,0 +1,80 @@
+#include <glib.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <dbus/dbus-glib-bindings.h>
+
+#include <libdbusmenu-glib/menuitem.h>
+#include <libdbusmenu-glib/menuitem-proxy.h>
+#include <libdbusmenu-glib/server.h>
+#include <libdbusmenu-glib/client.h>
+
+#include "test-glib-proxy.h"
+
+static DbusmenuServer * server = NULL;
+static DbusmenuClient * client = NULL;
+static GMainLoop * mainloop = NULL;
+
+void
+root_changed (DbusmenuClient * client, DbusmenuMenuitem * newroot, gpointer user_data)
+{
+	g_debug("New root: %X", (guint)newroot);
+
+	if (newroot == NULL) {
+		g_debug("Root removed, exiting");
+		g_main_loop_quit(mainloop);
+		return;
+	}
+
+	DbusmenuMenuitemProxy * pmi = dbusmenu_menuitem_proxy_new(newroot);
+	dbusmenu_server_set_root(server, DBUSMENU_MENUITEM(pmi));
+	return;
+}
+
+int
+main (int argc, char ** argv)
+{
+	g_type_init();
+
+	if (argc != 3) {
+		g_error ("Need two params");
+		return 1;
+	}
+	
+	gchar * whoami = argv[1];
+	gchar * myproxy = argv[2];
+
+	g_debug("I am '%s' and I'm proxying '%s'", whoami, myproxy);
+
+	GError * error = NULL;
+	DBusGConnection * connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
+
+	g_debug("DBus ID: %s", dbus_connection_get_server_id(dbus_g_connection_get_connection(connection)));
+
+	DBusGProxy * bus_proxy = dbus_g_proxy_new_for_name(connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
+	guint nameret = 0;
+
+	if (!org_freedesktop_DBus_request_name(bus_proxy, whoami, 0, &nameret, &error)) {
+		g_error("Unable to call to request name");
+		return 1;
+	}
+
+	if (nameret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+		g_error("Unable to get name");
+		return 1;
+	}
+
+	server = dbusmenu_server_new("/org/test");
+	client = dbusmenu_client_new(myproxy, "/org/test");
+
+	g_signal_connect(client, DBUSMENU_CLIENT_SIGNAL_ROOT_CHANGED, G_CALLBACK(root_changed), server);
+
+	mainloop = g_main_loop_new(NULL, FALSE);
+	g_main_loop_run(mainloop);
+
+	g_object_unref(G_OBJECT(server));
+	g_debug("Quiting");
+
+	return 0;
+}

=== added file 'tests/test-glib-proxy-server.c'
--- tests/test-glib-proxy-server.c	1970-01-01 00:00:00 +0000
+++ tests/test-glib-proxy-server.c	2010-02-12 22:45:52 +0000
@@ -0,0 +1,126 @@
+/*
+A test for libdbusmenu to ensure its quality.
+
+Copyright 2009 Canonical Ltd.
+
+Authors:
+    Ted Gould <ted@xxxxxxxxxxxxx>
+
+This program is free software: you can redistribute it and/or modify it 
+under the terms of the GNU General Public License version 3, as published 
+by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but 
+WITHOUT ANY WARRANTY; without even the implied warranties of 
+MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <dbus/dbus-glib-bindings.h>
+
+#include <libdbusmenu-glib/menuitem.h>
+#include <libdbusmenu-glib/server.h>
+
+#include "test-glib-proxy.h"
+
+static void
+set_props (DbusmenuMenuitem * mi, gchar ** props)
+{
+	if (props == NULL) return;
+
+	guint i;
+	for (i = 0; props[i] != NULL; i += 2) {
+		dbusmenu_menuitem_property_set(mi, props[i], props[i+1]);
+	}
+
+	return;
+}
+
+static DbusmenuMenuitem *
+layout2menuitem (proplayout_t * layout)
+{
+	if (layout == NULL || layout->id == -1) return NULL;
+
+	DbusmenuMenuitem * local = dbusmenu_menuitem_new();
+	set_props(local, layout->properties);
+	
+	if (layout->submenu != NULL) {
+		guint count;
+		for (count = 0; layout->submenu[count].id != -1; count++) {
+			DbusmenuMenuitem * child = layout2menuitem(&layout->submenu[count]);
+			if (child != NULL) {
+				dbusmenu_menuitem_child_append(local, child);
+			}
+		}
+	}
+
+	/* g_debug("Layout to menu return: 0x%X", (unsigned int)local); */
+	return local;
+}
+
+static guint layouton = 0;
+static DbusmenuServer * server = NULL;
+static GMainLoop * mainloop = NULL;
+
+static gboolean
+timer_func (gpointer data)
+{
+	if (layouts[layouton].id == -1) {
+		g_main_loop_quit(mainloop);
+		return FALSE;
+	}
+	g_debug("Updating to Layout %d", layouton);
+
+	DbusmenuMenuitem * mi = layout2menuitem(&layouts[layouton]);
+	dbusmenu_server_set_root(server, mi);
+	g_object_unref(G_OBJECT(mi));
+	layouton++;
+
+	return TRUE;
+}
+
+int
+main (int argc, char ** argv)
+{
+	g_type_init();
+
+	GError * error = NULL;
+	DBusGConnection * connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
+
+	g_debug("DBus ID: %s", dbus_connection_get_server_id(dbus_g_connection_get_connection(connection)));
+
+	DBusGProxy * bus_proxy = dbus_g_proxy_new_for_name(connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
+	guint nameret = 0;
+
+	if (!org_freedesktop_DBus_request_name(bus_proxy, "test.proxy.server", 0, &nameret, &error)) {
+		g_error("Unable to call to request name");
+		return 1;
+	}
+
+	if (nameret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+		g_error("Unable to get name");
+		return 1;
+	}
+
+	server = dbusmenu_server_new("/org/test");
+
+	timer_func(NULL);
+	g_timeout_add(2500, timer_func, NULL);
+
+	mainloop = g_main_loop_new(NULL, FALSE);
+	g_main_loop_run(mainloop);
+
+	g_object_unref(G_OBJECT(server));
+	g_debug("Quiting");
+
+	return 0;
+}
+

=== added file 'tests/test-glib-proxy.h'
--- tests/test-glib-proxy.h	1970-01-01 00:00:00 +0000
+++ tests/test-glib-proxy.h	2010-02-13 06:41:53 +0000
@@ -0,0 +1,142 @@
+/*
+A test for libdbusmenu to ensure its quality.
+
+Copyright 2009 Canonical Ltd.
+
+Authors:
+    Ted Gould <ted@xxxxxxxxxxxxx>
+
+This program is free software: you can redistribute it and/or modify it 
+under the terms of the GNU General Public License version 3, as published 
+by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but 
+WITHOUT ANY WARRANTY; without even the implied warranties of 
+MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <glib.h>
+
+typedef struct _proplayout_t proplayout_t;
+struct _proplayout_t {
+	gint id;
+	gchar ** properties;
+	proplayout_t * submenu;
+};
+
+gchar * props1[] = {"property1", "value1", "property2", "value2", NULL};
+gchar * props2[] = {"property00", "value00", "property01", "value01", "property02", "value02", "property03", "value03", "property04", "value04",
+                    "property05", "value05", "property06", "value06", "property07", "value07", "property08", "value08", "property09", "value09",
+                    "property10", "value10", "property11", "value11", "property12", "value12", "property13", "value13", "property14", "value14",
+                    "property15", "value15", "property16", "value16", "property17", "value17", "property18", "value18", "property19", "value19",
+                    "property20", "value20", "property21", "value21", "property22", "value22", "property23", "value23", "property24", "value24",
+                    "property25", "value25", "property26", "value26", "property27", "value27", "property28", "value28", "property29", "value29",
+                    "property30", "value30", "property31", "value31", "property32", "value32", "property33", "value33", "property34", "value34",
+                    "property35", "value35", "property36", "value36", "property37", "value37", "property38", "value38", "property39", "value39",
+                    "property40", "value40", "property41", "value41", "property42", "value42", "property43", "value43", "property44", "value44",
+                    "property45", "value45", "property46", "value46", "property47", "value47", "property48", "value48", "property49", "value49",
+                    "property50", "value50", "property51", "value51", "property52", "value52", "property53", "value53", "property54", "value54",
+                    "property55", "value55", "property56", "value56", "property57", "value57", "property58", "value58", "property59", "value59",
+                    "property60", "value60", "property61", "value61", "property62", "value62", "property63", "value63", "property64", "value64",
+                    "property65", "value65", "property66", "value66", "property67", "value67", "property68", "value68", "property69", "value69",
+                    "property70", "value70", "property71", "value71", "property72", "value72", "property73", "value73", "property74", "value74",
+                    "property75", "value75", "property76", "value76", "property77", "value77", "property78", "value78", "property79", "value79",
+                    "property80", "value80", "property81", "value81", "property82", "value82", "property83", "value83", "property84", "value84",
+                    "property85", "value85", "property86", "value86", "property87", "value87", "property88", "value88", "property89", "value89",
+                    "property90", "value90", "property91", "value91", "property92", "value92", "property93", "value93", "property94", "value94",
+                    "property95", "value95", "property96", "value96", "property97", "value97", "property98", "value98", "property99", "value99",
+                    NULL};
+gchar * props3[] = {"property name that is really long and will ensure that we can really have long property names, which could be important at some point.",
+                    "And a property name that is really long should have a value that is really long, because well, that's an important part of the yin and yang of software testing.",
+                    NULL};
+gchar * props4[] = {"icon-name", "network-status", "label", "Look at network", "right-column", "10:32", NULL};
+
+
+proplayout_t submenu_4_1[] = {
+	{id: 10, properties: props2, submenu: NULL},
+	{id: 11, properties: props2, submenu: NULL},
+	{id: 12, properties: props2, submenu: NULL},
+	{id: 13, properties: props2, submenu: NULL},
+	{id: 14, properties: props2, submenu: NULL},
+	{id: 15, properties: props2, submenu: NULL},
+	{id: 16, properties: props2, submenu: NULL},
+	{id: 17, properties: props2, submenu: NULL},
+	{id: 18, properties: props2, submenu: NULL},
+	{id: 19, properties: props2, submenu: NULL},
+	{id: -1, properties: NULL, submenu: NULL}
+};
+
+proplayout_t submenu_4_2[] = {
+	{id: 20, properties: props2, submenu: NULL},
+	{id: 21, properties: props2, submenu: NULL},
+	{id: 22, properties: props2, submenu: NULL},
+	{id: 23, properties: props2, submenu: NULL},
+	{id: 24, properties: props2, submenu: NULL},
+	{id: 25, properties: props2, submenu: NULL},
+	{id: 26, properties: props2, submenu: NULL},
+	{id: 27, properties: props2, submenu: NULL},
+	{id: 28, properties: props2, submenu: NULL},
+	{id: 29, properties: props2, submenu: NULL},
+	{id: -1, properties: NULL, submenu: NULL}
+};
+
+proplayout_t submenu_4_3[] = {
+	{id: 30, properties: props2, submenu: NULL},
+	{id: 31, properties: props2, submenu: NULL},
+	{id: 32, properties: props2, submenu: NULL},
+	{id: 33, properties: props2, submenu: NULL},
+	{id: 34, properties: props2, submenu: NULL},
+	{id: 35, properties: props2, submenu: NULL},
+	{id: 36, properties: props2, submenu: NULL},
+	{id: 37, properties: props2, submenu: NULL},
+	{id: 38, properties: props2, submenu: NULL},
+	{id: 39, properties: props2, submenu: NULL},
+	{id: -1, properties: NULL, submenu: NULL}
+};
+
+proplayout_t submenu_4_0[] = {
+	{id: 1, properties: props2, submenu: submenu_4_1},
+	{id: 2, properties: props2, submenu: submenu_4_2},
+	{id: 3, properties: props2, submenu: submenu_4_3},
+	{id: -1, properties: NULL, submenu: NULL}
+};
+
+proplayout_t submenu_5_5[] = {
+	{id: 205, properties: props3, submenu: NULL},
+	{id: -1, properties: NULL, submenu: NULL}
+};
+
+proplayout_t submenu_5_4[] = {
+	{id: 204, properties: props3, submenu: submenu_5_5},
+	{id: -1, properties: NULL, submenu: NULL}
+};
+
+proplayout_t submenu_5_3[] = {
+	{id: 203, properties: props3, submenu: submenu_5_4},
+	{id: -1, properties: NULL, submenu: NULL}
+};
+
+proplayout_t submenu_5_2[] = {
+	{id: 202, properties: props3, submenu: submenu_5_3},
+	{id: -1, properties: NULL, submenu: NULL}
+};
+
+proplayout_t submenu_5_1[] = {
+	{id: 201, properties: props3, submenu: submenu_5_2},
+	{id: -1, properties: NULL, submenu: NULL}
+};
+
+proplayout_t layouts[] = {
+	{id: 1, properties: props1, submenu: NULL},
+	{id: 10, properties: props2, submenu: submenu_4_1},
+	{id: 20, properties: props3, submenu: submenu_4_2},
+	{id: 100, properties: props2, submenu: submenu_4_0},
+	{id: 200, properties: props3, submenu: submenu_5_1},
+	{id: -1, properties: NULL, submenu: NULL}
+};
+