diff options
-rw-r--r-- | .bzrignore | 2 | ||||
-rw-r--r-- | debian/changelog | 7 | ||||
-rw-r--r-- | libdbusmenu-glib/Makefile.am | 1 | ||||
-rw-r--r-- | libdbusmenu-glib/client.c | 80 | ||||
-rw-r--r-- | libdbusmenu-glib/client.h | 2 | ||||
-rw-r--r-- | libdbusmenu-glib/dbus-menu.xml | 120 | ||||
-rw-r--r-- | libdbusmenu-glib/menuitem-private.h | 40 | ||||
-rw-r--r-- | libdbusmenu-glib/menuitem.c | 217 | ||||
-rw-r--r-- | libdbusmenu-glib/menuitem.h | 15 | ||||
-rw-r--r-- | libdbusmenu-glib/server-marshal.list | 3 | ||||
-rw-r--r-- | libdbusmenu-glib/server.c | 113 | ||||
-rw-r--r-- | libdbusmenu-glib/server.h | 6 | ||||
-rw-r--r-- | libdbusmenu-gtk/Makefile.am | 2 | ||||
-rw-r--r-- | libdbusmenu-gtk/client.c | 157 | ||||
-rw-r--r-- | libdbusmenu-gtk/genericmenuitem.c | 444 | ||||
-rw-r--r-- | libdbusmenu-gtk/genericmenuitem.h | 91 | ||||
-rw-r--r-- | tests/Makefile.am | 17 | ||||
-rw-r--r-- | tests/test-glib-layout-client.c | 2 | ||||
-rw-r--r-- | tests/test-glib-properties-client.c | 2 | ||||
-rw-r--r-- | tests/test-gtk-label-client.c | 3 | ||||
-rw-r--r-- | tests/test-gtk-label.json | 77 | ||||
-rw-r--r-- | tools/dbusmenu-dumper.c | 6 |
22 files changed, 1206 insertions, 201 deletions
@@ -53,3 +53,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/debian/changelog b/debian/changelog index 09c2236..370acaa 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +libdbusmenu (0.2.0~dev-0ubuntu1~ppa3) UNRELEASED; urgency=low + + * Updating trunk + * Changing API to be V0.2 for reals + + -- Ted Gould <ted@ubuntu.com> Fri, 18 Dec 2009 13:21:40 -0600 + libdbusmenu (0.2.0~dev-0ubuntu1~ppa2) karmic; urgency=low * Updating to trunk 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..59494a3 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,10 +321,14 @@ 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>"); + GValue valstr = {0}; + g_value_init(&valstr, G_TYPE_STRING); + g_value_transform(value, &valstr); + g_debug("Property change sent to client for item %d property %s value %s", id, property, g_utf8_strlen(g_value_get_string(&valstr), 50) < 25 ? g_value_get_string(&valstr) : "<too long>"); + g_value_unset(&valstr); #endif DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client); @@ -332,7 +337,7 @@ 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 +356,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 +495,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 +572,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 +614,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 +655,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 +700,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 +783,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 +820,7 @@ update_layout (DbusmenuClient * client) { DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client); - if (priv->propproxy == NULL) { + if (priv->menuproxy == NULL) { return; } @@ -824,14 +828,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..27ac4dc 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; } @@ -394,11 +465,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 +493,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 +551,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); |