aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzrignore22
-rw-r--r--configure.ac6
-rw-r--r--docs/libdbusmenu-glib/reference/libdbusmenu-glib-sections.txt3
-rw-r--r--docs/libdbusmenu-gtk/reference/libdbusmenu-gtk-sections.txt1
-rw-r--r--libdbusmenu-glib/client.c172
-rw-r--r--libdbusmenu-glib/dbus-menu.xml2
-rw-r--r--libdbusmenu-glib/menuitem.c2
-rw-r--r--libdbusmenu-glib/menuitem.h26
-rw-r--r--libdbusmenu-gtk/client.c190
-rw-r--r--libdbusmenu-gtk/dbusmenu-gtk-0.4.pc.in2
-rw-r--r--libdbusmenu-gtk/dbusmenu-gtk3-0.4.pc.in2
-rw-r--r--libdbusmenu-gtk/menuitem.c7
-rw-r--r--libdbusmenu-gtk/parser.c64
-rw-r--r--libdbusmenu-gtk/parser.h1
-rw-r--r--tests/Makefile.am10
15 files changed, 451 insertions, 59 deletions
diff --git a/.bzrignore b/.bzrignore
index 837b948..ea8b315 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -232,3 +232,25 @@ enum-types.c
enum-types.h
libdbusmenu_glib_la-enum-types.lo
stamp-enum-types
+libdbusmenu-0.3.100
+docs/libdbusmenu-glib/reference/gtkdoc-in-srcdir
+docs/libdbusmenu-glib/reference/html/api-index-deprecated.html
+docs/libdbusmenu-glib/reference/html/libdbusmenu-glib-Types.html
+docs/libdbusmenu-glib/reference/tmpl/types.sgml
+docs/libdbusmenu-gtk/reference/gtkdoc-in-srcdir
+docs/libdbusmenu-gtk/reference/html/annotation-glossary.html
+docs/libdbusmenu-gtk/reference/html/api-index-deprecated.html
+docs/libdbusmenu-gtk/reference/html/libdbusmenu-gtk-DbusmenuGtkSerializableMenuItem.html
+docs/libdbusmenu-gtk/reference/html/libdbusmenu-gtk-parser.html
+docs/libdbusmenu-gtk/reference/tmpl/parser.sgml
+docs/libdbusmenu-gtk/reference/tmpl/parser.sgml.bak
+docs/libdbusmenu-gtk/reference/tmpl/serializablemenuitem.sgml.bak
+libdbusmenu-glib/Dbusmenu-0.4.gir
+libdbusmenu-glib/Dbusmenu-0.4.typelib
+libdbusmenu-glib/Dbusmenu-0.4.vapi
+po/libdbusmenu.pot
+tests/test-gtk-shortcut-python
+tests/test-gtk-submenu
+tests/test-gtk-submenu-client
+tests/test-gtk-submenu-server
+tests/test-json-01.output.json
diff --git a/configure.ac b/configure.ac
index 98f7cc7..8cfa421 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,11 +1,11 @@
-AC_INIT(libdbusmenu, 0.3.100, ted@canonical.com)
+AC_INIT(libdbusmenu, 0.3.101, ted@canonical.com)
AC_COPYRIGHT([Copyright 2009,2010 Canonical])
AC_PREREQ(2.62)
AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE(libdbusmenu, 0.3.100, [-Wno-portability])
+AM_INIT_AUTOMAKE(libdbusmenu, 0.3.101, [-Wno-portability])
AM_MAINTAINER_MODE
@@ -134,7 +134,7 @@ AC_PATH_PROG([XSLT_PROC], [xsltproc])
###########################
LIBDBUSMENU_CURRENT=3
-LIBDBUSMENU_REVISION=8
+LIBDBUSMENU_REVISION=9
LIBDBUSMENU_AGE=0
AC_SUBST(LIBDBUSMENU_CURRENT)
diff --git a/docs/libdbusmenu-glib/reference/libdbusmenu-glib-sections.txt b/docs/libdbusmenu-glib/reference/libdbusmenu-glib-sections.txt
index 84d8257..4d1e50a 100644
--- a/docs/libdbusmenu-glib/reference/libdbusmenu-glib-sections.txt
+++ b/docs/libdbusmenu-glib/reference/libdbusmenu-glib-sections.txt
@@ -70,6 +70,9 @@ DBUSMENU_MENUITEM_SHORTCUT_ALT
DBUSMENU_MENUITEM_SHORTCUT_CONTROL
DBUSMENU_MENUITEM_SHORTCUT_SHIFT
DBUSMENU_MENUITEM_SHORTCUT_SUPER
+DBUSMENU_MENUITEM_EVENT_ACTIVATED
+DBUSMENU_MENUITEM_EVENT_CLOSED
+DBUSMENU_MENUITEM_EVENT_OPENED
DbusmenuMenuitem
dbusmenu_menuitem_about_to_show_cb
dbusmenu_menuitem_buildvariant_slot_t
diff --git a/docs/libdbusmenu-gtk/reference/libdbusmenu-gtk-sections.txt b/docs/libdbusmenu-gtk/reference/libdbusmenu-gtk-sections.txt
index 77101f7..efffeaa 100644
--- a/docs/libdbusmenu-gtk/reference/libdbusmenu-gtk-sections.txt
+++ b/docs/libdbusmenu-gtk/reference/libdbusmenu-gtk-sections.txt
@@ -73,5 +73,6 @@ dbusmenu_menuitem_property_get_shortcut
<SECTION>
<FILE>parser</FILE>
dbusmenu_gtk_parse_menu_structure
+dbusmenu_gtk_parse_get_cached_item
</SECTION>
diff --git a/libdbusmenu-glib/client.c b/libdbusmenu-glib/client.c
index c95b161..2976436 100644
--- a/libdbusmenu-glib/client.c
+++ b/libdbusmenu-glib/client.c
@@ -598,20 +598,27 @@ get_properties_callback (GObject *obj, GAsyncResult * res, gpointer user_data)
}
/* Callback all the folks we can find */
- GVariantIter * iter = g_variant_iter_new(g_variant_get_child_value(params, 0));
- GVariant * child;
+ GVariant * child = g_variant_get_child_value(params, 0);
+ GVariantIter * iter = g_variant_iter_new(child);
+ g_variant_unref(child);
while ((child = g_variant_iter_next_value(iter)) != NULL) {
if (g_strcmp0(g_variant_get_type_string(child), "(ia{sv})") != 0) {
g_warning("Properties return signature is not '(ia{sv})' it is '%s'", g_variant_get_type_string(child));
+ g_variant_unref(child);
continue;
}
- gint id = g_variant_get_int32(g_variant_get_child_value(child, 0));
+ GVariant * idv = g_variant_get_child_value(child, 0);
+ gint id = g_variant_get_int32(idv);
+ g_variant_unref(idv);
+
GVariant * properties = g_variant_get_child_value(child, 1);
properties_listener_t * listener = find_listener(listeners, 0, id);
if (listener == NULL) {
g_warning("Unable to find listener for ID %d", id);
+ g_variant_unref(properties);
+ g_variant_unref(child);
continue;
}
@@ -621,6 +628,8 @@ get_properties_callback (GObject *obj, GAsyncResult * res, gpointer user_data)
} else {
g_warning("Odd, we've already replied to the listener on ID %d", id);
}
+ g_variant_unref(properties);
+ g_variant_unref(child);
}
g_variant_iter_free(iter);
g_variant_unref(params);
@@ -676,7 +685,9 @@ get_properties_idle (gpointer user_data)
GVariant * variant_ids = g_variant_builder_end(&builder);
/* Build up a prop list to pass */
- g_variant_builder_init(&builder, g_variant_type_new("as"));
+ GVariantType * type = g_variant_type_new("as");
+ g_variant_builder_init(&builder, type);
+ g_variant_type_free(type);
/* TODO: need to use delayed property list here */
GVariant * variant_props = g_variant_builder_end(&builder);
@@ -1050,16 +1061,47 @@ menuproxy_build_cb (GObject * object, GAsyncResult * res, gpointer user_data)
/* Check the text direction if available */
GVariant * textdir = g_dbus_proxy_get_cached_property(priv->menuproxy, "TextDirection");
if (textdir != NULL) {
- GVariant * str = textdir;
- if (g_variant_is_of_type(str, G_VARIANT_TYPE_VARIANT)) {
- str = g_variant_get_variant(str);
+ if (g_variant_is_of_type(textdir, G_VARIANT_TYPE_VARIANT)) {
+ GVariant * tmp = g_variant_get_variant(textdir);
+ g_variant_unref(textdir);
+ textdir = tmp;
}
- priv->text_direction = dbusmenu_text_direction_get_value_from_nick(g_variant_get_string(str, NULL));
+ priv->text_direction = dbusmenu_text_direction_get_value_from_nick(g_variant_get_string(textdir, NULL));
+ g_object_notify(G_OBJECT(user_data), DBUSMENU_CLIENT_PROP_TEXT_DIRECTION);
g_variant_unref(textdir);
}
+ /* Check the status if available */
+ GVariant * status = g_dbus_proxy_get_cached_property(priv->menuproxy, "Status");
+ if (status != NULL) {
+ if (g_variant_is_of_type(status, G_VARIANT_TYPE_VARIANT)) {
+ GVariant * tmp = g_variant_get_variant(status);
+ g_variant_unref(status);
+ status = tmp;
+ }
+
+ priv->status = dbusmenu_status_get_value_from_nick(g_variant_get_string(status, NULL));
+ g_object_notify(G_OBJECT(user_data), DBUSMENU_CLIENT_PROP_STATUS);
+
+ g_variant_unref(status);
+ }
+
+ /* Get the icon theme directories if available */
+ GVariant * icon_dirs = g_dbus_proxy_get_cached_property(priv->menuproxy, "IconThemePath");
+ if (icon_dirs != NULL) {
+ if (priv->icon_dirs != NULL) {
+ g_strfreev(priv->icon_dirs);
+ priv->icon_dirs = NULL;
+ }
+
+ priv->icon_dirs = g_variant_dup_strv(icon_dirs, NULL);
+ g_signal_emit(G_OBJECT(client), signals[ICON_THEME_DIRS], 0, priv->icon_dirs, TRUE);
+
+ g_variant_unref(icon_dirs);
+ }
+
/* If we get here, we don't need the DBus proxy */
if (priv->dbusproxy != 0) {
g_bus_unwatch_name(priv->dbusproxy);
@@ -1113,20 +1155,22 @@ menuproxy_prop_changed_cb (GDBusProxy * proxy, GVariant * properties, GStrv inva
g_variant_iter_init(&iters, properties);
while (g_variant_iter_next(&iters, "{sv}", &key, &value)) {
if (g_strcmp0(key, "TextDirection") == 0) {
- GVariant * str = value;
- if (g_variant_is_of_type(str, G_VARIANT_TYPE_VARIANT)) {
- str = g_variant_get_variant(str);
+ if (g_variant_is_of_type(value, G_VARIANT_TYPE_VARIANT)) {
+ GVariant * tmp = g_variant_get_variant(value);
+ g_variant_unref(value);
+ value = tmp;
}
- priv->text_direction = dbusmenu_text_direction_get_value_from_nick(g_variant_get_string(str, NULL));
+ priv->text_direction = dbusmenu_text_direction_get_value_from_nick(g_variant_get_string(value, NULL));
}
if (g_strcmp0(key, "Status") == 0) {
- GVariant * str = value;
- if (g_variant_is_of_type(str, G_VARIANT_TYPE_VARIANT)) {
- str = g_variant_get_variant(str);
+ if (g_variant_is_of_type(value, G_VARIANT_TYPE_VARIANT)) {
+ GVariant * tmp = g_variant_get_variant(value);
+ g_variant_unref(value);
+ value = tmp;
}
- priv->status = dbusmenu_status_get_value_from_nick(g_variant_get_string(str, NULL));
+ priv->status = dbusmenu_status_get_value_from_nick(g_variant_get_string(value, NULL));
}
if (g_strcmp0(key, "IconThemePath") == 0) {
if (priv->icon_dirs != NULL) {
@@ -1195,11 +1239,14 @@ menuproxy_signal_cb (GDBusProxy * proxy, gchar * sender, gchar * signal, GVarian
/* 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 * ritemsv = g_variant_get_child_value(params, 1);
+ g_variant_iter_init(&ritems, ritemsv);
GVariant * ritem;
while ((ritem = g_variant_iter_next_value(&ritems)) != NULL) {
- gint id = g_variant_get_int32(g_variant_get_child_value(ritem, 0));
+ GVariant * idv = g_variant_get_child_value(ritem, 0);
+ gint id = g_variant_get_int32(idv);
+ g_variant_unref(idv);
DbusmenuMenuitem * menuitem = dbusmenu_menuitem_find_id(priv->root, id);
if (menuitem == NULL) {
@@ -1207,23 +1254,33 @@ menuproxy_signal_cb (GDBusProxy * proxy, gchar * sender, gchar * signal, GVarian
}
GVariantIter properties;
- g_variant_iter_init(&properties, g_variant_get_child_value(ritem, 1));
+ GVariant * propv = g_variant_get_child_value(ritem, 1);
+ g_variant_iter_init(&properties, propv);
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);
+ g_free(property);
}
+ g_variant_unref(ritem);
+ g_variant_unref(propv);
}
+ g_variant_unref(ritemsv);
GVariantIter items;
- g_variant_iter_init(&items, g_variant_get_child_value(params, 0));
+ GVariant * itemsv = g_variant_get_child_value(params, 0);
+ g_variant_iter_init(&items, itemsv);
GVariant * item;
while ((item = g_variant_iter_next_value(&items)) != NULL) {
- gint id = g_variant_get_int32(g_variant_get_child_value(item, 0));
+ GVariant * idv = g_variant_get_child_value(item, 0);
+ gint id = g_variant_get_int32(idv);
+ g_variant_unref(idv);
+
GVariantIter properties;
- g_variant_iter_init(&properties, g_variant_get_child_value(item, 1));
+ GVariant * propv = g_variant_get_child_value(item, 1);
+ g_variant_iter_init(&properties, propv);
gchar * property;
GVariant * value;
@@ -1232,14 +1289,21 @@ menuproxy_signal_cb (GDBusProxy * proxy, gchar * sender, gchar * signal, GVarian
if (G_LIKELY(g_variant_is_of_type(value, G_VARIANT_TYPE_VARIANT))) {
/* Unboxing if needed */
internalvalue = g_variant_get_variant(value);
+ g_variant_unref(value);
}
id_prop_update(proxy, id, property, internalvalue, client);
+ g_variant_unref(internalvalue);
}
+ g_variant_unref(propv);
+ g_variant_unref(item);
}
+ g_variant_unref(itemsv);
} else if (g_strcmp0(signal, "ItemPropertyUpdated") == 0) {
gint id; gchar * property; GVariant * value;
g_variant_get(params, "(isv)", &id, &property, &value);
id_prop_update(proxy, id, property, value, client);
+ g_free(property);
+ g_variant_unref(value);
} else if (g_strcmp0(signal, "ItemUpdated") == 0) {
gint id;
g_variant_get(params, "(i)", &id);
@@ -1304,12 +1368,11 @@ menuitem_get_properties_replace_cb (GVariant * properties, GError * error, gpoin
have_error = TRUE;
}
- GList * current_props = NULL;
+ GList * current_props = dbusmenu_menuitem_properties_list(DBUSMENU_MENUITEM(data));
+ GList * tmp = NULL;
- for (current_props = dbusmenu_menuitem_properties_list(DBUSMENU_MENUITEM(data));
- current_props != NULL && have_error == FALSE;
- current_props = g_list_next(current_props)) {
- dbusmenu_menuitem_property_remove(DBUSMENU_MENUITEM(data), (const gchar *)current_props->data);
+ for (tmp = current_props; tmp != NULL && have_error == FALSE; tmp = g_list_next(tmp)) {
+ dbusmenu_menuitem_property_remove(DBUSMENU_MENUITEM(data), (const gchar *)tmp->data);
}
g_list_free(current_props);
@@ -1564,7 +1627,9 @@ parse_layout_xml(DbusmenuClient * client, GVariant * layout, DbusmenuMenuitem *
}
/* First verify and figure out what we've got */
- gint id = g_variant_get_int32(g_variant_get_child_value(layout, 0));
+ GVariant * idv = g_variant_get_child_value(layout, 0);
+ gint id = g_variant_get_int32(idv);
+ g_variant_unref(idv);
if (id < 0) {
return NULL;
}
@@ -1577,8 +1642,10 @@ parse_layout_xml(DbusmenuClient * client, GVariant * layout, DbusmenuMenuitem *
/* Some variables */
GVariantIter children;
- g_variant_iter_init(&children, g_variant_get_child_value(layout, 2));
- GVariant * child;
+ GVariant * childrenv;
+
+ childrenv = g_variant_get_child_value(layout, 2);
+ g_variant_iter_init(&children, childrenv);
guint position = 0;
GList * oldchildren = g_list_copy(dbusmenu_menuitem_get_children(item));
@@ -1586,16 +1653,22 @@ parse_layout_xml(DbusmenuClient * client, GVariant * layout, DbusmenuMenuitem *
/* Go through all the XML Nodes and make sure that we have menuitems
to cover those XML nodes. */
+ GVariant * child;
while ((child = g_variant_iter_next_value(&children)) != NULL) {
/* g_debug("Looking at child: %d", position); */
if (g_variant_is_of_type(child, G_VARIANT_TYPE_VARIANT)) {
- child = g_variant_get_variant(child);
+ GVariant * tmp = g_variant_get_variant(child);
+ g_variant_unref(child);
+ child = tmp;
}
- gint childid = g_variant_get_int32(g_variant_get_child_value(child, 0));
+ GVariant * childidv = g_variant_get_child_value(child, 0);
+ gint childid = g_variant_get_int32(childidv);
+ g_variant_unref(childidv);
if (childid < 0) {
/* Don't increment the position when there isn't a valid
node in the XML tree. It's probably a comment. */
+ g_variant_unref(child);
continue;
}
DbusmenuMenuitem * childmi = NULL;
@@ -1635,10 +1708,12 @@ parse_layout_xml(DbusmenuClient * client, GVariant * layout, DbusmenuMenuitem *
GVariantIter iter;
gchar * prop;
GVariant * value;
+ GVariant * child_props;
/* Set the type first as it can manage the behavior of
all other properties. */
- g_variant_iter_init(&iter, g_variant_get_child_value(child, 1));
+ child_props = g_variant_get_child_value(child, 1);
+ g_variant_iter_init(&iter, child_props);
while (g_variant_iter_next(&iter, "{sv}", &prop, &value)) {
if (g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_TYPE) == 0) {
dbusmenu_menuitem_property_set_variant(childmi, prop, value);
@@ -1648,15 +1723,17 @@ parse_layout_xml(DbusmenuClient * client, GVariant * layout, DbusmenuMenuitem *
}
/* Now go through and do all the properties. */
- g_variant_iter_init(&iter, g_variant_get_child_value(child, 1));
+ g_variant_iter_init(&iter, child_props);
while (g_variant_iter_next(&iter, "{sv}", &prop, &value)) {
dbusmenu_menuitem_property_set_variant(childmi, prop, value);
g_free(prop);
g_variant_unref(value);
}
+ g_variant_unref(child_props);
}
position++;
+ g_variant_unref(child);
}
/* Remove any children that are no longer used by this version of
@@ -1679,19 +1756,24 @@ parse_layout_xml(DbusmenuClient * client, GVariant * layout, DbusmenuMenuitem *
}
/* now it's time to recurse down the tree. */
- g_variant_iter_init(&children, g_variant_get_child_value(layout, 2));
+ g_variant_iter_init(&children, childrenv);
child = g_variant_iter_next_value(&children);
GList * childmis = dbusmenu_menuitem_get_children(item);
while (child != NULL && childmis != NULL) {
if (g_variant_is_of_type(child, G_VARIANT_TYPE_VARIANT)) {
- child = g_variant_get_variant(child);
+ GVariant * tmp = g_variant_get_variant(child);
+ g_variant_unref(child);
+ child = tmp;
}
- gint xmlid = g_variant_get_int32(g_variant_get_child_value(child, 0));
+ GVariant * xmlidv = g_variant_get_child_value(child, 0);
+ gint xmlid = g_variant_get_int32(xmlidv);
+ g_variant_unref(xmlidv);
/* 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) {
+ g_variant_unref(child);
child = g_variant_iter_next_value(&children);
continue;
}
@@ -1703,10 +1785,13 @@ parse_layout_xml(DbusmenuClient * client, GVariant * layout, DbusmenuMenuitem *
parse_layout_xml(client, child, DBUSMENU_MENUITEM(childmis->data), item, proxy);
+ g_variant_unref(child);
child = g_variant_iter_next_value(&children);
childmis = g_list_next(childmis);
}
+ g_variant_unref(childrenv);
+
if (child != NULL) {
g_warning("Sync failed, now we've got extra layout nodes.");
}
@@ -1771,6 +1856,7 @@ update_layout_cb (GObject * proxy, GAsyncResult * res, gpointer data)
GError * error = NULL;
GVariant * params = NULL;
+ GVariant * layout = NULL;
params = g_dbus_proxy_call_finish(G_DBUS_PROXY(proxy), res, &error);
@@ -1780,8 +1866,11 @@ update_layout_cb (GObject * proxy, GAsyncResult * res, gpointer data)
goto out;
}
- guint rev = g_variant_get_uint32(g_variant_get_child_value(params, 0));
- GVariant * layout = g_variant_get_child_value(params, 1);
+ GVariant * revv = g_variant_get_child_value(params, 0);
+ guint rev = g_variant_get_uint32(revv);
+ g_variant_unref(revv);
+
+ layout = g_variant_get_child_value(params, 1);
guint parseable = parse_layout(client, layout);
@@ -1809,6 +1898,10 @@ out:
priv->layoutcall = NULL;
}
+ if (layout != NULL) {
+ g_variant_unref(layout);
+ }
+
if (params != NULL) {
g_variant_unref(params);
}
@@ -1823,6 +1916,7 @@ static void
update_layout (DbusmenuClient * client)
{
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ g_return_if_fail(priv->layout_props != NULL);
if (priv->menuproxy == NULL) {
return;
diff --git a/libdbusmenu-glib/dbus-menu.xml b/libdbusmenu-glib/dbus-menu.xml
index efb55d4..a36c148 100644
--- a/libdbusmenu-glib/dbus-menu.xml
+++ b/libdbusmenu-glib/dbus-menu.xml
@@ -294,6 +294,8 @@ License version 3 and version 2.1 along with this program. If not, see
@li "clicked"
@li "hovered"
+ @li "opened"
+ @li "closed"
Vendor specific events can be added by prefixing them with "x-<vendor>-"
]]></dox:d>
diff --git a/libdbusmenu-glib/menuitem.c b/libdbusmenu-glib/menuitem.c
index 2e5a345..70b5fd2 100644
--- a/libdbusmenu-glib/menuitem.c
+++ b/libdbusmenu-glib/menuitem.c
@@ -430,7 +430,7 @@ get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
static void
handle_event (DbusmenuMenuitem * mi, const gchar * name, GVariant * value, guint timestamp)
{
- if (g_strcmp0(name, "clicked") == 0) {
+ if (g_strcmp0(name, DBUSMENU_MENUITEM_EVENT_ACTIVATED) == 0) {
g_signal_emit(G_OBJECT(mi), signals[ITEM_ACTIVATED], 0, timestamp, TRUE);
}
diff --git a/libdbusmenu-glib/menuitem.h b/libdbusmenu-glib/menuitem.h
index f4eb989..afd1d4e 100644
--- a/libdbusmenu-glib/menuitem.h
+++ b/libdbusmenu-glib/menuitem.h
@@ -267,6 +267,32 @@ G_BEGIN_DECLS
*/
#define DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU "submenu"
+/**
+ * DBUSMENU_MENUITEM_EVENT_ACTIVATED:
+ *
+ * String for the event identifier when a menu item is clicked
+ * on by the user.
+ */
+#define DBUSMENU_MENUITEM_EVENT_ACTIVATED "clicked"
+
+/**
+ * DBUSMENU_MENUITEM_EVENT_OPENED:
+ *
+ * String for the event identifier when a menu is opened and
+ * displayed to the user. Only valid for items that contain
+ * submenus.
+ */
+#define DBUSMENU_MENUITEM_EVENT_OPENED "opened"
+
+/**
+ * DBUSMENU_MENUITEM_EVENT_CLOSED:
+ *
+ * String for the event identifier when a menu is closed and
+ * displayed to the user. Only valid for items that contain
+ * submenus.
+ */
+#define DBUSMENU_MENUITEM_EVENT_CLOSED "closed"
+
typedef struct _DbusmenuMenuitemPrivate DbusmenuMenuitemPrivate;
/**
diff --git a/libdbusmenu-gtk/client.c b/libdbusmenu-gtk/client.c
index 50978ff..497808b 100644
--- a/libdbusmenu-gtk/client.c
+++ b/libdbusmenu-gtk/client.c
@@ -38,9 +38,12 @@ License version 3 and version 2.1 along with this program. If not, see
/* Private */
struct _DbusmenuGtkClientPrivate {
+ GStrv old_themedirs;
GtkAccelGroup * agroup;
};
+GHashTable * theme_dir_db = NULL;
+
#define DBUSMENU_GTKCLIENT_GET_PRIVATE(o) (DBUSMENU_GTKCLIENT(o)->priv)
#define USE_FALLBACK_PROP "use-fallback"
@@ -54,6 +57,8 @@ static void new_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint po
static void delete_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, DbusmenuGtkClient * gtkclient);
static void move_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint new, guint old, DbusmenuGtkClient * gtkclient);
static void item_activate (DbusmenuClient * client, DbusmenuMenuitem * mi, guint timestamp, gpointer userdata);
+static void theme_dir_changed (DbusmenuClient * client, GStrv theme_dirs, gpointer userdata);
+static void remove_theme_dirs (GtkIconTheme * theme, GStrv dirs);
static gboolean new_item_normal (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data);
static gboolean new_item_seperator (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data);
@@ -89,6 +94,23 @@ dbusmenu_gtkclient_init (DbusmenuGtkClient *self)
DbusmenuGtkClientPrivate * priv = DBUSMENU_GTKCLIENT_GET_PRIVATE(self);
priv->agroup = NULL;
+ priv->old_themedirs = NULL;
+
+ /* We either build the theme db or we get a reference
+ to it. This way when all clients die the hashtable
+ will be free'd as well. */
+ if (theme_dir_db == NULL) {
+ theme_dir_db = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+
+ /* NOTE: We're adding an extra ref here because there
+ is no way to clear the pointer when the hash table
+ dies, so it's safer to keep the hash table around
+ forever than not know if it's free'd or not. Patch
+ submitted to GLib. */
+ g_hash_table_ref(theme_dir_db);
+ } else {
+ g_hash_table_ref(theme_dir_db);
+ }
dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(self), DBUSMENU_CLIENT_TYPES_DEFAULT, new_item_normal);
dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(self), DBUSMENU_CLIENT_TYPES_SEPARATOR, new_item_seperator);
@@ -96,6 +118,9 @@ dbusmenu_gtkclient_init (DbusmenuGtkClient *self)
/* TODO: I think these can be handled in the class... */
g_signal_connect(G_OBJECT(self), DBUSMENU_CLIENT_SIGNAL_NEW_MENUITEM, G_CALLBACK(new_menuitem), NULL);
g_signal_connect(G_OBJECT(self), DBUSMENU_CLIENT_SIGNAL_ITEM_ACTIVATE, G_CALLBACK(item_activate), NULL);
+ g_signal_connect(G_OBJECT(self), DBUSMENU_CLIENT_SIGNAL_ICON_THEME_DIRS_CHANGED, G_CALLBACK(theme_dir_changed), NULL);
+
+ theme_dir_changed(DBUSMENU_CLIENT(self), dbusmenu_client_get_icon_paths(DBUSMENU_CLIENT(self)), NULL);
return;
}
@@ -111,6 +136,18 @@ dbusmenu_gtkclient_dispose (GObject *object)
priv->agroup = NULL;
}
+ if (priv->old_themedirs) {
+ remove_theme_dirs(gtk_icon_theme_get_default(), priv->old_themedirs);
+ g_strfreev(priv->old_themedirs);
+ priv->old_themedirs = NULL;
+ }
+
+ if (theme_dir_db != NULL) {
+ g_hash_table_unref(theme_dir_db);
+ } else {
+ g_assert_not_reached();
+ }
+
G_OBJECT_CLASS (dbusmenu_gtkclient_parent_class)->dispose (object);
return;
}
@@ -124,6 +161,141 @@ dbusmenu_gtkclient_finalize (GObject *object)
return;
}
+/* Add a theme directory to the table and the theme's list of available
+ themes to use. */
+static void
+theme_dir_ref (GtkIconTheme * theme, GHashTable * db, const gchar * dir)
+{
+ g_return_if_fail(db != NULL);
+ g_return_if_fail(theme != NULL);
+ g_return_if_fail(dir != NULL);
+
+ int count = 0;
+ if ((count = GPOINTER_TO_INT(g_hash_table_lookup(db, dir))) != 0) {
+ /* It exists so what we need to do is increase the ref
+ count of this dir. */
+ count++;
+ } else {
+ /* It doesn't exist, so we need to add it to the table
+ and to the search path. */
+ gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), dir);
+ g_debug("\tAppending search path: %s", dir);
+ count = 1;
+ }
+
+ g_hash_table_insert(db, g_strdup(dir), GINT_TO_POINTER(count));
+
+ return;
+}
+
+/* Unreference the theme directory, and if it's count goes to zero then
+ we need to remove it from the search path. */
+static void
+theme_dir_unref (GtkIconTheme * theme, GHashTable * db, const gchar * dir)
+{
+ g_return_if_fail(db != NULL);
+ g_return_if_fail(theme != NULL);
+ g_return_if_fail(dir != NULL);
+
+ /* Grab the count for this dir */
+ int count = GPOINTER_TO_INT(g_hash_table_lookup(db, dir));
+
+ /* Is this a simple deprecation, if so, we can just lower the
+ number and move on. */
+ if (count > 1) {
+ count--;
+ g_hash_table_insert(db, g_strdup(dir), GINT_TO_POINTER(count));
+ return;
+ }
+
+ /* Try to remove it from the hash table, this makes sure
+ that it existed */
+ if (!g_hash_table_remove(db, dir)) {
+ g_warning("Unref'd a directory that wasn't in the theme dir hash table.");
+ return;
+ }
+
+ gchar ** paths;
+ gint path_count;
+
+ gtk_icon_theme_get_search_path(theme, &paths, &path_count);
+
+ gint i;
+ gboolean found = FALSE;
+ for (i = 0; i < path_count; i++) {
+ if (found) {
+ /* If we've already found the right entry */
+ paths[i - 1] = paths[i];
+ } else {
+ /* We're still looking, is this the one? */
+ if (!g_strcmp0(paths[i], dir)) {
+ found = TRUE;
+ /* We're freeing this here as it won't be captured by the
+ g_strfreev() below as it's out of the array. */
+ g_free(paths[i]);
+ }
+ }
+ }
+
+ /* If we found one we need to reset the path to
+ accomidate the changes */
+ if (found) {
+ paths[path_count - 1] = NULL; /* Clear the last one */
+ gtk_icon_theme_set_search_path(theme, (const gchar **)paths, path_count - 1);
+ }
+
+ g_strfreev(paths);
+
+ return;
+}
+
+/* Unregister this list of theme directories */
+static void
+remove_theme_dirs (GtkIconTheme * theme, GStrv dirs)
+{
+ g_return_if_fail(GTK_ICON_THEME(theme));
+ g_return_if_fail(dirs != NULL);
+
+ int dir;
+
+ for (dir = 0; dirs[dir] != NULL; dir++) {
+ theme_dir_unref(theme, theme_dir_db, dirs[dir]);
+ }
+
+ return;
+}
+
+/* Called when the theme directories are changed by the
+ server part of things. */
+static void
+theme_dir_changed (DbusmenuClient * client, GStrv theme_dirs, gpointer userdata)
+{
+ DbusmenuGtkClientPrivate * priv = DBUSMENU_GTKCLIENT_GET_PRIVATE(client);
+ GtkIconTheme * theme = gtk_icon_theme_get_default();
+
+ /* Ref the new directories */
+ if (theme_dirs != NULL) {
+ int dir;
+ for (dir = 0; theme_dirs[dir] != NULL; dir++) {
+ theme_dir_ref(theme, theme_dir_db, theme_dirs[dir]);
+ }
+ }
+
+ /* Unref the old ones */
+ if (priv->old_themedirs) {
+ remove_theme_dirs(theme, priv->old_themedirs);
+ g_strfreev(priv->old_themedirs);
+ priv->old_themedirs = NULL;
+ }
+
+ /* Copy the new to the old */
+ if (theme_dirs != NULL) {
+ priv->old_themedirs = g_strdupv(theme_dirs);
+ }
+
+ return;
+}
+
/* Structure for passing data to swap_agroup */
typedef struct _swap_agroup_t swap_agroup_t;
struct _swap_agroup_t {
@@ -286,7 +458,7 @@ menu_pressed_cb (GtkMenuItem * gmi, DbusmenuMenuitem * mi)
{
if (gtk_menu_item_get_submenu(gmi) == NULL) {
GVariant * variant = g_variant_new("i", 0);
- dbusmenu_menuitem_handle_event(mi, "clicked", variant, gtk_get_current_event_time());
+ dbusmenu_menuitem_handle_event(mi, DBUSMENU_MENUITEM_EVENT_ACTIVATED, variant, gtk_get_current_event_time());
} else {
/* TODO: We need to stop the display of the submenu
until this callback returns. */
@@ -295,6 +467,15 @@ menu_pressed_cb (GtkMenuItem * gmi, DbusmenuMenuitem * mi)
return TRUE;
}
+static void
+submenu_notify_visible_cb (GtkWidget * menu, GParamSpec * pspec, DbusmenuMenuitem * mi)
+{
+ if (gtk_widget_get_visible (menu))
+ dbusmenu_menuitem_handle_event(mi, DBUSMENU_MENUITEM_EVENT_OPENED, NULL, gtk_get_current_event_time());
+ else
+ dbusmenu_menuitem_handle_event(mi, DBUSMENU_MENUITEM_EVENT_CLOSED, NULL, gtk_get_current_event_time());
+}
+
/* Process the visible property */
static void
process_visible (DbusmenuMenuitem * mi, GtkMenuItem * gmi, GVariant * value)
@@ -568,11 +749,12 @@ new_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position, Dbus
GtkMenuItem * parent = dbusmenu_gtkclient_menuitem_get(gtkclient, mi);
gtk_menu_item_set_submenu(parent, GTK_WIDGET(menu));
+
+ g_signal_connect(menu, "notify::visible", G_CALLBACK(submenu_notify_visible_cb), mi);
}
GtkMenuItem * childmi = dbusmenu_gtkclient_menuitem_get(gtkclient, child);
gtk_menu_shell_insert(GTK_MENU_SHELL(menu), GTK_WIDGET(childmi), position);
- gtk_widget_show(GTK_WIDGET(menu));
return;
}
@@ -697,6 +879,7 @@ new_item_normal (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, Dbusmenu
if (gmi != NULL) {
dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, gmi, parent);
+ g_object_unref(gmi);
} else {
return FALSE;
}
@@ -870,6 +1053,9 @@ image_property_handle (DbusmenuMenuitem * item, const gchar * property, GVariant
} else {
gtk_image_set_from_pixbuf(GTK_IMAGE(gtkimage), image);
}
+ if (image) {
+ g_object_unref(image);
+ }
}
}
diff --git a/libdbusmenu-gtk/dbusmenu-gtk-0.4.pc.in b/libdbusmenu-gtk/dbusmenu-gtk-0.4.pc.in
index 8784556..9a1b460 100644
--- a/libdbusmenu-gtk/dbusmenu-gtk-0.4.pc.in
+++ b/libdbusmenu-gtk/dbusmenu-gtk-0.4.pc.in
@@ -5,7 +5,7 @@ bindir=@bindir@
includedir=@includedir@
Cflags: -I${includedir}/libdbusmenu-0.4
-Requires: dbusmenu-glib-0.4
+Requires: dbusmenu-glib-0.4 gdk-pixbuf-2.0 gtk+-2.0
Libs: -L${libdir} -ldbusmenu-gtk
Name: libdbusmenu-gtk
diff --git a/libdbusmenu-gtk/dbusmenu-gtk3-0.4.pc.in b/libdbusmenu-gtk/dbusmenu-gtk3-0.4.pc.in
index 804b13e..c297db3 100644
--- a/libdbusmenu-gtk/dbusmenu-gtk3-0.4.pc.in
+++ b/libdbusmenu-gtk/dbusmenu-gtk3-0.4.pc.in
@@ -5,7 +5,7 @@ bindir=@bindir@
includedir=@includedir@
Cflags: -I${includedir}/libdbusmenu-0.4
-Requires: dbusmenu-glib-0.4
+Requires: dbusmenu-glib-0.4 gdk-pixbuf-2.0 gtk+-3.0
Libs: -L${libdir} -ldbusmenu-gtk3
Name: libdbusmenu-gtk3
diff --git a/libdbusmenu-gtk/menuitem.c b/libdbusmenu-gtk/menuitem.c
index 370dbf2..b3358fe 100644
--- a/libdbusmenu-gtk/menuitem.c
+++ b/libdbusmenu-gtk/menuitem.c
@@ -119,12 +119,7 @@ dbusmenu_menuitem_property_get_image (DbusmenuMenuitem * menuitem, const gchar *
g_error_free(error);
}
- error = NULL;
- g_input_stream_close(input, NULL, &error);
- if (error != NULL) {
- g_warning("Unable to close input stream: %s", error->message);
- g_error_free(error);
- }
+ g_object_unref(input);
g_free(icondata);
return icon;
diff --git a/libdbusmenu-gtk/parser.c b/libdbusmenu-gtk/parser.c
index a0bb5c9..9d93a1e 100644
--- a/libdbusmenu-gtk/parser.c
+++ b/libdbusmenu-gtk/parser.c
@@ -122,6 +122,27 @@ dbusmenu_gtk_parse_menu_structure (GtkWidget * widget)
return returnval;
}
+/**
+ * dbusmenu_gtk_parse_get_cached_item:
+ * @widget: A #GtkMenuItem that may have a cached #DbusmenuMenuitem from the parser
+ *
+ * The Dbusmenu GTK parser adds cached items on the various
+ * menu items throughout the tree. Sometimes it can be useful
+ * to get that cached item to use directly. This function
+ * will retrieve it for you.
+ *
+ * Return value: (transfer none): A pointer to the cached item
+ * or NULL if it isn't there.
+ */
+DbusmenuMenuitem *
+dbusmenu_gtk_parse_get_cached_item (GtkWidget * widget)
+{
+ if (!GTK_IS_MENU_ITEM(widget)) {
+ return NULL;
+ }
+ return DBUSMENU_MENUITEM(g_object_get_data(G_OBJECT(widget), CACHED_MENUITEM));
+}
+
static void
parse_data_free (gpointer data)
{
@@ -370,14 +391,46 @@ sanitize_label_text (const gchar * label)
which we don't. */
gchar * sanitized = NULL;
GError * error = NULL;
+
+ if (label == NULL) {
+ return NULL;
+ }
+
if (pango_parse_markup (label, -1, 0, NULL, &sanitized, NULL, &error)) {
return sanitized;
}
- else {
+
+ if (error != NULL) {
g_warning ("Could not parse '%s': %s", label, error->message);
g_error_free (error);
- return g_strdup (label);
}
+ return g_strdup (label);
+}
+
+static gchar *
+sanitize_label (GtkLabel * label)
+{
+ gchar * text;
+
+ if (gtk_label_get_use_markup (label)) {
+ text = sanitize_label_text (gtk_label_get_label (label));
+ }
+ else {
+ text = g_strdup (gtk_label_get_label (label));
+ }
+
+ if (!gtk_label_get_use_underline (label)) {
+ /* Insert extra underscores */
+ GRegex * regex = g_regex_new ("_", 0, 0, NULL);
+ gchar * escaped = g_regex_replace_literal (regex, text, -1, 0, "__", 0, NULL);
+
+ g_regex_unref (regex);
+ g_free (text);
+
+ text = escaped;
+ }
+
+ return text;
}
/* Turn a widget into a dbusmenu item depending on the type of GTK
@@ -463,7 +516,7 @@ construct_dbusmenu_for_widget (GtkWidget * widget)
{
// Sometimes, an app will directly find and modify the label
// (like empathy), so watch the label especially for that.
- gchar * text = sanitize_label_text (gtk_label_get_label (GTK_LABEL (label)));
+ gchar * text = sanitize_label (GTK_LABEL (label));
dbusmenu_menuitem_property_set (mi, "label", text);
g_free (text);
@@ -602,6 +655,9 @@ update_icon (DbusmenuMenuitem *menuitem, GtkImage *image)
if (image != NULL && should_show_image (image)) {
switch (gtk_image_get_storage_type (image)) {
+ case GTK_IMAGE_EMPTY:
+ break;
+
case GTK_IMAGE_PIXBUF:
pixbuf = g_object_ref (gtk_image_get_pixbuf (image));
break;
@@ -712,7 +768,7 @@ label_notify_cb (GtkWidget *widget,
if (pspec->name == g_intern_static_string ("label"))
{
- gchar * text = sanitize_label_text (gtk_label_get_label (GTK_LABEL (widget)));
+ gchar * text = sanitize_label (GTK_LABEL (widget));
dbusmenu_menuitem_property_set (child,
DBUSMENU_MENUITEM_PROP_LABEL,
text);
diff --git a/libdbusmenu-gtk/parser.h b/libdbusmenu-gtk/parser.h
index 8187a8e..97fa9c6 100644
--- a/libdbusmenu-gtk/parser.h
+++ b/libdbusmenu-gtk/parser.h
@@ -35,6 +35,7 @@ License version 3 and version 2.1 along with this program. If not, see
G_BEGIN_DECLS
DbusmenuMenuitem * dbusmenu_gtk_parse_menu_structure (GtkWidget * widget);
+DbusmenuMenuitem * dbusmenu_gtk_parse_get_cached_item (GtkWidget * widget);
/**
SECTION:parser
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 61b3e69..a2c0716 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -17,10 +17,16 @@ TESTS = \
test-gtk-objects-test \
test-gtk-label \
test-gtk-shortcut \
- test-gtk-shortcut-python \
test-gtk-reorder \
test-gtk-submenu \
- test-gtk-parser-test \
+ test-gtk-parser-test
+
+# The Python test only work on the system copy of
+# dbusmenu, so while they can be usefule they're not
+# good tests of what you're currently building. Handy
+# to check GI support though. FIXME!
+PYTHON_TESTS = \
+ test-gtk-shortcut-python \
test-glib-simple-items.py
check_PROGRAMS = \