Merge lp:~ted/libdbusmenu/now-with-parsing into lp:libdbusmenu/0.5
- now-with-parsing
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mikkel Kamstrup Erlandsen (community) | Needs Fixing | ||
Review via email: mp+47603@code.launchpad.net |
Commit message
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:
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_
> documentation.
r211
> In order to help would/be consumers with debugging you
> should also add a g_return_
> GTK_IS_MENU_SHELL (widget),NULL).
r212
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", >k_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 | +} |
review needsfixing
Just two minor things; dbusmenu_ gtk_parse_ menu_structure( ) needs val_if_ fail(GTK_ IS_MENU_ ITEM(widget) ||
documentation. In order to help would/be consumers with debugging you
should also add a g_return_
GTK_IS_MENU_SHELL (widget),NULL).