← Back to team overview

ayatana-commits team mailing list archive

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

 

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

Requested reviews:
    DBus Menu Team (dbusmenu-team)


Adds a simple system for extending the menu items to the point where it can say "I handle these types." Then use this in the gtk version of the library to add separators and a shell for images in the future.  Also now we handle visibility.
-- 
https://code.launchpad.net/~ted/dbusmenu/types/+merge/10772
Your team ayatana-commits is subscribed to branch lp:dbusmenu.
=== modified file 'configure.ac'
--- configure.ac	2009-06-24 17:35:35 +0000
+++ configure.ac	2009-08-26 21:31:40 +0000
@@ -5,7 +5,7 @@
 AC_PREREQ(2.53)
 
 AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE(libdbusmenu, 0.0.1)
+AM_INIT_AUTOMAKE(libdbusmenu, 0.0.2)
 
 AM_MAINTAINER_MODE
 
@@ -19,6 +19,8 @@
 AC_SUBST(VERSION)
 AC_CONFIG_MACRO_DIR([m4])
 
+m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
+
 ###########################
 # Dependencies - GLib
 ###########################

=== modified file 'libdbusmenu-glib/client.c'
--- libdbusmenu-glib/client.c	2009-07-20 21:34:39 +0000
+++ libdbusmenu-glib/client.c	2009-08-26 20:42:27 +0000
@@ -68,6 +68,16 @@
 	DBusGProxyCall * layoutcall;
 
 	DBusGProxy * dbusproxy;
+
+	GHashTable * type_handlers;
+};
+
+typedef struct _newItemPropData newItemPropData;
+struct _newItemPropData
+{
+	DbusmenuClient * client;
+	DbusmenuMenuitem * item;
+	DbusmenuMenuitem * parent;
 };
 
 #define DBUSMENU_CLIENT_GET_PRIVATE(o) \
@@ -187,6 +197,9 @@
 
 	priv->dbusproxy = NULL;
 
+	priv->type_handlers = g_hash_table_new_full(g_str_hash, g_str_equal,
+	                                            g_free, NULL);
+
 	return;
 }
 
@@ -230,6 +243,10 @@
 	g_free(priv->dbus_name);
 	g_free(priv->dbus_object);
 
+	if (priv->type_handlers != NULL) {
+		g_hash_table_destroy(priv->type_handlers);
+	}
+
 	G_OBJECT_CLASS (dbusmenu_client_parent_class)->finalize (object);
 	return;
 }
@@ -515,6 +532,43 @@
 	return;
 }
 
+/* This is a different get properites call back that also sends
+   new signals.  It basically is a small wrapper around the original. */
+static void
+menuitem_get_properties_new_cb (DBusGProxy * proxy, GHashTable * properties, GError * error, gpointer data)
+{
+	newItemPropData * propdata = (newItemPropData *)data;
+	DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(propdata->client);
+
+	menuitem_get_properties_cb (proxy, properties, error, propdata->item);
+
+	gboolean handled = FALSE;
+
+	const gchar * type;
+	DbusmenuClientTypeHandler newfunc = NULL;
+	
+	type = dbusmenu_menuitem_property_get(propdata->item, "type");
+	if (type != NULL) {
+		newfunc = g_hash_table_lookup(priv->type_handlers, type);
+	} else {
+		newfunc = g_hash_table_lookup(priv->type_handlers, DBUSMENU_CLIENT_TYPES_DEFAULT);
+	}
+
+	if (newfunc != NULL) {
+		handled = newfunc(propdata->item, propdata->parent, propdata->client);
+	}
+
+	g_signal_emit(G_OBJECT(propdata->item), DBUSMENU_MENUITEM_SIGNAL_REALIZED_ID, 0, TRUE);
+
+	if (!handled) {
+		g_signal_emit(G_OBJECT(propdata->client), signals[NEW_MENUITEM], 0, propdata->item, TRUE);
+	}
+
+	g_free(propdata);
+
+	return;
+}
+
 static void
 menuitem_call_cb (DBusGProxy * proxy, GError * error, gpointer userdata)
 {
@@ -562,9 +616,15 @@
 			dbusmenu_menuitem_set_root(item, TRUE);
 		}
 		g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(menuitem_activate), client);
-		g_signal_emit(G_OBJECT(client), signals[NEW_MENUITEM], 0, item, TRUE);
+
 		/* Get the properties queued up for this item */
-		org_freedesktop_dbusmenu_get_properties_async(proxy, id, menuitem_get_properties_cb, item);
+		/* Not happy about this, but I need these :( */
+		newItemPropData * propdata = g_new0(newItemPropData, 1);
+		propdata->client  = client;
+		propdata->item    = item;
+		propdata->parent  = parent;
+
+		org_freedesktop_dbusmenu_get_properties_async(proxy, id, menuitem_get_properties_new_cb, propdata);
 	} 
 
 	xmlNodePtr children;
@@ -744,3 +804,46 @@
 
 	return priv->root;
 }
+
+/**
+	dbusmenu_client_add_type_handler:
+	@client: Client where we're getting types coming in
+	@type: A text string that will be matched with the 'type'
+	    property on incoming menu items
+	@newfunc: The function that will be executed with those new
+	    items when they come in.
+
+	This function connects into the type handling of the #DbusmenuClient.
+	Every new menuitem that comes in immediately gets asked for it's
+	properties.  When we get those properties we check the 'type'
+	property and look to see if it matches a handler that is known
+	by the client.  If so, the @newfunc function is executed on that
+	#DbusmenuMenuitem.  If not, then the DbusmenuClient::new-menuitem
+	signal is sent.
+
+	In the future the known types will be sent to the server so that it
+	can make choices about the menu item types availble.
+
+	Return value: If registering the new type was successful.
+*/
+gboolean
+dbusmenu_client_add_type_handler (DbusmenuClient * client, const gchar * type, DbusmenuClientTypeHandler newfunc)
+{
+	g_return_val_if_fail(DBUSMENU_IS_CLIENT(client), FALSE);
+
+	DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+
+	if (priv->type_handlers == NULL) {
+		g_warning("Type handlers hashtable not built");
+		return FALSE;
+	}
+
+	gpointer value = g_hash_table_lookup(priv->type_handlers, type);
+	if (value != NULL) {
+		g_warning("Type '%s' already had a registered handler.", type);
+		return FALSE;
+	}
+
+	g_hash_table_insert(priv->type_handlers, g_strdup(type), newfunc);
+	return TRUE;
+}

=== modified file 'libdbusmenu-glib/client.h'
--- libdbusmenu-glib/client.h	2009-06-23 20:44:29 +0000
+++ libdbusmenu-glib/client.h	2009-08-26 20:15:13 +0000
@@ -50,10 +50,15 @@
 #define DBUSMENU_CLIENT_PROP_DBUS_NAME     "dbus-name"
 #define DBUSMENU_CLIENT_PROP_DBUS_OBJECT   "dbus-object"
 
+#define DBUSMENU_CLIENT_TYPES_DEFAULT      "menuitem"
+#define DBUSMENU_CLIENT_TYPES_SEPARATOR    "separator"
+#define DBUSMENU_CLIENT_TYPES_IMAGE        "imageitem"
+
 /**
 	DbusmenuClientClass:
 	@parent_class: #GObjectClass
 	@layout_updated: Slot for #DbusmenuClient::layout-updated.
+	@new_menuitem: Slot for #DbusmenuClient::new-menuitem.
 	@reserved1: Reserved for future use.
 	@reserved2: Reserved for future use.
 	@reserved3: Reserved for future use.
@@ -90,9 +95,15 @@
 	GObject parent;
 };
 
-GType                dbusmenu_client_get_type   (void);
-DbusmenuClient *     dbusmenu_client_new        (const gchar * name, const gchar * object);
-DbusmenuMenuitem *   dbusmenu_client_get_root   (DbusmenuClient * client);
+typedef gboolean (*DbusmenuClientTypeHandler) (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client);
+
+GType                dbusmenu_client_get_type          (void);
+DbusmenuClient *     dbusmenu_client_new               (const gchar * name,
+                                                        const gchar * object);
+DbusmenuMenuitem *   dbusmenu_client_get_root          (DbusmenuClient * client);
+gboolean             dbusmenu_client_add_type_handler  (DbusmenuClient * client,
+                                                        const gchar * type,
+                                                        DbusmenuClientTypeHandler newfunc);
 
 /**
 	SECTION:client

=== modified file 'libdbusmenu-glib/menuitem.c'
--- libdbusmenu-glib/menuitem.c	2009-06-26 18:57:59 +0000
+++ libdbusmenu-glib/menuitem.c	2009-08-25 16:16:26 +0000
@@ -61,6 +61,7 @@
 	CHILD_ADDED,
 	CHILD_REMOVED,
 	CHILD_MOVED,
+	REALIZED,
 	LAST_SIGNAL
 };
 
@@ -178,6 +179,22 @@
 	                                           NULL, NULL,
 	                                           _dbusmenu_menuitem_marshal_VOID__OBJECT_UINT_UINT,
 	                                           G_TYPE_NONE, 3, G_TYPE_OBJECT, G_TYPE_UINT, G_TYPE_UINT);
+	/**
+		DbusmenuMenuitem::realized:
+		@arg0: The #DbusmenuMenuitem object.
+
+		Emitted when the initial request for properties
+		is complete on the item.  If there is a type
+		handler configured for the "type" parameter
+		that will be executed before this is signaled.
+	*/
+	signals[REALIZED] =           g_signal_new(DBUSMENU_MENUITEM_SIGNAL_REALIZED,
+	                                           G_TYPE_FROM_CLASS(klass),
+	                                           G_SIGNAL_RUN_LAST,
+	                                           G_STRUCT_OFFSET(DbusmenuMenuitemClass, realized),
+	                                           NULL, NULL,
+	                                           _dbusmenu_menuitem_marshal_VOID__VOID,
+	                                           G_TYPE_NONE, 0, G_TYPE_NONE);
 
 	g_object_class_install_property (object_class, PROP_ID,
 	                                 g_param_spec_uint("id", "ID for the menu item",

=== modified file 'libdbusmenu-glib/menuitem.h'
--- libdbusmenu-glib/menuitem.h	2009-06-26 17:49:52 +0000
+++ libdbusmenu-glib/menuitem.h	2009-08-26 20:18:50 +0000
@@ -47,6 +47,13 @@
 #define DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED         "child-added"
 #define DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED       "child-removed"
 #define DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED         "child-moved"
+#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_VISIBLE               "visible"
+#define DBUSMENU_MENUITEM_PROP_LABEL                 "label"
+#define DBUSMENU_MENUITEM_PROP_ICON                  "icon"
+#define DBUSMENU_MENUITEM_PROP_ICON_DATA             "icon-data"
 
 /**
 	DbusmenuMenuitem:
@@ -71,6 +78,7 @@
 	@child_added: Slot for #DbusmenuMenuitem::child-added.
 	@child_removed: Slot for #DbusmenuMenuitem::child-removed.
 	@child_moved: Slot for #DbusmenuMenuitem::child-moved.
+	@realized: Slot for #DbusmenuMenuitem::realized.
 	@buildxml: Virtual function that appends the strings required
 	           to represent this menu item in the menu XML file.
 	@reserved1: Reserved for future use.
@@ -89,6 +97,7 @@
 	void (*child_added) (DbusmenuMenuitem * child, guint position);
 	void (*child_removed) (DbusmenuMenuitem * child);
 	void (*child_moved) (DbusmenuMenuitem * child, guint newpos, guint oldpos);
+	void (*realized) (void);
 
 	/* Virtual functions */
 	void (*buildxml) (GPtrArray * stringarray);
@@ -96,7 +105,7 @@
 	void (*reserved1) (void);
 	void (*reserved2) (void);
 	void (*reserved3) (void);
-	void (*reserved4) (void);
+	/* void (*reserved4) (void); -- realized, realloc when bumping lib version */
 };
 
 GType dbusmenu_menuitem_get_type (void);

=== modified file 'libdbusmenu-gtk/client.c'
--- libdbusmenu-gtk/client.c	2009-06-26 17:53:15 +0000
+++ libdbusmenu-gtk/client.c	2009-08-26 20:25:37 +0000
@@ -44,6 +44,10 @@
 static void delete_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, DbusmenuGtkClient * gtkclient);
 static void move_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint new, guint old, DbusmenuGtkClient * gtkclient);
 
+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);
+
 /* GObject Stuff */
 G_DEFINE_TYPE (DbusmenuGtkClient, dbusmenu_gtkclient, DBUSMENU_TYPE_CLIENT);
 
@@ -61,7 +65,12 @@
 static void
 dbusmenu_gtkclient_init (DbusmenuGtkClient *self)
 {
+	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);
+
 	return;
 }
 
@@ -95,14 +104,27 @@
 	return TRUE;
 }
 
+/* Process the visible property */
+static void
+process_visible (GtkMenuItem * gmi, const gchar * value)
+{
+	if (value == NULL || !g_strcmp0(value, "true")) {
+		gtk_widget_show(GTK_WIDGET(gmi));
+	} else {
+		gtk_widget_hide(GTK_WIDGET(gmi));
+	}
+	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)
 {
-	if (!g_strcmp0(prop, "label")) {
+	if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_LABEL)) {
 		gtk_menu_item_set_label(gmi, value);
-		gtk_widget_show(GTK_WIDGET(gmi));
+	} else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_VISIBLE)) {
+		process_visible(gmi, value);
 	}
 
 	return;
@@ -125,23 +147,20 @@
 static void
 new_menuitem (DbusmenuClient * client, DbusmenuMenuitem * mi, gpointer userdata)
 {
-	gpointer ann_mi = g_object_get_data(G_OBJECT(mi), data_menuitem);
-	GtkMenuItem * gmi = GTK_MENU_ITEM(ann_mi);
-
-	if (gmi != NULL) {
-		/* It's possible we've already been looked at, that's
-		   okay, but we can just ignore this signal then. */
-		return;
-	}
-
-	gmi = GTK_MENU_ITEM(gtk_menu_item_new());
-
+	g_warning("Got new menuitem signal, which means they want something");
+	g_warning("  that I simply don't have.");
+
+	return;
+}
+
+static void
+base_new_menuitem (DbusmenuMenuitem * mi, GtkMenuItem * gmi, DbusmenuGtkClient * client)
+{
 	/* Attach these two */
 	g_object_set_data(G_OBJECT(mi), data_menuitem, gmi);
 
 	/* DbusmenuMenuitem signals */
 	g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(menu_prop_change_cb), gmi);
-	g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED,   G_CALLBACK(new_child),    client);
 	g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED, G_CALLBACK(delete_child), client);
 	g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED,   G_CALLBACK(move_child),   client);
 
@@ -151,6 +170,8 @@
 	/* Life insurance */
 	g_object_weak_ref(G_OBJECT(mi), destoryed_dbusmenuitem_cb, gmi);
 
+	process_visible(gmi, dbusmenu_menuitem_property_get(mi, DBUSMENU_MENUITEM_PROP_VISIBLE));
+
 	return;
 }
 
@@ -251,10 +272,47 @@
 
 	GtkMenuItem * mi = GTK_MENU_ITEM(g_object_get_data(G_OBJECT(item), data_menuitem));
 	if (mi == NULL) {
-		new_menuitem(DBUSMENU_CLIENT(client), item, NULL);
+		// new_menuitem(DBUSMENU_CLIENT(client), item, NULL);
+		g_warning("GTK not updated");
 		mi = GTK_MENU_ITEM(g_object_get_data(G_OBJECT(item), data_menuitem));
 	}
 
 	return mi;
 }
 
+static gboolean
+new_item_normal (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client)
+{
+	GtkMenuItem * gmi;
+
+	gmi = GTK_MENU_ITEM(gtk_menu_item_new_with_label(dbusmenu_menuitem_property_get(newitem, "label")));
+
+	base_new_menuitem(newitem, gmi, DBUSMENU_GTKCLIENT(client));
+	if (parent != NULL) {
+		new_child(parent, newitem, dbusmenu_menuitem_get_position(newitem, parent), DBUSMENU_GTKCLIENT(client));
+	}
+
+	return TRUE;
+}
+
+static gboolean
+new_item_seperator (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client)
+{
+	GtkMenuItem * gmi;
+
+	gmi = GTK_MENU_ITEM(gtk_separator_menu_item_new());
+
+	base_new_menuitem(newitem, gmi, DBUSMENU_GTKCLIENT(client));
+	if (parent != NULL) {
+		new_child(parent, newitem, dbusmenu_menuitem_get_position(newitem, parent), DBUSMENU_GTKCLIENT(client));
+	}
+
+	return TRUE;
+}
+
+static gboolean
+new_item_image (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client)
+{
+
+	return TRUE;
+}

=== modified file 'libdbusmenu-gtk/menu.c'
--- libdbusmenu-gtk/menu.c	2009-06-26 18:40:04 +0000
+++ libdbusmenu-gtk/menu.c	2009-08-26 21:24:58 +0000
@@ -64,6 +64,7 @@
 static void get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec);
 /* Internal */
 static void build_client (DbusmenuGtkMenu * self);
+static void child_realized (DbusmenuMenuitem * child, gpointer userdata);
 
 /* GObject Stuff */
 G_DEFINE_TYPE (DbusmenuGtkMenu, dbusmenu_gtkmenu, GTK_TYPE_MENU);
@@ -188,9 +189,7 @@
 root_child_added (DbusmenuMenuitem * root, DbusmenuMenuitem * child, guint position, DbusmenuGtkMenu * menu)
 {
 	g_debug("Root new child");
-	DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(menu);
-	gtk_menu_shell_insert(GTK_MENU_SHELL(menu), GTK_WIDGET(dbusmenu_gtkclient_menuitem_get(priv->client, child)), position);
-	gtk_widget_show(GTK_WIDGET(menu));
+	g_signal_connect(G_OBJECT(child), DBUSMENU_MENUITEM_SIGNAL_REALIZED, G_CALLBACK(child_realized), menu);
 	return;
 }
 
@@ -214,6 +213,16 @@
 }
 
 static void
+child_realized (DbusmenuMenuitem * child, gpointer userdata)
+{
+	DbusmenuGtkMenu * menu = DBUSMENU_GTKMENU(userdata);
+	DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(menu);
+
+	gtk_menu_append(menu, GTK_WIDGET(dbusmenu_gtkclient_menuitem_get(priv->client, child)));
+	return;
+}
+
+static void
 root_changed (DbusmenuGtkClient * client, DbusmenuMenuitem * newroot, DbusmenuGtkMenu * menu) {
 	if (newroot == NULL) {
 		gtk_widget_hide(GTK_WIDGET(menu));
@@ -227,7 +236,8 @@
 	GList * child = NULL;
 	guint count = 0;
 	for (child = dbusmenu_menuitem_get_children(newroot); child != NULL; child = g_list_next(child)) {
-		gtk_menu_append(menu, GTK_WIDGET(dbusmenu_gtkclient_menuitem_get(client, child->data)));
+		/* gtk_menu_append(menu, GTK_WIDGET(dbusmenu_gtkclient_menuitem_get(client, child->data))); */
+		g_signal_connect(G_OBJECT(child->data), DBUSMENU_MENUITEM_SIGNAL_REALIZED, G_CALLBACK(child_realized), menu);
 		count++;
 	}
 

=== modified file 'po/Makefile.in.in'
--- po/Makefile.in.in	2009-03-25 17:33:11 +0000
+++ po/Makefile.in.in	2009-08-20 17:55:36 +0000
@@ -56,7 +56,7 @@
 
 PO_LINGUAS=$(shell if test -r $(srcdir)/LINGUAS; then grep -v "^\#" $(srcdir)/LINGUAS; else echo "$(ALL_LINGUAS)"; fi)
 
-USER_LINGUAS=$(shell if test -n "$(LINGUAS)"; then LLINGUAS="$(LINGUAS)"; ALINGUAS="$(ALL_LINGUAS)"; for lang in $$LLINGUAS; do if test -n "`grep '^$$lang$$' $(srcdir)/LINGUAS 2>/dev/null`" -o -n "`echo $$ALINGUAS|tr ' ' '\n'|grep '^$$lang$$'`"; then printf "$$lang "; fi; done; fi)
+USER_LINGUAS=$(shell if test -n "$(LINGUAS)"; then LLINGUAS="$(LINGUAS)"; ALINGUAS="$(ALL_LINGUAS)"; for lang in $$LLINGUAS; do if test -n "`grep \^$$lang$$ $(srcdir)/LINGUAS 2>/dev/null`" -o -n "`echo $$ALINGUAS|tr ' ' '\n'|grep \^$$lang$$`"; then printf "$$lang "; fi; done; fi)
 
 USE_LINGUAS=$(shell if test -n "$(USER_LINGUAS)" -o -n "$(LINGUAS)"; then LLINGUAS="$(USER_LINGUAS)"; else if test -n "$(PO_LINGUAS)"; then LLINGUAS="$(PO_LINGUAS)"; else LLINGUAS="$(ALL_LINGUAS)"; fi; fi; for lang in $$LLINGUAS; do printf "$$lang "; done)
 

=== modified file 'tests/test-gtk-label.json'
--- tests/test-gtk-label.json	2009-06-17 19:59:57 +0000
+++ tests/test-gtk-label.json	2009-08-26 20:32:37 +0000
@@ -1,155 +1,155 @@
 [
-	{"id": 1,
+	{"id": 1, "type": "menuitem",
 	 "label": "value1",
 	 "submenu": [
-	 	{"id": 30,
+	 	{"id": 30, "type": "menuitem",
 		 "label": "value30"},
-	 	{"id": 31,
+	 	{"id": 31, "type": "menuitem",
 		 "label": "value31"},
-	 	{"id": 32,
+	 	{"id": 32, "type": "menuitem",
 		 "label": "value32"},
-	 	{"id": 33,
+	 	{"id": 33, "type": "menuitem",
 		 "label": "value33"},
-	 	{"id": 34,
+	 	{"id": 34, "type": "menuitem",
 		 "label": "value34"},
-	 	{"id": 35,
+	 	{"id": 35, "type": "menuitem",
 		 "label": "value35"},
-	 	{"id": 36,
+	 	{"id": 36, "type": "menuitem",
 		 "label": "value36"},
-	 	{"id": 37,
+	 	{"id": 37, "type": "menuitem",
 		 "label": "value37"},
-	 	{"id": 38,
+	 	{"id": 38, "type": "menuitem",
 		 "label": "value38"},
-	 	{"id": 39,
+	 	{"id": 39, "type": "menuitem",
 		 "label": "value39"}
 	 ]
 	},
-	{"id": 2,
+	{"id": 2, "type": "menuitem",
 	 "label": "value2",
 	 "submenu": [
-	 	{"id": 20,
+	 	{"id": 20, "type": "menuitem",
 		 "label": "value20"},
-	 	{"id": 21,
+	 	{"id": 21, "type": "separator",
 		 "label": "value21"},
-	 	{"id": 22,
+	 	{"id": 22, "type": "menuitem",
 		 "label": "value22"},
-	 	{"id": 23,
+	 	{"id": 23, "type": "separator",
 		 "label": "value23"},
-	 	{"id": 24,
+	 	{"id": 24, "type": "menuitem",
 		 "label": "value24"},
-	 	{"id": 25,
+	 	{"id": 25, "type": "separator",
 		 "label": "value25"},
-	 	{"id": 26,
+	 	{"id": 26, "type": "menuitem",
 		 "label": "value26"},
-	 	{"id": 27,
+	 	{"id": 27, "type": "separator",
 		 "label": "value27"},
-	 	{"id": 28,
+	 	{"id": 28, "type": "menuitem",
 		 "label": "value28"},
-	 	{"id": 29,
+	 	{"id": 29, "type": "menuitem", "visible": "false",
 		 "label": "value29"}
 	 ]
 	},
-	{"id": 3,
+	{"id": 3, "type": "menuitem",
 	 "label": "a super long label that is really of unreasonable length but we should make sure it makes it across the bus",
 	 "not.a.value": "A useless value",
 	 "submenu": [
-	 	{"id": 10,
+	 	{"id": 10, "type": "menuitem",
 		 "label": "value10"},
-	 	{"id": 11,
+	 	{"id": 11, "type": "menuitem",
 		 "label": "value11"},
-	 	{"id": 12,
+	 	{"id": 12, "type": "menuitem",
 		 "label": "value12"},
-	 	{"id": 13,
+	 	{"id": 13, "type": "menuitem",
 		 "label": "value13"},
-	 	{"id": 14,
+	 	{"id": 14, "type": "menuitem",
 		 "label": "value14"},
-	 	{"id": 15,
+	 	{"id": 15, "type": "menuitem",
 		 "label": "value15"},
-	 	{"id": 16,
+	 	{"id": 16, "type": "menuitem",
 		 "label": "value16"},
-	 	{"id": 17,
+	 	{"id": 17, "type": "menuitem",
 		 "label": "value17"},
-	 	{"id": 18,
+	 	{"id": 18, "type": "menuitem",
 		 "label": "value18"},
-	 	{"id": 19,
+	 	{"id": 19, "type": "menuitem",
 		 "label": "value19"}
 	 ]
 	},
-	{"id": 4,
+	{"id": 4, "type": "menuitem",
 	 "label": "value2",
 	 "submenu": [
-	 	{"id": 5,
+	 	{"id": 5, "type": "menuitem",
 		 "label": "value5",
 		 "submenu": [
-			{"id": 10,
+			{"id": 10, "type": "menuitem",
 			 "label": "value10"},
-			{"id": 11,
+			{"id": 11, "type": "menuitem",
 			 "label": "value11"},
-			{"id": 12,
+			{"id": 12, "type": "menuitem",
 			 "label": "value12"},
-			{"id": 13,
+			{"id": 13, "type": "menuitem",
 			 "label": "value13"},
-			{"id": 14,
+			{"id": 14, "type": "menuitem",
 			 "label": "value14"},
-			{"id": 15,
+			{"id": 15, "type": "menuitem",
 			 "label": "value15"},
-			{"id": 16,
+			{"id": 16, "type": "menuitem",
 			 "label": "value16"},
-			{"id": 17,
+			{"id": 17, "type": "menuitem",
 			 "label": "value17"},
-			{"id": 18,
+			{"id": 18, "type": "menuitem",
 			 "label": "value18"},
-			{"id": 19,
+			{"id": 19, "type": "menuitem",
 			 "label": "value19"}
 		 ]
 		},
-	 	{"id": 6,
+	 	{"id": 6, "type": "menuitem",
 		 "label": "value6",
 		 "submenu": [
-			{"id": 20,
+			{"id": 20, "type": "menuitem",
 			 "label": "value20"},
-			{"id": 21,
+			{"id": 21, "type": "menuitem",
 			 "label": "value21"},
-			{"id": 22,
+			{"id": 22, "type": "menuitem",
 			 "label": "value22"},
-			{"id": 23,
+			{"id": 23, "type": "menuitem",
 			 "label": "value23"},
-			{"id": 24,
+			{"id": 24, "type": "menuitem",
 			 "label": "value24"},
-			{"id": 25,
+			{"id": 25, "type": "menuitem",
 			 "label": "value25"},
-			{"id": 26,
+			{"id": 26, "type": "menuitem",
 			 "label": "value26"},
-			{"id": 27,
+			{"id": 27, "type": "menuitem",
 			 "label": "value27"},
-			{"id": 28,
+			{"id": 28, "type": "menuitem",
 			 "label": "value28"},
-			{"id": 29,
+			{"id": 29, "type": "menuitem",
 			 "label": "value29"}
 		 ]
 		},
-	 	{"id": 7,
+	 	{"id": 7, "type": "menuitem",
 		 "label": "value7",
 		 "submenu": [
-			{"id": 30,
+			{"id": 30, "type": "menuitem",
 			 "label": "value30"},
-			{"id": 31,
+			{"id": 31, "type": "menuitem",
 			 "label": "value31"},
-			{"id": 32,
+			{"id": 32, "type": "menuitem",
 			 "label": "value32"},
-			{"id": 33,
+			{"id": 33, "type": "menuitem",
 			 "label": "value33"},
-			{"id": 34,
+			{"id": 34, "type": "menuitem",
 			 "label": "value34"},
-			{"id": 35,
+			{"id": 35, "type": "menuitem",
 			 "label": "value35"},
-			{"id": 36,
+			{"id": 36, "type": "menuitem",
 			 "label": "value36"},
-			{"id": 37,
+			{"id": 37, "type": "menuitem",
 			 "label": "value37"},
-			{"id": 38,
+			{"id": 38, "type": "menuitem",
 			 "label": "value38"},
-			{"id": 39,
+			{"id": 39, "type": "menuitem",
 			 "label": "value39"}
 		 ]
 		},


Follow ups