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