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 
172 typedef struct _GtkNotebookPage GtkNotebookPage;
173 
174 typedef enum
175 {
176   DRAG_OPERATION_NONE,
177   DRAG_OPERATION_REORDER,
178   DRAG_OPERATION_DETACH
179 } GtkNotebookDragOperation;
180 
181 enum {
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 
190 typedef struct _GtkNotebookClass         GtkNotebookClass;
191 
192 struct _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 
235 struct _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 
288 enum {
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 
302 enum {
303   STEP_PREV,
304   STEP_NEXT
305 };
306 
307 typedef enum
308 {
309   ARROW_LEFT_BEFORE,
310   ARROW_RIGHT_BEFORE,
311   ARROW_LEFT_AFTER,
312   ARROW_RIGHT_AFTER,
313   ARROW_NONE
314 } GtkNotebookArrow;
315 
316 typedef 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 
326 enum {
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 
339 static GParamSpec *properties[LAST_PROP];
340 
341 enum {
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 
360 struct _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 
387 typedef struct _GtkNotebookPageClass GtkNotebookPageClass;
388 struct _GtkNotebookPageClass
389 {
390   GObjectClass parent_class;
391 };
392 
G_DEFINE_TYPE(GtkNotebookPage,gtk_notebook_page,G_TYPE_OBJECT)393 G_DEFINE_TYPE (GtkNotebookPage, gtk_notebook_page, G_TYPE_OBJECT)
394 
395 static void
396 gtk_notebook_page_init (GtkNotebookPage *page)
397 {
398   page->default_tab = TRUE;
399   page->default_menu = TRUE;
400   page->fill = TRUE;
401 }
402 
403 static void
gtk_notebook_page_finalize(GObject * object)404 gtk_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 (page->tab_text);
413   g_free (page->menu_text);
414 
415   G_OBJECT_CLASS (gtk_notebook_page_parent_class)->finalize (object);
416 }
417 
418 
419 static void
gtk_notebook_page_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)420 gtk_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 (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), page->tab_text);
448       break;
449 
450     case CHILD_PROP_MENU_LABEL:
451       g_free (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), 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, page->child, 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 
506 static void
gtk_notebook_page_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)507 gtk_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, page->child);
518       break;
519 
520     case CHILD_PROP_TAB:
521       g_value_set_object (value, page->tab_label);
522       break;
523 
524     case CHILD_PROP_MENU:
525       g_value_set_object (value, page->menu_label);
526       break;
527 
528     case CHILD_PROP_TAB_LABEL:
529       g_value_set_string (value, page->tab_text);
530       break;
531 
532     case CHILD_PROP_MENU_LABEL:
533       g_value_set_string (value, 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, g_list_index (notebook->children, page));
544       }
545       break;
546 
547     case CHILD_PROP_TAB_EXPAND:
548         g_value_set_boolean (value, page->expand);
549       break;
550 
551     case CHILD_PROP_TAB_FILL:
552         g_value_set_boolean (value, page->fill);
553       break;
554 
555     case CHILD_PROP_REORDERABLE:
556       g_value_set_boolean (value, page->reorderable);
557       break;
558 
559     case CHILD_PROP_DETACHABLE:
560       g_value_set_boolean (value, page->detachable);
561       break;
562 
563     default:
564       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
565       break;
566     }
567 }
568 
569 static void
gtk_notebook_page_class_init(GtkNotebookPageClass * class)570 gtk_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 (object_class,
584                                    CHILD_PROP_CHILD,
585                                    g_param_spec_object ("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 (object_class,
597                                    CHILD_PROP_TAB,
598                                    g_param_spec_object ("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 (object_class,
610                                    CHILD_PROP_MENU,
611                                    g_param_spec_object ("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 (object_class,
623                                    CHILD_PROP_TAB_LABEL,
624                                    g_param_spec_string ("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 (object_class,
636                                    CHILD_PROP_MENU_LABEL,
637                                    g_param_spec_string ("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 (object_class,
649                                    CHILD_PROP_POSITION,
650                                    g_param_spec_int ("position",
651                                                      P_("Position"),
652                                                      P_("The index of the child in the parent"),
653                                                      -1, G_MAXINT, 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 (object_class,
662                                    CHILD_PROP_TAB_EXPAND,
663                                    g_param_spec_boolean ("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 (object_class,
675                                    CHILD_PROP_TAB_FILL,
676                                    g_param_spec_boolean ("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 (object_class,
688                                    CHILD_PROP_REORDERABLE,
689                                    g_param_spec_boolean ("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 (object_class,
701                                    CHILD_PROP_DETACHABLE,
702                                    g_param_spec_boolean ("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 
714 typedef struct _GtkNotebookRootContent GtkNotebookRootContent;
715 typedef struct _GtkNotebookRootContentClass GtkNotebookRootContentClass;
716 
717 struct _GtkNotebookRootContent
718 {
719   GdkContentProvider parent_instance;
720 
721   GtkNotebook *notebook;
722 };
723 
724 struct _GtkNotebookRootContentClass
725 {
726   GdkContentProviderClass parent_class;
727 };
728 
729 static GdkContentFormats *
gtk_notebook_root_content_ref_formats(GdkContentProvider * provider)730 gtk_notebook_root_content_ref_formats (GdkContentProvider *provider)
731 {
732   return gdk_content_formats_new ((const char *[1]) { "application/x-rootwindow-drop" }, 1);
733 }
734 
735 GType gtk_notebook_root_content_get_type (void);
736 
G_DEFINE_TYPE(GtkNotebookRootContent,gtk_notebook_root_content,GDK_TYPE_CONTENT_PROVIDER)737 G_DEFINE_TYPE (GtkNotebookRootContent, gtk_notebook_root_content, GDK_TYPE_CONTENT_PROVIDER)
738 
739 static void
740 gtk_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 (self, cancellable, callback, user_data);
754   g_task_set_priority (task, 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 (task);
758 }
759 
760 static gboolean
gtk_notebook_root_content_write_mime_type_finish(GdkContentProvider * provider,GAsyncResult * result,GError ** error)761 gtk_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 
768 static void
gtk_notebook_root_content_finalize(GObject * object)769 gtk_notebook_root_content_finalize (GObject *object)
770 {
771   GtkNotebookRootContent *self = GTK_NOTEBOOK_ROOT_CONTENT (object);
772 
773   g_object_unref (self->notebook);
774 
775   G_OBJECT_CLASS (gtk_notebook_root_content_parent_class)->finalize (object);
776 }
777 
778 static void
gtk_notebook_root_content_class_init(GtkNotebookRootContentClass * class)779 gtk_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 
791 static void
gtk_notebook_root_content_init(GtkNotebookRootContent * self)792 gtk_notebook_root_content_init (GtkNotebookRootContent *self)
793 {
794 }
795 
796 static GdkContentProvider *
gtk_notebook_root_content_new(GtkNotebook * notebook)797 gtk_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 ***/
809 static gboolean gtk_notebook_select_page         (GtkNotebook      *notebook,
810                                                   gboolean          move_focus);
811 static gboolean gtk_notebook_focus_tab           (GtkNotebook      *notebook,
812                                                   GtkNotebookTab    type);
813 static gboolean gtk_notebook_change_current_page (GtkNotebook      *notebook,
814                                                   int               offset);
815 static void     gtk_notebook_move_focus_out      (GtkNotebook      *notebook,
816                                                   GtkDirectionType  direction_type);
817 static gboolean gtk_notebook_reorder_tab         (GtkNotebook      *notebook,
818                                                   GtkDirectionType  direction_type,
819                                                   gboolean          move_to_last);
820 static void     gtk_notebook_remove_tab_label    (GtkNotebook      *notebook,
821                                                   GtkNotebookPage  *page);
822 
823 /*** GObject Methods ***/
824 static void gtk_notebook_set_property        (GObject         *object,
825                                               guint            prop_id,
826                                               const GValue    *value,
827                                               GParamSpec      *pspec);
828 static void gtk_notebook_get_property        (GObject         *object,
829                                               guint            prop_id,
830                                               GValue          *value,
831                                               GParamSpec      *pspec);
832 static void gtk_notebook_finalize            (GObject         *object);
833 static void gtk_notebook_dispose             (GObject         *object);
834 
835 /*** GtkWidget Methods ***/
836 static void gtk_notebook_unmap               (GtkWidget        *widget);
837 static void gtk_notebook_popup_menu          (GtkWidget        *widget,
838                                               const char       *action_name,
839                                               GVariant         *parameters);
840 static void gtk_notebook_motion              (GtkEventController *controller,
841                                              double              x,
842                                               double              y,
843                                               gpointer            user_data);
844 static void gtk_notebook_state_flags_changed (GtkWidget          *widget,
845                                               GtkStateFlags       previous_state);
846 static void gtk_notebook_direction_changed   (GtkWidget        *widget,
847                                               GtkTextDirection  previous_direction);
848 static gboolean gtk_notebook_focus           (GtkWidget        *widget,
849                                               GtkDirectionType  direction);
850 static gboolean gtk_notebook_grab_focus      (GtkWidget        *widget);
851 static void     gtk_notebook_set_focus_child (GtkWidget        *widget,
852                                               GtkWidget        *child);
853 
854 /*** Drag and drop Methods ***/
855 static void gtk_notebook_dnd_finished_cb     (GdkDrag          *drag,
856                                               GtkWidget        *widget);
857 static void gtk_notebook_drag_cancel_cb      (GdkDrag          *drag,
858                                               GdkDragCancelReason reason,
859                                               GtkWidget        *widget);
860 static GdkDragAction gtk_notebook_drag_motion(GtkDropTarget    *dest,
861                                               double            x,
862                                               double            y,
863                                               GtkNotebook      *notebook);
864 static gboolean gtk_notebook_drag_drop       (GtkDropTarget    *dest,
865                                               const GValue     *value,
866                                               double            x,
867                                               double            y,
868                                               GtkNotebook      *notebook);
869 
870 static void gtk_notebook_remove              (GtkNotebook      *notebook,
871                                               GtkWidget        *widget);
872 
873 /*** GtkNotebook Methods ***/
874 static int gtk_notebook_real_insert_page     (GtkNotebook      *notebook,
875                                               GtkWidget        *child,
876                                               GtkWidget        *tab_label,
877                                               GtkWidget        *menu_label,
878                                               int               position);
879 
880 static GtkNotebook *gtk_notebook_create_window (GtkNotebook    *notebook,
881                                                 GtkWidget      *page);
882 
883 static 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);
890 static void gtk_notebook_allocate_tabs       (GtkGizmo         *gizmo,
891                                               int               width,
892                                               int               height,
893                                               int               baseline);
894 static void     gtk_notebook_snapshot_tabs   (GtkGizmo         *gizmo,
895                                               GtkSnapshot      *snapshot);
896 
897 /*** GtkNotebook Private Functions ***/
898 static void gtk_notebook_real_remove         (GtkNotebook      *notebook,
899                                               GList            *list);
900 static void gtk_notebook_update_labels       (GtkNotebook      *notebook);
901 static int gtk_notebook_timer                (GtkNotebook      *notebook);
902 static void gtk_notebook_set_scroll_timer    (GtkNotebook *notebook);
903 static int gtk_notebook_page_compare         (gconstpointer     a,
904                                               gconstpointer     b);
905 static GList* gtk_notebook_find_child        (GtkNotebook      *notebook,
906                                               GtkWidget        *child);
907 static GList * gtk_notebook_search_page      (GtkNotebook      *notebook,
908                                               GList            *list,
909                                               int               direction,
910                                               gboolean          find_visible);
911 static void  gtk_notebook_child_reordered    (GtkNotebook      *notebook,
912                                               GtkNotebookPage  *page);
913 static int gtk_notebook_insert_notebook_page (GtkNotebook     *notebook,
914                                               GtkNotebookPage *page,
915                                               int              position);
916 
917 /*** GtkNotebook Size Allocate Functions ***/
918 static void gtk_notebook_pages_allocate      (GtkNotebook      *notebook,
919                                               int               width,
920                                               int               height);
921 static 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 ***/
928 static void gtk_notebook_real_switch_page    (GtkNotebook      *notebook,
929                                               GtkWidget        *child,
930                                               guint             page_num);
931 
932 /*** GtkNotebook Page Switch Functions ***/
933 static void gtk_notebook_switch_page         (GtkNotebook      *notebook,
934                                               GtkNotebookPage  *page);
935 static int gtk_notebook_page_select          (GtkNotebook      *notebook,
936                                               gboolean          move_focus);
937 static void gtk_notebook_switch_focus_tab    (GtkNotebook      *notebook,
938                                               GList            *new_child);
939 static void gtk_notebook_menu_switch_page    (GtkWidget        *widget,
940                                               GtkNotebookPage  *page);
941 
942 /*** GtkNotebook Menu Functions ***/
943 static void gtk_notebook_menu_item_create    (GtkNotebook      *notebook,
944                                               GtkNotebookPage  *page);
945 static void gtk_notebook_menu_item_recreate  (GtkNotebook      *notebook,
946                                               GList            *list);
947 static void gtk_notebook_menu_label_unparent (GtkWidget        *widget);
948 
949 static void gtk_notebook_update_tab_pos      (GtkNotebook      *notebook);
950 
951 /*** GtkNotebook Private Setters ***/
952 static gboolean gtk_notebook_mnemonic_activate_switch_page (GtkWidget *child,
953                                                             gboolean overload,
954                                                             gpointer data);
955 
956 static gboolean focus_tabs_in  (GtkNotebook      *notebook);
957 static gboolean focus_child_in (GtkNotebook      *notebook,
958                                 GtkDirectionType  direction);
959 
960 static void stop_scrolling (GtkNotebook *notebook);
961 static void do_detach_tab  (GtkNotebook *from,
962                             GtkNotebook *to,
963                             GtkWidget   *child);
964 
965 /* GtkBuildable */
966 static void gtk_notebook_buildable_init           (GtkBuildableIface *iface);
967 static void gtk_notebook_buildable_add_child      (GtkBuildable *buildable,
968                                                    GtkBuilder   *builder,
969                                                    GObject      *child,
970                                                    const char   *type);
971 
972 static void gtk_notebook_gesture_pressed (GtkGestureClick *gesture,
973                                           int                   n_press,
974                                           double                x,
975                                           double                y,
976                                           gpointer              user_data);
977 static void gtk_notebook_gesture_released (GtkGestureClick *gesture,
978                                            int                   n_press,
979                                            double                x,
980                                            double                y,
981                                            gpointer              user_data);
982 static void gtk_notebook_gesture_cancel   (GtkGestureClick  *gesture,
983                                            GdkEventSequence *sequence,
984                                            GtkNotebook      *notebook);
985 
986 static guint notebook_signals[LAST_SIGNAL] = { 0 };
987 
G_DEFINE_TYPE_WITH_CODE(GtkNotebook,gtk_notebook,GTK_TYPE_WIDGET,G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,gtk_notebook_buildable_init))988 G_DEFINE_TYPE_WITH_CODE (GtkNotebook, gtk_notebook, GTK_TYPE_WIDGET,
989                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
990                                                 gtk_notebook_buildable_init))
991 
992 static void
993 add_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, modifiers,
999                                        "move_focus_out",
1000                                        "(i)", direction);
1001   gtk_widget_class_add_binding_signal (widget_class,
1002                                        GDK_KEY_KP_Tab, modifiers,
1003                                        "move_focus_out",
1004                                        "(i)", direction);
1005 }
1006 
1007 static void
add_arrow_bindings(GtkWidgetClass * widget_class,guint keysym,GtkDirectionType direction)1008 add_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                                        keysym, GDK_CONTROL_MASK,
1016                                        "move_focus_out",
1017                                        "(i)", direction);
1018   gtk_widget_class_add_binding_signal (widget_class,
1019                                        keypad_keysym, GDK_CONTROL_MASK,
1020                                        "move_focus_out",
1021                                        "(i)", direction);
1022 }
1023 
1024 static void
add_reorder_bindings(GtkWidgetClass * widget_class,guint keysym,GtkDirectionType direction,gboolean move_to_last)1025 add_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                                        keysym, GDK_ALT_MASK,
1034                                        "reorder_tab",
1035                                        "(ib)", direction, move_to_last);
1036   gtk_widget_class_add_binding_signal (widget_class,
1037                                        keypad_keysym, GDK_ALT_MASK,
1038                                        "reorder_tab",
1039                                        "(ib)", direction, move_to_last);
1040 }
1041 
1042 static gboolean
gtk_object_handled_accumulator(GSignalInvocationHint * ihint,GValue * return_accu,const GValue * handler_return,gpointer dummy)1043 gtk_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 (handler_return);
1052   g_value_set_object (return_accu, object);
1053   continue_emission = !object;
1054 
1055   return continue_emission;
1056 }
1057 
1058 static void
gtk_notebook_compute_expand(GtkWidget * widget,gboolean * hexpand_p,gboolean * vexpand_p)1059 gtk_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 (page->child, GTK_ORIENTATION_HORIZONTAL);
1078 
1079       vexpand = vexpand ||
1080         gtk_widget_compute_expand (page->child, GTK_ORIENTATION_VERTICAL);
1081 
1082       if (hexpand & vexpand)
1083         break;
1084     }
1085 
1086   *hexpand_p = hexpand;
1087   *vexpand_p = vexpand;
1088 }
1089 
1090 static void
gtk_notebook_class_init(GtkNotebookClass * class)1091 gtk_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 ("page",
1126                         P_("Page"),
1127                         P_("The index of the current page"),
1128                         -1, G_MAXINT,
1129                         -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 ("tab-pos",
1139                          P_("Tab Position"),
1140                          P_("Which side of the notebook holds the tabs"),
1141                          GTK_TYPE_POSITION_TYPE,
1142                          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 ("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 ("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 ("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 ("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 ("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 ("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 (gobject_class, LAST_PROP, 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                   G_SIGNAL_RUN_LAST,
1231                   G_STRUCT_OFFSET (GtkNotebookClass, switch_page),
1232                   NULL, NULL,
1233                   _gtk_marshal_VOID__OBJECT_UINT,
1234                   G_TYPE_NONE, 2,
1235                   GTK_TYPE_WIDGET,
1236                   G_TYPE_UINT);
1237   g_signal_set_va_marshaller (notebook_signals[SWITCH_PAGE],
1238                               G_TYPE_FROM_CLASS (gobject_class),
1239                               _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                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1244                   G_STRUCT_OFFSET (GtkNotebookClass, focus_tab),
1245                   NULL, NULL,
1246                   _gtk_marshal_BOOLEAN__ENUM,
1247                   G_TYPE_BOOLEAN, 1,
1248                   GTK_TYPE_NOTEBOOK_TAB);
1249   g_signal_set_va_marshaller (notebook_signals[FOCUS_TAB],
1250                               G_TYPE_FROM_CLASS (gobject_class),
1251                               _gtk_marshal_BOOLEAN__ENUMv);
1252   notebook_signals[SELECT_PAGE] =
1253     g_signal_new (I_("select-page"),
1254                   G_TYPE_FROM_CLASS (gobject_class),
1255                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1256                   G_STRUCT_OFFSET (GtkNotebookClass, select_page),
1257                   NULL, NULL,
1258                   _gtk_marshal_BOOLEAN__BOOLEAN,
1259                   G_TYPE_BOOLEAN, 1,
1260                   G_TYPE_BOOLEAN);
1261   g_signal_set_va_marshaller (notebook_signals[SELECT_PAGE],
1262                               G_TYPE_FROM_CLASS (gobject_class),
1263                               _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                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1268                   G_STRUCT_OFFSET (GtkNotebookClass, change_current_page),
1269                   NULL, NULL,
1270                   _gtk_marshal_BOOLEAN__INT,
1271                   G_TYPE_BOOLEAN, 1,
1272                   G_TYPE_INT);
1273   g_signal_set_va_marshaller (notebook_signals[CHANGE_CURRENT_PAGE],
1274                               G_TYPE_FROM_CLASS (gobject_class),
1275                               _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                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1280                   G_STRUCT_OFFSET (GtkNotebookClass, move_focus_out),
1281                   NULL, NULL,
1282                   NULL,
1283                   G_TYPE_NONE, 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                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1289                   G_STRUCT_OFFSET (GtkNotebookClass, reorder_tab),
1290                   NULL, NULL,
1291                   _gtk_marshal_BOOLEAN__ENUM_BOOLEAN,
1292                   G_TYPE_BOOLEAN, 2,
1293                   GTK_TYPE_DIRECTION_TYPE,
1294                   G_TYPE_BOOLEAN);
1295   g_signal_set_va_marshaller (notebook_signals[REORDER_TAB],
1296                               G_TYPE_FROM_CLASS (gobject_class),
1297                               _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                   G_SIGNAL_RUN_LAST,
1311                   G_STRUCT_OFFSET (GtkNotebookClass, page_reordered),
1312                   NULL, NULL,
1313                   _gtk_marshal_VOID__OBJECT_UINT,
1314                   G_TYPE_NONE, 2,
1315                   GTK_TYPE_WIDGET,
1316                   G_TYPE_UINT);
1317   g_signal_set_va_marshaller (notebook_signals[PAGE_REORDERED],
1318                               G_TYPE_FROM_CLASS (gobject_class),
1319                               _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                   G_SIGNAL_RUN_LAST,
1333                   G_STRUCT_OFFSET (GtkNotebookClass, page_removed),
1334                   NULL, NULL,
1335                   _gtk_marshal_VOID__OBJECT_UINT,
1336                   G_TYPE_NONE, 2,
1337                   GTK_TYPE_WIDGET,
1338                   G_TYPE_UINT);
1339   g_signal_set_va_marshaller (notebook_signals[PAGE_REMOVED],
1340                               G_TYPE_FROM_CLASS (gobject_class),
1341                               _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                   G_SIGNAL_RUN_LAST,
1355                   G_STRUCT_OFFSET (GtkNotebookClass, page_added),
1356                   NULL, NULL,
1357                   _gtk_marshal_VOID__OBJECT_UINT,
1358                   G_TYPE_NONE, 2,
1359                   GTK_TYPE_WIDGET,
1360                   G_TYPE_UINT);
1361   g_signal_set_va_marshaller (notebook_signals[PAGE_ADDED],
1362                               G_TYPE_FROM_CLASS (gobject_class),
1363                               _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                   G_SIGNAL_RUN_LAST,
1386                   G_STRUCT_OFFSET (GtkNotebookClass, create_window),
1387                   gtk_object_handled_accumulator, NULL,
1388                   _gtk_marshal_OBJECT__OBJECT,
1389                   GTK_TYPE_NOTEBOOK, 1,
1390                   GTK_TYPE_WIDGET);
1391   g_signal_set_va_marshaller (notebook_signals[CREATE_WINDOW],
1392                               G_TYPE_FROM_CLASS (gobject_class),
1393                               _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, "menu.popup", NULL, gtk_notebook_popup_menu);
1401 
1402   gtk_widget_class_add_binding_signal (widget_class,
1403                                        GDK_KEY_space, 0,
1404                                        "select-page",
1405                                        "(b)", FALSE);
1406   gtk_widget_class_add_binding_signal (widget_class,
1407                                        GDK_KEY_KP_Space, 0,
1408                                        "select-page",
1409                                        "(b)", FALSE);
1410 
1411   gtk_widget_class_add_binding_signal (widget_class,
1412                                        GDK_KEY_Home, 0,
1413                                        "focus-tab",
1414                                        "(i)", GTK_NOTEBOOK_TAB_FIRST);
1415   gtk_widget_class_add_binding_signal (widget_class,
1416                                        GDK_KEY_KP_Home, 0,
1417                                        "focus-tab",
1418                                        "(i)", GTK_NOTEBOOK_TAB_FIRST);
1419   gtk_widget_class_add_binding_signal (widget_class,
1420                                        GDK_KEY_End, 0,
1421                                        "focus-tab",
1422                                        "(i)", GTK_NOTEBOOK_TAB_LAST);
1423   gtk_widget_class_add_binding_signal (widget_class,
1424                                        GDK_KEY_KP_End, 0,
1425                                        "focus-tab",
1426                                        "(i)", GTK_NOTEBOOK_TAB_LAST);
1427 
1428   gtk_widget_class_add_binding_action (widget_class,
1429                                        GDK_KEY_F10, GDK_SHIFT_MASK,
1430                                        "menu.popup",
1431                                        NULL);
1432   gtk_widget_class_add_binding_action (widget_class,
1433                                        GDK_KEY_Menu, 0,
1434                                        "menu.popup",
1435                                        NULL);
1436 
1437   gtk_widget_class_add_binding_signal (widget_class,
1438                                        GDK_KEY_Page_Up, GDK_CONTROL_MASK,
1439                                        "change-current-page",
1440                                        "(i)", -1);
1441   gtk_widget_class_add_binding_signal (widget_class,
1442                                        GDK_KEY_Page_Down, GDK_CONTROL_MASK,
1443                                        "change-current-page",
1444                                        "(i)", 1);
1445 
1446   gtk_widget_class_add_binding_signal (widget_class,
1447                                        GDK_KEY_Page_Up, GDK_CONTROL_MASK | GDK_ALT_MASK,
1448                                        "change-current-page",
1449                                        "(i)", -1);
1450   gtk_widget_class_add_binding_signal (widget_class,
1451                                        GDK_KEY_Page_Down, GDK_CONTROL_MASK | GDK_ALT_MASK,
1452                                        "change-current-page",
1453                                        "(i)", 1);
1454 
1455   add_arrow_bindings (widget_class, GDK_KEY_Up, GTK_DIR_UP);
1456   add_arrow_bindings (widget_class, GDK_KEY_Down, GTK_DIR_DOWN);
1457   add_arrow_bindings (widget_class, GDK_KEY_Left, GTK_DIR_LEFT);
1458   add_arrow_bindings (widget_class, GDK_KEY_Right, GTK_DIR_RIGHT);
1459 
1460   add_reorder_bindings (widget_class, GDK_KEY_Up, GTK_DIR_UP, FALSE);
1461   add_reorder_bindings (widget_class, GDK_KEY_Down, GTK_DIR_DOWN, FALSE);
1462   add_reorder_bindings (widget_class, GDK_KEY_Left, GTK_DIR_LEFT, FALSE);
1463   add_reorder_bindings (widget_class, GDK_KEY_Right, GTK_DIR_RIGHT, FALSE);
1464   add_reorder_bindings (widget_class, GDK_KEY_Home, GTK_DIR_LEFT, TRUE);
1465   add_reorder_bindings (widget_class, GDK_KEY_Home, GTK_DIR_UP, TRUE);
1466   add_reorder_bindings (widget_class, GDK_KEY_End, GTK_DIR_RIGHT, TRUE);
1467   add_reorder_bindings (widget_class, GDK_KEY_End, GTK_DIR_DOWN, TRUE);
1468 
1469   add_tab_bindings (widget_class, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
1470   add_tab_bindings (widget_class, GDK_CONTROL_MASK | GDK_SHIFT_MASK, 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, GTK_ACCESSIBLE_ROLE_GROUP);
1475 }
1476 
1477 static void
gtk_notebook_init(GtkNotebook * notebook)1478 gtk_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                                           "css-name", "header",
1511                                           NULL);
1512   gtk_widget_add_css_class (notebook->header_widget, "top");
1513   gtk_widget_hide (notebook->header_widget);
1514   gtk_widget_set_parent (notebook->header_widget, GTK_WIDGET (notebook));
1515 
1516   notebook->tabs_widget = gtk_gizmo_new_with_role ("tabs",
1517                                                    GTK_ACCESSIBLE_ROLE_TAB_LIST,
1518                                                    gtk_notebook_measure_tabs,
1519                                                    gtk_notebook_allocate_tabs,
1520                                                    gtk_notebook_snapshot_tabs,
1521                                                    NULL,
1522                                                    (GtkGizmoFocusFunc)gtk_widget_focus_self,
1523                                                    (GtkGizmoGrabFocusFunc)gtk_widget_grab_focus_self);
1524   gtk_widget_set_hexpand (notebook->tabs_widget, TRUE);
1525   gtk_box_append (GTK_BOX (notebook->header_widget), notebook->tabs_widget);
1526   gtk_accessible_update_property (GTK_ACCESSIBLE (notebook->tabs_widget),
1527                                   GTK_ACCESSIBLE_PROPERTY_LABEL, _("Tab list"),
1528                                   -1);
1529 
1530   notebook->stack_widget = gtk_stack_new ();
1531   gtk_widget_set_hexpand (notebook->stack_widget, TRUE);
1532   gtk_widget_set_vexpand (notebook->stack_widget, TRUE);
1533   gtk_widget_set_parent (notebook->stack_widget, GTK_WIDGET (notebook));
1534 
1535   dest = gtk_drop_target_new (GTK_TYPE_NOTEBOOK_PAGE, GDK_ACTION_MOVE);
1536   gtk_drop_target_set_preload (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), 0);
1543   gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), 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), "frame");
1554 
1555   layout = gtk_widget_get_layout_manager (GTK_WIDGET (notebook));
1556   gtk_orientable_set_orientation (GTK_ORIENTABLE (layout), GTK_ORIENTATION_VERTICAL);
1557 }
1558 
1559 static GtkBuildableIface *parent_buildable_iface;
1560 
1561 static void
gtk_notebook_buildable_init(GtkBuildableIface * iface)1562 gtk_notebook_buildable_init (GtkBuildableIface *iface)
1563 {
1564   parent_buildable_iface = g_type_interface_peek_parent (iface);
1565 
1566   iface->add_child = gtk_notebook_buildable_add_child;
1567 }
1568 
1569 static void
gtk_notebook_buildable_add_child(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const char * type)1570 gtk_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), -1);
1580     }
1581   else if (GTK_IS_WIDGET (child))
1582     {
1583       if (type && strcmp (type, "tab") == 0)
1584         {
1585           GtkWidget * page;
1586 
1587           page = gtk_notebook_get_nth_page (notebook, -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, page))
1593             g_warning ("Overriding tab label for notebook");
1594           gtk_notebook_set_tab_label (notebook, page, GTK_WIDGET (child));
1595         }
1596       else if (type && strcmp (type, "action-start") == 0)
1597         {
1598           gtk_notebook_set_action_widget (notebook, GTK_WIDGET (child), GTK_PACK_START);
1599         }
1600       else if (type && strcmp (type, "action-end") == 0)
1601         {
1602           gtk_notebook_set_action_widget (notebook, GTK_WIDGET (child), 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 
1615 static gboolean
gtk_notebook_has_current_page(GtkNotebook * notebook)1616 gtk_notebook_has_current_page (GtkNotebook *notebook)
1617 {
1618   return notebook->cur_page &&
1619          gtk_widget_get_visible (notebook->cur_page->child);
1620 }
1621 
1622 static gboolean
gtk_notebook_select_page(GtkNotebook * notebook,gboolean move_focus)1623 gtk_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 
1635 static gboolean
gtk_notebook_focus_tab(GtkNotebook * notebook,GtkNotebookTab type)1636 gtk_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, STEP_NEXT, TRUE);
1647           if (list)
1648             gtk_notebook_switch_focus_tab (notebook, list);
1649           break;
1650         case GTK_NOTEBOOK_TAB_LAST:
1651           list = gtk_notebook_search_page (notebook, NULL, STEP_PREV, TRUE);
1652           if (list)
1653             gtk_notebook_switch_focus_tab (notebook, list);
1654           break;
1655 
1656         default:
1657           break;
1658         }
1659 
1660       return TRUE;
1661     }
1662   else
1663     return FALSE;
1664 }
1665 
1666 static gboolean
gtk_notebook_change_current_page(GtkNotebook * notebook,int offset)1667 gtk_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 (notebook->children, notebook->cur_page);
1677 
1678   while (offset != 0)
1679     {
1680       current = gtk_notebook_search_page (notebook, current,
1681                                           offset < 0 ? STEP_PREV : STEP_NEXT,
1682                                           TRUE);
1683 
1684       if (!current)
1685         {
1686           current = gtk_notebook_search_page (notebook, NULL,
1687                                               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, current->data);
1696   else
1697     gtk_widget_error_bell (GTK_WIDGET (notebook));
1698 
1699   return TRUE;
1700 }
1701 
1702 static GtkDirectionType
get_effective_direction(GtkNotebook * notebook,GtkDirectionType direction)1703 get_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 
1730 static GtkPositionType
get_effective_tab_pos(GtkNotebook * notebook)1731 get_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 
1748 static void
gtk_notebook_move_focus_out(GtkNotebook * notebook,GtkDirectionType direction_type)1749 gtk_notebook_move_focus_out (GtkNotebook      *notebook,
1750                              GtkDirectionType  direction_type)
1751 {
1752   GtkDirectionType effective_direction = get_effective_direction (notebook, 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, 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 (toplevel))
1767     return;
1768 
1769   g_object_ref (notebook);
1770 
1771   notebook->focus_out = TRUE;
1772   g_signal_emit_by_name (toplevel, "move-focus", direction_type);
1773   notebook->focus_out = FALSE;
1774 
1775   g_object_unref (notebook);
1776 }
1777 
1778 static int
reorder_tab(GtkNotebook * notebook,GList * position,GList * tab)1779 reorder_tab (GtkNotebook *notebook, GList *position, GList *tab)
1780 {
1781   GList *elem;
1782 
1783   if (position == tab)
1784     return g_list_position (notebook->children, 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 (notebook->children);
1789 
1790   if (elem == tab)
1791     return g_list_position (notebook->children, tab);
1792 
1793   /* now actually reorder the tab */
1794   if (notebook->first_tab == tab)
1795     notebook->first_tab = gtk_notebook_search_page (notebook, notebook->first_tab,
1796                                                     STEP_NEXT, TRUE);
1797 
1798   notebook->children = g_list_remove_link (notebook->children, tab);
1799 
1800   if (!position)
1801     elem = g_list_last (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 (notebook->children, tab);
1817 }
1818 
1819 static gboolean
gtk_notebook_reorder_tab(GtkNotebook * notebook,GtkDirectionType direction_type,gboolean move_to_last)1820 gtk_notebook_reorder_tab (GtkNotebook      *notebook,
1821                           GtkDirectionType  direction_type,
1822                           gboolean          move_to_last)
1823 {
1824   GtkDirectionType effective_direction = get_effective_direction (notebook, 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, last,
1847                                             (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, notebook->focus_tab,
1856                                       (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 (notebook->children, notebook->focus_tab);
1863   if (effective_direction == GTK_DIR_RIGHT)
1864     page_num = reorder_tab (notebook, child->next, notebook->focus_tab);
1865   else
1866     page_num = reorder_tab (notebook, child, notebook->focus_tab);
1867 
1868   gtk_notebook_child_reordered (notebook, 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), "position");
1873     }
1874   g_signal_emit (notebook,
1875                  notebook_signals[PAGE_REORDERED],
1876                  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  */
1890 GtkWidget*
gtk_notebook_new(void)1891 gtk_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  */
1901 static void
gtk_notebook_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1902 gtk_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, g_value_get_boolean (value));
1915       break;
1916     case PROP_SHOW_BORDER:
1917       gtk_notebook_set_show_border (notebook, g_value_get_boolean (value));
1918       break;
1919     case PROP_SCROLLABLE:
1920       gtk_notebook_set_scrollable (notebook, 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, g_value_get_int (value));
1930       break;
1931     case PROP_TAB_POS:
1932       gtk_notebook_set_tab_pos (notebook, g_value_get_enum (value));
1933       break;
1934     case PROP_GROUP_NAME:
1935       gtk_notebook_set_group_name (notebook, 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 
1943 static void
gtk_notebook_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1944 gtk_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, notebook->show_tabs);
1955       break;
1956     case PROP_SHOW_BORDER:
1957       g_value_set_boolean (value, notebook->show_border);
1958       break;
1959     case PROP_SCROLLABLE:
1960       g_value_set_boolean (value, notebook->scrollable);
1961       break;
1962     case PROP_ENABLE_POPUP:
1963       g_value_set_boolean (value, notebook->menu != NULL);
1964       break;
1965     case PROP_PAGE:
1966       g_value_set_int (value, gtk_notebook_get_current_page (notebook));
1967       break;
1968     case PROP_TAB_POS:
1969       g_value_set_enum (value, notebook->tab_pos);
1970       break;
1971     case PROP_GROUP_NAME:
1972       g_value_set_string (value, gtk_notebook_get_group_name (notebook));
1973       break;
1974     case PROP_PAGES:
1975       g_value_take_object (value, 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 
1997 static void
gtk_notebook_finalize(GObject * object)1998 gtk_notebook_finalize (GObject *object)
1999 {
2000   GtkNotebook *notebook = GTK_NOTEBOOK (object);
2001 
2002   gtk_widget_unparent (notebook->header_widget);
2003   gtk_widget_unparent (notebook->stack_widget);
2004 
2005   G_OBJECT_CLASS (gtk_notebook_parent_class)->finalize (object);
2006 }
2007 
2008 static void
gtk_notebook_dispose(GObject * object)2009 gtk_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 (G_LIST_MODEL (notebook->pages), 0, g_list_length (notebook->children), 0);
2016 
2017   while (l != NULL)
2018     {
2019       GtkNotebookPage *page = l->data;
2020       l = l->next;
2021 
2022       gtk_notebook_remove (notebook, page->child);
2023     }
2024 
2025   G_OBJECT_CLASS (gtk_notebook_parent_class)->dispose (object);
2026 }
2027 
2028 static gboolean
gtk_notebook_get_tab_area_position(GtkNotebook * notebook,graphene_rect_t * rectangle)2029 gtk_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 (notebook->header_widget,
2035                                         GTK_WIDGET (notebook),
2036                                         rectangle);
2037     }
2038   else
2039     {
2040       graphene_rect_init_from_rect (rectangle, graphene_rect_zero ());
2041     }
2042 
2043   return FALSE;
2044 }
2045 
2046 static void
gtk_notebook_unmap(GtkWidget * widget)2047 gtk_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 
2056 static void
gtk_notebook_distribute_arrow_width(GtkNotebook * notebook,GtkPackType type,int size,int * out_left,int * out_right)2057 gtk_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 (notebook->arrow_widget[2 * type],
2081                           GTK_ORIENTATION_HORIZONTAL,
2082                           -1,
2083                           &sizes[0].minimum_size, &sizes[0].natural_size,
2084                           NULL, NULL);
2085       gtk_widget_measure (notebook->arrow_widget[2 * type + 1],
2086                           GTK_ORIENTATION_HORIZONTAL,
2087                           -1,
2088                           &sizes[1].minimum_size, &sizes[1].natural_size,
2089                           NULL, NULL);
2090 
2091       size -= sizes[0].minimum_size + sizes[1].minimum_size;
2092       size = gtk_distribute_natural_allocation (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 
2099 static void
gtk_notebook_measure_arrows(GtkNotebook * notebook,GtkPackType type,GtkOrientation orientation,int for_size,int * minimum,int * natural,int * minimum_baseline,int * natural_baseline)2100 gtk_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 (notebook->arrow_widget[2 * type],
2117                               orientation,
2118                               for_size,
2119                               &child1_min, &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 (notebook->arrow_widget[2 * type + 1],
2129                               orientation,
2130                               for_size,
2131                               &child2_min, &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, for_size, &child1_size, &child2_size);
2151       else
2152         child1_size = child2_size = for_size;
2153 
2154       if (notebook->arrow_widget[2 * type])
2155         {
2156           gtk_widget_measure (notebook->arrow_widget[2 * type],
2157                               orientation,
2158                               child1_size,
2159                               &child1_min, &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 (notebook->arrow_widget[2 * type + 1],
2169                               orientation,
2170                               child2_size,
2171                               &child2_min, &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 
2187 static void
gtk_notebook_get_preferred_tabs_size(GtkNotebook * notebook,GtkRequisition * requisition)2188 gtk_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 (page->child))
2205         {
2206           vis_pages++;
2207 
2208           if (!gtk_widget_get_visible (page->tab_label))
2209             gtk_widget_show (page->tab_label);
2210 
2211           gtk_widget_measure (page->tab_widget,
2212                               GTK_ORIENTATION_HORIZONTAL,
2213                               -1,
2214                               &page->requisition.width, NULL,
2215                               NULL, NULL);
2216           gtk_widget_measure (page->tab_widget,
2217                               GTK_ORIENTATION_VERTICAL,
2218                               page->requisition.width,
2219                               &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 (page->tab_label))
2240         gtk_widget_hide (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                                            GTK_PACK_START,
2259                                            GTK_ORIENTATION_VERTICAL,
2260                                            -1,
2261                                            &arrow_height, &unused,
2262                                            NULL, NULL);
2263               tab_height = MAX (tab_height, arrow_height);
2264               gtk_notebook_measure_arrows (notebook,
2265                                            GTK_PACK_END,
2266                                            GTK_ORIENTATION_VERTICAL,
2267                                            -1,
2268                                            &arrow_height, &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 (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                                            GTK_PACK_START,
2291                                            GTK_ORIENTATION_HORIZONTAL,
2292                                            tab_height,
2293                                            &start_arrow_width, &unused,
2294                                            NULL, NULL);
2295               gtk_notebook_measure_arrows (notebook,
2296                                            GTK_PACK_END,
2297                                            GTK_ORIENTATION_HORIZONTAL,
2298                                            tab_height,
2299                                            &end_arrow_width, &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                                            GTK_PACK_START,
2319                                            GTK_ORIENTATION_HORIZONTAL,
2320                                            -1,
2321                                            &arrow_width, &unused,
2322                                            NULL, NULL);
2323               tab_width = MAX (tab_width, arrow_width);
2324               gtk_notebook_measure_arrows (notebook,
2325                                            GTK_PACK_END,
2326                                            GTK_ORIENTATION_HORIZONTAL,
2327                                            -1,
2328                                            &arrow_width, &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 (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                                            GTK_PACK_START,
2352                                            GTK_ORIENTATION_VERTICAL,
2353                                            tab_width,
2354                                            &start_arrow_height, &unused,
2355                                            NULL, NULL);
2356               gtk_notebook_measure_arrows (notebook,
2357                                            GTK_PACK_END,
2358                                            GTK_ORIENTATION_VERTICAL,
2359                                            tab_width,
2360                                            &end_arrow_height, &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 
2383 static void
gtk_notebook_measure_tabs(GtkGizmo * gizmo,GtkOrientation orientation,int size,int * minimum,int * natural,int * minimum_baseline,int * natural_baseline)2384 gtk_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, &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 
2409 static void
gtk_notebook_allocate_tabs(GtkGizmo * gizmo,int width,int height,int baseline)2410 gtk_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 
2421 static gboolean
gtk_notebook_show_arrows(GtkNotebook * notebook)2422 gtk_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 (page->tab_widget))
2435         return TRUE;
2436 
2437       children = children->next;
2438     }
2439 
2440   return FALSE;
2441 }
2442 
2443 static GtkNotebookArrow
gtk_notebook_get_arrow(GtkNotebook * notebook,int x,int y)2444 gtk_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 (notebook->arrow_widget[i],
2460                                           GTK_WIDGET (notebook),
2461                                           &arrow_bounds))
2462             continue;
2463 
2464           if (graphene_rect_contains_point (&arrow_bounds,
2465                                             &(graphene_point_t){x, y}))
2466             return i;
2467         }
2468     }
2469 
2470   return ARROW_NONE;
2471 }
2472 
2473 static void
gtk_notebook_do_arrow(GtkNotebook * notebook,GtkNotebookArrow arrow)2474 gtk_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, notebook->focus_tab,
2486                                 left ? STEP_PREV : STEP_NEXT,
2487                                 TRUE))
2488     {
2489       gtk_notebook_change_current_page (notebook, left ? -1 : 1);
2490       gtk_widget_grab_focus (widget);
2491     }
2492 }
2493 
2494 static gboolean
gtk_notebook_arrow_button_press(GtkNotebook * notebook,GtkNotebookArrow arrow,int button)2495 gtk_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                                    gtk_notebook_search_page (notebook,
2523                                                              NULL,
2524                                                              left ? STEP_NEXT : STEP_PREV,
2525                                                              TRUE));
2526 
2527   return TRUE;
2528 }
2529 
2530 static gboolean
gtk_notebook_page_tab_label_is_visible(GtkNotebookPage * page)2531 gtk_notebook_page_tab_label_is_visible (GtkNotebookPage *page)
2532 {
2533   return page->tab_label &&
2534          gtk_widget_get_visible (page->tab_widget) &&
2535          gtk_widget_get_child_visible (page->tab_widget) &&
2536          gtk_widget_get_visible (page->tab_label) &&
2537          gtk_widget_get_child_visible (page->tab_label);
2538 }
2539 
2540 static gboolean
in_tabs(GtkNotebook * notebook,double x,double y)2541 in_tabs (GtkNotebook *notebook,
2542          double       x,
2543          double       y)
2544 {
2545   graphene_rect_t tabs_bounds;
2546 
2547   if (!gtk_widget_compute_bounds (notebook->tabs_widget, GTK_WIDGET (notebook), &tabs_bounds))
2548     return FALSE;
2549 
2550   return graphene_rect_contains_point (&tabs_bounds,
2551                                        &(graphene_point_t){x, y});
2552 }
2553 
2554 static GList*
get_tab_at_pos(GtkNotebook * notebook,double x,double y)2555 get_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 (page->tab_widget, GTK_WIDGET (notebook), &bounds))
2572         continue;
2573 
2574       if (graphene_rect_contains_point (&bounds, &(graphene_point_t){x, y}))
2575         return children;
2576     }
2577 
2578   return NULL;
2579 }
2580 
2581 static void
gtk_notebook_gesture_pressed(GtkGestureClick * gesture,int n_press,double x,double y,gpointer user_data)2582 gtk_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       return;
2609     }
2610 
2611   if (in_tabs (notebook, x, y) && notebook->menu && gdk_event_triggers_context_menu (event))
2612     {
2613       GdkRectangle rect;
2614 
2615       rect.x = x;
2616       rect.y = y;
2617       rect.width = 1;
2618       rect.height = 1;
2619       gtk_popover_set_pointing_to (GTK_POPOVER (notebook->menu), &rect);
2620       gtk_popover_popup (GTK_POPOVER (notebook->menu));
2621       return;
2622     }
2623 
2624   if (button != GDK_BUTTON_PRIMARY)
2625     return;
2626 
2627   if ((tab = get_tab_at_pos (notebook, x, y)) != NULL)
2628     {
2629       gboolean page_changed, was_focus;
2630 
2631       page = tab->data;
2632       page_changed = page != notebook->cur_page;
2633       was_focus = gtk_widget_is_focus (widget);
2634 
2635       gtk_notebook_switch_focus_tab (notebook, tab);
2636       gtk_widget_grab_focus (widget);
2637 
2638       if (page_changed && !was_focus)
2639         gtk_widget_child_focus (page->child, GTK_DIR_TAB_FORWARD);
2640 
2641       /* save press to possibly begin a drag */
2642       if (page->reorderable || page->detachable)
2643         {
2644           graphene_rect_t tab_bounds;
2645 
2646           notebook->pressed_button = button;
2647 
2648           notebook->mouse_x = x;
2649           notebook->mouse_y = y;
2650 
2651           notebook->drag_begin_x = notebook->mouse_x;
2652           notebook->drag_begin_y = notebook->mouse_y;
2653 
2654           /* tab bounds get set to empty, which is fine */
2655           notebook->drag_offset_x = notebook->drag_begin_x;
2656           notebook->drag_offset_y = notebook->drag_begin_y;
2657           if (gtk_widget_compute_bounds (page->tab_widget, GTK_WIDGET (notebook), &tab_bounds))
2658             {
2659               notebook->drag_offset_x -= tab_bounds.origin.x;
2660               notebook->drag_offset_y -= tab_bounds.origin.y;
2661             }
2662         }
2663     }
2664 }
2665 
2666 static void
gtk_notebook_popup_menu(GtkWidget * widget,const char * action_name,GVariant * parameters)2667 gtk_notebook_popup_menu (GtkWidget  *widget,
2668                          const char *action_name,
2669                          GVariant   *parameters)
2670 {
2671   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2672 
2673   if (notebook->menu)
2674     gtk_popover_popup (GTK_POPOVER (notebook->menu));
2675 }
2676 
2677 static void
stop_scrolling(GtkNotebook * notebook)2678 stop_scrolling (GtkNotebook *notebook)
2679 {
2680 
2681   if (notebook->timer)
2682     {
2683       g_source_remove (notebook->timer);
2684       notebook->timer = 0;
2685       notebook->need_timer = FALSE;
2686     }
2687   notebook->click_child = ARROW_NONE;
2688   notebook->pressed_button = 0;
2689 }
2690 
2691 static GList*
get_drop_position(GtkNotebook * notebook)2692 get_drop_position (GtkNotebook *notebook)
2693 {
2694   GList *children, *last_child;
2695   GtkNotebookPage *page;
2696   gboolean is_rtl;
2697   int x, y;
2698 
2699   x = notebook->mouse_x;
2700   y = notebook->mouse_y;
2701 
2702   is_rtl = gtk_widget_get_direction ((GtkWidget *) notebook) == GTK_TEXT_DIR_RTL;
2703   last_child = NULL;
2704 
2705   for (children = notebook->children; children; children = children->next)
2706     {
2707       page = children->data;
2708 
2709       if ((notebook->operation != DRAG_OPERATION_REORDER || page != notebook->cur_page) &&
2710           gtk_widget_get_visible (page->child) &&
2711           page->tab_label &&
2712           gtk_widget_get_mapped (page->tab_label))
2713         {
2714           graphene_rect_t tab_bounds;
2715 
2716           if (!gtk_widget_compute_bounds (page->tab_widget, GTK_WIDGET (notebook), &tab_bounds))
2717             continue;
2718 
2719           switch (notebook->tab_pos)
2720             {
2721             case GTK_POS_TOP:
2722             case GTK_POS_BOTTOM:
2723               if (!is_rtl)
2724                 {
2725                   if (tab_bounds.origin.x + tab_bounds.size.width / 2 > x)
2726                     return children;
2727                 }
2728               else
2729                 {
2730                   if (tab_bounds.origin.x + tab_bounds.size.width / 2 < x)
2731                     return children;
2732                 }
2733               break;
2734 
2735             case GTK_POS_LEFT:
2736             case GTK_POS_RIGHT:
2737               if (tab_bounds.origin.y + tab_bounds.size.height / 2 > y)
2738                 return children;
2739               break;
2740 
2741             default:
2742               g_assert_not_reached ();
2743               break;
2744             }
2745 
2746           last_child = children->next;
2747         }
2748     }
2749 
2750   return last_child;
2751 }
2752 
2753 static void
tab_drag_begin(GtkNotebook * notebook,GtkNotebookPage * page)2754 tab_drag_begin (GtkNotebook     *notebook,
2755                 GtkNotebookPage *page)
2756 {
2757   gtk_widget_add_css_class (page->tab_widget, "dnd");
2758 }
2759 
2760 /* This function undoes the reparenting that happens both when drag_surface
2761  * is shown for reordering and when the DnD icon is shown for detaching
2762  */
2763 static void
tab_drag_end(GtkNotebook * notebook,GtkNotebookPage * page)2764 tab_drag_end (GtkNotebook     *notebook,
2765               GtkNotebookPage *page)
2766 {
2767   if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
2768     {
2769       g_object_ref (page->tab_label);
2770       gtk_box_remove (GTK_BOX (gtk_widget_get_parent (page->tab_label)), page->tab_label);
2771       gtk_widget_set_parent (page->tab_label, page->tab_widget);
2772       g_object_unref (page->tab_label);
2773     }
2774 
2775   gtk_widget_remove_css_class (page->tab_widget, "dnd");
2776 }
2777 
2778 static void
gtk_notebook_stop_reorder(GtkNotebook * notebook)2779 gtk_notebook_stop_reorder (GtkNotebook *notebook)
2780 {
2781   GtkNotebookPage *page;
2782 
2783   if (notebook->operation == DRAG_OPERATION_DETACH)
2784     page = notebook->detached_tab;
2785   else
2786     page = notebook->cur_page;
2787 
2788   if (!page || !page->tab_label)
2789     return;
2790 
2791   notebook->pressed_button = 0;
2792 
2793   if (page->reorderable || page->detachable)
2794     {
2795       if (notebook->operation == DRAG_OPERATION_REORDER)
2796         {
2797           int old_page_num, page_num, i;
2798           GList *element;
2799 
2800           element = get_drop_position (notebook);
2801           old_page_num = g_list_position (notebook->children, notebook->focus_tab);
2802           page_num = reorder_tab (notebook, element, notebook->focus_tab);
2803           gtk_notebook_child_reordered (notebook, page);
2804 
2805           if (notebook->has_scrolled || old_page_num != page_num)
2806             {
2807               for (element = notebook->children, i = 0; element; element = element->next, i++)
2808                 {
2809                   if (MIN (old_page_num, page_num) <= i && i <= MAX (old_page_num, page_num))
2810                     g_object_notify (G_OBJECT (element->data), "position");
2811                 }
2812               g_signal_emit (notebook,
2813                              notebook_signals[PAGE_REORDERED], 0,
2814                              page->child, page_num);
2815             }
2816         }
2817 
2818       notebook->has_scrolled = FALSE;
2819 
2820       tab_drag_end (notebook, page);
2821 
2822       notebook->operation = DRAG_OPERATION_NONE;
2823 
2824       if (notebook->dnd_timer)
2825         {
2826           g_source_remove (notebook->dnd_timer);
2827           notebook->dnd_timer = 0;
2828         }
2829 
2830       gtk_widget_queue_allocate (GTK_WIDGET (notebook));
2831     }
2832 }
2833 
2834 static void
gtk_notebook_gesture_released(GtkGestureClick * gesture,int n_press,double x,double y,gpointer user_data)2835 gtk_notebook_gesture_released (GtkGestureClick *gesture,
2836                                int              n_press,
2837                                double           x,
2838                                double           y,
2839                                gpointer         user_data)
2840 {
2841   GtkNotebook *notebook = user_data;
2842   GdkEventSequence *sequence;
2843   GdkEvent *event;
2844   guint button;
2845 
2846   sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
2847   button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
2848   event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
2849 
2850   if (!event)
2851     return;
2852 
2853   if (notebook->pressed_button != button)
2854     return;
2855 
2856   if (notebook->operation == DRAG_OPERATION_REORDER &&
2857       notebook->cur_page &&
2858       notebook->cur_page->reorderable)
2859     gtk_notebook_stop_reorder (notebook);
2860 
2861   stop_scrolling (notebook);
2862 }
2863 
2864 static void
gtk_notebook_gesture_cancel(GtkGestureClick * gesture,GdkEventSequence * sequence,GtkNotebook * notebook)2865 gtk_notebook_gesture_cancel (GtkGestureClick  *gesture,
2866                              GdkEventSequence *sequence,
2867                              GtkNotebook      *notebook)
2868 {
2869   gtk_notebook_stop_reorder (notebook);
2870   stop_scrolling (notebook);
2871 }
2872 
2873 static GtkNotebookPointerPosition
get_pointer_position(GtkNotebook * notebook)2874 get_pointer_position (GtkNotebook *notebook)
2875 {
2876   GtkWidget *widget = GTK_WIDGET (notebook);
2877   graphene_rect_t area;
2878   int width, height;
2879   gboolean is_rtl;
2880 
2881   if (!notebook->scrollable)
2882     return POINTER_BETWEEN;
2883 
2884   gtk_notebook_get_tab_area_position (notebook, &area);
2885   width = area.size.width;
2886   height = area.size.height;
2887 
2888   if (notebook->tab_pos == GTK_POS_TOP ||
2889       notebook->tab_pos == GTK_POS_BOTTOM)
2890     {
2891       int x = notebook->mouse_x;
2892 
2893       is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2894 
2895       if (x > width - SCROLL_THRESHOLD)
2896         return (is_rtl) ? POINTER_BEFORE : POINTER_AFTER;
2897       else if (x < SCROLL_THRESHOLD)
2898         return (is_rtl) ? POINTER_AFTER : POINTER_BEFORE;
2899       else
2900         return POINTER_BETWEEN;
2901     }
2902   else
2903     {
2904       int y = notebook->mouse_y;
2905 
2906       if (y > height - SCROLL_THRESHOLD)
2907         return POINTER_AFTER;
2908       else if (y < SCROLL_THRESHOLD)
2909         return POINTER_BEFORE;
2910       else
2911         return POINTER_BETWEEN;
2912     }
2913 }
2914 
2915 static gboolean
scroll_notebook_timer(gpointer data)2916 scroll_notebook_timer (gpointer data)
2917 {
2918   GtkNotebook *notebook = GTK_NOTEBOOK (data);
2919   GtkNotebookPointerPosition pointer_position;
2920   GList *element, *first_tab;
2921 
2922   pointer_position = get_pointer_position (notebook);
2923 
2924   element = get_drop_position (notebook);
2925   reorder_tab (notebook, element, notebook->focus_tab);
2926   first_tab = gtk_notebook_search_page (notebook, notebook->first_tab,
2927                                         (pointer_position == POINTER_BEFORE) ? STEP_PREV : STEP_NEXT,
2928                                         TRUE);
2929   if (first_tab && notebook->cur_page)
2930     {
2931       notebook->first_tab = first_tab;
2932 
2933       gtk_widget_queue_allocate (notebook->tabs_widget);
2934     }
2935 
2936   return TRUE;
2937 }
2938 
2939 static gboolean
check_threshold(GtkNotebook * notebook,int current_x,int current_y)2940 check_threshold (GtkNotebook *notebook,
2941                  int          current_x,
2942                  int          current_y)
2943 {
2944   int dnd_threshold;
2945   graphene_rect_t rectangle;
2946   GtkSettings *settings;
2947 
2948   settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
2949   g_object_get (G_OBJECT (settings), "gtk-dnd-drag-threshold", &dnd_threshold, NULL);
2950 
2951   /* we want a large threshold */
2952   dnd_threshold *= DND_THRESHOLD_MULTIPLIER;
2953 
2954   gtk_notebook_get_tab_area_position (notebook, &rectangle);
2955   graphene_rect_inset (&rectangle, -dnd_threshold, -dnd_threshold);
2956 
2957   /* The negation here is important! */
2958   return !graphene_rect_contains_point (&rectangle, &(graphene_point_t){current_x, current_y});
2959 }
2960 
2961 static void
gtk_notebook_motion(GtkEventController * controller,double x,double y,gpointer user_data)2962 gtk_notebook_motion (GtkEventController *controller,
2963                      double              x,
2964                      double              y,
2965                      gpointer            user_data)
2966 {
2967   GtkWidget *widget = GTK_WIDGET (user_data);
2968   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2969   GtkNotebookPage *page;
2970   guint state;
2971 
2972   page = notebook->cur_page;
2973   if (!page)
2974     return;
2975 
2976   state = gtk_event_controller_get_current_event_state (controller);
2977 
2978   if (!(state & GDK_BUTTON1_MASK) &&
2979       notebook->pressed_button != 0)
2980     {
2981       gtk_notebook_stop_reorder (notebook);
2982       stop_scrolling (notebook);
2983     }
2984 
2985   notebook->mouse_x = x;
2986   notebook->mouse_y = y;
2987 
2988   if (notebook->pressed_button == 0)
2989     return;
2990 
2991   if (page->detachable &&
2992       check_threshold (notebook, notebook->mouse_x, notebook->mouse_y))
2993     {
2994       GdkSurface *surface;
2995       GdkDevice *device;
2996       GdkContentProvider *content;
2997       GdkDrag *drag;
2998       GdkPaintable *paintable;
2999 
3000       notebook->detached_tab = notebook->cur_page;
3001 
3002       surface = gtk_native_get_surface (gtk_widget_get_native (GTK_WIDGET (notebook)));
3003       device = gtk_event_controller_get_current_event_device (controller);
3004 
3005       content = gdk_content_provider_new_union ((GdkContentProvider *[2]) {
3006                                                   gtk_notebook_root_content_new (notebook),
3007                                                   gdk_content_provider_new_typed (GTK_TYPE_NOTEBOOK_PAGE, notebook->cur_page)
3008                                                 }, 2);
3009       drag = gdk_drag_begin (surface, device, content, GDK_ACTION_MOVE, notebook->drag_begin_x, notebook->drag_begin_y);
3010       g_object_unref (content);
3011 
3012       g_signal_connect (drag, "dnd-finished", G_CALLBACK (gtk_notebook_dnd_finished_cb), notebook);
3013       g_signal_connect (drag, "cancel", G_CALLBACK (gtk_notebook_drag_cancel_cb), notebook);
3014 
3015       paintable = gtk_widget_paintable_new (notebook->detached_tab->tab_widget);
3016       gtk_drag_icon_set_from_paintable (drag, paintable, -2, -2);
3017       g_object_unref (paintable);
3018 
3019       if (notebook->dnd_timer)
3020         {
3021           g_source_remove (notebook->dnd_timer);
3022           notebook->dnd_timer = 0;
3023         }
3024 
3025       notebook->operation = DRAG_OPERATION_DETACH;
3026       tab_drag_end (notebook, notebook->cur_page);
3027 
3028       g_object_set_data (G_OBJECT (drag), "gtk-notebook-drag-origin", notebook);
3029 
3030       g_object_unref (drag);
3031 
3032       return;
3033     }
3034 
3035   if (page->reorderable &&
3036       (notebook->operation == DRAG_OPERATION_REORDER ||
3037        gtk_drag_check_threshold_double (widget,
3038                                         notebook->drag_begin_x,
3039                                         notebook->drag_begin_y,
3040                                         notebook->mouse_x,
3041                                         notebook->mouse_y)))
3042     {
3043       GtkNotebookPointerPosition pointer_position = get_pointer_position (notebook);
3044 
3045       if (pointer_position != POINTER_BETWEEN &&
3046           gtk_notebook_show_arrows (notebook))
3047         {
3048           /* scroll tabs */
3049           if (!notebook->dnd_timer)
3050             {
3051               notebook->has_scrolled = TRUE;
3052               notebook->dnd_timer = g_timeout_add (TIMEOUT_REPEAT * SCROLL_DELAY_FACTOR,
3053                                                scroll_notebook_timer,
3054                                                notebook);
3055               gdk_source_set_static_name_by_id (notebook->dnd_timer, "[gtk] scroll_notebook_timer");
3056             }
3057         }
3058       else
3059         {
3060           if (notebook->dnd_timer)
3061             {
3062               g_source_remove (notebook->dnd_timer);
3063               notebook->dnd_timer = 0;
3064             }
3065         }
3066 
3067       if (notebook->operation != DRAG_OPERATION_REORDER)
3068         {
3069           notebook->operation = DRAG_OPERATION_REORDER;
3070           tab_drag_begin (notebook, page);
3071         }
3072     }
3073 
3074   if (notebook->operation == DRAG_OPERATION_REORDER)
3075     gtk_widget_queue_allocate (notebook->tabs_widget);
3076 }
3077 
3078 static void
update_arrow_state(GtkNotebook * notebook)3079 update_arrow_state (GtkNotebook *notebook)
3080 {
3081   int i;
3082   gboolean is_rtl, left;
3083 
3084   is_rtl = gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_RTL;
3085 
3086   for (i = 0; i < 4; i++)
3087     {
3088       gboolean sensitive = TRUE;
3089 
3090       if (notebook->arrow_widget[i] == NULL)
3091         continue;
3092 
3093       left = (ARROW_IS_LEFT (i) && !is_rtl) ||
3094              (!ARROW_IS_LEFT (i) && is_rtl);
3095 
3096       if (notebook->focus_tab &&
3097           !gtk_notebook_search_page (notebook, notebook->focus_tab,
3098                                      left ? STEP_PREV : STEP_NEXT, TRUE))
3099         {
3100           sensitive = FALSE;
3101         }
3102 
3103       gtk_widget_set_sensitive (notebook->arrow_widget[i], sensitive);
3104     }
3105 }
3106 
3107 static void
gtk_notebook_state_flags_changed(GtkWidget * widget,GtkStateFlags previous_state)3108 gtk_notebook_state_flags_changed (GtkWidget     *widget,
3109                                   GtkStateFlags  previous_state)
3110 {
3111   if (!gtk_widget_is_sensitive (widget))
3112     stop_scrolling (GTK_NOTEBOOK (widget));
3113 }
3114 
3115 static void
gtk_notebook_arrow_drag_enter(GtkDropControllerMotion * motion,double x,double y,GtkNotebook * notebook)3116 gtk_notebook_arrow_drag_enter (GtkDropControllerMotion *motion,
3117                                double                   x,
3118                                double                   y,
3119                                GtkNotebook             *notebook)
3120 {
3121   GtkWidget *arrow_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (motion));
3122   guint arrow;
3123 
3124   for (arrow = 0; arrow < 4; arrow++)
3125     {
3126       if (notebook->arrow_widget[arrow] == arrow_widget)
3127         break;
3128     }
3129 
3130   g_assert (arrow != ARROW_NONE);
3131 
3132   notebook->click_child = arrow;
3133   gtk_notebook_set_scroll_timer (notebook);
3134 }
3135 
3136 static void
gtk_notebook_arrow_drag_leave(GtkDropTarget * target,GdkDrop * drop,GtkNotebook * notebook)3137 gtk_notebook_arrow_drag_leave (GtkDropTarget *target,
3138                                GdkDrop       *drop,
3139                                GtkNotebook   *notebook)
3140 {
3141   stop_scrolling (notebook);
3142 }
3143 
3144 static void
update_arrow_nodes(GtkNotebook * notebook)3145 update_arrow_nodes (GtkNotebook *notebook)
3146 {
3147   gboolean arrow[4];
3148   const char *up_icon_name;
3149   const char *down_icon_name;
3150   int i;
3151 
3152   if (notebook->tab_pos == GTK_POS_LEFT ||
3153       notebook->tab_pos == GTK_POS_RIGHT)
3154     {
3155       up_icon_name = "pan-down-symbolic";
3156       down_icon_name = "pan-up-symbolic";
3157     }
3158   else if (gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_LTR)
3159     {
3160       up_icon_name = "pan-end-symbolic";
3161       down_icon_name = "pan-start-symbolic";
3162     }
3163   else
3164     {
3165       up_icon_name = "pan-start-symbolic";
3166       down_icon_name = "pan-end-symbolic";
3167     }
3168 
3169   arrow[0] = TRUE;
3170   arrow[1] = FALSE;
3171   arrow[2] = FALSE;
3172   arrow[3] = TRUE;
3173 
3174   for (i = 0; i < 4; i++)
3175     {
3176       if (notebook->scrollable && arrow[i])
3177         {
3178           if (notebook->arrow_widget[i] == NULL)
3179             {
3180               GtkWidget *next_widget;
3181               GtkEventController *controller;
3182 
3183               switch (i)
3184                 {
3185                 case 0:
3186                   if (notebook->arrow_widget[1])
3187                     {
3188                       next_widget = notebook->arrow_widget[1];
3189                       break;
3190                     }
3191                   G_GNUC_FALLTHROUGH;
3192                 case 1:
3193                   if (notebook->children)
3194                     {
3195                       GtkNotebookPage *page = notebook->children->data;
3196                       next_widget = page->tab_widget;
3197                       break;
3198                     }
3199                   if (notebook->arrow_widget[2])
3200                     {
3201                       next_widget = notebook->arrow_widget[2];
3202                       break;
3203                     }
3204                   G_GNUC_FALLTHROUGH;
3205                 case 2:
3206                   if (notebook->arrow_widget[3])
3207                     {
3208                       next_widget = notebook->arrow_widget[3];
3209                       break;
3210                     }
3211                   G_GNUC_FALLTHROUGH;
3212                 case 3:
3213                   next_widget = NULL;
3214                   break;
3215 
3216                 default:
3217                   g_assert_not_reached ();
3218                   next_widget = NULL;
3219                   break;
3220                 }
3221 
3222               notebook->arrow_widget[i] = g_object_new (GTK_TYPE_BUTTON,
3223                                                         "css-name", "arrow",
3224                                                         NULL);
3225               controller = gtk_drop_controller_motion_new ();
3226               g_signal_connect (controller, "enter", G_CALLBACK (gtk_notebook_arrow_drag_enter), notebook);
3227               g_signal_connect (controller, "leave", G_CALLBACK (gtk_notebook_arrow_drag_leave), notebook);
3228               gtk_widget_add_controller (notebook->arrow_widget[i], controller);
3229 
3230               if (i == ARROW_LEFT_BEFORE || i == ARROW_LEFT_AFTER)
3231                 {
3232                   gtk_widget_add_css_class (notebook->arrow_widget[i], "down");
3233                   gtk_widget_insert_after (notebook->arrow_widget[i], notebook->tabs_widget, next_widget);
3234                 }
3235               else
3236                 {
3237                   gtk_widget_add_css_class (notebook->arrow_widget[i], "up");
3238                   gtk_widget_insert_before (notebook->arrow_widget[i], notebook->tabs_widget, next_widget);
3239                 }
3240            }
3241 
3242           if (i == ARROW_LEFT_BEFORE || i == ARROW_LEFT_AFTER)
3243             gtk_button_set_icon_name (GTK_BUTTON (notebook->arrow_widget[i]), down_icon_name);
3244           else
3245             gtk_button_set_icon_name (GTK_BUTTON (notebook->arrow_widget[i]), up_icon_name);
3246 
3247           if (i == ARROW_LEFT_BEFORE || i == ARROW_LEFT_AFTER)
3248             gtk_accessible_update_property (GTK_ACCESSIBLE (notebook->arrow_widget[i]),
3249                                             GTK_ACCESSIBLE_PROPERTY_LABEL, _("Previous tab"),
3250                                             -1);
3251           else
3252             gtk_accessible_update_property (GTK_ACCESSIBLE (notebook->arrow_widget[i]),
3253                                             GTK_ACCESSIBLE_PROPERTY_LABEL, _("Next tab"),
3254                                             -1);
3255         }
3256       else
3257         {
3258           g_clear_pointer (&notebook->arrow_widget[i], gtk_widget_unparent);
3259         }
3260     }
3261 }
3262 
3263 static void
gtk_notebook_direction_changed(GtkWidget * widget,GtkTextDirection previous_direction)3264 gtk_notebook_direction_changed (GtkWidget        *widget,
3265                                 GtkTextDirection  previous_direction)
3266 {
3267   update_arrow_nodes (GTK_NOTEBOOK (widget));
3268 }
3269 
3270 static void
gtk_notebook_dnd_finished_cb(GdkDrag * drag,GtkWidget * widget)3271 gtk_notebook_dnd_finished_cb (GdkDrag   *drag,
3272                               GtkWidget *widget)
3273 {
3274   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3275 
3276   gtk_notebook_stop_reorder (notebook);
3277 
3278   if (notebook->rootwindow_drop)
3279     {
3280       GtkNotebook *dest_notebook = NULL;
3281 
3282       g_signal_emit (notebook, notebook_signals[CREATE_WINDOW], 0,
3283                      notebook->detached_tab->child, &dest_notebook);
3284 
3285       if (dest_notebook)
3286         do_detach_tab (notebook, dest_notebook, notebook->detached_tab->child);
3287 
3288       notebook->rootwindow_drop = FALSE;
3289     }
3290   else if (notebook->detached_tab)
3291     {
3292       gtk_notebook_switch_page (notebook, notebook->detached_tab);
3293     }
3294 
3295   notebook->operation = DRAG_OPERATION_NONE;
3296 }
3297 
3298 static GtkNotebook *
gtk_notebook_create_window(GtkNotebook * notebook,GtkWidget * page)3299 gtk_notebook_create_window (GtkNotebook *notebook,
3300                             GtkWidget   *page)
3301 {
3302   return NULL;
3303 }
3304 
3305 static void
gtk_notebook_drag_cancel_cb(GdkDrag * drag,GdkDragCancelReason reason,GtkWidget * widget)3306 gtk_notebook_drag_cancel_cb (GdkDrag             *drag,
3307                              GdkDragCancelReason  reason,
3308                              GtkWidget           *widget)
3309 {
3310   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3311 
3312   notebook->rootwindow_drop = FALSE;
3313 
3314   if (reason == GDK_DRAG_CANCEL_NO_TARGET)
3315     {
3316       GtkNotebook *dest_notebook = NULL;
3317 
3318       g_signal_emit (notebook, notebook_signals[CREATE_WINDOW], 0,
3319                      notebook->detached_tab->child, &dest_notebook);
3320 
3321       if (dest_notebook)
3322         do_detach_tab (notebook, dest_notebook, notebook->detached_tab->child);
3323     }
3324 }
3325 
3326 static gboolean
gtk_notebook_switch_page_timeout(gpointer data)3327 gtk_notebook_switch_page_timeout (gpointer data)
3328 {
3329   GtkNotebook *notebook = GTK_NOTEBOOK (data);
3330   GtkNotebookPage *switch_page;
3331 
3332   notebook->switch_page_timer = 0;
3333 
3334   switch_page = notebook->switch_page;
3335   notebook->switch_page = NULL;
3336 
3337   if (switch_page)
3338     {
3339       /* FIXME: hack, we don't want the
3340        * focus to move fom the source widget
3341        */
3342       notebook->child_has_focus = FALSE;
3343       gtk_notebook_switch_focus_tab (notebook,
3344                                      g_list_find (notebook->children,
3345                                                   switch_page));
3346     }
3347 
3348   return FALSE;
3349 }
3350 
3351 static gboolean
gtk_notebook_can_drag_from(GtkNotebook * self,GtkNotebook * other,GtkNotebookPage * page)3352 gtk_notebook_can_drag_from (GtkNotebook     *self,
3353                             GtkNotebook     *other,
3354                             GtkNotebookPage *page)
3355 {
3356   /* always allow dragging inside self */
3357   if (self == other)
3358     return TRUE;
3359 
3360   /* if the groups don't match, fail */
3361   if (self->group == 0 ||
3362       self->group != other->group)
3363     return FALSE;
3364 
3365   /* Check that the dragged page is not a parent of the notebook
3366    * being dragged into */
3367   if (GTK_WIDGET (self) == page->child ||
3368       gtk_widget_is_ancestor (GTK_WIDGET (self), GTK_WIDGET (page->child)) ||
3369       GTK_WIDGET (self) == page->tab_label ||
3370       gtk_widget_is_ancestor (GTK_WIDGET (self), GTK_WIDGET (page->tab_label)))
3371     return FALSE;
3372 
3373   return TRUE;
3374 }
3375 
3376 static GdkDragAction
gtk_notebook_drag_motion(GtkDropTarget * dest,double x,double y,GtkNotebook * notebook)3377 gtk_notebook_drag_motion (GtkDropTarget *dest,
3378                           double         x,
3379                           double         y,
3380                           GtkNotebook   *notebook)
3381 {
3382   GdkDrag *drag = gdk_drop_get_drag (gtk_drop_target_get_current_drop (dest));
3383   GtkNotebook *source;
3384 
3385   notebook->mouse_x = x;
3386   notebook->mouse_y = y;
3387 
3388   if (!drag)
3389     return 0;
3390 
3391   source = GTK_NOTEBOOK (g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin"));
3392   g_assert (source->cur_page != NULL);
3393 
3394   if (!gtk_notebook_can_drag_from (notebook, source, source->cur_page))
3395     return 0;
3396 
3397   return GDK_ACTION_MOVE;
3398 }
3399 
3400 static gboolean
gtk_notebook_drag_drop(GtkDropTarget * dest,const GValue * value,double x,double y,GtkNotebook * self)3401 gtk_notebook_drag_drop (GtkDropTarget *dest,
3402                         const GValue  *value,
3403                         double         x,
3404                         double         y,
3405                         GtkNotebook   *self)
3406 {
3407   GdkDrag *drag = gdk_drop_get_drag (gtk_drop_target_get_current_drop (dest));
3408   GtkNotebook *source;
3409   GtkNotebookPage *page = g_value_get_object (value);
3410 
3411   source = drag ? g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin") : NULL;
3412 
3413   if (!source || !gtk_notebook_can_drag_from (self, source, source->cur_page))
3414     return FALSE;
3415 
3416   self->mouse_x = x;
3417   self->mouse_y = y;
3418 
3419   do_detach_tab (source, self, page->child);
3420 
3421   return TRUE;
3422 }
3423 
3424 /**
3425  * gtk_notebook_detach_tab:
3426  * @notebook: a `GtkNotebook`
3427  * @child: a child
3428  *
3429  * Removes the child from the notebook.
3430  *
3431  * This function is very similar to [method@Gtk.Notebook.remove_page],
3432  * but additionally informs the notebook that the removal
3433  * is happening as part of a tab DND operation, which should
3434  * not be cancelled.
3435  */
3436 void
gtk_notebook_detach_tab(GtkNotebook * notebook,GtkWidget * child)3437 gtk_notebook_detach_tab (GtkNotebook *notebook,
3438                          GtkWidget   *child)
3439 {
3440   notebook->remove_in_detach = TRUE;
3441   gtk_notebook_remove (notebook, child);
3442   notebook->remove_in_detach = FALSE;
3443 }
3444 
3445 static void
do_detach_tab(GtkNotebook * from,GtkNotebook * to,GtkWidget * child)3446 do_detach_tab (GtkNotebook *from,
3447                GtkNotebook *to,
3448                GtkWidget   *child)
3449 {
3450   GtkWidget *tab_label, *menu_label;
3451   gboolean tab_expand, tab_fill, reorderable, detachable;
3452   GList *element;
3453   int page_num;
3454   GtkNotebookPage *page;
3455 
3456   menu_label = gtk_notebook_get_menu_label (from, child);
3457 
3458   if (menu_label)
3459     g_object_ref (menu_label);
3460 
3461   tab_label = gtk_notebook_get_tab_label (from, child);
3462 
3463   if (tab_label)
3464     g_object_ref (tab_label);
3465 
3466   g_object_ref (child);
3467 
3468   page = gtk_notebook_get_page (from, child);
3469   g_object_get (page,
3470                 "tab-expand", &tab_expand,
3471                 "tab-fill", &tab_fill,
3472                 "reorderable", &reorderable,
3473                 "detachable", &detachable,
3474                 NULL);
3475 
3476   gtk_notebook_detach_tab (from, child);
3477 
3478   element = get_drop_position (to);
3479   page_num = g_list_position (to->children, element);
3480   gtk_notebook_insert_page_menu (to, child, tab_label, menu_label, page_num);
3481 
3482   page = gtk_notebook_get_page (to, child);
3483   g_object_set (page,
3484                 "tab-expand", tab_expand,
3485                 "tab-fill", tab_fill,
3486                 "reorderable", reorderable,
3487                 "detachable", detachable,
3488                 NULL);
3489 
3490   if (child)
3491     g_object_unref (child);
3492 
3493   if (tab_label)
3494     g_object_unref (tab_label);
3495 
3496   if (menu_label)
3497     g_object_unref (menu_label);
3498 
3499   gtk_notebook_set_current_page (to, page_num);
3500 }
3501 
3502 /* Private methods:
3503  *
3504  * gtk_notebook_remove
3505  * gtk_notebook_focus
3506  * gtk_notebook_set_focus_child
3507  */
3508 static void
gtk_notebook_remove(GtkNotebook * notebook,GtkWidget * widget)3509 gtk_notebook_remove (GtkNotebook *notebook,
3510                      GtkWidget   *widget)
3511 {
3512   GtkNotebookPage *page;
3513   GList *children, *list;
3514   int page_num = 0;
3515 
3516   children = notebook->children;
3517   while (children)
3518     {
3519       page = children->data;
3520 
3521       if (page->child == widget)
3522         break;
3523 
3524       page_num++;
3525       children = children->next;
3526     }
3527 
3528   if (children == NULL)
3529     return;
3530 
3531   g_object_ref (widget);
3532 
3533   list = children->next;
3534   gtk_notebook_real_remove (notebook, children);
3535 
3536   while (list)
3537     {
3538       g_object_notify (G_OBJECT (list->data), "position");
3539       list = list->next;
3540     }
3541 
3542   g_signal_emit (notebook,
3543                  notebook_signals[PAGE_REMOVED],
3544                  0,
3545                  widget,
3546                  page_num);
3547 
3548   g_object_unref (widget);
3549 }
3550 
3551 static gboolean
focus_tabs_in(GtkNotebook * notebook)3552 focus_tabs_in (GtkNotebook *notebook)
3553 {
3554   if (notebook->show_tabs && gtk_notebook_has_current_page (notebook))
3555     {
3556       gtk_widget_grab_focus (GTK_WIDGET (notebook));
3557       gtk_notebook_set_focus_child (GTK_WIDGET (notebook), NULL);
3558       gtk_notebook_switch_focus_tab (notebook,
3559                                      g_list_find (notebook->children,
3560                                                   notebook->cur_page));
3561 
3562       return TRUE;
3563     }
3564   else
3565     return FALSE;
3566 }
3567 
3568 static gboolean
focus_tabs_move(GtkNotebook * notebook,GtkDirectionType direction,int search_direction)3569 focus_tabs_move (GtkNotebook     *notebook,
3570                  GtkDirectionType direction,
3571                  int              search_direction)
3572 {
3573   GList *new_page;
3574 
3575   new_page = gtk_notebook_search_page (notebook, notebook->focus_tab,
3576                                        search_direction, TRUE);
3577   if (!new_page)
3578     {
3579       new_page = gtk_notebook_search_page (notebook, NULL,
3580                                            search_direction, TRUE);
3581     }
3582 
3583   if (new_page)
3584     gtk_notebook_switch_focus_tab (notebook, new_page);
3585   else
3586     gtk_widget_error_bell (GTK_WIDGET (notebook));
3587 
3588   return TRUE;
3589 }
3590 
3591 static gboolean
focus_child_in(GtkNotebook * notebook,GtkDirectionType direction)3592 focus_child_in (GtkNotebook      *notebook,
3593                 GtkDirectionType  direction)
3594 {
3595   if (notebook->cur_page)
3596     return gtk_widget_child_focus (notebook->cur_page->child, direction);
3597   else
3598     return FALSE;
3599 }
3600 
3601 static gboolean
focus_action_in(GtkNotebook * notebook,int action,GtkDirectionType direction)3602 focus_action_in (GtkNotebook      *notebook,
3603                  int               action,
3604                  GtkDirectionType  direction)
3605 {
3606   if (notebook->action_widget[action] &&
3607       gtk_widget_get_visible (notebook->action_widget[action]))
3608     return gtk_widget_child_focus (notebook->action_widget[action], direction);
3609   else
3610     return FALSE;
3611 }
3612 
3613 /* Focus in the notebook can either be on the pages, or on
3614  * the tabs or on the action_widgets.
3615  */
3616 static gboolean
gtk_notebook_focus(GtkWidget * widget,GtkDirectionType direction)3617 gtk_notebook_focus (GtkWidget        *widget,
3618                     GtkDirectionType  direction)
3619 {
3620   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3621   GtkWidget *old_focus_child;
3622   GtkDirectionType effective_direction;
3623   int first_action;
3624   int last_action;
3625 
3626   gboolean widget_is_focus;
3627 
3628   if (notebook->tab_pos == GTK_POS_TOP ||
3629       notebook->tab_pos == GTK_POS_LEFT)
3630     {
3631       first_action = ACTION_WIDGET_START;
3632       last_action = ACTION_WIDGET_END;
3633     }
3634   else
3635     {
3636       first_action = ACTION_WIDGET_END;
3637       last_action = ACTION_WIDGET_START;
3638     }
3639 
3640   if (notebook->focus_out)
3641     {
3642       notebook->focus_out = FALSE; /* Clear this to catch the wrap-around case */
3643       return FALSE;
3644     }
3645 
3646   widget_is_focus = gtk_widget_is_focus (widget);
3647   old_focus_child = gtk_widget_get_focus_child (widget);
3648   if (old_focus_child)
3649     old_focus_child = gtk_widget_get_focus_child (old_focus_child);
3650 
3651   effective_direction = get_effective_direction (notebook, direction);
3652 
3653   if (old_focus_child)          /* Focus on page child or action widget */
3654     {
3655       if (gtk_widget_child_focus (old_focus_child, direction))
3656         return TRUE;
3657 
3658       if (old_focus_child == notebook->action_widget[ACTION_WIDGET_START])
3659         {
3660           switch ((guint) effective_direction)
3661             {
3662             case GTK_DIR_DOWN:
3663               return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
3664             case GTK_DIR_RIGHT:
3665               return focus_tabs_in (notebook);
3666             case GTK_DIR_LEFT:
3667               return FALSE;
3668             case GTK_DIR_UP:
3669               return FALSE;
3670             default:
3671               switch ((guint) direction)
3672                 {
3673                 case GTK_DIR_TAB_FORWARD:
3674                   if ((notebook->tab_pos == GTK_POS_RIGHT || notebook->tab_pos == GTK_POS_BOTTOM) &&
3675                       focus_child_in (notebook, direction))
3676                     return TRUE;
3677                   return focus_tabs_in (notebook);
3678                 case GTK_DIR_TAB_BACKWARD:
3679                   return FALSE;
3680                 default:
3681                   g_assert_not_reached ();
3682                   break;
3683                 }
3684             }
3685         }
3686       else if (old_focus_child == notebook->action_widget[ACTION_WIDGET_END])
3687         {
3688           switch ((guint) effective_direction)
3689             {
3690             case GTK_DIR_DOWN:
3691               return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
3692             case GTK_DIR_RIGHT:
3693               return FALSE;
3694             case GTK_DIR_LEFT:
3695               return focus_tabs_in (notebook);
3696             case GTK_DIR_UP:
3697               return FALSE;
3698             default:
3699               switch ((guint) direction)
3700                 {
3701                 case GTK_DIR_TAB_FORWARD:
3702                   return FALSE;
3703                 case GTK_DIR_TAB_BACKWARD:
3704                   if ((notebook->tab_pos == GTK_POS_TOP || notebook->tab_pos == GTK_POS_LEFT) &&
3705                       focus_child_in (notebook, direction))
3706                     return TRUE;
3707                   return focus_tabs_in (notebook);
3708                 default:
3709                   g_assert_not_reached ();
3710                   break;
3711                 }
3712             }
3713         }
3714       else
3715         {
3716           switch ((guint) effective_direction)
3717             {
3718             case GTK_DIR_TAB_BACKWARD:
3719             case GTK_DIR_UP:
3720               /* Focus onto the tabs */
3721               return focus_tabs_in (notebook);
3722             case GTK_DIR_DOWN:
3723             case GTK_DIR_LEFT:
3724             case GTK_DIR_RIGHT:
3725               return FALSE;
3726             case GTK_DIR_TAB_FORWARD:
3727               return focus_action_in (notebook, last_action, direction);
3728             default:
3729               break;
3730             }
3731         }
3732     }
3733   else if (widget_is_focus)     /* Focus was on tabs */
3734     {
3735       switch ((guint) effective_direction)
3736         {
3737         case GTK_DIR_TAB_BACKWARD:
3738               return focus_action_in (notebook, first_action, direction);
3739         case GTK_DIR_UP:
3740           return FALSE;
3741         case GTK_DIR_TAB_FORWARD:
3742           if (focus_child_in (notebook, GTK_DIR_TAB_FORWARD))
3743             return TRUE;
3744           return focus_action_in (notebook, last_action, direction);
3745         case GTK_DIR_DOWN:
3746           /* We use TAB_FORWARD rather than direction so that we focus a more
3747            * predictable widget for the user; users may be using arrow focusing
3748            * in this situation even if they don't usually use arrow focusing.
3749            */
3750           return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
3751         case GTK_DIR_LEFT:
3752           return focus_tabs_move (notebook, direction, STEP_PREV);
3753         case GTK_DIR_RIGHT:
3754           return focus_tabs_move (notebook, direction, STEP_NEXT);
3755         default:
3756           break;
3757         }
3758     }
3759   else /* Focus was not on widget */
3760     {
3761       switch ((guint) effective_direction)
3762         {
3763         case GTK_DIR_TAB_FORWARD:
3764         case GTK_DIR_DOWN:
3765           if (focus_action_in (notebook, first_action, direction))
3766             return TRUE;
3767           if (focus_tabs_in (notebook))
3768             return TRUE;
3769           if (focus_action_in (notebook, last_action, direction))
3770             return TRUE;
3771           if (focus_child_in (notebook, direction))
3772             return TRUE;
3773           return FALSE;
3774         case GTK_DIR_TAB_BACKWARD:
3775           if (focus_action_in (notebook, last_action, direction))
3776             return TRUE;
3777           if (focus_child_in (notebook, direction))
3778             return TRUE;
3779           if (focus_tabs_in (notebook))
3780             return TRUE;
3781           if (focus_action_in (notebook, first_action, direction))
3782             return TRUE;
3783           return FALSE;
3784         case GTK_DIR_UP:
3785         case GTK_DIR_LEFT:
3786         case GTK_DIR_RIGHT:
3787           return focus_child_in (notebook, direction);
3788         default:
3789           break;
3790         }
3791     }
3792 
3793   g_assert_not_reached ();
3794   return FALSE;
3795 }
3796 
3797 static gboolean
gtk_notebook_grab_focus(GtkWidget * widget)3798 gtk_notebook_grab_focus (GtkWidget *widget)
3799 {
3800   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3801 
3802   if (notebook->show_tabs)
3803     return gtk_widget_grab_focus_self (widget);
3804   else
3805     return gtk_widget_grab_focus_child (widget);
3806 }
3807 
3808 static void
gtk_notebook_set_focus_child(GtkWidget * widget,GtkWidget * child)3809 gtk_notebook_set_focus_child (GtkWidget *widget,
3810                               GtkWidget *child)
3811 {
3812   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3813   GtkWidget *page_child;
3814   GtkWidget *toplevel;
3815 
3816   /* If the old focus widget was within a page of the notebook,
3817    * (child may either be NULL or not in this case), record it
3818    * for future use if we switch to the page with a mnemonic.
3819    */
3820 
3821   toplevel = GTK_WIDGET (gtk_widget_get_root (widget));
3822   if (GTK_IS_WINDOW (toplevel))
3823     {
3824       page_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
3825       while (page_child)
3826         {
3827           if (gtk_widget_get_parent (page_child) == widget)
3828             {
3829               GList *list = gtk_notebook_find_child (notebook, page_child);
3830               if (list != NULL)
3831                 {
3832                   GtkNotebookPage *page = list->data;
3833 
3834                   if (page->last_focus_child)
3835                     g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
3836 
3837                   page->last_focus_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
3838                   g_object_add_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
3839 
3840                   break;
3841                 }
3842             }
3843 
3844           page_child = gtk_widget_get_parent (page_child);
3845         }
3846     }
3847 
3848   if (child)
3849     {
3850       g_return_if_fail (GTK_IS_WIDGET (child));
3851 
3852       notebook->child_has_focus = TRUE;
3853       if (!notebook->focus_tab)
3854         {
3855           GList *children;
3856           GtkNotebookPage *page;
3857 
3858           children = notebook->children;
3859           while (children)
3860             {
3861               page = children->data;
3862               if (page->child == child || page->tab_label == child)
3863                 gtk_notebook_switch_focus_tab (notebook, children);
3864               children = children->next;
3865             }
3866         }
3867     }
3868   else
3869     notebook->child_has_focus = FALSE;
3870 
3871   GTK_WIDGET_CLASS (gtk_notebook_parent_class)->set_focus_child (widget, child);
3872 }
3873 
3874 /* Private GtkNotebook Methods:
3875  *
3876  * gtk_notebook_real_insert_page
3877  */
3878 static void
page_visible_cb(GtkWidget * child,GParamSpec * arg,gpointer data)3879 page_visible_cb (GtkWidget  *child,
3880                  GParamSpec *arg,
3881                  gpointer    data)
3882 {
3883   GtkNotebook *notebook = GTK_NOTEBOOK (data);
3884   GList *list = gtk_notebook_find_child (notebook, GTK_WIDGET (child));
3885   GtkNotebookPage *page = list->data;
3886   GList *next = NULL;
3887 
3888   if (notebook->menu && page->menu_label)
3889     {
3890       GtkWidget *parent = gtk_widget_get_parent (page->menu_label);
3891       if (parent)
3892         gtk_widget_set_visible (parent, gtk_widget_get_visible (child));
3893     }
3894 
3895   gtk_widget_set_visible (page->tab_widget, gtk_widget_get_visible (child));
3896 
3897   if (notebook->cur_page == page)
3898     {
3899       if (!gtk_widget_get_visible (child))
3900         {
3901           list = g_list_find (notebook->children, notebook->cur_page);
3902           if (list)
3903             {
3904               next = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
3905               if (!next)
3906                 next = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
3907             }
3908 
3909           if (next)
3910             gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE_FROM_LIST (next));
3911         }
3912       gtk_widget_set_visible (notebook->header_widget, notebook->show_tabs && gtk_notebook_has_current_page (notebook));
3913     }
3914 
3915   if (!gtk_notebook_has_current_page (notebook) && gtk_widget_get_visible (child))
3916     {
3917       gtk_notebook_switch_page (notebook, page);
3918       /* focus_tab is set in the switch_page method */
3919       gtk_notebook_switch_focus_tab (notebook, notebook->focus_tab);
3920     }
3921 }
3922 
3923 static void
measure_tab(GtkGizmo * gizmo,GtkOrientation orientation,int for_size,int * minimum,int * natural,int * minimum_baseline,int * natural_baseline)3924 measure_tab (GtkGizmo       *gizmo,
3925              GtkOrientation  orientation,
3926              int             for_size,
3927              int            *minimum,
3928              int            *natural,
3929              int            *minimum_baseline,
3930              int            *natural_baseline)
3931 {
3932   GtkNotebook *notebook = g_object_get_data (G_OBJECT (gizmo), "notebook");
3933   GList *l;
3934   GtkNotebookPage *page = NULL;
3935 
3936   for (l = notebook->children; l; l = l->next)
3937     {
3938       GtkNotebookPage *p = GTK_NOTEBOOK_PAGE_FROM_LIST (l);
3939       if (p->tab_widget == GTK_WIDGET (gizmo))
3940         {
3941           page = p;
3942           break;
3943         }
3944     }
3945 
3946   g_assert (page != NULL);
3947 
3948   gtk_widget_measure (page->tab_label,
3949                       orientation,
3950                       for_size,
3951                       minimum, natural,
3952                       minimum_baseline, natural_baseline);
3953 }
3954 
3955 static void
allocate_tab(GtkGizmo * gizmo,int width,int height,int baseline)3956 allocate_tab (GtkGizmo *gizmo,
3957               int       width,
3958               int       height,
3959               int       baseline)
3960 {
3961   GtkNotebook *notebook = g_object_get_data (G_OBJECT (gizmo), "notebook");
3962   GList *l;
3963   GtkNotebookPage *page = NULL;
3964   GtkAllocation child_allocation;
3965 
3966   for (l = notebook->children; l; l = l->next)
3967     {
3968       GtkNotebookPage *p = GTK_NOTEBOOK_PAGE_FROM_LIST (l);
3969       if (p->tab_widget == GTK_WIDGET (gizmo))
3970         {
3971           page = p;
3972           break;
3973         }
3974     }
3975 
3976   g_assert (page != NULL);
3977 
3978   child_allocation = (GtkAllocation) {0, 0, width, height};
3979 
3980   if (!page->fill)
3981     {
3982       if (notebook->tab_pos == GTK_POS_TOP || notebook->tab_pos == GTK_POS_BOTTOM)
3983         {
3984           gtk_widget_measure (page->tab_label, GTK_ORIENTATION_HORIZONTAL, height,
3985                               NULL, &child_allocation.width, NULL, NULL);
3986           if (child_allocation.width > width)
3987             child_allocation.width = width;
3988           else
3989             child_allocation.x += (width - child_allocation.width) / 2;
3990 
3991         }
3992       else
3993         {
3994           gtk_widget_measure (page->tab_label, GTK_ORIENTATION_VERTICAL, width,
3995                               NULL, &child_allocation.height, NULL, NULL);
3996 
3997           if (child_allocation.height > height)
3998             child_allocation.height = height;
3999           else
4000             child_allocation.y += (height - child_allocation.height) / 2;
4001         }
4002     }
4003 
4004   gtk_widget_size_allocate (page->tab_label, &child_allocation, baseline);
4005 }
4006 
4007 static void
gtk_notebook_tab_drop_enter(GtkEventController * controller,double x,double y,GtkNotebookPage * page)4008 gtk_notebook_tab_drop_enter (GtkEventController *controller,
4009                              double              x,
4010                              double              y,
4011                              GtkNotebookPage    *page)
4012 {
4013   GtkWidget *widget = gtk_event_controller_get_widget (controller);
4014   GtkNotebook *notebook = g_object_get_data (G_OBJECT (widget), "notebook");
4015 
4016   g_assert (!notebook->switch_page_timer);
4017 
4018   notebook->switch_page = page;
4019 
4020   notebook->switch_page_timer = g_timeout_add (TIMEOUT_EXPAND, gtk_notebook_switch_page_timeout, notebook);
4021   gdk_source_set_static_name_by_id (notebook->switch_page_timer, "[gtk] gtk_notebook_switch_page_timeout");
4022 }
4023 
4024 static void
gtk_notebook_tab_drop_leave(GtkEventController * controller,GtkNotebookPage * page)4025 gtk_notebook_tab_drop_leave (GtkEventController *controller,
4026                              GtkNotebookPage    *page)
4027 {
4028   GtkWidget *widget = gtk_event_controller_get_widget (controller);
4029   GtkNotebook *notebook = g_object_get_data (G_OBJECT (widget), "notebook");
4030 
4031   g_clear_handle_id (&notebook->switch_page_timer, g_source_remove);
4032 }
4033 
4034 static int
gtk_notebook_insert_notebook_page(GtkNotebook * notebook,GtkNotebookPage * page,int position)4035 gtk_notebook_insert_notebook_page (GtkNotebook *notebook,
4036                                    GtkNotebookPage *page,
4037                                    int position)
4038 {
4039   int nchildren;
4040   GList *list;
4041   GtkWidget *sibling;
4042   GtkEventController *controller;
4043   GtkStackPage *stack_page;
4044 
4045   nchildren = g_list_length (notebook->children);
4046   if ((position < 0) || (position > nchildren))
4047     position = nchildren;
4048 
4049   notebook->children = g_list_insert (notebook->children, g_object_ref (page), position);
4050 
4051   if (position < nchildren)
4052     sibling = GTK_NOTEBOOK_PAGE_FROM_LIST (g_list_nth (notebook->children, position))->tab_widget;
4053   else if (notebook->arrow_widget[ARROW_LEFT_AFTER])
4054     sibling = notebook->arrow_widget[ARROW_LEFT_AFTER];
4055   else
4056   sibling = notebook->arrow_widget[ARROW_RIGHT_AFTER];
4057 
4058   page->tab_widget = gtk_gizmo_new_with_role ("tab",
4059                                               GTK_ACCESSIBLE_ROLE_TAB,
4060                                               measure_tab,
4061                                               allocate_tab,
4062                                               NULL,
4063                                               NULL,
4064                                               NULL,
4065                                               NULL);
4066   g_object_set_data (G_OBJECT (page->tab_widget), "notebook", notebook);
4067   gtk_widget_insert_before (page->tab_widget, notebook->tabs_widget, sibling);
4068   controller = gtk_drop_controller_motion_new ();
4069   g_signal_connect (controller, "enter", G_CALLBACK (gtk_notebook_tab_drop_enter), page);
4070   g_signal_connect (controller, "leave", G_CALLBACK (gtk_notebook_tab_drop_leave), page);
4071   gtk_widget_add_controller (page->tab_widget, controller);
4072   gtk_accessible_update_property (GTK_ACCESSIBLE (page->tab_widget),
4073                                   GTK_ACCESSIBLE_PROPERTY_LABEL, _("Tab"),
4074                                   -1);
4075 
4076   page->expand = FALSE;
4077   page->fill = TRUE;
4078 
4079   if (notebook->menu)
4080     gtk_notebook_menu_item_create (notebook, page);
4081 
4082   gtk_stack_add_named (GTK_STACK (notebook->stack_widget), page->child, NULL);
4083 
4084   if (page->tab_label)
4085     {
4086       gtk_widget_set_parent (page->tab_label, page->tab_widget);
4087       g_object_set_data (G_OBJECT (page->tab_label), "notebook", notebook);
4088     }
4089 
4090   stack_page = gtk_stack_get_page (GTK_STACK (notebook->stack_widget), page->child);
4091   gtk_accessible_update_relation (GTK_ACCESSIBLE (page->tab_widget),
4092                                   GTK_ACCESSIBLE_RELATION_CONTROLS, stack_page, NULL,
4093                                   -1);
4094   gtk_accessible_update_relation (GTK_ACCESSIBLE (stack_page),
4095                                   GTK_ACCESSIBLE_RELATION_LABELLED_BY, page->tab_widget, NULL,
4096                                   -1);
4097 
4098   gtk_accessible_update_state (GTK_ACCESSIBLE (page->tab_widget),
4099                                GTK_ACCESSIBLE_STATE_SELECTED, FALSE,
4100                                -1);
4101 
4102   gtk_notebook_update_labels (notebook);
4103 
4104   if (!notebook->first_tab)
4105     notebook->first_tab = notebook->children;
4106 
4107   if (page->tab_label)
4108     {
4109       if (notebook->show_tabs && gtk_widget_get_visible (page->child))
4110         gtk_widget_show (page->tab_label);
4111       else
4112         gtk_widget_hide (page->tab_label);
4113 
4114     page->mnemonic_activate_signal =
4115       g_signal_connect (page->tab_label,
4116                         "mnemonic-activate",
4117                         G_CALLBACK (gtk_notebook_mnemonic_activate_switch_page),
4118                         notebook);
4119     }
4120 
4121   page->notify_visible_handler = g_signal_connect (page->child, "notify::visible",
4122                                                    G_CALLBACK (page_visible_cb), notebook);
4123 
4124   g_signal_emit (notebook, notebook_signals[PAGE_ADDED], 0, page->child, position);
4125 
4126   if (!gtk_notebook_has_current_page (notebook))
4127     {
4128       gtk_notebook_switch_page (notebook, page);
4129       /* focus_tab is set in the switch_page method */
4130       gtk_notebook_switch_focus_tab (notebook, notebook->focus_tab);
4131     }
4132 
4133   g_object_notify (G_OBJECT (page), "tab-expand");
4134   g_object_notify (G_OBJECT (page), "tab-fill");
4135   g_object_notify (G_OBJECT (page), "tab-label");
4136   g_object_notify (G_OBJECT (page), "menu-label");
4137 
4138   list = g_list_nth (notebook->children, position);
4139   while (list)
4140     {
4141       g_object_notify (G_OBJECT (list->data), "position");
4142       list = list->next;
4143     }
4144 
4145   update_arrow_state (notebook);
4146 
4147   if (notebook->pages)
4148     g_list_model_items_changed (notebook->pages, position, 0, 1);
4149 
4150   /* The page-added handler might have reordered the pages, re-get the position */
4151   return gtk_notebook_page_num (notebook, page->child);
4152 }
4153 
4154 static int
gtk_notebook_real_insert_page(GtkNotebook * notebook,GtkWidget * child,GtkWidget * tab_label,GtkWidget * menu_label,int position)4155 gtk_notebook_real_insert_page (GtkNotebook *notebook,
4156                                GtkWidget   *child,
4157                                GtkWidget   *tab_label,
4158                                GtkWidget   *menu_label,
4159                                int          position)
4160 {
4161   GtkNotebookPage *page;
4162   int ret;
4163 
4164   page = g_object_new (GTK_TYPE_NOTEBOOK_PAGE,
4165                        "child", child,
4166                        "tab", tab_label,
4167                        "menu", menu_label,
4168                        NULL);
4169 
4170   ret = gtk_notebook_insert_notebook_page (notebook, page, position);
4171 
4172   g_object_unref (page);
4173 
4174   return ret;
4175 }
4176 
4177 static gboolean
gtk_notebook_timer(GtkNotebook * notebook)4178 gtk_notebook_timer (GtkNotebook *notebook)
4179 {
4180   gboolean retval = FALSE;
4181 
4182   if (notebook->timer)
4183     {
4184       gtk_notebook_do_arrow (notebook, notebook->click_child);
4185 
4186       if (notebook->need_timer)
4187         {
4188           notebook->need_timer = FALSE;
4189           notebook->timer = g_timeout_add (TIMEOUT_REPEAT * SCROLL_DELAY_FACTOR,
4190                                        (GSourceFunc) gtk_notebook_timer,
4191                                        notebook);
4192           gdk_source_set_static_name_by_id (notebook->timer, "[gtk] gtk_notebook_timer");
4193         }
4194       else
4195         retval = TRUE;
4196     }
4197 
4198   return retval;
4199 }
4200 
4201 static void
gtk_notebook_set_scroll_timer(GtkNotebook * notebook)4202 gtk_notebook_set_scroll_timer (GtkNotebook *notebook)
4203 {
4204   if (!notebook->timer)
4205     {
4206       notebook->timer = g_timeout_add (TIMEOUT_INITIAL,
4207                                    (GSourceFunc) gtk_notebook_timer,
4208                                    notebook);
4209       gdk_source_set_static_name_by_id (notebook->timer, "[gtk] gtk_notebook_timer");
4210       notebook->need_timer = TRUE;
4211     }
4212 }
4213 
4214 static int
gtk_notebook_page_compare(gconstpointer a,gconstpointer b)4215 gtk_notebook_page_compare (gconstpointer a,
4216                            gconstpointer b)
4217 {
4218   return (((GtkNotebookPage *) a)->child != b);
4219 }
4220 
4221 static GList*
gtk_notebook_find_child(GtkNotebook * notebook,GtkWidget * child)4222 gtk_notebook_find_child (GtkNotebook *notebook,
4223                          GtkWidget   *child)
4224 {
4225   return g_list_find_custom (notebook->children,
4226                              child,
4227                              gtk_notebook_page_compare);
4228 }
4229 
4230 static void
gtk_notebook_remove_tab_label(GtkNotebook * notebook,GtkNotebookPage * page)4231 gtk_notebook_remove_tab_label (GtkNotebook     *notebook,
4232                                GtkNotebookPage *page)
4233 {
4234   if (page->tab_label)
4235     {
4236       if (page->mnemonic_activate_signal)
4237         g_signal_handler_disconnect (page->tab_label,
4238                                      page->mnemonic_activate_signal);
4239       page->mnemonic_activate_signal = 0;
4240 
4241       if (gtk_widget_get_native (page->tab_label) != gtk_widget_get_native (GTK_WIDGET (notebook)) ||
4242           !NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
4243         {
4244           GtkWidget *parent;
4245 
4246           /* we hit this condition during dnd of a detached tab */
4247           parent = gtk_widget_get_parent (page->tab_label);
4248           if (GTK_IS_WINDOW (parent))
4249             gtk_box_remove (GTK_BOX (parent), page->tab_label);
4250           else
4251             gtk_widget_unparent (page->tab_label);
4252         }
4253       else
4254         {
4255           gtk_widget_unparent (page->tab_label);
4256         }
4257 
4258       page->tab_label = NULL;
4259     }
4260 }
4261 
4262 static void
gtk_notebook_real_remove(GtkNotebook * notebook,GList * list)4263 gtk_notebook_real_remove (GtkNotebook *notebook,
4264                           GList       *list)
4265 {
4266   GtkNotebookPage *page;
4267   GList * next_list;
4268   int need_resize = FALSE;
4269   GtkWidget *tab_label;
4270   gboolean destroying;
4271   int position;
4272 
4273   page = list->data;
4274 
4275   destroying = gtk_widget_in_destruction (GTK_WIDGET (notebook));
4276 
4277   next_list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
4278   if (!next_list)
4279     next_list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
4280 
4281   notebook->children = g_list_remove_link (notebook->children, list);
4282 
4283   if (notebook->cur_page == list->data)
4284     {
4285       notebook->cur_page = NULL;
4286       if (next_list && !destroying)
4287         gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE_FROM_LIST (next_list));
4288       if (notebook->operation == DRAG_OPERATION_REORDER && !notebook->remove_in_detach)
4289         gtk_notebook_stop_reorder (notebook);
4290     }
4291 
4292   if (notebook->detached_tab == list->data)
4293     notebook->detached_tab = NULL;
4294 
4295   if (notebook->switch_page == page)
4296     notebook->switch_page = NULL;
4297 
4298   if (list == notebook->first_tab)
4299     notebook->first_tab = next_list;
4300   if (list == notebook->focus_tab && !destroying)
4301     gtk_notebook_switch_focus_tab (notebook, next_list);
4302 
4303   position = g_list_index (notebook->children, page);
4304 
4305   g_signal_handler_disconnect (page->child, page->notify_visible_handler);
4306 
4307   if (gtk_widget_get_visible (page->child) &&
4308       gtk_widget_get_visible (GTK_WIDGET (notebook)))
4309     need_resize = TRUE;
4310 
4311   gtk_stack_remove (GTK_STACK (notebook->stack_widget), page->child);
4312 
4313   tab_label = page->tab_label;
4314   if (tab_label)
4315     {
4316       g_object_ref (tab_label);
4317       gtk_notebook_remove_tab_label (notebook, page);
4318       if (destroying)
4319         gtk_widget_unparent (tab_label);
4320       g_object_unref (tab_label);
4321     }
4322 
4323   if (notebook->menu)
4324     {
4325       GtkWidget *parent = gtk_widget_get_parent (page->menu_label);
4326 
4327       if (parent)
4328         gtk_notebook_menu_label_unparent (parent);
4329       gtk_popover_set_child (GTK_POPOVER (notebook->menu), NULL);
4330 
4331       gtk_widget_queue_resize (notebook->menu);
4332     }
4333 
4334   g_list_free (list);
4335 
4336   if (page->last_focus_child)
4337     {
4338       g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4339       page->last_focus_child = NULL;
4340     }
4341 
4342   gtk_widget_unparent (page->tab_widget);
4343 
4344   g_object_unref (page);
4345 
4346   gtk_notebook_update_labels (notebook);
4347   if (need_resize)
4348     gtk_widget_queue_resize (GTK_WIDGET (notebook));
4349 
4350   if (notebook->pages)
4351     g_list_model_items_changed (notebook->pages, position, 1, 0);
4352 }
4353 
4354 static void
gtk_notebook_update_labels(GtkNotebook * notebook)4355 gtk_notebook_update_labels (GtkNotebook *notebook)
4356 {
4357   GtkNotebookPage *page;
4358   GList *list;
4359   char string[32];
4360   int page_num = 1;
4361 
4362   if (!notebook->show_tabs && !notebook->menu)
4363     return;
4364 
4365   for (list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, FALSE);
4366        list;
4367        list = gtk_notebook_search_page (notebook, list, STEP_NEXT, FALSE))
4368     {
4369       const char *text;
4370       page = list->data;
4371       g_snprintf (string, sizeof (string), _("Page %u"), page_num++);
4372       if (page->tab_text)
4373         text = page->tab_text;
4374       else
4375         text = string;
4376       if (notebook->show_tabs)
4377         {
4378           if (page->default_tab)
4379             {
4380               if (!page->tab_label)
4381                 {
4382                   page->tab_label = gtk_label_new ("");
4383                   g_object_ref_sink (page->tab_label);
4384                   g_object_set_data (G_OBJECT (page->tab_label), "notebook", notebook);
4385                   gtk_widget_set_parent (page->tab_label, page->tab_widget);
4386                 }
4387               gtk_label_set_text (GTK_LABEL (page->tab_label), text);
4388             }
4389 
4390           if (page->child && page->tab_label)
4391             gtk_widget_set_visible (page->tab_label, gtk_widget_get_visible (page->child));
4392         }
4393 
4394       if (notebook->menu && page->default_menu)
4395         {
4396           if (page->menu_text)
4397             text = page->menu_text;
4398           else if (GTK_IS_LABEL (page->tab_label))
4399             text = gtk_label_get_text (GTK_LABEL (page->tab_label));
4400           else
4401             text = string;
4402           gtk_label_set_text (GTK_LABEL (page->menu_label), text);
4403         }
4404     }
4405 }
4406 
4407 static GList *
gtk_notebook_search_page(GtkNotebook * notebook,GList * list,int direction,gboolean find_visible)4408 gtk_notebook_search_page (GtkNotebook *notebook,
4409                           GList       *list,
4410                           int          direction,
4411                           gboolean     find_visible)
4412 {
4413   GtkNotebookPage *page = NULL;
4414   GList *old_list = NULL;
4415 
4416   if (list)
4417     page = list->data;
4418 
4419   if (!page || direction == STEP_NEXT)
4420     {
4421       if (list)
4422         {
4423           old_list = list;
4424           list = list->next;
4425         }
4426       else
4427         list = notebook->children;
4428 
4429       while (list)
4430         {
4431           page = list->data;
4432           if (direction == STEP_NEXT &&
4433               (!find_visible ||
4434                (gtk_widget_get_visible (page->child) &&
4435                 (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
4436             return list;
4437           old_list = list;
4438           list = list->next;
4439         }
4440       list = old_list;
4441     }
4442   else
4443     {
4444       list = list->prev;
4445     }
4446   while (list)
4447     {
4448       page = list->data;
4449       if (direction == STEP_PREV &&
4450           (!find_visible ||
4451            (gtk_widget_get_visible (page->child) &&
4452             (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
4453         return list;
4454       list = list->prev;
4455     }
4456   return NULL;
4457 }
4458 
4459 static void
gtk_notebook_snapshot_tabs(GtkGizmo * gizmo,GtkSnapshot * snapshot)4460 gtk_notebook_snapshot_tabs (GtkGizmo    *gizmo,
4461                             GtkSnapshot *snapshot)
4462 {
4463   GtkWidget *widget = gtk_widget_get_parent (GTK_WIDGET (gizmo));
4464   GtkNotebook *notebook = GTK_NOTEBOOK (gtk_widget_get_parent (widget));
4465   GtkNotebookPage *page;
4466   GList *children;
4467   gboolean showarrow;
4468   int step = STEP_PREV;
4469   gboolean is_rtl;
4470   GtkPositionType tab_pos;
4471   guint i;
4472 
4473   is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
4474   tab_pos = get_effective_tab_pos (notebook);
4475   showarrow = FALSE;
4476 
4477   if (!gtk_notebook_has_current_page (notebook))
4478     return;
4479 
4480   if (!notebook->first_tab)
4481     notebook->first_tab = notebook->children;
4482 
4483   if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, notebook->cur_page) ||
4484       !gtk_widget_get_mapped (notebook->cur_page->tab_label))
4485     {
4486       step = STEP_PREV;
4487     }
4488   else
4489     {
4490       switch (tab_pos)
4491         {
4492         case GTK_POS_TOP:
4493         case GTK_POS_BOTTOM:
4494           step = is_rtl ? STEP_PREV : STEP_NEXT;
4495           break;
4496         case GTK_POS_LEFT:
4497         case GTK_POS_RIGHT:
4498           step = STEP_PREV;
4499           break;
4500         default:
4501           g_assert_not_reached ();
4502           break;
4503         }
4504     }
4505 
4506   for (children = notebook->children; children; children = children->next)
4507     {
4508       page = children->data;
4509 
4510       if (!gtk_widget_get_visible (page->child) ||
4511           page == notebook->detached_tab)
4512         continue;
4513 
4514       if (!gtk_widget_get_mapped (page->tab_label))
4515         showarrow = TRUE;
4516 
4517       /* No point in keeping searching */
4518       if (showarrow)
4519         break;
4520     }
4521 
4522   for (children = gtk_notebook_search_page (notebook, NULL, step, TRUE);
4523        children;
4524        children = gtk_notebook_search_page (notebook, children, step, TRUE))
4525     {
4526       page = children->data;
4527 
4528       if (page == notebook->cur_page)
4529         break;
4530 
4531       if (!gtk_notebook_page_tab_label_is_visible (page))
4532         continue;
4533 
4534       gtk_widget_snapshot_child (GTK_WIDGET (gizmo), page->tab_widget, snapshot);
4535     }
4536 
4537   if (children != NULL)
4538     {
4539       GList *other_order = NULL;
4540 
4541       for (children = gtk_notebook_search_page (notebook, children, step, TRUE);
4542            children;
4543            children = gtk_notebook_search_page (notebook, children, step, TRUE))
4544         {
4545           page = children->data;
4546 
4547           if (!gtk_notebook_page_tab_label_is_visible (page))
4548             continue;
4549 
4550           other_order = g_list_prepend (other_order, page);
4551         }
4552 
4553       /* draw them with the opposite order */
4554       for (children = other_order; children; children = children->next)
4555         {
4556           page = children->data;
4557           gtk_widget_snapshot_child (GTK_WIDGET (gizmo), page->tab_widget, snapshot);
4558         }
4559 
4560       g_list_free (other_order);
4561     }
4562 
4563   if (showarrow && notebook->scrollable)
4564     {
4565       for (i = 0; i < 4; i++)
4566         {
4567           if (notebook->arrow_widget[i] == NULL)
4568             continue;
4569 
4570           gtk_widget_snapshot_child (GTK_WIDGET (gizmo), notebook->arrow_widget[i], snapshot);
4571         }
4572     }
4573 
4574   if (notebook->operation != DRAG_OPERATION_DETACH)
4575     gtk_widget_snapshot_child (GTK_WIDGET (gizmo), notebook->cur_page->tab_widget, snapshot);
4576 }
4577 
4578 /* Private GtkNotebook Size Allocate Functions:
4579  *
4580  * gtk_notebook_calculate_shown_tabs
4581  * gtk_notebook_calculate_tabs_allocation
4582  * gtk_notebook_calc_tabs
4583  */
4584 static void
gtk_notebook_allocate_arrows(GtkNotebook * notebook,GtkAllocation * allocation)4585 gtk_notebook_allocate_arrows (GtkNotebook   *notebook,
4586                               GtkAllocation *allocation)
4587 {
4588   GtkAllocation arrow_allocation;
4589   int size1, size2, min, nat;
4590   guint i, ii;
4591 
4592   switch (notebook->tab_pos)
4593     {
4594     case GTK_POS_TOP:
4595     case GTK_POS_BOTTOM:
4596       arrow_allocation.y = allocation->y;
4597       arrow_allocation.height = allocation->height;
4598       for (i = 0; i < 4; i++)
4599         {
4600           ii = i < 2 ? i : i ^ 1;
4601 
4602           if (notebook->arrow_widget[ii] == NULL)
4603             continue;
4604 
4605           gtk_widget_measure (notebook->arrow_widget[ii],
4606                               GTK_ORIENTATION_HORIZONTAL,
4607                               allocation->height,
4608                               &min, &nat,
4609                               NULL, NULL);
4610           if (i < 2)
4611             {
4612               arrow_allocation.x = allocation->x;
4613               arrow_allocation.width = min;
4614               gtk_widget_size_allocate (notebook->arrow_widget[ii],
4615                                         &arrow_allocation,
4616                                         -1);
4617               allocation->x += min;
4618               allocation->width -= min;
4619             }
4620           else
4621             {
4622               arrow_allocation.x = allocation->x + allocation->width - min;
4623               arrow_allocation.width = min;
4624               gtk_widget_size_allocate (notebook->arrow_widget[ii],
4625                                         &arrow_allocation,
4626                                         -1);
4627               allocation->width -= min;
4628             }
4629         }
4630       break;
4631 
4632     case GTK_POS_LEFT:
4633     case GTK_POS_RIGHT:
4634       if (notebook->arrow_widget[0] || notebook->arrow_widget[1])
4635         {
4636           gtk_notebook_measure_arrows (notebook,
4637                                        GTK_PACK_START,
4638                                        GTK_ORIENTATION_VERTICAL,
4639                                        allocation->width,
4640                                        &min, &nat,
4641                                        NULL, NULL);
4642           gtk_notebook_distribute_arrow_width (notebook, GTK_PACK_START, allocation->width, &size1, &size2);
4643           arrow_allocation.x = allocation->x;
4644           arrow_allocation.y = allocation->y;
4645           arrow_allocation.width = size1;
4646           arrow_allocation.height = min;
4647           if (notebook->arrow_widget[0])
4648             gtk_widget_size_allocate (notebook->arrow_widget[0], &arrow_allocation, -1);
4649           arrow_allocation.x += size1;
4650           arrow_allocation.width = size2;
4651           if (notebook->arrow_widget[1])
4652             gtk_widget_size_allocate (notebook->arrow_widget[1], &arrow_allocation, -1);
4653           allocation->y += min;
4654           allocation->height -= min;
4655         }
4656       if (notebook->arrow_widget[2] || notebook->arrow_widget[3])
4657         {
4658           gtk_notebook_measure_arrows (notebook,
4659                                        GTK_PACK_END,
4660                                        GTK_ORIENTATION_VERTICAL,
4661                                        allocation->width,
4662                                        &min, &nat,
4663                                        NULL, NULL);
4664           gtk_notebook_distribute_arrow_width (notebook, GTK_PACK_END, allocation->width, &size1, &size2);
4665           arrow_allocation.x = allocation->x;
4666           arrow_allocation.y = allocation->y + allocation->height - min;
4667           arrow_allocation.width = size1;
4668           arrow_allocation.height = min;
4669           if (notebook->arrow_widget[2])
4670             gtk_widget_size_allocate (notebook->arrow_widget[2], &arrow_allocation, -1);
4671           arrow_allocation.x += size1;
4672           arrow_allocation.width = size2;
4673           if (notebook->arrow_widget[3])
4674             gtk_widget_size_allocate (notebook->arrow_widget[3], &arrow_allocation, -1);
4675           allocation->height -= min;
4676         }
4677       break;
4678 
4679     default:
4680       g_assert_not_reached ();
4681       break;
4682     }
4683 }
4684 
4685 
4686 static void
gtk_notebook_tab_space(GtkNotebook * notebook,int notebook_width,int notebook_height,gboolean * show_arrows,GtkAllocation * tabs_allocation,int * tab_space)4687 gtk_notebook_tab_space (GtkNotebook   *notebook,
4688                         int            notebook_width,
4689                         int            notebook_height,
4690                         gboolean      *show_arrows,
4691                         GtkAllocation *tabs_allocation,
4692                         int           *tab_space)
4693 {
4694   GList *children;
4695   GtkPositionType tab_pos = get_effective_tab_pos (notebook);
4696 
4697   children = notebook->children;
4698 
4699   *tabs_allocation = (GtkAllocation) {0, 0, notebook_width, notebook_height};
4700 
4701   switch (tab_pos)
4702     {
4703     case GTK_POS_TOP:
4704     case GTK_POS_BOTTOM:
4705       while (children)
4706         {
4707           GtkNotebookPage *page;
4708 
4709           page = children->data;
4710           children = children->next;
4711 
4712           if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
4713               gtk_widget_get_visible (page->child))
4714             *tab_space += page->requisition.width;
4715         }
4716       break;
4717     case GTK_POS_RIGHT:
4718     case GTK_POS_LEFT:
4719       while (children)
4720         {
4721           GtkNotebookPage *page;
4722 
4723           page = children->data;
4724           children = children->next;
4725 
4726           if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
4727               gtk_widget_get_visible (page->child))
4728             *tab_space += page->requisition.height;
4729         }
4730       break;
4731 
4732     default:
4733       g_assert_not_reached ();
4734       break;
4735     }
4736 
4737   if (!notebook->scrollable)
4738     *show_arrows = FALSE;
4739   else
4740     {
4741       switch (tab_pos)
4742         {
4743         case GTK_POS_TOP:
4744         case GTK_POS_BOTTOM:
4745           if (*tab_space > tabs_allocation->width)
4746             {
4747               *show_arrows = TRUE;
4748 
4749               gtk_notebook_allocate_arrows (notebook, tabs_allocation);
4750 
4751               *tab_space = tabs_allocation->width;
4752             }
4753           break;
4754         case GTK_POS_LEFT:
4755         case GTK_POS_RIGHT:
4756           if (*tab_space > tabs_allocation->height)
4757             {
4758               *show_arrows = TRUE;
4759 
4760               gtk_notebook_allocate_arrows (notebook, tabs_allocation);
4761 
4762               *tab_space = tabs_allocation->height;
4763             }
4764           break;
4765 
4766         default:
4767           g_assert_not_reached ();
4768           break;
4769         }
4770     }
4771 }
4772 
4773 static void
gtk_notebook_calculate_shown_tabs(GtkNotebook * notebook,gboolean show_arrows,const GtkAllocation * tabs_allocation,int tab_space,GList ** last_child,int * n,int * remaining_space)4774 gtk_notebook_calculate_shown_tabs (GtkNotebook          *notebook,
4775                                    gboolean              show_arrows,
4776                                    const GtkAllocation  *tabs_allocation,
4777                                    int                   tab_space,
4778                                    GList               **last_child,
4779                                    int                  *n,
4780                                    int                  *remaining_space)
4781 {
4782   GList *children;
4783   GtkNotebookPage *page;
4784 
4785   if (show_arrows) /* first_tab <- focus_tab */
4786     {
4787       *remaining_space = tab_space;
4788 
4789       if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, notebook->cur_page) &&
4790           gtk_widget_get_visible (notebook->cur_page->child))
4791         {
4792           gtk_notebook_calc_tabs (notebook,
4793                                   notebook->focus_tab,
4794                                   &(notebook->focus_tab),
4795                                   remaining_space, STEP_NEXT);
4796         }
4797 
4798       if (tab_space <= 0 || *remaining_space <= 0)
4799         {
4800           /* show 1 tab */
4801           notebook->first_tab = notebook->focus_tab;
4802           *last_child = gtk_notebook_search_page (notebook, notebook->focus_tab,
4803                                                   STEP_NEXT, TRUE);
4804           *n = 1;
4805         }
4806       else
4807         {
4808           children = NULL;
4809 
4810           if (notebook->first_tab && notebook->first_tab != notebook->focus_tab)
4811             {
4812               /* Is first_tab really predecessor of focus_tab? */
4813               page = notebook->first_tab->data;
4814               if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
4815                   gtk_widget_get_visible (page->child))
4816                 for (children = notebook->focus_tab;
4817                      children && children != notebook->first_tab;
4818                      children = gtk_notebook_search_page (notebook,
4819                                                           children,
4820                                                           STEP_PREV,
4821                                                           TRUE));
4822             }
4823 
4824           if (!children)
4825             {
4826               if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, notebook->cur_page))
4827                 notebook->first_tab = notebook->focus_tab;
4828               else
4829                 notebook->first_tab = gtk_notebook_search_page (notebook, notebook->focus_tab,
4830                                                             STEP_NEXT, TRUE);
4831             }
4832           else
4833             /* calculate shown tabs counting backwards from the focus tab */
4834             gtk_notebook_calc_tabs (notebook,
4835                                     gtk_notebook_search_page (notebook,
4836                                                               notebook->focus_tab,
4837                                                               STEP_PREV,
4838                                                               TRUE),
4839                                     &(notebook->first_tab),
4840                                     remaining_space,
4841                                     STEP_PREV);
4842 
4843           if (*remaining_space < 0)
4844             {
4845               notebook->first_tab =
4846                 gtk_notebook_search_page (notebook, notebook->first_tab,
4847                                           STEP_NEXT, TRUE);
4848               if (!notebook->first_tab)
4849                 notebook->first_tab = notebook->focus_tab;
4850 
4851               *last_child = gtk_notebook_search_page (notebook, notebook->focus_tab,
4852                                                       STEP_NEXT, TRUE);
4853             }
4854           else /* focus_tab -> end */
4855             {
4856               if (!notebook->first_tab)
4857                 notebook->first_tab = gtk_notebook_search_page (notebook,
4858                                                             NULL,
4859                                                             STEP_NEXT,
4860                                                             TRUE);
4861               children = NULL;
4862               gtk_notebook_calc_tabs (notebook,
4863                                       gtk_notebook_search_page (notebook,
4864                                                                 notebook->focus_tab,
4865                                                                 STEP_NEXT,
4866                                                                 TRUE),
4867                                       &children,
4868                                       remaining_space,
4869                                       STEP_NEXT);
4870 
4871               if (*remaining_space <= 0)
4872                 *last_child = children;
4873               else /* start <- first_tab */
4874                 {
4875                   *last_child = NULL;
4876                   children = NULL;
4877 
4878                   gtk_notebook_calc_tabs (notebook,
4879                                           gtk_notebook_search_page (notebook,
4880                                                                     notebook->first_tab,
4881                                                                     STEP_PREV,
4882                                                                     TRUE),
4883                                           &children,
4884                                           remaining_space,
4885                                           STEP_PREV);
4886 
4887                   if (*remaining_space == 0)
4888                     notebook->first_tab = children;
4889                   else
4890                     notebook->first_tab = gtk_notebook_search_page(notebook,
4891                                                                children,
4892                                                                STEP_NEXT,
4893                                                                TRUE);
4894                 }
4895             }
4896 
4897           if (*remaining_space < 0)
4898             {
4899               /* calculate number of tabs */
4900               *remaining_space = - (*remaining_space);
4901               *n = 0;
4902 
4903               for (children = notebook->first_tab;
4904                    children && children != *last_child;
4905                    children = gtk_notebook_search_page (notebook, children,
4906                                                         STEP_NEXT, TRUE))
4907                 (*n)++;
4908             }
4909           else
4910             *remaining_space = 0;
4911         }
4912 
4913       /* unmap all non-visible tabs */
4914       for (children = gtk_notebook_search_page (notebook, NULL,
4915                                                 STEP_NEXT, TRUE);
4916            children && children != notebook->first_tab;
4917            children = gtk_notebook_search_page (notebook, children,
4918                                                 STEP_NEXT, TRUE))
4919         {
4920           page = children->data;
4921 
4922           if (page->tab_label &&
4923               NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
4924             gtk_widget_set_child_visible (page->tab_widget, FALSE);
4925         }
4926 
4927       for (children = *last_child; children;
4928            children = gtk_notebook_search_page (notebook, children,
4929                                                 STEP_NEXT, TRUE))
4930         {
4931           page = children->data;
4932 
4933           if (page->tab_label &&
4934               NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
4935             gtk_widget_set_child_visible (page->tab_widget, FALSE);
4936         }
4937     }
4938   else /* !show_arrows */
4939     {
4940       GtkOrientation tab_expand_orientation;
4941       int c = 0;
4942       *n = 0;
4943 
4944       if (notebook->tab_pos == GTK_POS_TOP || notebook->tab_pos == GTK_POS_BOTTOM)
4945         {
4946           tab_expand_orientation = GTK_ORIENTATION_HORIZONTAL;
4947           *remaining_space = tabs_allocation->width - tab_space;
4948         }
4949       else
4950         {
4951           tab_expand_orientation = GTK_ORIENTATION_VERTICAL;
4952           *remaining_space = tabs_allocation->height - tab_space;
4953         }
4954       children = notebook->children;
4955       notebook->first_tab = gtk_notebook_search_page (notebook, NULL,
4956                                                   STEP_NEXT, TRUE);
4957       while (children)
4958         {
4959           page = children->data;
4960           children = children->next;
4961 
4962           if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) ||
4963               !gtk_widget_get_visible (page->child))
4964             continue;
4965 
4966           c++;
4967 
4968           if (page->expand ||
4969               (gtk_widget_compute_expand (page->tab_label, tab_expand_orientation)))
4970             (*n)++;
4971         }
4972     }
4973 }
4974 
4975 static gboolean
get_allocate_at_bottom(GtkWidget * widget,int search_direction)4976 get_allocate_at_bottom (GtkWidget *widget,
4977                         int        search_direction)
4978 {
4979   gboolean is_rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
4980   GtkPositionType tab_pos = get_effective_tab_pos (GTK_NOTEBOOK (widget));
4981 
4982   switch (tab_pos)
4983     {
4984     case GTK_POS_TOP:
4985     case GTK_POS_BOTTOM:
4986       if (!is_rtl)
4987         return (search_direction == STEP_PREV);
4988       else
4989         return (search_direction == STEP_NEXT);
4990 
4991     case GTK_POS_RIGHT:
4992     case GTK_POS_LEFT:
4993       return (search_direction == STEP_PREV);
4994 
4995     default:
4996       g_assert_not_reached ();
4997       return FALSE;
4998     }
4999 }
5000 
5001 static void
gtk_notebook_calculate_tabs_allocation(GtkNotebook * notebook,GList ** children,GList * last_child,gboolean showarrow,int direction,int * remaining_space,int * expanded_tabs,const GtkAllocation * allocation)5002 gtk_notebook_calculate_tabs_allocation (GtkNotebook          *notebook,
5003                                         GList               **children,
5004                                         GList                *last_child,
5005                                         gboolean              showarrow,
5006                                         int                   direction,
5007                                         int                  *remaining_space,
5008                                         int                  *expanded_tabs,
5009                                         const GtkAllocation  *allocation)
5010 {
5011   GtkWidget *widget;
5012   GtkNotebookPage *page;
5013   gboolean allocate_at_bottom;
5014   int tab_extra_space;
5015   GtkPositionType tab_pos;
5016   int left_x, right_x, top_y, bottom_y, anchor;
5017   gboolean gap_left, packing_changed;
5018   GtkAllocation child_allocation;
5019   GtkOrientation tab_expand_orientation;
5020   graphene_rect_t drag_bounds;
5021 
5022   g_assert (notebook->cur_page != NULL);
5023 
5024   widget = GTK_WIDGET (notebook);
5025   tab_pos = get_effective_tab_pos (notebook);
5026   allocate_at_bottom = get_allocate_at_bottom (widget, direction);
5027 
5028   child_allocation = *allocation;
5029 
5030   switch (tab_pos)
5031     {
5032     case GTK_POS_BOTTOM:
5033     case GTK_POS_TOP:
5034       if (allocate_at_bottom)
5035         child_allocation.x += allocation->width;
5036       anchor = child_allocation.x;
5037       break;
5038 
5039     case GTK_POS_RIGHT:
5040     case GTK_POS_LEFT:
5041       if (allocate_at_bottom)
5042         child_allocation.y += allocation->height;
5043       anchor = child_allocation.y;
5044       break;
5045 
5046     default:
5047       g_assert_not_reached ();
5048       anchor = 0;
5049       break;
5050     }
5051 
5052   if (!gtk_widget_compute_bounds (notebook->cur_page->tab_widget, notebook->cur_page->tab_widget, &drag_bounds))
5053     graphene_rect_init_from_rect (&drag_bounds, graphene_rect_zero ());
5054 
5055   left_x   = CLAMP (notebook->mouse_x - notebook->drag_offset_x,
5056                     allocation->x, allocation->x + allocation->width - drag_bounds.size.width);
5057   top_y    = CLAMP (notebook->mouse_y - notebook->drag_offset_y,
5058                     allocation->y, allocation->y + allocation->height - drag_bounds.size.height);
5059   right_x  = left_x + drag_bounds.size.width;
5060   bottom_y = top_y + drag_bounds.size.height;
5061   gap_left = packing_changed = FALSE;
5062 
5063   if (notebook->tab_pos == GTK_POS_TOP || notebook->tab_pos == GTK_POS_BOTTOM)
5064     tab_expand_orientation = GTK_ORIENTATION_HORIZONTAL;
5065   else
5066     tab_expand_orientation = GTK_ORIENTATION_VERTICAL;
5067 
5068   while (*children && *children != last_child)
5069     {
5070       page = (*children)->data;
5071 
5072       if (direction == STEP_NEXT)
5073         *children = gtk_notebook_search_page (notebook, *children, direction, TRUE);
5074       else
5075         {
5076           *children = (*children)->next;
5077           continue;
5078         }
5079 
5080       if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5081         continue;
5082 
5083       tab_extra_space = 0;
5084       if (*expanded_tabs && (showarrow || page->expand || gtk_widget_compute_expand (page->tab_label, tab_expand_orientation)))
5085         {
5086           tab_extra_space = *remaining_space / *expanded_tabs;
5087           *remaining_space -= tab_extra_space;
5088           (*expanded_tabs)--;
5089         }
5090 
5091       switch (tab_pos)
5092         {
5093         case GTK_POS_TOP:
5094         case GTK_POS_BOTTOM:
5095           child_allocation.width = MAX (1, page->requisition.width + tab_extra_space);
5096 
5097           /* make sure that the reordered tab doesn't go past the last position */
5098           if (notebook->operation == DRAG_OPERATION_REORDER &&
5099               !gap_left && packing_changed)
5100             {
5101               if (!allocate_at_bottom)
5102                 {
5103                   if (left_x >= anchor)
5104                     {
5105                       left_x = notebook->drag_surface_x = anchor;
5106                       anchor += drag_bounds.size.width;
5107                     }
5108                 }
5109               else
5110                 {
5111                   if (right_x <= anchor)
5112                     {
5113                       anchor -= drag_bounds.size.width;
5114                       left_x = notebook->drag_surface_x = anchor;
5115                     }
5116                 }
5117 
5118               gap_left = TRUE;
5119             }
5120 
5121           if (notebook->operation == DRAG_OPERATION_REORDER && page == notebook->cur_page)
5122             {
5123               notebook->drag_surface_x = left_x;
5124               notebook->drag_surface_y = child_allocation.y;
5125             }
5126           else
5127             {
5128               if (allocate_at_bottom)
5129                 anchor -= child_allocation.width;
5130 
5131               if (notebook->operation == DRAG_OPERATION_REORDER)
5132                 {
5133                   if (!allocate_at_bottom &&
5134                       left_x >= anchor &&
5135                       left_x <= anchor + child_allocation.width / 2)
5136                     anchor += drag_bounds.size.width;
5137                   else if (allocate_at_bottom &&
5138                            right_x >= anchor + child_allocation.width / 2 &&
5139                            right_x <= anchor + child_allocation.width)
5140                     anchor -= drag_bounds.size.width;
5141                 }
5142 
5143               child_allocation.x = anchor;
5144             }
5145 
5146           break;
5147         case GTK_POS_LEFT:
5148         case GTK_POS_RIGHT:
5149           child_allocation.height = MAX (1, page->requisition.height + tab_extra_space);
5150 
5151           /* make sure that the reordered tab doesn't go past the last position */
5152           if (notebook->operation == DRAG_OPERATION_REORDER &&
5153               !gap_left && packing_changed)
5154             {
5155               if (!allocate_at_bottom && top_y >= anchor)
5156                 {
5157                   top_y = notebook->drag_surface_y = anchor;
5158                   anchor += drag_bounds.size.height;
5159                 }
5160 
5161               gap_left = TRUE;
5162             }
5163 
5164           if (notebook->operation == DRAG_OPERATION_REORDER && page == notebook->cur_page)
5165             {
5166               notebook->drag_surface_x = child_allocation.x;
5167               notebook->drag_surface_y = top_y;
5168             }
5169           else
5170             {
5171               if (allocate_at_bottom)
5172                 anchor -= child_allocation.height;
5173 
5174               if (notebook->operation == DRAG_OPERATION_REORDER)
5175                 {
5176                   if (!allocate_at_bottom &&
5177                       top_y >= anchor &&
5178                       top_y <= anchor + child_allocation.height / 2)
5179                     anchor += drag_bounds.size.height;
5180                   else if (allocate_at_bottom &&
5181                            bottom_y >= anchor + child_allocation.height / 2 &&
5182                            bottom_y <= anchor + child_allocation.height)
5183                     anchor -= drag_bounds.size.height;
5184                 }
5185 
5186               child_allocation.y = anchor;
5187             }
5188           break;
5189 
5190         default:
5191           g_assert_not_reached ();
5192           break;
5193         }
5194 
5195       /* set child visible */
5196       if (page->tab_label)
5197         gtk_widget_set_child_visible (page->tab_widget, TRUE);
5198 
5199       if (page == notebook->cur_page && notebook->operation == DRAG_OPERATION_REORDER)
5200         {
5201           GtkAllocation fixed_allocation = { notebook->drag_surface_x, notebook->drag_surface_y,
5202                                              child_allocation.width, child_allocation.height };
5203           gtk_widget_size_allocate (page->tab_widget, &fixed_allocation, -1);
5204         }
5205       else if (page == notebook->detached_tab && notebook->operation == DRAG_OPERATION_DETACH)
5206         {
5207           /* needs to be allocated at 0,0
5208            * to be shown in the drag window */
5209           GtkAllocation fixed_allocation = { 0, 0, child_allocation.width, child_allocation.height };
5210           gtk_widget_size_allocate (page->tab_widget, &fixed_allocation, -1);
5211         }
5212       else if (gtk_notebook_page_tab_label_is_visible (page))
5213         {
5214           gtk_widget_size_allocate (page->tab_widget, &child_allocation, -1);
5215         }
5216 
5217       /* calculate whether to leave a gap based on reorder operation or not */
5218       switch (tab_pos)
5219         {
5220         case GTK_POS_TOP:
5221         case GTK_POS_BOTTOM:
5222           if (notebook->operation != DRAG_OPERATION_REORDER || page != notebook->cur_page)
5223             {
5224               if (notebook->operation == DRAG_OPERATION_REORDER)
5225                 {
5226                   if (!allocate_at_bottom &&
5227                       left_x >  anchor + child_allocation.width / 2 &&
5228                       left_x <= anchor + child_allocation.width)
5229                     anchor += drag_bounds.size.width;
5230                   else if (allocate_at_bottom &&
5231                            right_x >= anchor &&
5232                            right_x <= anchor + child_allocation.width / 2)
5233                     anchor -= drag_bounds.size.width;
5234                 }
5235 
5236               if (!allocate_at_bottom)
5237                 anchor += child_allocation.width;
5238             }
5239 
5240           break;
5241         case GTK_POS_LEFT:
5242         case GTK_POS_RIGHT:
5243           if (notebook->operation != DRAG_OPERATION_REORDER || page != notebook->cur_page)
5244             {
5245               if (notebook->operation == DRAG_OPERATION_REORDER)
5246                 {
5247                   if (!allocate_at_bottom &&
5248                       top_y >= anchor + child_allocation.height / 2 &&
5249                       top_y <= anchor + child_allocation.height)
5250                     anchor += drag_bounds.size.height;
5251                   else if (allocate_at_bottom &&
5252                            bottom_y >= anchor &&
5253                            bottom_y <= anchor + child_allocation.height / 2)
5254                     anchor -= drag_bounds.size.height;
5255                 }
5256 
5257               if (!allocate_at_bottom)
5258                 anchor += child_allocation.height;
5259             }
5260 
5261           break;
5262         default:
5263           g_assert_not_reached ();
5264       break;
5265         }
5266     }
5267 
5268   /* Don't move the current tab past the last position during tabs reordering */
5269   if (notebook->operation == DRAG_OPERATION_REORDER &&
5270       direction == STEP_NEXT)
5271     {
5272       switch (tab_pos)
5273         {
5274         case GTK_POS_TOP:
5275         case GTK_POS_BOTTOM:
5276           if (allocate_at_bottom)
5277             anchor -= drag_bounds.size.width;
5278 
5279           if ((!allocate_at_bottom && notebook->drag_surface_x > anchor) ||
5280               (allocate_at_bottom && notebook->drag_surface_x < anchor))
5281             notebook->drag_surface_x = anchor;
5282           break;
5283         case GTK_POS_LEFT:
5284         case GTK_POS_RIGHT:
5285           if (allocate_at_bottom)
5286             anchor -= drag_bounds.size.height;
5287 
5288           if ((!allocate_at_bottom && notebook->drag_surface_y > anchor) ||
5289               (allocate_at_bottom && notebook->drag_surface_y < anchor))
5290             notebook->drag_surface_y = anchor;
5291           break;
5292         default:
5293           g_assert_not_reached ();
5294           break;
5295         }
5296     }
5297 }
5298 
5299 static void
gtk_notebook_pages_allocate(GtkNotebook * notebook,int width,int height)5300 gtk_notebook_pages_allocate (GtkNotebook *notebook,
5301                              int          width,
5302                              int          height)
5303 {
5304   GList *children = NULL;
5305   GList *last_child = NULL;
5306   gboolean showarrow = FALSE;
5307   GtkAllocation tabs_allocation;
5308   int tab_space, remaining_space;
5309   int expanded_tabs;
5310 
5311   if (!notebook->show_tabs || !gtk_notebook_has_current_page (notebook))
5312     return;
5313 
5314   tab_space = remaining_space = 0;
5315   expanded_tabs = 1;
5316 
5317   gtk_notebook_tab_space (notebook, width, height,
5318                           &showarrow, &tabs_allocation, &tab_space);
5319 
5320   gtk_notebook_calculate_shown_tabs (notebook, showarrow,
5321                                      &tabs_allocation, tab_space, &last_child,
5322                                      &expanded_tabs, &remaining_space);
5323 
5324   children = notebook->first_tab;
5325   gtk_notebook_calculate_tabs_allocation (notebook, &children, last_child,
5326                                           showarrow, STEP_NEXT,
5327                                           &remaining_space, &expanded_tabs, &tabs_allocation);
5328   if (children && children != last_child)
5329     {
5330       children = notebook->children;
5331       gtk_notebook_calculate_tabs_allocation (notebook, &children, last_child,
5332                                               showarrow, STEP_PREV,
5333                                               &remaining_space, &expanded_tabs, &tabs_allocation);
5334     }
5335 
5336   if (!notebook->first_tab)
5337     notebook->first_tab = notebook->children;
5338 }
5339 
5340 static void
gtk_notebook_calc_tabs(GtkNotebook * notebook,GList * start,GList ** end,int * tab_space,guint direction)5341 gtk_notebook_calc_tabs (GtkNotebook  *notebook,
5342                         GList        *start,
5343                         GList       **end,
5344                         int          *tab_space,
5345                         guint         direction)
5346 {
5347   GtkNotebookPage *page = NULL;
5348   GList *children;
5349   GList *last_calculated_child = NULL;
5350   GtkPositionType tab_pos = get_effective_tab_pos (notebook);
5351 
5352   if (!start)
5353     return;
5354 
5355   children = start;
5356 
5357   switch (tab_pos)
5358     {
5359     case GTK_POS_TOP:
5360     case GTK_POS_BOTTOM:
5361       while (children)
5362         {
5363           page = children->data;
5364           if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5365               gtk_widget_get_visible (page->child))
5366             {
5367               *tab_space -= page->requisition.width;
5368               if (*tab_space < 0 || children == *end)
5369                 {
5370                   if (*tab_space < 0)
5371                     {
5372                       *tab_space = - (*tab_space +
5373                                       page->requisition.width);
5374 
5375                       if (*tab_space == 0 && direction == STEP_PREV)
5376                         children = last_calculated_child;
5377 
5378                       *end = children;
5379                     }
5380                   return;
5381                 }
5382 
5383               last_calculated_child = children;
5384             }
5385           if (direction == STEP_NEXT)
5386             children = children->next;
5387           else
5388             children = children->prev;
5389         }
5390       break;
5391     case GTK_POS_LEFT:
5392     case GTK_POS_RIGHT:
5393       while (children)
5394         {
5395           page = children->data;
5396           if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5397               gtk_widget_get_visible (page->child))
5398             {
5399               *tab_space -= page->requisition.height;
5400               if (*tab_space < 0 || children == *end)
5401                 {
5402                   if (*tab_space < 0)
5403                     {
5404                       *tab_space = - (*tab_space + page->requisition.height);
5405 
5406                       if (*tab_space == 0 && direction == STEP_PREV)
5407                         children = last_calculated_child;
5408 
5409                       *end = children;
5410                     }
5411                   return;
5412                 }
5413 
5414               last_calculated_child = children;
5415             }
5416           if (direction == STEP_NEXT)
5417             children = children->next;
5418           else
5419             children = children->prev;
5420         }
5421       break;
5422     default:
5423       g_assert_not_reached ();
5424       break;
5425     }
5426 }
5427 
5428 /* Private GtkNotebook Page Switch Methods:
5429  *
5430  * gtk_notebook_real_switch_page
5431  */
5432 static void
gtk_notebook_real_switch_page(GtkNotebook * notebook,GtkWidget * child,guint page_num)5433 gtk_notebook_real_switch_page (GtkNotebook     *notebook,
5434                                GtkWidget*       child,
5435                                guint            page_num)
5436 {
5437   GList *list = gtk_notebook_find_child (notebook, GTK_WIDGET (child));
5438   GtkNotebookPage *page = GTK_NOTEBOOK_PAGE_FROM_LIST (list);
5439   gboolean child_has_focus;
5440 
5441   if (notebook->cur_page == page || !gtk_widget_get_visible (GTK_WIDGET (child)))
5442     return;
5443 
5444   /* save the value here, changing visibility changes focus */
5445   child_has_focus = notebook->child_has_focus;
5446 
5447   if (notebook->cur_page)
5448     {
5449       GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (notebook));
5450       GtkWidget *focus = gtk_root_get_focus (root);
5451       if (focus)
5452         child_has_focus = gtk_widget_is_ancestor (focus, notebook->cur_page->child);
5453       gtk_widget_unset_state_flags (notebook->cur_page->tab_widget, GTK_STATE_FLAG_CHECKED);
5454 
5455       gtk_accessible_update_state (GTK_ACCESSIBLE (notebook->cur_page->tab_widget),
5456                                    GTK_ACCESSIBLE_STATE_SELECTED, FALSE,
5457                                    -1);
5458     }
5459 
5460   notebook->cur_page = page;
5461   gtk_widget_set_state_flags (page->tab_widget, GTK_STATE_FLAG_CHECKED, FALSE);
5462   gtk_widget_set_visible (notebook->header_widget, notebook->show_tabs);
5463 
5464   if (gtk_widget_get_realized (GTK_WIDGET (notebook)))
5465     gtk_widget_realize_at_context (notebook->cur_page->tab_widget);
5466 
5467   gtk_accessible_update_state (GTK_ACCESSIBLE (notebook->cur_page->tab_widget),
5468                                GTK_ACCESSIBLE_STATE_SELECTED, TRUE,
5469                                -1);
5470 
5471   if (!notebook->focus_tab ||
5472       notebook->focus_tab->data != (gpointer) notebook->cur_page)
5473     notebook->focus_tab =
5474       g_list_find (notebook->children, notebook->cur_page);
5475 
5476   gtk_stack_set_visible_child (GTK_STACK (notebook->stack_widget), notebook->cur_page->child);
5477   gtk_widget_set_child_visible (notebook->cur_page->tab_widget, TRUE);
5478 
5479   /* If the focus was on the previous page, move it to the first
5480    * element on the new page, if possible, or if not, to the
5481    * notebook itself.
5482    */
5483   if (child_has_focus)
5484     {
5485       if (notebook->cur_page->last_focus_child &&
5486           gtk_widget_is_ancestor (notebook->cur_page->last_focus_child, notebook->cur_page->child))
5487         gtk_widget_grab_focus (notebook->cur_page->last_focus_child);
5488       else
5489         if (!gtk_widget_child_focus (notebook->cur_page->child, GTK_DIR_TAB_FORWARD))
5490           gtk_widget_grab_focus (GTK_WIDGET (notebook));
5491     }
5492 
5493   update_arrow_state (notebook);
5494 
5495   gtk_widget_queue_resize (GTK_WIDGET (notebook));
5496   gtk_widget_queue_resize (notebook->tabs_widget);
5497   g_object_notify_by_pspec (G_OBJECT (notebook), properties[PROP_PAGE]);
5498 }
5499 
5500 /* Private GtkNotebook Page Switch Functions:
5501  *
5502  * gtk_notebook_switch_page
5503  * gtk_notebook_page_select
5504  * gtk_notebook_switch_focus_tab
5505  * gtk_notebook_menu_switch_page
5506  */
5507 static void
gtk_notebook_switch_page(GtkNotebook * notebook,GtkNotebookPage * page)5508 gtk_notebook_switch_page (GtkNotebook     *notebook,
5509                           GtkNotebookPage *page)
5510 {
5511   guint page_num;
5512 
5513   if (notebook->cur_page == page)
5514     return;
5515 
5516   page_num = g_list_index (notebook->children, page);
5517 
5518   g_signal_emit (notebook,
5519                  notebook_signals[SWITCH_PAGE],
5520                  0,
5521                  page->child,
5522                  page_num);
5523 }
5524 
5525 static int
gtk_notebook_page_select(GtkNotebook * notebook,gboolean move_focus)5526 gtk_notebook_page_select (GtkNotebook *notebook,
5527                           gboolean     move_focus)
5528 {
5529   GtkNotebookPage *page;
5530   GtkDirectionType dir;
5531   GtkPositionType tab_pos = get_effective_tab_pos (notebook);
5532 
5533   if (!notebook->focus_tab)
5534     return FALSE;
5535 
5536   page = notebook->focus_tab->data;
5537   gtk_notebook_switch_page (notebook, page);
5538 
5539   if (move_focus)
5540     {
5541       switch (tab_pos)
5542         {
5543         case GTK_POS_TOP:
5544           dir = GTK_DIR_DOWN;
5545           break;
5546         case GTK_POS_BOTTOM:
5547           dir = GTK_DIR_UP;
5548           break;
5549         case GTK_POS_LEFT:
5550           dir = GTK_DIR_RIGHT;
5551           break;
5552         case GTK_POS_RIGHT:
5553           dir = GTK_DIR_LEFT;
5554           break;
5555         default:
5556           g_assert_not_reached ();
5557           dir = GTK_DIR_DOWN;
5558           break;
5559         }
5560 
5561       if (gtk_widget_child_focus (page->child, dir))
5562         return TRUE;
5563     }
5564   return FALSE;
5565 }
5566 
5567 static void
gtk_notebook_switch_focus_tab(GtkNotebook * notebook,GList * new_child)5568 gtk_notebook_switch_focus_tab (GtkNotebook *notebook,
5569                                GList       *new_child)
5570 {
5571   GtkNotebookPage *page;
5572 
5573   if (notebook->focus_tab == new_child)
5574     return;
5575 
5576   notebook->focus_tab = new_child;
5577 
5578   if (!notebook->show_tabs || !notebook->focus_tab)
5579     return;
5580 
5581   page = notebook->focus_tab->data;
5582   gtk_notebook_switch_page (notebook, page);
5583 }
5584 
5585 static void
gtk_notebook_menu_switch_page(GtkWidget * widget,GtkNotebookPage * page)5586 gtk_notebook_menu_switch_page (GtkWidget       *widget,
5587                                GtkNotebookPage *page)
5588 {
5589   GtkNotebook *notebook;
5590   GList *children;
5591   guint page_num;
5592 
5593   notebook = GTK_NOTEBOOK (gtk_widget_get_ancestor (widget, GTK_TYPE_NOTEBOOK));
5594 
5595   gtk_popover_popdown (GTK_POPOVER (notebook->menu));
5596 
5597   if (notebook->cur_page == page)
5598     return;
5599 
5600   page_num = 0;
5601   children = notebook->children;
5602   while (children && children->data != page)
5603     {
5604       children = children->next;
5605       page_num++;
5606     }
5607 
5608   g_signal_emit (notebook,
5609                  notebook_signals[SWITCH_PAGE],
5610                  0,
5611                  page->child,
5612                  page_num);
5613 }
5614 
5615 /* Private GtkNotebook Menu Functions:
5616  *
5617  * gtk_notebook_menu_item_create
5618  * gtk_notebook_menu_item_recreate
5619  * gtk_notebook_menu_label_unparent
5620  */
5621 static void
gtk_notebook_menu_item_create(GtkNotebook * notebook,GtkNotebookPage * page)5622 gtk_notebook_menu_item_create (GtkNotebook *notebook,
5623                                GtkNotebookPage *page)
5624 {
5625   GtkWidget *menu_item;
5626 
5627   if (page->default_menu)
5628     {
5629       if (GTK_IS_LABEL (page->tab_label))
5630         page->menu_label = gtk_label_new (gtk_label_get_text (GTK_LABEL (page->tab_label)));
5631       else
5632         page->menu_label = gtk_label_new ("");
5633       g_object_ref_sink (page->menu_label);
5634       gtk_widget_set_halign (page->menu_label, GTK_ALIGN_START);
5635       gtk_widget_set_valign (page->menu_label, GTK_ALIGN_CENTER);
5636     }
5637 
5638   menu_item = gtk_button_new ();
5639   gtk_button_set_has_frame (GTK_BUTTON (menu_item), FALSE);
5640   gtk_button_set_child (GTK_BUTTON (menu_item), page->menu_label);
5641   gtk_box_append (GTK_BOX (notebook->menu_box), menu_item);
5642   g_signal_connect (menu_item, "clicked",
5643                     G_CALLBACK (gtk_notebook_menu_switch_page), page);
5644   if (!gtk_widget_get_visible (page->child))
5645     gtk_widget_hide (menu_item);
5646 }
5647 
5648 static void
gtk_notebook_menu_item_recreate(GtkNotebook * notebook,GList * list)5649 gtk_notebook_menu_item_recreate (GtkNotebook *notebook,
5650                                  GList       *list)
5651 {
5652   GtkNotebookPage *page = list->data;
5653   GtkWidget *menu_item = gtk_widget_get_parent (page->menu_label);
5654 
5655   gtk_button_set_child (GTK_BUTTON (menu_item), NULL);
5656   gtk_box_remove (GTK_BOX (notebook->menu_box), menu_item);
5657   gtk_notebook_menu_item_create (notebook, page);
5658 }
5659 
5660 static void
gtk_notebook_menu_label_unparent(GtkWidget * widget)5661 gtk_notebook_menu_label_unparent (GtkWidget *widget)
5662 {
5663   gtk_button_set_child (GTK_BUTTON (widget), NULL);
5664 }
5665 
5666 /* Public GtkNotebook Page Insert/Remove Methods :
5667  *
5668  * gtk_notebook_append_page
5669  * gtk_notebook_append_page_menu
5670  * gtk_notebook_prepend_page
5671  * gtk_notebook_prepend_page_menu
5672  * gtk_notebook_insert_page
5673  * gtk_notebook_insert_page_menu
5674  * gtk_notebook_remove_page
5675  */
5676 /**
5677  * gtk_notebook_append_page:
5678  * @notebook: a `GtkNotebook`
5679  * @child: the `GtkWidget` to use as the contents of the page
5680  * @tab_label: (nullable): the `GtkWidget` to be used as the label
5681  *   for the page, or %NULL to use the default label, “page N”
5682  *
5683  * Appends a page to @notebook.
5684  *
5685  * Returns: the index (starting from 0) of the appended
5686  *   page in the notebook, or -1 if function fails
5687  */
5688 int
gtk_notebook_append_page(GtkNotebook * notebook,GtkWidget * child,GtkWidget * tab_label)5689 gtk_notebook_append_page (GtkNotebook *notebook,
5690                           GtkWidget   *child,
5691                           GtkWidget   *tab_label)
5692 {
5693   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
5694   g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
5695   g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
5696 
5697   return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, -1);
5698 }
5699 
5700 /**
5701  * gtk_notebook_append_page_menu:
5702  * @notebook: a `GtkNotebook`
5703  * @child: the `GtkWidget` to use as the contents of the page
5704  * @tab_label: (nullable): the `GtkWidget` to be used as the label
5705  *   for the page, or %NULL to use the default label, “page N”
5706  * @menu_label: (nullable): the widget to use as a label for the
5707  *   page-switch menu, if that is enabled. If %NULL, and @tab_label
5708  *   is a `GtkLabel` or %NULL, then the menu label will be a newly
5709  *   created label with the same text as @tab_label; if @tab_label
5710  *   is not a `GtkLabel`, @menu_label must be specified if the
5711  *   page-switch menu is to be used.
5712  *
5713  * Appends a page to @notebook, specifying the widget to use as the
5714  * label in the popup menu.
5715  *
5716  * Returns: the index (starting from 0) of the appended
5717  *   page in the notebook, or -1 if function fails
5718  */
5719 int
gtk_notebook_append_page_menu(GtkNotebook * notebook,GtkWidget * child,GtkWidget * tab_label,GtkWidget * menu_label)5720 gtk_notebook_append_page_menu (GtkNotebook *notebook,
5721                                GtkWidget   *child,
5722                                GtkWidget   *tab_label,
5723                                GtkWidget   *menu_label)
5724 {
5725   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
5726   g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
5727   g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
5728   g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
5729 
5730   return gtk_notebook_insert_page_menu (notebook, child, tab_label, menu_label, -1);
5731 }
5732 
5733 /**
5734  * gtk_notebook_prepend_page:
5735  * @notebook: a `GtkNotebook`
5736  * @child: the `GtkWidget` to use as the contents of the page
5737  * @tab_label: (nullable): the `GtkWidget` to be used as the label
5738  *   for the page, or %NULL to use the default label, “page N”
5739  *
5740  * Prepends a page to @notebook.
5741  *
5742  * Returns: the index (starting from 0) of the prepended
5743  *   page in the notebook, or -1 if function fails
5744  */
5745 int
gtk_notebook_prepend_page(GtkNotebook * notebook,GtkWidget * child,GtkWidget * tab_label)5746 gtk_notebook_prepend_page (GtkNotebook *notebook,
5747                            GtkWidget   *child,
5748                            GtkWidget   *tab_label)
5749 {
5750   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
5751   g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
5752   g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
5753 
5754   return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, 0);
5755 }
5756 
5757 /**
5758  * gtk_notebook_prepend_page_menu:
5759  * @notebook: a `GtkNotebook`
5760  * @child: the `GtkWidget` to use as the contents of the page
5761  * @tab_label: (nullable): the `GtkWidget` to be used as the label
5762  *   for the page, or %NULL to use the default label, “page N”
5763  * @menu_label: (nullable): the widget to use as a label for the
5764  *   page-switch menu, if that is enabled. If %NULL, and @tab_label
5765  *   is a `GtkLabel` or %NULL, then the menu label will be a newly
5766  *   created label with the same text as @tab_label; if @tab_label
5767  *   is not a `GtkLabel`, @menu_label must be specified if the
5768  *   page-switch menu is to be used.
5769  *
5770  * Prepends a page to @notebook, specifying the widget to use as the
5771  * label in the popup menu.
5772  *
5773  * Returns: the index (starting from 0) of the prepended
5774  *   page in the notebook, or -1 if function fails
5775  */
5776 int
gtk_notebook_prepend_page_menu(GtkNotebook * notebook,GtkWidget * child,GtkWidget * tab_label,GtkWidget * menu_label)5777 gtk_notebook_prepend_page_menu (GtkNotebook *notebook,
5778                                 GtkWidget   *child,
5779                                 GtkWidget   *tab_label,
5780                                 GtkWidget   *menu_label)
5781 {
5782   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
5783   g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
5784   g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
5785   g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
5786 
5787   return gtk_notebook_insert_page_menu (notebook, child, tab_label, menu_label, 0);
5788 }
5789 
5790 /**
5791  * gtk_notebook_insert_page:
5792  * @notebook: a `GtkNotebook`
5793  * @child: the `GtkWidget` to use as the contents of the page
5794  * @tab_label: (nullable): the `GtkWidget` to be used as the label
5795  *   for the page, or %NULL to use the default label, “page N”
5796  * @position: the index (starting at 0) at which to insert the page,
5797  *   or -1 to append the page after all other pages
5798  *
5799  * Insert a page into @notebook at the given position.
5800  *
5801  * Returns: the index (starting from 0) of the inserted
5802  *   page in the notebook, or -1 if function fails
5803  */
5804 int
gtk_notebook_insert_page(GtkNotebook * notebook,GtkWidget * child,GtkWidget * tab_label,int position)5805 gtk_notebook_insert_page (GtkNotebook *notebook,
5806                           GtkWidget   *child,
5807                           GtkWidget   *tab_label,
5808                           int          position)
5809 {
5810   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
5811   g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
5812   g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
5813 
5814   return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, position);
5815 }
5816 
5817 
5818 static int
gtk_notebook_page_compare_tab(gconstpointer a,gconstpointer b)5819 gtk_notebook_page_compare_tab (gconstpointer a,
5820                                gconstpointer b)
5821 {
5822   return (((GtkNotebookPage *) a)->tab_label != b);
5823 }
5824 
5825 static gboolean
gtk_notebook_mnemonic_activate_switch_page(GtkWidget * child,gboolean overload,gpointer data)5826 gtk_notebook_mnemonic_activate_switch_page (GtkWidget *child,
5827                                             gboolean overload,
5828                                             gpointer data)
5829 {
5830   GtkNotebook *notebook = GTK_NOTEBOOK (data);
5831   GList *list;
5832 
5833   list = g_list_find_custom (notebook->children, child,
5834                              gtk_notebook_page_compare_tab);
5835   if (list)
5836     {
5837       GtkNotebookPage *page = list->data;
5838 
5839       gtk_widget_grab_focus (GTK_WIDGET (notebook));    /* Do this first to avoid focusing new page */
5840       gtk_notebook_switch_page (notebook, page);
5841       focus_tabs_in (notebook);
5842     }
5843 
5844   return TRUE;
5845 }
5846 
5847 /**
5848  * gtk_notebook_insert_page_menu:
5849  * @notebook: a `GtkNotebook`
5850  * @child: the `GtkWidget` to use as the contents of the page
5851  * @tab_label: (nullable): the `GtkWidget` to be used as the label
5852  *   for the page, or %NULL to use the default label, “page N”
5853  * @menu_label: (nullable): the widget to use as a label for the
5854  *   page-switch menu, if that is enabled. If %NULL, and @tab_label
5855  *   is a `GtkLabel` or %NULL, then the menu label will be a newly
5856  *   created label with the same text as @tab_label; if @tab_label
5857  *   is not a `GtkLabel`, @menu_label must be specified if the
5858  *   page-switch menu is to be used.
5859  * @position: the index (starting at 0) at which to insert the page,
5860  *   or -1 to append the page after all other pages.
5861  *
5862  * Insert a page into @notebook at the given position, specifying
5863  * the widget to use as the label in the popup menu.
5864  *
5865  * Returns: the index (starting from 0) of the inserted
5866  *   page in the notebook
5867  */
5868 int
gtk_notebook_insert_page_menu(GtkNotebook * notebook,GtkWidget * child,GtkWidget * tab_label,GtkWidget * menu_label,int position)5869 gtk_notebook_insert_page_menu (GtkNotebook *notebook,
5870                                GtkWidget   *child,
5871                                GtkWidget   *tab_label,
5872                                GtkWidget   *menu_label,
5873                                int          position)
5874 {
5875   GtkNotebookClass *class;
5876 
5877   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
5878   g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
5879   g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
5880   g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
5881 
5882   class = GTK_NOTEBOOK_GET_CLASS (notebook);
5883 
5884   return (class->insert_page) (notebook, child, tab_label, menu_label, position);
5885 }
5886 
5887 /**
5888  * gtk_notebook_remove_page:
5889  * @notebook: a `GtkNotebook`
5890  * @page_num: the index of a notebook page, starting
5891  *   from 0. If -1, the last page will be removed.
5892  *
5893  * Removes a page from the notebook given its index
5894  * in the notebook.
5895  */
5896 void
gtk_notebook_remove_page(GtkNotebook * notebook,int page_num)5897 gtk_notebook_remove_page (GtkNotebook *notebook,
5898                           int          page_num)
5899 {
5900   GList *list = NULL;
5901 
5902   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
5903 
5904   if (page_num >= 0)
5905     list = g_list_nth (notebook->children, page_num);
5906   else
5907     list = g_list_last (notebook->children);
5908 
5909   if (list)
5910     gtk_notebook_remove (notebook, ((GtkNotebookPage *) list->data)->child);
5911 }
5912 
5913 /* Public GtkNotebook Page Switch Methods :
5914  * gtk_notebook_get_current_page
5915  * gtk_notebook_page_num
5916  * gtk_notebook_set_current_page
5917  * gtk_notebook_next_page
5918  * gtk_notebook_prev_page
5919  */
5920 
5921 /**
5922  * gtk_notebook_get_current_page: (attributes org.gtk.Method.get_property=page)
5923  * @notebook: a `GtkNotebook`
5924  *
5925  * Returns the page number of the current page.
5926  *
5927  * Returns: the index (starting from 0) of the current
5928  *   page in the notebook. If the notebook has no pages,
5929  *   then -1 will be returned.
5930  */
5931 int
gtk_notebook_get_current_page(GtkNotebook * notebook)5932 gtk_notebook_get_current_page (GtkNotebook *notebook)
5933 {
5934   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
5935 
5936   if (!notebook->cur_page)
5937     return -1;
5938 
5939   return g_list_index (notebook->children, notebook->cur_page);
5940 }
5941 
5942 /**
5943  * gtk_notebook_get_nth_page:
5944  * @notebook: a `GtkNotebook`
5945  * @page_num: the index of a page in the notebook, or -1
5946  *   to get the last page
5947  *
5948  * Returns the child widget contained in page number @page_num.
5949  *
5950  * Returns: (nullable) (transfer none): the child widget, or %NULL if @page_num
5951  * is out of bounds
5952  */
5953 GtkWidget*
gtk_notebook_get_nth_page(GtkNotebook * notebook,int page_num)5954 gtk_notebook_get_nth_page (GtkNotebook *notebook,
5955                            int          page_num)
5956 {
5957   GtkNotebookPage *page;
5958   GList *list;
5959 
5960   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
5961 
5962   if (page_num >= 0)
5963     list = g_list_nth (notebook->children, page_num);
5964   else
5965     list = g_list_last (notebook->children);
5966 
5967   if (list)
5968     {
5969       page = list->data;
5970       return page->child;
5971     }
5972 
5973   return NULL;
5974 }
5975 
5976 /**
5977  * gtk_notebook_get_n_pages:
5978  * @notebook: a `GtkNotebook`
5979  *
5980  * Gets the number of pages in a notebook.
5981  *
5982  * Returns: the number of pages in the notebook
5983  */
5984 int
gtk_notebook_get_n_pages(GtkNotebook * notebook)5985 gtk_notebook_get_n_pages (GtkNotebook *notebook)
5986 {
5987   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), 0);
5988 
5989   return g_list_length (notebook->children);
5990 }
5991 
5992 /**
5993  * gtk_notebook_page_num:
5994  * @notebook: a `GtkNotebook`
5995  * @child: a `GtkWidget`
5996  *
5997  * Finds the index of the page which contains the given child
5998  * widget.
5999  *
6000  * Returns: the index of the page containing @child, or
6001  *   -1 if @child is not in the notebook
6002  */
6003 int
gtk_notebook_page_num(GtkNotebook * notebook,GtkWidget * child)6004 gtk_notebook_page_num (GtkNotebook      *notebook,
6005                        GtkWidget        *child)
6006 {
6007   GList *children;
6008   int num;
6009 
6010   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6011 
6012   num = 0;
6013   children = notebook->children;
6014   while (children)
6015     {
6016       GtkNotebookPage *page =  children->data;
6017 
6018       if (page->child == child)
6019         return num;
6020 
6021       children = children->next;
6022       num++;
6023     }
6024 
6025   return -1;
6026 }
6027 
6028 /**
6029  * gtk_notebook_set_current_page: (attributes org.gtk.Method.set_property=page)
6030  * @notebook: a `GtkNotebook`
6031  * @page_num: index of the page to switch to, starting from 0.
6032  *   If negative, the last page will be used. If greater
6033  *   than the number of pages in the notebook, nothing
6034  *   will be done.
6035  *
6036  * Switches to the page number @page_num.
6037  *
6038  * Note that due to historical reasons, GtkNotebook refuses
6039  * to switch to a page unless the child widget is visible.
6040  * Therefore, it is recommended to show child widgets before
6041  * adding them to a notebook.
6042  */
6043 void
gtk_notebook_set_current_page(GtkNotebook * notebook,int page_num)6044 gtk_notebook_set_current_page (GtkNotebook *notebook,
6045                                int          page_num)
6046 {
6047   GList *list;
6048 
6049   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6050 
6051   if (page_num < 0)
6052     page_num = g_list_length (notebook->children) - 1;
6053 
6054   list = g_list_nth (notebook->children, page_num);
6055   if (list)
6056     gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE_FROM_LIST (list));
6057 }
6058 
6059 /**
6060  * gtk_notebook_next_page:
6061  * @notebook: a `GtkNotebook`
6062  *
6063  * Switches to the next page.
6064  *
6065  * Nothing happens if the current page is the last page.
6066  */
6067 void
gtk_notebook_next_page(GtkNotebook * notebook)6068 gtk_notebook_next_page (GtkNotebook *notebook)
6069 {
6070   GList *list;
6071 
6072   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6073 
6074   list = g_list_find (notebook->children, notebook->cur_page);
6075   if (!list)
6076     return;
6077 
6078   list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
6079   if (!list)
6080     return;
6081 
6082   gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE_FROM_LIST (list));
6083 }
6084 
6085 /**
6086  * gtk_notebook_prev_page:
6087  * @notebook: a `GtkNotebook`
6088  *
6089  * Switches to the previous page.
6090  *
6091  * Nothing happens if the current page is the first page.
6092  */
6093 void
gtk_notebook_prev_page(GtkNotebook * notebook)6094 gtk_notebook_prev_page (GtkNotebook *notebook)
6095 {
6096   GList *list;
6097 
6098   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6099 
6100   list = g_list_find (notebook->children, notebook->cur_page);
6101   if (!list)
6102     return;
6103 
6104   list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
6105   if (!list)
6106     return;
6107 
6108   gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE_FROM_LIST (list));
6109 }
6110 
6111 /* Public GtkNotebook/Tab Style Functions
6112  *
6113  * gtk_notebook_set_show_border
6114  * gtk_notebook_get_show_border
6115  * gtk_notebook_set_show_tabs
6116  * gtk_notebook_get_show_tabs
6117  * gtk_notebook_set_tab_pos
6118  * gtk_notebook_get_tab_pos
6119  * gtk_notebook_set_scrollable
6120  * gtk_notebook_get_scrollable
6121  */
6122 /**
6123  * gtk_notebook_set_show_border: (attributes org.gtk.Method.set_property=show-border)
6124  * @notebook: a `GtkNotebook`
6125  * @show_border: %TRUE if a bevel should be drawn around the notebook
6126  *
6127  * Sets whether a bevel will be drawn around the notebook pages.
6128  *
6129  * This only has a visual effect when the tabs are not shown.
6130  */
6131 void
gtk_notebook_set_show_border(GtkNotebook * notebook,gboolean show_border)6132 gtk_notebook_set_show_border (GtkNotebook *notebook,
6133                               gboolean     show_border)
6134 {
6135   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6136 
6137   if (notebook->show_border != show_border)
6138     {
6139       notebook->show_border = show_border;
6140 
6141       if (show_border)
6142         gtk_widget_add_css_class (GTK_WIDGET (notebook), "frame");
6143       else
6144         gtk_widget_remove_css_class (GTK_WIDGET (notebook), "frame");
6145 
6146       g_object_notify_by_pspec (G_OBJECT (notebook), properties[PROP_SHOW_BORDER]);
6147     }
6148 }
6149 
6150 /**
6151  * gtk_notebook_get_show_border: (attributes org.gtk.Method.get_property=show-border)
6152  * @notebook: a `GtkNotebook`
6153  *
6154  * Returns whether a bevel will be drawn around the notebook pages.
6155  *
6156  * Returns: %TRUE if the bevel is drawn
6157  */
6158 gboolean
gtk_notebook_get_show_border(GtkNotebook * notebook)6159 gtk_notebook_get_show_border (GtkNotebook *notebook)
6160 {
6161   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
6162 
6163   return notebook->show_border;
6164 }
6165 
6166 /**
6167  * gtk_notebook_set_show_tabs: (attributes org.gtk.Method.set_property=show-tabs)
6168  * @notebook: a `GtkNotebook`
6169  * @show_tabs: %TRUE if the tabs should be shown
6170  *
6171  * Sets whether to show the tabs for the notebook or not.
6172  */
6173 void
gtk_notebook_set_show_tabs(GtkNotebook * notebook,gboolean show_tabs)6174 gtk_notebook_set_show_tabs (GtkNotebook *notebook,
6175                             gboolean     show_tabs)
6176 {
6177   GtkNotebookPage *page;
6178   GList *children;
6179   int i;
6180 
6181   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6182 
6183   show_tabs = show_tabs != FALSE;
6184 
6185   if (notebook->show_tabs == show_tabs)
6186     return;
6187 
6188   notebook->show_tabs = show_tabs;
6189   children = notebook->children;
6190 
6191   if (!show_tabs)
6192     {
6193       while (children)
6194         {
6195           page = children->data;
6196           children = children->next;
6197           if (page->default_tab)
6198             {
6199               gtk_widget_unparent (page->tab_label);
6200               page->tab_label = NULL;
6201             }
6202           else
6203             gtk_widget_hide (page->tab_label);
6204         }
6205 
6206       gtk_widget_hide (notebook->header_widget);
6207     }
6208   else
6209     {
6210       gtk_notebook_update_labels (notebook);
6211       gtk_widget_show (notebook->header_widget);
6212     }
6213 
6214   for (i = 0; i < N_ACTION_WIDGETS; i++)
6215     {
6216       if (notebook->action_widget[i])
6217         gtk_widget_set_child_visible (notebook->action_widget[i], show_tabs);
6218     }
6219 
6220   gtk_notebook_update_tab_pos (notebook);
6221   gtk_widget_queue_resize (GTK_WIDGET (notebook));
6222 
6223   g_object_notify_by_pspec (G_OBJECT (notebook), properties[PROP_SHOW_TABS]);
6224 }
6225 
6226 /**
6227  * gtk_notebook_get_show_tabs: (attributes org.gtk.Method.get_property=show-tabs)
6228  * @notebook: a `GtkNotebook`
6229  *
6230  * Returns whether the tabs of the notebook are shown.
6231  *
6232  * Returns: %TRUE if the tabs are shown
6233  */
6234 gboolean
gtk_notebook_get_show_tabs(GtkNotebook * notebook)6235 gtk_notebook_get_show_tabs (GtkNotebook *notebook)
6236 {
6237   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
6238 
6239   return notebook->show_tabs;
6240 }
6241 
6242 static void
gtk_notebook_update_tab_pos(GtkNotebook * notebook)6243 gtk_notebook_update_tab_pos (GtkNotebook *notebook)
6244 {
6245   GtkLayoutManager *layout;
6246   GtkPositionType tab_pos;
6247   const char *tab_pos_names[] = {
6248     "left", "right", "top", "bottom",
6249   };
6250   int i;
6251 
6252   tab_pos = get_effective_tab_pos (notebook);
6253 
6254   for (i = 0; i < G_N_ELEMENTS (tab_pos_names); i++)
6255     {
6256       if (tab_pos == i)
6257         gtk_widget_add_css_class (notebook->header_widget, tab_pos_names[i]);
6258       else
6259         gtk_widget_remove_css_class (notebook->header_widget, tab_pos_names[i]);
6260     }
6261 
6262   layout = gtk_widget_get_layout_manager (GTK_WIDGET (notebook));
6263 
6264   switch (tab_pos)
6265     {
6266     case GTK_POS_TOP:
6267       gtk_widget_set_hexpand (notebook->tabs_widget, TRUE);
6268       gtk_widget_set_vexpand (notebook->tabs_widget, FALSE);
6269       gtk_widget_set_hexpand (notebook->header_widget, TRUE);
6270       gtk_widget_set_vexpand (notebook->header_widget, FALSE);
6271       if (notebook->show_tabs)
6272         {
6273           gtk_widget_insert_before (notebook->header_widget, GTK_WIDGET (notebook), notebook->stack_widget);
6274         }
6275 
6276       gtk_orientable_set_orientation (GTK_ORIENTABLE (layout), GTK_ORIENTATION_VERTICAL);
6277       gtk_orientable_set_orientation (GTK_ORIENTABLE (notebook->header_widget), GTK_ORIENTATION_HORIZONTAL);
6278       break;
6279 
6280     case GTK_POS_BOTTOM:
6281       gtk_widget_set_hexpand (notebook->tabs_widget, TRUE);
6282       gtk_widget_set_vexpand (notebook->tabs_widget, FALSE);
6283       gtk_widget_set_hexpand (notebook->header_widget, TRUE);
6284       gtk_widget_set_vexpand (notebook->header_widget, FALSE);
6285       if (notebook->show_tabs)
6286         {
6287           gtk_widget_insert_after (notebook->header_widget, GTK_WIDGET (notebook), notebook->stack_widget);
6288         }
6289 
6290       gtk_orientable_set_orientation (GTK_ORIENTABLE (layout), GTK_ORIENTATION_VERTICAL);
6291       gtk_orientable_set_orientation (GTK_ORIENTABLE (notebook->header_widget), GTK_ORIENTATION_HORIZONTAL);
6292       break;
6293 
6294     case GTK_POS_LEFT:
6295       gtk_widget_set_hexpand (notebook->tabs_widget, FALSE);
6296       gtk_widget_set_vexpand (notebook->tabs_widget, TRUE);
6297       gtk_widget_set_hexpand (notebook->header_widget, FALSE);
6298       gtk_widget_set_vexpand (notebook->header_widget, TRUE);
6299       if (notebook->show_tabs)
6300         {
6301           gtk_widget_insert_before (notebook->header_widget, GTK_WIDGET (notebook), notebook->stack_widget);
6302         }
6303 
6304       gtk_orientable_set_orientation (GTK_ORIENTABLE (layout), GTK_ORIENTATION_HORIZONTAL);
6305       gtk_orientable_set_orientation (GTK_ORIENTABLE (notebook->header_widget), GTK_ORIENTATION_VERTICAL);
6306       break;
6307 
6308     case GTK_POS_RIGHT:
6309       gtk_widget_set_hexpand (notebook->tabs_widget, FALSE);
6310       gtk_widget_set_vexpand (notebook->tabs_widget, TRUE);
6311       gtk_widget_set_hexpand (notebook->header_widget, FALSE);
6312       gtk_widget_set_vexpand (notebook->header_widget, TRUE);
6313       if (notebook->show_tabs)
6314         {
6315           gtk_widget_insert_after (notebook->header_widget, GTK_WIDGET (notebook), notebook->stack_widget);
6316         }
6317 
6318       gtk_orientable_set_orientation (GTK_ORIENTABLE (layout), GTK_ORIENTATION_HORIZONTAL);
6319       gtk_orientable_set_orientation (GTK_ORIENTABLE (notebook->header_widget), GTK_ORIENTATION_VERTICAL);
6320       break;
6321     default:
6322       g_assert_not_reached ();
6323       break;
6324     }
6325 }
6326 
6327 /**
6328  * gtk_notebook_set_tab_pos: (attributes org.gtk.Method.set_property=tab-pos)
6329  * @notebook: a `GtkNotebook`.
6330  * @pos: the edge to draw the tabs at
6331  *
6332  * Sets the edge at which the tabs are drawn.
6333  */
6334 void
gtk_notebook_set_tab_pos(GtkNotebook * notebook,GtkPositionType pos)6335 gtk_notebook_set_tab_pos (GtkNotebook     *notebook,
6336                           GtkPositionType  pos)
6337 {
6338   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6339 
6340   if (notebook->tab_pos != pos)
6341     {
6342       notebook->tab_pos = pos;
6343       gtk_widget_queue_resize (GTK_WIDGET (notebook));
6344 
6345       gtk_notebook_update_tab_pos (notebook);
6346 
6347       g_object_notify_by_pspec (G_OBJECT (notebook), properties[PROP_TAB_POS]);
6348     }
6349 }
6350 
6351 /**
6352  * gtk_notebook_get_tab_pos: (attributes org.gtk.Method.get_property=tab-pos)
6353  * @notebook: a `GtkNotebook`
6354  *
6355  * Gets the edge at which the tabs are drawn.
6356  *
6357  * Returns: the edge at which the tabs are drawn
6358  */
6359 GtkPositionType
gtk_notebook_get_tab_pos(GtkNotebook * notebook)6360 gtk_notebook_get_tab_pos (GtkNotebook *notebook)
6361 {
6362   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), GTK_POS_TOP);
6363 
6364   return notebook->tab_pos;
6365 }
6366 
6367 /**
6368  * gtk_notebook_set_scrollable: (attributes org.gtk.Method.set_property=scrollable)
6369  * @notebook: a `GtkNotebook`
6370  * @scrollable: %TRUE if scroll arrows should be added
6371  *
6372  * Sets whether the tab label area will have arrows for
6373  * scrolling if there are too many tabs to fit in the area.
6374  */
6375 void
gtk_notebook_set_scrollable(GtkNotebook * notebook,gboolean scrollable)6376 gtk_notebook_set_scrollable (GtkNotebook *notebook,
6377                              gboolean     scrollable)
6378 {
6379   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6380 
6381   scrollable = (scrollable != FALSE);
6382 
6383   if (notebook->scrollable == scrollable)
6384     return;
6385 
6386   notebook->scrollable = scrollable;
6387 
6388   update_arrow_nodes (notebook);
6389   update_arrow_state (notebook);
6390 
6391   gtk_widget_queue_resize (GTK_WIDGET (notebook));
6392 
6393   g_object_notify_by_pspec (G_OBJECT (notebook), properties[PROP_SCROLLABLE]);
6394 }
6395 
6396 /**
6397  * gtk_notebook_get_scrollable: (attributes or.gtk.Method.get_property=scrollable)
6398  * @notebook: a `GtkNotebook`
6399  *
6400  * Returns whether the tab label area has arrows for scrolling.
6401  *
6402  * Returns: %TRUE if arrows for scrolling are present
6403  */
6404 gboolean
gtk_notebook_get_scrollable(GtkNotebook * notebook)6405 gtk_notebook_get_scrollable (GtkNotebook *notebook)
6406 {
6407   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
6408 
6409   return notebook->scrollable;
6410 }
6411 
6412 
6413 /* Public GtkNotebook Popup Menu Methods:
6414  *
6415  * gtk_notebook_popup_enable
6416  * gtk_notebook_popup_disable
6417  */
6418 
6419 
6420 /**
6421  * gtk_notebook_popup_enable:
6422  * @notebook: a `GtkNotebook`
6423  *
6424  * Enables the popup menu.
6425  *
6426  * If the user clicks with the right mouse button on the tab labels,
6427  * a menu with all the pages will be popped up.
6428  */
6429 void
gtk_notebook_popup_enable(GtkNotebook * notebook)6430 gtk_notebook_popup_enable (GtkNotebook *notebook)
6431 {
6432   GList *list;
6433 
6434   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6435 
6436   if (notebook->menu)
6437     return;
6438 
6439   notebook->menu = gtk_popover_menu_new ();
6440   gtk_widget_set_parent (notebook->menu, notebook->tabs_widget);
6441 
6442   notebook->menu_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
6443   g_object_ref_sink (notebook->menu_box);
6444   gtk_popover_menu_add_submenu (GTK_POPOVER_MENU (notebook->menu), notebook->menu_box, "main");
6445 
6446   for (list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, FALSE);
6447        list;
6448        list = gtk_notebook_search_page (notebook, list, STEP_NEXT, FALSE))
6449     gtk_notebook_menu_item_create (notebook, list->data);
6450 
6451   gtk_notebook_update_labels (notebook);
6452 
6453   g_object_notify_by_pspec (G_OBJECT (notebook), properties[PROP_ENABLE_POPUP]);
6454 }
6455 
6456 /**
6457  * gtk_notebook_popup_disable:
6458  * @notebook: a `GtkNotebook`
6459  *
6460  * Disables the popup menu.
6461  */
6462 void
gtk_notebook_popup_disable(GtkNotebook * notebook)6463 gtk_notebook_popup_disable (GtkNotebook *notebook)
6464 {
6465   GtkWidget *child;
6466 
6467   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6468 
6469   if (!notebook->menu)
6470     return;
6471 
6472   for (child = gtk_widget_get_first_child (notebook->menu_box);
6473        child != NULL;
6474        child = gtk_widget_get_next_sibling (child))
6475     gtk_notebook_menu_label_unparent (child);
6476   notebook->menu = NULL;
6477   notebook->menu_box = NULL;
6478 
6479   g_object_notify_by_pspec (G_OBJECT (notebook), properties[PROP_ENABLE_POPUP]);
6480 }
6481 
6482 /* Public GtkNotebook Page Properties Functions:
6483  *
6484  * gtk_notebook_get_tab_label
6485  * gtk_notebook_set_tab_label
6486  * gtk_notebook_set_tab_label_text
6487  * gtk_notebook_get_menu_label
6488  * gtk_notebook_set_menu_label
6489  * gtk_notebook_set_menu_label_text
6490  * gtk_notebook_get_tab_reorderable
6491  * gtk_notebook_set_tab_reorderable
6492  * gtk_notebook_get_tab_detachable
6493  * gtk_notebook_set_tab_detachable
6494  */
6495 
6496 /**
6497  * gtk_notebook_get_tab_label:
6498  * @notebook: a `GtkNotebook`
6499  * @child: the page
6500  *
6501  * Returns the tab label widget for the page @child.
6502  *
6503  * %NULL is returned if @child is not in @notebook or
6504  * if no tab label has specifically been set for @child.
6505  *
6506  * Returns: (transfer none) (nullable): the tab label
6507  */
6508 GtkWidget *
gtk_notebook_get_tab_label(GtkNotebook * notebook,GtkWidget * child)6509 gtk_notebook_get_tab_label (GtkNotebook *notebook,
6510                             GtkWidget   *child)
6511 {
6512   GList *list;
6513 
6514   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
6515   g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
6516 
6517   list = gtk_notebook_find_child (notebook, child);
6518   if (list == NULL)
6519     return NULL;
6520 
6521   if (GTK_NOTEBOOK_PAGE_FROM_LIST (list)->default_tab)
6522     return NULL;
6523 
6524   return GTK_NOTEBOOK_PAGE_FROM_LIST (list)->tab_label;
6525 }
6526 
6527 /**
6528  * gtk_notebook_set_tab_label:
6529  * @notebook: a `GtkNotebook`
6530  * @child: the page
6531  * @tab_label: (nullable): the tab label widget to use, or %NULL
6532  *   for default tab label
6533  *
6534  * Changes the tab label for @child.
6535  *
6536  * If %NULL is specified for @tab_label, then the page will
6537  * have the label “page N”.
6538  */
6539 void
gtk_notebook_set_tab_label(GtkNotebook * notebook,GtkWidget * child,GtkWidget * tab_label)6540 gtk_notebook_set_tab_label (GtkNotebook *notebook,
6541                             GtkWidget   *child,
6542                             GtkWidget   *tab_label)
6543 {
6544   GtkNotebookPage *page;
6545   GList *list;
6546 
6547   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6548   g_return_if_fail (GTK_IS_WIDGET (child));
6549 
6550   list = gtk_notebook_find_child (notebook, child);
6551   g_return_if_fail (list != NULL);
6552 
6553   /* a NULL pointer indicates a default_tab setting, otherwise
6554    * we need to set the associated label
6555    */
6556   page = list->data;
6557 
6558   if (page->tab_label == tab_label)
6559     return;
6560 
6561   gtk_notebook_remove_tab_label (notebook, page);
6562 
6563   if (tab_label)
6564     {
6565       page->default_tab = FALSE;
6566       page->tab_label = tab_label;
6567       g_object_set_data (G_OBJECT (page->tab_label), "notebook", notebook);
6568       gtk_widget_set_parent (page->tab_label, page->tab_widget);
6569     }
6570   else
6571     {
6572       page->default_tab = TRUE;
6573       page->tab_label = NULL;
6574 
6575       if (notebook->show_tabs)
6576         {
6577           char string[32];
6578 
6579           g_snprintf (string, sizeof(string), _("Page %u"),
6580                       g_list_position (notebook->children, list));
6581           page->tab_label = gtk_label_new (string);
6582           gtk_widget_set_parent (page->tab_label, page->tab_widget);
6583           g_object_set_data (G_OBJECT (page->tab_label), "notebook", notebook);
6584         }
6585     }
6586 
6587   if (page->tab_label)
6588     page->mnemonic_activate_signal =
6589       g_signal_connect (page->tab_label,
6590                         "mnemonic-activate",
6591                         G_CALLBACK (gtk_notebook_mnemonic_activate_switch_page),
6592                         notebook);
6593 
6594   if (notebook->show_tabs && gtk_widget_get_visible (child))
6595     {
6596       gtk_widget_show (page->tab_label);
6597       gtk_widget_queue_resize (GTK_WIDGET (notebook));
6598     }
6599 
6600   if (notebook->menu)
6601     gtk_notebook_menu_item_recreate (notebook, list);
6602 
6603   g_object_notify (G_OBJECT (page), "tab-label");
6604 }
6605 
6606 /**
6607  * gtk_notebook_set_tab_label_text:
6608  * @notebook: a `GtkNotebook`
6609  * @child: the page
6610  * @tab_text: the label text
6611  *
6612  * Creates a new label and sets it as the tab label for the page
6613  * containing @child.
6614  */
6615 void
gtk_notebook_set_tab_label_text(GtkNotebook * notebook,GtkWidget * child,const char * tab_text)6616 gtk_notebook_set_tab_label_text (GtkNotebook *notebook,
6617                                  GtkWidget   *child,
6618                                  const char *tab_text)
6619 {
6620   GtkWidget *tab_label = NULL;
6621 
6622   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6623 
6624   if (tab_text)
6625     tab_label = gtk_label_new (tab_text);
6626   gtk_notebook_set_tab_label (notebook, child, tab_label);
6627 }
6628 
6629 /**
6630  * gtk_notebook_get_tab_label_text:
6631  * @notebook: a `GtkNotebook`
6632  * @child: a widget contained in a page of @notebook
6633  *
6634  * Retrieves the text of the tab label for the page containing
6635  * @child.
6636  *
6637  * Returns: (nullable): the text of the tab label, or %NULL if
6638  *   the tab label idget is not a `GtkLabel`. The string is owned
6639  *   by the widget and must not be freed.
6640  */
6641 const char *
gtk_notebook_get_tab_label_text(GtkNotebook * notebook,GtkWidget * child)6642 gtk_notebook_get_tab_label_text (GtkNotebook *notebook,
6643                                  GtkWidget   *child)
6644 {
6645   GtkWidget *tab_label;
6646 
6647   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
6648   g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
6649 
6650   tab_label = gtk_notebook_get_tab_label (notebook, child);
6651 
6652   if (GTK_IS_LABEL (tab_label))
6653     return gtk_label_get_text (GTK_LABEL (tab_label));
6654   else
6655     return NULL;
6656 }
6657 
6658 /**
6659  * gtk_notebook_get_menu_label:
6660  * @notebook: a `GtkNotebook`
6661  * @child: a widget contained in a page of @notebook
6662  *
6663  * Retrieves the menu label widget of the page containing @child.
6664  *
6665  * Returns: (nullable) (transfer none): the menu label, or %NULL
6666  *   if the notebook page does not have a menu label other than
6667  *   the default (the tab label).
6668  */
6669 GtkWidget*
gtk_notebook_get_menu_label(GtkNotebook * notebook,GtkWidget * child)6670 gtk_notebook_get_menu_label (GtkNotebook *notebook,
6671                              GtkWidget   *child)
6672 {
6673   GList *list;
6674 
6675   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
6676   g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
6677 
6678   list = gtk_notebook_find_child (notebook, child);
6679   g_return_val_if_fail (list != NULL, NULL);
6680 
6681   if (GTK_NOTEBOOK_PAGE_FROM_LIST (list)->default_menu)
6682     return NULL;
6683 
6684   return GTK_NOTEBOOK_PAGE_FROM_LIST (list)->menu_label;
6685 }
6686 
6687 /**
6688  * gtk_notebook_set_menu_label:
6689  * @notebook: a `GtkNotebook`
6690  * @child: the child widget
6691  * @menu_label: (nullable): the menu label, or %NULL for default
6692  *
6693  * Changes the menu label for the page containing @child.
6694  */
6695 void
gtk_notebook_set_menu_label(GtkNotebook * notebook,GtkWidget * child,GtkWidget * menu_label)6696 gtk_notebook_set_menu_label (GtkNotebook *notebook,
6697                              GtkWidget   *child,
6698                              GtkWidget   *menu_label)
6699 {
6700   GtkNotebookPage *page;
6701   GList *list;
6702 
6703   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6704   g_return_if_fail (GTK_IS_WIDGET (child));
6705 
6706   list = gtk_notebook_find_child (notebook, child);
6707   g_return_if_fail (list != NULL);
6708 
6709   page = list->data;
6710   if (page->menu_label)
6711     {
6712       if (notebook->menu)
6713         gtk_widget_unparent (gtk_widget_get_parent (page->menu_label));
6714 
6715       g_clear_object (&page->menu_label);
6716     }
6717 
6718   if (menu_label)
6719     {
6720       page->menu_label = menu_label;
6721       g_object_ref_sink (page->menu_label);
6722       page->default_menu = FALSE;
6723     }
6724   else
6725     page->default_menu = TRUE;
6726 
6727   if (notebook->menu)
6728     gtk_notebook_menu_item_create (notebook, page);
6729   g_object_notify (G_OBJECT (page), "menu-label");
6730 }
6731 
6732 /**
6733  * gtk_notebook_set_menu_label_text:
6734  * @notebook: a `GtkNotebook`
6735  * @child: the child widget
6736  * @menu_text: the label text
6737  *
6738  * Creates a new label and sets it as the menu label of @child.
6739  */
6740 void
gtk_notebook_set_menu_label_text(GtkNotebook * notebook,GtkWidget * child,const char * menu_text)6741 gtk_notebook_set_menu_label_text (GtkNotebook *notebook,
6742                                   GtkWidget   *child,
6743                                   const char *menu_text)
6744 {
6745   GtkWidget *menu_label = NULL;
6746 
6747   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6748 
6749   if (menu_text)
6750     {
6751       menu_label = gtk_label_new (menu_text);
6752       gtk_widget_set_halign (menu_label, GTK_ALIGN_START);
6753       gtk_widget_set_valign (menu_label, GTK_ALIGN_CENTER);
6754     }
6755   gtk_notebook_set_menu_label (notebook, child, menu_label);
6756 }
6757 
6758 /**
6759  * gtk_notebook_get_menu_label_text:
6760  * @notebook: a `GtkNotebook`
6761  * @child: the child widget of a page of the notebook.
6762  *
6763  * Retrieves the text of the menu label for the page containing
6764  * @child.
6765  *
6766  * Returns: (nullable): the text of the tab label, or %NULL if
6767  *   the widget does not have a menu label other than the default
6768  *   menu label, or the menu label widget is not a `GtkLabel`.
6769  *   The string is owned by the widget and must not be freed.
6770  */
6771 const char *
gtk_notebook_get_menu_label_text(GtkNotebook * notebook,GtkWidget * child)6772 gtk_notebook_get_menu_label_text (GtkNotebook *notebook,
6773                                   GtkWidget *child)
6774 {
6775   GtkWidget *menu_label;
6776 
6777   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
6778   g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
6779 
6780   menu_label = gtk_notebook_get_menu_label (notebook, child);
6781 
6782   if (GTK_IS_LABEL (menu_label))
6783     return gtk_label_get_text (GTK_LABEL (menu_label));
6784   else
6785     return NULL;
6786 }
6787 
6788 /* Helper function called when pages are reordered
6789  */
6790 static void
gtk_notebook_child_reordered(GtkNotebook * notebook,GtkNotebookPage * page)6791 gtk_notebook_child_reordered (GtkNotebook     *notebook,
6792                               GtkNotebookPage *page)
6793 {
6794   GList *list;
6795   GtkWidget *sibling;
6796 
6797   list = g_list_find (notebook->children, page);
6798 
6799   if (notebook->menu)
6800     gtk_notebook_menu_item_recreate (notebook, list);
6801 
6802   if (list->prev)
6803     sibling = GTK_NOTEBOOK_PAGE_FROM_LIST (list->prev)->tab_widget;
6804   else if (notebook->arrow_widget[ARROW_RIGHT_BEFORE])
6805     sibling = notebook->arrow_widget[ARROW_RIGHT_BEFORE];
6806   else if (notebook->arrow_widget[ARROW_LEFT_BEFORE])
6807     sibling = notebook->arrow_widget[ARROW_LEFT_BEFORE];
6808   else
6809     sibling = NULL;
6810 
6811   gtk_widget_insert_after (page->tab_widget, notebook->tabs_widget, sibling);
6812 
6813   gtk_notebook_update_labels (notebook);
6814   gtk_widget_queue_allocate (notebook->tabs_widget);
6815 }
6816 
6817 /**
6818  * gtk_notebook_reorder_child:
6819  * @notebook: a `GtkNotebook`
6820  * @child: the child to move
6821  * @position: the new position, or -1 to move to the end
6822  *
6823  * Reorders the page containing @child, so that it appears in position
6824  * @position.
6825  *
6826  * If @position is greater than or equal to the number of children in
6827  * the list or negative, @child will be moved to the end of the list.
6828  */
6829 void
gtk_notebook_reorder_child(GtkNotebook * notebook,GtkWidget * child,int position)6830 gtk_notebook_reorder_child (GtkNotebook *notebook,
6831                             GtkWidget   *child,
6832                             int          position)
6833 {
6834   GList *list, *new_list;
6835   GtkNotebookPage *page;
6836   int old_pos;
6837   int max_pos;
6838   int i;
6839 
6840   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6841   g_return_if_fail (GTK_IS_WIDGET (child));
6842 
6843   list = gtk_notebook_find_child (notebook, child);
6844   g_return_if_fail (list != NULL);
6845 
6846   max_pos = g_list_length (notebook->children) - 1;
6847   if (position < 0 || position > max_pos)
6848     position = max_pos;
6849 
6850   old_pos = g_list_position (notebook->children, list);
6851 
6852   if (old_pos == position)
6853     return;
6854 
6855   page = list->data;
6856   notebook->children = g_list_delete_link (notebook->children, list);
6857 
6858   notebook->children = g_list_insert (notebook->children, page, position);
6859   new_list = g_list_nth (notebook->children, position);
6860 
6861   /* Fix up GList references in GtkNotebook structure */
6862   if (notebook->first_tab == list)
6863     notebook->first_tab = new_list;
6864   if (notebook->focus_tab == list)
6865     notebook->focus_tab = new_list;
6866 
6867   /* Move around the menu items if necessary */
6868   gtk_notebook_child_reordered (notebook, page);
6869 
6870   for (list = notebook->children, i = 0; list; list = list->next, i++)
6871     {
6872       if (MIN (old_pos, position) <= i && i <= MAX (old_pos, position))
6873         g_object_notify (G_OBJECT (list->data), "position");
6874     }
6875 
6876   g_signal_emit (notebook,
6877                  notebook_signals[PAGE_REORDERED],
6878                  0,
6879                  child,
6880                  position);
6881 }
6882 
6883 /**
6884  * gtk_notebook_set_group_name: (attributes org.gtk.Method.set_property=group-name)
6885  * @notebook: a `GtkNotebook`
6886  * @group_name: (nullable): the name of the notebook group,
6887  *   or %NULL to unset it
6888  *
6889  * Sets a group name for @notebook.
6890  *
6891  * Notebooks with the same name will be able to exchange tabs
6892  * via drag and drop. A notebook with a %NULL group name will
6893  * not be able to exchange tabs with any other notebook.
6894  */
6895 void
gtk_notebook_set_group_name(GtkNotebook * notebook,const char * group_name)6896 gtk_notebook_set_group_name (GtkNotebook *notebook,
6897                              const char *group_name)
6898 {
6899   GQuark group;
6900 
6901   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6902 
6903   group = g_quark_from_string (group_name);
6904 
6905   if (notebook->group != group)
6906     {
6907       notebook->group = group;
6908 
6909       g_object_notify_by_pspec (G_OBJECT (notebook), properties[PROP_GROUP_NAME]);
6910     }
6911 }
6912 
6913 /**
6914  * gtk_notebook_get_group_name: (attributes org.gtk.Method.get_property=group-name)
6915  * @notebook: a `GtkNotebook`
6916  *
6917  * Gets the current group name for @notebook.
6918  *
6919  * Returns: (nullable) (transfer none): the group name,
6920  *   or %NULL if none is set
6921  */
6922 const char *
gtk_notebook_get_group_name(GtkNotebook * notebook)6923 gtk_notebook_get_group_name (GtkNotebook *notebook)
6924 {
6925   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
6926 
6927   return g_quark_to_string (notebook->group);
6928 }
6929 
6930 /**
6931  * gtk_notebook_get_tab_reorderable:
6932  * @notebook: a `GtkNotebook`
6933  * @child: a child `GtkWidget`
6934  *
6935  * Gets whether the tab can be reordered via drag and drop or not.
6936  *
6937  * Returns: %TRUE if the tab is reorderable.
6938  */
6939 gboolean
gtk_notebook_get_tab_reorderable(GtkNotebook * notebook,GtkWidget * child)6940 gtk_notebook_get_tab_reorderable (GtkNotebook *notebook,
6941                                   GtkWidget   *child)
6942 {
6943   GList *list;
6944 
6945   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
6946   g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
6947 
6948   list = gtk_notebook_find_child (notebook, child);
6949   g_return_val_if_fail (list != NULL, FALSE);
6950 
6951   return GTK_NOTEBOOK_PAGE_FROM_LIST (list)->reorderable;
6952 }
6953 
6954 /**
6955  * gtk_notebook_set_tab_reorderable:
6956  * @notebook: a `GtkNotebook`
6957  * @child: a child `GtkWidget`
6958  * @reorderable: whether the tab is reorderable or not
6959  *
6960  * Sets whether the notebook tab can be reordered
6961  * via drag and drop or not.
6962  */
6963 void
gtk_notebook_set_tab_reorderable(GtkNotebook * notebook,GtkWidget * child,gboolean reorderable)6964 gtk_notebook_set_tab_reorderable (GtkNotebook *notebook,
6965                                   GtkWidget   *child,
6966                                   gboolean     reorderable)
6967 {
6968   GtkNotebookPage *page;
6969   GList *list;
6970 
6971   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6972   g_return_if_fail (GTK_IS_WIDGET (child));
6973 
6974   list = gtk_notebook_find_child (notebook, child);
6975   g_return_if_fail (list != NULL);
6976 
6977   page = GTK_NOTEBOOK_PAGE_FROM_LIST (list);
6978   reorderable = reorderable != FALSE;
6979 
6980   if (page->reorderable != reorderable)
6981     {
6982       page->reorderable = reorderable;
6983       if (reorderable)
6984         gtk_widget_add_css_class (page->tab_widget, "reorderable-page");
6985       else
6986         gtk_widget_remove_css_class (page->tab_widget, "reorderable-page");
6987 
6988       g_object_notify (G_OBJECT (page), "reorderable");
6989     }
6990 }
6991 
6992 /**
6993  * gtk_notebook_get_tab_detachable:
6994  * @notebook: a `GtkNotebook`
6995  * @child: a child `GtkWidget`
6996  *
6997  * Returns whether the tab contents can be detached from @notebook.
6998  *
6999  * Returns: %TRUE if the tab is detachable.
7000  */
7001 gboolean
gtk_notebook_get_tab_detachable(GtkNotebook * notebook,GtkWidget * child)7002 gtk_notebook_get_tab_detachable (GtkNotebook *notebook,
7003                                  GtkWidget   *child)
7004 {
7005   GList *list;
7006 
7007   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7008   g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
7009 
7010   list = gtk_notebook_find_child (notebook, child);
7011   g_return_val_if_fail (list != NULL, FALSE);
7012 
7013   return GTK_NOTEBOOK_PAGE_FROM_LIST (list)->detachable;
7014 }
7015 
7016 /**
7017  * gtk_notebook_set_tab_detachable:
7018  * @notebook: a `GtkNotebook`
7019  * @child: a child `GtkWidget`
7020  * @detachable: whether the tab is detachable or not
7021  *
7022  * Sets whether the tab can be detached from @notebook to another
7023  * notebook or widget.
7024  *
7025  * Note that two notebooks must share a common group identificator
7026  * (see [method@Gtk.Notebook.set_group_name]) to allow automatic tabs
7027  * interchange between them.
7028  *
7029  * If you want a widget to interact with a notebook through DnD
7030  * (i.e.: accept dragged tabs from it) it must be set as a drop
7031  * destination and accept the target “GTK_NOTEBOOK_TAB”. The notebook
7032  * will fill the selection with a GtkWidget** pointing to the child
7033  * widget that corresponds to the dropped tab.
7034  *
7035  * Note that you should use [method@Gtk.Notebook.detach_tab] instead
7036  * of [method@Gtk.Notebook.remove_page] if you want to remove the tab
7037  * from the source notebook as part of accepting a drop. Otherwise,
7038  * the source notebook will think that the dragged tab was removed
7039  * from underneath the ongoing drag operation, and will initiate a
7040  * drag cancel animation.
7041  *
7042  * ```c
7043  * static void
7044  * on_drag_data_received (GtkWidget        *widget,
7045  *                        GdkDrop          *drop,
7046  *                        GtkSelectionData *data,
7047  *                        guint             time,
7048  *                        gpointer          user_data)
7049  * {
7050  *   GtkDrag *drag;
7051  *   GtkWidget *notebook;
7052  *   GtkWidget **child;
7053  *
7054  *   drag = gtk_drop_get_drag (drop);
7055  *   notebook = g_object_get_data (drag, "gtk-notebook-drag-origin");
7056  *   child = (void*) gtk_selection_data_get_data (data);
7057  *
7058  *   // process_widget (*child);
7059  *
7060  *   gtk_notebook_detach_tab (GTK_NOTEBOOK (notebook), *child);
7061  * }
7062  * ```
7063  *
7064  * If you want a notebook to accept drags from other widgets,
7065  * you will have to set your own DnD code to do it.
7066  */
7067 void
gtk_notebook_set_tab_detachable(GtkNotebook * notebook,GtkWidget * child,gboolean detachable)7068 gtk_notebook_set_tab_detachable (GtkNotebook *notebook,
7069                                  GtkWidget  *child,
7070                                  gboolean    detachable)
7071 {
7072   GList *list;
7073   GtkNotebookPage *page;
7074 
7075   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7076   g_return_if_fail (GTK_IS_WIDGET (child));
7077 
7078   list = gtk_notebook_find_child (notebook, child);
7079   g_return_if_fail (list != NULL);
7080 
7081   page = GTK_NOTEBOOK_PAGE_FROM_LIST (list);
7082   detachable = detachable != FALSE;
7083 
7084   if (page->detachable != detachable)
7085     {
7086       page->detachable = detachable;
7087       g_object_notify (G_OBJECT (page), "detachable");
7088     }
7089 }
7090 
7091 /**
7092  * gtk_notebook_get_action_widget:
7093  * @notebook: a `GtkNotebook`
7094  * @pack_type: pack type of the action widget to receive
7095  *
7096  * Gets one of the action widgets.
7097  *
7098  * See [method@Gtk.Notebook.set_action_widget].
7099  *
7100  * Returns: (nullable) (transfer none): The action widget
7101  *   with the given @pack_type or %NULL when this action
7102  *   widget has not been set
7103  */
7104 GtkWidget*
gtk_notebook_get_action_widget(GtkNotebook * notebook,GtkPackType pack_type)7105 gtk_notebook_get_action_widget (GtkNotebook *notebook,
7106                                 GtkPackType  pack_type)
7107 {
7108   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7109 
7110   return notebook->action_widget[pack_type];
7111 }
7112 
7113 /**
7114  * gtk_notebook_set_action_widget:
7115  * @notebook: a `GtkNotebook`
7116  * @widget: a `GtkWidget`
7117  * @pack_type: pack type of the action widget
7118  *
7119  * Sets @widget as one of the action widgets.
7120  *
7121  * Depending on the pack type the widget will be placed before
7122  * or after the tabs. You can use a `GtkBox` if you need to pack
7123  * more than one widget on the same side.
7124  */
7125 void
gtk_notebook_set_action_widget(GtkNotebook * notebook,GtkWidget * widget,GtkPackType pack_type)7126 gtk_notebook_set_action_widget (GtkNotebook *notebook,
7127                                 GtkWidget   *widget,
7128                                 GtkPackType  pack_type)
7129 {
7130   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7131   g_return_if_fail (!widget || GTK_IS_WIDGET (widget));
7132   g_return_if_fail (!widget || gtk_widget_get_parent (widget) == NULL);
7133 
7134   if (notebook->action_widget[pack_type])
7135     gtk_box_remove (GTK_BOX (notebook->header_widget), notebook->action_widget[pack_type]);
7136 
7137   notebook->action_widget[pack_type] = widget;
7138 
7139   if (widget)
7140     {
7141       gtk_box_append (GTK_BOX (notebook->header_widget), widget);
7142       if (pack_type == GTK_PACK_START)
7143         gtk_box_reorder_child_after (GTK_BOX (notebook->header_widget), widget, NULL);
7144       else
7145         gtk_box_reorder_child_after (GTK_BOX (notebook->header_widget), widget, gtk_widget_get_last_child (notebook->header_widget));
7146       gtk_widget_set_child_visible (widget, notebook->show_tabs);
7147     }
7148 
7149   gtk_widget_queue_resize (GTK_WIDGET (notebook));
7150 }
7151 
7152 /**
7153  * gtk_notebook_get_page:
7154  * @notebook: a `GtkNotebook`
7155  * @child: a child of @notebook
7156  *
7157  * Returns the `GtkNotebookPage` for @child.
7158  *
7159  * Returns: (transfer none): the `GtkNotebookPage` for @child
7160  */
7161 GtkNotebookPage *
gtk_notebook_get_page(GtkNotebook * notebook,GtkWidget * child)7162 gtk_notebook_get_page (GtkNotebook *notebook,
7163                        GtkWidget   *child)
7164 {
7165   GList *list;
7166   GtkNotebookPage *page = NULL;
7167 
7168   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7169   g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7170 
7171   list = gtk_notebook_find_child (notebook, child);
7172   if (list != NULL)
7173     page = list->data;
7174 
7175   return page;
7176 }
7177 
7178 /**
7179  * gtk_notebook_page_get_child: (attributes org.gtk.Method.get_property=child)
7180  * @page: a `GtkNotebookPage`
7181  *
7182  * Returns the notebook child to which @page belongs.
7183  *
7184  * Returns: (transfer none): the child to which @page belongs
7185  */
7186 GtkWidget *
gtk_notebook_page_get_child(GtkNotebookPage * page)7187 gtk_notebook_page_get_child (GtkNotebookPage *page)
7188 {
7189   return page->child;
7190 }
7191 
7192 #define GTK_TYPE_NOTEBOOK_PAGES (gtk_notebook_pages_get_type ())
7193 G_DECLARE_FINAL_TYPE (GtkNotebookPages, gtk_notebook_pages, GTK, NOTEBOOK_PAGES, GObject)
7194 
7195 struct _GtkNotebookPages
7196 {
7197   GObject parent_instance;
7198   GtkNotebook *notebook;
7199 };
7200 
7201 struct _GtkNotebookPagesClass
7202 {
7203   GObjectClass parent_class;
7204 };
7205 
7206 static GType
gtk_notebook_pages_get_item_type(GListModel * model)7207 gtk_notebook_pages_get_item_type (GListModel *model)
7208 {
7209   return GTK_TYPE_NOTEBOOK_PAGE;
7210 }
7211 
7212 static guint
gtk_notebook_pages_get_n_items(GListModel * model)7213 gtk_notebook_pages_get_n_items (GListModel *model)
7214 {
7215   GtkNotebookPages *pages = GTK_NOTEBOOK_PAGES (model);
7216 
7217   return g_list_length (pages->notebook->children);
7218 }
7219 
7220 
7221 static gpointer
gtk_notebook_pages_get_item(GListModel * model,guint position)7222 gtk_notebook_pages_get_item (GListModel *model,
7223                              guint       position)
7224 {
7225   GtkNotebookPages *pages = GTK_NOTEBOOK_PAGES (model);
7226   GtkNotebookPage *page;
7227 
7228   page = g_list_nth_data (pages->notebook->children, position);
7229 
7230   return g_object_ref (page);
7231 }
7232 
7233 static void
gtk_notebook_pages_list_model_init(GListModelInterface * iface)7234 gtk_notebook_pages_list_model_init (GListModelInterface *iface)
7235 {
7236   iface->get_item_type = gtk_notebook_pages_get_item_type;
7237   iface->get_n_items = gtk_notebook_pages_get_n_items;
7238   iface->get_item = gtk_notebook_pages_get_item;
7239 }
G_DEFINE_TYPE_WITH_CODE(GtkNotebookPages,gtk_notebook_pages,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,gtk_notebook_pages_list_model_init))7240 G_DEFINE_TYPE_WITH_CODE (GtkNotebookPages, gtk_notebook_pages, G_TYPE_OBJECT,
7241                          G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_notebook_pages_list_model_init))
7242 
7243 static void
7244 gtk_notebook_pages_init (GtkNotebookPages *pages)
7245 {
7246 }
7247 
7248 static void
gtk_notebook_pages_class_init(GtkNotebookPagesClass * class)7249 gtk_notebook_pages_class_init (GtkNotebookPagesClass *class)
7250 {
7251 }
7252 
7253 static GtkNotebookPages *
gtk_notebook_pages_new(GtkNotebook * notebook)7254 gtk_notebook_pages_new (GtkNotebook *notebook)
7255 {
7256   GtkNotebookPages *pages;
7257 
7258   pages = g_object_new (GTK_TYPE_NOTEBOOK_PAGES, NULL);
7259   pages->notebook = notebook;
7260 
7261   return pages;
7262 }
7263 
7264 /**
7265  * gtk_notebook_get_pages: (attributes org.gtk.Method.get_property=pages)
7266  * @notebook: a `GtkNotebook`
7267  *
7268  * Returns a `GListModel` that contains the pages of the notebook.
7269  *
7270  * This can be used to keep an up-to-date view. The model also
7271  * implements [iface@Gtk.SelectionModel] and can be used to track
7272  * and modify the visible page.
7273 
7274  * Returns: (transfer full) (attributes element-type=GtkNotebookPage): a
7275  *   `GListModel` for the notebook's children
7276  */
7277 GListModel *
gtk_notebook_get_pages(GtkNotebook * notebook)7278 gtk_notebook_get_pages (GtkNotebook *notebook)
7279 {
7280   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7281 
7282   if (notebook->pages)
7283     return g_object_ref (notebook->pages);
7284 
7285   notebook->pages = G_LIST_MODEL (gtk_notebook_pages_new (notebook));
7286 
7287   g_object_add_weak_pointer (G_OBJECT (notebook->pages), (gpointer *)&notebook->pages);
7288 
7289   return notebook->pages;
7290 }
7291 
7292