diff options
Diffstat (limited to 'libdbusmenu-glib')
-rw-r--r-- | libdbusmenu-glib/Makefile.am | 34 | ||||
-rw-r--r-- | libdbusmenu-glib/client.c | 362 | ||||
-rw-r--r-- | libdbusmenu-glib/client.h | 2 | ||||
-rw-r--r-- | libdbusmenu-glib/dbus-menu.xml | 99 | ||||
-rw-r--r-- | libdbusmenu-glib/defaults.c | 291 | ||||
-rw-r--r-- | libdbusmenu-glib/defaults.h | 86 | ||||
-rw-r--r-- | libdbusmenu-glib/menuitem-private.h | 5 | ||||
-rw-r--r-- | libdbusmenu-glib/menuitem-proxy.c | 34 | ||||
-rw-r--r-- | libdbusmenu-glib/menuitem.c | 876 | ||||
-rw-r--r-- | libdbusmenu-glib/menuitem.h | 15 | ||||
-rw-r--r-- | libdbusmenu-glib/server.c | 363 |
11 files changed, 1478 insertions, 689 deletions
diff --git a/libdbusmenu-glib/Makefile.am b/libdbusmenu-glib/Makefile.am index a139f7c..5ddbeb8 100644 --- a/libdbusmenu-glib/Makefile.am +++ b/libdbusmenu-glib/Makefile.am @@ -24,6 +24,8 @@ libdbusmenu_glibinclude_HEADERS = \ libdbusmenu_glib_la_SOURCES = \ dbus-menu-clean.xml.h \ dbus-menu-clean.xml.c \ + defaults.h \ + defaults.c \ menuitem.h \ menuitem.c \ menuitem-marshal.h \ @@ -122,34 +124,34 @@ if INTROSPECTION_TEN INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir) \ --warn-all \ --add-include-path=$(srcdir) \ - $(addprefix --c-include=libdbusmenu-glib/, $(introspection_sources)) \ + $(addprefix --c-include=libdbusmenu-glib/, $(libdbusmenu_glibinclude_HEADERS)) \ --symbol-prefix=dbusmenu \ --identifier-prefix=Dbusmenu else INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir) \ --warn-all \ --add-include-path=$(srcdir) \ - $(addprefix --c-include=libdbusmenu-glib/, $(introspection_sources)) + $(addprefix --c-include=libdbusmenu-glib/, $(libdbusmenu_glibinclude_HEADERS)) endif INTROSPECTION_COMPILER_ARGS = --includedir=$(builddir) if HAVE_INTROSPECTION -introspection_sources = $(libdbusmenu_glibinclude_HEADERS) +introspection_sources = $(libdbusmenu_glibinclude_HEADERS) $(libdbusmenu_glib_la_SOURCES) -Dbusmenu_Glib-0.4.gir: libdbusmenu-glib.la -Dbusmenu_Glib_0_4_gir_INCLUDES = \ +Dbusmenu-0.4.gir: libdbusmenu-glib.la +Dbusmenu_0_4_gir_INCLUDES = \ GObject-2.0 -Dbusmenu_Glib_0_4_gir_CFLAGS = $(DBUSMENUGLIB_CFLAGS) -I$(top_srcdir) -Dbusmenu_Glib_0_4_gir_LIBS = libdbusmenu-glib.la -Dbusmenu_Glib_0_4_gir_FILES = $(addprefix $(srcdir)/, $(introspection_sources)) -Dbusmenu_Glib_0_4_gir_NAMESPACE = Dbusmenu -Dbusmenu_Glib_0_4_gir_VERSION = Glib-0.4 -Dbusmenu_Glib_0_4_gir_EXPORT_PACKAGES = dbusmenu-glib-0.4 -Dbusmenu_Glib_0_4_gir_SCANNER_FLAGS = $(INTROSPECTION_SCANNER_ARGS) +Dbusmenu_0_4_gir_CFLAGS = $(DBUSMENUGLIB_CFLAGS) -I$(top_srcdir) +Dbusmenu_0_4_gir_LIBS = libdbusmenu-glib.la +Dbusmenu_0_4_gir_FILES = $(addprefix $(srcdir)/, $(introspection_sources)) +Dbusmenu_0_4_gir_NAMESPACE = Dbusmenu +Dbusmenu_0_4_gir_VERSION = 0.4 +Dbusmenu_0_4_gir_EXPORT_PACKAGES = dbusmenu-glib-0.4 +Dbusmenu_0_4_gir_SCANNER_FLAGS = $(INTROSPECTION_SCANNER_ARGS) -INTROSPECTION_GIRS += Dbusmenu-Glib-0.4.gir +INTROSPECTION_GIRS += Dbusmenu-0.4.gir girdir = $(datadir)/gir-1.0 gir_DATA = $(INTROSPECTION_GIRS) @@ -168,10 +170,10 @@ endif if HAVE_INTROSPECTION vapidir = $(datadir)/vala/vapi -vapi_DATA = Dbusmenu-Glib-0.4.vapi +vapi_DATA = Dbusmenu-0.4.vapi -Dbusmenu-Glib-0.4.vapi: Dbusmenu-Glib-0.4.gir - $(VALA_API_GEN) --library=Dbusmenu-Glib-0.4 $< +Dbusmenu-0.4.vapi: Dbusmenu-0.4.gir + $(VALA_API_GEN) --library=Dbusmenu-0.4 $< CLEANFILES += $(vapi_DATA) diff --git a/libdbusmenu-glib/client.c b/libdbusmenu-glib/client.c index b196c9f..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" @@ -129,6 +126,12 @@ struct _type_handler_t { gchar * type; }; +typedef struct _properties_callback_t properties_callback_t; +struct _properties_callback_t { + DbusmenuClient * client; + GArray * listeners; +}; + #define DBUSMENU_CLIENT_GET_PRIVATE(o) (DBUSMENU_CLIENT(o)->priv) #define DBUSMENU_INTERFACE "com.canonical.dbusmenu" @@ -145,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); @@ -512,7 +514,8 @@ find_listener (GArray * listeners, guint index, gint id) static void get_properties_callback (GObject *obj, GAsyncResult * res, gpointer user_data) { - GArray * listeners = (GArray *)user_data; + properties_callback_t * cbdata = (properties_callback_t *)user_data; + GArray * listeners = cbdata->listeners; int i; GError * error = NULL; GVariant * params = NULL; @@ -526,9 +529,8 @@ get_properties_callback (GObject *obj, GAsyncResult * res, gpointer user_data) properties_listener_t * listener = &g_array_index(listeners, properties_listener_t, i); listener->callback(NULL, error, listener->user_data); } - g_array_free(listeners, TRUE); g_error_free(error); - return; + goto out; } /* Callback all the folks we can find */ @@ -575,8 +577,11 @@ get_properties_callback (GObject *obj, GAsyncResult * res, gpointer user_data) g_error_free(localerror); } +out: /* Clean up */ g_array_free(listeners, TRUE); + g_object_unref(cbdata->client); + g_free(user_data); return; } @@ -586,6 +591,7 @@ get_properties_callback (GObject *obj, GAsyncResult * res, gpointer user_data) static gboolean get_properties_idle (gpointer user_data) { + properties_callback_t * cbdata = NULL; DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(user_data); g_return_val_if_fail(priv->menuproxy != NULL, TRUE); @@ -616,6 +622,11 @@ get_properties_idle (gpointer user_data) g_variant_builder_add_value(&builder, variant_props); GVariant * variant_params = g_variant_builder_end(&builder); + cbdata = g_new(properties_callback_t, 1); + cbdata->listeners = priv->delayed_property_listeners; + cbdata->client = DBUSMENU_CLIENT(user_data); + g_object_ref(G_OBJECT(user_data)); + g_dbus_proxy_call(priv->menuproxy, "GetGroupProperties", variant_params, @@ -623,7 +634,7 @@ get_properties_idle (gpointer user_data) -1, /* timeout */ NULL, /* cancellable */ get_properties_callback, - priv->delayed_property_listeners); + cbdata); /* Free properties */ gchar ** dataregion = (gchar **)g_array_free(priv->delayed_property_list, FALSE); @@ -1015,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); @@ -1039,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. @@ -1250,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", @@ -1375,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; } @@ -1390,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; @@ -1436,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 @@ -1458,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; } @@ -1474,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."); @@ -1492,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"); @@ -1500,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) { @@ -1519,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) { @@ -1553,11 +1586,6 @@ update_layout_cb (GObject * proxy, GAsyncResult * res, gpointer data) DbusmenuClient * client = DBUSMENU_CLIENT(data); DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client); - if (priv->layoutcall != NULL) { - g_object_unref(priv->layoutcall); - priv->layoutcall = NULL; - } - GError * error = NULL; GVariant * params = NULL; @@ -1566,21 +1594,17 @@ update_layout_cb (GObject * proxy, GAsyncResult * res, gpointer data) if (error != NULL) { g_warning("Getting layout failed: %s", error->message); g_error_free(error); - return; + goto out; } - guint rev; - gchar * xml; + guint rev = g_variant_get_uint32(g_variant_get_child_value(params, 0)); + GVariant * layout = g_variant_get_child_value(params, 1); - g_variant_get(params, "(us)", &rev, &xml); - g_variant_unref(params); - - guint parseable = parse_layout(client, xml); - g_free(xml); + guint parseable = parse_layout(client, layout); if (parseable == 0) { g_warning("Unable to parse layout!"); - return; + goto out; } priv->my_revision = rev; @@ -1596,6 +1620,17 @@ update_layout_cb (GObject * proxy, GAsyncResult * res, gpointer data) update_layout(client); } +out: + if (priv->layoutcall != NULL) { + g_object_unref(priv->layoutcall); + priv->layoutcall = NULL; + } + + if (params != NULL) { + g_variant_unref(params); + } + + g_object_unref(G_OBJECT(client)); return; } @@ -1622,9 +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 */ @@ -1636,17 +1682,17 @@ update_layout (DbusmenuClient * client) /* Public API */ /** - dbusmenu_client_new: - @name: The DBus name for the server to connect to - @object: The object on the server to monitor - - This function creates a new client that connects to a specific - server on DBus. That server is at a specific location sharing - a known object. The interface is assumed by the code to be - the DBus menu interface. The newly created client will start - sending out events as it syncs up with the server. - - Return value: A brand new #DbusmenuClient + * dbusmenu_client_new: + * @name: The DBus name for the server to connect to + * @object: The object on the server to monitor + * + * This function creates a new client that connects to a specific + * server on DBus. That server is at a specific location sharing + * a known object. The interface is assumed by the code to be + * the DBus menu interface. The newly created client will start + * sending out events as it syncs up with the server. + * + * Return value: A brand new #DbusmenuClient */ DbusmenuClient * dbusmenu_client_new (const gchar * name, const gchar * object) @@ -1660,21 +1706,21 @@ dbusmenu_client_new (const gchar * name, const gchar * object) } /** - dbusmenu_client_get_root: - @client: The #DbusmenuClient to get the root node from - - Grabs the root node for the specified client @client. This - function may block. It will block if there is currently a - call to update the layout, it will block on that layout - updated and then return the newly updated layout. Chances - are that this update is in the queue for the mainloop as - it would have been requested some time ago, but in theory - it could block longer. - - Return value: A #DbusmenuMenuitem representing the root of - menu on the server. If there is no server or there is - an error receiving its layout it'll return #NULL. -*/ + * dbusmenu_client_get_root: + * @client: The #DbusmenuClient to get the root node from + * + * Grabs the root node for the specified client @client. This + * function may block. It will block if there is currently a + * call to update the layout, it will block on that layout + * updated and then return the newly updated layout. Chances + * are that this update is in the queue for the mainloop as + * it would have been requested some time ago, but in theory + * it could block longer. + * + * Return value: (transfer none): A #DbusmenuMenuitem representing the root of + * menu on the server. If there is no server or there is + * an error receiving its layout it'll return #NULL. + */ DbusmenuMenuitem * dbusmenu_client_get_root (DbusmenuClient * client) { @@ -1703,25 +1749,25 @@ type_handler_destroy (gpointer user_data) } /** - dbusmenu_client_add_type_handler: - @client: Client where we're getting types coming in - @type: A text string that will be matched with the 'type' - property on incoming menu items - @newfunc: The function that will be executed with those new - items when they come in. - - This function connects into the type handling of the #DbusmenuClient. - Every new menuitem that comes in immediately gets asked for it's - properties. When we get those properties we check the 'type' - property and look to see if it matches a handler that is known - by the client. If so, the @newfunc function is executed on that - #DbusmenuMenuitem. If not, then the DbusmenuClient::new-menuitem - signal is sent. - - In the future the known types will be sent to the server so that it - can make choices about the menu item types availble. - - Return value: If registering the new type was successful. + * dbusmenu_client_add_type_handler: + * @client: Client where we're getting types coming in + * @type: A text string that will be matched with the 'type' + * property on incoming menu items + * @newfunc: The function that will be executed with those new + * items when they come in. + * + * This function connects into the type handling of the #DbusmenuClient. + * Every new menuitem that comes in immediately gets asked for it's + * properties. When we get those properties we check the 'type' + * property and look to see if it matches a handler that is known + * by the client. If so, the @newfunc function is executed on that + * #DbusmenuMenuitem. If not, then the DbusmenuClient::new-menuitem + * signal is sent. + * + * In the future the known types will be sent to the server so that it + * can make choices about the menu item types availble. + * + * Return value: If registering the new type was successful. */ gboolean dbusmenu_client_add_type_handler (DbusmenuClient * client, const gchar * type, DbusmenuClientTypeHandler newfunc) @@ -1730,29 +1776,29 @@ dbusmenu_client_add_type_handler (DbusmenuClient * client, const gchar * type, D } /** - dbusmenu_client_add_type_handler_full: - @client: Client where we're getting types coming in - @type: A text string that will be matched with the 'type' - property on incoming menu items - @newfunc: The function that will be executed with those new - items when they come in. - @user_data: Data passed to @newfunc when it is called - @destroy_func: A function that is called when the type handler is - removed (usually on client destruction) which will free - the resources in @user_data. - - This function connects into the type handling of the #DbusmenuClient. - Every new menuitem that comes in immediately gets asked for it's - properties. When we get those properties we check the 'type' - property and look to see if it matches a handler that is known - by the client. If so, the @newfunc function is executed on that - #DbusmenuMenuitem. If not, then the DbusmenuClient::new-menuitem - signal is sent. - - In the future the known types will be sent to the server so that it - can make choices about the menu item types availble. - - Return value: If registering the new type was successful. + * dbusmenu_client_add_type_handler_full: + * @client: Client where we're getting types coming in + * @type: A text string that will be matched with the 'type' + * property on incoming menu items + * @newfunc: The function that will be executed with those new + * items when they come in. + * @user_data: Data passed to @newfunc when it is called + * @destroy_func: A function that is called when the type handler is + * removed (usually on client destruction) which will free + * the resources in @user_data. + * + * This function connects into the type handling of the #DbusmenuClient. + * Every new menuitem that comes in immediately gets asked for it's + * properties. When we get those properties we check the 'type' + * property and look to see if it matches a handler that is known + * by the client. If so, the @newfunc function is executed on that + * #DbusmenuMenuitem. If not, then the DbusmenuClient::new-menuitem + * signal is sent. + * + * In the future the known types will be sent to the server so that it + * can make choices about the menu item types availble. + * + * Return value: If registering the new type was successful. */ gboolean dbusmenu_client_add_type_handler_full (DbusmenuClient * client, const gchar * type, DbusmenuClientTypeHandler newfunc, gpointer user_data, DbusmenuClientTypeDestroyHandler destroy_func) diff --git a/libdbusmenu-glib/client.h b/libdbusmenu-glib/client.h index 6d78edf..79c0ee2 100644 --- a/libdbusmenu-glib/client.h +++ b/libdbusmenu-glib/client.h @@ -145,7 +145,7 @@ gboolean dbusmenu_client_add_type_handler_full (DbusmenuClient * cli const gchar * type, DbusmenuClientTypeHandler newfunc, gpointer user_data, - DbusmenuClientTypeDestroyHandler destory_func); + DbusmenuClientTypeDestroyHandler destroy_func); void dbusmenu_client_send_event (DbusmenuClient * client, gint id, const gchar * name, 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/defaults.c b/libdbusmenu-glib/defaults.c new file mode 100644 index 0000000..3ad5d76 --- /dev/null +++ b/libdbusmenu-glib/defaults.c @@ -0,0 +1,291 @@ +/* +A library to communicate a menu object set accross DBus and +track updates and maintain consistency. + +Copyright 2011 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 <glib/gi18n.h> + +#include "defaults.h" +#include "menuitem.h" +#include "client.h" + +struct _DbusmenuDefaultsPrivate { + GHashTable * types; +}; + +typedef struct _DefaultEntry DefaultEntry; +struct _DefaultEntry { + GVariantType * type; + GVariant * value; +}; + +#define DBUSMENU_DEFAULTS_GET_PRIVATE(o) \ +(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_DEFAULTS, DbusmenuDefaultsPrivate)) + +static void dbusmenu_defaults_class_init (DbusmenuDefaultsClass *klass); +static void dbusmenu_defaults_init (DbusmenuDefaults *self); +static void dbusmenu_defaults_dispose (GObject *object); +static void dbusmenu_defaults_finalize (GObject *object); + +static DefaultEntry * entry_create (const GVariantType * type, GVariant * variant); +static void entry_destroy (gpointer entry); + +G_DEFINE_TYPE (DbusmenuDefaults, dbusmenu_defaults, G_TYPE_OBJECT); + +static void +dbusmenu_defaults_class_init (DbusmenuDefaultsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (DbusmenuDefaultsPrivate)); + + object_class->dispose = dbusmenu_defaults_dispose; + object_class->finalize = dbusmenu_defaults_finalize; + return; +} + +static void +dbusmenu_defaults_init (DbusmenuDefaults *self) +{ + self->priv = DBUSMENU_DEFAULTS_GET_PRIVATE(self); + + self->priv->types = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_hash_table_destroy); + + /* Standard defaults */ + dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_DEFAULT, DBUSMENU_MENUITEM_PROP_VISIBLE, G_VARIANT_TYPE_BOOLEAN, g_variant_new_boolean(TRUE)); + dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_DEFAULT, DBUSMENU_MENUITEM_PROP_ENABLED, G_VARIANT_TYPE_BOOLEAN, g_variant_new_boolean(TRUE)); + dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_DEFAULT, DBUSMENU_MENUITEM_PROP_LABEL, G_VARIANT_TYPE_STRING, g_variant_new_string(_("Label Empty"))); + dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_DEFAULT, DBUSMENU_MENUITEM_PROP_ICON_NAME, G_VARIANT_TYPE_STRING, NULL); + dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_DEFAULT, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, G_VARIANT_TYPE_STRING, NULL); + dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_DEFAULT, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, G_VARIANT_TYPE_INT32, NULL); + dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_DEFAULT, DBUSMENU_MENUITEM_PROP_SHORTCUT, G_VARIANT_TYPE_ARRAY, NULL); + dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_DEFAULT, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY, G_VARIANT_TYPE_STRING, NULL); + + /* Separator defaults */ + dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_SEPARATOR, DBUSMENU_MENUITEM_PROP_VISIBLE, G_VARIANT_TYPE_BOOLEAN, g_variant_new_boolean(TRUE)); + + return; +} + +static void +dbusmenu_defaults_dispose (GObject *object) +{ + DbusmenuDefaults * self = DBUSMENU_DEFAULTS(object); + + if (self->priv->types != NULL) { + g_hash_table_destroy(self->priv->types); + self->priv->types = NULL; + } + + G_OBJECT_CLASS (dbusmenu_defaults_parent_class)->dispose (object); + return; +} + +static void +dbusmenu_defaults_finalize (GObject *object) +{ + + G_OBJECT_CLASS (dbusmenu_defaults_parent_class)->finalize (object); + return; +} + +/* Create a new entry based on the info provided */ +static DefaultEntry * +entry_create (const GVariantType * type, GVariant * variant) +{ + DefaultEntry * defentry = g_new0(DefaultEntry, 1); + + if (type != NULL) { + defentry->type = g_variant_type_copy(type); + } + + if (variant != NULL) { + defentry->value = variant; + g_variant_ref_sink(variant); + } + + return defentry; +} + +/* Destroy an entry */ +static void +entry_destroy (gpointer entry) +{ + DefaultEntry * defentry = (DefaultEntry *)entry; + + if (defentry->type != NULL) { + g_variant_type_free(defentry->type); + defentry->type = NULL; + } + + if (defentry->value != NULL) { + g_variant_unref(defentry->value); + defentry->value = NULL; + } + + g_free(defentry); + return; +} + +static DbusmenuDefaults * default_defaults = NULL; + +/** + * dbusmenu_defaults_ref_default: + * + * Get a reference to the default instance. If it doesn't exist this + * function will create it. + * + * Return value: (transfer full): A reference to the defaults + */ +DbusmenuDefaults * +dbusmenu_defaults_ref_default (void) +{ + if (default_defaults == NULL) { + default_defaults = DBUSMENU_DEFAULTS(g_object_new(DBUSMENU_TYPE_DEFAULTS, NULL)); + g_object_add_weak_pointer(G_OBJECT(default_defaults), (gpointer *)&default_defaults); + } else { + g_object_ref(default_defaults); + } + + return default_defaults; +} + +/** + * dbusmenu_defaults_default_set: + * @defaults: The #DbusmenuDefaults object to add to + * @type: (allow-none): The #DbusmenuMenuitem type for this default if #NULL will default to #DBUSMENU_CLIENT_TYPE_DEFAULT + * @property: Property name for the default + * @prop_type: (allow-none): Type of the property for runtime checking. To disable checking set to #NULL. + * @value: (allow-none): Default value for @property. #NULL if by default it is unset, but you want type checking from @prop_type. + * + * Sets up an entry in the defaults database for a given @property + * and menuitem type @type. @prop_type and @value can both be #NULL + * but both of them can not. + */ +void +dbusmenu_defaults_default_set (DbusmenuDefaults * defaults, const gchar * type, const gchar * property, const GVariantType * prop_type, GVariant * value) +{ + g_return_if_fail(DBUSMENU_IS_DEFAULTS(defaults)); + g_return_if_fail(property != NULL); + g_return_if_fail(prop_type != NULL || value != NULL); + + if (type == NULL) { + type = DBUSMENU_CLIENT_TYPES_DEFAULT; + } + + GHashTable * prop_table = (GHashTable *)g_hash_table_lookup(defaults->priv->types, type); + + /* We've never had a default for this type, so we need + to create a table for it. */ + if (prop_table == NULL) { + prop_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, entry_destroy); + + g_hash_table_insert(prop_table, g_strdup(property), entry_create(prop_type, value)); + g_hash_table_insert(defaults->priv->types, g_strdup(type), prop_table); + } else { + g_hash_table_replace(prop_table, g_strdup(property), entry_create(prop_type, value)); + } + + return; +} + +/** + * dbusmenu_defaults_default_get: + * @defaults: The default database to use + * @type: (allow-none): The #DbusmenuMenuitem type for this default if #NULL will default to #DBUSMENU_CLIENT_TYPE_DEFAULT + * @property: Property name to lookup + * + * Gets an entry in the database for a give @property and @type. + * + * Return value: (transfer none): Returns a variant that does not + * have it's ref count increased. If you want to keep it, you should + * do that. + */ +GVariant * +dbusmenu_defaults_default_get (DbusmenuDefaults * defaults, const gchar * type, const gchar * property) +{ + g_return_val_if_fail(DBUSMENU_IS_DEFAULTS(defaults), NULL); + g_return_val_if_fail(property != NULL, NULL); + + if (type == NULL) { + type = DBUSMENU_CLIENT_TYPES_DEFAULT; + } + + GHashTable * prop_table = (GHashTable *)g_hash_table_lookup(defaults->priv->types, type); + + if (prop_table == NULL) { + return NULL; + } + + DefaultEntry * entry = (DefaultEntry *)g_hash_table_lookup(prop_table, property); + + if (entry == NULL) { + return NULL; + } + + return entry->value; +} + +/** + * dbusmenu_defaults_default_get_type: + * @defaults: The default database to use + * @type: (allow-none): The #DbusmenuMenuitem type for this default if #NULL will default to #DBUSMENU_CLIENT_TYPE_DEFAULT + * @property: Property name to lookup + * + * Gets the type for an entry in the database for a give @property and @type. + * + * Return value: (transfer none): Returns a type for the given + * @property value. + */ +GVariantType * +dbusmenu_defaults_default_get_type (DbusmenuDefaults * defaults, const gchar * type, const gchar * property) +{ + g_return_val_if_fail(DBUSMENU_IS_DEFAULTS(defaults), NULL); + g_return_val_if_fail(property != NULL, NULL); + + if (type == NULL) { + type = DBUSMENU_CLIENT_TYPES_DEFAULT; + } + + GHashTable * prop_table = (GHashTable *)g_hash_table_lookup(defaults->priv->types, type); + + if (prop_table == NULL) { + return NULL; + } + + DefaultEntry * entry = (DefaultEntry *)g_hash_table_lookup(prop_table, property); + + if (entry == NULL) { + return NULL; + } + + return entry->type; +} + diff --git a/libdbusmenu-glib/defaults.h b/libdbusmenu-glib/defaults.h new file mode 100644 index 0000000..ab377f7 --- /dev/null +++ b/libdbusmenu-glib/defaults.h @@ -0,0 +1,86 @@ +/* +A library to communicate a menu object set accross DBus and +track updates and maintain consistency. + +Copyright 2011 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_DEFAULTS_H__ +#define __DBUSMENU_DEFAULTS_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define DBUSMENU_TYPE_DEFAULTS (dbusmenu_defaults_get_type ()) +#define DBUSMENU_DEFAULTS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DBUSMENU_TYPE_DEFAULTS, DbusmenuDefaults)) +#define DBUSMENU_DEFAULTS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUSMENU_TYPE_DEFAULTS, DbusmenuDefaultsClass)) +#define DBUSMENU_IS_DEFAULTS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DBUSMENU_TYPE_DEFAULTS)) +#define DBUSMENU_IS_DEFAULTS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_TYPE_DEFAULTS)) +#define DBUSMENU_DEFAULTS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_DEFAULTS, DbusmenuDefaultsClass)) + +typedef struct _DbusmenuDefaults DbusmenuDefaults; +typedef struct _DbusmenuDefaultsClass DbusmenuDefaultsClass; +typedef struct _DbusmenuDefaultsPrivate DbusmenuDefaultsPrivate; + +/** + * DbusmenuDefaultsClass: + * + * All of the signals and functions for #DbusmenuDefaults + */ +struct _DbusmenuDefaultsClass { + GObjectClass parent_class; +}; + +/** + * DbusmenuDefaults: + * + * A singleton to hold all of the defaults for the menuitems + * so they can use those easily. + */ +struct _DbusmenuDefaults { + GObject parent; + + /*< Private >*/ + DbusmenuDefaultsPrivate * priv; +}; + +GType dbusmenu_defaults_get_type (void); +DbusmenuDefaults * dbusmenu_defaults_ref_default (void); +void dbusmenu_defaults_default_set (DbusmenuDefaults * defaults, + const gchar * type, + const gchar * property, + const GVariantType * prop_type, + GVariant * value); +GVariant * dbusmenu_defaults_default_get (DbusmenuDefaults * defaults, + const gchar * type, + const gchar * property); +GVariantType * dbusmenu_defaults_default_get_type (DbusmenuDefaults * defaults, + const gchar * type, + const gchar * property); + +G_END_DECLS + +#endif 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-proxy.c b/libdbusmenu-glib/menuitem-proxy.c index 1d97c4c..ae6a334 100644 --- a/libdbusmenu-glib/menuitem-proxy.c +++ b/libdbusmenu-glib/menuitem-proxy.c @@ -325,14 +325,14 @@ remove_menuitem (DbusmenuMenuitemProxy * pmi) } /** - dbusmenu_menuitem_proxy_new: - @mi: The #DbusmenuMenuitem to proxy - - Builds a new #DbusmenuMenuitemProxy object that proxies - all of the values for @mi. - - Return value: A new #DbusmenuMenuitemProxy object. -*/ + * dbusmenu_menuitem_proxy_new: + * @mi: The #DbusmenuMenuitem to proxy + * + * Builds a new #DbusmenuMenuitemProxy object that proxies + * all of the values for @mi. + * + * Return value: A new #DbusmenuMenuitemProxy object. + */ DbusmenuMenuitemProxy * dbusmenu_menuitem_proxy_new (DbusmenuMenuitem * mi) { @@ -344,15 +344,15 @@ dbusmenu_menuitem_proxy_new (DbusmenuMenuitem * mi) } /** - dbusmenu_menuitem_proxy_get_wrapped: - @pmi: #DbusmenuMenuitemProxy to look into - - Accesses the private variable of which #DbusmenuMenuitem - we are doing the proxying for. - - Return value: A #DbusmenuMenuitem object or a #NULL if we - don't have one or there is an error. -*/ + * dbusmenu_menuitem_proxy_get_wrapped: + * @pmi: #DbusmenuMenuitemProxy to look into + * + * Accesses the private variable of which #DbusmenuMenuitem + * we are doing the proxying for. + * + * Return value: (transfer none): A #DbusmenuMenuitem object or a #NULL if we + * don't have one or there is an error. + */ DbusmenuMenuitem * dbusmenu_menuitem_proxy_get_wrapped (DbusmenuMenuitemProxy * pmi) { diff --git a/libdbusmenu-glib/menuitem.c b/libdbusmenu-glib/menuitem.c index b153900..d8ae1a1 100644 --- a/libdbusmenu-glib/menuitem.c +++ b/libdbusmenu-glib/menuitem.c @@ -33,6 +33,7 @@ License version 3 and version 2.1 along with this program. If not, see #include "menuitem.h" #include "menuitem-marshal.h" #include "menuitem-private.h" +#include "defaults.h" #ifdef MASSIVEDEBUGGING #define LABEL(x) dbusmenu_menuitem_property_get(DBUSMENU_MENUITEM(x), DBUSMENU_MENUITEM_PROP_LABEL) @@ -59,6 +60,7 @@ struct _DbusmenuMenuitemPrivate GHashTable * properties; gboolean root; gboolean realized; + DbusmenuDefaults * defaults; }; /* Signals */ @@ -96,7 +98,7 @@ static void set_property (GObject * obj, guint id, const GValue * value, GParamS 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); -static void handle_event (DbusmenuMenuitem * mi, const gchar * name, GVariant * value, guint timestamp); +static void handle_event (DbusmenuMenuitem * mi, const gchar * name, GVariant * variant, guint timestamp); static void send_about_to_show (DbusmenuMenuitem * mi, void (*cb) (DbusmenuMenuitem * mi, gpointer user_data), gpointer cb_data); /* GObject stuff */ @@ -330,6 +332,8 @@ dbusmenu_menuitem_init (DbusmenuMenuitem *self) priv->root = FALSE; priv->realized = FALSE; + + priv->defaults = dbusmenu_defaults_ref_default(); return; } @@ -346,6 +350,11 @@ dbusmenu_menuitem_dispose (GObject *object) g_list_free(priv->children); priv->children = NULL; + if (priv->defaults != NULL) { + g_object_unref(priv->defaults); + priv->defaults = NULL; + } + G_OBJECT_CLASS (dbusmenu_menuitem_parent_class)->dispose (object); return; } @@ -443,15 +452,28 @@ send_about_to_show (DbusmenuMenuitem * mi, void (*cb) (DbusmenuMenuitem * mi, gp return; } +/* A helper function to get the type of the menuitem, this might + be a candidate for optimization in the future. */ +static const gchar * +menuitem_get_type (DbusmenuMenuitem * mi) +{ + DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi); + GVariant * currentval = (GVariant *)g_hash_table_lookup(priv->properties, DBUSMENU_MENUITEM_PROP_TYPE); + if (currentval != NULL) { + return g_variant_get_string(currentval, NULL); + } + return NULL; +} + /* Public interface */ /** - dbusmenu_menuitem_new: - - Create a new #DbusmenuMenuitem with all default values. - - Return value: A newly allocated #DbusmenuMenuitem. -*/ + * dbusmenu_menuitem_new: + * + * Create a new #DbusmenuMenuitem with all default values. + * + * Return value: A newly allocated #DbusmenuMenuitem. + */ DbusmenuMenuitem * dbusmenu_menuitem_new (void) { @@ -459,13 +481,13 @@ dbusmenu_menuitem_new (void) } /** - dbusmenu_menuitem_new_with_id: - @id: ID to use for this menuitem - - This creates a blank #DbusmenuMenuitem with a specific ID. - - Return value: A newly allocated #DbusmenuMenuitem. -*/ + * dbusmenu_menuitem_new_with_id: + * @id: ID to use for this menuitem + * + * This creates a blank #DbusmenuMenuitem with a specific ID. + * + * Return value: A newly allocated #DbusmenuMenuitem. + */ DbusmenuMenuitem * dbusmenu_menuitem_new_with_id (gint id) { @@ -475,13 +497,13 @@ dbusmenu_menuitem_new_with_id (gint id) } /** - dbusmenu_menuitem_get_id: - @mi: The #DbusmenuMenuitem to query. - - Gets the unique ID for @mi. - - Return value: The ID of the @mi. -*/ + * dbusmenu_menuitem_get_id: + * @mi: The #DbusmenuMenuitem to query. + * + * Gets the unique ID for @mi. + * + * Return value: The ID of the @mi. + */ gint dbusmenu_menuitem_get_id (DbusmenuMenuitem * mi) { @@ -498,17 +520,17 @@ dbusmenu_menuitem_get_id (DbusmenuMenuitem * mi) } /** - dbusmenu_menuitem_realized: - @mi: #DbusmenuMenuitem to check on - - This function returns whether the menuitem has been realized or - not. This is significant mostly in client implementations that - can use this additional state to see if the second layers of - the implementation have been built yet. - - Return value: Returns whether or not the menu item has been realized - yet or not. -*/ + * dbusmenu_menuitem_realized: + * @mi: #DbusmenuMenuitem to check on + * + * This function returns whether the menuitem has been realized or + * not. This is significant mostly in client implementations that + * can use this additional state to see if the second layers of + * the implementation have been built yet. + * + * Return value: Returns whether or not the menu item has been realized + * yet or not. + */ gboolean dbusmenu_menuitem_realized (DbusmenuMenuitem * mi) { @@ -518,12 +540,12 @@ dbusmenu_menuitem_realized (DbusmenuMenuitem * mi) } /** - dbusmenu_menuitem_set_realized: - @mi: #DbusmenuMenuitem to realize - - Sets the internal variable tracking whether it's been realized and - signals the DbusmenuMenuitem::realized event. -*/ + * dbusmenu_menuitem_set_realized: + * @mi: #DbusmenuMenuitem to realize + * + * Sets the internal variable tracking whether it's been realized and + * signals the DbusmenuMenuitem::realized event. + */ void dbusmenu_menuitem_set_realized (DbusmenuMenuitem * mi) { @@ -538,15 +560,15 @@ dbusmenu_menuitem_set_realized (DbusmenuMenuitem * mi) } /** - dbusmenu_menuitem_get_children: - @mi: The #DbusmenuMenuitem to query. - - Returns simply the list of children that this menu item - has. The list is valid until another child related function - is called, where it might be changed. - - Return value: A #GList of pointers to #DbusmenuMenuitem objects. -*/ + * dbusmenu_menuitem_get_children: + * @mi: The #DbusmenuMenuitem to query. + * + * Returns simply the list of children that this menu item + * has. The list is valid until another child related function + * is called, where it might be changed. + * + * Return value: (transfer none): A #GList of pointers to #DbusmenuMenuitem objects. + */ GList * dbusmenu_menuitem_get_children (DbusmenuMenuitem * mi) { @@ -565,22 +587,22 @@ take_children_signal (gpointer data, gpointer user_data) g_debug("Menuitem %d (%s) signalling child removed %d (%s)", ID(user_data), LABEL(user_data), ID(data), LABEL(data)); #endif g_signal_emit(G_OBJECT(user_data), signals[CHILD_REMOVED], 0, DBUSMENU_MENUITEM(data), TRUE); - g_object_unref(G_OBJECT(data)); return; } /** - dbusmenu_menuitem_take_children: - @mi: The #DbusmenMenuitem to take the children from. - - While the name sounds devious that's exactly what this function - does. It takes the list of children from the @mi and clears the - internal list. The calling function is now in charge of the ref's - on the children it has taken. A lot of responsibility involved - in taking children. - - Return value: A #GList of pointers to #DbusmenuMenuitem objects. -*/ + * dbusmenu_menuitem_take_children: + * @mi: The #DbusmenMenuitem to take the children from. + * + * While the name sounds devious that's exactly what this function + * does. It takes the list of children from the @mi and clears the + * internal list. The calling function is now in charge of the ref's + * on the children it has taken. A lot of responsibility involved + * in taking children. + * + * Return value: (transfer full) (element-type Dbusmenu.Menuitem): + * A #GList of pointers to #DbusmenuMenuitem objects. + */ GList * dbusmenu_menuitem_take_children (DbusmenuMenuitem * mi) { @@ -597,16 +619,16 @@ dbusmenu_menuitem_take_children (DbusmenuMenuitem * mi) } /** - dbusmenu_menuitem_get_position: - @mi: The #DbusmenuMenuitem to find the position of - @parent: The #DbusmenuMenuitem who's children contain @mi - - This function returns the position of the menu item @mi - in the children of @parent. It will return zero if the - menu item can't be found. - - Return value: The position of @mi in the children of @parent. -*/ + * dbusmenu_menuitem_get_position: + * @mi: The #DbusmenuMenuitem to find the position of + * @parent: The #DbusmenuMenuitem who's children contain @mi + * + * This function returns the position of the menu item @mi + * in the children of @parent. It will return zero if the + * menu item can't be found. + * + * Return value: The position of @mi in the children of @parent. + */ guint dbusmenu_menuitem_get_position (DbusmenuMenuitem * mi, DbusmenuMenuitem * parent) { @@ -636,15 +658,15 @@ dbusmenu_menuitem_get_position (DbusmenuMenuitem * mi, DbusmenuMenuitem * parent } /** - dbusmenu_menuitem_get_position_realized: - @mi: The #DbusmenuMenuitem to find the position of - @parent: The #DbusmenuMenuitem who's children contain @mi - - This function is very similar to #dbusmenu_menuitem_get_position - except that it only counts in the children that have been realized. - - Return value: The position of @mi in the realized children of @parent. -*/ + * dbusmenu_menuitem_get_position_realized: + * @mi: The #DbusmenuMenuitem to find the position of + * @parent: The #DbusmenuMenuitem who's children contain @mi + * + * This function is very similar to #dbusmenu_menuitem_get_position + * except that it only counts in the children that have been realized. + * + * Return value: The position of @mi in the realized children of @parent. + */ guint dbusmenu_menuitem_get_position_realized (DbusmenuMenuitem * mi, DbusmenuMenuitem * parent) { @@ -680,15 +702,15 @@ dbusmenu_menuitem_get_position_realized (DbusmenuMenuitem * mi, DbusmenuMenuitem } /** - dbusmenu_menuitem_child_append: - @mi: The #DbusmenuMenuitem which will become a new parent - @child: The #DbusmenMenuitem that will be a child - - This function adds @child to the list of children on @mi at - the end of that list. - - Return value: Whether the child has been added successfully. -*/ + * dbusmenu_menuitem_child_append: + * @mi: The #DbusmenuMenuitem which will become a new parent + * @child: The #DbusmenMenuitem that will be a child + * + * This function adds @child to the list of children on @mi at + * the end of that list. + * + * Return value: Whether the child has been added successfully. + */ gboolean dbusmenu_menuitem_child_append (DbusmenuMenuitem * mi, DbusmenuMenuitem * child) { @@ -712,15 +734,15 @@ dbusmenu_menuitem_child_append (DbusmenuMenuitem * mi, DbusmenuMenuitem * child) } /** - dbusmenu_menuitem_child_prepend: - @mi: The #DbusmenuMenuitem which will become a new parent - @child: The #DbusmenMenuitem that will be a child - - This function adds @child to the list of children on @mi at - the beginning of that list. - - Return value: Whether the child has been added successfully. -*/ + * dbusmenu_menuitem_child_prepend: + * @mi: The #DbusmenuMenuitem which will become a new parent + * @child: The #DbusmenMenuitem that will be a child + * + * This function adds @child to the list of children on @mi at + * the beginning of that list. + * + * Return value: Whether the child has been added successfully. + */ gboolean dbusmenu_menuitem_child_prepend (DbusmenuMenuitem * mi, DbusmenuMenuitem * child) { @@ -744,16 +766,16 @@ dbusmenu_menuitem_child_prepend (DbusmenuMenuitem * mi, DbusmenuMenuitem * child } /** - dbusmenu_menuitem_child_delete: - @mi: The #DbusmenuMenuitem which has @child as a child - @child: The child #DbusmenuMenuitem that you want to no longer - be a child of @mi. - - This function removes @child from the children list of @mi. It does - not call #g_object_unref on @child. - - Return value: If we were able to delete @child. -*/ + * dbusmenu_menuitem_child_delete: + * @mi: The #DbusmenuMenuitem which has @child as a child + * @child: The child #DbusmenuMenuitem that you want to no longer + * be a child of @mi. + * + * This function removes @child from the children list of @mi. It does + * not call #g_object_unref on @child. + * + * Return value: If we were able to delete @child. + */ gboolean dbusmenu_menuitem_child_delete (DbusmenuMenuitem * mi, DbusmenuMenuitem * child) { @@ -776,17 +798,17 @@ dbusmenu_menuitem_child_delete (DbusmenuMenuitem * mi, DbusmenuMenuitem * child) } /** - dbusmenu_menuitem_child_add_position: - @mi: The #DbusmenuMenuitem that we're adding the child @child to. - @child: The #DbusmenuMenuitem to make a child of @mi. - @position: Where in @mi object's list of chidren @child should be placed. - - Puts @child in the list of children for @mi at the location - specified in @position. If there is not enough entires available - then @child will be placed at the end of the list. - - Return value: Whether @child was added successfully. -*/ + * dbusmenu_menuitem_child_add_position: + * @mi: The #DbusmenuMenuitem that we're adding the child @child to. + * @child: The #DbusmenuMenuitem to make a child of @mi. + * @position: Where in @mi object's list of chidren @child should be placed. + * + * Puts @child in the list of children for @mi at the location + * specified in @position. If there is not enough entires available + * then @child will be placed at the end of the list. + * + * Return value: Whether @child was added successfully. + */ gboolean dbusmenu_menuitem_child_add_position (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position) { @@ -810,17 +832,17 @@ dbusmenu_menuitem_child_add_position (DbusmenuMenuitem * mi, DbusmenuMenuitem * } /** - dbusmenu_menuitem_child_reorder: - @base: The #DbusmenuMenuitem that has children needing realignment - @child: The #DbusmenuMenuitem that is a child needing to be moved - @position: The position in the list to place it in - - This function moves a child on the list of children. It is - for a child that is already in the list, but simply needs a - new location. - - Return value: Whether the move was successful. -*/ + * dbusmenu_menuitem_child_reorder: + * @mi: The #DbusmenuMenuitem that has children needing realignment + * @child: The #DbusmenuMenuitem that is a child needing to be moved + * @position: The position in the list to place it in + * + * This function moves a child on the list of children. It is + * for a child that is already in the list, but simply needs a + * new location. + * + * Return value: Whether the move was successful. + */ gboolean dbusmenu_menuitem_child_reorder(DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position) { @@ -850,16 +872,16 @@ dbusmenu_menuitem_child_reorder(DbusmenuMenuitem * mi, DbusmenuMenuitem * child, } /** - dbusmenu_menuitem_child_find: - @mi: The #DbusmenuMenuitem who's children to look on - @id: The ID of the child that we're looking for. - - Search the children of @mi to find one with the ID of @id. - If it doesn't exist then we return #NULL. - - Return value: The menu item with the ID @id or #NULL if it - can't be found. -*/ + * dbusmenu_menuitem_child_find: + * @mi: The #DbusmenuMenuitem who's children to look on + * @id: The ID of the child that we're looking for. + * + * Search the children of @mi to find one with the ID of @id. + * If it doesn't exist then we return #NULL. + * + * Return value: (transfer none): The menu item with the ID @id or #NULL if it + * can't be found. + */ DbusmenuMenuitem * dbusmenu_menuitem_child_find (DbusmenuMenuitem * mi, gint id) { @@ -903,18 +925,18 @@ find_id_helper (gpointer in_mi, gpointer in_find_id) } /** - dbusmenu_menuitem_find_id: - @mi: #DbusmenuMenuitem at the top of the tree to search - @id: ID of the #DbusmenuMenuitem to search for - - This function searchs the whole tree of children that - are attached to @mi. This could be quite a few nodes, all - the way down the tree. It is a depth first search. - - Return value: The #DbusmenuMenuitem with the ID of @id - or #NULL if there isn't such a menu item in the tree - represented by @mi. -*/ + * dbusmenu_menuitem_find_id: + * @mi: #DbusmenuMenuitem at the top of the tree to search + * @id: ID of the #DbusmenuMenuitem to search for + * + * This function searchs the whole tree of children that + * are attached to @mi. This could be quite a few nodes, all + * the way down the tree. It is a depth first search. + * + * Return value: (transfer none): The #DbusmenuMenuitem with the ID of @id + * or #NULL if there isn't such a menu item in the tree + * represented by @mi. + */ DbusmenuMenuitem * dbusmenu_menuitem_find_id (DbusmenuMenuitem * mi, gint id) { @@ -931,20 +953,20 @@ dbusmenu_menuitem_find_id (DbusmenuMenuitem * mi, gint id) } /** - 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. -*/ + * 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 (DbusmenuMenuitem * mi, const gchar * property, const gchar * value) { @@ -956,20 +978,20 @@ dbusmenu_menuitem_property_set (DbusmenuMenuitem * mi, const gchar * property, c } /** - 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. -*/ + * 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) { @@ -978,20 +1000,20 @@ dbusmenu_menuitem_property_set_bool (DbusmenuMenuitem * mi, const gchar * proper } /** - 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. -*/ + * 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) { @@ -1000,20 +1022,20 @@ dbusmenu_menuitem_property_set_int (DbusmenuMenuitem * mi, const gchar * propert } /** - dbusmenu_menuitem_property_set_variant: - @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. -*/ + * dbusmenu_menuitem_property_set_variant: + * @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_variant (DbusmenuMenuitem * mi, const gchar * property, GVariant * value) { @@ -1022,17 +1044,49 @@ dbusmenu_menuitem_property_set_variant (DbusmenuMenuitem * mi, const gchar * pro DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi); + const gchar * type = menuitem_get_type(mi); + + /* Check the expected type to see if we want to have a warning */ + GVariantType * default_type = dbusmenu_defaults_default_get_type(priv->defaults, type, property); + if (default_type != NULL) { + /* If we have an expected type we should check to see if + the value we've been given is of the same type and generate + a warning if it isn't */ + if (!g_variant_is_of_type(value, default_type)) { + g_warning("Setting menuitem property '%s' with value of type '%s' when expecting '%s'", property, g_variant_get_type_string(value), g_variant_type_peek_string(default_type)); + } + } + + /* Check the defaults database to see if we have a default + for this property. */ + GVariant * default_value = dbusmenu_defaults_default_get(priv->defaults, type, property); + if (default_value != NULL) { + /* Now see if we're setting this to the same value as the + default. If we are then we just want to swallow this variant + and make the function behave like we're clearing it. */ + if (g_variant_equal(default_value, value)) { + g_variant_ref_sink(value); + g_variant_unref(value); + value = NULL; + } + } + gboolean replaced = FALSE; gpointer currentval = g_hash_table_lookup(priv->properties, property); if (value != NULL) { - gchar * lprop = g_strdup(property); - g_variant_ref(value); - + /* NOTE: We're only marking this as replaced if this is true + but we're actually replacing it no matter. This is so that + the variant passed in sticks around which the caller may + expect. They shouldn't, but it's low cost to remove bugs. */ if (currentval == NULL || !g_variant_equal((GVariant*)currentval, value)) { - g_hash_table_replace(priv->properties, lprop, value); replaced = TRUE; } + + gchar * lprop = g_strdup(property); + g_variant_ref_sink(value); + + g_hash_table_replace(priv->properties, lprop, value); } else { if (currentval != NULL) { g_hash_table_remove(priv->properties, property); @@ -1045,25 +1099,33 @@ dbusmenu_menuitem_property_set_variant (DbusmenuMenuitem * mi, const gchar * pro table. But the fact that there was a value is the imporant part. */ if (currentval == NULL || replaced) { - g_signal_emit(G_OBJECT(mi), signals[PROPERTY_CHANGED], 0, property, value, TRUE); + GVariant * signalval = value; + + if (signalval == NULL) { + /* Might also be NULL, but if it is we're definitely + clearing this thing. */ + signalval = default_value; + } + + g_signal_emit(G_OBJECT(mi), signals[PROPERTY_CHANGED], 0, property, signalval, TRUE); } return TRUE; } /** - dbusmenu_menuitem_property_get: - @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 string with the value of the property - that shouldn't be free'd. Or #NULL if the property - is not set or is not a string. -*/ + * dbusmenu_menuitem_property_get: + * @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: (transfer none): A string with the value of the property + * that shouldn't be free'd. Or #NULL if the property + * is not set or is not a string. + */ const gchar * dbusmenu_menuitem_property_get (DbusmenuMenuitem * mi, const gchar * property) { @@ -1074,16 +1136,16 @@ dbusmenu_menuitem_property_get (DbusmenuMenuitem * mi, const gchar * property) } /** - dbusmenu_menuitem_property_get_variant: - @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 GVariant for the property. -*/ + * dbusmenu_menuitem_property_get_variant: + * @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: (transfer none): A GVariant for the property. + */ GVariant * dbusmenu_menuitem_property_get_variant (DbusmenuMenuitem * mi, const gchar * property) { @@ -1092,19 +1154,25 @@ dbusmenu_menuitem_property_get_variant (DbusmenuMenuitem * mi, const gchar * pro DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi); - return (GVariant *)g_hash_table_lookup(priv->properties, property); -} + GVariant * currentval = (GVariant *)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. + if (currentval == NULL) { + currentval = dbusmenu_defaults_default_get(priv->defaults, menuitem_get_type(mi), property); + } - Look up a property on @mi and return the value of it if - it exits. Returns #FALSE if the property doesn't exist. + return currentval; +} - Return value: The value of the property or #FALSE. -*/ +/** + * 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) { @@ -1130,15 +1198,15 @@ dbusmenu_menuitem_property_get_bool (DbusmenuMenuitem * mi, const gchar * proper } /** - 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. -*/ + * 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) { @@ -1160,15 +1228,15 @@ dbusmenu_menuitem_property_get_int (DbusmenuMenuitem * mi, const gchar * propert /** - dbusmenu_menuitem_property_exit: - @mi: The #DbusmenuMenuitem to look for the property on. - @property: The property to look for. - - Checkes to see if a particular property exists on @mi and - returns #TRUE if so. - - Return value: A boolean checking to see if the property is available -*/ + * dbusmenu_menuitem_property_exit: + * @mi: The #DbusmenuMenuitem to look for the property on. + * @property: The property to look for. + * + * Checkes to see if a particular property exists on @mi and + * returns #TRUE if so. + * + * Return value: A boolean checking to see if the property is available + */ gboolean dbusmenu_menuitem_property_exist (DbusmenuMenuitem * mi, const gchar * property) { @@ -1183,12 +1251,12 @@ dbusmenu_menuitem_property_exist (DbusmenuMenuitem * mi, const gchar * property) } /** - dbusmenu_menuitem_property_remove: - @mi: The #DbusmenuMenuitem to remove the property on. - @property: The property to look for. - - Removes a property from the menuitem. -*/ + * dbusmenu_menuitem_property_remove: + * @mi: The #DbusmenuMenuitem to remove the property on. + * @property: The property to look for. + * + * Removes a property from the menuitem. + */ void dbusmenu_menuitem_property_remove (DbusmenuMenuitem * mi, const gchar * property) { @@ -1203,15 +1271,16 @@ dbusmenu_menuitem_property_remove (DbusmenuMenuitem * mi, const gchar * property } /** - dbusmenu_menuitem_properties_list: - @mi: #DbusmenuMenuitem to list the properties on - - This functiong gets a list of the names of all the properties - that are set on this menu item. This data on the list is owned - by the menuitem but the list is not and should be freed using - g_list_free() when the calling function is done with it. - - Return value: A list of strings or NULL if there are none. + * dbusmenu_menuitem_properties_list: + * @mi: #DbusmenuMenuitem to list the properties on + * + * This functiong gets a list of the names of all the properties + * that are set on this menu item. This data on the list is owned + * by the menuitem but the list is not and should be freed using + * g_list_free() when the calling function is done with it. + * + * Return value: (transfer container): A list of strings or NULL if there are + * none. */ GList * dbusmenu_menuitem_properties_list (DbusmenuMenuitem * mi) @@ -1231,24 +1300,24 @@ 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; } /** - dbusmenu_menuitem_properties_copy: - @mi: #DbusmenuMenuitem that we're interested in the properties of - - This function takes the properties of a #DbusmenuMenuitem - and puts them into a #GHashTable that is referenced by the - key of a string and has the value of a string. The hash - table may not have any entries if there aren't any or there - is an error in processing. It is the caller's responsibility - to destroy the created #GHashTable. - - Return value: A brand new #GHashTable that contains all of the - properties that are on this #DbusmenuMenuitem @mi. + * dbusmenu_menuitem_properties_copy: + * @mi: #DbusmenuMenuitem that we're interested in the properties of + * + * This function takes the properties of a #DbusmenuMenuitem + * and puts them into a #GHashTable that is referenced by the + * key of a string and has the value of a string. The hash + * table may not have any entries if there aren't any or there + * is an error in processing. It is the caller's responsibility + * to destroy the created #GHashTable. + * + * Return value: (transfer full): A brand new #GHashTable that contains all of + * theroperties that are on this #DbusmenuMenuitem @mi. */ GHashTable * dbusmenu_menuitem_properties_copy (DbusmenuMenuitem * mi) @@ -1268,21 +1337,23 @@ 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; } /** - dbusmenu_menuitem_properties_variant: - @mi: #DbusmenuMenuitem to get properties from - - Grabs the properties of the menuitem as a GVariant with the - type "a{sv}". - - Return Value: A GVariant of type "a{sv}" or NULL on error. -*/ + * dbusmenu_menuitem_properties_variant: + * @mi: #DbusmenuMenuitem to get properties from + * + * Grabs the properties of the menuitem as a GVariant with the + * type "a{sv}". + * + * 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); @@ -1292,7 +1363,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); @@ -1303,15 +1374,15 @@ dbusmenu_menuitem_properties_variant (DbusmenuMenuitem * mi) } /** - dbusmenu_menuitem_set_root: - @mi: #DbusmenuMenuitem to set whether it's root - @root: Whether @mi is a root node or not - - This function sets the internal value of whether this is a - root node or not. - - Return value: None -*/ + * dbusmenu_menuitem_set_root: + * @mi: #DbusmenuMenuitem to set whether it's root + * @root: Whether @mi is a root node or not + * + * This function sets the internal value of whether this is a + * root node or not. + * + * Return value: None + */ void dbusmenu_menuitem_set_root (DbusmenuMenuitem * mi, gboolean root) { @@ -1322,14 +1393,14 @@ dbusmenu_menuitem_set_root (DbusmenuMenuitem * mi, gboolean root) } /** - dbusmenu_menuitem_get_root: - @mi: #DbusmenuMenuitem to see whether it's root - - This function returns the internal value of whether this is a - root node or not. - - Return value: #TRUE if this is a root node -*/ + * dbusmenu_menuitem_get_root: + * @mi: #DbusmenuMenuitem to see whether it's root + * + * This function returns the internal value of whether this is a + * root node or not. + * + * Return value: #TRUE if this is a root node + */ gboolean dbusmenu_menuitem_get_root (DbusmenuMenuitem * mi) { @@ -1340,38 +1411,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 - - 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 - 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. + * 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 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. + * + * Return value: (transfer full): Variant representing @properties */ -void -dbusmenu_menuitem_buildxml (DbusmenuMenuitem * mi, GPtrArray * array) +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 { @@ -1387,15 +1482,15 @@ foreach_helper (gpointer data, gpointer user_data) } /** - dbusmenu_menuitem_foreach: - @mi: The #DbusmenItem to start from - @func: Function to call on every node in the tree - @data: (closure): User data to pass to the function - - This calls the function @func on this menu item and all - of the children of this item. And their children. And - their children. And... you get the point. It will get - called on the whole tree. + * dbusmenu_menuitem_foreach: + * @mi: The #DbusmenItem to start from + * @func: Function to call on every node in the tree + * @data: (closure): User data to pass to the function + * + * This calls the function @func on this menu item and all + * of the children of this item. And their children. And + * their children. And... you get the point. It will get + * called on the whole tree. */ void dbusmenu_menuitem_foreach (DbusmenuMenuitem * mi, void (*func) (DbusmenuMenuitem * mi, gpointer data), gpointer data) @@ -1411,23 +1506,23 @@ dbusmenu_menuitem_foreach (DbusmenuMenuitem * mi, void (*func) (DbusmenuMenuitem } /** - dbusmenu_menuitem_handle_event: - @mi: The #DbusmenuMenuitem to send the signal on. - @name: The name of the signal - @variant: A value that could be set for the event - @timestamp: The timestamp of when the event happened - - This function is called to create an event. It is likely - to be overrided by subclasses. The default menu item - will respond to the activate signal and do: - - Emits the #DbusmenuMenuitem::item-activate signal on this - menu item. Called by server objects when they get the - appropriate DBus signals from the client. - - If you subclass this function you should really think - about calling the parent function unless you have a good - reason not to. + * dbusmenu_menuitem_handle_event: + * @mi: The #DbusmenuMenuitem to send the signal on. + * @name: The name of the signal + * @variant: A value that could be set for the event + * @timestamp: The timestamp of when the event happened + * + * This function is called to create an event. It is likely + * to be overrided by subclasses. The default menu item + * will respond to the activate signal and do: + * + * Emits the #DbusmenuMenuitem::item-activate signal on this + * menu item. Called by server objects when they get the + * appropriate DBus signals from the client. + * + * If you subclass this function you should really think + * about calling the parent function unless you have a good + * reason not to. */ void dbusmenu_menuitem_handle_event (DbusmenuMenuitem * mi, const gchar * name, GVariant * variant, guint timestamp) @@ -1448,16 +1543,16 @@ dbusmenu_menuitem_handle_event (DbusmenuMenuitem * mi, const gchar * name, GVari } /** - dbusmenu_menuitem_send_about_to_show: - @mi: The #DbusmenuMenuitem to send the signal on. - @cb: Callback to call when the call has returned. - @cb_data: (closure): Data to pass to the callback. - - This function is used to send the even that the submenu - of this item is about to be shown. Callers to this event - should delay showing the menu until their callback is - called if possible. -*/ + * dbusmenu_menuitem_send_about_to_show: + * @mi: The #DbusmenuMenuitem to send the signal on. + * @cb: Callback to call when the call has returned. + * @cb_data: (closure): Data to pass to the callback. + * + * This function is used to send the even that the submenu + * of this item is about to be shown. Callers to this event + * should delay showing the menu until their callback is + * called if possible. + */ void dbusmenu_menuitem_send_about_to_show (DbusmenuMenuitem * mi, void (*cb) (DbusmenuMenuitem * mi, gpointer user_data), gpointer cb_data) { @@ -1477,14 +1572,14 @@ dbusmenu_menuitem_send_about_to_show (DbusmenuMenuitem * mi, void (*cb) (Dbusmen } /** - dbusmenu_menuitem_show_to_user: - @mi: #DbusmenuMenuitem to show - @timestamp: The time that the user requested it to be shown - - Signals that this menu item should be shown to the user. If this is - server side the server will then take it and send it over the - bus. -*/ + * dbusmenu_menuitem_show_to_user: + * @mi: #DbusmenuMenuitem to show + * @timestamp: The time that the user requested it to be shown + * + * Signals that this menu item should be shown to the user. If this is + * server side the server will then take it and send it over the + * bus. + */ void dbusmenu_menuitem_show_to_user (DbusmenuMenuitem * mi, guint timestamp) { @@ -1494,3 +1589,26 @@ 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. */ +gboolean +dbusmenu_menuitem_property_is_default (DbusmenuMenuitem * mi, const gchar * property) +{ + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE); + DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi); + + GVariant * currentval = (GVariant *)g_hash_table_lookup(priv->properties, property); + if (currentval != NULL) { + /* If we're storing it locally, then it shouldn't be a default */ + return FALSE; + } + + currentval = dbusmenu_defaults_default_get(priv->defaults, menuitem_get_type(mi), property); + if (currentval != NULL) { + return TRUE; + } + + g_warn_if_reached(); + return FALSE; +} diff --git a/libdbusmenu-glib/menuitem.h b/libdbusmenu-glib/menuitem.h index dee7ee1..35d7ca8 100644 --- a/libdbusmenu-glib/menuitem.h +++ b/libdbusmenu-glib/menuitem.h @@ -112,14 +112,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: @@ -157,8 +158,8 @@ struct _DbusmenuMenuitemClass void (*realized) (void); /* Virtual functions */ - dbusmenu_menuitem_buildxml_slot_t buildxml; - void (*handle_event) (DbusmenuMenuitem * mi, const gchar * name, GVariant * value, guint timestamp); + 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); void (*show_to_user) (DbusmenuMenuitem * mi, guint timestamp, gpointer cb_data); @@ -210,7 +211,7 @@ void dbusmenu_menuitem_set_root (DbusmenuMenuitem * mi, gboolean root); gboolean dbusmenu_menuitem_get_root (DbusmenuMenuitem * mi); void dbusmenu_menuitem_foreach (DbusmenuMenuitem * mi, void (*func) (DbusmenuMenuitem * mi, gpointer data), gpointer data); -void dbusmenu_menuitem_handle_event (DbusmenuMenuitem * mi, const gchar * name, GVariant * value, guint timestamp); +void dbusmenu_menuitem_handle_event (DbusmenuMenuitem * mi, const gchar * name, GVariant * variant, guint timestamp); void dbusmenu_menuitem_send_about_to_show (DbusmenuMenuitem * mi, void (*cb) (DbusmenuMenuitem * mi, gpointer user_data), gpointer cb_data); void dbusmenu_menuitem_show_to_user (DbusmenuMenuitem * mi, guint timestamp); diff --git a/libdbusmenu-glib/server.c b/libdbusmenu-glib/server.c index adb9f91..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; @@ -946,9 +1207,9 @@ bus_get_group_properties (DbusmenuServer * server, GVariant * params, GDBusMetho ret = g_variant_builder_end(&builder); } else { GError * error = NULL; - ret = g_variant_parse(g_variant_type_new("a(ia(sv))"), "[]", NULL, NULL, NULL); + ret = g_variant_parse(g_variant_type_new("a(ia{sv})"), "[]", NULL, NULL, NULL); if (error != NULL) { - g_warning("Unable to parse '[]' as a 'a(ia(sv))': %s", error->message); + g_warning("Unable to parse '[]' as a 'a(ia{sv})': %s", error->message); g_error_free(error); ret = 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); |