From c644e10c9da09e443a00422df00a6ba9df7a7e83 Mon Sep 17 00:00:00 2001 From: Ted Gould Date: Sun, 23 Jan 2011 12:45:05 -0600 Subject: Adding the base files for the parser --- .bzrignore | 1 + libdbusmenu-gtk/Makefile.am | 7 +++++-- libdbusmenu-gtk/parser.c | 9 +++++++++ libdbusmenu-gtk/parser.h | 6 ++++++ 4 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 libdbusmenu-gtk/parser.c create mode 100644 libdbusmenu-gtk/parser.h diff --git a/.bzrignore b/.bzrignore index 44c6edb..e0fd93e 100644 --- a/.bzrignore +++ b/.bzrignore @@ -220,3 +220,4 @@ libdbusmenu-gtk/DbusmenuGtk-0.4.typelib libdbusmenu-gtk/DbusmenuGtk-0.4.vapi libdbusmenu-gtk/dbusmenu-gtk-0.4.pc libdbusmenu-gtk/dbusmenu-gtk3-0.4.pc +libdbusmenu-gtk/libdbusmenu_gtk_la-parser.lo diff --git a/libdbusmenu-gtk/Makefile.am b/libdbusmenu-gtk/Makefile.am index e5df6b1..201a631 100644 --- a/libdbusmenu-gtk/Makefile.am +++ b/libdbusmenu-gtk/Makefile.am @@ -23,7 +23,8 @@ libdbusmenu_gtkinclude_HEADERS = \ dbusmenu-gtk.h \ client.h \ menu.h \ - menuitem.h + menuitem.h \ + parser.h libdbusmenu_gtk_la_SOURCES = \ client.h \ @@ -33,7 +34,9 @@ libdbusmenu_gtk_la_SOURCES = \ menu.h \ menu.c \ menuitem.h \ - menuitem.c + menuitem.c \ + parser.h \ + parser.c libdbusmenu_gtk_la_LDFLAGS = \ -version-info $(LIBDBUSMENU_CURRENT):$(LIBDBUSMENU_REVISION):$(LIBDBUSMENU_AGE) \ diff --git a/libdbusmenu-gtk/parser.c b/libdbusmenu-gtk/parser.c new file mode 100644 index 0000000..98b3f7d --- /dev/null +++ b/libdbusmenu-gtk/parser.c @@ -0,0 +1,9 @@ + +#include "parser.h" + +DbusmenuMenuitem * +dbusmenu_gtk_parse_menu_structure (GtkMenuItem * mi) +{ + + return NULL; +} diff --git a/libdbusmenu-gtk/parser.h b/libdbusmenu-gtk/parser.h new file mode 100644 index 0000000..222d9f4 --- /dev/null +++ b/libdbusmenu-gtk/parser.h @@ -0,0 +1,6 @@ + +#include +#include + +DbusmenuMenuitem * dbusmenu_gtk_parse_menu_structure (GtkMenuItem * mi); + -- cgit v1.2.3 From b39a625defde44aa345253237d4f186abedf7196 Mon Sep 17 00:00:00 2001 From: Cody Russell Date: Sun, 23 Jan 2011 13:05:28 -0600 Subject: Taking the menu parsing code from appmenu-gtk --- libdbusmenu-gtk/parser.c | 426 ++++++++++++++++++++++++++++++++++++++++++++++- libdbusmenu-gtk/parser.h | 2 +- 2 files changed, 426 insertions(+), 2 deletions(-) diff --git a/libdbusmenu-gtk/parser.c b/libdbusmenu-gtk/parser.c index 98b3f7d..8fd0d12 100644 --- a/libdbusmenu-gtk/parser.c +++ b/libdbusmenu-gtk/parser.c @@ -1,9 +1,433 @@ #include "parser.h" +#include "menuitem.h" + +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 GtkWidget * find_menu_label (GtkWidget *widget); +static void label_notify_cb (GtkWidget *widget, GParamSpec *pspec, gpointer data); +static void action_notify_cb (GtkAction *action, GParamSpec *pspec, 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 void widget_notify_cb (GtkWidget *widget, GParamSpec *pspec, gpointer data); +static gboolean should_show_image (GtkImage *image); DbusmenuMenuitem * -dbusmenu_gtk_parse_menu_structure (GtkMenuItem * mi) +dbusmenu_gtk_parse_menu_structure (GtkWidget * widget) { + DbusmenuMenuitem *mi = dbusmenu_menuitem_new (); + + if (GTK_IS_MENU_ITEM (widget)) + { + gboolean visible = FALSE; + gboolean sensitive = FALSE; + if (GTK_IS_SEPARATOR_MENU_ITEM (widget)) + { + dbusmenu_menuitem_property_set (mi, + "type", + "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), + mi); + + if (GTK_IS_CHECK_MENU_ITEM (widget)) + { + dbusmenu_menuitem_property_set (mi, + DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, + gtk_check_menu_item_get_draw_as_radio (GTK_CHECK_MENU_ITEM (widget)) ? DBUSMENU_MENUITEM_TOGGLE_RADIO : DBUSMENU_MENUITEM_TOGGLE_CHECK); + + dbusmenu_menuitem_property_set_int (mi, + DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, + gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget)) ? DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED : DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED); + + g_signal_connect (widget, + "activate", + G_CALLBACK (checkbox_toggled), + mi); + } + + 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))); + } + } + } + + GtkWidget *label = find_menu_label (widget); + + dbusmenu_menuitem_property_set (mi, + "label", + label ? gtk_label_get_text (GTK_LABEL (label)) : NULL); + + 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); + } + + if (GTK_IS_ACTIVATABLE (widget)) + { + GtkActivatable *activatable = GTK_ACTIVATABLE (widget); + + if (gtk_activatable_get_use_action_appearance (activatable)) + { + GtkAction *action = gtk_activatable_get_related_action (activatable); + + if (action) + { + visible = gtk_action_is_visible (action); + sensitive = gtk_action_is_sensitive (action); + + g_signal_connect_object (action, "notify", + G_CALLBACK (action_notify_cb), + mi, + G_CONNECT_AFTER); + } + } + } + + if (!g_object_get_data (G_OBJECT (widget), "gtk-empty-menu-item") && !GTK_IS_TEAROFF_MENU_ITEM (widget)) + { + visible = gtk_widget_get_visible (widget); + sensitive = gtk_widget_get_sensitive (widget); + } + + dbusmenu_menuitem_property_set_shortcut_menuitem (mi, GTK_MENU_ITEM (widget)); + + g_signal_connect (G_OBJECT (mi), + DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, + G_CALLBACK (item_activated), + widget); + + g_signal_connect (G_OBJECT (mi), + DBUSMENU_MENUITEM_SIGNAL_ABOUT_TO_SHOW, + G_CALLBACK (item_about_to_show), + widget); + } + + dbusmenu_menuitem_property_set_bool (mi, + DBUSMENU_MENUITEM_PROP_VISIBLE, + visible); + + dbusmenu_menuitem_property_set_bool (mi, + DBUSMENU_MENUITEM_PROP_ENABLED, + sensitive); + + g_signal_connect (widget, + "notify", + G_CALLBACK (widget_notify_cb), + mi); + } + + return mi; return NULL; } + +static void +accel_changed (GtkWidget *widget, + gpointer data) +{ + DbusmenuMenuitem *mi = (DbusmenuMenuitem *)data; + 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; + + gtk_stock_lookup (image->data.stock.stock_id, &stock); + + if (should_show_image (image)) + dbusmenu_menuitem_property_set (menuitem, + DBUSMENU_MENUITEM_PROP_ICON_NAME, + image->data.stock.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) +{ + dbusmenu_menuitem_property_set_int (mi, + DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, + gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget)) ? DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED : DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED); +} + +static void +update_icon_name (DbusmenuMenuitem *menuitem, + GtkWidget *widget) +{ + GtkImage *image; + + g_return_if_fail (GTK_IS_IMAGE (widget)); + + image = GTK_IMAGE (widget); + + if (gtk_image_get_storage_type (image) != GTK_IMAGE_ICON_NAME) + return; + + if (should_show_image (image)) + dbusmenu_menuitem_property_set (menuitem, + DBUSMENU_MENUITEM_PROP_ICON_NAME, + image->data.name.icon_name); + else + dbusmenu_menuitem_property_remove (menuitem, + DBUSMENU_MENUITEM_PROP_ICON_NAME); +} + +static GtkWidget * +find_menu_label (GtkWidget *widget) +{ + GtkWidget *label = NULL; + + if (GTK_IS_LABEL (widget)) + return widget; + + if (GTK_IS_CONTAINER (widget)) + { + GList *children; + GList *l; + + children = gtk_container_get_children (GTK_CONTAINER (widget)); + + for (l = children; l; l = l->next) + { + label = find_menu_label (l->data); + + if (label) + break; + } + + g_list_free (children); + } + + return label; +} + +static void +label_notify_cb (GtkWidget *widget, + GParamSpec *pspec, + gpointer data) +{ + DbusmenuMenuitem *child = (DbusmenuMenuitem *)data; + + if (pspec->name == g_intern_static_string ("label")) + { + dbusmenu_menuitem_property_set (child, + DBUSMENU_MENUITEM_PROP_LABEL, + gtk_label_get_text (GTK_LABEL (widget))); + } +} + +static void +action_notify_cb (GtkAction *action, + GParamSpec *pspec, + gpointer data) +{ + DbusmenuMenuitem *mi = (DbusmenuMenuitem *)data; + + if (pspec->name == g_intern_static_string ("sensitive")) + { + dbusmenu_menuitem_property_set_bool (mi, + DBUSMENU_MENUITEM_PROP_ENABLED, + gtk_action_is_sensitive (action)); + } + else if (pspec->name == g_intern_static_string ("visible")) + { + dbusmenu_menuitem_property_set_bool (mi, + DBUSMENU_MENUITEM_PROP_VISIBLE, + gtk_action_is_visible (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))); + } + else if (pspec->name == g_intern_static_string ("label")) + { + dbusmenu_menuitem_property_set (mi, + DBUSMENU_MENUITEM_PROP_LABEL, + gtk_action_get_label (action)); + } +} + +static void +item_activated (DbusmenuMenuitem *item, guint timestamp, gpointer user_data) +{ + GtkWidget *child; + + if (user_data != NULL) + { + child = (GtkWidget *)user_data; + + if (GTK_IS_MENU_ITEM (child)) + { + gtk_menu_item_activate (GTK_MENU_ITEM (child)); + } + } +} + +static gboolean +item_about_to_show (DbusmenuMenuitem *item, gpointer user_data) +{ + GtkWidget *child; + + if (user_data != NULL) + { + child = (GtkWidget *)user_data; + + if (GTK_IS_MENU_ITEM (child)) + { + // Only called for items with submens. So we activate it here in + // case the program dynamically creates menus (like empathy does) + gtk_menu_item_activate (GTK_MENU_ITEM (child)); + } + } + + return TRUE; +} + +static void +widget_notify_cb (GtkWidget *widget, + GParamSpec *pspec, + gpointer data) +{ + DbusmenuMenuitem *child = (DbusmenuMenuitem *)data; + + if (pspec->name == g_intern_static_string ("sensitive")) + { + dbusmenu_menuitem_property_set_bool (child, + DBUSMENU_MENUITEM_PROP_ENABLED, + gtk_widget_get_sensitive (widget)); + } + else if (pspec->name == g_intern_static_string ("label")) + { + dbusmenu_menuitem_property_set (child, + DBUSMENU_MENUITEM_PROP_LABEL, + gtk_menu_item_get_label (GTK_MENU_ITEM (widget))); + } + else if (pspec->name == g_intern_static_string ("visible")) + { + dbusmenu_menuitem_property_set_bool (child, + DBUSMENU_MENUITEM_PROP_VISIBLE, + gtk_widget_get_visible (widget)); + } + else if (pspec->name == g_intern_static_string ("stock")) + { + update_stock_item (child, widget); + } + else if (pspec->name == g_intern_static_string ("icon-name")) + { + update_icon_name (child, widget); + } + else if (pspec->name == g_intern_static_string ("parent")) + { + /* + * 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) + { + g_signal_handlers_disconnect_by_func (widget, + G_CALLBACK (widget_notify_cb), + child); + + DbusmenuMenuitem *parent = g_object_get_data (G_OBJECT (child), "dbusmenu-parent"); + + if (DBUSMENU_IS_MENUITEM (parent) && DBUSMENU_IS_MENUITEM (child)) + { + dbusmenu_menuitem_child_delete (parent, child); + } + } + } +} + +static gboolean +should_show_image (GtkImage *image) +{ + GtkWidget *item; + + item = gtk_widget_get_ancestor (GTK_WIDGET (image), + GTK_TYPE_IMAGE_MENU_ITEM); + + if (item) + { + GtkSettings *settings; + gboolean gtk_menu_images; + + settings = gtk_widget_get_settings (item); + + g_object_get (settings, "gtk-menu-images", >k_menu_images, NULL); + + if (gtk_menu_images) + return TRUE; + + return gtk_image_menu_item_get_always_show_image (GTK_IMAGE_MENU_ITEM (item)); + } + + return FALSE; +} + diff --git a/libdbusmenu-gtk/parser.h b/libdbusmenu-gtk/parser.h index 222d9f4..3cffb9c 100644 --- a/libdbusmenu-gtk/parser.h +++ b/libdbusmenu-gtk/parser.h @@ -2,5 +2,5 @@ #include #include -DbusmenuMenuitem * dbusmenu_gtk_parse_menu_structure (GtkMenuItem * mi); +DbusmenuMenuitem * dbusmenu_gtk_parse_menu_structure (GtkWidget * widget); -- cgit v1.2.3 From 708ccab44c2c88b43ad35048dc0aee81023e61f9 Mon Sep 17 00:00:00 2001 From: Ted Gould Date: Sun, 23 Jan 2011 13:11:46 -0600 Subject: Reformating prototypes because it makes the code run faster. --- libdbusmenu-gtk/parser.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/libdbusmenu-gtk/parser.c b/libdbusmenu-gtk/parser.c index 8fd0d12..f0c22be 100644 --- a/libdbusmenu-gtk/parser.c +++ b/libdbusmenu-gtk/parser.c @@ -2,17 +2,30 @@ #include "parser.h" #include "menuitem.h" -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 GtkWidget * find_menu_label (GtkWidget *widget); -static void label_notify_cb (GtkWidget *widget, GParamSpec *pspec, gpointer data); -static void action_notify_cb (GtkAction *action, GParamSpec *pspec, 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 void widget_notify_cb (GtkWidget *widget, GParamSpec *pspec, gpointer data); -static gboolean should_show_image (GtkImage *image); +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 GtkWidget * find_menu_label (GtkWidget * widget); +static void label_notify_cb (GtkWidget * widget, + GParamSpec * pspec, + gpointer data); +static void action_notify_cb (GtkAction * action, + GParamSpec * pspec, + 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 void widget_notify_cb (GtkWidget * widget, + GParamSpec * pspec, + gpointer data); +static gboolean should_show_image (GtkImage * image); DbusmenuMenuitem * dbusmenu_gtk_parse_menu_structure (GtkWidget * widget) -- cgit v1.2.3 From 401bc7c326c518ad01c1edd38155d89d9e35fd49 Mon Sep 17 00:00:00 2001 From: Ted Gould Date: Sun, 23 Jan 2011 13:16:20 -0600 Subject: Adding copyright headers --- libdbusmenu-gtk/parser.c | 27 +++++++++++++++++++++++++++ libdbusmenu-gtk/parser.h | 31 +++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/libdbusmenu-gtk/parser.c b/libdbusmenu-gtk/parser.c index f0c22be..cba6b42 100644 --- a/libdbusmenu-gtk/parser.c +++ b/libdbusmenu-gtk/parser.c @@ -1,3 +1,30 @@ +/* +Parse to take a set of GTK Menus and turn them into something that can +be sent over the wire. + +Copyright 2011 Canonical Ltd. + +Authors: + Numerous (check Bazaar) + +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 + +*/ #include "parser.h" #include "menuitem.h" diff --git a/libdbusmenu-gtk/parser.h b/libdbusmenu-gtk/parser.h index 3cffb9c..a40d709 100644 --- a/libdbusmenu-gtk/parser.h +++ b/libdbusmenu-gtk/parser.h @@ -1,6 +1,37 @@ +/* +Parse to take a set of GTK Menus and turn them into something that can +be sent over the wire. + +Copyright 2011 Canonical Ltd. + +Authors: + Ted Gould + +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 + +*/ + +#ifndef DBUSMENU_GTK_PARSER_H__ +#define DBUSMENU_GTK_PARSER_H__ #include #include DbusmenuMenuitem * dbusmenu_gtk_parse_menu_structure (GtkWidget * widget); +#endif /* DBUSMENU_GTK_PARSER_H__ */ -- cgit v1.2.3 From fda2b25645a35ec7b3baddc366e483a8a9b83e58 Mon Sep 17 00:00:00 2001 From: Ted Gould Date: Sun, 23 Jan 2011 13:30:00 -0600 Subject: Adding in a basic test that just ensures the parser isn't entirely broken --- .bzrignore | 3 +++ tests/Makefile.am | 35 +++++++++++++++++++++++++++-- tests/test-gtk-parser.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 tests/test-gtk-parser.c diff --git a/.bzrignore b/.bzrignore index e0fd93e..df33247 100644 --- a/.bzrignore +++ b/.bzrignore @@ -221,3 +221,6 @@ libdbusmenu-gtk/DbusmenuGtk-0.4.vapi libdbusmenu-gtk/dbusmenu-gtk-0.4.pc libdbusmenu-gtk/dbusmenu-gtk3-0.4.pc libdbusmenu-gtk/libdbusmenu_gtk_la-parser.lo +test-gtk-parser +test-gtk-parser-test +test-gtk-parser.xml diff --git a/tests/Makefile.am b/tests/Makefile.am index 17b44d1..62142dc 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -16,7 +16,8 @@ TESTS = \ test-gtk-label \ test-gtk-shortcut \ test-gtk-reorder \ - test-gtk-submenu + test-gtk-submenu \ + test-gtk-parser-test check_PROGRAMS = \ glib-server-nomenu \ @@ -42,7 +43,8 @@ check_PROGRAMS = \ test-json-client \ test-json-server \ test-gtk-submenu-server \ - test-gtk-submenu-client + test-gtk-submenu-client \ + test-gtk-parser XVFB_RUN=". $(srcdir)/run-xvfb.sh" @@ -384,6 +386,35 @@ test_gtk_objects_LDADD = \ $(DBUSMENUGLIB_LIBS) \ $(DBUSMENUGTK_LIBS) +###################### +# Test GTK Parser +###################### + +GTK_PARSER_XML_REPORT = test-gtk-parser.xml + +test-gtk-parser-test: test-gtk-parser Makefile.am + @echo "#!/bin/bash" > $@ + @echo $(XVFB_RUN) >> $@ + @echo gtester --verbose -k -o $(GTK_PARSER_XML_REPORT) ./test-gtk-parser >> $@ + @chmod +x $@ + +test_gtk_parser_SOURCES = \ + test-gtk-parser.c + +test_gtk_parser_CFLAGS = \ + -I $(srcdir)/.. \ + $(DBUSMENUGLIB_CFLAGS) \ + $(DBUSMENUGTK_CFLAGS) \ + -DSRCDIR="\"$(srcdir)\"" \ + -Wall -Werror + +test_gtk_parser_LDADD = \ + ../libdbusmenu-glib/libdbusmenu-glib.la \ + ../libdbusmenu-gtk/libdbusmenu-gtk.la \ + $(DBUSMENUGLIB_LIBS) \ + $(DBUSMENUGTK_LIBS) + + ######################### # Test GTK Label ######################### diff --git a/tests/test-gtk-parser.c b/tests/test-gtk-parser.c new file mode 100644 index 0000000..74ff9b8 --- /dev/null +++ b/tests/test-gtk-parser.c @@ -0,0 +1,58 @@ +/* +Testing for the various objects just by themselves. + +Copyright 2011 Canonical Ltd. + +Authors: + Ted Gould + +This program is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License version 3, 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 GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program. If not, see . +*/ + +#include + +/* Just makes sure we can connect here people */ +static void +test_parser_runs (void) +{ + GtkWidget * gmi = gtk_menu_item_new_with_label("Test Item"); + g_assert(gmi != NULL); + DbusmenuMenuitem * mi = dbusmenu_gtk_parse_menu_structure(gmi); + g_assert(mi != NULL); + + g_object_unref(gmi); + g_object_unref(mi); + + return; +} + +/* Build the test suite */ +static void +test_gtk_parser_suite (void) +{ + g_test_add_func ("/dbusmenu/gtk/parser/base", test_parser_runs); + return; +} + +gint +main (gint argc, gchar * argv[]) +{ + gtk_init(&argc, &argv); + g_test_init(&argc, &argv, NULL); + + /* Test suites */ + test_gtk_parser_suite(); + + + return g_test_run (); +} -- cgit v1.2.3 From 0ab1ae08ad882569f301b00b174d1c823adb9674 Mon Sep 17 00:00:00 2001 From: Ted Gould Date: Sun, 23 Jan 2011 14:00:19 -0600 Subject: Adding a child test, but it's failing to make the children. --- tests/test-gtk-parser.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/tests/test-gtk-parser.c b/tests/test-gtk-parser.c index 74ff9b8..e6d5385 100644 --- a/tests/test-gtk-parser.c +++ b/tests/test-gtk-parser.c @@ -36,11 +36,62 @@ test_parser_runs (void) return; } +const gchar * test_parser_children_builder = +"" +"" +"" +/* Start menu bar */ +"True" +/* Child 1 */ +"TrueChild One" +/* Child 2 */ +"TrueChild Two" +/* Child 3 */ +"TrueChild Three" +/* Child 4 */ +"TrueChild Four" +/* Stop menubar */ +"" +""; + +/* Ensure the parser can find children */ +static void +test_parser_children (void) { + GtkBuilder * builder = gtk_builder_new(); + g_assert(builder != NULL); + + GError * error = NULL; + gtk_builder_add_from_string(builder, test_parser_children_builder, -1, &error); + if (error != NULL) { + g_error("Unable to parse UI definition: %s", error->message); + g_error_free(error); + error = NULL; + } + + GtkWidget * menu = GTK_WIDGET(gtk_builder_get_object(builder, "menubar")); + g_assert(menu != NULL); + + DbusmenuMenuitem * mi = dbusmenu_gtk_parse_menu_structure(menu); + g_assert(mi != NULL); + + GList * children = dbusmenu_menuitem_get_children(mi); + g_assert(children != NULL); + + g_assert(g_list_length(children) == 4); + + g_object_unref(mi); + g_object_unref(menu); + g_object_unref(builder); + + return; +} + /* Build the test suite */ static void test_gtk_parser_suite (void) { g_test_add_func ("/dbusmenu/gtk/parser/base", test_parser_runs); + g_test_add_func ("/dbusmenu/gtk/parser/children", test_parser_children); return; } -- cgit v1.2.3 From 90cc7e167c0fc9c709379afa1f22e0a2c494c347 Mon Sep 17 00:00:00 2001 From: Ted Gould Date: Sun, 23 Jan 2011 14:10:43 -0600 Subject: Adding some debug code, commented out. --- tests/test-gtk-parser.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test-gtk-parser.c b/tests/test-gtk-parser.c index e6d5385..ee608ae 100644 --- a/tests/test-gtk-parser.c +++ b/tests/test-gtk-parser.c @@ -19,6 +19,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#include #include /* Just makes sure we can connect here people */ @@ -74,6 +75,12 @@ test_parser_children (void) { DbusmenuMenuitem * mi = dbusmenu_gtk_parse_menu_structure(menu); g_assert(mi != NULL); +/* + GPtrArray * xmlarray = g_ptr_array_new(); + dbusmenu_menuitem_buildxml(mi, xmlarray); + g_debug("XML: %s", g_strjoinv("", (gchar **)xmlarray->pdata)); +*/ + GList * children = dbusmenu_menuitem_get_children(mi); g_assert(children != NULL); -- cgit v1.2.3 From 32a7eb938893bcc1bc5644aa95c63041e9d4db01 Mon Sep 17 00:00:00 2001 From: Ted Gould Date: Sun, 23 Jan 2011 15:09:51 -0600 Subject: Needed to go up a level to get recursion code. --- libdbusmenu-gtk/parser.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/libdbusmenu-gtk/parser.c b/libdbusmenu-gtk/parser.c index cba6b42..08613ac 100644 --- a/libdbusmenu-gtk/parser.c +++ b/libdbusmenu-gtk/parser.c @@ -29,6 +29,14 @@ License version 3 and version 2.1 along with this program. If not, see #include "parser.h" #include "menuitem.h" +typedef struct _RecurseContext +{ + gint count; + DbusmenuMenuitem *stack[30]; +} RecurseContext; + +static void parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse); +static DbusmenuMenuitem * construct_dbusmenu_for_widget (GtkWidget * widget); static void accel_changed (GtkWidget * widget, gpointer data); static gboolean update_stock_item (DbusmenuMenuitem * menuitem, @@ -56,6 +64,156 @@ static gboolean should_show_image (GtkImage * image); DbusmenuMenuitem * dbusmenu_gtk_parse_menu_structure (GtkWidget * widget) +{ + RecurseContext recurse = {0}; + recurse.count = -1; + + parse_menu_structure_helper(widget, &recurse); + + if (recurse.stack[0] != NULL && DBUSMENU_IS_MENUITEM(recurse.stack[0])) { + return recurse.stack[0]; + } + + return NULL; +} + +static void +parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse) +{ + if (GTK_IS_CONTAINER (widget)) + { + gboolean increment = GTK_IS_MENU_BAR (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) + { + DbusmenuMenuitem *dmi = NULL; //g_hash_table_lookup (recurse->context->lookup, widget); + if (dmi != NULL) + { + if (increment) + recurse->count--; + + return; + } + else + { + recurse->stack[recurse->count] = construct_dbusmenu_for_widget (widget); + //g_hash_table_insert (recurse->context->lookup, widget, recurse->stack[recurse->count]); + } + + if (!gtk_widget_get_visible (widget)) + { + /*g_signal_connect (G_OBJECT (widget), + "notify", + G_CALLBACK (menuitem_notify_cb), + recurse->context); */ + } + + 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]); + + dbusmenu_menuitem_child_append (item, recurse->stack[recurse->count]); + } + } + } + } + } + + 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 (increment) + recurse->count--; + } +} + +static DbusmenuMenuitem * +construct_dbusmenu_for_widget (GtkWidget * widget) { DbusmenuMenuitem *mi = dbusmenu_menuitem_new (); -- cgit v1.2.3 From f9cca3a5b9f42963b43b78670720f0cecd7843ea Mon Sep 17 00:00:00 2001 From: Ted Gould Date: Sun, 23 Jan 2011 15:10:08 -0600 Subject: For some reason this isn't an object, seems like a builder error, but we're not checking for those really. --- tests/test-gtk-parser.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test-gtk-parser.c b/tests/test-gtk-parser.c index ee608ae..b66b46a 100644 --- a/tests/test-gtk-parser.c +++ b/tests/test-gtk-parser.c @@ -88,7 +88,6 @@ test_parser_children (void) { g_object_unref(mi); g_object_unref(menu); - g_object_unref(builder); return; } -- cgit v1.2.3 From 65b9cda89a5bef2c541f5ed2624e6e199f136435 Mon Sep 17 00:00:00 2001 From: Ted Gould Date: Sun, 23 Jan 2011 15:40:26 -0600 Subject: Trying to put back in the visibility notifier to force the rebuild, but I'm not quite sure how to do that yet. --- libdbusmenu-gtk/parser.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/libdbusmenu-gtk/parser.c b/libdbusmenu-gtk/parser.c index 08613ac..9d0f9b4 100644 --- a/libdbusmenu-gtk/parser.c +++ b/libdbusmenu-gtk/parser.c @@ -31,6 +31,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]; } RecurseContext; @@ -61,12 +62,17 @@ static void widget_notify_cb (GtkWidget * widget, GParamSpec * pspec, gpointer data); static gboolean should_show_image (GtkImage * image); +static void menuitem_notify_cb (GtkWidget * widget, + GParamSpec * pspec, + gpointer data); DbusmenuMenuitem * dbusmenu_gtk_parse_menu_structure (GtkWidget * widget) { RecurseContext recurse = {0}; + recurse.count = -1; + recurse.toplevel = gtk_widget_get_toplevel(widget); parse_menu_structure_helper(widget, &recurse); @@ -129,10 +135,10 @@ parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse) if (!gtk_widget_get_visible (widget)) { - /*g_signal_connect (G_OBJECT (widget), - "notify", + g_signal_connect (G_OBJECT (widget), + "notify::visible", G_CALLBACK (menuitem_notify_cb), - recurse->context); */ + recurse->toplevel); } if (GTK_IS_TEAROFF_MENU_ITEM (widget)) @@ -358,6 +364,27 @@ construct_dbusmenu_for_widget (GtkWidget * widget) return NULL; } +static void +menuitem_notify_cb (GtkWidget *widget, + GParamSpec *pspec, + gpointer data) +{ + if (pspec->name == g_intern_static_string ("visible")) + { + GtkWidget * new_toplevel = gtk_widget_get_toplevel (widget); + GtkWidget * old_toplevel = GTK_WIDGET(data); + + if (new_toplevel == old_toplevel) { + /* TODO: Figure this out -> rebuild (context->bridge, window); */ + } + + /* We only care about this once, so let's disconnect now. */ + g_signal_handlers_disconnect_by_func (widget, + G_CALLBACK (menuitem_notify_cb), + data); + } +} + static void accel_changed (GtkWidget *widget, gpointer data) -- cgit v1.2.3 From a439aeb8fd08d9858055bbb2ae1326a1335600d1 Mon Sep 17 00:00:00 2001 From: Ted Gould Date: Sun, 23 Jan 2011 15:53:27 -0600 Subject: Reimplementing the cache based on object data with signals to cleanup --- libdbusmenu-gtk/parser.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/libdbusmenu-gtk/parser.c b/libdbusmenu-gtk/parser.c index 9d0f9b4..f1c46ba 100644 --- a/libdbusmenu-gtk/parser.c +++ b/libdbusmenu-gtk/parser.c @@ -29,6 +29,8 @@ License version 3 and version 2.1 along with this program. If not, see #include "parser.h" #include "menuitem.h" +#define CACHED_MENUITEM "dbusmenu-gtk-parser-cached-item" + typedef struct _RecurseContext { GtkWidget * toplevel; @@ -83,6 +85,20 @@ dbusmenu_gtk_parse_menu_structure (GtkWidget * widget) return NULL; } +static void +dbusmenu_cache_freed (gpointer data, GObject * obj) +{ + g_object_set_data(G_OBJECT(data), CACHED_MENUITEM, NULL); + return; +} + +static void +object_cache_freed (gpointer data) +{ + g_object_weak_unref(G_OBJECT(data), dbusmenu_cache_freed, data); + return; +} + static void parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse) { @@ -119,7 +135,10 @@ parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse) if (recurse->count > -1 && increment) { - DbusmenuMenuitem *dmi = NULL; //g_hash_table_lookup (recurse->context->lookup, widget); + 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) @@ -130,7 +149,8 @@ parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse) else { recurse->stack[recurse->count] = construct_dbusmenu_for_widget (widget); - //g_hash_table_insert (recurse->context->lookup, widget, recurse->stack[recurse->count]); + 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)) -- cgit v1.2.3 From fc096ca12b152461dfddbfdf74720b98691448ce Mon Sep 17 00:00:00 2001 From: Ted Gould Date: Sun, 23 Jan 2011 16:08:39 -0600 Subject: Stop using the data field in gtk image for GTK3 --- libdbusmenu-gtk/parser.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/libdbusmenu-gtk/parser.c b/libdbusmenu-gtk/parser.c index f1c46ba..cfce42a 100644 --- a/libdbusmenu-gtk/parser.c +++ b/libdbusmenu-gtk/parser.c @@ -427,12 +427,15 @@ update_stock_item (DbusmenuMenuitem *menuitem, if (gtk_image_get_storage_type (image) != GTK_IMAGE_STOCK) return FALSE; - gtk_stock_lookup (image->data.stock.stock_id, &stock); + 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, - image->data.stock.stock_id); + stock_id); else dbusmenu_menuitem_property_remove (menuitem, DBUSMENU_MENUITEM_PROP_ICON_NAME); @@ -473,13 +476,16 @@ update_icon_name (DbusmenuMenuitem *menuitem, if (gtk_image_get_storage_type (image) != GTK_IMAGE_ICON_NAME) return; - if (should_show_image (image)) + if (should_show_image (image)) { + const gchar * icon_name = NULL; + gtk_image_get_icon_name(image, &icon_name, NULL); dbusmenu_menuitem_property_set (menuitem, DBUSMENU_MENUITEM_PROP_ICON_NAME, - image->data.name.icon_name); - else + icon_name); + } else { dbusmenu_menuitem_property_remove (menuitem, DBUSMENU_MENUITEM_PROP_ICON_NAME); + } } static GtkWidget * -- cgit v1.2.3 From 25ab785826d59d8ca7860b0d0bc39d6187a3a1ed Mon Sep 17 00:00:00 2001 From: Ted Gould Date: Mon, 24 Jan 2011 14:56:07 -0600 Subject: Fixing referencing warnings on destruction. --- libdbusmenu-gtk/parser.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libdbusmenu-gtk/parser.c b/libdbusmenu-gtk/parser.c index cfce42a..52bd8a8 100644 --- a/libdbusmenu-gtk/parser.c +++ b/libdbusmenu-gtk/parser.c @@ -88,7 +88,9 @@ dbusmenu_gtk_parse_menu_structure (GtkWidget * widget) static void dbusmenu_cache_freed (gpointer data, GObject * obj) { - g_object_set_data(G_OBJECT(data), CACHED_MENUITEM, NULL); + /* 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); return; } -- cgit v1.2.3 From 32def89e3191d994a43d222c1e81c746922b688d Mon Sep 17 00:00:00 2001 From: Ted Gould Date: Tue, 25 Jan 2011 11:50:46 -0600 Subject: Moving one up the stack so GtkMenu works as well --- libdbusmenu-gtk/parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdbusmenu-gtk/parser.c b/libdbusmenu-gtk/parser.c index 52bd8a8..d26f8fb 100644 --- a/libdbusmenu-gtk/parser.c +++ b/libdbusmenu-gtk/parser.c @@ -106,7 +106,7 @@ parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse) { if (GTK_IS_CONTAINER (widget)) { - gboolean increment = GTK_IS_MENU_BAR (widget) || GTK_IS_MENU_ITEM (widget); + gboolean increment = GTK_IS_MENU_SHELL (widget) || GTK_IS_MENU_ITEM (widget); if (increment) recurse->count++; -- cgit v1.2.3