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