← Back to team overview

ayatana-commits team mailing list archive

[Merge] lp:~ted/dbusmenu/v2work into lp:dbusmenu

 

Ted Gould has proposed merging lp:~ted/dbusmenu/v2work into lp:dbusmenu.

    Requested reviews:
    DBus Menu Team (dbusmenu-team)


This is the basis for all of the v2 work we'll be doing.  It changes the protocol to use variants.  Provides for better grouping, though it's not used and optimized for yet.  And it also makes it so that checkboxes and radio buttons are available for use.  Whew, we're not done yet though.
-- 
https://code.launchpad.net/~ted/dbusmenu/v2work/+merge/16323
Your team ayatana-commits is subscribed to branch lp:dbusmenu.
=== modified file '.bzrignore'
--- .bzrignore	2009-12-10 19:00:56 +0000
+++ .bzrignore	2009-12-18 05:11:13 +0000
@@ -52,3 +52,5 @@
 libdbusmenu-[0-9].[0-9].[0-9].tar.gz
 libdbusmenu-[0-9].[0-9].[0-9].tar.gz.asc
 tests/test-mago
+tests/*.bustle
+libdbusmenu-gtk/libdbusmenu_gtk_la-genericmenuitem.lo

=== modified file 'libdbusmenu-glib/Makefile.am'
--- libdbusmenu-glib/Makefile.am	2009-05-15 20:27:40 +0000
+++ libdbusmenu-glib/Makefile.am	2009-12-18 05:11:13 +0000
@@ -22,6 +22,7 @@
 	menuitem.c \
 	menuitem-marshal.h \
 	menuitem-marshal.c \
+	menuitem-private.h \
 	server.h \
 	server.c \
 	server-marshal.h \

=== modified file 'libdbusmenu-glib/client.c'
--- libdbusmenu-glib/client.c	2009-10-28 16:58:38 +0000
+++ libdbusmenu-glib/client.c	2009-12-18 05:11:13 +0000
@@ -34,6 +34,7 @@
 #include <libxml/tree.h>
 
 #include "client.h"
+#include "menuitem.h"
 #include "dbusmenu-client.h"
 #include "server-marshal.h"
 
@@ -94,14 +95,14 @@
 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);
 /* Private Funcs */
-static void layout_update (DBusGProxy * proxy, gint revision, DbusmenuClient * client);
-static void id_prop_update (DBusGProxy * proxy, guint id, gchar * property, gchar * value, DbusmenuClient * client);
+static void layout_update (DBusGProxy * proxy, gint revision, guint parent, DbusmenuClient * client);
+static void id_prop_update (DBusGProxy * proxy, guint id, gchar * property, GValue * value, DbusmenuClient * client);
 static void id_update (DBusGProxy * proxy, guint id, DbusmenuClient * client);
 static void build_proxies (DbusmenuClient * client);
 static guint parse_node_get_id (xmlNodePtr node);
 static DbusmenuMenuitem * parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, DBusGProxy * proxy);
 static gint parse_layout (DbusmenuClient * client, const gchar * layout);
-static void update_layout_cb (DBusGProxy * proxy, DBusGProxyCall * call, void * data);
+static void update_layout_cb (DBusGProxy * proxy, guint rev, gchar * xml, GError * in_error, void * data);
 static void update_layout (DbusmenuClient * client);
 static void menuitem_get_properties_cb (DBusGProxy * proxy, GHashTable * properties, GError * error, gpointer data);
 
@@ -215,7 +216,7 @@
 	DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(object);
 
 	if (priv->layoutcall != NULL) {
-		dbus_g_proxy_cancel_call(priv->propproxy, priv->layoutcall);
+		dbus_g_proxy_cancel_call(priv->menuproxy, priv->layoutcall);
 		priv->layoutcall = NULL;
 	}
 	if (priv->menuproxy != NULL) {
@@ -307,7 +308,7 @@
 
 /* Annoying little wrapper to make the right function update */
 static void
-layout_update (DBusGProxy * proxy, gint revision, DbusmenuClient * client)
+layout_update (DBusGProxy * proxy, gint revision, guint parent, DbusmenuClient * client)
 {
 	DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
 	priv->current_revision = revision;
@@ -320,7 +321,7 @@
 /* Signal from the server that a property has changed
    on one of our menuitems */
 static void
-id_prop_update (DBusGProxy * proxy, guint id, gchar * property, gchar * value, DbusmenuClient * client)
+id_prop_update (DBusGProxy * proxy, guint id, gchar * property, GValue * value, DbusmenuClient * client)
 {
 	#ifdef MASSIVEDEBUGGING
 	g_debug("Property change sent to client for item %d property %s value %s", id, property, g_utf8_strlen(value, 50) < 25 ? value : "<too long>");
@@ -332,7 +333,7 @@
 	DbusmenuMenuitem * menuitem = dbusmenu_menuitem_find_id(priv->root, id);
 	g_return_if_fail(menuitem != NULL);
 
-	dbusmenu_menuitem_property_set(menuitem, property, value);
+	dbusmenu_menuitem_property_set_value(menuitem, property, value);
 	return;
 }
 
@@ -351,7 +352,9 @@
 	DbusmenuMenuitem * menuitem = dbusmenu_menuitem_find_id(priv->root, id);
 	g_return_if_fail(menuitem != NULL);
 
-	org_ayatana_dbusmenu_get_properties_async(proxy, id, menuitem_get_properties_cb, menuitem);
+	gchar * properties[1] = {NULL}; /* This gets them all */
+	g_debug("Getting properties");
+	org_ayatana_dbusmenu_get_properties_async(proxy, id, (const gchar **)properties, menuitem_get_properties_cb, menuitem);
 	return;
 }
 
@@ -488,15 +491,16 @@
 		priv->dbusproxy = NULL;
 	}
 
-	dbus_g_proxy_add_signal(priv->menuproxy, "LayoutUpdate", G_TYPE_INT, G_TYPE_INVALID);
+	dbus_g_object_register_marshaller(_dbusmenu_server_marshal_VOID__INT_UINT, G_TYPE_NONE, G_TYPE_INT, G_TYPE_UINT, G_TYPE_INVALID);
+	dbus_g_proxy_add_signal(priv->menuproxy, "LayoutUpdate", G_TYPE_INT, G_TYPE_UINT, G_TYPE_INVALID);
 	dbus_g_proxy_connect_signal(priv->menuproxy, "LayoutUpdate", G_CALLBACK(layout_update), client, NULL);
 
-	dbus_g_object_register_marshaller(_dbusmenu_server_marshal_VOID__UINT_STRING_STRING, G_TYPE_NONE, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
-	dbus_g_proxy_add_signal(priv->menuproxy, "IdPropUpdate", G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal(priv->menuproxy, "IdPropUpdate", G_CALLBACK(id_prop_update), client, NULL);
+	dbus_g_object_register_marshaller(_dbusmenu_server_marshal_VOID__UINT_STRING_POINTER, G_TYPE_NONE, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID);
+	dbus_g_proxy_add_signal(priv->menuproxy, "ItemPropertyUpdated", G_TYPE_UINT, G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal(priv->menuproxy, "ItemPropertyUpdated", G_CALLBACK(id_prop_update), client, NULL);
 
-	dbus_g_proxy_add_signal(priv->menuproxy, "IdUpdate", G_TYPE_UINT, G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal(priv->menuproxy, "IdUpdate", G_CALLBACK(id_update), client, NULL);
+	dbus_g_proxy_add_signal(priv->menuproxy, "ItemUpdated", G_TYPE_UINT, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal(priv->menuproxy, "ItemUpdated", G_CALLBACK(id_update), client, NULL);
 
 	update_layout(client);
 
@@ -564,7 +568,7 @@
 static void
 get_properties_helper (gpointer key, gpointer value, gpointer data)
 {
-	dbusmenu_menuitem_property_set((DbusmenuMenuitem *)data, (gchar *)key, (gchar *)value);
+	dbusmenu_menuitem_property_set_value((DbusmenuMenuitem *)data, (gchar *)key, (GValue *)value);
 	return;
 }
 
@@ -606,7 +610,7 @@
 	const gchar * type;
 	DbusmenuClientTypeHandler newfunc = NULL;
 	
-	type = dbusmenu_menuitem_property_get(propdata->item, "type");
+	type = dbusmenu_menuitem_property_get(propdata->item, DBUSMENU_MENUITEM_PROP_TYPE);
 	if (type != NULL) {
 		newfunc = g_hash_table_lookup(priv->type_handlers, type);
 	} else {
@@ -647,7 +651,10 @@
 menuitem_activate (DbusmenuMenuitem * mi, DbusmenuClient * client)
 {
 	DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
-	org_ayatana_dbusmenu_call_async (priv->menuproxy, dbusmenu_menuitem_get_id(mi), menuitem_call_cb, mi);
+	GValue value = {0};
+	g_value_init(&value, G_TYPE_INT);
+	g_value_set_int(&value, 0);
+	org_ayatana_dbusmenu_event_async (priv->menuproxy, dbusmenu_menuitem_get_id(mi), "clicked", &value, menuitem_call_cb, mi);
 	return;
 }
 
@@ -689,7 +696,8 @@
 			propdata->item    = item;
 			propdata->parent  = parent;
 
-			org_ayatana_dbusmenu_get_properties_async(proxy, id, menuitem_get_properties_new_cb, propdata);
+			gchar * properties[1] = {NULL}; /* This gets them all */
+			org_ayatana_dbusmenu_get_properties_async(proxy, id, (const gchar **)properties, menuitem_get_properties_new_cb, propdata);
 		} else {
 			g_warning("Unable to allocate memory to get properties for menuitem.  This menuitem will never be realized.");
 		}
@@ -771,32 +779,24 @@
 
 /* When the layout property returns, here's where we take care of that. */
 static void
-update_layout_cb (DBusGProxy * proxy, DBusGProxyCall * call, void * data)
+update_layout_cb (DBusGProxy * proxy, guint rev, gchar * xml, GError * error, void * data)
 {
 	DbusmenuClient * client = DBUSMENU_CLIENT(data);
 	DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
 
-	GError * error = NULL;
-	GValue value = {0};
-
-	priv->layoutcall = NULL;
-	if (!dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_VALUE, &value, G_TYPE_INVALID)) {
+	if (error != NULL) {
 		g_warning("Getting layout failed on client %s object %s: %s", priv->dbus_name, priv->dbus_object, error->message);
-		g_error_free(error);
 		return;
 	}
 
-	const gchar * xml = g_value_get_string(&value);
-	/* g_debug("Got layout string: %s", xml); */
-	gint rev = parse_layout(client, xml);
-
-	if (rev == 0) {
+	if (!parse_layout(client, xml)) {
 		g_warning("Unable to parse layout!");
 		return;
 	}
 
 	priv->my_revision = rev;
 	/* g_debug("Root is now: 0x%X", (unsigned int)priv->root); */
+	priv->layoutcall = NULL;
 	#ifdef MASSIVEDEBUGGING
 	g_debug("Client signaling layout has changed.");
 	#endif 
@@ -816,7 +816,7 @@
 {
 	DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
 
-	if (priv->propproxy == NULL) {
+	if (priv->menuproxy == NULL) {
 		return;
 	}
 
@@ -824,14 +824,10 @@
 		return;
 	}
 
-	priv->layoutcall = dbus_g_proxy_begin_call (priv->propproxy,
-	                                            "Get",
-	                                            update_layout_cb,
-	                                            client,
-	                                            NULL,
-	                                            G_TYPE_STRING, "org.ayatana.dbusmenu",
-	                                            G_TYPE_STRING, "layout",
-	                                            G_TYPE_INVALID, G_TYPE_VALUE, G_TYPE_INVALID);
+	priv->layoutcall = org_ayatana_dbusmenu_get_layout_async(priv->menuproxy,
+	                                                         0, /* Parent is the root */
+	                                                         update_layout_cb,
+	                                                         client);
 
 	return;
 }

=== modified file 'libdbusmenu-glib/client.h'
--- libdbusmenu-glib/client.h	2009-08-26 20:15:13 +0000
+++ libdbusmenu-glib/client.h	2009-12-18 05:11:13 +0000
@@ -52,7 +52,7 @@
 
 #define DBUSMENU_CLIENT_TYPES_DEFAULT      "menuitem"
 #define DBUSMENU_CLIENT_TYPES_SEPARATOR    "separator"
-#define DBUSMENU_CLIENT_TYPES_IMAGE        "imageitem"
+#define DBUSMENU_CLIENT_TYPES_IMAGE        "menuitem"
 
 /**
 	DbusmenuClientClass:

=== modified file 'libdbusmenu-glib/dbus-menu.xml'
--- libdbusmenu-glib/dbus-menu.xml	2009-11-20 18:01:22 +0000
+++ libdbusmenu-glib/dbus-menu.xml	2009-12-18 05:11:13 +0000
@@ -32,8 +32,22 @@
 
 <!-- Properties -->
 <!--
+Provides the version of the DBusmenu API that this API is
+implementing.
+-->
+		<property name="version" type="u" access="read"/>
+
+<!-- Functions -->
+
+<!--
 Provides an XML representation of the menu hierarchy
 
+@param parentId The ID of the parent node for the layout.  For
+	grabbing the layout from the root node use zero.
+@param revision The revision number of the layout.  For matching
+	with layoutUpdated signals.
+@param layout The layout as an XML string of IDs.
+
 XML syntax:
 
 <menu id="1" revision="2"> # Root container
@@ -48,73 +62,123 @@
   ...
 </menu>
 -->
-		<property name="layout" type="s" access="read"/>
-
-<!-- Functions -->
+		<method name="GetLayout">
+			<arg type="u" name="parentId" direction="in" />
+			<arg type="u" name="revision" direction="out" />
+			<arg type="s" name="layout" direction="out" />
+		</method>
+
+<!--
+Returns the list of items which are children of @a parentId.
+
+@param Ids A list of ids that we should be finding the properties
+	on.  If the list is empty, all menu items should be sent.
+@param propertyNames list of string the list of item properties we
+	are interested in.  If there are no entries in the list all of
+	the properties will be sent.
+
+An item is represented as a struct following this format:
+@li id unsigned the item id
+@li properties map(string => variant) the requested item properties
+
+-->
+		<method name="GetGroupProperties">
+			<arg type="au" name="Ids" direction="in" />
+			<arg type="as" name="propertyNames" direction="in" />
+			<arg type="a(ua{sv})" name="properties" direction="out" />
+		</method>
 
 <!--
 Each menu item has a set of properties. Property keys are in menuitem.h:
 
-- visible
-- sensitive
-- label
-- icon
-- icon-data
-- type
-
-"type" property is an enum which can take the following values (client.h):
-
-- menuitem
-- separator
-- imageitem
+@li type string Type of the item (see below)
+@li label string Text of the item
+@li icon-data binary Raw data of the icon (TODO: define format)
+@li icon string Icon name of the item, following icon spec
+@li sensitive boolean Whether the item can be activated or not
+@li visible boolean Whether the item is visible or not (XXX: Is this necessary?)
+@li checked boolean Whether a checkbox or radio item is checked
+@li shortcut string The keyboard shortcut
+
+@c type property is an enum which can take the following values (client.h):
+
+@li action An item which can be clicked to trigger an action
+@li checkbox An item which can be checked or unchecked
+@li radio An item which can be checked or unchecked as part of a group
+@li separator A separator
+@li menu An item which contains more items
 -->
 		<method name="GetProperty">
 			<arg type="u" name="id" direction="in" />
-			<arg type="s" name="property" direction="in" />
-			<arg type="s" name="value" direction="out" />
+			<arg type="s" name="name" direction="in" />
+			<arg type="v" name="value" direction="out" />
 		</method>
 
 <!--
-Convenience method to retrieve all properties in one call (more efficient)
+Returns multiple properties in one call. This is more efficient than
+GetProperty.
+
+@param id unsigned the item whose properties we want to retrieve.
+@param propertyNames list of string name of the properties we want.  If the list contains no entries, all properties are sent.
 -->
 		<method name="GetProperties">
+			<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
 			<arg type="u" name="id" direction="in" />
-			<arg type="a{ss}" name="properties" direction="out" />
+			<arg type="as" name="propertyNames" direction="in" />
+			<arg type="a{sv}" name="properties" direction="out" />
 		</method>
 
 <!--
-This is called by the display to notify the application it should trigger
-the action associated with a specific menu id
+This is called by the applet to notify the application an event happened on a
+menu item.
+
+@param id the id of the item which received the event
+@param type the type of event
+@param data event-specific data
+
+@a type can be one of the following:
+
+@li "clicked"
+@li "hovered"
+
+Vendor specific events can be added by prefixing them with "x-<vendor>-"
 -->
-		<method name="Call">
+		<method name="Event">
 			<arg type="u" name="id" direction="in" />
+			<arg type="s" name="eventId" direction="in" />
+			<arg type="v" name="data" direction="in" />
 		</method>
 
 <!-- Signals -->
 <!--
-Triggered by the application to notify display that the property prop from menu id
-as changed to value.
+Triggered by the application to notify the applet that the property @a property
+from item @a id has changed to @a value.
 -->
-		<signal name="IdPropUpdate">
+		<signal name="ItemPropertyUpdated">
 			<arg type="u" name="id" direction="out" />
 			<arg type="s" name="prop" direction="out" />
-			<arg type="s" name="value" direction="out" />
+			<arg type="v" name="value" direction="out" />
 		</signal>
 
 <!--
-Triggered by the application to notify display that all properties of menu id
-should be considered outdated
+Triggered by the application to notify the applet that all properties of item
+@a id should be considered outdated
 -->
-		<signal name="IdUpdate">
+		<signal name="ItemUpdated">
 			<arg type="u" name="id" direction="out" />
 		</signal>
 
 <!--
 Triggered by the application to notify display of a layout update, up to
 revision
+@param revsion The revision of the layout that we're currently on
+@param parent  If the layout update is only of a subtree, this is the parent
+         item for the entries that have changed.  It is zero if the
+		 whole layout should be considered invalid.
 -->
 		<signal name="LayoutUpdate">
 			<arg type="i" name="revision" direction="out" />
+			<arg type="u" name="parent" direction="out" />
 		</signal>
 
 <!-- End of interesting stuff -->

=== added file 'libdbusmenu-glib/menuitem-private.h'
--- libdbusmenu-glib/menuitem-private.h	1970-01-01 00:00:00 +0000
+++ libdbusmenu-glib/menuitem-private.h	2009-12-18 05:11:13 +0000
@@ -0,0 +1,40 @@
+/*
+A library to communicate a menu object set accross DBus and
+track updates and maintain consistency.
+
+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 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_PRIVATE_H__
+#define __DBUSMENU_MENUITEM_PRIVATE_H__
+
+#include "menuitem.h"
+
+G_BEGIN_DECLS
+
+void dbusmenu_menuitem_buildxml (DbusmenuMenuitem * mi, GPtrArray * array, gint revision);
+
+G_END_DECLS
+
+#endif

=== modified file 'libdbusmenu-glib/menuitem.c'
--- libdbusmenu-glib/menuitem.c	2009-10-01 19:23:35 +0000
+++ libdbusmenu-glib/menuitem.c	2009-12-18 05:11:13 +0000
@@ -26,11 +26,13 @@
 <http://www.gnu.org/licenses/>
 */
 
+#include <stdlib.h>
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 #include "menuitem.h"
 #include "menuitem-marshal.h"
+#include "menuitem-private.h"
 
 #ifdef MASSIVEDEBUGGING
 #define LABEL(x)  dbusmenu_menuitem_property_get(DBUSMENU_MENUITEM(x), DBUSMENU_MENUITEM_PROP_LABEL)
@@ -88,6 +90,8 @@
 static void dbusmenu_menuitem_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 g_value_transform_STRING_BOOLEAN (const GValue * in, GValue * out);
+static void g_value_transform_STRING_INT (const GValue * in, GValue * out);
 
 /* GObject stuff */
 G_DEFINE_TYPE (DbusmenuMenuitem, dbusmenu_menuitem, G_TYPE_OBJECT);
@@ -207,11 +211,56 @@
 												  0, 30000, 0,
 	                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 
+	/* Check transfer functions for GValue */
+	if (!g_value_type_transformable(G_TYPE_STRING, G_TYPE_BOOLEAN)) {
+		g_value_register_transform_func(G_TYPE_STRING, G_TYPE_BOOLEAN, g_value_transform_STRING_BOOLEAN);
+	}
+	if (!g_value_type_transformable(G_TYPE_STRING, G_TYPE_INT)) {
+		g_value_register_transform_func(G_TYPE_STRING, G_TYPE_INT, g_value_transform_STRING_INT);
+	}
+
+	return;
+}
+
+/* A little helper function to translate a string into
+   a boolean value */
+static void
+g_value_transform_STRING_BOOLEAN (const GValue * in, GValue * out)
+{
+	const gchar * string = g_value_get_string(in);
+	if (!g_strcmp0(string, "TRUE") || !g_strcmp0(string, "true") || !g_strcmp0(string, "True")) {
+		g_value_set_boolean(out, TRUE);
+	} else {
+		g_value_set_boolean(out, FALSE);
+	}
+	return;
+}
+
+/* A little helper function to translate a string into
+   a integer value */
+static void
+g_value_transform_STRING_INT (const GValue * in, GValue * out)
+{
+	g_value_set_int(out, atoi(g_value_get_string(in)));
 	return;
 }
 
 static guint menuitem_next_id = 1;
 
+/* A small little function to both clear the insides of a 
+   value as well as the memory it itself uses. */
+static void
+g_value_free (gpointer data)
+{
+	if (data == NULL) return;
+	GValue * value = (GValue*)data;
+	g_value_unset(value);
+	g_free(data);
+	return;
+}
+
+/* Initialize the values of the in the object, and build the
+   properties hash table. */
 static void
 dbusmenu_menuitem_init (DbusmenuMenuitem *self)
 {
@@ -220,7 +269,7 @@
 	priv->id = 0; 
 	priv->children = NULL;
 
-	priv->properties = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+	priv->properties = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_value_free);
 
 	priv->root = FALSE;
 	
@@ -673,27 +722,106 @@
 gboolean
 dbusmenu_menuitem_property_set (DbusmenuMenuitem * mi, const gchar * property, const gchar * value)
 {
+	GValue val = {0};
+	g_value_init(&val, G_TYPE_STRING);
+	g_value_set_static_string(&val, value);
+	return dbusmenu_menuitem_property_set_value(mi, property, &val);
+}
+
+/**
+	dbusmenu_menuitem_property_set_bool:
+	@mi: The #DbusmenuMenuitem to set the property on.
+	@property: Name of the property to set.
+	@value: The value of the property.
+
+	Takes a boolean @value and sets it on @property as a
+	property on @mi.  If a property already exists by that name,
+	then the value is set to the new value.  If not, the property
+	is added.  If the value is changed or the property was previously
+	unset then the signal #DbusmenuMenuitem::prop-changed will be
+	emitted by this function.
+
+	Return value:  A boolean representing if the property value was set.
+*/
+gboolean
+dbusmenu_menuitem_property_set_bool (DbusmenuMenuitem * mi, const gchar * property, const gboolean value)
+{
+	GValue val = {0};
+	g_value_init(&val, G_TYPE_BOOLEAN);
+	g_value_set_boolean(&val, value);
+	return dbusmenu_menuitem_property_set_value(mi, property, &val);
+}
+
+/**
+	dbusmenu_menuitem_property_set_int:
+	@mi: The #DbusmenuMenuitem to set the property on.
+	@property: Name of the property to set.
+	@value: The value of the property.
+
+	Takes a boolean @value and sets it on @property as a
+	property on @mi.  If a property already exists by that name,
+	then the value is set to the new value.  If not, the property
+	is added.  If the value is changed or the property was previously
+	unset then the signal #DbusmenuMenuitem::prop-changed will be
+	emitted by this function.
+
+	Return value:  A boolean representing if the property value was set.
+*/
+gboolean
+dbusmenu_menuitem_property_set_int (DbusmenuMenuitem * mi, const gchar * property, const gint value)
+{
+	GValue val = {0};
+	g_value_init(&val, G_TYPE_INT);
+	g_value_set_int(&val, value);
+	return dbusmenu_menuitem_property_set_value(mi, property, &val);
+}
+
+/**
+	dbusmenu_menuitem_property_set:
+	@mi: The #DbusmenuMenuitem to set the property on.
+	@property: Name of the property to set.
+	@value: The value of the property.
+
+	Takes the pair of @property and @value and places them as a
+	property on @mi.  If a property already exists by that name,
+	then the value is set to the new value.  If not, the property
+	is added.  If the value is changed or the property was previously
+	unset then the signal #DbusmenuMenuitem::prop-changed will be
+	emitted by this function.
+
+	Return value:  A boolean representing if the property value was set.
+*/
+gboolean
+dbusmenu_menuitem_property_set_value (DbusmenuMenuitem * mi, const gchar * property, const GValue * value)
+{
 	g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE);
 	g_return_val_if_fail(property != NULL, FALSE);
+	g_return_val_if_fail(G_IS_VALUE(value), FALSE);
 
 	DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
 	/* g_debug("Setting a property.  ID: %d  Prop: %s  Value: %s", priv->id, property, value); */
 
+	#if 0
 	gpointer lookup = g_hash_table_lookup(priv->properties, property);
 	if (g_strcmp0((gchar *)lookup, value) == 0) {
 		/* The value is the same as the value currently in the
 		   table so we don't really care.  Just say everything's okay */
 		return TRUE;
 	}
+	#endif
 
 	gchar * lprop = g_strdup(property);
-	gchar * lval = g_strdup(value);
+	GValue * lval = g_new0(GValue, 1);
+	g_value_init(lval, G_VALUE_TYPE(value));
+	g_value_copy(value, lval);
 
 	g_hash_table_insert(priv->properties, lprop, lval);
 	#ifdef MASSIVEDEBUGGING
-	g_debug("Menuitem %d (%s) signalling property '%s' changed to '%s'", ID(mi), LABEL(mi), property, g_utf8_strlen(value, 50) < 25 ? value : "<too long>");
+	gchar * valstr = g_strdup_value_contents(lval);
+	g_debug("Menuitem %d (%s) signalling property '%s' changed to '%s'", ID(mi), LABEL(mi), property, g_utf8_strlen(valstr, 50) < 25 ? valstr : "<too long>");
+	g_free(valstr);
 	#endif
-	g_signal_emit(G_OBJECT(mi), signals[PROPERTY_CHANGED], 0, property, value, TRUE);
+	g_signal_emit(G_OBJECT(mi), signals[PROPERTY_CHANGED], 0, lprop, lval, TRUE);
 
 	return TRUE;
 }
@@ -709,18 +837,95 @@
 
 	Return value: A string with the value of the property
 		that shouldn't be free'd.  Or #NULL if the property
-		is not set.
+		is not set or is not a string.
 */
 const gchar *
 dbusmenu_menuitem_property_get (DbusmenuMenuitem * mi, const gchar * property)
 {
+	const GValue * value = dbusmenu_menuitem_property_get_value(mi, property);
+	if (value == NULL) return NULL;
+	if (G_VALUE_TYPE(value) != G_TYPE_STRING) return NULL;
+	return g_value_get_string(value);
+}
+
+/**
+	dbusmenu_menuitem_property_get_value:
+	@mi: The #DbusmenuMenuitem to look for the property on.
+	@property: The property to grab.
+
+	Look up a property on @mi and return the value of it if
+	it exits.  #NULL will be returned if the property doesn't
+	exist.
+
+	Return value: A GValue for the property.
+*/
+const GValue *
+dbusmenu_menuitem_property_get_value (DbusmenuMenuitem * mi, const gchar * property)
+{
 	g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL);
 	g_return_val_if_fail(property != NULL, NULL);
 
 	DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
 
-	return (const gchar *)g_hash_table_lookup(priv->properties, property);
-}
+	return (const GValue *)g_hash_table_lookup(priv->properties, property);
+}
+
+/**
+	dbusmenu_menuitem_property_get_bool:
+	@mi: The #DbusmenuMenuitem to look for the property on.
+	@property: The property to grab.
+
+	Look up a property on @mi and return the value of it if
+	it exits.  Returns #FALSE if the property doesn't exist.
+
+	Return value: The value of the property or #FALSE.
+*/
+gboolean
+dbusmenu_menuitem_property_get_bool (DbusmenuMenuitem * mi, const gchar * property)
+{
+	const GValue * value = dbusmenu_menuitem_property_get_value(mi, property);
+	if (value == NULL) return FALSE;
+	if (G_VALUE_TYPE(value) != G_TYPE_BOOLEAN) {
+		if (g_value_type_transformable(G_VALUE_TYPE(value), G_TYPE_BOOLEAN)) {
+			GValue boolval = {0};
+			g_value_init(&boolval, G_TYPE_BOOLEAN);
+			g_value_transform(value, &boolval);
+			return g_value_get_boolean(&boolval);
+		} else {
+			return FALSE;
+		}
+	}
+	return g_value_get_boolean(value);
+}
+
+/**
+	dbusmenu_menuitem_property_get_int:
+	@mi: The #DbusmenuMenuitem to look for the property on.
+	@property: The property to grab.
+
+	Look up a property on @mi and return the value of it if
+	it exits.  Returns zero if the property doesn't exist.
+
+	Return value: The value of the property or zero.
+*/
+gint
+dbusmenu_menuitem_property_get_int (DbusmenuMenuitem * mi, const gchar * property)
+{
+	const GValue * value = dbusmenu_menuitem_property_get_value(mi, property);
+	if (value == NULL) return 0;
+	if (G_VALUE_TYPE(value) != G_TYPE_INT) {
+		if (g_value_type_transformable(G_VALUE_TYPE(value), G_TYPE_INT)) {
+			GValue intval = {0};
+			g_value_init(&intval, G_TYPE_INT);
+			g_value_transform(value, &intval);
+			return g_value_get_int(&intval);
+		} else {
+			return 0;
+		}
+	}
+	return g_value_get_int(value);
+}
+
 
 /**
 	dbusmenu_menuitem_property_exit:

=== modified file 'libdbusmenu-glib/menuitem.h'
--- libdbusmenu-glib/menuitem.h	2009-09-21 20:01:40 +0000
+++ libdbusmenu-glib/menuitem.h	2009-12-18 05:11:13 +0000
@@ -50,11 +50,17 @@
 #define DBUSMENU_MENUITEM_SIGNAL_REALIZED            "realized"
 #define DBUSMENU_MENUITEM_SIGNAL_REALIZED_ID         (g_signal_lookup(DBUSMENU_MENUITEM_SIGNAL_REALIZED, DBUSMENU_TYPE_MENUITEM))
 
+#define DBUSMENU_MENUITEM_PROP_TYPE                  "type"
 #define DBUSMENU_MENUITEM_PROP_VISIBLE               "visible"
 #define DBUSMENU_MENUITEM_PROP_SENSITIVE             "sensitive"
 #define DBUSMENU_MENUITEM_PROP_LABEL                 "label"
 #define DBUSMENU_MENUITEM_PROP_ICON                  "icon"
 #define DBUSMENU_MENUITEM_PROP_ICON_DATA             "icon-data"
+#define DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE           "toggle-type"
+#define DBUSMENU_MENUITEM_PROP_TOGGLE_CHECKED        "toggle-checked"
+
+#define DBUSMENU_MENUITEM_TOGGLE_CHECK               "checkmark"
+#define DBUSMENU_MENUITEM_TOGGLE_RADIO               "radio"
 
 /**
 	DbusmenuMenuitem:
@@ -93,7 +99,7 @@
 	GObjectClass parent_class;
 
 	/* Signals */
-	void (*property_changed) (gchar * property, gchar * value);
+	void (*property_changed) (gchar * property, GValue * value);
 	void (*item_activated) (void);
 	void (*child_added) (DbusmenuMenuitem * child, guint position);
 	void (*child_removed) (DbusmenuMenuitem * child);
@@ -128,7 +134,13 @@
 DbusmenuMenuitem * dbusmenu_menuitem_find_id (DbusmenuMenuitem * mi, guint id);
 
 gboolean dbusmenu_menuitem_property_set (DbusmenuMenuitem * mi, const gchar * property, const gchar * value);
+gboolean dbusmenu_menuitem_property_set_value (DbusmenuMenuitem * mi, const gchar * property, const GValue * value);
+gboolean dbusmenu_menuitem_property_set_bool (DbusmenuMenuitem * mi, const gchar * property, const gboolean value);
+gboolean dbusmenu_menuitem_property_set_int (DbusmenuMenuitem * mi, const gchar * property, const gint value);
 const gchar * dbusmenu_menuitem_property_get (DbusmenuMenuitem * mi, const gchar * property);
+const GValue * dbusmenu_menuitem_property_get_value (DbusmenuMenuitem * mi, const gchar * property);
+gboolean dbusmenu_menuitem_property_get_bool (DbusmenuMenuitem * mi, const gchar * property);
+gint dbusmenu_menuitem_property_get_int (DbusmenuMenuitem * mi, const gchar * property);
 gboolean dbusmenu_menuitem_property_exist (DbusmenuMenuitem * mi, const gchar * property);
 GList * dbusmenu_menuitem_properties_list (DbusmenuMenuitem * mi) G_GNUC_WARN_UNUSED_RESULT;
 GHashTable * dbusmenu_menuitem_properties_copy (DbusmenuMenuitem * mi);
@@ -136,7 +148,6 @@
 void dbusmenu_menuitem_set_root (DbusmenuMenuitem * mi, gboolean root);
 gboolean dbusmenu_menuitem_get_root (DbusmenuMenuitem * mi);
 
-void dbusmenu_menuitem_buildxml (DbusmenuMenuitem * mi, GPtrArray * array, gint revision);
 void dbusmenu_menuitem_foreach (DbusmenuMenuitem * mi, void (*func) (DbusmenuMenuitem * mi, gpointer data), gpointer data);
 void dbusmenu_menuitem_activate (DbusmenuMenuitem * mi);
 

=== modified file 'libdbusmenu-glib/server-marshal.list'
--- libdbusmenu-glib/server-marshal.list	2009-04-16 16:26:18 +0000
+++ libdbusmenu-glib/server-marshal.list	2009-12-18 05:11:13 +0000
@@ -1,1 +1,2 @@
-VOID: UINT, STRING, STRING
+VOID: UINT, STRING, POINTER
+VOID: INT, UINT

=== modified file 'libdbusmenu-glib/server.c'
--- libdbusmenu-glib/server.c	2009-10-28 16:58:24 +0000
+++ libdbusmenu-glib/server.c	2009-12-18 05:11:13 +0000
@@ -30,16 +30,21 @@
 #include "config.h"
 #endif
 
+#include "menuitem-private.h"
 #include "server.h"
 #include "server-marshal.h"
 
 /* DBus Prototypes */
+static gboolean _dbusmenu_server_get_layout (DbusmenuServer * server, guint parent, guint * revision, gchar ** layout, GError ** error);
 static gboolean _dbusmenu_server_get_property (DbusmenuServer * server, guint id, gchar * property, gchar ** value, GError ** error);
-static gboolean _dbusmenu_server_get_properties (DbusmenuServer * server, guint id, GHashTable ** dict, GError ** error);
-static gboolean _dbusmenu_server_call (DbusmenuServer * server, guint id, GError ** error);
+static gboolean _dbusmenu_server_get_properties (DbusmenuServer * server, guint id, GPtrArray * properties, GHashTable ** dict, GError ** error);
+static gboolean _dbusmenu_server_get_group_properties (DbusmenuServer * server, GArray * ids, GArray * properties, GHashTable ** values, GError ** error);
+static gboolean _dbusmenu_server_event (DbusmenuServer * server, guint id, gchar * eventid, GValue * data, GError ** error);
 
 #include "dbusmenu-server.h"
 
+#define DBUSMENU_VERSION_NUMBER  1
+
 /* Privates, I'll show you mine... */
 typedef struct _DbusmenuServerPrivate DbusmenuServerPrivate;
 
@@ -68,7 +73,7 @@
 	PROP_0,
 	PROP_DBUS_OBJECT,
 	PROP_ROOT_NODE,
-	PROP_LAYOUT
+	PROP_VERSION
 };
 
 /* Errors */
@@ -76,6 +81,7 @@
 	INVALID_MENUITEM_ID,
 	INVALID_PROPERTY_NAME,
 	UNKNOWN_DBUS_ERROR,
+	NOT_IMPLEMENTED,
 	LAST_ERROR
 };
 
@@ -122,8 +128,8 @@
 	                                         G_SIGNAL_RUN_LAST,
 	                                         G_STRUCT_OFFSET(DbusmenuServerClass, id_prop_update),
 	                                         NULL, NULL,
-	                                         _dbusmenu_server_marshal_VOID__UINT_STRING_STRING,
-	                                         G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING);
+	                                         _dbusmenu_server_marshal_VOID__UINT_STRING_POINTER,
+	                                         G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_VALUE);
 	/**
 		DbusmenuServer::id-update:
 		@arg0: The #DbusmenuServer emitting the signal.
@@ -145,6 +151,7 @@
 		@arg0: The #DbusmenuServer emitting the signal.
 		@arg1: A revision number representing which revision the update
 		       represents itself as.
+		@arg2: The ID of the parent for this update.
 
 		This signal is emitted any time the layout of the
 		menuitems under this server is changed.
@@ -154,8 +161,8 @@
 	                                         G_SIGNAL_RUN_LAST,
 	                                         G_STRUCT_OFFSET(DbusmenuServerClass, layout_update),
 	                                         NULL, NULL,
-	                                         g_cclosure_marshal_VOID__INT,
-	                                         G_TYPE_NONE, 1, G_TYPE_INT);
+	                                         _dbusmenu_server_marshal_VOID__INT_UINT,
+	                                         G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_UINT);
 
 
 	g_object_class_install_property (object_class, PROP_DBUS_OBJECT,
@@ -168,10 +175,10 @@
 	                                              "The base object of the menus that are served",
 	                                              DBUSMENU_TYPE_MENUITEM,
 	                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-	g_object_class_install_property (object_class, PROP_LAYOUT,
-	                                 g_param_spec_string(DBUSMENU_SERVER_PROP_LAYOUT, "XML Layout of the menus",
-	                                              "A simple XML string that describes the layout of the menus",
-	                                              "<menu />",
+	g_object_class_install_property (object_class, PROP_VERSION,
+	                                 g_param_spec_uint(DBUSMENU_SERVER_PROP_VERSION, "Dbusmenu API version",
+	                                              "The version of the DBusmenu API that we're implementing.",
+	                                              DBUSMENU_VERSION_NUMBER, DBUSMENU_VERSION_NUMBER, DBUSMENU_VERSION_NUMBER,
 	                                              G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
 	dbus_g_object_type_install_info(DBUSMENU_TYPE_SERVER, &dbus_glib__dbusmenu_server_object_info);
@@ -240,11 +247,8 @@
 			g_debug("Setting root node to NULL");
 		}
 		priv->layout_revision++;
-		g_signal_emit(obj, signals[LAYOUT_UPDATE], 0, priv->layout_revision, TRUE);
+		g_signal_emit(obj, signals[LAYOUT_UPDATE], 0, priv->layout_revision, 0, TRUE);
 		break;
-	case PROP_LAYOUT:
-		/* Can't set this, fall through to error */
-		g_warning("Can not set property: layout");
 	default:
 		g_return_if_reached();
 		break;
@@ -276,25 +280,9 @@
 	case PROP_ROOT_NODE:
 		g_value_set_object(value, priv->root);
 		break;
-	case PROP_LAYOUT: {
-		GPtrArray * xmlarray = g_ptr_array_new();
-		if (priv->root == NULL) {
-			/* g_debug("Getting layout without root node!"); */
-			g_ptr_array_add(xmlarray, g_strdup_printf("<menu revision=\"%d\" />", priv->layout_revision));
-		} else {
-			dbusmenu_menuitem_buildxml(priv->root, xmlarray, priv->layout_revision);
-		}
-		g_ptr_array_add(xmlarray, NULL);
-
-		/* build string */
-		gchar * finalstring = g_strjoinv("", (gchar **)xmlarray->pdata);
-		g_value_take_string(value, finalstring);
-		/* g_debug("Final string: %s", finalstring); */
-
-		g_ptr_array_foreach(xmlarray, xmlarray_foreach_free, NULL);
-		g_ptr_array_free(xmlarray, TRUE);
+	case PROP_VERSION:
+		g_value_set_uint(value, DBUSMENU_VERSION_NUMBER);
 		break;
-	}
 	default:
 		g_return_if_reached();
 		break;
@@ -304,9 +292,12 @@
 }
 
 static void 
-menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, gchar * value, DbusmenuServer * server)
+menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, gchar * strvalue, DbusmenuServer * server)
 {
-	g_signal_emit(G_OBJECT(server), signals[ID_PROP_UPDATE], 0, dbusmenu_menuitem_get_id(mi), property, value, TRUE);
+	GValue value = {0};
+	g_value_init(&value, G_TYPE_STRING);
+	g_value_set_static_string(&value, strvalue);
+	g_signal_emit(G_OBJECT(server), signals[ID_PROP_UPDATE], 0, dbusmenu_menuitem_get_id(mi), property, &value, TRUE);
 	return;
 }
 
@@ -317,7 +308,7 @@
 	/* 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++;
-	g_signal_emit(G_OBJECT(server), signals[LAYOUT_UPDATE], 0, priv->layout_revision, TRUE);
+	g_signal_emit(G_OBJECT(server), signals[LAYOUT_UPDATE], 0, priv->layout_revision, 0, TRUE);
 	return;
 }
 
@@ -328,7 +319,7 @@
 	/* 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++;
-	g_signal_emit(G_OBJECT(server), signals[LAYOUT_UPDATE], 0, priv->layout_revision, TRUE);
+	g_signal_emit(G_OBJECT(server), signals[LAYOUT_UPDATE], 0, priv->layout_revision, 0, TRUE);
 	return;
 }
 
@@ -337,7 +328,7 @@
 {
 	DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
 	priv->layout_revision++;
-	g_signal_emit(G_OBJECT(server), signals[LAYOUT_UPDATE], 0, priv->layout_revision, TRUE);
+	g_signal_emit(G_OBJECT(server), signals[LAYOUT_UPDATE], 0, priv->layout_revision, 0, TRUE);
 	return;
 }
 
@@ -376,6 +367,36 @@
 }
 
 /* DBus interface */
+static gboolean
+_dbusmenu_server_get_layout (DbusmenuServer * server, guint parent, guint * revision, gchar ** layout, GError ** error)
+{
+	DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
+	*revision = priv->layout_revision;
+	GPtrArray * xmlarray = g_ptr_array_new();
+
+	if (parent == 0) {
+		if (priv->root == NULL) {
+			/* g_debug("Getting layout without root node!"); */
+			g_ptr_array_add(xmlarray, g_strdup_printf("<menu revision=\"%d\" />", priv->layout_revision));
+		} else {
+			dbusmenu_menuitem_buildxml(priv->root, xmlarray, priv->layout_revision);
+		}
+	} else {
+		DbusmenuMenuitem * item = dbusmenu_menuitem_find_id(priv->root, parent);
+		dbusmenu_menuitem_buildxml(item, xmlarray, priv->layout_revision);
+	}
+	g_ptr_array_add(xmlarray, NULL);
+
+	/* build string */
+	*layout = g_strjoinv("", (gchar **)xmlarray->pdata);
+
+	g_ptr_array_foreach(xmlarray, xmlarray_foreach_free, NULL);
+	g_ptr_array_free(xmlarray, TRUE);
+
+	return TRUE;
+}
+
 static gboolean 
 _dbusmenu_server_get_property (DbusmenuServer * server, guint id, gchar * property, gchar ** value, GError ** error)
 {
@@ -422,7 +443,7 @@
 }
 
 static gboolean
-_dbusmenu_server_get_properties (DbusmenuServer * server, guint id, GHashTable ** dict, GError ** error)
+_dbusmenu_server_get_properties (DbusmenuServer * server, guint id, GPtrArray * properties, GHashTable ** dict, GError ** error)
 {
 	DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
 	DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
@@ -444,7 +465,19 @@
 }
 
 static gboolean
-_dbusmenu_server_call (DbusmenuServer * server, guint id, GError ** error)
+_dbusmenu_server_get_group_properties (DbusmenuServer * server, GArray * ids, GArray * properties, GHashTable ** values, GError ** error)
+{
+	if (error != NULL) {
+		g_set_error(error,
+					error_quark(),
+					NOT_IMPLEMENTED,
+					"The GetGroupProperties function is not implemented, sorry.");
+	}
+	return FALSE;
+}
+
+static gboolean
+_dbusmenu_server_event (DbusmenuServer * server, guint id, gchar * eventid, GValue * data, GError ** error)
 {
 	DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
 	DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);

=== modified file 'libdbusmenu-glib/server.h'
--- libdbusmenu-glib/server.h	2009-09-21 19:55:10 +0000
+++ libdbusmenu-glib/server.h	2009-12-18 05:11:13 +0000
@@ -43,13 +43,13 @@
 #define DBUSMENU_IS_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_TYPE_SERVER))
 #define DBUSMENU_SERVER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_SERVER, DbusmenuServerClass))
 
-#define DBUSMENU_SERVER_SIGNAL_ID_PROP_UPDATE  "id-prop-update"
-#define DBUSMENU_SERVER_SIGNAL_ID_UPDATE       "id-update"
+#define DBUSMENU_SERVER_SIGNAL_ID_PROP_UPDATE  "item-property-updated"
+#define DBUSMENU_SERVER_SIGNAL_ID_UPDATE       "item-updated"
 #define DBUSMENU_SERVER_SIGNAL_LAYOUT_UPDATE   "layout-update"
 
 #define DBUSMENU_SERVER_PROP_DBUS_OBJECT       "dbus-object"
 #define DBUSMENU_SERVER_PROP_ROOT_NODE         "root-node"
-#define DBUSMENU_SERVER_PROP_LAYOUT            "layout"
+#define DBUSMENU_SERVER_PROP_VERSION           "version"
 
 /**
 	DbusmenuServerClass:

=== modified file 'libdbusmenu-gtk/Makefile.am'
--- libdbusmenu-gtk/Makefile.am	2009-10-01 19:22:18 +0000
+++ libdbusmenu-gtk/Makefile.am	2009-12-18 05:11:13 +0000
@@ -15,6 +15,8 @@
 libdbusmenu_gtk_la_SOURCES = \
 	client.h \
 	client.c \
+	genericmenuitem.h \
+	genericmenuitem.c \
 	menu.h \
 	menu.c \
 	menuitem.h \

=== modified file 'libdbusmenu-gtk/client.c'
--- libdbusmenu-gtk/client.c	2009-12-10 16:30:09 +0000
+++ libdbusmenu-gtk/client.c	2009-12-18 05:11:13 +0000
@@ -34,6 +34,7 @@
 
 #include "client.h"
 #include "menuitem.h"
+#include "genericmenuitem.h"
 
 /* Prototypes */
 static void dbusmenu_gtkclient_class_init (DbusmenuGtkClientClass *klass);
@@ -47,10 +48,10 @@
 
 static gboolean new_item_normal     (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client);
 static gboolean new_item_seperator  (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client);
-static gboolean new_item_image      (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client);
 
-static void process_visible (GtkMenuItem * gmi, const gchar * value);
-static void process_sensitive (GtkMenuItem * gmi, const gchar * value);
+static void process_visible (DbusmenuMenuitem * mi, GtkMenuItem * gmi, const GValue * value);
+static void process_sensitive (DbusmenuMenuitem * mi, GtkMenuItem * gmi, const GValue * value);
+static void image_property_handle (DbusmenuMenuitem * item, const gchar * property, const GValue * invalue, gpointer userdata);
 
 /* GObject Stuff */
 G_DEFINE_TYPE (DbusmenuGtkClient, dbusmenu_gtkclient, DBUSMENU_TYPE_CLIENT);
@@ -74,7 +75,6 @@
 {
 	dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(self), DBUSMENU_CLIENT_TYPES_DEFAULT,   new_item_normal);
 	dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(self), DBUSMENU_CLIENT_TYPES_SEPARATOR, new_item_seperator);
-	dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(self), DBUSMENU_CLIENT_TYPES_IMAGE,     new_item_image);
 
 	g_signal_connect(G_OBJECT(self), DBUSMENU_CLIENT_SIGNAL_NEW_MENUITEM, G_CALLBACK(new_menuitem), NULL);
 
@@ -115,9 +115,14 @@
 
 /* Process the visible property */
 static void
-process_visible (GtkMenuItem * gmi, const gchar * value)
+process_visible (DbusmenuMenuitem * mi, GtkMenuItem * gmi, const GValue * value)
 {
-	if (value == NULL || !g_strcmp0(value, "true")) {
+	gboolean val = TRUE;
+	if (value != NULL) {
+		val = dbusmenu_menuitem_property_get_bool(mi, DBUSMENU_MENUITEM_PROP_VISIBLE);
+	}
+
+	if (val) {
 		gtk_widget_show(GTK_WIDGET(gmi));
 	} else {
 		gtk_widget_hide(GTK_WIDGET(gmi));
@@ -127,27 +132,76 @@
 
 /* Process the sensitive property */
 static void
-process_sensitive (GtkMenuItem * gmi, const gchar * value)
-{
-	if (value == NULL || !g_strcmp0(value, "true")) {
-		gtk_widget_set_sensitive(GTK_WIDGET(gmi), TRUE);
-	} else {
-		gtk_widget_set_sensitive(GTK_WIDGET(gmi), FALSE);
-	}
+process_sensitive (DbusmenuMenuitem * mi, GtkMenuItem * gmi, const GValue * value)
+{
+	gboolean val = TRUE;
+	if (value != NULL) {
+		val = dbusmenu_menuitem_property_get_bool(mi, DBUSMENU_MENUITEM_PROP_SENSITIVE);
+	}
+	gtk_widget_set_sensitive(GTK_WIDGET(gmi), val);
+	return;
+}
+
+/* Process the sensitive property */
+static void
+process_toggle_type (DbusmenuMenuitem * mi, GtkMenuItem * gmi, const GValue * value)
+{
+	if (!IS_GENERICMENUITEM(gmi)) return;
+
+	GenericmenuitemCheckType type = GENERICMENUITEM_CHECK_TYPE_NONE;
+
+	if (value != NULL && G_VALUE_TYPE(value) == G_TYPE_STRING) {
+		const gchar * strval = g_value_get_string(value);
+
+		if (!g_strcmp0(strval, "checkbox")) {
+			type = GENERICMENUITEM_CHECK_TYPE_CHECKBOX;
+		} else if (!g_strcmp0(strval, "radio")) {
+			type = GENERICMENUITEM_CHECK_TYPE_RADIO;
+		}
+	}
+
+	genericmenuitem_set_check_type(GENERICMENUITEM(gmi), type);
+	
+	return;
+}
+
+/* Process the sensitive property */
+static void
+process_toggle_checked (DbusmenuMenuitem * mi, GtkMenuItem * gmi, const GValue * value)
+{
+	if (!IS_GENERICMENUITEM(gmi)) return;
+
+	GenericmenuitemState state = GENERICMENUITEM_STATE_UNCHECKED;
+
+	if (value != NULL && G_VALUE_TYPE(value) == G_TYPE_STRING) {
+		const gchar * strval = g_value_get_string(value);
+
+		if (!g_strcmp0(strval, "checked")) {
+			state = GENERICMENUITEM_STATE_CHECKED;
+		} else if (!g_strcmp0(strval, "indeterminate")) {
+			state = GENERICMENUITEM_STATE_INDETERMINATE;
+		}
+	}
+
+	genericmenuitem_set_state(GENERICMENUITEM(gmi), state);
 	return;
 }
 
 /* Whenever we have a property change on a DbusmenuMenuitem
    we need to be responsive to that. */
 static void
-menu_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, gchar * value, GtkMenuItem * gmi)
+menu_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GValue * value, GtkMenuItem * gmi)
 {
 	if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_LABEL)) {
-		gtk_menu_item_set_label(gmi, value);
+		gtk_menu_item_set_label(gmi, g_value_get_string(value));
 	} else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_VISIBLE)) {
-		process_visible(gmi, value);
+		process_visible(mi, gmi, value);
 	} else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_SENSITIVE)) {
-		process_sensitive(gmi, value);
+		process_sensitive(mi, gmi, value);
+	} else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE)) {
+		process_toggle_type(mi, gmi, value);
+	} else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_TOGGLE_CHECKED)) {
+		process_toggle_checked(mi, gmi, value);
 	}
 
 	return;
@@ -228,9 +282,13 @@
 	/* Life insurance */
 	g_object_weak_ref(G_OBJECT(item), destoryed_dbusmenuitem_cb, gmi);
 
-	process_visible(gmi, dbusmenu_menuitem_property_get(item, DBUSMENU_MENUITEM_PROP_VISIBLE));
-	process_sensitive(gmi, dbusmenu_menuitem_property_get(item, DBUSMENU_MENUITEM_PROP_SENSITIVE));
+	/* Check our set of props to see if any are set already */
+	process_visible(item, gmi, dbusmenu_menuitem_property_get_value(item, DBUSMENU_MENUITEM_PROP_VISIBLE));
+	process_sensitive(item, gmi, dbusmenu_menuitem_property_get_value(item, DBUSMENU_MENUITEM_PROP_SENSITIVE));
+	process_toggle_type(item, gmi, dbusmenu_menuitem_property_get_value(item, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE));
+	process_toggle_checked(item, gmi, dbusmenu_menuitem_property_get_value(item, DBUSMENU_MENUITEM_PROP_TOGGLE_CHECKED));
 
+	/* Oh, we're a child, let's deal with that */
 	if (parent != NULL) {
 		new_child(parent, item, dbusmenu_menuitem_get_position(item, parent), DBUSMENU_GTKCLIENT(client));
 	}
@@ -358,8 +416,8 @@
 	/* Note: not checking parent, it's reasonable for it to be NULL */
 
 	GtkMenuItem * gmi;
-	gmi = GTK_MENU_ITEM(gtk_menu_item_new_with_label(dbusmenu_menuitem_property_get(newitem, DBUSMENU_MENUITEM_PROP_LABEL)));
-        gtk_menu_item_set_use_underline (gmi, TRUE);
+	gmi = GTK_MENU_ITEM(g_object_new(GENERICMENUITEM_TYPE, NULL));
+	gtk_menu_item_set_label(gmi, dbusmenu_menuitem_property_get(newitem, DBUSMENU_MENUITEM_PROP_LABEL));
 
 	if (gmi != NULL) {
 		dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, gmi, parent);
@@ -367,6 +425,19 @@
 		return FALSE;
 	}
 
+	image_property_handle(newitem,
+	                      DBUSMENU_MENUITEM_PROP_ICON,
+	                      dbusmenu_menuitem_property_get_value(newitem, DBUSMENU_MENUITEM_PROP_ICON),
+	                      client);
+	image_property_handle(newitem,
+	                      DBUSMENU_MENUITEM_PROP_ICON_DATA,
+	                      dbusmenu_menuitem_property_get_value(newitem, DBUSMENU_MENUITEM_PROP_ICON_DATA),
+	                      client);
+	g_signal_connect(G_OBJECT(newitem),
+	                 DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED,
+	                 G_CALLBACK(image_property_handle),
+	                 client);
+
 	return TRUE;
 }
 
@@ -394,11 +465,17 @@
 /* This handler looks at property changes for items that are
    image menu items. */
 static void
-image_property_handle (DbusmenuMenuitem * item, const gchar * property, const gchar * value, gpointer userdata)
+image_property_handle (DbusmenuMenuitem * item, const gchar * property, const GValue * invalue, gpointer userdata)
 {
 	/* We're only looking at these two properties here */
 	g_return_if_fail(!g_strcmp0(property, DBUSMENU_MENUITEM_PROP_ICON) || !g_strcmp0(property, DBUSMENU_MENUITEM_PROP_ICON_DATA));
 
+	const gchar * value = NULL;
+	
+	if (invalue != NULL && G_VALUE_TYPE(invalue) == G_TYPE_STRING) {
+		value = g_value_get_string(invalue);
+	}
+
 	if (value == NULL || value[0] == '\0') {
 		/* This means that we're unsetting a value. */
 		/* Try to use the other one */
@@ -416,12 +493,12 @@
 		g_warning("Oddly we're handling image properties on a menuitem that doesn't have any GTK structures associated with it.");
 		return;
 	}
-	GtkWidget * gtkimage = gtk_image_menu_item_get_image(GTK_IMAGE_MENU_ITEM(gimi));
+	GtkWidget * gtkimage = genericmenuitem_get_image(GENERICMENUITEM(gimi));
 
 	if (!g_strcmp0(property, DBUSMENU_MENUITEM_PROP_ICON_DATA)) {
 		/* If we have an image already built from a name that is
 		   way better than a pixbuf.  Keep it. */
-		if (gtk_image_get_storage_type(GTK_IMAGE(gtkimage)) == GTK_IMAGE_ICON_NAME) {
+		if (gtkimage != NULL && gtk_image_get_storage_type(GTK_IMAGE(gtkimage)) == GTK_IMAGE_ICON_NAME) {
 			return;
 		}
 	}
@@ -474,42 +551,8 @@
 
 	}
 
-	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(gimi), gtkimage);
+	genericmenuitem_set_image(GENERICMENUITEM(gimi), gtkimage);
 
 	return;
 }
 
-/* This is a type call back for the image type where
-   it uses the GtkImageMenuitem to create the menu item. */
-static gboolean
-new_item_image (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client)
-{
-	g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE);
-	g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE);
-	/* Note: not checking parent, it's reasonable for it to be NULL */
-
-	GtkMenuItem * gmi;
-	gmi = GTK_MENU_ITEM(gtk_image_menu_item_new_with_label(dbusmenu_menuitem_property_get(newitem, DBUSMENU_MENUITEM_PROP_LABEL)));
-        gtk_menu_item_set_use_underline (gmi, TRUE);
-
-	if (gmi != NULL) {
-		dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, gmi, parent);
-	} else {
-		return FALSE;
-	}
-
-	image_property_handle(newitem,
-	                      DBUSMENU_MENUITEM_PROP_ICON,
-	                      dbusmenu_menuitem_property_get(newitem, DBUSMENU_MENUITEM_PROP_ICON),
-	                      client);
-	image_property_handle(newitem,
-	                      DBUSMENU_MENUITEM_PROP_ICON_DATA,
-	                      dbusmenu_menuitem_property_get(newitem, DBUSMENU_MENUITEM_PROP_ICON_DATA),
-	                      client);
-	g_signal_connect(G_OBJECT(newitem),
-	                 DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED,
-	                 G_CALLBACK(image_property_handle),
-	                 client);
-
-	return TRUE;
-}

=== added file 'libdbusmenu-gtk/genericmenuitem.c'
--- libdbusmenu-gtk/genericmenuitem.c	1970-01-01 00:00:00 +0000
+++ libdbusmenu-gtk/genericmenuitem.c	2009-12-18 05:11:13 +0000
@@ -0,0 +1,444 @@
+/*
+A menuitem subclass that has the ability to do lots of different
+things depending on it's settings.
+
+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 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 "genericmenuitem.h"
+
+/**
+	GenericmenuitemPrivate:
+	@check_type: What type of check we have, or none at all.
+	@state: What the state of our check is.
+*/
+struct _GenericmenuitemPrivate {
+	GenericmenuitemCheckType   check_type;
+	GenericmenuitemState       state;
+};
+
+/* Private macro */
+#define GENERICMENUITEM_GET_PRIVATE(o) \
+(G_TYPE_INSTANCE_GET_PRIVATE ((o), GENERICMENUITEM_TYPE, GenericmenuitemPrivate))
+
+/* Prototypes */
+static void genericmenuitem_class_init (GenericmenuitemClass *klass);
+static void genericmenuitem_init       (Genericmenuitem *self);
+static void genericmenuitem_dispose    (GObject *object);
+static void genericmenuitem_finalize   (GObject *object);
+static void draw_indicator (GtkCheckMenuItem *check_menu_item, GdkRectangle *area);
+static void set_label (GtkMenuItem * menu_item, const gchar * label);
+static const gchar * get_label (GtkMenuItem * menu_item);
+static void activate (GtkMenuItem * menu_item);
+
+/* GObject stuff */
+G_DEFINE_TYPE (Genericmenuitem, genericmenuitem, GTK_TYPE_CHECK_MENU_ITEM);
+
+/* Globals */
+static void (*parent_draw_indicator) (GtkCheckMenuItem *check_menu_item, GdkRectangle *area) = NULL;
+
+/* Initializing all of the classes.  Most notably we're
+   disabling the drawing of the check early. */
+static void
+genericmenuitem_class_init (GenericmenuitemClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (GenericmenuitemPrivate));
+
+	object_class->dispose = genericmenuitem_dispose;
+	object_class->finalize = genericmenuitem_finalize;
+
+	GtkCheckMenuItemClass * check_class = GTK_CHECK_MENU_ITEM_CLASS (klass);
+
+	parent_draw_indicator = check_class->draw_indicator;
+	check_class->draw_indicator = draw_indicator;
+
+	GtkMenuItemClass * menuitem_class = GTK_MENU_ITEM_CLASS (klass);
+	menuitem_class->set_label = set_label;
+	menuitem_class->get_label = get_label;
+	menuitem_class->activate = activate;
+
+	return;
+}
+
+/* Sets default values for all the class variables.  Mostly,
+   this puts us in a default state. */
+static void
+genericmenuitem_init (Genericmenuitem *self)
+{
+	self->priv = GENERICMENUITEM_GET_PRIVATE(self);
+
+	self->priv->check_type = GENERICMENUITEM_CHECK_TYPE_NONE;
+	self->priv->state = GENERICMENUITEM_STATE_UNCHECKED;
+
+	return;
+}
+
+/* Clean everything up.  Whew, that can be work. */
+static void
+genericmenuitem_dispose (GObject *object)
+{
+
+	G_OBJECT_CLASS (genericmenuitem_parent_class)->dispose (object);
+	return;
+}
+
+/* Now free memory, we no longer need it. */
+static void
+genericmenuitem_finalize (GObject *object)
+{
+
+	G_OBJECT_CLASS (genericmenuitem_parent_class)->finalize (object);
+	return;
+}
+
+/* Checks to see if we should be drawing a little box at
+   all.  If we should be, let's do that, otherwise we're
+   going suppress the box drawing. */
+static void
+draw_indicator (GtkCheckMenuItem *check_menu_item, GdkRectangle *area)
+{
+	Genericmenuitem * self = GENERICMENUITEM(check_menu_item);
+	if (self->priv->check_type != GENERICMENUITEM_CHECK_TYPE_NONE) {
+		parent_draw_indicator(check_menu_item, area);
+	}
+	return;
+}
+
+/* A small helper to look through the widgets in the
+   box and find the one that is the label. */
+static void
+set_label_helper (GtkWidget * widget, gpointer data)
+{
+	GtkWidget ** labelval = (GtkWidget **)data;
+	if (GTK_IS_LABEL(widget)) {
+		*labelval = widget;
+	}
+	return;
+}
+
+/* Set the label on the item */
+static void
+set_label (GtkMenuItem * menu_item, const gchar * label)
+{
+	GtkWidget * child = gtk_bin_get_child(GTK_BIN(menu_item));
+	GtkLabel * labelw = NULL;
+	gboolean suppress_update = FALSE;
+
+	/* Try to find if we have a label already */
+	if (child != NULL) {
+		if (GTK_IS_LABEL(child)) {
+			/* We've got a label, let's update it. */
+			labelw = GTK_LABEL(child);
+		} else if (GTK_IS_BOX(child)) {
+			/* Look for the label in the box */
+			gtk_container_foreach(GTK_CONTAINER(child), set_label_helper, &labelw);
+		} else {
+			/* We need to put the child into a new box and
+			   make the box the child of the menu item.  Basically
+			   we're inserting a box in the middle. */
+			GtkWidget * hbox = gtk_hbox_new(FALSE, 0);
+			g_object_ref(child);
+			gtk_container_remove(GTK_CONTAINER(menu_item), child);
+			gtk_box_pack_start(GTK_BOX(hbox), child, FALSE, FALSE, 0);
+			gtk_container_add(GTK_CONTAINER(menu_item), hbox);
+			gtk_widget_show(hbox);
+			g_object_unref(child);
+			child = hbox;
+			/* It's important to notice that labelw is not set
+			   by this condition.  There was no label to find. */
+		}
+	}
+
+	/* No we can see if we need to ethier build a label or just
+	   update the one that we already have. */
+	if (labelw == NULL) {
+		/* Build it */
+		labelw = GTK_LABEL(gtk_label_new(label));
+		gtk_label_set_use_underline(GTK_LABEL(labelw), TRUE);
+		gtk_misc_set_alignment(GTK_MISC(labelw), 0.0, 0.5);
+		gtk_widget_show(GTK_WIDGET(labelw));
+
+		/* Check to see if it needs to be in the bin for this
+		   menu item or whether it gets packed in a box. */
+		if (child == NULL) {
+			gtk_container_add(GTK_CONTAINER(menu_item), GTK_WIDGET(labelw));
+		} else {
+			gtk_box_pack_end(GTK_BOX(child), GTK_WIDGET(labelw), TRUE, TRUE, 0);
+		}
+	} else {
+		/* Oh, just an update.  No biggie. */
+		if (!g_strcmp0(label, gtk_label_get_label(labelw))) {
+			/* The only reason to suppress the update is if we had
+			   a label and the value was the same as the one we're
+			   getting in. */
+			suppress_update = TRUE;
+		} else {
+			gtk_label_set_label(labelw, label);
+		}
+	}
+
+	/* If we changed the value, tell folks. */
+	if (!suppress_update) {
+		g_object_notify(G_OBJECT(menu_item), "label");
+	}
+
+	return;
+}
+
+/* Get the text of the label for the item */
+static const gchar *
+get_label (GtkMenuItem * menu_item)
+{
+	GtkWidget * child = gtk_bin_get_child(GTK_BIN(menu_item));
+	GtkLabel * labelw = NULL;
+
+	/* Try to find if we have a label already */
+	if (child != NULL) {
+		if (GTK_IS_LABEL(child)) {
+			/* We've got a label, let's update it. */
+			labelw = GTK_LABEL(child);
+		} else if (GTK_IS_BOX(child)) {
+			/* Look for the label in the box */
+			gtk_container_foreach(GTK_CONTAINER(child), set_label_helper, &labelw);
+		}
+	}
+
+	if (labelw != NULL) {
+		return gtk_label_get_label(labelw);
+	}
+
+	return NULL;
+}
+
+/* Make sure we don't toggle when there is an
+   activate like a normal check menu item. */
+static void
+activate (GtkMenuItem * menu_item)
+{
+	return;
+}
+
+/**
+	genericmenuitem_set_check_type:
+	@item: #Genericmenuitem to set the type on
+	@check_type: Which type of check should be displayed
+
+	This function changes the type of the checkmark that
+	appears in the left hand gutter for the menuitem.
+*/
+void
+genericmenuitem_set_check_type (Genericmenuitem * item, GenericmenuitemCheckType check_type)
+{
+	if (item->priv->check_type == check_type) {
+		return;
+	}
+
+	item->priv->check_type = check_type;
+	GValue value = {0};
+
+	switch (item->priv->check_type) {
+	case GENERICMENUITEM_CHECK_TYPE_NONE:
+		/* We don't need to do anything here as we're queuing the
+		   draw and then when it draws it'll avoid drawing the
+		   check on the item. */
+		break;
+	case GENERICMENUITEM_CHECK_TYPE_CHECKBOX:
+		g_value_init(&value, G_TYPE_BOOLEAN);
+		g_value_set_boolean(&value, FALSE);
+		g_object_set_property(G_OBJECT(item), "draw-as-radio", &value);
+		break;
+	case GENERICMENUITEM_CHECK_TYPE_RADIO:
+		g_value_init(&value, G_TYPE_BOOLEAN);
+		g_value_set_boolean(&value, TRUE);
+		g_object_set_property(G_OBJECT(item), "draw-as-radio", &value);
+		break;
+	default:
+		g_warning("Generic Menuitem invalid check type: %d", check_type);
+		return;
+	}
+
+	gtk_widget_queue_draw(GTK_WIDGET(item));
+
+	return;
+}
+
+/**
+	genericmenuitem_set_state:
+	@item: #Genericmenuitem to set the type on
+	@check_type: What is the state of the check 
+
+	Sets the state of the check in the menu item.  It does
+	not require, but isn't really useful if the type of
+	check that the menuitem is set to #GENERICMENUITEM_CHECK_TYPE_NONE.
+*/
+void
+genericmenuitem_set_state (Genericmenuitem * item, GenericmenuitemState state)
+{
+	if (item->priv->state == state) {
+		return;
+	}
+
+	item->priv->state = state;
+
+	GtkCheckMenuItem * check = GTK_CHECK_MENU_ITEM(item);
+
+	gboolean old_active = check->active;
+	gboolean old_inconsist = check->inconsistent;
+
+	switch (item->priv->state) {
+	case GENERICMENUITEM_STATE_UNCHECKED:
+		check->active = FALSE;
+		check->inconsistent = FALSE;
+		break;
+	case GENERICMENUITEM_STATE_CHECKED:
+		check->active = TRUE;
+		check->inconsistent = FALSE;
+		break;
+	case GENERICMENUITEM_STATE_INDETERMINATE:
+		check->active = TRUE;
+		check->inconsistent = TRUE;
+		break;
+	default:
+		g_warning("Generic Menuitem invalid check state: %d", state);
+		return;
+	}
+
+	if (old_active != check->active) {
+		g_object_notify(G_OBJECT(item), "active");
+	}
+
+	if (old_inconsist != check->inconsistent) {
+		g_object_notify(G_OBJECT(item), "inconsistent");
+	}
+
+	gtk_widget_queue_draw(GTK_WIDGET(item));
+
+	return;
+}
+
+/* A small helper to look through the widgets in the
+   box and find the one that is the image. */
+static void
+set_image_helper (GtkWidget * widget, gpointer data)
+{
+	GtkWidget ** labelval = (GtkWidget **)data;
+	if (GTK_IS_IMAGE(widget)) {
+		*labelval = widget;
+	}
+	return;
+}
+
+/**
+	genericmenuitem_set_image:
+	@item: A #Genericmenuitem
+	@image: The image to set as the image of @item
+
+	Sets the image of the menu item.
+*/
+void
+genericmenuitem_set_image (Genericmenuitem * menu_item, GtkWidget * image)
+{
+	GtkWidget * child = gtk_bin_get_child(GTK_BIN(menu_item));
+	GtkImage * imagew = NULL;
+
+	/* Try to find if we have a label already */
+	if (child != NULL) {
+		if (GTK_IS_IMAGE(child)) {
+			/* We've got a label, let's update it. */
+			imagew = GTK_IMAGE(child);
+		} else if (GTK_IS_BOX(child)) {
+			/* Look for the label in the box */
+			gtk_container_foreach(GTK_CONTAINER(child), set_image_helper, &imagew);
+		} else if (image != NULL) {
+			/* We need to put the child into a new box and
+			   make the box the child of the menu item.  Basically
+			   we're inserting a box in the middle. */
+			GtkWidget * hbox = gtk_hbox_new(FALSE, 0);
+			g_object_ref(child);
+			gtk_container_remove(GTK_CONTAINER(menu_item), child);
+			gtk_box_pack_end(GTK_BOX(hbox), child, TRUE, TRUE, 0);
+			gtk_container_add(GTK_CONTAINER(menu_item), hbox);
+			gtk_widget_show(hbox);
+			g_object_unref(child);
+			child = hbox;
+			/* It's important to notice that imagew is not set
+			   by this condition.  There was no label to find. */
+		}
+	}
+
+	/* No we can see if we need to ethier replace and image or
+	   just put ourselves into the structures */
+	if (imagew != NULL) {
+		gtk_widget_destroy(GTK_WIDGET(imagew));
+	}
+
+	/* Check to see if it needs to be in the bin for this
+	   menu item or whether it gets packed in a box. */
+	if (image != NULL) {
+		if (child == NULL) {
+			gtk_container_add(GTK_CONTAINER(menu_item), GTK_WIDGET(image));
+		} else {
+			gtk_box_pack_start(GTK_BOX(child), GTK_WIDGET(image), FALSE, FALSE, 0);
+		}
+
+		gtk_widget_show(image);
+	}
+
+	return;
+}
+
+/**
+	genericmenuitem_get_image:
+	@item: A #Genericmenuitem
+
+	Returns the image if there is one.
+
+	Return value: A pointer to the image of the item or #NULL
+		if there isn't one.
+*/
+GtkWidget *
+genericmenuitem_get_image (Genericmenuitem * menu_item)
+{
+	GtkWidget * child = gtk_bin_get_child(GTK_BIN(menu_item));
+	GtkWidget * imagew = NULL;
+
+	/* Try to find if we have a label already */
+	if (child != NULL) {
+		if (GTK_IS_IMAGE(child)) {
+			/* We've got a label, let's update it. */
+			imagew = child;
+		} else if (GTK_IS_BOX(child)) {
+			/* Look for the label in the box */
+			gtk_container_foreach(GTK_CONTAINER(child), set_image_helper, &imagew);
+		}
+	}
+
+	return imagew;
+}

=== added file 'libdbusmenu-gtk/genericmenuitem.h'
--- libdbusmenu-gtk/genericmenuitem.h	1970-01-01 00:00:00 +0000
+++ libdbusmenu-gtk/genericmenuitem.h	2009-12-18 05:11:13 +0000
@@ -0,0 +1,91 @@
+/*
+A menuitem subclass that has the ability to do lots of different
+things depending on it's settings.
+
+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 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 __GENERICMENUITEM_H__
+#define __GENERICMENUITEM_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GENERICMENUITEM_TYPE            (genericmenuitem_get_type ())
+#define GENERICMENUITEM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GENERICMENUITEM_TYPE, Genericmenuitem))
+#define GENERICMENUITEM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GENERICMENUITEM_TYPE, GenericmenuitemClass))
+#define IS_GENERICMENUITEM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GENERICMENUITEM_TYPE))
+#define IS_GENERICMENUITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GENERICMENUITEM_TYPE))
+#define GENERICMENUITEM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GENERICMENUITEM_TYPE, GenericmenuitemClass))
+
+typedef struct _Genericmenuitem           Genericmenuitem;
+typedef struct _GenericmenuitemClass      GenericmenuitemClass;
+typedef struct _GenericmenuitemPrivate    GenericmenuitemPrivate;
+typedef enum   _GenericmenuitemCheckType  GenericmenuitemCheckType;
+typedef enum   _GenericmenuitemState      GenericmenuitemState;
+
+/**
+	GenericmenuitemClass:
+	@parent_class: Our parent #GtkCheckMenuItemClass
+*/
+struct _GenericmenuitemClass {
+	GtkCheckMenuItemClass parent_class;
+};
+
+/**
+	Genericmenuitem:
+	@parent: Our parent #GtkCheckMenuItem
+*/
+struct _Genericmenuitem {
+	GtkCheckMenuItem parent;
+	GenericmenuitemPrivate * priv;
+};
+
+enum _GenericmenuitemCheckType {
+	GENERICMENUITEM_CHECK_TYPE_NONE,
+	GENERICMENUITEM_CHECK_TYPE_CHECKBOX,
+	GENERICMENUITEM_CHECK_TYPE_RADIO
+};
+
+enum _GenericmenuitemState {
+	GENERICMENUITEM_STATE_UNCHECKED,
+	GENERICMENUITEM_STATE_CHECKED,
+	GENERICMENUITEM_STATE_INDETERMINATE
+};
+
+GType        genericmenuitem_get_type        (void);
+void         genericmenuitem_set_check_type  (Genericmenuitem *         item,
+                                              GenericmenuitemCheckType  check_type);
+void         genericmenuitem_set_state       (Genericmenuitem *         item,
+                                              GenericmenuitemState      state);
+void         genericmenuitem_set_image       (Genericmenuitem *         item,
+                                              GtkWidget *               image);
+GtkWidget *  genericmenuitem_get_image       (Genericmenuitem *         item);
+
+G_END_DECLS
+
+#endif

=== modified file 'tests/Makefile.am'
--- tests/Makefile.am	2009-12-10 19:00:56 +0000
+++ tests/Makefile.am	2009-12-18 05:11:13 +0000
@@ -1,5 +1,5 @@
 
-DBUS_RUNNER=dbus-test-runner --dbus-config /usr/share/dbus-test-runner/session.conf
+DBUS_RUNNER=dbus-test-runner
 
 TESTS = \
 	test-glib-layout \
@@ -42,7 +42,7 @@
 
 test-glib-layout: test-glib-layout-client test-glib-layout-server Makefile.am
 	@echo "#!/bin/bash" > $@
-	@echo $(DBUS_RUNNER) --task ./test-glib-layout-client --task-name Client --task ./test-glib-layout-server --task-name Server --ignore-return >> $@
+	@echo $(DBUS_RUNNER) -b $@.bustle --task ./test-glib-layout-client --task-name Client --task ./test-glib-layout-server --task-name Server --ignore-return >> $@
 	@chmod +x $@
 
 test_glib_layout_server_SOURCES = \
@@ -76,7 +76,7 @@
 
 test-glib-properties: test-glib-properties-client test-glib-properties-server Makefile.am
 	@echo "#!/bin/bash" > $@
-	@echo $(DBUS_RUNNER) --task ./test-glib-properties-client --task-name Client --task ./test-glib-properties-server --task-name Server --ignore-return >> $@
+	@echo $(DBUS_RUNNER) -b $@.bustle --task ./test-glib-properties-client --task-name Client --task ./test-glib-properties-server --task-name Server --ignore-return >> $@
 	@chmod +x $@
 
 test_glib_properties_server_SOURCES = \
@@ -125,7 +125,7 @@
 test-gtk-label: test-gtk-label-client test-gtk-label-server test-gtk-label.json Makefile.am
 	@echo "#!/bin/bash" > $@
 	@echo $(XVFB_RUN) >> $@
-	@echo $(DBUS_RUNNER) --task ./test-gtk-label-client --task-name Client --task ./test-gtk-label-server --parameter $(srcdir)/test-gtk-label.json --task-name Server --ignore-return >> $@
+	@echo $(DBUS_RUNNER) -b $@.bustle --task ./test-gtk-label-client --task-name Client --task ./test-gtk-label-server --parameter $(srcdir)/test-gtk-label.json --task-name Server --ignore-return >> $@
 	@chmod +x $@
 
 test_gtk_label_server_SOURCES = \
@@ -165,7 +165,7 @@
 test-gtk-reorder: test-gtk-label-client test-gtk-reorder-server Makefile.am
 	@echo "#!/bin/bash" > $@
 	@echo $(XVFB_RUN) >> $@
-	@echo $(DBUS_RUNNER) --task ./test-gtk-label-client --task-name Client --task ./test-gtk-reorder-server --parameter $(srcdir)/test-gtk-label.json --task-name Server --ignore-return >> $@
+	@echo $(DBUS_RUNNER) -b $@.bustle --task ./test-gtk-label-client --task-name Client --task ./test-gtk-reorder-server --parameter $(srcdir)/test-gtk-label.json --task-name Server --ignore-return >> $@
 	@chmod +x $@
 
 test_gtk_reorder_server_SOURCES = \
@@ -238,4 +238,9 @@
 	-rm -rf $(builddir)/mago.results
 
 DISTCLEANFILES = \
-	$(TESTS)
+	$(TESTS) \
+	test-glib-layout.bustle \
+	test-glib-properties.bustle \
+	test-glib-simple-items.bustle \
+	test-gtk-label.bustle \
+	test-gtk-reorder.bustle

=== modified file 'tests/test-glib-layout-client.c'
--- tests/test-glib-layout-client.c	2009-12-10 19:00:56 +0000
+++ tests/test-glib-layout-client.c	2009-12-18 05:11:13 +0000
@@ -111,7 +111,7 @@
 
 	g_usleep(500000);
 
-	DbusmenuClient * client = dbusmenu_client_new(":1.0", "/org/test");
+	DbusmenuClient * client = dbusmenu_client_new(":1.1", "/org/test");
 	g_signal_connect(G_OBJECT(client), DBUSMENU_CLIENT_SIGNAL_LAYOUT_UPDATED, G_CALLBACK(layout_updated), NULL);
 
 	g_timeout_add_seconds(10, timer_func, client);

=== modified file 'tests/test-glib-properties-client.c'
--- tests/test-glib-properties-client.c	2009-12-10 19:00:56 +0000
+++ tests/test-glib-properties-client.c	2009-12-18 05:11:13 +0000
@@ -155,7 +155,7 @@
 	/* Make sure the server starts up and all that */
 	g_usleep(500000);
 
-	DbusmenuClient * client = dbusmenu_client_new(":1.0", "/org/test");
+	DbusmenuClient * client = dbusmenu_client_new(":1.1", "/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);

=== modified file 'tests/test-gtk-label-client.c'
--- tests/test-gtk-label-client.c	2009-12-10 19:00:56 +0000
+++ tests/test-gtk-label-client.c	2009-12-18 05:11:13 +0000
@@ -152,9 +152,11 @@
 {
 	gtk_init(&argc, &argv);
 
+	g_debug("Client Initialized.  Waiting.");
 	/* Make sure the server starts up and all that */
 	g_usleep(500000);
 
+	g_debug("Building Window");
 	GtkWidget * window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 	GtkWidget * menubar = gtk_menu_bar_new();
 	GtkWidget * menuitem = gtk_menu_item_new_with_label("Test");
@@ -168,6 +170,7 @@
 
 	death_timer = g_timeout_add_seconds(60, timer_func, window);
 
+	g_debug("Entering Mainloop");
 	mainloop = g_main_loop_new(NULL, FALSE);
 	g_main_loop_run(mainloop);
 

=== modified file 'tests/test-gtk-label.json'
--- tests/test-gtk-label.json	2009-09-03 19:16:01 +0000
+++ tests/test-gtk-label.json	2009-12-18 05:11:13 +0000
@@ -205,43 +205,43 @@
 	 "label": "value1",
 	 "submenu": [
 	 	{"id": 80,
-		 "type": "imageitem",
+		 "type": "menuitem",
 		 "icon": "face-angel",
 		 "label": "angel"},
 	 	{"id": 81,
-		 "type": "imageitem",
+		 "type": "menuitem",
 		 "icon": "face-angry",
 		 "label": "angry"},
 	 	{"id": 82,
-		 "type": "imageitem",
+		 "type": "menuitem",
 		 "icon": "face-cool",
 		 "label": "cool"},
 	 	{"id": 83,
-		 "type":"imageitem",
+		 "type":"menuitem",
 		 "icon": "face-devilish",
 		 "label": "devilish"},
 	 	{"id": 84,
-		 "type": "imageitem",
+		 "type": "menuitem",
 		 "icon": "face-embarrassed",
 		 "label": "embarrassed"},
 	 	{"id": 85,
-		 "type": "imageitem",
+		 "type": "menuitem",
 		 "icon": "face-kiss",
 		 "label": "kiss"},
 	 	{"id": 86,
-		 "type": "imageitem",
+		 "type": "menuitem",
 		 "icon": "face-laugh",
 		 "label": "laugh"},
 	 	{"id": 87,
-		 "type": "imageitem",
+		 "type": "menuitem",
 		 "icon": "face-monkey",
 		 "label": "monkey"},
 	 	{"id": 88,
-		 "type": "imageitem",
+		 "type": "menuitem",
 		 "icon": "face-sad",
 		 "label": "sad"},
 	 	{"id": 89,
-		 "type": "imageitem",
+		 "type": "menuitem",
 		 "icon": "face-sick",
 		 "label": "sick"}
 	 ]
@@ -250,7 +250,7 @@
 	 "label": "value1",
 	 "submenu": [
 	 	{"id": 90,
-		 "type": "imageitem",
+		 "type": "menuitem",
 		 "icon-data":
 "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACPUlEQVR4nGJgoBAAAAAA///Ch1gW
 BzK0LQ5iaGNgYGDBpQgAAAD//8KpeY4/Q9+DCV7/H/S4/p8byDABlyEAAAAA///CqnluAMOEx5O8
@@ -266,7 +266,7 @@
 QmCC",
 		 "label": "up"},
 	 	{"id": 91,
-		 "type": "imageitem",
+		 "type": "menuitem",
 		 "icon-data":
 "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACl0lEQVR4nGJgoBAAAAAA//9ixCLG
 sSWS4bs0B1QWip/+YGDwWcLAycDA8ANZMQAAAP//YsFigIA0JwODdvIsBob/fxgY/vxk+P/7OwPD
@@ -283,7 +283,7 @@
 +ys2zQwMDAwAAAAA//8DAAF5nhyE7tENAAAAAElFTkSuQmCC",
 		 "label": "down"},
 	 	{"id": 92,
-		 "type": "imageitem",
+		 "type": "menuitem",
 		 "icon": "up",
 		 "icon-data":
 "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACl0lEQVR4nGJgoBAAAAAA//9ixCLG
@@ -301,7 +301,7 @@
 +ys2zQwMDAwAAAAA//8DAAF5nhyE7tENAAAAAElFTkSuQmCC",
 		 "label": "up"},
 	 	{"id": 93,
-		 "type": "imageitem",
+		 "type": "menuitem",
 		 "icon": "down",
 		 "icon-data":
 "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACPUlEQVR4nGJgoBAAAAAA///Ch1gW
@@ -319,4 +319,53 @@
 		 "label": "down"}
 	 ]
 	},
+	{"id": 1, "type": "menuitem",
+	 "label": "value1",
+	 "submenu": [
+	 	{"id": 30,
+		 "label": "No check (empty)",
+		 "toggle-type": "none"
+		 },
+	 	{"id": 31,
+		 "label": "No check (checked)",
+		 "toggle-type": "none",
+		 "toggle-checked": "checked"
+		 },
+	 	{"id": 32,
+		 "label": "No check (????)",
+		 "toggle-type": "none",
+		 "toggle-checked": "indeterminate"
+		 },
+	 	{"id": 33,
+		 "label": "Check (empty)",
+		 "toggle-type": "checkbox",
+		 "toggle-checked": "unchecked"
+		 },
+	 	{"id": 34,
+		 "label": "Check (checked)",
+		 "toggle-type": "checkbox",
+		 "toggle-checked": "checked"
+		 },
+	 	{"id": 35,
+		 "label": "Check (?????)",
+		 "toggle-type": "checkbox",
+		 "toggle-checked": "indeterminate"
+		 },
+	 	{"id": 36,
+		 "label": "Radio (empty)",
+		 "toggle-type": "radio",
+		 "toggle-checked": "unchecked"
+		 },
+	 	{"id": 37,
+		 "label": "Radio (checked)",
+		 "toggle-type": "radio",
+		 "toggle-checked": "checked"
+		 },
+	 	{"id": 38,
+		 "label": "Radio (?????)",
+		 "toggle-type": "radio",
+		 "toggle-checked": "indeterminate"
+		 }
+	 ]
+	},
 ]

=== modified file 'tools/dbusmenu-dumper.c'
--- tools/dbusmenu-dumper.c	2009-10-06 02:28:14 +0000
+++ tools/dbusmenu-dumper.c	2009-12-18 05:11:13 +0000
@@ -36,7 +36,11 @@
 	GList * properties = dbusmenu_menuitem_properties_list(item);
 	GList * property;
 	for (property = properties; property != NULL; property = g_list_next(property)) {
-		g_print(",\n%s\"%s\": \"%s\"", space, (gchar *)property->data, dbusmenu_menuitem_property_get(item, (gchar *)property->data));
+		GValue value = {0};
+		g_value_init(&value, G_TYPE_STRING);
+		g_value_transform(dbusmenu_menuitem_property_get_value(item, (gchar *)property->data), &value);
+		g_print(",\n%s\"%s\": \"%s\"", space, (gchar *)property->data, g_value_get_string(&value));
+		g_value_unset(&value);
 	}
 	g_list_free(properties);
 


Follow ups