aboutsummaryrefslogtreecommitdiff
path: root/libdbusmenu-gtk
diff options
context:
space:
mode:
Diffstat (limited to 'libdbusmenu-gtk')
-rw-r--r--libdbusmenu-gtk/client.c28
-rw-r--r--libdbusmenu-gtk/parser.c259
2 files changed, 146 insertions, 141 deletions
diff --git a/libdbusmenu-gtk/client.c b/libdbusmenu-gtk/client.c
index d957f25..7ab2fe9 100644
--- a/libdbusmenu-gtk/client.c
+++ b/libdbusmenu-gtk/client.c
@@ -42,6 +42,7 @@ struct _DbusmenuGtkClientPrivate {
};
#define DBUSMENU_GTKCLIENT_GET_PRIVATE(o) (DBUSMENU_GTKCLIENT(o)->priv)
+#define USE_FALLBACK_PROP "use-fallback"
/* Prototypes */
static void dbusmenu_gtkclient_class_init (DbusmenuGtkClientClass *klass);
@@ -737,6 +738,29 @@ new_item_seperator (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, Dbusm
return TRUE;
}
+/* A little helper so we don't generate a bunch of warnings
+ about being able to set use-fallback */
+static void
+set_use_fallback (GtkWidget * widget)
+{
+ static gboolean checked = FALSE;
+ static gboolean available = FALSE;
+
+ if (!checked) {
+ available = (g_object_class_find_property(G_OBJECT_CLASS(GTK_IMAGE_GET_CLASS(widget)), USE_FALLBACK_PROP) != NULL);
+ if (!available) {
+ g_warning("The '" USE_FALLBACK_PROP "' is not available on GtkImage so icons may not show correctly.");
+ }
+ checked = TRUE;
+ }
+
+ if (available) {
+ g_object_set(G_OBJECT(widget), USE_FALLBACK_PROP, TRUE, NULL);
+ }
+
+ return;
+}
+
/* This handler looks at property changes for items that are
image menu items. */
static void
@@ -789,7 +813,7 @@ image_property_handle (DbusmenuMenuitem * item, const gchar * property, GVariant
gtkimage = NULL;
} else if (g_strcmp0(iconname, DBUSMENU_MENUITEM_ICON_NAME_BLANK) == 0) {
gtkimage = gtk_image_new();
- g_object_set(G_OBJECT(gtkimage), "use-fallback", TRUE, NULL);
+ set_use_fallback(gtkimage);
} else {
/* Look to see if we want to have an icon with the 'ltr' or
'rtl' depending on what we're doing. */
@@ -808,7 +832,7 @@ image_property_handle (DbusmenuMenuitem * item, const gchar * property, GVariant
can just convert it to this name. */
if (gtkimage == NULL) {
gtkimage = gtk_image_new_from_icon_name(finaliconname, GTK_ICON_SIZE_MENU);
- g_object_set(G_OBJECT(gtkimage), "use-fallback", TRUE, NULL);
+ set_use_fallback(gtkimage);
} else {
gtk_image_set_from_icon_name(GTK_IMAGE(gtkimage), finaliconname, GTK_ICON_SIZE_MENU);
}
diff --git a/libdbusmenu-gtk/parser.c b/libdbusmenu-gtk/parser.c
index cdbc001..5d71585 100644
--- a/libdbusmenu-gtk/parser.c
+++ b/libdbusmenu-gtk/parser.c
@@ -35,8 +35,7 @@ License version 3 and version 2.1 along with this program. If not, see
typedef struct _RecurseContext
{
GtkWidget * toplevel;
- gint count;
- DbusmenuMenuitem *stack[30];
+ DbusmenuMenuitem * parent;
} RecurseContext;
static void parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse);
@@ -69,35 +68,49 @@ 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
+
+ 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: 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);
+
RecurseContext recurse = {0};
- recurse.count = -1;
recurse.toplevel = gtk_widget_get_toplevel(widget);
parse_menu_structure_helper(widget, &recurse);
- if (recurse.stack[0] != NULL && DBUSMENU_IS_MENUITEM(recurse.stack[0])) {
- return recurse.stack[0];
- }
-
- return NULL;
+ return recurse.parent;
}
+/* Called when the dbusmenu item that we're keeping around
+ is finalized */
static void
dbusmenu_cache_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;
}
+/* Called if we replace the cache on the object with a new
+ dbusmenu menuitem */
static void
object_cache_freed (gpointer data)
{
+ if (!G_IS_OBJECT(data)) return;
g_object_weak_unref(G_OBJECT(data), dbusmenu_cache_freed, data);
return;
}
@@ -105,140 +118,108 @@ object_cache_freed (gpointer data)
static void
parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse)
{
- if (GTK_IS_CONTAINER (widget))
- {
- gboolean increment = GTK_IS_MENU_SHELL (widget) || GTK_IS_MENU_ITEM (widget);
-
- if (increment)
- recurse->count++;
-
- /* Okay, this is a little janky and all.. but some applications update some
- * menuitem properties such as sensitivity on the activate callback. This
- * seems a little weird, but it's not our place to judge when all this code
- * is so crazy. So we're going to get ever crazier and activate all the
- * menus that are directly below the menubar and force the applications to
- * update their sensitivity. The menus won't actually popup in the app
- * window due to our gtk+ patches.
- *
- * Note that this will not force menuitems in submenus to be updated as well.
- */
- if (recurse->count == 0 && 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);
- }
-
- if (recurse->count > -1 && increment)
- {
- gpointer pmi = g_object_get_data(G_OBJECT(widget), CACHED_MENUITEM);
- DbusmenuMenuitem *dmi = NULL;
- if (pmi != NULL) dmi = DBUSMENU_MENUITEM(pmi);
-
- if (dmi != NULL)
- {
- if (increment)
- recurse->count--;
-
- return;
- }
- else
- {
- recurse->stack[recurse->count] = construct_dbusmenu_for_widget (widget);
- g_object_set_data_full(G_OBJECT(widget), CACHED_MENUITEM, recurse->stack[recurse->count], object_cache_freed);
- g_object_weak_ref(G_OBJECT(recurse->stack[recurse->count]), dbusmenu_cache_freed, widget);
- }
-
- if (!gtk_widget_get_visible (widget))
- {
- g_signal_connect (G_OBJECT (widget),
- "notify::visible",
- G_CALLBACK (menuitem_notify_cb),
- recurse->toplevel);
- }
-
- if (GTK_IS_TEAROFF_MENU_ITEM (widget))
- {
- dbusmenu_menuitem_property_set_bool (recurse->stack[recurse->count],
- DBUSMENU_MENUITEM_PROP_VISIBLE,
- FALSE);
- }
- if (recurse->count > 0)
- {
- GList *children = NULL;
- GList *peek = NULL;
-
- if (recurse->stack[recurse->count - 1])
- {
- children = dbusmenu_menuitem_get_children (recurse->stack[recurse->count - 1]);
-
- if (children)
- {
- peek = g_list_find (children, recurse->stack[recurse->count]);
- }
-
- if (!peek)
- {
- /* Should we set a weak ref on the parent? */
- g_object_set_data (G_OBJECT (recurse->stack[recurse->count]),
- "dbusmenu-parent",
- recurse->stack[recurse->count - 1]);
- dbusmenu_menuitem_child_append (recurse->stack[recurse->count - 1],
- recurse->stack[recurse->count]);
- }
- }
- else
- {
- DbusmenuMenuitem *item = NULL; /* g_hash_table_lookup (recurse->context->lookup,
- gtk_widget_get_parent (widget)); */
-
- if (item)
- {
- children = dbusmenu_menuitem_get_children (item);
-
- if (children)
- {
- peek = g_list_find (children, recurse->stack[recurse->count]);
- }
-
- if (!peek)
- {
- g_object_set_data (G_OBJECT (recurse->stack[recurse->count]),
- "dbusmenu-parent",
- recurse->stack[recurse->count - 1]);
+ /* 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
+ * menuitem properties such as sensitivity on the activate callback. This
+ * seems a little weird, but it's not our place to judge when all this code
+ * is so crazy. So we're going to get ever crazier and activate all the
+ * menus that are directly below the menubar and force the applications to
+ * update their sensitivity. The menus won't actually popup in the app
+ * window due to our gtk+ patches.
+ *
+ * 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);
+ }
+
+ if (recurse->parent == NULL) {
+ recurse->parent = dbusmenu_menuitem_new();
+ }
+
+ gtk_container_foreach (GTK_CONTAINER (widget),
+ (GtkCallback)parse_menu_structure_helper,
+ recurse);
+ return;
+ }
- dbusmenu_menuitem_child_append (item, recurse->stack[recurse->count]);
- }
- }
- }
+ if (GTK_IS_MENU_ITEM(widget)) {
+ DbusmenuMenuitem * thisitem = NULL;
+
+ /* Check to see if we're cached already */
+ gpointer pmi = g_object_get_data(G_OBJECT(widget), CACHED_MENUITEM);
+ if (pmi != NULL) {
+ thisitem = DBUSMENU_MENUITEM(pmi);
+ g_object_ref(G_OBJECT(thisitem));
+ }
+
+ /* 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),
+ "notify::visible",
+ G_CALLBACK (menuitem_notify_cb),
+ recurse->toplevel);
}
- }
-
- gtk_container_foreach (GTK_CONTAINER (widget),
- (GtkCallback)parse_menu_structure_helper,
- recurse);
-
- if (GTK_IS_MENU_ITEM (widget))
- {
- GtkWidget *menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
- if (menu != NULL)
- {
- parse_menu_structure_helper (menu, recurse);
+ if (GTK_IS_TEAROFF_MENU_ITEM (widget)) {
+ dbusmenu_menuitem_property_set_bool (thisitem,
+ DBUSMENU_MENUITEM_PROP_VISIBLE,
+ FALSE);
}
- }
+ }
+
+ /* Check to see if we're in our parents list of children, if we have
+ a parent. */
+ if (recurse->parent != NULL) {
+ GList * children = dbusmenu_menuitem_get_children (recurse->parent);
+ GList * peek = NULL;
+
+ if (children != NULL) {
+ peek = g_list_find (children, thisitem);
+ }
+
+ /* 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);
+ }
+ }
+
+ GtkWidget *menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
+ if (menu != NULL) {
+ DbusmenuMenuitem * parent_save = recurse->parent;
+ recurse->parent = thisitem;
+ parse_menu_structure_helper (menu, recurse);
+ recurse->parent = parent_save;
+ }
+
+ if (recurse->parent == NULL) {
+ recurse->parent = thisitem;
+ } else {
+ g_object_unref(thisitem);
+ }
+ }
- if (increment)
- recurse->count--;
- }
+ return;
}
/* Turn a widget into a dbusmenu item depending on the type of GTK
@@ -567,9 +548,9 @@ action_notify_cb (GtkAction *action,
}
else if (pspec->name == g_intern_static_string ("active"))
{
- dbusmenu_menuitem_property_set_bool (mi,
- DBUSMENU_MENUITEM_PROP_TOGGLE_STATE,
- gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
+ dbusmenu_menuitem_property_set_int (mi,
+ DBUSMENU_MENUITEM_PROP_TOGGLE_STATE,
+ gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) ? DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED : DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED);
}
else if (pspec->name == g_intern_static_string ("label"))
{