]> Pileus Git - ~andy/gtk/blob - gtk/gtkapplicationwindow.c
04b947109433552e335e788fc1571979e5d752e1
[~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, see <http://www.gnu.org/licenses/>.
16  *
17  * Author: Ryan Lortie <desrt@desrt.ca>
18  */
19
20 #include "config.h"
21
22 #include "gtkapplicationwindow.h"
23
24 #include "gtkapplicationprivate.h"
25 #include "gtkwindowprivate.h"
26 #include "gtkmodelmenu.h"
27 #include "gactionmuxer.h"
28 #include "gtkaccelgroup.h"
29 #include "gtkaccelmap.h"
30 #include "gtkintl.h"
31 #include "gtksettings.h"
32
33 #include <gdk/gdk.h>
34 #ifdef GDK_WINDOWING_X11
35 #include <gdk/x11/gdkx.h>
36 #endif
37
38 /**
39  * SECTION:gtkapplicationwindow
40  * @title: GtkApplicationWindow
41  * @short_description: GtkWindow subclass with GtkApplication support
42  *
43  * GtkApplicationWindow is a #GtkWindow subclass that offers some
44  * extra functionality for better integration with #GtkApplication
45  * features.  Notably, it can handle both the application menu as well
46  * as the menubar. See gtk_application_set_app_menu() and
47  * gtk_application_set_menubar().
48  *
49  * This class implements the #GActionGroup and #GActionMap interfaces,
50  * to let you add window-specific actions that will be exported by the
51  * associated #GtkApplication, together with its application-wide
52  * actions.  Window-specific actions are prefixed with the "win."
53  * prefix and application-wide actions are prefixed with the "app."
54  * prefix.  Actions must be addressed with the prefixed name when
55  * referring to them from a #GMenuModel.
56  *
57  * Note that widgets that are placed inside a GtkApplicationWindow
58  * can also activate these actions, if they implement the
59  * GtkActionable interface.
60  *
61  * As with #GtkApplication, the GDK lock will be acquired when
62  * processing actions arriving from other processes and should therefore
63  * be held when activating actions locally (if GDK threads are enabled).
64  *
65  * The settings #GtkSettings:gtk-shell-shows-app-menu and
66  * #GtkSettings:gtk-shell-shows-menubar tell GTK+ whether the
67  * desktop environment is showing the application menu and menubar
68  * models outside the application as part of the desktop shell.
69  * For instance, on OS X, both menus will be displayed remotely;
70  * on Windows neither will be. gnome-shell (starting with version 3.4)
71  * will display the application menu, but not the menubar.
72  *
73  * If the desktop environment does not display the menubar, then
74  * #GApplicationWindow will automatically show a #GtkMenubar for it.
75  * (see the #GtkApplication docs for some screenshots of how this
76  * looks on different platforms).
77  * This behaviour can be overridden with the #GtkApplicationWindow:show-menubar
78  * property. If the desktop environment does not display the application
79  * menu, then it will automatically be included in the menubar.
80  *
81  * <example><title>A GtkApplicationWindow with a menubar</title>
82  * <programlisting><![CDATA[
83  * app = gtk_application_new ();
84  *
85  * builder = gtk_builder_new ();
86  * gtk_builder_add_from_string (builder,
87  *     "<interface>"
88  *     "  <menu id='menubar'>"
89  *     "    <submenu label='_Edit'>"
90  *     "      <item label='_Copy' action='win.copy'/>"
91  *     "      <item label='_Paste' action='win.paste'/>"
92  *     "    </submenu>"
93  *     "  </menu>"
94  *     "</interface>");
95  * gtk_application_set_menubar (G_APPLICATION (app),
96  *                              G_MENU_MODEL (gtk_builder_get_object (builder, "menubar")));
97  * g_object_unref (builder);
98  *
99  * ...
100  *
101  * window = gtk_application_window_new (app);
102  * ]]>
103  * </programlisting>
104  * </example>
105  *
106  * The XML format understood by #GtkBuilder for #GMenuModel consists
107  * of a toplevel <tag class="starttag">menu</tag> element, which contains
108  * one or more <tag class="starttag">item</tag> elements. Each
109  * <tag class="starttag">item</tag> element contains
110  * <tag class="starttag">attribute</tag> and <tag class="starttag">link</tag>
111  * elements with a mandatory name attribute.
112  * <tag class="starttag">link</tag> elements have the same content
113  * model as <tag class="starttag">menu</tag>.
114  *
115  * Attribute values can be translated using gettext, like other #GtkBuilder
116  * content. <tag class="starttag">attribute</tag> elements can be marked for
117  * translation with a <literal>translatable="yes"</literal> attribute.
118  * It is also possible to specify message context and translator comments,
119  * using the context and comments attributes. To make use of this, the
120  * #GtkBuilder must have been given the gettext domain to use.
121  */
122
123 typedef GSimpleActionGroupClass GtkApplicationWindowActionsClass;
124 typedef struct
125 {
126   GSimpleActionGroup parent_instance;
127   GtkWindow *window;
128 } GtkApplicationWindowActions;
129
130 static GType gtk_application_window_actions_get_type   (void);
131 static void  gtk_application_window_actions_iface_init (GRemoteActionGroupInterface *iface);
132 G_DEFINE_TYPE_WITH_CODE (GtkApplicationWindowActions, gtk_application_window_actions, G_TYPE_SIMPLE_ACTION_GROUP,
133                          G_IMPLEMENT_INTERFACE (G_TYPE_REMOTE_ACTION_GROUP, gtk_application_window_actions_iface_init))
134
135 static void
136 gtk_application_window_actions_activate_action_full (GRemoteActionGroup *remote,
137                                                      const gchar        *action_name,
138                                                      GVariant           *parameter,
139                                                      GVariant           *platform_data)
140 {
141   GtkApplicationWindowActions *actions = (GtkApplicationWindowActions *) remote;
142   GApplication *application;
143   GApplicationClass *class;
144
145   application = G_APPLICATION (gtk_window_get_application (actions->window));
146   class = G_APPLICATION_GET_CLASS (application);
147
148   class->before_emit (application, platform_data);
149   g_action_group_activate_action (G_ACTION_GROUP (actions), action_name, parameter);
150   class->after_emit (application, platform_data);
151 }
152
153 static void
154 gtk_application_window_actions_change_action_state_full (GRemoteActionGroup *remote,
155                                                          const gchar        *action_name,
156                                                          GVariant           *value,
157                                                          GVariant           *platform_data)
158 {
159   GtkApplicationWindowActions *actions = (GtkApplicationWindowActions *) remote;
160   GApplication *application;
161   GApplicationClass *class;
162
163   application = G_APPLICATION (gtk_window_get_application (actions->window));
164   class = G_APPLICATION_GET_CLASS (application);
165
166   class->before_emit (application, platform_data);
167   g_action_group_change_action_state (G_ACTION_GROUP (actions), action_name, value);
168   class->after_emit (application, platform_data);
169 }
170
171 static void
172 gtk_application_window_actions_init (GtkApplicationWindowActions *actions)
173 {
174 }
175
176 static void
177 gtk_application_window_actions_iface_init (GRemoteActionGroupInterface *iface)
178 {
179   iface->activate_action_full = gtk_application_window_actions_activate_action_full;
180   iface->change_action_state_full = gtk_application_window_actions_change_action_state_full;
181 }
182
183 static void
184 gtk_application_window_actions_class_init (GtkApplicationWindowActionsClass *class)
185 {
186 }
187
188 static GSimpleActionGroup *
189 gtk_application_window_actions_new (GtkApplicationWindow *window)
190 {
191   GtkApplicationWindowActions *actions;
192
193   actions = g_object_new (gtk_application_window_actions_get_type (), NULL);
194   actions->window = GTK_WINDOW (window);
195
196   return G_SIMPLE_ACTION_GROUP (actions);
197 }
198
199 /* Now onto GtkApplicationWindow... */
200
201 struct _GtkApplicationWindowPrivate
202 {
203   GSimpleActionGroup *actions;
204   GActionObservable *muxer;
205   gboolean muxer_initialised;
206   GtkWidget *menubar;
207   GtkAccelGroup *accels;
208   GSList *accel_closures;
209
210   GMenu *app_menu_section;
211   GMenu *menubar_section;
212   gboolean show_menubar;
213
214   GDBusConnection *session;
215   gchar           *object_path;
216   guint            export_id;
217 };
218
219 static void
220 gtk_application_window_update_menubar (GtkApplicationWindow *window)
221 {
222   gboolean should_have_menubar;
223   gboolean have_menubar;
224
225   have_menubar = window->priv->menubar != NULL;
226
227   should_have_menubar = window->priv->show_menubar &&
228                         (g_menu_model_get_n_items (G_MENU_MODEL (window->priv->app_menu_section)) ||
229                          g_menu_model_get_n_items (G_MENU_MODEL (window->priv->menubar_section)));
230
231   if (have_menubar && !should_have_menubar)
232     {
233       gtk_widget_unparent (window->priv->menubar);
234       window->priv->menubar = NULL;
235
236       gtk_widget_queue_resize (GTK_WIDGET (window));
237     }
238
239   if (!have_menubar && should_have_menubar)
240     {
241       GMenu *combined;
242
243       combined = g_menu_new ();
244       g_menu_append_section (combined, NULL, G_MENU_MODEL (window->priv->app_menu_section));
245       g_menu_append_section (combined, NULL, G_MENU_MODEL (window->priv->menubar_section));
246
247       window->priv->menubar = gtk_model_menu_create_menu_bar (G_MENU_MODEL (combined), window->priv->muxer, window->priv->accels);
248       gtk_widget_set_parent (window->priv->menubar, GTK_WIDGET (window));
249       gtk_widget_show_all (window->priv->menubar);
250       g_object_unref (combined);
251
252       gtk_widget_queue_resize (GTK_WIDGET (window));
253     }
254 }
255
256 static void
257 gtk_application_window_update_shell_shows_app_menu (GtkApplicationWindow *window,
258                                                     GtkSettings          *settings)
259 {
260   gboolean shown_by_shell;
261
262   g_object_get (settings, "gtk-shell-shows-app-menu", &shown_by_shell, NULL);
263
264   if (shown_by_shell)
265     {
266       /* the shell shows it, so don't show it locally */
267       if (g_menu_model_get_n_items (G_MENU_MODEL (window->priv->app_menu_section)) != 0)
268         g_menu_remove (window->priv->app_menu_section, 0);
269     }
270   else
271     {
272       /* the shell does not show it, so make sure we show it */
273       if (g_menu_model_get_n_items (G_MENU_MODEL (window->priv->app_menu_section)) == 0)
274         {
275           GMenuModel *app_menu;
276
277           app_menu = gtk_application_get_app_menu (gtk_window_get_application (GTK_WINDOW (window)));
278
279           if (app_menu != NULL)
280             {
281               const gchar *name;
282
283               name = g_get_application_name ();
284               if (name == g_get_prgname ())
285                 name = _("Application");
286               g_menu_append_submenu (window->priv->app_menu_section, name, app_menu);
287             }
288         }
289     }
290 }
291
292 static void
293 gtk_application_window_update_shell_shows_menubar (GtkApplicationWindow *window,
294                                                    GtkSettings          *settings)
295 {
296   gboolean shown_by_shell;
297
298   g_object_get (settings, "gtk-shell-shows-menubar", &shown_by_shell, NULL);
299
300   if (shown_by_shell)
301     {
302       /* the shell shows it, so don't show it locally */
303       if (g_menu_model_get_n_items (G_MENU_MODEL (window->priv->menubar_section)) != 0)
304         g_menu_remove (window->priv->menubar_section, 0);
305     }
306   else
307     {
308       /* the shell does not show it, so make sure we show it */
309       if (g_menu_model_get_n_items (G_MENU_MODEL (window->priv->menubar_section)) == 0)
310         {
311           GMenuModel *menubar;
312
313           menubar = gtk_application_get_menubar (gtk_window_get_application (GTK_WINDOW (window)));
314
315           if (menubar != NULL)
316             g_menu_append_section (window->priv->menubar_section, NULL, menubar);
317         }
318     }
319 }
320
321 typedef struct {
322   GClosure closure;
323   gchar *action_name;
324   GVariant *parameter;
325 } AccelClosure;
326
327 static void
328 accel_activate (GClosure     *closure,
329                 GValue       *return_value,
330                 guint         n_param_values,
331                 const GValue *param_values,
332                 gpointer      invocation_hint,
333                 gpointer      marshal_data)
334 {
335   AccelClosure *aclosure = (AccelClosure*)closure;
336   GActionGroup *actions;
337
338   actions = G_ACTION_GROUP (closure->data);
339   if (g_action_group_get_action_enabled (actions, aclosure->action_name))
340     {
341        g_action_group_activate_action (actions, aclosure->action_name, aclosure->parameter);
342
343       /* we handled the accelerator */
344       g_value_set_boolean (return_value, TRUE);
345     }
346 }
347
348 static void
349 free_accel_closures (GtkApplicationWindow *window)
350 {
351   GSList *l;
352
353   for (l = window->priv->accel_closures; l; l = l->next)
354     {
355        AccelClosure *closure = l->data;
356
357        gtk_accel_group_disconnect (window->priv->accels, &closure->closure);
358
359        g_object_unref (closure->closure.data);
360        if (closure->parameter)
361          g_variant_unref (closure->parameter);
362        g_free (closure->action_name);
363        g_closure_invalidate (&closure->closure);
364        g_closure_unref (&closure->closure);
365     }
366   g_slist_free (window->priv->accel_closures);
367   window->priv->accel_closures = NULL;
368 }
369
370 typedef struct {
371   GtkApplicationWindow *window;
372   GActionGroup *actions;
373 } AccelData;
374
375 /* Hack. We iterate over the accel map instead of the actions,
376  * in order to pull the parameters out of accel map entries
377  */
378 static void
379 add_accel_closure (gpointer         data,
380                    const gchar     *accel_path,
381                    guint            accel_key,
382                    GdkModifierType  accel_mods,
383                    gboolean         changed)
384 {
385   AccelData *d = data;
386   GtkApplicationWindow *window = d->window;
387   GActionGroup *actions = d->actions;
388   const gchar *path;
389   const gchar *p;
390   gchar *action_name;
391   GVariant *parameter;
392   AccelClosure *closure;
393
394   if (accel_key == 0)
395     return;
396
397   if (!g_str_has_prefix (accel_path, "<GAction>/"))
398     return;
399
400   path = accel_path + strlen ("<GAction>/");
401   p = strchr (path, '/');
402   if (p)
403     {
404       action_name = g_strndup (path, p - path);
405       parameter = g_variant_parse (NULL, p + 1, NULL, NULL, NULL);
406       if (!parameter)
407         g_warning ("Failed to parse parameter from '%s'\n", accel_path);
408     }
409   else
410     {
411       action_name = g_strdup (path);
412       parameter = NULL;
413     }
414
415   if (g_action_group_has_action (actions, action_name))
416     {
417       closure = (AccelClosure*) g_closure_new_object (sizeof (AccelClosure), g_object_ref (actions));
418       g_closure_set_marshal (&closure->closure, accel_activate);
419
420       closure->action_name = g_strdup (action_name);
421       closure->parameter = parameter ? g_variant_ref_sink (parameter) : NULL;
422
423       window->priv->accel_closures = g_slist_prepend (window->priv->accel_closures, g_closure_ref (&closure->closure));
424       g_closure_sink (&closure->closure);
425
426       gtk_accel_group_connect_by_path (window->priv->accels, accel_path, &closure->closure);
427     }
428
429   g_free (action_name);
430 }
431
432 static void
433 gtk_application_window_update_accels (GtkApplicationWindow *window)
434 {
435   AccelData data;
436
437   free_accel_closures (window);
438
439   data.window = window;
440   data.actions = G_ACTION_GROUP (window->priv->muxer);
441
442   gtk_accel_map_foreach (&data, add_accel_closure);
443 }
444
445 static void
446 gtk_application_window_shell_shows_app_menu_changed (GObject    *object,
447                                                      GParamSpec *pspec,
448                                                      gpointer    user_data)
449 {
450   GtkApplicationWindow *window = user_data;
451
452   gtk_application_window_update_shell_shows_app_menu (window, GTK_SETTINGS (object));
453   gtk_application_window_update_menubar (window);
454 }
455
456 static void
457 gtk_application_window_shell_shows_menubar_changed (GObject    *object,
458                                                     GParamSpec *pspec,
459                                                     gpointer    user_data)
460 {
461   GtkApplicationWindow *window = user_data;
462
463   gtk_application_window_update_shell_shows_menubar (window, GTK_SETTINGS (object));
464   gtk_application_window_update_menubar (window);
465 }
466
467 static gchar **
468 gtk_application_window_list_actions (GActionGroup *group)
469 {
470   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
471
472   return g_action_group_list_actions (G_ACTION_GROUP (window->priv->actions));
473 }
474
475 static gboolean
476 gtk_application_window_query_action (GActionGroup        *group,
477                                      const gchar         *action_name,
478                                      gboolean            *enabled,
479                                      const GVariantType **parameter_type,
480                                      const GVariantType **state_type,
481                                      GVariant           **state_hint,
482                                      GVariant           **state)
483 {
484   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
485
486   return g_action_group_query_action (G_ACTION_GROUP (window->priv->actions),
487                                       action_name, enabled, parameter_type, state_type, state_hint, state);
488 }
489
490 static void
491 gtk_application_window_activate_action (GActionGroup *group,
492                                         const gchar  *action_name,
493                                         GVariant     *parameter)
494 {
495   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
496
497   return g_action_group_activate_action (G_ACTION_GROUP (window->priv->actions), action_name, parameter);
498 }
499
500 static void
501 gtk_application_window_change_action_state (GActionGroup *group,
502                                             const gchar  *action_name,
503                                             GVariant     *state)
504 {
505   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
506
507   return g_action_group_change_action_state (G_ACTION_GROUP (window->priv->actions), action_name, state);
508 }
509
510 static GAction *
511 gtk_application_window_lookup_action (GActionMap  *action_map,
512                                       const gchar *action_name)
513 {
514   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (action_map);
515
516   return g_action_map_lookup_action (G_ACTION_MAP (window->priv->actions), action_name);
517 }
518
519 static void
520 gtk_application_window_add_action (GActionMap *action_map,
521                                    GAction    *action)
522 {
523   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (action_map);
524
525   g_action_map_add_action (G_ACTION_MAP (window->priv->actions), action);
526 }
527
528 static void
529 gtk_application_window_remove_action (GActionMap  *action_map,
530                                       const gchar *action_name)
531 {
532   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (action_map);
533
534   g_action_map_remove_action (G_ACTION_MAP (window->priv->actions), action_name);
535 }
536
537 static void
538 gtk_application_window_group_iface_init (GActionGroupInterface *iface)
539 {
540   iface->list_actions = gtk_application_window_list_actions;
541   iface->query_action = gtk_application_window_query_action;
542   iface->activate_action = gtk_application_window_activate_action;
543   iface->change_action_state = gtk_application_window_change_action_state;
544 }
545
546 static void
547 gtk_application_window_map_iface_init (GActionMapInterface *iface)
548 {
549   iface->lookup_action = gtk_application_window_lookup_action;
550   iface->add_action = gtk_application_window_add_action;
551   iface->remove_action = gtk_application_window_remove_action;
552 }
553
554 G_DEFINE_TYPE_WITH_CODE (GtkApplicationWindow, gtk_application_window, GTK_TYPE_WINDOW,
555                          G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, gtk_application_window_group_iface_init)
556                          G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_MAP, gtk_application_window_map_iface_init))
557
558 enum {
559   PROP_0,
560   PROP_SHOW_MENUBAR,
561   N_PROPS
562 };
563 static GParamSpec *gtk_application_window_properties[N_PROPS];
564
565 static void
566 gtk_application_window_real_get_preferred_height (GtkWidget *widget,
567                                                   gint      *minimum_height,
568                                                   gint      *natural_height)
569 {
570   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
571
572   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
573     ->get_preferred_height (widget, minimum_height, natural_height);
574
575   if (window->priv->menubar != NULL)
576     {
577       gint menubar_min_height, menubar_nat_height;
578
579       gtk_widget_get_preferred_height (window->priv->menubar, &menubar_min_height, &menubar_nat_height);
580       *minimum_height += menubar_min_height;
581       *natural_height += menubar_nat_height;
582     }
583 }
584
585 static void
586 gtk_application_window_real_get_preferred_height_for_width (GtkWidget *widget,
587                                                             gint       width,
588                                                             gint      *minimum_height,
589                                                             gint      *natural_height)
590 {
591   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
592
593   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
594     ->get_preferred_height_for_width (widget, width, minimum_height, natural_height);
595
596   if (window->priv->menubar != NULL)
597     {
598       gint menubar_min_height, menubar_nat_height;
599
600       gtk_widget_get_preferred_height_for_width (window->priv->menubar, width, &menubar_min_height, &menubar_nat_height);
601       *minimum_height += menubar_min_height;
602       *natural_height += menubar_nat_height;
603     }
604 }
605
606 static void
607 gtk_application_window_real_get_preferred_width (GtkWidget *widget,
608                                                  gint      *minimum_width,
609                                                  gint      *natural_width)
610 {
611   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
612
613   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
614     ->get_preferred_width (widget, minimum_width, natural_width);
615
616   if (window->priv->menubar != NULL)
617     {
618       gint menubar_min_width, menubar_nat_width;
619
620       gtk_widget_get_preferred_width (window->priv->menubar, &menubar_min_width, &menubar_nat_width);
621       *minimum_width = MAX (*minimum_width, menubar_min_width);
622       *natural_width = MAX (*natural_width, menubar_nat_width);
623     }
624 }
625
626 static void
627 gtk_application_window_real_get_preferred_width_for_height (GtkWidget *widget,
628                                                             gint       height,
629                                                             gint      *minimum_width,
630                                                             gint      *natural_width)
631 {
632   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
633   gint menubar_height;
634
635   if (window->priv->menubar != NULL)
636     gtk_widget_get_preferred_height (window->priv->menubar, &menubar_height, NULL);
637   else
638     menubar_height = 0;
639
640   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
641     ->get_preferred_width_for_height (widget, height - menubar_height, minimum_width, natural_width);
642
643   if (window->priv->menubar != NULL)
644     {
645       gint menubar_min_width, menubar_nat_width;
646
647       gtk_widget_get_preferred_width_for_height (window->priv->menubar, menubar_height, &menubar_min_width, &menubar_nat_width);
648       *minimum_width = MAX (*minimum_width, menubar_min_width);
649       *natural_width = MAX (*natural_width, menubar_nat_width);
650     }
651 }
652
653 static void
654 gtk_application_window_real_size_allocate (GtkWidget     *widget,
655                                            GtkAllocation *allocation)
656 {
657   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
658
659   if (window->priv->menubar != NULL)
660     {
661       GtkAllocation menubar_allocation = *allocation;
662       gint menubar_height;
663       GtkWidget *child;
664
665       _gtk_window_set_allocation (GTK_WINDOW (widget), allocation);
666
667       gtk_widget_get_preferred_height_for_width (window->priv->menubar, allocation->width, &menubar_height, NULL);
668
669       menubar_allocation.height = menubar_height;
670       gtk_widget_size_allocate (window->priv->menubar, &menubar_allocation);
671
672       child = gtk_bin_get_child (GTK_BIN (window));
673       if (child != NULL && gtk_widget_get_visible (child))
674         {
675           GtkAllocation child_allocation = *allocation;
676           gint border_width;
677
678           border_width = gtk_container_get_border_width (GTK_CONTAINER (window));
679           child_allocation.x += border_width;
680           child_allocation.y += border_width + menubar_height;
681           child_allocation.width = MAX (1, child_allocation.width - border_width * 2);
682           child_allocation.height = MAX (1, child_allocation.height - border_width * 2 - menubar_height);
683
684           gtk_widget_size_allocate (child, &child_allocation);
685         }
686     }
687   else
688     GTK_WIDGET_CLASS (gtk_application_window_parent_class)
689       ->size_allocate (widget, allocation);
690 }
691
692 static void
693 gtk_application_window_real_realize (GtkWidget *widget)
694 {
695   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
696   GtkApplication *application;
697   GtkSettings *settings;
698
699   application = gtk_window_get_application (GTK_WINDOW (window));
700   settings = gtk_widget_get_settings (widget);
701
702   g_signal_connect (settings, "notify::gtk-shell-shows-app-menu",
703                     G_CALLBACK (gtk_application_window_shell_shows_app_menu_changed), window);
704   g_signal_connect (settings, "notify::gtk-shell-shows-menubar",
705                     G_CALLBACK (gtk_application_window_shell_shows_menubar_changed), window);
706
707   if (!window->priv->muxer_initialised)
708     {
709       g_action_muxer_insert (G_ACTION_MUXER (window->priv->muxer), "app", G_ACTION_GROUP (application));
710       g_action_muxer_insert (G_ACTION_MUXER (window->priv->muxer), "win", G_ACTION_GROUP (window));
711       window->priv->muxer_initialised = TRUE;
712     }
713
714   gtk_application_window_update_shell_shows_app_menu (window, settings);
715   gtk_application_window_update_shell_shows_menubar (window, settings);
716   gtk_application_window_update_menubar (window);
717   gtk_application_window_update_accels (window);
718
719   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
720     ->realize (widget);
721
722 #ifdef GDK_WINDOWING_X11
723   {
724     GdkWindow *gdkwindow;
725
726     gdkwindow = gtk_widget_get_window (GTK_WIDGET (window));
727
728     if (GDK_IS_X11_WINDOW (gdkwindow))
729       {
730         gdk_x11_window_set_utf8_property (gdkwindow, "_GTK_APPLICATION_ID",
731                                           g_application_get_application_id (G_APPLICATION (application)));
732
733         gdk_x11_window_set_utf8_property (gdkwindow, "_GTK_UNIQUE_BUS_NAME",
734                                           g_dbus_connection_get_unique_name (window->priv->session));
735
736         gdk_x11_window_set_utf8_property (gdkwindow, "_GTK_APPLICATION_OBJECT_PATH",
737                                           gtk_application_get_dbus_object_path (application));
738
739         gdk_x11_window_set_utf8_property (gdkwindow, "_GTK_WINDOW_OBJECT_PATH",
740                                           window->priv->object_path);
741
742         gdk_x11_window_set_utf8_property (gdkwindow, "_GTK_APP_MENU_OBJECT_PATH",
743                                           gtk_application_get_app_menu_object_path (application));
744
745         gdk_x11_window_set_utf8_property (gdkwindow, "_GTK_MENUBAR_OBJECT_PATH",
746                                           gtk_application_get_menubar_object_path (application));
747       }
748   }
749 #endif
750 }
751
752 static void
753 gtk_application_window_real_unrealize (GtkWidget *widget)
754 {
755   GtkSettings *settings;
756
757   settings = gtk_widget_get_settings (widget);
758
759   g_signal_handlers_disconnect_by_func (settings, gtk_application_window_shell_shows_app_menu_changed, widget);
760   g_signal_handlers_disconnect_by_func (settings, gtk_application_window_shell_shows_menubar_changed, widget);
761
762   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
763     ->unrealize (widget);
764 }
765
766 gboolean
767 gtk_application_window_publish (GtkApplicationWindow *window,
768                                 GDBusConnection      *session,
769                                 const gchar          *object_path)
770 {
771   g_assert (window->priv->session == NULL);
772   g_assert (window->priv->export_id == 0);
773   g_assert (window->priv->object_path == NULL);
774
775   window->priv->export_id = g_dbus_connection_export_action_group (session, object_path,
776                                                                    G_ACTION_GROUP (window->priv->actions),
777                                                                    NULL);
778
779   if (window->priv->export_id == 0)
780     return FALSE;
781
782   window->priv->session = session;
783   window->priv->object_path = g_strdup (object_path);
784
785   return TRUE;
786 }
787
788 void
789 gtk_application_window_unpublish (GtkApplicationWindow *window)
790 {
791   g_assert (window->priv->session != NULL);
792   g_assert (window->priv->export_id != 0);
793   g_assert (window->priv->object_path != NULL);
794
795   g_dbus_connection_unexport_action_group (window->priv->session, window->priv->export_id);
796   window->priv->session = NULL;
797   window->priv->export_id = 0;
798
799   g_free (window->priv->object_path);
800   window->priv->object_path = NULL;
801 }
802
803 static void
804 gtk_application_window_real_map (GtkWidget *widget)
805 {
806   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
807
808   /* XXX could eliminate this by tweaking gtk_window_map */
809   if (window->priv->menubar)
810     gtk_widget_map (window->priv->menubar);
811
812   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
813     ->map (widget);
814 }
815
816 static void
817 gtk_application_window_real_forall_internal (GtkContainer *container,
818                                              gboolean      include_internal,
819                                              GtkCallback   callback,
820                                              gpointer      user_data)
821 {
822   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (container);
823
824   if (window->priv->menubar)
825     callback (window->priv->menubar, user_data);
826
827   GTK_CONTAINER_CLASS (gtk_application_window_parent_class)
828     ->forall (container, include_internal, callback, user_data);
829 }
830
831
832 static void
833 gtk_application_window_get_property (GObject    *object,
834                                      guint       prop_id,
835                                      GValue     *value,
836                                      GParamSpec *pspec)
837 {
838   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (object);
839
840   switch (prop_id)
841     {
842     case PROP_SHOW_MENUBAR:
843       g_value_set_boolean (value, window->priv->show_menubar);
844       break;
845
846     default:
847       g_assert_not_reached ();
848     }
849 }
850
851 static void
852 gtk_application_window_set_property (GObject      *object,
853                                      guint         prop_id,
854                                      const GValue *value,
855                                      GParamSpec   *pspec)
856 {
857   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (object);
858
859   switch (prop_id)
860     {
861     case PROP_SHOW_MENUBAR:
862       gtk_application_window_set_show_menubar (window, g_value_get_boolean (value));
863       break;
864
865     default:
866       g_assert_not_reached ();
867     }
868 }
869
870 static void
871 gtk_application_window_dispose (GObject *object)
872 {
873   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (object);
874
875   if (window->priv->menubar)
876     {
877       gtk_widget_unparent (window->priv->menubar);
878       window->priv->menubar = NULL;
879     }
880
881   free_accel_closures (window);
882
883   g_clear_object (&window->priv->app_menu_section);
884   g_clear_object (&window->priv->menubar_section);
885   g_clear_object (&window->priv->actions);
886   g_clear_object (&window->priv->accels);
887   g_clear_object (&window->priv->muxer);
888
889   G_OBJECT_CLASS (gtk_application_window_parent_class)
890     ->dispose (object);
891 }
892
893 static void
894 gtk_application_window_init (GtkApplicationWindow *window)
895 {
896   window->priv = G_TYPE_INSTANCE_GET_PRIVATE (window, GTK_TYPE_APPLICATION_WINDOW, GtkApplicationWindowPrivate);
897
898   window->priv->actions = gtk_application_window_actions_new (window);
899   window->priv->app_menu_section = g_menu_new ();
900   window->priv->menubar_section = g_menu_new ();
901   window->priv->accels = gtk_accel_group_new ();
902   gtk_window_add_accel_group (GTK_WINDOW (window), window->priv->accels);
903
904   /* window->priv->actions is the one and only ref on the group, so when
905    * we dispose, the action group will die, disconnecting all signals.
906    */
907   g_signal_connect_swapped (window->priv->actions, "action-added",
908                             G_CALLBACK (g_action_group_action_added), window);
909   g_signal_connect_swapped (window->priv->actions, "action-enabled-changed",
910                             G_CALLBACK (g_action_group_action_enabled_changed), window);
911   g_signal_connect_swapped (window->priv->actions, "action-state-changed",
912                             G_CALLBACK (g_action_group_action_state_changed), window);
913   g_signal_connect_swapped (window->priv->actions, "action-removed",
914                             G_CALLBACK (g_action_group_action_removed), window);
915
916   window->priv->muxer = G_ACTION_OBSERVABLE (g_action_muxer_new ());
917 }
918
919 static void
920 gtk_application_window_class_init (GtkApplicationWindowClass *class)
921 {
922   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
923   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
924   GObjectClass *object_class = G_OBJECT_CLASS (class);
925
926   container_class->forall = gtk_application_window_real_forall_internal;
927   widget_class->get_preferred_height = gtk_application_window_real_get_preferred_height;
928   widget_class->get_preferred_height_for_width = gtk_application_window_real_get_preferred_height_for_width;
929   widget_class->get_preferred_width = gtk_application_window_real_get_preferred_width;
930   widget_class->get_preferred_width_for_height = gtk_application_window_real_get_preferred_width_for_height;
931   widget_class->size_allocate = gtk_application_window_real_size_allocate;
932   widget_class->realize = gtk_application_window_real_realize;
933   widget_class->unrealize = gtk_application_window_real_unrealize;
934   widget_class->map = gtk_application_window_real_map;
935   object_class->get_property = gtk_application_window_get_property;
936   object_class->set_property = gtk_application_window_set_property;
937   object_class->dispose = gtk_application_window_dispose;
938
939   /**
940    * GtkApplicationWindow:show-menubar:
941    *
942    * If this property is %TRUE, the window will display a menubar
943    * that includes the app menu and menubar, unless these are
944    * shown by the desktop shell. See gtk_application_set_app_menu()
945    * and gtk_application_set_menubar().
946    *
947    * If %FALSE, the window will not display a menubar, regardless
948    * of whether the desktop shell is showing the menus or not.
949    */
950   gtk_application_window_properties[PROP_SHOW_MENUBAR] =
951     g_param_spec_boolean ("show-menubar",
952                           P_("Show a menubar"),
953                           P_("TRUE if the window should show a "
954                              "menubar at the top of the window"),
955                           TRUE, G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
956   g_object_class_install_properties (object_class, N_PROPS, gtk_application_window_properties);
957   g_type_class_add_private (class, sizeof (GtkApplicationWindowPrivate));
958 }
959
960 /**
961  * gtk_application_window_new:
962  * @application: a #GtkApplication
963  *
964  * Creates a new #GtkApplicationWindow.
965  *
966  * Returns: a newly created #GtkApplicationWindow
967  *
968  * Since: 3.4
969  */
970 GtkWidget *
971 gtk_application_window_new (GtkApplication *application)
972 {
973   g_return_val_if_fail (GTK_IS_APPLICATION (application), NULL);
974
975   return g_object_new (GTK_TYPE_APPLICATION_WINDOW,
976                        "application", application,
977                        NULL);
978 }
979
980 /**
981  * gtk_application_window_get_show_menubar:
982  * @window: a #GtkApplicationWindow
983  *
984  * Returns whether the window will display a menubar for the app menu
985  * and menubar as needed.
986  *
987  * Returns: %TRUE if @window will display a menubar when needed
988  *
989  * Since: 3.4
990  */
991 gboolean
992 gtk_application_window_get_show_menubar (GtkApplicationWindow *window)
993 {
994   return window->priv->show_menubar;
995 }
996
997 /**
998  * gtk_application_window_set_show_menubar:
999  * @window: a #GtkApplicationWindow
1000  * @show_menubar: whether to show a menubar when needed
1001  *
1002  * Sets whether the window will display a menubar for the app menu
1003  * and menubar as needed.
1004  *
1005  * Since: 3.4
1006  */
1007 void
1008 gtk_application_window_set_show_menubar (GtkApplicationWindow *window,
1009                                          gboolean              show_menubar)
1010 {
1011   g_return_if_fail (GTK_IS_APPLICATION_WINDOW (window));
1012
1013   show_menubar = !!show_menubar;
1014
1015   if (window->priv->show_menubar != show_menubar)
1016     {
1017       window->priv->show_menubar = show_menubar;
1018
1019       gtk_application_window_update_menubar (window);
1020
1021       g_object_notify_by_pspec (G_OBJECT (window), gtk_application_window_properties[PROP_SHOW_MENUBAR]);
1022     }
1023 }
1024
1025 GSimpleActionObserver *
1026 gtk_application_window_create_observer (GtkApplicationWindow *window,
1027                                         const gchar          *action_name,
1028                                         GVariant             *target)
1029 {
1030   g_return_val_if_fail (GTK_IS_APPLICATION_WINDOW (window), NULL);
1031
1032   return g_simple_action_observer_new (window->priv->muxer, action_name, target);
1033 }
1034
1035 GActionObservable *
1036 gtk_application_window_get_observable (GtkApplicationWindow *window)
1037 {
1038   return G_ACTION_OBSERVABLE (window->priv->muxer);
1039 }
1040
1041 GtkAccelGroup *
1042 gtk_application_window_get_accel_group (GtkApplicationWindow *window)
1043 {
1044   return window->priv->accels;
1045 }