diff options
39 files changed, 1270 insertions, 719 deletions
@@ -254,3 +254,6 @@ tests/test-gtk-submenu tests/test-gtk-submenu-client tests/test-gtk-submenu-server tests/test-json-01.output.json +genericmenuitem-enum-types.c +genericmenuitem-enum-types.h +libdbusmenu_gtk3_la-genericmenuitem-enum-types.lo diff --git a/Makefile.am b/Makefile.am index c2c0980..6b11b31 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,13 +1,23 @@ +ACLOCAL_AMFLAGS = -I m4 + EXTRA_DIST = \ COPYING.2.1 \ COPYING-GPL3 \ m4/introspection.m4 +if WANT_TESTS +TESTS_SUBDIR = tests +endif + +if WANT_LIBDBUSMENUGTK +LIBDBUSMENUGTK_SUBDIR = libdbusmenu-gtk +endif + SUBDIRS = \ libdbusmenu-glib \ - libdbusmenu-gtk \ + $(LIBDBUSMENUGTK_SUBDIR) \ tools \ - tests \ + $(TESTS_SUBDIR) \ docs \ po diff --git a/configure.ac b/configure.ac index a400680..6276327 100644 --- a/configure.ac +++ b/configure.ac @@ -1,11 +1,11 @@ -AC_INIT(libdbusmenu, 0.4.0, ted@canonical.com) +AC_INIT(libdbusmenu, 0.4.94, ted@canonical.com) AC_COPYRIGHT([Copyright 2009,2010 Canonical]) AC_PREREQ(2.62) AM_CONFIG_HEADER(config.h) -AM_INIT_AUTOMAKE(libdbusmenu, 0.4.0, [-Wno-portability]) +AM_INIT_AUTOMAKE(libdbusmenu, 0.4.94, [-Wno-portability]) AM_MAINTAINER_MODE @@ -58,27 +58,37 @@ AC_SUBST(DBUSMENUGLIB_LIBS) GTK_REQUIRED_VERSION=2.16 GTK3_REQUIRED_VERSION=2.91 +AC_ARG_ENABLE([gtk], + AC_HELP_STRING([--disable-gtk], [Disable libdbusmenu-gtk library]), + [enable_gtk=$enableval], [enable_gtk=auto]) +AM_CONDITIONAL([WANT_LIBDBUSMENUGTK], [test "x$enable_gtk" != "xno"]) + AC_ARG_WITH([gtk], [AS_HELP_STRING([--with-gtk], - [Which version of gtk to use @<:@default=2@:>@])], + [Which version of gtk to use @<:@default=3@:>@])], [], - [with_gtk=2]) + [with_gtk=3]) +AM_CONDITIONAL([USE_GTK3], [test "x$with_gtk" = "x3"]) + +AS_IF([test "x$enable_gtk" != "xno"],[ AS_IF([test "x$with_gtk" = x3], [PKG_CHECK_MODULES(DBUSMENUGTK, gtk+-3.0 >= $GTK3_REQUIRED_VERSION - glib-2.0 >= $GLIB_REQUIRED_VERSION) - AC_SUBST(DBUSMENUGTK_CFLAGS) - AC_SUBST(DBUSMENUGTK_LIBS) + glib-2.0 >= $GLIB_REQUIRED_VERSION, + [have_gtk=yes] +) AC_DEFINE(HAVE_GTK3, 1, [whether gtk3 is available]) ], [test "x$with_gtk" = x2], [PKG_CHECK_MODULES(DBUSMENUGTK, gtk+-2.0 >= $GTK_REQUIRED_VERSION - glib-2.0 >= $GLIB_REQUIRED_VERSION) - AC_SUBST(DBUSMENUGTK_CFLAGS) - AC_SUBST(DBUSMENUGTK_LIBS) + glib-2.0 >= $GLIB_REQUIRED_VERSION, + [have_gtk=yes] +) ], [AC_MSG_FAILURE([Value for --with-gtk was neither 2 nor 3])] ) -AM_CONDITIONAL(USE_GTK3, [test "x$with_gtk" = x3]) +]) +AC_SUBST(DBUSMENUGTK_CFLAGS) +AC_SUBST(DBUSMENUGTK_LIBS) ########################### # Dependencies - dumper @@ -86,8 +96,17 @@ AM_CONDITIONAL(USE_GTK3, [test "x$with_gtk" = x3]) X11_REQUIRED_VERSION=1.3 +AC_ARG_ENABLE([dumper], + AC_HELP_STRING([--disable-dumper], [Disable dbusmenudumper tool]), + [enable_dumper=$enableval], [enable_dumper=auto]) +AM_CONDITIONAL([WANT_DBUSMENUDUMPER], [test "x$enable_dumper" != "xno"]) + +AS_IF([test "x$enable_dumper" != "xno"],[ PKG_CHECK_MODULES(DBUSMENUDUMPER, gtk+-2.0 >= $GTK_REQUIRED_VERSION - x11 >= $X11_REQUIRED_VERSION) + x11 >= $X11_REQUIRED_VERSION, + [have_dumper=yes] +) +]) AC_SUBST(DBUSMENUDUMPER_CFLAGS) AC_SUBST(DBUSMENUDUMPER_LIBS) @@ -96,11 +115,20 @@ AC_SUBST(DBUSMENUDUMPER_LIBS) # Dependencies - Testing ########################### -JSON_GLIB_REQUIRED_VERSION=0.6.0 +JSON_GLIB_REQUIRED_VERSION=0.13.4 GIO_UNIX_REQUIRED_VERSION=2.24 +AC_ARG_ENABLE([tests], + AC_HELP_STRING([--disable-tests], [Disable tests]), + [enable_tests=$enableval], [enable_tests=auto]) +AM_CONDITIONAL([WANT_TESTS], [test "x$enable_tests" != "xno"]) + +AS_IF([test "x$enable_tests" != "xno"],[ PKG_CHECK_MODULES(DBUSMENUTESTS, json-glib-1.0 >= $JSON_GLIB_REQUIRED_VERSION - gio-unix-2.0 >= $GIO_UNIX_REQUIRED_VERSION) + gio-unix-2.0 >= $GIO_UNIX_REQUIRED_VERSION, + [have_tests=yes] +) +]) AC_SUBST(DBUSMENUTESTS_CFLAGS) AC_SUBST(DBUSMENUTESTS_LIBS) @@ -121,7 +149,17 @@ AM_CONDITIONAL(INTROSPECTION_TEN, [test "x$introspection_ten" = "xyes"]) # Vala API Generation ########################### +AC_ARG_ENABLE([vala], + AC_HELP_STRING([--disable-vala], [Disable vala]), + [enable_vala=$enableval], [enable_vala=auto]) + +AS_IF([test "x$enable_vala" != "xno"],[ + AM_COND_IF([HAVE_INTROSPECTION],,[ + AC_MSG_ERROR([Vala bindings require introspection support, please --enable-introspection]) + ]) AC_PATH_PROG([VALA_API_GEN], [vapigen]) +]) +AM_CONDITIONAL([HAVE_VALA], [test -n "$VALA_API_GEN"]) ########################### # XSLT Processor @@ -133,8 +171,8 @@ AC_PATH_PROG([XSLT_PROC], [xsltproc]) # Lib versioning ########################### -LIBDBUSMENU_CURRENT=3 -LIBDBUSMENU_REVISION=11 +LIBDBUSMENU_CURRENT=4 +LIBDBUSMENU_REVISION=4 LIBDBUSMENU_AGE=0 AC_SUBST(LIBDBUSMENU_CURRENT) @@ -199,3 +237,23 @@ libdbusmenu Configuration: GTK+ Version: $with_gtk ]) +AS_IF([test "x$have_tests" = "xyes"], + AC_MSG_NOTICE([ Tests: yes]), + AC_MSG_NOTICE([ Tests: no]) +) + +AS_IF([test "x$enable_gtk" = "xno"], + AC_MSG_NOTICE([ Gtk: no]), + AC_MSG_NOTICE([ Gtk: yes (gtk$with_gtk)]) +) + +AM_COND_IF([HAVE_VALA], + AC_MSG_NOTICE([ Vala bindings: yes]), + AC_MSG_NOTICE([ Vala bindings no]) +) + +AS_IF([test "x$have_dumper" = "xyes"], + AC_MSG_NOTICE([ Dumper: yes]), + AC_MSG_NOTICE([ Dumper: no]) +) + diff --git a/docs/Makefile.am b/docs/Makefile.am index 87ffe5f..23a4c86 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -1 +1,7 @@ -SUBDIRS = libdbusmenu-glib libdbusmenu-gtk +if WANT_LIBDBUSMENUGTK +LIBDBUSMENUGTK_SUBDIR = libdbusmenu-gtk +endif + +SUBDIRS = \ + libdbusmenu-glib \ + $(LIBDBUSMENUGTK_SUBDIR) diff --git a/docs/libdbusmenu-glib/reference/libdbusmenu-glib-sections.txt b/docs/libdbusmenu-glib/reference/libdbusmenu-glib-sections.txt index 4d1e50a..d95ec4c 100644 --- a/docs/libdbusmenu-glib/reference/libdbusmenu-glib-sections.txt +++ b/docs/libdbusmenu-glib/reference/libdbusmenu-glib-sections.txt @@ -59,6 +59,7 @@ DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE DBUSMENU_MENUITEM_PROP_TOGGLE_STATE DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY DBUSMENU_MENUITEM_PROP_SHORTCUT +DBUSMENU_MENUITEM_PROP_DISPOSITION DBUSMENU_MENUITEM_TOGGLE_CHECK DBUSMENU_MENUITEM_TOGGLE_RADIO DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED @@ -70,6 +71,10 @@ DBUSMENU_MENUITEM_SHORTCUT_ALT DBUSMENU_MENUITEM_SHORTCUT_CONTROL DBUSMENU_MENUITEM_SHORTCUT_SHIFT DBUSMENU_MENUITEM_SHORTCUT_SUPER +DBUSMENU_MENUITEM_DISPOSITION_NORMAL +DBUSMENU_MENUITEM_DISPOSITION_INFORMATIVE +DBUSMENU_MENUITEM_DISPOSITION_WARNING +DBUSMENU_MENUITEM_DISPOSITION_ALERT DBUSMENU_MENUITEM_EVENT_ACTIVATED DBUSMENU_MENUITEM_EVENT_CLOSED DBUSMENU_MENUITEM_EVENT_OPENED diff --git a/docs/libdbusmenu-gtk/reference/Makefile.am b/docs/libdbusmenu-gtk/reference/Makefile.am index 06d0009..62f80ba 100644 --- a/docs/libdbusmenu-gtk/reference/Makefile.am +++ b/docs/libdbusmenu-gtk/reference/Makefile.am @@ -54,7 +54,7 @@ CFILE_GLOB=$(top_srcdir)/libdbusmenu-gtk/*.c # Header files to ignore when scanning. # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h -IGNORE_HFILES=genericmenuitem.h +IGNORE_HFILES=genericmenuitem.h genericmenuitem-enum-types.h # Images to copy into HTML directory. # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png diff --git a/docs/libdbusmenu-gtk/reference/libdbusmenu-gtk-sections.txt b/docs/libdbusmenu-gtk/reference/libdbusmenu-gtk-sections.txt index efffeaa..bc1f0ba 100644 --- a/docs/libdbusmenu-gtk/reference/libdbusmenu-gtk-sections.txt +++ b/docs/libdbusmenu-gtk/reference/libdbusmenu-gtk-sections.txt @@ -23,26 +23,6 @@ DbusmenuGtkClientPrivate </SECTION> <SECTION> -<FILE>serializablemenuitem</FILE> -<TITLE>DbusmenuGtkSerializableMenuItem</TITLE> -DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_PROP_MENUITEM -DbusmenuGtkSerializableMenuItemClass -dbusmenu_gtk_serializable_menu_item_build_menuitem -dbusmenu_gtk_serializable_menu_item_register_to_client -dbusmenu_gtk_serializable_menu_item_set_menuitem -<SUBSECTION Standard> -DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM -DBUSMENU_IS_GTK_SERIALIZABLE_MENU_ITEM -DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_CLASS -DBUSMENU_IS_GTK_SERIALIZABLE_MENU_ITEM_CLASS -DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_GET_CLASS -<SUBSECTION Private> -DBUSMENU_TYPE_GTK_SERIALIZABLE_MENU_ITEM -dbusmenu_gtk_serializable_menu_item_get_type -DbusmenuGtkSerializableMenuItemPrivate -</SECTION> - -<SECTION> <FILE>menu</FILE> <TITLE>DbusmenuGtkMenu</TITLE> DbusmenuGtkMenuClass diff --git a/libdbusmenu-glib/Makefile.am b/libdbusmenu-glib/Makefile.am index 5b04415..7d4ea5d 100644 --- a/libdbusmenu-glib/Makefile.am +++ b/libdbusmenu-glib/Makefile.am @@ -189,6 +189,7 @@ endif # VAPI Files ######################### +if HAVE_VALA if HAVE_INTROSPECTION vapidir = $(datadir)/vala/vapi @@ -200,4 +201,5 @@ Dbusmenu-0.4.vapi: Dbusmenu-0.4.gir CLEANFILES += $(vapi_DATA) endif +endif diff --git a/libdbusmenu-glib/client-menuitem.c b/libdbusmenu-glib/client-menuitem.c index 60f8637..483470a 100644 --- a/libdbusmenu-glib/client-menuitem.c +++ b/libdbusmenu-glib/client-menuitem.c @@ -106,7 +106,7 @@ static void handle_event (DbusmenuMenuitem * mi, const gchar * name, GVariant * variant, guint timestamp) { DbusmenuClientMenuitemPrivate * priv = DBUSMENU_CLIENT_MENUITEM_GET_PRIVATE(mi); - dbusmenu_client_send_event(priv->client, dbusmenu_menuitem_get_id(mi), name, variant, timestamp); + dbusmenu_client_send_event(priv->client, dbusmenu_menuitem_get_id(mi), name, variant, timestamp, mi); return; } diff --git a/libdbusmenu-glib/client-private.h b/libdbusmenu-glib/client-private.h index f6df372..44c0066 100644 --- a/libdbusmenu-glib/client-private.h +++ b/libdbusmenu-glib/client-private.h @@ -37,7 +37,8 @@ void dbusmenu_client_send_event (DbusmenuClient * client, gint id, const gchar * name, GVariant * variant, - guint timestamp); + guint timestamp, + DbusmenuMenuitem * mi); void dbusmenu_client_send_about_to_show(DbusmenuClient * client, gint id, void (*cb) (gpointer user_data), diff --git a/libdbusmenu-glib/client.c b/libdbusmenu-glib/client.c index a98ae98..1ee8641 100644 --- a/libdbusmenu-glib/client.c +++ b/libdbusmenu-glib/client.c @@ -331,6 +331,8 @@ dbusmenu_client_class_init (DbusmenuClientClass *klass) return; } +#define LAYOUT_PROPS_COUNT 5 + static void dbusmenu_client_init (DbusmenuClient *self) { @@ -351,13 +353,14 @@ dbusmenu_client_init (DbusmenuClient *self) priv->layoutcall = NULL; - gchar * layout_props[5]; + gchar * layout_props[LAYOUT_PROPS_COUNT + 1]; layout_props[0] = DBUSMENU_MENUITEM_PROP_TYPE; layout_props[1] = DBUSMENU_MENUITEM_PROP_LABEL; layout_props[2] = DBUSMENU_MENUITEM_PROP_VISIBLE; layout_props[3] = DBUSMENU_MENUITEM_PROP_ENABLED; - layout_props[4] = NULL; - priv->layout_props = g_variant_new_strv((const gchar * const *)layout_props, 4); + layout_props[4] = DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY; + layout_props[LAYOUT_PROPS_COUNT] = NULL; + priv->layout_props = g_variant_new_strv((const gchar * const *)layout_props, LAYOUT_PROPS_COUNT); g_variant_ref_sink(priv->layout_props); priv->current_revision = 0; @@ -441,6 +444,9 @@ dbusmenu_client_dispose (GObject *object) priv->menuproxy_cancel = NULL; } if (priv->menuproxy != NULL) { + g_signal_handlers_disconnect_matched(priv->menuproxy, + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, object); g_object_unref(G_OBJECT(priv->menuproxy)); priv->menuproxy = NULL; } @@ -598,10 +604,10 @@ get_properties_callback (GObject *obj, GAsyncResult * res, gpointer user_data) } /* Callback all the folks we can find */ - GVariant * child = g_variant_get_child_value(params, 0); + GVariant * parent = g_variant_get_child_value(params, 0); GVariantIter iter; - g_variant_iter_init(&iter, child); - g_variant_unref(child); + g_variant_iter_init(&iter, parent); + GVariant * 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)); @@ -632,6 +638,7 @@ get_properties_callback (GObject *obj, GAsyncResult * res, gpointer user_data) g_variant_unref(properties); g_variant_unref(child); } + g_variant_unref(parent); g_variant_unref(params); /* Provide errors for those who we can't */ @@ -1232,7 +1239,7 @@ menuproxy_signal_cb (GDBusProxy * proxy, gchar * sender, gchar * signal, GVarian } 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) { + } else if (g_strcmp0(signal, "ItemsPropertiesUpdated") == 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; @@ -1333,8 +1340,16 @@ menuitem_get_properties_cb (GVariant * properties, GError * error, gpointer data if (error != NULL) { g_warning("Error getting properties on a menuitem: %s", error->message); - g_object_unref(data); - return; + goto out; + } + + if (properties == NULL) { + goto out; + } + + if (!g_variant_is_of_type(properties, G_VARIANT_TYPE("a{sv}"))) { + g_warning("Properties are of type '%s' instead of type '%s'", g_variant_get_type_string(properties), "a{sv}"); + goto out; } GVariantIter iter; @@ -1347,6 +1362,7 @@ menuitem_get_properties_cb (GVariant * properties, GError * error, gpointer data dbusmenu_menuitem_property_set_variant(item, key, value); } +out: g_object_unref(data); return; @@ -1365,12 +1381,16 @@ menuitem_get_properties_replace_cb (GVariant * properties, GError * error, gpoin g_warning("Unable to replace properties on %d: %s", dbusmenu_menuitem_get_id(DBUSMENU_MENUITEM(data)), error->message); have_error = TRUE; } + + if (properties == NULL) { + have_error = TRUE; + } /* Get the list of the current properties */ GList * current_props = dbusmenu_menuitem_properties_list(DBUSMENU_MENUITEM(data)); GList * tmp = NULL; - if (properties != NULL) { + if (!have_error && g_variant_is_of_type(properties, G_VARIANT_TYPE("a{sv}"))) { GVariantIter iter; g_variant_iter_init(&iter, properties); gchar * name; GVariant * value; @@ -1378,7 +1398,7 @@ menuitem_get_properties_replace_cb (GVariant * properties, GError * error, gpoin /* Remove the entries from the current list that we have new values for. This way we don't create signals of them being removed with the duplication of the value being changed. */ - while (g_variant_iter_loop(&iter, "{sv}", &name, &value) && have_error == FALSE) { + while (g_variant_iter_loop(&iter, "{sv}", &name, &value)) { for (tmp = current_props; tmp != NULL; tmp = g_list_next(tmp)) { if (g_strcmp0((gchar *)tmp->data, name) == 0) { current_props = g_list_delete_link(current_props, tmp); @@ -1414,8 +1434,12 @@ menuitem_get_properties_new_cb (GVariant * properties, GError * error, gpointer if (error != NULL) { g_warning("Error getting properties on a new menuitem: %s", error->message); - g_object_unref(propdata->item); - return; + goto out; + } + + if (properties == NULL) { + g_warning("Not realizing new item as properties for it were unavailable"); + goto out; } DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(propdata->client); @@ -1449,6 +1473,7 @@ menuitem_get_properties_new_cb (GVariant * properties, GError * error, gpointer g_signal_emit(G_OBJECT(propdata->client), signals[NEW_MENUITEM], 0, propdata->item, TRUE); } +out: g_object_unref(propdata->item); g_free(propdata); @@ -1475,6 +1500,7 @@ menuitem_call_cb (GObject * proxy, GAsyncResult * res, gpointer userdata) g_variant_unref(edata->variant); g_free(edata->event); g_object_unref(edata->menuitem); + g_object_unref(edata->client); g_free(edata); if (G_UNLIKELY(error != NULL)) { @@ -1490,14 +1516,13 @@ menuitem_call_cb (GObject * proxy, GAsyncResult * res, gpointer userdata) /* Sends the event over DBus to the server on the other side of the bus. */ void -dbusmenu_client_send_event (DbusmenuClient * client, gint id, const gchar * name, GVariant * variant, guint timestamp) +dbusmenu_client_send_event (DbusmenuClient * client, gint id, const gchar * name, GVariant * variant, guint timestamp, DbusmenuMenuitem * mi) { g_return_if_fail(DBUSMENU_IS_CLIENT(client)); g_return_if_fail(id >= 0); g_return_if_fail(name != NULL); DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client); - DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id); if (mi == NULL) { g_warning("Asked to activate a menuitem %d that we don't know about", id); return; @@ -1509,6 +1534,7 @@ dbusmenu_client_send_event (DbusmenuClient * client, gint id, const gchar * name event_data_t * edata = g_new0(event_data_t, 1); edata->client = client; + g_object_ref(client); edata->menuitem = mi; g_object_ref(edata->menuitem); edata->event = g_strdup(name); diff --git a/libdbusmenu-glib/dbus-menu.xml b/libdbusmenu-glib/dbus-menu.xml index a36c148..4b5a5d8 100644 --- a/libdbusmenu-glib/dbus-menu.xml +++ b/libdbusmenu-glib/dbus-menu.xml @@ -157,6 +157,19 @@ License version 3 and version 2.1 along with this program. If not, see </td> <td>""</td> </tr> + <tr> + <td>disposition</td> + <td>string</td> + <td> + How the menuitem feels the information it's displaying to the + user should be presented. + - "normal" a standard menu item + - "informative" providing additional information to the user + - "warning" looking at potentially harmful results + - "alert" something bad could potentially happen + </td> + <td>"normal"</td> + </tr> </table> Vendor specific properties can be added by prefixing them with diff --git a/libdbusmenu-glib/defaults.c b/libdbusmenu-glib/defaults.c index 9eaf9e5..a5caf0b 100644 --- a/libdbusmenu-glib/defaults.c +++ b/libdbusmenu-glib/defaults.c @@ -81,12 +81,13 @@ dbusmenu_defaults_init (DbusmenuDefaults *self) /* 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_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_SHORTCUT, G_VARIANT_TYPE("aas"), NULL); dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_DEFAULT, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY, G_VARIANT_TYPE_STRING, NULL); + dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_DEFAULT, DBUSMENU_MENUITEM_PROP_DISPOSITION, G_VARIANT_TYPE_STRING, g_variant_new_string(DBUSMENU_MENUITEM_DISPOSITION_NORMAL)); /* Separator defaults */ dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_SEPARATOR, DBUSMENU_MENUITEM_PROP_VISIBLE, G_VARIANT_TYPE_BOOLEAN, g_variant_new_boolean(TRUE)); diff --git a/libdbusmenu-glib/menuitem-proxy.c b/libdbusmenu-glib/menuitem-proxy.c index ae6a334..fdbd892 100644 --- a/libdbusmenu-glib/menuitem-proxy.c +++ b/libdbusmenu-glib/menuitem-proxy.c @@ -188,6 +188,7 @@ proxy_item_child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, gui DbusmenuMenuitemProxy * pmi = DBUSMENU_MENUITEM_PROXY(user_data); DbusmenuMenuitemProxy * child_pmi = dbusmenu_menuitem_proxy_new(child); dbusmenu_menuitem_child_add_position(DBUSMENU_MENUITEM(pmi), DBUSMENU_MENUITEM(child_pmi), position); + g_object_unref (child_pmi); return; } @@ -283,6 +284,7 @@ add_menuitem (DbusmenuMenuitemProxy * pmi, DbusmenuMenuitem * mi) for (child = children; child != NULL; child = g_list_next(child)) { DbusmenuMenuitemProxy * child_pmi = dbusmenu_menuitem_proxy_new(DBUSMENU_MENUITEM(child->data)); dbusmenu_menuitem_child_append(DBUSMENU_MENUITEM(pmi), DBUSMENU_MENUITEM(child_pmi)); + g_object_unref (child_pmi); } return; diff --git a/libdbusmenu-glib/menuitem.c b/libdbusmenu-glib/menuitem.c index 70b5fd2..4e037ee 100644 --- a/libdbusmenu-glib/menuitem.c +++ b/libdbusmenu-glib/menuitem.c @@ -446,7 +446,7 @@ send_about_to_show (DbusmenuMenuitem * mi, void (*cb) (DbusmenuMenuitem * mi, gp { g_return_if_fail(DBUSMENU_IS_MENUITEM(mi)); - if (dbusmenu_menuitem_get_children(mi) == NULL) { + if (dbusmenu_menuitem_get_children(mi) == NULL && g_strcmp0(DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU, dbusmenu_menuitem_property_get(mi, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY)) != 0) { g_warning("About to Show called on an item wihtout submenus. We're ignoring it."); } else { gboolean dummy; @@ -575,7 +575,7 @@ dbusmenu_menuitem_set_realized (DbusmenuMenuitem * mi) * 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. + * Return value: (transfer none) (element-type Dbusmenu.Menuitem): A #GList of pointers to #DbusmenuMenuitem objects. */ GList * dbusmenu_menuitem_get_children (DbusmenuMenuitem * mi) @@ -1694,25 +1694,20 @@ dbusmenu_menuitem_handle_event (DbusmenuMenuitem * mi, const gchar * name, GVari #endif DbusmenuMenuitemClass * class = DBUSMENU_MENUITEM_GET_CLASS(mi); - /* We need to keep a ref to the variant because the signal - handler will drop the floating ref and then we'll be up - a creek if we don't have our own later. */ - if (variant != NULL) { - g_variant_ref_sink(variant); + gboolean handled = FALSE; + if (variant == NULL) { + variant = g_variant_new_int32(0); } - gboolean handled = FALSE; + g_variant_ref_sink(variant); + g_signal_emit(G_OBJECT(mi), signals[EVENT], g_quark_from_string(name), name, variant, timestamp, &handled); if (!handled && class->handle_event != NULL) { class->handle_event(mi, name, variant, timestamp); } - if (variant != NULL) { - g_variant_unref(variant); - } - - return; + g_variant_unref(variant); } /** diff --git a/libdbusmenu-glib/menuitem.h b/libdbusmenu-glib/menuitem.h index afd1d4e..985e1a3 100644 --- a/libdbusmenu-glib/menuitem.h +++ b/libdbusmenu-glib/menuitem.h @@ -41,7 +41,9 @@ G_BEGIN_DECLS #define DBUSMENU_IS_MENUITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_TYPE_MENUITEM)) #define DBUSMENU_MENUITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_MENUITEM, DbusmenuMenuitemClass)) - +/* ***************************************** */ +/* ********* GLib Object Signals ********* */ +/* ***************************************** */ /** * DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED: * @@ -103,6 +105,9 @@ G_BEGIN_DECLS */ #define DBUSMENU_MENUITEM_SIGNAL_EVENT "event" +/* ***************************************** */ +/* ********* Menuitem Properties ********* */ +/* ***************************************** */ /** * DBUSMENU_MENUITEM_PROP_TYPE: * @@ -184,7 +189,17 @@ G_BEGIN_DECLS * #DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU. Type: #G_VARIANT_TYPE_STRING */ #define DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY "children-display" +/** + * DBUSMENU_MENUITEM_PROP_DISPOSITION: + * + * #DbusmenuMenuitem property to tell what type of information that the + * menu item is displaying to the user. Type: #G_VARIANT_TYPE_STRING + */ +#define DBUSMENU_MENUITEM_PROP_DISPOSITION "disposition" +/* ***************************************** */ +/* ********* Toggle Values ********* */ +/* ***************************************** */ /** * DBUSMENU_MENUITEM_TOGGLE_CHECK: * @@ -200,6 +215,9 @@ G_BEGIN_DECLS */ #define DBUSMENU_MENUITEM_TOGGLE_RADIO "radio" +/* ***************************************** */ +/* ********* Toggle States ********* */ +/* ***************************************** */ /** * DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED: * @@ -222,6 +240,9 @@ G_BEGIN_DECLS */ #define DBUSMENU_MENUITEM_TOGGLE_STATE_UNKNOWN -1 +/* ***************************************** */ +/* ********* Icon specials ********* */ +/* ***************************************** */ /** * DBUSMENU_MENUITEM_ICON_NAME_BLANK: * @@ -230,6 +251,9 @@ G_BEGIN_DECLS */ #define DBUSMENU_MENUITEM_ICON_NAME_BLANK "blank-icon" +/* ***************************************** */ +/* ********* Shortcut Modifiers ********* */ +/* ***************************************** */ /** * DBUSMENU_MENUITEM_SHORTCUT_CONTROL: * @@ -259,6 +283,9 @@ G_BEGIN_DECLS */ #define DBUSMENU_MENUITEM_SHORTCUT_SUPER "Super" +/* ***************************************** */ +/* ********* Child Display Types ********* */ +/* ***************************************** */ /** * DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU: * @@ -267,6 +294,44 @@ G_BEGIN_DECLS */ #define DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU "submenu" +/* ***************************************** */ +/* ********* Menuitem Dispositions ********* */ +/* ***************************************** */ +/** + * DBUSMENU_MENUITEM_DISPOSITION_NORMAL: + * + * Used in #DBUSMENU_MENUITEM_PROP_DISPOSITION to have a menu + * item displayed in the normal manner. Default value. + */ +#define DBUSMENU_MENUITEM_DISPOSITION_NORMAL "normal" +/** + * DBUSMENU_MENUITEM_DISPOSITION_INFORMATIVE: + * + * Used in #DBUSMENU_MENUITEM_PROP_DISPOSITION to have a menu + * item displayed in a way that conveys it's giving additional + * information to the user. + */ +#define DBUSMENU_MENUITEM_DISPOSITION_INFORMATIVE "informative" +/** + * DBUSMENU_MENUITEM_DISPOSITION_WARNING: + * + * Used in #DBUSMENU_MENUITEM_PROP_DISPOSITION to have a menu + * item displayed in a way that conveys it's giving a warning + * to the user. + */ +#define DBUSMENU_MENUITEM_DISPOSITION_WARNING "warning" +/** + * DBUSMENU_MENUITEM_DISPOSITION_ALERT: + * + * Used in #DBUSMENU_MENUITEM_PROP_DISPOSITION to have a menu + * item displayed in a way that conveys it's giving an alert + * to the user. + */ +#define DBUSMENU_MENUITEM_DISPOSITION_ALERT "alert" + +/* ***************************************** */ +/* ********* Dbusmenu Events ********* */ +/* ***************************************** */ /** * DBUSMENU_MENUITEM_EVENT_ACTIVATED: * diff --git a/libdbusmenu-glib/server.c b/libdbusmenu-glib/server.c index a41e6ce..9c085f7 100644 --- a/libdbusmenu-glib/server.c +++ b/libdbusmenu-glib/server.c @@ -455,6 +455,7 @@ set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec) g_return_if_fail(priv->bus_lookup != NULL); } + g_object_ref(obj); g_bus_get(G_BUS_TYPE_SESSION, priv->bus_lookup, bus_got_cb, obj); } else { register_object(DBUSMENU_SERVER(obj)); @@ -694,6 +695,7 @@ bus_got_cb (GObject * obj, GAsyncResult * result, gpointer user_data) if (error != NULL) { g_warning("Unable to get session bus: %s", error->message); g_error_free(error); + g_object_unref(G_OBJECT(user_data)); return; } @@ -706,6 +708,7 @@ bus_got_cb (GObject * obj, GAsyncResult * result, gpointer user_data) register_object(DBUSMENU_SERVER(user_data)); + g_object_unref(G_OBJECT(user_data)); return; } @@ -949,11 +952,13 @@ menuitem_property_idle (gpointer user_data) } } + /* these are going to be standard references in all code paths and must be unrefed */ GVariant * megadata[2]; gboolean gotsomething = FALSE; if (item_init) { megadata[0] = g_variant_builder_end(&itembuilder); + g_variant_ref_sink(megadata[0]); gotsomething = TRUE; } else { GError * error = NULL; @@ -967,13 +972,14 @@ menuitem_property_idle (gpointer user_data) if (removeitem_init) { megadata[1] = g_variant_builder_end(&removeitembuilder); + g_variant_ref_sink(megadata[1]); gotsomething = TRUE; } else { GError * error = NULL; - megadata[1] = g_variant_parse(G_VARIANT_TYPE("a(ia(s))"), "[ ]", NULL, NULL, &error); + megadata[1] = g_variant_parse(G_VARIANT_TYPE("a(ias)"), "[ ]", NULL, NULL, &error); if (error != NULL) { - g_warning("Unable to parse '[ ]' as a 'a(ia(s))': %s", error->message); + g_warning("Unable to parse '[ ]' as a 'a(ias)': %s", error->message); g_error_free(error); } } @@ -983,14 +989,14 @@ menuitem_property_idle (gpointer user_data) NULL, priv->dbusobject, DBUSMENU_INTERFACE, - "ItemPropertiesUpdated", + "ItemsPropertiesUpdated", g_variant_new_tuple(megadata, 2), NULL); - } else { - g_variant_unref(megadata[0]); - g_variant_unref(megadata[1]); } + g_variant_unref(megadata[0]); + g_variant_unref(megadata[1]); + /* Clean everything up */ prop_array_teardown(priv->prop_array); priv->prop_array = NULL; @@ -1188,12 +1194,16 @@ error_quark (void) static void bus_get_layout (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation) { + g_return_if_fail(DBUSMENU_IS_SERVER(server)); DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server); + g_return_if_fail(priv != NULL); /* 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); + gint32 parent; + gint32 recurse; + const gchar ** props; + + g_variant_get(params, "(ii^a&s)", &parent, &recurse, &props); /* Output */ guint revision = priv->layout_revision; @@ -1206,6 +1216,7 @@ bus_get_layout (DbusmenuServer * server, GVariant * params, GDBusMethodInvocatio items = dbusmenu_menuitem_build_variant(mi, props, recurse); } } + g_free(props); /* What happens if we don't have anything? */ if (items == NULL) { @@ -1252,9 +1263,11 @@ bus_get_property (DbusmenuServer * server, GVariant * params, GDBusMethodInvocat "There currently isn't a layout in this server"); return; } - - gint id = g_variant_get_int32(g_variant_get_child_value(params, 0)); - const gchar * property = g_variant_get_string(g_variant_get_child_value(params, 1), NULL); + + gint32 id; + const gchar * property; + + g_variant_get(params, "(i&s)", &id, &property); DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id); @@ -1296,7 +1309,8 @@ bus_get_properties (DbusmenuServer * server, GVariant * params, GDBusMethodInvoc return; } - gint id = g_variant_get_int32(g_variant_get_child_value(params, 0)); + gint32 id; + g_variant_get(params, "(i)", &id); DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id); @@ -1324,28 +1338,43 @@ bus_get_group_properties (DbusmenuServer * server, GVariant * params, GDBusMetho DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server); if (priv->root == NULL) { + /* Allow a request for just id 0 when root is null. Return no properties. + So that a request always returns a valid structure no matter the + state of the structure in the server. + */ GVariant * idlist = g_variant_get_child_value(params, 0); - if (g_variant_n_children(idlist) == 1 && g_variant_get_int32(g_variant_get_child_value(idlist, 0)) == 0) { - GVariant * final = g_variant_parse(g_variant_type_new("(a(ia{sv}))"), "([(0, {})],)", NULL, NULL, NULL); - g_dbus_method_invocation_return_value(invocation, final); - return; - } + if (g_variant_n_children(idlist) == 1) { - g_dbus_method_invocation_return_error(invocation, - error_quark(), - NO_VALID_LAYOUT, - "There currently isn't a layout in this server"); + GVariant *id_v = g_variant_get_child_value(idlist, 0); + gint32 id = g_variant_get_int32(id_v); + g_variant_unref(id_v); + + if (id == 0) { + + GVariant * final = g_variant_parse(G_VARIANT_TYPE("(a(ia{sv}))"), "([(0, {})],)", NULL, NULL, NULL); + g_dbus_method_invocation_return_value(invocation, final); + g_variant_unref(final); + } + } else { + + g_dbus_method_invocation_return_error(invocation, + error_quark(), + NO_VALID_LAYOUT, + "There currently isn't a layout in this server"); + } + g_variant_unref(idlist); return; } - GVariantIter ids; - g_variant_iter_init(&ids, g_variant_get_child_value(params, 0)); + GVariantIter *ids; + g_variant_get(params, "(aias)", &ids, NULL); + /* TODO: implementation ignores propertyNames declared in XML */ GVariantBuilder builder; gboolean builder_init = FALSE; - gint id; - while (g_variant_iter_next(&ids, "i", &id)) { + gint32 id; + while (g_variant_iter_loop(ids, "i", &id)) { DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id); if (mi == NULL) continue; @@ -1361,7 +1390,7 @@ bus_get_group_properties (DbusmenuServer * server, GVariant * params, GDBusMetho if (props == NULL) { GError * error = NULL; - props = g_variant_parse(g_variant_type_new("a{sv}"), "{}", NULL, NULL, &error); + props = g_variant_parse(G_VARIANT_TYPE("a{sv}"), "{}", NULL, NULL, &error); if (error != NULL) { g_warning("Unable to parse '{}' as a 'a{sv}': %s", error->message); g_error_free(error); @@ -1374,18 +1403,20 @@ bus_get_group_properties (DbusmenuServer * server, GVariant * params, GDBusMetho g_variant_builder_add_value(&builder, mi_data); } + g_variant_iter_free(ids); + /* a standard reference that must be unrefed */ GVariant * ret = NULL; if (builder_init) { ret = g_variant_builder_end(&builder); + g_variant_ref_sink(ret); } 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("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); - ret = NULL; } } @@ -1393,6 +1424,7 @@ bus_get_group_properties (DbusmenuServer * server, GVariant * params, GDBusMetho if (ret != NULL) { g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE); g_variant_builder_add_value(&builder, ret); + g_variant_unref(ret); final = g_variant_builder_end(&builder); } else { g_warning("Error building property list, final variant is NULL"); @@ -1431,7 +1463,8 @@ static void bus_get_children (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation) { DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server); - gint id = g_variant_get_int32(g_variant_get_child_value(params, 0)); + gint32 id; + g_variant_get(params, "(i)", &id); if (priv->root == NULL) { g_dbus_method_invocation_return_error(invocation, @@ -1465,7 +1498,7 @@ bus_get_children (DbusmenuServer * server, GVariant * params, GDBusMethodInvocat ret = g_variant_new_tuple(&end, 1); } else { GError * error = NULL; - ret = g_variant_parse(g_variant_type_new("(a(ia{sv}))"), "([(0, {})],)", NULL, NULL, &error); + ret = g_variant_parse(G_VARIANT_TYPE("(a(ia{sv}))"), "([(0, {})],)", NULL, NULL, &error); if (error != NULL) { g_warning("Unable to parse '([(0, {})],)' as a '(a(ia{sv}))': %s", error->message); g_error_free(error); @@ -1517,32 +1550,35 @@ bus_event (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * i return; } - gint id = g_variant_get_int32(g_variant_get_child_value(params, 0)); + gint32 id; + gchar *etype; + GVariant *data; + guint32 ts; + + g_variant_get(params, "(isvu)", &id, &etype, &data, &ts); + DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id); if (mi == NULL) { + g_dbus_method_invocation_return_error(invocation, error_quark(), INVALID_MENUITEM_ID, "The ID supplied %d does not refer to a menu item we have", id); - return; - } - - idle_event_t * event_data = g_new0(idle_event_t, 1); - event_data->mi = mi; - g_object_ref(event_data->mi); - event_data->eventid = g_strdup(g_variant_get_string(g_variant_get_child_value(params, 1), NULL)); - event_data->timestamp = g_variant_get_uint32(g_variant_get_child_value(params, 3)); - event_data->variant = g_variant_get_child_value(params, 2); + g_free(etype); + g_variant_unref(data); - if (g_variant_is_of_type(event_data->variant, G_VARIANT_TYPE_VARIANT)) { - event_data->variant = g_variant_get_variant(event_data->variant); - } + } else { - g_variant_ref_sink(event_data->variant); + idle_event_t * event_data = g_new0(idle_event_t, 1); + event_data->mi = g_object_ref(mi); + event_data->eventid = etype; + event_data->timestamp = ts; + event_data->variant = data; /* give away our reference */ - g_timeout_add(0, event_local_handler, event_data); + g_timeout_add(0, event_local_handler, event_data); + } g_dbus_method_invocation_return_value(invocation, NULL); return; @@ -1562,7 +1598,8 @@ bus_about_to_show (DbusmenuServer * server, GVariant * params, GDBusMethodInvoca return; } - gint id = g_variant_get_int32(g_variant_get_child_value(params, 0)); + gint32 id; + g_variant_get(params, "(i)", &id); DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id); if (mi == NULL) { diff --git a/libdbusmenu-gtk/Makefile.am b/libdbusmenu-gtk/Makefile.am index 50a8f2c..b4564d1 100644 --- a/libdbusmenu-gtk/Makefile.am +++ b/libdbusmenu-gtk/Makefile.am @@ -1,6 +1,4 @@ -CLEANFILES = - if USE_GTK3 VER=3 GTKGIR=Gtk-3.0 @@ -13,10 +11,28 @@ GTKVALA=gtk+-2.0 lib_LTLIBRARIES = libdbusmenu-gtk.la endif +BUILT_SOURCES = +CLEANFILES = +DISTCLEANFILES = EXTRA_DIST = \ dbusmenu-gtk-0.4.pc.in \ dbusmenu-gtk3-0.4.pc.in +############## +# Enum Stuff +############## + +include $(top_srcdir)/Makefile.am.enum + +glib_enum_h = genericmenuitem-enum-types.h +glib_enum_c = genericmenuitem-enum-types.c +glib_enum_headers = $(srcdir)/genericmenuitem.h + + +##################### +# Include Directory +##################### + libdbusmenu_gtkincludedir=$(includedir)/libdbusmenu-0.4/libdbusmenu-gtk$(VER)/ libdbusmenu_gtkinclude_HEADERS = \ @@ -24,22 +40,21 @@ libdbusmenu_gtkinclude_HEADERS = \ client.h \ menu.h \ menuitem.h \ - parser.h \ - serializablemenuitem.h + parser.h libdbusmenu_gtk_la_SOURCES = \ client.h \ client.c \ genericmenuitem.h \ genericmenuitem.c \ + genericmenuitem-enum-types.h \ + genericmenuitem-enum-types.c \ menu.h \ menu.c \ menuitem.h \ menuitem.c \ parser.h \ - parser.c \ - serializablemenuitem.h \ - serializablemenuitem.c + parser.c libdbusmenu_gtk_la_LDFLAGS = \ -version-info $(LIBDBUSMENU_CURRENT):$(LIBDBUSMENU_REVISION):$(LIBDBUSMENU_AGE) \ @@ -92,7 +107,7 @@ INTROSPECTION_COMPILER_ARGS = --includedir=$(builddir) --includedir=$(top_buildd if HAVE_INTROSPECTION -introspection_sources = $(filter-out genericmenuitem.%, $(libdbusmenu_gtkinclude_HEADERS) $(libdbusmenu_gtk_la_SOURCES)) +introspection_sources = $(filter-out genericmenuitem%, $(libdbusmenu_gtkinclude_HEADERS) $(libdbusmenu_gtk_la_SOURCES)) DbusmenuGtk$(VER)-0.4.gir: libdbusmenu-gtk$(VER).la DbusmenuGtk_0_4_gir_INCLUDES = \ @@ -103,7 +118,6 @@ DbusmenuGtk_0_4_gir_CFLAGS = $(DBUSMENUGTK_CFLAGS) -I$(top_srcdir) DbusmenuGtk_0_4_gir_LIBS = libdbusmenu-gtk$(VER).la DbusmenuGtk_0_4_gir_FILES = $(addprefix $(srcdir)/, $(introspection_sources)) DbusmenuGtk_0_4_gir_NAMESPACE = DbusmenuGtk$(VER) -DbusmenuGtk_0_4_gir_SCANNERFLAGS = $(INTROSPECTION_SCANNER_ARGS) DbusmenuGtk_0_4_gir_EXPORT_PACKAGES = dbusmenu-gtk$(VER)-0.4 # We duplicate these for the same reason as libdbusmenu_gtk3includedir above @@ -112,7 +126,6 @@ DbusmenuGtk3_0_4_gir_CFLAGS = $(DbusmenuGtk_0_4_gir_CFLAGS) DbusmenuGtk3_0_4_gir_LIBS = $(DbusmenuGtk_0_4_gir_LIBS) DbusmenuGtk3_0_4_gir_FILES = $(DbusmenuGtk_0_4_gir_FILES) DbusmenuGtk3_0_4_gir_NAMESPACE = $(DbusmenuGtk_0_4_gir_NAMESPACE) -DbusmenuGtk3_0_4_gir_SCANNERFLAGS = $(DbusmenuGtk_0_4_gir_SCANNERFLAGS) DbusmenuGtk3_0_4_gir_EXPORT_PACKAGES = $(DbusmenuGtk_0_4_gir_EXPORT_PACKAGES) INTROSPECTION_GIRS += DbusmenuGtk$(VER)-0.4.gir @@ -131,6 +144,7 @@ endif # VAPI Files ######################### +if HAVE_VALA if HAVE_INTROSPECTION vapidir = $(datadir)/vala/vapi @@ -154,4 +168,5 @@ DbusmenuGtk$(VER)-0.4.tmp.gir: DbusmenuGtk$(VER)-0.4.gir CLEANFILES += $(vapi_DATA) DbusmenuGtk$(VER)-0.4.tmp.gir endif +endif diff --git a/libdbusmenu-gtk/client.c b/libdbusmenu-gtk/client.c index 497808b..1051f20 100644 --- a/libdbusmenu-gtk/client.c +++ b/libdbusmenu-gtk/client.c @@ -31,10 +31,12 @@ License version 3 and version 2.1 along with this program. If not, see #endif #include <gtk/gtk.h> +#include <glib.h> #include "client.h" #include "menuitem.h" #include "genericmenuitem.h" +#include "genericmenuitem-enum-types.h" /* Private */ struct _DbusmenuGtkClientPrivate { @@ -59,6 +61,7 @@ static void move_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint n 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 void event_result (DbusmenuClient * client, DbusmenuMenuitem * mi, const gchar * event, GVariant * variant, guint timestamp, GError * error); 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); @@ -119,6 +122,7 @@ dbusmenu_gtkclient_init (DbusmenuGtkClient *self) 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); + g_signal_connect(G_OBJECT(self), DBUSMENU_CLIENT_SIGNAL_EVENT_RESULT, G_CALLBACK(event_result), NULL); theme_dir_changed(DBUSMENU_CLIENT(self), dbusmenu_client_get_icon_paths(DBUSMENU_CLIENT(self)), NULL); @@ -448,8 +452,97 @@ dbusmenu_gtkclient_get_accel_group (DbusmenuGtkClient * client) /* Internal Functions */ -static const gchar * data_menuitem = "dbusmenugtk-data-gtkmenuitem"; -static const gchar * data_menu = "dbusmenugtk-data-gtkmenu"; +static const gchar * data_menuitem = "dbusmenugtk-data-gtkmenuitem"; +static const gchar * data_menu = "dbusmenugtk-data-gtkmenu"; +static const gchar * data_activating = "dbusmenugtk-data-activating"; +static const gchar * data_idle_close_id = "dbusmenugtk-data-idle-close-id"; +static const gchar * data_delayed_close = "dbusmenugtk-data-delayed-close"; + +static void +menu_item_start_activating(DbusmenuMenuitem * mi) +{ + /* Mark this item and all it's parents as activating */ + DbusmenuMenuitem * parent = mi; + do { + g_object_set_data(G_OBJECT(parent), data_activating, + GINT_TO_POINTER(TRUE)); + } while ((parent = dbusmenu_menuitem_get_parent (parent)) != NULL); + + GVariant * variant = g_variant_new("i", 0); + dbusmenu_menuitem_handle_event(mi, DBUSMENU_MENUITEM_EVENT_ACTIVATED, variant, gtk_get_current_event_time()); +} + +static gboolean +menu_item_is_activating(DbusmenuMenuitem * mi) +{ + return GPOINTER_TO_INT(g_object_get_data(G_OBJECT(mi), data_activating)); +} + +static void +menu_item_stop_activating(DbusmenuMenuitem * mi) +{ + if (!menu_item_is_activating(mi)) + return; + + /* Mark this item and all it's parents as not activating and finally + send their queued close event. */ + g_object_set_data(G_OBJECT(mi), data_activating, GINT_TO_POINTER(FALSE)); + + /* There is one master root parent that we don't care about, so stop + right before it */ + DbusmenuMenuitem * parent = dbusmenu_menuitem_get_parent (mi); + while (dbusmenu_menuitem_get_parent (parent) != NULL && + menu_item_is_activating(parent)) { + /* Now clean up the activating flag */ + g_object_set_data(G_OBJECT(parent), data_activating, + GINT_TO_POINTER(FALSE)); + + gboolean should_close = FALSE; + + /* Note that dbus might be fast enough to have already + processed the app's reply before close_in_idle() is called. + So to avoid that, we shut down any pending close_in_idle call */ + guint id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(parent), + data_idle_close_id)); + if (id > 0) { + g_source_remove(id); + g_object_set_data(G_OBJECT(parent), data_idle_close_id, + GINT_TO_POINTER(0)); + should_close = TRUE; + } + + gboolean delayed = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(mi), + data_delayed_close)); + if (delayed) { + g_object_set_data(G_OBJECT(mi), data_delayed_close, + GINT_TO_POINTER(FALSE)); + should_close = TRUE; + } + + /* And finally send a delayed closed event if one would have + happened */ + if (should_close) { + dbusmenu_menuitem_handle_event(parent, + DBUSMENU_MENUITEM_EVENT_CLOSED, + NULL, + gtk_get_current_event_time()); + } + + parent = dbusmenu_menuitem_get_parent (parent); + } +} + +static void +event_result (DbusmenuClient * client, DbusmenuMenuitem * mi, + const gchar * event, GVariant * variant, guint timestamp, + GError * error) +{ + if (g_strcmp0(event, DBUSMENU_MENUITEM_EVENT_ACTIVATED) == 0) { + menu_item_stop_activating(mi); + } + + return; +} /* This is the call back for the GTK widget for when it gets clicked on by the user to send it back across the bus. */ @@ -457,8 +550,7 @@ static gboolean 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, DBUSMENU_MENUITEM_EVENT_ACTIVATED, variant, gtk_get_current_event_time()); + menu_item_start_activating(mi); } else { /* TODO: We need to stop the display of the submenu until this callback returns. */ @@ -467,13 +559,42 @@ menu_pressed_cb (GtkMenuItem * gmi, DbusmenuMenuitem * mi) return TRUE; } +static gboolean +close_in_idle (DbusmenuMenuitem * mi) +{ + /* Don't send closed signal if we also sent activating signal. + We'd just be asking for race conditions. We'll send closed + when done with activation. */ + if (!menu_item_is_activating(mi)) + dbusmenu_menuitem_handle_event(mi, DBUSMENU_MENUITEM_EVENT_CLOSED, NULL, gtk_get_current_event_time()); + else + g_object_set_data(G_OBJECT(mi), data_delayed_close, GINT_TO_POINTER(TRUE)); + + g_object_set_data(G_OBJECT(mi), data_idle_close_id, GINT_TO_POINTER(0)); + return FALSE; +} + 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()); + if (gtk_widget_get_visible (menu)) { + menu_item_stop_activating(mi); /* just in case */ + dbusmenu_menuitem_handle_event(mi, DBUSMENU_MENUITEM_EVENT_OPENED, NULL, gtk_get_current_event_time()); + } else { + /* Try to close in the idle loop because we actually get a menu + close notification before we get notified that a menu item + was clicked. We want to give that clicked signal some + time, so we wait until all queued signals are handled before + continuing. (our handling of the closed signal depends on + whether the user clicked an item or not) */ + guint id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(mi), + data_idle_close_id)); + if (id == 0) { + id = g_idle_add((GSourceFunc)close_in_idle, mi); + g_object_set_data(G_OBJECT(mi), data_idle_close_id, + GINT_TO_POINTER(id)); + } + } } /* Process the visible property */ @@ -551,11 +672,58 @@ process_toggle_state (DbusmenuMenuitem * mi, GtkMenuItem * gmi, GVariant * varia return; } +/* Submenu processing */ +static void +process_submenu (DbusmenuMenuitem * mi, GtkMenuItem * gmi, GVariant * variant, DbusmenuGtkClient * gtkclient) +{ + const gchar * submenu = NULL; + if (variant != NULL) { + submenu = g_variant_get_string(variant, NULL); + } + + if (g_strcmp0(submenu, DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU) != 0) { + /* This is the only case we're really supporting right now, + so if it's not this, we want to clean up. */ + /* We're just going to warn for now. */ + gpointer pmenu = g_object_get_data(G_OBJECT(mi), data_menu); + if (pmenu != NULL) { + g_warning("The child-display variable is set to '%s' but there's a menu, odd?", submenu); + } + } else { + /* We need to build a menu for these guys to live in. */ + GtkMenu * menu = GTK_MENU(gtk_menu_new()); + g_object_ref_sink(menu); + g_object_set_data_full(G_OBJECT(mi), data_menu, menu, g_object_unref); + + gtk_menu_item_set_submenu(gmi, GTK_WIDGET(menu)); + + g_signal_connect(menu, "notify::visible", G_CALLBACK(submenu_notify_visible_cb), mi); + } + + return; +} + +/* Process the disposition changing */ +static void +process_disposition (DbusmenuMenuitem * mi, GtkMenuItem * gmi, GVariant * variant, DbusmenuGtkClient * gtkclient) +{ + /* We can only handle generic menu items here. Perhaps someone else + will find the value useful. Not us. */ + if (!IS_GENERICMENUITEM(gmi)) { + return; + } + + genericmenuitem_set_disposition(GENERICMENUITEM(gmi), genericmenuitem_disposition_get_value_from_nick(g_variant_get_string(variant, NULL))); + return; +} + /* Whenever we have a property change on a DbusmenuMenuitem we need to be responsive to that. */ static void -menu_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * variant, GtkMenuItem * gmi) +menu_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * variant, DbusmenuGtkClient * gtkclient) { + GtkMenuItem * gmi = dbusmenu_gtkclient_menuitem_get(gtkclient, mi); + if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_LABEL)) { gtk_menu_item_set_label(gmi, variant == NULL ? NULL : g_variant_get_string(variant, NULL)); } else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_VISIBLE)) { @@ -566,6 +734,10 @@ menu_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * variant, Gt process_toggle_type(mi, gmi, variant); } else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE)) { process_toggle_state(mi, gmi, variant); + } else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY)) { + process_submenu(mi, gmi, variant, gtkclient); + } else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_DISPOSITION)) { + process_disposition(mi, gmi, variant, gtkclient); } return; @@ -582,19 +754,6 @@ menu_shortcut_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * value, return; } -/* Call back that happens when the DbusmenuMenuitem - is destroyed. We're making sure to clean up everything - else down the pipe. */ -static void -destoryed_dbusmenuitem_cb (gpointer udata, GObject * dbusmenuitem) -{ - #ifdef MASSIVEDEBUGGING - g_debug("DbusmenuMenuitem was destroyed"); - #endif - gtk_widget_destroy(GTK_WIDGET(udata)); - return; -} - /* The new menuitem signal only happens if we don't have a type handler for the type of the item. This should be an error condition and we're printing out a message. */ @@ -697,14 +856,11 @@ dbusmenu_gtkclient_newitem_base (DbusmenuGtkClient * client, DbusmenuMenuitem * #endif /* Attach these two */ - g_object_set_data(G_OBJECT(item), data_menuitem, gmi); - g_object_ref(G_OBJECT(gmi)); - #ifdef MASSIVEDEBUGGING - g_signal_connect(G_OBJECT(gmi), "destroy", G_CALLBACK(destroy_gmi), item); - #endif + g_object_ref_sink(G_OBJECT(gmi)); + g_object_set_data_full(G_OBJECT(item), data_menuitem, gmi, (GDestroyNotify)gtk_widget_destroy); /* DbusmenuMenuitem signals */ - g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(menu_prop_change_cb), gmi); + g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(menu_prop_change_cb), client); g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(menu_shortcut_change_cb), client); g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED, G_CALLBACK(delete_child), client); g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED, G_CALLBACK(move_child), client); @@ -712,14 +868,13 @@ dbusmenu_gtkclient_newitem_base (DbusmenuGtkClient * client, DbusmenuMenuitem * /* GtkMenuitem signals */ g_signal_connect(G_OBJECT(gmi), "activate", G_CALLBACK(menu_pressed_cb), item); - /* Life insurance */ - g_object_weak_ref(G_OBJECT(item), destoryed_dbusmenuitem_cb, gmi); - /* Check our set of props to see if any are set already */ process_visible(item, gmi, dbusmenu_menuitem_property_get_variant(item, DBUSMENU_MENUITEM_PROP_VISIBLE)); process_sensitive(item, gmi, dbusmenu_menuitem_property_get_variant(item, DBUSMENU_MENUITEM_PROP_ENABLED)); process_toggle_type(item, gmi, dbusmenu_menuitem_property_get_variant(item, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE)); process_toggle_state(item, gmi, dbusmenu_menuitem_property_get_variant(item, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE)); + process_submenu(item, gmi, dbusmenu_menuitem_property_get_variant(item, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY), client); + process_disposition(item, gmi, dbusmenu_menuitem_property_get_variant(item, DBUSMENU_MENUITEM_PROP_DISPOSITION), client); refresh_shortcut(client, item); /* Oh, we're a child, let's deal with that */ @@ -741,17 +896,12 @@ new_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position, Dbus if (g_strcmp0(dbusmenu_menuitem_property_get(mi, DBUSMENU_MENUITEM_PROP_TYPE), DBUSMENU_CLIENT_TYPES_SEPARATOR) == 0) { return; } gpointer ann_menu = g_object_get_data(G_OBJECT(mi), data_menu); - GtkMenu * menu = GTK_MENU(ann_menu); - if (menu == NULL) { - /* Oh, we don't have a submenu, build one! */ - menu = GTK_MENU(gtk_menu_new()); - g_object_set_data(G_OBJECT(mi), data_menu, menu); - - GtkMenuItem * parent = dbusmenu_gtkclient_menuitem_get(gtkclient, mi); - gtk_menu_item_set_submenu(parent, GTK_WIDGET(menu)); + if (ann_menu == NULL) { + g_warning("Children but no menu, someone's been naughty with their '" DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY "' property: '%s'", dbusmenu_menuitem_property_get(mi, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY)); + return; + } - g_signal_connect(menu, "notify::visible", G_CALLBACK(submenu_notify_visible_cb), mi); - } + GtkMenu * menu = GTK_MENU(ann_menu); GtkMenuItem * childmi = dbusmenu_gtkclient_menuitem_get(gtkclient, child); gtk_menu_shell_insert(GTK_MENU_SHELL(menu), GTK_WIDGET(childmi), position); @@ -771,7 +921,7 @@ delete_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, DbusmenuGtkClient if (menu != NULL) { gtk_widget_destroy(GTK_WIDGET(menu)); - g_object_set_data(G_OBJECT(mi), data_menu, NULL); + g_object_steal_data(G_OBJECT(mi), data_menu); } } @@ -875,11 +1025,10 @@ new_item_normal (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, Dbusmenu GtkMenuItem * gmi; gmi = GTK_MENU_ITEM(g_object_new(GENERICMENUITEM_TYPE, NULL)); - gtk_menu_item_set_label(gmi, dbusmenu_menuitem_property_get(newitem, DBUSMENU_MENUITEM_PROP_LABEL)); if (gmi != NULL) { + gtk_menu_item_set_label(gmi, dbusmenu_menuitem_property_get(newitem, DBUSMENU_MENUITEM_PROP_LABEL)); dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, gmi, parent); - g_object_unref(gmi); } else { return FALSE; } diff --git a/libdbusmenu-gtk/dbusmenu-gtk.h b/libdbusmenu-gtk/dbusmenu-gtk.h index f2fe5be..de63c61 100644 --- a/libdbusmenu-gtk/dbusmenu-gtk.h +++ b/libdbusmenu-gtk/dbusmenu-gtk.h @@ -36,6 +36,5 @@ License version 3 and version 2.1 along with this program. If not, see #include <libdbusmenu-gtk/client.h> #include <libdbusmenu-gtk/menu.h> #include <libdbusmenu-gtk/menuitem.h> -#include <libdbusmenu-gtk/serializablemenuitem.h> #endif /* __DBUSMENU_GLIB_H__ */ diff --git a/libdbusmenu-gtk/genericmenuitem-enum-types.c.in b/libdbusmenu-gtk/genericmenuitem-enum-types.c.in new file mode 100644 index 0000000..8b2c046 --- /dev/null +++ b/libdbusmenu-gtk/genericmenuitem-enum-types.c.in @@ -0,0 +1,116 @@ +/*** BEGIN file-header ***/ +/* +Enums from the dbusmenu headers + +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/> +*/ + +#include "genericmenuitem-enum-types.h" + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +#include "@basename@" +/*** END file-production ***/ + +/*** BEGIN value-header ***/ +/** + @enum_name@_get_type: + + Builds a GLib type for the #@EnumName@ enumeration. + + Return value: A unique #GType for the #@EnumName@ enum. +*/ +GType +@enum_name@_get_type (void) +{ + static GType etype = 0; + if (G_UNLIKELY(etype == 0)) { + static const G@Type@Value values[] = { +/*** END value-header ***/ + +/*** BEGIN value-production ***/ + { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, +/*** END value-production ***/ + +/*** BEGIN value-tail ***/ + { 0, NULL, NULL} + }; + + etype = g_@type@_register_static (g_intern_static_string("@EnumName@"), values); + } + + return etype; +} + +/** + @enum_name@_get_nick: + @value: The value of @EnumName@ to get the nick of + + Looks up in the enum table for the nick of @value. + + Return value: The nick for the given value or #NULL on error +*/ +const gchar * +@enum_name@_get_nick (@EnumName@ value) +{ + GEnumClass * class = G_ENUM_CLASS(g_type_class_ref(@enum_name@_get_type())); + g_return_val_if_fail(class != NULL, NULL); + + const gchar * ret = NULL; + GEnumValue * val = g_enum_get_value(class, value); + if (val != NULL) { + ret = val->value_nick; + } + + g_type_class_unref(class); + return ret; +} + +/** + @enum_name@_get_value_from_nick: + @nick: The enum nick to lookup + + Looks up in the enum table for the value of @nick. + + Return value: The value for the given @nick +*/ +@EnumName@ +@enum_name@_get_value_from_nick (const gchar * nick) +{ + GEnumClass * class = G_ENUM_CLASS(g_type_class_ref(@enum_name@_get_type())); + g_return_val_if_fail(class != NULL, 0); + + @EnumName@ ret = 0; + GEnumValue * val = g_enum_get_value_by_nick(class, nick); + if (val != NULL) { + ret = val->value; + } + + g_type_class_unref(class); + return ret; +} + + +/*** END value-tail ***/ diff --git a/libdbusmenu-gtk/genericmenuitem-enum-types.h.in b/libdbusmenu-gtk/genericmenuitem-enum-types.h.in new file mode 100644 index 0000000..5758438 --- /dev/null +++ b/libdbusmenu-gtk/genericmenuitem-enum-types.h.in @@ -0,0 +1,65 @@ +/*** BEGIN file-header ***/ +/* +Enums from the dbusmenu headers + +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_ENUM_TYPES_H__ +#define __DBUSMENU_ENUM_TYPES_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +/*** END file-header ***/ + +/*** BEGIN file-tail ***/ + +G_END_DECLS + +#endif /* __DBUSMENU_ENUM_TYPES_H__ */ +/*** END file-tail ***/ + +/*** BEGIN file-production ***/ +/* Enumerations from file: "@filename@" */ +#include "@basename@" + +/*** END file-production ***/ + +/*** BEGIN value-header ***/ + +GType @enum_name@_get_type (void) G_GNUC_CONST; +const gchar * @enum_name@_get_nick (@EnumName@ value) G_GNUC_CONST; +@EnumName@ @enum_name@_get_value_from_nick (const gchar * nick) G_GNUC_CONST; + +/** + DBUSMENU_TYPE_@ENUMSHORT@: + + Gets the #GType value for the type associated with the + #@EnumName@ enumerated type. +*/ +#define DBUSMENU_TYPE_@ENUMSHORT@ (@enum_name@_get_type()) + +/*** END value-header ***/ diff --git a/libdbusmenu-gtk/genericmenuitem.c b/libdbusmenu-gtk/genericmenuitem.c index 3652ceb..5488f93 100644 --- a/libdbusmenu-gtk/genericmenuitem.c +++ b/libdbusmenu-gtk/genericmenuitem.c @@ -30,6 +30,8 @@ License version 3 and version 2.1 along with this program. If not, see #include "config.h" #endif +#include <gdk/gdk.h> + #include "genericmenuitem.h" /* @@ -40,6 +42,7 @@ License version 3 and version 2.1 along with this program. If not, see struct _GenericmenuitemPrivate { GenericmenuitemCheckType check_type; GenericmenuitemState state; + GenericmenuitemDisposition disposition; }; /* Private macro */ @@ -102,6 +105,7 @@ genericmenuitem_init (Genericmenuitem *self) self->priv->check_type = GENERICMENUITEM_CHECK_TYPE_NONE; self->priv->state = GENERICMENUITEM_STATE_UNCHECKED; + self->priv->disposition = GENERICMENUITEM_DISPOSITION_NORMAL; return; } @@ -165,18 +169,61 @@ set_label_helper (GtkWidget * widget, gpointer data) style. It should be considered for caching when optimizing. */ static gint -get_hpadding (GtkWidget * widget) +get_toggle_space (GtkWidget * widget) { gint padding = 0; - gtk_widget_style_get(widget, "horizontal-padding", &padding, NULL); + gtk_widget_style_get(widget, "toggle-spacing", &padding, NULL); return padding; } +/* Get the value to put in the span for the disposition */ +static gchar * +get_text_color (GenericmenuitemDisposition disposition, GtkWidget * widget) +{ + struct {const gchar * color_name; const gchar * default_color;} values[] = { + /* NORMAL */ { NULL, NULL}, + /* INFO */ { "informational-color", "blue"}, + /* WARN */ { "warning-color", "orange"}, + /* ALERT */ { "error-color", "red"} + }; + +#if GTK_CHECK_VERSION(3, 0, 0) + GtkStyleContext * context = gtk_widget_get_style_context(widget); + GdkRGBA color; + + if (gtk_style_context_lookup_color(context, values[disposition].color_name, &color)) { + return g_strdup_printf("rgb(%d, %d, %d)", (gint)(color.red * 255), (gint)(color.green * 255), (gint)(color.blue * 255)); + } +#endif + + return g_strdup(values[disposition].default_color); +} + /* Set the label on the item */ static void -set_label (GtkMenuItem * menu_item, const gchar * label) +set_label (GtkMenuItem * menu_item, const gchar * in_label) { - if (label == NULL) return; + if (in_label == NULL) return; + + /* Build a label that might include the colors of the disposition + so that it gets rendered in the menuitem. */ + gchar * local_label = NULL; + switch (GENERICMENUITEM(menu_item)->priv->disposition) { + case GENERICMENUITEM_DISPOSITION_NORMAL: + local_label = g_strdup(in_label); + break; + case GENERICMENUITEM_DISPOSITION_INFORMATIONAL: + case GENERICMENUITEM_DISPOSITION_WARNING: + case GENERICMENUITEM_DISPOSITION_ALERT: { + gchar * color = get_text_color(GENERICMENUITEM(menu_item)->priv->disposition, GTK_WIDGET(menu_item)); + local_label = g_markup_printf_escaped("<span fgcolor=\"%s\">%s</span>", color, in_label); + g_free(color); + break; + } + default: + g_warn_if_reached(); + break; + } GtkWidget * child = gtk_bin_get_child(GTK_BIN(menu_item)); GtkLabel * labelw = NULL; @@ -194,10 +241,10 @@ set_label (GtkMenuItem * menu_item, const gchar * label) /* We need to put the child into a new box and make the box the child of the menu item. Basically we're inserting a box in the middle. */ - GtkWidget * hbox = gtk_hbox_new(FALSE, 0); + GtkWidget * hbox = gtk_hbox_new(FALSE, get_toggle_space(GTK_WIDGET(menu_item))); g_object_ref(child); gtk_container_remove(GTK_CONTAINER(menu_item), child); - gtk_box_pack_start(GTK_BOX(hbox), child, FALSE, FALSE, get_hpadding(GTK_WIDGET(menu_item))); + gtk_box_pack_start(GTK_BOX(hbox), child, FALSE, FALSE, 0); gtk_container_add(GTK_CONTAINER(menu_item), hbox); gtk_widget_show(hbox); g_object_unref(child); @@ -211,10 +258,12 @@ set_label (GtkMenuItem * menu_item, const gchar * label) update the one that we already have. */ if (labelw == NULL) { /* Build it */ - labelw = GTK_LABEL(gtk_accel_label_new(label)); + labelw = GTK_LABEL(gtk_accel_label_new(local_label)); gtk_label_set_use_underline(GTK_LABEL(labelw), TRUE); + gtk_label_set_use_markup(GTK_LABEL(labelw), TRUE); gtk_misc_set_alignment(GTK_MISC(labelw), 0.0, 0.5); gtk_accel_label_set_accel_widget(GTK_ACCEL_LABEL(labelw), GTK_WIDGET(menu_item)); + gtk_label_set_markup_with_mnemonic(labelw, local_label); gtk_widget_show(GTK_WIDGET(labelw)); /* Check to see if it needs to be in the bin for this @@ -222,17 +271,17 @@ set_label (GtkMenuItem * menu_item, const gchar * label) if (child == NULL) { gtk_container_add(GTK_CONTAINER(menu_item), GTK_WIDGET(labelw)); } else { - gtk_box_pack_end(GTK_BOX(child), GTK_WIDGET(labelw), TRUE, TRUE, get_hpadding(GTK_WIDGET(menu_item))); + gtk_box_pack_end(GTK_BOX(child), GTK_WIDGET(labelw), TRUE, TRUE, 0); } } else { /* Oh, just an update. No biggie. */ - if (!g_strcmp0(label, gtk_label_get_label(labelw))) { + if (!g_strcmp0(local_label, gtk_label_get_label(labelw))) { /* The only reason to suppress the update is if we had a label and the value was the same as the one we're getting in. */ suppress_update = TRUE; } else { - gtk_label_set_label(labelw, label); + gtk_label_set_markup_with_mnemonic(labelw, local_label); } } @@ -241,6 +290,12 @@ set_label (GtkMenuItem * menu_item, const gchar * label) g_object_notify(G_OBJECT(menu_item), "label"); } + /* Clean up this */ + if (local_label != NULL) { + g_free(local_label); + local_label = NULL; + } + return; } @@ -401,10 +456,10 @@ genericmenuitem_set_image (Genericmenuitem * menu_item, GtkWidget * image) /* We need to put the child into a new box and make the box the child of the menu item. Basically we're inserting a box in the middle. */ - GtkWidget * hbox = gtk_hbox_new(FALSE, 0); + GtkWidget * hbox = gtk_hbox_new(FALSE, get_toggle_space(GTK_WIDGET(menu_item))); g_object_ref(child); gtk_container_remove(GTK_CONTAINER(menu_item), child); - gtk_box_pack_end(GTK_BOX(hbox), child, TRUE, TRUE, get_hpadding(GTK_WIDGET(menu_item))); + gtk_box_pack_end(GTK_BOX(hbox), child, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(menu_item), hbox); gtk_widget_show(hbox); g_object_unref(child); @@ -429,7 +484,7 @@ genericmenuitem_set_image (Genericmenuitem * menu_item, GtkWidget * image) if (child == NULL) { gtk_container_add(GTK_CONTAINER(menu_item), GTK_WIDGET(image)); } else { - gtk_box_pack_start(GTK_BOX(child), GTK_WIDGET(image), FALSE, FALSE, get_hpadding(GTK_WIDGET(menu_item))); + gtk_box_pack_start(GTK_BOX(child), GTK_WIDGET(image), FALSE, FALSE, 0); } gtk_widget_show(image); @@ -466,3 +521,41 @@ genericmenuitem_get_image (Genericmenuitem * menu_item) return imagew; } + +/** + * genericmenuitem_set_disposition: + * @item: A #Genericmenuitem + * @disposition: The disposition of the item + * + * Sets the disposition of the menuitem. + */ +void +genericmenuitem_set_disposition (Genericmenuitem * item, GenericmenuitemDisposition disposition) +{ + g_return_if_fail(IS_GENERICMENUITEM(item)); + + if (item->priv->disposition == disposition) + return; + + item->priv->disposition = disposition; + + set_label(GTK_MENU_ITEM(item), get_label(GTK_MENU_ITEM(item))); + + return; +} + +/** + * genericmenuitem_get_disposition: + * @item: A #Genericmenuitem + * + * Gets the disposition of the menuitem. + * + * Return value: The disposition of the menuitem. + */ +GenericmenuitemDisposition +genericmenuitem_get_disposition (Genericmenuitem * item) +{ + g_return_val_if_fail(IS_GENERICMENUITEM(item), GENERICMENUITEM_DISPOSITION_NORMAL); + + return item->priv->disposition; +} diff --git a/libdbusmenu-gtk/genericmenuitem.h b/libdbusmenu-gtk/genericmenuitem.h index 5e3c640..0b7df55 100644 --- a/libdbusmenu-gtk/genericmenuitem.h +++ b/libdbusmenu-gtk/genericmenuitem.h @@ -42,11 +42,9 @@ G_BEGIN_DECLS #define IS_GENERICMENUITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GENERICMENUITEM_TYPE)) #define GENERICMENUITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GENERICMENUITEM_TYPE, GenericmenuitemClass)) -typedef struct _Genericmenuitem Genericmenuitem; -typedef struct _GenericmenuitemClass GenericmenuitemClass; -typedef struct _GenericmenuitemPrivate GenericmenuitemPrivate; -typedef enum _GenericmenuitemCheckType GenericmenuitemCheckType; -typedef enum _GenericmenuitemState GenericmenuitemState; +typedef struct _Genericmenuitem Genericmenuitem; +typedef struct _GenericmenuitemClass GenericmenuitemClass; +typedef struct _GenericmenuitemPrivate GenericmenuitemPrivate; /* GenericmenuitemClass: @@ -65,26 +63,61 @@ struct _Genericmenuitem { GenericmenuitemPrivate * priv; }; -enum _GenericmenuitemCheckType { +/** + * GenericmenuitemCheckType: + * @GENERICMENUITEM_CHECK_TYPE_NONE: No check + * @GENERICMENUITEM_CHECK_TYPE_CHECKBOX: Nice little check + * @GENERICMENUITEM_CHECK_TYPE_RADIO: Radio button + * + * Tracks what type of checkmark should be shown on the item + */ +typedef enum { /*< prefix=GENERICMENUITEM_CHECK_TYPE >*/ GENERICMENUITEM_CHECK_TYPE_NONE, GENERICMENUITEM_CHECK_TYPE_CHECKBOX, GENERICMENUITEM_CHECK_TYPE_RADIO -}; - -enum _GenericmenuitemState { +} GenericmenuitemCheckType; + +/** + * GenericmenuitemState: + * @GENERICMENUITEM_STATE_UNCHECKED: No check visisble + * @GENERICMENUITEM_STATE_CHECKED: Check visible + * @GENERICMENUITEM_STATE_INDETERMINATE: We have no clue + * + * What the state of the check mark on the item is + */ +typedef enum { /*< prefix=GENERICMENUITEM_STATE >*/ GENERICMENUITEM_STATE_UNCHECKED, GENERICMENUITEM_STATE_CHECKED, GENERICMENUITEM_STATE_INDETERMINATE -}; - -GType genericmenuitem_get_type (void); -void genericmenuitem_set_check_type (Genericmenuitem * item, - GenericmenuitemCheckType check_type); -void genericmenuitem_set_state (Genericmenuitem * item, - GenericmenuitemState state); -void genericmenuitem_set_image (Genericmenuitem * item, - GtkWidget * image); -GtkWidget * genericmenuitem_get_image (Genericmenuitem * item); +} GenericmenuitemState; + +/** + * GenericmenuitemDisposition: + * @GENERICMENUITEM_DISPOSITION_NORMAL: Normal state + * @GENERICMENUITEM_DISPOSITION_INFORMATIONAL: Item is informational + * @GENERICMENUITEM_DISPOSITION_WARNING: Oh, you should watch out for this one + * @GENERICMENUITEM_DISPOSITION_ALERT: Boom! + * + * What the disposition of the menu item is + */ +typedef enum { /*< prefix=GENERICMENUITEM_DISPOSITION >*/ + GENERICMENUITEM_DISPOSITION_NORMAL, + GENERICMENUITEM_DISPOSITION_INFORMATIONAL, + GENERICMENUITEM_DISPOSITION_WARNING, + GENERICMENUITEM_DISPOSITION_ALERT +} GenericmenuitemDisposition; + +GType genericmenuitem_get_type (void); +void genericmenuitem_set_check_type (Genericmenuitem * item, + GenericmenuitemCheckType check_type); +void genericmenuitem_set_state (Genericmenuitem * item, + GenericmenuitemState state); +void genericmenuitem_set_image (Genericmenuitem * item, + GtkWidget * image); +GtkWidget * genericmenuitem_get_image (Genericmenuitem * item); +void genericmenuitem_set_disposition (Genericmenuitem * item, + GenericmenuitemDisposition disposition); +GenericmenuitemDisposition genericmenuitem_get_disposition (Genericmenuitem * item); G_END_DECLS diff --git a/libdbusmenu-gtk/menu.c b/libdbusmenu-gtk/menu.c index 0b31069..236a596 100644 --- a/libdbusmenu-gtk/menu.c +++ b/libdbusmenu-gtk/menu.c @@ -329,6 +329,18 @@ remove_child_signals (gpointer data, gpointer user_data) return; } +/* Handler for all of the menu items on a root change to ensure that + the menus are hidden before we start going and deleting things. */ +static void +popdown_all (DbusmenuMenuitem * mi, gpointer user_data) +{ + GtkMenu * menu = dbusmenu_gtkclient_menuitem_get_submenu(DBUSMENU_GTKCLIENT(user_data), mi); + if (menu != NULL) { + gtk_menu_popdown(menu); + } + return; +} + /* When the root menuitem changes we need to resetup things so that we're back in the game. */ static void @@ -344,6 +356,8 @@ root_changed (DbusmenuGtkClient * client, DbusmenuMenuitem * newroot, DbusmenuGt g_signal_handlers_disconnect_by_func(G_OBJECT(priv->root), root_child_moved, menu); g_signal_handlers_disconnect_by_func(G_OBJECT(priv->root), root_child_delete, menu); + dbusmenu_menuitem_foreach(priv->root, popdown_all, client); + g_object_unref(priv->root); priv->root = NULL; } diff --git a/libdbusmenu-gtk/menuitem.c b/libdbusmenu-gtk/menuitem.c index ca2bc3e..0f511bc 100644 --- a/libdbusmenu-gtk/menuitem.c +++ b/libdbusmenu-gtk/menuitem.c @@ -275,6 +275,17 @@ dbusmenu_menuitem_property_set_shortcut_menuitem (DbusmenuMenuitem * menuitem, c void dbusmenu_menuitem_property_get_shortcut (DbusmenuMenuitem * menuitem, guint * key, GdkModifierType * modifier) { + guint dummykey; + GdkModifierType dummymodifier; + + if (key == NULL) { + key = &dummykey; + } + + if (modifier == NULL) { + modifier = &dummymodifier; + } + *key = 0; *modifier = 0; @@ -287,15 +298,15 @@ dbusmenu_menuitem_property_get_shortcut (DbusmenuMenuitem * menuitem, guint * ke if (g_variant_n_children(wrapper) != 1) { g_warning("Unable to parse shortcut, too many keys"); - g_variant_unref(wrapper); return; } GVariantIter iter; - g_variant_iter_init(&iter, g_variant_get_child_value(wrapper, 0)); + GVariant * child = g_variant_get_child_value(wrapper, 0); + g_variant_iter_init(&iter, child); gchar * string; - while(g_variant_iter_next(&iter, "s", &string)) { + while(g_variant_iter_loop(&iter, "s", &string)) { if (g_strcmp0(string, DBUSMENU_MENUITEM_SHORTCUT_CONTROL) == 0) { *modifier |= GDK_CONTROL_MASK; } else if (g_strcmp0(string, DBUSMENU_MENUITEM_SHORTCUT_ALT) == 0) { @@ -308,10 +319,10 @@ dbusmenu_menuitem_property_get_shortcut (DbusmenuMenuitem * menuitem, guint * ke GdkModifierType tempmod; gtk_accelerator_parse(string, key, &tempmod); } - - g_free(string); } + g_variant_unref(child); + return; } diff --git a/libdbusmenu-gtk/parser.c b/libdbusmenu-gtk/parser.c index a7f90a2..e988c62 100644 --- a/libdbusmenu-gtk/parser.c +++ b/libdbusmenu-gtk/parser.c @@ -28,7 +28,7 @@ License version 3 and version 2.1 along with this program. If not, see #include "parser.h" #include "menuitem.h" -#include "serializablemenuitem.h" +#include "client.h" #define CACHED_MENUITEM "dbusmenu-gtk-parser-cached-item" #define PARSER_DATA "dbusmenu-gtk-parser-data" @@ -79,9 +79,17 @@ static void item_activated (DbusmenuMenuitem * item, gpointer user_data); static gboolean item_about_to_show (DbusmenuMenuitem * item, gpointer user_data); +static gboolean item_handle_event (DbusmenuMenuitem * item, + const gchar * name, + GVariant * variant, + guint timestamp, + GtkWidget * widget); static void widget_notify_cb (GtkWidget * widget, GParamSpec * pspec, gpointer data); +static void widget_add_cb (GtkWidget * widget, + GtkWidget * child, + gpointer data); static gboolean should_show_image (GtkImage * image); static void menuitem_notify_cb (GtkWidget * widget, GParamSpec * pspec, @@ -164,6 +172,8 @@ parse_data_free (gpointer data) g_signal_handlers_disconnect_matched(pdata->widget, (GSignalMatchType)G_SIGNAL_MATCH_FUNC, 0, 0, NULL, G_CALLBACK(widget_notify_cb), NULL); g_signal_handlers_disconnect_matched(pdata->widget, (GSignalMatchType)G_SIGNAL_MATCH_FUNC, + 0, 0, NULL, G_CALLBACK(widget_add_cb), NULL); + g_signal_handlers_disconnect_matched(pdata->widget, (GSignalMatchType)G_SIGNAL_MATCH_FUNC, 0, 0, NULL, G_CALLBACK(accel_changed), NULL); g_signal_handlers_disconnect_matched(pdata->widget, (GSignalMatchType)G_SIGNAL_MATCH_FUNC, 0, 0, NULL, G_CALLBACK(checkbox_toggled), NULL); @@ -260,6 +270,71 @@ new_menuitem (GtkWidget * widget) return item; } +static gboolean +toggle_widget_visibility (GtkWidget * widget) +{ + gboolean vis = gtk_widget_get_visible (widget); + gtk_widget_set_visible (widget, !vis); + gtk_widget_set_visible (widget, vis); + g_object_unref (G_OBJECT (widget)); + return FALSE; +} + +static void +watch_submenu(DbusmenuMenuitem * mi, GtkWidget * menu) +{ + g_return_if_fail(DBUSMENU_IS_MENUITEM(mi)); + g_return_if_fail(GTK_IS_MENU_SHELL(menu)); + + ParserData *pdata = (ParserData *)g_object_get_data(G_OBJECT(mi), PARSER_DATA); + + pdata->shell = menu; + g_signal_connect (G_OBJECT (menu), + "child-added", + G_CALLBACK (child_added_cb), + mi); + g_signal_connect (G_OBJECT (menu), + "child-removed", + G_CALLBACK (child_removed_cb), + mi); + g_object_add_weak_pointer(G_OBJECT (menu), (gpointer*)&pdata->shell); + + /* Some apps (notably Eclipse RCP apps) don't fill contents of submenus + until the menu is shown. So we fake that by toggling the visibility of + any submenus we come across. Further, these apps need it done with a + delay while they finish initializing, so we put the call in the idle + queue. */ + g_idle_add((GSourceFunc)toggle_widget_visibility, + g_object_ref (G_OBJECT (menu))); +} + +static void +activate_toplevel_item (GtkWidget * item) +{ + /* Make sure that we have a menu item before we start calling + functions that depend on it. This should almost always be + the case. */ + if (!GTK_IS_MENU_ITEM(item)) { + return; + } + + /* If the item is not opening a submenu we don't want to activate + it as that'd cause an action. Like opening a preferences dialog + to the user. That's not a good idea. */ + if (gtk_menu_item_get_submenu(GTK_MENU_ITEM(item)) == NULL) { + return; + } + + GtkWidget * shell = gtk_widget_get_parent (item); + if (!GTK_IS_MENU_BAR (shell)) { + return; + } + + gtk_menu_shell_activate_item (GTK_MENU_SHELL (shell), + item, + TRUE); +} + static void parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse) { @@ -276,33 +351,14 @@ parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse) * Note that this will not force menuitems in submenus to be updated as well. */ if (recurse->parent == NULL && GTK_IS_MENU_BAR(widget)) { - GList *children = gtk_container_get_children (GTK_CONTAINER (widget)); - GList *iter; - - for (iter = children; iter != NULL; iter = iter->next) { - gtk_menu_shell_activate_item (GTK_MENU_SHELL (widget), - iter->data, - TRUE); - } - - g_list_free (children); + gtk_container_foreach (GTK_CONTAINER (widget), + (GtkCallback)activate_toplevel_item, + NULL); } if (recurse->parent == NULL) { recurse->parent = new_menuitem(widget); - - ParserData *pdata = (ParserData *)g_object_get_data(G_OBJECT(recurse->parent), PARSER_DATA); - - pdata->shell = widget; - g_signal_connect (G_OBJECT (widget), - "child-added", - G_CALLBACK (child_added_cb), - recurse->parent); - g_signal_connect (G_OBJECT (widget), - "child-removed", - G_CALLBACK (child_removed_cb), - recurse->parent); - g_object_add_weak_pointer(G_OBJECT (widget), (gpointer*)&pdata->shell); + watch_submenu(recurse->parent, widget); } gtk_container_foreach (GTK_CONTAINER (widget), @@ -438,13 +494,6 @@ sanitize_label (GtkLabel * label) static DbusmenuMenuitem * construct_dbusmenu_for_widget (GtkWidget * widget) { - /* If it's a subclass of our serializable menu item then we can - use its own build function */ - if (DBUSMENU_IS_GTK_SERIALIZABLE_MENU_ITEM(widget)) { - DbusmenuGtkSerializableMenuItem * smi = DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM(widget); - return dbusmenu_gtk_serializable_menu_item_build_menuitem(smi); - } - /* If it's a standard GTK Menu Item we need to do some of our own work */ if (GTK_IS_MENU_ITEM (widget)) { @@ -454,11 +503,11 @@ construct_dbusmenu_for_widget (GtkWidget * widget) gboolean visible = FALSE; gboolean sensitive = FALSE; - if (GTK_IS_SEPARATOR_MENU_ITEM (widget)) + if (GTK_IS_SEPARATOR_MENU_ITEM (widget) || !find_menu_label (widget)) { dbusmenu_menuitem_property_set (mi, - "type", - "separator"); + DBUSMENU_MENUITEM_PROP_TYPE, + DBUSMENU_CLIENT_TYPES_SEPARATOR); visible = gtk_widget_get_visible (widget); sensitive = gtk_widget_get_sensitive (widget); @@ -512,21 +561,18 @@ construct_dbusmenu_for_widget (GtkWidget * widget) GtkWidget *label = find_menu_label (widget); - if (label) - { - // Sometimes, an app will directly find and modify the label - // (like empathy), so watch the label especially for that. - gchar * text = sanitize_label (GTK_LABEL (label)); - dbusmenu_menuitem_property_set (mi, "label", text); - g_free (text); - - pdata->label = label; - g_signal_connect (G_OBJECT (label), - "notify", - G_CALLBACK (label_notify_cb), - mi); - g_object_add_weak_pointer(G_OBJECT (label), (gpointer*)&pdata->label); - } + // Sometimes, an app will directly find and modify the label + // (like empathy), so watch the label especially for that. + gchar * text = sanitize_label (GTK_LABEL (label)); + dbusmenu_menuitem_property_set (mi, "label", text); + g_free (text); + + pdata->label = label; + g_signal_connect (G_OBJECT (label), + "notify", + G_CALLBACK (label_notify_cb), + mi); + g_object_add_weak_pointer(G_OBJECT (label), (gpointer*)&pdata->label); if (GTK_IS_ACTIVATABLE (widget)) { @@ -554,16 +600,7 @@ construct_dbusmenu_for_widget (GtkWidget * widget) GtkWidget *submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(widget)); if (submenu) { - pdata->shell = submenu; - g_signal_connect (G_OBJECT (submenu), - "child-added", - G_CALLBACK (child_added_cb), - mi); - g_signal_connect (G_OBJECT (submenu), - "child-removed", - G_CALLBACK (child_removed_cb), - mi); - g_object_add_weak_pointer(G_OBJECT(submenu), (gpointer*)&pdata->shell); + watch_submenu(mi, submenu); } if (!g_object_get_data (G_OBJECT (widget), "gtk-empty-menu-item") && !GTK_IS_TEAROFF_MENU_ITEM (widget)) @@ -583,6 +620,11 @@ construct_dbusmenu_for_widget (GtkWidget * widget) DBUSMENU_MENUITEM_SIGNAL_ABOUT_TO_SHOW, G_CALLBACK (item_about_to_show), widget); + + g_signal_connect (G_OBJECT (mi), + DBUSMENU_MENUITEM_SIGNAL_EVENT, + G_CALLBACK (item_handle_event), + widget); } dbusmenu_menuitem_property_set_bool (mi, @@ -598,6 +640,11 @@ construct_dbusmenu_for_widget (GtkWidget * widget) G_CALLBACK (widget_notify_cb), mi); + g_signal_connect (widget, + "add", + G_CALLBACK (widget_add_cb), + mi); + return mi; } @@ -760,11 +807,48 @@ find_menu_label (GtkWidget *widget) } static void +recreate_menu_item (DbusmenuMenuitem * parent, DbusmenuMenuitem * child) +{ + if (parent == NULL) + { + /* We need a parent */ + return; + } + ParserData * pdata = g_object_get_data (G_OBJECT (child), PARSER_DATA); + /* Keep a pointer to the GtkMenuItem, as pdata->widget might be + * invalidated when we delete the DbusmenuMenuitem + */ + GtkWidget * menuitem = pdata->widget; + + dbusmenu_menuitem_child_delete (parent, child); + + RecurseContext recurse = {0}; + recurse.toplevel = gtk_widget_get_toplevel(menuitem); + recurse.parent = parent; + + parse_menu_structure_helper(menuitem, &recurse); +} + +static gboolean +recreate_menu_item_in_idle_cb (gpointer data) +{ + DbusmenuMenuitem * child = (DbusmenuMenuitem *)data; + DbusmenuMenuitem * parent = dbusmenu_menuitem_get_parent (child); + g_object_unref (child); + recreate_menu_item (parent, child); + return FALSE; +} + +static void label_notify_cb (GtkWidget *widget, GParamSpec *pspec, gpointer data) { DbusmenuMenuitem *child = (DbusmenuMenuitem *)data; + GValue prop_value = {0}; + + g_value_init (&prop_value, pspec->value_type); + g_object_get_property (G_OBJECT (widget), pspec->name, &prop_value); if (pspec->name == g_intern_static_string ("label")) { @@ -774,6 +858,27 @@ label_notify_cb (GtkWidget *widget, text); g_free (text); } + else if (pspec->name == g_intern_static_string ("parent")) + { + if (GTK_WIDGET (g_value_get_object (&prop_value)) == NULL) + { + /* This label is being removed from its GtkMenuItem. The + * menuitem becomes a separator now. As the client doesn't handle + * changing types so well, we remove the current DbusmenuMenuitem + * and add a new one. + * + * Note, we have to defer this to idle, as we are called before + * bin->child member of our old parent is invalidated. If we go ahead + * and call parse_menu_structure_helper now, the GtkMenuItem will + * still appear to have a label and we never convert it to a separator + */ + g_object_ref (child); + g_idle_add ((GSourceFunc)recreate_menu_item_in_idle_cb, child); + } + } + + g_value_unset(&prop_value); + return; } static void @@ -872,6 +977,54 @@ item_about_to_show (DbusmenuMenuitem *item, gpointer user_data) return TRUE; } +static gboolean +item_handle_event (DbusmenuMenuitem *item, const gchar *name, + GVariant *variant, guint timestamp, GtkWidget *widget) +{ + if (g_strcmp0 (name, DBUSMENU_MENUITEM_EVENT_OPENED) == 0) + { + GtkWidget *submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)); + if (submenu != NULL) + { + // Show the submenu so the app can notice and futz with the menus as + // desired (empathy and geany do this) + gtk_widget_show (submenu); + } + } + else if (g_strcmp0 (name, DBUSMENU_MENUITEM_EVENT_CLOSED) == 0) + { + GtkWidget *submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)); + if (submenu != NULL) + { + // Hide the submenu so the app can notice and futz with the menus as + // desired (empathy and geany do this) + gtk_widget_hide (submenu); + } + } + + return FALSE; // just pass through on everything +} + +static gboolean +handle_first_label (DbusmenuMenuitem *mi) +{ + ParserData *pdata = g_object_get_data (G_OBJECT (mi), PARSER_DATA); + if (!pdata->label) + { + /* GtkMenuItem's can start life as a separator if they have no child + * GtkLabel. In this case, we need to convert the DbusmenuMenuitem from + * a separator to a normal menuitem if the application adds a label. + * As changing types isn't handled too well by the client, we delete + * this menuitem for now and then recreate it + */ + DbusmenuMenuitem * parent = dbusmenu_menuitem_get_parent (mi); + recreate_menu_item (parent, mi); + return TRUE; + } + + return FALSE; +} + static void widget_notify_cb (GtkWidget *widget, GParamSpec *pspec, @@ -891,6 +1044,11 @@ widget_notify_cb (GtkWidget *widget, } else if (pspec->name == g_intern_static_string ("label")) { + if (handle_first_label (child)) + { + return; + } + dbusmenu_menuitem_property_set (child, DBUSMENU_MENUITEM_PROP_LABEL, g_value_get_string (&prop_value)); @@ -952,6 +1110,7 @@ widget_notify_cb (GtkWidget *widget, if (item != NULL) { GtkWidget * menu = GTK_WIDGET (g_value_get_object (&prop_value)); parse_menu_structure_helper(menu, &recurse); + watch_submenu(item, menu); } else { /* Note: it would be really odd that we wouldn't have a cached item, but we should handle that appropriately. */ @@ -962,6 +1121,15 @@ widget_notify_cb (GtkWidget *widget, g_value_unset (&prop_value); } +static void +widget_add_cb (GtkWidget *widget, + GtkWidget *child, + gpointer data) +{ + if (find_menu_label (child) != NULL) + handle_first_label (data); +} + /* A child item was added to a menu we're watching. Let's try to integrate it. */ static void child_added_cb (GtkContainer *menu, GtkWidget *widget, gpointer data) @@ -972,6 +1140,10 @@ child_added_cb (GtkContainer *menu, GtkWidget *widget, gpointer data) recurse.toplevel = gtk_widget_get_toplevel(GTK_WIDGET(menu)); recurse.parent = menuitem; + if (GTK_IS_MENU_BAR(menu)) { + activate_toplevel_item (widget); + } + parse_menu_structure_helper(widget, &recurse); } diff --git a/libdbusmenu-gtk/serializablemenuitem.c b/libdbusmenu-gtk/serializablemenuitem.c deleted file mode 100644 index b560fe3..0000000 --- a/libdbusmenu-gtk/serializablemenuitem.c +++ /dev/null @@ -1,288 +0,0 @@ -/* -An object to act as a base class for easy GTK widgets that can be -transfered over dbusmenu. - -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 "client.h" -#include "serializablemenuitem.h" - -/* - DbusmenuGtkSerializableMenuItemPrivate: - @mi: Menuitem to watch the property changes from -*/ -struct _DbusmenuGtkSerializableMenuItemPrivate { - DbusmenuMenuitem * mi; -}; - -/* Properties */ -enum { - PROP_0, - PROP_MENUITEM -}; - -/* Private macro, only used in object init */ -#define DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_GET_PRIVATE(o) \ -(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_GTK_SERIALIZABLE_MENU_ITEM, DbusmenuGtkSerializableMenuItemPrivate)) - -/* Function prototypes */ -static void dbusmenu_gtk_serializable_menu_item_class_init (DbusmenuGtkSerializableMenuItemClass *klass); -static void dbusmenu_gtk_serializable_menu_item_init (DbusmenuGtkSerializableMenuItem *self); -static void dbusmenu_gtk_serializable_menu_item_dispose (GObject *object); -static void dbusmenu_gtk_serializable_menu_item_finalize (GObject *object); -static void set_property (GObject * obj, - guint id, - const GValue * value, - GParamSpec * pspec); -static void get_property (GObject * obj, - guint id, - GValue * value, - GParamSpec * pspec); - -/* GObject boiler plate */ -G_DEFINE_TYPE (DbusmenuGtkSerializableMenuItem, dbusmenu_gtk_serializable_menu_item, GTK_TYPE_MENU_ITEM); - -/* Initialize the stuff in the class structure */ -static void -dbusmenu_gtk_serializable_menu_item_class_init (DbusmenuGtkSerializableMenuItemClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (DbusmenuGtkSerializableMenuItemPrivate)); - - object_class->dispose = dbusmenu_gtk_serializable_menu_item_dispose; - object_class->finalize = dbusmenu_gtk_serializable_menu_item_finalize; - object_class->set_property = set_property; - object_class->get_property = get_property; - - g_object_class_install_property (object_class, PROP_MENUITEM, - g_param_spec_object(DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_PROP_MENUITEM, "DBusmenu Menuitem attached to item", - "A menuitem who's properties are being watched and where changes should be watched for updates. It is the responsibility of subclasses to set up the signal handlers for those property changes.", - DBUSMENU_TYPE_MENUITEM, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - return; -} - -/* Initialize the object structures and private structure */ -static void -dbusmenu_gtk_serializable_menu_item_init (DbusmenuGtkSerializableMenuItem *self) -{ - self->priv = DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_GET_PRIVATE(self); - - self->priv->mi = NULL; - - return; -} - -/* Free all references to objects */ -static void -dbusmenu_gtk_serializable_menu_item_dispose (GObject *object) -{ - DbusmenuGtkSerializableMenuItem * smi = DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM(object); - g_return_if_fail(smi != NULL); - - if (smi->priv->mi != NULL) { - g_object_unref(G_OBJECT(smi->priv->mi)); - smi->priv->mi = NULL; - } - - - G_OBJECT_CLASS (dbusmenu_gtk_serializable_menu_item_parent_class)->dispose (object); - return; -} - -/* Free memory */ -static void -dbusmenu_gtk_serializable_menu_item_finalize (GObject *object) -{ - - - - G_OBJECT_CLASS (dbusmenu_gtk_serializable_menu_item_parent_class)->finalize (object); - return; -} - -/* Set an object property */ -static void -set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec) -{ - DbusmenuGtkSerializableMenuItem * smi = DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM(obj); - - switch (id) { - case PROP_MENUITEM: - smi->priv->mi = g_value_get_object(value); - break; - default: - g_return_if_reached(); - break; - } - - return; -} - -/* Get an object property */ -static void -get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec) -{ - DbusmenuGtkSerializableMenuItem * smi = DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM(obj); - - switch (id) { - case PROP_MENUITEM: - g_value_set_object(value, smi->priv->mi); - break; - default: - g_return_if_reached(); - break; - } - - return; -} - -/** - * dbusmenu_gtk_serializable_menu_item_build_menuitem: - * @smi: #DbusmenuGtkSerializableMenuItem to build a #DbusmenuMenuitem mirroring - * - * This function is for menu items that are instanciated from - * GTK and have their properites set using GTK functions. This - * builds a #DbusmenuMenuitem that then has the properties that - * should be sent over the bus to create a new item of this - * type on the other side. - * - * Return value: (transfer full): A #DbusmenuMenuitem who's values will be - * set by this object. - */ -DbusmenuMenuitem * -dbusmenu_gtk_serializable_menu_item_build_menuitem (DbusmenuGtkSerializableMenuItem * smi) -{ - g_return_val_if_fail(DBUSMENU_IS_GTK_SERIALIZABLE_MENU_ITEM(smi), NULL); - - DbusmenuGtkSerializableMenuItemClass * klass = DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_GET_CLASS(smi); - if (klass->build_dbusmenu_menuitem != NULL) { - return klass->build_dbusmenu_menuitem(smi); - } - - return NULL; -} - -/* Callback to the generic type handler */ -typedef struct _type_handler_t type_handler_t; -struct _type_handler_t { - DbusmenuGtkSerializableMenuItemClass * class; - GType type; -}; - -/* Handle the type with this item. */ -static gboolean -type_handler (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data) -{ - type_handler_t * th = (type_handler_t *)user_data; - - DbusmenuGtkSerializableMenuItem * smi = DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM(g_object_new(th->type, NULL)); - g_return_val_if_fail(smi != NULL, FALSE); - - dbusmenu_gtk_serializable_menu_item_set_menuitem(smi, newitem); - dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, GTK_MENU_ITEM(smi), parent); - - return TRUE; -} - -/* Destruction is inevitable */ -static void -type_destroy_handler (gpointer user_data) -{ - g_return_if_fail(user_data != NULL); - type_handler_t * th = (type_handler_t *)user_data; - g_type_class_unref(th->class); - g_free(user_data); - return; -} - -/** - * dbusmenu_gtk_serializable_menu_item_register_to_client: - * @client: #DbusmenuClient that we should register a type at. - * @item_type: The #GType of a class that is a subclass of #DbusmenuGtkSerializableMenuItem - * - * Registers a generic handler for dealing with all subclasses of - * #DbusmenuGtkSerializableMenuItem. This handler responds to the callback, - * creates a new object and attaches it to the appropriate #DbusmenuMenuitem - * object. - */ -void -dbusmenu_gtk_serializable_menu_item_register_to_client (DbusmenuClient * client, GType item_type) -{ - g_return_if_fail(g_type_is_a(item_type, DBUSMENU_TYPE_GTK_SERIALIZABLE_MENU_ITEM)); - - gpointer type_class = g_type_class_ref(item_type); - g_return_if_fail(type_class != NULL); - - DbusmenuGtkSerializableMenuItemClass * class = DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_CLASS(type_class); - - if (class->get_type_string == NULL) { - g_type_class_unref(type_class); - g_error("No 'get_type_string' in subclass of DbusmenuGtkSerializableMenuItem"); - return; - } - - /* Register type */ - type_handler_t * th = g_new0(type_handler_t, 1); - th->class = class; - th->type = item_type; - if (!dbusmenu_client_add_type_handler_full(client, class->get_type_string(), type_handler, th, type_destroy_handler)) { - type_destroy_handler(th); - } - - /* Register defaults */ - /* TODO: Need API on another branch */ - - return; -} - -/** - * dbusmenu_gtk_serializable_menu_item_set_menuitem: - * @smi: #DbusmenuGtkSerializableMenuItem to set the @DbusmenuGtkSerializableMenuItem::dbusmenu-menuitem of - * @mi: Menuitem to get the properties from - * - * This function is used on the server side to signal to the object - * that it should get its' property change events from @mi instead - * of expecting calls to its' API. A call to this function sets the - * property and subclasses should listen to the notify signal to - * pick up this property being set. - */ -void -dbusmenu_gtk_serializable_menu_item_set_menuitem (DbusmenuGtkSerializableMenuItem * smi, DbusmenuMenuitem * mi) -{ - g_return_if_fail(DBUSMENU_IS_GTK_SERIALIZABLE_MENU_ITEM(smi)); - g_return_if_fail(mi != NULL); - - smi->priv->mi = mi; - g_object_notify(G_OBJECT(smi), DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_PROP_MENUITEM); - - return; -} diff --git a/libdbusmenu-gtk/serializablemenuitem.h b/libdbusmenu-gtk/serializablemenuitem.h deleted file mode 100644 index 9bea89f..0000000 --- a/libdbusmenu-gtk/serializablemenuitem.h +++ /dev/null @@ -1,133 +0,0 @@ -/* -An object to act as a base class for easy GTK widgets that can be -transfered over dbusmenu. - -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_GTK_SERIALIZABLE_MENU_ITEM_H__ -#define DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_H__ 1 - -#include <glib.h> -#include <glib-object.h> -#include <gtk/gtk.h> -#include <libdbusmenu-glib/menuitem.h> -#include <libdbusmenu-glib/client.h> - -G_BEGIN_DECLS - -#define DBUSMENU_TYPE_GTK_SERIALIZABLE_MENU_ITEM (dbusmenu_gtk_serializable_menu_item_get_type ()) -#define DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DBUSMENU_TYPE_GTK_SERIALIZABLE_MENU_ITEM, DbusmenuGtkSerializableMenuItem)) -#define DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUSMENU_TYPE_GTK_SERIALIZABLE_MENU_ITEM, DbusmenuGtkSerializableMenuItemClass)) -#define DBUSMENU_IS_GTK_SERIALIZABLE_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DBUSMENU_TYPE_GTK_SERIALIZABLE_MENU_ITEM)) -#define DBUSMENU_IS_GTK_SERIALIZABLE_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_TYPE_GTK_SERIALIZABLE_MENU_ITEM)) -#define DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_GTK_SERIALIZABLE_MENU_ITEM, DbusmenuGtkSerializableMenuItemClass)) - -/** - * DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_PROP_MENUITEM: - * - * String to access property #DbusmenuGtkSerializableMenuItem:dbusmenu-menuitem - */ -#define DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_PROP_MENUITEM "dbusmenu-menuitem" - -typedef struct _DbusmenuGtkSerializableMenuItem DbusmenuGtkSerializableMenuItem; -typedef struct _DbusmenuGtkSerializableMenuItemClass DbusmenuGtkSerializableMenuItemClass; -typedef struct _DbusmenuGtkSerializableMenuItemPrivate DbusmenuGtkSerializableMenuItemPrivate; - -/** - DbusmenuGtkSerializableMenuItemClass: - @parent_class: Inherit from GtkMenuItem - @get_type_string: Static function to get a string describing this type - @get_default_properties: Return a hashtable of defaults for the menu item type - @build_dbusmenu_menuitem: Build a menuitem that can be sent over dbus - @_dbusmenu_gtk_serializable_menu_item_reserved1: Reserved for future use. - @_dbusmenu_gtk_serializable_menu_item_reserved2: Reserved for future use. - @_dbusmenu_gtk_serializable_menu_item_reserved3: Reserved for future use. - @_dbusmenu_gtk_serializable_menu_item_reserved4: Reserved for future use. - @_dbusmenu_gtk_serializable_menu_item_reserved5: Reserved for future use. - @_dbusmenu_gtk_serializable_menu_item_reserved6: Reserved for future use. - - Signals and functions for #DbusmenuGtkSerializableMenuItem. -*/ -struct _DbusmenuGtkSerializableMenuItemClass { - GtkMenuItemClass parent_class; - - /* Subclassable functions */ - const gchar * (*get_type_string) (void); - GHashTable * (*get_default_properties) (void); - - DbusmenuMenuitem * (*build_dbusmenu_menuitem) (DbusmenuGtkSerializableMenuItem * smi); - - /* Signals */ - - - - /* Empty Space */ - /*< Private >*/ - void (*_dbusmenu_gtk_serializable_menu_item_reserved1) (void); - void (*_dbusmenu_gtk_serializable_menu_item_reserved2) (void); - void (*_dbusmenu_gtk_serializable_menu_item_reserved3) (void); - void (*_dbusmenu_gtk_serializable_menu_item_reserved4) (void); - void (*_dbusmenu_gtk_serializable_menu_item_reserved5) (void); - void (*_dbusmenu_gtk_serializable_menu_item_reserved6) (void); -}; - -/** - DbusmenuGtkSerializableMenuItem: - @parent: Inherit from GtkMenuItem - @priv: Blind structure of private variables - - The Serializable Menuitem provides a way for menu items to be created - that can easily be picked up by the Dbusmenu GTK Parser. This way - you can create custom items, and transport them across dbusmenu to - your menus or the appmenu on the other side of the bus. By providing - these function the parser has enough information to both serialize, and - deserialize on the other side, the menuitem you've so carefully created. -*/ -struct _DbusmenuGtkSerializableMenuItem { - GtkMenuItem parent; - - DbusmenuGtkSerializableMenuItemPrivate * priv; -}; - -GType dbusmenu_gtk_serializable_menu_item_get_type (void); - -DbusmenuMenuitem * dbusmenu_gtk_serializable_menu_item_build_menuitem (DbusmenuGtkSerializableMenuItem * smi); -void dbusmenu_gtk_serializable_menu_item_register_to_client (DbusmenuClient * client, GType item_type); -void dbusmenu_gtk_serializable_menu_item_set_menuitem (DbusmenuGtkSerializableMenuItem * smi, DbusmenuMenuitem * mi); - -/** - SECTION:serializablemenuitem - @short_description: A way to build #GtkMenuItems that can be sent over Dbusmenu - @stability: Unstable - @include: libdbusmenu-gtk/serializablemenuitem.h - - Menuitems can subclass from this instead of #GtkMenuItem and - by providing the appropriate functions Dbusmenu will be able - to parse them and send them over the bus. -*/ - -G_END_DECLS - -#endif diff --git a/tests/Makefile.am b/tests/Makefile.am index a2c0716..a4e8682 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -12,14 +12,25 @@ TESTS = \ test-glib-properties \ test-glib-proxy \ test-glib-simple-items \ - test-glib-submenu \ - test-json \ + test-glib-submenu + +if WANT_DBUSMENUDUMPER +TESTS += \ + test-json +endif + +if WANT_LIBDBUSMENUGTK +TESTS += \ test-gtk-objects-test \ test-gtk-label \ test-gtk-shortcut \ test-gtk-reorder \ test-gtk-submenu \ test-gtk-parser-test +endif + +XFAIL_TESTS = \ + test-glib-proxy # The Python test only work on the system copy of # dbusmenu, so while they can be usefule they're not @@ -41,20 +52,28 @@ check_PROGRAMS = \ test-glib-proxy-client \ test-glib-proxy-server \ test-glib-proxy-proxy \ - test-gtk-objects \ test-glib-submenu-client \ test-glib-submenu-server \ + test-glib-simple-items + +if WANT_DBUSMENUDUMPER +check_PROGRAMS += \ + test-json-client \ + test-json-server +endif + +if WANT_LIBDBUSMENUGTK +check_PROGRAMS += \ + test-gtk-objects \ test-gtk-label-client \ test-gtk-label-server \ test-gtk-shortcut-client \ test-gtk-shortcut-server \ - test-glib-simple-items \ test-gtk-reorder-server \ - test-json-client \ - test-json-server \ test-gtk-submenu-server \ test-gtk-submenu-client \ test-gtk-parser +endif XVFB_RUN=". $(srcdir)/run-xvfb.sh" @@ -399,7 +418,7 @@ test_gtk_objects_CFLAGS = \ test_gtk_objects_LDADD = \ ../libdbusmenu-glib/libdbusmenu-glib.la \ - ../libdbusmenu-gtk/libdbusmenu-gtk.la \ + ../libdbusmenu-gtk/libdbusmenu-gtk$(VER).la \ $(DBUSMENUGLIB_LIBS) \ $(DBUSMENUGTK_LIBS) @@ -429,7 +448,7 @@ test_gtk_parser_CFLAGS = \ test_gtk_parser_LDADD = \ ../libdbusmenu-glib/libdbusmenu-glib.la \ - ../libdbusmenu-gtk/libdbusmenu-gtk.la \ + ../libdbusmenu-gtk/libdbusmenu-gtk$(VER).la \ $(DBUSMENUGLIB_LIBS) \ $(DBUSMENUGTK_LIBS) @@ -456,7 +475,7 @@ test_gtk_label_server_CFLAGS = \ test_gtk_label_server_LDADD = \ ../libdbusmenu-glib/libdbusmenu-glib.la \ - ../libdbusmenu-gtk/libdbusmenu-gtk.la \ + ../libdbusmenu-gtk/libdbusmenu-gtk$(VER).la \ libdbusmenu-jsonloader.la \ $(DBUSMENUGTK_LIBS) \ $(DBUSMENUTESTS_LIBS) @@ -472,7 +491,7 @@ test_gtk_label_client_CFLAGS = \ test_gtk_label_client_LDADD = \ ../libdbusmenu-glib/libdbusmenu-glib.la \ - ../libdbusmenu-gtk/libdbusmenu-gtk.la \ + ../libdbusmenu-gtk/libdbusmenu-gtk$(VER).la \ $(DBUSMENUGTK_LIBS) \ $(DBUSMENUTESTS_LIBS) @@ -497,7 +516,7 @@ test_gtk_shortcut_server_CFLAGS = \ test_gtk_shortcut_server_LDADD = \ ../libdbusmenu-glib/libdbusmenu-glib.la \ - ../libdbusmenu-gtk/libdbusmenu-gtk.la \ + ../libdbusmenu-gtk/libdbusmenu-gtk$(VER).la \ $(DBUSMENUGTK_LIBS) \ $(DBUSMENUTESTS_LIBS) @@ -512,7 +531,7 @@ test_gtk_shortcut_client_CFLAGS = \ test_gtk_shortcut_client_LDADD = \ ../libdbusmenu-glib/libdbusmenu-glib.la \ - ../libdbusmenu-gtk/libdbusmenu-gtk.la \ + ../libdbusmenu-gtk/libdbusmenu-gtk$(VER).la \ $(DBUSMENUGTK_LIBS) \ $(DBUSMENUTESTS_LIBS) @@ -550,7 +569,7 @@ test_gtk_reorder_server_CFLAGS = \ test_gtk_reorder_server_LDADD = \ ../libdbusmenu-glib/libdbusmenu-glib.la \ - ../libdbusmenu-gtk/libdbusmenu-gtk.la \ + ../libdbusmenu-gtk/libdbusmenu-gtk$(VER).la \ $(DBUSMENUGTK_LIBS) \ $(DBUSMENUTESTS_LIBS) @@ -575,7 +594,7 @@ test_gtk_submenu_server_CFLAGS = \ test_gtk_submenu_server_LDADD = \ ../libdbusmenu-glib/libdbusmenu-glib.la \ - ../libdbusmenu-gtk/libdbusmenu-gtk.la \ + ../libdbusmenu-gtk/libdbusmenu-gtk$(VER).la \ $(DBUSMENUGTK_LIBS) \ $(DBUSMENUTESTS_LIBS) @@ -590,7 +609,7 @@ test_gtk_submenu_client_CFLAGS = \ test_gtk_submenu_client_LDADD = \ ../libdbusmenu-glib/libdbusmenu-glib.la \ - ../libdbusmenu-gtk/libdbusmenu-gtk.la \ + ../libdbusmenu-gtk/libdbusmenu-gtk$(VER).la \ $(DBUSMENUGTK_LIBS) \ $(DBUSMENUTESTS_LIBS) @@ -658,3 +677,8 @@ distclean-local: DISTCLEANFILES += \ $(filter-out %.py, $(TESTS)) +if USE_GTK3 +VER=3 +else +VER= +endif diff --git a/tests/test-gtk-label-client.c b/tests/test-gtk-label-client.c index 14eb5bd..415c6b0 100644 --- a/tests/test-gtk-label-client.c +++ b/tests/test-gtk-label-client.c @@ -160,7 +160,7 @@ main (int argc, char ** argv) GtkWidget * menuitem = gtk_menu_item_new_with_label("Test"); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), GTK_WIDGET(dbusmenu_gtkmenu_new ("glib.label.test", "/org/test"))); gtk_widget_show(menuitem); - gtk_menu_bar_append(menubar, menuitem); + gtk_menu_shell_append(GTK_MENU_SHELL(menubar), menuitem); gtk_widget_show(menubar); gtk_container_add(GTK_CONTAINER(window), menubar); gtk_window_set_title(GTK_WINDOW(window), "libdbusmenu-gtk test"); diff --git a/tests/test-gtk-label.json b/tests/test-gtk-label.json index 0189fbe..86d18e8 100644 --- a/tests/test-gtk-label.json +++ b/tests/test-gtk-label.json @@ -367,5 +367,58 @@ QmCC", "toggle-state": -1 } ] + }, + {"id": 1, "type": "standard", + "label": "value1", + "submenu": [ + {"id": 60, + "label": "Disposition Normal", + "disposition": "normal" + }, + {"id": 61, + "label": "Disposition Info", + "disposition": "informational" + }, + {"id": 62, + "label": "Disposition Warning", + "disposition": "warning" + }, + {"id": 63, + "label": "Disposition Alert", + "disposition": "alert" + } + ] + }, + {"id": 1, "type": "standard", + "label": "value1", + "submenu": [ + {"id": 70, + "label": "_Mnemonic" + }, + {"id": 71, + "label": "M_nemonic" + }, + {"id": 72, + "label": "Mn_emonic" + }, + {"id": 73, + "label": "Mne_monic" + }, + {"id": 74, + "label": "Mnem_onic" + }, + {"id": 75, + "label": "Mnemo_nic" + }, + {"id": 76, + "label": "Mnemon_ic" + }, + {"id": 77, + "label": "Mnemoni_c" + }, + {"id": 78, + "label": "__Underline__" + } + ] } ] diff --git a/tests/test-gtk-objects.c b/tests/test-gtk-objects.c index 30fc022..4bd26d3 100644 --- a/tests/test-gtk-objects.c +++ b/tests/test-gtk-objects.c @@ -97,7 +97,11 @@ test_object_prop_shortcut (void) g_assert(G_IS_OBJECT(item)); g_assert(DBUSMENU_IS_MENUITEM(item)); +#if GTK_CHECK_VERSION(3,0,0) + guint key = GDK_KEY_c; +#else guint key = GDK_c; +#endif GdkModifierType modifier = GDK_CONTROL_MASK; /* Set a shortcut */ diff --git a/tests/test-gtk-parser.c b/tests/test-gtk-parser.c index b66b46a..afd6194 100644 --- a/tests/test-gtk-parser.c +++ b/tests/test-gtk-parser.c @@ -28,6 +28,8 @@ test_parser_runs (void) { GtkWidget * gmi = gtk_menu_item_new_with_label("Test Item"); g_assert(gmi != NULL); + g_object_ref_sink(gmi); + DbusmenuMenuitem * mi = dbusmenu_gtk_parse_menu_structure(gmi); g_assert(mi != NULL); @@ -55,9 +57,23 @@ const gchar * test_parser_children_builder = "</object>" "</interface>"; +/* Checks the log level to let warnings not stop the program */ +static gboolean +test_parser_children_log_handler (const gchar * domain, GLogLevelFlags level, const gchar * message, gpointer user_data) +{ + if (level & (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG)) { + return FALSE; + } + + return TRUE; +} + /* Ensure the parser can find children */ static void test_parser_children (void) { + /* Hide GTK errors */ + g_test_log_set_fatal_handler(test_parser_children_log_handler, NULL); + GtkBuilder * builder = gtk_builder_new(); g_assert(builder != NULL); diff --git a/tests/test-gtk-shortcut-client.c b/tests/test-gtk-shortcut-client.c index 003885c..259e177 100644 --- a/tests/test-gtk-shortcut-client.c +++ b/tests/test-gtk-shortcut-client.c @@ -53,7 +53,7 @@ main (int argc, char ** argv) gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), GTK_WIDGET(dmenu)); gtk_widget_show(menuitem); - gtk_menu_bar_append(menubar, menuitem); + gtk_menu_shell_append(GTK_MENU_SHELL(menubar), menuitem); gtk_widget_show(menubar); gtk_container_add(GTK_CONTAINER(window), menubar); gtk_window_set_title(GTK_WINDOW(window), "libdbusmenu-gtk test"); diff --git a/tests/test-gtk-shortcut-server.c b/tests/test-gtk-shortcut-server.c index b205d03..5df4881 100644 --- a/tests/test-gtk-shortcut-server.c +++ b/tests/test-gtk-shortcut-server.c @@ -46,7 +46,11 @@ build_menu (void) item = dbusmenu_menuitem_new(); dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, "Control-L"); +#if GTK_CHECK_VERSION(3,0,0) + dbusmenu_menuitem_property_set_shortcut(item, GDK_KEY_l, GDK_CONTROL_MASK); +#else dbusmenu_menuitem_property_set_shortcut(item, GDK_l, GDK_CONTROL_MASK); +#endif dbusmenu_menuitem_child_append(root, item); g_object_unref(item); diff --git a/tests/test-gtk-submenu-client.c b/tests/test-gtk-submenu-client.c index ec46122..81637f1 100644 --- a/tests/test-gtk-submenu-client.c +++ b/tests/test-gtk-submenu-client.c @@ -127,7 +127,7 @@ main (int argc, char ** argv) GtkWidget * menuitem = gtk_menu_item_new_with_label("Test"); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), GTK_WIDGET(dbusmenu_gtkmenu_new ("glib.label.test", "/org/test"))); gtk_widget_show(menuitem); - gtk_menu_bar_append(menubar, menuitem); + gtk_menu_shell_append(GTK_MENU_SHELL(menubar), menuitem); gtk_widget_show(menubar); gtk_container_add(GTK_CONTAINER(window), menubar); gtk_window_set_title(GTK_WINDOW(window), "libdbusmenu-gtk test"); diff --git a/tools/Makefile.am b/tools/Makefile.am index 48993f1..029b73e 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,7 +1,11 @@ SUBDIRS = testapp -libexec_PROGRAMS = dbusmenu-dumper +libexec_PROGRAMS = + +if WANT_DBUSMENUDUMPER +libexec_PROGRAMS += dbusmenu-dumper +endif libexec_SCRIPTS = dbusmenu-bench diff --git a/tools/testapp/Makefile.am b/tools/testapp/Makefile.am index 39de532..e2bbde7 100644 --- a/tools/testapp/Makefile.am +++ b/tools/testapp/Makefile.am @@ -1,11 +1,9 @@ -if USE_GTK3 -VER=3 -else -VER= -endif +libexec_PROGRAMS = -libexec_PROGRAMS = dbusmenu-testapp +if WANT_TESTS +libexec_PROGRAMS += dbusmenu-testapp +endif dbusmenu_testapp_SOURCES = \ main.c @@ -18,6 +16,4 @@ dbusmenu_testapp_CFLAGS = \ dbusmenu_testapp_LDADD = \ $(builddir)/../../libdbusmenu-glib/libdbusmenu-glib.la \ - $(builddir)/../../libdbusmenu-gtk/libdbusmenu-gtk$(VER).la \ - $(DBUSMENUGTK_LIBS) \ $(DBUSMENUTESTS_LIBS) |