diff options
-rw-r--r-- | .bzrignore | 22 | ||||
-rw-r--r-- | configure.ac | 6 | ||||
-rw-r--r-- | docs/libdbusmenu-glib/reference/libdbusmenu-glib-sections.txt | 3 | ||||
-rw-r--r-- | docs/libdbusmenu-gtk/reference/libdbusmenu-gtk-sections.txt | 1 | ||||
-rw-r--r-- | libdbusmenu-glib/client.c | 172 | ||||
-rw-r--r-- | libdbusmenu-glib/dbus-menu.xml | 2 | ||||
-rw-r--r-- | libdbusmenu-glib/menuitem.c | 2 | ||||
-rw-r--r-- | libdbusmenu-glib/menuitem.h | 26 | ||||
-rw-r--r-- | libdbusmenu-gtk/client.c | 190 | ||||
-rw-r--r-- | libdbusmenu-gtk/dbusmenu-gtk-0.4.pc.in | 2 | ||||
-rw-r--r-- | libdbusmenu-gtk/dbusmenu-gtk3-0.4.pc.in | 2 | ||||
-rw-r--r-- | libdbusmenu-gtk/menuitem.c | 7 | ||||
-rw-r--r-- | libdbusmenu-gtk/parser.c | 64 | ||||
-rw-r--r-- | libdbusmenu-gtk/parser.h | 1 | ||||
-rw-r--r-- | tests/Makefile.am | 10 |
15 files changed, 451 insertions, 59 deletions
@@ -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 = \ |