1 /*
2  * Copyright © 2011 Canonical Limited
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the licence, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Author: Ryan Lortie <desrt@desrt.ca>
18  */
19 
20 #include "config.h"
21 
22 #include "gtkapplicationwindow.h"
23 
24 #include "gtkapplicationprivate.h"
25 #include "gtkwidgetprivate.h"
26 #include "gtkwindowprivate.h"
27 #include "gtkheaderbar.h"
28 #include "gtkpopovermenubar.h"
29 #include "gtkintl.h"
30 #include "gtksettings.h"
31 #include "gtkshortcutswindowprivate.h"
32 #include "gtktooltipprivate.h"
33 
34 #if defined(HAVE_GIO_UNIX) && !defined(__APPLE__)
35 #include <gio/gdesktopappinfo.h>
36 #endif
37 
38 /**
39  * GtkApplicationWindow:
40  *
41  * `GtkApplicationWindow` is a `GtkWindow` subclass that integrates with
42  * `GtkApplication`.
43  *
44  * Notably, `GtkApplicationWindow` can handle an application menubar.
45  *
46  * This class implements the `GActionGroup` and `GActionMap` interfaces,
47  * to let you add window-specific actions that will be exported by the
48  * associated [class@Gtk.Application], together with its application-wide
49  * actions. Window-specific actions are prefixed with the “win.”
50  * prefix and application-wide actions are prefixed with the “app.”
51  * prefix. Actions must be addressed with the prefixed name when
52  * referring to them from a `GMenuModel`.
53  *
54  * Note that widgets that are placed inside a `GtkApplicationWindow`
55  * can also activate these actions, if they implement the
56  * [iface@Gtk.Actionable] interface.
57  *
58  * The settings [property@Gtk.Settings:gtk-shell-shows-app-menu] and
59  * [property@Gtk.Settings:gtk-shell-shows-menubar] tell GTK whether the
60  * desktop environment is showing the application menu and menubar
61  * models outside the application as part of the desktop shell.
62  * For instance, on OS X, both menus will be displayed remotely;
63  * on Windows neither will be.
64  *
65  * If the desktop environment does not display the menubar, then
66  * `GtkApplicationWindow` will automatically show a menubar for it.
67  * This behaviour can be overridden with the
68  * [property@Gtk.ApplicationWindow:show-menubar] property. If the
69  * desktop environment does not display the application menu, then
70  * it will automatically be included in the menubar or in the windows
71  * client-side decorations.
72  *
73  * See [class@Gtk.PopoverMenu] for information about the XML language
74  * used by `GtkBuilder` for menu models.
75  *
76  * See also: [method@Gtk.Application.set_menubar].
77  *
78  * ## A GtkApplicationWindow with a menubar
79  *
80  * The code sample below shows how to set up a `GtkApplicationWindow`
81  * with a menu bar defined on the [class@Gtk.Application]:
82  *
83  * ```c
84  * GtkApplication *app = gtk_application_new ("org.gtk.test", 0);
85  *
86  * GtkBuilder *builder = gtk_builder_new_from_string (
87  *     "<interface>"
88  *     "  <menu id='menubar'>"
89  *     "    <submenu>"
90  *     "      <attribute name='label' translatable='yes'>_Edit</attribute>"
91  *     "      <item>"
92  *     "        <attribute name='label' translatable='yes'>_Copy</attribute>"
93  *     "        <attribute name='action'>win.copy</attribute>"
94  *     "      </item>"
95  *     "      <item>"
96  *     "        <attribute name='label' translatable='yes'>_Paste</attribute>"
97  *     "        <attribute name='action'>win.paste</attribute>"
98  *     "      </item>"
99  *     "    </submenu>"
100  *     "  </menu>"
101  *     "</interface>",
102  *     -1);
103  *
104  * GMenuModel *menubar = G_MENU_MODEL (gtk_builder_get_object (builder, "menubar"));
105  * gtk_application_set_menubar (GTK_APPLICATION (app), menubar);
106  * g_object_unref (builder);
107  *
108  * // ...
109  *
110  * GtkWidget *window = gtk_application_window_new (app);
111  * ```
112  */
113 
114 typedef GSimpleActionGroupClass GtkApplicationWindowActionsClass;
115 typedef struct
116 {
117   GSimpleActionGroup parent_instance;
118   GtkWindow *window;
119 } GtkApplicationWindowActions;
120 
121 static GType gtk_application_window_actions_get_type   (void);
122 static void  gtk_application_window_actions_iface_init (GRemoteActionGroupInterface *iface);
G_DEFINE_TYPE_WITH_CODE(GtkApplicationWindowActions,gtk_application_window_actions,G_TYPE_SIMPLE_ACTION_GROUP,G_IMPLEMENT_INTERFACE (G_TYPE_REMOTE_ACTION_GROUP,gtk_application_window_actions_iface_init))123 G_DEFINE_TYPE_WITH_CODE (GtkApplicationWindowActions, gtk_application_window_actions, G_TYPE_SIMPLE_ACTION_GROUP,
124                          G_IMPLEMENT_INTERFACE (G_TYPE_REMOTE_ACTION_GROUP, gtk_application_window_actions_iface_init))
125 
126 static void
127 gtk_application_window_actions_activate_action_full (GRemoteActionGroup *remote,
128                                                      const char         *action_name,
129                                                      GVariant           *parameter,
130                                                      GVariant           *platform_data)
131 {
132   GtkApplicationWindowActions *actions = (GtkApplicationWindowActions *) remote;
133   GApplication *application;
134   GApplicationClass *class;
135 
136   application = G_APPLICATION (gtk_window_get_application (actions->window));
137   class = G_APPLICATION_GET_CLASS (application);
138 
139   class->before_emit (application, platform_data);
140   g_action_group_activate_action (G_ACTION_GROUP (actions), action_name, parameter);
141   class->after_emit (application, platform_data);
142 }
143 
144 static void
gtk_application_window_actions_change_action_state_full(GRemoteActionGroup * remote,const char * action_name,GVariant * value,GVariant * platform_data)145 gtk_application_window_actions_change_action_state_full (GRemoteActionGroup *remote,
146                                                          const char         *action_name,
147                                                          GVariant           *value,
148                                                          GVariant           *platform_data)
149 {
150   GtkApplicationWindowActions *actions = (GtkApplicationWindowActions *) remote;
151   GApplication *application;
152   GApplicationClass *class;
153 
154   application = G_APPLICATION (gtk_window_get_application (actions->window));
155   class = G_APPLICATION_GET_CLASS (application);
156 
157   class->before_emit (application, platform_data);
158   g_action_group_change_action_state (G_ACTION_GROUP (actions), action_name, value);
159   class->after_emit (application, platform_data);
160 }
161 
162 static void
gtk_application_window_actions_init(GtkApplicationWindowActions * actions)163 gtk_application_window_actions_init (GtkApplicationWindowActions *actions)
164 {
165 }
166 
167 static void
gtk_application_window_actions_iface_init(GRemoteActionGroupInterface * iface)168 gtk_application_window_actions_iface_init (GRemoteActionGroupInterface *iface)
169 {
170   iface->activate_action_full = gtk_application_window_actions_activate_action_full;
171   iface->change_action_state_full = gtk_application_window_actions_change_action_state_full;
172 }
173 
174 static void
gtk_application_window_actions_class_init(GtkApplicationWindowActionsClass * class)175 gtk_application_window_actions_class_init (GtkApplicationWindowActionsClass *class)
176 {
177 }
178 
179 static GSimpleActionGroup *
gtk_application_window_actions_new(GtkApplicationWindow * window)180 gtk_application_window_actions_new (GtkApplicationWindow *window)
181 {
182   GtkApplicationWindowActions *actions;
183 
184   actions = g_object_new (gtk_application_window_actions_get_type (), NULL);
185   actions->window = GTK_WINDOW (window);
186 
187   return G_SIMPLE_ACTION_GROUP (actions);
188 }
189 
190 /* Now onto GtkApplicationWindow... */
191 
192 typedef struct _GtkApplicationWindowPrivate GtkApplicationWindowPrivate;
193 struct _GtkApplicationWindowPrivate
194 {
195   GSimpleActionGroup *actions;
196   GtkWidget *menubar;
197 
198   gboolean show_menubar;
199   guint id;
200   GMenu *menubar_section;
201 
202   GtkShortcutsWindow *help_overlay;
203 };
204 
205 static void gtk_application_window_group_iface_init (GActionGroupInterface *iface);
206 static void gtk_application_window_map_iface_init (GActionMapInterface *iface);
207 
G_DEFINE_TYPE_WITH_CODE(GtkApplicationWindow,gtk_application_window,GTK_TYPE_WINDOW,G_ADD_PRIVATE (GtkApplicationWindow)G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP,gtk_application_window_group_iface_init)G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_MAP,gtk_application_window_map_iface_init))208 G_DEFINE_TYPE_WITH_CODE (GtkApplicationWindow, gtk_application_window, GTK_TYPE_WINDOW,
209                          G_ADD_PRIVATE (GtkApplicationWindow)
210                          G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, gtk_application_window_group_iface_init)
211                          G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_MAP, gtk_application_window_map_iface_init))
212 
213 static void
214 gtk_application_window_update_menubar (GtkApplicationWindow *window)
215 {
216   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
217   gboolean should_have_menubar;
218   gboolean have_menubar;
219 
220   have_menubar = priv->menubar != NULL;
221 
222   should_have_menubar = priv->show_menubar &&
223                         g_menu_model_get_n_items (G_MENU_MODEL (priv->menubar_section));
224 
225   if (have_menubar && !should_have_menubar)
226     {
227       gtk_widget_unparent (priv->menubar);
228       priv->menubar = NULL;
229     }
230 
231   if (!have_menubar && should_have_menubar)
232     {
233       GMenu *combined;
234 
235       combined = g_menu_new ();
236       g_menu_append_section (combined, NULL, G_MENU_MODEL (priv->menubar_section));
237 
238       priv->menubar = gtk_popover_menu_bar_new_from_model (G_MENU_MODEL (combined));
239       gtk_widget_set_parent (priv->menubar, GTK_WIDGET (window));
240       g_object_unref (combined);
241     }
242 }
243 
244 static void
gtk_application_window_update_shell_shows_menubar(GtkApplicationWindow * window,GtkSettings * settings)245 gtk_application_window_update_shell_shows_menubar (GtkApplicationWindow *window,
246                                                    GtkSettings          *settings)
247 {
248   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
249   gboolean shown_by_shell;
250 
251   g_object_get (settings, "gtk-shell-shows-menubar", &shown_by_shell, NULL);
252 
253   if (shown_by_shell)
254     {
255       /* the shell shows it, so don't show it locally */
256       if (g_menu_model_get_n_items (G_MENU_MODEL (priv->menubar_section)) != 0)
257         g_menu_remove (priv->menubar_section, 0);
258     }
259   else
260     {
261       /* the shell does not show it, so make sure we show it */
262       if (g_menu_model_get_n_items (G_MENU_MODEL (priv->menubar_section)) == 0)
263         {
264           GMenuModel *menubar = NULL;
265 
266           if (gtk_window_get_application (GTK_WINDOW (window)) != NULL)
267             menubar = gtk_application_get_menubar (gtk_window_get_application (GTK_WINDOW (window)));
268 
269           if (menubar != NULL)
270             g_menu_append_section (priv->menubar_section, NULL, menubar);
271         }
272     }
273 }
274 
275 static void
gtk_application_window_shell_shows_menubar_changed(GObject * object,GParamSpec * pspec,gpointer user_data)276 gtk_application_window_shell_shows_menubar_changed (GObject    *object,
277                                                     GParamSpec *pspec,
278                                                     gpointer    user_data)
279 {
280   GtkApplicationWindow *window = user_data;
281 
282   gtk_application_window_update_shell_shows_menubar (window, GTK_SETTINGS (object));
283   gtk_application_window_update_menubar (window);
284 }
285 
286 static char **
gtk_application_window_list_actions(GActionGroup * group)287 gtk_application_window_list_actions (GActionGroup *group)
288 {
289   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
290   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
291 
292   /* may be NULL after dispose has run */
293   if (!priv->actions)
294     return g_new0 (char *, 0 + 1);
295 
296   return g_action_group_list_actions (G_ACTION_GROUP (priv->actions));
297 }
298 
299 static gboolean
gtk_application_window_query_action(GActionGroup * group,const char * action_name,gboolean * enabled,const GVariantType ** parameter_type,const GVariantType ** state_type,GVariant ** state_hint,GVariant ** state)300 gtk_application_window_query_action (GActionGroup        *group,
301                                      const char          *action_name,
302                                      gboolean            *enabled,
303                                      const GVariantType **parameter_type,
304                                      const GVariantType **state_type,
305                                      GVariant           **state_hint,
306                                      GVariant           **state)
307 {
308   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
309   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
310 
311   if (!priv->actions)
312     return FALSE;
313 
314   return g_action_group_query_action (G_ACTION_GROUP (priv->actions),
315                                       action_name, enabled, parameter_type, state_type, state_hint, state);
316 }
317 
318 static void
gtk_application_window_activate_action(GActionGroup * group,const char * action_name,GVariant * parameter)319 gtk_application_window_activate_action (GActionGroup *group,
320                                         const char   *action_name,
321                                         GVariant     *parameter)
322 {
323   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
324   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
325 
326   if (!priv->actions)
327     return;
328 
329   g_action_group_activate_action (G_ACTION_GROUP (priv->actions), action_name, parameter);
330 }
331 
332 static void
gtk_application_window_change_action_state(GActionGroup * group,const char * action_name,GVariant * state)333 gtk_application_window_change_action_state (GActionGroup *group,
334                                             const char   *action_name,
335                                             GVariant     *state)
336 {
337   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
338   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
339 
340   if (!priv->actions)
341     return;
342 
343   g_action_group_change_action_state (G_ACTION_GROUP (priv->actions), action_name, state);
344 }
345 
346 static GAction *
gtk_application_window_lookup_action(GActionMap * action_map,const char * action_name)347 gtk_application_window_lookup_action (GActionMap  *action_map,
348                                       const char *action_name)
349 {
350   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (action_map);
351   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
352 
353   if (!priv->actions)
354     return NULL;
355 
356   return g_action_map_lookup_action (G_ACTION_MAP (priv->actions), action_name);
357 }
358 
359 static void
gtk_application_window_add_action(GActionMap * action_map,GAction * action)360 gtk_application_window_add_action (GActionMap *action_map,
361                                    GAction    *action)
362 {
363   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (action_map);
364   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
365 
366   if (!priv->actions)
367     return;
368 
369   g_action_map_add_action (G_ACTION_MAP (priv->actions), action);
370 }
371 
372 static void
gtk_application_window_remove_action(GActionMap * action_map,const char * action_name)373 gtk_application_window_remove_action (GActionMap  *action_map,
374                                       const char *action_name)
375 {
376   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (action_map);
377   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
378 
379   if (!priv->actions)
380     return;
381 
382   g_action_map_remove_action (G_ACTION_MAP (priv->actions), action_name);
383 }
384 
385 static void
gtk_application_window_group_iface_init(GActionGroupInterface * iface)386 gtk_application_window_group_iface_init (GActionGroupInterface *iface)
387 {
388   iface->list_actions = gtk_application_window_list_actions;
389   iface->query_action = gtk_application_window_query_action;
390   iface->activate_action = gtk_application_window_activate_action;
391   iface->change_action_state = gtk_application_window_change_action_state;
392 }
393 
394 static void
gtk_application_window_map_iface_init(GActionMapInterface * iface)395 gtk_application_window_map_iface_init (GActionMapInterface *iface)
396 {
397   iface->lookup_action = gtk_application_window_lookup_action;
398   iface->add_action = gtk_application_window_add_action;
399   iface->remove_action = gtk_application_window_remove_action;
400 }
401 
402 enum {
403   PROP_0,
404   PROP_SHOW_MENUBAR,
405   N_PROPS
406 };
407 static GParamSpec *gtk_application_window_properties[N_PROPS];
408 
409 static void
gtk_application_window_measure(GtkWidget * widget,GtkOrientation orientation,int for_size,int * minimum,int * natural,int * minimum_baseline,int * natural_baseline)410 gtk_application_window_measure (GtkWidget      *widget,
411                                 GtkOrientation  orientation,
412                                 int             for_size,
413                                 int            *minimum,
414                                 int            *natural,
415                                 int            *minimum_baseline,
416                                 int            *natural_baseline)
417 {
418   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
419   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
420 
421   GTK_WIDGET_CLASS (gtk_application_window_parent_class)->measure (widget,
422                                                                    orientation,
423                                                                    for_size,
424                                                                    minimum, natural,
425                                                                    minimum_baseline, natural_baseline);
426 
427   if (priv->menubar != NULL)
428     {
429       int menubar_min, menubar_nat;
430 
431       if (orientation == GTK_ORIENTATION_HORIZONTAL)
432         {
433           int menubar_height = 0;
434 
435           gtk_widget_measure (priv->menubar, GTK_ORIENTATION_VERTICAL,
436                               for_size, &menubar_height, NULL, NULL, NULL);
437 
438           GTK_WIDGET_CLASS (gtk_application_window_parent_class)->measure (widget,
439                                                                            orientation,
440                                                                            for_size - menubar_height,
441                                                                            minimum, natural,
442                                                                            minimum_baseline, natural_baseline);
443 
444 
445           gtk_widget_measure (priv->menubar, orientation, menubar_height, &menubar_min, &menubar_nat, NULL, NULL);
446 
447           *minimum = MAX (*minimum, menubar_min);
448           *natural = MAX (*natural, menubar_nat);
449 
450         }
451       else /* VERTICAL */
452         {
453           gtk_widget_measure (priv->menubar, orientation, for_size, &menubar_min, &menubar_nat, NULL, NULL);
454           *minimum += menubar_min;
455           *natural += menubar_nat;
456         }
457     }
458 }
459 
460 static void
gtk_application_window_real_size_allocate(GtkWidget * widget,int width,int height,int baseline)461 gtk_application_window_real_size_allocate (GtkWidget *widget,
462                                            int        width,
463                                            int        height,
464                                            int        baseline)
465 {
466   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
467   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
468 
469   if (priv->menubar != NULL)
470     {
471       GtkAllocation menubar_allocation;
472       GtkAllocation child_allocation;
473       int menubar_height;
474       GtkWidget *child;
475 
476       _gtk_window_set_allocation (GTK_WINDOW (widget), width, height, &child_allocation);
477       menubar_allocation = child_allocation;
478 
479       gtk_widget_measure (priv->menubar, GTK_ORIENTATION_VERTICAL,
480                           menubar_allocation.width,
481                           &menubar_height, NULL, NULL, NULL);
482 
483       menubar_allocation.height = menubar_height;
484       gtk_widget_size_allocate  (priv->menubar, &menubar_allocation, baseline);
485 
486       child_allocation.y += menubar_height;
487       child_allocation.height -= menubar_height;
488       child = gtk_window_get_child (GTK_WINDOW (window));
489       if (child != NULL && gtk_widget_get_visible (child))
490         gtk_widget_size_allocate (child, &child_allocation, baseline);
491 
492       gtk_tooltip_maybe_allocate (GTK_NATIVE (widget));
493     }
494   else
495     GTK_WIDGET_CLASS (gtk_application_window_parent_class)->size_allocate (widget,
496                                                                            width,
497                                                                            height,
498                                                                            baseline);
499 }
500 
501 static void
gtk_application_window_real_realize(GtkWidget * widget)502 gtk_application_window_real_realize (GtkWidget *widget)
503 {
504   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
505   GtkSettings *settings;
506 
507   settings = gtk_widget_get_settings (widget);
508 
509   g_signal_connect (settings, "notify::gtk-shell-shows-menubar",
510                     G_CALLBACK (gtk_application_window_shell_shows_menubar_changed), window);
511 
512   GTK_WIDGET_CLASS (gtk_application_window_parent_class)->realize (widget);
513 
514   gtk_application_window_update_shell_shows_menubar (window, settings);
515   gtk_application_window_update_menubar (window);
516 }
517 
518 static void
gtk_application_window_real_unrealize(GtkWidget * widget)519 gtk_application_window_real_unrealize (GtkWidget *widget)
520 {
521   GtkSettings *settings;
522 
523   settings = gtk_widget_get_settings (widget);
524 
525   g_signal_handlers_disconnect_by_func (settings, gtk_application_window_shell_shows_menubar_changed, widget);
526 
527   GTK_WIDGET_CLASS (gtk_application_window_parent_class)->unrealize (widget);
528 }
529 
530 GActionGroup *
gtk_application_window_get_action_group(GtkApplicationWindow * window)531 gtk_application_window_get_action_group (GtkApplicationWindow *window)
532 {
533   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
534   return G_ACTION_GROUP (priv->actions);
535 }
536 
537 static void
gtk_application_window_real_map(GtkWidget * widget)538 gtk_application_window_real_map (GtkWidget *widget)
539 {
540   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
541   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
542 
543   /* XXX could eliminate this by tweaking gtk_window_map */
544   if (priv->menubar)
545     gtk_widget_map (priv->menubar);
546 
547   GTK_WIDGET_CLASS (gtk_application_window_parent_class)->map (widget);
548 }
549 
550 static void
gtk_application_window_real_unmap(GtkWidget * widget)551 gtk_application_window_real_unmap (GtkWidget *widget)
552 {
553   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
554   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
555 
556   /* XXX could eliminate this by tweaking gtk_window_unmap */
557   if (priv->menubar)
558     gtk_widget_unmap (priv->menubar);
559 
560   GTK_WIDGET_CLASS (gtk_application_window_parent_class)->unmap (widget);
561 }
562 
563 static void
gtk_application_window_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)564 gtk_application_window_get_property (GObject    *object,
565                                      guint       prop_id,
566                                      GValue     *value,
567                                      GParamSpec *pspec)
568 {
569   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (object);
570   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
571 
572   switch (prop_id)
573     {
574     case PROP_SHOW_MENUBAR:
575       g_value_set_boolean (value, priv->show_menubar);
576       break;
577 
578     default:
579       g_assert_not_reached ();
580     }
581 }
582 
583 static void
gtk_application_window_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)584 gtk_application_window_set_property (GObject      *object,
585                                      guint         prop_id,
586                                      const GValue *value,
587                                      GParamSpec   *pspec)
588 {
589   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (object);
590 
591   switch (prop_id)
592     {
593     case PROP_SHOW_MENUBAR:
594       gtk_application_window_set_show_menubar (window, g_value_get_boolean (value));
595       break;
596 
597     default:
598       g_assert_not_reached ();
599     }
600 }
601 
602 static void
gtk_application_window_dispose(GObject * object)603 gtk_application_window_dispose (GObject *object)
604 {
605   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (object);
606   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
607 
608   if (priv->menubar)
609     {
610       gtk_widget_unparent (priv->menubar);
611       priv->menubar = NULL;
612     }
613 
614   g_clear_object (&priv->menubar_section);
615 
616   if (priv->help_overlay)
617     {
618       gtk_window_destroy (GTK_WINDOW (priv->help_overlay));
619       g_clear_object (&priv->help_overlay);
620     }
621 
622   G_OBJECT_CLASS (gtk_application_window_parent_class)->dispose (object);
623 
624   /* We do this below the chain-up above to give us a chance to be
625    * removed from the GtkApplication (which is done in the dispose
626    * handler of GtkWindow).
627    *
628    * That reduces our chances of being watched as a GActionGroup from a
629    * muxer constructed by GtkApplication.
630    */
631   g_clear_object (&priv->actions);
632 }
633 
634 static void
gtk_application_window_init(GtkApplicationWindow * window)635 gtk_application_window_init (GtkApplicationWindow *window)
636 {
637   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
638 
639   priv->actions = gtk_application_window_actions_new (window);
640   priv->menubar_section = g_menu_new ();
641 
642   gtk_widget_insert_action_group (GTK_WIDGET (window), "win", G_ACTION_GROUP (priv->actions));
643 
644   /* priv->actions is the one and only ref on the group, so when
645    * we dispose, the action group will die, disconnecting all signals.
646    */
647   g_signal_connect_swapped (priv->actions, "action-added",
648                             G_CALLBACK (g_action_group_action_added), window);
649   g_signal_connect_swapped (priv->actions, "action-enabled-changed",
650                             G_CALLBACK (g_action_group_action_enabled_changed), window);
651   g_signal_connect_swapped (priv->actions, "action-state-changed",
652                             G_CALLBACK (g_action_group_action_state_changed), window);
653   g_signal_connect_swapped (priv->actions, "action-removed",
654                             G_CALLBACK (g_action_group_action_removed), window);
655 }
656 
657 static void
gtk_application_window_class_init(GtkApplicationWindowClass * class)658 gtk_application_window_class_init (GtkApplicationWindowClass *class)
659 {
660   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
661   GObjectClass *object_class = G_OBJECT_CLASS (class);
662 
663   widget_class->measure = gtk_application_window_measure;
664   widget_class->size_allocate = gtk_application_window_real_size_allocate;
665   widget_class->realize = gtk_application_window_real_realize;
666   widget_class->unrealize = gtk_application_window_real_unrealize;
667   widget_class->map = gtk_application_window_real_map;
668   widget_class->unmap = gtk_application_window_real_unmap;
669 
670   object_class->get_property = gtk_application_window_get_property;
671   object_class->set_property = gtk_application_window_set_property;
672   object_class->dispose = gtk_application_window_dispose;
673 
674   /**
675    * GtkApplicationWindow:show-menubar: (attributes org.gtk.Property.get=gtk_application_window_get_show_menubar org.gtk.Property.set=gtk_application_window_set_show_menubar)
676    *
677    * If this property is %TRUE, the window will display a menubar
678    * unless it is shown by the desktop shell.
679    *
680    * See [method@Gtk.Application.set_menubar].
681    *
682    * If %FALSE, the window will not display a menubar, regardless
683    * of whether the desktop shell is showing it or not.
684    */
685   gtk_application_window_properties[PROP_SHOW_MENUBAR] =
686     g_param_spec_boolean ("show-menubar",
687                           P_("Show a menubar"),
688                           P_("TRUE if the window should show a "
689                              "menubar at the top of the window"),
690                           FALSE, G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
691   g_object_class_install_properties (object_class, N_PROPS, gtk_application_window_properties);
692 }
693 
694 /**
695  * gtk_application_window_new:
696  * @application: a `GtkApplication`
697  *
698  * Creates a new `GtkApplicationWindow`.
699  *
700  * Returns: a newly created `GtkApplicationWindow`
701  */
702 GtkWidget *
gtk_application_window_new(GtkApplication * application)703 gtk_application_window_new (GtkApplication *application)
704 {
705   g_return_val_if_fail (GTK_IS_APPLICATION (application), NULL);
706 
707   return g_object_new (GTK_TYPE_APPLICATION_WINDOW,
708                        "application", application,
709                        NULL);
710 }
711 
712 /**
713  * gtk_application_window_get_show_menubar: (attributes org.gtk.Method.get_property=show-menubar)
714  * @window: a `GtkApplicationWindow`
715  *
716  * Returns whether the window will display a menubar for the app menu
717  * and menubar as needed.
718  *
719  * Returns: %TRUE if @window will display a menubar when needed
720  */
721 gboolean
gtk_application_window_get_show_menubar(GtkApplicationWindow * window)722 gtk_application_window_get_show_menubar (GtkApplicationWindow *window)
723 {
724   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
725   return priv->show_menubar;
726 }
727 
728 /**
729  * gtk_application_window_set_show_menubar: (attributes org.gtk.Method.set_property=show-menubar)
730  * @window: a `GtkApplicationWindow`
731  * @show_menubar: whether to show a menubar when needed
732  *
733  * Sets whether the window will display a menubar for the app menu
734  * and menubar as needed.
735  */
736 void
gtk_application_window_set_show_menubar(GtkApplicationWindow * window,gboolean show_menubar)737 gtk_application_window_set_show_menubar (GtkApplicationWindow *window,
738                                          gboolean              show_menubar)
739 {
740   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
741   g_return_if_fail (GTK_IS_APPLICATION_WINDOW (window));
742 
743   show_menubar = !!show_menubar;
744 
745   if (priv->show_menubar != show_menubar)
746     {
747       priv->show_menubar = show_menubar;
748 
749       gtk_application_window_update_menubar (window);
750 
751       g_object_notify_by_pspec (G_OBJECT (window), gtk_application_window_properties[PROP_SHOW_MENUBAR]);
752     }
753 }
754 
755 /**
756  * gtk_application_window_get_id:
757  * @window: a `GtkApplicationWindow`
758  *
759  * Returns the unique ID of the window.
760  *
761  *  If the window has not yet been added to a `GtkApplication`, returns `0`.
762  *
763  * Returns: the unique ID for @window, or `0` if the window
764  *   has not yet been added to a `GtkApplication`
765  */
766 guint
gtk_application_window_get_id(GtkApplicationWindow * window)767 gtk_application_window_get_id (GtkApplicationWindow *window)
768 {
769   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
770   g_return_val_if_fail (GTK_IS_APPLICATION_WINDOW (window), 0);
771 
772   return priv->id;
773 }
774 
775 void
gtk_application_window_set_id(GtkApplicationWindow * window,guint id)776 gtk_application_window_set_id (GtkApplicationWindow *window,
777                                guint                 id)
778 {
779   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
780   g_return_if_fail (GTK_IS_APPLICATION_WINDOW (window));
781   priv->id = id;
782 }
783 
784 static void
show_help_overlay(GSimpleAction * action,GVariant * parameter,gpointer user_data)785 show_help_overlay (GSimpleAction *action,
786                    GVariant      *parameter,
787                    gpointer       user_data)
788 {
789   GtkApplicationWindow *window = user_data;
790   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
791 
792   if (priv->help_overlay)
793     gtk_widget_show (GTK_WIDGET (priv->help_overlay));
794 }
795 
796 /**
797  * gtk_application_window_set_help_overlay:
798  * @window: a `GtkApplicationWindow`
799  * @help_overlay: (nullable): a `GtkShortcutsWindow`
800  *
801  * Associates a shortcuts window with the application window.
802  *
803  * Additionally, sets up an action with the name
804  * `win.show-help-overlay` to present it.
805  *
806  * @window takes responsibility for destroying @help_overlay.
807  */
808 void
gtk_application_window_set_help_overlay(GtkApplicationWindow * window,GtkShortcutsWindow * help_overlay)809 gtk_application_window_set_help_overlay (GtkApplicationWindow *window,
810                                          GtkShortcutsWindow   *help_overlay)
811 {
812   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
813   g_return_if_fail (GTK_IS_APPLICATION_WINDOW (window));
814   g_return_if_fail (help_overlay == NULL || GTK_IS_SHORTCUTS_WINDOW (help_overlay));
815 
816   if (priv->help_overlay)
817     gtk_window_destroy (GTK_WINDOW (priv->help_overlay));
818   g_set_object (&priv->help_overlay, help_overlay);
819 
820   if (!priv->help_overlay)
821     return;
822 
823   gtk_window_set_modal (GTK_WINDOW (help_overlay), TRUE);
824   gtk_window_set_hide_on_close (GTK_WINDOW (help_overlay), TRUE);
825   gtk_window_set_transient_for (GTK_WINDOW (help_overlay), GTK_WINDOW (window));
826   gtk_shortcuts_window_set_window (help_overlay, GTK_WINDOW (window));
827 
828   if (!g_action_map_lookup_action (G_ACTION_MAP (priv->actions), "show-help-overlay"))
829     {
830       GSimpleAction *action;
831 
832       action = g_simple_action_new ("show-help-overlay", NULL);
833       g_signal_connect (action, "activate", G_CALLBACK (show_help_overlay), window);
834 
835       g_action_map_add_action (G_ACTION_MAP (priv->actions), G_ACTION (action));
836       g_object_unref (G_OBJECT (action));
837     }
838 }
839 
840 /**
841  * gtk_application_window_get_help_overlay:
842  * @window: a `GtkApplicationWindow`
843  *
844  * Gets the `GtkShortcutsWindow` that is associated with @window.
845  *
846  * See [method@Gtk.ApplicationWindow.set_help_overlay].
847  *
848  * Returns: (transfer none) (nullable): the help overlay associated
849  *   with @window
850  */
851 GtkShortcutsWindow *
gtk_application_window_get_help_overlay(GtkApplicationWindow * window)852 gtk_application_window_get_help_overlay (GtkApplicationWindow *window)
853 {
854   GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (window);
855   g_return_val_if_fail (GTK_IS_APPLICATION_WINDOW (window), NULL);
856 
857   return priv->help_overlay;
858 }
859