Merge lp:~ted/libdbusmenu/now-with-parsing into lp:libdbusmenu/0.5

Proposed by Ted Gould
Status: Merged
Merged at revision: 196
Proposed branch: lp:~ted/libdbusmenu/now-with-parsing
Merge into: lp:libdbusmenu/0.5
Diff against target: 930 lines (+861/-4)
6 files modified
.bzrignore (+4/-0)
libdbusmenu-gtk/Makefile.am (+5/-2)
libdbusmenu-gtk/parser.c (+667/-0)
libdbusmenu-gtk/parser.h (+37/-0)
tests/Makefile.am (+33/-2)
tests/test-gtk-parser.c (+115/-0)
To merge this branch: bzr merge lp:~ted/libdbusmenu/now-with-parsing
Reviewer Review Type Date Requested Status
Mikkel Kamstrup Erlandsen (community) Needs Fixing
Review via email: mp+47603@code.launchpad.net

Description of the change

This is the parser taken from appmenu-gtk with some clean ups so that it can work well with libappindicator as well. It is basically a GtkMenu to DbusmenuMenuitem translator.

To post a comment you must log in.
Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

 review needsfixing

Just two minor things; dbusmenu_gtk_parse_menu_structure() needs
documentation. In order to help would/be consumers with debugging you
should also add a g_return_val_if_fail(GTK_IS_MENU_ITEM(widget) ||
GTK_IS_MENU_SHELL (widget),NULL).

review: Needs Fixing
Revision history for this message
Ted Gould (ted) wrote :

On Thu, 2011-01-27 at 08:46 +0000, Mikkel Kamstrup Erlandsen wrote:
> dbusmenu_gtk_parse_menu_structure() needs
> documentation.

r211

> In order to help would/be consumers with debugging you
> should also add a g_return_val_if_fail(GTK_IS_MENU_ITEM(widget) ||
> GTK_IS_MENU_SHELL (widget),NULL).

r212

lp:~ted/libdbusmenu/now-with-parsing updated
211. By Ted Gould

Adding documentation of the public API.

212. By Ted Gould

Adding an early check on the type of widget coming into the function.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2010-12-02 20:13:09 +0000
3+++ .bzrignore 2011-01-27 14:52:20 +0000
4@@ -220,3 +220,7 @@
5 libdbusmenu-gtk/DbusmenuGtk-0.4.vapi
6 libdbusmenu-gtk/dbusmenu-gtk-0.4.pc
7 libdbusmenu-gtk/dbusmenu-gtk3-0.4.pc
8+libdbusmenu-gtk/libdbusmenu_gtk_la-parser.lo
9+test-gtk-parser
10+test-gtk-parser-test
11+test-gtk-parser.xml
12
13=== modified file 'libdbusmenu-gtk/Makefile.am'
14--- libdbusmenu-gtk/Makefile.am 2011-01-21 16:35:55 +0000
15+++ libdbusmenu-gtk/Makefile.am 2011-01-27 14:52:20 +0000
16@@ -23,7 +23,8 @@
17 dbusmenu-gtk.h \
18 client.h \
19 menu.h \
20- menuitem.h
21+ menuitem.h \
22+ parser.h
23
24 libdbusmenu_gtk_la_SOURCES = \
25 client.h \
26@@ -33,7 +34,9 @@
27 menu.h \
28 menu.c \
29 menuitem.h \
30- menuitem.c
31+ menuitem.c \
32+ parser.h \
33+ parser.c
34
35 libdbusmenu_gtk_la_LDFLAGS = \
36 -version-info $(LIBDBUSMENU_CURRENT):$(LIBDBUSMENU_REVISION):$(LIBDBUSMENU_AGE) \
37
38=== added file 'libdbusmenu-gtk/parser.c'
39--- libdbusmenu-gtk/parser.c 1970-01-01 00:00:00 +0000
40+++ libdbusmenu-gtk/parser.c 2011-01-27 14:52:20 +0000
41@@ -0,0 +1,667 @@
42+/*
43+Parse to take a set of GTK Menus and turn them into something that can
44+be sent over the wire.
45+
46+Copyright 2011 Canonical Ltd.
47+
48+Authors:
49+ Numerous (check Bazaar)
50+
51+This program is free software: you can redistribute it and/or modify it
52+under the terms of either or both of the following licenses:
53+
54+1) the GNU Lesser General Public License version 3, as published by the
55+Free Software Foundation; and/or
56+2) the GNU Lesser General Public License version 2.1, as published by
57+the Free Software Foundation.
58+
59+This program is distributed in the hope that it will be useful, but
60+WITHOUT ANY WARRANTY; without even the implied warranties of
61+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
62+PURPOSE. See the applicable version of the GNU Lesser General Public
63+License for more details.
64+
65+You should have received a copy of both the GNU Lesser General Public
66+License version 3 and version 2.1 along with this program. If not, see
67+<http://www.gnu.org/licenses/>
68+*/
69+
70+#include "parser.h"
71+#include "menuitem.h"
72+
73+#define CACHED_MENUITEM "dbusmenu-gtk-parser-cached-item"
74+
75+typedef struct _RecurseContext
76+{
77+ GtkWidget * toplevel;
78+ DbusmenuMenuitem * parent;
79+} RecurseContext;
80+
81+static void parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse);
82+static DbusmenuMenuitem * construct_dbusmenu_for_widget (GtkWidget * widget);
83+static void accel_changed (GtkWidget * widget,
84+ gpointer data);
85+static gboolean update_stock_item (DbusmenuMenuitem * menuitem,
86+ GtkWidget * widget);
87+static void checkbox_toggled (GtkWidget * widget,
88+ DbusmenuMenuitem * mi);
89+static void update_icon_name (DbusmenuMenuitem * menuitem,
90+ GtkWidget * widget);
91+static GtkWidget * find_menu_label (GtkWidget * widget);
92+static void label_notify_cb (GtkWidget * widget,
93+ GParamSpec * pspec,
94+ gpointer data);
95+static void action_notify_cb (GtkAction * action,
96+ GParamSpec * pspec,
97+ gpointer data);
98+static void item_activated (DbusmenuMenuitem * item,
99+ guint timestamp,
100+ gpointer user_data);
101+static gboolean item_about_to_show (DbusmenuMenuitem * item,
102+ gpointer user_data);
103+static void widget_notify_cb (GtkWidget * widget,
104+ GParamSpec * pspec,
105+ gpointer data);
106+static gboolean should_show_image (GtkImage * image);
107+static void menuitem_notify_cb (GtkWidget * widget,
108+ GParamSpec * pspec,
109+ gpointer data);
110+
111+/**
112+ dbusmenu_gtk_parse_menu_structure:
113+ @widget: A #GtkMenuItem or #GtkMenuShell to turn into a #DbusmenuMenuitem
114+
115+ Goes through the GTK structures and turns them into the appropraite
116+ Dbusmenu structures along with setting up all the relationships
117+ between the objects. It also stores the dbusmenu items as a cache
118+ on the GTK items so that they'll be reused if necissary.
119+
120+ Return value: A dbusmenu item representing the menu structure
121+*/
122+DbusmenuMenuitem *
123+dbusmenu_gtk_parse_menu_structure (GtkWidget * widget)
124+{
125+ g_return_val_if_fail(GTK_IS_MENU_ITEM(widget) || GTK_IS_MENU_SHELL(widget), NULL);
126+
127+ RecurseContext recurse = {0};
128+
129+ recurse.toplevel = gtk_widget_get_toplevel(widget);
130+
131+ parse_menu_structure_helper(widget, &recurse);
132+
133+ return recurse.parent;
134+}
135+
136+/* Called when the dbusmenu item that we're keeping around
137+ is finalized */
138+static void
139+dbusmenu_cache_freed (gpointer data, GObject * obj)
140+{
141+ /* If the dbusmenu item is killed we don't need to remove
142+ the weak ref as well. */
143+ g_object_steal_data(G_OBJECT(data), CACHED_MENUITEM);
144+ g_signal_handlers_disconnect_by_func(data, G_CALLBACK(widget_notify_cb), obj);
145+ return;
146+}
147+
148+/* Called if we replace the cache on the object with a new
149+ dbusmenu menuitem */
150+static void
151+object_cache_freed (gpointer data)
152+{
153+ if (!G_IS_OBJECT(data)) return;
154+ g_object_weak_unref(G_OBJECT(data), dbusmenu_cache_freed, data);
155+ return;
156+}
157+
158+static void
159+parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse)
160+{
161+
162+ /* If this is a shell, then let's handle the items in it. */
163+ if (GTK_IS_MENU_SHELL (widget)) {
164+ /* Okay, this is a little janky and all.. but some applications update some
165+ * menuitem properties such as sensitivity on the activate callback. This
166+ * seems a little weird, but it's not our place to judge when all this code
167+ * is so crazy. So we're going to get ever crazier and activate all the
168+ * menus that are directly below the menubar and force the applications to
169+ * update their sensitivity. The menus won't actually popup in the app
170+ * window due to our gtk+ patches.
171+ *
172+ * Note that this will not force menuitems in submenus to be updated as well.
173+ */
174+ if (recurse->parent == NULL && GTK_IS_MENU_BAR(widget)) {
175+ GList *children = gtk_container_get_children (GTK_CONTAINER (widget));
176+
177+ for (; children != NULL; children = children->next) {
178+ gtk_menu_shell_activate_item (GTK_MENU_SHELL (widget),
179+ children->data,
180+ TRUE);
181+ }
182+
183+ g_list_free (children);
184+ }
185+
186+ if (recurse->parent == NULL) {
187+ recurse->parent = dbusmenu_menuitem_new();
188+ }
189+
190+ gtk_container_foreach (GTK_CONTAINER (widget),
191+ (GtkCallback)parse_menu_structure_helper,
192+ recurse);
193+ return;
194+ }
195+
196+ if (GTK_IS_MENU_ITEM(widget)) {
197+ DbusmenuMenuitem * thisitem = NULL;
198+
199+ /* Check to see if we're cached already */
200+ gpointer pmi = g_object_get_data(G_OBJECT(widget), CACHED_MENUITEM);
201+ if (pmi != NULL) {
202+ thisitem = DBUSMENU_MENUITEM(pmi);
203+ g_object_ref(G_OBJECT(thisitem));
204+ }
205+
206+ /* We don't have one, so we'll need to build it */
207+ if (thisitem == NULL) {
208+ thisitem = construct_dbusmenu_for_widget (widget);
209+ g_object_set_data_full(G_OBJECT(widget), CACHED_MENUITEM, thisitem, object_cache_freed);
210+ g_object_weak_ref(G_OBJECT(thisitem), dbusmenu_cache_freed, widget);
211+
212+ if (!gtk_widget_get_visible (widget)) {
213+ g_signal_connect (G_OBJECT (widget),
214+ "notify::visible",
215+ G_CALLBACK (menuitem_notify_cb),
216+ recurse->toplevel);
217+ }
218+
219+ if (GTK_IS_TEAROFF_MENU_ITEM (widget)) {
220+ dbusmenu_menuitem_property_set_bool (thisitem,
221+ DBUSMENU_MENUITEM_PROP_VISIBLE,
222+ FALSE);
223+ }
224+ }
225+
226+ /* Check to see if we're in our parents list of children, if we have
227+ a parent. */
228+ if (recurse->parent != NULL) {
229+ GList * children = dbusmenu_menuitem_get_children (recurse->parent);
230+ GList * peek = NULL;
231+
232+ if (children != NULL) {
233+ peek = g_list_find (children, thisitem);
234+ }
235+
236+ /* Oops, let's tell our parents about us */
237+ if (peek == NULL) {
238+ /* TODO: Should we set a weak ref on the parent? */
239+ g_object_set_data (G_OBJECT (thisitem),
240+ "dbusmenu-parent",
241+ recurse->parent);
242+ dbusmenu_menuitem_child_append (recurse->parent,
243+ thisitem);
244+ }
245+ }
246+
247+ GtkWidget *menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
248+ if (menu != NULL) {
249+ DbusmenuMenuitem * parent_save = recurse->parent;
250+ recurse->parent = thisitem;
251+ parse_menu_structure_helper (menu, recurse);
252+ recurse->parent = parent_save;
253+ }
254+
255+ if (recurse->parent == NULL) {
256+ recurse->parent = thisitem;
257+ } else {
258+ g_object_unref(thisitem);
259+ }
260+ }
261+
262+ return;
263+}
264+
265+static DbusmenuMenuitem *
266+construct_dbusmenu_for_widget (GtkWidget * widget)
267+{
268+ DbusmenuMenuitem *mi = dbusmenu_menuitem_new ();
269+
270+ if (GTK_IS_MENU_ITEM (widget))
271+ {
272+ gboolean visible = FALSE;
273+ gboolean sensitive = FALSE;
274+ if (GTK_IS_SEPARATOR_MENU_ITEM (widget))
275+ {
276+ dbusmenu_menuitem_property_set (mi,
277+ "type",
278+ "separator");
279+
280+ visible = gtk_widget_get_visible (widget);
281+ sensitive = gtk_widget_get_sensitive (widget);
282+ }
283+ else
284+ {
285+ gboolean label_set = FALSE;
286+
287+ g_signal_connect (widget,
288+ "accel-closures-changed",
289+ G_CALLBACK (accel_changed),
290+ mi);
291+
292+ if (GTK_IS_CHECK_MENU_ITEM (widget))
293+ {
294+ dbusmenu_menuitem_property_set (mi,
295+ DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE,
296+ gtk_check_menu_item_get_draw_as_radio (GTK_CHECK_MENU_ITEM (widget)) ? DBUSMENU_MENUITEM_TOGGLE_RADIO : DBUSMENU_MENUITEM_TOGGLE_CHECK);
297+
298+ dbusmenu_menuitem_property_set_int (mi,
299+ DBUSMENU_MENUITEM_PROP_TOGGLE_STATE,
300+ gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget)) ? DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED : DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED);
301+
302+ g_signal_connect (widget,
303+ "activate",
304+ G_CALLBACK (checkbox_toggled),
305+ mi);
306+ }
307+
308+ if (GTK_IS_IMAGE_MENU_ITEM (widget))
309+ {
310+ GtkWidget *image;
311+ GtkImageType image_type;
312+
313+ image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (widget));
314+
315+ if (GTK_IS_IMAGE (image))
316+ {
317+ image_type = gtk_image_get_storage_type (GTK_IMAGE (image));
318+
319+ if (image_type == GTK_IMAGE_STOCK)
320+ {
321+ label_set = update_stock_item (mi, image);
322+ }
323+ else if (image_type == GTK_IMAGE_ICON_NAME)
324+ {
325+ update_icon_name (mi, image);
326+ }
327+ else if (image_type == GTK_IMAGE_PIXBUF)
328+ {
329+ dbusmenu_menuitem_property_set_image (mi,
330+ DBUSMENU_MENUITEM_PROP_ICON_DATA,
331+ gtk_image_get_pixbuf (GTK_IMAGE (image)));
332+ }
333+ }
334+ }
335+
336+ GtkWidget *label = find_menu_label (widget);
337+
338+ dbusmenu_menuitem_property_set (mi,
339+ "label",
340+ label ? gtk_label_get_text (GTK_LABEL (label)) : NULL);
341+
342+ if (label)
343+ {
344+ // Sometimes, an app will directly find and modify the label
345+ // (like empathy), so watch the label especially for that.
346+ g_signal_connect (G_OBJECT (label),
347+ "notify",
348+ G_CALLBACK (label_notify_cb),
349+ mi);
350+ }
351+
352+ if (GTK_IS_ACTIVATABLE (widget))
353+ {
354+ GtkActivatable *activatable = GTK_ACTIVATABLE (widget);
355+
356+ if (gtk_activatable_get_use_action_appearance (activatable))
357+ {
358+ GtkAction *action = gtk_activatable_get_related_action (activatable);
359+
360+ if (action)
361+ {
362+ visible = gtk_action_is_visible (action);
363+ sensitive = gtk_action_is_sensitive (action);
364+
365+ g_signal_connect_object (action, "notify",
366+ G_CALLBACK (action_notify_cb),
367+ mi,
368+ G_CONNECT_AFTER);
369+ }
370+ }
371+ }
372+
373+ if (!g_object_get_data (G_OBJECT (widget), "gtk-empty-menu-item") && !GTK_IS_TEAROFF_MENU_ITEM (widget))
374+ {
375+ visible = gtk_widget_get_visible (widget);
376+ sensitive = gtk_widget_get_sensitive (widget);
377+ }
378+
379+ dbusmenu_menuitem_property_set_shortcut_menuitem (mi, GTK_MENU_ITEM (widget));
380+
381+ g_signal_connect (G_OBJECT (mi),
382+ DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
383+ G_CALLBACK (item_activated),
384+ widget);
385+
386+ g_signal_connect (G_OBJECT (mi),
387+ DBUSMENU_MENUITEM_SIGNAL_ABOUT_TO_SHOW,
388+ G_CALLBACK (item_about_to_show),
389+ widget);
390+ }
391+
392+ dbusmenu_menuitem_property_set_bool (mi,
393+ DBUSMENU_MENUITEM_PROP_VISIBLE,
394+ visible);
395+
396+ dbusmenu_menuitem_property_set_bool (mi,
397+ DBUSMENU_MENUITEM_PROP_ENABLED,
398+ sensitive);
399+
400+ g_signal_connect (widget,
401+ "notify",
402+ G_CALLBACK (widget_notify_cb),
403+ mi);
404+ }
405+
406+ return mi;
407+
408+ return NULL;
409+}
410+
411+static void
412+menuitem_notify_cb (GtkWidget *widget,
413+ GParamSpec *pspec,
414+ gpointer data)
415+{
416+ if (pspec->name == g_intern_static_string ("visible"))
417+ {
418+ GtkWidget * new_toplevel = gtk_widget_get_toplevel (widget);
419+ GtkWidget * old_toplevel = GTK_WIDGET(data);
420+
421+ if (new_toplevel == old_toplevel) {
422+ /* TODO: Figure this out -> rebuild (context->bridge, window); */
423+ }
424+
425+ /* We only care about this once, so let's disconnect now. */
426+ g_signal_handlers_disconnect_by_func (widget,
427+ G_CALLBACK (menuitem_notify_cb),
428+ data);
429+ }
430+}
431+
432+static void
433+accel_changed (GtkWidget *widget,
434+ gpointer data)
435+{
436+ DbusmenuMenuitem *mi = (DbusmenuMenuitem *)data;
437+ dbusmenu_menuitem_property_set_shortcut_menuitem (mi, GTK_MENU_ITEM (widget));
438+}
439+
440+static gboolean
441+update_stock_item (DbusmenuMenuitem *menuitem,
442+ GtkWidget *widget)
443+{
444+ GtkStockItem stock;
445+ GtkImage *image;
446+
447+ g_return_val_if_fail (GTK_IS_IMAGE (widget), FALSE);
448+
449+ image = GTK_IMAGE (widget);
450+
451+ if (gtk_image_get_storage_type (image) != GTK_IMAGE_STOCK)
452+ return FALSE;
453+
454+ gchar * stock_id = NULL;
455+ gtk_image_get_stock(image, &stock_id, NULL);
456+
457+ gtk_stock_lookup (stock_id, &stock);
458+
459+ if (should_show_image (image))
460+ dbusmenu_menuitem_property_set (menuitem,
461+ DBUSMENU_MENUITEM_PROP_ICON_NAME,
462+ stock_id);
463+ else
464+ dbusmenu_menuitem_property_remove (menuitem,
465+ DBUSMENU_MENUITEM_PROP_ICON_NAME);
466+
467+ const gchar *label = dbusmenu_menuitem_property_get (menuitem,
468+ DBUSMENU_MENUITEM_PROP_LABEL);
469+
470+ if (stock.label != NULL && label != NULL)
471+ {
472+ dbusmenu_menuitem_property_set (menuitem,
473+ DBUSMENU_MENUITEM_PROP_LABEL,
474+ stock.label);
475+
476+ return TRUE;
477+ }
478+
479+ return FALSE;
480+}
481+
482+static void
483+checkbox_toggled (GtkWidget *widget, DbusmenuMenuitem *mi)
484+{
485+ dbusmenu_menuitem_property_set_int (mi,
486+ DBUSMENU_MENUITEM_PROP_TOGGLE_STATE,
487+ gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget)) ? DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED : DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED);
488+}
489+
490+static void
491+update_icon_name (DbusmenuMenuitem *menuitem,
492+ GtkWidget *widget)
493+{
494+ GtkImage *image;
495+
496+ g_return_if_fail (GTK_IS_IMAGE (widget));
497+
498+ image = GTK_IMAGE (widget);
499+
500+ if (gtk_image_get_storage_type (image) != GTK_IMAGE_ICON_NAME)
501+ return;
502+
503+ if (should_show_image (image)) {
504+ const gchar * icon_name = NULL;
505+ gtk_image_get_icon_name(image, &icon_name, NULL);
506+ dbusmenu_menuitem_property_set (menuitem,
507+ DBUSMENU_MENUITEM_PROP_ICON_NAME,
508+ icon_name);
509+ } else {
510+ dbusmenu_menuitem_property_remove (menuitem,
511+ DBUSMENU_MENUITEM_PROP_ICON_NAME);
512+ }
513+}
514+
515+static GtkWidget *
516+find_menu_label (GtkWidget *widget)
517+{
518+ GtkWidget *label = NULL;
519+
520+ if (GTK_IS_LABEL (widget))
521+ return widget;
522+
523+ if (GTK_IS_CONTAINER (widget))
524+ {
525+ GList *children;
526+ GList *l;
527+
528+ children = gtk_container_get_children (GTK_CONTAINER (widget));
529+
530+ for (l = children; l; l = l->next)
531+ {
532+ label = find_menu_label (l->data);
533+
534+ if (label)
535+ break;
536+ }
537+
538+ g_list_free (children);
539+ }
540+
541+ return label;
542+}
543+
544+static void
545+label_notify_cb (GtkWidget *widget,
546+ GParamSpec *pspec,
547+ gpointer data)
548+{
549+ DbusmenuMenuitem *child = (DbusmenuMenuitem *)data;
550+
551+ if (pspec->name == g_intern_static_string ("label"))
552+ {
553+ dbusmenu_menuitem_property_set (child,
554+ DBUSMENU_MENUITEM_PROP_LABEL,
555+ gtk_label_get_text (GTK_LABEL (widget)));
556+ }
557+}
558+
559+static void
560+action_notify_cb (GtkAction *action,
561+ GParamSpec *pspec,
562+ gpointer data)
563+{
564+ DbusmenuMenuitem *mi = (DbusmenuMenuitem *)data;
565+
566+ if (pspec->name == g_intern_static_string ("sensitive"))
567+ {
568+ dbusmenu_menuitem_property_set_bool (mi,
569+ DBUSMENU_MENUITEM_PROP_ENABLED,
570+ gtk_action_is_sensitive (action));
571+ }
572+ else if (pspec->name == g_intern_static_string ("visible"))
573+ {
574+ dbusmenu_menuitem_property_set_bool (mi,
575+ DBUSMENU_MENUITEM_PROP_VISIBLE,
576+ gtk_action_is_visible (action));
577+ }
578+ else if (pspec->name == g_intern_static_string ("active"))
579+ {
580+ dbusmenu_menuitem_property_set_int (mi,
581+ DBUSMENU_MENUITEM_PROP_TOGGLE_STATE,
582+ gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) ? DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED : DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED);
583+ }
584+ else if (pspec->name == g_intern_static_string ("label"))
585+ {
586+ dbusmenu_menuitem_property_set (mi,
587+ DBUSMENU_MENUITEM_PROP_LABEL,
588+ gtk_action_get_label (action));
589+ }
590+}
591+
592+static void
593+item_activated (DbusmenuMenuitem *item, guint timestamp, gpointer user_data)
594+{
595+ GtkWidget *child;
596+
597+ if (user_data != NULL)
598+ {
599+ child = (GtkWidget *)user_data;
600+
601+ if (GTK_IS_MENU_ITEM (child))
602+ {
603+ gtk_menu_item_activate (GTK_MENU_ITEM (child));
604+ }
605+ }
606+}
607+
608+static gboolean
609+item_about_to_show (DbusmenuMenuitem *item, gpointer user_data)
610+{
611+ GtkWidget *child;
612+
613+ if (user_data != NULL)
614+ {
615+ child = (GtkWidget *)user_data;
616+
617+ if (GTK_IS_MENU_ITEM (child))
618+ {
619+ // Only called for items with submens. So we activate it here in
620+ // case the program dynamically creates menus (like empathy does)
621+ gtk_menu_item_activate (GTK_MENU_ITEM (child));
622+ }
623+ }
624+
625+ return TRUE;
626+}
627+
628+static void
629+widget_notify_cb (GtkWidget *widget,
630+ GParamSpec *pspec,
631+ gpointer data)
632+{
633+ DbusmenuMenuitem *child = (DbusmenuMenuitem *)data;
634+
635+ if (pspec->name == g_intern_static_string ("sensitive"))
636+ {
637+ dbusmenu_menuitem_property_set_bool (child,
638+ DBUSMENU_MENUITEM_PROP_ENABLED,
639+ gtk_widget_get_sensitive (widget));
640+ }
641+ else if (pspec->name == g_intern_static_string ("label"))
642+ {
643+ dbusmenu_menuitem_property_set (child,
644+ DBUSMENU_MENUITEM_PROP_LABEL,
645+ gtk_menu_item_get_label (GTK_MENU_ITEM (widget)));
646+ }
647+ else if (pspec->name == g_intern_static_string ("visible"))
648+ {
649+ dbusmenu_menuitem_property_set_bool (child,
650+ DBUSMENU_MENUITEM_PROP_VISIBLE,
651+ gtk_widget_get_visible (widget));
652+ }
653+ else if (pspec->name == g_intern_static_string ("stock"))
654+ {
655+ update_stock_item (child, widget);
656+ }
657+ else if (pspec->name == g_intern_static_string ("icon-name"))
658+ {
659+ update_icon_name (child, widget);
660+ }
661+ else if (pspec->name == g_intern_static_string ("parent"))
662+ {
663+ /*
664+ * We probably should have added a 'remove' method to the
665+ * UbuntuMenuProxy early on, but it's late in the cycle now.
666+ */
667+ if (gtk_widget_get_parent (widget) == NULL)
668+ {
669+ g_signal_handlers_disconnect_by_func (widget,
670+ G_CALLBACK (widget_notify_cb),
671+ child);
672+
673+ DbusmenuMenuitem *parent = g_object_get_data (G_OBJECT (child), "dbusmenu-parent");
674+
675+ if (DBUSMENU_IS_MENUITEM (parent) && DBUSMENU_IS_MENUITEM (child))
676+ {
677+ dbusmenu_menuitem_child_delete (parent, child);
678+ }
679+ }
680+ }
681+}
682+
683+static gboolean
684+should_show_image (GtkImage *image)
685+{
686+ GtkWidget *item;
687+
688+ item = gtk_widget_get_ancestor (GTK_WIDGET (image),
689+ GTK_TYPE_IMAGE_MENU_ITEM);
690+
691+ if (item)
692+ {
693+ GtkSettings *settings;
694+ gboolean gtk_menu_images;
695+
696+ settings = gtk_widget_get_settings (item);
697+
698+ g_object_get (settings, "gtk-menu-images", &gtk_menu_images, NULL);
699+
700+ if (gtk_menu_images)
701+ return TRUE;
702+
703+ return gtk_image_menu_item_get_always_show_image (GTK_IMAGE_MENU_ITEM (item));
704+ }
705+
706+ return FALSE;
707+}
708+
709
710=== added file 'libdbusmenu-gtk/parser.h'
711--- libdbusmenu-gtk/parser.h 1970-01-01 00:00:00 +0000
712+++ libdbusmenu-gtk/parser.h 2011-01-27 14:52:20 +0000
713@@ -0,0 +1,37 @@
714+/*
715+Parse to take a set of GTK Menus and turn them into something that can
716+be sent over the wire.
717+
718+Copyright 2011 Canonical Ltd.
719+
720+Authors:
721+ Ted Gould <ted@canonical.com>
722+
723+This program is free software: you can redistribute it and/or modify it
724+under the terms of either or both of the following licenses:
725+
726+1) the GNU Lesser General Public License version 3, as published by the
727+Free Software Foundation; and/or
728+2) the GNU Lesser General Public License version 2.1, as published by
729+the Free Software Foundation.
730+
731+This program is distributed in the hope that it will be useful, but
732+WITHOUT ANY WARRANTY; without even the implied warranties of
733+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
734+PURPOSE. See the applicable version of the GNU Lesser General Public
735+License for more details.
736+
737+You should have received a copy of both the GNU Lesser General Public
738+License version 3 and version 2.1 along with this program. If not, see
739+<http://www.gnu.org/licenses/>
740+*/
741+
742+#ifndef DBUSMENU_GTK_PARSER_H__
743+#define DBUSMENU_GTK_PARSER_H__
744+
745+#include <libdbusmenu-glib/menuitem.h>
746+#include <gtk/gtk.h>
747+
748+DbusmenuMenuitem * dbusmenu_gtk_parse_menu_structure (GtkWidget * widget);
749+
750+#endif /* DBUSMENU_GTK_PARSER_H__ */
751
752=== modified file 'tests/Makefile.am'
753--- tests/Makefile.am 2010-12-08 03:17:43 +0000
754+++ tests/Makefile.am 2011-01-27 14:52:20 +0000
755@@ -16,7 +16,8 @@
756 test-gtk-label \
757 test-gtk-shortcut \
758 test-gtk-reorder \
759- test-gtk-submenu
760+ test-gtk-submenu \
761+ test-gtk-parser-test
762
763 check_PROGRAMS = \
764 glib-server-nomenu \
765@@ -42,7 +43,8 @@
766 test-json-client \
767 test-json-server \
768 test-gtk-submenu-server \
769- test-gtk-submenu-client
770+ test-gtk-submenu-client \
771+ test-gtk-parser
772
773 XVFB_RUN=". $(srcdir)/run-xvfb.sh"
774
775@@ -384,6 +386,35 @@
776 $(DBUSMENUGLIB_LIBS) \
777 $(DBUSMENUGTK_LIBS)
778
779+######################
780+# Test GTK Parser
781+######################
782+
783+GTK_PARSER_XML_REPORT = test-gtk-parser.xml
784+
785+test-gtk-parser-test: test-gtk-parser Makefile.am
786+ @echo "#!/bin/bash" > $@
787+ @echo $(XVFB_RUN) >> $@
788+ @echo gtester --verbose -k -o $(GTK_PARSER_XML_REPORT) ./test-gtk-parser >> $@
789+ @chmod +x $@
790+
791+test_gtk_parser_SOURCES = \
792+ test-gtk-parser.c
793+
794+test_gtk_parser_CFLAGS = \
795+ -I $(srcdir)/.. \
796+ $(DBUSMENUGLIB_CFLAGS) \
797+ $(DBUSMENUGTK_CFLAGS) \
798+ -DSRCDIR="\"$(srcdir)\"" \
799+ -Wall -Werror
800+
801+test_gtk_parser_LDADD = \
802+ ../libdbusmenu-glib/libdbusmenu-glib.la \
803+ ../libdbusmenu-gtk/libdbusmenu-gtk.la \
804+ $(DBUSMENUGLIB_LIBS) \
805+ $(DBUSMENUGTK_LIBS)
806+
807+
808 #########################
809 # Test GTK Label
810 #########################
811
812=== added file 'tests/test-gtk-parser.c'
813--- tests/test-gtk-parser.c 1970-01-01 00:00:00 +0000
814+++ tests/test-gtk-parser.c 2011-01-27 14:52:20 +0000
815@@ -0,0 +1,115 @@
816+/*
817+Testing for the various objects just by themselves.
818+
819+Copyright 2011 Canonical Ltd.
820+
821+Authors:
822+ Ted Gould <ted@canonical.com>
823+
824+This program is free software: you can redistribute it and/or modify it
825+under the terms of the GNU General Public License version 3, as published
826+by the Free Software Foundation.
827+
828+This program is distributed in the hope that it will be useful, but
829+WITHOUT ANY WARRANTY; without even the implied warranties of
830+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
831+PURPOSE. See the GNU General Public License for more details.
832+
833+You should have received a copy of the GNU General Public License along
834+with this program. If not, see <http://www.gnu.org/licenses/>.
835+*/
836+
837+#include <libdbusmenu-glib/menuitem-private.h>
838+#include <libdbusmenu-gtk/parser.h>
839+
840+/* Just makes sure we can connect here people */
841+static void
842+test_parser_runs (void)
843+{
844+ GtkWidget * gmi = gtk_menu_item_new_with_label("Test Item");
845+ g_assert(gmi != NULL);
846+ DbusmenuMenuitem * mi = dbusmenu_gtk_parse_menu_structure(gmi);
847+ g_assert(mi != NULL);
848+
849+ g_object_unref(gmi);
850+ g_object_unref(mi);
851+
852+ return;
853+}
854+
855+const gchar * test_parser_children_builder =
856+"<?xml version=\"1.0\"?>"
857+"<interface>"
858+"<requires lib=\"gtk+\" version=\"2.16\"/>"
859+/* Start menu bar */
860+"<object class=\"GtkMenuBar\" id=\"menubar\"><property name=\"visible\">True</property>"
861+/* Child 1 */
862+"<child><object class=\"GtkMenuItem\" id=\"child_one\"><property name=\"visible\">True</property><property name=\"label\">Child One</property></object></child>"
863+/* Child 2 */
864+"<child><object class=\"GtkMenuItem\" id=\"child_two\"><property name=\"visible\">True</property><property name=\"label\">Child Two</property></object></child>"
865+/* Child 3 */
866+"<child><object class=\"GtkMenuItem\" id=\"child_three\"><property name=\"visible\">True</property><property name=\"label\">Child Three</property></object></child>"
867+/* Child 4 */
868+"<child><object class=\"GtkMenuItem\" id=\"child_four\"><property name=\"visible\">True</property><property name=\"label\">Child Four</property></object></child>"
869+/* Stop menubar */
870+"</object>"
871+"</interface>";
872+
873+/* Ensure the parser can find children */
874+static void
875+test_parser_children (void) {
876+ GtkBuilder * builder = gtk_builder_new();
877+ g_assert(builder != NULL);
878+
879+ GError * error = NULL;
880+ gtk_builder_add_from_string(builder, test_parser_children_builder, -1, &error);
881+ if (error != NULL) {
882+ g_error("Unable to parse UI definition: %s", error->message);
883+ g_error_free(error);
884+ error = NULL;
885+ }
886+
887+ GtkWidget * menu = GTK_WIDGET(gtk_builder_get_object(builder, "menubar"));
888+ g_assert(menu != NULL);
889+
890+ DbusmenuMenuitem * mi = dbusmenu_gtk_parse_menu_structure(menu);
891+ g_assert(mi != NULL);
892+
893+/*
894+ GPtrArray * xmlarray = g_ptr_array_new();
895+ dbusmenu_menuitem_buildxml(mi, xmlarray);
896+ g_debug("XML: %s", g_strjoinv("", (gchar **)xmlarray->pdata));
897+*/
898+
899+ GList * children = dbusmenu_menuitem_get_children(mi);
900+ g_assert(children != NULL);
901+
902+ g_assert(g_list_length(children) == 4);
903+
904+ g_object_unref(mi);
905+ g_object_unref(menu);
906+
907+ return;
908+}
909+
910+/* Build the test suite */
911+static void
912+test_gtk_parser_suite (void)
913+{
914+ g_test_add_func ("/dbusmenu/gtk/parser/base", test_parser_runs);
915+ g_test_add_func ("/dbusmenu/gtk/parser/children", test_parser_children);
916+ return;
917+}
918+
919+gint
920+main (gint argc, gchar * argv[])
921+{
922+ gtk_init(&argc, &argv);
923+ g_test_init(&argc, &argv, NULL);
924+
925+ /* Test suites */
926+ test_gtk_parser_suite();
927+
928+
929+ return g_test_run ();
930+}

Subscribers

People subscribed via source and target branches