]> Pileus Git - ~andy/gtk/blob - gtk/gtkapplicationwindow.c
Move X property handling to 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 "gtkapplicationprivate.h"
27 #include "gtkmodelmenu.h"
28 #include "gactionmuxer.h"
29 #include "gtkaccelgroup.h"
30 #include "gtkaccelmap.h"
31 #include "gtkintl.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 g_application_set_app_menu() and
47  * g_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  * The settings #GtkSettings:gtk-shell-shows-app-menu and
58  * #GtkSettings:gtk-shell-shows-menubar tell GTK+ whether the
59  * desktop environment is showing the application menu and menubar
60  * models outside the application as part of the desktop shell.
61  * For instance, on OS X, both menus will be displayed remotely;
62  * on Windows neither will be. gnome-shell (starting with version 3.4)
63  * will display the application menu, but not the menubar.
64  *
65  * If the desktop environment does not display the menubar, then
66  * #GApplicationWindow will automatically show a #GtkMenubar for it.
67  * This behaviour can be overridden with the #GtkApplicationWindow:show-menubar
68  * property. If the desktop environment does not display the application
69  * menu, then it will automatilly be included in the menubar.
70  *
71  * <example><title>A GtkApplicationWindow with a menubar</title>
72  * <programlisting><![CDATA[
73  * app = gtk_application_new ();
74  *
75  * builder = gtk_builder_new ();
76  * gtk_builder_add_from_string (builder,
77  *     "<interface>"
78  *     "  <menu id='menubar'>"
79  *     "    <submenu label='_Edit'>"
80  *     "      <item label='_Copy' action='win.copy'/>"
81  *     "      <item label='_Paste' action='win.paste'/>"
82  *     "    </submenu>"
83  *     "  </menu>"
84  *     "</interface>");
85  * g_application_set_menubar (G_APPLICATION (app),
86  *                            G_MENU_MODEL (gtk_builder_get_object (builder, "menubar")));
87  * g_object_unref (builder);
88  *
89  * ...
90  *
91  * window = gtk_application_window_new (app);
92  * ]]>
93  * </programlisting>
94  * </example>
95  */
96 struct _GtkApplicationWindowPrivate
97 {
98   GSimpleActionGroup *actions;
99   GActionObservable *muxer;
100   GtkWidget *menubar;
101   GtkAccelGroup *accels;
102   GSList *accel_closures;
103
104   GMenu *app_menu_section;
105   GMenu *menubar_section;
106   gboolean show_menubar;
107
108   GDBusConnection *session;
109   gchar           *object_path;
110   guint            export_id;
111 };
112
113 static void
114 gtk_application_window_update_menubar (GtkApplicationWindow *window)
115 {
116   gboolean should_have_menubar;
117   gboolean have_menubar;
118
119   have_menubar = window->priv->menubar != NULL;
120
121   should_have_menubar = window->priv->show_menubar &&
122                         (g_menu_model_get_n_items (G_MENU_MODEL (window->priv->app_menu_section)) ||
123                          g_menu_model_get_n_items (G_MENU_MODEL (window->priv->menubar_section)));
124
125   if (have_menubar && !should_have_menubar)
126     {
127       gtk_widget_unparent (window->priv->menubar);
128       window->priv->menubar = NULL;
129
130       gtk_widget_queue_resize (GTK_WIDGET (window));
131     }
132
133   if (!have_menubar && should_have_menubar)
134     {
135       GMenu *combined;
136
137       combined = g_menu_new ();
138       g_menu_append_section (combined, NULL, G_MENU_MODEL (window->priv->app_menu_section));
139       g_menu_append_section (combined, NULL, G_MENU_MODEL (window->priv->menubar_section));
140
141       window->priv->menubar = gtk_model_menu_create_menu_bar (G_MENU_MODEL (combined), window->priv->muxer, window->priv->accels);
142       gtk_widget_set_parent (window->priv->menubar, GTK_WIDGET (window));
143       gtk_widget_show_all (window->priv->menubar);
144       g_object_unref (combined);
145
146       gtk_widget_queue_resize (GTK_WIDGET (window));
147     }
148 }
149
150 static void
151 gtk_application_window_update_shell_shows_app_menu (GtkApplicationWindow *window,
152                                                     GtkSettings          *settings)
153 {
154   gboolean shown_by_shell;
155
156   g_object_get (settings, "gtk-shell-shows-app-menu", &shown_by_shell, NULL);
157
158   if (shown_by_shell)
159     {
160       /* the shell shows it, so don't show it locally */
161       if (g_menu_model_get_n_items (G_MENU_MODEL (window->priv->app_menu_section)) != 0)
162         g_menu_remove (window->priv->app_menu_section, 0);
163     }
164   else
165     {
166       /* the shell does not show it, so make sure we show it */
167       if (g_menu_model_get_n_items (G_MENU_MODEL (window->priv->app_menu_section)) == 0)
168         {
169           GMenuModel *app_menu;
170
171           app_menu = g_application_get_app_menu (G_APPLICATION (gtk_window_get_application (GTK_WINDOW (window))));
172
173           if (app_menu != NULL)
174             g_menu_append_submenu (window->priv->app_menu_section, _("Application"), app_menu);
175         }
176     }
177 }
178
179 static void
180 gtk_application_window_update_shell_shows_menubar (GtkApplicationWindow *window,
181                                                    GtkSettings          *settings)
182 {
183   gboolean shown_by_shell;
184
185   g_object_get (settings, "gtk-shell-shows-menubar", &shown_by_shell, NULL);
186
187   if (shown_by_shell)
188     {
189       /* the shell shows it, so don't show it locally */
190       if (g_menu_model_get_n_items (G_MENU_MODEL (window->priv->menubar_section)) != 0)
191         g_menu_remove (window->priv->menubar_section, 0);
192     }
193   else
194     {
195       /* the shell does not show it, so make sure we show it */
196       if (g_menu_model_get_n_items (G_MENU_MODEL (window->priv->menubar_section)) == 0)
197         {
198           GMenuModel *menubar;
199
200           menubar = g_application_get_menubar (G_APPLICATION (gtk_window_get_application (GTK_WINDOW (window))));
201
202           if (menubar != NULL)
203             g_menu_append_section (window->priv->menubar_section, NULL, menubar);
204         }
205     }
206 }
207
208 typedef struct {
209   GClosure closure;
210   gchar *action_name;
211   GVariant *parameter;
212 } AccelClosure;
213
214 static void
215 accel_activate (GClosure     *closure,
216                 GValue       *return_value,
217                 guint         n_param_values,
218                 const GValue *param_values,
219                 gpointer      invocation_hint,
220                 gpointer      marshal_data)
221 {
222   AccelClosure *aclosure = (AccelClosure*)closure;
223   GActionGroup *actions;
224
225   actions = G_ACTION_GROUP (closure->data);
226   if (g_action_group_get_action_enabled (actions, aclosure->action_name))
227     {
228        g_action_group_activate_action (actions, aclosure->action_name, aclosure->parameter);
229
230       /* we handled the accelerator */
231       g_value_set_boolean (return_value, TRUE);
232     }
233 }
234
235 static void
236 free_accel_closures (GtkApplicationWindow *window)
237 {
238   GSList *l;
239
240   for (l = window->priv->accel_closures; l; l = l->next)
241     {
242        AccelClosure *closure = l->data;
243
244        gtk_accel_group_disconnect (window->priv->accels, &closure->closure);
245
246        g_object_unref (closure->closure.data);
247        if (closure->parameter)
248          g_variant_unref (closure->parameter);
249        g_free (closure->action_name);
250        g_closure_invalidate (&closure->closure);
251        g_closure_unref (&closure->closure);
252     }
253   g_slist_free (window->priv->accel_closures);
254   window->priv->accel_closures = NULL;
255 }
256
257 typedef struct {
258   GtkApplicationWindow *window;
259   GActionGroup *actions;
260 } AccelData;
261
262 /* Hack. We iterate over the accel map instead of the actions,
263  * in order to pull the parameters out of accel map entries
264  */
265 static void
266 add_accel_closure (gpointer         data,
267                    const gchar     *accel_path,
268                    guint            accel_key,
269                    GdkModifierType  accel_mods,
270                    gboolean         changed)
271 {
272   AccelData *d = data;
273   GtkApplicationWindow *window = d->window;
274   GActionGroup *actions = d->actions;
275   const gchar *path;
276   const gchar *p;
277   gchar *action_name;
278   GVariant *parameter;
279   AccelClosure *closure;
280
281   if (accel_key == 0)
282     return;
283
284   if (!g_str_has_prefix (accel_path, "<Actions>/"))
285     return;
286
287   path = accel_path + strlen ("<Actions>/");
288   p = strchr (path, '/');
289   if (p)
290     {
291       action_name = g_strndup (path, p - path);
292       parameter = g_variant_parse (NULL, p + 1, NULL, NULL, NULL);
293       if (!parameter)
294         g_warning ("Failed to parse parameter from '%s'\n", accel_path);
295     }
296   else
297     {
298       action_name = g_strdup (path);
299       parameter = NULL;
300     }
301
302   if (g_action_group_has_action (actions, action_name))
303     {
304       closure = (AccelClosure*) g_closure_new_object (sizeof (AccelClosure), g_object_ref (actions));
305       g_closure_set_marshal (&closure->closure, accel_activate);
306
307       closure->action_name = g_strdup (action_name);
308       closure->parameter = parameter ? g_variant_ref_sink (parameter) : NULL;
309
310       window->priv->accel_closures = g_slist_prepend (window->priv->accel_closures, g_closure_ref (&closure->closure));
311       g_closure_sink (&closure->closure);
312
313       gtk_accel_group_connect_by_path (window->priv->accels, accel_path, &closure->closure);
314     }
315
316   g_free (action_name);
317 }
318
319 static void
320 gtk_application_window_update_accels (GtkApplicationWindow *window)
321 {
322   AccelData data;
323
324   free_accel_closures (window);
325
326   data.window = window;
327   data.actions = G_ACTION_GROUP (window->priv->muxer);
328
329   gtk_accel_map_foreach (&data, add_accel_closure);
330 }
331
332 static void
333 gtk_application_window_shell_shows_app_menu_changed (GObject    *object,
334                                                      GParamSpec *pspec,
335                                                      gpointer    user_data)
336 {
337   GtkApplicationWindow *window = user_data;
338
339   gtk_application_window_update_shell_shows_app_menu (window, GTK_SETTINGS (object));
340   gtk_application_window_update_menubar (window);
341 }
342
343 static void
344 gtk_application_window_shell_shows_menubar_changed (GObject    *object,
345                                                     GParamSpec *pspec,
346                                                     gpointer    user_data)
347 {
348   GtkApplicationWindow *window = user_data;
349
350   gtk_application_window_update_shell_shows_menubar (window, GTK_SETTINGS (object));
351   gtk_application_window_update_menubar (window);
352 }
353
354 static gchar **
355 gtk_application_window_list_actions (GActionGroup *group)
356 {
357   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
358
359   return g_action_group_list_actions (G_ACTION_GROUP (window->priv->actions));
360 }
361
362 static gboolean
363 gtk_application_window_query_action (GActionGroup        *group,
364                                      const gchar         *action_name,
365                                      gboolean            *enabled,
366                                      const GVariantType **parameter_type,
367                                      const GVariantType **state_type,
368                                      GVariant           **state_hint,
369                                      GVariant           **state)
370 {
371   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
372
373   return g_action_group_query_action (G_ACTION_GROUP (window->priv->actions),
374                                       action_name, enabled, parameter_type, state_type, state_hint, state);
375 }
376
377 static void
378 gtk_application_window_activate_action (GActionGroup *group,
379                                         const gchar  *action_name,
380                                         GVariant     *parameter)
381 {
382   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
383
384   return g_action_group_activate_action (G_ACTION_GROUP (window->priv->actions), action_name, parameter);
385 }
386
387 static void
388 gtk_application_window_change_action_state (GActionGroup *group,
389                                             const gchar  *action_name,
390                                             GVariant     *state)
391 {
392   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
393
394   return g_action_group_change_action_state (G_ACTION_GROUP (window->priv->actions), action_name, state);
395 }
396
397 static GAction *
398 gtk_application_window_lookup_action (GActionMap  *action_map,
399                                       const gchar *action_name)
400 {
401   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (action_map);
402
403   return g_action_map_lookup_action (G_ACTION_MAP (window->priv->actions), action_name);
404 }
405
406 static void
407 gtk_application_window_add_action (GActionMap *action_map,
408                                    GAction    *action)
409 {
410   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (action_map);
411
412   g_action_map_add_action (G_ACTION_MAP (window->priv->actions), action);
413 }
414
415 static void
416 gtk_application_window_remove_action (GActionMap  *action_map,
417                                       const gchar *action_name)
418 {
419   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (action_map);
420
421   g_action_map_remove_action (G_ACTION_MAP (window->priv->actions), action_name);
422 }
423
424 static void
425 gtk_application_window_group_iface_init (GActionGroupInterface *iface)
426 {
427   iface->list_actions = gtk_application_window_list_actions;
428   iface->query_action = gtk_application_window_query_action;
429   iface->activate_action = gtk_application_window_activate_action;
430   iface->change_action_state = gtk_application_window_change_action_state;
431 }
432
433 static void
434 gtk_application_window_map_iface_init (GActionMapInterface *iface)
435 {
436   iface->lookup_action = gtk_application_window_lookup_action;
437   iface->add_action = gtk_application_window_add_action;
438   iface->remove_action = gtk_application_window_remove_action;
439 }
440
441 G_DEFINE_TYPE_WITH_CODE (GtkApplicationWindow, gtk_application_window, GTK_TYPE_WINDOW,
442                          G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, gtk_application_window_group_iface_init)
443                          G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_MAP, gtk_application_window_map_iface_init))
444
445 enum {
446   PROP_0,
447   PROP_SHOW_MENUBAR,
448   N_PROPS
449 };
450 static GParamSpec *gtk_application_window_properties[N_PROPS];
451
452 static void
453 gtk_application_window_real_get_preferred_height (GtkWidget *widget,
454                                                   gint      *minimum_height,
455                                                   gint      *natural_height)
456 {
457   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
458
459   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
460     ->get_preferred_height (widget, minimum_height, natural_height);
461
462   if (window->priv->menubar != NULL)
463     {
464       gint menubar_min_height, menubar_nat_height;
465
466       gtk_widget_get_preferred_height (window->priv->menubar, &menubar_min_height, &menubar_nat_height);
467       *minimum_height += menubar_min_height;
468       *natural_height += menubar_nat_height;
469     }
470 }
471
472 static void
473 gtk_application_window_real_get_preferred_height_for_width (GtkWidget *widget,
474                                                             gint       width,
475                                                             gint      *minimum_height,
476                                                             gint      *natural_height)
477 {
478   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
479
480   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
481     ->get_preferred_height_for_width (widget, width, minimum_height, natural_height);
482
483   if (window->priv->menubar != NULL)
484     {
485       gint menubar_min_height, menubar_nat_height;
486
487       gtk_widget_get_preferred_height_for_width (window->priv->menubar, width, &menubar_min_height, &menubar_nat_height);
488       *minimum_height += menubar_min_height;
489       *natural_height += menubar_nat_height;
490     }
491 }
492
493 static void
494 gtk_application_window_real_get_preferred_width (GtkWidget *widget,
495                                                  gint      *minimum_width,
496                                                  gint      *natural_width)
497 {
498   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
499
500   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
501     ->get_preferred_width (widget, minimum_width, natural_width);
502
503   if (window->priv->menubar != NULL)
504     {
505       gint menubar_min_width, menubar_nat_width;
506
507       gtk_widget_get_preferred_width (window->priv->menubar, &menubar_min_width, &menubar_nat_width);
508       *minimum_width = MAX (*minimum_width, menubar_min_width);
509       *natural_width = MAX (*natural_width, menubar_nat_width);
510     }
511 }
512
513 static void
514 gtk_application_window_real_get_preferred_width_for_height (GtkWidget *widget,
515                                                             gint       height,
516                                                             gint      *minimum_width,
517                                                             gint      *natural_width)
518 {
519   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
520
521   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
522     ->get_preferred_width_for_height (widget, height, minimum_width, natural_width);
523
524   if (window->priv->menubar != NULL)
525     {
526       gint menubar_min_width, menubar_nat_width;
527
528       gtk_widget_get_preferred_width_for_height (window->priv->menubar, height, &menubar_min_width, &menubar_nat_width);
529       *minimum_width = MAX (*minimum_width, menubar_min_width);
530       *natural_width = MAX (*natural_width, menubar_nat_width);
531     }
532 }
533
534 static void
535 gtk_application_window_real_size_allocate (GtkWidget     *widget,
536                                            GtkAllocation *allocation)
537 {
538   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
539
540   if (window->priv->menubar != NULL)
541     {
542       GtkAllocation menubar_allocation = *allocation;
543       gint menubar_min_height, menubar_nat_height;
544       GtkWidget *child;
545
546       gtk_widget_get_preferred_height_for_width (window->priv->menubar, allocation->width, &menubar_min_height, &menubar_nat_height);
547
548       menubar_allocation.height = menubar_min_height;
549       gtk_widget_size_allocate (window->priv->menubar, &menubar_allocation);
550
551       child = gtk_bin_get_child (GTK_BIN (window));
552       if (child != NULL && gtk_widget_get_visible (child))
553         {
554           GtkAllocation child_allocation = *allocation;
555           gint border_width;
556
557           child_allocation.height = MAX (1, child_allocation.height - menubar_min_height);
558
559           border_width = gtk_container_get_border_width (GTK_CONTAINER (window));
560           child_allocation.x += border_width;
561           child_allocation.y += border_width + menubar_min_height;
562           child_allocation.width -= border_width * 2;
563           child_allocation.height -= border_width * 2 - menubar_min_height;
564           gtk_widget_size_allocate (child, &child_allocation);
565         }
566
567       gtk_widget_set_allocation (widget, allocation);
568     }
569   else
570     GTK_WIDGET_CLASS (gtk_application_window_parent_class)
571       ->size_allocate (widget, allocation);
572 }
573
574 static void
575 gtk_application_window_real_realize (GtkWidget *widget)
576 {
577   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
578   GtkApplication *application;
579   GtkSettings *settings;
580
581   application = gtk_window_get_application (GTK_WINDOW (window));
582   settings = gtk_widget_get_settings (widget);
583
584   g_signal_connect (settings, "notify::gtk-shell-shows-app-menu",
585                     G_CALLBACK (gtk_application_window_shell_shows_app_menu_changed), window);
586   g_signal_connect (settings, "notify::gtk-shell-shows-menubar",
587                     G_CALLBACK (gtk_application_window_shell_shows_menubar_changed), window);
588
589   if (window->priv->muxer == NULL)
590     {
591       GActionMuxer *muxer;
592
593       muxer = g_action_muxer_new ();
594       g_action_muxer_insert (muxer, "app", G_ACTION_GROUP (application));
595       g_action_muxer_insert (muxer, "win", G_ACTION_GROUP (window));
596       window->priv->muxer = G_ACTION_OBSERVABLE (muxer);
597     }
598
599   gtk_application_window_update_shell_shows_app_menu (window, settings);
600   gtk_application_window_update_shell_shows_menubar (window, settings);
601   gtk_application_window_update_menubar (window);
602   gtk_application_window_update_accels (window);
603
604   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
605     ->realize (widget);
606
607 #ifdef GDK_WINDOWING_X11
608   {
609     GdkWindow *gdkwindow;
610
611     gdkwindow = gtk_widget_get_window (GTK_WIDGET (window));
612
613     if (GDK_IS_X11_WINDOW (gdkwindow))
614       {
615         const gchar *unique_id;
616         const gchar *app_id;
617
618         gdk_x11_window_set_utf8_property (gdkwindow, "_DBUS_OBJECT_PATH", window->priv->object_path);
619
620         unique_id = g_dbus_connection_get_unique_name (window->priv->session);
621         gdk_x11_window_set_utf8_property (gdkwindow, "_DBUS_UNIQUE_NAME", unique_id);
622
623         app_id = g_application_get_application_id (G_APPLICATION (application));
624         gdk_x11_window_set_utf8_property (gdkwindow, "_DBUS_APPLICATION_ID", app_id);
625       }
626   }
627 #endif
628 }
629
630 static void
631 gtk_application_window_real_unrealize (GtkWidget *widget)
632 {
633   GtkSettings *settings;
634
635   settings = gtk_widget_get_settings (widget);
636
637   g_signal_handlers_disconnect_by_func (settings, gtk_application_window_shell_shows_app_menu_changed, widget);
638   g_signal_handlers_disconnect_by_func (settings, gtk_application_window_shell_shows_menubar_changed, widget);
639
640   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
641     ->unrealize (widget);
642 }
643
644 gboolean
645 gtk_application_window_publish (GtkApplicationWindow *window,
646                                 GDBusConnection      *session,
647                                 const gchar          *object_path)
648 {
649   g_assert (window->priv->session == NULL);
650   g_assert (window->priv->export_id == 0);
651   g_assert (window->priv->object_path == NULL);
652
653   window->priv->export_id = g_dbus_connection_export_action_group (session, object_path, G_ACTION_GROUP (window), NULL);
654
655   if (window->priv->export_id == 0)
656     return FALSE;
657
658   window->priv->session = session;
659   window->priv->object_path = g_strdup (object_path);
660
661   return TRUE;
662 }
663
664 void
665 gtk_application_window_unpublish (GtkApplicationWindow *window)
666 {
667   g_assert (window->priv->session != NULL);
668   g_assert (window->priv->export_id != 0);
669   g_assert (window->priv->object_path != NULL);
670
671   g_dbus_connection_unexport_action_group (window->priv->session, window->priv->export_id);
672   window->priv->session = NULL;
673   window->priv->export_id = 0;
674
675   g_free (window->priv->object_path);
676   window->priv->object_path = NULL;
677 }
678
679 static void
680 gtk_application_window_real_map (GtkWidget *widget)
681 {
682   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
683
684   /* XXX could eliminate this by tweaking gtk_window_map */
685   if (window->priv->menubar)
686     gtk_widget_map (window->priv->menubar);
687
688   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
689     ->map (widget);
690 }
691
692 static void
693 gtk_application_window_real_forall_internal (GtkContainer *container,
694                                              gboolean      include_internal,
695                                              GtkCallback   callback,
696                                              gpointer      user_data)
697 {
698   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (container);
699
700   if (window->priv->menubar)
701     callback (window->priv->menubar, user_data);
702
703   GTK_CONTAINER_CLASS (gtk_application_window_parent_class)
704     ->forall (container, include_internal, callback, user_data);
705 }
706
707
708 static void
709 gtk_application_window_get_property (GObject    *object,
710                                      guint       prop_id,
711                                      GValue     *value,
712                                      GParamSpec *pspec)
713 {
714   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (object);
715
716   switch (prop_id)
717     {
718     case PROP_SHOW_MENUBAR:
719       g_value_set_boolean (value, window->priv->show_menubar);
720       break;
721
722     default:
723       g_assert_not_reached ();
724     }
725 }
726
727 static void
728 gtk_application_window_set_property (GObject      *object,
729                                      guint         prop_id,
730                                      const GValue *value,
731                                      GParamSpec   *pspec)
732 {
733   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (object);
734
735   switch (prop_id)
736     {
737     case PROP_SHOW_MENUBAR:
738       gtk_application_window_set_show_menubar (window, g_value_get_boolean (value));
739       break;
740
741     default:
742       g_assert_not_reached ();
743     }
744 }
745
746 static void
747 gtk_application_window_dispose (GObject *object)
748 {
749   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (object);
750
751   if (window->priv->menubar)
752     {
753       gtk_widget_unparent (window->priv->menubar);
754       window->priv->menubar = NULL;
755     }
756
757   free_accel_closures (window);
758
759   g_clear_object (&window->priv->app_menu_section);
760   g_clear_object (&window->priv->menubar_section);
761   g_clear_object (&window->priv->actions);
762   g_clear_object (&window->priv->accels);
763   g_clear_object (&window->priv->muxer);
764
765   G_OBJECT_CLASS (gtk_application_window_parent_class)
766     ->dispose (object);
767 }
768
769 static void
770 gtk_application_window_init (GtkApplicationWindow *window)
771 {
772   window->priv = G_TYPE_INSTANCE_GET_PRIVATE (window, GTK_TYPE_APPLICATION_WINDOW, GtkApplicationWindowPrivate);
773
774   window->priv->actions = g_simple_action_group_new ();
775   window->priv->app_menu_section = g_menu_new ();
776   window->priv->menubar_section = g_menu_new ();
777   window->priv->accels = gtk_accel_group_new ();
778   gtk_window_add_accel_group (GTK_WINDOW (window), window->priv->accels);
779
780   /* window->priv->actions is the one and only ref on the group, so when
781    * we dispose, the action group will die, disconnecting all signals.
782    */
783   g_signal_connect_swapped (window->priv->actions, "action-added",
784                             G_CALLBACK (g_action_group_action_added), window);
785   g_signal_connect_swapped (window->priv->actions, "action-enabled-changed",
786                             G_CALLBACK (g_action_group_action_enabled_changed), window);
787   g_signal_connect_swapped (window->priv->actions, "action-state-changed",
788                             G_CALLBACK (g_action_group_action_state_changed), window);
789   g_signal_connect_swapped (window->priv->actions, "action-removed",
790                             G_CALLBACK (g_action_group_action_removed), window);
791 }
792
793 static void
794 gtk_application_window_class_init (GtkApplicationWindowClass *class)
795 {
796   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
797   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
798   GObjectClass *object_class = G_OBJECT_CLASS (class);
799
800   container_class->forall = gtk_application_window_real_forall_internal;
801   widget_class->get_preferred_height = gtk_application_window_real_get_preferred_height;
802   widget_class->get_preferred_height_for_width = gtk_application_window_real_get_preferred_height_for_width;
803   widget_class->get_preferred_width = gtk_application_window_real_get_preferred_width;
804   widget_class->get_preferred_width_for_height = gtk_application_window_real_get_preferred_width_for_height;
805   widget_class->size_allocate = gtk_application_window_real_size_allocate;
806   widget_class->realize = gtk_application_window_real_realize;
807   widget_class->unrealize = gtk_application_window_real_unrealize;
808   widget_class->map = gtk_application_window_real_map;
809   object_class->get_property = gtk_application_window_get_property;
810   object_class->set_property = gtk_application_window_set_property;
811   object_class->dispose = gtk_application_window_dispose;
812
813   /**
814    * GtkApplicationWindow:show-menubar:
815    *
816    * If this property is %TRUE, the window will display a menubar
817    * that includes the app menu and menubar, unless these are
818    * shown by the desktop shell. See g_application_set_app_menu()
819    * and g_application_set_menubar().
820    *
821    * If %FALSE, the window will not display a menubar, regardless
822    * of whether the desktop shell is showing the menus or not.
823    */
824   gtk_application_window_properties[PROP_SHOW_MENUBAR] =
825     g_param_spec_boolean ("show-menubar",
826                           P_("Show a menubar"),
827                           P_("TRUE if the window should show a "
828                              "menubar at the top of the window"),
829                           TRUE, G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
830   g_object_class_install_properties (object_class, N_PROPS, gtk_application_window_properties);
831   g_type_class_add_private (class, sizeof (GtkApplicationWindowPrivate));
832 }
833
834 /**
835  * gtk_application_window_new:
836  * @application: a #GtkApplication
837  *
838  * Creates a new #GtkApplicationWindow.
839  *
840  * Returns: a newly created #GtkApplicationWindow
841  *
842  * Since: 3.4
843  */
844 GtkWidget *
845 gtk_application_window_new (GtkApplication *application)
846 {
847   g_return_val_if_fail (GTK_IS_APPLICATION (application), NULL);
848
849   return g_object_new (GTK_TYPE_APPLICATION_WINDOW,
850                        "application", application,
851                        NULL);
852 }
853
854 /**
855  * gtk_application_window_get_show_menubar:
856  * @window: a #GtkApplicationWindow
857  *
858  * Returns whether the window will display a menubar for the app menu
859  * and menubar as needed.
860  *
861  * Returns: %TRUE if @window will display a menubar when needed
862  *
863  * Since: 3.4
864  */
865 gboolean
866 gtk_application_window_get_show_menubar (GtkApplicationWindow *window)
867 {
868   return window->priv->show_menubar;
869 }
870
871 /**
872  * gtk_application_window_set_show_menubar:
873  * @window: a #GtkApplicationWindow
874  * @show_menubar: whether to show a menubar when needed
875  *
876  * Sets whether the window will display a menubar for the app menu
877  * and menubar as needed.
878  *
879  * Since: 3.4
880  */
881 void
882 gtk_application_window_set_show_menubar (GtkApplicationWindow *window,
883                                          gboolean              show_menubar)
884 {
885   g_return_if_fail (GTK_IS_APPLICATION_WINDOW (window));
886
887   show_menubar = !!show_menubar;
888
889   if (window->priv->show_menubar != show_menubar)
890     {
891       window->priv->show_menubar = show_menubar;
892
893       gtk_application_window_update_menubar (window);
894
895       g_object_notify_by_pspec (G_OBJECT (window), gtk_application_window_properties[PROP_SHOW_MENUBAR]);
896     }
897 }