Merge lp:~ted/libdbusmenu/parse-serializable-menuitem into lp:libdbusmenu/0.5

Proposed by Ted Gould
Status: Superseded
Proposed branch: lp:~ted/libdbusmenu/parse-serializable-menuitem
Merge into: lp:libdbusmenu/0.5
Prerequisite: lp:~ted/libdbusmenu/serializable-menuitem
Diff against target: 1069 lines (+925/-9)
9 files modified
.bzrignore (+4/-0)
libdbusmenu-glib/client.h (+21/-0)
libdbusmenu-gtk/Makefile.am (+3/-0)
libdbusmenu-gtk/parser.c (+698/-0)
libdbusmenu-gtk/parser.h (+37/-0)
libdbusmenu-gtk/serializablemenuitem.c (+5/-5)
libdbusmenu-gtk/serializablemenuitem.h (+9/-2)
tests/Makefile.am (+33/-2)
tests/test-gtk-parser.c (+115/-0)
To merge this branch: bzr merge lp:~ted/libdbusmenu/parse-serializable-menuitem
Reviewer Review Type Date Requested Status
Mikkel Kamstrup Erlandsen Pending
Review via email: mp+47668@code.launchpad.net

This proposal supersedes a proposal from 2011-01-26.

This proposal has been superseded by a proposal from 2011-01-27.

Description of the change

Makes the parser look at serializable menuitems. This is also dependent on the serializable menuitem branch, but I can't be dependent on two. Please ignore that file :)

Resubmitting so it's dependent on serializable-menuitem as the parser is now in trunk. Hopefully will create a better diff.

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

How does this branch relate to https://code.launchpad.net/~ted/dbusmenu/serializable-menuitem/+merge/47604 ? They look identical?

review: Needs Information
196. By Ted Gould

Updating to serializeable menuitem branch. Some function prototype changes.

197. By Ted Gould

Fix changing prototypes.

Unmerged revisions

Preview Diff

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

Subscribers

People subscribed via source and target branches