]> Pileus Git - ~andy/gtk/blob - gtk/gtkapplicationwindow.c
add GtkApplicationWindow
[~andy/gtk] / gtk / gtkapplicationwindow.c
1 /*
2  * Copyright © 2011 Canonical Limited
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the licence, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  *
19  * Author: Ryan Lortie <desrt@desrt.ca>
20  */
21
22 #include "config.h"
23
24 #include "gtkapplicationwindow.h"
25
26 #include "gtkseparatormenuitem.h"
27 #include "gtkcheckmenuitem.h"
28 #include "gtkmenubar.h"
29 #include "gactionmuxer.h"
30
31 struct _GtkApplicationWindowPrivate
32 {
33   GtkMenuBar *menubar;
34   GMenuModel *menu;
35
36   gboolean show_app_menu;
37 };
38
39 G_DEFINE_TYPE (GtkApplicationWindow, gtk_application_window, GTK_TYPE_WINDOW)
40
41 enum {
42   PROP_0,
43   PROP_SHOW_APP_MENU,
44   N_PROPS
45 };
46 static GParamSpec *gtk_application_window_properties[N_PROPS];
47
48 static void
49 gtk_application_window_real_get_preferred_height (GtkWidget *widget,
50                                                   gint      *minimum_height,
51                                                   gint      *natural_height)
52 {
53   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
54
55   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
56     ->get_preferred_height (widget, minimum_height, natural_height);
57
58   if (window->priv->menubar != NULL)
59     {
60       gint menubar_min_height, menubar_nat_height;
61
62       gtk_widget_get_preferred_height (GTK_WIDGET (window->priv->menubar), &menubar_min_height, &menubar_nat_height);
63       *minimum_height += menubar_min_height;
64       *natural_height += menubar_nat_height;
65     }
66 }
67
68 static void
69 gtk_application_window_real_get_preferred_height_for_width (GtkWidget *widget,
70                                                             gint       width,
71                                                             gint      *minimum_height,
72                                                             gint      *natural_height)
73 {
74   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
75
76   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
77     ->get_preferred_height_for_width (widget, width, minimum_height, natural_height);
78
79   if (window->priv->menubar != NULL)
80     {
81       gint menubar_min_height, menubar_nat_height;
82
83       gtk_widget_get_preferred_height_for_width (GTK_WIDGET (window->priv->menubar), width, &menubar_min_height, &menubar_nat_height);
84       *minimum_height += menubar_min_height;
85       *natural_height += menubar_nat_height;
86     }
87 }
88
89 static void
90 gtk_application_window_real_get_preferred_width (GtkWidget *widget,
91                                                  gint      *minimum_width,
92                                                  gint      *natural_width)
93 {
94   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
95
96   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
97     ->get_preferred_width (widget, minimum_width, natural_width);
98
99   if (window->priv->menubar != NULL)
100     {
101       gint menubar_min_width, menubar_nat_width;
102
103       gtk_widget_get_preferred_width (GTK_WIDGET (window->priv->menubar), &menubar_min_width, &menubar_nat_width);
104       *minimum_width = MAX (*minimum_width, menubar_min_width);
105       *natural_width = MAX (*natural_width, menubar_nat_width);
106     }
107 }
108
109 static void
110 gtk_application_window_real_get_preferred_width_for_height (GtkWidget *widget,
111                                                             gint       height,
112                                                             gint      *minimum_width,
113                                                             gint      *natural_width)
114 {
115   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
116
117   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
118     ->get_preferred_width_for_height (widget, height, minimum_width, natural_width);
119
120   if (window->priv->menubar != NULL)
121     {
122       gint menubar_min_width, menubar_nat_width;
123
124       gtk_widget_get_preferred_width_for_height (GTK_WIDGET (window->priv->menubar), height, &menubar_min_width, &menubar_nat_width);
125       *minimum_width = MAX (*minimum_width, menubar_min_width);
126       *natural_width = MAX (*natural_width, menubar_nat_width);
127     }
128 }
129
130 static void
131 gtk_application_window_real_size_allocate (GtkWidget     *widget,
132                                            GtkAllocation *allocation)
133 {
134   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
135
136   if (window->priv->menubar != NULL)
137     {
138       GtkAllocation menubar_allocation = *allocation;
139       gint menubar_min_height, menubar_nat_height;
140       GtkWidget *child;
141
142       gtk_widget_get_preferred_height_for_width (GTK_WIDGET (window->priv->menubar), allocation->width, &menubar_min_height, &menubar_nat_height);
143
144       menubar_allocation.height = menubar_min_height;
145       gtk_widget_size_allocate (GTK_WIDGET (window->priv->menubar), &menubar_allocation);
146
147       child = gtk_bin_get_child (GTK_BIN (window));
148       if (child != NULL && gtk_widget_get_visible (child))
149         {
150           GtkAllocation child_allocation = *allocation;
151           gint border_width;
152
153           child_allocation.height = MAX (1, child_allocation.height - menubar_min_height);
154
155           border_width = gtk_container_get_border_width (GTK_CONTAINER (window));
156           child_allocation.x += border_width;
157           child_allocation.y += border_width + menubar_min_height;
158           child_allocation.width -= border_width * 2;
159           child_allocation.height -= border_width * 2 - menubar_min_height;
160           gtk_widget_size_allocate (child, &child_allocation);
161         }
162
163       gtk_widget_set_allocation (widget, allocation);
164     }
165   else
166     GTK_WIDGET_CLASS (gtk_application_window_parent_class)
167       ->size_allocate (widget, allocation);
168 }
169
170 static void
171 gtk_application_window_real_map (GtkWidget *widget)
172 {
173   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
174
175   /* XXX could elimate this by tweaking gtk_window_map */
176   if (window->priv->menubar)
177     gtk_widget_map (GTK_WIDGET (window->priv->menubar));
178
179   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
180     ->map (widget);
181 }
182
183 static void
184 gtk_application_window_real_forall_internal (GtkContainer *container,
185                                              gboolean      include_internal,
186                                              GtkCallback   callback,
187                                              gpointer      user_data)
188 {
189   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (container);
190
191   if (window->priv->menubar)
192     callback (GTK_WIDGET (window->priv->menubar), user_data);
193
194   GTK_CONTAINER_CLASS (gtk_application_window_parent_class)
195     ->forall (container, include_internal, callback, user_data);
196 }
197
198
199 static void
200 gtk_application_window_get_property (GObject *object, guint prop_id,
201                                      GValue *value, GParamSpec *pspec)
202 {
203   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (object);
204
205   switch (prop_id)
206     {
207     case PROP_SHOW_APP_MENU:
208       g_value_set_boolean (value, window->priv->show_app_menu);
209       break;
210
211     default:
212       g_assert_not_reached ();
213     }
214 }
215
216 static void
217 gtk_application_window_set_property (GObject *object, guint prop_id,
218                                      const GValue *value, GParamSpec *pspec)
219 {
220   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (object);
221
222   switch (prop_id)
223     {
224     case PROP_SHOW_APP_MENU:
225       gtk_application_window_set_show_app_menu (window, g_value_get_boolean (value));
226       break;
227
228     default:
229       g_assert_not_reached ();
230     }
231 }
232
233 static void
234 gtk_application_window_finalize (GObject *object)
235 {
236   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (object);
237
238   if (window->priv->menubar)
239     g_object_unref (window->priv->menubar);
240
241   if (window->priv->menu)
242     g_object_unref (window->priv->menu);
243
244   G_OBJECT_CLASS (gtk_application_window_parent_class)
245     ->finalize (object);
246 }
247
248 static void
249 gtk_application_window_init (GtkApplicationWindow *window)
250 {
251   window->priv = G_TYPE_INSTANCE_GET_PRIVATE (window, GTK_TYPE_APPLICATION_WINDOW, GtkApplicationWindowPrivate);
252 }
253
254 static void
255 gtk_application_window_class_init (GtkApplicationWindowClass *class)
256 {
257   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
258   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
259   GObjectClass *object_class = G_OBJECT_CLASS (class);
260
261   container_class->forall = gtk_application_window_real_forall_internal;
262   widget_class->get_preferred_height = gtk_application_window_real_get_preferred_height;
263   widget_class->get_preferred_height_for_width = gtk_application_window_real_get_preferred_height_for_width;
264   widget_class->get_preferred_width = gtk_application_window_real_get_preferred_width;
265   widget_class->get_preferred_width_for_height = gtk_application_window_real_get_preferred_width_for_height;
266   widget_class->size_allocate = gtk_application_window_real_size_allocate;
267   widget_class->map = gtk_application_window_real_map;
268   object_class->get_property = gtk_application_window_get_property;
269   object_class->set_property = gtk_application_window_set_property;
270   object_class->finalize = gtk_application_window_finalize;
271
272   gtk_application_window_properties[PROP_SHOW_APP_MENU] =
273     g_param_spec_boolean ("show-app-menu", "show application menu",
274                           "TRUE if the application menu should be included in the menubar at the top of the window",
275                           FALSE, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
276   g_object_class_install_properties (object_class, N_PROPS, gtk_application_window_properties);
277   g_type_class_add_private (class, sizeof (GtkApplicationWindowPrivate));
278 }
279
280 GtkWidget *
281 gtk_application_window_new (GtkApplication *application)
282 {
283   g_return_val_if_fail (GTK_IS_APPLICATION (application), NULL);
284
285   return g_object_new (GTK_TYPE_APPLICATION_WINDOW,
286                        "application", application,
287                        NULL);
288 }
289
290 gboolean
291 gtk_application_window_get_show_app_menu (GtkApplicationWindow *window)
292 {
293   return window->priv->show_app_menu;
294 }
295
296 void
297 gtk_application_window_set_show_app_menu (GtkApplicationWindow *window,
298                                           gboolean              show_app_menu)
299 {
300   if (window->priv->show_app_menu != show_app_menu)
301     {
302       window->priv->show_app_menu = show_app_menu;
303       g_object_notify_by_pspec (G_OBJECT (window), gtk_application_window_properties[PROP_SHOW_APP_MENU]);
304
305       if (show_app_menu)
306         {
307           GtkWidget *menubar;
308           GtkWidget *item;
309
310           item = gtk_menu_item_new_with_label ("Application");
311           gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), gtk_application_window_get_app_menu (window));
312
313           menubar = gtk_menu_bar_new ();
314           window->priv->menubar = g_object_ref_sink (menubar);
315           gtk_menu_shell_append (GTK_MENU_SHELL (menubar), item);
316           gtk_widget_set_parent (menubar, GTK_WIDGET (window));
317           gtk_widget_show_all (menubar);
318         }
319       else
320         {
321           gtk_widget_unparent (GTK_WIDGET (window->priv->menubar));
322           g_object_unref (window->priv->menubar);
323         }
324     }
325 }
326
327 /* GtkMenu construction {{{1 */
328
329 typedef struct {
330   GActionGroup *group;
331   gchar        *name;
332   gchar        *target;
333   gulong        enabled_changed_id;
334   gulong        state_changed_id;
335   gulong        activate_handler;
336 } ActionData;
337
338 static void
339 action_data_free (gpointer data)
340 {
341   ActionData *a = data;
342
343   if (a->enabled_changed_id)
344     g_signal_handler_disconnect (a->group, a->enabled_changed_id);
345
346   if (a->state_changed_id)
347     g_signal_handler_disconnect (a->group, a->state_changed_id);
348
349   g_object_unref (a->group);
350   g_free (a->name);
351   g_free (a->target);
352
353   g_free (a);
354 }
355
356 static void
357 enabled_changed (GActionGroup *group,
358                  const gchar  *action_name,
359                  gboolean      enabled,
360                  GtkWidget    *widget)
361 {
362   gtk_widget_set_sensitive (widget, enabled);
363 }
364
365 static void
366 item_activated (GtkWidget *w,
367                 gpointer   data)
368 {
369   ActionData *a;
370   GVariant *parameter;
371
372   a = g_object_get_data (G_OBJECT (w), "action");
373   if (a->target)
374     parameter = g_variant_ref_sink (g_variant_new_string (a->target));
375   else
376     parameter = NULL;
377   g_action_group_activate_action (a->group, a->name, parameter);
378   if (parameter)
379     g_variant_unref (parameter);
380 }
381
382 static void
383 toggle_state_changed (GActionGroup     *group,
384                       const gchar      *name,
385                       GVariant         *state,
386                       GtkCheckMenuItem *w)
387 {
388   ActionData *a;
389
390   a = g_object_get_data (G_OBJECT (w), "action");
391   g_signal_handler_block (w, a->activate_handler);
392   gtk_check_menu_item_set_active (w, g_variant_get_boolean (state));
393   g_signal_handler_unblock (w, a->activate_handler);
394 }
395
396 static void
397 radio_state_changed (GActionGroup     *group,
398                      const gchar      *name,
399                      GVariant         *state,
400                      GtkCheckMenuItem *w)
401 {
402   ActionData *a;
403   gboolean b;
404
405   a = g_object_get_data (G_OBJECT (w), "action");
406   g_signal_handler_block (w, a->activate_handler);
407   b = g_strcmp0 (a->target, g_variant_get_string (state, NULL)) == 0;
408   gtk_check_menu_item_set_active (w, b);
409   g_signal_handler_unblock (w, a->activate_handler);
410 }
411
412 static GtkWidget *
413 create_menuitem_from_model (GMenuModel   *model,
414                             gint          item,
415                             GActionGroup *group)
416 {
417   GtkWidget *w;
418   gchar *label;
419   gchar *action;
420   gchar *target;
421   gchar *s;
422   ActionData *a;
423   const GVariantType *type;
424   GVariant *v;
425
426   label = NULL;
427   g_menu_model_get_item_attribute (model, item, G_MENU_ATTRIBUTE_LABEL, "s", &label);
428
429   action = NULL;
430   g_menu_model_get_item_attribute (model, item, G_MENU_ATTRIBUTE_ACTION, "s", &action);
431
432   if (action != NULL)
433     type = g_action_group_get_action_state_type (group, action);
434   else
435     type = NULL;
436
437   if (type == NULL)
438     w = gtk_menu_item_new_with_mnemonic (label);
439   else if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
440     w = gtk_check_menu_item_new_with_label (label);
441   else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
442     {
443       w = gtk_check_menu_item_new_with_label (label);
444       gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (w), TRUE);
445     }
446   else
447     g_assert_not_reached ();
448
449   if (action != NULL)
450     {
451       a = g_new0 (ActionData, 1);
452       a->group = g_object_ref (group);
453       a->name = g_strdup (action);
454       g_object_set_data_full (G_OBJECT (w), "action", a, action_data_free);
455
456       if (!g_action_group_get_action_enabled (group, action))
457         gtk_widget_set_sensitive (w, FALSE);
458
459       s = g_strconcat ("action-enabled-changed::", action, NULL);
460       a->enabled_changed_id = g_signal_connect (group, s,
461                                                 G_CALLBACK (enabled_changed), w);
462       g_free (s);
463       a->activate_handler = g_signal_connect (w, "activate",
464                                               G_CALLBACK (item_activated), NULL);
465
466       if (type == NULL)
467         {
468           /* all set */
469         }
470       else if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
471         {
472           s = g_strconcat ("action-state-changed::", action, NULL);
473           a->state_changed_id = g_signal_connect (group, s,
474                                                   G_CALLBACK (toggle_state_changed), w);
475           g_free (s);
476           v = g_action_group_get_action_state (group, action);
477           gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w),
478                                           g_variant_get_boolean (v));
479           g_variant_unref (v);
480         }
481       else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
482         {
483           s = g_strconcat ("action-state-changed::", action, NULL);
484           a->state_changed_id = g_signal_connect (group, s,
485                                                   G_CALLBACK (radio_state_changed), w);
486           g_free (s);
487           g_menu_model_get_item_attribute (model, item, G_MENU_ATTRIBUTE_TARGET, "s", &target);
488           a->target = g_strdup (target);
489           v = g_action_group_get_action_state (group, action);
490           gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w),
491                                           g_strcmp0 (g_variant_get_string (v, NULL), target) == 0);
492           g_variant_unref (v);
493           g_free (target);
494         }
495       else
496         g_assert_not_reached ();
497     }
498
499   g_free (label);
500   g_free (action);
501
502   return w;
503 }
504
505 static void populate_menu_from_model (GtkMenuShell *menu,
506                                       GMenuModel   *model,
507                                       GActionGroup *group);
508
509 static void
510 append_items_from_model (GtkMenuShell *menu,
511                          GMenuModel   *model,
512                          GActionGroup *group,
513                          gboolean     *need_separator,
514                          const gchar  *heading)
515 {
516   gint n;
517   gint i;
518   GtkWidget *w;
519   GtkWidget *menuitem;
520   GtkWidget *submenu;
521   GMenuModel *m;
522   gchar *label;
523
524   n = g_menu_model_get_n_items (model);
525
526   if (*need_separator && n > 0)
527     {
528       w = gtk_separator_menu_item_new ();
529       gtk_widget_show (w);
530       gtk_menu_shell_append (menu, w);
531       *need_separator = FALSE;
532     }
533
534   if (heading != NULL)
535     {
536       w = gtk_menu_item_new_with_label (heading);
537       gtk_widget_show (w);
538       gtk_widget_set_sensitive (w, FALSE);
539       gtk_menu_shell_append (GTK_MENU_SHELL (menu), w);
540     }
541
542   for (i = 0; i < n; i++)
543     {
544       if ((m = g_menu_model_get_item_link (model, i, G_MENU_LINK_SECTION)))
545         {
546           label = NULL;
547           g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_LABEL, "s", &label);
548           append_items_from_model (menu, m, group, need_separator, label);
549           g_object_unref (m);
550           g_free (label);
551           continue;
552         }
553
554       if (*need_separator)
555         {
556           w = gtk_separator_menu_item_new ();
557           gtk_widget_show (w);
558           gtk_menu_shell_append (menu, w);
559           *need_separator = FALSE;
560         }
561
562       menuitem = create_menuitem_from_model (model, i, group);
563
564       if ((m = g_menu_model_get_item_link (model, i, G_MENU_LINK_SUBMENU)))
565         {
566           submenu = gtk_menu_new ();
567           populate_menu_from_model (GTK_MENU_SHELL (submenu), m, group);
568           gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
569           g_object_unref (m);
570         }
571
572       gtk_widget_show (menuitem);
573       gtk_menu_shell_append (menu, menuitem);
574
575       *need_separator = TRUE;
576     }
577 }
578
579 static void
580 populate_menu_from_model (GtkMenuShell *menu,
581                           GMenuModel   *model,
582                           GActionGroup *group)
583 {
584   gboolean need_separator;
585
586   need_separator = FALSE;
587   append_items_from_model (menu, model, group, &need_separator, NULL);
588 }
589
590 typedef struct {
591   GtkApplication *application;
592   GtkMenuShell   *menu;
593   guint           update_idle;
594   GHashTable     *connected;
595 } ItemsChangedData;
596
597 static void
598 free_items_changed_data (gpointer data)
599 {
600   ItemsChangedData *d = data;
601
602   g_object_unref (d->application);
603
604   if (d->update_idle != 0)
605     g_source_remove (d->update_idle);
606
607   g_hash_table_unref (d->connected);
608
609   g_free (d);
610 }
611
612 static gboolean
613 repopulate_menu (gpointer data)
614 {
615   ItemsChangedData *d = data;
616   GList *children, *l;
617   GtkWidget *child;
618   GMenuModel *model;
619
620   /* remove current children */
621   children = gtk_container_get_children (GTK_CONTAINER (d->menu));
622   for (l = children; l; l = l->next)
623     {
624       child = l->data;
625       gtk_container_remove (GTK_CONTAINER (d->menu), child);
626     }
627   g_list_free (children);
628
629   /* repopulate */
630   model = g_application_get_menu (G_APPLICATION (d->application));
631   populate_menu_from_model (d->menu, model, G_ACTION_GROUP (d->application));
632
633   d->update_idle = 0;
634
635   return FALSE;
636 }
637
638 static void
639 connect_to_items_changed (GMenuModel *model,
640                           GCallback   callback,
641                           gpointer    data)
642 {
643   ItemsChangedData *d = data;
644   gint i;
645   GMenuModel *m;
646   GMenuLinkIter *iter;
647
648   if (!g_hash_table_lookup (d->connected, model))
649     {
650       g_signal_connect (model, "items-changed", callback, data);
651       g_hash_table_insert (d->connected, model, model);
652     }
653
654   for (i = 0; i < g_menu_model_get_n_items (model); i++)
655     {
656       iter = g_menu_model_iterate_item_links (model, i);
657       while (g_menu_link_iter_next (iter))
658         {
659           m = g_menu_link_iter_get_value (iter);
660           connect_to_items_changed (m, callback, data);
661           g_object_unref (m);
662         }
663       g_object_unref (iter);
664     }
665 }
666
667 static void
668 items_changed (GMenuModel *model,
669                gint        position,
670                gint        removed,
671                gint        added,
672                gpointer    data)
673 {
674   ItemsChangedData *d = data;
675
676   if (d->update_idle == 0)
677     d->update_idle = gdk_threads_add_idle (repopulate_menu, data);
678   connect_to_items_changed (model, G_CALLBACK (items_changed), data);
679 }
680
681 /**
682  * gtk_application_window_get_app_menu:
683  * @application: a #GtkApplication
684  *
685  * Populates a menu widget from a menu model that is
686  * associated with @application. See g_application_set_menu().
687  * The menu items will be connected to action of @application,
688  * as indicated by the menu model. The menus contents will be
689  * updated automatically in response to menu model changes.
690  *
691  * It is the callers responsibility to add the menu at a
692  * suitable place in the widget hierarchy.
693  *
694  * This function returns %NULL if @application has no associated
695  * menu model. It also returns %NULL if the menu model is
696  * represented outside the application, e.g. by an application
697  * menu in the desktop shell.
698  *
699  * @menu may be a #GtkMenu or a #GtkMenuBar.
700  *
701  * Returns: A #GtkMenu that has been populated from the
702  *     #GMenuModel that is associated with @application,
703  *     or %NULL
704  */
705 GtkWidget *
706 gtk_application_window_get_app_menu (GtkApplicationWindow *window)
707 {
708   GtkApplication *application;
709   GtkWidget *menu;
710   GMenuModel *model;
711   ItemsChangedData *data;
712   GActionMuxer *muxer;
713
714   application = gtk_window_get_application (GTK_WINDOW (window));
715
716   model = g_application_get_menu (G_APPLICATION (application));
717
718   if (!model)
719     return NULL;
720
721   menu = gtk_menu_new ();
722
723   muxer = g_action_muxer_new ();
724   g_action_muxer_insert (muxer, "app", G_ACTION_GROUP (application));
725   populate_menu_from_model (GTK_MENU_SHELL (menu), model, G_ACTION_GROUP (muxer));
726
727   data = g_new (ItemsChangedData, 1);
728   data->application = g_object_ref (application);
729   data->menu = GTK_MENU_SHELL (menu);
730   data->update_idle = 0;
731   data->connected = g_hash_table_new (NULL, NULL);
732
733   g_object_set_data_full (G_OBJECT (menu), "gtk-application-menu-data",
734                           data, free_items_changed_data);
735
736   connect_to_items_changed (model, G_CALLBACK (items_changed), data);
737
738   return menu;
739 }