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