diff options
38 files changed, 1060 insertions, 681 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 0618dd1..6276327 100644 --- a/configure.ac +++ b/configure.ac @@ -1,11 +1,11 @@ -AC_INIT(libdbusmenu, 0.4.2, 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.2, [-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=13 +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 45cf0d1..588c940 100644 --- a/libdbusmenu-glib/client.c +++ b/libdbusmenu-glib/client.c @@ -1239,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; @@ -1516,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; 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 aa479b1..a5caf0b 100644 --- a/libdbusmenu-glib/defaults.c +++ b/libdbusmenu-glib/defaults.c @@ -87,6 +87,7 @@ dbusmenu_defaults_init (DbusmenuDefaults *self) 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("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 bdaf49f..4e037ee 100644 --- a/libdbusmenu-glib/menuitem.c +++ b/libdbusmenu-glib/menuitem.c @@ -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 7bfac3a..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; @@ -1193,9 +1199,11 @@ bus_get_layout (DbusmenuServer * server, GVariant * params, GDBusMethodInvocatio 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; @@ -1208,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) { @@ -1254,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); @@ -1298,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); @@ -1326,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; @@ -1363,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); @@ -1376,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; } } @@ -1395,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"); @@ -1433,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, @@ -1467,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); @@ -1519,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; @@ -1564,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 aa36445..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 = \ @@ -129,6 +144,7 @@ endif # VAPI Files ######################### +if HAVE_VALA if HAVE_INTROSPECTION vapidir = $(datadir)/vala/vapi @@ -152,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 7f05d46..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 */ @@ -571,7 +692,8 @@ process_submenu (DbusmenuMenuitem * mi, GtkMenuItem * gmi, GVariant * variant, D } else { /* We need to build a menu for these guys to live in. */ GtkMenu * menu = GTK_MENU(gtk_menu_new()); - g_object_set_data(G_OBJECT(mi), data_menu, menu); + 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)); @@ -581,6 +703,20 @@ process_submenu (DbusmenuMenuitem * mi, GtkMenuItem * gmi, GVariant * variant, D 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 @@ -600,6 +736,8 @@ menu_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * variant, Db 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; @@ -616,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. */ @@ -731,11 +856,8 @@ 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), client); @@ -746,15 +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 */ @@ -801,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); } } @@ -905,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/parser.c b/libdbusmenu-gtk/parser.c index c627854..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" @@ -87,6 +87,9 @@ static gboolean item_handle_event (DbusmenuMenuitem * item, 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, @@ -169,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); @@ -265,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) { @@ -281,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), @@ -443,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)) { @@ -462,8 +506,8 @@ construct_dbusmenu_for_widget (GtkWidget * 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); @@ -556,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)) @@ -605,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; } @@ -836,6 +876,9 @@ label_notify_cb (GtkWidget *widget, g_idle_add ((GSourceFunc)recreate_menu_item_in_idle_cb, child); } } + + g_value_unset(&prop_value); + return; } static void @@ -962,6 +1005,26 @@ item_handle_event (DbusmenuMenuitem *item, const gchar *name, 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, @@ -981,20 +1044,11 @@ widget_notify_cb (GtkWidget *widget, } else if (pspec->name == g_intern_static_string ("label")) { - ParserData *pdata = g_object_get_data (G_OBJECT (child), PARSER_DATA); - if (!pdata->label) + if (handle_first_label (child)) { - /* 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 (child); - recreate_menu_item (parent, child); return; } - + dbusmenu_menuitem_property_set (child, DBUSMENU_MENUITEM_PROP_LABEL, g_value_get_string (&prop_value)); @@ -1056,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. */ @@ -1066,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) @@ -1076,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) |