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