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