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

Subscribers

People subscribed via source and target branches