← Back to team overview

ayatana-commits team mailing list archive

[Merge] lp:~ted/dbusmenu/menuitem-proxy into lp:dbusmenu

 

Ted Gould has proposed merging lp:~ted/dbusmenu/menuitem-proxy into lp:dbusmenu.

    Requested reviews:
    DBus Menu Team (dbusmenu-team)


This branch adds in proxy menu item support.  Basically it's just a menu
item that looks like the one it's mimicing.  But this allows for the
menu item to be placed in a new server, and thus reexported over the
bus.  You can look at the tests to see how this would work.

Also, this involved fixing some other bugs in dbusmenu that we hadn't
encountered yet :)

-- 
https://code.launchpad.net/~ted/dbusmenu/menuitem-proxy/+merge/19258
Your team ayatana-commits is subscribed to branch lp:dbusmenu.
=== modified file '.bzrignore'
--- .bzrignore	2010-02-05 05:35:32 +0000
+++ .bzrignore	2010-02-13 07:10:29 +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-13 07:10:29 +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-13 07:10:29 +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-13 07:10:29 +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-13 07:10:29 +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-09 16:52:21 +0000
+++ libdbusmenu-glib/server.c	2010-02-13 07:10:30 +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-13 07:10:30 +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 07:10:30 +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-13 07:10:30 +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-13 07:10:30 +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 07:10:30 +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}
+};
+


Follow ups