aboutsummaryrefslogtreecommitdiff
path: root/libdbusmenu-glib
diff options
context:
space:
mode:
authorMartin Pitt <martin@piware.de>2011-02-21 13:12:33 +0100
committerMartin Pitt <martin@piware.de>2011-02-21 13:12:33 +0100
commitded5bb1e7c5bf42c24ed25a66c7e73ae8eaa54e0 (patch)
treeeeac4c7bace411463570b6d0b06b18264d09afe8 /libdbusmenu-glib
parent3f3db4e1ea5e5b2321a7266edf018bb510fcdfb4 (diff)
parent87703b408d42ce97b5a64142b0c33951d366890a (diff)
downloadlibdbusmenu-ded5bb1e7c5bf42c24ed25a66c7e73ae8eaa54e0.tar.gz
libdbusmenu-ded5bb1e7c5bf42c24ed25a66c7e73ae8eaa54e0.tar.bz2
libdbusmenu-ded5bb1e7c5bf42c24ed25a66c7e73ae8eaa54e0.zip
merge trunk
Diffstat (limited to 'libdbusmenu-glib')
-rw-r--r--libdbusmenu-glib/client.c186
-rw-r--r--libdbusmenu-glib/dbus-menu.xml99
-rw-r--r--libdbusmenu-glib/menuitem-private.h5
-rw-r--r--libdbusmenu-glib/menuitem.c78
-rw-r--r--libdbusmenu-glib/menuitem.h11
-rw-r--r--libdbusmenu-glib/server.c359
6 files changed, 524 insertions, 214 deletions
diff --git a/libdbusmenu-glib/client.c b/libdbusmenu-glib/client.c
index 8a1d213..0848294 100644
--- a/libdbusmenu-glib/client.c
+++ b/libdbusmenu-glib/client.c
@@ -32,9 +32,6 @@ License version 3 and version 2.1 along with this program. If not, see
#include <gio/gio.h>
-#include <libxml/parser.h>
-#include <libxml/tree.h>
-
#include "client.h"
#include "menuitem.h"
#include "menuitem-private.h"
@@ -151,9 +148,8 @@ static void layout_update (GDBusProxy * proxy, guint revision, gint parent, Dbus
static void id_prop_update (GDBusProxy * proxy, gint id, gchar * property, GVariant * value, DbusmenuClient * client);
static void id_update (GDBusProxy * proxy, gint id, DbusmenuClient * client);
static void build_proxies (DbusmenuClient * client);
-static gint parse_node_get_id (xmlNodePtr node);
-static DbusmenuMenuitem * parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, GDBusProxy * proxy);
-static gint parse_layout (DbusmenuClient * client, const gchar * layout);
+static DbusmenuMenuitem * parse_layout_xml(DbusmenuClient * client, GVariant * layout, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, GDBusProxy * proxy);
+static gint parse_layout (DbusmenuClient * client, GVariant * layout);
static void update_layout_cb (GObject * proxy, GAsyncResult * res, gpointer data);
static void update_layout (DbusmenuClient * client);
static void menuitem_get_properties_cb (GVariant * properties, GError * error, gpointer data);
@@ -1030,11 +1026,60 @@ menuproxy_signal_cb (GDBusProxy * proxy, gchar * sender, gchar * signal, GVarian
{
g_return_if_fail(DBUSMENU_IS_CLIENT(user_data));
DbusmenuClient * client = DBUSMENU_CLIENT(user_data);
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
if (g_strcmp0(signal, "LayoutUpdated") == 0) {
guint revision; gint parent;
g_variant_get(params, "(ui)", &revision, &parent);
layout_update(proxy, revision, parent, client);
+ } else if (priv->root == NULL) {
+ /* Drop out here, all the rest of these really need to have a root
+ node so we can just ignore them if there isn't one. */
+ } else if (g_strcmp0(signal, "ItemPropertiesUpdated") == 0) {
+ /* Remove before adding just incase there is a duplicate, against the
+ rules, but we can handle it so let's do it. */
+ GVariantIter ritems;
+ g_variant_iter_init(&ritems, g_variant_get_child_value(params, 1));
+
+ GVariant * ritem;
+ while ((ritem = g_variant_iter_next_value(&ritems)) != NULL) {
+ gint id = g_variant_get_int32(g_variant_get_child_value(ritem, 0));
+ DbusmenuMenuitem * menuitem = dbusmenu_menuitem_find_id(priv->root, id);
+
+ if (menuitem == NULL) {
+ continue;
+ }
+
+ GVariantIter properties;
+ g_variant_iter_init(&properties, g_variant_get_child_value(ritem, 1));
+ gchar * property;
+
+ while (g_variant_iter_next(&properties, "s", &property)) {
+ g_debug("Removing property '%s' on %d", property, id);
+ dbusmenu_menuitem_property_remove(menuitem, property);
+ }
+ }
+
+ GVariantIter items;
+ g_variant_iter_init(&items, g_variant_get_child_value(params, 0));
+
+ GVariant * item;
+ while ((item = g_variant_iter_next_value(&items)) != NULL) {
+ gint id = g_variant_get_int32(g_variant_get_child_value(item, 0));
+ GVariantIter properties;
+ g_variant_iter_init(&properties, g_variant_get_child_value(item, 1));
+ gchar * property;
+ GVariant * value;
+
+ while (g_variant_iter_next(&properties, "{sv}", &property, &value)) {
+ GVariant * internalvalue = value;
+ if (G_LIKELY(g_variant_is_of_type(value, G_VARIANT_TYPE_VARIANT))) {
+ /* Unboxing if needed */
+ internalvalue = g_variant_get_variant(value);
+ }
+ id_prop_update(proxy, id, property, internalvalue, client);
+ }
+ }
} else if (g_strcmp0(signal, "ItemPropertyUpdated") == 0) {
gint id; gchar * property; GVariant * value;
g_variant_get(params, "(isv)", &id, &property, &value);
@@ -1054,40 +1099,6 @@ menuproxy_signal_cb (GDBusProxy * proxy, gchar * sender, gchar * signal, GVarian
return;
}
-/* Get the ID attribute of the node, parse it and
- return it. Also we're checking to ensure the node
- is a 'menu' here. */
-static gint
-parse_node_get_id (xmlNodePtr node)
-{
- if (node == NULL) {
- return -1;
- }
- if (node->type != XML_ELEMENT_NODE) {
- return -1;
- }
- if (g_strcmp0((gchar *)node->name, "menu") != 0) {
- /* This kills some nodes early */
- g_warning("XML Node is not 'menu' it is '%s'", node->name);
- return -1;
- }
-
- xmlAttrPtr attrib;
- for (attrib = node->properties; attrib != NULL; attrib = attrib->next) {
- if (g_strcmp0((gchar *)attrib->name, "id") == 0) {
- if (attrib->children != NULL) {
- gint id = (guint)g_ascii_strtoll((gchar *)attrib->children->content, NULL, 10);
- /* g_debug ("Found ID: %d", id); */
- return id;
- }
- break;
- }
- }
-
- g_warning("Unable to find an ID on the node");
- return -1;
-}
-
/* This is the callback for the properties on a menu item. There
should be all of them in the Hash, and they we use foreach to
copy them into the menuitem.
@@ -1265,7 +1276,7 @@ dbusmenu_client_send_event (DbusmenuClient * client, gint id, const gchar * name
edata->event = g_strdup(name);
edata->timestamp = timestamp;
edata->variant = variant;
- g_variant_ref(variant);
+ g_variant_ref_sink(variant);
g_dbus_proxy_call(priv->menuproxy,
"Event",
@@ -1390,10 +1401,14 @@ parse_layout_update (DbusmenuMenuitem * item, DbusmenuClient * client)
/* Parse recursively through the XML and make it into
objects as need be */
static DbusmenuMenuitem *
-parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, GDBusProxy * proxy)
+parse_layout_xml(DbusmenuClient * client, GVariant * layout, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, GDBusProxy * proxy)
{
+ if (layout == NULL) {
+ return NULL;
+ }
+
/* First verify and figure out what we've got */
- gint id = parse_node_get_id(node);
+ gint id = g_variant_get_int32(g_variant_get_child_value(layout, 0));
if (id < 0) {
return NULL;
}
@@ -1405,20 +1420,26 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it
g_return_val_if_fail(id == dbusmenu_menuitem_get_id(item), NULL);
/* Some variables */
- xmlNodePtr children;
- guint position;
+ GVariantIter children;
+ g_variant_iter_init(&children, g_variant_get_child_value(layout, 2));
+ GVariant * child;
+
+ guint position = 0;
GList * oldchildren = g_list_copy(dbusmenu_menuitem_get_children(item));
/* g_debug("Starting old children: %d", g_list_length(oldchildren)); */
/* Go through all the XML Nodes and make sure that we have menuitems
to cover those XML nodes. */
- for (children = node->children, position = 0; children != NULL; children = children->next, position++) {
+ while ((child = g_variant_iter_next_value(&children)) != NULL) {
/* g_debug("Looking at child: %d", position); */
- gint childid = parse_node_get_id(children);
+ if (g_variant_is_of_type(child, G_VARIANT_TYPE_VARIANT)) {
+ child = g_variant_get_variant(child);
+ }
+
+ gint childid = g_variant_get_int32(g_variant_get_child_value(child, 0));
if (childid < 0) {
/* Don't increment the position when there isn't a valid
node in the XML tree. It's probably a comment. */
- position--;
continue;
}
DbusmenuMenuitem * childmi = NULL;
@@ -1451,6 +1472,8 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it
dbusmenu_menuitem_child_reorder(item, childmi, position);
parse_layout_update(childmi, client);
}
+
+ position++;
}
/* Remove any children that are no longer used by this version of
@@ -1473,14 +1496,20 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it
}
/* now it's time to recurse down the tree. */
- children = node->children;
+ g_variant_iter_init(&children, g_variant_get_child_value(layout, 2));
+
+ child = g_variant_iter_next_value(&children);
GList * childmis = dbusmenu_menuitem_get_children(item);
- while (children != NULL && childmis != NULL) {
- gint xmlid = parse_node_get_id(children);
+ while (child != NULL && childmis != NULL) {
+ if (g_variant_is_of_type(child, G_VARIANT_TYPE_VARIANT)) {
+ child = g_variant_get_variant(child);
+ }
+
+ gint xmlid = g_variant_get_int32(g_variant_get_child_value(child, 0));
/* If this isn't a valid menu item we need to move on
until we have one. This avoids things like comments. */
if (xmlid < 0) {
- children = children->next;
+ child = g_variant_iter_next_value(&children);
continue;
}
@@ -1489,13 +1518,14 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it
g_debug("Recursing parse_layout_xml. XML ID: %d MI ID: %d", xmlid, miid);
#endif
- parse_layout_xml(client, children, DBUSMENU_MENUITEM(childmis->data), item, proxy);
+ parse_layout_xml(client, child, DBUSMENU_MENUITEM(childmis->data), item, proxy);
- children = children->next;
+ child = g_variant_iter_next_value(&children);
childmis = g_list_next(childmis);
}
- if (children != NULL) {
- g_warning("Sync failed, now we've got extra XML nodes.");
+
+ if (child != NULL) {
+ g_warning("Sync failed, now we've got extra layout nodes.");
}
if (childmis != NULL) {
g_warning("Sync failed, now we've got extra menu items.");
@@ -1507,7 +1537,7 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it
/* Take the layout passed to us over DBus and turn it into
a set of beautiful objects */
static gint
-parse_layout (DbusmenuClient * client, const gchar * layout)
+parse_layout (DbusmenuClient * client, GVariant * layout)
{
#ifdef MASSIVEDEBUGGING
g_debug("Client Parsing a new layout");
@@ -1515,17 +1545,6 @@ parse_layout (DbusmenuClient * client, const gchar * layout)
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
- xmlDocPtr xmldoc;
-
- /* No one should need more characters than this! */
- xmldoc = xmlReadMemory(layout, g_utf8_strlen(layout, 1024*1024), "dbusmenu.xml", NULL, 0);
-
- xmlNodePtr root = xmlDocGetRootElement(xmldoc);
-
- if (root == NULL) {
- g_warning("Unable to get root node of menu XML");
- }
-
DbusmenuMenuitem * oldroot = priv->root;
if (priv->root == NULL) {
@@ -1534,11 +1553,10 @@ parse_layout (DbusmenuClient * client, const gchar * layout)
parse_layout_update(priv->root, client);
}
- priv->root = parse_layout_xml(client, root, priv->root, NULL, priv->menuproxy);
- xmlFreeDoc(xmldoc);
+ priv->root = parse_layout_xml(client, layout, priv->root, NULL, priv->menuproxy);
if (priv->root == NULL) {
- g_warning("Unable to parse layout on client %s object %s: %s", priv->dbus_name, priv->dbus_object, layout);
+ g_warning("Unable to parse layout on client %s object %s: %s", priv->dbus_name, priv->dbus_object, g_variant_print(layout, TRUE));
}
if (priv->root != oldroot) {
@@ -1579,14 +1597,10 @@ update_layout_cb (GObject * proxy, GAsyncResult * res, gpointer data)
goto out;
}
- guint rev;
- gchar * xml;
-
- g_variant_get(params, "(us)", &rev, &xml);
- g_variant_unref(params);
+ guint rev = g_variant_get_uint32(g_variant_get_child_value(params, 0));
+ GVariant * layout = g_variant_get_child_value(params, 1);
- guint parseable = parse_layout(client, xml);
- g_free(xml);
+ guint parseable = parse_layout(client, layout);
if (parseable == 0) {
g_warning("Unable to parse layout!");
@@ -1612,6 +1626,10 @@ out:
priv->layoutcall = NULL;
}
+ if (params != NULL) {
+ g_variant_unref(params);
+ }
+
g_object_unref(G_OBJECT(client));
return;
}
@@ -1639,10 +1657,20 @@ update_layout (DbusmenuClient * client)
priv->layoutcall = g_cancellable_new();
+ GVariantBuilder tupleb;
+ g_variant_builder_init(&tupleb, G_VARIANT_TYPE_TUPLE);
+
+ g_variant_builder_add_value(&tupleb, g_variant_new_int32(0)); // root
+ g_variant_builder_add_value(&tupleb, g_variant_new_int32(-1)); // recurse
+ g_variant_builder_add_value(&tupleb, g_variant_new_array(G_VARIANT_TYPE_STRING, NULL, 0)); // props
+
+ GVariant * args = g_variant_builder_end(&tupleb);
+ // g_debug("Args (type: %s): %s", g_variant_get_type_string(args), g_variant_print(args, TRUE));
+
g_object_ref(G_OBJECT(client));
g_dbus_proxy_call(priv->menuproxy,
"GetLayout",
- g_variant_new("(i)", 0), /* root */
+ args,
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
priv->layoutcall, /* cancellable */
diff --git a/libdbusmenu-glib/dbus-menu.xml b/libdbusmenu-glib/dbus-menu.xml
index 042a24c..da14c63 100644
--- a/libdbusmenu-glib/dbus-menu.xml
+++ b/libdbusmenu-glib/dbus-menu.xml
@@ -174,34 +174,38 @@ License version 3 and version 2.1 along with this program. If not, see
<!-- Functions -->
<method name="GetLayout">
- <dox:d><![CDATA[
- Provides an XML representation of the menu hierarchy
-
- XML syntax:
-
- @verbatim
-<menu id="0"> # Root container
- <menu id="1"> # First level menu, for example "File"
- <menu id="2"/> ~ Second level menu, for example "Open"
- <menu id="3"/>
- ...
- </menu>
- <menu id="4"> # Another first level menu, say "Edit"
- ...
- </menu>
- ...
-</menu>
- @endverbatim
- ]]></dox:d>
+ <dox:d>
+ Provides the layout and propertiers that are attached to the entries
+ that are in the layout. It only gives the items that are children
+ of the item that is specified in @parentId. It will return all of the
+ properties or specific ones depending of the value in @propertyNames.
+
+ The format is recursive, where the second 'v' is in the same format
+ as the original 'a(ia(sv)a(v))'. If the @recursive flag is set to
+ less than one then the second array will have zero entries.
+ </dox:d>
<arg type="i" name="parentId" direction="in">
<dox:d>The ID of the parent node for the layout. For
grabbing the layout from the root node use zero.</dox:d>
</arg>
+ <arg type="i" name="recurse" direction="in">
+ <dox:d>
+ The amount of levels of recursion to use. -1, as value would
+ deliver all the items under the @parentId.
+ </dox:d>
+ </arg>
+ <arg type="as" name="propertyNames" direction="in" >
+ <dox:d>
+ The list of item properties we are
+ interested in. If there are no entries in the list all of
+ the properties will be sent.
+ </dox:d>
+ </arg>
<arg type="u" name="revision" direction="out">
<dox:d>The revision number of the layout. For matching
with layoutUpdated signals.</dox:d>
</arg>
- <arg type="s" name="layout" direction="out">
+ <arg type="(ia{sv}av)" name="layout" direction="out">
<dox:d>The layout as an XML string of IDs.</dox:d>
</arg>
</method>
@@ -236,33 +240,21 @@ License version 3 and version 2.1 along with this program. If not, see
</arg>
</method>
- <method name="GetChildren">
- <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="DBusMenuItemList"/>
- <arg type="i" name="id" direction="in" />
- <arg type="as" name="propertyNames" direction="in" />
- <arg type="a(ia{sv})" name="properties" direction="out" />
- </method>
-
<method name="GetProperty">
- <arg type="i" name="id" direction="in" />
- <arg type="s" name="name" direction="in" />
- <arg type="v" name="value" direction="out" />
- </method>
-
- <method name="GetProperties">
<dox:d>
- Returns multiple properties in one call. This is more efficient than
- GetProperty.
-
+ Get a signal property on a single item. This is not useful if you're
+ going to implement this interface, it should only be used if you're
+ debugging via a commandline tool.
</dox:d>
- <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
- <arg type="i" name="id" direction="in" >
- <dox:d>The item whose properties we want to retrieve.</dox:d>
+ <arg type="i" name="id" direction="in">
+ <dox:d>the id of the item which received the event</dox:d>
</arg>
- <arg type="as" name="propertyNames" direction="in" >
- <dox:d>List of string name of the properties we want. If the list contains no entries, all properties are sent.</dox:d>
+ <arg type="s" name="name" direction="in">
+ <dox:d>the name of the property to get</dox:d>
+ </arg>
+ <arg type="v" name="value" direction="out">
+ <dox:d>the value of the property</dox:d>
</arg>
- <arg type="a{sv}" name="properties" direction="out" />
</method>
<method name="Event">
@@ -309,25 +301,16 @@ License version 3 and version 2.1 along with this program. If not, see
</method>
<!-- Signals -->
- <signal name="ItemPropertyUpdated">
- <dox:d>
- Triggered by the application to notify the applet that the property @a property
- from item @a id has changed to @a value.
- </dox:d>
- <arg type="i" name="id" direction="out" />
- <arg type="s" name="prop" direction="out" />
- <arg type="v" name="value" direction="out" />
- </signal>
-
- <signal name="ItemUpdated">
+ <signal name="ItemsPropertiesUpdated">
<dox:d>
- Triggered by the application to notify the applet that all properties of item
+ Triggered when there are lots of property updates across many items
+ so they all get grouped into a single dbus message. The format is
+ the ID of the item with a hashtable of names and values for those
+ properties.
</dox:d>
- <arg type="i" name="id" direction="out" >
- <dox:d>id which should be considered outdated</dox:d>
- </arg>
+ <arg type="a(ia(sv))" name="props" direction="out" />
+ <arg type="a(ia(s))" name="props_removed" direction="out" />
</signal>
-
<signal name="LayoutUpdated">
<dox:d>
Triggered by the application to notify display of a layout update, up to
diff --git a/libdbusmenu-glib/menuitem-private.h b/libdbusmenu-glib/menuitem-private.h
index 2028464..89319dc 100644
--- a/libdbusmenu-glib/menuitem-private.h
+++ b/libdbusmenu-glib/menuitem-private.h
@@ -33,10 +33,11 @@ License version 3 and version 2.1 along with this program. If not, see
G_BEGIN_DECLS
-void dbusmenu_menuitem_buildxml (DbusmenuMenuitem * mi, GPtrArray * array);
+GVariant * dbusmenu_menuitem_build_variant (DbusmenuMenuitem * mi, const gchar ** properties, gint recurse);
gboolean dbusmenu_menuitem_realized (DbusmenuMenuitem * mi);
void dbusmenu_menuitem_set_realized (DbusmenuMenuitem * mi);
-GVariant * dbusmenu_menuitem_properties_variant (DbusmenuMenuitem * mi);
+GVariant * dbusmenu_menuitem_properties_variant (DbusmenuMenuitem * mi, const gchar ** properties);
+gboolean dbusmenu_menuitem_property_is_default (DbusmenuMenuitem * mi, const gchar * property);
G_END_DECLS
diff --git a/libdbusmenu-glib/menuitem.c b/libdbusmenu-glib/menuitem.c
index b5a87a4..c994130 100644
--- a/libdbusmenu-glib/menuitem.c
+++ b/libdbusmenu-glib/menuitem.c
@@ -1009,7 +1009,7 @@ dbusmenu_menuitem_property_set_variant (DbusmenuMenuitem * mi, const gchar * pro
if (value != NULL) {
gchar * lprop = g_strdup(property);
- g_variant_ref(value);
+ g_variant_ref_sink(value);
if (currentval == NULL || !g_variant_equal((GVariant*)currentval, value)) {
g_hash_table_replace(priv->properties, lprop, value);
@@ -1214,7 +1214,7 @@ copy_helper (gpointer in_key, gpointer in_value, gpointer in_data)
GHashTable * table = (GHashTable *)in_data;
gchar * key = (gchar *)in_key;
GVariant * value = (GVariant *)in_value;
- g_variant_ref(value);
+ g_variant_ref_sink(value);
g_hash_table_insert(table, g_strdup(key), value);
return;
}
@@ -1251,7 +1251,9 @@ dbusmenu_menuitem_properties_copy (DbusmenuMenuitem * mi)
static void
variant_helper (gpointer in_key, gpointer in_value, gpointer user_data)
{
- g_variant_builder_add((GVariantBuilder *)user_data, "{sv}", in_key, in_value);
+ GVariant * value = g_variant_new_dict_entry(g_variant_new_string((gchar *)in_key),
+ g_variant_new_variant((GVariant *)in_value));
+ g_variant_builder_add_value((GVariantBuilder *)user_data, value);
return;
}
@@ -1265,7 +1267,7 @@ variant_helper (gpointer in_key, gpointer in_value, gpointer user_data)
* Return Value: (transfer full): A GVariant of type "a{sv}" or NULL on error.
*/
GVariant *
-dbusmenu_menuitem_properties_variant (DbusmenuMenuitem * mi)
+dbusmenu_menuitem_properties_variant (DbusmenuMenuitem * mi, const gchar ** properties)
{
g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL);
@@ -1275,7 +1277,7 @@ dbusmenu_menuitem_properties_variant (DbusmenuMenuitem * mi)
if (g_hash_table_size(priv->properties) > 0) {
GVariantBuilder builder;
- g_variant_builder_init(&builder, g_variant_type_new("a{sv}"));
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
g_hash_table_foreach(priv->properties, variant_helper, &builder);
@@ -1323,38 +1325,62 @@ dbusmenu_menuitem_get_root (DbusmenuMenuitem * mi)
/**
- * dbusmenu_menuitem_buildxml:
- * @mi: #DbusmenuMenuitem to represent in XML
- * @array: (element-type utf8): A list of string that will be turned into an XML file
+ * dbusmenu_menuitem_buildvariant:
+ * @mi: #DbusmenuMenuitem to represent in a variant
+ * @properties: (element-type utf8): A list of string that will be put into
+ * a variant
*
- * This function will add strings to the array @array. It will put
- * at least one entry if this menu item has no children. If it has
- * children it will put two for this entry, one representing the
+ * This function will put at least one entry if this menu item has no children.
+ * If it has children it will put two for this entry, one representing the
* start tag and one that is a closing tag. It will allow it's
* children to place their own tags in the array in between those two.
- */
-void
-dbusmenu_menuitem_buildxml (DbusmenuMenuitem * mi, GPtrArray * array)
+ *
+ * Return value: (transfer full): Variant representing @properties
+*/
+GVariant *
+dbusmenu_menuitem_build_variant (DbusmenuMenuitem * mi, const gchar ** properties, gint recurse)
{
- g_return_if_fail(DBUSMENU_IS_MENUITEM(mi));
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL);
gint id = 0;
if (!dbusmenu_menuitem_get_root(mi)) {
id = dbusmenu_menuitem_get_id(mi);
}
+ /* This is the tuple that'll build up being a representation of
+ this entry */
+ GVariantBuilder tupleb;
+ g_variant_builder_init(&tupleb, G_VARIANT_TYPE_TUPLE);
+
+ /* Add our ID */
+ g_variant_builder_add_value(&tupleb, g_variant_new_int32(id));
+
+ /* Figure out the properties */
+ GVariant * props = dbusmenu_menuitem_properties_variant(mi, properties);
+ if (props != NULL) {
+ g_variant_builder_add_value(&tupleb, props);
+ } else {
+ g_variant_builder_add_value(&tupleb, g_variant_parse(G_VARIANT_TYPE("a{sv}"), "[ ]", NULL, NULL, NULL));
+ }
+
+ /* Pillage the children */
GList * children = dbusmenu_menuitem_get_children(mi);
- if (children == NULL) {
- g_ptr_array_add(array, g_strdup_printf("<menu id=\"%d\"/>", id));
+ if (children == NULL && recurse != 0) {
+ g_variant_builder_add_value(&tupleb, g_variant_new_array(G_VARIANT_TYPE_VARIANT, NULL, 0));
} else {
- g_ptr_array_add(array, g_strdup_printf("<menu id=\"%d\">", id));
+ GVariantBuilder childrenbuilder;
+ g_variant_builder_init(&childrenbuilder, G_VARIANT_TYPE_ARRAY);
+
for ( ; children != NULL; children = children->next) {
- dbusmenu_menuitem_buildxml(DBUSMENU_MENUITEM(children->data), array);
+ GVariant * child = dbusmenu_menuitem_build_variant(DBUSMENU_MENUITEM(children->data), properties, recurse - 1);
+
+ g_variant_builder_add_value(&childrenbuilder, g_variant_new_variant(child));
}
- g_ptr_array_add(array, g_strdup("</menu>"));
+
+ g_variant_builder_add_value(&tupleb, g_variant_builder_end(&childrenbuilder));
}
- return;
+ return g_variant_builder_end(&tupleb);
}
typedef struct {
@@ -1474,3 +1500,13 @@ dbusmenu_menuitem_show_to_user (DbusmenuMenuitem * mi, guint timestamp)
return;
}
+
+/* Checks to see if the value of this property is unique or just the
+ default value. */
+/* TODO: Implement this */
+gboolean
+dbusmenu_menuitem_property_is_default (DbusmenuMenuitem * mi, const gchar * property)
+{
+ /* No defaults system yet */
+ return FALSE;
+}
diff --git a/libdbusmenu-glib/menuitem.h b/libdbusmenu-glib/menuitem.h
index 9b538f6..afd6084 100644
--- a/libdbusmenu-glib/menuitem.h
+++ b/libdbusmenu-glib/menuitem.h
@@ -111,14 +111,15 @@ struct _DbusmenuMenuitem
typedef void (*dbusmenu_menuitem_about_to_show_cb) (DbusmenuMenuitem * mi, gpointer user_data);
/**
- * dbusmenu_menuitem_buildxml_slot_t:
+ * dbusmenu_menuitem_buildvariant_slot_t:
* @mi: (in): Menu item that should be built from
- * @stringarray: (inout) (transfer none) (array) (element-type utf8): An array of strings that can be combined into an XML file.
*
* This is the function that is called to represent this menu item
- * as an XML fragment. Should call it's own children.
+ * as a variant. Should call it's own children.
+ *
+ * Return value: (transfer full) A variant representing this item and it's children
*/
-typedef void (*dbusmenu_menuitem_buildxml_slot_t) (DbusmenuMenuitem * mi, GPtrArray* stringarray);
+typedef GVariant * (*dbusmenu_menuitem_buildvariant_slot_t) (DbusmenuMenuitem * mi, gchar ** properties);
/**
* DbusmenuMenuitemClass:
@@ -155,7 +156,7 @@ struct _DbusmenuMenuitemClass
void (*realized) (void);
/* Virtual functions */
- dbusmenu_menuitem_buildxml_slot_t buildxml;
+ dbusmenu_menuitem_buildvariant_slot_t buildvariant;
void (*handle_event) (DbusmenuMenuitem * mi, const gchar * name, GVariant * variant, guint timestamp);
void (*send_about_to_show) (DbusmenuMenuitem * mi, void (*cb) (DbusmenuMenuitem * mi, gpointer user_data), gpointer cb_data);
diff --git a/libdbusmenu-glib/server.c b/libdbusmenu-glib/server.c
index 777e4ef..aa39991 100644
--- a/libdbusmenu-glib/server.c
+++ b/libdbusmenu-glib/server.c
@@ -54,6 +54,9 @@ struct _DbusmenuServerPrivate
GDBusConnection * bus;
GCancellable * bus_lookup;
guint dbus_registration;
+
+ GArray * prop_array;
+ guint property_idle;
};
#define DBUSMENU_SERVER_GET_PRIVATE(o) (DBUSMENU_SERVER(o)->priv)
@@ -156,6 +159,7 @@ static void menuitem_signals_create (DbusmenuMenuitem * mi,
static void menuitem_signals_remove (DbusmenuMenuitem * mi,
gpointer data);
static GQuark error_quark (void);
+static void prop_array_teardown (GArray * prop_array);
static void bus_get_layout (DbusmenuServer * server,
GVariant * params,
GDBusMethodInvocation * invocation);
@@ -354,6 +358,17 @@ dbusmenu_server_dispose (GObject *object)
if (priv->layout_idle != 0) {
g_source_remove(priv->layout_idle);
+ priv->layout_idle = 0;
+ }
+
+ if (priv->property_idle != 0) {
+ g_source_remove(priv->property_idle);
+ priv->property_idle = 0;
+ }
+
+ if (priv->prop_array != NULL) {
+ prop_array_teardown(priv->prop_array);
+ priv->prop_array = NULL;
}
if (priv->root != NULL) {
@@ -418,6 +433,15 @@ set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
if (priv->root != NULL) {
dbusmenu_menuitem_foreach(priv->root, menuitem_signals_remove, obj);
dbusmenu_menuitem_set_root(priv->root, FALSE);
+
+ GList * properties = dbusmenu_menuitem_properties_list(priv->root);
+ GList * iter;
+ for (iter = properties; iter != NULL; iter = g_list_next(iter)) {
+ gchar * property = (gchar *)iter->data;
+ menuitem_property_changed(priv->root, property, NULL, DBUSMENU_SERVER(obj));
+ }
+ g_list_free(properties);
+
g_object_unref(G_OBJECT(priv->root));
priv->root = NULL;
}
@@ -426,6 +450,14 @@ set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
g_object_ref(G_OBJECT(priv->root));
dbusmenu_menuitem_set_root(priv->root, TRUE);
dbusmenu_menuitem_foreach(priv->root, menuitem_signals_create, obj);
+
+ GList * properties = dbusmenu_menuitem_properties_list(priv->root);
+ GList * iter;
+ for (iter = properties; iter != NULL; iter = g_list_next(iter)) {
+ gchar * property = (gchar *)iter->data;
+ menuitem_property_changed(priv->root, property, dbusmenu_menuitem_property_get_variant(priv->root, property), DBUSMENU_SERVER(obj));
+ }
+ g_list_free(properties);
} else {
g_debug("Setting root node to NULL");
}
@@ -440,17 +472,6 @@ set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
}
static void
-xmlarray_foreach_free (gpointer arrayentry, gpointer userdata)
-{
- if (arrayentry != NULL) {
- /* g_debug("Freeing pointer: %s", (gchar *)arrayentry); */
- g_free(arrayentry);
- }
-
- return;
-}
-
-static void
get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
{
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(obj);
@@ -633,22 +654,264 @@ layout_update_signal (DbusmenuServer * server)
return;
}
-static void
-menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, GVariant * variant, DbusmenuServer * server)
+typedef struct _prop_idle_item_t prop_idle_item_t;
+struct _prop_idle_item_t {
+ gint id;
+ GArray * array;
+};
+
+typedef struct _prop_idle_prop_t prop_idle_prop_t;
+struct _prop_idle_prop_t {
+ gchar * property;
+ GVariant * variant;
+};
+
+/* Takes appart our data structure so we don't leak any
+ memory or references. */
+static void
+prop_array_teardown (GArray * prop_array)
{
- DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+ int i, j;
+
+ for (i = 0; i < prop_array->len; i++) {
+ prop_idle_item_t * iitem = &g_array_index(prop_array, prop_idle_item_t, i);
+
+ for (j = 0; j < iitem->array->len; j++) {
+ prop_idle_prop_t * iprop = &g_array_index(iitem->array, prop_idle_prop_t, j);
+
+ g_free(iprop->property);
+
+ if (iprop->variant != NULL) {
+ g_variant_unref(iprop->variant);
+ }
+ }
+
+ g_array_free(iitem->array, TRUE);
+ }
+
+ g_array_free(prop_array, TRUE);
+
+ return;
+}
+
+/* Works in the idle to send a set of property updates so that they'll
+ all update in a single dbus message. */
+static gboolean
+menuitem_property_idle (gpointer user_data)
+{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(user_data);
+
+ /* Source will get removed as we return */
+ priv->property_idle = 0;
+
+ /* If there are no items, let's just not signal */
+ if (priv->prop_array == NULL) {
+ return FALSE;
+ }
+
+ int i, j;
+ GVariantBuilder itembuilder;
+ gboolean item_init = FALSE;
+
+ GVariantBuilder removeitembuilder;
+ gboolean removeitem_init = FALSE;
+
+ for (i = 0; i < priv->prop_array->len; i++) {
+ prop_idle_item_t * iitem = &g_array_index(priv->prop_array, prop_idle_item_t, i);
+
+ GVariantBuilder dictbuilder;
+ gboolean dictinit = FALSE;
+
+ GVariantBuilder removedictbuilder;
+ gboolean removedictinit = FALSE;
+
+ /* Go throught each item and see if it should go in the removal list
+ or the additive list. */
+ for (j = 0; j < iitem->array->len; j++) {
+ prop_idle_prop_t * iprop = &g_array_index(iitem->array, prop_idle_prop_t, j);
+
+ if (iprop->variant != NULL) {
+ if (!dictinit) {
+ g_variant_builder_init(&dictbuilder, G_VARIANT_TYPE_DICTIONARY);
+ dictinit = TRUE;
+ }
+
+ GVariant * entry = g_variant_new_dict_entry(g_variant_new_string(iprop->property),
+ g_variant_new_variant(iprop->variant));
+
+ g_variant_builder_add_value(&dictbuilder, entry);
+ } else {
+ if (!removedictinit) {
+ g_variant_builder_init(&removedictbuilder, G_VARIANT_TYPE_ARRAY);
+ removedictinit = TRUE;
+ }
+
+ g_variant_builder_add_value(&removedictbuilder, g_variant_new_string(iprop->property));
+ }
+ }
+
+ /* If we've got new values that are real values we need to add that
+ to the list of items to send the value of */
+ if (dictinit) {
+ GVariantBuilder tuplebuilder;
+ g_variant_builder_init(&tuplebuilder, G_VARIANT_TYPE_TUPLE);
+
+ g_variant_builder_add_value(&tuplebuilder, g_variant_new_int32(iitem->id));
+ g_variant_builder_add_value(&tuplebuilder, g_variant_builder_end(&dictbuilder));
+
+ if (!item_init) {
+ g_variant_builder_init(&itembuilder, G_VARIANT_TYPE_ARRAY);
+ item_init = TRUE;
+ }
+
+ g_variant_builder_add_value(&itembuilder, g_variant_builder_end(&tuplebuilder));
+ }
+
+ /* If we've got properties that have been removed then we need to add
+ them to the list of removed items */
+ if (removedictinit) {
+ GVariantBuilder tuplebuilder;
+ g_variant_builder_init(&tuplebuilder, G_VARIANT_TYPE_TUPLE);
+
+ g_variant_builder_add_value(&tuplebuilder, g_variant_new_int32(iitem->id));
+ g_variant_builder_add_value(&tuplebuilder, g_variant_builder_end(&removedictbuilder));
+
+ if (!removeitem_init) {
+ g_variant_builder_init(&removeitembuilder, G_VARIANT_TYPE_ARRAY);
+ removeitem_init = TRUE;
+ }
- g_signal_emit(G_OBJECT(server), signals[ID_PROP_UPDATE], 0, dbusmenu_menuitem_get_id(mi), property, variant, TRUE);
+ g_variant_builder_add_value(&removeitembuilder, g_variant_builder_end(&tuplebuilder));
+ }
+ }
+
+ GVariant * megadata[2];
+
+ if (item_init) {
+ megadata[0] = g_variant_builder_end(&itembuilder);
+ } else {
+ GError * error = NULL;
+ megadata[0] = g_variant_parse(G_VARIANT_TYPE("a(ia{sv})"), "[ ]", NULL, NULL, &error);
+
+ if (error != NULL) {
+ g_warning("Unable to parse '[ ]' as a 'a(ia{sv})': %s", error->message);
+ g_error_free(error);
+ }
+ }
+
+ if (removeitem_init) {
+ megadata[1] = g_variant_builder_end(&removeitembuilder);
+ } else {
+ GError * error = NULL;
+ megadata[1] = g_variant_parse(G_VARIANT_TYPE("a(ia(s))"), "[ ]", NULL, NULL, &error);
+
+ if (error != NULL) {
+ g_warning("Unable to parse '[ ]' as a 'a(ia(s))': %s", error->message);
+ g_error_free(error);
+ }
+ }
if (priv->dbusobject != NULL && priv->bus != NULL) {
g_dbus_connection_emit_signal(priv->bus,
NULL,
priv->dbusobject,
DBUSMENU_INTERFACE,
- "ItemPropertyUpdated",
- g_variant_new("(isv)", dbusmenu_menuitem_get_id(mi), property, variant),
+ "ItemPropertiesUpdated",
+ g_variant_new_tuple(megadata, 2),
NULL);
+ } else {
+ g_variant_unref(megadata[0]);
+ g_variant_unref(megadata[1]);
+ }
+
+ /* Clean everything up */
+ prop_array_teardown(priv->prop_array);
+ priv->prop_array = NULL;
+
+ return FALSE;
+}
+
+static void
+menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, GVariant * variant, DbusmenuServer * server)
+{
+ int i;
+ gint item_id;
+
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
+ item_id = dbusmenu_menuitem_get_id(mi);
+
+ g_signal_emit(G_OBJECT(server), signals[ID_PROP_UPDATE], 0, item_id, property, variant, TRUE);
+
+ /* See if we have a property array, if not, we need to
+ build one of these suckers */
+ if (priv->prop_array == NULL) {
+ priv->prop_array = g_array_new(FALSE, FALSE, sizeof(prop_idle_item_t));
}
+
+ /* Look to see if we already have this item in the list
+ and use it if so */
+ prop_idle_item_t * item = NULL;
+ for (i = 0; i < priv->prop_array->len; i++) {
+ prop_idle_item_t * iitem = &g_array_index(priv->prop_array, prop_idle_item_t, i);
+ if (iitem->id == item_id) {
+ item = iitem;
+ break;
+ }
+ }
+
+ GArray * properties = NULL;
+ /* If not, we'll need to build ourselves one */
+ if (item == NULL) {
+ prop_idle_item_t myitem;
+ myitem.id = item_id;
+ myitem.array = g_array_new(FALSE, FALSE, sizeof(prop_idle_prop_t));
+
+ g_array_append_val(priv->prop_array, myitem);
+ properties = myitem.array;
+ } else {
+ properties = item->array;
+ }
+
+ /* Check to see if this property is in the list */
+ prop_idle_prop_t * prop = NULL;
+ for (i = 0; i < properties->len; i++) {
+ prop_idle_prop_t * iprop = &g_array_index(properties, prop_idle_prop_t, i);
+ if (g_strcmp0(iprop->property, property) == 0) {
+ prop = iprop;
+ break;
+ }
+ }
+
+ /* If it's the default value we want to treat it like a clearing
+ of the value so that it doesn't get sent over dbus and waste
+ bandwidth */
+ if (dbusmenu_menuitem_property_is_default(mi, property)) {
+ variant = NULL;
+ }
+
+ /* If so, we need to swap the value */
+ if (prop != NULL) {
+ g_variant_unref(prop->variant);
+ prop->variant = variant;
+ } else {
+ /* else we need to add it */
+ prop_idle_prop_t myprop;
+ myprop.property = g_strdup(property);
+ myprop.variant = variant;
+
+ g_array_append_val(properties, myprop);
+ }
+ if (variant != NULL) {
+ g_variant_ref_sink(variant);
+ }
+
+ /* Check to see if the idle is already queued, and queue it
+ if not. */
+ if (priv->property_idle == 0) {
+ priv->property_idle = g_idle_add(menuitem_property_idle, server);
+ }
+
return;
}
@@ -757,26 +1020,28 @@ bus_get_layout (DbusmenuServer * server, GVariant * params, GDBusMethodInvocatio
{
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
- gint parent = 0;
- g_variant_get(params, "(i)", &parent);
+ /* Input */
+ gint parent = g_variant_get_int32(g_variant_get_child_value(params, 0));
+ gint recurse = g_variant_get_int32(g_variant_get_child_value(params, 1));
+ const gchar ** props = g_variant_get_strv(g_variant_get_child_value(params, 2), NULL);
+ /* Output */
guint revision = priv->layout_revision;
- GPtrArray * xmlarray = g_ptr_array_new();
+ GVariant * items = NULL;
- if (parent == 0) {
- if (priv->root == NULL) {
- /* g_debug("Getting layout without root node!"); */
- g_ptr_array_add(xmlarray, g_strdup("<menu id=\"0\"/>"));
- } else {
- dbusmenu_menuitem_buildxml(priv->root, xmlarray);
- }
- } else {
- DbusmenuMenuitem * item = NULL;
- if (priv->root != NULL) {
- item = dbusmenu_menuitem_find_id(priv->root, parent);
- }
+ if (priv->root != NULL) {
+ items = dbusmenu_menuitem_build_variant(priv->root, props, recurse);
+ }
- if (item == NULL) {
+ /* What happens if we don't have anything? */
+ if (items == NULL) {
+ if (parent == 0) {
+ /* We should always have a root, so we'll make up one for
+ right now. */
+ items = g_variant_parse(G_VARIANT_TYPE("(ia{sv}av)"), "(0, [], [])", NULL, NULL, NULL);
+ } else {
+ /* If we were looking for a specific ID that's an error that
+ we should send back, so let's do that. */
g_dbus_method_invocation_return_error(invocation,
error_quark(),
INVALID_MENUITEM_ID,
@@ -784,23 +1049,19 @@ bus_get_layout (DbusmenuServer * server, GVariant * params, GDBusMethodInvocatio
parent);
return;
}
- dbusmenu_menuitem_buildxml(item, xmlarray);
}
- g_ptr_array_add(xmlarray, NULL);
- /* build string */
- gchar * layout = g_strjoinv("", (gchar **)xmlarray->pdata);
+ /* Build the final variant tuple */
+ GVariantBuilder tuplebuilder;
+ g_variant_builder_init(&tuplebuilder, G_VARIANT_TYPE_TUPLE);
- g_ptr_array_foreach(xmlarray, xmlarray_foreach_free, NULL);
- g_ptr_array_free(xmlarray, TRUE);
+ g_variant_builder_add_value(&tuplebuilder, g_variant_new_uint32(revision));
+ g_variant_builder_add_value(&tuplebuilder, items);
+ GVariant * retval = g_variant_builder_end(&tuplebuilder);
+ // g_debug("Sending layout type: %s", g_variant_get_type_string(retval));
g_dbus_method_invocation_return_value(invocation,
- g_variant_new("(us)",
- revision,
- layout));
-
- g_free(layout);
-
+ retval);
return;
}
@@ -874,7 +1135,7 @@ bus_get_properties (DbusmenuServer * server, GVariant * params, GDBusMethodInvoc
return;
}
- GVariant * dict = dbusmenu_menuitem_properties_variant(mi);
+ GVariant * dict = dbusmenu_menuitem_properties_variant(mi, NULL);
g_dbus_method_invocation_return_value(invocation, g_variant_new("(a{sv})", dict));
@@ -922,7 +1183,7 @@ bus_get_group_properties (DbusmenuServer * server, GVariant * params, GDBusMetho
GVariantBuilder wbuilder;
g_variant_builder_init(&wbuilder, G_VARIANT_TYPE_TUPLE);
g_variant_builder_add(&wbuilder, "i", id);
- GVariant * props = dbusmenu_menuitem_properties_variant(mi);
+ GVariant * props = dbusmenu_menuitem_properties_variant(mi, NULL);
if (props == NULL) {
GError * error = NULL;
@@ -982,7 +1243,7 @@ serialize_menuitem(gpointer data, gpointer user_data)
gint id = dbusmenu_menuitem_get_id(mi);
g_variant_builder_add_value(&tuple, g_variant_new_int32(id));
- GVariant * props = dbusmenu_menuitem_properties_variant(mi);
+ GVariant * props = dbusmenu_menuitem_properties_variant(mi, NULL);
g_variant_builder_add_value(&tuple, props);
g_variant_builder_add_value(builder, g_variant_builder_end(&tuple));
@@ -1105,7 +1366,7 @@ bus_event (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * i
event_data->variant = g_variant_get_variant(event_data->variant);
}
- g_variant_ref(event_data->variant);
+ g_variant_ref_sink(event_data->variant);
g_timeout_add(0, event_local_handler, event_data);