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