aboutsummaryrefslogtreecommitdiff
path: root/libdbusmenu-gtk
diff options
context:
space:
mode:
Diffstat (limited to 'libdbusmenu-gtk')
-rw-r--r--libdbusmenu-gtk/Makefile.am41
-rw-r--r--libdbusmenu-gtk/client.c521
-rw-r--r--libdbusmenu-gtk/client.h42
-rw-r--r--libdbusmenu-gtk/dbusmenu-gtk-0.4.pc.in2
-rw-r--r--libdbusmenu-gtk/dbusmenu-gtk.h1
-rw-r--r--libdbusmenu-gtk/dbusmenu-gtk3-0.4.pc.in2
-rw-r--r--libdbusmenu-gtk/genericmenuitem-enum-types.c.in116
-rw-r--r--libdbusmenu-gtk/genericmenuitem-enum-types.h.in65
-rw-r--r--libdbusmenu-gtk/genericmenuitem.c173
-rw-r--r--libdbusmenu-gtk/genericmenuitem.h71
-rw-r--r--libdbusmenu-gtk/menu.c89
-rw-r--r--libdbusmenu-gtk/menu.h33
-rw-r--r--libdbusmenu-gtk/menuitem.c163
-rw-r--r--libdbusmenu-gtk/menuitem.h15
-rw-r--r--libdbusmenu-gtk/parser.c854
-rw-r--r--libdbusmenu-gtk/parser.h15
-rw-r--r--libdbusmenu-gtk/serializablemenuitem.c288
-rw-r--r--libdbusmenu-gtk/serializablemenuitem.h115
18 files changed, 1747 insertions, 859 deletions
diff --git a/libdbusmenu-gtk/Makefile.am b/libdbusmenu-gtk/Makefile.am
index f3556e9..4ec464b 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) \
@@ -78,32 +93,31 @@ if INTROSPECTION_TEN
INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir) \
--warn-all \
--add-include-path=$(top_builddir)/libdbusmenu-glib \
- $(addprefix --c-include=libdbusmenu-gtk/, $(introspection_sources)) \
+ $(addprefix --c-include=libdbusmenu-gtk/, $(libdbusmenu_gtkinclude_HEADERS)) \
--symbol-prefix=dbusmenu \
--identifier-prefix=DbusmenuGtk
else
INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir) \
--warn-all \
--add-include-path=$(top_builddir)/libdbusmenu-glib \
- $(addprefix --c-include=libdbusmenu-gtk/, $(introspection_sources))
+ $(addprefix --c-include=libdbusmenu-gtk/, $(libdbusmenu_gtkinclude_HEADERS))
endif
INTROSPECTION_COMPILER_ARGS = --includedir=$(builddir) --includedir=$(top_builddir)/libdbusmenu-glib
if HAVE_INTROSPECTION
-introspection_sources = $(libdbusmenu_gtkinclude_HEADERS)
+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 = \
GObject-2.0 \
$(GTKGIR) \
- Dbusmenu-Glib-0.4
+ Dbusmenu-0.4
DbusmenuGtk_0_4_gir_CFLAGS = $(DBUSMENUGTK_CFLAGS) -I$(top_srcdir)
DbusmenuGtk_0_4_gir_LIBS = libdbusmenu-gtk$(VER).la
DbusmenuGtk_0_4_gir_FILES = $(addprefix $(srcdir)/, $(introspection_sources))
DbusmenuGtk_0_4_gir_NAMESPACE = DbusmenuGtk$(VER)
-DbusmenuGtk_0_4_gir_SCANNERFLAGS = $(INTROSPECTION_SCANNER_ARGS)
DbusmenuGtk_0_4_gir_EXPORT_PACKAGES = dbusmenu-gtk$(VER)-0.4
# We duplicate these for the same reason as libdbusmenu_gtk3includedir above
@@ -112,7 +126,6 @@ DbusmenuGtk3_0_4_gir_CFLAGS = $(DbusmenuGtk_0_4_gir_CFLAGS)
DbusmenuGtk3_0_4_gir_LIBS = $(DbusmenuGtk_0_4_gir_LIBS)
DbusmenuGtk3_0_4_gir_FILES = $(DbusmenuGtk_0_4_gir_FILES)
DbusmenuGtk3_0_4_gir_NAMESPACE = $(DbusmenuGtk_0_4_gir_NAMESPACE)
-DbusmenuGtk3_0_4_gir_SCANNERFLAGS = $(DbusmenuGtk_0_4_gir_SCANNERFLAGS)
DbusmenuGtk3_0_4_gir_EXPORT_PACKAGES = $(DbusmenuGtk_0_4_gir_EXPORT_PACKAGES)
INTROSPECTION_GIRS += DbusmenuGtk$(VER)-0.4.gir
@@ -141,7 +154,7 @@ DbusmenuGtk$(VER)-0.4.vapi: DbusmenuGtk$(VER)-0.4.tmp.gir Makefile.am
--pkg gdk-pixbuf-2.0 \
--pkg $(GTKVALA) \
--pkg atk \
- --pkg Dbusmenu-Glib-0.4 \
+ --pkg Dbusmenu-0.4 \
--vapidir=$(top_builddir)/libdbusmenu-glib \
$<
diff --git a/libdbusmenu-gtk/client.c b/libdbusmenu-gtk/client.c
index 7ab2fe9..1051f20 100644
--- a/libdbusmenu-gtk/client.c
+++ b/libdbusmenu-gtk/client.c
@@ -31,16 +31,21 @@ 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 {
+ GStrv old_themedirs;
GtkAccelGroup * agroup;
};
+GHashTable * theme_dir_db = NULL;
+
#define DBUSMENU_GTKCLIENT_GET_PRIVATE(o) (DBUSMENU_GTKCLIENT(o)->priv)
#define USE_FALLBACK_PROP "use-fallback"
@@ -54,6 +59,9 @@ static void new_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint po
static void delete_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, DbusmenuGtkClient * gtkclient);
static void move_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint new, guint old, DbusmenuGtkClient * gtkclient);
static void item_activate (DbusmenuClient * client, DbusmenuMenuitem * mi, guint timestamp, gpointer userdata);
+static void theme_dir_changed (DbusmenuClient * client, GStrv theme_dirs, gpointer userdata);
+static void remove_theme_dirs (GtkIconTheme * theme, GStrv dirs);
+static 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);
@@ -89,6 +97,23 @@ dbusmenu_gtkclient_init (DbusmenuGtkClient *self)
DbusmenuGtkClientPrivate * priv = DBUSMENU_GTKCLIENT_GET_PRIVATE(self);
priv->agroup = NULL;
+ priv->old_themedirs = NULL;
+
+ /* We either build the theme db or we get a reference
+ to it. This way when all clients die the hashtable
+ will be free'd as well. */
+ if (theme_dir_db == NULL) {
+ theme_dir_db = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+
+ /* NOTE: We're adding an extra ref here because there
+ is no way to clear the pointer when the hash table
+ dies, so it's safer to keep the hash table around
+ forever than not know if it's free'd or not. Patch
+ submitted to GLib. */
+ g_hash_table_ref(theme_dir_db);
+ } else {
+ g_hash_table_ref(theme_dir_db);
+ }
dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(self), DBUSMENU_CLIENT_TYPES_DEFAULT, new_item_normal);
dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(self), DBUSMENU_CLIENT_TYPES_SEPARATOR, new_item_seperator);
@@ -96,6 +121,10 @@ dbusmenu_gtkclient_init (DbusmenuGtkClient *self)
/* TODO: I think these can be handled in the class... */
g_signal_connect(G_OBJECT(self), DBUSMENU_CLIENT_SIGNAL_NEW_MENUITEM, G_CALLBACK(new_menuitem), NULL);
g_signal_connect(G_OBJECT(self), DBUSMENU_CLIENT_SIGNAL_ITEM_ACTIVATE, G_CALLBACK(item_activate), NULL);
+ g_signal_connect(G_OBJECT(self), DBUSMENU_CLIENT_SIGNAL_ICON_THEME_DIRS_CHANGED, G_CALLBACK(theme_dir_changed), NULL);
+ 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);
return;
}
@@ -111,6 +140,18 @@ dbusmenu_gtkclient_dispose (GObject *object)
priv->agroup = NULL;
}
+ if (priv->old_themedirs) {
+ remove_theme_dirs(gtk_icon_theme_get_default(), priv->old_themedirs);
+ g_strfreev(priv->old_themedirs);
+ priv->old_themedirs = NULL;
+ }
+
+ if (theme_dir_db != NULL) {
+ g_hash_table_unref(theme_dir_db);
+ } else {
+ g_assert_not_reached();
+ }
+
G_OBJECT_CLASS (dbusmenu_gtkclient_parent_class)->dispose (object);
return;
}
@@ -124,6 +165,141 @@ dbusmenu_gtkclient_finalize (GObject *object)
return;
}
+/* Add a theme directory to the table and the theme's list of available
+ themes to use. */
+static void
+theme_dir_ref (GtkIconTheme * theme, GHashTable * db, const gchar * dir)
+{
+ g_return_if_fail(db != NULL);
+ g_return_if_fail(theme != NULL);
+ g_return_if_fail(dir != NULL);
+
+ int count = 0;
+ if ((count = GPOINTER_TO_INT(g_hash_table_lookup(db, dir))) != 0) {
+ /* It exists so what we need to do is increase the ref
+ count of this dir. */
+ count++;
+ } else {
+ /* It doesn't exist, so we need to add it to the table
+ and to the search path. */
+ gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), dir);
+ g_debug("\tAppending search path: %s", dir);
+ count = 1;
+ }
+
+ g_hash_table_insert(db, g_strdup(dir), GINT_TO_POINTER(count));
+
+ return;
+}
+
+/* Unreference the theme directory, and if it's count goes to zero then
+ we need to remove it from the search path. */
+static void
+theme_dir_unref (GtkIconTheme * theme, GHashTable * db, const gchar * dir)
+{
+ g_return_if_fail(db != NULL);
+ g_return_if_fail(theme != NULL);
+ g_return_if_fail(dir != NULL);
+
+ /* Grab the count for this dir */
+ int count = GPOINTER_TO_INT(g_hash_table_lookup(db, dir));
+
+ /* Is this a simple deprecation, if so, we can just lower the
+ number and move on. */
+ if (count > 1) {
+ count--;
+ g_hash_table_insert(db, g_strdup(dir), GINT_TO_POINTER(count));
+ return;
+ }
+
+ /* Try to remove it from the hash table, this makes sure
+ that it existed */
+ if (!g_hash_table_remove(db, dir)) {
+ g_warning("Unref'd a directory that wasn't in the theme dir hash table.");
+ return;
+ }
+
+ gchar ** paths;
+ gint path_count;
+
+ gtk_icon_theme_get_search_path(theme, &paths, &path_count);
+
+ gint i;
+ gboolean found = FALSE;
+ for (i = 0; i < path_count; i++) {
+ if (found) {
+ /* If we've already found the right entry */
+ paths[i - 1] = paths[i];
+ } else {
+ /* We're still looking, is this the one? */
+ if (!g_strcmp0(paths[i], dir)) {
+ found = TRUE;
+ /* We're freeing this here as it won't be captured by the
+ g_strfreev() below as it's out of the array. */
+ g_free(paths[i]);
+ }
+ }
+ }
+
+ /* If we found one we need to reset the path to
+ accomidate the changes */
+ if (found) {
+ paths[path_count - 1] = NULL; /* Clear the last one */
+ gtk_icon_theme_set_search_path(theme, (const gchar **)paths, path_count - 1);
+ }
+
+ g_strfreev(paths);
+
+ return;
+}
+
+/* Unregister this list of theme directories */
+static void
+remove_theme_dirs (GtkIconTheme * theme, GStrv dirs)
+{
+ g_return_if_fail(GTK_ICON_THEME(theme));
+ g_return_if_fail(dirs != NULL);
+
+ int dir;
+
+ for (dir = 0; dirs[dir] != NULL; dir++) {
+ theme_dir_unref(theme, theme_dir_db, dirs[dir]);
+ }
+
+ return;
+}
+
+/* Called when the theme directories are changed by the
+ server part of things. */
+static void
+theme_dir_changed (DbusmenuClient * client, GStrv theme_dirs, gpointer userdata)
+{
+ DbusmenuGtkClientPrivate * priv = DBUSMENU_GTKCLIENT_GET_PRIVATE(client);
+ GtkIconTheme * theme = gtk_icon_theme_get_default();
+
+ /* Ref the new directories */
+ if (theme_dirs != NULL) {
+ int dir;
+ for (dir = 0; theme_dirs[dir] != NULL; dir++) {
+ theme_dir_ref(theme, theme_dir_db, theme_dirs[dir]);
+ }
+ }
+
+ /* Unref the old ones */
+ if (priv->old_themedirs) {
+ remove_theme_dirs(theme, priv->old_themedirs);
+ g_strfreev(priv->old_themedirs);
+ priv->old_themedirs = NULL;
+ }
+
+ /* Copy the new to the old */
+ if (theme_dirs != NULL) {
+ priv->old_themedirs = g_strdupv(theme_dirs);
+ }
+
+ return;
+}
+
/* Structure for passing data to swap_agroup */
typedef struct _swap_agroup_t swap_agroup_t;
struct _swap_agroup_t {
@@ -219,13 +395,13 @@ refresh_shortcut (DbusmenuGtkClient * client, DbusmenuMenuitem * mi)
/**
- dbusmenu_gtkclient_set_accel_group:
- @client: To set the group on
- @agroup: The new acceleration group
-
- Sets the acceleration group for the menu items with accelerators
- on this client.
-*/
+ * dbusmenu_gtkclient_set_accel_group:
+ * @client: To set the group on
+ * @agroup: The new acceleration group
+ *
+ * Sets the acceleration group for the menu items with accelerators
+ * on this client.
+ */
void
dbusmenu_gtkclient_set_accel_group (DbusmenuGtkClient * client, GtkAccelGroup * agroup)
{
@@ -256,14 +432,14 @@ dbusmenu_gtkclient_set_accel_group (DbusmenuGtkClient * client, GtkAccelGroup *
}
/**
- dbusmenu_gtkclient_get_accel_group:
- @client: Client to query for an accelerator group
-
- Gets the accel group for this client.
-
- Return value: Either a valid group or #NULL on error or
- none set.
-*/
+ * dbusmenu_gtkclient_get_accel_group:
+ * @client: Client to query for an accelerator group
+ *
+ * Gets the accel group for this client.
+ *
+ * Return value: (transfer none): Either a valid group or #NULL on error or
+ * none set.
+ */
GtkAccelGroup *
dbusmenu_gtkclient_get_accel_group (DbusmenuGtkClient * client)
{
@@ -276,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. */
@@ -285,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, "clicked", 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. */
@@ -295,6 +559,44 @@ 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)) {
+ 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 */
static void
process_visible (DbusmenuMenuitem * mi, GtkMenuItem * gmi, GVariant * value)
@@ -370,11 +672,58 @@ process_toggle_state (DbusmenuMenuitem * mi, GtkMenuItem * gmi, GVariant * varia
return;
}
+/* Submenu processing */
+static void
+process_submenu (DbusmenuMenuitem * mi, GtkMenuItem * gmi, GVariant * variant, DbusmenuGtkClient * gtkclient)
+{
+ const gchar * submenu = NULL;
+ if (variant != NULL) {
+ submenu = g_variant_get_string(variant, NULL);
+ }
+
+ if (g_strcmp0(submenu, DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU) != 0) {
+ /* This is the only case we're really supporting right now,
+ so if it's not this, we want to clean up. */
+ /* We're just going to warn for now. */
+ gpointer pmenu = g_object_get_data(G_OBJECT(mi), data_menu);
+ if (pmenu != NULL) {
+ g_warning("The child-display variable is set to '%s' but there's a menu, odd?", submenu);
+ }
+ } else {
+ /* We need to build a menu for these guys to live in. */
+ GtkMenu * menu = GTK_MENU(gtk_menu_new());
+ g_object_ref_sink(menu);
+ g_object_set_data_full(G_OBJECT(mi), data_menu, menu, g_object_unref);
+
+ gtk_menu_item_set_submenu(gmi, GTK_WIDGET(menu));
+
+ g_signal_connect(menu, "notify::visible", G_CALLBACK(submenu_notify_visible_cb), mi);
+ }
+
+ return;
+}
+
+/* Process the disposition changing */
+static void
+process_disposition (DbusmenuMenuitem * mi, GtkMenuItem * gmi, GVariant * variant, DbusmenuGtkClient * gtkclient)
+{
+ /* We can only handle generic menu items here. Perhaps someone else
+ will find the value useful. Not us. */
+ if (!IS_GENERICMENUITEM(gmi)) {
+ return;
+ }
+
+ genericmenuitem_set_disposition(GENERICMENUITEM(gmi), genericmenuitem_disposition_get_value_from_nick(g_variant_get_string(variant, NULL)));
+ return;
+}
+
/* Whenever we have a property change on a DbusmenuMenuitem
we need to be responsive to that. */
static void
-menu_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * variant, GtkMenuItem * gmi)
+menu_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * variant, DbusmenuGtkClient * gtkclient)
{
+ GtkMenuItem * gmi = dbusmenu_gtkclient_menuitem_get(gtkclient, mi);
+
if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_LABEL)) {
gtk_menu_item_set_label(gmi, variant == NULL ? NULL : g_variant_get_string(variant, NULL));
} else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_VISIBLE)) {
@@ -385,6 +734,10 @@ menu_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * variant, Gt
process_toggle_type(mi, gmi, variant);
} else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE)) {
process_toggle_state(mi, gmi, variant);
+ } else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY)) {
+ process_submenu(mi, gmi, variant, gtkclient);
+ } else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_DISPOSITION)) {
+ process_disposition(mi, gmi, variant, gtkclient);
}
return;
@@ -401,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. */
@@ -493,21 +833,21 @@ destroy_gmi (GtkMenuItem * gmi, DbusmenuMenuitem * mi)
#endif
/**
- dbusmenu_gtkclient_newitem_base:
- @client: The client handling everything on this connection
- @item: The #DbusmenuMenuitem to attach the GTK-isms to
- @gmi: A #GtkMenuItem representing the GTK world's view of this menuitem
- @parent: The parent #DbusmenuMenuitem
-
- This function provides some of the basic connectivity for being in
- the GTK world. Things like visibility and sensitivity of the item are
- handled here so that the subclasses don't have to. If you're building
- your on GTK menu item you can use this function to apply those basic
- attributes so that you don't have to deal with them either.
-
- This also handles passing the "activate" signal back to the
- #DbusmenuMenuitem side of thing.
-*/
+ * dbusmenu_gtkclient_newitem_base:
+ * @client: The client handling everything on this connection
+ * @item: The #DbusmenuMenuitem to attach the GTK-isms to
+ * @gmi: A #GtkMenuItem representing the GTK world's view of this menuitem
+ * @parent: The parent #DbusmenuMenuitem
+ *
+ * This function provides some of the basic connectivity for being in
+ * the GTK world. Things like visibility and sensitivity of the item are
+ * handled here so that the subclasses don't have to. If you're building
+ * your on GTK menu item you can use this function to apply those basic
+ * attributes so that you don't have to deal with them either.
+ *
+ * This also handles passing the "activate" signal back to the
+ * #DbusmenuMenuitem side of thing.
+ */
void
dbusmenu_gtkclient_newitem_base (DbusmenuGtkClient * client, DbusmenuMenuitem * item, GtkMenuItem * gmi, DbusmenuMenuitem * parent)
{
@@ -516,14 +856,11 @@ dbusmenu_gtkclient_newitem_base (DbusmenuGtkClient * client, DbusmenuMenuitem *
#endif
/* Attach these two */
- g_object_set_data(G_OBJECT(item), data_menuitem, gmi);
- g_object_ref(G_OBJECT(gmi));
- #ifdef MASSIVEDEBUGGING
- g_signal_connect(G_OBJECT(gmi), "destroy", G_CALLBACK(destroy_gmi), item);
- #endif
+ g_object_ref_sink(G_OBJECT(gmi));
+ g_object_set_data_full(G_OBJECT(item), data_menuitem, gmi, (GDestroyNotify)gtk_widget_destroy);
/* DbusmenuMenuitem signals */
- g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(menu_prop_change_cb), gmi);
+ g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(menu_prop_change_cb), client);
g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(menu_shortcut_change_cb), client);
g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED, G_CALLBACK(delete_child), client);
g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED, G_CALLBACK(move_child), client);
@@ -531,14 +868,13 @@ dbusmenu_gtkclient_newitem_base (DbusmenuGtkClient * client, DbusmenuMenuitem *
/* GtkMenuitem signals */
g_signal_connect(G_OBJECT(gmi), "activate", G_CALLBACK(menu_pressed_cb), item);
- /* Life insurance */
- g_object_weak_ref(G_OBJECT(item), destoryed_dbusmenuitem_cb, gmi);
-
/* Check our set of props to see if any are set already */
process_visible(item, gmi, dbusmenu_menuitem_property_get_variant(item, DBUSMENU_MENUITEM_PROP_VISIBLE));
process_sensitive(item, gmi, dbusmenu_menuitem_property_get_variant(item, DBUSMENU_MENUITEM_PROP_ENABLED));
process_toggle_type(item, gmi, dbusmenu_menuitem_property_get_variant(item, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE));
process_toggle_state(item, gmi, dbusmenu_menuitem_property_get_variant(item, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE));
+ process_submenu(item, gmi, dbusmenu_menuitem_property_get_variant(item, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY), client);
+ process_disposition(item, gmi, dbusmenu_menuitem_property_get_variant(item, DBUSMENU_MENUITEM_PROP_DISPOSITION), client);
refresh_shortcut(client, item);
/* Oh, we're a child, let's deal with that */
@@ -560,19 +896,15 @@ new_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position, Dbus
if (g_strcmp0(dbusmenu_menuitem_property_get(mi, DBUSMENU_MENUITEM_PROP_TYPE), DBUSMENU_CLIENT_TYPES_SEPARATOR) == 0) { return; }
gpointer ann_menu = g_object_get_data(G_OBJECT(mi), data_menu);
- GtkMenu * menu = GTK_MENU(ann_menu);
- if (menu == NULL) {
- /* Oh, we don't have a submenu, build one! */
- menu = GTK_MENU(gtk_menu_new());
- g_object_set_data(G_OBJECT(mi), data_menu, menu);
+ if (ann_menu == NULL) {
+ g_warning("Children but no menu, someone's been naughty with their '" DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY "' property: '%s'", dbusmenu_menuitem_property_get(mi, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY));
+ return;
+ }
- GtkMenuItem * parent = dbusmenu_gtkclient_menuitem_get(gtkclient, mi);
- gtk_menu_item_set_submenu(parent, GTK_WIDGET(menu));
- }
+ GtkMenu * menu = GTK_MENU(ann_menu);
GtkMenuItem * childmi = dbusmenu_gtkclient_menuitem_get(gtkclient, child);
gtk_menu_shell_insert(GTK_MENU_SHELL(menu), GTK_WIDGET(childmi), position);
- gtk_widget_show(GTK_WIDGET(menu));
return;
}
@@ -589,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);
}
}
@@ -617,15 +949,15 @@ move_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint new, guint ol
/* Public API */
/**
- dbusmenu_gtkclient_new:
- @dbus_name: Name of the #DbusmenuServer on DBus
- @dbus_name: Name of the object on the #DbusmenuServer
-
- Creates a new #DbusmenuGtkClient object and creates a #DbusmenuClient
- that connects across DBus to a #DbusmenuServer.
-
- Return value: A new #DbusmenuGtkClient sync'd with a server
-*/
+ * dbusmenu_gtkclient_new:
+ * @dbus_name: Name of the #DbusmenuServer on DBus
+ * @dbus_object: Name of the object on the #DbusmenuServer
+ *
+ * Creates a new #DbusmenuGtkClient object and creates a #DbusmenuClient
+ * that connects across DBus to a #DbusmenuServer.
+ *
+ * Return value: A new #DbusmenuGtkClient sync'd with a server
+ */
DbusmenuGtkClient *
dbusmenu_gtkclient_new (gchar * dbus_name, gchar * dbus_object)
{
@@ -636,15 +968,15 @@ dbusmenu_gtkclient_new (gchar * dbus_name, gchar * dbus_object)
}
/**
- dbusmenu_gtkclient_menuitem_get:
- @client: A #DbusmenuGtkClient with the item in it.
- @item: #DbusmenuMenuitem to get associated #GtkMenuItem on.
-
- This grabs the #GtkMenuItem that is associated with the
- #DbusmenuMenuitem.
-
- Return value: The #GtkMenuItem that can be played with.
-*/
+ * dbusmenu_gtkclient_menuitem_get:
+ * @client: A #DbusmenuGtkClient with the item in it.
+ * @item: #DbusmenuMenuitem to get associated #GtkMenuItem on.
+ *
+ * This grabs the #GtkMenuItem that is associated with the
+ * #DbusmenuMenuitem.
+ *
+ * Return value: (transfer none): The #GtkMenuItem that can be played with.
+ */
GtkMenuItem *
dbusmenu_gtkclient_menuitem_get (DbusmenuGtkClient * client, DbusmenuMenuitem * item)
{
@@ -660,13 +992,13 @@ dbusmenu_gtkclient_menuitem_get (DbusmenuGtkClient * client, DbusmenuMenuitem *
}
/**
- dbusmenu_gtkclient_menuitem_get_submenu:
- @client: A #DbusmenuGtkClient with the item in it.
- @item: #DbusmenuMenuitem to get associated #GtkMenu on.
-
- This grabs the submenu associated with the menuitem.
-
- Return value: The #GtkMenu if there is one.
+ * dbusmenu_gtkclient_menuitem_get_submenu:
+ * @client: A #DbusmenuGtkClient with the item in it.
+ * @item: #DbusmenuMenuitem to get associated #GtkMenu on.
+ *
+ * This grabs the submenu associated with the menuitem.
+ *
+ * Return value: (transfer none): The #GtkMenu if there is one.
*/
GtkMenu *
dbusmenu_gtkclient_menuitem_get_submenu (DbusmenuGtkClient * client, DbusmenuMenuitem * item)
@@ -693,9 +1025,9 @@ 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);
} else {
return FALSE;
@@ -870,6 +1202,9 @@ image_property_handle (DbusmenuMenuitem * item, const gchar * property, GVariant
} else {
gtk_image_set_from_pixbuf(GTK_IMAGE(gtkimage), image);
}
+ if (image) {
+ g_object_unref(image);
+ }
}
}
diff --git a/libdbusmenu-gtk/client.h b/libdbusmenu-gtk/client.h
index c986a5d..75b59a0 100644
--- a/libdbusmenu-gtk/client.h
+++ b/libdbusmenu-gtk/client.h
@@ -41,20 +41,28 @@ G_BEGIN_DECLS
#define DBUSMENU_IS_GTKCLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_GTKCLIENT_TYPE))
#define DBUSMENU_GTKCLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_GTKCLIENT_TYPE, DbusmenuGtkClientClass))
+/**
+ * DBUSMENU_GTKCLIENT_SIGNAL_ROOT_CHANGED:
+ *
+ * String to attach to signal #DbusmenuClient::root-changed
+ */
#define DBUSMENU_GTKCLIENT_SIGNAL_ROOT_CHANGED DBUSMENU_CLIENT_SIGNAL_ROOT_CHANGED
typedef struct _DbusmenuGtkClientPrivate DbusmenuGtkClientPrivate;
/**
- DbusmenuGtkClientClass:
- @parent_class: #GtkMenuClass
- @reserved1: Reserved for future use.
- @reserved2: Reserved for future use.
- @reserved3: Reserved for future use.
- @reserved4: Reserved for future use.
- @reserved5: Reserved for future use.
- @reserved6: Reserved for future use.
-*/
+ * DbusmenuGtkClientClass:
+ * @parent_class: #GtkMenuClass
+ * @root_changed: Slot for signal #DbusmenuGtkClient::root-changed
+ * @reserved1: Reserved for future use.
+ * @reserved2: Reserved for future use.
+ * @reserved3: Reserved for future use.
+ * @reserved4: Reserved for future use.
+ * @reserved5: Reserved for future use.
+ * @reserved6: Reserved for future use.
+ *
+ * Functions and signal slots for using a #DbusmenuGtkClient
+ */
typedef struct _DbusmenuGtkClientClass DbusmenuGtkClientClass;
struct _DbusmenuGtkClientClass {
DbusmenuClientClass parent_class;
@@ -72,9 +80,11 @@ struct _DbusmenuGtkClientClass {
};
/**
- DbusmenuGtkClient:
- @parent: #GtkMenu
-*/
+ * DbusmenuGtkClient:
+ *
+ * A subclass of #DbusmenuClient to add functionality with regarding
+ * building GTK items out of the abstract tree.
+ */
typedef struct _DbusmenuGtkClient DbusmenuGtkClient;
struct _DbusmenuGtkClient {
DbusmenuClient parent;
@@ -94,10 +104,10 @@ GtkAccelGroup * dbusmenu_gtkclient_get_accel_group (DbusmenuGtkClient * client);
void dbusmenu_gtkclient_newitem_base (DbusmenuGtkClient * client, DbusmenuMenuitem * item, GtkMenuItem * gmi, DbusmenuMenuitem * parent);
/**
- SECTION:gtkmenu
- @short_description: A GTK Menu Object that syncronizes over DBus
+ SECTION:client
+ @short_description: A subclass of #DbusmenuClient adding GTK level features
@stability: Unstable
- @include: libdbusmenu-gtk/menu.h
+ @include: libdbusmenu-gtk/client.h
In general, this is just a #GtkMenu, why else would you care? Oh,
because this menu is created by someone else on a server that exists
@@ -115,8 +125,6 @@ void dbusmenu_gtkclient_newitem_base (DbusmenuGtkClient * client, DbusmenuMenuit
number of entries change, the menus change, if they change thier
properties change, they update in the items. All of this should
be handled transparently to the user of this object.
-
- TODO: Document properties.
*/
G_END_DECLS
diff --git a/libdbusmenu-gtk/dbusmenu-gtk-0.4.pc.in b/libdbusmenu-gtk/dbusmenu-gtk-0.4.pc.in
index 8784556..9a1b460 100644
--- a/libdbusmenu-gtk/dbusmenu-gtk-0.4.pc.in
+++ b/libdbusmenu-gtk/dbusmenu-gtk-0.4.pc.in
@@ -5,7 +5,7 @@ bindir=@bindir@
includedir=@includedir@
Cflags: -I${includedir}/libdbusmenu-0.4
-Requires: dbusmenu-glib-0.4
+Requires: dbusmenu-glib-0.4 gdk-pixbuf-2.0 gtk+-2.0
Libs: -L${libdir} -ldbusmenu-gtk
Name: libdbusmenu-gtk
diff --git a/libdbusmenu-gtk/dbusmenu-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/dbusmenu-gtk3-0.4.pc.in b/libdbusmenu-gtk/dbusmenu-gtk3-0.4.pc.in
index 804b13e..c297db3 100644
--- a/libdbusmenu-gtk/dbusmenu-gtk3-0.4.pc.in
+++ b/libdbusmenu-gtk/dbusmenu-gtk3-0.4.pc.in
@@ -5,7 +5,7 @@ bindir=@bindir@
includedir=@includedir@
Cflags: -I${includedir}/libdbusmenu-0.4
-Requires: dbusmenu-glib-0.4
+Requires: dbusmenu-glib-0.4 gdk-pixbuf-2.0 gtk+-3.0
Libs: -L${libdir} -ldbusmenu-gtk3
Name: libdbusmenu-gtk3
diff --git a/libdbusmenu-gtk/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 2af70f3..5488f93 100644
--- a/libdbusmenu-gtk/genericmenuitem.c
+++ b/libdbusmenu-gtk/genericmenuitem.c
@@ -30,9 +30,11 @@ 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"
-/**
+/*
GenericmenuitemPrivate:
@check_type: What type of check we have, or none at all.
@state: What the state of our check is.
@@ -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;
}
@@ -278,12 +333,12 @@ activate (GtkMenuItem * menu_item)
}
/**
- genericmenuitem_set_check_type:
- @item: #Genericmenuitem to set the type on
- @check_type: Which type of check should be displayed
-
- This function changes the type of the checkmark that
- appears in the left hand gutter for the menuitem.
+ * genericmenuitem_set_check_type:
+ * @item: #Genericmenuitem to set the type on
+ * @check_type: Which type of check should be displayed
+ *
+ * This function changes the type of the checkmark that
+ * appears in the left hand gutter for the menuitem.
*/
void
genericmenuitem_set_check_type (Genericmenuitem * item, GenericmenuitemCheckType check_type)
@@ -317,14 +372,14 @@ genericmenuitem_set_check_type (Genericmenuitem * item, GenericmenuitemCheckType
}
/**
- genericmenuitem_set_state:
- @item: #Genericmenuitem to set the type on
- @check_type: What is the state of the check
-
- Sets the state of the check in the menu item. It does
- not require, but isn't really useful if the type of
- check that the menuitem is set to #GENERICMENUITEM_CHECK_TYPE_NONE.
-*/
+ * genericmenuitem_set_state:
+ * @item: #Genericmenuitem to set the type on
+ * @check_type: What is the state of the check
+ *
+ * Sets the state of the check in the menu item. It does
+ * not require, but isn't really useful if the type of
+ * check that the menuitem is set to #GENERICMENUITEM_CHECK_TYPE_NONE.
+ */
void
genericmenuitem_set_state (Genericmenuitem * item, GenericmenuitemState state)
{
@@ -377,11 +432,11 @@ set_image_helper (GtkWidget * widget, gpointer data)
}
/**
- genericmenuitem_set_image:
- @item: A #Genericmenuitem
- @image: The image to set as the image of @item
-
- Sets the image of the menu item.
+ * genericmenuitem_set_image:
+ * @item: A #Genericmenuitem
+ * @image: The image to set as the image of @item
+ *
+ * Sets the image of the menu item.
*/
void
genericmenuitem_set_image (Genericmenuitem * menu_item, GtkWidget * image)
@@ -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);
@@ -439,13 +494,13 @@ genericmenuitem_set_image (Genericmenuitem * menu_item, GtkWidget * image)
}
/**
- genericmenuitem_get_image:
- @item: A #Genericmenuitem
-
- Returns the image if there is one.
-
- Return value: A pointer to the image of the item or #NULL
- if there isn't one.
+ * genericmenuitem_get_image:
+ * @item: A #Genericmenuitem
+ *
+ * Returns the image if there is one.
+ *
+ * Return value: (transfer none): A pointer to the image of the item or #NULL
+ * if there isn't one.
*/
GtkWidget *
genericmenuitem_get_image (Genericmenuitem * menu_item)
@@ -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 3c4af0c..0b7df55 100644
--- a/libdbusmenu-gtk/genericmenuitem.h
+++ b/libdbusmenu-gtk/genericmenuitem.h
@@ -42,13 +42,11 @@ 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:
@parent_class: Our parent #GtkCheckMenuItemClass
*/
@@ -56,7 +54,7 @@ struct _GenericmenuitemClass {
GtkCheckMenuItemClass parent_class;
};
-/**
+/*
Genericmenuitem:
@parent: Our parent #GtkCheckMenuItem
*/
@@ -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
-};
+} GenericmenuitemCheckType;
-enum _GenericmenuitemState {
+/**
+ * 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
-};
+} GenericmenuitemState;
-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);
+/**
+ * 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 9c4f7f8..236a596 100644
--- a/libdbusmenu-gtk/menu.c
+++ b/libdbusmenu-gtk/menu.c
@@ -46,6 +46,7 @@ enum {
/* Private */
struct _DbusmenuGtkMenuPrivate {
DbusmenuGtkClient * client;
+ DbusmenuMenuitem * root;
gchar * dbus_object;
gchar * dbus_name;
@@ -63,6 +64,8 @@ static void get_property (GObject * obj, guint id, GValue * value, GParamSpec *
/* Internal */
static void build_client (DbusmenuGtkMenu * self);
static void child_realized (DbusmenuMenuitem * child, gpointer userdata);
+static void remove_child_signals (gpointer data, gpointer user_data);
+static void root_changed (DbusmenuGtkClient * client, DbusmenuMenuitem * newroot, DbusmenuGtkMenu * menu);
/* GObject Stuff */
G_DEFINE_TYPE (DbusmenuGtkMenu, dbusmenu_gtkmenu, GTK_TYPE_MENU);
@@ -100,7 +103,7 @@ menu_focus_cb(DbusmenuGtkMenu * menu, gpointer userdata)
if (priv->client != NULL) {
/* TODO: We should stop the display of the menu
until the about to show call returns. */
- dbusmenu_client_send_about_to_show(DBUSMENU_CLIENT(priv->client), 0, NULL, NULL);
+ dbusmenu_menuitem_send_about_to_show(priv->root, NULL, NULL);
}
return;
}
@@ -127,6 +130,12 @@ dbusmenu_gtkmenu_dispose (GObject *object)
{
DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(object);
+ /* Remove signals from the root */
+ if (priv->root != NULL) {
+ /* This will clear the root */
+ root_changed(priv->client, NULL, DBUSMENU_GTKMENU(object));
+ }
+
if (priv->client != NULL) {
g_object_unref(G_OBJECT(priv->client));
priv->client = NULL;
@@ -271,6 +280,10 @@ root_child_delete (DbusmenuMenuitem * root, DbusmenuMenuitem * child, DbusmenuGt
#ifdef MASSIVEDEBUGGING
g_debug("Root child deleted");
#endif
+
+ /* Remove signal for realized */
+ remove_child_signals(child, menu);
+
DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(menu);
GtkWidget * item = GTK_WIDGET(dbusmenu_gtkclient_menuitem_get(priv->client, child));
if (item != NULL) {
@@ -308,15 +321,55 @@ child_realized (DbusmenuMenuitem * child, gpointer userdata)
return;
}
+/* Remove any signals we attached to children -- just realized right now */
+static void
+remove_child_signals (gpointer data, gpointer user_data)
+{
+ g_signal_handlers_disconnect_by_func(G_OBJECT(data), child_realized, 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
root_changed (DbusmenuGtkClient * client, DbusmenuMenuitem * newroot, DbusmenuGtkMenu * menu) {
+ DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(menu);
+
+ /* Clear out our interest in the old root */
+ if (priv->root != NULL) {
+ GList * children = dbusmenu_menuitem_get_children(priv->root);
+ g_list_foreach(children, remove_child_signals, menu);
+
+ g_signal_handlers_disconnect_by_func(G_OBJECT(priv->root), root_child_added, menu);
+ 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;
+ }
+
if (newroot == NULL) {
gtk_widget_hide(GTK_WIDGET(menu));
return;
}
+ priv->root = newroot;
+ g_object_ref(priv->root);
+
g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED, G_CALLBACK(root_child_added), menu);
g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED, G_CALLBACK(root_child_moved), menu);
g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED, G_CALLBACK(root_child_delete), menu);
@@ -359,15 +412,15 @@ build_client (DbusmenuGtkMenu * self)
/* Public API */
/**
- dbusmenu_gtkmenu_new:
- @dbus_name: Name of the #DbusmenuServer on DBus
- @dbus_name: Name of the object on the #DbusmenuServer
-
- Creates a new #DbusmenuGtkMenu object and creates a #DbusmenuClient
- that connects across DBus to a #DbusmenuServer.
-
- Return value: A new #DbusmenuGtkMenu sync'd with a server
-*/
+ * dbusmenu_gtkmenu_new:
+ * @dbus_name: Name of the #DbusmenuServer on DBus
+ * @dbus_object: Name of the object on the #DbusmenuServer
+ *
+ * Creates a new #DbusmenuGtkMenu object and creates a #DbusmenuClient
+ * that connects across DBus to a #DbusmenuServer.
+ *
+ * Return value: A new #DbusmenuGtkMenu sync'd with a server
+ */
DbusmenuGtkMenu *
dbusmenu_gtkmenu_new (gchar * dbus_name, gchar * dbus_object)
{
@@ -378,14 +431,14 @@ dbusmenu_gtkmenu_new (gchar * dbus_name, gchar * dbus_object)
}
/**
- dbusmenu_gtkmenu_get_client:
- @menu: The #DbusmenuGtkMenu to get the client from
-
- An accessor for the client that this menu is using to
- communicate with the server.
-
- Return value: A valid #DbusmenuGtkClient or NULL on error.
-*/
+ * dbusmenu_gtkmenu_get_client:
+ * @menu: The #DbusmenuGtkMenu to get the client from
+ *
+ * An accessor for the client that this menu is using to
+ * communicate with the server.
+ *
+ * Return value: (transfer none): A valid #DbusmenuGtkClient or NULL on error.
+ */
DbusmenuGtkClient *
dbusmenu_gtkmenu_get_client (DbusmenuGtkMenu * menu)
{
diff --git a/libdbusmenu-gtk/menu.h b/libdbusmenu-gtk/menu.h
index 896e466..c413ab8 100644
--- a/libdbusmenu-gtk/menu.h
+++ b/libdbusmenu-gtk/menu.h
@@ -45,15 +45,18 @@ G_BEGIN_DECLS
typedef struct _DbusmenuGtkMenuPrivate DbusmenuGtkMenuPrivate;
/**
- DbusmenuGtkMenuClass:
- @parent_class: #GtkMenuClass
- @reserved1: Reserved for future use.
- @reserved2: Reserved for future use.
- @reserved3: Reserved for future use.
- @reserved4: Reserved for future use.
- @reserved5: Reserved for future use.
- @reserved6: Reserved for future use.
-*/
+ * DbusmenuGtkMenuClass:
+ * @parent_class: #GtkMenuClass
+ * @reserved1: Reserved for future use.
+ * @reserved2: Reserved for future use.
+ * @reserved3: Reserved for future use.
+ * @reserved4: Reserved for future use.
+ * @reserved5: Reserved for future use.
+ * @reserved6: Reserved for future use.
+ *
+ * All of the subclassable functions and signal slots for a
+ * #DbusmenuGtkMenu.
+ */
typedef struct _DbusmenuGtkMenuClass DbusmenuGtkMenuClass;
struct _DbusmenuGtkMenuClass {
GtkMenuClass parent_class;
@@ -68,9 +71,11 @@ struct _DbusmenuGtkMenuClass {
};
/**
- DbusmenuGtkMenu:
- @parent: #GtkMenu
-*/
+ * DbusmenuGtkMenu:
+ *
+ * A #GtkMenu that is built using an abstract tree built from
+ * a #DbusmenuGtkClient.
+ */
typedef struct _DbusmenuGtkMenu DbusmenuGtkMenu;
struct _DbusmenuGtkMenu {
GtkMenu parent;
@@ -84,7 +89,7 @@ DbusmenuGtkMenu * dbusmenu_gtkmenu_new (gchar * dbus_name, gchar * dbus_object);
DbusmenuGtkClient * dbusmenu_gtkmenu_get_client (DbusmenuGtkMenu * menu);
/**
- SECTION:gtkmenu
+ SECTION:menu
@short_description: A GTK Menu Object that syncronizes over DBus
@stability: Unstable
@include: libdbusmenu-gtk/menu.h
@@ -105,8 +110,6 @@ DbusmenuGtkClient * dbusmenu_gtkmenu_get_client (DbusmenuGtkMenu * menu);
number of entries change, the menus change, if they change thier
properties change, they update in the items. All of this should
be handled transparently to the user of this object.
-
- TODO: Document properties.
*/
G_END_DECLS
diff --git a/libdbusmenu-gtk/menuitem.c b/libdbusmenu-gtk/menuitem.c
index fa5eb89..0f511bc 100644
--- a/libdbusmenu-gtk/menuitem.c
+++ b/libdbusmenu-gtk/menuitem.c
@@ -31,17 +31,17 @@ License version 3 and version 2.1 along with this program. If not, see
#include <gtk/gtk.h>
/**
- dbusmenu_menuitem_property_set_image:
- @menuitem: The #DbusmenuMenuitem to set the property on.
- @property: Name of the property to set.
- @data: The image to place on the property.
-
- This function takes the pixbuf that is stored in @data and
- turns it into a base64 encoded PNG so that it can be placed
- onto a standard #DbusmenuMenuitem property.
-
- Return value: Whether the function was able to set the property
- or not.
+ * dbusmenu_menuitem_property_set_image:
+ * @menuitem: The #DbusmenuMenuitem to set the property on.
+ * @property: Name of the property to set.
+ * @data: The image to place on the property.
+ *
+ * This function takes the pixbuf that is stored in @data and
+ * turns it into a base64 encoded PNG so that it can be placed
+ * onto a standard #DbusmenuMenuitem property.
+ *
+ * Return value: Whether the function was able to set the property
+ * or not.
*/
gboolean
dbusmenu_menuitem_property_set_image (DbusmenuMenuitem * menuitem, const gchar * property, const GdkPixbuf * data)
@@ -77,17 +77,17 @@ dbusmenu_menuitem_property_set_image (DbusmenuMenuitem * menuitem, const gchar *
}
/**
- dbusmenu_menuitem_property_get_image:
- @menuitem: The #DbusmenuMenuite to look for the property on
- @property: The name of the property to look for.
-
- This function looks on the menu item for a property by the
- name of @property. If one exists it tries to turn it into
- a #GdkPixbuf. It assumes that the property is a base64 encoded
- PNG file like the one created by #dbusmenu_menuite_property_set_image.
-
- Return value: A pixbuf or #NULL to signal error.
-*/
+ * dbusmenu_menuitem_property_get_image:
+ * @menuitem: The #DbusmenuMenuitem to look for the property on
+ * @property: The name of the property to look for.
+ *
+ * This function looks on the menu item for a property by the
+ * name of @property. If one exists it tries to turn it into
+ * a #GdkPixbuf. It assumes that the property is a base64 encoded
+ * PNG file like the one created by #dbusmenu_menuite_property_set_image.
+ *
+ * Return value: (transfer full): A pixbuf or #NULL to signal error.
+ */
GdkPixbuf *
dbusmenu_menuitem_property_get_image (DbusmenuMenuitem * menuitem, const gchar * property)
{
@@ -119,28 +119,23 @@ dbusmenu_menuitem_property_get_image (DbusmenuMenuitem * menuitem, const gchar *
g_error_free(error);
}
- error = NULL;
- g_input_stream_close(input, NULL, &error);
- if (error != NULL) {
- g_warning("Unable to close input stream: %s", error->message);
- g_error_free(error);
- }
+ g_object_unref(input);
g_free(icondata);
return icon;
}
/**
- dbusmenu_menuitem_property_set_shortcut_string:
- @menuitem: The #DbusmenuMenuitem to set the shortcut on
- @shortcut: String describing the shortcut
-
- This function takes a GTK shortcut string as defined in
- #gtk_accelerator_parse and turns that into the information
- required to send it over DBusmenu.
-
- Return value: Whether it was successful at setting the property.
-*/
+ * dbusmenu_menuitem_property_set_shortcut_string:
+ * @menuitem: The #DbusmenuMenuitem to set the shortcut on
+ * @shortcut: String describing the shortcut
+ *
+ * This function takes a GTK shortcut string as defined in
+ * #gtk_accelerator_parse and turns that into the information
+ * required to send it over DBusmenu.
+ *
+ * Return value: Whether it was successful at setting the property.
+ */
gboolean
dbusmenu_menuitem_property_set_shortcut_string (DbusmenuMenuitem * menuitem, const gchar * shortcut)
{
@@ -161,22 +156,25 @@ dbusmenu_menuitem_property_set_shortcut_string (DbusmenuMenuitem * menuitem, con
}
/**
- dbusmenu_menuitem_property_set_shortcut:
- @menuitem: The #DbusmenuMenuitem to set the shortcut on
- @key: The keycode of the key to send
- @modifier: A bitmask of modifiers used to activate the item
-
- Takes the modifer described by @key and @modifier and places that into
- the format sending across Dbus for shortcuts.
-
- Return value: Whether it was successful at setting the property.
-*/
+ * dbusmenu_menuitem_property_set_shortcut:
+ * @menuitem: The #DbusmenuMenuitem to set the shortcut on
+ * @key: The keycode of the key to send
+ * @modifier: A bitmask of modifiers used to activate the item
+ *
+ * Takes the modifer described by @key and @modifier and places that into
+ * the format sending across Dbus for shortcuts.
+ *
+ * Return value: Whether it was successful at setting the property.
+ */
gboolean
dbusmenu_menuitem_property_set_shortcut (DbusmenuMenuitem * menuitem, guint key, GdkModifierType modifier)
{
g_return_val_if_fail(DBUSMENU_IS_MENUITEM(menuitem), FALSE);
g_return_val_if_fail(gtk_accelerator_valid(key, modifier), FALSE);
+ const gchar * keyname = gdk_keyval_name(key);
+ g_return_val_if_fail(keyname != NULL, FALSE);
+
GVariantBuilder builder;
g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
@@ -193,7 +191,6 @@ dbusmenu_menuitem_property_set_shortcut (DbusmenuMenuitem * menuitem, guint key,
g_variant_builder_add(&builder, "s", DBUSMENU_MENUITEM_SHORTCUT_SUPER);
}
- const gchar * keyname = gdk_keyval_name(key);
g_variant_builder_add(&builder, "s", keyname);
GVariant * inside = g_variant_builder_end(&builder);
@@ -213,16 +210,16 @@ find_closure (GtkAccelKey * key, GClosure * closure, gpointer user_data)
}
/**
- dbusmenu_menuitem_property_set_shortcut_menuitem:
- @menuitem: The #DbusmenuMenuitem to set the shortcut on
- @gmi: A menu item to steal the shortcut off of
-
- Takes the shortcut that is installed on a menu item and calls
- #dbusmenu_menuitem_property_set_shortcut with it. It also sets
- up listeners to watch it change.
-
- Return value: Whether it was successful at setting the property.
-*/
+ * dbusmenu_menuitem_property_set_shortcut_menuitem:
+ * @menuitem: The #DbusmenuMenuitem to set the shortcut on
+ * @gmi: A menu item to steal the shortcut off of
+ *
+ * Takes the shortcut that is installed on a menu item and calls
+ * #dbusmenu_menuitem_property_set_shortcut with it. It also sets
+ * up listeners to watch it change.
+ *
+ * Return value: Whether it was successful at setting the property.
+ */
gboolean
dbusmenu_menuitem_property_set_shortcut_menuitem (DbusmenuMenuitem * menuitem, const GtkMenuItem * gmi)
{
@@ -239,8 +236,15 @@ dbusmenu_menuitem_property_set_shortcut_menuitem (DbusmenuMenuitem * menuitem, c
NULL);
}
- if (closure == NULL)
- return FALSE;
+ if (closure == NULL) {
+ /* As a fallback, check for a closure in the related menu item. This
+ actually happens with SWT menu items. */
+ GList * closures = gtk_widget_list_accel_closures (GTK_WIDGET (gmi));
+ if (closures == NULL)
+ return FALSE;
+ closure = closures->data;
+ g_list_free (closures);
+ }
GtkAccelGroup * group = gtk_accel_group_from_accel_closure(closure);
@@ -260,17 +264,28 @@ dbusmenu_menuitem_property_set_shortcut_menuitem (DbusmenuMenuitem * menuitem, c
}
/**
- dbusmenu_menuitem_property_get_shortcut:
- @menuitem: The #DbusmenuMenuitem to get the shortcut off
- @key: Location to put the key value
- @modifier: Location to put the modifier mask
-
- This function gets a GTK shortcut as a key and a mask
- for use to set the accelerators.
-*/
+ * dbusmenu_menuitem_property_get_shortcut:
+ * @menuitem: The #DbusmenuMenuitem to get the shortcut off
+ * @key: (out): Location to put the key value
+ * @modifier: (out): Location to put the modifier mask
+ *
+ * This function gets a GTK shortcut as a key and a mask
+ * for use to set the accelerators.
+ */
void
dbusmenu_menuitem_property_get_shortcut (DbusmenuMenuitem * menuitem, guint * key, GdkModifierType * modifier)
{
+ guint dummykey;
+ GdkModifierType dummymodifier;
+
+ if (key == NULL) {
+ key = &dummykey;
+ }
+
+ if (modifier == NULL) {
+ modifier = &dummymodifier;
+ }
+
*key = 0;
*modifier = 0;
@@ -283,15 +298,15 @@ dbusmenu_menuitem_property_get_shortcut (DbusmenuMenuitem * menuitem, guint * ke
if (g_variant_n_children(wrapper) != 1) {
g_warning("Unable to parse shortcut, too many keys");
- g_variant_unref(wrapper);
return;
}
GVariantIter iter;
- g_variant_iter_init(&iter, g_variant_get_child_value(wrapper, 0));
+ GVariant * child = g_variant_get_child_value(wrapper, 0);
+ g_variant_iter_init(&iter, child);
gchar * string;
- while(g_variant_iter_next(&iter, "s", &string)) {
+ while(g_variant_iter_loop(&iter, "s", &string)) {
if (g_strcmp0(string, DBUSMENU_MENUITEM_SHORTCUT_CONTROL) == 0) {
*modifier |= GDK_CONTROL_MASK;
} else if (g_strcmp0(string, DBUSMENU_MENUITEM_SHORTCUT_ALT) == 0) {
@@ -304,10 +319,10 @@ dbusmenu_menuitem_property_get_shortcut (DbusmenuMenuitem * menuitem, guint * ke
GdkModifierType tempmod;
gtk_accelerator_parse(string, key, &tempmod);
}
-
- g_free(string);
}
+ g_variant_unref(child);
+
return;
}
diff --git a/libdbusmenu-gtk/menuitem.h b/libdbusmenu-gtk/menuitem.h
index 4fc42f9..41f2187 100644
--- a/libdbusmenu-gtk/menuitem.h
+++ b/libdbusmenu-gtk/menuitem.h
@@ -43,8 +43,19 @@ GdkPixbuf * dbusmenu_menuitem_property_get_image (DbusmenuMenuitem * menuitem, c
gboolean dbusmenu_menuitem_property_set_shortcut (DbusmenuMenuitem * menuitem, guint key, GdkModifierType modifier);
gboolean dbusmenu_menuitem_property_set_shortcut_string (DbusmenuMenuitem * menuitem, const gchar * shortcut);
gboolean dbusmenu_menuitem_property_set_shortcut_menuitem (DbusmenuMenuitem * menuitem, const GtkMenuItem * gmi);
-void dbusmenu_menuitem_property_get_shortcut (DbusmenuMenuitem * menuitem, guint * key, GdkModifierType * modifiers);
-
+void dbusmenu_menuitem_property_get_shortcut (DbusmenuMenuitem * menuitem, guint * key, GdkModifierType * modifier);
+
+/**
+ SECTION:menuitem
+ @short_description: Helpers for #DbusmenuMenuitem properties that require a GTK dependency
+ @stability: Unstable
+ @include: libdbusmenu-gtk/menuitem.h
+
+ Some property helpers can't be done without picking up a GTK+
+ dependency. So those sit in libdbusmenu-gtk but have very similar
+ prototypes to the code in libdbusmenu-glib so your code will
+ look consistent, just with the extra depedency.
+*/
G_END_DECLS
#endif
diff --git a/libdbusmenu-gtk/parser.c b/libdbusmenu-gtk/parser.c
index 5d71585..3243c81 100644
--- a/libdbusmenu-gtk/parser.c
+++ b/libdbusmenu-gtk/parser.c
@@ -28,9 +28,19 @@ 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"
+
+typedef struct _ParserData
+{
+ GtkWidget *label;
+ GtkAction *action;
+ GtkWidget *widget;
+ GtkWidget *shell;
+ GtkWidget *image;
+} ParserData;
typedef struct _RecurseContext
{
@@ -42,83 +52,289 @@ static void parse_menu_structure_helper (GtkWidget * widget, RecurseContext * re
static DbusmenuMenuitem * construct_dbusmenu_for_widget (GtkWidget * widget);
static void accel_changed (GtkWidget * widget,
gpointer data);
-static gboolean update_stock_item (DbusmenuMenuitem * menuitem,
- GtkWidget * widget);
static void checkbox_toggled (GtkWidget * widget,
DbusmenuMenuitem * mi);
-static void update_icon_name (DbusmenuMenuitem * menuitem,
- GtkWidget * widget);
+static void update_icon (DbusmenuMenuitem * menuitem,
+ GtkImage * image);
static GtkWidget * find_menu_label (GtkWidget * widget);
static void label_notify_cb (GtkWidget * widget,
GParamSpec * pspec,
gpointer data);
+static void image_notify_cb (GtkWidget * widget,
+ GParamSpec * pspec,
+ gpointer data);
static void action_notify_cb (GtkAction * action,
GParamSpec * pspec,
gpointer data);
+static void child_added_cb (GtkContainer * menu,
+ GtkWidget * widget,
+ gpointer data);
+static void child_removed_cb (GtkContainer * menu,
+ GtkWidget * widget,
+ gpointer data);
+static void theme_changed_cb (GtkIconTheme * theme,
+ gpointer data);
static void item_activated (DbusmenuMenuitem * item,
guint timestamp,
gpointer user_data);
static gboolean item_about_to_show (DbusmenuMenuitem * item,
gpointer user_data);
+static gboolean item_handle_event (DbusmenuMenuitem * item,
+ const gchar * name,
+ GVariant * variant,
+ guint timestamp,
+ GtkWidget * widget);
static void widget_notify_cb (GtkWidget * widget,
GParamSpec * pspec,
gpointer data);
+static void widget_add_cb (GtkWidget * widget,
+ GtkWidget * child,
+ gpointer data);
static gboolean should_show_image (GtkImage * image);
static void menuitem_notify_cb (GtkWidget * widget,
GParamSpec * pspec,
gpointer data);
/**
- dbusmenu_gtk_parse_menu_structure:
- @widget: A #GtkMenuItem or #GtkMenuShell to turn into a #DbusmenuMenuitem
+ * dbusmenu_gtk_parse_menu_structure:
+ * @widget: A #GtkMenuItem or #GtkMenuShell to turn into a #DbusmenuMenuitem
+ *
+ * Goes through the GTK structures and turns them into the appropraite
+ * Dbusmenu structures along with setting up all the relationships
+ * between the objects. It also stores the dbusmenu items as a cache
+ * on the GTK items so that they'll be reused if necissary.
+ *
+ * Return value: (transfer full): A dbusmenu item representing the menu structure
+ */
+DbusmenuMenuitem *
+dbusmenu_gtk_parse_menu_structure (GtkWidget * widget)
+{
+ g_return_val_if_fail(GTK_IS_MENU_ITEM(widget) || GTK_IS_MENU_SHELL(widget), NULL);
- Goes through the GTK structures and turns them into the appropraite
- Dbusmenu structures along with setting up all the relationships
- between the objects. It also stores the dbusmenu items as a cache
- on the GTK items so that they'll be reused if necissary.
+ DbusmenuMenuitem * returnval = NULL;
+ gpointer data = g_object_get_data(G_OBJECT(widget), CACHED_MENUITEM);
- Return value: A dbusmenu item representing the menu structure
-*/
+ if (data == NULL) {
+ RecurseContext recurse = {0};
+
+ recurse.toplevel = gtk_widget_get_toplevel(widget);
+
+ parse_menu_structure_helper(widget, &recurse);
+
+ returnval = recurse.parent;
+ } else {
+ returnval = DBUSMENU_MENUITEM(data);
+ g_object_ref(G_OBJECT(returnval));
+ }
+
+ return returnval;
+}
+
+/**
+ * dbusmenu_gtk_parse_get_cached_item:
+ * @widget: A #GtkMenuItem that may have a cached #DbusmenuMenuitem from the parser
+ *
+ * The Dbusmenu GTK parser adds cached items on the various
+ * menu items throughout the tree. Sometimes it can be useful
+ * to get that cached item to use directly. This function
+ * will retrieve it for you.
+ *
+ * Return value: (transfer none): A pointer to the cached item
+ * or NULL if it isn't there.
+ */
DbusmenuMenuitem *
-dbusmenu_gtk_parse_menu_structure (GtkWidget * widget)
+dbusmenu_gtk_parse_get_cached_item (GtkWidget * widget)
{
- g_return_val_if_fail(GTK_IS_MENU_ITEM(widget) || GTK_IS_MENU_SHELL(widget), NULL);
+ if (!GTK_IS_MENU_ITEM(widget)) {
+ return NULL;
+ }
+ return DBUSMENU_MENUITEM(g_object_get_data(G_OBJECT(widget), CACHED_MENUITEM));
+}
- RecurseContext recurse = {0};
+static void
+parse_data_free (gpointer data)
+{
+ ParserData *pdata = (ParserData *)data;
+
+ if (pdata != NULL && pdata->label != NULL) {
+ g_signal_handlers_disconnect_matched(pdata->label, (GSignalMatchType)G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, G_CALLBACK(label_notify_cb), NULL);
+ g_object_remove_weak_pointer(G_OBJECT(pdata->label), (gpointer*)&pdata->label);
+ }
+
+ if (pdata != NULL && pdata->action != NULL) {
+ g_signal_handlers_disconnect_matched(pdata->action, (GSignalMatchType)G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, G_CALLBACK(action_notify_cb), NULL);
+ g_object_remove_weak_pointer(G_OBJECT(pdata->action), (gpointer*)&pdata->action);
+ }
- recurse.toplevel = gtk_widget_get_toplevel(widget);
+ if (pdata != NULL && pdata->widget != NULL) {
+ 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);
+ g_signal_handlers_disconnect_matched(pdata->widget, (GSignalMatchType)G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, G_CALLBACK(menuitem_notify_cb), NULL);
+ g_object_remove_weak_pointer(G_OBJECT(pdata->widget), (gpointer*)&pdata->widget);
+ }
+
+ if (pdata != NULL && pdata->shell != NULL) {
+ g_signal_handlers_disconnect_matched(pdata->shell, (GSignalMatchType)G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, G_CALLBACK(child_added_cb), NULL);
+ g_signal_handlers_disconnect_matched(pdata->shell, (GSignalMatchType)G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, G_CALLBACK(child_removed_cb), NULL);
+ g_object_remove_weak_pointer(G_OBJECT(pdata->shell), (gpointer*)&pdata->shell);
+ }
+
+ if (pdata != NULL && pdata->image != NULL) {
+ g_signal_handlers_disconnect_matched(pdata->image, (GSignalMatchType)G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, G_CALLBACK(image_notify_cb), NULL);
+ g_object_remove_weak_pointer(G_OBJECT(pdata->image), (gpointer*)&pdata->image);
+ }
- parse_menu_structure_helper(widget, &recurse);
+ g_free(pdata);
- return recurse.parent;
+ return;
+}
+
+static void
+widget_freed (gpointer data, GObject * obj)
+{
+ g_signal_handlers_disconnect_by_func(gtk_icon_theme_get_default(), G_CALLBACK(theme_changed_cb), obj);
+
+ return;
}
/* Called when the dbusmenu item that we're keeping around
is finalized */
static void
-dbusmenu_cache_freed (gpointer data, GObject * obj)
+dbusmenu_item_freed (gpointer data, GObject * obj)
{
- /* If the dbusmenu item is killed we don't need to remove
- the weak ref as well. */
- g_object_steal_data(G_OBJECT(data), CACHED_MENUITEM);
- g_signal_handlers_disconnect_by_func(data, G_CALLBACK(widget_notify_cb), obj);
- return;
+ ParserData *pdata = (ParserData *)g_object_get_data(G_OBJECT(obj), PARSER_DATA);
+
+ if (pdata != NULL && pdata->widget != NULL) {
+ g_signal_handlers_disconnect_by_func(gtk_icon_theme_get_default(), G_CALLBACK(theme_changed_cb), pdata->widget);
+ g_object_steal_data(G_OBJECT(pdata->widget), CACHED_MENUITEM);
+ g_object_weak_unref(G_OBJECT(pdata->widget), widget_freed, NULL);
+ }
+}
+
+/* Gets the positon of the child with its' parent if it has one.
+ Returns -1 if the position is unable to be calculated. */
+static gint
+get_child_position (GtkWidget * child)
+{
+ GtkWidget * parent = gtk_widget_get_parent (child);
+ if (parent == NULL || !GTK_IS_CONTAINER (parent))
+ return -1;
+
+ GList * children = gtk_container_get_children (GTK_CONTAINER (parent));
+ GList * iter;
+ gint position = 0;
+
+ for (iter = children; iter != NULL; iter = iter->next) {
+ if (iter->data == child)
+ break;
+ ++position;
+ }
+
+ g_list_free (children);
+
+ if (iter == NULL)
+ return -1;
+ else
+ return position;
+}
+
+/* Creates a new menu item that is attached to the widget and has
+ the data linkages hooked up. Also allocates the ParserData */
+static DbusmenuMenuitem *
+new_menuitem (GtkWidget * widget)
+{
+ DbusmenuMenuitem * item = dbusmenu_menuitem_new();
+
+ ParserData *pdata = g_new0 (ParserData, 1);
+ g_object_set_data_full(G_OBJECT(item), PARSER_DATA, pdata, parse_data_free);
+
+ g_object_weak_ref(G_OBJECT(item), dbusmenu_item_freed, NULL);
+ g_object_weak_ref(G_OBJECT(widget), widget_freed, NULL);
+
+ pdata->widget = widget;
+ g_object_add_weak_pointer(G_OBJECT (widget), (gpointer*)&pdata->widget);
+ g_object_set_data(G_OBJECT(widget), CACHED_MENUITEM, item);
+
+ 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;
}
-/* Called if we replace the cache on the object with a new
- dbusmenu menuitem */
static void
-object_cache_freed (gpointer data)
+watch_submenu(DbusmenuMenuitem * mi, GtkWidget * menu)
{
- if (!G_IS_OBJECT(data)) return;
- g_object_weak_unref(G_OBJECT(data), dbusmenu_cache_freed, data);
- return;
+ 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
-parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse)
+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)
+{
/* If this is a shell, then let's handle the items in it. */
if (GTK_IS_MENU_SHELL (widget)) {
/* Okay, this is a little janky and all.. but some applications update some
@@ -132,19 +348,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));
-
- for (; children != NULL; children = children->next) {
- gtk_menu_shell_activate_item (GTK_MENU_SHELL (widget),
- children->data,
- TRUE);
- }
-
- g_list_free (children);
+ gtk_container_foreach (GTK_CONTAINER (widget),
+ (GtkCallback)activate_toplevel_item,
+ NULL);
}
if (recurse->parent == NULL) {
- recurse->parent = dbusmenu_menuitem_new();
+ recurse->parent = new_menuitem(widget);
+ watch_submenu(recurse->parent, widget);
}
gtk_container_foreach (GTK_CONTAINER (widget),
@@ -166,8 +377,6 @@ parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse)
/* We don't have one, so we'll need to build it */
if (thisitem == NULL) {
thisitem = construct_dbusmenu_for_widget (widget);
- g_object_set_data_full(G_OBJECT(widget), CACHED_MENUITEM, thisitem, object_cache_freed);
- g_object_weak_ref(G_OBJECT(thisitem), dbusmenu_cache_freed, widget);
if (!gtk_widget_get_visible (widget)) {
g_signal_connect (G_OBJECT (widget),
@@ -195,12 +404,18 @@ parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse)
/* Oops, let's tell our parents about us */
if (peek == NULL) {
- /* TODO: Should we set a weak ref on the parent? */
- g_object_set_data (G_OBJECT (thisitem),
- "dbusmenu-parent",
- recurse->parent);
- dbusmenu_menuitem_child_append (recurse->parent,
- thisitem);
+ if (dbusmenu_menuitem_get_parent(thisitem) != NULL) {
+ dbusmenu_menuitem_unparent(thisitem);
+ }
+
+ gint pos = get_child_position (widget);
+ if (pos >= 0)
+ dbusmenu_menuitem_child_add_position (recurse->parent,
+ thisitem,
+ pos);
+ else
+ dbusmenu_menuitem_child_append (recurse->parent,
+ thisitem);
}
}
@@ -222,38 +437,80 @@ parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse)
return;
}
+static gchar *
+sanitize_label_text (const gchar * label)
+{
+ /* Label contains underscores, which we like, and pango markup,
+ which we don't. */
+ gchar * sanitized = NULL;
+ GError * error = NULL;
+
+ if (label == NULL) {
+ return NULL;
+ }
+
+ if (pango_parse_markup (label, -1, 0, NULL, &sanitized, NULL, &error)) {
+ return sanitized;
+ }
+
+ if (error != NULL) {
+ g_warning ("Could not parse '%s': %s", label, error->message);
+ g_error_free (error);
+ }
+ return g_strdup (label);
+}
+
+static gchar *
+sanitize_label (GtkLabel * label)
+{
+ gchar * text;
+
+ if (gtk_label_get_use_markup (label)) {
+ text = sanitize_label_text (gtk_label_get_label (label));
+ }
+ else {
+ text = g_strdup (gtk_label_get_label (label));
+ }
+
+ if (!gtk_label_get_use_underline (label)) {
+ /* Insert extra underscores */
+ GRegex * regex = g_regex_new ("_", 0, 0, NULL);
+ gchar * escaped = g_regex_replace_literal (regex, text, -1, 0, "__", 0, NULL);
+
+ g_regex_unref (regex);
+ g_free (text);
+
+ text = escaped;
+ }
+
+ return text;
+}
+
/* Turn a widget into a dbusmenu item depending on the type of GTK
object that it is. */
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))
{
- DbusmenuMenuitem *mi = dbusmenu_menuitem_new ();
+ DbusmenuMenuitem *mi = new_menuitem(widget);
+
+ ParserData *pdata = (ParserData *)g_object_get_data(G_OBJECT(mi), PARSER_DATA);
gboolean visible = FALSE;
gboolean sensitive = FALSE;
- if (GTK_IS_SEPARATOR_MENU_ITEM (widget))
+ if (GTK_IS_SEPARATOR_MENU_ITEM (widget) || !find_menu_label (widget))
{
dbusmenu_menuitem_property_set (mi,
- "type",
- "separator");
+ DBUSMENU_MENUITEM_PROP_TYPE,
+ DBUSMENU_CLIENT_TYPES_SEPARATOR);
visible = gtk_widget_get_visible (widget);
sensitive = gtk_widget_get_sensitive (widget);
}
else
{
- gboolean label_set = FALSE;
-
g_signal_connect (widget,
"accel-closures-changed",
G_CALLBACK (accel_changed),
@@ -278,46 +535,41 @@ construct_dbusmenu_for_widget (GtkWidget * widget)
if (GTK_IS_IMAGE_MENU_ITEM (widget))
{
GtkWidget *image;
- GtkImageType image_type;
image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (widget));
if (GTK_IS_IMAGE (image))
{
- image_type = gtk_image_get_storage_type (GTK_IMAGE (image));
-
- if (image_type == GTK_IMAGE_STOCK)
- {
- label_set = update_stock_item (mi, image);
- }
- else if (image_type == GTK_IMAGE_ICON_NAME)
- {
- update_icon_name (mi, image);
- }
- else if (image_type == GTK_IMAGE_PIXBUF)
- {
- dbusmenu_menuitem_property_set_image (mi,
- DBUSMENU_MENUITEM_PROP_ICON_DATA,
- gtk_image_get_pixbuf (GTK_IMAGE (image)));
- }
+ update_icon (mi, GTK_IMAGE (image));
+
+ /* Watch for theme changes because if gicon changes, we want to send a
+ different pixbuf. */
+ g_signal_connect(G_OBJECT(gtk_icon_theme_get_default()),
+ "changed", G_CALLBACK(theme_changed_cb), widget);
+
+ pdata->image = image;
+ g_signal_connect (G_OBJECT (image),
+ "notify",
+ G_CALLBACK (image_notify_cb),
+ mi);
+ g_object_add_weak_pointer(G_OBJECT (image), (gpointer*)&pdata->image);
}
}
GtkWidget *label = find_menu_label (widget);
- dbusmenu_menuitem_property_set (mi,
- "label",
- label ? gtk_label_get_text (GTK_LABEL (label)) : NULL);
+ // Sometimes, an app will directly find and modify the label
+ // (like empathy), so watch the label especially for that.
+ gchar * text = sanitize_label (GTK_LABEL (label));
+ dbusmenu_menuitem_property_set (mi, "label", text);
+ g_free (text);
- if (label)
- {
- // Sometimes, an app will directly find and modify the label
- // (like empathy), so watch the label especially for that.
- g_signal_connect (G_OBJECT (label),
- "notify",
- G_CALLBACK (label_notify_cb),
- mi);
- }
+ pdata->label = label;
+ g_signal_connect (G_OBJECT (label),
+ "notify",
+ G_CALLBACK (label_notify_cb),
+ mi);
+ g_object_add_weak_pointer(G_OBJECT (label), (gpointer*)&pdata->label);
if (GTK_IS_ACTIVATABLE (widget))
{
@@ -332,14 +584,22 @@ construct_dbusmenu_for_widget (GtkWidget * widget)
visible = gtk_action_is_visible (action);
sensitive = gtk_action_is_sensitive (action);
+ pdata->action = action;
g_signal_connect_object (action, "notify",
G_CALLBACK (action_notify_cb),
mi,
G_CONNECT_AFTER);
+ g_object_add_weak_pointer(G_OBJECT (action), (gpointer*)&pdata->action);
}
}
}
+ GtkWidget *submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(widget));
+ if (submenu)
+ {
+ watch_submenu(mi, submenu);
+ }
+
if (!g_object_get_data (G_OBJECT (widget), "gtk-empty-menu-item") && !GTK_IS_TEAROFF_MENU_ITEM (widget))
{
visible = gtk_widget_get_visible (widget);
@@ -357,6 +617,11 @@ construct_dbusmenu_for_widget (GtkWidget * widget)
DBUSMENU_MENUITEM_SIGNAL_ABOUT_TO_SHOW,
G_CALLBACK (item_about_to_show),
widget);
+
+ g_signal_connect (G_OBJECT (mi),
+ DBUSMENU_MENUITEM_SIGNAL_EVENT,
+ G_CALLBACK (item_handle_event),
+ widget);
}
dbusmenu_menuitem_property_set_bool (mi,
@@ -371,12 +636,18 @@ construct_dbusmenu_for_widget (GtkWidget * widget)
"notify",
G_CALLBACK (widget_notify_cb),
mi);
+
+ g_signal_connect (widget,
+ "add",
+ G_CALLBACK (widget_add_cb),
+ mi);
+
return mi;
}
/* If it's none of those we're going to just create a
generic menuitem as a place holder for it. */
- return dbusmenu_menuitem_new();
+ return new_menuitem(widget);
}
static void
@@ -408,48 +679,6 @@ accel_changed (GtkWidget *widget,
dbusmenu_menuitem_property_set_shortcut_menuitem (mi, GTK_MENU_ITEM (widget));
}
-static gboolean
-update_stock_item (DbusmenuMenuitem *menuitem,
- GtkWidget *widget)
-{
- GtkStockItem stock;
- GtkImage *image;
-
- g_return_val_if_fail (GTK_IS_IMAGE (widget), FALSE);
-
- image = GTK_IMAGE (widget);
-
- if (gtk_image_get_storage_type (image) != GTK_IMAGE_STOCK)
- return FALSE;
-
- gchar * stock_id = NULL;
- gtk_image_get_stock(image, &stock_id, NULL);
-
- gtk_stock_lookup (stock_id, &stock);
-
- if (should_show_image (image))
- dbusmenu_menuitem_property_set (menuitem,
- DBUSMENU_MENUITEM_PROP_ICON_NAME,
- stock_id);
- else
- dbusmenu_menuitem_property_remove (menuitem,
- DBUSMENU_MENUITEM_PROP_ICON_NAME);
-
- const gchar *label = dbusmenu_menuitem_property_get (menuitem,
- DBUSMENU_MENUITEM_PROP_LABEL);
-
- if (stock.label != NULL && label != NULL)
- {
- dbusmenu_menuitem_property_set (menuitem,
- DBUSMENU_MENUITEM_PROP_LABEL,
- stock.label);
-
- return TRUE;
- }
-
- return FALSE;
-}
-
static void
checkbox_toggled (GtkWidget *widget, DbusmenuMenuitem *mi)
{
@@ -459,27 +688,89 @@ checkbox_toggled (GtkWidget *widget, DbusmenuMenuitem *mi)
}
static void
-update_icon_name (DbusmenuMenuitem *menuitem,
- GtkWidget *widget)
+update_icon (DbusmenuMenuitem *menuitem, GtkImage *image)
{
- GtkImage *image;
-
- g_return_if_fail (GTK_IS_IMAGE (widget));
-
- image = GTK_IMAGE (widget);
+ GdkPixbuf * pixbuf = NULL;
+ const gchar * icon_name = NULL;
+ GtkStockItem stock;
+ GIcon * gicon;
+ GtkIconInfo * info;
+ gint width;
+
+ if (image != NULL && should_show_image (image)) {
+ switch (gtk_image_get_storage_type (image)) {
+ case GTK_IMAGE_EMPTY:
+ break;
+
+ case GTK_IMAGE_PIXBUF:
+ pixbuf = g_object_ref (gtk_image_get_pixbuf (image));
+ break;
+
+ case GTK_IMAGE_ICON_NAME:
+ gtk_image_get_icon_name (image, &icon_name, NULL);
+ break;
+
+ case GTK_IMAGE_STOCK:
+ gtk_image_get_stock (image, (gchar **) &icon_name, NULL);
+ if (gtk_stock_lookup (icon_name, &stock)) {
+ /* Now set label too */
+ const gchar * label = NULL;
+ label = dbusmenu_menuitem_property_get (menuitem,
+ DBUSMENU_MENUITEM_PROP_LABEL);
+ if (stock.label != NULL && label != NULL) {
+ dbusmenu_menuitem_property_set (menuitem,
+ DBUSMENU_MENUITEM_PROP_LABEL,
+ stock.label);
+ }
+ }
+ break;
+
+ case GTK_IMAGE_GICON:
+ /* Load up a pixbuf and send that over. We don't bother differentiating
+ between icon-name gicons and pixbuf gicons because even when given a
+ icon-name gicon, there's no easy way to lookup which icon-name among
+ its set is present and should be used among the icon themes available.
+ So instead, we render to a pixbuf and watch icon theme changes. */
+ gtk_image_get_gicon (image, &gicon, NULL);
+ gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, NULL);
+ info = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default (),
+ gicon, width,
+ GTK_ICON_LOOKUP_FORCE_SIZE);
+ if (info != NULL) {
+ pixbuf = gtk_icon_info_load_icon (info, NULL);
+ gtk_icon_info_free (info);
+ }
+ break;
- if (gtk_image_get_storage_type (image) != GTK_IMAGE_ICON_NAME)
- return;
+ default:
+ g_debug ("Could not handle image type %i\n", gtk_image_get_storage_type (image));
+ break;
+ }
+ }
- if (should_show_image (image)) {
- const gchar * icon_name = NULL;
- gtk_image_get_icon_name(image, &icon_name, NULL);
+ if (icon_name != NULL) {
dbusmenu_menuitem_property_set (menuitem,
DBUSMENU_MENUITEM_PROP_ICON_NAME,
icon_name);
- } else {
+ dbusmenu_menuitem_property_remove (menuitem,
+ DBUSMENU_MENUITEM_PROP_ICON_DATA);
+ }
+ else if (pixbuf != NULL) {
dbusmenu_menuitem_property_remove (menuitem,
DBUSMENU_MENUITEM_PROP_ICON_NAME);
+ dbusmenu_menuitem_property_set_image (menuitem,
+ DBUSMENU_MENUITEM_PROP_ICON_DATA,
+ pixbuf);
+ }
+ else {
+ dbusmenu_menuitem_property_remove (menuitem,
+ DBUSMENU_MENUITEM_PROP_ICON_NAME);
+ dbusmenu_menuitem_property_remove (menuitem,
+ DBUSMENU_MENUITEM_PROP_ICON_DATA);
+ }
+
+ if (pixbuf != NULL) {
+ g_object_unref (pixbuf);
}
}
@@ -513,17 +804,100 @@ find_menu_label (GtkWidget *widget)
}
static void
+recreate_menu_item (DbusmenuMenuitem * parent, DbusmenuMenuitem * child)
+{
+ if (parent == NULL)
+ {
+ /* We need a parent */
+ return;
+ }
+ ParserData * pdata = g_object_get_data (G_OBJECT (child), PARSER_DATA);
+ /* Keep a pointer to the GtkMenuItem, as pdata->widget might be
+ * invalidated when we delete the DbusmenuMenuitem
+ */
+ GtkWidget * menuitem = pdata->widget;
+
+ dbusmenu_menuitem_child_delete (parent, child);
+
+ RecurseContext recurse = {0};
+ recurse.toplevel = gtk_widget_get_toplevel(menuitem);
+ recurse.parent = parent;
+
+ parse_menu_structure_helper(menuitem, &recurse);
+}
+
+static gboolean
+recreate_menu_item_in_idle_cb (gpointer data)
+{
+ DbusmenuMenuitem * child = (DbusmenuMenuitem *)data;
+ DbusmenuMenuitem * parent = dbusmenu_menuitem_get_parent (child);
+ g_object_unref (child);
+ recreate_menu_item (parent, child);
+ return FALSE;
+}
+
+static void
label_notify_cb (GtkWidget *widget,
GParamSpec *pspec,
gpointer data)
{
DbusmenuMenuitem *child = (DbusmenuMenuitem *)data;
+ GValue prop_value = {0};
+
+ g_value_init (&prop_value, pspec->value_type);
+ g_object_get_property (G_OBJECT (widget), pspec->name, &prop_value);
if (pspec->name == g_intern_static_string ("label"))
{
+ gchar * text = sanitize_label (GTK_LABEL (widget));
dbusmenu_menuitem_property_set (child,
DBUSMENU_MENUITEM_PROP_LABEL,
- gtk_label_get_text (GTK_LABEL (widget)));
+ text);
+ g_free (text);
+ }
+ else if (pspec->name == g_intern_static_string ("parent"))
+ {
+ if (GTK_WIDGET (g_value_get_object (&prop_value)) == NULL)
+ {
+ /* This label is being removed from its GtkMenuItem. The
+ * menuitem becomes a separator now. As the client doesn't handle
+ * changing types so well, we remove the current DbusmenuMenuitem
+ * and add a new one.
+ *
+ * Note, we have to defer this to idle, as we are called before
+ * bin->child member of our old parent is invalidated. If we go ahead
+ * and call parse_menu_structure_helper now, the GtkMenuItem will
+ * still appear to have a label and we never convert it to a separator
+ */
+ g_object_ref (child);
+ g_idle_add ((GSourceFunc)recreate_menu_item_in_idle_cb, child);
+ }
+ }
+
+ g_value_unset(&prop_value);
+ return;
+}
+
+static void
+image_notify_cb (GtkWidget *widget,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ DbusmenuMenuitem *mi = (DbusmenuMenuitem *)data;
+
+ if (pspec->name == g_intern_static_string ("file") ||
+ pspec->name == g_intern_static_string ("gicon") ||
+ pspec->name == g_intern_static_string ("icon-name") ||
+ pspec->name == g_intern_static_string ("icon-set") ||
+ pspec->name == g_intern_static_string ("image") ||
+ pspec->name == g_intern_static_string ("mask") ||
+ pspec->name == g_intern_static_string ("pixbuf") ||
+ pspec->name == g_intern_static_string ("pixbuf-animation") ||
+ pspec->name == g_intern_static_string ("pixmap") ||
+ pspec->name == g_intern_static_string ("stock") ||
+ pspec->name == g_intern_static_string ("storage-type"))
+ {
+ update_icon (mi, GTK_IMAGE (widget));
}
}
@@ -554,9 +928,11 @@ action_notify_cb (GtkAction *action,
}
else if (pspec->name == g_intern_static_string ("label"))
{
+ gchar * text = sanitize_label_text (gtk_action_get_label (action));
dbusmenu_menuitem_property_set (mi,
DBUSMENU_MENUITEM_PROP_LABEL,
- gtk_action_get_label (action));
+ text);
+ g_free (text);
}
}
@@ -571,7 +947,9 @@ item_activated (DbusmenuMenuitem *item, guint timestamp, gpointer user_data)
if (GTK_IS_MENU_ITEM (child))
{
+ gdk_threads_enter ();
gtk_menu_item_activate (GTK_MENU_ITEM (child));
+ gdk_threads_leave ();
}
}
}
@@ -596,38 +974,94 @@ item_about_to_show (DbusmenuMenuitem *item, gpointer user_data)
return TRUE;
}
+static gboolean
+item_handle_event (DbusmenuMenuitem *item, const gchar *name,
+ GVariant *variant, guint timestamp, GtkWidget *widget)
+{
+ if (g_strcmp0 (name, DBUSMENU_MENUITEM_EVENT_OPENED) == 0)
+ {
+ GtkWidget *submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
+ if (submenu != NULL)
+ {
+ // Show the submenu so the app can notice and futz with the menus as
+ // desired (empathy and geany do this)
+ gtk_widget_show (submenu);
+ }
+ }
+ else if (g_strcmp0 (name, DBUSMENU_MENUITEM_EVENT_CLOSED) == 0)
+ {
+ GtkWidget *submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
+ if (submenu != NULL)
+ {
+ // Hide the submenu so the app can notice and futz with the menus as
+ // desired (empathy and geany do this)
+ gtk_widget_hide (submenu);
+ }
+ }
+
+ return FALSE; // just pass through on everything
+}
+
+static gboolean
+handle_first_label (DbusmenuMenuitem *mi)
+{
+ ParserData *pdata = g_object_get_data (G_OBJECT (mi), PARSER_DATA);
+ if (!pdata->label)
+ {
+ /* GtkMenuItem's can start life as a separator if they have no child
+ * GtkLabel. In this case, we need to convert the DbusmenuMenuitem from
+ * a separator to a normal menuitem if the application adds a label.
+ * As changing types isn't handled too well by the client, we delete
+ * this menuitem for now and then recreate it
+ */
+ DbusmenuMenuitem * parent = dbusmenu_menuitem_get_parent (mi);
+ recreate_menu_item (parent, mi);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static void
widget_notify_cb (GtkWidget *widget,
GParamSpec *pspec,
gpointer data)
{
DbusmenuMenuitem *child = (DbusmenuMenuitem *)data;
+ GValue prop_value = {0};
+
+ g_value_init (&prop_value, pspec->value_type);
+ g_object_get_property (G_OBJECT (widget), pspec->name, &prop_value);
if (pspec->name == g_intern_static_string ("sensitive"))
{
dbusmenu_menuitem_property_set_bool (child,
DBUSMENU_MENUITEM_PROP_ENABLED,
- gtk_widget_get_sensitive (widget));
+ g_value_get_boolean (&prop_value));
}
else if (pspec->name == g_intern_static_string ("label"))
{
+ if (handle_first_label (child))
+ {
+ return;
+ }
+
dbusmenu_menuitem_property_set (child,
DBUSMENU_MENUITEM_PROP_LABEL,
- gtk_menu_item_get_label (GTK_MENU_ITEM (widget)));
+ g_value_get_string (&prop_value));
}
else if (pspec->name == g_intern_static_string ("visible"))
{
dbusmenu_menuitem_property_set_bool (child,
DBUSMENU_MENUITEM_PROP_VISIBLE,
- gtk_widget_get_visible (widget));
+ g_value_get_boolean (&prop_value));
}
- else if (pspec->name == g_intern_static_string ("stock"))
+ else if (pspec->name == g_intern_static_string ("image") ||
+ pspec->name == g_intern_static_string ("always-show-image"))
{
- update_stock_item (child, widget);
- }
- else if (pspec->name == g_intern_static_string ("icon-name"))
- {
- update_icon_name (child, widget);
+ GtkWidget *image;
+ image = GTK_WIDGET (g_value_get_object (&prop_value));
+ update_icon (child, GTK_IMAGE (image));
}
else if (pspec->name == g_intern_static_string ("parent"))
{
@@ -635,13 +1069,13 @@ widget_notify_cb (GtkWidget *widget,
* We probably should have added a 'remove' method to the
* UbuntuMenuProxy early on, but it's late in the cycle now.
*/
- if (gtk_widget_get_parent (widget) == NULL)
+ if (GTK_WIDGET (g_value_get_object (&prop_value)) == NULL)
{
g_signal_handlers_disconnect_by_func (widget,
G_CALLBACK (widget_notify_cb),
child);
- DbusmenuMenuitem *parent = g_object_get_data (G_OBJECT (child), "dbusmenu-parent");
+ DbusmenuMenuitem *parent = dbusmenu_menuitem_get_parent (child);
if (DBUSMENU_IS_MENUITEM (parent) && DBUSMENU_IS_MENUITEM (child))
{
@@ -649,6 +1083,104 @@ widget_notify_cb (GtkWidget *widget,
}
}
}
+ else if (pspec->name == g_intern_static_string ("submenu"))
+ {
+ /* The underlying submenu got swapped out. Let's see what it is now. */
+ /* First, delete any children that may exist currently. */
+ DbusmenuMenuitem * item = DBUSMENU_MENUITEM(g_object_get_data(G_OBJECT(widget), CACHED_MENUITEM));
+ if (item != NULL)
+ {
+ GList * children = dbusmenu_menuitem_take_children (item);
+ GList * child = children;
+ while (child != NULL) {
+ g_object_unref (G_OBJECT(child->data));
+ child = child->next;
+ }
+ g_list_free(children);
+ }
+
+ /* Now parse new submenu. */
+ RecurseContext recurse = {0};
+ recurse.toplevel = gtk_widget_get_toplevel(widget);
+ recurse.parent = item;
+
+ 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. */
+ parse_menu_structure_helper(widget, &recurse);
+ g_object_unref(G_OBJECT(recurse.parent));
+ }
+ }
+ 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)
+{
+ DbusmenuMenuitem *menuitem = (DbusmenuMenuitem *)data;
+
+ RecurseContext recurse = {0};
+ 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);
+}
+
+/* A child item was added to a menu we're watching. Let's try to integrate it. */
+static void
+child_removed_cb (GtkContainer *menu, GtkWidget *widget, gpointer data)
+{
+ gpointer pmi = g_object_get_data(G_OBJECT(widget), CACHED_MENUITEM);
+ if (pmi == NULL) {
+ return;
+ }
+
+ DbusmenuMenuitem * child = DBUSMENU_MENUITEM(pmi);
+
+ pmi = g_object_get_data(G_OBJECT(menu), CACHED_MENUITEM);
+ if (pmi == NULL) {
+ return;
+ }
+
+ DbusmenuMenuitem * parent = DBUSMENU_MENUITEM(pmi);
+
+ dbusmenu_menuitem_child_delete(parent, child);
+ return;
+}
+
+static void
+theme_changed_cb (GtkIconTheme *theme, gpointer data)
+{
+ GtkWidget *image;
+
+ image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (data));
+
+ gpointer pmi = g_object_get_data(G_OBJECT(data), CACHED_MENUITEM);
+ if (pmi != NULL) {
+ update_icon(DBUSMENU_MENUITEM(pmi), GTK_IMAGE(image));
+ }
+
+ /* Switch signal to new theme */
+ g_signal_handlers_disconnect_by_func(theme, G_CALLBACK(theme_changed_cb), data);
+ g_signal_connect(gtk_icon_theme_get_default(), "changed", G_CALLBACK(theme_changed_cb), data);
}
static gboolean
diff --git a/libdbusmenu-gtk/parser.h b/libdbusmenu-gtk/parser.h
index a40d709..97fa9c6 100644
--- a/libdbusmenu-gtk/parser.h
+++ b/libdbusmenu-gtk/parser.h
@@ -32,6 +32,21 @@ License version 3 and version 2.1 along with this program. If not, see
#include <libdbusmenu-glib/menuitem.h>
#include <gtk/gtk.h>
+G_BEGIN_DECLS
+
DbusmenuMenuitem * dbusmenu_gtk_parse_menu_structure (GtkWidget * widget);
+DbusmenuMenuitem * dbusmenu_gtk_parse_get_cached_item (GtkWidget * widget);
+
+/**
+ SECTION:parser
+ @short_description: A parser of in-memory GTK menu trees
+ @stability: Unstable
+ @include: libdbusmenu-gtk/parser.h
+
+ The parser will take a GTK menu tree and attach it to a Dbusmenu menu
+ tree. Along with setting up all the signals for updates and destruction.
+ The returned item would be the root item of the given tree.
+*/
+G_END_DECLS
#endif /* DBUSMENU_GTK_PARSER_H__ */
diff --git a/libdbusmenu-gtk/serializablemenuitem.c b/libdbusmenu-gtk/serializablemenuitem.c
deleted file mode 100644
index cfd864d..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 (DbusmenuClient * client, const gchar * type, 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(client, class->get_type_string(), 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 db28a24..0000000
--- a/libdbusmenu-gtk/serializablemenuitem.h
+++ /dev/null
@@ -1,115 +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))
-
-#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.
-*/
-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);
-
-G_END_DECLS
-
-#endif