aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac11
-rw-r--r--debian/changelog6
-rw-r--r--libdbusmenu-glib/client.c111
-rw-r--r--libdbusmenu-gtk/Makefile.am2
-rw-r--r--tests/Makefile.am18
-rw-r--r--tests/test-gtk-label-client.c3
-rw-r--r--tests/test-gtk-label-server.c72
-rw-r--r--tests/test-gtk-label.h75
-rw-r--r--tests/test-gtk-label.json158
9 files changed, 353 insertions, 103 deletions
diff --git a/configure.ac b/configure.ac
index 9770814..d35e27b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -49,6 +49,17 @@ AC_SUBST(DBUSMENUGTK_CFLAGS)
AC_SUBST(DBUSMENUGTK_LIBS)
###########################
+# Dependencies - Testing
+###########################
+
+JSON_GLIB_REQUIRED_VERSION=0.6.0
+
+PKG_CHECK_MODULES(DBUSMENUTESTS, json-glib-1.0 >= $JSON_GLIB_REQUIRED_VERSION)
+
+AC_SUBST(DBUSMENUTESTS_CFLAGS)
+AC_SUBST(DBUSMENUTESTS_LIBS)
+
+###########################
# Lib versioning
###########################
diff --git a/debian/changelog b/debian/changelog
index ce6bf83..6f66462 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+libdbusmenu (0.0-0ubuntu1~ppa6~dbusdeath1) UNRELEASED; urgency=low
+
+ * Dbus death branch.
+
+ -- Ted Gould <ted@ubuntu.com> Thu, 18 Jun 2009 22:35:12 -0500
+
libdbusmenu (0.0-0ubuntu1~ppa5) jaunty; urgency=low
* Fix for 64-bit builds
diff --git a/libdbusmenu-glib/client.c b/libdbusmenu-glib/client.c
index 61f8c34..d5e4334 100644
--- a/libdbusmenu-glib/client.c
+++ b/libdbusmenu-glib/client.c
@@ -64,6 +64,8 @@ struct _DbusmenuClientPrivate
DBusGProxy * menuproxy;
DBusGProxy * propproxy;
DBusGProxyCall * layoutcall;
+
+ DBusGProxy * dbusproxy;
};
#define DBUSMENU_CLIENT_GET_PRIVATE(o) \
@@ -86,6 +88,7 @@ static DbusmenuMenuitem * parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * i
static void parse_layout (DbusmenuClient * client, const gchar * layout);
static void update_layout_cb (DBusGProxy * proxy, DBusGProxyCall * call, void * data);
static void update_layout (DbusmenuClient * client);
+static void menuitem_get_properties_cb (DBusGProxy * proxy, GHashTable * properties, GError * error, gpointer data);
/* Build a type */
G_DEFINE_TYPE (DbusmenuClient, dbusmenu_client, G_TYPE_OBJECT);
@@ -147,6 +150,8 @@ dbusmenu_client_init (DbusmenuClient *self)
priv->propproxy = NULL;
priv->layoutcall = NULL;
+ priv->dbusproxy = NULL;
+
return;
}
@@ -167,6 +172,10 @@ dbusmenu_client_dispose (GObject *object)
g_object_unref(G_OBJECT(priv->propproxy));
priv->propproxy = NULL;
}
+ if (priv->dbusproxy != NULL) {
+ g_object_unref(G_OBJECT(priv->dbusproxy));
+ priv->dbusproxy = NULL;
+ }
priv->session_bus = NULL;
if (priv->root != NULL) {
@@ -272,7 +281,85 @@ id_update (DBusGProxy * proxy, guint id, DbusmenuClient * client)
DbusmenuMenuitem * menuitem = dbusmenu_menuitem_find_id(priv->root, id);
g_return_if_fail(menuitem != NULL);
- /* dbusmenu_menuitem_property_set(menuitem, property, value); */
+ org_freedesktop_dbusmenu_get_properties_async(proxy, id, menuitem_get_properties_cb, menuitem);
+ return;
+}
+
+/* Watches to see if our DBus savior comes onto the bus */
+static void
+dbus_owner_change (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, DbusmenuClient * client)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+
+ if (!(new != NULL && prev == NULL)) {
+ /* If it's not someone new getting on the bus, sorry we
+ simply just don't care. It's not that your service isn't
+ important to someone, just not us. You'll find the right
+ process someday, there's lots of processes out there. */
+ return;
+ }
+
+ if (g_strcmp0(new, priv->dbus_name)) {
+ /* Again, someone else's service. */
+ return;
+ }
+
+ /* Woot! A service for us to love and to hold for ever
+ and ever and ever! */
+ return build_proxies(client);
+}
+
+/* This function builds the DBus proxy which will look out for
+ the service coming up. */
+static void
+build_dbus_proxy (DbusmenuClient * client)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ GError * error = NULL;
+
+ if (priv->dbusproxy != NULL) {
+ return;
+ }
+
+ priv->dbusproxy = dbus_g_proxy_new_for_name_owner (priv->session_bus,
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ &error);
+ if (error != NULL) {
+ g_debug("Oh, that's bad. That's really bad. We can't get a proxy to DBus itself? Seriously? Here's all I know: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ dbus_g_proxy_add_signal(priv->dbusproxy, "NameOwnerChanged",
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal(priv->dbusproxy, "NameOwnerChanged",
+ G_CALLBACK(dbus_owner_change), client, NULL);
+
+ return;
+}
+
+/* A signal handler that gets called when a proxy is destoryed a
+ so it needs to clean up a little. Make sure we don't think we
+ have a layout and setup the dbus watcher. */
+static void
+proxy_destroyed (GObject * gobj_proxy, gpointer userdata)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(userdata);
+
+ if (priv->root != NULL) {
+ g_object_unref(G_OBJECT(priv->root));
+ priv->root = NULL;
+ g_signal_emit(G_OBJECT(userdata), signals[LAYOUT_UPDATED], 0, TRUE);
+ }
+
+ if ((gpointer)priv->menuproxy == (gpointer)gobj_proxy) {
+ priv->layoutcall = NULL;
+ }
+
+ build_dbus_proxy(DBUSMENU_CLIENT(userdata));
return;
}
@@ -291,6 +378,7 @@ build_proxies (DbusmenuClient * client)
if (error != NULL) {
g_error("Unable to get session bus: %s", error->message);
g_error_free(error);
+ build_dbus_proxy(client);
return;
}
@@ -304,6 +392,8 @@ build_proxies (DbusmenuClient * client)
g_error_free(error);
return;
}
+ g_object_add_weak_pointer(G_OBJECT(priv->propproxy), (gpointer *)&priv->propproxy);
+ g_signal_connect(G_OBJECT(priv->propproxy), "destroy", G_CALLBACK(proxy_destroyed), client);
priv->menuproxy = dbus_g_proxy_new_for_name_owner(priv->session_bus,
priv->dbus_name,
@@ -315,6 +405,14 @@ build_proxies (DbusmenuClient * client)
g_error_free(error);
return;
}
+ g_object_add_weak_pointer(G_OBJECT(priv->menuproxy), (gpointer *)&priv->menuproxy);
+ g_signal_connect(G_OBJECT(priv->menuproxy), "destroy", G_CALLBACK(proxy_destroyed), client);
+
+ /* If we get here, we don't need the DBus proxy */
+ if (priv->dbusproxy != NULL) {
+ g_object_unref(G_OBJECT(priv->dbusproxy));
+ priv->dbusproxy = NULL;
+ }
dbus_g_proxy_add_signal(priv->menuproxy, "LayoutUpdate", G_TYPE_INVALID);
dbus_g_proxy_connect_signal(priv->menuproxy, "LayoutUpdate", G_CALLBACK(layout_update), client, NULL);
@@ -499,6 +597,10 @@ update_layout (DbusmenuClient * client)
{
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ if (priv->propproxy == NULL) {
+ return;
+ }
+
if (priv->layoutcall != NULL) {
return;
}
@@ -554,7 +656,8 @@ dbusmenu_client_new (const gchar * name, const gchar * object)
it could block longer.
Return value: A #DbusmenuMenuitem representing the root of
- menu on the server.
+ menu on the server. If there is no server or there is
+ an error receiving its layout it'll return #NULL.
*/
DbusmenuMenuitem *
dbusmenu_client_get_root (DbusmenuClient * client)
@@ -563,6 +666,10 @@ dbusmenu_client_get_root (DbusmenuClient * client)
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ if (priv->propproxy == NULL) {
+ return NULL;
+ }
+
if (priv->layoutcall != NULL) {
/* Will end the current call and block on it's completion */
update_layout_cb(priv->propproxy, priv->layoutcall, client);
diff --git a/libdbusmenu-gtk/Makefile.am b/libdbusmenu-gtk/Makefile.am
index 80e6058..831719c 100644
--- a/libdbusmenu-gtk/Makefile.am
+++ b/libdbusmenu-gtk/Makefile.am
@@ -19,7 +19,7 @@ libdbusmenu_gtk_la_LDFLAGS = \
-export-symbols-regex "^[^_].*"
libdbusmenu_gtk_la_CFLAGS = \
- $(DBUSMENUGTK_CFLAGS) -Wall -Werror
+ $(DBUSMENUGTK_CFLAGS) -I$(srcdir)/.. -Wall -Werror
libdbusmenu_gtk_la_LIBADD = \
../libdbusmenu-glib/libdbusmenu-glib.la \
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 70750ac..2ec6ca7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -84,36 +84,38 @@ test_glib_properties_client_LDADD = \
-test-gtk-label: test-gtk-label-client test-gtk-label-server
- $(DBUS_RUNNER) --task ./test-gtk-label-client --task-name Client --task ./test-gtk-label-server --task-name Server --ignore-return
+test-gtk-label: test-gtk-label-client test-gtk-label-server test-gtk-label.json
+ $(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
test_gtk_label_server_SOURCES = \
- test-gtk-label.h \
test-gtk-label-server.c
test_gtk_label_server_CFLAGS = \
-I $(srcdir)/.. \
$(DBUSMENUGTK_CFLAGS) \
+ $(DBUSMENUTESTS_CFLAGS) \
$(DBUSMENUGLIB_CFLAGS) -Wall -Werror
test_gtk_label_server_LDADD = \
../libdbusmenu-glib/libdbusmenu-glib.la \
../libdbusmenu-gtk/libdbusmenu-gtk.la \
- $(DBUSMENUGTK_LIBS)
+ $(DBUSMENUGTK_LIBS) \
+ $(DBUSMENUTESTS_LIBS)
test_gtk_label_client_SOURCES = \
- test-gtk-label.h \
test-gtk-label-client.c
test_gtk_label_client_CFLAGS = \
-I $(srcdir)/.. \
$(DBUSMENUGTK_CFLAGS) \
+ $(DBUSMENUTESTS_CFLAGS) \
$(DBUSMENUGLIB_CFLAGS) -Wall -Werror
test_gtk_label_client_LDADD = \
../libdbusmenu-glib/libdbusmenu-glib.la \
../libdbusmenu-gtk/libdbusmenu-gtk.la \
- $(DBUSMENUGTK_LIBS)
+ $(DBUSMENUGTK_LIBS) \
+ $(DBUSMENUTESTS_LIBS)
@@ -122,4 +124,6 @@ examplesdir = $(docdir)/examples/
examples_DATA = \
$(glib_server_nomenu_SOURCES)
-EXTRA_DIST = $(examples_DATA)
+EXTRA_DIST = \
+ $(examples_DATA) \
+ test-gtk-label.json
diff --git a/tests/test-gtk-label-client.c b/tests/test-gtk-label-client.c
index 44a847f..b691f84 100644
--- a/tests/test-gtk-label-client.c
+++ b/tests/test-gtk-label-client.c
@@ -163,9 +163,10 @@ main (int argc, char ** argv)
gtk_menu_bar_append(menubar, menuitem);
gtk_widget_show(menubar);
gtk_container_add(GTK_CONTAINER(window), menubar);
+ gtk_window_set_title(GTK_WINDOW(window), "libdbusmenu-gtk test");
gtk_widget_show(window);
- death_timer = g_timeout_add_seconds(10, timer_func, window);
+ death_timer = g_timeout_add_seconds(60, timer_func, window);
mainloop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(mainloop);
diff --git a/tests/test-gtk-label-server.c b/tests/test-gtk-label-server.c
index 4e2fe0a..a7534f2 100644
--- a/tests/test-gtk-label-server.c
+++ b/tests/test-gtk-label-server.c
@@ -8,33 +8,55 @@
#include <libdbusmenu-glib/menuitem.h>
#include <libdbusmenu-glib/server.h>
-#include "test-gtk-label.h"
+#include <json-glib/json-glib.h>
static void
-set_props (DbusmenuMenuitem * mi, gchar ** props)
+set_props (DbusmenuMenuitem * mi, JsonObject * node)
{
- if (props == NULL) return;
+ if (node == NULL) return;
- guint i;
- for (i = 0; props[i] != NULL; i += 2) {
- dbusmenu_menuitem_property_set(mi, props[i], props[i+1]);
+ GList * members = NULL;
+ for (members = json_object_get_members(node); members != NULL; members = g_list_next(members)) {
+ const gchar * member = members->data;
+
+ if (!g_strcmp0(member, "id")) { continue; }
+ if (!g_strcmp0(member, "submenu")) { continue; }
+
+ JsonNode * lnode = json_object_get_member(node, member);
+ if (JSON_NODE_TYPE(lnode) != JSON_NODE_VALUE) { continue; }
+
+ dbusmenu_menuitem_property_set(mi, member, json_node_get_string(lnode));
}
return;
}
static DbusmenuMenuitem *
-layout2menuitem (proplayout_t * layout)
+layout2menuitem (JsonNode * inlayout)
{
- if (layout == NULL || layout->id == 0) return NULL;
+ if (inlayout == NULL) return NULL;
+ if (JSON_NODE_TYPE(inlayout) != JSON_NODE_OBJECT) return NULL;
+
+ JsonObject * layout = json_node_get_object(inlayout);
+
+ DbusmenuMenuitem * local = NULL;
+ if (json_object_has_member(layout, "id")) {
+ JsonNode * node = json_object_get_member(layout, "id");
+ g_return_val_if_fail(JSON_NODE_TYPE(node) == JSON_NODE_VALUE, NULL);
+ local = dbusmenu_menuitem_new_with_id(json_node_get_int(node));
+ } else {
+ local = dbusmenu_menuitem_new();
+ }
- DbusmenuMenuitem * local = dbusmenu_menuitem_new_with_id(layout->id);
- set_props(local, layout->properties);
+ set_props(local, layout);
- if (layout->submenu != NULL) {
+ if (json_object_has_member(layout, "submenu")) {
+ JsonNode * node = json_object_get_member(layout, "submenu");
+ g_return_val_if_fail(JSON_NODE_TYPE(node) == JSON_NODE_ARRAY, local);
+ JsonArray * array = json_node_get_array(node);
guint count;
- for (count = 0; layout->submenu[count].id != 0; count++) {
- DbusmenuMenuitem * child = layout2menuitem(&layout->submenu[count]);
+ for (count = 0; count < json_array_get_length(array); count++) {
+ DbusmenuMenuitem * child = layout2menuitem(json_array_get_element(array, count));
if (child != NULL) {
dbusmenu_menuitem_child_append(local, child);
}
@@ -45,6 +67,7 @@ layout2menuitem (proplayout_t * layout)
return local;
}
+static JsonArray * root_array = NULL;
static guint layouton = 0;
static DbusmenuServer * server = NULL;
static GMainLoop * mainloop = NULL;
@@ -52,13 +75,14 @@ static GMainLoop * mainloop = NULL;
static gboolean
timer_func (gpointer data)
{
- if (layouts[layouton].id == 0) {
+ if (layouton == json_array_get_length(root_array)) {
+ g_debug("Completed %d layouts", layouton);
g_main_loop_quit(mainloop);
return FALSE;
}
g_debug("Updating to Layout %d", layouton);
- dbusmenu_server_set_root(server, layout2menuitem(&layouts[layouton]));
+ dbusmenu_server_set_root(server, layout2menuitem(json_array_get_element(root_array, layouton)));
layouton++;
return TRUE;
@@ -69,11 +93,25 @@ main (int argc, char ** argv)
{
g_type_init();
+ JsonParser * parser = json_parser_new();
+ GError * error = NULL;
+ if (!json_parser_load_from_file(parser, argv[1], &error)) {
+ g_debug("Failed parsing file %s because: %s", argv[1], error->message);
+ return 1;
+ }
+ JsonNode * root_node = json_parser_get_root(parser);
+ if (JSON_NODE_TYPE(root_node) != JSON_NODE_ARRAY) {
+ g_debug("Root node is not an array, fail. It's an: %s", json_node_type_name(root_node));
+ return 1;
+ }
+
+ root_array = json_node_get_array(root_node);
+ g_debug("%d layouts in test description '%s'", json_array_get_length(root_array), argv[1]);
+
DBusGConnection * connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
g_debug("DBus ID: %s", dbus_connection_get_server_id(dbus_g_connection_get_connection(dbus_g_bus_get(DBUS_BUS_SESSION, NULL))));
DBusGProxy * bus_proxy = dbus_g_proxy_new_for_name(connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
- GError * error = NULL;
guint nameret = 0;
if (!org_freedesktop_DBus_request_name(bus_proxy, "glib.label.test", 0, &nameret, &error)) {
@@ -89,7 +127,7 @@ main (int argc, char ** argv)
server = dbusmenu_server_new("/org/test");
timer_func(NULL);
- g_timeout_add(2500, timer_func, NULL);
+ g_timeout_add_seconds(15, timer_func, NULL);
mainloop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(mainloop);
diff --git a/tests/test-gtk-label.h b/tests/test-gtk-label.h
deleted file mode 100644
index e63ac44..0000000
--- a/tests/test-gtk-label.h
+++ /dev/null
@@ -1,75 +0,0 @@
-
-#include <glib.h>
-
-typedef struct _proplayout_t proplayout_t;
-struct _proplayout_t {
- guint id;
- gchar ** properties;
- proplayout_t * submenu;
-};
-
-gchar * props1[] = {"label", "value1", NULL};
-gchar * props2[] = {"label", "value1", NULL};
-gchar * props3[] = {"label",
- "And a property name that is really long should have a value that is really long, because well, that's an important part of the yin and yang of software testing.",
- NULL};
-gchar * props4[] = {"icon-name", "network-status", "label", "Look at network", "right-column", "10:32", NULL};
-
-
-proplayout_t submenu_4_1[] = {
- {id: 10, properties: props2, submenu: NULL},
- {id: 11, properties: props2, submenu: NULL},
- {id: 12, properties: props2, submenu: NULL},
- {id: 13, properties: props2, submenu: NULL},
- {id: 14, properties: props2, submenu: NULL},
- {id: 15, properties: props2, submenu: NULL},
- {id: 16, properties: props2, submenu: NULL},
- {id: 17, properties: props2, submenu: NULL},
- {id: 18, properties: props2, submenu: NULL},
- {id: 19, properties: props2, submenu: NULL},
- {id: 0, properties: NULL, submenu: NULL}
-};
-
-proplayout_t submenu_4_2[] = {
- {id: 20, properties: props2, submenu: NULL},
- {id: 21, properties: props2, submenu: NULL},
- {id: 22, properties: props2, submenu: NULL},
- {id: 23, properties: props2, submenu: NULL},
- {id: 24, properties: props2, submenu: NULL},
- {id: 25, properties: props2, submenu: NULL},
- {id: 26, properties: props2, submenu: NULL},
- {id: 27, properties: props2, submenu: NULL},
- {id: 28, properties: props2, submenu: NULL},
- {id: 29, properties: props2, submenu: NULL},
- {id: 0, properties: NULL, submenu: NULL}
-};
-
-proplayout_t submenu_4_3[] = {
- {id: 30, properties: props2, submenu: NULL},
- {id: 31, properties: props2, submenu: NULL},
- {id: 32, properties: props2, submenu: NULL},
- {id: 33, properties: props2, submenu: NULL},
- {id: 34, properties: props2, submenu: NULL},
- {id: 35, properties: props2, submenu: NULL},
- {id: 36, properties: props2, submenu: NULL},
- {id: 37, properties: props2, submenu: NULL},
- {id: 38, properties: props2, submenu: NULL},
- {id: 39, properties: props2, submenu: NULL},
- {id: 0, properties: NULL, submenu: NULL}
-};
-
-proplayout_t submenu_4_0[] = {
- {id: 1, properties: props2, submenu: submenu_4_1},
- {id: 2, properties: props2, submenu: submenu_4_2},
- {id: 3, properties: props2, submenu: submenu_4_3},
- {id: 0, properties: NULL, submenu: NULL}
-};
-
-proplayout_t layouts[] = {
- {id: 1, properties: props1, submenu: submenu_4_3},
- {id: 10, properties: props2, submenu: submenu_4_2},
- {id: 20, properties: props3, submenu: submenu_4_1},
- {id: 100, properties: props2, submenu: submenu_4_0},
- {id: 0, properties: NULL, submenu: NULL}
-};
-
diff --git a/tests/test-gtk-label.json b/tests/test-gtk-label.json
new file mode 100644
index 0000000..64c1386
--- /dev/null
+++ b/tests/test-gtk-label.json
@@ -0,0 +1,158 @@
+[
+ {"id": 1,
+ "label": "value1",
+ "submenu": [
+ {"id": 30,
+ "label": "value30"},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ },
+ {"id": 2,
+ "label": "value2",
+ "submenu": [
+ {"id": 20,
+ "label": "value20"},
+ {"id": 21,
+ "label": "value21"},
+ {"id": 22,
+ "label": "value22"},
+ {"id": 23,
+ "label": "value23"},
+ {"id": 24,
+ "label": "value24"},
+ {"id": 25,
+ "label": "value25"},
+ {"id": 26,
+ "label": "value26"},
+ {"id": 27,
+ "label": "value27"},
+ {"id": 28,
+ "label": "value28"},
+ {"id": 29,
+ "label": "value29"}
+ ]
+ },
+ {"id": 3,
+ "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,
+ "label": "value10"},
+ {"id": 11,
+ "label": "value11"},
+ {"id": 12,
+ "label": "value12"},
+ {"id": 13,
+ "label": "value13"},
+ {"id": 14,
+ "label": "value14"},
+ {"id": 15,
+ "label": "value15"},
+ {"id": 16,
+ "label": "value16"},
+ {"id": 17,
+ "label": "value17"},
+ {"id": 18,
+ "label": "value18"},
+ {"id": 19,
+ "label": "value19"}
+ ]
+ },
+ {"id": 4,
+ "label": "value2",
+ "submenu": [
+ {"id": 5,
+ "label": "value5",
+ "submenu": [
+ {"id": 10,
+ "label": "value10"},
+ {"id": 11,
+ "label": "value11"},
+ {"id": 12,
+ "label": "value12"},
+ {"id": 13,
+ "label": "value13"},
+ {"id": 14,
+ "label": "value14"},
+ {"id": 15,
+ "label": "value15"},
+ {"id": 16,
+ "label": "value16"},
+ {"id": 17,
+ "label": "value17"},
+ {"id": 18,
+ "label": "value18"},
+ {"id": 19,
+ "label": "value19"}
+ ]
+ },
+ {"id": 6,
+ "label": "value6",
+ "submenu": [
+ {"id": 20,
+ "label": "value20"},
+ {"id": 21,
+ "label": "value21"},
+ {"id": 22,
+ "label": "value22"},
+ {"id": 23,
+ "label": "value23"},
+ {"id": 24,
+ "label": "value24"},
+ {"id": 25,
+ "label": "value25"},
+ {"id": 26,
+ "label": "value26"},
+ {"id": 27,
+ "label": "value27"},
+ {"id": 28,
+ "label": "value28"},
+ {"id": 29,
+ "label": "value29"}
+ ]
+ },
+ {"id": 7,
+ "label": "value7",
+ "submenu": [
+ {"id": 30,
+ "label": "value30"},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ },
+ ]
+ }
+]