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
114typedef GSimpleActionGroupClass GtkApplicationWindowActionsClass;
115typedef struct
116{
117 GSimpleActionGroup parent_instance;
118 GtkWindow *window;
119} GtkApplicationWindowActions;
120
121static GType gtk_application_window_actions_get_type (void);
122static void gtk_application_window_actions_iface_init (GRemoteActionGroupInterface *iface);
123G_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
126static void
127gtk_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
144static void
145gtk_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
162static void
163gtk_application_window_actions_init (GtkApplicationWindowActions *actions)
164{
165}
166
167static void
168gtk_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
174static void
175gtk_application_window_actions_class_init (GtkApplicationWindowActionsClass *class)
176{
177}
178
179static GSimpleActionGroup *
180gtk_application_window_actions_new (GtkApplicationWindow *window)
181{
182 GtkApplicationWindowActions *actions;
183
184 actions = g_object_new (object_type: 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
192typedef struct _GtkApplicationWindowPrivate GtkApplicationWindowPrivate;
193struct _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
205static void gtk_application_window_group_iface_init (GActionGroupInterface *iface);
206static void gtk_application_window_map_iface_init (GActionMapInterface *iface);
207
208G_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
213static void
214gtk_application_window_update_menubar (GtkApplicationWindow *window)
215{
216 GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (self: 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 (widget: 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 (menu: 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 (widget: priv->menubar, GTK_WIDGET (window));
240 g_object_unref (object: combined);
241 }
242}
243
244static void
245gtk_application_window_update_shell_shows_menubar (GtkApplicationWindow *window,
246 GtkSettings *settings)
247{
248 GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (self: window);
249 gboolean shown_by_shell;
250
251 g_object_get (object: settings, first_property_name: "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 (menu: priv->menubar_section, position: 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 (application: gtk_window_get_application (GTK_WINDOW (window)));
268
269 if (menubar != NULL)
270 g_menu_append_section (menu: priv->menubar_section, NULL, section: menubar);
271 }
272 }
273}
274
275static void
276gtk_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
286static char **
287gtk_application_window_list_actions (GActionGroup *group)
288{
289 GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
290 GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (self: 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
299static gboolean
300gtk_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 (self: 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
318static void
319gtk_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 (self: 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
332static void
333gtk_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 (self: window);
339
340 if (!priv->actions)
341 return;
342
343 g_action_group_change_action_state (G_ACTION_GROUP (priv->actions), action_name, value: state);
344}
345
346static GAction *
347gtk_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 (self: 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
359static void
360gtk_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 (self: window);
365
366 if (!priv->actions)
367 return;
368
369 g_action_map_add_action (G_ACTION_MAP (priv->actions), action);
370}
371
372static void
373gtk_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 (self: window);
378
379 if (!priv->actions)
380 return;
381
382 g_action_map_remove_action (G_ACTION_MAP (priv->actions), action_name);
383}
384
385static void
386gtk_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
394static void
395gtk_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
402enum {
403 PROP_0,
404 PROP_SHOW_MENUBAR,
405 N_PROPS
406};
407static GParamSpec *gtk_application_window_properties[N_PROPS];
408
409static void
410gtk_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 (self: 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 (widget: priv->menubar, orientation: GTK_ORIENTATION_VERTICAL,
436 for_size, minimum: &menubar_height, NULL, NULL, NULL);
437
438
439 GTK_WIDGET_CLASS (gtk_application_window_parent_class)->measure (widget,
440 orientation,
441 for_size > -1 ?
442 for_size - menubar_height : -1,
443 minimum, natural,
444 minimum_baseline, natural_baseline);
445
446
447 gtk_widget_measure (widget: priv->menubar, orientation, for_size: menubar_height, minimum: &menubar_min, natural: &menubar_nat, NULL, NULL);
448
449 *minimum = MAX (*minimum, menubar_min);
450 *natural = MAX (*natural, menubar_nat);
451
452 }
453 else /* VERTICAL */
454 {
455 gtk_widget_measure (widget: priv->menubar, orientation, for_size, minimum: &menubar_min, natural: &menubar_nat, NULL, NULL);
456 *minimum += menubar_min;
457 *natural += menubar_nat;
458 }
459 }
460}
461
462static void
463gtk_application_window_real_size_allocate (GtkWidget *widget,
464 int width,
465 int height,
466 int baseline)
467{
468 GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
469 GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (self: window);
470
471 if (priv->menubar != NULL)
472 {
473 GtkAllocation menubar_allocation;
474 GtkAllocation child_allocation;
475 int menubar_height;
476 GtkWidget *child;
477
478 _gtk_window_set_allocation (GTK_WINDOW (widget), width, height, allocation_out: &child_allocation);
479 menubar_allocation = child_allocation;
480
481 gtk_widget_measure (widget: priv->menubar, orientation: GTK_ORIENTATION_VERTICAL,
482 for_size: menubar_allocation.width,
483 minimum: &menubar_height, NULL, NULL, NULL);
484
485 menubar_allocation.height = menubar_height;
486 gtk_widget_size_allocate (widget: priv->menubar, allocation: &menubar_allocation, baseline);
487
488 child_allocation.y += menubar_height;
489 child_allocation.height -= menubar_height;
490 child = gtk_window_get_child (GTK_WINDOW (window));
491 if (child != NULL && gtk_widget_get_visible (widget: child))
492 gtk_widget_size_allocate (widget: child, allocation: &child_allocation, baseline);
493
494 gtk_tooltip_maybe_allocate (native: GTK_NATIVE (ptr: widget));
495 }
496 else
497 GTK_WIDGET_CLASS (gtk_application_window_parent_class)->size_allocate (widget,
498 width,
499 height,
500 baseline);
501}
502
503static void
504gtk_application_window_real_realize (GtkWidget *widget)
505{
506 GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
507 GtkSettings *settings;
508
509 settings = gtk_widget_get_settings (widget);
510
511 g_signal_connect (settings, "notify::gtk-shell-shows-menubar",
512 G_CALLBACK (gtk_application_window_shell_shows_menubar_changed), window);
513
514 GTK_WIDGET_CLASS (gtk_application_window_parent_class)->realize (widget);
515
516 gtk_application_window_update_shell_shows_menubar (window, settings);
517 gtk_application_window_update_menubar (window);
518}
519
520static void
521gtk_application_window_real_unrealize (GtkWidget *widget)
522{
523 GtkSettings *settings;
524
525 settings = gtk_widget_get_settings (widget);
526
527 g_signal_handlers_disconnect_by_func (settings, gtk_application_window_shell_shows_menubar_changed, widget);
528
529 GTK_WIDGET_CLASS (gtk_application_window_parent_class)->unrealize (widget);
530}
531
532GActionGroup *
533gtk_application_window_get_action_group (GtkApplicationWindow *window)
534{
535 GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (self: window);
536 return G_ACTION_GROUP (priv->actions);
537}
538
539static void
540gtk_application_window_real_map (GtkWidget *widget)
541{
542 GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
543 GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (self: window);
544
545 /* XXX could eliminate this by tweaking gtk_window_map */
546 if (priv->menubar)
547 gtk_widget_map (widget: priv->menubar);
548
549 GTK_WIDGET_CLASS (gtk_application_window_parent_class)->map (widget);
550}
551
552static void
553gtk_application_window_real_unmap (GtkWidget *widget)
554{
555 GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
556 GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (self: window);
557
558 /* XXX could eliminate this by tweaking gtk_window_unmap */
559 if (priv->menubar)
560 gtk_widget_unmap (widget: priv->menubar);
561
562 GTK_WIDGET_CLASS (gtk_application_window_parent_class)->unmap (widget);
563}
564
565static void
566gtk_application_window_get_property (GObject *object,
567 guint prop_id,
568 GValue *value,
569 GParamSpec *pspec)
570{
571 GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (object);
572 GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (self: window);
573
574 switch (prop_id)
575 {
576 case PROP_SHOW_MENUBAR:
577 g_value_set_boolean (value, v_boolean: priv->show_menubar);
578 break;
579
580 default:
581 g_assert_not_reached ();
582 }
583}
584
585static void
586gtk_application_window_set_property (GObject *object,
587 guint prop_id,
588 const GValue *value,
589 GParamSpec *pspec)
590{
591 GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (object);
592
593 switch (prop_id)
594 {
595 case PROP_SHOW_MENUBAR:
596 gtk_application_window_set_show_menubar (window, show_menubar: g_value_get_boolean (value));
597 break;
598
599 default:
600 g_assert_not_reached ();
601 }
602}
603
604static void
605gtk_application_window_dispose (GObject *object)
606{
607 GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (object);
608 GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (self: window);
609
610 if (priv->menubar)
611 {
612 gtk_widget_unparent (widget: priv->menubar);
613 priv->menubar = NULL;
614 }
615
616 g_clear_object (&priv->menubar_section);
617
618 if (priv->help_overlay)
619 {
620 gtk_window_destroy (GTK_WINDOW (priv->help_overlay));
621 g_clear_object (&priv->help_overlay);
622 }
623
624 G_OBJECT_CLASS (gtk_application_window_parent_class)->dispose (object);
625
626 /* We do this below the chain-up above to give us a chance to be
627 * removed from the GtkApplication (which is done in the dispose
628 * handler of GtkWindow).
629 *
630 * That reduces our chances of being watched as a GActionGroup from a
631 * muxer constructed by GtkApplication.
632 */
633 g_clear_object (&priv->actions);
634}
635
636static void
637gtk_application_window_init (GtkApplicationWindow *window)
638{
639 GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (self: window);
640
641 priv->actions = gtk_application_window_actions_new (window);
642 priv->menubar_section = g_menu_new ();
643
644 gtk_widget_insert_action_group (GTK_WIDGET (window), name: "win", G_ACTION_GROUP (priv->actions));
645
646 /* priv->actions is the one and only ref on the group, so when
647 * we dispose, the action group will die, disconnecting all signals.
648 */
649 g_signal_connect_swapped (priv->actions, "action-added",
650 G_CALLBACK (g_action_group_action_added), window);
651 g_signal_connect_swapped (priv->actions, "action-enabled-changed",
652 G_CALLBACK (g_action_group_action_enabled_changed), window);
653 g_signal_connect_swapped (priv->actions, "action-state-changed",
654 G_CALLBACK (g_action_group_action_state_changed), window);
655 g_signal_connect_swapped (priv->actions, "action-removed",
656 G_CALLBACK (g_action_group_action_removed), window);
657}
658
659static void
660gtk_application_window_class_init (GtkApplicationWindowClass *class)
661{
662 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
663 GObjectClass *object_class = G_OBJECT_CLASS (class);
664
665 widget_class->measure = gtk_application_window_measure;
666 widget_class->size_allocate = gtk_application_window_real_size_allocate;
667 widget_class->realize = gtk_application_window_real_realize;
668 widget_class->unrealize = gtk_application_window_real_unrealize;
669 widget_class->map = gtk_application_window_real_map;
670 widget_class->unmap = gtk_application_window_real_unmap;
671
672 object_class->get_property = gtk_application_window_get_property;
673 object_class->set_property = gtk_application_window_set_property;
674 object_class->dispose = gtk_application_window_dispose;
675
676 /**
677 * GtkApplicationWindow:show-menubar: (attributes org.gtk.Property.get=gtk_application_window_get_show_menubar org.gtk.Property.set=gtk_application_window_set_show_menubar)
678 *
679 * If this property is %TRUE, the window will display a menubar
680 * unless it is shown by the desktop shell.
681 *
682 * See [method@Gtk.Application.set_menubar].
683 *
684 * If %FALSE, the window will not display a menubar, regardless
685 * of whether the desktop shell is showing it or not.
686 */
687 gtk_application_window_properties[PROP_SHOW_MENUBAR] =
688 g_param_spec_boolean (name: "show-menubar",
689 P_("Show a menubar"),
690 P_("TRUE if the window should show a "
691 "menubar at the top of the window"),
692 FALSE, flags: G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
693 g_object_class_install_properties (oclass: object_class, n_pspecs: N_PROPS, pspecs: gtk_application_window_properties);
694}
695
696/**
697 * gtk_application_window_new:
698 * @application: a `GtkApplication`
699 *
700 * Creates a new `GtkApplicationWindow`.
701 *
702 * Returns: a newly created `GtkApplicationWindow`
703 */
704GtkWidget *
705gtk_application_window_new (GtkApplication *application)
706{
707 g_return_val_if_fail (GTK_IS_APPLICATION (application), NULL);
708
709 return g_object_new (GTK_TYPE_APPLICATION_WINDOW,
710 first_property_name: "application", application,
711 NULL);
712}
713
714/**
715 * gtk_application_window_get_show_menubar: (attributes org.gtk.Method.get_property=show-menubar)
716 * @window: a `GtkApplicationWindow`
717 *
718 * Returns whether the window will display a menubar for the app menu
719 * and menubar as needed.
720 *
721 * Returns: %TRUE if @window will display a menubar when needed
722 */
723gboolean
724gtk_application_window_get_show_menubar (GtkApplicationWindow *window)
725{
726 GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (self: window);
727 return priv->show_menubar;
728}
729
730/**
731 * gtk_application_window_set_show_menubar: (attributes org.gtk.Method.set_property=show-menubar)
732 * @window: a `GtkApplicationWindow`
733 * @show_menubar: whether to show a menubar when needed
734 *
735 * Sets whether the window will display a menubar for the app menu
736 * and menubar as needed.
737 */
738void
739gtk_application_window_set_show_menubar (GtkApplicationWindow *window,
740 gboolean show_menubar)
741{
742 GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (self: window);
743 g_return_if_fail (GTK_IS_APPLICATION_WINDOW (window));
744
745 show_menubar = !!show_menubar;
746
747 if (priv->show_menubar != show_menubar)
748 {
749 priv->show_menubar = show_menubar;
750
751 gtk_application_window_update_menubar (window);
752
753 g_object_notify_by_pspec (G_OBJECT (window), pspec: gtk_application_window_properties[PROP_SHOW_MENUBAR]);
754 }
755}
756
757/**
758 * gtk_application_window_get_id:
759 * @window: a `GtkApplicationWindow`
760 *
761 * Returns the unique ID of the window.
762 *
763 * If the window has not yet been added to a `GtkApplication`, returns `0`.
764 *
765 * Returns: the unique ID for @window, or `0` if the window
766 * has not yet been added to a `GtkApplication`
767 */
768guint
769gtk_application_window_get_id (GtkApplicationWindow *window)
770{
771 GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (self: window);
772 g_return_val_if_fail (GTK_IS_APPLICATION_WINDOW (window), 0);
773
774 return priv->id;
775}
776
777void
778gtk_application_window_set_id (GtkApplicationWindow *window,
779 guint id)
780{
781 GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (self: window);
782 g_return_if_fail (GTK_IS_APPLICATION_WINDOW (window));
783 priv->id = id;
784}
785
786static void
787show_help_overlay (GSimpleAction *action,
788 GVariant *parameter,
789 gpointer user_data)
790{
791 GtkApplicationWindow *window = user_data;
792 GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (self: window);
793
794 if (priv->help_overlay)
795 gtk_widget_show (GTK_WIDGET (priv->help_overlay));
796}
797
798/**
799 * gtk_application_window_set_help_overlay:
800 * @window: a `GtkApplicationWindow`
801 * @help_overlay: (nullable): a `GtkShortcutsWindow`
802 *
803 * Associates a shortcuts window with the application window.
804 *
805 * Additionally, sets up an action with the name
806 * `win.show-help-overlay` to present it.
807 *
808 * @window takes responsibility for destroying @help_overlay.
809 */
810void
811gtk_application_window_set_help_overlay (GtkApplicationWindow *window,
812 GtkShortcutsWindow *help_overlay)
813{
814 GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (self: window);
815 g_return_if_fail (GTK_IS_APPLICATION_WINDOW (window));
816 g_return_if_fail (help_overlay == NULL || GTK_IS_SHORTCUTS_WINDOW (help_overlay));
817
818 if (priv->help_overlay)
819 gtk_window_destroy (GTK_WINDOW (priv->help_overlay));
820 g_set_object (&priv->help_overlay, help_overlay);
821
822 if (!priv->help_overlay)
823 return;
824
825 gtk_window_set_modal (GTK_WINDOW (help_overlay), TRUE);
826 gtk_window_set_hide_on_close (GTK_WINDOW (help_overlay), TRUE);
827 gtk_window_set_transient_for (GTK_WINDOW (help_overlay), GTK_WINDOW (window));
828 gtk_shortcuts_window_set_window (self: help_overlay, GTK_WINDOW (window));
829
830 if (!g_action_map_lookup_action (G_ACTION_MAP (priv->actions), action_name: "show-help-overlay"))
831 {
832 GSimpleAction *action;
833
834 action = g_simple_action_new (name: "show-help-overlay", NULL);
835 g_signal_connect (action, "activate", G_CALLBACK (show_help_overlay), window);
836
837 g_action_map_add_action (G_ACTION_MAP (priv->actions), G_ACTION (action));
838 g_object_unref (G_OBJECT (action));
839 }
840}
841
842/**
843 * gtk_application_window_get_help_overlay:
844 * @window: a `GtkApplicationWindow`
845 *
846 * Gets the `GtkShortcutsWindow` that is associated with @window.
847 *
848 * See [method@Gtk.ApplicationWindow.set_help_overlay].
849 *
850 * Returns: (transfer none) (nullable): the help overlay associated
851 * with @window
852 */
853GtkShortcutsWindow *
854gtk_application_window_get_help_overlay (GtkApplicationWindow *window)
855{
856 GtkApplicationWindowPrivate *priv = gtk_application_window_get_instance_private (self: window);
857 g_return_val_if_fail (GTK_IS_APPLICATION_WINDOW (window), NULL);
858
859 return priv->help_overlay;
860}
861

source code of gtk/gtk/gtkapplicationwindow.c