1/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
2/* GTK - The GIMP Toolkit
3 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/*
20 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
21 * file for a list of people on the GTK+ Team. See the ChangeLog
22 * files for a list of changes. These files are distributed with
23 * GTK+ at ftp:ftp.gtk.org/pub/gtk/.
24 */
25
26#include "config.h"
27
28#include "gtknotebook.h"
29
30#include "gtkbox.h"
31#include "gtkboxlayout.h"
32#include "gtkbuildable.h"
33#include "gtkbutton.h"
34#include "gtkdroptarget.h"
35#include "gtkdragicon.h"
36#include "gtkdropcontrollermotion.h"
37#include "gtkeventcontrollermotion.h"
38#include "gtkgestureclick.h"
39#include "gtkgizmoprivate.h"
40#include "gtkbuiltiniconprivate.h"
41#include "gtkintl.h"
42#include "gtklabel.h"
43#include "gtkmain.h"
44#include "gtkmarshalers.h"
45#include "gtkpopovermenuprivate.h"
46#include "gtkorientable.h"
47#include "gtksizerequest.h"
48#include "gtkstylecontextprivate.h"
49#include "gtkprivate.h"
50#include "gtkstack.h"
51#include "gtktypebuiltins.h"
52#include "gtkwidgetprivate.h"
53#include "gtkdragsourceprivate.h"
54#include "gtkwidgetpaintable.h"
55#include "gtknative.h"
56
57#include <stdio.h>
58#include <string.h>
59#include <math.h>
60
61/**
62 * GtkNotebook:
63 *
64 * `GtkNotebook` is a container whose children are pages switched
65 * between using tabs.
66 *
67 * ![An example GtkNotebook](notebook.png)
68 *
69 * There are many configuration options for `GtkNotebook`. Among
70 * other things, you can choose on which edge the tabs appear
71 * (see [method@Gtk.Notebook.set_tab_pos]), whether, if there are
72 * too many tabs to fit the notebook should be made bigger or scrolling
73 * arrows added (see [method@Gtk.Notebook.set_scrollable]), and whether
74 * there will be a popup menu allowing the users to switch pages.
75 * (see [method@Gtk.Notebook.popup_enable]).
76 *
77 * # GtkNotebook as GtkBuildable
78 *
79 * The `GtkNotebook` implementation of the `GtkBuildable` interface
80 * supports placing children into tabs by specifying “tab” as the
81 * “type” attribute of a <child> element. Note that the content
82 * of the tab must be created before the tab can be filled.
83 * A tab child can be specified without specifying a <child>
84 * type attribute.
85 *
86 * To add a child widget in the notebooks action area, specify
87 * "action-start" or “action-end” as the “type” attribute of the
88 * <child> element.
89 *
90 * An example of a UI definition fragment with `GtkNotebook`:
91 *
92 * ```xml
93 * <object class="GtkNotebook">
94 * <child>
95 * <object class="GtkLabel" id="notebook-content">
96 * <property name="label">Content</property>
97 * </object>
98 * </child>
99 * <child type="tab">
100 * <object class="GtkLabel" id="notebook-tab">
101 * <property name="label">Tab</property>
102 * </object>
103 * </child>
104 * </object>
105 * ```
106 *
107 * # CSS nodes
108 *
109 * ```
110 * notebook
111 * ├── header.top
112 * │ ├── [<action widget>]
113 * │ ├── tabs
114 * │ │ ├── [arrow]
115 * │ │ ├── tab
116 * │ │ │ ╰── <tab label>
117 * ┊ ┊ ┊
118 * │ │ ├── tab[.reorderable-page]
119 * │ │ │ ╰── <tab label>
120 * │ │ ╰── [arrow]
121 * │ ╰── [<action widget>]
122 * │
123 * ╰── stack
124 * ├── <child>
125 * ┊
126 * ╰── <child>
127 * ```
128 *
129 * `GtkNotebook` has a main CSS node with name `notebook`, a subnode
130 * with name `header` and below that a subnode with name `tabs` which
131 * contains one subnode per tab with name `tab`.
132 *
133 * If action widgets are present, their CSS nodes are placed next
134 * to the `tabs` node. If the notebook is scrollable, CSS nodes with
135 * name `arrow` are placed as first and last child of the `tabs` node.
136 *
137 * The main node gets the `.frame` style class when the notebook
138 * has a border (see [method@Gtk.Notebook.set_show_border]).
139 *
140 * The header node gets one of the style class `.top`, `.bottom`,
141 * `.left` or `.right`, depending on where the tabs are placed. For
142 * reorderable pages, the tab node gets the `.reorderable-page` class.
143 *
144 * A `tab` node gets the `.dnd` style class while it is moved with drag-and-drop.
145 *
146 * The nodes are always arranged from left-to-right, regardless of text direction.
147 *
148 * # Accessibility
149 *
150 * `GtkNotebook` uses the following roles:
151 *
152 * - %GTK_ACCESSIBLE_ROLE_GROUP for the notebook widget
153 * - %GTK_ACCESSIBLE_ROLE_TAB_LIST for the list of tabs
154 * - %GTK_ACCESSIBLE_ROLE_TAB role for each tab
155 * - %GTK_ACCESSIBLE_ROLE_TAB_PANEL for each page
156 */
157
158/**
159 * GtkNotebookPage:
160 *
161 * `GtkNotebookPage` is an auxiliary object used by `GtkNotebook`.
162 */
163
164#define SCROLL_DELAY_FACTOR 5
165#define SCROLL_THRESHOLD 12
166#define DND_THRESHOLD_MULTIPLIER 4
167
168#define TIMEOUT_INITIAL 500
169#define TIMEOUT_REPEAT 50
170#define TIMEOUT_EXPAND 500
171
172typedef struct _GtkNotebookPage GtkNotebookPage;
173
174typedef enum
175{
176 DRAG_OPERATION_NONE,
177 DRAG_OPERATION_REORDER,
178 DRAG_OPERATION_DETACH
179} GtkNotebookDragOperation;
180
181enum {
182 ACTION_WIDGET_START,
183 ACTION_WIDGET_END,
184 N_ACTION_WIDGETS
185};
186
187#define GTK_NOTEBOOK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_NOTEBOOK, GtkNotebookClass))
188#define GTK_NOTEBOOK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_NOTEBOOK, GtkNotebookClass))
189
190typedef struct _GtkNotebookClass GtkNotebookClass;
191
192struct _GtkNotebookClass
193{
194 GtkWidgetClass parent_class;
195
196 void (* switch_page) (GtkNotebook *notebook,
197 GtkWidget *page,
198 guint page_num);
199
200 /* Action signals for keybindings */
201 gboolean (* select_page) (GtkNotebook *notebook,
202 gboolean move_focus);
203 gboolean (* focus_tab) (GtkNotebook *notebook,
204 GtkNotebookTab type);
205 gboolean (* change_current_page) (GtkNotebook *notebook,
206 int offset);
207 void (* move_focus_out) (GtkNotebook *notebook,
208 GtkDirectionType direction);
209 gboolean (* reorder_tab) (GtkNotebook *notebook,
210 GtkDirectionType direction,
211 gboolean move_to_last);
212 /* More vfuncs */
213 int (* insert_page) (GtkNotebook *notebook,
214 GtkWidget *child,
215 GtkWidget *tab_label,
216 GtkWidget *menu_label,
217 int position);
218
219 GtkNotebook * (* create_window) (GtkNotebook *notebook,
220 GtkWidget *page);
221
222 void (* page_reordered) (GtkNotebook *notebook,
223 GtkWidget *child,
224 guint page_num);
225
226 void (* page_removed) (GtkNotebook *notebook,
227 GtkWidget *child,
228 guint page_num);
229
230 void (* page_added) (GtkNotebook *notebook,
231 GtkWidget *child,
232 guint page_num);
233};
234
235struct _GtkNotebook
236{
237 GtkWidget container;
238
239 GtkNotebookDragOperation operation;
240 GtkNotebookPage *cur_page;
241 GtkNotebookPage *detached_tab;
242 GtkWidget *action_widget[N_ACTION_WIDGETS];
243 GtkWidget *menu;
244 GtkWidget *menu_box;
245
246 GtkWidget *stack_widget;
247 GtkWidget *header_widget;
248 GtkWidget *tabs_widget;
249 GtkWidget *arrow_widget[4];
250
251 GListModel *pages;
252
253 GList *children;
254 GList *first_tab; /* The first tab visible (for scrolling notebooks) */
255 GList *focus_tab;
256
257 double drag_begin_x;
258 double drag_begin_y;
259 int drag_offset_x;
260 int drag_offset_y;
261 int drag_surface_x;
262 int drag_surface_y;
263 double mouse_x;
264 double mouse_y;
265 int pressed_button;
266
267 GQuark group;
268
269 guint dnd_timer;
270 guint switch_page_timer;
271 GtkNotebookPage *switch_page;
272
273 guint32 timer;
274
275 guint child_has_focus : 1;
276 guint click_child : 3;
277 guint remove_in_detach : 1;
278 guint focus_out : 1; /* Flag used by ::move-focus-out implementation */
279 guint has_scrolled : 1;
280 guint need_timer : 1;
281 guint show_border : 1;
282 guint show_tabs : 1;
283 guint scrollable : 1;
284 guint tab_pos : 2;
285 guint rootwindow_drop : 1;
286};
287
288enum {
289 SWITCH_PAGE,
290 FOCUS_TAB,
291 SELECT_PAGE,
292 CHANGE_CURRENT_PAGE,
293 MOVE_FOCUS_OUT,
294 REORDER_TAB,
295 PAGE_REORDERED,
296 PAGE_REMOVED,
297 PAGE_ADDED,
298 CREATE_WINDOW,
299 LAST_SIGNAL
300};
301
302enum {
303 STEP_PREV,
304 STEP_NEXT
305};
306
307typedef enum
308{
309 ARROW_LEFT_BEFORE,
310 ARROW_RIGHT_BEFORE,
311 ARROW_LEFT_AFTER,
312 ARROW_RIGHT_AFTER,
313 ARROW_NONE
314} GtkNotebookArrow;
315
316typedef enum
317{
318 POINTER_BEFORE,
319 POINTER_AFTER,
320 POINTER_BETWEEN
321} GtkNotebookPointerPosition;
322
323#define ARROW_IS_LEFT(arrow) ((arrow) == ARROW_LEFT_BEFORE || (arrow) == ARROW_LEFT_AFTER)
324#define ARROW_IS_BEFORE(arrow) ((arrow) == ARROW_LEFT_BEFORE || (arrow) == ARROW_RIGHT_BEFORE)
325
326enum {
327 PROP_0,
328 PROP_TAB_POS,
329 PROP_SHOW_TABS,
330 PROP_SHOW_BORDER,
331 PROP_SCROLLABLE,
332 PROP_PAGE,
333 PROP_ENABLE_POPUP,
334 PROP_GROUP_NAME,
335 PROP_PAGES,
336 LAST_PROP
337};
338
339static GParamSpec *properties[LAST_PROP];
340
341enum {
342 CHILD_PROP_0,
343 CHILD_PROP_TAB_LABEL,
344 CHILD_PROP_MENU_LABEL,
345 CHILD_PROP_POSITION,
346 CHILD_PROP_TAB_EXPAND,
347 CHILD_PROP_TAB_FILL,
348 CHILD_PROP_REORDERABLE,
349 CHILD_PROP_DETACHABLE,
350 CHILD_PROP_CHILD,
351 CHILD_PROP_TAB,
352 CHILD_PROP_MENU,
353};
354
355#define GTK_NOTEBOOK_PAGE_FROM_LIST(_glist_) ((GtkNotebookPage *)(_glist_)->data)
356
357#define NOTEBOOK_IS_TAB_LABEL_PARENT(_notebook_,_page_) \
358 (g_object_get_data (G_OBJECT ((_page_)->tab_label), "notebook") == _notebook_)
359
360struct _GtkNotebookPage
361{
362 GObject instance;
363
364 GtkWidget *child;
365 GtkWidget *tab_label;
366 GtkWidget *menu_label;
367 GtkWidget *last_focus_child; /* Last descendant of the page that had focus */
368
369 GtkWidget *tab_widget; /* widget used for the tab itself */
370
371 char *tab_text;
372 char *menu_text;
373
374 guint default_menu : 1; /* If true, we create the menu label ourself */
375 guint default_tab : 1; /* If true, we create the tab label ourself */
376 guint expand : 1;
377 guint fill : 1;
378 guint reorderable : 1;
379 guint detachable : 1;
380
381 GtkRequisition requisition;
382
383 gulong mnemonic_activate_signal;
384 gulong notify_visible_handler;
385};
386
387typedef struct _GtkNotebookPageClass GtkNotebookPageClass;
388struct _GtkNotebookPageClass
389{
390 GObjectClass parent_class;
391};
392
393G_DEFINE_TYPE (GtkNotebookPage, gtk_notebook_page, G_TYPE_OBJECT)
394
395static void
396gtk_notebook_page_init (GtkNotebookPage *page)
397{
398 page->default_tab = TRUE;
399 page->default_menu = TRUE;
400 page->fill = TRUE;
401}
402
403static void
404gtk_notebook_page_finalize (GObject *object)
405{
406 GtkNotebookPage *page = GTK_NOTEBOOK_PAGE (object);
407
408 g_clear_object (&page->child);
409 g_clear_object (&page->tab_label);
410 g_clear_object (&page->menu_label);
411
412 g_free (mem: page->tab_text);
413 g_free (mem: page->menu_text);
414
415 G_OBJECT_CLASS (gtk_notebook_page_parent_class)->finalize (object);
416}
417
418
419static void
420gtk_notebook_page_set_property (GObject *object,
421 guint property_id,
422 const GValue *value,
423 GParamSpec *pspec)
424{
425 GtkNotebookPage *page = GTK_NOTEBOOK_PAGE (object);
426
427 switch (property_id)
428 {
429 case CHILD_PROP_CHILD:
430 g_set_object (&page->child, g_value_get_object (value));
431 break;
432
433 case CHILD_PROP_TAB:
434 g_set_object (&page->tab_label, g_value_get_object (value));
435 page->default_tab = page->tab_label == NULL;
436 break;
437
438 case CHILD_PROP_MENU:
439 g_set_object (&page->menu_label, g_value_get_object (value));
440 page->default_menu = page->menu_label == NULL;
441 break;
442
443 case CHILD_PROP_TAB_LABEL:
444 g_free (mem: page->tab_text);
445 page->tab_text = g_value_dup_string (value);
446 if (page->default_tab && GTK_IS_LABEL (page->tab_label))
447 gtk_label_set_label (GTK_LABEL (page->tab_label), str: page->tab_text);
448 break;
449
450 case CHILD_PROP_MENU_LABEL:
451 g_free (mem: page->menu_text);
452 page->menu_text = g_value_dup_string (value);
453 if (page->default_menu && GTK_IS_LABEL (page->menu_label))
454 gtk_label_set_label (GTK_LABEL (page->menu_label), str: page->menu_text);
455 break;
456
457 case CHILD_PROP_POSITION:
458 {
459 GtkNotebook *notebook = NULL;
460 if (page->tab_widget)
461 notebook = GTK_NOTEBOOK (g_object_get_data (G_OBJECT (page->tab_widget), "notebook"));
462
463 if (notebook)
464 gtk_notebook_reorder_child (notebook, child: page->child, position: g_value_get_int (value));
465 }
466 break;
467
468 case CHILD_PROP_TAB_EXPAND:
469 if (page->expand != g_value_get_boolean (value))
470 {
471 page->expand = g_value_get_boolean (value);
472 g_object_notify_by_pspec (object, pspec);
473 }
474 break;
475
476 case CHILD_PROP_TAB_FILL:
477 if (page->fill != g_value_get_boolean (value))
478 {
479 page->fill = g_value_get_boolean (value);
480 g_object_notify_by_pspec (object, pspec);
481 }
482 break;
483
484 case CHILD_PROP_REORDERABLE:
485 if (page->reorderable != g_value_get_boolean (value))
486 {
487 page->reorderable = g_value_get_boolean (value);
488 g_object_notify_by_pspec (object, pspec);
489 }
490 break;
491
492 case CHILD_PROP_DETACHABLE:
493 if (page->detachable != g_value_get_boolean (value))
494 {
495 page->detachable = g_value_get_boolean (value);
496 g_object_notify_by_pspec (object, pspec);
497 }
498 break;
499
500 default:
501 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
502 break;
503 }
504}
505
506static void
507gtk_notebook_page_get_property (GObject *object,
508 guint property_id,
509 GValue *value,
510 GParamSpec *pspec)
511{
512 GtkNotebookPage *page = GTK_NOTEBOOK_PAGE (object);
513
514 switch (property_id)
515 {
516 case CHILD_PROP_CHILD:
517 g_value_set_object (value, v_object: page->child);
518 break;
519
520 case CHILD_PROP_TAB:
521 g_value_set_object (value, v_object: page->tab_label);
522 break;
523
524 case CHILD_PROP_MENU:
525 g_value_set_object (value, v_object: page->menu_label);
526 break;
527
528 case CHILD_PROP_TAB_LABEL:
529 g_value_set_string (value, v_string: page->tab_text);
530 break;
531
532 case CHILD_PROP_MENU_LABEL:
533 g_value_set_string (value, v_string: page->menu_text);
534 break;
535
536 case CHILD_PROP_POSITION:
537 {
538 GtkNotebook *notebook = NULL;
539 if (page->tab_widget)
540 notebook = GTK_NOTEBOOK (g_object_get_data (G_OBJECT (page->tab_widget), "notebook"));
541
542 if (notebook)
543 g_value_set_int (value, v_int: g_list_index (list: notebook->children, data: page));
544 }
545 break;
546
547 case CHILD_PROP_TAB_EXPAND:
548 g_value_set_boolean (value, v_boolean: page->expand);
549 break;
550
551 case CHILD_PROP_TAB_FILL:
552 g_value_set_boolean (value, v_boolean: page->fill);
553 break;
554
555 case CHILD_PROP_REORDERABLE:
556 g_value_set_boolean (value, v_boolean: page->reorderable);
557 break;
558
559 case CHILD_PROP_DETACHABLE:
560 g_value_set_boolean (value, v_boolean: page->detachable);
561 break;
562
563 default:
564 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
565 break;
566 }
567}
568
569static void
570gtk_notebook_page_class_init (GtkNotebookPageClass *class)
571{
572 GObjectClass *object_class = G_OBJECT_CLASS (class);
573
574 object_class->finalize = gtk_notebook_page_finalize;
575 object_class->get_property = gtk_notebook_page_get_property;
576 object_class->set_property = gtk_notebook_page_set_property;
577
578 /**
579 * GtkNotebookPage:child: (attributes org.gtk.Property.get=gtk_notebook_page_get_child)
580 *
581 * The child for this page.
582 */
583 g_object_class_install_property (oclass: object_class,
584 property_id: CHILD_PROP_CHILD,
585 pspec: g_param_spec_object (name: "child",
586 P_("Child"),
587 P_("The child for this page"),
588 GTK_TYPE_WIDGET,
589 GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
590
591 /**
592 * GtkNotebookPage:tab:
593 *
594 * The tab widget for tihs page.
595 */
596 g_object_class_install_property (oclass: object_class,
597 property_id: CHILD_PROP_TAB,
598 pspec: g_param_spec_object (name: "tab",
599 P_("Tab"),
600 P_("The tab widget for this page"),
601 GTK_TYPE_WIDGET,
602 GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
603
604 /**
605 * GtkNotebookPage:menu:
606 *
607 * The label widget displayed in the childs menu entry.
608 */
609 g_object_class_install_property (oclass: object_class,
610 property_id: CHILD_PROP_MENU,
611 pspec: g_param_spec_object (name: "menu",
612 P_("Menu"),
613 P_("The label widget displayed in the child’s menu entry"),
614 GTK_TYPE_WIDGET,
615 GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
616
617 /**
618 * GtkNotebookPage:tab-label:
619 *
620 * The text of the tab widget.
621 */
622 g_object_class_install_property (oclass: object_class,
623 property_id: CHILD_PROP_TAB_LABEL,
624 pspec: g_param_spec_string (name: "tab-label",
625 P_("Tab label"),
626 P_("The text of the tab widget"),
627 NULL,
628 GTK_PARAM_READWRITE));
629
630 /**
631 * GtkNotebookPage:menu-label:
632 *
633 * The text of the menu widget.
634 */
635 g_object_class_install_property (oclass: object_class,
636 property_id: CHILD_PROP_MENU_LABEL,
637 pspec: g_param_spec_string (name: "menu-label",
638 P_("Menu label"),
639 P_("The text of the menu widget"),
640 NULL,
641 GTK_PARAM_READWRITE));
642
643 /**
644 * GtkNotebookPage:position:
645 *
646 * The index of the child in the parent.
647 */
648 g_object_class_install_property (oclass: object_class,
649 property_id: CHILD_PROP_POSITION,
650 pspec: g_param_spec_int (name: "position",
651 P_("Position"),
652 P_("The index of the child in the parent"),
653 minimum: -1, G_MAXINT, default_value: 0,
654 GTK_PARAM_READWRITE));
655
656 /**
657 * GtkNotebookPage:tab-expand:
658 *
659 * Whether to expand the childs tab.
660 */
661 g_object_class_install_property (oclass: object_class,
662 property_id: CHILD_PROP_TAB_EXPAND,
663 pspec: g_param_spec_boolean (name: "tab-expand",
664 P_("Tab expand"),
665 P_("Whether to expand the child’s tab"),
666 FALSE,
667 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
668
669 /**
670 * GtkNotebookPage:tab-fill:
671 *
672 * Whether the childs tab should fill the allocated area.
673 */
674 g_object_class_install_property (oclass: object_class,
675 property_id: CHILD_PROP_TAB_FILL,
676 pspec: g_param_spec_boolean (name: "tab-fill",
677 P_("Tab fill"),
678 P_("Whether the child’s tab should fill the allocated area"),
679 TRUE,
680 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
681
682 /**
683 * GtkNotebookPage:reorderable:
684 *
685 * Whether the tab is reorderable by user action.
686 */
687 g_object_class_install_property (oclass: object_class,
688 property_id: CHILD_PROP_REORDERABLE,
689 pspec: g_param_spec_boolean (name: "reorderable",
690 P_("Tab reorderable"),
691 P_("Whether the tab is reorderable by user action"),
692 FALSE,
693 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
694
695 /**
696 * GtkNotebookPage:detachable:
697 *
698 * Whether the tab is detachable.
699 */
700 g_object_class_install_property (oclass: object_class,
701 property_id: CHILD_PROP_DETACHABLE,
702 pspec: g_param_spec_boolean (name: "detachable",
703 P_("Tab detachable"),
704 P_("Whether the tab is detachable"),
705 FALSE,
706 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
707
708}
709
710#define GTK_TYPE_NOTEBOOK_ROOT_CONTENT (gtk_notebook_root_content_get_type ())
711#define GTK_NOTEBOOK_ROOT_CONTENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_NOTEBOOK_ROOT_CONTENT, GtkNotebookRootContent))
712#define GTK_IS_NOTEBOOK_ROOT_CONTENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_NOTEBOOK_ROOT_CONTENT))
713
714typedef struct _GtkNotebookRootContent GtkNotebookRootContent;
715typedef struct _GtkNotebookRootContentClass GtkNotebookRootContentClass;
716
717struct _GtkNotebookRootContent
718{
719 GdkContentProvider parent_instance;
720
721 GtkNotebook *notebook;
722};
723
724struct _GtkNotebookRootContentClass
725{
726 GdkContentProviderClass parent_class;
727};
728
729static GdkContentFormats *
730gtk_notebook_root_content_ref_formats (GdkContentProvider *provider)
731{
732 return gdk_content_formats_new (mime_types: (const char *[1]) { "application/x-rootwindow-drop" }, n_mime_types: 1);
733}
734
735GType gtk_notebook_root_content_get_type (void);
736
737G_DEFINE_TYPE (GtkNotebookRootContent, gtk_notebook_root_content, GDK_TYPE_CONTENT_PROVIDER)
738
739static void
740gtk_notebook_root_content_write_mime_type_async (GdkContentProvider *provider,
741 const char *mime_type,
742 GOutputStream *stream,
743 int io_priority,
744 GCancellable *cancellable,
745 GAsyncReadyCallback callback,
746 gpointer user_data)
747{
748 GtkNotebookRootContent *self = GTK_NOTEBOOK_ROOT_CONTENT (provider);
749 GTask *task;
750
751 self->notebook->rootwindow_drop = TRUE;
752
753 task = g_task_new (source_object: self, cancellable, callback, callback_data: user_data);
754 g_task_set_priority (task, priority: io_priority);
755 g_task_set_source_tag (task, gtk_notebook_root_content_write_mime_type_async);
756 g_task_return_boolean (task, TRUE);
757 g_object_unref (object: task);
758}
759
760static gboolean
761gtk_notebook_root_content_write_mime_type_finish (GdkContentProvider *provider,
762 GAsyncResult *result,
763 GError **error)
764{
765 return g_task_propagate_boolean (G_TASK (result), error);
766}
767
768static void
769gtk_notebook_root_content_finalize (GObject *object)
770{
771 GtkNotebookRootContent *self = GTK_NOTEBOOK_ROOT_CONTENT (object);
772
773 g_object_unref (object: self->notebook);
774
775 G_OBJECT_CLASS (gtk_notebook_root_content_parent_class)->finalize (object);
776}
777
778static void
779gtk_notebook_root_content_class_init (GtkNotebookRootContentClass *class)
780{
781 GObjectClass *object_class = G_OBJECT_CLASS (class);
782 GdkContentProviderClass *provider_class = GDK_CONTENT_PROVIDER_CLASS (class);
783
784 object_class->finalize = gtk_notebook_root_content_finalize;
785
786 provider_class->ref_formats = gtk_notebook_root_content_ref_formats;
787 provider_class->write_mime_type_async = gtk_notebook_root_content_write_mime_type_async;
788 provider_class->write_mime_type_finish = gtk_notebook_root_content_write_mime_type_finish;
789}
790
791static void
792gtk_notebook_root_content_init (GtkNotebookRootContent *self)
793{
794}
795
796static GdkContentProvider *
797gtk_notebook_root_content_new (GtkNotebook *notebook)
798{
799 GtkNotebookRootContent *result;
800
801 result = g_object_new (GTK_TYPE_NOTEBOOK_ROOT_CONTENT, NULL);
802
803 result->notebook = g_object_ref (notebook);
804
805 return GDK_CONTENT_PROVIDER (result);
806}
807
808/*** GtkNotebook Methods ***/
809static gboolean gtk_notebook_select_page (GtkNotebook *notebook,
810 gboolean move_focus);
811static gboolean gtk_notebook_focus_tab (GtkNotebook *notebook,
812 GtkNotebookTab type);
813static gboolean gtk_notebook_change_current_page (GtkNotebook *notebook,
814 int offset);
815static void gtk_notebook_move_focus_out (GtkNotebook *notebook,
816 GtkDirectionType direction_type);
817static gboolean gtk_notebook_reorder_tab (GtkNotebook *notebook,
818 GtkDirectionType direction_type,
819 gboolean move_to_last);
820static void gtk_notebook_remove_tab_label (GtkNotebook *notebook,
821 GtkNotebookPage *page);
822
823/*** GObject Methods ***/
824static void gtk_notebook_set_property (GObject *object,
825 guint prop_id,
826 const GValue *value,
827 GParamSpec *pspec);
828static void gtk_notebook_get_property (GObject *object,
829 guint prop_id,
830 GValue *value,
831 GParamSpec *pspec);
832static void gtk_notebook_finalize (GObject *object);
833static void gtk_notebook_dispose (GObject *object);
834
835/*** GtkWidget Methods ***/
836static void gtk_notebook_unmap (GtkWidget *widget);
837static void gtk_notebook_popup_menu (GtkWidget *widget,
838 const char *action_name,
839 GVariant *parameters);
840static void gtk_notebook_motion (GtkEventController *controller,
841 double x,
842 double y,
843 gpointer user_data);
844static void gtk_notebook_state_flags_changed (GtkWidget *widget,
845 GtkStateFlags previous_state);
846static void gtk_notebook_direction_changed (GtkWidget *widget,
847 GtkTextDirection previous_direction);
848static gboolean gtk_notebook_focus (GtkWidget *widget,
849 GtkDirectionType direction);
850static gboolean gtk_notebook_grab_focus (GtkWidget *widget);
851static void gtk_notebook_set_focus_child (GtkWidget *widget,
852 GtkWidget *child);
853
854/*** Drag and drop Methods ***/
855static void gtk_notebook_dnd_finished_cb (GdkDrag *drag,
856 GtkWidget *widget);
857static void gtk_notebook_drag_cancel_cb (GdkDrag *drag,
858 GdkDragCancelReason reason,
859 GtkWidget *widget);
860static GdkDragAction gtk_notebook_drag_motion(GtkDropTarget *dest,
861 double x,
862 double y,
863 GtkNotebook *notebook);
864static gboolean gtk_notebook_drag_drop (GtkDropTarget *dest,
865 const GValue *value,
866 double x,
867 double y,
868 GtkNotebook *notebook);
869
870static void gtk_notebook_remove (GtkNotebook *notebook,
871 GtkWidget *widget);
872
873/*** GtkNotebook Methods ***/
874static int gtk_notebook_real_insert_page (GtkNotebook *notebook,
875 GtkWidget *child,
876 GtkWidget *tab_label,
877 GtkWidget *menu_label,
878 int position);
879
880static GtkNotebook *gtk_notebook_create_window (GtkNotebook *notebook,
881 GtkWidget *page);
882
883static void gtk_notebook_measure_tabs (GtkGizmo *gizmo,
884 GtkOrientation orientation,
885 int for_size,
886 int *minimum,
887 int *natural,
888 int *minimum_baseline,
889 int *natural_baseline);
890static void gtk_notebook_allocate_tabs (GtkGizmo *gizmo,
891 int width,
892 int height,
893 int baseline);
894static void gtk_notebook_snapshot_tabs (GtkGizmo *gizmo,
895 GtkSnapshot *snapshot);
896
897/*** GtkNotebook Private Functions ***/
898static void gtk_notebook_real_remove (GtkNotebook *notebook,
899 GList *list);
900static void gtk_notebook_update_labels (GtkNotebook *notebook);
901static int gtk_notebook_timer (GtkNotebook *notebook);
902static void gtk_notebook_set_scroll_timer (GtkNotebook *notebook);
903static int gtk_notebook_page_compare (gconstpointer a,
904 gconstpointer b);
905static GList* gtk_notebook_find_child (GtkNotebook *notebook,
906 GtkWidget *child);
907static GList * gtk_notebook_search_page (GtkNotebook *notebook,
908 GList *list,
909 int direction,
910 gboolean find_visible);
911static void gtk_notebook_child_reordered (GtkNotebook *notebook,
912 GtkNotebookPage *page);
913static int gtk_notebook_insert_notebook_page (GtkNotebook *notebook,
914 GtkNotebookPage *page,
915 int position);
916
917/*** GtkNotebook Size Allocate Functions ***/
918static void gtk_notebook_pages_allocate (GtkNotebook *notebook,
919 int width,
920 int height);
921static void gtk_notebook_calc_tabs (GtkNotebook *notebook,
922 GList *start,
923 GList **end,
924 int *tab_space,
925 guint direction);
926
927/*** GtkNotebook Page Switch Methods ***/
928static void gtk_notebook_real_switch_page (GtkNotebook *notebook,
929 GtkWidget *child,
930 guint page_num);
931
932/*** GtkNotebook Page Switch Functions ***/
933static void gtk_notebook_switch_page (GtkNotebook *notebook,
934 GtkNotebookPage *page);
935static int gtk_notebook_page_select (GtkNotebook *notebook,
936 gboolean move_focus);
937static void gtk_notebook_switch_focus_tab (GtkNotebook *notebook,
938 GList *new_child);
939static void gtk_notebook_menu_switch_page (GtkWidget *widget,
940 GtkNotebookPage *page);
941
942/*** GtkNotebook Menu Functions ***/
943static void gtk_notebook_menu_item_create (GtkNotebook *notebook,
944 GtkNotebookPage *page);
945static void gtk_notebook_menu_item_recreate (GtkNotebook *notebook,
946 GList *list);
947static void gtk_notebook_menu_label_unparent (GtkWidget *widget);
948
949static void gtk_notebook_update_tab_pos (GtkNotebook *notebook);
950
951/*** GtkNotebook Private Setters ***/
952static gboolean gtk_notebook_mnemonic_activate_switch_page (GtkWidget *child,
953 gboolean overload,
954 gpointer data);
955
956static gboolean focus_tabs_in (GtkNotebook *notebook);
957static gboolean focus_child_in (GtkNotebook *notebook,
958 GtkDirectionType direction);
959
960static void stop_scrolling (GtkNotebook *notebook);
961static void do_detach_tab (GtkNotebook *from,
962 GtkNotebook *to,
963 GtkWidget *child);
964
965/* GtkBuildable */
966static void gtk_notebook_buildable_init (GtkBuildableIface *iface);
967static void gtk_notebook_buildable_add_child (GtkBuildable *buildable,
968 GtkBuilder *builder,
969 GObject *child,
970 const char *type);
971
972static void gtk_notebook_gesture_pressed (GtkGestureClick *gesture,
973 int n_press,
974 double x,
975 double y,
976 gpointer user_data);
977static void gtk_notebook_gesture_released (GtkGestureClick *gesture,
978 int n_press,
979 double x,
980 double y,
981 gpointer user_data);
982static void gtk_notebook_gesture_cancel (GtkGestureClick *gesture,
983 GdkEventSequence *sequence,
984 GtkNotebook *notebook);
985
986static guint notebook_signals[LAST_SIGNAL] = { 0 };
987
988G_DEFINE_TYPE_WITH_CODE (GtkNotebook, gtk_notebook, GTK_TYPE_WIDGET,
989 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
990 gtk_notebook_buildable_init))
991
992static void
993add_tab_bindings (GtkWidgetClass *widget_class,
994 GdkModifierType modifiers,
995 GtkDirectionType direction)
996{
997 gtk_widget_class_add_binding_signal (widget_class,
998 GDK_KEY_Tab, mods: modifiers,
999 signal: "move_focus_out",
1000 format_string: "(i)", direction);
1001 gtk_widget_class_add_binding_signal (widget_class,
1002 GDK_KEY_KP_Tab, mods: modifiers,
1003 signal: "move_focus_out",
1004 format_string: "(i)", direction);
1005}
1006
1007static void
1008add_arrow_bindings (GtkWidgetClass *widget_class,
1009 guint keysym,
1010 GtkDirectionType direction)
1011{
1012 guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
1013
1014 gtk_widget_class_add_binding_signal (widget_class,
1015 keyval: keysym, mods: GDK_CONTROL_MASK,
1016 signal: "move_focus_out",
1017 format_string: "(i)", direction);
1018 gtk_widget_class_add_binding_signal (widget_class,
1019 keyval: keypad_keysym, mods: GDK_CONTROL_MASK,
1020 signal: "move_focus_out",
1021 format_string: "(i)", direction);
1022}
1023
1024static void
1025add_reorder_bindings (GtkWidgetClass *widget_class,
1026 guint keysym,
1027 GtkDirectionType direction,
1028 gboolean move_to_last)
1029{
1030 guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
1031
1032 gtk_widget_class_add_binding_signal (widget_class,
1033 keyval: keysym, mods: GDK_ALT_MASK,
1034 signal: "reorder_tab",
1035 format_string: "(ib)", direction, move_to_last);
1036 gtk_widget_class_add_binding_signal (widget_class,
1037 keyval: keypad_keysym, mods: GDK_ALT_MASK,
1038 signal: "reorder_tab",
1039 format_string: "(ib)", direction, move_to_last);
1040}
1041
1042static gboolean
1043gtk_object_handled_accumulator (GSignalInvocationHint *ihint,
1044 GValue *return_accu,
1045 const GValue *handler_return,
1046 gpointer dummy)
1047{
1048 gboolean continue_emission;
1049 GObject *object;
1050
1051 object = g_value_get_object (value: handler_return);
1052 g_value_set_object (value: return_accu, v_object: object);
1053 continue_emission = !object;
1054
1055 return continue_emission;
1056}
1057
1058static void
1059gtk_notebook_compute_expand (GtkWidget *widget,
1060 gboolean *hexpand_p,
1061 gboolean *vexpand_p)
1062{
1063 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1064 gboolean hexpand;
1065 gboolean vexpand;
1066 GList *list;
1067 GtkNotebookPage *page;
1068
1069 hexpand = FALSE;
1070 vexpand = FALSE;
1071
1072 for (list = notebook->children; list; list = list->next)
1073 {
1074 page = list->data;
1075
1076 hexpand = hexpand ||
1077 gtk_widget_compute_expand (widget: page->child, orientation: GTK_ORIENTATION_HORIZONTAL);
1078
1079 vexpand = vexpand ||
1080 gtk_widget_compute_expand (widget: page->child, orientation: GTK_ORIENTATION_VERTICAL);
1081
1082 if (hexpand & vexpand)
1083 break;
1084 }
1085
1086 *hexpand_p = hexpand;
1087 *vexpand_p = vexpand;
1088}
1089
1090static void
1091gtk_notebook_class_init (GtkNotebookClass *class)
1092{
1093 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1094 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
1095
1096 gobject_class->set_property = gtk_notebook_set_property;
1097 gobject_class->get_property = gtk_notebook_get_property;
1098 gobject_class->finalize = gtk_notebook_finalize;
1099 gobject_class->dispose = gtk_notebook_dispose;
1100
1101 widget_class->unmap = gtk_notebook_unmap;
1102 widget_class->state_flags_changed = gtk_notebook_state_flags_changed;
1103 widget_class->direction_changed = gtk_notebook_direction_changed;
1104 widget_class->focus = gtk_notebook_focus;
1105 widget_class->grab_focus = gtk_notebook_grab_focus;
1106 widget_class->set_focus_child = gtk_notebook_set_focus_child;
1107 widget_class->compute_expand = gtk_notebook_compute_expand;
1108
1109 class->switch_page = gtk_notebook_real_switch_page;
1110 class->insert_page = gtk_notebook_real_insert_page;
1111
1112 class->focus_tab = gtk_notebook_focus_tab;
1113 class->select_page = gtk_notebook_select_page;
1114 class->change_current_page = gtk_notebook_change_current_page;
1115 class->move_focus_out = gtk_notebook_move_focus_out;
1116 class->reorder_tab = gtk_notebook_reorder_tab;
1117 class->create_window = gtk_notebook_create_window;
1118
1119 /**
1120 * GtkNotebook:page: (attributes org.gtk.Property.get=gtk_notebook_get_current_page org.gtk.Property.set=gtk_notebook_set_current_page)
1121 *
1122 * The index of the current page.
1123 */
1124 properties[PROP_PAGE] =
1125 g_param_spec_int (name: "page",
1126 P_("Page"),
1127 P_("The index of the current page"),
1128 minimum: -1, G_MAXINT,
1129 default_value: -1,
1130 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1131
1132 /**
1133 * GtkNotebook:tab-pos: (attributes org.gtk.Property.get=gtk_notebook_get_tab_pos org.gtk.Property.set=gtk_notebook_set_tab_pos)
1134 *
1135 * Which side of the notebook holds the tabs.
1136 */
1137 properties[PROP_TAB_POS] =
1138 g_param_spec_enum (name: "tab-pos",
1139 P_("Tab Position"),
1140 P_("Which side of the notebook holds the tabs"),
1141 enum_type: GTK_TYPE_POSITION_TYPE,
1142 default_value: GTK_POS_TOP,
1143 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1144
1145 /**
1146 * GtkNotebook:show-tabs: (attributes org.gtk.Property.get=gtk_notebook_get_show_tabs org.gtk.Property.set=gtk_notebook_set_show_tabs)
1147 *
1148 * Whether tabs should be shown.
1149 */
1150 properties[PROP_SHOW_TABS] =
1151 g_param_spec_boolean (name: "show-tabs",
1152 P_("Show Tabs"),
1153 P_("Whether tabs should be shown"),
1154 TRUE,
1155 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1156
1157 /**
1158 * GtkNotebook:show-border: (attributes org.gtk.Property.get=gtk_notebook_get_show_border org.gtk.Property.set=gtk_notebook_set_show_border)
1159 *
1160 * Whether the border should be shown.
1161 */
1162 properties[PROP_SHOW_BORDER] =
1163 g_param_spec_boolean (name: "show-border",
1164 P_("Show Border"),
1165 P_("Whether the border should be shown"),
1166 TRUE,
1167 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1168
1169 /**
1170 * GtkNotebook:scrollable: (attributes org.gtk.Property.get=gtk_notebook_get_scrollable org.gtk.Property.set=gtk_notebook_set_scrollable)
1171 *
1172 * If %TRUE, scroll arrows are added if there are too many pages to fit.
1173 */
1174 properties[PROP_SCROLLABLE] =
1175 g_param_spec_boolean (name: "scrollable",
1176 P_("Scrollable"),
1177 P_("If TRUE, scroll arrows are added if there are too many tabs to fit"),
1178 FALSE,
1179 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1180
1181 /**
1182 * GtkNotebook:enable-popup:
1183 *
1184 * If %TRUE, pressing the right mouse button on the notebook shows a page switching menu.
1185 */
1186 properties[PROP_ENABLE_POPUP] =
1187 g_param_spec_boolean (name: "enable-popup",
1188 P_("Enable Popup"),
1189 P_("If TRUE, pressing the right mouse button on the notebook pops up a menu that you can use to go to a page"),
1190 FALSE,
1191 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1192
1193 /**
1194 * GtkNotebook:group-name: (attributes org.gtk.Property.get=gtk_notebook_get_group_name org.gtk.Property.set=gtk_notebook_set_group_name)
1195 *
1196 * Group name for tab drag and drop.
1197 */
1198 properties[PROP_GROUP_NAME] =
1199 g_param_spec_string (name: "group-name",
1200 P_("Group Name"),
1201 P_("Group name for tab drag and drop"),
1202 NULL,
1203 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1204
1205 /**
1206 * GtkNotebook:pages: (attributes org.gtk.Property.get=gtk_notebook_get_pages)
1207 *
1208 * A selection model with the pages.
1209 */
1210 properties[PROP_PAGES] =
1211 g_param_spec_object (name: "pages",
1212 P_("Pages"),
1213 P_("The pages of the notebook."),
1214 G_TYPE_LIST_MODEL,
1215 GTK_PARAM_READABLE);
1216
1217 g_object_class_install_properties (oclass: gobject_class, n_pspecs: LAST_PROP, pspecs: properties);
1218
1219 /**
1220 * GtkNotebook::switch-page:
1221 * @notebook: the object which received the signal.
1222 * @page: the new current page
1223 * @page_num: the index of the page
1224 *
1225 * Emitted when the user or a function changes the current page.
1226 */
1227 notebook_signals[SWITCH_PAGE] =
1228 g_signal_new (I_("switch-page"),
1229 G_TYPE_FROM_CLASS (gobject_class),
1230 signal_flags: G_SIGNAL_RUN_LAST,
1231 G_STRUCT_OFFSET (GtkNotebookClass, switch_page),
1232 NULL, NULL,
1233 c_marshaller: _gtk_marshal_VOID__OBJECT_UINT,
1234 G_TYPE_NONE, n_params: 2,
1235 GTK_TYPE_WIDGET,
1236 G_TYPE_UINT);
1237 g_signal_set_va_marshaller (signal_id: notebook_signals[SWITCH_PAGE],
1238 G_TYPE_FROM_CLASS (gobject_class),
1239 va_marshaller: _gtk_marshal_VOID__OBJECT_UINTv);
1240 notebook_signals[FOCUS_TAB] =
1241 g_signal_new (I_("focus-tab"),
1242 G_TYPE_FROM_CLASS (gobject_class),
1243 signal_flags: G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1244 G_STRUCT_OFFSET (GtkNotebookClass, focus_tab),
1245 NULL, NULL,
1246 c_marshaller: _gtk_marshal_BOOLEAN__ENUM,
1247 G_TYPE_BOOLEAN, n_params: 1,
1248 GTK_TYPE_NOTEBOOK_TAB);
1249 g_signal_set_va_marshaller (signal_id: notebook_signals[FOCUS_TAB],
1250 G_TYPE_FROM_CLASS (gobject_class),
1251 va_marshaller: _gtk_marshal_BOOLEAN__ENUMv);
1252 notebook_signals[SELECT_PAGE] =
1253 g_signal_new (I_("select-page"),
1254 G_TYPE_FROM_CLASS (gobject_class),
1255 signal_flags: G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1256 G_STRUCT_OFFSET (GtkNotebookClass, select_page),
1257 NULL, NULL,
1258 c_marshaller: _gtk_marshal_BOOLEAN__BOOLEAN,
1259 G_TYPE_BOOLEAN, n_params: 1,
1260 G_TYPE_BOOLEAN);
1261 g_signal_set_va_marshaller (signal_id: notebook_signals[SELECT_PAGE],
1262 G_TYPE_FROM_CLASS (gobject_class),
1263 va_marshaller: _gtk_marshal_BOOLEAN__BOOLEANv);
1264 notebook_signals[CHANGE_CURRENT_PAGE] =
1265 g_signal_new (I_("change-current-page"),
1266 G_TYPE_FROM_CLASS (gobject_class),
1267 signal_flags: G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1268 G_STRUCT_OFFSET (GtkNotebookClass, change_current_page),
1269 NULL, NULL,
1270 c_marshaller: _gtk_marshal_BOOLEAN__INT,
1271 G_TYPE_BOOLEAN, n_params: 1,
1272 G_TYPE_INT);
1273 g_signal_set_va_marshaller (signal_id: notebook_signals[CHANGE_CURRENT_PAGE],
1274 G_TYPE_FROM_CLASS (gobject_class),
1275 va_marshaller: _gtk_marshal_BOOLEAN__INTv);
1276 notebook_signals[MOVE_FOCUS_OUT] =
1277 g_signal_new (I_("move-focus-out"),
1278 G_TYPE_FROM_CLASS (gobject_class),
1279 signal_flags: G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1280 G_STRUCT_OFFSET (GtkNotebookClass, move_focus_out),
1281 NULL, NULL,
1282 NULL,
1283 G_TYPE_NONE, n_params: 1,
1284 GTK_TYPE_DIRECTION_TYPE);
1285 notebook_signals[REORDER_TAB] =
1286 g_signal_new (I_("reorder-tab"),
1287 G_TYPE_FROM_CLASS (gobject_class),
1288 signal_flags: G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1289 G_STRUCT_OFFSET (GtkNotebookClass, reorder_tab),
1290 NULL, NULL,
1291 c_marshaller: _gtk_marshal_BOOLEAN__ENUM_BOOLEAN,
1292 G_TYPE_BOOLEAN, n_params: 2,
1293 GTK_TYPE_DIRECTION_TYPE,
1294 G_TYPE_BOOLEAN);
1295 g_signal_set_va_marshaller (signal_id: notebook_signals[REORDER_TAB],
1296 G_TYPE_FROM_CLASS (gobject_class),
1297 va_marshaller: _gtk_marshal_BOOLEAN__ENUM_BOOLEANv);
1298 /**
1299 * GtkNotebook::page-reordered:
1300 * @notebook: the `GtkNotebook`
1301 * @child: the child `GtkWidget` affected
1302 * @page_num: the new page number for @child
1303 *
1304 * the ::page-reordered signal is emitted in the notebook
1305 * right after a page has been reordered.
1306 */
1307 notebook_signals[PAGE_REORDERED] =
1308 g_signal_new (I_("page-reordered"),
1309 G_TYPE_FROM_CLASS (gobject_class),
1310 signal_flags: G_SIGNAL_RUN_LAST,
1311 G_STRUCT_OFFSET (GtkNotebookClass, page_reordered),
1312 NULL, NULL,
1313 c_marshaller: _gtk_marshal_VOID__OBJECT_UINT,
1314 G_TYPE_NONE, n_params: 2,
1315 GTK_TYPE_WIDGET,
1316 G_TYPE_UINT);
1317 g_signal_set_va_marshaller (signal_id: notebook_signals[PAGE_REORDERED],
1318 G_TYPE_FROM_CLASS (gobject_class),
1319 va_marshaller: _gtk_marshal_VOID__OBJECT_UINTv);
1320 /**
1321 * GtkNotebook::page-removed:
1322 * @notebook: the `GtkNotebook`
1323 * @child: the child `GtkWidget` affected
1324 * @page_num: the @child page number
1325 *
1326 * the ::page-removed signal is emitted in the notebook
1327 * right after a page is removed from the notebook.
1328 */
1329 notebook_signals[PAGE_REMOVED] =
1330 g_signal_new (I_("page-removed"),
1331 G_TYPE_FROM_CLASS (gobject_class),
1332 signal_flags: G_SIGNAL_RUN_LAST,
1333 G_STRUCT_OFFSET (GtkNotebookClass, page_removed),
1334 NULL, NULL,
1335 c_marshaller: _gtk_marshal_VOID__OBJECT_UINT,
1336 G_TYPE_NONE, n_params: 2,
1337 GTK_TYPE_WIDGET,
1338 G_TYPE_UINT);
1339 g_signal_set_va_marshaller (signal_id: notebook_signals[PAGE_REMOVED],
1340 G_TYPE_FROM_CLASS (gobject_class),
1341 va_marshaller: _gtk_marshal_VOID__OBJECT_UINTv);
1342 /**
1343 * GtkNotebook::page-added:
1344 * @notebook: the `GtkNotebook`
1345 * @child: the child `GtkWidget` affected
1346 * @page_num: the new page number for @child
1347 *
1348 * the ::page-added signal is emitted in the notebook
1349 * right after a page is added to the notebook.
1350 */
1351 notebook_signals[PAGE_ADDED] =
1352 g_signal_new (I_("page-added"),
1353 G_TYPE_FROM_CLASS (gobject_class),
1354 signal_flags: G_SIGNAL_RUN_LAST,
1355 G_STRUCT_OFFSET (GtkNotebookClass, page_added),
1356 NULL, NULL,
1357 c_marshaller: _gtk_marshal_VOID__OBJECT_UINT,
1358 G_TYPE_NONE, n_params: 2,
1359 GTK_TYPE_WIDGET,
1360 G_TYPE_UINT);
1361 g_signal_set_va_marshaller (signal_id: notebook_signals[PAGE_ADDED],
1362 G_TYPE_FROM_CLASS (gobject_class),
1363 va_marshaller: _gtk_marshal_VOID__OBJECT_UINTv);
1364
1365 /**
1366 * GtkNotebook::create-window:
1367 * @notebook: the `GtkNotebook` emitting the signal
1368 * @page: the tab of @notebook that is being detached
1369 *
1370 * The ::create-window signal is emitted when a detachable
1371 * tab is dropped on the root window.
1372 *
1373 * A handler for this signal can create a window containing
1374 * a notebook where the tab will be attached. It is also
1375 * responsible for moving/resizing the window and adding the
1376 * necessary properties to the notebook (e.g. the
1377 * `GtkNotebook`:group-name ).
1378 *
1379 * Returns: (nullable) (transfer none): a `GtkNotebook` that
1380 * @page should be added to
1381 */
1382 notebook_signals[CREATE_WINDOW] =
1383 g_signal_new (I_("create-window"),
1384 G_TYPE_FROM_CLASS (gobject_class),
1385 signal_flags: G_SIGNAL_RUN_LAST,
1386 G_STRUCT_OFFSET (GtkNotebookClass, create_window),
1387 accumulator: gtk_object_handled_accumulator, NULL,
1388 c_marshaller: _gtk_marshal_OBJECT__OBJECT,
1389 GTK_TYPE_NOTEBOOK, n_params: 1,
1390 GTK_TYPE_WIDGET);
1391 g_signal_set_va_marshaller (signal_id: notebook_signals[CREATE_WINDOW],
1392 G_TYPE_FROM_CLASS (gobject_class),
1393 va_marshaller: _gtk_marshal_OBJECT__OBJECTv);
1394
1395 /**
1396 * GtkNotebook|menu.popup:
1397 *
1398 * Opens the context menu.
1399 */
1400 gtk_widget_class_install_action (widget_class, action_name: "menu.popup", NULL, activate: gtk_notebook_popup_menu);
1401
1402 gtk_widget_class_add_binding_signal (widget_class,
1403 GDK_KEY_space, mods: 0,
1404 signal: "select-page",
1405 format_string: "(b)", FALSE);
1406 gtk_widget_class_add_binding_signal (widget_class,
1407 GDK_KEY_KP_Space, mods: 0,
1408 signal: "select-page",
1409 format_string: "(b)", FALSE);
1410
1411 gtk_widget_class_add_binding_signal (widget_class,
1412 GDK_KEY_Home, mods: 0,
1413 signal: "focus-tab",
1414 format_string: "(i)", GTK_NOTEBOOK_TAB_FIRST);
1415 gtk_widget_class_add_binding_signal (widget_class,
1416 GDK_KEY_KP_Home, mods: 0,
1417 signal: "focus-tab",
1418 format_string: "(i)", GTK_NOTEBOOK_TAB_FIRST);
1419 gtk_widget_class_add_binding_signal (widget_class,
1420 GDK_KEY_End, mods: 0,
1421 signal: "focus-tab",
1422 format_string: "(i)", GTK_NOTEBOOK_TAB_LAST);
1423 gtk_widget_class_add_binding_signal (widget_class,
1424 GDK_KEY_KP_End, mods: 0,
1425 signal: "focus-tab",
1426 format_string: "(i)", GTK_NOTEBOOK_TAB_LAST);
1427
1428 gtk_widget_class_add_binding_action (widget_class,
1429 GDK_KEY_F10, mods: GDK_SHIFT_MASK,
1430 action_name: "menu.popup",
1431 NULL);
1432 gtk_widget_class_add_binding_action (widget_class,
1433 GDK_KEY_Menu, mods: 0,
1434 action_name: "menu.popup",
1435 NULL);
1436
1437 gtk_widget_class_add_binding_signal (widget_class,
1438 GDK_KEY_Page_Up, mods: GDK_CONTROL_MASK,
1439 signal: "change-current-page",
1440 format_string: "(i)", -1);
1441 gtk_widget_class_add_binding_signal (widget_class,
1442 GDK_KEY_Page_Down, mods: GDK_CONTROL_MASK,
1443 signal: "change-current-page",
1444 format_string: "(i)", 1);
1445
1446 gtk_widget_class_add_binding_signal (widget_class,
1447 GDK_KEY_Page_Up, mods: GDK_CONTROL_MASK | GDK_ALT_MASK,
1448 signal: "change-current-page",
1449 format_string: "(i)", -1);
1450 gtk_widget_class_add_binding_signal (widget_class,
1451 GDK_KEY_Page_Down, mods: GDK_CONTROL_MASK | GDK_ALT_MASK,
1452 signal: "change-current-page",
1453 format_string: "(i)", 1);
1454
1455 add_arrow_bindings (widget_class, GDK_KEY_Up, direction: GTK_DIR_UP);
1456 add_arrow_bindings (widget_class, GDK_KEY_Down, direction: GTK_DIR_DOWN);
1457 add_arrow_bindings (widget_class, GDK_KEY_Left, direction: GTK_DIR_LEFT);
1458 add_arrow_bindings (widget_class, GDK_KEY_Right, direction: GTK_DIR_RIGHT);
1459
1460 add_reorder_bindings (widget_class, GDK_KEY_Up, direction: GTK_DIR_UP, FALSE);
1461 add_reorder_bindings (widget_class, GDK_KEY_Down, direction: GTK_DIR_DOWN, FALSE);
1462 add_reorder_bindings (widget_class, GDK_KEY_Left, direction: GTK_DIR_LEFT, FALSE);
1463 add_reorder_bindings (widget_class, GDK_KEY_Right, direction: GTK_DIR_RIGHT, FALSE);
1464 add_reorder_bindings (widget_class, GDK_KEY_Home, direction: GTK_DIR_LEFT, TRUE);
1465 add_reorder_bindings (widget_class, GDK_KEY_Home, direction: GTK_DIR_UP, TRUE);
1466 add_reorder_bindings (widget_class, GDK_KEY_End, direction: GTK_DIR_RIGHT, TRUE);
1467 add_reorder_bindings (widget_class, GDK_KEY_End, direction: GTK_DIR_DOWN, TRUE);
1468
1469 add_tab_bindings (widget_class, modifiers: GDK_CONTROL_MASK, direction: GTK_DIR_TAB_FORWARD);
1470 add_tab_bindings (widget_class, modifiers: GDK_CONTROL_MASK | GDK_SHIFT_MASK, direction: GTK_DIR_TAB_BACKWARD);
1471
1472 gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT);
1473 gtk_widget_class_set_css_name (widget_class, I_("notebook"));
1474 gtk_widget_class_set_accessible_role (widget_class, accessible_role: GTK_ACCESSIBLE_ROLE_GROUP);
1475}
1476
1477static void
1478gtk_notebook_init (GtkNotebook *notebook)
1479{
1480 GtkEventController *controller;
1481 GtkGesture *gesture;
1482 GtkLayoutManager *layout;
1483 GtkDropTarget *dest;
1484
1485 notebook->cur_page = NULL;
1486 notebook->children = NULL;
1487 notebook->first_tab = NULL;
1488 notebook->focus_tab = NULL;
1489 notebook->menu = NULL;
1490
1491 notebook->show_tabs = TRUE;
1492 notebook->show_border = TRUE;
1493 notebook->tab_pos = GTK_POS_TOP;
1494 notebook->scrollable = FALSE;
1495 notebook->click_child = ARROW_NONE;
1496 notebook->need_timer = 0;
1497 notebook->child_has_focus = FALSE;
1498 notebook->focus_out = FALSE;
1499
1500 notebook->group = 0;
1501 notebook->pressed_button = 0;
1502 notebook->dnd_timer = 0;
1503 notebook->operation = DRAG_OPERATION_NONE;
1504 notebook->detached_tab = NULL;
1505 notebook->has_scrolled = FALSE;
1506
1507 gtk_widget_set_focusable (GTK_WIDGET (notebook), TRUE);
1508
1509 notebook->header_widget = g_object_new (GTK_TYPE_BOX,
1510 first_property_name: "css-name", "header",
1511 NULL);
1512 gtk_widget_add_css_class (widget: notebook->header_widget, css_class: "top");
1513 gtk_widget_hide (widget: notebook->header_widget);
1514 gtk_widget_set_parent (widget: notebook->header_widget, GTK_WIDGET (notebook));
1515
1516 notebook->tabs_widget = gtk_gizmo_new_with_role (css_name: "tabs",
1517 role: GTK_ACCESSIBLE_ROLE_TAB_LIST,
1518 measure_func: gtk_notebook_measure_tabs,
1519 allocate_func: gtk_notebook_allocate_tabs,
1520 snapshot_func: gtk_notebook_snapshot_tabs,
1521 NULL,
1522 focus_func: (GtkGizmoFocusFunc)gtk_widget_focus_self,
1523 grab_focus_func: (GtkGizmoGrabFocusFunc)gtk_widget_grab_focus_self);
1524 gtk_widget_set_hexpand (widget: notebook->tabs_widget, TRUE);
1525 gtk_box_append (GTK_BOX (notebook->header_widget), child: notebook->tabs_widget);
1526 gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: notebook->tabs_widget),
1527 first_property: GTK_ACCESSIBLE_PROPERTY_LABEL, _("Tab list"),
1528 -1);
1529
1530 notebook->stack_widget = gtk_stack_new ();
1531 gtk_widget_set_hexpand (widget: notebook->stack_widget, TRUE);
1532 gtk_widget_set_vexpand (widget: notebook->stack_widget, TRUE);
1533 gtk_widget_set_parent (widget: notebook->stack_widget, GTK_WIDGET (notebook));
1534
1535 dest = gtk_drop_target_new (GTK_TYPE_NOTEBOOK_PAGE, actions: GDK_ACTION_MOVE);
1536 gtk_drop_target_set_preload (self: dest, TRUE);
1537 g_signal_connect (dest, "motion", G_CALLBACK (gtk_notebook_drag_motion), notebook);
1538 g_signal_connect (dest, "drop", G_CALLBACK (gtk_notebook_drag_drop), notebook);
1539 gtk_widget_add_controller (GTK_WIDGET (notebook->tabs_widget), GTK_EVENT_CONTROLLER (dest));
1540
1541 gesture = gtk_gesture_click_new ();
1542 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), button: 0);
1543 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), phase: GTK_PHASE_CAPTURE);
1544 g_signal_connect (gesture, "pressed", G_CALLBACK (gtk_notebook_gesture_pressed), notebook);
1545 g_signal_connect (gesture, "released", G_CALLBACK (gtk_notebook_gesture_released), notebook);
1546 g_signal_connect (gesture, "cancel", G_CALLBACK (gtk_notebook_gesture_cancel), notebook);
1547 gtk_widget_add_controller (GTK_WIDGET (notebook), GTK_EVENT_CONTROLLER (gesture));
1548
1549 controller = gtk_event_controller_motion_new ();
1550 g_signal_connect (controller, "motion", G_CALLBACK (gtk_notebook_motion), notebook);
1551 gtk_widget_add_controller (GTK_WIDGET (notebook), controller);
1552
1553 gtk_widget_add_css_class (GTK_WIDGET (notebook), css_class: "frame");
1554
1555 layout = gtk_widget_get_layout_manager (GTK_WIDGET (notebook));
1556 gtk_orientable_set_orientation (GTK_ORIENTABLE (layout), orientation: GTK_ORIENTATION_VERTICAL);
1557}
1558
1559static GtkBuildableIface *parent_buildable_iface;
1560
1561static void
1562gtk_notebook_buildable_init (GtkBuildableIface *iface)
1563{
1564 parent_buildable_iface = g_type_interface_peek_parent (g_iface: iface);
1565
1566 iface->add_child = gtk_notebook_buildable_add_child;
1567}
1568
1569static void
1570gtk_notebook_buildable_add_child (GtkBuildable *buildable,
1571 GtkBuilder *builder,
1572 GObject *child,
1573 const char *type)
1574{
1575 GtkNotebook *notebook = GTK_NOTEBOOK (buildable);
1576
1577 if (GTK_IS_NOTEBOOK_PAGE (child))
1578 {
1579 gtk_notebook_insert_notebook_page (notebook, GTK_NOTEBOOK_PAGE (child), position: -1);
1580 }
1581 else if (GTK_IS_WIDGET (child))
1582 {
1583 if (type && strcmp (s1: type, s2: "tab") == 0)
1584 {
1585 GtkWidget * page;
1586
1587 page = gtk_notebook_get_nth_page (notebook, page_num: -1);
1588 /* To set the tab label widget, we must have already a child
1589 * inside the tab container. */
1590 g_assert (page != NULL);
1591 /* warn when Glade tries to overwrite label */
1592 if (gtk_notebook_get_tab_label (notebook, child: page))
1593 g_warning ("Overriding tab label for notebook");
1594 gtk_notebook_set_tab_label (notebook, child: page, GTK_WIDGET (child));
1595 }
1596 else if (type && strcmp (s1: type, s2: "action-start") == 0)
1597 {
1598 gtk_notebook_set_action_widget (notebook, GTK_WIDGET (child), pack_type: GTK_PACK_START);
1599 }
1600 else if (type && strcmp (s1: type, s2: "action-end") == 0)
1601 {
1602 gtk_notebook_set_action_widget (notebook, GTK_WIDGET (child), pack_type: GTK_PACK_END);
1603 }
1604 else if (!type)
1605 gtk_notebook_append_page (notebook, GTK_WIDGET (child), NULL);
1606 else
1607 GTK_BUILDER_WARN_INVALID_CHILD_TYPE (notebook, type);
1608 }
1609 else
1610 {
1611 parent_buildable_iface->add_child (buildable, builder, child, type);
1612 }
1613}
1614
1615static gboolean
1616gtk_notebook_has_current_page (GtkNotebook *notebook)
1617{
1618 return notebook->cur_page &&
1619 gtk_widget_get_visible (widget: notebook->cur_page->child);
1620}
1621
1622static gboolean
1623gtk_notebook_select_page (GtkNotebook *notebook,
1624 gboolean move_focus)
1625{
1626 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && notebook->show_tabs)
1627 {
1628 gtk_notebook_page_select (notebook, move_focus);
1629 return TRUE;
1630 }
1631 else
1632 return FALSE;
1633}
1634
1635static gboolean
1636gtk_notebook_focus_tab (GtkNotebook *notebook,
1637 GtkNotebookTab type)
1638{
1639 GList *list;
1640
1641 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && notebook->show_tabs)
1642 {
1643 switch (type)
1644 {
1645 case GTK_NOTEBOOK_TAB_FIRST:
1646 list = gtk_notebook_search_page (notebook, NULL, direction: STEP_NEXT, TRUE);
1647 if (list)
1648 gtk_notebook_switch_focus_tab (notebook, new_child: list);
1649 break;
1650 case GTK_NOTEBOOK_TAB_LAST:
1651 list = gtk_notebook_search_page (notebook, NULL, direction: STEP_PREV, TRUE);
1652 if (list)
1653 gtk_notebook_switch_focus_tab (notebook, new_child: list);
1654 break;
1655
1656 default:
1657 break;
1658 }
1659
1660 return TRUE;
1661 }
1662 else
1663 return FALSE;
1664}
1665
1666static gboolean
1667gtk_notebook_change_current_page (GtkNotebook *notebook,
1668 int offset)
1669{
1670 GList *current = NULL;
1671
1672 if (!notebook->show_tabs)
1673 return FALSE;
1674
1675 if (notebook->cur_page)
1676 current = g_list_find (list: notebook->children, data: notebook->cur_page);
1677
1678 while (offset != 0)
1679 {
1680 current = gtk_notebook_search_page (notebook, list: current,
1681 direction: offset < 0 ? STEP_PREV : STEP_NEXT,
1682 TRUE);
1683
1684 if (!current)
1685 {
1686 current = gtk_notebook_search_page (notebook, NULL,
1687 direction: offset < 0 ? STEP_PREV : STEP_NEXT,
1688 TRUE);
1689 }
1690
1691 offset += offset < 0 ? 1 : -1;
1692 }
1693
1694 if (current)
1695 gtk_notebook_switch_page (notebook, page: current->data);
1696 else
1697 gtk_widget_error_bell (GTK_WIDGET (notebook));
1698
1699 return TRUE;
1700}
1701
1702static GtkDirectionType
1703get_effective_direction (GtkNotebook *notebook,
1704 GtkDirectionType direction)
1705{
1706 /* Remap the directions into the effective direction it would be for a
1707 * GTK_POS_TOP notebook
1708 */
1709
1710#define D(rest) GTK_DIR_##rest
1711
1712 static const GtkDirectionType translate_direction[2][4][6] = {
1713 /* LEFT */ {{ D(TAB_FORWARD), D(TAB_BACKWARD), D(LEFT), D(RIGHT), D(UP), D(DOWN) },
1714 /* RIGHT */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(LEFT), D(RIGHT), D(DOWN), D(UP) },
1715 /* TOP */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(UP), D(DOWN), D(LEFT), D(RIGHT) },
1716 /* BOTTOM */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(DOWN), D(UP), D(LEFT), D(RIGHT) }},
1717 /* LEFT */ {{ D(TAB_BACKWARD), D(TAB_FORWARD), D(LEFT), D(RIGHT), D(DOWN), D(UP) },
1718 /* RIGHT */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(LEFT), D(RIGHT), D(UP), D(DOWN) },
1719 /* TOP */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(UP), D(DOWN), D(RIGHT), D(LEFT) },
1720 /* BOTTOM */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(DOWN), D(UP), D(RIGHT), D(LEFT) }},
1721 };
1722
1723#undef D
1724
1725 int text_dir = gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_RTL ? 1 : 0;
1726
1727 return translate_direction[text_dir][notebook->tab_pos][direction];
1728}
1729
1730static GtkPositionType
1731get_effective_tab_pos (GtkNotebook *notebook)
1732{
1733 if (gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_RTL)
1734 {
1735 switch (notebook->tab_pos)
1736 {
1737 case GTK_POS_LEFT:
1738 return GTK_POS_RIGHT;
1739 case GTK_POS_RIGHT:
1740 return GTK_POS_LEFT;
1741 default: ;
1742 }
1743 }
1744
1745 return notebook->tab_pos;
1746}
1747
1748static void
1749gtk_notebook_move_focus_out (GtkNotebook *notebook,
1750 GtkDirectionType direction_type)
1751{
1752 GtkDirectionType effective_direction = get_effective_direction (notebook, direction: direction_type);
1753 GtkWidget *toplevel;
1754
1755 if (gtk_widget_get_focus_child (GTK_WIDGET (notebook)) && effective_direction == GTK_DIR_UP)
1756 if (focus_tabs_in (notebook))
1757 return;
1758 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && effective_direction == GTK_DIR_DOWN)
1759 if (focus_child_in (notebook, direction: GTK_DIR_TAB_FORWARD))
1760 return;
1761
1762 /* At this point, we know we should be focusing out of the notebook entirely. We
1763 * do this by setting a flag, then propagating the focus motion to the notebook.
1764 */
1765 toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (notebook)));
1766 if (!GTK_IS_ROOT (ptr: toplevel))
1767 return;
1768
1769 g_object_ref (notebook);
1770
1771 notebook->focus_out = TRUE;
1772 g_signal_emit_by_name (instance: toplevel, detailed_signal: "move-focus", direction_type);
1773 notebook->focus_out = FALSE;
1774
1775 g_object_unref (object: notebook);
1776}
1777
1778static int
1779reorder_tab (GtkNotebook *notebook, GList *position, GList *tab)
1780{
1781 GList *elem;
1782
1783 if (position == tab)
1784 return g_list_position (list: notebook->children, llink: tab);
1785
1786 /* check that we aren't inserting the tab in the
1787 * same relative position, taking packing into account */
1788 elem = (position) ? position->prev : g_list_last (list: notebook->children);
1789
1790 if (elem == tab)
1791 return g_list_position (list: notebook->children, llink: tab);
1792
1793 /* now actually reorder the tab */
1794 if (notebook->first_tab == tab)
1795 notebook->first_tab = gtk_notebook_search_page (notebook, list: notebook->first_tab,
1796 direction: STEP_NEXT, TRUE);
1797
1798 notebook->children = g_list_remove_link (list: notebook->children, llink: tab);
1799
1800 if (!position)
1801 elem = g_list_last (list: notebook->children);
1802 else
1803 {
1804 elem = position->prev;
1805 position->prev = tab;
1806 }
1807
1808 if (elem)
1809 elem->next = tab;
1810 else
1811 notebook->children = tab;
1812
1813 tab->prev = elem;
1814 tab->next = position;
1815
1816 return g_list_position (list: notebook->children, llink: tab);
1817}
1818
1819static gboolean
1820gtk_notebook_reorder_tab (GtkNotebook *notebook,
1821 GtkDirectionType direction_type,
1822 gboolean move_to_last)
1823{
1824 GtkDirectionType effective_direction = get_effective_direction (notebook, direction: direction_type);
1825 GList *last, *child, *element;
1826 int page_num, old_page_num, i;
1827
1828 if (!gtk_widget_is_focus (GTK_WIDGET (notebook)) || !notebook->show_tabs)
1829 return FALSE;
1830
1831 if (!gtk_notebook_has_current_page (notebook) ||
1832 !notebook->cur_page->reorderable)
1833 return FALSE;
1834
1835 if (effective_direction != GTK_DIR_LEFT &&
1836 effective_direction != GTK_DIR_RIGHT)
1837 return FALSE;
1838
1839 if (move_to_last)
1840 {
1841 child = notebook->focus_tab;
1842
1843 do
1844 {
1845 last = child;
1846 child = gtk_notebook_search_page (notebook, list: last,
1847 direction: (effective_direction == GTK_DIR_RIGHT) ? STEP_NEXT : STEP_PREV,
1848 TRUE);
1849 }
1850 while (child);
1851
1852 child = last;
1853 }
1854 else
1855 child = gtk_notebook_search_page (notebook, list: notebook->focus_tab,
1856 direction: (effective_direction == GTK_DIR_RIGHT) ? STEP_NEXT : STEP_PREV,
1857 TRUE);
1858
1859 if (!child || child->data == notebook->cur_page)
1860 return FALSE;
1861
1862 old_page_num = g_list_position (list: notebook->children, llink: notebook->focus_tab);
1863 if (effective_direction == GTK_DIR_RIGHT)
1864 page_num = reorder_tab (notebook, position: child->next, tab: notebook->focus_tab);
1865 else
1866 page_num = reorder_tab (notebook, position: child, tab: notebook->focus_tab);
1867
1868 gtk_notebook_child_reordered (notebook, page: notebook->focus_tab->data);
1869 for (element = notebook->children, i = 0; element; element = element->next, i++)
1870 {
1871 if (MIN (old_page_num, page_num) <= i && i <= MAX (old_page_num, page_num))
1872 g_object_notify (G_OBJECT (element->data), property_name: "position");
1873 }
1874 g_signal_emit (instance: notebook,
1875 signal_id: notebook_signals[PAGE_REORDERED],
1876 detail: 0,
1877 ((GtkNotebookPage *) notebook->focus_tab->data)->child,
1878 page_num);
1879
1880 return TRUE;
1881}
1882
1883/**
1884 * gtk_notebook_new:
1885 *
1886 * Creates a new `GtkNotebook` widget with no pages.
1887
1888 * Returns: the newly created `GtkNotebook`
1889 */
1890GtkWidget*
1891gtk_notebook_new (void)
1892{
1893 return g_object_new (GTK_TYPE_NOTEBOOK, NULL);
1894}
1895
1896/* Private GObject Methods :
1897 *
1898 * gtk_notebook_set_property
1899 * gtk_notebook_get_property
1900 */
1901static void
1902gtk_notebook_set_property (GObject *object,
1903 guint prop_id,
1904 const GValue *value,
1905 GParamSpec *pspec)
1906{
1907 GtkNotebook *notebook;
1908
1909 notebook = GTK_NOTEBOOK (object);
1910
1911 switch (prop_id)
1912 {
1913 case PROP_SHOW_TABS:
1914 gtk_notebook_set_show_tabs (notebook, show_tabs: g_value_get_boolean (value));
1915 break;
1916 case PROP_SHOW_BORDER:
1917 gtk_notebook_set_show_border (notebook, show_border: g_value_get_boolean (value));
1918 break;
1919 case PROP_SCROLLABLE:
1920 gtk_notebook_set_scrollable (notebook, scrollable: g_value_get_boolean (value));
1921 break;
1922 case PROP_ENABLE_POPUP:
1923 if (g_value_get_boolean (value))
1924 gtk_notebook_popup_enable (notebook);
1925 else
1926 gtk_notebook_popup_disable (notebook);
1927 break;
1928 case PROP_PAGE:
1929 gtk_notebook_set_current_page (notebook, page_num: g_value_get_int (value));
1930 break;
1931 case PROP_TAB_POS:
1932 gtk_notebook_set_tab_pos (notebook, pos: g_value_get_enum (value));
1933 break;
1934 case PROP_GROUP_NAME:
1935 gtk_notebook_set_group_name (notebook, group_name: g_value_get_string (value));
1936 break;
1937 default:
1938 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1939 break;
1940 }
1941}
1942
1943static void
1944gtk_notebook_get_property (GObject *object,
1945 guint prop_id,
1946 GValue *value,
1947 GParamSpec *pspec)
1948{
1949 GtkNotebook *notebook = GTK_NOTEBOOK (object);
1950
1951 switch (prop_id)
1952 {
1953 case PROP_SHOW_TABS:
1954 g_value_set_boolean (value, v_boolean: notebook->show_tabs);
1955 break;
1956 case PROP_SHOW_BORDER:
1957 g_value_set_boolean (value, v_boolean: notebook->show_border);
1958 break;
1959 case PROP_SCROLLABLE:
1960 g_value_set_boolean (value, v_boolean: notebook->scrollable);
1961 break;
1962 case PROP_ENABLE_POPUP:
1963 g_value_set_boolean (value, v_boolean: notebook->menu != NULL);
1964 break;
1965 case PROP_PAGE:
1966 g_value_set_int (value, v_int: gtk_notebook_get_current_page (notebook));
1967 break;
1968 case PROP_TAB_POS:
1969 g_value_set_enum (value, v_enum: notebook->tab_pos);
1970 break;
1971 case PROP_GROUP_NAME:
1972 g_value_set_string (value, v_string: gtk_notebook_get_group_name (notebook));
1973 break;
1974 case PROP_PAGES:
1975 g_value_take_object (value, v_object: gtk_notebook_get_pages (notebook));
1976 break;
1977 default:
1978 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1979 break;
1980 }
1981}
1982
1983/* Private GtkWidget Methods :
1984 *
1985 * gtk_notebook_map
1986 * gtk_notebook_unmap
1987 * gtk_notebook_snapshot
1988 * gtk_notebook_popup_menu
1989 * gtk_notebook_drag_begin
1990 * gtk_notebook_drag_end
1991 * gtk_notebook_drag_failed
1992 * gtk_notebook_drag_motion
1993 * gtk_notebook_drag_drop
1994 * gtk_notebook_drag_data_get
1995 */
1996
1997static void
1998gtk_notebook_finalize (GObject *object)
1999{
2000 GtkNotebook *notebook = GTK_NOTEBOOK (object);
2001
2002 gtk_widget_unparent (widget: notebook->header_widget);
2003 gtk_widget_unparent (widget: notebook->stack_widget);
2004
2005 G_OBJECT_CLASS (gtk_notebook_parent_class)->finalize (object);
2006}
2007
2008static void
2009gtk_notebook_dispose (GObject *object)
2010{
2011 GtkNotebook *notebook = GTK_NOTEBOOK (object);
2012 GList *l = notebook->children;
2013
2014 if (notebook->pages)
2015 g_list_model_items_changed (list: G_LIST_MODEL (ptr: notebook->pages), position: 0, removed: g_list_length (list: notebook->children), added: 0);
2016
2017 while (l != NULL)
2018 {
2019 GtkNotebookPage *page = l->data;
2020 l = l->next;
2021
2022 gtk_notebook_remove (notebook, widget: page->child);
2023 }
2024
2025 G_OBJECT_CLASS (gtk_notebook_parent_class)->dispose (object);
2026}
2027
2028static gboolean
2029gtk_notebook_get_tab_area_position (GtkNotebook *notebook,
2030 graphene_rect_t *rectangle)
2031{
2032 if (notebook->show_tabs && gtk_notebook_has_current_page (notebook))
2033 {
2034 return gtk_widget_compute_bounds (widget: notebook->header_widget,
2035 GTK_WIDGET (notebook),
2036 out_bounds: rectangle);
2037 }
2038 else
2039 {
2040 graphene_rect_init_from_rect (r: rectangle, src: graphene_rect_zero ());
2041 }
2042
2043 return FALSE;
2044}
2045
2046static void
2047gtk_notebook_unmap (GtkWidget *widget)
2048{
2049 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2050
2051 stop_scrolling (notebook);
2052
2053 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->unmap (widget);
2054}
2055
2056static void
2057gtk_notebook_distribute_arrow_width (GtkNotebook *notebook,
2058 GtkPackType type,
2059 int size,
2060 int *out_left,
2061 int *out_right)
2062{
2063 GtkRequestedSize sizes[2];
2064
2065 if (notebook->arrow_widget[2 * type + 1] == NULL)
2066 {
2067 if (notebook->arrow_widget[2 * type] == NULL)
2068 *out_left = 0;
2069 else
2070 *out_left = size;
2071 *out_right = 0;
2072 }
2073 else if (notebook->arrow_widget[2 * type] == NULL)
2074 {
2075 *out_left = 0;
2076 *out_right = size;
2077 }
2078 else
2079 {
2080 gtk_widget_measure (widget: notebook->arrow_widget[2 * type],
2081 orientation: GTK_ORIENTATION_HORIZONTAL,
2082 for_size: -1,
2083 minimum: &sizes[0].minimum_size, natural: &sizes[0].natural_size,
2084 NULL, NULL);
2085 gtk_widget_measure (widget: notebook->arrow_widget[2 * type + 1],
2086 orientation: GTK_ORIENTATION_HORIZONTAL,
2087 for_size: -1,
2088 minimum: &sizes[1].minimum_size, natural: &sizes[1].natural_size,
2089 NULL, NULL);
2090
2091 size -= sizes[0].minimum_size + sizes[1].minimum_size;
2092 size = gtk_distribute_natural_allocation (extra_space: size, G_N_ELEMENTS (sizes), sizes);
2093
2094 *out_left = sizes[0].minimum_size + size / 2;
2095 *out_right = sizes[1].minimum_size + (size + 1) / 2;
2096 }
2097}
2098
2099static void
2100gtk_notebook_measure_arrows (GtkNotebook *notebook,
2101 GtkPackType type,
2102 GtkOrientation orientation,
2103 int for_size,
2104 int *minimum,
2105 int *natural,
2106 int *minimum_baseline,
2107 int *natural_baseline)
2108{
2109 int child1_min, child1_nat;
2110 int child2_min, child2_nat;
2111
2112 if (orientation == GTK_ORIENTATION_HORIZONTAL)
2113 {
2114 if (notebook->arrow_widget[2 * type])
2115 {
2116 gtk_widget_measure (widget: notebook->arrow_widget[2 * type],
2117 orientation,
2118 for_size,
2119 minimum: &child1_min, natural: &child1_nat,
2120 NULL, NULL);
2121 }
2122 else
2123 {
2124 child1_min = child1_nat = 0;
2125 }
2126 if (notebook->arrow_widget[2 * type + 1])
2127 {
2128 gtk_widget_measure (widget: notebook->arrow_widget[2 * type + 1],
2129 orientation,
2130 for_size,
2131 minimum: &child2_min, natural: &child2_nat,
2132 NULL, NULL);
2133 }
2134 else
2135 {
2136 child2_min = child2_nat = 0;
2137 }
2138 *minimum = child1_min + child2_min;
2139 *natural = child1_nat + child2_nat;
2140 if (minimum_baseline)
2141 *minimum_baseline = -1;
2142 if (natural_baseline)
2143 *natural_baseline = -1;
2144 }
2145 else
2146 {
2147 int child1_size, child2_size;
2148
2149 if (for_size > -1)
2150 gtk_notebook_distribute_arrow_width (notebook, type, size: for_size, out_left: &child1_size, out_right: &child2_size);
2151 else
2152 child1_size = child2_size = for_size;
2153
2154 if (notebook->arrow_widget[2 * type])
2155 {
2156 gtk_widget_measure (widget: notebook->arrow_widget[2 * type],
2157 orientation,
2158 for_size: child1_size,
2159 minimum: &child1_min, natural: &child1_nat,
2160 NULL, NULL);
2161 }
2162 else
2163 {
2164 child1_min = child1_nat = 0;
2165 }
2166 if (notebook->arrow_widget[2 * type + 1])
2167 {
2168 gtk_widget_measure (widget: notebook->arrow_widget[2 * type + 1],
2169 orientation,
2170 for_size: child2_size,
2171 minimum: &child2_min, natural: &child2_nat,
2172 NULL, NULL);
2173 }
2174 else
2175 {
2176 child2_min = child2_nat = 0;
2177 }
2178 *minimum = MAX (child1_min, child2_min);
2179 *natural = MAX (child1_nat, child2_nat);
2180 if (minimum_baseline)
2181 *minimum_baseline = -1;
2182 if (natural_baseline)
2183 *natural_baseline = -1;
2184 }
2185}
2186
2187static void
2188gtk_notebook_get_preferred_tabs_size (GtkNotebook *notebook,
2189 GtkRequisition *requisition)
2190{
2191 int tab_width = 0;
2192 int tab_height = 0;
2193 int tab_max = 0;
2194 guint vis_pages = 0;
2195 GList *children;
2196 GtkNotebookPage *page;
2197
2198
2199 for (children = notebook->children; children;
2200 children = children->next)
2201 {
2202 page = children->data;
2203
2204 if (gtk_widget_get_visible (widget: page->child))
2205 {
2206 vis_pages++;
2207
2208 if (!gtk_widget_get_visible (widget: page->tab_label))
2209 gtk_widget_show (widget: page->tab_label);
2210
2211 gtk_widget_measure (widget: page->tab_widget,
2212 orientation: GTK_ORIENTATION_HORIZONTAL,
2213 for_size: -1,
2214 minimum: &page->requisition.width, NULL,
2215 NULL, NULL);
2216 gtk_widget_measure (widget: page->tab_widget,
2217 orientation: GTK_ORIENTATION_VERTICAL,
2218 for_size: page->requisition.width,
2219 minimum: &page->requisition.height, NULL,
2220 NULL, NULL);
2221
2222 switch (notebook->tab_pos)
2223 {
2224 case GTK_POS_TOP:
2225 case GTK_POS_BOTTOM:
2226 tab_height = MAX (tab_height, page->requisition.height);
2227 tab_max = MAX (tab_max, page->requisition.width);
2228 break;
2229 case GTK_POS_LEFT:
2230 case GTK_POS_RIGHT:
2231 tab_width = MAX (tab_width, page->requisition.width);
2232 tab_max = MAX (tab_max, page->requisition.height);
2233 break;
2234 default:
2235 g_assert_not_reached ();
2236 break;
2237 }
2238 }
2239 else if (gtk_widget_get_visible (widget: page->tab_label))
2240 gtk_widget_hide (widget: page->tab_label);
2241 }
2242
2243 children = notebook->children;
2244
2245 if (vis_pages)
2246 {
2247 switch (notebook->tab_pos)
2248 {
2249 case GTK_POS_TOP:
2250 case GTK_POS_BOTTOM:
2251 if (tab_height == 0)
2252 break;
2253
2254 if (notebook->scrollable)
2255 {
2256 int arrow_height, unused;
2257 gtk_notebook_measure_arrows (notebook,
2258 type: GTK_PACK_START,
2259 orientation: GTK_ORIENTATION_VERTICAL,
2260 for_size: -1,
2261 minimum: &arrow_height, natural: &unused,
2262 NULL, NULL);
2263 tab_height = MAX (tab_height, arrow_height);
2264 gtk_notebook_measure_arrows (notebook,
2265 type: GTK_PACK_END,
2266 orientation: GTK_ORIENTATION_VERTICAL,
2267 for_size: -1,
2268 minimum: &arrow_height, natural: &unused,
2269 NULL, NULL);
2270 tab_height = MAX (tab_height, arrow_height);
2271 }
2272
2273 while (children)
2274 {
2275 page = children->data;
2276 children = children->next;
2277
2278 if (!gtk_widget_get_visible (widget: page->child))
2279 continue;
2280
2281 tab_width += page->requisition.width;
2282 page->requisition.height = tab_height;
2283 }
2284
2285 if (notebook->scrollable)
2286 {
2287 int start_arrow_width, end_arrow_width, unused;
2288
2289 gtk_notebook_measure_arrows (notebook,
2290 type: GTK_PACK_START,
2291 orientation: GTK_ORIENTATION_HORIZONTAL,
2292 for_size: tab_height,
2293 minimum: &start_arrow_width, natural: &unused,
2294 NULL, NULL);
2295 gtk_notebook_measure_arrows (notebook,
2296 type: GTK_PACK_END,
2297 orientation: GTK_ORIENTATION_HORIZONTAL,
2298 for_size: tab_height,
2299 minimum: &end_arrow_width, natural: &unused,
2300 NULL, NULL);
2301 tab_width = MIN (tab_width,
2302 tab_max + start_arrow_width + end_arrow_width);
2303 }
2304
2305 requisition->width = tab_width;
2306
2307 requisition->height = tab_height;
2308 break;
2309 case GTK_POS_LEFT:
2310 case GTK_POS_RIGHT:
2311 if (tab_width == 0)
2312 break;
2313
2314 if (notebook->scrollable)
2315 {
2316 int arrow_width, unused;
2317 gtk_notebook_measure_arrows (notebook,
2318 type: GTK_PACK_START,
2319 orientation: GTK_ORIENTATION_HORIZONTAL,
2320 for_size: -1,
2321 minimum: &arrow_width, natural: &unused,
2322 NULL, NULL);
2323 tab_width = MAX (tab_width, arrow_width);
2324 gtk_notebook_measure_arrows (notebook,
2325 type: GTK_PACK_END,
2326 orientation: GTK_ORIENTATION_HORIZONTAL,
2327 for_size: -1,
2328 minimum: &arrow_width, natural: &unused,
2329 NULL, NULL);
2330 tab_width = MAX (tab_width, arrow_width);
2331 }
2332
2333 while (children)
2334 {
2335 page = children->data;
2336 children = children->next;
2337
2338 if (!gtk_widget_get_visible (widget: page->child))
2339 continue;
2340
2341 page->requisition.width = tab_width;
2342
2343 tab_height += page->requisition.height;
2344 }
2345
2346 if (notebook->scrollable)
2347 {
2348 int start_arrow_height, end_arrow_height, unused;
2349
2350 gtk_notebook_measure_arrows (notebook,
2351 type: GTK_PACK_START,
2352 orientation: GTK_ORIENTATION_VERTICAL,
2353 for_size: tab_width,
2354 minimum: &start_arrow_height, natural: &unused,
2355 NULL, NULL);
2356 gtk_notebook_measure_arrows (notebook,
2357 type: GTK_PACK_END,
2358 orientation: GTK_ORIENTATION_VERTICAL,
2359 for_size: tab_width,
2360 minimum: &end_arrow_height, natural: &unused,
2361 NULL, NULL);
2362 tab_height = MIN (tab_height, tab_max + start_arrow_height + end_arrow_height);
2363 }
2364
2365 requisition->height = tab_height;
2366 requisition->height = MAX (requisition->height, tab_max);
2367
2368 requisition->width = tab_width;
2369 break;
2370 default:
2371 g_assert_not_reached ();
2372 requisition->width = 0;
2373 requisition->height = 0;
2374 }
2375 }
2376 else
2377 {
2378 requisition->width = 0;
2379 requisition->height = 0;
2380 }
2381}
2382
2383static void
2384gtk_notebook_measure_tabs (GtkGizmo *gizmo,
2385 GtkOrientation orientation,
2386 int size,
2387 int *minimum,
2388 int *natural,
2389 int *minimum_baseline,
2390 int *natural_baseline)
2391{
2392 GtkWidget *widget = gtk_widget_get_parent (GTK_WIDGET (gizmo));
2393 GtkNotebook *notebook = GTK_NOTEBOOK (gtk_widget_get_parent (widget));
2394 GtkRequisition tabs_requisition = { 0 };
2395
2396 gtk_notebook_get_preferred_tabs_size (notebook, requisition: &tabs_requisition);
2397 if (orientation == GTK_ORIENTATION_HORIZONTAL)
2398 {
2399 *minimum = tabs_requisition.width;
2400 *natural = tabs_requisition.width;
2401 }
2402 else
2403 {
2404 *minimum = tabs_requisition.height;
2405 *natural = tabs_requisition.height;
2406 }
2407}
2408
2409static void
2410gtk_notebook_allocate_tabs (GtkGizmo *gizmo,
2411 int width,
2412 int height,
2413 int baseline)
2414{
2415 GtkWidget *widget = gtk_widget_get_parent (GTK_WIDGET (gizmo));
2416 GtkNotebook *notebook = GTK_NOTEBOOK (gtk_widget_get_parent (widget));
2417
2418 gtk_notebook_pages_allocate (notebook, width, height);
2419}
2420
2421static gboolean
2422gtk_notebook_show_arrows (GtkNotebook *notebook)
2423{
2424 GList *children;
2425
2426 if (!notebook->scrollable)
2427 return FALSE;
2428
2429 children = notebook->children;
2430 while (children)
2431 {
2432 GtkNotebookPage *page = children->data;
2433
2434 if (!gtk_widget_get_child_visible (widget: page->tab_widget))
2435 return TRUE;
2436
2437 children = children->next;
2438 }
2439
2440 return FALSE;
2441}
2442
2443static GtkNotebookArrow
2444gtk_notebook_get_arrow (GtkNotebook *notebook,
2445 int x,
2446 int y)
2447{
2448 int i;
2449
2450 if (gtk_notebook_show_arrows (notebook))
2451 {
2452 for (i = 0; i < 4; i++)
2453 {
2454 graphene_rect_t arrow_bounds;
2455
2456 if (notebook->arrow_widget[i] == NULL)
2457 continue;
2458
2459 if (!gtk_widget_compute_bounds (widget: notebook->arrow_widget[i],
2460 GTK_WIDGET (notebook),
2461 out_bounds: &arrow_bounds))
2462 continue;
2463
2464 if (graphene_rect_contains_point (r: &arrow_bounds,
2465 p: &(graphene_point_t){x, y}))
2466 return i;
2467 }
2468 }
2469
2470 return ARROW_NONE;
2471}
2472
2473static void
2474gtk_notebook_do_arrow (GtkNotebook *notebook,
2475 GtkNotebookArrow arrow)
2476{
2477 GtkWidget *widget = GTK_WIDGET (notebook);
2478 gboolean is_rtl, left;
2479
2480 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2481 left = (ARROW_IS_LEFT (arrow) && !is_rtl) ||
2482 (!ARROW_IS_LEFT (arrow) && is_rtl);
2483
2484 if (!notebook->focus_tab ||
2485 gtk_notebook_search_page (notebook, list: notebook->focus_tab,
2486 direction: left ? STEP_PREV : STEP_NEXT,
2487 TRUE))
2488 {
2489 gtk_notebook_change_current_page (notebook, offset: left ? -1 : 1);
2490 gtk_widget_grab_focus (widget);
2491 }
2492}
2493
2494static gboolean
2495gtk_notebook_arrow_button_press (GtkNotebook *notebook,
2496 GtkNotebookArrow arrow,
2497 int button)
2498{
2499 GtkWidget *widget = GTK_WIDGET (notebook);
2500 gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2501 gboolean left = (ARROW_IS_LEFT (arrow) && !is_rtl) ||
2502 (!ARROW_IS_LEFT (arrow) && is_rtl);
2503
2504 if (notebook->pressed_button)
2505 return FALSE;
2506
2507 if (!gtk_widget_has_focus (widget))
2508 gtk_widget_grab_focus (widget);
2509
2510 notebook->pressed_button = button;
2511 notebook->click_child = arrow;
2512
2513 if (button == GDK_BUTTON_PRIMARY)
2514 {
2515 gtk_notebook_do_arrow (notebook, arrow);
2516 gtk_notebook_set_scroll_timer (notebook);
2517 }
2518 else if (button == GDK_BUTTON_MIDDLE)
2519 gtk_notebook_page_select (notebook, TRUE);
2520 else if (button == GDK_BUTTON_SECONDARY)
2521 gtk_notebook_switch_focus_tab (notebook,
2522 new_child: gtk_notebook_search_page (notebook,
2523 NULL,
2524 direction: left ? STEP_NEXT : STEP_PREV,
2525 TRUE));
2526
2527 return TRUE;
2528}
2529
2530static gboolean
2531gtk_notebook_page_tab_label_is_visible (GtkNotebookPage *page)
2532{
2533 return page->tab_label &&
2534 gtk_widget_get_visible (widget: page->tab_widget) &&
2535 gtk_widget_get_child_visible (widget: page->tab_widget) &&
2536 gtk_widget_get_visible (widget: page->tab_label) &&
2537 gtk_widget_get_child_visible (widget: page->tab_label);
2538}
2539
2540static gboolean
2541in_tabs (GtkNotebook *notebook,
2542 double x,
2543 double y)
2544{
2545 graphene_rect_t tabs_bounds;
2546
2547 if (!gtk_widget_compute_bounds (widget: notebook->tabs_widget, GTK_WIDGET (notebook), out_bounds: &tabs_bounds))
2548 return FALSE;
2549
2550 return graphene_rect_contains_point (r: &tabs_bounds,
2551 p: &(graphene_point_t){x, y});
2552}
2553
2554static GList*
2555get_tab_at_pos (GtkNotebook *notebook,
2556 double x,
2557 double y)
2558{
2559 GtkNotebookPage *page;
2560 GList *children;
2561
2562 for (children = notebook->children; children; children = children->next)
2563 {
2564 graphene_rect_t bounds;
2565
2566 page = children->data;
2567
2568 if (!gtk_notebook_page_tab_label_is_visible (page))
2569 continue;
2570
2571 if (!gtk_widget_compute_bounds (widget: page->tab_widget, GTK_WIDGET (notebook), out_bounds: &bounds))
2572 continue;
2573
2574 if (graphene_rect_contains_point (r: &bounds, p: &(graphene_point_t){x, y}))
2575 return children;
2576 }
2577
2578 return NULL;
2579}
2580
2581static void
2582gtk_notebook_gesture_pressed (GtkGestureClick *gesture,
2583 int n_press,
2584 double x,
2585 double y,
2586 gpointer user_data)
2587{
2588 GtkNotebook *notebook = user_data;
2589 GtkWidget *widget = user_data;
2590 GdkEventSequence *sequence;
2591 GtkNotebookArrow arrow;
2592 GtkNotebookPage *page;
2593 GdkEvent *event;
2594 guint button;
2595 GList *tab;
2596
2597 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
2598 button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
2599 event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
2600
2601 if (!notebook->children)
2602 return;
2603
2604 arrow = gtk_notebook_get_arrow (notebook, x, y);
2605 if (arrow != ARROW_NONE)
2606 {
2607 gtk_notebook_arrow_button_press (notebook, arrow, button);
2608 gtk_gesture_set_state (GTK_GESTURE (gesture), state: GTK_EVENT_SEQUENCE_CLAIMED);
2609 return;
2610 }
2611
2612 if (in_tabs (notebook, x, y) && notebook->menu && gdk_event_triggers_context_menu (event))
2613 {
2614 GdkRectangle rect;
2615
2616 rect.x = x;
2617 rect.y = y;
2618 rect.width = 1;
2619 rect.height = 1;
2620 gtk_popover_set_pointing_to (GTK_POPOVER (notebook->menu), rect: &rect);
2621 gtk_popover_popup (GTK_POPOVER (notebook->menu));
2622 return;
2623 }
2624
2625 if (button != GDK_BUTTON_PRIMARY)
2626 return;
2627
2628 if ((tab = get_tab_at_pos (notebook, x, y)) != NULL)
2629 {
2630 gboolean page_changed, was_focus;
2631
2632 page = tab->data;
2633 page_changed = page != notebook->cur_page;
2634 was_focus = gtk_widget_is_focus (widget);
2635
2636 gtk_notebook_switch_focus_tab (notebook, new_child: tab);
2637 gtk_widget_grab_focus (widget);
2638
2639 if (page_changed && !was_focus)
2640 gtk_widget_child_focus (widget: page->child, direction: GTK_DIR_TAB_FORWARD);
2641
2642 /* save press to possibly begin a drag */
2643 if (page->reorderable || page->detachable)
2644 {
2645 graphene_rect_t tab_bounds;
2646
2647 notebook->pressed_button = button;
2648
2649 notebook->mouse_x = x;
2650 notebook->mouse_y = y;
2651
2652 notebook->drag_begin_x = notebook->mouse_x;
2653 notebook->drag_begin_y = notebook->mouse_y;
2654
2655 /* tab bounds get set to empty, which is fine */
2656 notebook->drag_offset_x = notebook->drag_begin_x;
2657 notebook->drag_offset_y = notebook->drag_begin_y;
2658 if (gtk_widget_compute_bounds (widget: page->tab_widget, GTK_WIDGET (notebook), out_bounds: &tab_bounds))
2659 {
2660 notebook->drag_offset_x -= tab_bounds.origin.x;
2661 notebook->drag_offset_y -= tab_bounds.origin.y;
2662 }
2663 }
2664 }
2665}
2666
2667static void
2668gtk_notebook_popup_menu (GtkWidget *widget,
2669 const char *action_name,
2670 GVariant *parameters)
2671{
2672 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2673
2674 if (notebook->menu)
2675 gtk_popover_popup (GTK_POPOVER (notebook->menu));
2676}
2677
2678static void
2679stop_scrolling (GtkNotebook *notebook)
2680{
2681
2682 if (notebook->timer)
2683 {
2684 g_source_remove (tag: notebook->timer);
2685 notebook->timer = 0;
2686 notebook->need_timer = FALSE;
2687 }
2688 notebook->click_child = ARROW_NONE;
2689 notebook->pressed_button = 0;
2690}
2691
2692static GList*
2693get_drop_position (GtkNotebook *notebook)
2694{
2695 GList *children, *last_child;
2696 GtkNotebookPage *page;
2697 gboolean is_rtl;
2698 int x, y;
2699
2700 x = notebook->mouse_x;
2701 y = notebook->mouse_y;
2702
2703 is_rtl = gtk_widget_get_direction (widget: (GtkWidget *) notebook) == GTK_TEXT_DIR_RTL;
2704 last_child = NULL;
2705
2706 for (children = notebook->children; children; children = children->next)
2707 {
2708 page = children->data;
2709
2710 if ((notebook->operation != DRAG_OPERATION_REORDER || page != notebook->cur_page) &&
2711 gtk_widget_get_visible (widget: page->child) &&
2712 page->tab_label &&
2713 gtk_widget_get_mapped (widget: page->tab_label))
2714 {
2715 graphene_rect_t tab_bounds;
2716
2717 if (!gtk_widget_compute_bounds (widget: page->tab_widget, GTK_WIDGET (notebook), out_bounds: &tab_bounds))
2718 continue;
2719
2720 switch (notebook->tab_pos)
2721 {
2722 case GTK_POS_TOP:
2723 case GTK_POS_BOTTOM:
2724 if (!is_rtl)
2725 {
2726 if (tab_bounds.origin.x + tab_bounds.size.width / 2 > x)
2727 return children;
2728 }
2729 else
2730 {
2731 if (tab_bounds.origin.x + tab_bounds.size.width / 2 < x)
2732 return children;
2733 }
2734 break;
2735
2736 case GTK_POS_LEFT:
2737 case GTK_POS_RIGHT:
2738 if (tab_bounds.origin.y + tab_bounds.size.height / 2 > y)
2739 return children;
2740 break;
2741
2742 default:
2743 g_assert_not_reached ();
2744 break;
2745 }
2746
2747 last_child = children->next;
2748 }
2749 }
2750
2751 return last_child;
2752}
2753
2754static void
2755tab_drag_begin (GtkNotebook *notebook,
2756 GtkNotebookPage *page)
2757{
2758 gtk_widget_add_css_class (widget: page->tab_widget, css_class: "dnd");
2759}
2760
2761/* This function undoes the reparenting that happens both when drag_surface
2762 * is shown for reordering and when the DnD icon is shown for detaching
2763 */
2764static void
2765tab_drag_end (GtkNotebook *notebook,
2766 GtkNotebookPage *page)
2767{
2768 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
2769 {
2770 g_object_ref (page->tab_label);
2771 gtk_box_remove (GTK_BOX (gtk_widget_get_parent (page->tab_label)), child: page->tab_label);
2772 gtk_widget_set_parent (widget: page->tab_label, parent: page->tab_widget);
2773 g_object_unref (object: page->tab_label);
2774 }
2775
2776 gtk_widget_remove_css_class (widget: page->tab_widget, css_class: "dnd");
2777}
2778
2779static void
2780gtk_notebook_stop_reorder (GtkNotebook *notebook)
2781{
2782 GtkNotebookPage *page;
2783
2784 if (notebook->operation == DRAG_OPERATION_DETACH)
2785 page = notebook->detached_tab;
2786 else
2787 page = notebook->cur_page;
2788
2789 if (!page || !page->tab_label)
2790 return;
2791
2792 notebook->pressed_button = 0;
2793
2794 if (page->reorderable || page->detachable)
2795 {
2796 if (notebook->operation == DRAG_OPERATION_REORDER)
2797 {
2798 int old_page_num, page_num, i;
2799 GList *element;
2800
2801 element = get_drop_position (notebook);
2802 old_page_num = g_list_position (list: notebook->children, llink: notebook->focus_tab);
2803 page_num = reorder_tab (notebook, position: element, tab: notebook->focus_tab);
2804 gtk_notebook_child_reordered (notebook, page);
2805
2806 if (notebook->has_scrolled || old_page_num != page_num)
2807 {
2808 for (element = notebook->children, i = 0; element; element = element->next, i++)
2809 {
2810 if (MIN (old_page_num, page_num) <= i && i <= MAX (old_page_num, page_num))
2811 g_object_notify (G_OBJECT (element->data), property_name: "position");
2812 }
2813 g_signal_emit (instance: notebook,
2814 signal_id: notebook_signals[PAGE_REORDERED], detail: 0,
2815 page->child, page_num);
2816 }
2817 }
2818
2819 notebook->has_scrolled = FALSE;
2820
2821 tab_drag_end (notebook, page);
2822
2823 notebook->operation = DRAG_OPERATION_NONE;
2824
2825 if (notebook->dnd_timer)
2826 {
2827 g_source_remove (tag: notebook->dnd_timer);
2828 notebook->dnd_timer = 0;
2829 }
2830
2831 gtk_widget_queue_allocate (GTK_WIDGET (notebook));
2832 }
2833}
2834
2835static void
2836gtk_notebook_gesture_released (GtkGestureClick *gesture,
2837 int n_press,
2838 double x,
2839 double y,
2840 gpointer user_data)
2841{
2842 GtkNotebook *notebook = user_data;
2843 GdkEventSequence *sequence;
2844 GdkEvent *event;
2845 guint button;
2846
2847 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
2848 button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
2849 event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
2850
2851 if (!event)
2852 return;
2853
2854 if (notebook->pressed_button != button)
2855 return;
2856
2857 if (notebook->operation == DRAG_OPERATION_REORDER &&
2858 notebook->cur_page &&
2859 notebook->cur_page->reorderable)
2860 gtk_notebook_stop_reorder (notebook);
2861
2862 stop_scrolling (notebook);
2863}
2864
2865static void
2866gtk_notebook_gesture_cancel (GtkGestureClick *gesture,
2867 GdkEventSequence *sequence,
2868 GtkNotebook *notebook)
2869{
2870 gtk_notebook_stop_reorder (notebook);
2871 stop_scrolling (notebook);
2872}
2873
2874static GtkNotebookPointerPosition
2875get_pointer_position (GtkNotebook *notebook)
2876{
2877 GtkWidget *widget = GTK_WIDGET (notebook);
2878 graphene_rect_t area;
2879 int width, height;
2880 gboolean is_rtl;
2881
2882 if (!notebook->scrollable)
2883 return POINTER_BETWEEN;
2884
2885 gtk_notebook_get_tab_area_position (notebook, rectangle: &area);
2886 width = area.size.width;
2887 height = area.size.height;
2888
2889 if (notebook->tab_pos == GTK_POS_TOP ||
2890 notebook->tab_pos == GTK_POS_BOTTOM)
2891 {
2892 int x = notebook->mouse_x;
2893
2894 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2895
2896 if (x > width - SCROLL_THRESHOLD)
2897 return (is_rtl) ? POINTER_BEFORE : POINTER_AFTER;
2898 else if (x < SCROLL_THRESHOLD)
2899 return (is_rtl) ? POINTER_AFTER : POINTER_BEFORE;
2900 else
2901 return POINTER_BETWEEN;
2902 }
2903 else
2904 {
2905 int y = notebook->mouse_y;
2906
2907 if (y > height - SCROLL_THRESHOLD)
2908 return POINTER_AFTER;
2909 else if (y < SCROLL_THRESHOLD)
2910 return POINTER_BEFORE;
2911 else
2912 return POINTER_BETWEEN;
2913 }
2914}
2915
2916static gboolean
2917scroll_notebook_timer (gpointer data)
2918{
2919 GtkNotebook *notebook = GTK_NOTEBOOK (data);
2920 GtkNotebookPointerPosition pointer_position;
2921 GList *element, *first_tab;
2922
2923 pointer_position = get_pointer_position (notebook);
2924
2925 element = get_drop_position (notebook);
2926 reorder_tab (notebook, position: element, tab: notebook->focus_tab);
2927 first_tab = gtk_notebook_search_page (notebook, list: notebook->first_tab,
2928 direction: (pointer_position == POINTER_BEFORE) ? STEP_PREV : STEP_NEXT,
2929 TRUE);
2930 if (first_tab && notebook->cur_page)
2931 {
2932 notebook->first_tab = first_tab;
2933
2934 gtk_widget_queue_allocate (widget: notebook->tabs_widget);
2935 }
2936
2937 return TRUE;
2938}
2939
2940static gboolean
2941check_threshold (GtkNotebook *notebook,
2942 int current_x,
2943 int current_y)
2944{
2945 int dnd_threshold;
2946 graphene_rect_t rectangle;
2947 GtkSettings *settings;
2948
2949 settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
2950 g_object_get (G_OBJECT (settings), first_property_name: "gtk-dnd-drag-threshold", &dnd_threshold, NULL);
2951
2952 /* we want a large threshold */
2953 dnd_threshold *= DND_THRESHOLD_MULTIPLIER;
2954
2955 gtk_notebook_get_tab_area_position (notebook, rectangle: &rectangle);
2956 graphene_rect_inset (r: &rectangle, d_x: -dnd_threshold, d_y: -dnd_threshold);
2957
2958 /* The negation here is important! */
2959 return !graphene_rect_contains_point (r: &rectangle, p: &(graphene_point_t){current_x, current_y});
2960}
2961
2962static void
2963gtk_notebook_motion (GtkEventController *controller,
2964 double x,
2965 double y,
2966 gpointer user_data)
2967{
2968 GtkWidget *widget = GTK_WIDGET (user_data);
2969 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2970 GtkNotebookPage *page;
2971 guint state;
2972
2973 page = notebook->cur_page;
2974 if (!page)
2975 return;
2976
2977 state = gtk_event_controller_get_current_event_state (controller);
2978
2979 if (!(state & GDK_BUTTON1_MASK) &&
2980 notebook->pressed_button != 0)
2981 {
2982 gtk_notebook_stop_reorder (notebook);
2983 stop_scrolling (notebook);
2984 }
2985
2986 notebook->mouse_x = x;
2987 notebook->mouse_y = y;
2988
2989 if (notebook->pressed_button == 0)
2990 return;
2991
2992 if (page->detachable &&
2993 check_threshold (notebook, current_x: notebook->mouse_x, current_y: notebook->mouse_y))
2994 {
2995 GdkSurface *surface;
2996 GdkDevice *device;
2997 GdkContentProvider *content;
2998 GdkDrag *drag;
2999 GdkPaintable *paintable;
3000
3001 notebook->detached_tab = notebook->cur_page;
3002
3003 surface = gtk_native_get_surface (self: gtk_widget_get_native (GTK_WIDGET (notebook)));
3004 device = gtk_event_controller_get_current_event_device (controller);
3005
3006 content = gdk_content_provider_new_union (providers: (GdkContentProvider *[2]) {
3007 gtk_notebook_root_content_new (notebook),
3008 gdk_content_provider_new_typed (GTK_TYPE_NOTEBOOK_PAGE, notebook->cur_page)
3009 }, n_providers: 2);
3010 drag = gdk_drag_begin (surface, device, content, actions: GDK_ACTION_MOVE, dx: notebook->drag_begin_x, dy: notebook->drag_begin_y);
3011 g_object_unref (object: content);
3012
3013 g_signal_connect (drag, "dnd-finished", G_CALLBACK (gtk_notebook_dnd_finished_cb), notebook);
3014 g_signal_connect (drag, "cancel", G_CALLBACK (gtk_notebook_drag_cancel_cb), notebook);
3015
3016 paintable = gtk_widget_paintable_new (widget: notebook->detached_tab->tab_widget);
3017 gtk_drag_icon_set_from_paintable (drag, paintable, hot_x: -2, hot_y: -2);
3018 g_object_unref (object: paintable);
3019
3020 if (notebook->dnd_timer)
3021 {
3022 g_source_remove (tag: notebook->dnd_timer);
3023 notebook->dnd_timer = 0;
3024 }
3025
3026 notebook->operation = DRAG_OPERATION_DETACH;
3027 tab_drag_end (notebook, page: notebook->cur_page);
3028
3029 g_object_set_data (G_OBJECT (drag), key: "gtk-notebook-drag-origin", data: notebook);
3030
3031 g_object_unref (object: drag);
3032
3033 return;
3034 }
3035
3036 if (page->reorderable &&
3037 (notebook->operation == DRAG_OPERATION_REORDER ||
3038 gtk_drag_check_threshold_double (widget,
3039 start_x: notebook->drag_begin_x,
3040 start_y: notebook->drag_begin_y,
3041 current_x: notebook->mouse_x,
3042 current_y: notebook->mouse_y)))
3043 {
3044 GtkNotebookPointerPosition pointer_position = get_pointer_position (notebook);
3045
3046 if (pointer_position != POINTER_BETWEEN &&
3047 gtk_notebook_show_arrows (notebook))
3048 {
3049 /* scroll tabs */
3050 if (!notebook->dnd_timer)
3051 {
3052 notebook->has_scrolled = TRUE;
3053 notebook->dnd_timer = g_timeout_add (TIMEOUT_REPEAT * SCROLL_DELAY_FACTOR,
3054 function: scroll_notebook_timer,
3055 data: notebook);
3056 gdk_source_set_static_name_by_id (tag: notebook->dnd_timer, name: "[gtk] scroll_notebook_timer");
3057 }
3058 }
3059 else
3060 {
3061 if (notebook->dnd_timer)
3062 {
3063 g_source_remove (tag: notebook->dnd_timer);
3064 notebook->dnd_timer = 0;
3065 }
3066 }
3067
3068 if (notebook->operation != DRAG_OPERATION_REORDER)
3069 {
3070 notebook->operation = DRAG_OPERATION_REORDER;
3071 tab_drag_begin (notebook, page);
3072 }
3073 }
3074
3075 if (notebook->operation == DRAG_OPERATION_REORDER)
3076 gtk_widget_queue_allocate (widget: notebook->tabs_widget);
3077}
3078
3079static void
3080update_arrow_state (GtkNotebook *notebook)
3081{
3082 int i;
3083 gboolean is_rtl, left;
3084
3085 is_rtl = gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_RTL;
3086
3087 for (i = 0; i < 4; i++)
3088 {
3089 gboolean sensitive = TRUE;
3090
3091 if (notebook->arrow_widget[i] == NULL)
3092 continue;
3093
3094 left = (ARROW_IS_LEFT (i) && !is_rtl) ||
3095 (!ARROW_IS_LEFT (i) && is_rtl);
3096
3097 if (notebook->focus_tab &&
3098 !gtk_notebook_search_page (notebook, list: notebook->focus_tab,
3099 direction: left ? STEP_PREV : STEP_NEXT, TRUE))
3100 {
3101 sensitive = FALSE;
3102 }
3103
3104 gtk_widget_set_sensitive (widget: notebook->arrow_widget[i], sensitive);
3105 }
3106}
3107
3108static void
3109gtk_notebook_state_flags_changed (GtkWidget *widget,
3110 GtkStateFlags previous_state)
3111{
3112 if (!gtk_widget_is_sensitive (widget))
3113 stop_scrolling (GTK_NOTEBOOK (widget));
3114}
3115
3116static void
3117gtk_notebook_arrow_drag_enter (GtkDropControllerMotion *motion,
3118 double x,
3119 double y,
3120 GtkNotebook *notebook)
3121{
3122 GtkWidget *arrow_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (motion));
3123 guint arrow;
3124
3125 for (arrow = 0; arrow < 4; arrow++)
3126 {
3127 if (notebook->arrow_widget[arrow] == arrow_widget)
3128 break;
3129 }
3130
3131 g_assert (arrow != ARROW_NONE);
3132
3133 notebook->click_child = arrow;
3134 gtk_notebook_set_scroll_timer (notebook);
3135}
3136
3137static void
3138gtk_notebook_arrow_drag_leave (GtkDropTarget *target,
3139 GdkDrop *drop,
3140 GtkNotebook *notebook)
3141{
3142 stop_scrolling (notebook);
3143}
3144
3145static void
3146update_arrow_nodes (GtkNotebook *notebook)
3147{
3148 gboolean arrow[4];
3149 const char *up_icon_name;
3150 const char *down_icon_name;
3151 int i;
3152
3153 if (notebook->tab_pos == GTK_POS_LEFT ||
3154 notebook->tab_pos == GTK_POS_RIGHT)
3155 {
3156 up_icon_name = "pan-down-symbolic";
3157 down_icon_name = "pan-up-symbolic";
3158 }
3159 else if (gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_LTR)
3160 {
3161 up_icon_name = "pan-end-symbolic";
3162 down_icon_name = "pan-start-symbolic";
3163 }
3164 else
3165 {
3166 up_icon_name = "pan-start-symbolic";
3167 down_icon_name = "pan-end-symbolic";
3168 }
3169
3170 arrow[0] = TRUE;
3171 arrow[1] = FALSE;
3172 arrow[2] = FALSE;
3173 arrow[3] = TRUE;
3174
3175 for (i = 0; i < 4; i++)
3176 {
3177 if (notebook->scrollable && arrow[i])
3178 {
3179 if (notebook->arrow_widget[i] == NULL)
3180 {
3181 GtkWidget *next_widget;
3182 GtkEventController *controller;
3183
3184 switch (i)
3185 {
3186 case 0:
3187 if (notebook->arrow_widget[1])
3188 {
3189 next_widget = notebook->arrow_widget[1];
3190 break;
3191 }
3192 G_GNUC_FALLTHROUGH;
3193 case 1:
3194 if (notebook->children)
3195 {
3196 GtkNotebookPage *page = notebook->children->data;
3197 next_widget = page->tab_widget;
3198 break;
3199 }
3200 if (notebook->arrow_widget[2])
3201 {
3202 next_widget = notebook->arrow_widget[2];
3203 break;
3204 }
3205 G_GNUC_FALLTHROUGH;
3206 case 2:
3207 if (notebook->arrow_widget[3])
3208 {
3209 next_widget = notebook->arrow_widget[3];
3210 break;
3211 }
3212 G_GNUC_FALLTHROUGH;
3213 case 3:
3214 next_widget = NULL;
3215 break;
3216
3217 default:
3218 g_assert_not_reached ();
3219 next_widget = NULL;
3220 break;
3221 }
3222
3223 notebook->arrow_widget[i] = g_object_new (GTK_TYPE_BUTTON,
3224 first_property_name: "css-name", "arrow",
3225 NULL);
3226 controller = gtk_drop_controller_motion_new ();
3227 g_signal_connect (controller, "enter", G_CALLBACK (gtk_notebook_arrow_drag_enter), notebook);
3228 g_signal_connect (controller, "leave", G_CALLBACK (gtk_notebook_arrow_drag_leave), notebook);
3229 gtk_widget_add_controller (widget: notebook->arrow_widget[i], controller);
3230
3231 if (i == ARROW_LEFT_BEFORE || i == ARROW_LEFT_AFTER)
3232 {
3233 gtk_widget_add_css_class (widget: notebook->arrow_widget[i], css_class: "down");
3234 gtk_widget_insert_after (widget: notebook->arrow_widget[i], parent: notebook->tabs_widget, previous_sibling: next_widget);
3235 }
3236 else
3237 {
3238 gtk_widget_add_css_class (widget: notebook->arrow_widget[i], css_class: "up");
3239 gtk_widget_insert_before (widget: notebook->arrow_widget[i], parent: notebook->tabs_widget, next_sibling: next_widget);
3240 }
3241 }
3242
3243 if (i == ARROW_LEFT_BEFORE || i == ARROW_LEFT_AFTER)
3244 gtk_button_set_icon_name (GTK_BUTTON (notebook->arrow_widget[i]), icon_name: down_icon_name);
3245 else
3246 gtk_button_set_icon_name (GTK_BUTTON (notebook->arrow_widget[i]), icon_name: up_icon_name);
3247
3248 if (i == ARROW_LEFT_BEFORE || i == ARROW_LEFT_AFTER)
3249 gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: notebook->arrow_widget[i]),
3250 first_property: GTK_ACCESSIBLE_PROPERTY_LABEL, _("Previous tab"),
3251 -1);
3252 else
3253 gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: notebook->arrow_widget[i]),
3254 first_property: GTK_ACCESSIBLE_PROPERTY_LABEL, _("Next tab"),
3255 -1);
3256 }
3257 else
3258 {
3259 g_clear_pointer (&notebook->arrow_widget[i], gtk_widget_unparent);
3260 }
3261 }
3262}
3263
3264static void
3265gtk_notebook_direction_changed (GtkWidget *widget,
3266 GtkTextDirection previous_direction)
3267{
3268 update_arrow_nodes (GTK_NOTEBOOK (widget));
3269}
3270
3271static void
3272gtk_notebook_dnd_finished_cb (GdkDrag *drag,
3273 GtkWidget *widget)
3274{
3275 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3276
3277 gtk_notebook_stop_reorder (notebook);
3278
3279 if (notebook->rootwindow_drop)
3280 {
3281 GtkNotebook *dest_notebook = NULL;
3282
3283 g_signal_emit (instance: notebook, signal_id: notebook_signals[CREATE_WINDOW], detail: 0,
3284 notebook->detached_tab->child, &dest_notebook);
3285
3286 if (dest_notebook)
3287 do_detach_tab (from: notebook, to: dest_notebook, child: notebook->detached_tab->child);
3288
3289 notebook->rootwindow_drop = FALSE;
3290 }
3291 else if (notebook->detached_tab)
3292 {
3293 gtk_notebook_switch_page (notebook, page: notebook->detached_tab);
3294 }
3295
3296 notebook->operation = DRAG_OPERATION_NONE;
3297}
3298
3299static GtkNotebook *
3300gtk_notebook_create_window (GtkNotebook *notebook,
3301 GtkWidget *page)
3302{
3303 return NULL;
3304}
3305
3306static void
3307gtk_notebook_drag_cancel_cb (GdkDrag *drag,
3308 GdkDragCancelReason reason,
3309 GtkWidget *widget)
3310{
3311 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3312
3313 notebook->rootwindow_drop = FALSE;
3314
3315 if (reason == GDK_DRAG_CANCEL_NO_TARGET)
3316 {
3317 GtkNotebook *dest_notebook = NULL;
3318
3319 g_signal_emit (instance: notebook, signal_id: notebook_signals[CREATE_WINDOW], detail: 0,
3320 notebook->detached_tab->child, &dest_notebook);
3321
3322 if (dest_notebook)
3323 do_detach_tab (from: notebook, to: dest_notebook, child: notebook->detached_tab->child);
3324 }
3325}
3326
3327static gboolean
3328gtk_notebook_switch_page_timeout (gpointer data)
3329{
3330 GtkNotebook *notebook = GTK_NOTEBOOK (data);
3331 GtkNotebookPage *switch_page;
3332
3333 notebook->switch_page_timer = 0;
3334
3335 switch_page = notebook->switch_page;
3336 notebook->switch_page = NULL;
3337
3338 if (switch_page)
3339 {
3340 /* FIXME: hack, we don't want the
3341 * focus to move fom the source widget
3342 */
3343 notebook->child_has_focus = FALSE;
3344 gtk_notebook_switch_focus_tab (notebook,
3345 new_child: g_list_find (list: notebook->children,
3346 data: switch_page));
3347 }
3348
3349 return FALSE;
3350}
3351
3352static gboolean
3353gtk_notebook_can_drag_from (GtkNotebook *self,
3354 GtkNotebook *other,
3355 GtkNotebookPage *page)
3356{
3357 /* always allow dragging inside self */
3358 if (self == other)
3359 return TRUE;
3360
3361 /* if the groups don't match, fail */
3362 if (self->group == 0 ||
3363 self->group != other->group)
3364 return FALSE;
3365
3366 /* Check that the dragged page is not a parent of the notebook
3367 * being dragged into */
3368 if (GTK_WIDGET (self) == page->child ||
3369 gtk_widget_is_ancestor (GTK_WIDGET (self), GTK_WIDGET (page->child)) ||
3370 GTK_WIDGET (self) == page->tab_label ||
3371 gtk_widget_is_ancestor (GTK_WIDGET (self), GTK_WIDGET (page->tab_label)))
3372 return FALSE;
3373
3374 return TRUE;
3375}
3376
3377static GdkDragAction
3378gtk_notebook_drag_motion (GtkDropTarget *dest,
3379 double x,
3380 double y,
3381 GtkNotebook *notebook)
3382{
3383 GdkDrag *drag = gdk_drop_get_drag (self: gtk_drop_target_get_current_drop (self: dest));
3384 GtkNotebook *source;
3385
3386 notebook->mouse_x = x;
3387 notebook->mouse_y = y;
3388
3389 if (!drag)
3390 return 0;
3391
3392 source = GTK_NOTEBOOK (g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin"));
3393 g_assert (source->cur_page != NULL);
3394
3395 if (!gtk_notebook_can_drag_from (self: notebook, other: source, page: source->cur_page))
3396 return 0;
3397
3398 return GDK_ACTION_MOVE;
3399}
3400
3401static gboolean
3402gtk_notebook_drag_drop (GtkDropTarget *dest,
3403 const GValue *value,
3404 double x,
3405 double y,
3406 GtkNotebook *self)
3407{
3408 GdkDrag *drag = gdk_drop_get_drag (self: gtk_drop_target_get_current_drop (self: dest));
3409 GtkNotebook *source;
3410 GtkNotebookPage *page = g_value_get_object (value);
3411
3412 source = drag ? g_object_get_data (G_OBJECT (drag), key: "gtk-notebook-drag-origin") : NULL;
3413
3414 if (!source || !gtk_notebook_can_drag_from (self, other: source, page: source->cur_page))
3415 return FALSE;
3416
3417 self->mouse_x = x;
3418 self->mouse_y = y;
3419
3420 do_detach_tab (from: source, to: self, child: page->child);
3421
3422 return TRUE;
3423}
3424
3425/**
3426 * gtk_notebook_detach_tab:
3427 * @notebook: a `GtkNotebook`
3428 * @child: a child
3429 *
3430 * Removes the child from the notebook.
3431 *
3432 * This function is very similar to [method@Gtk.Notebook.remove_page],
3433 * but additionally informs the notebook that the removal
3434 * is happening as part of a tab DND operation, which should
3435 * not be cancelled.
3436 */
3437void
3438gtk_notebook_detach_tab (GtkNotebook *notebook,
3439 GtkWidget *child)
3440{
3441 notebook->remove_in_detach = TRUE;
3442 gtk_notebook_remove (notebook, widget: child);
3443 notebook->remove_in_detach = FALSE;
3444}
3445
3446static void
3447do_detach_tab (GtkNotebook *from,
3448 GtkNotebook *to,
3449 GtkWidget *child)
3450{
3451 GtkWidget *tab_label, *menu_label;
3452 gboolean tab_expand, tab_fill, reorderable, detachable;
3453 GList *element;
3454 int page_num;
3455 GtkNotebookPage *page;
3456
3457 menu_label = gtk_notebook_get_menu_label (notebook: from, child);
3458
3459 if (menu_label)
3460 g_object_ref (menu_label);
3461
3462 tab_label = gtk_notebook_get_tab_label (notebook: from, child);
3463
3464 if (tab_label)
3465 g_object_ref (tab_label);
3466
3467 g_object_ref (child);
3468
3469 page = gtk_notebook_get_page (notebook: from, child);
3470 g_object_get (object: page,
3471 first_property_name: "tab-expand", &tab_expand,
3472 "tab-fill", &tab_fill,
3473 "reorderable", &reorderable,
3474 "detachable", &detachable,
3475 NULL);
3476
3477 gtk_notebook_detach_tab (notebook: from, child);
3478
3479 element = get_drop_position (notebook: to);
3480 page_num = g_list_position (list: to->children, llink: element);
3481 gtk_notebook_insert_page_menu (notebook: to, child, tab_label, menu_label, position: page_num);
3482
3483 page = gtk_notebook_get_page (notebook: to, child);
3484 g_object_set (object: page,
3485 first_property_name: "tab-expand", tab_expand,
3486 "tab-fill", tab_fill,
3487 "reorderable", reorderable,
3488 "detachable", detachable,
3489 NULL);
3490
3491 if (child)
3492 g_object_unref (object: child);
3493
3494 if (tab_label)
3495 g_object_unref (object: tab_label);
3496
3497 if (menu_label)
3498 g_object_unref (object: menu_label);
3499
3500 gtk_notebook_set_current_page (notebook: to, page_num);
3501}
3502
3503/* Private methods:
3504 *
3505 * gtk_notebook_remove
3506 * gtk_notebook_focus
3507 * gtk_notebook_set_focus_child
3508 */
3509static void
3510gtk_notebook_remove (GtkNotebook *notebook,
3511 GtkWidget *widget)
3512{
3513 GtkNotebookPage *page;
3514 GList *children, *list;
3515 int page_num = 0;
3516
3517 children = notebook->children;
3518 while (children)
3519 {
3520 page = children->data;
3521
3522 if (page->child == widget)
3523 break;
3524
3525 page_num++;
3526 children = children->next;
3527 }
3528
3529 if (children == NULL)
3530 return;
3531
3532 g_object_ref (widget);
3533
3534 list = children->next;
3535 gtk_notebook_real_remove (notebook, list: children);
3536
3537 while (list)
3538 {
3539 g_object_notify (G_OBJECT (list->data), property_name: "position");
3540 list = list->next;
3541 }
3542
3543 g_signal_emit (instance: notebook,
3544 signal_id: notebook_signals[PAGE_REMOVED],
3545 detail: 0,
3546 widget,
3547 page_num);
3548
3549 g_object_unref (object: widget);
3550}
3551
3552static gboolean
3553focus_tabs_in (GtkNotebook *notebook)
3554{
3555 if (notebook->show_tabs && gtk_notebook_has_current_page (notebook))
3556 {
3557 gtk_widget_grab_focus (GTK_WIDGET (notebook));
3558 gtk_notebook_set_focus_child (GTK_WIDGET (notebook), NULL);
3559 gtk_notebook_switch_focus_tab (notebook,
3560 new_child: g_list_find (list: notebook->children,
3561 data: notebook->cur_page));
3562
3563 return TRUE;
3564 }
3565 else
3566 return FALSE;
3567}
3568
3569static gboolean
3570focus_tabs_move (GtkNotebook *notebook,
3571 GtkDirectionType direction,
3572 int search_direction)
3573{
3574 GList *new_page;
3575
3576 new_page = gtk_notebook_search_page (notebook, list: notebook->focus_tab,
3577 direction: search_direction, TRUE);
3578 if (!new_page)
3579 {
3580 new_page = gtk_notebook_search_page (notebook, NULL,
3581 direction: search_direction, TRUE);
3582 }
3583
3584 if (new_page)
3585 gtk_notebook_switch_focus_tab (notebook, new_child: new_page);
3586 else
3587 gtk_widget_error_bell (GTK_WIDGET (notebook));
3588
3589 return TRUE;
3590}
3591
3592static gboolean
3593focus_child_in (GtkNotebook *notebook,
3594 GtkDirectionType direction)
3595{
3596 if (notebook->cur_page)
3597 return gtk_widget_child_focus (widget: notebook->cur_page->child, direction);
3598 else
3599 return FALSE;
3600}
3601
3602static gboolean
3603focus_action_in (GtkNotebook *notebook,
3604 int action,
3605 GtkDirectionType direction)
3606{
3607 if (notebook->action_widget[action] &&
3608 gtk_widget_get_visible (widget: notebook->action_widget[action]))
3609 return gtk_widget_child_focus (widget: notebook->action_widget[action], direction);
3610 else
3611 return FALSE;
3612}
3613
3614/* Focus in the notebook can either be on the pages, or on
3615 * the tabs or on the action_widgets.
3616 */
3617static gboolean
3618gtk_notebook_focus (GtkWidget *widget,
3619 GtkDirectionType direction)
3620{
3621 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3622 GtkWidget *old_focus_child;
3623 GtkDirectionType effective_direction;
3624 int first_action;
3625 int last_action;
3626
3627 gboolean widget_is_focus;
3628
3629 if (notebook->tab_pos == GTK_POS_TOP ||
3630 notebook->tab_pos == GTK_POS_LEFT)
3631 {
3632 first_action = ACTION_WIDGET_START;
3633 last_action = ACTION_WIDGET_END;
3634 }
3635 else
3636 {
3637 first_action = ACTION_WIDGET_END;
3638 last_action = ACTION_WIDGET_START;
3639 }
3640
3641 if (notebook->focus_out)
3642 {
3643 notebook->focus_out = FALSE; /* Clear this to catch the wrap-around case */
3644 return FALSE;
3645 }
3646
3647 widget_is_focus = gtk_widget_is_focus (widget);
3648 old_focus_child = gtk_widget_get_focus_child (widget);
3649 if (old_focus_child)
3650 old_focus_child = gtk_widget_get_focus_child (widget: old_focus_child);
3651
3652 effective_direction = get_effective_direction (notebook, direction);
3653
3654 if (old_focus_child) /* Focus on page child or action widget */
3655 {
3656 if (gtk_widget_child_focus (widget: old_focus_child, direction))
3657 return TRUE;
3658
3659 if (old_focus_child == notebook->action_widget[ACTION_WIDGET_START])
3660 {
3661 switch ((guint) effective_direction)
3662 {
3663 case GTK_DIR_DOWN:
3664 return focus_child_in (notebook, direction: GTK_DIR_TAB_FORWARD);
3665 case GTK_DIR_RIGHT:
3666 return focus_tabs_in (notebook);
3667 case GTK_DIR_LEFT:
3668 return FALSE;
3669 case GTK_DIR_UP:
3670 return FALSE;
3671 default:
3672 switch ((guint) direction)
3673 {
3674 case GTK_DIR_TAB_FORWARD:
3675 if ((notebook->tab_pos == GTK_POS_RIGHT || notebook->tab_pos == GTK_POS_BOTTOM) &&
3676 focus_child_in (notebook, direction))
3677 return TRUE;
3678 return focus_tabs_in (notebook);
3679 case GTK_DIR_TAB_BACKWARD:
3680 return FALSE;
3681 default:
3682 g_assert_not_reached ();
3683 break;
3684 }
3685 }
3686 }
3687 else if (old_focus_child == notebook->action_widget[ACTION_WIDGET_END])
3688 {
3689 switch ((guint) effective_direction)
3690 {
3691 case GTK_DIR_DOWN:
3692 return focus_child_in (notebook, direction: GTK_DIR_TAB_FORWARD);
3693 case GTK_DIR_RIGHT:
3694 return FALSE;
3695 case GTK_DIR_LEFT:
3696 return focus_tabs_in (notebook);
3697 case GTK_DIR_UP:
3698 return FALSE;
3699 default:
3700 switch ((guint) direction)
3701 {
3702 case GTK_DIR_TAB_FORWARD:
3703 return FALSE;
3704 case GTK_DIR_TAB_BACKWARD:
3705 if ((notebook->tab_pos == GTK_POS_TOP || notebook->tab_pos == GTK_POS_LEFT) &&
3706 focus_child_in (notebook, direction))
3707 return TRUE;
3708 return focus_tabs_in (notebook);
3709 default:
3710 g_assert_not_reached ();
3711 break;
3712 }
3713 }
3714 }
3715 else
3716 {
3717 switch ((guint) effective_direction)
3718 {
3719 case GTK_DIR_TAB_BACKWARD:
3720 case GTK_DIR_UP:
3721 /* Focus onto the tabs */
3722 return focus_tabs_in (notebook);
3723 case GTK_DIR_DOWN:
3724 case GTK_DIR_LEFT:
3725 case GTK_DIR_RIGHT:
3726 return FALSE;
3727 case GTK_DIR_TAB_FORWARD:
3728 return focus_action_in (notebook, action: last_action, direction);
3729 default:
3730 break;
3731 }
3732 }
3733 }
3734 else if (widget_is_focus) /* Focus was on tabs */
3735 {
3736 switch ((guint) effective_direction)
3737 {
3738 case GTK_DIR_TAB_BACKWARD:
3739 return focus_action_in (notebook, action: first_action, direction);
3740 case GTK_DIR_UP:
3741 return FALSE;
3742 case GTK_DIR_TAB_FORWARD:
3743 if (focus_child_in (notebook, direction: GTK_DIR_TAB_FORWARD))
3744 return TRUE;
3745 return focus_action_in (notebook, action: last_action, direction);
3746 case GTK_DIR_DOWN:
3747 /* We use TAB_FORWARD rather than direction so that we focus a more
3748 * predictable widget for the user; users may be using arrow focusing
3749 * in this situation even if they don't usually use arrow focusing.
3750 */
3751 return focus_child_in (notebook, direction: GTK_DIR_TAB_FORWARD);
3752 case GTK_DIR_LEFT:
3753 return focus_tabs_move (notebook, direction, search_direction: STEP_PREV);
3754 case GTK_DIR_RIGHT:
3755 return focus_tabs_move (notebook, direction, search_direction: STEP_NEXT);
3756 default:
3757 break;
3758 }
3759 }
3760 else /* Focus was not on widget */
3761 {
3762 switch ((guint) effective_direction)
3763 {
3764 case GTK_DIR_TAB_FORWARD:
3765 case GTK_DIR_DOWN:
3766 if (focus_action_in (notebook, action: first_action, direction))
3767 return TRUE;
3768 if (focus_tabs_in (notebook))
3769 return TRUE;
3770 if (focus_action_in (notebook, action: last_action, direction))
3771 return TRUE;
3772 if (focus_child_in (notebook, direction))
3773 return TRUE;
3774 return FALSE;
3775 case GTK_DIR_TAB_BACKWARD:
3776 if (focus_action_in (notebook, action: last_action, direction))
3777 return TRUE;
3778 if (focus_child_in (notebook, direction))
3779 return TRUE;
3780 if (focus_tabs_in (notebook))
3781 return TRUE;
3782 if (focus_action_in (notebook, action: first_action, direction))
3783 return TRUE;
3784 return FALSE;
3785 case GTK_DIR_UP:
3786 case GTK_DIR_LEFT:
3787 case GTK_DIR_RIGHT:
3788 return focus_child_in (notebook, direction);
3789 default:
3790 break;
3791 }
3792 }
3793
3794 g_assert_not_reached ();
3795 return FALSE;
3796}
3797
3798static gboolean
3799gtk_notebook_grab_focus (GtkWidget *widget)
3800{
3801 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3802
3803 if (notebook->show_tabs)
3804 return gtk_widget_grab_focus_self (widget);
3805 else
3806 return gtk_widget_grab_focus_child (widget);
3807}
3808
3809static void
3810gtk_notebook_set_focus_child (GtkWidget *widget,
3811 GtkWidget *child)
3812{
3813 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3814 GtkWidget *page_child;
3815 GtkWidget *toplevel;
3816
3817 /* If the old focus widget was within a page of the notebook,
3818 * (child may either be NULL or not in this case), record it
3819 * for future use if we switch to the page with a mnemonic.
3820 */
3821
3822 toplevel = GTK_WIDGET (gtk_widget_get_root (widget));
3823 if (GTK_IS_WINDOW (toplevel))
3824 {
3825 page_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
3826 while (page_child)
3827 {
3828 if (gtk_widget_get_parent (widget: page_child) == widget)
3829 {
3830 GList *list = gtk_notebook_find_child (notebook, child: page_child);
3831 if (list != NULL)
3832 {
3833 GtkNotebookPage *page = list->data;
3834
3835 if (page->last_focus_child)
3836 g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), weak_pointer_location: (gpointer *)&page->last_focus_child);
3837
3838 page->last_focus_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
3839 g_object_add_weak_pointer (G_OBJECT (page->last_focus_child), weak_pointer_location: (gpointer *)&page->last_focus_child);
3840
3841 break;
3842 }
3843 }
3844
3845 page_child = gtk_widget_get_parent (widget: page_child);
3846 }
3847 }
3848
3849 if (child)
3850 {
3851 g_return_if_fail (GTK_IS_WIDGET (child));
3852
3853 notebook->child_has_focus = TRUE;
3854 if (!notebook->focus_tab)
3855 {
3856 GList *children;
3857 GtkNotebookPage *page;
3858
3859 children = notebook->children;
3860 while (children)
3861 {
3862 page = children->data;
3863 if (page->child == child || page->tab_label == child)
3864 gtk_notebook_switch_focus_tab (notebook, new_child: children);
3865 children = children->next;
3866 }
3867 }
3868 }
3869 else
3870 notebook->child_has_focus = FALSE;
3871
3872 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->set_focus_child (widget, child);
3873}
3874
3875/* Private GtkNotebook Methods:
3876 *
3877 * gtk_notebook_real_insert_page
3878 */
3879static void
3880page_visible_cb (GtkWidget *child,
3881 GParamSpec *arg,
3882 gpointer data)
3883{
3884 GtkNotebook *notebook = GTK_NOTEBOOK (data);
3885 GList *list = gtk_notebook_find_child (notebook, GTK_WIDGET (child));
3886 GtkNotebookPage *page = list->data;
3887 GList *next = NULL;
3888
3889 if (notebook->menu && page->menu_label)
3890 {
3891 GtkWidget *parent = gtk_widget_get_parent (widget: page->menu_label);
3892 if (parent)
3893 gtk_widget_set_visible (widget: parent, visible: gtk_widget_get_visible (widget: child));
3894 }
3895
3896 gtk_widget_set_visible (widget: page->tab_widget, visible: gtk_widget_get_visible (widget: child));
3897
3898 if (notebook->cur_page == page)
3899 {
3900 if (!gtk_widget_get_visible (widget: child))
3901 {
3902 list = g_list_find (list: notebook->children, data: notebook->cur_page);
3903 if (list)
3904 {
3905 next = gtk_notebook_search_page (notebook, list, direction: STEP_NEXT, TRUE);
3906 if (!next)
3907 next = gtk_notebook_search_page (notebook, list, direction: STEP_PREV, TRUE);
3908 }
3909
3910 if (next)
3911 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE_FROM_LIST (next));
3912 }
3913 gtk_widget_set_visible (widget: notebook->header_widget, visible: notebook->show_tabs && gtk_notebook_has_current_page (notebook));
3914 }
3915
3916 if (!gtk_notebook_has_current_page (notebook) && gtk_widget_get_visible (widget: child))
3917 {
3918 gtk_notebook_switch_page (notebook, page);
3919 /* focus_tab is set in the switch_page method */
3920 gtk_notebook_switch_focus_tab (notebook, new_child: notebook->focus_tab);
3921 }
3922}
3923
3924static void
3925measure_tab (GtkGizmo *gizmo,
3926 GtkOrientation orientation,
3927 int for_size,
3928 int *minimum,
3929 int *natural,
3930 int *minimum_baseline,
3931 int *natural_baseline)
3932{
3933 GtkNotebook *notebook = g_object_get_data (G_OBJECT (gizmo), key: "notebook");
3934 GList *l;
3935 GtkNotebookPage *page = NULL;
3936
3937 for (l = notebook->children; l; l = l->next)
3938 {
3939 GtkNotebookPage *p = GTK_NOTEBOOK_PAGE_FROM_LIST (l);
3940 if (p->tab_widget == GTK_WIDGET (gizmo))
3941 {
3942 page = p;
3943 break;
3944 }
3945 }
3946
3947 g_assert (page != NULL);
3948
3949 gtk_widget_measure (widget: page->tab_label,
3950 orientation,
3951 for_size,
3952 minimum, natural,
3953 minimum_baseline, natural_baseline);
3954}
3955
3956static void
3957allocate_tab (GtkGizmo *gizmo,
3958 int width,
3959 int height,
3960 int baseline)
3961{
3962 GtkNotebook *notebook = g_object_get_data (G_OBJECT (gizmo), key: "notebook");
3963 GList *l;
3964 GtkNotebookPage *page = NULL;
3965 GtkAllocation child_allocation;
3966
3967 for (l = notebook->children; l; l = l->next)
3968 {
3969 GtkNotebookPage *p = GTK_NOTEBOOK_PAGE_FROM_LIST (l);
3970 if (p->tab_widget == GTK_WIDGET (gizmo))
3971 {
3972 page = p;
3973 break;
3974 }
3975 }
3976
3977 g_assert (page != NULL);
3978
3979 child_allocation = (GtkAllocation) {0, 0, width, height};
3980
3981 if (!page->fill)
3982 {
3983 if (notebook->tab_pos == GTK_POS_TOP || notebook->tab_pos == GTK_POS_BOTTOM)
3984 {
3985 gtk_widget_measure (widget: page->tab_label, orientation: GTK_ORIENTATION_HORIZONTAL, for_size: height,
3986 NULL, natural: &child_allocation.width, NULL, NULL);
3987 if (child_allocation.width > width)
3988 child_allocation.width = width;
3989 else
3990 child_allocation.x += (width - child_allocation.width) / 2;
3991
3992 }
3993 else
3994 {
3995 gtk_widget_measure (widget: page->tab_label, orientation: GTK_ORIENTATION_VERTICAL, for_size: width,
3996 NULL, natural: &child_allocation.height, NULL, NULL);
3997
3998 if (child_allocation.height > height)
3999 child_allocation.height = height;
4000 else
4001 child_allocation.y += (height - child_allocation.height) / 2;
4002 }
4003 }
4004
4005 gtk_widget_size_allocate (widget: page->tab_label, allocation: &child_allocation, baseline);
4006}
4007
4008static void
4009gtk_notebook_tab_drop_enter (GtkEventController *controller,
4010 double x,
4011 double y,
4012 GtkNotebookPage *page)
4013{
4014 GtkWidget *widget = gtk_event_controller_get_widget (controller);
4015 GtkNotebook *notebook = g_object_get_data (G_OBJECT (widget), key: "notebook");
4016
4017 g_assert (!notebook->switch_page_timer);
4018
4019 notebook->switch_page = page;
4020
4021 notebook->switch_page_timer = g_timeout_add (TIMEOUT_EXPAND, function: gtk_notebook_switch_page_timeout, data: notebook);
4022 gdk_source_set_static_name_by_id (tag: notebook->switch_page_timer, name: "[gtk] gtk_notebook_switch_page_timeout");
4023}
4024
4025static void
4026gtk_notebook_tab_drop_leave (GtkEventController *controller,
4027 GtkNotebookPage *page)
4028{
4029 GtkWidget *widget = gtk_event_controller_get_widget (controller);
4030 GtkNotebook *notebook = g_object_get_data (G_OBJECT (widget), key: "notebook");
4031
4032 g_clear_handle_id (&notebook->switch_page_timer, g_source_remove);
4033}
4034
4035static int
4036gtk_notebook_insert_notebook_page (GtkNotebook *notebook,
4037 GtkNotebookPage *page,
4038 int position)
4039{
4040 int nchildren;
4041 GList *list;
4042 GtkWidget *sibling;
4043 GtkEventController *controller;
4044 GtkStackPage *stack_page;
4045
4046 nchildren = g_list_length (list: notebook->children);
4047 if ((position < 0) || (position > nchildren))
4048 position = nchildren;
4049
4050 notebook->children = g_list_insert (list: notebook->children, g_object_ref (page), position);
4051
4052 if (position < nchildren)
4053 sibling = GTK_NOTEBOOK_PAGE_FROM_LIST (g_list_nth (notebook->children, position))->tab_widget;
4054 else if (notebook->arrow_widget[ARROW_LEFT_AFTER])
4055 sibling = notebook->arrow_widget[ARROW_LEFT_AFTER];
4056 else
4057 sibling = notebook->arrow_widget[ARROW_RIGHT_AFTER];
4058
4059 page->tab_widget = gtk_gizmo_new_with_role (css_name: "tab",
4060 role: GTK_ACCESSIBLE_ROLE_TAB,
4061 measure_func: measure_tab,
4062 allocate_func: allocate_tab,
4063 NULL,
4064 NULL,
4065 NULL,
4066 NULL);
4067 g_object_set_data (G_OBJECT (page->tab_widget), key: "notebook", data: notebook);
4068 gtk_widget_insert_before (widget: page->tab_widget, parent: notebook->tabs_widget, next_sibling: sibling);
4069 controller = gtk_drop_controller_motion_new ();
4070 g_signal_connect (controller, "enter", G_CALLBACK (gtk_notebook_tab_drop_enter), page);
4071 g_signal_connect (controller, "leave", G_CALLBACK (gtk_notebook_tab_drop_leave), page);
4072 gtk_widget_add_controller (widget: page->tab_widget, controller);
4073 gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: page->tab_widget),
4074 first_property: GTK_ACCESSIBLE_PROPERTY_LABEL, _("Tab"),
4075 -1);
4076
4077 page->expand = FALSE;
4078 page->fill = TRUE;
4079
4080 if (notebook->menu)
4081 gtk_notebook_menu_item_create (notebook, page);
4082
4083 gtk_stack_add_named (GTK_STACK (notebook->stack_widget), child: page->child, NULL);
4084
4085 if (page->tab_label)
4086 {
4087 gtk_widget_set_parent (widget: page->tab_label, parent: page->tab_widget);
4088 g_object_set_data (G_OBJECT (page->tab_label), key: "notebook", data: notebook);
4089 }
4090
4091 stack_page = gtk_stack_get_page (GTK_STACK (notebook->stack_widget), child: page->child);
4092 gtk_accessible_update_relation (self: GTK_ACCESSIBLE (ptr: page->tab_widget),
4093 first_relation: GTK_ACCESSIBLE_RELATION_CONTROLS, stack_page, NULL,
4094 -1);
4095 gtk_accessible_update_relation (self: GTK_ACCESSIBLE (ptr: stack_page),
4096 first_relation: GTK_ACCESSIBLE_RELATION_LABELLED_BY, page->tab_widget, NULL,
4097 -1);
4098
4099 gtk_accessible_update_state (self: GTK_ACCESSIBLE (ptr: page->tab_widget),
4100 first_state: GTK_ACCESSIBLE_STATE_SELECTED, FALSE,
4101 -1);
4102
4103 gtk_notebook_update_labels (notebook);
4104
4105 if (!notebook->first_tab)
4106 notebook->first_tab = notebook->children;
4107
4108 if (page->tab_label)
4109 {
4110 if (notebook->show_tabs && gtk_widget_get_visible (widget: page->child))
4111 gtk_widget_show (widget: page->tab_label);
4112 else
4113 gtk_widget_hide (widget: page->tab_label);
4114
4115 page->mnemonic_activate_signal =
4116 g_signal_connect (page->tab_label,
4117 "mnemonic-activate",
4118 G_CALLBACK (gtk_notebook_mnemonic_activate_switch_page),
4119 notebook);
4120 }
4121
4122 page->notify_visible_handler = g_signal_connect (page->child, "notify::visible",
4123 G_CALLBACK (page_visible_cb), notebook);
4124
4125 g_signal_emit (instance: notebook, signal_id: notebook_signals[PAGE_ADDED], detail: 0, page->child, position);
4126
4127 if (!gtk_notebook_has_current_page (notebook))
4128 {
4129 gtk_notebook_switch_page (notebook, page);
4130 /* focus_tab is set in the switch_page method */
4131 gtk_notebook_switch_focus_tab (notebook, new_child: notebook->focus_tab);
4132 }
4133
4134 g_object_notify (G_OBJECT (page), property_name: "tab-expand");
4135 g_object_notify (G_OBJECT (page), property_name: "tab-fill");
4136 g_object_notify (G_OBJECT (page), property_name: "tab-label");
4137 g_object_notify (G_OBJECT (page), property_name: "menu-label");
4138
4139 list = g_list_nth (list: notebook->children, n: position);
4140 while (list)
4141 {
4142 g_object_notify (G_OBJECT (list->data), property_name: "position");
4143 list = list->next;
4144 }
4145
4146 update_arrow_state (notebook);
4147
4148 if (notebook->pages)
4149 g_list_model_items_changed (list: notebook->pages, position, removed: 0, added: 1);
4150
4151 /* The page-added handler might have reordered the pages, re-get the position */
4152 return gtk_notebook_page_num (notebook, child: page->child);
4153}
4154
4155static int
4156gtk_notebook_real_insert_page (GtkNotebook *notebook,
4157 GtkWidget *child,
4158 GtkWidget *tab_label,
4159 GtkWidget *menu_label,
4160 int position)
4161{
4162 GtkNotebookPage *page;
4163 int ret;
4164
4165 page = g_object_new (GTK_TYPE_NOTEBOOK_PAGE,
4166 first_property_name: "child", child,
4167 "tab", tab_label,
4168 "menu", menu_label,
4169 NULL);
4170
4171 ret = gtk_notebook_insert_notebook_page (notebook, page, position);
4172
4173 g_object_unref (object: page);
4174
4175 return ret;
4176}
4177
4178static gboolean
4179gtk_notebook_timer (GtkNotebook *notebook)
4180{
4181 gboolean retval = FALSE;
4182
4183 if (notebook->timer)
4184 {
4185 gtk_notebook_do_arrow (notebook, arrow: notebook->click_child);
4186
4187 if (notebook->need_timer)
4188 {
4189 notebook->need_timer = FALSE;
4190 notebook->timer = g_timeout_add (TIMEOUT_REPEAT * SCROLL_DELAY_FACTOR,
4191 function: (GSourceFunc) gtk_notebook_timer,
4192 data: notebook);
4193 gdk_source_set_static_name_by_id (tag: notebook->timer, name: "[gtk] gtk_notebook_timer");
4194 }
4195 else
4196 retval = TRUE;
4197 }
4198
4199 return retval;
4200}
4201
4202static void
4203gtk_notebook_set_scroll_timer (GtkNotebook *notebook)
4204{
4205 if (!notebook->timer)
4206 {
4207 notebook->timer = g_timeout_add (TIMEOUT_INITIAL,
4208 function: (GSourceFunc) gtk_notebook_timer,
4209 data: notebook);
4210 gdk_source_set_static_name_by_id (tag: notebook->timer, name: "[gtk] gtk_notebook_timer");
4211 notebook->need_timer = TRUE;
4212 }
4213}
4214
4215static int
4216gtk_notebook_page_compare (gconstpointer a,
4217 gconstpointer b)
4218{
4219 return (((GtkNotebookPage *) a)->child != b);
4220}
4221
4222static GList*
4223gtk_notebook_find_child (GtkNotebook *notebook,
4224 GtkWidget *child)
4225{
4226 return g_list_find_custom (list: notebook->children,
4227 data: child,
4228 func: gtk_notebook_page_compare);
4229}
4230
4231static void
4232gtk_notebook_remove_tab_label (GtkNotebook *notebook,
4233 GtkNotebookPage *page)
4234{
4235 if (page->tab_label)
4236 {
4237 if (page->mnemonic_activate_signal)
4238 g_signal_handler_disconnect (instance: page->tab_label,
4239 handler_id: page->mnemonic_activate_signal);
4240 page->mnemonic_activate_signal = 0;
4241
4242 if (gtk_widget_get_native (widget: page->tab_label) != gtk_widget_get_native (GTK_WIDGET (notebook)) ||
4243 !NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
4244 {
4245 GtkWidget *parent;
4246
4247 /* we hit this condition during dnd of a detached tab */
4248 parent = gtk_widget_get_parent (widget: page->tab_label);
4249 if (GTK_IS_WINDOW (parent))
4250 gtk_box_remove (GTK_BOX (parent), child: page->tab_label);
4251 else
4252 gtk_widget_unparent (widget: page->tab_label);
4253 }
4254 else
4255 {
4256 gtk_widget_unparent (widget: page->tab_label);
4257 }
4258
4259 page->tab_label = NULL;
4260 }
4261}
4262
4263static void
4264gtk_notebook_real_remove (GtkNotebook *notebook,
4265 GList *list)
4266{
4267 GtkNotebookPage *page;
4268 GList * next_list;
4269 int need_resize = FALSE;
4270 GtkWidget *tab_label;
4271 gboolean destroying;
4272 int position;
4273
4274 page = list->data;
4275
4276 destroying = gtk_widget_in_destruction (GTK_WIDGET (notebook));
4277
4278 next_list = gtk_notebook_search_page (notebook, list, direction: STEP_NEXT, TRUE);
4279 if (!next_list)
4280 next_list = gtk_notebook_search_page (notebook, list, direction: STEP_PREV, TRUE);
4281
4282 notebook->children = g_list_remove_link (list: notebook->children, llink: list);
4283
4284 if (notebook->cur_page == list->data)
4285 {
4286 notebook->cur_page = NULL;
4287 if (next_list && !destroying)
4288 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE_FROM_LIST (next_list));
4289 if (notebook->operation == DRAG_OPERATION_REORDER && !notebook->remove_in_detach)
4290 gtk_notebook_stop_reorder (notebook);
4291 }
4292
4293 if (notebook->detached_tab == list->data)
4294 notebook->detached_tab = NULL;
4295
4296 if (notebook->switch_page == page)
4297 notebook->switch_page = NULL;
4298
4299 if (list == notebook->first_tab)
4300 notebook->first_tab = next_list;
4301 if (list == notebook->focus_tab && !destroying)
4302 gtk_notebook_switch_focus_tab (notebook, new_child: next_list);
4303
4304 position = g_list_index (list: notebook->children, data: page);
4305
4306 g_signal_handler_disconnect (instance: page->child, handler_id: page->notify_visible_handler);
4307
4308 if (gtk_widget_get_visible (widget: page->child) &&
4309 gtk_widget_get_visible (GTK_WIDGET (notebook)))
4310 need_resize = TRUE;
4311
4312 gtk_stack_remove (GTK_STACK (notebook->stack_widget), child: page->child);
4313
4314 tab_label = page->tab_label;
4315 if (tab_label)
4316 {
4317 g_object_ref (tab_label);
4318 gtk_notebook_remove_tab_label (notebook, page);
4319 if (destroying)
4320 gtk_widget_unparent (widget: tab_label);
4321 g_object_unref (object: tab_label);
4322 }
4323
4324 if (notebook->menu)
4325 {
4326 GtkWidget *parent = gtk_widget_get_parent (widget: page->menu_label);
4327
4328 if (parent)
4329 gtk_notebook_menu_label_unparent (widget: parent);
4330 gtk_popover_set_child (GTK_POPOVER (notebook->menu), NULL);
4331
4332 gtk_widget_queue_resize (widget: notebook->menu);
4333 }
4334
4335 g_list_free (list);
4336
4337 if (page->last_focus_child)
4338 {
4339 g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), weak_pointer_location: (gpointer *)&page->last_focus_child);
4340 page->last_focus_child = NULL;
4341 }
4342
4343 gtk_widget_unparent (widget: page->tab_widget);
4344
4345 g_object_unref (object: page);
4346
4347 gtk_notebook_update_labels (notebook);
4348 if (need_resize)
4349 gtk_widget_queue_resize (GTK_WIDGET (notebook));
4350
4351 if (notebook->pages)
4352 g_list_model_items_changed (list: notebook->pages, position, removed: 1, added: 0);
4353}
4354
4355static void
4356gtk_notebook_update_labels (GtkNotebook *notebook)
4357{
4358 GtkNotebookPage *page;
4359 GList *list;
4360 char string[32];
4361 int page_num = 1;
4362
4363 if (!notebook->show_tabs && !notebook->menu)
4364 return;
4365
4366 for (list = gtk_notebook_search_page (notebook, NULL, direction: STEP_NEXT, FALSE);
4367 list;
4368 list = gtk_notebook_search_page (notebook, list, direction: STEP_NEXT, FALSE))
4369 {
4370 const char *text;
4371 page = list->data;
4372 g_snprintf (string, n: sizeof (string), _("Page %u"), page_num++);
4373 if (page->tab_text)
4374 text = page->tab_text;
4375 else
4376 text = string;
4377 if (notebook->show_tabs)
4378 {
4379 if (page->default_tab)
4380 {
4381 if (!page->tab_label)
4382 {
4383 page->tab_label = gtk_label_new (str: "");
4384 g_object_ref_sink (page->tab_label);
4385 g_object_set_data (G_OBJECT (page->tab_label), key: "notebook", data: notebook);
4386 gtk_widget_set_parent (widget: page->tab_label, parent: page->tab_widget);
4387 }
4388 gtk_label_set_text (GTK_LABEL (page->tab_label), str: text);
4389 }
4390
4391 if (page->child && page->tab_label)
4392 gtk_widget_set_visible (widget: page->tab_label, visible: gtk_widget_get_visible (widget: page->child));
4393 }
4394
4395 if (notebook->menu && page->default_menu)
4396 {
4397 if (page->menu_text)
4398 text = page->menu_text;
4399 else if (GTK_IS_LABEL (page->tab_label))
4400 text = gtk_label_get_text (GTK_LABEL (page->tab_label));
4401 else
4402 text = string;
4403 gtk_label_set_text (GTK_LABEL (page->menu_label), str: text);
4404 }
4405 }
4406}
4407
4408static GList *
4409gtk_notebook_search_page (GtkNotebook *notebook,
4410 GList *list,
4411 int direction,
4412 gboolean find_visible)
4413{
4414 GtkNotebookPage *page = NULL;
4415 GList *old_list = NULL;
4416
4417 if (list)
4418 page = list->data;
4419
4420 if (!page || direction == STEP_NEXT)
4421 {
4422 if (list)
4423 {
4424 old_list = list;
4425 list = list->next;
4426 }
4427 else
4428 list = notebook->children;
4429
4430 while (list)
4431 {
4432 page = list->data;
4433 if (direction == STEP_NEXT &&
4434 (!find_visible ||
4435 (gtk_widget_get_visible (widget: page->child) &&
4436 (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
4437 return list;
4438 old_list = list;
4439 list = list->next;
4440 }
4441 list = old_list;
4442 }
4443 else
4444 {
4445 list = list->prev;
4446 }
4447 while (list)
4448 {
4449 page = list->data;
4450 if (direction == STEP_PREV &&
4451 (!find_visible ||
4452 (gtk_widget_get_visible (widget: page->child) &&
4453 (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
4454 return list;
4455 list = list->prev;
4456 }
4457 return NULL;
4458}
4459
4460static void
4461gtk_notebook_snapshot_tabs (GtkGizmo *gizmo,
4462 GtkSnapshot *snapshot)
4463{
4464 GtkWidget *widget = gtk_widget_get_parent (GTK_WIDGET (gizmo));
4465 GtkNotebook *notebook = GTK_NOTEBOOK (gtk_widget_get_parent (widget));
4466 GtkNotebookPage *page;
4467 GList *children;
4468 gboolean showarrow;
4469 int step = STEP_PREV;
4470 gboolean is_rtl;
4471 GtkPositionType tab_pos;
4472 guint i;
4473
4474 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
4475 tab_pos = get_effective_tab_pos (notebook);
4476 showarrow = FALSE;
4477
4478 if (!gtk_notebook_has_current_page (notebook))
4479 return;
4480
4481 if (!notebook->first_tab)
4482 notebook->first_tab = notebook->children;
4483
4484 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, notebook->cur_page) ||
4485 !gtk_widget_get_mapped (widget: notebook->cur_page->tab_label))
4486 {
4487 step = STEP_PREV;
4488 }
4489 else
4490 {
4491 switch (tab_pos)
4492 {
4493 case GTK_POS_TOP:
4494 case GTK_POS_BOTTOM:
4495 step = is_rtl ? STEP_PREV : STEP_NEXT;
4496 break;
4497 case GTK_POS_LEFT:
4498 case GTK_POS_RIGHT:
4499 step = STEP_PREV;
4500 break;
4501 default:
4502 g_assert_not_reached ();
4503 break;
4504 }
4505 }
4506
4507 for (children = notebook->children; children; children = children->next)
4508 {
4509 page = children->data;
4510
4511 if (!gtk_widget_get_visible (widget: page->child) ||
4512 page == notebook->detached_tab)
4513 continue;
4514
4515 if (!gtk_widget_get_mapped (widget: page->tab_label))
4516 showarrow = TRUE;
4517
4518 /* No point in keeping searching */
4519 if (showarrow)
4520 break;
4521 }
4522
4523 for (children = gtk_notebook_search_page (notebook, NULL, direction: step, TRUE);
4524 children;
4525 children = gtk_notebook_search_page (notebook, list: children, direction: step, TRUE))
4526 {
4527 page = children->data;
4528
4529 if (page == notebook->cur_page)
4530 break;
4531
4532 if (!gtk_notebook_page_tab_label_is_visible (page))
4533 continue;
4534
4535 gtk_widget_snapshot_child (GTK_WIDGET (gizmo), child: page->tab_widget, snapshot);
4536 }
4537
4538 if (children != NULL)
4539 {
4540 GList *other_order = NULL;
4541
4542 for (children = gtk_notebook_search_page (notebook, list: children, direction: step, TRUE);
4543 children;
4544 children = gtk_notebook_search_page (notebook, list: children, direction: step, TRUE))
4545 {
4546 page = children->data;
4547
4548 if (!gtk_notebook_page_tab_label_is_visible (page))
4549 continue;
4550
4551 other_order = g_list_prepend (list: other_order, data: page);
4552 }
4553
4554 /* draw them with the opposite order */
4555 for (children = other_order; children; children = children->next)
4556 {
4557 page = children->data;
4558 gtk_widget_snapshot_child (GTK_WIDGET (gizmo), child: page->tab_widget, snapshot);
4559 }
4560
4561 g_list_free (list: other_order);
4562 }
4563
4564 if (showarrow && notebook->scrollable)
4565 {
4566 for (i = 0; i < 4; i++)
4567 {
4568 if (notebook->arrow_widget[i] == NULL)
4569 continue;
4570
4571 gtk_widget_snapshot_child (GTK_WIDGET (gizmo), child: notebook->arrow_widget[i], snapshot);
4572 }
4573 }
4574
4575 if (notebook->operation != DRAG_OPERATION_DETACH)
4576 gtk_widget_snapshot_child (GTK_WIDGET (gizmo), child: notebook->cur_page->tab_widget, snapshot);
4577}
4578
4579/* Private GtkNotebook Size Allocate Functions:
4580 *
4581 * gtk_notebook_calculate_shown_tabs
4582 * gtk_notebook_calculate_tabs_allocation
4583 * gtk_notebook_calc_tabs
4584 */
4585static void
4586gtk_notebook_allocate_arrows (GtkNotebook *notebook,
4587 GtkAllocation *allocation)
4588{
4589 GtkAllocation arrow_allocation;
4590 int size1, size2, min, nat;
4591 guint i, ii;
4592
4593 switch (notebook->tab_pos)
4594 {
4595 case GTK_POS_TOP:
4596 case GTK_POS_BOTTOM:
4597 arrow_allocation.y = allocation->y;
4598 arrow_allocation.height = allocation->height;
4599 for (i = 0; i < 4; i++)
4600 {
4601 ii = i < 2 ? i : i ^ 1;
4602
4603 if (notebook->arrow_widget[ii] == NULL)
4604 continue;
4605
4606 gtk_widget_measure (widget: notebook->arrow_widget[ii],
4607 orientation: GTK_ORIENTATION_HORIZONTAL,
4608 for_size: allocation->height,
4609 minimum: &min, natural: &nat,
4610 NULL, NULL);
4611 if (i < 2)
4612 {
4613 arrow_allocation.x = allocation->x;
4614 arrow_allocation.width = min;
4615 gtk_widget_size_allocate (widget: notebook->arrow_widget[ii],
4616 allocation: &arrow_allocation,
4617 baseline: -1);
4618 allocation->x += min;
4619 allocation->width -= min;
4620 }
4621 else
4622 {
4623 arrow_allocation.x = allocation->x + allocation->width - min;
4624 arrow_allocation.width = min;
4625 gtk_widget_size_allocate (widget: notebook->arrow_widget[ii],
4626 allocation: &arrow_allocation,
4627 baseline: -1);
4628 allocation->width -= min;
4629 }
4630 }
4631 break;
4632
4633 case GTK_POS_LEFT:
4634 case GTK_POS_RIGHT:
4635 if (notebook->arrow_widget[0] || notebook->arrow_widget[1])
4636 {
4637 gtk_notebook_measure_arrows (notebook,
4638 type: GTK_PACK_START,
4639 orientation: GTK_ORIENTATION_VERTICAL,
4640 for_size: allocation->width,
4641 minimum: &min, natural: &nat,
4642 NULL, NULL);
4643 gtk_notebook_distribute_arrow_width (notebook, type: GTK_PACK_START, size: allocation->width, out_left: &size1, out_right: &size2);
4644 arrow_allocation.x = allocation->x;
4645 arrow_allocation.y = allocation->y;
4646 arrow_allocation.width = size1;
4647 arrow_allocation.height = min;
4648 if (notebook->arrow_widget[0])
4649 gtk_widget_size_allocate (widget: notebook->arrow_widget[0], allocation: &arrow_allocation, baseline: -1);
4650 arrow_allocation.x += size1;
4651 arrow_allocation.width = size2;
4652 if (notebook->arrow_widget[1])
4653 gtk_widget_size_allocate (widget: notebook->arrow_widget[1], allocation: &arrow_allocation, baseline: -1);
4654 allocation->y += min;
4655 allocation->height -= min;
4656 }
4657 if (notebook->arrow_widget[2] || notebook->arrow_widget[3])
4658 {
4659 gtk_notebook_measure_arrows (notebook,
4660 type: GTK_PACK_END,
4661 orientation: GTK_ORIENTATION_VERTICAL,
4662 for_size: allocation->width,
4663 minimum: &min, natural: &nat,
4664 NULL, NULL);
4665 gtk_notebook_distribute_arrow_width (notebook, type: GTK_PACK_END, size: allocation->width, out_left: &size1, out_right: &size2);
4666 arrow_allocation.x = allocation->x;
4667 arrow_allocation.y = allocation->y + allocation->height - min;
4668 arrow_allocation.width = size1;
4669 arrow_allocation.height = min;
4670 if (notebook->arrow_widget[2])
4671 gtk_widget_size_allocate (widget: notebook->arrow_widget[2], allocation: &arrow_allocation, baseline: -1);
4672 arrow_allocation.x += size1;
4673 arrow_allocation.width = size2;
4674 if (notebook->arrow_widget[3])
4675 gtk_widget_size_allocate (widget: notebook->arrow_widget[3], allocation: &arrow_allocation, baseline: -1);
4676 allocation->height -= min;
4677 }
4678 break;
4679
4680 default:
4681 g_assert_not_reached ();
4682 break;
4683 }
4684}
4685
4686
4687static void
4688gtk_notebook_tab_space (GtkNotebook *notebook,
4689 int notebook_width,
4690 int notebook_height,
4691 gboolean *show_arrows,
4692 GtkAllocation *tabs_allocation,
4693 int *tab_space)
4694{
4695 GList *children;
4696 GtkPositionType tab_pos = get_effective_tab_pos (notebook);
4697
4698 children = notebook->children;
4699
4700 *tabs_allocation = (GtkAllocation) {0, 0, notebook_width, notebook_height};
4701
4702 switch (tab_pos)
4703 {
4704 case GTK_POS_TOP:
4705 case GTK_POS_BOTTOM:
4706 while (children)
4707 {
4708 GtkNotebookPage *page;
4709
4710 page = children->data;
4711 children = children->next;
4712
4713 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
4714 gtk_widget_get_visible (widget: page->child))
4715 *tab_space += page->requisition.width;
4716 }
4717 break;
4718 case GTK_POS_RIGHT:
4719 case GTK_POS_LEFT:
4720 while (children)
4721 {
4722 GtkNotebookPage *page;
4723
4724 page = children->data;
4725 children = children->next;
4726
4727 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
4728 gtk_widget_get_visible (widget: page->child))
4729 *tab_space += page->requisition.height;
4730 }
4731 break;
4732
4733 default:
4734 g_assert_not_reached ();
4735 break;
4736 }
4737
4738 if (!notebook->scrollable)
4739 *show_arrows = FALSE;
4740 else
4741 {
4742 switch (tab_pos)
4743 {
4744 case GTK_POS_TOP:
4745 case GTK_POS_BOTTOM:
4746 if (*tab_space > tabs_allocation->width)
4747 {
4748 *show_arrows = TRUE;
4749
4750 gtk_notebook_allocate_arrows (notebook, allocation: tabs_allocation);
4751
4752 *tab_space = tabs_allocation->width;
4753 }
4754 break;
4755 case GTK_POS_LEFT:
4756 case GTK_POS_RIGHT:
4757 if (*tab_space > tabs_allocation->height)
4758 {
4759 *show_arrows = TRUE;
4760
4761 gtk_notebook_allocate_arrows (notebook, allocation: tabs_allocation);
4762
4763 *tab_space = tabs_allocation->height;
4764 }
4765 break;
4766
4767 default:
4768 g_assert_not_reached ();
4769 break;
4770 }
4771 }
4772}
4773
4774static void
4775gtk_notebook_calculate_shown_tabs (GtkNotebook *notebook,
4776 gboolean show_arrows,
4777 const GtkAllocation *tabs_allocation,
4778 int tab_space,
4779 GList **last_child,
4780 int *n,
4781 int *remaining_space)
4782{
4783 GList *children;
4784 GtkNotebookPage *page;
4785
4786 if (show_arrows) /* first_tab <- focus_tab */
4787 {
4788 *remaining_space = tab_space;
4789
4790 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, notebook->cur_page) &&
4791 gtk_widget_get_visible (widget: notebook->cur_page->child))
4792 {
4793 gtk_notebook_calc_tabs (notebook,
4794 start: notebook->focus_tab,
4795 end: &(notebook->focus_tab),
4796 tab_space: remaining_space, direction: STEP_NEXT);
4797 }
4798
4799 if (tab_space <= 0 || *remaining_space <= 0)
4800 {
4801 /* show 1 tab */
4802 notebook->first_tab = notebook->focus_tab;
4803 *last_child = gtk_notebook_search_page (notebook, list: notebook->focus_tab,
4804 direction: STEP_NEXT, TRUE);
4805 *n = 1;
4806 }
4807 else
4808 {
4809 children = NULL;
4810
4811 if (notebook->first_tab && notebook->first_tab != notebook->focus_tab)
4812 {
4813 /* Is first_tab really predecessor of focus_tab? */
4814 page = notebook->first_tab->data;
4815 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
4816 gtk_widget_get_visible (widget: page->child))
4817 for (children = notebook->focus_tab;
4818 children && children != notebook->first_tab;
4819 children = gtk_notebook_search_page (notebook,
4820 list: children,
4821 direction: STEP_PREV,
4822 TRUE));
4823 }
4824
4825 if (!children)
4826 {
4827 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, notebook->cur_page))
4828 notebook->first_tab = notebook->focus_tab;
4829 else
4830 notebook->first_tab = gtk_notebook_search_page (notebook, list: notebook->focus_tab,
4831 direction: STEP_NEXT, TRUE);
4832 }
4833 else
4834 /* calculate shown tabs counting backwards from the focus tab */
4835 gtk_notebook_calc_tabs (notebook,
4836 start: gtk_notebook_search_page (notebook,
4837 list: notebook->focus_tab,
4838 direction: STEP_PREV,
4839 TRUE),
4840 end: &(notebook->first_tab),
4841 tab_space: remaining_space,
4842 direction: STEP_PREV);
4843
4844 if (*remaining_space < 0)
4845 {
4846 notebook->first_tab =
4847 gtk_notebook_search_page (notebook, list: notebook->first_tab,
4848 direction: STEP_NEXT, TRUE);
4849 if (!notebook->first_tab)
4850 notebook->first_tab = notebook->focus_tab;
4851
4852 *last_child = gtk_notebook_search_page (notebook, list: notebook->focus_tab,
4853 direction: STEP_NEXT, TRUE);
4854 }
4855 else /* focus_tab -> end */
4856 {
4857 if (!notebook->first_tab)
4858 notebook->first_tab = gtk_notebook_search_page (notebook,
4859 NULL,
4860 direction: STEP_NEXT,
4861 TRUE);
4862 children = NULL;
4863 gtk_notebook_calc_tabs (notebook,
4864 start: gtk_notebook_search_page (notebook,
4865 list: notebook->focus_tab,
4866 direction: STEP_NEXT,
4867 TRUE),
4868 end: &children,
4869 tab_space: remaining_space,
4870 direction: STEP_NEXT);
4871
4872 if (*remaining_space <= 0)
4873 *last_child = children;
4874 else /* start <- first_tab */
4875 {
4876 *last_child = NULL;
4877 children = NULL;
4878
4879 gtk_notebook_calc_tabs (notebook,
4880 start: gtk_notebook_search_page (notebook,
4881 list: notebook->first_tab,
4882 direction: STEP_PREV,
4883 TRUE),
4884 end: &children,
4885 tab_space: remaining_space,
4886 direction: STEP_PREV);
4887
4888 if (*remaining_space == 0)
4889 notebook->first_tab = children;
4890 else
4891 notebook->first_tab = gtk_notebook_search_page(notebook,
4892 list: children,
4893 direction: STEP_NEXT,
4894 TRUE);
4895 }
4896 }
4897
4898 if (*remaining_space < 0)
4899 {
4900 /* calculate number of tabs */
4901 *remaining_space = - (*remaining_space);
4902 *n = 0;
4903
4904 for (children = notebook->first_tab;
4905 children && children != *last_child;
4906 children = gtk_notebook_search_page (notebook, list: children,
4907 direction: STEP_NEXT, TRUE))
4908 (*n)++;
4909 }
4910 else
4911 *remaining_space = 0;
4912 }
4913
4914 /* unmap all non-visible tabs */
4915 for (children = gtk_notebook_search_page (notebook, NULL,
4916 direction: STEP_NEXT, TRUE);
4917 children && children != notebook->first_tab;
4918 children = gtk_notebook_search_page (notebook, list: children,
4919 direction: STEP_NEXT, TRUE))
4920 {
4921 page = children->data;
4922
4923 if (page->tab_label &&
4924 NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
4925 gtk_widget_set_child_visible (widget: page->tab_widget, FALSE);
4926 }
4927
4928 for (children = *last_child; children;
4929 children = gtk_notebook_search_page (notebook, list: children,
4930 direction: STEP_NEXT, TRUE))
4931 {
4932 page = children->data;
4933
4934 if (page->tab_label &&
4935 NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
4936 gtk_widget_set_child_visible (widget: page->tab_widget, FALSE);
4937 }
4938 }
4939 else /* !show_arrows */
4940 {
4941 GtkOrientation tab_expand_orientation;
4942 int c = 0;
4943 *n = 0;
4944
4945 if (notebook->tab_pos == GTK_POS_TOP || notebook->tab_pos == GTK_POS_BOTTOM)
4946 {
4947 tab_expand_orientation = GTK_ORIENTATION_HORIZONTAL;
4948 *remaining_space = tabs_allocation->width - tab_space;
4949 }
4950 else
4951 {
4952 tab_expand_orientation = GTK_ORIENTATION_VERTICAL;
4953 *remaining_space = tabs_allocation->height - tab_space;
4954 }
4955 children = notebook->children;
4956 notebook->first_tab = gtk_notebook_search_page (notebook, NULL,
4957 direction: STEP_NEXT, TRUE);
4958 while (children)
4959 {
4960 page = children->data;
4961 children = children->next;
4962
4963 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) ||
4964 !gtk_widget_get_visible (widget: page->child))
4965 continue;
4966
4967 c++;
4968
4969 if (page->expand ||
4970 (gtk_widget_compute_expand (widget: page->tab_label, orientation: tab_expand_orientation)))
4971 (*n)++;
4972 }
4973 }
4974}
4975
4976static gboolean
4977get_allocate_at_bottom (GtkWidget *widget,
4978 int search_direction)
4979{
4980 gboolean is_rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
4981 GtkPositionType tab_pos = get_effective_tab_pos (GTK_NOTEBOOK (widget));
4982
4983 switch (tab_pos)
4984 {
4985 case GTK_POS_TOP:
4986 case GTK_POS_BOTTOM:
4987 if (!is_rtl)
4988 return (search_direction == STEP_PREV);
4989 else
4990 return (search_direction == STEP_NEXT);
4991
4992 case GTK_POS_RIGHT:
4993 case GTK_POS_LEFT:
4994 return (search_direction == STEP_PREV);
4995
4996 default:
4997 g_assert_not_reached ();
4998 return FALSE;
4999 }
5000}
5001
5002static void
5003gtk_notebook_calculate_tabs_allocation (GtkNotebook *notebook,
5004 GList **children,
5005 GList *last_child,
5006 gboolean showarrow,
5007 int direction,
5008 int *remaining_space,
5009 int *expanded_tabs,
5010 const GtkAllocation *allocation)
5011{
5012 GtkWidget *widget;
5013 GtkNotebookPage *page;
5014 gboolean allocate_at_bottom;
5015 int tab_extra_space;
5016 GtkPositionType tab_pos;
5017 int left_x, right_x, top_y, bottom_y, anchor;
5018 gboolean gap_left, packing_changed;
5019 GtkAllocation child_allocation;
5020 GtkOrientation tab_expand_orientation;
5021 graphene_rect_t drag_bounds;
5022
5023 g_assert (notebook->cur_page != NULL);
5024
5025 widget = GTK_WIDGET (notebook);
5026 tab_pos = get_effective_tab_pos (notebook);
5027 allocate_at_bottom = get_allocate_at_bottom (widget, search_direction: direction);
5028
5029 child_allocation = *allocation;
5030
5031 switch (tab_pos)
5032 {
5033 case GTK_POS_BOTTOM:
5034 case GTK_POS_TOP:
5035 if (allocate_at_bottom)
5036 child_allocation.x += allocation->width;
5037 anchor = child_allocation.x;
5038 break;
5039
5040 case GTK_POS_RIGHT:
5041 case GTK_POS_LEFT:
5042 if (allocate_at_bottom)
5043 child_allocation.y += allocation->height;
5044 anchor = child_allocation.y;
5045 break;
5046
5047 default:
5048 g_assert_not_reached ();
5049 anchor = 0;
5050 break;
5051 }
5052
5053 if (!gtk_widget_compute_bounds (widget: notebook->cur_page->tab_widget, target: notebook->cur_page->tab_widget, out_bounds: &drag_bounds))
5054 graphene_rect_init_from_rect (r: &drag_bounds, src: graphene_rect_zero ());
5055
5056 left_x = CLAMP (notebook->mouse_x - notebook->drag_offset_x,
5057 allocation->x, allocation->x + allocation->width - drag_bounds.size.width);
5058 top_y = CLAMP (notebook->mouse_y - notebook->drag_offset_y,
5059 allocation->y, allocation->y + allocation->height - drag_bounds.size.height);
5060 right_x = left_x + drag_bounds.size.width;
5061 bottom_y = top_y + drag_bounds.size.height;
5062 gap_left = packing_changed = FALSE;
5063
5064 if (notebook->tab_pos == GTK_POS_TOP || notebook->tab_pos == GTK_POS_BOTTOM)
5065 tab_expand_orientation = GTK_ORIENTATION_HORIZONTAL;
5066 else
5067 tab_expand_orientation = GTK_ORIENTATION_VERTICAL;
5068
5069 while (*children && *children != last_child)
5070 {
5071 page = (*children)->data;
5072
5073 if (direction == STEP_NEXT)
5074 *children = gtk_notebook_search_page (notebook, list: *children, direction, TRUE);
5075 else
5076 {
5077 *children = (*children)->next;
5078 continue;
5079 }
5080
5081 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5082 continue;
5083
5084 tab_extra_space = 0;
5085 if (*expanded_tabs && (showarrow || page->expand || gtk_widget_compute_expand (widget: page->tab_label, orientation: tab_expand_orientation)))
5086 {
5087 tab_extra_space = *remaining_space / *expanded_tabs;
5088 *remaining_space -= tab_extra_space;
5089 (*expanded_tabs)--;
5090 }
5091
5092 switch (tab_pos)
5093 {
5094 case GTK_POS_TOP:
5095 case GTK_POS_BOTTOM:
5096 child_allocation.width = MAX (1, page->requisition.width + tab_extra_space);
5097
5098 /* make sure that the reordered tab doesn't go past the last position */
5099 if (notebook->operation == DRAG_OPERATION_REORDER &&
5100 !gap_left && packing_changed)
5101 {
5102 if (!allocate_at_bottom)
5103 {
5104 if (left_x >= anchor)
5105 {
5106 left_x = notebook->drag_surface_x = anchor;
5107 anchor += drag_bounds.size.width;
5108 }
5109 }
5110 else
5111 {
5112 if (right_x <= anchor)
5113 {
5114 anchor -= drag_bounds.size.width;
5115 left_x = notebook->drag_surface_x = anchor;
5116 }
5117 }
5118
5119 gap_left = TRUE;
5120 }
5121
5122 if (notebook->operation == DRAG_OPERATION_REORDER && page == notebook->cur_page)
5123 {
5124 notebook->drag_surface_x = left_x;
5125 notebook->drag_surface_y = child_allocation.y;
5126 }
5127 else
5128 {
5129 if (allocate_at_bottom)
5130 anchor -= child_allocation.width;
5131
5132 if (notebook->operation == DRAG_OPERATION_REORDER)
5133 {
5134 if (!allocate_at_bottom &&
5135 left_x >= anchor &&
5136 left_x <= anchor + child_allocation.width / 2)
5137 anchor += drag_bounds.size.width;
5138 else if (allocate_at_bottom &&
5139 right_x >= anchor + child_allocation.width / 2 &&
5140 right_x <= anchor + child_allocation.width)
5141 anchor -= drag_bounds.size.width;
5142 }
5143
5144 child_allocation.x = anchor;
5145 }
5146
5147 break;
5148 case GTK_POS_LEFT:
5149 case GTK_POS_RIGHT:
5150 child_allocation.height = MAX (1, page->requisition.height + tab_extra_space);
5151
5152 /* make sure that the reordered tab doesn't go past the last position */
5153 if (notebook->operation == DRAG_OPERATION_REORDER &&
5154 !gap_left && packing_changed)
5155 {
5156 if (!allocate_at_bottom && top_y >= anchor)
5157 {
5158 top_y = notebook->drag_surface_y = anchor;
5159 anchor += drag_bounds.size.height;
5160 }
5161
5162 gap_left = TRUE;
5163 }
5164
5165 if (notebook->operation == DRAG_OPERATION_REORDER && page == notebook->cur_page)
5166 {
5167 notebook->drag_surface_x = child_allocation.x;
5168 notebook->drag_surface_y = top_y;
5169 }
5170 else
5171 {
5172 if (allocate_at_bottom)
5173 anchor -= child_allocation.height;
5174
5175 if (notebook->operation == DRAG_OPERATION_REORDER)
5176 {
5177 if (!allocate_at_bottom &&
5178 top_y >= anchor &&
5179 top_y <= anchor + child_allocation.height / 2)
5180 anchor += drag_bounds.size.height;
5181 else if (allocate_at_bottom &&
5182 bottom_y >= anchor + child_allocation.height / 2 &&
5183 bottom_y <= anchor + child_allocation.height)
5184 anchor -= drag_bounds.size.height;
5185 }
5186
5187 child_allocation.y = anchor;
5188 }
5189 break;
5190
5191 default:
5192 g_assert_not_reached ();
5193 break;
5194 }
5195
5196 /* set child visible */
5197 if (page->tab_label)
5198 gtk_widget_set_child_visible (widget: page->tab_widget, TRUE);
5199
5200 if (page == notebook->cur_page && notebook->operation == DRAG_OPERATION_REORDER)
5201 {
5202 GtkAllocation fixed_allocation = { notebook->drag_surface_x, notebook->drag_surface_y,
5203 child_allocation.width, child_allocation.height };
5204 gtk_widget_size_allocate (widget: page->tab_widget, allocation: &fixed_allocation, baseline: -1);
5205 }
5206 else if (page == notebook->detached_tab && notebook->operation == DRAG_OPERATION_DETACH)
5207 {
5208 /* needs to be allocated at 0,0
5209 * to be shown in the drag window */
5210 GtkAllocation fixed_allocation = { 0, 0, child_allocation.width, child_allocation.height };
5211 gtk_widget_size_allocate (widget: page->tab_widget, allocation: &fixed_allocation, baseline: -1);
5212 }
5213 else if (gtk_notebook_page_tab_label_is_visible (page))
5214 {
5215 gtk_widget_size_allocate (widget: page->tab_widget, allocation: &child_allocation, baseline: -1);
5216 }
5217
5218 /* calculate whether to leave a gap based on reorder operation or not */
5219 switch (tab_pos)
5220 {
5221 case GTK_POS_TOP:
5222 case GTK_POS_BOTTOM:
5223 if (notebook->operation != DRAG_OPERATION_REORDER || page != notebook->cur_page)
5224 {
5225 if (notebook->operation == DRAG_OPERATION_REORDER)
5226 {
5227 if (!allocate_at_bottom &&
5228 left_x > anchor + child_allocation.width / 2 &&
5229 left_x <= anchor + child_allocation.width)
5230 anchor += drag_bounds.size.width;
5231 else if (allocate_at_bottom &&
5232 right_x >= anchor &&
5233 right_x <= anchor + child_allocation.width / 2)
5234 anchor -= drag_bounds.size.width;
5235 }
5236
5237 if (!allocate_at_bottom)
5238 anchor += child_allocation.width;
5239 }
5240
5241 break;
5242 case GTK_POS_LEFT:
5243 case GTK_POS_RIGHT:
5244 if (notebook->operation != DRAG_OPERATION_REORDER || page != notebook->cur_page)
5245 {
5246 if (notebook->operation == DRAG_OPERATION_REORDER)
5247 {
5248 if (!allocate_at_bottom &&
5249 top_y >= anchor + child_allocation.height / 2 &&
5250 top_y <= anchor + child_allocation.height)
5251 anchor += drag_bounds.size.height;
5252 else if (allocate_at_bottom &&
5253 bottom_y >= anchor &&
5254 bottom_y <= anchor + child_allocation.height / 2)
5255 anchor -= drag_bounds.size.height;
5256 }
5257
5258 if (!allocate_at_bottom)
5259 anchor += child_allocation.height;
5260 }
5261
5262 break;
5263 default:
5264 g_assert_not_reached ();
5265 break;
5266 }
5267 }
5268
5269 /* Don't move the current tab past the last position during tabs reordering */
5270 if (notebook->operation == DRAG_OPERATION_REORDER &&
5271 direction == STEP_NEXT)
5272 {
5273 switch (tab_pos)
5274 {
5275 case GTK_POS_TOP:
5276 case GTK_POS_BOTTOM:
5277 if (allocate_at_bottom)
5278 anchor -= drag_bounds.size.width;
5279
5280 if ((!allocate_at_bottom && notebook->drag_surface_x > anchor) ||
5281 (allocate_at_bottom && notebook->drag_surface_x < anchor))
5282 notebook->drag_surface_x = anchor;
5283 break;
5284 case GTK_POS_LEFT:
5285 case GTK_POS_RIGHT:
5286 if (allocate_at_bottom)
5287 anchor -= drag_bounds.size.height;
5288
5289 if ((!allocate_at_bottom && notebook->drag_surface_y > anchor) ||
5290 (allocate_at_bottom && notebook->drag_surface_y < anchor))
5291 notebook->drag_surface_y = anchor;
5292 break;
5293 default:
5294 g_assert_not_reached ();
5295 break;
5296 }
5297 }
5298}
5299
5300static void
5301gtk_notebook_pages_allocate (GtkNotebook *notebook,
5302 int width,
5303 int height)
5304{
5305 GList *children = NULL;
5306 GList *last_child = NULL;
5307 gboolean showarrow = FALSE;
5308 GtkAllocation tabs_allocation;
5309 int tab_space, remaining_space;
5310 int expanded_tabs;
5311
5312 if (!notebook->show_tabs || !gtk_notebook_has_current_page (notebook))
5313 return;
5314
5315 tab_space = remaining_space = 0;
5316 expanded_tabs = 1;
5317
5318 gtk_notebook_tab_space (notebook, notebook_width: width, notebook_height: height,
5319 show_arrows: &showarrow, tabs_allocation: &tabs_allocation, tab_space: &tab_space);
5320
5321 gtk_notebook_calculate_shown_tabs (notebook, show_arrows: showarrow,
5322 tabs_allocation: &tabs_allocation, tab_space, last_child: &last_child,
5323 n: &expanded_tabs, remaining_space: &remaining_space);
5324
5325 children = notebook->first_tab;
5326 gtk_notebook_calculate_tabs_allocation (notebook, children: &children, last_child,
5327 showarrow, direction: STEP_NEXT,
5328 remaining_space: &remaining_space, expanded_tabs: &expanded_tabs, allocation: &tabs_allocation);
5329 if (children && children != last_child)
5330 {
5331 children = notebook->children;
5332 gtk_notebook_calculate_tabs_allocation (notebook, children: &children, last_child,
5333 showarrow, direction: STEP_PREV,
5334 remaining_space: &remaining_space, expanded_tabs: &expanded_tabs, allocation: &tabs_allocation);
5335 }
5336
5337 if (!notebook->first_tab)
5338 notebook->first_tab = notebook->children;
5339}
5340
5341static void
5342gtk_notebook_calc_tabs (GtkNotebook *notebook,
5343 GList *start,
5344 GList **end,
5345 int *tab_space,
5346 guint direction)
5347{
5348 GtkNotebookPage *page = NULL;
5349 GList *children;
5350 GList *last_calculated_child = NULL;
5351 GtkPositionType tab_pos = get_effective_tab_pos (notebook);
5352
5353 if (!start)
5354 return;
5355
5356 children = start;
5357
5358 switch (tab_pos)
5359 {
5360 case GTK_POS_TOP:
5361 case GTK_POS_BOTTOM:
5362 while (children)
5363 {
5364 page = children->data;
5365 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5366 gtk_widget_get_visible (widget: page->child))
5367 {
5368 *tab_space -= page->requisition.width;
5369 if (*tab_space < 0 || children == *end)
5370 {
5371 if (*tab_space < 0)
5372 {
5373 *tab_space = - (*tab_space +
5374 page->requisition.width);
5375
5376 if (*tab_space == 0 && direction == STEP_PREV)
5377 children = last_calculated_child;
5378
5379 *end = children;
5380 }
5381 return;
5382 }
5383
5384 last_calculated_child = children;
5385 }
5386 if (direction == STEP_NEXT)
5387 children = children->next;
5388 else
5389 children = children->prev;
5390 }
5391 break;
5392 case GTK_POS_LEFT:
5393 case GTK_POS_RIGHT:
5394 while (children)
5395 {
5396 page = children->data;
5397 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5398 gtk_widget_get_visible (widget: page->child))
5399 {
5400 *tab_space -= page->requisition.height;
5401 if (*tab_space < 0 || children == *end)
5402 {
5403 if (*tab_space < 0)
5404 {
5405 *tab_space = - (*tab_space + page->requisition.height);
5406
5407 if (*tab_space == 0 && direction == STEP_PREV)
5408 children = last_calculated_child;
5409
5410 *end = children;
5411 }
5412 return;
5413 }
5414
5415 last_calculated_child = children;
5416 }
5417 if (direction == STEP_NEXT)
5418 children = children->next;
5419 else
5420 children = children->prev;
5421 }
5422 break;
5423 default:
5424 g_assert_not_reached ();
5425 break;
5426 }
5427}
5428
5429/* Private GtkNotebook Page Switch Methods:
5430 *
5431 * gtk_notebook_real_switch_page
5432 */
5433static void
5434gtk_notebook_real_switch_page (GtkNotebook *notebook,
5435 GtkWidget* child,
5436 guint page_num)
5437{
5438 GList *list = gtk_notebook_find_child (notebook, GTK_WIDGET (child));
5439 GtkNotebookPage *page = GTK_NOTEBOOK_PAGE_FROM_LIST (list);
5440 gboolean child_has_focus;
5441
5442 if (notebook->cur_page == page || !gtk_widget_get_visible (GTK_WIDGET (child)))
5443 return;
5444
5445 /* save the value here, changing visibility changes focus */
5446 child_has_focus = notebook->child_has_focus;
5447
5448 if (notebook->cur_page)
5449 {
5450 GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (notebook));
5451 GtkWidget *focus = gtk_root_get_focus (self: root);
5452 if (focus)
5453 child_has_focus = gtk_widget_is_ancestor (widget: focus, ancestor: notebook->cur_page->child);
5454 gtk_widget_unset_state_flags (widget: notebook->cur_page->tab_widget, flags: GTK_STATE_FLAG_CHECKED);
5455
5456 gtk_accessible_update_state (self: GTK_ACCESSIBLE (ptr: notebook->cur_page->tab_widget),
5457 first_state: GTK_ACCESSIBLE_STATE_SELECTED, FALSE,
5458 -1);
5459 }
5460
5461 notebook->cur_page = page;
5462 gtk_widget_set_state_flags (widget: page->tab_widget, flags: GTK_STATE_FLAG_CHECKED, FALSE);
5463 gtk_widget_set_visible (widget: notebook->header_widget, visible: notebook->show_tabs);
5464
5465 if (gtk_widget_get_realized (GTK_WIDGET (notebook)))
5466 gtk_widget_realize_at_context (widget: notebook->cur_page->tab_widget);
5467
5468 gtk_accessible_update_state (self: GTK_ACCESSIBLE (ptr: notebook->cur_page->tab_widget),
5469 first_state: GTK_ACCESSIBLE_STATE_SELECTED, TRUE,
5470 -1);
5471
5472 if (!notebook->focus_tab ||
5473 notebook->focus_tab->data != (gpointer) notebook->cur_page)
5474 notebook->focus_tab =
5475 g_list_find (list: notebook->children, data: notebook->cur_page);
5476
5477 gtk_stack_set_visible_child (GTK_STACK (notebook->stack_widget), child: notebook->cur_page->child);
5478 gtk_widget_set_child_visible (widget: notebook->cur_page->tab_widget, TRUE);
5479
5480 /* If the focus was on the previous page, move it to the first
5481 * element on the new page, if possible, or if not, to the
5482 * notebook itself.
5483 */
5484 if (child_has_focus)
5485 {
5486 if (notebook->cur_page->last_focus_child &&
5487 gtk_widget_is_ancestor (widget: notebook->cur_page->last_focus_child, ancestor: notebook->cur_page->child))
5488 gtk_widget_grab_focus (widget: notebook->cur_page->last_focus_child);
5489 else
5490 if (!gtk_widget_child_focus (widget: notebook->cur_page->child, direction: GTK_DIR_TAB_FORWARD))
5491 gtk_widget_grab_focus (GTK_WIDGET (notebook));
5492 }
5493
5494 update_arrow_state (notebook);
5495
5496 gtk_widget_queue_resize (GTK_WIDGET (notebook));
5497 gtk_widget_queue_resize (widget: notebook->tabs_widget);
5498 g_object_notify_by_pspec (G_OBJECT (notebook), pspec: properties[PROP_PAGE]);
5499}
5500
5501/* Private GtkNotebook Page Switch Functions:
5502 *
5503 * gtk_notebook_switch_page
5504 * gtk_notebook_page_select
5505 * gtk_notebook_switch_focus_tab
5506 * gtk_notebook_menu_switch_page
5507 */
5508static void
5509gtk_notebook_switch_page (GtkNotebook *notebook,
5510 GtkNotebookPage *page)
5511{
5512 guint page_num;
5513
5514 if (notebook->cur_page == page)
5515 return;
5516
5517 page_num = g_list_index (list: notebook->children, data: page);
5518
5519 g_signal_emit (instance: notebook,
5520 signal_id: notebook_signals[SWITCH_PAGE],
5521 detail: 0,
5522 page->child,
5523 page_num);
5524}
5525
5526static int
5527gtk_notebook_page_select (GtkNotebook *notebook,
5528 gboolean move_focus)
5529{
5530 GtkNotebookPage *page;
5531 GtkDirectionType dir;
5532 GtkPositionType tab_pos = get_effective_tab_pos (notebook);
5533
5534 if (!notebook->focus_tab)
5535 return FALSE;
5536
5537 page = notebook->focus_tab->data;
5538 gtk_notebook_switch_page (notebook, page);
5539
5540 if (move_focus)
5541 {
5542 switch (tab_pos)
5543 {
5544 case GTK_POS_TOP:
5545 dir = GTK_DIR_DOWN;
5546 break;
5547 case GTK_POS_BOTTOM:
5548 dir = GTK_DIR_UP;
5549 break;
5550 case GTK_POS_LEFT:
5551 dir = GTK_DIR_RIGHT;
5552 break;
5553 case GTK_POS_RIGHT:
5554 dir = GTK_DIR_LEFT;
5555 break;
5556 default:
5557 g_assert_not_reached ();
5558 dir = GTK_DIR_DOWN;
5559 break;
5560 }
5561
5562 if (gtk_widget_child_focus (widget: page->child, direction: dir))
5563 return TRUE;
5564 }
5565 return FALSE;
5566}
5567
5568static void
5569gtk_notebook_switch_focus_tab (GtkNotebook *notebook,
5570 GList *new_child)
5571{
5572 GtkNotebookPage *page;
5573
5574 if (notebook->focus_tab == new_child)
5575 return;
5576
5577 notebook->focus_tab = new_child;
5578
5579 if (!notebook->show_tabs || !notebook->focus_tab)
5580 return;
5581
5582 page = notebook->focus_tab->data;
5583 gtk_notebook_switch_page (notebook, page);
5584}
5585
5586static void
5587gtk_notebook_menu_switch_page (GtkWidget *widget,
5588 GtkNotebookPage *page)
5589{
5590 GtkNotebook *notebook;
5591 GList *children;
5592 guint page_num;
5593
5594 notebook = GTK_NOTEBOOK (gtk_widget_get_ancestor (widget, GTK_TYPE_NOTEBOOK));
5595
5596 gtk_popover_popdown (GTK_POPOVER (notebook->menu));
5597
5598 if (notebook->cur_page == page)
5599 return;
5600
5601 page_num = 0;
5602 children = notebook->children;
5603 while (children && children->data != page)
5604 {
5605 children = children->next;
5606 page_num++;
5607 }
5608
5609 g_signal_emit (instance: notebook,
5610 signal_id: notebook_signals[SWITCH_PAGE],
5611 detail: 0,
5612 page->child,
5613 page_num);
5614}
5615
5616/* Private GtkNotebook Menu Functions:
5617 *
5618 * gtk_notebook_menu_item_create
5619 * gtk_notebook_menu_item_recreate
5620 * gtk_notebook_menu_label_unparent
5621 */
5622static void
5623gtk_notebook_menu_item_create (GtkNotebook *notebook,
5624 GtkNotebookPage *page)
5625{
5626 GtkWidget *menu_item;
5627
5628 if (page->default_menu)
5629 {
5630 if (GTK_IS_LABEL (page->tab_label))
5631 page->menu_label = gtk_label_new (str: gtk_label_get_text (GTK_LABEL (page->tab_label)));
5632 else
5633 page->menu_label = gtk_label_new (str: "");
5634 g_object_ref_sink (page->menu_label);
5635 gtk_widget_set_halign (widget: page->menu_label, align: GTK_ALIGN_START);
5636 gtk_widget_set_valign (widget: page->menu_label, align: GTK_ALIGN_CENTER);
5637 }
5638
5639 menu_item = gtk_button_new ();
5640 gtk_button_set_has_frame (GTK_BUTTON (menu_item), FALSE);
5641 gtk_button_set_child (GTK_BUTTON (menu_item), child: page->menu_label);
5642 gtk_box_append (GTK_BOX (notebook->menu_box), child: menu_item);
5643 g_signal_connect (menu_item, "clicked",
5644 G_CALLBACK (gtk_notebook_menu_switch_page), page);
5645 if (!gtk_widget_get_visible (widget: page->child))
5646 gtk_widget_hide (widget: menu_item);
5647}
5648
5649static void
5650gtk_notebook_menu_item_recreate (GtkNotebook *notebook,
5651 GList *list)
5652{
5653 GtkNotebookPage *page = list->data;
5654 GtkWidget *menu_item = gtk_widget_get_parent (widget: page->menu_label);
5655
5656 gtk_button_set_child (GTK_BUTTON (menu_item), NULL);
5657 gtk_box_remove (GTK_BOX (notebook->menu_box), child: menu_item);
5658 gtk_notebook_menu_item_create (notebook, page);
5659}
5660
5661static void
5662gtk_notebook_menu_label_unparent (GtkWidget *widget)
5663{
5664 gtk_button_set_child (GTK_BUTTON (widget), NULL);
5665}
5666
5667/* Public GtkNotebook Page Insert/Remove Methods :
5668 *
5669 * gtk_notebook_append_page
5670 * gtk_notebook_append_page_menu
5671 * gtk_notebook_prepend_page
5672 * gtk_notebook_prepend_page_menu
5673 * gtk_notebook_insert_page
5674 * gtk_notebook_insert_page_menu
5675 * gtk_notebook_remove_page
5676 */
5677/**
5678 * gtk_notebook_append_page:
5679 * @notebook: a `GtkNotebook`
5680 * @child: the `GtkWidget` to use as the contents of the page
5681 * @tab_label: (nullable): the `GtkWidget` to be used as the label
5682 * for the page, or %NULL to use the default label, “page N”
5683 *
5684 * Appends a page to @notebook.
5685 *
5686 * Returns: the index (starting from 0) of the appended
5687 * page in the notebook, or -1 if function fails
5688 */
5689int
5690gtk_notebook_append_page (GtkNotebook *notebook,
5691 GtkWidget *child,
5692 GtkWidget *tab_label)
5693{
5694 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
5695 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
5696 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
5697
5698 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, position: -1);
5699}
5700
5701/**
5702 * gtk_notebook_append_page_menu:
5703 * @notebook: a `GtkNotebook`
5704 * @child: the `GtkWidget` to use as the contents of the page
5705 * @tab_label: (nullable): the `GtkWidget` to be used as the label
5706 * for the page, or %NULL to use the default label, “page N”
5707 * @menu_label: (nullable): the widget to use as a label for the
5708 * page-switch menu, if that is enabled. If %NULL, and @tab_label
5709 * is a `GtkLabel` or %NULL, then the menu label will be a newly
5710 * created label with the same text as @tab_label; if @tab_label
5711 * is not a `GtkLabel`, @menu_label must be specified if the
5712 * page-switch menu is to be used.
5713 *
5714 * Appends a page to @notebook, specifying the widget to use as the
5715 * label in the popup menu.
5716 *
5717 * Returns: the index (starting from 0) of the appended
5718 * page in the notebook, or -1 if function fails
5719 */
5720int
5721gtk_notebook_append_page_menu (GtkNotebook *notebook,
5722 GtkWidget *child,
5723 GtkWidget *tab_label,
5724 GtkWidget *menu_label)
5725{
5726 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
5727 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
5728 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
5729 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
5730
5731 return gtk_notebook_insert_page_menu (notebook, child, tab_label, menu_label, position: -1);
5732}
5733
5734/**
5735 * gtk_notebook_prepend_page:
5736 * @notebook: a `GtkNotebook`
5737 * @child: the `GtkWidget` to use as the contents of the page
5738 * @tab_label: (nullable): the `GtkWidget` to be used as the label
5739 * for the page, or %NULL to use the default label, “page N”
5740 *
5741 * Prepends a page to @notebook.
5742 *
5743 * Returns: the index (starting from 0) of the prepended
5744 * page in the notebook, or -1 if function fails
5745 */
5746int
5747gtk_notebook_prepend_page (GtkNotebook *notebook,
5748 GtkWidget *child,
5749 GtkWidget *tab_label)
5750{
5751 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
5752 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
5753 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
5754
5755 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, position: 0);
5756}
5757
5758/**
5759 * gtk_notebook_prepend_page_menu:
5760 * @notebook: a `GtkNotebook`
5761 * @child: the `GtkWidget` to use as the contents of the page
5762 * @tab_label: (nullable): the `GtkWidget` to be used as the label
5763 * for the page, or %NULL to use the default label, “page N”
5764 * @menu_label: (nullable): the widget to use as a label for the
5765 * page-switch menu, if that is enabled. If %NULL, and @tab_label
5766 * is a `GtkLabel` or %NULL, then the menu label will be a newly
5767 * created label with the same text as @tab_label; if @tab_label
5768 * is not a `GtkLabel`, @menu_label must be specified if the
5769 * page-switch menu is to be used.
5770 *
5771 * Prepends a page to @notebook, specifying the widget to use as the
5772 * label in the popup menu.
5773 *
5774 * Returns: the index (starting from 0) of the prepended
5775 * page in the notebook, or -1 if function fails
5776 */
5777int
5778gtk_notebook_prepend_page_menu (GtkNotebook *notebook,
5779 GtkWidget *child,
5780 GtkWidget *tab_label,
5781 GtkWidget *menu_label)
5782{
5783 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
5784 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
5785 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
5786 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
5787
5788 return gtk_notebook_insert_page_menu (notebook, child, tab_label, menu_label, position: 0);
5789}
5790
5791/**
5792 * gtk_notebook_insert_page:
5793 * @notebook: a `GtkNotebook`
5794 * @child: the `GtkWidget` to use as the contents of the page
5795 * @tab_label: (nullable): the `GtkWidget` to be used as the label
5796 * for the page, or %NULL to use the default label, “page N”
5797 * @position: the index (starting at 0) at which to insert the page,
5798 * or -1 to append the page after all other pages
5799 *
5800 * Insert a page into @notebook at the given position.
5801 *
5802 * Returns: the index (starting from 0) of the inserted
5803 * page in the notebook, or -1 if function fails
5804 */
5805int
5806gtk_notebook_insert_page (GtkNotebook *notebook,
5807 GtkWidget *child,
5808 GtkWidget *tab_label,
5809 int position)
5810{
5811 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
5812 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
5813 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
5814
5815 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, position);
5816}
5817
5818
5819static int
5820gtk_notebook_page_compare_tab (gconstpointer a,
5821 gconstpointer b)
5822{
5823 return (((GtkNotebookPage *) a)->tab_label != b);
5824}
5825
5826static gboolean
5827gtk_notebook_mnemonic_activate_switch_page (GtkWidget *child,
5828 gboolean overload,
5829 gpointer data)
5830{
5831 GtkNotebook *notebook = GTK_NOTEBOOK (data);
5832 GList *list;
5833
5834 list = g_list_find_custom (list: notebook->children, data: child,
5835 func: gtk_notebook_page_compare_tab);
5836 if (list)
5837 {
5838 GtkNotebookPage *page = list->data;
5839
5840 gtk_widget_grab_focus (GTK_WIDGET (notebook)); /* Do this first to avoid focusing new page */
5841 gtk_notebook_switch_page (notebook, page);
5842 focus_tabs_in (notebook);
5843 }
5844
5845 return TRUE;
5846}
5847
5848/**
5849 * gtk_notebook_insert_page_menu:
5850 * @notebook: a `GtkNotebook`
5851 * @child: the `GtkWidget` to use as the contents of the page
5852 * @tab_label: (nullable): the `GtkWidget` to be used as the label
5853 * for the page, or %NULL to use the default label, “page N”
5854 * @menu_label: (nullable): the widget to use as a label for the
5855 * page-switch menu, if that is enabled. If %NULL, and @tab_label
5856 * is a `GtkLabel` or %NULL, then the menu label will be a newly
5857 * created label with the same text as @tab_label; if @tab_label
5858 * is not a `GtkLabel`, @menu_label must be specified if the
5859 * page-switch menu is to be used.
5860 * @position: the index (starting at 0) at which to insert the page,
5861 * or -1 to append the page after all other pages.
5862 *
5863 * Insert a page into @notebook at the given position, specifying
5864 * the widget to use as the label in the popup menu.
5865 *
5866 * Returns: the index (starting from 0) of the inserted
5867 * page in the notebook
5868 */
5869int
5870gtk_notebook_insert_page_menu (GtkNotebook *notebook,
5871 GtkWidget *child,
5872 GtkWidget *tab_label,
5873 GtkWidget *menu_label,
5874 int position)
5875{
5876 GtkNotebookClass *class;
5877
5878 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
5879 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
5880 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
5881 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
5882
5883 class = GTK_NOTEBOOK_GET_CLASS (notebook);
5884
5885 return (class->insert_page) (notebook, child, tab_label, menu_label, position);
5886}
5887
5888/**
5889 * gtk_notebook_remove_page:
5890 * @notebook: a `GtkNotebook`
5891 * @page_num: the index of a notebook page, starting
5892 * from 0. If -1, the last page will be removed.
5893 *
5894 * Removes a page from the notebook given its index
5895 * in the notebook.
5896 */
5897void
5898gtk_notebook_remove_page (GtkNotebook *notebook,
5899 int page_num)
5900{
5901 GList *list = NULL;
5902
5903 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
5904
5905 if (page_num >= 0)
5906 list = g_list_nth (list: notebook->children, n: page_num);
5907 else
5908 list = g_list_last (list: notebook->children);
5909
5910 if (list)
5911 gtk_notebook_remove (notebook, widget: ((GtkNotebookPage *) list->data)->child);
5912}
5913
5914/* Public GtkNotebook Page Switch Methods :
5915 * gtk_notebook_get_current_page
5916 * gtk_notebook_page_num
5917 * gtk_notebook_set_current_page
5918 * gtk_notebook_next_page
5919 * gtk_notebook_prev_page
5920 */
5921
5922/**
5923 * gtk_notebook_get_current_page: (attributes org.gtk.Method.get_property=page)
5924 * @notebook: a `GtkNotebook`
5925 *
5926 * Returns the page number of the current page.
5927 *
5928 * Returns: the index (starting from 0) of the current
5929 * page in the notebook. If the notebook has no pages,
5930 * then -1 will be returned.
5931 */
5932int
5933gtk_notebook_get_current_page (GtkNotebook *notebook)
5934{
5935 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
5936
5937 if (!notebook->cur_page)
5938 return -1;
5939
5940 return g_list_index (list: notebook->children, data: notebook->cur_page);
5941}
5942
5943/**
5944 * gtk_notebook_get_nth_page:
5945 * @notebook: a `GtkNotebook`
5946 * @page_num: the index of a page in the notebook, or -1
5947 * to get the last page
5948 *
5949 * Returns the child widget contained in page number @page_num.
5950 *
5951 * Returns: (nullable) (transfer none): the child widget, or %NULL if @page_num
5952 * is out of bounds
5953 */
5954GtkWidget*
5955gtk_notebook_get_nth_page (GtkNotebook *notebook,
5956 int page_num)
5957{
5958 GtkNotebookPage *page;
5959 GList *list;
5960
5961 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
5962
5963 if (page_num >= 0)
5964 list = g_list_nth (list: notebook->children, n: page_num);
5965 else
5966 list = g_list_last (list: notebook->children);
5967
5968 if (list)
5969 {
5970 page = list->data;
5971 return page->child;
5972 }
5973
5974 return NULL;
5975}
5976
5977/**
5978 * gtk_notebook_get_n_pages:
5979 * @notebook: a `GtkNotebook`
5980 *
5981 * Gets the number of pages in a notebook.
5982 *
5983 * Returns: the number of pages in the notebook
5984 */
5985int
5986gtk_notebook_get_n_pages (GtkNotebook *notebook)
5987{
5988 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), 0);
5989
5990 return g_list_length (list: notebook->children);
5991}
5992
5993/**
5994 * gtk_notebook_page_num:
5995 * @notebook: a `GtkNotebook`
5996 * @child: a `GtkWidget`
5997 *
5998 * Finds the index of the page which contains the given child
5999 * widget.
6000 *
6001 * Returns: the index of the page containing @child, or
6002 * -1 if @child is not in the notebook
6003 */
6004int
6005gtk_notebook_page_num (GtkNotebook *notebook,
6006 GtkWidget *child)
6007{
6008 GList *children;
6009 int num;
6010
6011 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6012
6013 num = 0;
6014 children = notebook->children;
6015 while (children)
6016 {
6017 GtkNotebookPage *page = children->data;
6018
6019 if (page->child == child)
6020 return num;
6021
6022 children = children->next;
6023 num++;
6024 }
6025
6026 return -1;
6027}
6028
6029/**
6030 * gtk_notebook_set_current_page: (attributes org.gtk.Method.set_property=page)
6031 * @notebook: a `GtkNotebook`
6032 * @page_num: index of the page to switch to, starting from 0.
6033 * If negative, the last page will be used. If greater
6034 * than the number of pages in the notebook, nothing
6035 * will be done.
6036 *
6037 * Switches to the page number @page_num.
6038 *
6039 * Note that due to historical reasons, GtkNotebook refuses
6040 * to switch to a page unless the child widget is visible.
6041 * Therefore, it is recommended to show child widgets before
6042 * adding them to a notebook.
6043 */
6044void
6045gtk_notebook_set_current_page (GtkNotebook *notebook,
6046 int page_num)
6047{
6048 GList *list;
6049
6050 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6051
6052 if (page_num < 0)
6053 page_num = g_list_length (list: notebook->children) - 1;
6054
6055 list = g_list_nth (list: notebook->children, n: page_num);
6056 if (list)
6057 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE_FROM_LIST (list));
6058}
6059
6060/**
6061 * gtk_notebook_next_page:
6062 * @notebook: a `GtkNotebook`
6063 *
6064 * Switches to the next page.
6065 *
6066 * Nothing happens if the current page is the last page.
6067 */
6068void
6069gtk_notebook_next_page (GtkNotebook *notebook)
6070{
6071 GList *list;
6072
6073 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6074
6075 list = g_list_find (list: notebook->children, data: notebook->cur_page);
6076 if (!list)
6077 return;
6078
6079 list = gtk_notebook_search_page (notebook, list, direction: STEP_NEXT, TRUE);
6080 if (!list)
6081 return;
6082
6083 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE_FROM_LIST (list));
6084}
6085
6086/**
6087 * gtk_notebook_prev_page:
6088 * @notebook: a `GtkNotebook`
6089 *
6090 * Switches to the previous page.
6091 *
6092 * Nothing happens if the current page is the first page.
6093 */
6094void
6095gtk_notebook_prev_page (GtkNotebook *notebook)
6096{
6097 GList *list;
6098
6099 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6100
6101 list = g_list_find (list: notebook->children, data: notebook->cur_page);
6102 if (!list)
6103 return;
6104
6105 list = gtk_notebook_search_page (notebook, list, direction: STEP_PREV, TRUE);
6106 if (!list)
6107 return;
6108
6109 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE_FROM_LIST (list));
6110}
6111
6112/* Public GtkNotebook/Tab Style Functions
6113 *
6114 * gtk_notebook_set_show_border
6115 * gtk_notebook_get_show_border
6116 * gtk_notebook_set_show_tabs
6117 * gtk_notebook_get_show_tabs
6118 * gtk_notebook_set_tab_pos
6119 * gtk_notebook_get_tab_pos
6120 * gtk_notebook_set_scrollable
6121 * gtk_notebook_get_scrollable
6122 */
6123/**
6124 * gtk_notebook_set_show_border: (attributes org.gtk.Method.set_property=show-border)
6125 * @notebook: a `GtkNotebook`
6126 * @show_border: %TRUE if a bevel should be drawn around the notebook
6127 *
6128 * Sets whether a bevel will be drawn around the notebook pages.
6129 *
6130 * This only has a visual effect when the tabs are not shown.
6131 */
6132void
6133gtk_notebook_set_show_border (GtkNotebook *notebook,
6134 gboolean show_border)
6135{
6136 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6137
6138 if (notebook->show_border != show_border)
6139 {
6140 notebook->show_border = show_border;
6141
6142 if (show_border)
6143 gtk_widget_add_css_class (GTK_WIDGET (notebook), css_class: "frame");
6144 else
6145 gtk_widget_remove_css_class (GTK_WIDGET (notebook), css_class: "frame");
6146
6147 g_object_notify_by_pspec (G_OBJECT (notebook), pspec: properties[PROP_SHOW_BORDER]);
6148 }
6149}
6150
6151/**
6152 * gtk_notebook_get_show_border: (attributes org.gtk.Method.get_property=show-border)
6153 * @notebook: a `GtkNotebook`
6154 *
6155 * Returns whether a bevel will be drawn around the notebook pages.
6156 *
6157 * Returns: %TRUE if the bevel is drawn
6158 */
6159gboolean
6160gtk_notebook_get_show_border (GtkNotebook *notebook)
6161{
6162 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
6163
6164 return notebook->show_border;
6165}
6166
6167/**
6168 * gtk_notebook_set_show_tabs: (attributes org.gtk.Method.set_property=show-tabs)
6169 * @notebook: a `GtkNotebook`
6170 * @show_tabs: %TRUE if the tabs should be shown
6171 *
6172 * Sets whether to show the tabs for the notebook or not.
6173 */
6174void
6175gtk_notebook_set_show_tabs (GtkNotebook *notebook,
6176 gboolean show_tabs)
6177{
6178 GtkNotebookPage *page;
6179 GList *children;
6180 int i;
6181
6182 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6183
6184 show_tabs = show_tabs != FALSE;
6185
6186 if (notebook->show_tabs == show_tabs)
6187 return;
6188
6189 notebook->show_tabs = show_tabs;
6190 children = notebook->children;
6191
6192 if (!show_tabs)
6193 {
6194 while (children)
6195 {
6196 page = children->data;
6197 children = children->next;
6198 if (page->default_tab)
6199 {
6200 gtk_widget_unparent (widget: page->tab_label);
6201 page->tab_label = NULL;
6202 }
6203 else
6204 gtk_widget_hide (widget: page->tab_label);
6205 }
6206
6207 gtk_widget_hide (widget: notebook->header_widget);
6208 }
6209 else
6210 {
6211 gtk_notebook_update_labels (notebook);
6212 gtk_widget_show (widget: notebook->header_widget);
6213 }
6214
6215 for (i = 0; i < N_ACTION_WIDGETS; i++)
6216 {
6217 if (notebook->action_widget[i])
6218 gtk_widget_set_child_visible (widget: notebook->action_widget[i], child_visible: show_tabs);
6219 }
6220
6221 gtk_notebook_update_tab_pos (notebook);
6222 gtk_widget_queue_resize (GTK_WIDGET (notebook));
6223
6224 g_object_notify_by_pspec (G_OBJECT (notebook), pspec: properties[PROP_SHOW_TABS]);
6225}
6226
6227/**
6228 * gtk_notebook_get_show_tabs: (attributes org.gtk.Method.get_property=show-tabs)
6229 * @notebook: a `GtkNotebook`
6230 *
6231 * Returns whether the tabs of the notebook are shown.
6232 *
6233 * Returns: %TRUE if the tabs are shown
6234 */
6235gboolean
6236gtk_notebook_get_show_tabs (GtkNotebook *notebook)
6237{
6238 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
6239
6240 return notebook->show_tabs;
6241}
6242
6243static void
6244gtk_notebook_update_tab_pos (GtkNotebook *notebook)
6245{
6246 GtkLayoutManager *layout;
6247 GtkPositionType tab_pos;
6248 const char *tab_pos_names[] = {
6249 "left", "right", "top", "bottom",
6250 };
6251 int i;
6252
6253 tab_pos = get_effective_tab_pos (notebook);
6254
6255 for (i = 0; i < G_N_ELEMENTS (tab_pos_names); i++)
6256 {
6257 if (tab_pos == i)
6258 gtk_widget_add_css_class (widget: notebook->header_widget, css_class: tab_pos_names[i]);
6259 else
6260 gtk_widget_remove_css_class (widget: notebook->header_widget, css_class: tab_pos_names[i]);
6261 }
6262
6263 layout = gtk_widget_get_layout_manager (GTK_WIDGET (notebook));
6264
6265 switch (tab_pos)
6266 {
6267 case GTK_POS_TOP:
6268 gtk_widget_set_hexpand (widget: notebook->tabs_widget, TRUE);
6269 gtk_widget_set_vexpand (widget: notebook->tabs_widget, FALSE);
6270 gtk_widget_set_hexpand (widget: notebook->header_widget, TRUE);
6271 gtk_widget_set_vexpand (widget: notebook->header_widget, FALSE);
6272 if (notebook->show_tabs)
6273 {
6274 gtk_widget_insert_before (widget: notebook->header_widget, GTK_WIDGET (notebook), next_sibling: notebook->stack_widget);
6275 }
6276
6277 gtk_orientable_set_orientation (GTK_ORIENTABLE (layout), orientation: GTK_ORIENTATION_VERTICAL);
6278 gtk_orientable_set_orientation (GTK_ORIENTABLE (notebook->header_widget), orientation: GTK_ORIENTATION_HORIZONTAL);
6279 break;
6280
6281 case GTK_POS_BOTTOM:
6282 gtk_widget_set_hexpand (widget: notebook->tabs_widget, TRUE);
6283 gtk_widget_set_vexpand (widget: notebook->tabs_widget, FALSE);
6284 gtk_widget_set_hexpand (widget: notebook->header_widget, TRUE);
6285 gtk_widget_set_vexpand (widget: notebook->header_widget, FALSE);
6286 if (notebook->show_tabs)
6287 {
6288 gtk_widget_insert_after (widget: notebook->header_widget, GTK_WIDGET (notebook), previous_sibling: notebook->stack_widget);
6289 }
6290
6291 gtk_orientable_set_orientation (GTK_ORIENTABLE (layout), orientation: GTK_ORIENTATION_VERTICAL);
6292 gtk_orientable_set_orientation (GTK_ORIENTABLE (notebook->header_widget), orientation: GTK_ORIENTATION_HORIZONTAL);
6293 break;
6294
6295 case GTK_POS_LEFT:
6296 gtk_widget_set_hexpand (widget: notebook->tabs_widget, FALSE);
6297 gtk_widget_set_vexpand (widget: notebook->tabs_widget, TRUE);
6298 gtk_widget_set_hexpand (widget: notebook->header_widget, FALSE);
6299 gtk_widget_set_vexpand (widget: notebook->header_widget, TRUE);
6300 if (notebook->show_tabs)
6301 {
6302 gtk_widget_insert_before (widget: notebook->header_widget, GTK_WIDGET (notebook), next_sibling: notebook->stack_widget);
6303 }
6304
6305 gtk_orientable_set_orientation (GTK_ORIENTABLE (layout), orientation: GTK_ORIENTATION_HORIZONTAL);
6306 gtk_orientable_set_orientation (GTK_ORIENTABLE (notebook->header_widget), orientation: GTK_ORIENTATION_VERTICAL);
6307 break;
6308
6309 case GTK_POS_RIGHT:
6310 gtk_widget_set_hexpand (widget: notebook->tabs_widget, FALSE);
6311 gtk_widget_set_vexpand (widget: notebook->tabs_widget, TRUE);
6312 gtk_widget_set_hexpand (widget: notebook->header_widget, FALSE);
6313 gtk_widget_set_vexpand (widget: notebook->header_widget, TRUE);
6314 if (notebook->show_tabs)
6315 {
6316 gtk_widget_insert_after (widget: notebook->header_widget, GTK_WIDGET (notebook), previous_sibling: notebook->stack_widget);
6317 }
6318
6319 gtk_orientable_set_orientation (GTK_ORIENTABLE (layout), orientation: GTK_ORIENTATION_HORIZONTAL);
6320 gtk_orientable_set_orientation (GTK_ORIENTABLE (notebook->header_widget), orientation: GTK_ORIENTATION_VERTICAL);
6321 break;
6322 default:
6323 g_assert_not_reached ();
6324 break;
6325 }
6326}
6327
6328/**
6329 * gtk_notebook_set_tab_pos: (attributes org.gtk.Method.set_property=tab-pos)
6330 * @notebook: a `GtkNotebook`.
6331 * @pos: the edge to draw the tabs at
6332 *
6333 * Sets the edge at which the tabs are drawn.
6334 */
6335void
6336gtk_notebook_set_tab_pos (GtkNotebook *notebook,
6337 GtkPositionType pos)
6338{
6339 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6340
6341 if (notebook->tab_pos != pos)
6342 {
6343 notebook->tab_pos = pos;
6344 gtk_widget_queue_resize (GTK_WIDGET (notebook));
6345
6346 gtk_notebook_update_tab_pos (notebook);
6347
6348 g_object_notify_by_pspec (G_OBJECT (notebook), pspec: properties[PROP_TAB_POS]);
6349 }
6350}
6351
6352/**
6353 * gtk_notebook_get_tab_pos: (attributes org.gtk.Method.get_property=tab-pos)
6354 * @notebook: a `GtkNotebook`
6355 *
6356 * Gets the edge at which the tabs are drawn.
6357 *
6358 * Returns: the edge at which the tabs are drawn
6359 */
6360GtkPositionType
6361gtk_notebook_get_tab_pos (GtkNotebook *notebook)
6362{
6363 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), GTK_POS_TOP);
6364
6365 return notebook->tab_pos;
6366}
6367
6368/**
6369 * gtk_notebook_set_scrollable: (attributes org.gtk.Method.set_property=scrollable)
6370 * @notebook: a `GtkNotebook`
6371 * @scrollable: %TRUE if scroll arrows should be added
6372 *
6373 * Sets whether the tab label area will have arrows for
6374 * scrolling if there are too many tabs to fit in the area.
6375 */
6376void
6377gtk_notebook_set_scrollable (GtkNotebook *notebook,
6378 gboolean scrollable)
6379{
6380 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6381
6382 scrollable = (scrollable != FALSE);
6383
6384 if (notebook->scrollable == scrollable)
6385 return;
6386
6387 notebook->scrollable = scrollable;
6388
6389 update_arrow_nodes (notebook);
6390 update_arrow_state (notebook);
6391
6392 gtk_widget_queue_resize (GTK_WIDGET (notebook));
6393
6394 g_object_notify_by_pspec (G_OBJECT (notebook), pspec: properties[PROP_SCROLLABLE]);
6395}
6396
6397/**
6398 * gtk_notebook_get_scrollable: (attributes or.gtk.Method.get_property=scrollable)
6399 * @notebook: a `GtkNotebook`
6400 *
6401 * Returns whether the tab label area has arrows for scrolling.
6402 *
6403 * Returns: %TRUE if arrows for scrolling are present
6404 */
6405gboolean
6406gtk_notebook_get_scrollable (GtkNotebook *notebook)
6407{
6408 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
6409
6410 return notebook->scrollable;
6411}
6412
6413
6414/* Public GtkNotebook Popup Menu Methods:
6415 *
6416 * gtk_notebook_popup_enable
6417 * gtk_notebook_popup_disable
6418 */
6419
6420
6421/**
6422 * gtk_notebook_popup_enable:
6423 * @notebook: a `GtkNotebook`
6424 *
6425 * Enables the popup menu.
6426 *
6427 * If the user clicks with the right mouse button on the tab labels,
6428 * a menu with all the pages will be popped up.
6429 */
6430void
6431gtk_notebook_popup_enable (GtkNotebook *notebook)
6432{
6433 GList *list;
6434
6435 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6436
6437 if (notebook->menu)
6438 return;
6439
6440 notebook->menu = gtk_popover_menu_new ();
6441 gtk_widget_set_parent (widget: notebook->menu, parent: notebook->tabs_widget);
6442
6443 notebook->menu_box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0);
6444 g_object_ref_sink (notebook->menu_box);
6445 gtk_popover_menu_add_submenu (GTK_POPOVER_MENU (notebook->menu), submenu: notebook->menu_box, name: "main");
6446
6447 for (list = gtk_notebook_search_page (notebook, NULL, direction: STEP_NEXT, FALSE);
6448 list;
6449 list = gtk_notebook_search_page (notebook, list, direction: STEP_NEXT, FALSE))
6450 gtk_notebook_menu_item_create (notebook, page: list->data);
6451
6452 gtk_notebook_update_labels (notebook);
6453
6454 g_object_notify_by_pspec (G_OBJECT (notebook), pspec: properties[PROP_ENABLE_POPUP]);
6455}
6456
6457/**
6458 * gtk_notebook_popup_disable:
6459 * @notebook: a `GtkNotebook`
6460 *
6461 * Disables the popup menu.
6462 */
6463void
6464gtk_notebook_popup_disable (GtkNotebook *notebook)
6465{
6466 GtkWidget *child;
6467
6468 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6469
6470 if (!notebook->menu)
6471 return;
6472
6473 for (child = gtk_widget_get_first_child (widget: notebook->menu_box);
6474 child != NULL;
6475 child = gtk_widget_get_next_sibling (widget: child))
6476 gtk_notebook_menu_label_unparent (widget: child);
6477 notebook->menu = NULL;
6478 notebook->menu_box = NULL;
6479
6480 g_object_notify_by_pspec (G_OBJECT (notebook), pspec: properties[PROP_ENABLE_POPUP]);
6481}
6482
6483/* Public GtkNotebook Page Properties Functions:
6484 *
6485 * gtk_notebook_get_tab_label
6486 * gtk_notebook_set_tab_label
6487 * gtk_notebook_set_tab_label_text
6488 * gtk_notebook_get_menu_label
6489 * gtk_notebook_set_menu_label
6490 * gtk_notebook_set_menu_label_text
6491 * gtk_notebook_get_tab_reorderable
6492 * gtk_notebook_set_tab_reorderable
6493 * gtk_notebook_get_tab_detachable
6494 * gtk_notebook_set_tab_detachable
6495 */
6496
6497/**
6498 * gtk_notebook_get_tab_label:
6499 * @notebook: a `GtkNotebook`
6500 * @child: the page
6501 *
6502 * Returns the tab label widget for the page @child.
6503 *
6504 * %NULL is returned if @child is not in @notebook or
6505 * if no tab label has specifically been set for @child.
6506 *
6507 * Returns: (transfer none) (nullable): the tab label
6508 */
6509GtkWidget *
6510gtk_notebook_get_tab_label (GtkNotebook *notebook,
6511 GtkWidget *child)
6512{
6513 GList *list;
6514
6515 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
6516 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
6517
6518 list = gtk_notebook_find_child (notebook, child);
6519 if (list == NULL)
6520 return NULL;
6521
6522 if (GTK_NOTEBOOK_PAGE_FROM_LIST (list)->default_tab)
6523 return NULL;
6524
6525 return GTK_NOTEBOOK_PAGE_FROM_LIST (list)->tab_label;
6526}
6527
6528/**
6529 * gtk_notebook_set_tab_label:
6530 * @notebook: a `GtkNotebook`
6531 * @child: the page
6532 * @tab_label: (nullable): the tab label widget to use, or %NULL
6533 * for default tab label
6534 *
6535 * Changes the tab label for @child.
6536 *
6537 * If %NULL is specified for @tab_label, then the page will
6538 * have the label “page N”.
6539 */
6540void
6541gtk_notebook_set_tab_label (GtkNotebook *notebook,
6542 GtkWidget *child,
6543 GtkWidget *tab_label)
6544{
6545 GtkNotebookPage *page;
6546 GList *list;
6547
6548 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6549 g_return_if_fail (GTK_IS_WIDGET (child));
6550
6551 list = gtk_notebook_find_child (notebook, child);
6552 g_return_if_fail (list != NULL);
6553
6554 /* a NULL pointer indicates a default_tab setting, otherwise
6555 * we need to set the associated label
6556 */
6557 page = list->data;
6558
6559 if (page->tab_label == tab_label)
6560 return;
6561
6562 gtk_notebook_remove_tab_label (notebook, page);
6563
6564 if (tab_label)
6565 {
6566 page->default_tab = FALSE;
6567 page->tab_label = tab_label;
6568 g_object_set_data (G_OBJECT (page->tab_label), key: "notebook", data: notebook);
6569 gtk_widget_set_parent (widget: page->tab_label, parent: page->tab_widget);
6570 }
6571 else
6572 {
6573 page->default_tab = TRUE;
6574 page->tab_label = NULL;
6575
6576 if (notebook->show_tabs)
6577 {
6578 char string[32];
6579
6580 g_snprintf (string, n: sizeof(string), _("Page %u"),
6581 g_list_position (list: notebook->children, llink: list));
6582 page->tab_label = gtk_label_new (str: string);
6583 gtk_widget_set_parent (widget: page->tab_label, parent: page->tab_widget);
6584 g_object_set_data (G_OBJECT (page->tab_label), key: "notebook", data: notebook);
6585 }
6586 }
6587
6588 if (page->tab_label)
6589 page->mnemonic_activate_signal =
6590 g_signal_connect (page->tab_label,
6591 "mnemonic-activate",
6592 G_CALLBACK (gtk_notebook_mnemonic_activate_switch_page),
6593 notebook);
6594
6595 if (notebook->show_tabs && gtk_widget_get_visible (widget: child))
6596 {
6597 gtk_widget_show (widget: page->tab_label);
6598 gtk_widget_queue_resize (GTK_WIDGET (notebook));
6599 }
6600
6601 if (notebook->menu)
6602 gtk_notebook_menu_item_recreate (notebook, list);
6603
6604 g_object_notify (G_OBJECT (page), property_name: "tab-label");
6605}
6606
6607/**
6608 * gtk_notebook_set_tab_label_text:
6609 * @notebook: a `GtkNotebook`
6610 * @child: the page
6611 * @tab_text: the label text
6612 *
6613 * Creates a new label and sets it as the tab label for the page
6614 * containing @child.
6615 */
6616void
6617gtk_notebook_set_tab_label_text (GtkNotebook *notebook,
6618 GtkWidget *child,
6619 const char *tab_text)
6620{
6621 GtkWidget *tab_label = NULL;
6622
6623 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6624
6625 if (tab_text)
6626 tab_label = gtk_label_new (str: tab_text);
6627 gtk_notebook_set_tab_label (notebook, child, tab_label);
6628}
6629
6630/**
6631 * gtk_notebook_get_tab_label_text:
6632 * @notebook: a `GtkNotebook`
6633 * @child: a widget contained in a page of @notebook
6634 *
6635 * Retrieves the text of the tab label for the page containing
6636 * @child.
6637 *
6638 * Returns: (nullable): the text of the tab label, or %NULL if
6639 * the tab label idget is not a `GtkLabel`. The string is owned
6640 * by the widget and must not be freed.
6641 */
6642const char *
6643gtk_notebook_get_tab_label_text (GtkNotebook *notebook,
6644 GtkWidget *child)
6645{
6646 GtkWidget *tab_label;
6647
6648 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
6649 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
6650
6651 tab_label = gtk_notebook_get_tab_label (notebook, child);
6652
6653 if (GTK_IS_LABEL (tab_label))
6654 return gtk_label_get_text (GTK_LABEL (tab_label));
6655 else
6656 return NULL;
6657}
6658
6659/**
6660 * gtk_notebook_get_menu_label:
6661 * @notebook: a `GtkNotebook`
6662 * @child: a widget contained in a page of @notebook
6663 *
6664 * Retrieves the menu label widget of the page containing @child.
6665 *
6666 * Returns: (nullable) (transfer none): the menu label, or %NULL
6667 * if the notebook page does not have a menu label other than
6668 * the default (the tab label).
6669 */
6670GtkWidget*
6671gtk_notebook_get_menu_label (GtkNotebook *notebook,
6672 GtkWidget *child)
6673{
6674 GList *list;
6675
6676 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
6677 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
6678
6679 list = gtk_notebook_find_child (notebook, child);
6680 g_return_val_if_fail (list != NULL, NULL);
6681
6682 if (GTK_NOTEBOOK_PAGE_FROM_LIST (list)->default_menu)
6683 return NULL;
6684
6685 return GTK_NOTEBOOK_PAGE_FROM_LIST (list)->menu_label;
6686}
6687
6688/**
6689 * gtk_notebook_set_menu_label:
6690 * @notebook: a `GtkNotebook`
6691 * @child: the child widget
6692 * @menu_label: (nullable): the menu label, or %NULL for default
6693 *
6694 * Changes the menu label for the page containing @child.
6695 */
6696void
6697gtk_notebook_set_menu_label (GtkNotebook *notebook,
6698 GtkWidget *child,
6699 GtkWidget *menu_label)
6700{
6701 GtkNotebookPage *page;
6702 GList *list;
6703
6704 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6705 g_return_if_fail (GTK_IS_WIDGET (child));
6706
6707 list = gtk_notebook_find_child (notebook, child);
6708 g_return_if_fail (list != NULL);
6709
6710 page = list->data;
6711 if (page->menu_label)
6712 {
6713 if (notebook->menu)
6714 gtk_widget_unparent (widget: gtk_widget_get_parent (widget: page->menu_label));
6715
6716 g_clear_object (&page->menu_label);
6717 }
6718
6719 if (menu_label)
6720 {
6721 page->menu_label = menu_label;
6722 g_object_ref_sink (page->menu_label);
6723 page->default_menu = FALSE;
6724 }
6725 else
6726 page->default_menu = TRUE;
6727
6728 if (notebook->menu)
6729 gtk_notebook_menu_item_create (notebook, page);
6730 g_object_notify (G_OBJECT (page), property_name: "menu-label");
6731}
6732
6733/**
6734 * gtk_notebook_set_menu_label_text:
6735 * @notebook: a `GtkNotebook`
6736 * @child: the child widget
6737 * @menu_text: the label text
6738 *
6739 * Creates a new label and sets it as the menu label of @child.
6740 */
6741void
6742gtk_notebook_set_menu_label_text (GtkNotebook *notebook,
6743 GtkWidget *child,
6744 const char *menu_text)
6745{
6746 GtkWidget *menu_label = NULL;
6747
6748 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6749
6750 if (menu_text)
6751 {
6752 menu_label = gtk_label_new (str: menu_text);
6753 gtk_widget_set_halign (widget: menu_label, align: GTK_ALIGN_START);
6754 gtk_widget_set_valign (widget: menu_label, align: GTK_ALIGN_CENTER);
6755 }
6756 gtk_notebook_set_menu_label (notebook, child, menu_label);
6757}
6758
6759/**
6760 * gtk_notebook_get_menu_label_text:
6761 * @notebook: a `GtkNotebook`
6762 * @child: the child widget of a page of the notebook.
6763 *
6764 * Retrieves the text of the menu label for the page containing
6765 * @child.
6766 *
6767 * Returns: (nullable): the text of the tab label, or %NULL if
6768 * the widget does not have a menu label other than the default
6769 * menu label, or the menu label widget is not a `GtkLabel`.
6770 * The string is owned by the widget and must not be freed.
6771 */
6772const char *
6773gtk_notebook_get_menu_label_text (GtkNotebook *notebook,
6774 GtkWidget *child)
6775{
6776 GtkWidget *menu_label;
6777
6778 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
6779 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
6780
6781 menu_label = gtk_notebook_get_menu_label (notebook, child);
6782
6783 if (GTK_IS_LABEL (menu_label))
6784 return gtk_label_get_text (GTK_LABEL (menu_label));
6785 else
6786 return NULL;
6787}
6788
6789/* Helper function called when pages are reordered
6790 */
6791static void
6792gtk_notebook_child_reordered (GtkNotebook *notebook,
6793 GtkNotebookPage *page)
6794{
6795 GList *list;
6796 GtkWidget *sibling;
6797
6798 list = g_list_find (list: notebook->children, data: page);
6799
6800 if (notebook->menu)
6801 gtk_notebook_menu_item_recreate (notebook, list);
6802
6803 if (list->prev)
6804 sibling = GTK_NOTEBOOK_PAGE_FROM_LIST (list->prev)->tab_widget;
6805 else if (notebook->arrow_widget[ARROW_RIGHT_BEFORE])
6806 sibling = notebook->arrow_widget[ARROW_RIGHT_BEFORE];
6807 else if (notebook->arrow_widget[ARROW_LEFT_BEFORE])
6808 sibling = notebook->arrow_widget[ARROW_LEFT_BEFORE];
6809 else
6810 sibling = NULL;
6811
6812 gtk_widget_insert_after (widget: page->tab_widget, parent: notebook->tabs_widget, previous_sibling: sibling);
6813
6814 update_arrow_state (notebook);
6815 gtk_notebook_update_labels (notebook);
6816 gtk_widget_queue_allocate (widget: notebook->tabs_widget);
6817}
6818
6819/**
6820 * gtk_notebook_reorder_child:
6821 * @notebook: a `GtkNotebook`
6822 * @child: the child to move
6823 * @position: the new position, or -1 to move to the end
6824 *
6825 * Reorders the page containing @child, so that it appears in position
6826 * @position.
6827 *
6828 * If @position is greater than or equal to the number of children in
6829 * the list or negative, @child will be moved to the end of the list.
6830 */
6831void
6832gtk_notebook_reorder_child (GtkNotebook *notebook,
6833 GtkWidget *child,
6834 int position)
6835{
6836 GList *list, *new_list;
6837 GtkNotebookPage *page;
6838 int old_pos;
6839 int max_pos;
6840 int i;
6841
6842 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6843 g_return_if_fail (GTK_IS_WIDGET (child));
6844
6845 list = gtk_notebook_find_child (notebook, child);
6846 g_return_if_fail (list != NULL);
6847
6848 max_pos = g_list_length (list: notebook->children) - 1;
6849 if (position < 0 || position > max_pos)
6850 position = max_pos;
6851
6852 old_pos = g_list_position (list: notebook->children, llink: list);
6853
6854 if (old_pos == position)
6855 return;
6856
6857 page = list->data;
6858 notebook->children = g_list_delete_link (list: notebook->children, link_: list);
6859
6860 notebook->children = g_list_insert (list: notebook->children, data: page, position);
6861 new_list = g_list_nth (list: notebook->children, n: position);
6862
6863 /* Fix up GList references in GtkNotebook structure */
6864 if (notebook->first_tab == list)
6865 notebook->first_tab = new_list;
6866 if (notebook->focus_tab == list)
6867 notebook->focus_tab = new_list;
6868
6869 /* Move around the menu items if necessary */
6870 gtk_notebook_child_reordered (notebook, page);
6871
6872 for (list = notebook->children, i = 0; list; list = list->next, i++)
6873 {
6874 if (MIN (old_pos, position) <= i && i <= MAX (old_pos, position))
6875 g_object_notify (G_OBJECT (list->data), property_name: "position");
6876 }
6877
6878 g_signal_emit (instance: notebook,
6879 signal_id: notebook_signals[PAGE_REORDERED],
6880 detail: 0,
6881 child,
6882 position);
6883}
6884
6885/**
6886 * gtk_notebook_set_group_name: (attributes org.gtk.Method.set_property=group-name)
6887 * @notebook: a `GtkNotebook`
6888 * @group_name: (nullable): the name of the notebook group,
6889 * or %NULL to unset it
6890 *
6891 * Sets a group name for @notebook.
6892 *
6893 * Notebooks with the same name will be able to exchange tabs
6894 * via drag and drop. A notebook with a %NULL group name will
6895 * not be able to exchange tabs with any other notebook.
6896 */
6897void
6898gtk_notebook_set_group_name (GtkNotebook *notebook,
6899 const char *group_name)
6900{
6901 GQuark group;
6902
6903 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6904
6905 group = g_quark_from_string (string: group_name);
6906
6907 if (notebook->group != group)
6908 {
6909 notebook->group = group;
6910
6911 g_object_notify_by_pspec (G_OBJECT (notebook), pspec: properties[PROP_GROUP_NAME]);
6912 }
6913}
6914
6915/**
6916 * gtk_notebook_get_group_name: (attributes org.gtk.Method.get_property=group-name)
6917 * @notebook: a `GtkNotebook`
6918 *
6919 * Gets the current group name for @notebook.
6920 *
6921 * Returns: (nullable) (transfer none): the group name,
6922 * or %NULL if none is set
6923 */
6924const char *
6925gtk_notebook_get_group_name (GtkNotebook *notebook)
6926{
6927 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
6928
6929 return g_quark_to_string (quark: notebook->group);
6930}
6931
6932/**
6933 * gtk_notebook_get_tab_reorderable:
6934 * @notebook: a `GtkNotebook`
6935 * @child: a child `GtkWidget`
6936 *
6937 * Gets whether the tab can be reordered via drag and drop or not.
6938 *
6939 * Returns: %TRUE if the tab is reorderable.
6940 */
6941gboolean
6942gtk_notebook_get_tab_reorderable (GtkNotebook *notebook,
6943 GtkWidget *child)
6944{
6945 GList *list;
6946
6947 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
6948 g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
6949
6950 list = gtk_notebook_find_child (notebook, child);
6951 g_return_val_if_fail (list != NULL, FALSE);
6952
6953 return GTK_NOTEBOOK_PAGE_FROM_LIST (list)->reorderable;
6954}
6955
6956/**
6957 * gtk_notebook_set_tab_reorderable:
6958 * @notebook: a `GtkNotebook`
6959 * @child: a child `GtkWidget`
6960 * @reorderable: whether the tab is reorderable or not
6961 *
6962 * Sets whether the notebook tab can be reordered
6963 * via drag and drop or not.
6964 */
6965void
6966gtk_notebook_set_tab_reorderable (GtkNotebook *notebook,
6967 GtkWidget *child,
6968 gboolean reorderable)
6969{
6970 GtkNotebookPage *page;
6971 GList *list;
6972
6973 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6974 g_return_if_fail (GTK_IS_WIDGET (child));
6975
6976 list = gtk_notebook_find_child (notebook, child);
6977 g_return_if_fail (list != NULL);
6978
6979 page = GTK_NOTEBOOK_PAGE_FROM_LIST (list);
6980 reorderable = reorderable != FALSE;
6981
6982 if (page->reorderable != reorderable)
6983 {
6984 page->reorderable = reorderable;
6985 if (reorderable)
6986 gtk_widget_add_css_class (widget: page->tab_widget, css_class: "reorderable-page");
6987 else
6988 gtk_widget_remove_css_class (widget: page->tab_widget, css_class: "reorderable-page");
6989
6990 g_object_notify (G_OBJECT (page), property_name: "reorderable");
6991 }
6992}
6993
6994/**
6995 * gtk_notebook_get_tab_detachable:
6996 * @notebook: a `GtkNotebook`
6997 * @child: a child `GtkWidget`
6998 *
6999 * Returns whether the tab contents can be detached from @notebook.
7000 *
7001 * Returns: %TRUE if the tab is detachable.
7002 */
7003gboolean
7004gtk_notebook_get_tab_detachable (GtkNotebook *notebook,
7005 GtkWidget *child)
7006{
7007 GList *list;
7008
7009 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7010 g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
7011
7012 list = gtk_notebook_find_child (notebook, child);
7013 g_return_val_if_fail (list != NULL, FALSE);
7014
7015 return GTK_NOTEBOOK_PAGE_FROM_LIST (list)->detachable;
7016}
7017
7018/**
7019 * gtk_notebook_set_tab_detachable:
7020 * @notebook: a `GtkNotebook`
7021 * @child: a child `GtkWidget`
7022 * @detachable: whether the tab is detachable or not
7023 *
7024 * Sets whether the tab can be detached from @notebook to another
7025 * notebook or widget.
7026 *
7027 * Note that two notebooks must share a common group identificator
7028 * (see [method@Gtk.Notebook.set_group_name]) to allow automatic tabs
7029 * interchange between them.
7030 *
7031 * If you want a widget to interact with a notebook through DnD
7032 * (i.e.: accept dragged tabs from it) it must be set as a drop
7033 * destination and accept the target “GTK_NOTEBOOK_TAB”. The notebook
7034 * will fill the selection with a GtkWidget** pointing to the child
7035 * widget that corresponds to the dropped tab.
7036 *
7037 * Note that you should use [method@Gtk.Notebook.detach_tab] instead
7038 * of [method@Gtk.Notebook.remove_page] if you want to remove the tab
7039 * from the source notebook as part of accepting a drop. Otherwise,
7040 * the source notebook will think that the dragged tab was removed
7041 * from underneath the ongoing drag operation, and will initiate a
7042 * drag cancel animation.
7043 *
7044 * ```c
7045 * static void
7046 * on_drag_data_received (GtkWidget *widget,
7047 * GdkDrop *drop,
7048 * GtkSelectionData *data,
7049 * guint time,
7050 * gpointer user_data)
7051 * {
7052 * GtkDrag *drag;
7053 * GtkWidget *notebook;
7054 * GtkWidget **child;
7055 *
7056 * drag = gtk_drop_get_drag (drop);
7057 * notebook = g_object_get_data (drag, "gtk-notebook-drag-origin");
7058 * child = (void*) gtk_selection_data_get_data (data);
7059 *
7060 * // process_widget (*child);
7061 *
7062 * gtk_notebook_detach_tab (GTK_NOTEBOOK (notebook), *child);
7063 * }
7064 * ```
7065 *
7066 * If you want a notebook to accept drags from other widgets,
7067 * you will have to set your own DnD code to do it.
7068 */
7069void
7070gtk_notebook_set_tab_detachable (GtkNotebook *notebook,
7071 GtkWidget *child,
7072 gboolean detachable)
7073{
7074 GList *list;
7075 GtkNotebookPage *page;
7076
7077 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7078 g_return_if_fail (GTK_IS_WIDGET (child));
7079
7080 list = gtk_notebook_find_child (notebook, child);
7081 g_return_if_fail (list != NULL);
7082
7083 page = GTK_NOTEBOOK_PAGE_FROM_LIST (list);
7084 detachable = detachable != FALSE;
7085
7086 if (page->detachable != detachable)
7087 {
7088 page->detachable = detachable;
7089 g_object_notify (G_OBJECT (page), property_name: "detachable");
7090 }
7091}
7092
7093/**
7094 * gtk_notebook_get_action_widget:
7095 * @notebook: a `GtkNotebook`
7096 * @pack_type: pack type of the action widget to receive
7097 *
7098 * Gets one of the action widgets.
7099 *
7100 * See [method@Gtk.Notebook.set_action_widget].
7101 *
7102 * Returns: (nullable) (transfer none): The action widget
7103 * with the given @pack_type or %NULL when this action
7104 * widget has not been set
7105 */
7106GtkWidget*
7107gtk_notebook_get_action_widget (GtkNotebook *notebook,
7108 GtkPackType pack_type)
7109{
7110 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7111
7112 return notebook->action_widget[pack_type];
7113}
7114
7115/**
7116 * gtk_notebook_set_action_widget:
7117 * @notebook: a `GtkNotebook`
7118 * @widget: a `GtkWidget`
7119 * @pack_type: pack type of the action widget
7120 *
7121 * Sets @widget as one of the action widgets.
7122 *
7123 * Depending on the pack type the widget will be placed before
7124 * or after the tabs. You can use a `GtkBox` if you need to pack
7125 * more than one widget on the same side.
7126 */
7127void
7128gtk_notebook_set_action_widget (GtkNotebook *notebook,
7129 GtkWidget *widget,
7130 GtkPackType pack_type)
7131{
7132 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7133 g_return_if_fail (!widget || GTK_IS_WIDGET (widget));
7134 g_return_if_fail (!widget || gtk_widget_get_parent (widget) == NULL);
7135
7136 if (notebook->action_widget[pack_type])
7137 gtk_box_remove (GTK_BOX (notebook->header_widget), child: notebook->action_widget[pack_type]);
7138
7139 notebook->action_widget[pack_type] = widget;
7140
7141 if (widget)
7142 {
7143 gtk_box_append (GTK_BOX (notebook->header_widget), child: widget);
7144 if (pack_type == GTK_PACK_START)
7145 gtk_box_reorder_child_after (GTK_BOX (notebook->header_widget), child: widget, NULL);
7146 else
7147 gtk_box_reorder_child_after (GTK_BOX (notebook->header_widget), child: widget, sibling: gtk_widget_get_last_child (widget: notebook->header_widget));
7148 gtk_widget_set_child_visible (widget, child_visible: notebook->show_tabs);
7149 }
7150
7151 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7152}
7153
7154/**
7155 * gtk_notebook_get_page:
7156 * @notebook: a `GtkNotebook`
7157 * @child: a child of @notebook
7158 *
7159 * Returns the `GtkNotebookPage` for @child.
7160 *
7161 * Returns: (transfer none): the `GtkNotebookPage` for @child
7162 */
7163GtkNotebookPage *
7164gtk_notebook_get_page (GtkNotebook *notebook,
7165 GtkWidget *child)
7166{
7167 GList *list;
7168 GtkNotebookPage *page = NULL;
7169
7170 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7171 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7172
7173 list = gtk_notebook_find_child (notebook, child);
7174 if (list != NULL)
7175 page = list->data;
7176
7177 return page;
7178}
7179
7180/**
7181 * gtk_notebook_page_get_child: (attributes org.gtk.Method.get_property=child)
7182 * @page: a `GtkNotebookPage`
7183 *
7184 * Returns the notebook child to which @page belongs.
7185 *
7186 * Returns: (transfer none): the child to which @page belongs
7187 */
7188GtkWidget *
7189gtk_notebook_page_get_child (GtkNotebookPage *page)
7190{
7191 return page->child;
7192}
7193
7194#define GTK_TYPE_NOTEBOOK_PAGES (gtk_notebook_pages_get_type ())
7195G_DECLARE_FINAL_TYPE (GtkNotebookPages, gtk_notebook_pages, GTK, NOTEBOOK_PAGES, GObject)
7196
7197struct _GtkNotebookPages
7198{
7199 GObject parent_instance;
7200 GtkNotebook *notebook;
7201};
7202
7203struct _GtkNotebookPagesClass
7204{
7205 GObjectClass parent_class;
7206};
7207
7208static GType
7209gtk_notebook_pages_get_item_type (GListModel *model)
7210{
7211 return GTK_TYPE_NOTEBOOK_PAGE;
7212}
7213
7214static guint
7215gtk_notebook_pages_get_n_items (GListModel *model)
7216{
7217 GtkNotebookPages *pages = GTK_NOTEBOOK_PAGES (ptr: model);
7218
7219 return g_list_length (list: pages->notebook->children);
7220}
7221
7222
7223static gpointer
7224gtk_notebook_pages_get_item (GListModel *model,
7225 guint position)
7226{
7227 GtkNotebookPages *pages = GTK_NOTEBOOK_PAGES (ptr: model);
7228 GtkNotebookPage *page;
7229
7230 page = g_list_nth_data (list: pages->notebook->children, n: position);
7231
7232 return g_object_ref (page);
7233}
7234
7235static void
7236gtk_notebook_pages_list_model_init (GListModelInterface *iface)
7237{
7238 iface->get_item_type = gtk_notebook_pages_get_item_type;
7239 iface->get_n_items = gtk_notebook_pages_get_n_items;
7240 iface->get_item = gtk_notebook_pages_get_item;
7241}
7242G_DEFINE_TYPE_WITH_CODE (GtkNotebookPages, gtk_notebook_pages, G_TYPE_OBJECT,
7243 G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_notebook_pages_list_model_init))
7244
7245static void
7246gtk_notebook_pages_init (GtkNotebookPages *pages)
7247{
7248}
7249
7250static void
7251gtk_notebook_pages_class_init (GtkNotebookPagesClass *class)
7252{
7253}
7254
7255static GtkNotebookPages *
7256gtk_notebook_pages_new (GtkNotebook *notebook)
7257{
7258 GtkNotebookPages *pages;
7259
7260 pages = g_object_new (GTK_TYPE_NOTEBOOK_PAGES, NULL);
7261 pages->notebook = notebook;
7262
7263 return pages;
7264}
7265
7266/**
7267 * gtk_notebook_get_pages: (attributes org.gtk.Method.get_property=pages)
7268 * @notebook: a `GtkNotebook`
7269 *
7270 * Returns a `GListModel` that contains the pages of the notebook.
7271 *
7272 * This can be used to keep an up-to-date view. The model also
7273 * implements [iface@Gtk.SelectionModel] and can be used to track
7274 * and modify the visible page.
7275
7276 * Returns: (transfer full) (attributes element-type=GtkNotebookPage): a
7277 * `GListModel` for the notebook's children
7278 */
7279GListModel *
7280gtk_notebook_get_pages (GtkNotebook *notebook)
7281{
7282 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7283
7284 if (notebook->pages)
7285 return g_object_ref (notebook->pages);
7286
7287 notebook->pages = G_LIST_MODEL (ptr: gtk_notebook_pages_new (notebook));
7288
7289 g_object_add_weak_pointer (G_OBJECT (notebook->pages), weak_pointer_location: (gpointer *)&notebook->pages);
7290
7291 return notebook->pages;
7292}
7293
7294

source code of gtk/gtk/gtknotebook.c