1 /*
2  * Copyright 2012 Canonical Ltd.
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation, version 3 of the License.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  *
16  * Authors: Ryan Lortie <desrt@desrt.ca>
17  *          William Hua <william.hua@canonical.com>
18  */
19 
20 /**
21  * SECTION:unity-gtk-menu-shell
22  * @short_description: Menu shell proxy
23  * @include: unity-gtk-parser.h
24  *
25  * A #UnityGtkMenuShell is a #GMenuModel that acts as a proxy for a
26  * #GtkMenuShell. This can be used for purposes such as exporting menu
27  * shells over DBus with g_dbus_connection_export_menu_model ().
28  *
29  * #UnityGtkMenuShell<!-- -->s are most useful when used with
30  * #UnityGtkActionGroup<!-- -->s.
31  */
32 
33 #include "unity-gtk-action-group-private.h"
34 #include "unity-gtk-menu-section-private.h"
35 #include "unity-gtk-menu-shell-private.h"
36 
37 G_DEFINE_QUARK(menu_shell, menu_shell);
38 
39 G_DEFINE_TYPE(UnityGtkMenuShell, unity_gtk_menu_shell, G_TYPE_MENU_MODEL);
40 
41 static gboolean unity_gtk_menu_shell_debug;
42 
g_uintcmp(gconstpointer a,gconstpointer b,gpointer user_data)43 static gint g_uintcmp(gconstpointer a, gconstpointer b, gpointer user_data)
44 {
45 	return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
46 }
47 
g_sequence_get_uint(GSequenceIter * iter)48 static guint g_sequence_get_uint(GSequenceIter *iter)
49 {
50 	return GPOINTER_TO_UINT(g_sequence_get(iter));
51 }
52 
g_sequence_set_uint(GSequenceIter * iter,guint i)53 static void g_sequence_set_uint(GSequenceIter *iter, guint i)
54 {
55 	g_sequence_set(iter, GUINT_TO_POINTER(i));
56 }
57 
g_sequence_insert_sorted_uint(GSequence * sequence,guint i)58 static GSequenceIter *g_sequence_insert_sorted_uint(GSequence *sequence, guint i)
59 {
60 	return g_sequence_insert_sorted(sequence, GUINT_TO_POINTER(i), g_uintcmp, NULL);
61 }
62 
g_sequence_lookup_uint(GSequence * sequence,guint i)63 static GSequenceIter *g_sequence_lookup_uint(GSequence *sequence, guint i)
64 {
65 	return g_sequence_lookup(sequence, GUINT_TO_POINTER(i), g_uintcmp, NULL);
66 }
67 
g_sequence_search_uint(GSequence * sequence,guint i)68 static GSequenceIter *g_sequence_search_uint(GSequence *sequence, guint i)
69 {
70 	return g_sequence_search(sequence, GUINT_TO_POINTER(i), g_uintcmp, NULL);
71 }
72 
g_sequence_search_inf_uint(GSequence * sequence,guint i)73 static GSequenceIter *g_sequence_search_inf_uint(GSequence *sequence, guint i)
74 {
75 	GSequenceIter *iter = g_sequence_iter_prev(g_sequence_search_uint(sequence, i));
76 
77 	return !g_sequence_iter_is_end(iter) && g_sequence_get_uint(iter) <= i ? iter : NULL;
78 }
79 
gtk_menu_item_handle_idle_activate(gpointer user_data)80 static gboolean gtk_menu_item_handle_idle_activate(gpointer user_data)
81 {
82 	g_return_val_if_fail(GTK_IS_MENU_ITEM(user_data), G_SOURCE_REMOVE);
83 
84 	gtk_menu_item_activate(user_data);
85 
86 	return G_SOURCE_REMOVE;
87 }
88 
unity_gtk_menu_shell_get_items(UnityGtkMenuShell * shell)89 static GPtrArray *unity_gtk_menu_shell_get_items(UnityGtkMenuShell *shell)
90 {
91 	g_return_val_if_fail(UNITY_GTK_IS_MENU_SHELL(shell), NULL);
92 
93 	if (shell->items == NULL)
94 	{
95 		GList *children;
96 		GList *iter;
97 		guint i;
98 
99 		g_return_val_if_fail(shell->menu_shell != NULL, NULL);
100 
101 		shell->items = g_ptr_array_new_with_free_func(g_object_unref);
102 		children     = gtk_container_get_children(GTK_CONTAINER(shell->menu_shell));
103 
104 		for (iter = children, i = 0; iter != NULL; i++)
105 		{
106 			g_ptr_array_add(shell->items,
107 			                unity_gtk_menu_item_new(iter->data, shell, i));
108 			iter = g_list_next(iter);
109 		}
110 
111 		g_list_free(children);
112 	}
113 
114 	return shell->items;
115 }
116 
unity_gtk_menu_shell_get_sections(UnityGtkMenuShell * shell)117 static GPtrArray *unity_gtk_menu_shell_get_sections(UnityGtkMenuShell *shell)
118 {
119 	g_return_val_if_fail(UNITY_GTK_IS_MENU_SHELL(shell), NULL);
120 
121 	if (shell->sections == NULL)
122 	{
123 		GSequence *separator_indices = unity_gtk_menu_shell_get_separator_indices(shell);
124 		guint n                      = g_sequence_get_length(separator_indices);
125 		guint i;
126 
127 		shell->sections = g_ptr_array_new_full(n + 1, g_object_unref);
128 
129 		for (i = 0; i <= n; i++)
130 			g_ptr_array_add(shell->sections, unity_gtk_menu_section_new(shell, i));
131 	}
132 
133 	return shell->sections;
134 }
135 
unity_gtk_menu_shell_show_item(UnityGtkMenuShell * shell,UnityGtkMenuItem * item)136 static void unity_gtk_menu_shell_show_item(UnityGtkMenuShell *shell, UnityGtkMenuItem *item)
137 {
138 	GSequence *visible_indices;
139 
140 	g_return_if_fail(UNITY_GTK_IS_MENU_SHELL(shell));
141 	g_return_if_fail(UNITY_GTK_IS_MENU_ITEM(item));
142 	g_warn_if_fail(item->parent_shell == shell);
143 
144 	visible_indices = shell->visible_indices;
145 
146 	if (visible_indices != NULL)
147 	{
148 		GSequence *separator_indices = shell->separator_indices;
149 		guint item_index             = item->item_index;
150 		GSequenceIter *insert_iter   = g_sequence_lookup_uint(visible_indices, item_index);
151 		gboolean already_visible     = insert_iter != NULL;
152 
153 		if (!already_visible)
154 			insert_iter = g_sequence_insert_sorted_uint(visible_indices, item_index);
155 		else
156 			g_warn_if_reached();
157 
158 		if (shell->action_group != NULL)
159 		{
160 			unity_gtk_action_group_connect_item(shell->action_group, item);
161 
162 			if (item->child_shell != NULL)
163 			{
164 				if (item->child_shell_valid)
165 					unity_gtk_action_group_connect_shell(shell->action_group,
166 					                                     item->child_shell);
167 				else
168 					g_warn_if_reached();
169 			}
170 		}
171 
172 		if (separator_indices != NULL)
173 		{
174 			GPtrArray *sections = shell->sections;
175 			GSequenceIter *separator_iter =
176 			    g_sequence_search_inf_uint(separator_indices, item_index);
177 			guint section_index =
178 			    separator_iter == NULL
179 			        ? 0
180 			        : g_sequence_iter_get_position(separator_iter) + 1;
181 			gboolean separator_already_visible =
182 			    separator_iter != NULL &&
183 			    g_sequence_get_uint(separator_iter) == item_index;
184 
185 			if (!separator_already_visible)
186 			{
187 				if (unity_gtk_menu_item_is_separator(item))
188 				{
189 					g_sequence_insert_sorted_uint(separator_indices,
190 					                              item_index);
191 
192 					if (sections != NULL)
193 					{
194 						UnityGtkMenuSection *section =
195 						    g_ptr_array_index(sections, section_index);
196 						GSequenceIter *section_iter =
197 						    unity_gtk_menu_section_get_begin_iter(section);
198 						guint position =
199 						    g_sequence_iter_get_position(insert_iter) -
200 						    g_sequence_iter_get_position(section_iter);
201 						UnityGtkMenuSection *new_section =
202 						    unity_gtk_menu_section_new(shell,
203 						                               section_index + 1);
204 						guint removed = g_menu_model_get_n_items(
205 						    G_MENU_MODEL(new_section));
206 						guint i;
207 
208 						g_ptr_array_insert(sections,
209 						                   section_index + 1,
210 						                   new_section);
211 
212 						for (i = section_index + 2; i < sections->len; i++)
213 							UNITY_GTK_MENU_SECTION(
214 							    g_ptr_array_index(sections, i))
215 							    ->section_index = i;
216 
217 						if (removed)
218 							g_menu_model_items_changed(G_MENU_MODEL(
219 							                               section),
220 							                           position,
221 							                           removed,
222 							                           0);
223 
224 						g_menu_model_items_changed(G_MENU_MODEL(shell),
225 						                           section_index + 1,
226 						                           0,
227 						                           1);
228 					}
229 				}
230 				else
231 				{
232 					if (sections != NULL)
233 					{
234 						UnityGtkMenuSection *section =
235 						    g_ptr_array_index(sections, section_index);
236 						GSequenceIter *section_iter =
237 						    unity_gtk_menu_section_get_begin_iter(section);
238 						guint position =
239 						    g_sequence_iter_get_position(insert_iter) -
240 						    g_sequence_iter_get_position(section_iter);
241 
242 						g_menu_model_items_changed(G_MENU_MODEL(section),
243 						                           position,
244 						                           0,
245 						                           1);
246 					}
247 				}
248 			}
249 			else
250 				g_warn_if_reached();
251 		}
252 	}
253 }
254 
unity_gtk_menu_shell_hide_item(UnityGtkMenuShell * shell,UnityGtkMenuItem * item)255 static void unity_gtk_menu_shell_hide_item(UnityGtkMenuShell *shell, UnityGtkMenuItem *item)
256 {
257 	GSequence *visible_indices;
258 
259 	g_return_if_fail(UNITY_GTK_IS_MENU_SHELL(shell));
260 	g_return_if_fail(UNITY_GTK_IS_MENU_ITEM(item));
261 	g_warn_if_fail(item->parent_shell == shell);
262 
263 	visible_indices = shell->visible_indices;
264 
265 	if (visible_indices != NULL)
266 	{
267 		GSequence *separator_indices = shell->separator_indices;
268 		guint item_index             = item->item_index;
269 		GSequenceIter *visible_iter  = g_sequence_lookup_uint(visible_indices, item_index);
270 
271 		if (shell->action_group != NULL)
272 		{
273 			if (item->child_shell != NULL)
274 			{
275 				if (item->child_shell_valid)
276 					unity_gtk_action_group_disconnect_shell(shell->action_group,
277 					                                        item->child_shell);
278 				else
279 					g_warn_if_reached();
280 			}
281 
282 			unity_gtk_action_group_disconnect_item(shell->action_group, item);
283 		}
284 
285 		if (separator_indices != NULL)
286 		{
287 			if (unity_gtk_menu_item_is_separator(item))
288 			{
289 				GSequenceIter *separator_iter =
290 				    g_sequence_lookup_uint(separator_indices, item_index);
291 
292 				if (separator_iter != NULL)
293 				{
294 					GPtrArray *sections = shell->sections;
295 					guint section_index =
296 					    g_sequence_iter_get_position(separator_iter);
297 
298 					if (shell->sections != NULL)
299 					{
300 						UnityGtkMenuSection *section =
301 						    g_ptr_array_index(sections, section_index);
302 						UnityGtkMenuSection *next_section =
303 						    g_ptr_array_index(sections, section_index + 1);
304 						guint position =
305 						    g_menu_model_get_n_items(G_MENU_MODEL(section));
306 						guint added = g_menu_model_get_n_items(
307 						    G_MENU_MODEL(next_section));
308 						guint i;
309 
310 						g_sequence_remove(separator_iter);
311 
312 						if (visible_iter != NULL)
313 							g_sequence_remove(visible_iter);
314 						else
315 							g_warn_if_reached();
316 
317 						g_menu_model_items_changed(G_MENU_MODEL(shell),
318 						                           section_index + 1,
319 						                           1,
320 						                           0);
321 
322 						if (added)
323 							g_menu_model_items_changed(G_MENU_MODEL(
324 							                               section),
325 							                           position,
326 							                           0,
327 							                           added);
328 
329 						g_ptr_array_remove_index(sections,
330 						                         section_index + 1);
331 
332 						for (i = section_index + 1; i < sections->len; i++)
333 							UNITY_GTK_MENU_SECTION(
334 							    g_ptr_array_index(sections, i))
335 							    ->section_index = i;
336 					}
337 					else
338 					{
339 						g_sequence_remove(separator_iter);
340 
341 						if (visible_iter != NULL)
342 							g_sequence_remove(visible_iter);
343 						else
344 							g_warn_if_reached();
345 					}
346 				}
347 				else
348 				{
349 					g_warn_if_reached();
350 
351 					if (visible_iter != NULL)
352 						g_sequence_remove(visible_iter);
353 					else
354 						g_warn_if_reached();
355 				}
356 			}
357 			else
358 			{
359 				if (visible_iter != NULL)
360 				{
361 					GPtrArray *sections = shell->sections;
362 					GSequenceIter *separator_iter =
363 					    g_sequence_search_inf_uint(separator_indices,
364 					                               item_index);
365 					guint section_index =
366 					    separator_iter == NULL
367 					        ? 0
368 					        : g_sequence_iter_get_position(separator_iter) + 1;
369 
370 					if (shell->sections != NULL)
371 					{
372 						UnityGtkMenuSection *section =
373 						    g_ptr_array_index(sections, section_index);
374 						GSequenceIter *section_iter =
375 						    unity_gtk_menu_section_get_begin_iter(section);
376 						guint position =
377 						    g_sequence_iter_get_position(visible_iter) -
378 						    g_sequence_iter_get_position(section_iter);
379 
380 						g_sequence_remove(visible_iter);
381 						g_menu_model_items_changed(G_MENU_MODEL(section),
382 						                           position,
383 						                           1,
384 						                           0);
385 					}
386 				}
387 				else
388 					g_warn_if_reached();
389 			}
390 		}
391 		else
392 		{
393 			if (visible_iter != NULL)
394 				g_sequence_remove(visible_iter);
395 			else
396 				g_warn_if_reached();
397 		}
398 	}
399 }
400 
unity_gtk_menu_shell_update_item(UnityGtkMenuShell * shell,UnityGtkMenuItem * item)401 static void unity_gtk_menu_shell_update_item(UnityGtkMenuShell *shell, UnityGtkMenuItem *item)
402 {
403 	GSequence *visible_indices;
404 	GSequenceIter *visible_iter;
405 
406 	g_return_if_fail(UNITY_GTK_IS_MENU_SHELL(shell));
407 	g_return_if_fail(UNITY_GTK_IS_MENU_ITEM(item));
408 	g_warn_if_fail(item->parent_shell == shell);
409 
410 	visible_indices = unity_gtk_menu_shell_get_visible_indices(shell);
411 	visible_iter    = g_sequence_lookup_uint(visible_indices, item->item_index);
412 
413 	if (visible_iter != NULL)
414 	{
415 		GSequence *separator_indices;
416 		GSequenceIter *separator_iter;
417 		guint section_index;
418 		GPtrArray *sections;
419 		UnityGtkMenuSection *section;
420 		GSequenceIter *section_iter;
421 		guint position;
422 
423 		separator_indices = unity_gtk_menu_shell_get_separator_indices(shell);
424 		separator_iter    = g_sequence_search_inf_uint(separator_indices, item->item_index);
425 		section_index =
426 		    separator_iter == NULL ? 0 : g_sequence_iter_get_position(separator_iter) + 1;
427 		sections     = unity_gtk_menu_shell_get_sections(shell);
428 		section      = g_ptr_array_index(sections, section_index);
429 		section_iter = unity_gtk_menu_section_get_begin_iter(section);
430 		position     = g_sequence_iter_get_position(visible_iter) -
431 		           g_sequence_iter_get_position(section_iter);
432 
433 		g_menu_model_items_changed(G_MENU_MODEL(section), position, 1, 1);
434 	}
435 }
436 
unity_gtk_menu_shell_handle_item_visible(UnityGtkMenuShell * shell,UnityGtkMenuItem * item)437 static void unity_gtk_menu_shell_handle_item_visible(UnityGtkMenuShell *shell,
438                                                      UnityGtkMenuItem *item)
439 {
440 	GSequence *visible_indices;
441 
442 	g_return_if_fail(UNITY_GTK_IS_MENU_SHELL(shell));
443 	g_return_if_fail(UNITY_GTK_IS_MENU_ITEM(item));
444 	g_warn_if_fail(item->parent_shell == shell);
445 
446 	visible_indices = shell->visible_indices;
447 
448 	if (visible_indices != NULL)
449 	{
450 		GSequenceIter *visible_iter =
451 		    g_sequence_lookup_uint(visible_indices, item->item_index);
452 		gboolean was_visible = visible_iter != NULL;
453 		gboolean is_visible  = unity_gtk_menu_item_is_visible(item);
454 
455 		if (!was_visible && is_visible)
456 			unity_gtk_menu_shell_show_item(shell, item);
457 		else if (was_visible && !is_visible)
458 			unity_gtk_menu_shell_hide_item(shell, item);
459 	}
460 }
461 
unity_gtk_menu_shell_handle_item_sensitive(UnityGtkMenuShell * shell,UnityGtkMenuItem * item)462 static void unity_gtk_menu_shell_handle_item_sensitive(UnityGtkMenuShell *shell,
463                                                        UnityGtkMenuItem *item)
464 {
465 	GActionGroup *action_group;
466 	UnityGtkAction *action;
467 
468 	g_return_if_fail(UNITY_GTK_IS_MENU_SHELL(shell));
469 	g_return_if_fail(UNITY_GTK_IS_MENU_ITEM(item));
470 	g_warn_if_fail(item->parent_shell == shell);
471 
472 	action_group = G_ACTION_GROUP(shell->action_group);
473 	action       = item->action;
474 
475 	if (action_group != NULL && action != NULL)
476 	{
477 		gboolean enabled = unity_gtk_menu_item_is_sensitive(item);
478 
479 		g_action_group_action_enabled_changed(action_group, action->name, enabled);
480 	}
481 }
482 
unity_gtk_menu_shell_handle_item_label(UnityGtkMenuShell * shell,UnityGtkMenuItem * item)483 static void unity_gtk_menu_shell_handle_item_label(UnityGtkMenuShell *shell, UnityGtkMenuItem *item)
484 {
485 	g_return_if_fail(UNITY_GTK_IS_MENU_SHELL(shell));
486 	g_return_if_fail(UNITY_GTK_IS_MENU_ITEM(item));
487 	g_warn_if_fail(item->parent_shell == shell);
488 
489 	g_free(item->label_label);
490 	item->label_label = NULL;
491 
492 	unity_gtk_menu_shell_update_item(shell, item);
493 }
494 
unity_gtk_menu_shell_handle_item_use_underline(UnityGtkMenuShell * shell,UnityGtkMenuItem * item)495 static void unity_gtk_menu_shell_handle_item_use_underline(UnityGtkMenuShell *shell,
496                                                            UnityGtkMenuItem *item)
497 {
498 	unity_gtk_menu_shell_handle_item_label(shell, item);
499 }
500 
unity_gtk_menu_shell_handle_item_accel_path(UnityGtkMenuShell * shell,UnityGtkMenuItem * item)501 static void unity_gtk_menu_shell_handle_item_accel_path(UnityGtkMenuShell *shell,
502                                                         UnityGtkMenuItem *item)
503 {
504 	unity_gtk_menu_shell_update_item(shell, item);
505 }
506 
unity_gtk_menu_shell_handle_item_active(UnityGtkMenuShell * shell,UnityGtkMenuItem * item)507 static void unity_gtk_menu_shell_handle_item_active(UnityGtkMenuShell *shell,
508                                                     UnityGtkMenuItem *item)
509 {
510 	GActionGroup *action_group;
511 	UnityGtkAction *action;
512 
513 	g_return_if_fail(UNITY_GTK_IS_MENU_SHELL(shell));
514 	g_return_if_fail(UNITY_GTK_IS_MENU_ITEM(item));
515 	g_warn_if_fail(item->parent_shell == shell);
516 
517 	action_group = G_ACTION_GROUP(shell->action_group);
518 	action       = item->action;
519 
520 	if (action_group != NULL && action != NULL)
521 	{
522 		if (action->items_by_name != NULL)
523 		{
524 			const char *name = NULL;
525 			GHashTableIter iter;
526 			gpointer key;
527 			gpointer value;
528 
529 			g_hash_table_iter_init(&iter, action->items_by_name);
530 			while (name == NULL && g_hash_table_iter_next(&iter, &key, &value))
531 				if (unity_gtk_menu_item_is_active(value))
532 					name = key;
533 
534 			if (name != NULL)
535 			{
536 				GVariant *state = g_variant_new_string(name);
537 
538 				g_action_group_action_state_changed(action_group,
539 				                                    action->name,
540 				                                    state);
541 			}
542 			else
543 				g_action_group_action_state_changed(action_group,
544 				                                    action->name,
545 				                                    NULL);
546 		}
547 		else if (unity_gtk_menu_item_is_check(item))
548 		{
549 			gboolean active = unity_gtk_menu_item_is_active(item);
550 			GVariant *state = g_variant_new_boolean(active);
551 
552 			g_action_group_action_state_changed(action_group, action->name, state);
553 		}
554 	}
555 }
556 
unity_gtk_menu_shell_handle_item_parent(UnityGtkMenuShell * shell,UnityGtkMenuItem * item)557 static void unity_gtk_menu_shell_handle_item_parent(UnityGtkMenuShell *shell,
558                                                     UnityGtkMenuItem *item)
559 {
560 	GtkMenuItem *menu_item;
561 	GtkWidget *parent;
562 
563 	g_return_if_fail(UNITY_GTK_IS_MENU_SHELL(shell));
564 	g_return_if_fail(UNITY_GTK_IS_MENU_ITEM(item));
565 	g_warn_if_fail(item->parent_shell == shell);
566 
567 	menu_item = item->menu_item;
568 	parent    = gtk_widget_get_parent(GTK_WIDGET(menu_item));
569 
570 	if (parent == NULL)
571 	{
572 		GPtrArray *items = shell->items;
573 
574 		if (unity_gtk_menu_item_is_visible(item))
575 			unity_gtk_menu_shell_hide_item(shell, item);
576 
577 		if (items != NULL)
578 		{
579 			GSequence *visible_indices   = shell->visible_indices;
580 			GSequence *separator_indices = shell->separator_indices;
581 			guint item_index             = item->item_index;
582 			guint i;
583 
584 			g_ptr_array_remove_index(items, item_index);
585 
586 			for (i = item_index; i < items->len; i++)
587 				UNITY_GTK_MENU_ITEM(g_ptr_array_index(items, i))->item_index = i;
588 
589 			if (visible_indices != NULL)
590 			{
591 				GSequenceIter *iter =
592 				    g_sequence_search_uint(visible_indices, item_index);
593 
594 				while (!g_sequence_iter_is_end(iter))
595 				{
596 					g_sequence_set_uint(iter, g_sequence_get_uint(iter) - 1);
597 					iter = g_sequence_iter_next(iter);
598 				}
599 			}
600 
601 			if (separator_indices != NULL)
602 			{
603 				GSequenceIter *iter =
604 				    g_sequence_search_uint(separator_indices, item_index);
605 
606 				while (!g_sequence_iter_is_end(iter))
607 				{
608 					g_sequence_set_uint(iter, g_sequence_get_uint(iter) - 1);
609 					iter = g_sequence_iter_next(iter);
610 				}
611 			}
612 		}
613 	}
614 }
615 
unity_gtk_menu_shell_handle_item_submenu(UnityGtkMenuShell * shell,UnityGtkMenuItem * item)616 static void unity_gtk_menu_shell_handle_item_submenu(UnityGtkMenuShell *shell,
617                                                      UnityGtkMenuItem *item)
618 {
619 	g_return_if_fail(UNITY_GTK_IS_MENU_SHELL(shell));
620 	g_return_if_fail(UNITY_GTK_IS_MENU_ITEM(item));
621 	g_warn_if_fail(item->parent_shell == shell);
622 
623 	if (shell->action_group != NULL)
624 	{
625 		/* If a submenu was added or removed, we need to update the submenu action. */
626 		unity_gtk_action_group_disconnect_item(shell->action_group, item);
627 		unity_gtk_action_group_connect_item(shell->action_group, item);
628 	}
629 
630 	if (item->child_shell_valid)
631 	{
632 		GtkMenuShell *old_submenu =
633 		    item->child_shell != NULL ? item->child_shell->menu_shell : NULL;
634 		GtkMenuShell *new_submenu =
635 		    item->menu_item != NULL
636 		        ? GTK_MENU_SHELL(gtk_menu_item_get_submenu(item->menu_item))
637 		        : NULL;
638 
639 		if (new_submenu != old_submenu)
640 		{
641 			UnityGtkMenuShell *child_shell = item->child_shell;
642 			GSequence *visible_indices =
643 			    unity_gtk_menu_shell_get_visible_indices(shell);
644 			GSequence *separator_indices =
645 			    unity_gtk_menu_shell_get_separator_indices(shell);
646 			GSequenceIter *separator_iter =
647 			    g_sequence_search_inf_uint(separator_indices, item->item_index);
648 			guint section_index =
649 			    separator_iter == NULL
650 			        ? 0
651 			        : g_sequence_iter_get_position(separator_iter) + 1;
652 			GPtrArray *sections          = unity_gtk_menu_shell_get_sections(shell);
653 			UnityGtkMenuSection *section = g_ptr_array_index(sections, section_index);
654 			GSequenceIter *section_iter =
655 			    unity_gtk_menu_section_get_begin_iter(section);
656 			GSequenceIter *visible_iter =
657 			    g_sequence_lookup_uint(visible_indices, item->item_index);
658 			guint position = g_sequence_iter_get_position(visible_iter) -
659 			                 g_sequence_iter_get_position(section_iter);
660 
661 			if (child_shell != NULL)
662 			{
663 				item->child_shell = NULL;
664 				g_object_unref(child_shell);
665 			}
666 
667 			item->child_shell_valid = FALSE;
668 
669 			g_menu_model_items_changed(G_MENU_MODEL(section), position, 1, 1);
670 		}
671 	}
672 }
673 
unity_gtk_menu_shell_handle_shell_insert(GtkMenuShell * menu_shell,GtkWidget * child,gint position,gpointer user_data)674 static void unity_gtk_menu_shell_handle_shell_insert(GtkMenuShell *menu_shell, GtkWidget *child,
675                                                      gint position, gpointer user_data)
676 {
677 	UnityGtkMenuShell *shell;
678 	GPtrArray *items;
679 
680 	g_return_if_fail(UNITY_GTK_IS_MENU_SHELL(user_data));
681 
682 	if (unity_gtk_menu_shell_is_debug())
683 		g_print("%s ((%s *) %p, (%s *) %p \"%s\", %d, (%s *) %p)\n",
684 		        G_STRFUNC,
685 		        G_OBJECT_TYPE_NAME(menu_shell),
686 		        menu_shell,
687 		        G_OBJECT_TYPE_NAME(child),
688 		        child,
689 		        gtk_menu_item_get_label(GTK_MENU_ITEM(child)),
690 		        position,
691 		        G_OBJECT_TYPE_NAME(user_data),
692 		        user_data);
693 
694 	shell = UNITY_GTK_MENU_SHELL(user_data);
695 	items = shell->items;
696 
697 	if (items != NULL)
698 	{
699 		UnityGtkMenuItem *item;
700 		GtkMenuItem *menu_item;
701 		GSequence *visible_indices;
702 		GSequence *separator_indices;
703 		guint i;
704 
705 		if (position < 0)
706 			position = items->len;
707 
708 		menu_item = GTK_MENU_ITEM(child);
709 		item      = unity_gtk_menu_item_new(menu_item, shell, position);
710 		g_ptr_array_insert(items, position, item);
711 
712 		for (i = position + 1; i < items->len; i++)
713 			UNITY_GTK_MENU_ITEM(g_ptr_array_index(items, i))->item_index = i;
714 
715 		visible_indices   = shell->visible_indices;
716 		separator_indices = shell->separator_indices;
717 
718 		if (visible_indices != NULL)
719 		{
720 			GSequenceIter *iter = g_sequence_search_uint(visible_indices, position - 1);
721 
722 			while (!g_sequence_iter_is_end(iter))
723 			{
724 				g_sequence_set_uint(iter, g_sequence_get_uint(iter) + 1);
725 				iter = g_sequence_iter_next(iter);
726 			}
727 		}
728 
729 		if (separator_indices != NULL)
730 		{
731 			GSequenceIter *iter =
732 			    g_sequence_search_uint(separator_indices, position - 1);
733 
734 			while (!g_sequence_iter_is_end(iter))
735 			{
736 				g_sequence_set_uint(iter, g_sequence_get_uint(iter) + 1);
737 				iter = g_sequence_iter_next(iter);
738 			}
739 		}
740 
741 		if (unity_gtk_menu_item_is_visible(item))
742 			unity_gtk_menu_shell_show_item(shell, item);
743 	}
744 }
745 
unity_gtk_menu_shell_set_has_mnemonics(UnityGtkMenuShell * shell,gboolean has_mnemonics)746 static void unity_gtk_menu_shell_set_has_mnemonics(UnityGtkMenuShell *shell, gboolean has_mnemonics)
747 {
748 	g_return_if_fail(UNITY_GTK_IS_MENU_SHELL(shell));
749 
750 	if (has_mnemonics != shell->has_mnemonics)
751 	{
752 		shell->has_mnemonics = has_mnemonics;
753 
754 		if (shell->items != NULL)
755 		{
756 			guint i;
757 
758 			for (i = 0; i < shell->items->len; i++)
759 				unity_gtk_menu_shell_handle_item_label(
760 				    shell, g_ptr_array_index(shell->items, i));
761 		}
762 	}
763 }
764 
unity_gtk_menu_shell_handle_settings_notify(GObject * object,GParamSpec * pspec,gpointer user_data)765 static void unity_gtk_menu_shell_handle_settings_notify(GObject *object, GParamSpec *pspec,
766                                                         gpointer user_data)
767 {
768 	gboolean has_mnemonics;
769 
770 	g_return_if_fail(GTK_IS_SETTINGS(object));
771 	g_return_if_fail(UNITY_GTK_IS_MENU_SHELL(user_data));
772 
773 	g_object_get(GTK_SETTINGS(object), "gtk-enable-mnemonics", &has_mnemonics, NULL);
774 
775 	unity_gtk_menu_shell_set_has_mnemonics(UNITY_GTK_MENU_SHELL(user_data), has_mnemonics);
776 }
777 
778 static void unity_gtk_menu_shell_clear_menu_shell(UnityGtkMenuShell *shell);
779 
unity_gtk_menu_shell_set_menu_shell(UnityGtkMenuShell * shell,GtkMenuShell * menu_shell)780 static void unity_gtk_menu_shell_set_menu_shell(UnityGtkMenuShell *shell, GtkMenuShell *menu_shell)
781 {
782 	g_return_if_fail(UNITY_GTK_IS_MENU_SHELL(shell));
783 
784 	if (menu_shell != shell->menu_shell)
785 	{
786 		GPtrArray *items             = shell->items;
787 		GPtrArray *sections          = shell->sections;
788 		GSequence *visible_indices   = shell->visible_indices;
789 		GSequence *separator_indices = shell->separator_indices;
790 
791 		if (shell->action_group != NULL)
792 			unity_gtk_action_group_disconnect_shell(shell->action_group, shell);
793 
794 		if (shell->menu_shell != NULL)
795 			g_signal_handlers_disconnect_by_data(shell->menu_shell, shell);
796 
797 		if (separator_indices != NULL)
798 		{
799 			shell->separator_indices = NULL;
800 			g_sequence_free(separator_indices);
801 		}
802 
803 		if (visible_indices != NULL)
804 		{
805 			shell->visible_indices = NULL;
806 			g_sequence_free(visible_indices);
807 		}
808 
809 		if (sections != NULL)
810 		{
811 			shell->sections = NULL;
812 			g_ptr_array_unref(sections);
813 		}
814 
815 		if (items != NULL)
816 		{
817 			shell->items = NULL;
818 			g_ptr_array_unref(items);
819 		}
820 
821 		if (shell->menu_shell != NULL)
822 			g_object_steal_qdata(G_OBJECT(shell->menu_shell), menu_shell_quark());
823 
824 		shell->menu_shell = menu_shell;
825 
826 		if (menu_shell != NULL)
827 		{
828 			g_object_set_qdata_full(G_OBJECT(menu_shell),
829 			                        menu_shell_quark(),
830 			                        shell,
831 			                        (GDestroyNotify)
832 			                            unity_gtk_menu_shell_clear_menu_shell);
833 
834 			g_signal_connect(menu_shell,
835 			                 "insert",
836 			                 G_CALLBACK(unity_gtk_menu_shell_handle_shell_insert),
837 			                 shell);
838 		}
839 	}
840 }
841 
unity_gtk_menu_shell_clear_menu_shell(UnityGtkMenuShell * shell)842 static void unity_gtk_menu_shell_clear_menu_shell(UnityGtkMenuShell *shell)
843 {
844 	g_return_if_fail(UNITY_GTK_IS_MENU_SHELL(shell));
845 
846 	unity_gtk_menu_shell_set_menu_shell(shell, NULL);
847 }
848 
unity_gtk_menu_shell_dispose(GObject * object)849 static void unity_gtk_menu_shell_dispose(GObject *object)
850 {
851 	UnityGtkMenuShell *shell;
852 	GtkSettings *settings;
853 
854 	g_return_if_fail(UNITY_GTK_IS_MENU_SHELL(object));
855 
856 	shell    = UNITY_GTK_MENU_SHELL(object);
857 	settings = gtk_settings_get_default();
858 
859 	unity_gtk_menu_shell_set_menu_shell(shell, NULL);
860 
861 	if (settings != NULL)
862 		g_signal_handlers_disconnect_by_data(settings, shell);
863 
864 	G_OBJECT_CLASS(unity_gtk_menu_shell_parent_class)->dispose(object);
865 }
866 
unity_gtk_menu_shell_is_mutable(GMenuModel * model)867 static gboolean unity_gtk_menu_shell_is_mutable(GMenuModel *model)
868 {
869 	g_return_val_if_fail(UNITY_GTK_IS_MENU_SHELL(model), TRUE);
870 
871 	return TRUE;
872 }
873 
unity_gtk_menu_shell_get_n_items(GMenuModel * model)874 static gint unity_gtk_menu_shell_get_n_items(GMenuModel *model)
875 {
876 	g_return_val_if_fail(UNITY_GTK_IS_MENU_SHELL(model), 0);
877 
878 	return unity_gtk_menu_shell_get_sections(UNITY_GTK_MENU_SHELL(model))->len;
879 }
880 
unity_gtk_menu_shell_get_item_attributes(GMenuModel * model,gint item_index,GHashTable ** attributes)881 static void unity_gtk_menu_shell_get_item_attributes(GMenuModel *model, gint item_index,
882                                                      GHashTable **attributes)
883 {
884 	g_return_if_fail(UNITY_GTK_IS_MENU_SHELL(model));
885 	g_return_if_fail(0 <= item_index && item_index < g_menu_model_get_n_items(model));
886 	g_return_if_fail(attributes != NULL);
887 
888 	*attributes =
889 	    g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_variant_unref);
890 }
891 
unity_gtk_menu_shell_get_item_links(GMenuModel * model,gint item_index,GHashTable ** links)892 static void unity_gtk_menu_shell_get_item_links(GMenuModel *model, gint item_index,
893                                                 GHashTable **links)
894 {
895 	UnityGtkMenuShell *shell;
896 	GPtrArray *sections;
897 	UnityGtkMenuSection *section;
898 
899 	g_return_if_fail(UNITY_GTK_IS_MENU_SHELL(model));
900 	g_return_if_fail(0 <= item_index && item_index < g_menu_model_get_n_items(model));
901 	g_return_if_fail(links != NULL);
902 
903 	shell    = UNITY_GTK_MENU_SHELL(model);
904 	sections = unity_gtk_menu_shell_get_sections(shell);
905 	section  = g_ptr_array_index(sections, item_index);
906 
907 	*links = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_object_unref);
908 	g_hash_table_insert(*links, G_MENU_LINK_SECTION, g_object_ref(section));
909 }
910 
unity_gtk_menu_shell_class_init(UnityGtkMenuShellClass * klass)911 static void unity_gtk_menu_shell_class_init(UnityGtkMenuShellClass *klass)
912 {
913 	GObjectClass *object_class        = G_OBJECT_CLASS(klass);
914 	GMenuModelClass *menu_model_class = G_MENU_MODEL_CLASS(klass);
915 
916 	object_class->dispose                 = unity_gtk_menu_shell_dispose;
917 	menu_model_class->is_mutable          = unity_gtk_menu_shell_is_mutable;
918 	menu_model_class->get_n_items         = unity_gtk_menu_shell_get_n_items;
919 	menu_model_class->get_item_attributes = unity_gtk_menu_shell_get_item_attributes;
920 	menu_model_class->get_item_links      = unity_gtk_menu_shell_get_item_links;
921 }
922 
unity_gtk_menu_shell_init(UnityGtkMenuShell * self)923 static void unity_gtk_menu_shell_init(UnityGtkMenuShell *self)
924 {
925 	self->has_mnemonics = TRUE;
926 }
927 
928 /**
929  * unity_gtk_menu_shell_new:
930  * @menu_shell: a #GtkMenuShell to watch.
931  *
932  * Creates a new #UnityGtkMenuShell based on the contents of the given
933  * @menu_shell. Any subsequent changes to @menu_shell are reflected in
934  * the returned #UnityGtkMenuShell.
935  *
936  * Returns: a new #UnityGtkMenuShell based on @menu_shell.
937  */
unity_gtk_menu_shell_new(GtkMenuShell * menu_shell)938 UnityGtkMenuShell *unity_gtk_menu_shell_new(GtkMenuShell *menu_shell)
939 {
940 	UnityGtkMenuShell *shell = g_object_new(UNITY_GTK_TYPE_MENU_SHELL, NULL);
941 	GtkSettings *settings    = gtk_settings_get_default();
942 
943 	if (settings != NULL)
944 	{
945 		g_signal_connect(settings,
946 		                 "notify::gtk-enable-mnemonics",
947 		                 G_CALLBACK(unity_gtk_menu_shell_handle_settings_notify),
948 		                 shell);
949 		g_object_get(settings, "gtk-enable-mnemonics", &shell->has_mnemonics, NULL);
950 	}
951 
952 	unity_gtk_menu_shell_set_menu_shell(shell, menu_shell);
953 
954 	return shell;
955 }
956 
unity_gtk_menu_shell_new_internal(GtkMenuShell * menu_shell)957 UnityGtkMenuShell *unity_gtk_menu_shell_new_internal(GtkMenuShell *menu_shell)
958 {
959 	UnityGtkMenuShell *shell = g_object_new(UNITY_GTK_TYPE_MENU_SHELL, NULL);
960 
961 	unity_gtk_menu_shell_set_menu_shell(shell, menu_shell);
962 
963 	return shell;
964 }
965 
unity_gtk_menu_shell_get_item(UnityGtkMenuShell * shell,guint index)966 UnityGtkMenuItem *unity_gtk_menu_shell_get_item(UnityGtkMenuShell *shell, guint index)
967 {
968 	GPtrArray *items;
969 
970 	g_return_val_if_fail(UNITY_GTK_IS_MENU_SHELL(shell), NULL);
971 
972 	items = unity_gtk_menu_shell_get_items(shell);
973 
974 	g_return_val_if_fail(index < items->len, NULL);
975 
976 	return g_ptr_array_index(items, index);
977 }
978 
unity_gtk_menu_shell_get_visible_indices(UnityGtkMenuShell * shell)979 GSequence *unity_gtk_menu_shell_get_visible_indices(UnityGtkMenuShell *shell)
980 {
981 	g_return_val_if_fail(UNITY_GTK_IS_MENU_SHELL(shell), NULL);
982 
983 	if (shell->visible_indices == NULL)
984 	{
985 		GPtrArray *items = unity_gtk_menu_shell_get_items(shell);
986 		guint i;
987 
988 		shell->visible_indices = g_sequence_new(NULL);
989 
990 		for (i = 0; i < items->len; i++)
991 		{
992 			UnityGtkMenuItem *item = g_ptr_array_index(items, i);
993 
994 			if (unity_gtk_menu_item_is_visible(item))
995 				g_sequence_append(shell->visible_indices, GUINT_TO_POINTER(i));
996 		}
997 
998 		if (shell->action_group != NULL)
999 			unity_gtk_action_group_connect_shell(shell->action_group, shell);
1000 	}
1001 
1002 	return shell->visible_indices;
1003 }
1004 
unity_gtk_menu_shell_get_separator_indices(UnityGtkMenuShell * shell)1005 GSequence *unity_gtk_menu_shell_get_separator_indices(UnityGtkMenuShell *shell)
1006 {
1007 	g_return_val_if_fail(UNITY_GTK_IS_MENU_SHELL(shell), NULL);
1008 
1009 	unity_gtk_menu_shell_get_visible_indices(shell);
1010 
1011 	if (shell->separator_indices == NULL)
1012 	{
1013 		GPtrArray *items = unity_gtk_menu_shell_get_items(shell);
1014 		guint i;
1015 
1016 		shell->separator_indices = g_sequence_new(NULL);
1017 
1018 		for (i = 0; i < items->len; i++)
1019 		{
1020 			UnityGtkMenuItem *item = g_ptr_array_index(items, i);
1021 
1022 			if (unity_gtk_menu_item_is_visible(item) &&
1023 			    unity_gtk_menu_item_is_separator(item))
1024 				g_sequence_append(shell->separator_indices, GUINT_TO_POINTER(i));
1025 		}
1026 	}
1027 
1028 	return shell->separator_indices;
1029 }
1030 
unity_gtk_menu_shell_handle_item_notify(UnityGtkMenuShell * shell,UnityGtkMenuItem * item,const char * property)1031 void unity_gtk_menu_shell_handle_item_notify(UnityGtkMenuShell *shell, UnityGtkMenuItem *item,
1032                                              const char *property)
1033 {
1034 	static const char *visible_name;
1035 	static const char *sensitive_name;
1036 	static const char *label_name;
1037 	static const char *use_underline_name;
1038 	static const char *accel_path_name;
1039 	static const char *active_name;
1040 	static const char *parent_name;
1041 	static const char *submenu_name;
1042 
1043 	const char *name;
1044 
1045 	g_return_if_fail(UNITY_GTK_IS_MENU_SHELL(shell));
1046 	g_return_if_fail(UNITY_GTK_IS_MENU_ITEM(item));
1047 
1048 	if (G_UNLIKELY(visible_name == NULL))
1049 		visible_name = g_intern_static_string("visible");
1050 	if (G_UNLIKELY(sensitive_name == NULL))
1051 		sensitive_name = g_intern_static_string("sensitive");
1052 	if (G_UNLIKELY(label_name == NULL))
1053 		label_name = g_intern_static_string("label");
1054 	if (G_UNLIKELY(use_underline_name == NULL))
1055 		use_underline_name = g_intern_static_string("use-underline");
1056 	if (G_UNLIKELY(accel_path_name == NULL))
1057 		accel_path_name = g_intern_static_string("accel-path");
1058 	if (G_UNLIKELY(active_name == NULL))
1059 		active_name = g_intern_static_string("active");
1060 	if (G_UNLIKELY(parent_name == NULL))
1061 		parent_name = g_intern_static_string("parent");
1062 	if (G_UNLIKELY(submenu_name == NULL))
1063 		submenu_name = g_intern_static_string("submenu");
1064 
1065 	name = g_intern_string(property);
1066 
1067 	if (unity_gtk_menu_shell_is_debug())
1068 		g_print("%s ((%s *) %p, (%s *) %p { \"%s\" }, %s)\n",
1069 		        G_STRFUNC,
1070 		        G_OBJECT_TYPE_NAME(shell),
1071 		        shell,
1072 		        G_OBJECT_TYPE_NAME(item),
1073 		        item,
1074 		        unity_gtk_menu_item_get_label(item),
1075 		        name);
1076 
1077 	if (name == visible_name)
1078 		unity_gtk_menu_shell_handle_item_visible(shell, item);
1079 	else if (name == sensitive_name)
1080 		unity_gtk_menu_shell_handle_item_sensitive(shell, item);
1081 	else if (name == label_name)
1082 		unity_gtk_menu_shell_handle_item_label(shell, item);
1083 	else if (name == use_underline_name)
1084 		unity_gtk_menu_shell_handle_item_use_underline(shell, item);
1085 	else if (name == accel_path_name)
1086 		unity_gtk_menu_shell_handle_item_accel_path(shell, item);
1087 	else if (name == active_name)
1088 		unity_gtk_menu_shell_handle_item_active(shell, item);
1089 	else if (name == parent_name)
1090 		unity_gtk_menu_shell_handle_item_parent(shell, item);
1091 	else if (name == submenu_name)
1092 		unity_gtk_menu_shell_handle_item_submenu(shell, item);
1093 }
1094 
unity_gtk_menu_shell_activate_item(UnityGtkMenuShell * shell,UnityGtkMenuItem * item)1095 void unity_gtk_menu_shell_activate_item(UnityGtkMenuShell *shell, UnityGtkMenuItem *item)
1096 {
1097 	g_return_if_fail(UNITY_GTK_IS_MENU_SHELL(shell));
1098 	g_return_if_fail(UNITY_GTK_IS_MENU_ITEM(item));
1099 
1100 	if (item->menu_item != NULL)
1101 	{
1102 		if (GTK_IS_MENU(shell->menu_shell))
1103 			gtk_menu_set_active(GTK_MENU(shell->menu_shell), item->item_index);
1104 
1105 		/*
1106 		 * We dispatch the menu item activation in an idle to fix LP: #1258669.
1107 		 *
1108 		 * We get a deadlock when the menu item is activated if something like
1109 		 * gtk_dialog_run () is called. gtk_dialog_run () releases the GDK lock
1110 		 * just before starting its own main loop, and tries to re-acquire it
1111 		 * once it terminates. For whatever reason, a direct call to
1112 		 * gtk_menu_item_activate () here causes the GDK lock to be acquired
1113 		 * before gtk_dialog_run () tries to acquire it, whereas dispatching it
1114 		 * using gdk_threads_add_idle_full () seems to cleanly acquire the lock
1115 		 * once only at the beginning, preventing the deadlock.
1116 		 *
1117 		 * Suspicion is that this was executing during the main context
1118 		 * iteration of gtk_main_iteration (), which grabs the GDK lock
1119 		 * immediately after. But it's still not clear how that's possible....
1120 		 */
1121 
1122 		gdk_threads_add_idle_full(G_PRIORITY_DEFAULT_IDLE,
1123 		                          gtk_menu_item_handle_idle_activate,
1124 		                          g_object_ref(item->menu_item),
1125 		                          g_object_unref);
1126 	}
1127 }
1128 
unity_gtk_menu_shell_print(UnityGtkMenuShell * shell,guint indent)1129 void unity_gtk_menu_shell_print(UnityGtkMenuShell *shell, guint indent)
1130 {
1131 	char *space;
1132 
1133 	g_return_if_fail(shell == NULL || UNITY_GTK_IS_MENU_SHELL(shell));
1134 
1135 	space = g_strnfill(indent, ' ');
1136 
1137 	if (shell != NULL)
1138 	{
1139 		g_print("%s(%s *) %p\n",
1140 		        space,
1141 		        G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(shell)),
1142 		        shell);
1143 
1144 		if (shell->menu_shell != NULL)
1145 			g_print("%s  (%s *) %p\n",
1146 			        space,
1147 			        G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(shell->menu_shell)),
1148 			        shell->menu_shell);
1149 
1150 		if (shell->items != NULL)
1151 		{
1152 			guint i;
1153 
1154 			for (i = 0; i < shell->items->len; i++)
1155 				unity_gtk_menu_item_print(g_ptr_array_index(shell->items, i),
1156 				                          indent + 2);
1157 		}
1158 
1159 		if (shell->sections != NULL)
1160 		{
1161 			guint i;
1162 
1163 			for (i = 0; i < shell->sections->len; i++)
1164 				unity_gtk_menu_section_print(g_ptr_array_index(shell->sections, i),
1165 				                             indent + 2);
1166 		}
1167 
1168 		if (shell->visible_indices != NULL)
1169 		{
1170 			GSequenceIter *iter = g_sequence_get_begin_iter(shell->visible_indices);
1171 
1172 			g_print("%s ", space);
1173 
1174 			while (!g_sequence_iter_is_end(iter))
1175 			{
1176 				g_print(" %u", g_sequence_get_uint(iter));
1177 				iter = g_sequence_iter_next(iter);
1178 			}
1179 
1180 			g_print("\n");
1181 		}
1182 
1183 		if (shell->separator_indices != NULL)
1184 		{
1185 			GSequenceIter *iter = g_sequence_get_begin_iter(shell->separator_indices);
1186 
1187 			g_print("%s ", space);
1188 
1189 			while (!g_sequence_iter_is_end(iter))
1190 			{
1191 				g_print(" %u", g_sequence_get_uint(iter));
1192 				iter = g_sequence_iter_next(iter);
1193 			}
1194 
1195 			g_print("\n");
1196 		}
1197 
1198 		if (shell->action_group != NULL)
1199 			g_print("%s  (%s *) %p\n",
1200 			        space,
1201 			        G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(shell->action_group)),
1202 			        shell->action_group);
1203 	}
1204 	else
1205 		g_print("%sNULL\n", space);
1206 
1207 	g_free(space);
1208 }
1209 
unity_gtk_menu_shell_is_debug(void)1210 gboolean unity_gtk_menu_shell_is_debug(void)
1211 {
1212 	return unity_gtk_menu_shell_debug;
1213 }
1214 
1215 /**
1216  * unity_gtk_menu_shell_set_debug:
1217  * @debug: #TRUE to enable debugging output
1218  *
1219  * Sets if menu shell changes should be logged using g_print ().
1220  */
unity_gtk_menu_shell_set_debug(gboolean debug)1221 void unity_gtk_menu_shell_set_debug(gboolean debug)
1222 {
1223 	unity_gtk_menu_shell_debug = debug;
1224 }
1225