aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzrignore2
-rw-r--r--libdbusmenu-glib/Makefile.am1
-rw-r--r--libdbusmenu-glib/client.c74
-rw-r--r--libdbusmenu-glib/client.h2
-rw-r--r--libdbusmenu-glib/dbus-menu.xml120
-rw-r--r--libdbusmenu-glib/menuitem-private.h40
-rw-r--r--libdbusmenu-glib/menuitem.c217
-rw-r--r--libdbusmenu-glib/menuitem.h15
-rw-r--r--libdbusmenu-glib/server-marshal.list3
-rw-r--r--libdbusmenu-glib/server.c113
-rw-r--r--libdbusmenu-glib/server.h6
-rw-r--r--libdbusmenu-gtk/Makefile.am2
-rw-r--r--libdbusmenu-gtk/client.c158
-rw-r--r--libdbusmenu-gtk/genericmenuitem.c444
-rw-r--r--libdbusmenu-gtk/genericmenuitem.h91
-rw-r--r--tests/Makefile.am17
-rw-r--r--tests/test-glib-layout-client.c2
-rw-r--r--tests/test-glib-properties-client.c2
-rw-r--r--tests/test-gtk-label-client.c3
-rw-r--r--tests/test-gtk-label.json77
-rw-r--r--tools/dbusmenu-dumper.c6
21 files changed, 1195 insertions, 200 deletions
diff --git a/.bzrignore b/.bzrignore
index 398d2da..d7b28a6 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -52,3 +52,5 @@ tools/dbusmenu-dumper
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
diff --git a/libdbusmenu-glib/Makefile.am b/libdbusmenu-glib/Makefile.am
index e74b9ab..fc37019 100644
--- a/libdbusmenu-glib/Makefile.am
+++ b/libdbusmenu-glib/Makefile.am
@@ -22,6 +22,7 @@ libdbusmenu_glib_la_SOURCES = \
menuitem.c \
menuitem-marshal.h \
menuitem-marshal.c \
+ menuitem-private.h \
server.h \
server.c \
server-marshal.h \
diff --git a/libdbusmenu-glib/client.c b/libdbusmenu-glib/client.c
index 61f1ccf..366d60d 100644
--- a/libdbusmenu-glib/client.c
+++ b/libdbusmenu-glib/client.c
@@ -34,6 +34,7 @@ License version 3 and version 2.1 along with this program. If not, see
#include <libxml/tree.h>
#include "client.h"
+#include "menuitem.h"
#include "dbusmenu-client.h"
#include "server-marshal.h"
@@ -94,14 +95,14 @@ static void dbusmenu_client_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);
/* 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 @@ dbusmenu_client_dispose (GObject *object)
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 @@ get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
/* Annoying little wrapper to make the right function update */
static void
-layout_update (DBusGProxy * proxy, gint revision, DbusmenuClient * client)
+layout_update (DBusGProxy * proxy, gint revision, guint parent, DbusmenuClient * client)
{
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
priv->current_revision = revision;
@@ -320,7 +321,7 @@ layout_update (DBusGProxy * proxy, gint revision, DbusmenuClient * client)
/* Signal from the server that a property has changed
on one of our menuitems */
static void
-id_prop_update (DBusGProxy * proxy, guint id, gchar * property, gchar * value, DbusmenuClient * client)
+id_prop_update (DBusGProxy * proxy, guint id, gchar * property, GValue * value, DbusmenuClient * client)
{
#ifdef MASSIVEDEBUGGING
g_debug("Property change sent to client for item %d property %s value %s", id, property, g_utf8_strlen(value, 50) < 25 ? value : "<too long>");
@@ -332,7 +333,7 @@ id_prop_update (DBusGProxy * proxy, guint id, gchar * property, gchar * value, D
DbusmenuMenuitem * menuitem = dbusmenu_menuitem_find_id(priv->root, id);
g_return_if_fail(menuitem != NULL);
- dbusmenu_menuitem_property_set(menuitem, property, value);
+ dbusmenu_menuitem_property_set_value(menuitem, property, value);
return;
}
@@ -351,7 +352,9 @@ id_update (DBusGProxy * proxy, guint id, DbusmenuClient * client)
DbusmenuMenuitem * menuitem = dbusmenu_menuitem_find_id(priv->root, id);
g_return_if_fail(menuitem != NULL);
- org_ayatana_dbusmenu_get_properties_async(proxy, id, menuitem_get_properties_cb, menuitem);
+ gchar * properties[1] = {NULL}; /* This gets them all */
+ g_debug("Getting properties");
+ org_ayatana_dbusmenu_get_properties_async(proxy, id, (const gchar **)properties, menuitem_get_properties_cb, menuitem);
return;
}
@@ -488,15 +491,16 @@ build_proxies (DbusmenuClient * client)
priv->dbusproxy = NULL;
}
- dbus_g_proxy_add_signal(priv->menuproxy, "LayoutUpdate", G_TYPE_INT, G_TYPE_INVALID);
+ dbus_g_object_register_marshaller(_dbusmenu_server_marshal_VOID__INT_UINT, G_TYPE_NONE, G_TYPE_INT, G_TYPE_UINT, G_TYPE_INVALID);
+ dbus_g_proxy_add_signal(priv->menuproxy, "LayoutUpdate", G_TYPE_INT, G_TYPE_UINT, G_TYPE_INVALID);
dbus_g_proxy_connect_signal(priv->menuproxy, "LayoutUpdate", G_CALLBACK(layout_update), client, NULL);
- dbus_g_object_register_marshaller(_dbusmenu_server_marshal_VOID__UINT_STRING_STRING, G_TYPE_NONE, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
- dbus_g_proxy_add_signal(priv->menuproxy, "IdPropUpdate", G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
- dbus_g_proxy_connect_signal(priv->menuproxy, "IdPropUpdate", G_CALLBACK(id_prop_update), client, NULL);
+ dbus_g_object_register_marshaller(_dbusmenu_server_marshal_VOID__UINT_STRING_POINTER, G_TYPE_NONE, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID);
+ dbus_g_proxy_add_signal(priv->menuproxy, "ItemPropertyUpdated", G_TYPE_UINT, G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal(priv->menuproxy, "ItemPropertyUpdated", G_CALLBACK(id_prop_update), client, NULL);
- dbus_g_proxy_add_signal(priv->menuproxy, "IdUpdate", G_TYPE_UINT, G_TYPE_INVALID);
- dbus_g_proxy_connect_signal(priv->menuproxy, "IdUpdate", G_CALLBACK(id_update), client, NULL);
+ dbus_g_proxy_add_signal(priv->menuproxy, "ItemUpdated", G_TYPE_UINT, G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal(priv->menuproxy, "ItemUpdated", G_CALLBACK(id_update), client, NULL);
update_layout(client);
@@ -564,7 +568,7 @@ parse_node_get_id (xmlNodePtr node)
static void
get_properties_helper (gpointer key, gpointer value, gpointer data)
{
- dbusmenu_menuitem_property_set((DbusmenuMenuitem *)data, (gchar *)key, (gchar *)value);
+ dbusmenu_menuitem_property_set_value((DbusmenuMenuitem *)data, (gchar *)key, (GValue *)value);
return;
}
@@ -606,7 +610,7 @@ menuitem_get_properties_new_cb (DBusGProxy * proxy, GHashTable * properties, GEr
const gchar * type;
DbusmenuClientTypeHandler newfunc = NULL;
- type = dbusmenu_menuitem_property_get(propdata->item, "type");
+ type = dbusmenu_menuitem_property_get(propdata->item, DBUSMENU_MENUITEM_PROP_TYPE);
if (type != NULL) {
newfunc = g_hash_table_lookup(priv->type_handlers, type);
} else {
@@ -647,7 +651,10 @@ static void
menuitem_activate (DbusmenuMenuitem * mi, DbusmenuClient * client)
{
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
- org_ayatana_dbusmenu_call_async (priv->menuproxy, dbusmenu_menuitem_get_id(mi), menuitem_call_cb, mi);
+ GValue value = {0};
+ g_value_init(&value, G_TYPE_INT);
+ g_value_set_int(&value, 0);
+ org_ayatana_dbusmenu_event_async (priv->menuproxy, dbusmenu_menuitem_get_id(mi), "clicked", &value, menuitem_call_cb, mi);
return;
}
@@ -689,7 +696,8 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it
propdata->item = item;
propdata->parent = parent;
- org_ayatana_dbusmenu_get_properties_async(proxy, id, menuitem_get_properties_new_cb, propdata);
+ gchar * properties[1] = {NULL}; /* This gets them all */
+ org_ayatana_dbusmenu_get_properties_async(proxy, id, (const gchar **)properties, menuitem_get_properties_new_cb, propdata);
} else {
g_warning("Unable to allocate memory to get properties for menuitem. This menuitem will never be realized.");
}
@@ -771,32 +779,24 @@ parse_layout (DbusmenuClient * client, const gchar * layout)
/* When the layout property returns, here's where we take care of that. */
static void
-update_layout_cb (DBusGProxy * proxy, DBusGProxyCall * call, void * data)
+update_layout_cb (DBusGProxy * proxy, guint rev, gchar * xml, GError * error, void * data)
{
DbusmenuClient * client = DBUSMENU_CLIENT(data);
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
- GError * error = NULL;
- GValue value = {0};
-
- priv->layoutcall = NULL;
- if (!dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_VALUE, &value, G_TYPE_INVALID)) {
+ if (error != NULL) {
g_warning("Getting layout failed on client %s object %s: %s", priv->dbus_name, priv->dbus_object, error->message);
- g_error_free(error);
return;
}
- const gchar * xml = g_value_get_string(&value);
- /* g_debug("Got layout string: %s", xml); */
- gint rev = parse_layout(client, xml);
-
- if (rev == 0) {
+ if (!parse_layout(client, xml)) {
g_warning("Unable to parse layout!");
return;
}
priv->my_revision = rev;
/* g_debug("Root is now: 0x%X", (unsigned int)priv->root); */
+ priv->layoutcall = NULL;
#ifdef MASSIVEDEBUGGING
g_debug("Client signaling layout has changed.");
#endif
@@ -816,7 +816,7 @@ update_layout (DbusmenuClient * client)
{
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
- if (priv->propproxy == NULL) {
+ if (priv->menuproxy == NULL) {
return;
}
@@ -824,14 +824,10 @@ update_layout (DbusmenuClient * client)
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;
}
diff --git a/libdbusmenu-glib/client.h b/libdbusmenu-glib/client.h
index fff9a6b..b42bc83 100644
--- a/libdbusmenu-glib/client.h
+++ b/libdbusmenu-glib/client.h
@@ -52,7 +52,7 @@ G_BEGIN_DECLS
#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:
diff --git a/libdbusmenu-glib/dbus-menu.xml b/libdbusmenu-glib/dbus-menu.xml
index 345c736..e2d872b 100644
--- a/libdbusmenu-glib/dbus-menu.xml
+++ b/libdbusmenu-glib/dbus-menu.xml
@@ -32,8 +32,22 @@ License version 3 and version 2.1 along with this program. If not, see
<!-- 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 @@ XML syntax:
...
</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>
<!--
-Each menu item has a set of properties. Property keys are in menuitem.h:
+Returns the list of items which are children of @a parentId.
-- visible
-- sensitive
-- label
-- icon
-- icon-data
-- type
+@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.
-"type" property is an enum which can take the following values (client.h):
+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
-- menuitem
-- separator
-- imageitem
+-->
+ <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:
+
+@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 -->
diff --git a/libdbusmenu-glib/menuitem-private.h b/libdbusmenu-glib/menuitem-private.h
new file mode 100644
index 0000000..0120435
--- /dev/null
+++ b/libdbusmenu-glib/menuitem-private.h
@@ -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@canonical.com>
+
+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
diff --git a/libdbusmenu-glib/menuitem.c b/libdbusmenu-glib/menuitem.c
index fdf5608..dc01157 100644
--- a/libdbusmenu-glib/menuitem.c
+++ b/libdbusmenu-glib/menuitem.c
@@ -26,11 +26,13 @@ License version 3 and version 2.1 along with this program. If not, see
<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_dispose (GObject *object);
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 @@ dbusmenu_menuitem_class_init (DbusmenuMenuitemClass *klass)
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 @@ dbusmenu_menuitem_init (DbusmenuMenuitem *self)
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 @@ dbusmenu_menuitem_find_id (DbusmenuMenuitem * mi, guint id)
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,19 +837,96 @@ dbusmenu_menuitem_property_set (DbusmenuMenuitem * mi, const gchar * property, c
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:
@mi: The #DbusmenuMenuitem to look for the property on.
diff --git a/libdbusmenu-glib/menuitem.h b/libdbusmenu-glib/menuitem.h
index 6c3c265..fc9e410 100644
--- a/libdbusmenu-glib/menuitem.h
+++ b/libdbusmenu-glib/menuitem.h
@@ -50,11 +50,17 @@ G_BEGIN_DECLS
#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 @@ struct _DbusmenuMenuitemClass
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_child_find (DbusmenuMenuitem * mi, guint id
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 @@ GHashTable * dbusmenu_menuitem_properties_copy (DbusmenuMenuitem * mi);
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);
diff --git a/libdbusmenu-glib/server-marshal.list b/libdbusmenu-glib/server-marshal.list
index 950fc9d..044c64d 100644
--- a/libdbusmenu-glib/server-marshal.list
+++ b/libdbusmenu-glib/server-marshal.list
@@ -1 +1,2 @@
-VOID: UINT, STRING, STRING
+VOID: UINT, STRING, POINTER
+VOID: INT, UINT
diff --git a/libdbusmenu-glib/server.c b/libdbusmenu-glib/server.c
index 84bfffe..f61b0fb 100644
--- a/libdbusmenu-glib/server.c
+++ b/libdbusmenu-glib/server.c
@@ -30,16 +30,21 @@ License version 3 and version 2.1 along with this program. If not, see
#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 @@ enum {
PROP_0,
PROP_DBUS_OBJECT,
PROP_ROOT_NODE,
- PROP_LAYOUT
+ PROP_VERSION
};
/* Errors */
@@ -76,6 +81,7 @@ enum {
INVALID_MENUITEM_ID,
INVALID_PROPERTY_NAME,
UNKNOWN_DBUS_ERROR,
+ NOT_IMPLEMENTED,
LAST_ERROR
};
@@ -122,8 +128,8 @@ dbusmenu_server_class_init (DbusmenuServerClass *class)
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 @@ dbusmenu_server_class_init (DbusmenuServerClass *class)
@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 @@ dbusmenu_server_class_init (DbusmenuServerClass *class)
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 @@ dbusmenu_server_class_init (DbusmenuServerClass *class)
"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 @@ set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
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 @@ get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
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 @@ get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
}
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 @@ menuitem_child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint
/* 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 @@ menuitem_child_removed (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, Dbu
/* 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 @@ menuitem_child_moved (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint
{
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 @@ error_quark (void)
}
/* 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 @@ _dbusmenu_server_get_property (DbusmenuServer * server, guint id, gchar * proper
}
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 @@ _dbusmenu_server_get_properties (DbusmenuServer * server, guint id, GHashTable *
}
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);
diff --git a/libdbusmenu-glib/server.h b/libdbusmenu-glib/server.h
index a966943..566b3cf 100644
--- a/libdbusmenu-glib/server.h
+++ b/libdbusmenu-glib/server.h
@@ -43,13 +43,13 @@ G_BEGIN_DECLS
#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:
diff --git a/libdbusmenu-gtk/Makefile.am b/libdbusmenu-gtk/Makefile.am
index 87a82a6..97d8563 100644
--- a/libdbusmenu-gtk/Makefile.am
+++ b/libdbusmenu-gtk/Makefile.am
@@ -15,6 +15,8 @@ libdbusmenu_gtkinclude_HEADERS = \
libdbusmenu_gtk_la_SOURCES = \
client.h \
client.c \
+ genericmenuitem.h \
+ genericmenuitem.c \
menu.h \
menu.c \
menuitem.h \
diff --git a/libdbusmenu-gtk/client.c b/libdbusmenu-gtk/client.c
index fdebc6b..80d2b4f 100644
--- a/libdbusmenu-gtk/client.c
+++ b/libdbusmenu-gtk/client.c
@@ -34,6 +34,7 @@ License version 3 and version 2.1 along with this program. If not, see
#include "client.h"
#include "menuitem.h"
+#include "genericmenuitem.h"
/* Prototypes */
static void dbusmenu_gtkclient_class_init (DbusmenuGtkClientClass *klass);
@@ -47,10 +48,10 @@ static void move_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint n
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_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);
@@ -115,9 +115,14 @@ menu_pressed_cb (GtkMenuItem * gmi, DbusmenuMenuitem * mi)
/* 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_visible (GtkMenuItem * gmi, const gchar * value)
/* Process the sensitive property */
static void
-process_sensitive (GtkMenuItem * gmi, const gchar * value)
+process_sensitive (DbusmenuMenuitem * mi, GtkMenuItem * gmi, const GValue * 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);
+ 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 @@ dbusmenu_gtkclient_newitem_base (DbusmenuGtkClient * client, DbusmenuMenuitem *
/* 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 @@ new_item_normal (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, Dbusmenu
/* 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 @@ new_item_normal (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, Dbusmenu
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;
}
@@ -381,6 +452,7 @@ new_item_seperator (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, Dbusm
GtkMenuItem * gmi;
gmi = GTK_MENU_ITEM(gtk_separator_menu_item_new());
+ gtk_menu_item_set_use_underline (gmi, TRUE);
if (gmi != NULL) {
dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, gmi, parent);
@@ -394,11 +466,17 @@ new_item_seperator (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, Dbusm
/* 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 +494,12 @@ image_property_handle (DbusmenuMenuitem * item, const gchar * property, const gc
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 +552,8 @@ image_property_handle (DbusmenuMenuitem * item, const gchar * property, const gc
}
- 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;
-}
diff --git a/libdbusmenu-gtk/genericmenuitem.c b/libdbusmenu-gtk/genericmenuitem.c
new file mode 100644
index 0000000..f927556
--- /dev/null
+++ b/libdbusmenu-gtk/genericmenuitem.c
@@ -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@canonical.com>
+
+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;
+}
diff --git a/libdbusmenu-gtk/genericmenuitem.h b/libdbusmenu-gtk/genericmenuitem.h
new file mode 100644
index 0000000..3c4af0c
--- /dev/null
+++ b/libdbusmenu-gtk/genericmenuitem.h
@@ -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@canonical.com>
+
+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
diff --git a/tests/Makefile.am b/tests/Makefile.am
index fa85d7f..1d58700 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -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 @@ glib_server_nomenu_LDADD = \
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_layout_client_LDADD = \
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_glib_simple_items_LDADD = \
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_label_client_LDADD = \
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 @@ distclean-local:
-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
diff --git a/tests/test-glib-layout-client.c b/tests/test-glib-layout-client.c
index a7dd683..6a79321 100644
--- a/tests/test-glib-layout-client.c
+++ b/tests/test-glib-layout-client.c
@@ -111,7 +111,7 @@ main (int argc, char ** argv)
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);
diff --git a/tests/test-glib-properties-client.c b/tests/test-glib-properties-client.c
index 39815aa..9e257ea 100644
--- a/tests/test-glib-properties-client.c
+++ b/tests/test-glib-properties-client.c
@@ -155,7 +155,7 @@ main (int argc, char ** argv)
/* 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);
diff --git a/tests/test-gtk-label-client.c b/tests/test-gtk-label-client.c
index 09325be..070c278 100644
--- a/tests/test-gtk-label-client.c
+++ b/tests/test-gtk-label-client.c
@@ -152,9 +152,11 @@ main (int argc, char ** argv)
{
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 @@ main (int argc, char ** argv)
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);
diff --git a/tests/test-gtk-label.json b/tests/test-gtk-label.json
index 14584c3..464dc2d 100644
--- a/tests/test-gtk-label.json
+++ b/tests/test-gtk-label.json
@@ -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 @@ Bi8YEIEIBwAAAAD//8JmAAcDA4MAlEYGPxgYGD5AaTgAAAAA//8DAD6xjTP5Y+A7AAAAAElFTkSu
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 @@ AwODR18kw4UJ0QyX8WkGAAAA///ClpkYoAolGBgYFKBqHjAwMDxnYGD4ha4QAAAA///CZQDMEG4o
+ys2zQwMDAwAAAAA//8DAAF5nhyE7tENAAAAAElFTkSuQmCC",
"label": "down"},
{"id": 92,
- "type": "imageitem",
+ "type": "menuitem",
"icon": "up",
"icon-data":
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACl0lEQVR4nGJgoBAAAAAA//9ixCLG
@@ -301,7 +301,7 @@ AwODR18kw4UJ0QyX8WkGAAAA///ClpkYoAolGBgYFKBqHjAwMDxnYGD4ha4QAAAA///CZQDMEG4o
+ys2zQwMDAwAAAAA//8DAAF5nhyE7tENAAAAAElFTkSuQmCC",
"label": "up"},
{"id": 93,
- "type": "imageitem",
+ "type": "menuitem",
"icon": "down",
"icon-data":
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACPUlEQVR4nGJgoBAAAAAA///Ch1gW
@@ -319,4 +319,53 @@ QmCC",
"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"
+ }
+ ]
+ },
]
diff --git a/tools/dbusmenu-dumper.c b/tools/dbusmenu-dumper.c
index 5704311..55d631e 100644
--- a/tools/dbusmenu-dumper.c
+++ b/tools/dbusmenu-dumper.c
@@ -36,7 +36,11 @@ print_menuitem (DbusmenuMenuitem * item, int depth)
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);