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