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