diff options
Diffstat (limited to 'libdbusmenu-gtk')
-rw-r--r-- | libdbusmenu-gtk/client.c | 28 | ||||
-rw-r--r-- | libdbusmenu-gtk/parser.c | 259 |
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")) { |