1/*
2 * Copyright (c) 2020 Alexander Mikhaylenko <alexm@gnome.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12 * License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19#include "config.h"
20
21#include "gtkwindowcontrols.h"
22
23#include "gtkaccessible.h"
24#include "gtkactionable.h"
25#include "gtkboxlayout.h"
26#include "gtkbutton.h"
27#include "gtkenums.h"
28#include "gtkicontheme.h"
29#include "gtkimage.h"
30#include "gtkintl.h"
31#include "gtkprivate.h"
32#include "gtktypebuiltins.h"
33#include "gtkwindowprivate.h"
34
35/**
36 * GtkWindowControls:
37 *
38 * `GtkWindowControls` shows window frame controls.
39 *
40 * Typical window frame controls are minimize, maximize and close buttons,
41 * and the window icon.
42 *
43 * ![An example GtkWindowControls](windowcontrols.png)
44 *
45 * `GtkWindowControls` only displays start or end side of the controls (see
46 * [property@Gtk.WindowControls:side]), so it's intended to be always used
47 * in pair with another `GtkWindowControls` for the opposite side, for example:
48 *
49 * ```xml
50 * <object class="GtkBox">
51 * <child>
52 * <object class="GtkWindowControls">
53 * <property name="side">start</property>
54 * </object>
55 * </child>
56 *
57 * ...
58 *
59 * <child>
60 * <object class="GtkWindowControls">
61 * <property name="side">end</property>
62 * </object>
63 * </child>
64 * </object>
65 * ```
66 *
67 * # CSS nodes
68 *
69 * ```
70 * windowcontrols
71 * ├── [image.icon]
72 * ├── [button.minimize]
73 * ├── [button.maximize]
74 * ╰── [button.close]
75 * ```
76 *
77 * A `GtkWindowControls`' CSS node is called windowcontrols. It contains
78 * subnodes corresponding to each title button. Which of the title buttons
79 * exist and where they are placed exactly depends on the desktop environment
80 * and [property@Gtk.WindowControls:decoration-layout] value.
81 *
82 * When [property@Gtk.WindowControls:empty] is %TRUE, it gets the .empty
83 * style class.
84 *
85 * # Accessibility
86 *
87 * `GtkWindowControls` uses the %GTK_ACCESSIBLE_ROLE_GROUP role.
88 */
89
90struct _GtkWindowControls {
91 GtkWidget parent_instance;
92
93 GtkPackType side;
94 char *decoration_layout;
95
96 gboolean empty;
97};
98
99enum {
100 PROP_0,
101 PROP_SIDE,
102 PROP_DECORATION_LAYOUT,
103 PROP_EMPTY,
104 LAST_PROP
105};
106
107static GParamSpec *props[LAST_PROP] = { NULL, };
108
109#define WINDOW_ICON_SIZE 16
110
111G_DEFINE_TYPE (GtkWindowControls, gtk_window_controls, GTK_TYPE_WIDGET)
112
113static char *
114get_layout (GtkWindowControls *self)
115{
116 GtkWidget *widget = GTK_WIDGET (self);
117 GtkRoot *root;
118 char *layout_desc, *layout_half;
119 char **tokens;
120
121 root = gtk_widget_get_root (widget);
122 if (!root || !GTK_IS_WINDOW (root))
123 return NULL;
124
125 if (self->decoration_layout)
126 layout_desc = g_strdup (str: self->decoration_layout);
127 else
128 g_object_get (object: gtk_widget_get_settings (widget),
129 first_property_name: "gtk-decoration-layout", &layout_desc,
130 NULL);
131
132 tokens = g_strsplit (string: layout_desc, delimiter: ":", max_tokens: 2);
133
134 switch (self->side)
135 {
136 case GTK_PACK_START:
137 layout_half = g_strdup (str: tokens[0]);
138 break;
139
140 case GTK_PACK_END:
141 layout_half = g_strdup (str: tokens[1]);
142 break;
143
144 default:
145 g_assert_not_reached ();
146 }
147
148 g_free (mem: layout_desc);
149 g_strfreev (str_array: tokens);
150
151 return layout_half;
152}
153
154static GdkPaintable *
155get_default_icon (GtkWidget *widget)
156{
157 GdkDisplay *display = gtk_widget_get_display (widget);
158 GtkIconPaintable *info;
159 int scale = gtk_widget_get_scale_factor (widget);
160
161 info = gtk_icon_theme_lookup_icon (self: gtk_icon_theme_get_for_display (display),
162 icon_name: gtk_window_get_default_icon_name (),
163 NULL,
164 WINDOW_ICON_SIZE,
165 scale,
166 direction: gtk_widget_get_direction (widget),
167 flags: 0);
168
169 return GDK_PAINTABLE (ptr: info);
170}
171
172static gboolean
173update_window_icon (GtkWindow *window,
174 GtkWidget *icon)
175{
176 GdkPaintable *paintable;
177
178 if (window)
179 paintable = gtk_window_get_icon_for_size (window, WINDOW_ICON_SIZE);
180 else
181 paintable = get_default_icon (widget: icon);
182
183 if (paintable)
184 {
185 gtk_image_set_from_paintable (GTK_IMAGE (icon), paintable);
186 g_object_unref (object: paintable);
187 gtk_widget_show (widget: icon);
188
189 return TRUE;
190 }
191
192 return FALSE;
193}
194
195static void
196set_empty (GtkWindowControls *self,
197 gboolean empty)
198{
199 if (empty == self->empty)
200 return;
201
202 self->empty = empty;
203
204 if (empty)
205 gtk_widget_add_css_class (GTK_WIDGET (self), css_class: "empty");
206 else
207 gtk_widget_remove_css_class (GTK_WIDGET (self), css_class: "empty");
208
209 g_object_notify_by_pspec (G_OBJECT (self), pspec: props[PROP_EMPTY]);
210}
211
212static void
213clear_controls (GtkWindowControls *self)
214{
215 GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self));
216
217 while (child)
218 {
219 GtkWidget *next = gtk_widget_get_next_sibling (widget: child);
220
221 gtk_widget_unparent (widget: child);
222
223 child = next;
224 }
225}
226
227static void
228update_window_buttons (GtkWindowControls *self)
229{
230 GtkWidget *widget = GTK_WIDGET (self);
231 char *layout;
232 char **tokens;
233 int i;
234 gboolean is_sovereign_window;
235 gboolean maximized;
236 gboolean resizable;
237 gboolean deletable;
238 gboolean empty = TRUE;
239 GtkRoot *root;
240 GtkWindow *window = NULL;
241
242 root = gtk_widget_get_root (widget);
243 if (!root || !GTK_IS_WINDOW (root))
244 {
245 set_empty (self, TRUE);
246
247 return;
248 }
249
250 clear_controls (self);
251
252 window = GTK_WINDOW (root);
253 is_sovereign_window = !gtk_window_get_modal (window) &&
254 gtk_window_get_transient_for (window) == NULL;
255 maximized = gtk_window_is_maximized (window);
256 resizable = gtk_window_get_resizable (window);
257 deletable = gtk_window_get_deletable (window);
258
259 layout = get_layout (self);
260
261 if (!layout)
262 {
263 set_empty (self, TRUE);
264
265 return;
266 }
267
268 tokens = g_strsplit (string: layout, delimiter: ",", max_tokens: -1);
269
270 for (i = 0; tokens[i]; i++)
271 {
272 GtkWidget *button = NULL;
273 GtkWidget *image = NULL;
274
275 if (strcmp (s1: tokens[i], s2: "icon") == 0 &&
276 is_sovereign_window)
277 {
278 /* The icon is not relevant for accessibility purposes */
279 button = g_object_new (GTK_TYPE_IMAGE,
280 first_property_name: "accessible-role", GTK_ACCESSIBLE_ROLE_PRESENTATION,
281 NULL);
282 gtk_widget_set_valign (widget: button, align: GTK_ALIGN_CENTER);
283 gtk_widget_add_css_class (widget: button, css_class: "icon");
284
285 if (!update_window_icon (window, icon: button))
286 {
287 g_object_ref_sink (button);
288 g_object_unref (object: button);
289 button = NULL;
290 }
291 }
292 else if (strcmp (s1: tokens[i], s2: "minimize") == 0 &&
293 is_sovereign_window)
294 {
295 button = gtk_button_new ();
296 gtk_widget_set_valign (widget: button, align: GTK_ALIGN_CENTER);
297 gtk_widget_add_css_class (widget: button, css_class: "minimize");
298 /* The icon is not relevant for accessibility purposes */
299 image = g_object_new (GTK_TYPE_IMAGE,
300 first_property_name: "accessible-role", GTK_ACCESSIBLE_ROLE_PRESENTATION,
301 "icon-name", "window-minimize-symbolic",
302 NULL);
303 g_object_set (object: image, first_property_name: "use-fallback", TRUE, NULL);
304 gtk_button_set_child (GTK_BUTTON (button), child: image);
305 gtk_widget_set_can_focus (widget: button, FALSE);
306 gtk_actionable_set_action_name (GTK_ACTIONABLE (button),
307 action_name: "window.minimize");
308 gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: button),
309 first_property: GTK_ACCESSIBLE_PROPERTY_LABEL, _("Minimize"),
310 GTK_ACCESSIBLE_PROPERTY_DESCRIPTION,
311 _("Minimize the window"),
312 -1);
313 }
314 else if (strcmp (s1: tokens[i], s2: "maximize") == 0 &&
315 resizable &&
316 is_sovereign_window)
317 {
318 const char *icon_name;
319
320 icon_name = maximized ? "window-restore-symbolic" : "window-maximize-symbolic";
321 button = gtk_button_new ();
322 gtk_widget_set_valign (widget: button, align: GTK_ALIGN_CENTER);
323 gtk_widget_add_css_class (widget: button, css_class: "maximize");
324 /* The icon is not relevant for accessibility purposes */
325 image = g_object_new (GTK_TYPE_IMAGE,
326 first_property_name: "accessible-role", GTK_ACCESSIBLE_ROLE_PRESENTATION,
327 "icon-name", icon_name,
328 NULL);
329 g_object_set (object: image, first_property_name: "use-fallback", TRUE, NULL);
330 gtk_button_set_child (GTK_BUTTON (button), child: image);
331 gtk_widget_set_can_focus (widget: button, FALSE);
332 gtk_actionable_set_action_name (GTK_ACTIONABLE (button),
333 action_name: "window.toggle-maximized");
334 gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: button),
335 first_property: GTK_ACCESSIBLE_PROPERTY_LABEL, _("Maximize"),
336 GTK_ACCESSIBLE_PROPERTY_DESCRIPTION,
337 _("Maximize the window"),
338 -1);
339 }
340 else if (strcmp (s1: tokens[i], s2: "close") == 0 &&
341 deletable)
342 {
343 button = gtk_button_new ();
344 gtk_widget_set_valign (widget: button, align: GTK_ALIGN_CENTER);
345 /* The icon is not relevant for accessibility purposes */
346 image = g_object_new (GTK_TYPE_IMAGE,
347 first_property_name: "accessible-role", GTK_ACCESSIBLE_ROLE_PRESENTATION,
348 "icon-name", "window-close-symbolic",
349 NULL);
350 gtk_widget_add_css_class (widget: button, css_class: "close");
351 g_object_set (object: image, first_property_name: "use-fallback", TRUE, NULL);
352 gtk_button_set_child (GTK_BUTTON (button), child: image);
353 gtk_widget_set_can_focus (widget: button, FALSE);
354 gtk_actionable_set_action_name (GTK_ACTIONABLE (button),
355 action_name: "window.close");
356 gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: button),
357 first_property: GTK_ACCESSIBLE_PROPERTY_LABEL, _("Close"),
358 GTK_ACCESSIBLE_PROPERTY_DESCRIPTION,
359 _("Close the window"),
360 -1);
361 }
362
363 if (button)
364 {
365 gtk_widget_set_parent (widget: button, parent: widget);
366 empty = FALSE;
367 }
368 }
369 g_free (mem: layout);
370 g_strfreev (str_array: tokens);
371
372 set_empty (self, empty);
373}
374
375static void
376window_notify_cb (GtkWindowControls *self,
377 GParamSpec *pspec,
378 GtkWindow *window)
379{
380 if (pspec->name == I_("deletable") ||
381 pspec->name == I_("icon-name") ||
382 pspec->name == I_("maximized") ||
383 pspec->name == I_("modal") ||
384 pspec->name == I_("resizable") ||
385 pspec->name == I_("scale-factor") ||
386 pspec->name == I_("transient-for"))
387 update_window_buttons (self);
388}
389
390static void
391gtk_window_controls_root (GtkWidget *widget)
392{
393 GtkSettings *settings;
394 GtkWidget *root;
395
396 GTK_WIDGET_CLASS (gtk_window_controls_parent_class)->root (widget);
397
398 settings = gtk_widget_get_settings (widget);
399 g_signal_connect_swapped (settings, "notify::gtk-decoration-layout",
400 G_CALLBACK (update_window_buttons), widget);
401
402 root = GTK_WIDGET (gtk_widget_get_root (widget));
403
404 if (GTK_IS_WINDOW (root))
405 g_signal_connect_swapped (root, "notify",
406 G_CALLBACK (window_notify_cb), widget);
407
408 update_window_buttons (self: GTK_WINDOW_CONTROLS (ptr: widget));
409}
410
411static void
412gtk_window_controls_unroot (GtkWidget *widget)
413{
414 GtkSettings *settings;
415
416 settings = gtk_widget_get_settings (widget);
417
418 g_signal_handlers_disconnect_by_func (settings, update_window_buttons, widget);
419 g_signal_handlers_disconnect_by_func (gtk_widget_get_root (widget), window_notify_cb, widget);
420
421 GTK_WIDGET_CLASS (gtk_window_controls_parent_class)->unroot (widget);
422}
423
424static void
425gtk_window_controls_dispose (GObject *object)
426{
427 GtkWindowControls *self = GTK_WINDOW_CONTROLS (ptr: object);
428
429 clear_controls (self);
430
431 G_OBJECT_CLASS (gtk_window_controls_parent_class)->dispose (object);
432}
433
434static void
435gtk_window_controls_finalize (GObject *object)
436{
437 GtkWindowControls *self = GTK_WINDOW_CONTROLS (ptr: object);
438
439 g_free (mem: self->decoration_layout);
440
441 G_OBJECT_CLASS (gtk_window_controls_parent_class)->finalize (object);
442}
443
444static void
445gtk_window_controls_get_property (GObject *object,
446 guint prop_id,
447 GValue *value,
448 GParamSpec *pspec)
449{
450 GtkWindowControls *self = GTK_WINDOW_CONTROLS (ptr: object);
451
452 switch (prop_id)
453 {
454 case PROP_SIDE:
455 g_value_set_enum (value, v_enum: gtk_window_controls_get_side (self));
456 break;
457
458 case PROP_DECORATION_LAYOUT:
459 g_value_set_string (value, v_string: gtk_window_controls_get_decoration_layout (self));
460 break;
461
462 case PROP_EMPTY:
463 g_value_set_boolean (value, v_boolean: gtk_window_controls_get_empty (self));
464 break;
465
466 default:
467 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
468 break;
469 }
470}
471
472static void
473gtk_window_controls_set_property (GObject *object,
474 guint prop_id,
475 const GValue *value,
476 GParamSpec *pspec)
477{
478 GtkWindowControls *self = GTK_WINDOW_CONTROLS (ptr: object);
479
480 switch (prop_id)
481 {
482 case PROP_SIDE:
483 gtk_window_controls_set_side (self, side: g_value_get_enum (value));
484 break;
485
486 case PROP_DECORATION_LAYOUT:
487 gtk_window_controls_set_decoration_layout (self, layout: g_value_get_string (value));
488 break;
489
490 default:
491 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
492 break;
493 }
494}
495
496static void
497gtk_window_controls_class_init (GtkWindowControlsClass *klass)
498{
499 GObjectClass *object_class = G_OBJECT_CLASS (klass);
500 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
501
502 object_class->dispose = gtk_window_controls_dispose;
503 object_class->finalize = gtk_window_controls_finalize;
504 object_class->get_property = gtk_window_controls_get_property;
505 object_class->set_property = gtk_window_controls_set_property;
506
507 widget_class->root = gtk_window_controls_root;
508 widget_class->unroot = gtk_window_controls_unroot;
509
510 /**
511 * GtkWindowControls:side: (attributes org.gtk.Property.get=gtk_window_controls_get_side org.gtk.Property.set=gtk_window_controls_set_side)
512 *
513 * Whether the widget shows start or end side of the decoration layout.
514 *
515 * See [property@Gtk.WindowControls:decoration_layout].
516 */
517 props[PROP_SIDE] =
518 g_param_spec_enum (name: "side",
519 P_("Side"),
520 P_("Whether the widget shows start or end portion of the decoration layout"),
521 enum_type: GTK_TYPE_PACK_TYPE,
522 default_value: GTK_PACK_START,
523 GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
524
525 /**
526 * GtkWindowControls:decoration-layout: (attributes org.gtk.Property.get=gtk_window_controls_get_decoration_layout org.gtk.Property.set=gtk_window_controls_set_decoration_layout)
527 *
528 * The decoration layout for window buttons.
529 *
530 * If this property is not set, the
531 * [property@Gtk.Settings:gtk-decoration-layout] setting is used.
532 */
533 props[PROP_DECORATION_LAYOUT] =
534 g_param_spec_string (name: "decoration-layout",
535 P_("Decoration Layout"),
536 P_("The layout for window decorations"),
537 NULL,
538 GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
539
540 /**
541 * GtkWindowControls:empty: (attributes org.gtk.Property.get=gtk_window_controls_get_empty)
542 *
543 * Whether the widget has any window buttons.
544 */
545 props[PROP_EMPTY] =
546 g_param_spec_boolean (name: "empty",
547 P_("Empty"),
548 P_("Whether the widget has any window buttons"),
549 TRUE,
550 GTK_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
551
552 g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_PROP, pspecs: props);
553
554 gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT);
555 gtk_widget_class_set_css_name (widget_class, I_("windowcontrols"));
556 gtk_widget_class_set_accessible_role (widget_class, accessible_role: GTK_ACCESSIBLE_ROLE_GROUP);
557}
558
559static void
560gtk_window_controls_init (GtkWindowControls *self)
561{
562 self->decoration_layout = NULL;
563 self->side = GTK_PACK_START;
564 self->empty = TRUE;
565
566 gtk_widget_add_css_class (GTK_WIDGET (self), css_class: "empty");
567 gtk_widget_add_css_class (GTK_WIDGET (self), css_class: "start");
568
569 gtk_widget_set_can_focus (GTK_WIDGET (self), FALSE);
570}
571
572/**
573 * gtk_window_controls_new:
574 * @side: the side
575 *
576 * Creates a new `GtkWindowControls`.
577 *
578 * Returns: a new `GtkWindowControls`.
579 **/
580GtkWidget *
581gtk_window_controls_new (GtkPackType side)
582{
583 return g_object_new (GTK_TYPE_WINDOW_CONTROLS,
584 first_property_name: "side", side,
585 NULL);
586}
587
588/**
589 * gtk_window_controls_get_side: (attributes org.gtk.Method.get_property=side)
590 * @self: a `GtkWindowControls`
591 *
592 * Gets the side to which this `GtkWindowControls` instance belongs.
593 *
594 * Returns: the side
595 */
596GtkPackType
597gtk_window_controls_get_side (GtkWindowControls *self)
598{
599 g_return_val_if_fail (GTK_IS_WINDOW_CONTROLS (self), GTK_PACK_START);
600
601 return self->side;
602}
603
604/**
605 * gtk_window_controls_set_side: (attributes org.gtk.Method.set_property=side)
606 * @self: a `GtkWindowControls`
607 * @side: a side
608 *
609 * Determines which part of decoration layout the `GtkWindowControls` uses.
610 *
611 * See [property@Gtk.WindowControls:decoration-layout].
612 */
613void
614gtk_window_controls_set_side (GtkWindowControls *self,
615 GtkPackType side)
616{
617 g_return_if_fail (GTK_IS_WINDOW_CONTROLS (self));
618
619 if (self->side == side)
620 return;
621
622 self->side = side;
623
624 switch (side)
625 {
626 case GTK_PACK_START:
627 gtk_widget_add_css_class (GTK_WIDGET (self), css_class: "start");
628 gtk_widget_remove_css_class (GTK_WIDGET (self), css_class: "end");
629 break;
630
631 case GTK_PACK_END:
632 gtk_widget_add_css_class (GTK_WIDGET (self), css_class: "end");
633 gtk_widget_remove_css_class (GTK_WIDGET (self), css_class: "start");
634 break;
635
636 default:
637 g_warning ("Unexpected side: %d", side);
638 break;
639 }
640
641 update_window_buttons (self);
642
643 g_object_notify_by_pspec (G_OBJECT (self), pspec: props[PROP_SIDE]);
644}
645
646/**
647 * gtk_window_controls_get_decoration_layout: (attributes org.gtk.Method.get_property=decoration-layout)
648 * @self: a `GtkWindowControls`
649 *
650 * Gets the decoration layout of this `GtkWindowControls`.
651 *
652 * Returns: (nullable): the decoration layout or %NULL if it is unset
653 */
654const char *
655gtk_window_controls_get_decoration_layout (GtkWindowControls *self)
656{
657 g_return_val_if_fail (GTK_IS_WINDOW_CONTROLS (self), NULL);
658
659 return self->decoration_layout;
660}
661
662/**
663 * gtk_window_controls_set_decoration_layout: (attributes org.gtk.Method.set_property=decoration-layout)
664 * @self: a `GtkWindowControls`
665 * @layout: (nullable): a decoration layout, or %NULL to unset the layout
666 *
667 * Sets the decoration layout for the title buttons.
668 *
669 * This overrides the [property@Gtk.Settings:gtk-decoration-layout]
670 * setting.
671 *
672 * The format of the string is button names, separated by commas.
673 * A colon separates the buttons that should appear on the left
674 * from those on the right. Recognized button names are minimize,
675 * maximize, close and icon (the window icon).
676 *
677 * For example, “icon:minimize,maximize,close” specifies a icon
678 * on the left, and minimize, maximize and close buttons on the right.
679 *
680 * If [property@Gtk.WindowControls:side] value is @GTK_PACK_START, @self
681 * will display the part before the colon, otherwise after that.
682 */
683void
684gtk_window_controls_set_decoration_layout (GtkWindowControls *self,
685 const char *layout)
686{
687 g_return_if_fail (GTK_IS_WINDOW_CONTROLS (self));
688
689 g_free (mem: self->decoration_layout);
690 self->decoration_layout = g_strdup (str: layout);
691
692 update_window_buttons (self);
693
694 g_object_notify_by_pspec (G_OBJECT (self), pspec: props[PROP_DECORATION_LAYOUT]);
695}
696
697/**
698 * gtk_window_controls_get_empty: (attributes org.gtk.Method.get_property=empty)
699 * @self: a `GtkWindowControls`
700 *
701 * Gets whether the widget has any window buttons.
702 *
703 * Returns: %TRUE if the widget has window buttons, otherwise %FALSE
704 */
705gboolean
706gtk_window_controls_get_empty (GtkWindowControls *self)
707{
708 g_return_val_if_fail (GTK_IS_WINDOW_CONTROLS (self), FALSE);
709
710 return self->empty;
711}
712

source code of gtk/gtk/gtkwindowcontrols.c