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 (¬ebook->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 (¬ebook->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 *)¬ebook->pages);
7288
7289 return notebook->pages;
7290 }
7291
7292