← Back to team overview

ayatana-commits team mailing list archive

[Branch ~dbusmenu-team/dbusmenu/trunk] Rev 40: Updating to the basis of the v2 changes for dbusmenu

 

Merge authors:
  Aurélien Gâteau (agateau)
  Cody Russell <cody.russell@xxxxxxxxxxxxx>
  Ted Gould (ted)
  Ted Gould (ted)
Related merge proposals:
  https://code.launchpad.net/~ted/dbusmenu/v2work/+merge/16323
  proposed by: Ted Gould (ted)
  review: Approve - Cody Russell (bratsche)
  review: Approve - Neil J. Patel (njpatel)
  https://code.launchpad.net/~agateau/dbusmenu/new-api/+merge/14845
  proposed by: Aurélien Gâteau (agateau)
  review: Needs Information - Robert Collins (lifeless)
------------------------------------------------------------
revno: 40 [merge]
committer: Ted Gould <ted@xxxxxxxx>
branch nick: trunk
timestamp: Fri 2009-12-18 12:08:12 -0600
message:
  Updating to the basis of the v2 changes for dbusmenu
added:
  libdbusmenu-glib/menuitem-private.h
  libdbusmenu-gtk/genericmenuitem.c
  libdbusmenu-gtk/genericmenuitem.h
modified:
  .bzrignore
  libdbusmenu-glib/Makefile.am
  libdbusmenu-glib/client.c
  libdbusmenu-glib/client.h
  libdbusmenu-glib/dbus-menu.xml
  libdbusmenu-glib/menuitem.c
  libdbusmenu-glib/menuitem.h
  libdbusmenu-glib/server-marshal.list
  libdbusmenu-glib/server.c
  libdbusmenu-glib/server.h
  libdbusmenu-gtk/Makefile.am
  libdbusmenu-gtk/client.c
  tests/Makefile.am
  tests/test-glib-layout-client.c
  tests/test-glib-properties-client.c
  tests/test-gtk-label-client.c
  tests/test-gtk-label.json
  tools/dbusmenu-dumper.c


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

Your team ayatana-commits is subscribed to branch lp:dbusmenu.
To unsubscribe from this branch go to https://code.launchpad.net/~dbusmenu-team/dbusmenu/trunk/+edit-subscription.
=== modified file '.bzrignore'
--- .bzrignore	2009-12-10 19:00:56 +0000
+++ .bzrignore	2009-12-17 22:41:01 +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-11-13 18:02:08 +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 17:59:58 +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,10 +321,14 @@
 /* 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>");
+	GValue valstr = {0};
+	g_value_init(&valstr, G_TYPE_STRING);
+	g_value_transform(value, &valstr);
+	g_debug("Property change sent to client for item %d property %s value %s", id, property, g_utf8_strlen(g_value_get_string(&valstr), 50) < 25 ? g_value_get_string(&valstr) : "<too long>");
+	g_value_unset(&valstr);
 	#endif
 
 	DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
@@ -332,7 +337,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 +356,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 +495,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 +572,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 +614,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 +655,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 +700,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 +783,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 +820,7 @@
 {
 	DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
 
-	if (priv->propproxy == NULL) {
+	if (priv->menuproxy == NULL) {
 		return;
 	}
 
@@ -824,14 +828,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-10 04:57:31 +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-08 17:54:27 +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-11-13 18:02:08 +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-15 20:03:08 +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-15 17:50:59 +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-09 17:17:32 +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-10 03:50:25 +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-09 19:58:32 +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-15 22:04:29 +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-17 22:43:32 +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-17 21:49:27 +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-17 20:25:32 +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-17 22:41:01 +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-17 22:41:01 +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-17 22:41:01 +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-17 22:41:01 +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-17 21:43:00 +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-15 20:35:08 +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);