]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtknotebook.c
Replace a lot of idle and timeout calls by the new gdk_threads api.
[~andy/gtk] / gtk / gtknotebook.c
index e7121d63422c5c11961d9dbf4bdfb591f231b723..1448a52073010fdddfbfc8e0443a2ce2474472ee 100644 (file)
 #include "gtkmarshalers.h"
 #include "gtkbindings.h"
 #include "gtkprivate.h"
-#include "gtkalias.h"
 #include "gtkdnd.h"
+#include "gtkalias.h"
 
-#define ARROW_SIZE            12
-#define ARROW_SPACING         0
 #define SCROLL_DELAY_FACTOR   5
 #define SCROLL_THRESHOLD      12
 #define DND_THRESHOLD_MULTIPLIER 4
+#define FRAMES_PER_SECOND     45
+#define MSECS_BETWEEN_UPDATES (1000 / FRAMES_PER_SECOND)
 
 enum {
   SWITCH_PAGE,
@@ -126,6 +126,7 @@ enum {
 #define PAGE_TOP_Y(_page_)    (((GtkNotebookPage *) (_page_))->allocation.y)
 #define PAGE_BOTTOM_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y + ((GtkNotebookPage *) (_page_))->allocation.height)
 #define PAGE_MIDDLE_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y + ((GtkNotebookPage *) (_page_))->allocation.height / 2)
+#define NOTEBOOK_IS_TAB_LABEL_PARENT(_notebook_,_page_) (((GtkNotebookPage *) (_page_))->tab_label->parent == ((GtkWidget *) (_notebook_)))
 
 struct _GtkNotebookPage
 {
@@ -161,9 +162,26 @@ struct _GtkNotebookPrivate
   gint  pressed_button;
   guint dnd_timer;
   guint switch_tab_timer;
+
+  gint  drag_begin_x;
+  gint  drag_begin_y;
+
+  gint  drag_offset_x;
+  gint  drag_offset_y;
+
+  GtkWidget *dnd_window;
   GtkTargetList *source_targets;
-  gboolean during_detach;
-  gboolean has_scrolled;
+  GtkNotebookDragOperation operation;
+  GdkWindow *drag_window;
+  gint drag_window_x;
+  gint drag_window_y;
+  GtkNotebookPage *detached_tab;
+
+  guint32 timestamp;
+
+  guint during_reorder : 1;
+  guint during_detach  : 1;
+  guint has_scrolled   : 1;
 };
 
 static const GtkTargetEntry notebook_source_targets [] = {
@@ -184,9 +202,6 @@ static const GtkTargetEntry notebook_dest_targets[] = {
 #endif
  
 /*** GtkNotebook Methods ***/
-static void gtk_notebook_class_init          (GtkNotebookClass *klass);
-static void gtk_notebook_init                (GtkNotebook      *notebook);
-
 static gboolean gtk_notebook_select_page         (GtkNotebook      *notebook,
                                                  gboolean          move_focus);
 static gboolean gtk_notebook_focus_tab           (GtkNotebook      *notebook,
@@ -228,8 +243,6 @@ static gint gtk_notebook_button_press        (GtkWidget        *widget,
 static gint gtk_notebook_button_release      (GtkWidget        *widget,
                                              GdkEventButton   *event);
 static gboolean gtk_notebook_popup_menu      (GtkWidget        *widget);
-static gint gtk_notebook_enter_notify        (GtkWidget        *widget,
-                                             GdkEventCrossing *event);
 static gint gtk_notebook_leave_notify        (GtkWidget        *widget,
                                              GdkEventCrossing *event);
 static gint gtk_notebook_motion_notify       (GtkWidget        *widget,
@@ -251,6 +264,8 @@ static void gtk_notebook_style_set           (GtkWidget        *widget,
 /*** Drag and drop Methods ***/
 static void gtk_notebook_drag_begin          (GtkWidget        *widget,
                                              GdkDragContext   *context);
+static void gtk_notebook_drag_end            (GtkWidget        *widget,
+                                             GdkDragContext   *context);
 static gboolean gtk_notebook_drag_motion     (GtkWidget        *widget,
                                              GdkDragContext   *context,
                                              gint              x,
@@ -314,6 +329,7 @@ static void gtk_notebook_real_remove         (GtkNotebook      *notebook,
                                              GList            *list);
 static void gtk_notebook_update_labels       (GtkNotebook      *notebook);
 static gint gtk_notebook_timer               (GtkNotebook      *notebook);
+static void gtk_notebook_set_scroll_timer    (GtkNotebook *notebook);
 static gint gtk_notebook_page_compare        (gconstpointer     a,
                                              gconstpointer     b);
 static GList* gtk_notebook_find_child        (GtkNotebook      *notebook,
@@ -336,8 +352,7 @@ static void gtk_notebook_draw_arrow          (GtkNotebook      *notebook,
                                              GtkNotebookArrow  arrow);
 
 /*** GtkNotebook Size Allocate Functions ***/
-static void gtk_notebook_pages_allocate      (GtkNotebook              *notebook,
-                                             GtkNotebookDragOperation  operation);
+static void gtk_notebook_pages_allocate      (GtkNotebook      *notebook);
 static void gtk_notebook_page_allocate       (GtkNotebook      *notebook,
                                              GtkNotebookPage  *page);
 static void gtk_notebook_calc_tabs           (GtkNotebook      *notebook,
@@ -394,36 +409,11 @@ static void stop_scrolling (GtkNotebook *notebook);
 
 static GtkNotebookWindowCreationFunc window_creation_hook = NULL;
 static gpointer window_creation_hook_data;
+static GDestroyNotify window_creation_hook_destroy = NULL;
 
-static GtkContainerClass *parent_class = NULL;
 static guint notebook_signals[LAST_SIGNAL] = { 0 };
 
-GType
-gtk_notebook_get_type (void)
-{
-  static GType notebook_type = 0;
-
-  if (!notebook_type)
-    {
-      static const GTypeInfo notebook_info =
-      {
-       sizeof (GtkNotebookClass),
-       NULL,           /* base_init */
-       NULL,           /* base_finalize */
-       (GClassInitFunc) gtk_notebook_class_init,
-       NULL,           /* class_finalize */
-       NULL,           /* class_data */
-       sizeof (GtkNotebook),
-       0,              /* n_preallocs */
-       (GInstanceInitFunc) gtk_notebook_init,
-      };
-
-      notebook_type = g_type_register_static (GTK_TYPE_CONTAINER, I_("GtkNotebook"),
-                                             &notebook_info, 0);
-    }
-
-  return notebook_type;
-}
+G_DEFINE_TYPE (GtkNotebook, gtk_notebook, GTK_TYPE_CONTAINER)
 
 static void
 add_tab_bindings (GtkBindingSet    *binding_set,
@@ -480,8 +470,6 @@ gtk_notebook_class_init (GtkNotebookClass *class)
   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
   GtkBindingSet *binding_set;
   
-  parent_class = g_type_class_peek_parent (class);
-
   gobject_class->set_property = gtk_notebook_set_property;
   gobject_class->get_property = gtk_notebook_get_property;
   object_class->destroy = gtk_notebook_destroy;
@@ -497,7 +485,6 @@ gtk_notebook_class_init (GtkNotebookClass *class)
   widget_class->button_press_event = gtk_notebook_button_press;
   widget_class->button_release_event = gtk_notebook_button_release;
   widget_class->popup_menu = gtk_notebook_popup_menu;
-  widget_class->enter_notify_event = gtk_notebook_enter_notify;
   widget_class->leave_notify_event = gtk_notebook_leave_notify;
   widget_class->motion_notify_event = gtk_notebook_motion_notify;
   widget_class->grab_notify = gtk_notebook_grab_notify;
@@ -507,6 +494,7 @@ gtk_notebook_class_init (GtkNotebookClass *class)
   widget_class->focus = gtk_notebook_focus;
   widget_class->style_set = gtk_notebook_style_set;
   widget_class->drag_begin = gtk_notebook_drag_begin;
+  widget_class->drag_end = gtk_notebook_drag_end;
   widget_class->drag_motion = gtk_notebook_drag_motion;
   widget_class->drag_leave = gtk_notebook_drag_leave;
   widget_class->drag_drop = gtk_notebook_drag_drop;
@@ -771,6 +759,23 @@ gtk_notebook_class_init (GtkNotebookClass *class)
                                                             1,
                                                             GTK_PARAM_READABLE));
 
+  /**
+   * GtkNotebook:arrow-spacing:
+   *
+   * The "arrow-size" property defines the spacing between the scroll
+   * arrows and the tabs.
+   *
+   * Since: 2.10
+   */
+  gtk_widget_class_install_style_property (widget_class,
+                                           g_param_spec_int ("arrow-spacing",
+                                                             _("Arrow spacing"),
+                                                             _("Scroll arrow spacing"),
+                                                             0,
+                                                             G_MAXINT,
+                                                             0,
+                                                             GTK_PARAM_READABLE));
+
   notebook_signals[SWITCH_PAGE] =
     g_signal_new (I_("switch_page"),
                  G_TYPE_FROM_CLASS (gobject_class),
@@ -996,6 +1001,10 @@ gtk_notebook_init (GtkNotebook *notebook)
   priv->switch_tab_timer = 0;
   priv->source_targets = gtk_target_list_new (notebook_source_targets,
                                              G_N_ELEMENTS (notebook_source_targets));
+  priv->operation = DRAG_OPERATION_NONE;
+  priv->detached_tab = NULL;
+  priv->during_detach = FALSE;
+  priv->has_scrolled = FALSE;
 
   gtk_drag_dest_set (GTK_WIDGET (notebook),
                     GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
@@ -1057,14 +1066,33 @@ gtk_notebook_change_current_page (GtkNotebook *notebook,
 
   while (offset != 0)
     {
-      current = gtk_notebook_search_page (notebook, current, offset < 0 ? STEP_PREV : STEP_NEXT, TRUE);
+      current = gtk_notebook_search_page (notebook, current,
+                                          offset < 0 ? STEP_PREV : STEP_NEXT,
+                                          TRUE);
+
+      if (!current)
+        {
+          gboolean wrap_around;
+
+          g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
+                        "gtk-keynav-wrap-around", &wrap_around,
+                        NULL);
+
+          if (wrap_around)
+            current = gtk_notebook_search_page (notebook, NULL,
+                                                offset < 0 ? STEP_PREV : STEP_NEXT,
+                                                TRUE);
+          else
+            break;
+        }
+
       offset += offset < 0 ? 1 : -1;
     }
 
   if (current)
     gtk_notebook_switch_page (notebook, current->data, -1);
   else
-    gdk_display_beep (gtk_widget_get_display (GTK_WIDGET (notebook)));
+    gtk_widget_error_bell (GTK_WIDGET (notebook));
 }
 
 static GtkDirectionType
@@ -1113,6 +1141,31 @@ get_effective_tab_pos (GtkNotebook *notebook)
   return notebook->tab_pos;
 }
 
+static gint
+get_tab_gap_pos (GtkNotebook *notebook)
+{
+  gint tab_pos = get_effective_tab_pos (notebook);
+  gint gap_side = GTK_POS_BOTTOM;
+  
+  switch (tab_pos)
+    {
+    case GTK_POS_TOP:
+      gap_side = GTK_POS_BOTTOM;
+      break;
+    case GTK_POS_BOTTOM:
+      gap_side = GTK_POS_TOP;
+      break;
+    case GTK_POS_LEFT:
+      gap_side = GTK_POS_RIGHT;
+      break;
+    case GTK_POS_RIGHT:
+      gap_side = GTK_POS_LEFT;
+      break;
+    }
+
+  return gap_side;
+}
+
 static void
 gtk_notebook_move_focus_out (GtkNotebook      *notebook,
                             GtkDirectionType  direction_type)
@@ -1148,11 +1201,14 @@ reorder_tab (GtkNotebook *notebook, GList *position, GList *tab)
 {
   GList *elem;
 
+  if (position == tab)
+    return g_list_position (notebook->children, tab);
+
   /* check that we aren't inserting the tab in the
    * same relative position, taking packing into account */
   elem = (position) ? position->prev : g_list_last (notebook->children);
 
-  while (elem && GTK_NOTEBOOK_PAGE (elem)->pack != GTK_NOTEBOOK_PAGE (tab)->pack)
+  while (elem && elem != tab && GTK_NOTEBOOK_PAGE (elem)->pack != GTK_NOTEBOOK_PAGE (tab)->pack)
     elem = elem->prev;
 
   if (elem == tab)
@@ -1194,6 +1250,9 @@ gtk_notebook_reorder_tab (GtkNotebook      *notebook,
   GList *last, *child;
   gint page_num;
 
+  if (!gtk_widget_is_focus (GTK_WIDGET (notebook)))
+    return;
+
   if (!notebook->cur_page ||
       !notebook->cur_page->reorderable)
     return;
@@ -1234,7 +1293,7 @@ gtk_notebook_reorder_tab (GtkNotebook      *notebook,
       else
        page_num = reorder_tab (notebook, (page->pack == GTK_PACK_START) ? child : child->next, notebook->focus_tab);
 
-      gtk_notebook_pages_allocate (notebook, DRAG_OPERATION_NONE);
+      gtk_notebook_pages_allocate (notebook);
 
       g_signal_emit (notebook,
                     notebook_signals[PAGE_REORDERED],
@@ -1284,7 +1343,7 @@ gtk_notebook_destroy (GtkObject *object)
       priv->switch_tab_timer = 0;
     }
 
-  GTK_OBJECT_CLASS (parent_class)->destroy (object);
+  GTK_OBJECT_CLASS (gtk_notebook_parent_class)->destroy (object);
 }
 
 static void
@@ -1402,7 +1461,6 @@ gtk_notebook_get_property (GObject         *object,
  * gtk_notebook_button_press
  * gtk_notebook_button_release
  * gtk_notebook_popup_menu
- * gtk_notebook_enter_notify
  * gtk_notebook_leave_notify
  * gtk_notebook_motion_notify
  * gtk_notebook_focus_in
@@ -1410,6 +1468,7 @@ gtk_notebook_get_property (GObject         *object,
  * gtk_notebook_draw_focus
  * gtk_notebook_style_set
  * gtk_notebook_drag_begin
+ * gtk_notebook_drag_end
  * gtk_notebook_drag_motion
  * gtk_notebook_drag_drop
  * gtk_notebook_drag_data_get
@@ -1494,7 +1553,7 @@ gtk_notebook_map (GtkWidget *widget)
     gtk_widget_map (notebook->cur_page->child);
 
   if (notebook->scrollable)
-    gtk_notebook_pages_allocate (notebook, DRAG_OPERATION_NONE);
+    gtk_notebook_pages_allocate (notebook);
   else
     {
       children = notebook->children;
@@ -1526,7 +1585,7 @@ gtk_notebook_unmap (GtkWidget *widget)
 
   gdk_window_hide (GTK_NOTEBOOK (widget)->event_window);
 
-  GTK_WIDGET_CLASS (parent_class)->unmap (widget);
+  GTK_WIDGET_CLASS (gtk_notebook_parent_class)->unmap (widget);
 }
 
 static void
@@ -1556,7 +1615,7 @@ gtk_notebook_realize (GtkWidget *widget)
   attributes.event_mask = gtk_widget_get_events (widget);
   attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
                            GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK |
-                           GDK_BUTTON1_MOTION_MASK |
+                           GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK |
                            GDK_SCROLL_MASK);
   attributes_mask = GDK_WA_X | GDK_WA_Y;
 
@@ -1571,17 +1630,26 @@ static void
 gtk_notebook_unrealize (GtkWidget *widget)
 {
   GtkNotebook *notebook;
+  GtkNotebookPrivate *priv;
 
   g_return_if_fail (GTK_IS_NOTEBOOK (widget));
 
   notebook = GTK_NOTEBOOK (widget);
+  priv = GTK_NOTEBOOK_GET_PRIVATE (widget);
 
   gdk_window_set_user_data (notebook->event_window, NULL);
   gdk_window_destroy (notebook->event_window);
   notebook->event_window = NULL;
 
-  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
-    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+  if (priv->drag_window)
+    {
+      gdk_window_set_user_data (priv->drag_window, NULL);
+      gdk_window_destroy (priv->drag_window);
+      priv->drag_window = NULL;
+    }
+
+  if (GTK_WIDGET_CLASS (gtk_notebook_parent_class)->unrealize)
+    (* GTK_WIDGET_CLASS (gtk_notebook_parent_class)->unrealize) (widget);
 }
 
 static void
@@ -1597,13 +1665,19 @@ gtk_notebook_size_request (GtkWidget      *widget,
   gint focus_width;
   gint tab_overlap;
   gint tab_curvature;
+  gint arrow_spacing;
+  gint scroll_arrow_hlength;
+  gint scroll_arrow_vlength;
 
   gtk_widget_style_get (widget,
                        "focus-line-width", &focus_width,
                        "tab-overlap", &tab_overlap,
                        "tab-curvature", &tab_curvature,
+                        "arrow-spacing", &arrow_spacing,
+                        "scroll-arrow-hlength", &scroll_arrow_hlength,
+                        "scroll-arrow-vlength", &scroll_arrow_vlength,
                        NULL);
-  
+
   widget->requisition.width = 0;
   widget->requisition.height = 0;
 
@@ -1703,7 +1777,7 @@ gtk_notebook_size_request (GtkWidget      *widget,
 
                  if (notebook->scrollable && vis_pages > 1 && 
                      widget->requisition.width < tab_width)
-                   tab_height = MAX (tab_height, ARROW_SIZE);
+                   tab_height = MAX (tab_height, scroll_arrow_hlength);
 
                  padding = 2 * (tab_curvature + focus_width +
                                 notebook->tab_hborder) - tab_overlap;
@@ -1727,7 +1801,7 @@ gtk_notebook_size_request (GtkWidget      *widget,
 
                  if (notebook->scrollable && vis_pages > 1 &&
                      widget->requisition.width < tab_width)
-                   tab_width = tab_max + 2 * (ARROW_SIZE + ARROW_SPACING);
+                   tab_width = tab_max + 2 * (scroll_arrow_hlength + arrow_spacing);
 
                   if (notebook->homogeneous && !notebook->scrollable)
                     widget->requisition.width = MAX (widget->requisition.width,
@@ -1746,7 +1820,8 @@ gtk_notebook_size_request (GtkWidget      *widget,
 
                  if (notebook->scrollable && vis_pages > 1 && 
                      widget->requisition.height < tab_height)
-                   tab_width = MAX (tab_width, ARROW_SPACING + 2 * ARROW_SIZE);
+                   tab_width = MAX (tab_width,
+                                     arrow_spacing + 2 * scroll_arrow_vlength);
 
                  padding = 2 * (tab_curvature + focus_width +
                                 notebook->tab_vborder) - tab_overlap;
@@ -1772,7 +1847,7 @@ gtk_notebook_size_request (GtkWidget      *widget,
 
                  if (notebook->scrollable && vis_pages > 1 && 
                      widget->requisition.height < tab_height)
-                   tab_height = tab_max + ARROW_SIZE + ARROW_SPACING;
+                   tab_height = tab_max + scroll_arrow_vlength + arrow_spacing;
 
                  widget->requisition.width += tab_width;
 
@@ -1847,7 +1922,6 @@ gtk_notebook_size_allocate (GtkWidget     *widget,
                            GtkAllocation *allocation)
 {
   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
-  gint vis_pages = 0;
   gint tab_pos = get_effective_tab_pos (notebook);
 
   widget->allocation = *allocation;
@@ -1917,13 +1991,10 @@ gtk_notebook_size_allocate (GtkWidget     *widget,
          children = children->next;
          
          if (GTK_WIDGET_VISIBLE (page->child))
-           {
-             gtk_widget_size_allocate (page->child, &child_allocation);
-             vis_pages++;
-           }
+           gtk_widget_size_allocate (page->child, &child_allocation);
        }
 
-      gtk_notebook_pages_allocate (notebook, DRAG_OPERATION_NONE);
+      gtk_notebook_pages_allocate (notebook);
     }
 }
 
@@ -1932,15 +2003,27 @@ gtk_notebook_expose (GtkWidget      *widget,
                     GdkEventExpose *event)
 {
   GtkNotebook *notebook;
+  GtkNotebookPrivate *priv;
   GdkRectangle child_area;
-   
+
   g_return_val_if_fail (GTK_IS_NOTEBOOK (widget), FALSE);
   g_return_val_if_fail (event != NULL, FALSE);
 
-  if (GTK_WIDGET_DRAWABLE (widget))
+  notebook = GTK_NOTEBOOK (widget);
+  priv = GTK_NOTEBOOK_GET_PRIVATE (widget);
+
+  if (event->window == priv->drag_window)
     {
-      notebook = GTK_NOTEBOOK (widget);
+      GdkRectangle area = { 0, };
 
+      gdk_drawable_get_size (priv->drag_window,
+                            &area.width, &area.height);
+      gtk_notebook_draw_tab (notebook,
+                            notebook->cur_page,
+                            &area);
+    }
+  else if (GTK_WIDGET_DRAWABLE (widget))
+    {
       gtk_notebook_paint (widget, &event->area);
       if (notebook->show_tabs)
        {
@@ -1994,13 +2077,21 @@ gtk_notebook_get_arrow_rect (GtkNotebook     *notebook,
 
   if (gtk_notebook_get_event_window_position (notebook, &event_window_pos))
     {
-      rectangle->width = ARROW_SIZE;
-      rectangle->height = ARROW_SIZE;
+      gint scroll_arrow_hlength;
+      gint scroll_arrow_vlength;
+
+      gtk_widget_style_get (GTK_WIDGET (notebook),
+                            "scroll-arrow-hlength", &scroll_arrow_hlength,
+                            "scroll-arrow-vlength", &scroll_arrow_vlength,
+                            NULL);
 
       switch (notebook->tab_pos)
        {
        case GTK_POS_LEFT:
        case GTK_POS_RIGHT:
+          rectangle->width = scroll_arrow_vlength;
+          rectangle->height = scroll_arrow_vlength;
+
          if ((before && (notebook->has_before_previous != notebook->has_before_next)) ||
              (!before && (notebook->has_after_previous != notebook->has_after_next))) 
          rectangle->x = event_window_pos.x + (event_window_pos.width - rectangle->width) / 2;
@@ -2012,8 +2103,12 @@ gtk_notebook_get_arrow_rect (GtkNotebook     *notebook,
          if (!before)
            rectangle->y += event_window_pos.height - rectangle->height;
          break;
+
        case GTK_POS_TOP:
        case GTK_POS_BOTTOM:
+          rectangle->width = scroll_arrow_hlength;
+          rectangle->height = scroll_arrow_hlength;
+
          if (before)
            {
              if (left || !notebook->has_before_previous)
@@ -2062,7 +2157,7 @@ gtk_notebook_get_arrow (GtkNotebook *notebook,
       
          x0 = x - arrow_rect.x;
          y0 = y - arrow_rect.y;
-         
+
          if (y0 >= 0 && y0 < arrow_rect.height &&
              x0 >= 0 && x0 < arrow_rect.width)
            return arrow[i];
@@ -2083,7 +2178,7 @@ gtk_notebook_do_arrow (GtkNotebook     *notebook,
   is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
   left = (ARROW_IS_LEFT (arrow) && !is_rtl) || 
          (!ARROW_IS_LEFT (arrow) && is_rtl);
-  
+
   if (!notebook->focus_tab ||
       gtk_notebook_search_page (notebook, notebook->focus_tab,
                                left ? STEP_PREV : STEP_NEXT,
@@ -2117,19 +2212,7 @@ gtk_notebook_arrow_button_press (GtkNotebook      *notebook,
   if (button == 1)
     {
       gtk_notebook_do_arrow (notebook, arrow);
-      
-      if (!notebook->timer)
-       {
-          GtkSettings *settings = gtk_widget_get_settings (widget);
-          guint        timeout;
-
-          g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
-
-         notebook->timer = g_timeout_add (timeout,
-                                          (GSourceFunc) gtk_notebook_timer,
-                                          (gpointer) notebook);
-         notebook->need_timer = TRUE;
-       }
+      gtk_notebook_set_scroll_timer (notebook);
     }
   else if (button == 2)
     gtk_notebook_page_select (notebook, TRUE);
@@ -2159,7 +2242,7 @@ get_widget_coordinates (GtkWidget *widget,
   while (window && window != widget->window)
     {
       gint window_x, window_y;
-      
+
       gdk_window_get_position (window, &window_x, &window_y);
       tx += window_x;
       ty += window_y;
@@ -2277,19 +2360,29 @@ gtk_notebook_button_press (GtkWidget      *widget,
       page = tab->data;
       page_changed = page != notebook->cur_page;
       was_focus = gtk_widget_is_focus (widget);
-         
+
       gtk_notebook_switch_focus_tab (notebook, tab);
       gtk_widget_grab_focus (widget);
-         
+
       if (page_changed && !was_focus)
        gtk_widget_child_focus (page->child, GTK_DIR_TAB_FORWARD);
-         
+
       /* save press to possibly begin a drag */
       if (page->reorderable || page->detachable)
        {
          priv->during_detach = FALSE;
+         priv->during_reorder = FALSE;
          priv->pressed_button = event->button;
-         gtk_grab_add (widget);
+
+         gdk_window_get_pointer (widget->window,
+                                 &priv->mouse_x,
+                                 &priv->mouse_y,
+                                 NULL);
+
+         priv->drag_begin_x = priv->mouse_x;
+         priv->drag_begin_y = priv->mouse_y;
+         priv->drag_offset_x = priv->drag_begin_x - page->allocation.x;
+         priv->drag_offset_y = priv->drag_begin_y - page->allocation.y;
        }
     }
 
@@ -2365,22 +2458,28 @@ stop_scrolling (GtkNotebook *notebook)
 
 static GList*
 get_drop_position (GtkNotebook *notebook,
-                  guint        pack,
-                  gint         x,
-                  gint         y)
+                  guint        pack)
 {
-  GList *children;
+  GtkNotebookPrivate *priv;
+  GList *children, *last_child;
   GtkNotebookPage *page;
   gboolean is_rtl;
-  
+  gint x, y;
+
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+  x = priv->mouse_x;
+  y = priv->mouse_y;
+
   is_rtl = gtk_widget_get_direction ((GtkWidget *) notebook) == GTK_TEXT_DIR_RTL;
   children = notebook->children;
+  last_child = NULL;
 
   while (children)
     {
       page = children->data;
 
-      if (GTK_WIDGET_VISIBLE (page->child) &&
+      if ((priv->operation != DRAG_OPERATION_REORDER || page != notebook->cur_page) &&
+         GTK_WIDGET_VISIBLE (page->child) &&
          page->tab_label &&
          GTK_WIDGET_MAPPED (page->tab_label) &&
          page->pack == pack)
@@ -2411,58 +2510,132 @@ get_drop_position (GtkNotebook *notebook,
 
              break;
            }
+
+         last_child = children->next;
        }
 
       children = children->next;
     }
 
-  return NULL;
+  return last_child;
 }
 
-static gint
-gtk_notebook_button_release (GtkWidget      *widget,
-                            GdkEventButton *event)
+static void
+show_drag_window (GtkNotebook        *notebook,
+                 GtkNotebookPrivate *priv,
+                 GtkNotebookPage    *page)
 {
-  GtkNotebook *notebook;
-  GtkNotebookPrivate *priv;
-  GList *element;
-  gint old_page_num, page_num;
+  GtkWidget *widget = GTK_WIDGET (notebook);
 
-  g_return_val_if_fail (GTK_IS_NOTEBOOK (widget), FALSE);
-  g_return_val_if_fail (event != NULL, FALSE);
+  if (!priv->drag_window)
+    {
+      GdkWindowAttr attributes;
+      guint attributes_mask;
 
-  if (event->type != GDK_BUTTON_RELEASE)
-    return FALSE;
+      attributes.x = page->allocation.x;
+      attributes.y = page->allocation.y;
+      attributes.width = page->allocation.width;
+      attributes.height = page->allocation.height;
+      attributes.window_type = GDK_WINDOW_CHILD;
+      attributes.wclass = GDK_INPUT_OUTPUT;
+      attributes.visual = gtk_widget_get_visual (widget);
+      attributes.colormap = gtk_widget_get_colormap (widget);
+      attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
+      attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
 
-  notebook = GTK_NOTEBOOK (widget);
-  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+      priv->drag_window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                                         &attributes,
+                                         attributes_mask);
+      gdk_window_set_user_data (priv->drag_window, widget);
+    }
 
-  if (notebook->cur_page->reorderable &&
-      event->button == priv->pressed_button)
+  g_object_ref (page->tab_label);
+  gtk_widget_unparent (page->tab_label);
+  gtk_widget_set_parent_window (page->tab_label, priv->drag_window);
+  gtk_widget_set_parent (page->tab_label, widget);
+  g_object_unref (page->tab_label);
+
+  gdk_window_show (priv->drag_window);
+
+  /* the grab will dissapear when the window is hidden */
+  gdk_pointer_grab (priv->drag_window,
+                   FALSE,
+                   GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
+                   NULL, NULL, GDK_CURRENT_TIME);
+}
+
+/* This function undoes the reparenting that happens both when drag_window
+ * is shown for reordering and when the DnD icon is shown for detaching
+ */
+static void
+hide_drag_window (GtkNotebook        *notebook,
+                 GtkNotebookPrivate *priv,
+                 GtkNotebookPage    *page)
+{
+  GtkWidget *widget = GTK_WIDGET (notebook);
+  GtkWidget *parent = page->tab_label->parent;
+
+  if (page->tab_label->window != widget->window ||
+      !NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
     {
-      gtk_grab_remove (widget);
-      priv->pressed_button = -1;
+      g_object_ref (page->tab_label);
+
+      if (GTK_IS_WINDOW (parent))
+       {
+         /* parent widget is the drag window */
+         gtk_container_remove (GTK_CONTAINER (parent), page->tab_label);
+       }
+      else
+       gtk_widget_unparent (page->tab_label);
+
+      gtk_widget_set_parent_window (page->tab_label, widget->window);
+      gtk_widget_set_parent (page->tab_label, widget);
+      g_object_unref (page->tab_label);
+    }
+
+  if (priv->drag_window &&
+      gdk_window_is_visible (priv->drag_window))
+    gdk_window_hide (priv->drag_window);
+}
 
-      if (!priv->during_detach)
+static void
+gtk_notebook_stop_reorder (GtkNotebook *notebook)
+{
+  GtkNotebookPrivate *priv;
+  GtkNotebookPage *page;
+
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+  page = notebook->cur_page;
+
+  if (!page)
+    return;
+
+  priv->pressed_button = -1;
+
+  if (page->reorderable || page->detachable)
+    {
+      if (priv->during_reorder)
        {
-         element = get_drop_position (notebook,
-                                      notebook->cur_page->pack,
-                                      PAGE_MIDDLE_X (notebook->cur_page),
-                                      PAGE_MIDDLE_Y (notebook->cur_page));
+         gint old_page_num, page_num;
+         GList *element;
+
+         element = get_drop_position (notebook, page->pack);
          old_page_num = g_list_position (notebook->children, notebook->focus_tab);
          page_num = reorder_tab (notebook, element, notebook->focus_tab);
 
-         if (priv->has_scrolled ||
-             old_page_num != page_num)
+         if (priv->has_scrolled || old_page_num != page_num)
            g_signal_emit (notebook,
                           notebook_signals[PAGE_REORDERED], 0,
-                          ((GtkNotebookPage *) notebook->focus_tab->data)->child,
-                          page_num);
+                          page->child, page_num);
 
          priv->has_scrolled = FALSE;
+          priv->during_reorder = FALSE; 
        }
 
-      gtk_notebook_pages_allocate (notebook, DRAG_OPERATION_NONE);
+      hide_drag_window (notebook, priv, page);
+
+      priv->operation = DRAG_OPERATION_NONE;
+      gtk_notebook_pages_allocate (notebook);
 
       if (priv->dnd_timer)
        {
@@ -2470,44 +2643,38 @@ gtk_notebook_button_release (GtkWidget      *widget,
          priv->dnd_timer = 0;
        }
     }
-
-  if (event->button == notebook->button)
-    {
-      stop_scrolling (notebook);
-
-      return TRUE;
-    }
-  else
-    return FALSE;
 }
 
 static gint
-gtk_notebook_enter_notify (GtkWidget        *widget,
-                          GdkEventCrossing *event)
+gtk_notebook_button_release (GtkWidget      *widget,
+                            GdkEventButton *event)
 {
   GtkNotebook *notebook;
-  GtkNotebookArrow arrow;
-  gint x, y;
+  GtkNotebookPrivate *priv;
+  GtkNotebookPage *page;
 
   g_return_val_if_fail (GTK_IS_NOTEBOOK (widget), FALSE);
   g_return_val_if_fail (event != NULL, FALSE);
 
-  notebook = GTK_NOTEBOOK (widget);
-
-  if (!get_widget_coordinates (widget, (GdkEvent *)event, &x, &y))
+  if (event->type != GDK_BUTTON_RELEASE)
     return FALSE;
 
-  arrow = gtk_notebook_get_arrow (notebook, x, y);
+  notebook = GTK_NOTEBOOK (widget);
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+  page = notebook->cur_page;
 
-  if (arrow != notebook->in_child)
-    {
-      notebook->in_child = arrow;
-      gtk_notebook_redraw_arrows (notebook);
+  if (!priv->during_detach &&
+      page->reorderable &&
+      event->button == priv->pressed_button)
+    gtk_notebook_stop_reorder (notebook);
 
+  if (event->button == notebook->button)
+    {
+      stop_scrolling (notebook);
       return TRUE;
     }
-
-  return TRUE;
+  else
+    return FALSE;
 }
 
 static gint
@@ -2543,20 +2710,26 @@ get_pointer_position (GtkNotebook *notebook)
   if (notebook->tab_pos == GTK_POS_TOP ||
       notebook->tab_pos == GTK_POS_BOTTOM)
     {
+      gint x;
+
       is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
+      x = priv->mouse_x - widget->allocation.x;
 
-      if (priv->mouse_x > widget->allocation.width - 2 * container->border_width - SCROLL_THRESHOLD)
+      if (x > widget->allocation.width - 2 * container->border_width - SCROLL_THRESHOLD)
        return (is_rtl) ? POINTER_BEFORE : POINTER_AFTER;
-      else if (priv->mouse_x < SCROLL_THRESHOLD + container->border_width)
+      else if (x < SCROLL_THRESHOLD + container->border_width)
        return (is_rtl) ? POINTER_AFTER : POINTER_BEFORE;
       else
        return POINTER_BETWEEN;
     }
   else
     {
-      if (priv->mouse_y > widget->allocation.height - 2 * container->border_width - SCROLL_THRESHOLD)
+      gint y;
+
+      y = priv->mouse_y - widget->allocation.y;
+      if (y > widget->allocation.height - 2 * container->border_width - SCROLL_THRESHOLD)
        return POINTER_AFTER;
-      else if (priv->mouse_y < SCROLL_THRESHOLD + container->border_width)
+      else if (y < SCROLL_THRESHOLD + container->border_width)
        return POINTER_BEFORE;
       else
        return POINTER_BETWEEN;
@@ -2574,20 +2747,22 @@ scroll_notebook_timer (gpointer data)
   priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
   pointer_position = get_pointer_position (notebook);
 
-  element = get_drop_position (notebook,
-                              notebook->cur_page->pack,
-                              PAGE_MIDDLE_X (notebook->cur_page),
-                              PAGE_MIDDLE_Y (notebook->cur_page));
-
+  element = get_drop_position (notebook, notebook->cur_page->pack);
   reorder_tab (notebook, element, notebook->focus_tab);
-
   first_tab = gtk_notebook_search_page (notebook, notebook->first_tab,
                                        (pointer_position == POINTER_BEFORE) ? STEP_PREV : STEP_NEXT,
                                        TRUE);
   if (first_tab)
     {
       notebook->first_tab = first_tab;
-      gtk_notebook_pages_allocate (notebook, DRAG_OPERATION_REORDER);
+      gtk_notebook_pages_allocate (notebook);
+
+      gdk_window_move_resize (priv->drag_window,
+                             priv->drag_window_x,
+                             priv->drag_window_y,
+                             notebook->cur_page->allocation.width,
+                             notebook->cur_page->allocation.height);
+      gdk_window_raise (priv->drag_window);
     }
 
   return TRUE;
@@ -2599,7 +2774,7 @@ check_threshold (GtkNotebook *notebook,
                 gint         current_y)
 {
   GtkWidget *widget;
-  gint dnd_threshold, width, height;
+  gint dnd_threshold;
   GdkRectangle rectangle = { 0, }; /* shut up gcc */
   GtkSettings *settings;
   
@@ -2607,15 +2782,16 @@ check_threshold (GtkNotebook *notebook,
   settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
   g_object_get (G_OBJECT (settings), "gtk-dnd-drag-threshold", &dnd_threshold, NULL);
 
-  gdk_drawable_get_size (GDK_DRAWABLE (notebook->event_window), &width, &height);
-
   /* we want a large threshold */
   dnd_threshold *= DND_THRESHOLD_MULTIPLIER;
 
-  rectangle.x = - dnd_threshold;
-  rectangle.width = width + 2 * dnd_threshold;
-  rectangle.y = - dnd_threshold;
-  rectangle.height = height + 2 * dnd_threshold;
+  gdk_window_get_position (notebook->event_window, &rectangle.x, &rectangle.y);
+  gdk_drawable_get_size (GDK_DRAWABLE (notebook->event_window), &rectangle.width, &rectangle.height);
+
+  rectangle.x -= dnd_threshold;
+  rectangle.width += 2 * dnd_threshold;
+  rectangle.y -= dnd_threshold;
+  rectangle.height += 2 * dnd_threshold;
 
   return (current_x < rectangle.x ||
          current_x > rectangle.x + rectangle.width ||
@@ -2629,31 +2805,47 @@ gtk_notebook_motion_notify (GtkWidget      *widget,
 {
   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
   GtkNotebookPrivate *priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+  GtkNotebookPage *page;
+  GtkNotebookArrow arrow;
   GtkNotebookPointerPosition pointer_position;
   GtkSettings *settings;
-  guint        timeout;
-  GList       *element;
+  guint timeout;
 
-  if (priv->pressed_button == -1)
+  page = notebook->cur_page;
+
+  if (!page)
     return FALSE;
 
-  if (!notebook->cur_page)
+  if (!(event->state & GDK_BUTTON1_MASK) &&
+      priv->pressed_button != -1)
+    {
+      gtk_notebook_stop_reorder (notebook);
+      stop_scrolling (notebook);
+    }
+
+  if (event->time < priv->timestamp + MSECS_BETWEEN_UPDATES)
     return FALSE;
 
-  gdk_window_get_pointer (notebook->event_window,
+  priv->timestamp = event->time;
+  gdk_window_get_pointer (widget->window,
                          &priv->mouse_x,
                          &priv->mouse_y,
                          NULL);
 
-  if (notebook->cur_page->detachable &&
+  arrow = gtk_notebook_get_arrow (notebook, priv->mouse_x, priv->mouse_y);
+  if (arrow != notebook->in_child)
+    {
+      notebook->in_child = arrow;
+      gtk_notebook_redraw_arrows (notebook);
+    }
+
+  if (priv->pressed_button == -1)
+    return FALSE;
+
+  if (page->detachable &&
       check_threshold (notebook, priv->mouse_x, priv->mouse_y))
     {
-      /* move the page to the current position */
-      element = get_drop_position (notebook,
-                                  notebook->cur_page->pack,
-                                  PAGE_MIDDLE_X (notebook->cur_page),
-                                  PAGE_MIDDLE_Y (notebook->cur_page));
-      reorder_tab (notebook, element, notebook->focus_tab);
+      priv->detached_tab = notebook->cur_page;
       priv->during_detach = TRUE;
 
       gtk_drag_begin (widget, priv->source_targets, GDK_ACTION_MOVE,
@@ -2661,21 +2853,26 @@ gtk_notebook_motion_notify (GtkWidget      *widget,
       return TRUE;
     }
 
-  if (notebook->cur_page->reorderable)
+  if (page->reorderable &&
+      (priv->during_reorder ||
+       gtk_drag_check_threshold (widget, priv->drag_begin_x, priv->drag_begin_y, priv->mouse_x, priv->mouse_y)))
     {
+      priv->during_reorder = TRUE;
       pointer_position = get_pointer_position (notebook);
 
-      if (pointer_position != POINTER_BETWEEN &&
+      if (event->window == priv->drag_window &&
+         pointer_position != POINTER_BETWEEN &&
          gtk_notebook_show_arrows (notebook))
        {
+         /* scroll tabs */
          if (!priv->dnd_timer)
            {
              priv->has_scrolled = TRUE;
              settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
              g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
 
-             priv->dnd_timer = g_timeout_add (timeout * SCROLL_DELAY_FACTOR,
-                                              (GSourceFunc) scroll_notebook_timer, 
+             priv->dnd_timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
+                                              scroll_notebook_timer, 
                                               (gpointer) notebook);
            }
        }
@@ -2688,7 +2885,23 @@ gtk_notebook_motion_notify (GtkWidget      *widget,
            }
        }
 
-      gtk_notebook_pages_allocate (notebook, DRAG_OPERATION_REORDER);
+      if (event->window == priv->drag_window ||
+         priv->operation != DRAG_OPERATION_REORDER)
+       {
+         /* the drag operation is beginning, create the window */
+         if (priv->operation != DRAG_OPERATION_REORDER)
+           {
+             priv->operation = DRAG_OPERATION_REORDER;
+             show_drag_window (notebook, priv, page);
+           }
+
+         gtk_notebook_pages_allocate (notebook);
+         gdk_window_move_resize (priv->drag_window,
+                                 priv->drag_window_x,
+                                 priv->drag_window_y,
+                                 page->allocation.width,
+                                 page->allocation.height);
+       }
     }
 
   return TRUE;
@@ -2698,8 +2911,13 @@ static void
 gtk_notebook_grab_notify (GtkWidget *widget,
                          gboolean   was_grabbed)
 {
+  GtkNotebook *notebook = GTK_NOTEBOOK (widget);
+
   if (!was_grabbed)
-    stop_scrolling (GTK_NOTEBOOK (widget));
+    {
+      gtk_notebook_stop_reorder (notebook);
+      stop_scrolling (notebook);
+    }
 }
 
 static void
@@ -2780,7 +2998,33 @@ gtk_notebook_style_set  (GtkWidget *widget,
   notebook->has_after_previous = has_after_previous;
   notebook->has_after_next = has_after_next;
   
-  (* GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous);
+  (* GTK_WIDGET_CLASS (gtk_notebook_parent_class)->style_set) (widget, previous);
+}
+
+static gboolean
+on_drag_icon_expose (GtkWidget      *widget,
+                    GdkEventExpose *event,
+                    gpointer        data)
+{
+  GtkWidget *notebook, *child = GTK_WIDGET (data);
+  GtkRequisition requisition;
+  gint gap_pos;
+
+  notebook = GTK_WIDGET (data);
+  child = GTK_BIN (widget)->child;
+  gtk_widget_size_request (widget, &requisition);
+  gap_pos = get_tab_gap_pos (GTK_NOTEBOOK (notebook));
+
+  gtk_paint_extension (notebook->style, widget->window,
+                      GTK_STATE_NORMAL, GTK_SHADOW_OUT,
+                      NULL, widget, "tab",
+                      0, 0,
+                      requisition.width, requisition.height,
+                      gap_pos);
+  if (child)
+    gtk_container_propagate_expose (GTK_CONTAINER (widget), child, event);
+
+  return TRUE;
 }
 
 static void
@@ -2790,7 +3034,6 @@ gtk_notebook_drag_begin (GtkWidget        *widget,
   GtkNotebookPrivate *priv = GTK_NOTEBOOK_GET_PRIVATE (widget);
   GtkNotebook *notebook = (GtkNotebook*) widget;
   GtkWidget *tab_label;
-  GdkPixmap *pixmap;
 
   if (priv->dnd_timer)
     {
@@ -2798,34 +3041,41 @@ gtk_notebook_drag_begin (GtkWidget        *widget,
       priv->dnd_timer = 0;
     }
 
-  tab_label = notebook->cur_page->tab_label;
+  priv->operation = DRAG_OPERATION_DETACH;
+  gtk_notebook_pages_allocate (notebook);
+
+  tab_label = priv->detached_tab->tab_label;
+
+  hide_drag_window (notebook, priv, notebook->cur_page);
+  g_object_ref (tab_label);
+  gtk_widget_unparent (tab_label);
 
-  pixmap = gdk_pixmap_new (tab_label->window,
-                          tab_label->allocation.width + 2,
-                          tab_label->allocation.height + 2,
-                          -1);
+  priv->dnd_window = gtk_window_new (GTK_WINDOW_POPUP);
+  gtk_container_add (GTK_CONTAINER (priv->dnd_window), tab_label);
+  gtk_widget_set_size_request (priv->dnd_window,
+                              priv->detached_tab->allocation.width,
+                              priv->detached_tab->allocation.height);
+  g_object_unref (tab_label);
 
-  gdk_draw_drawable (pixmap,
-                    tab_label->style->base_gc [GTK_WIDGET_STATE (widget)],
-                    tab_label->window,
-                    tab_label->allocation.x,
-                    tab_label->allocation.y,
-                    1, 1, -1, -1);
+  g_signal_connect (G_OBJECT (priv->dnd_window), "expose-event",
+                   G_CALLBACK (on_drag_icon_expose), notebook);
 
-  gdk_draw_rectangle (pixmap,
-                      widget->style->black_gc,
-                      FALSE,
-                      0, 0,
-                     tab_label->allocation.width + 1,
-                     tab_label->allocation.height + 1);
+  gtk_drag_set_icon_widget (context, priv->dnd_window, -2, -2);
+}
+
+static void
+gtk_notebook_drag_end (GtkWidget      *widget,
+                      GdkDragContext *context)
+{
+  GtkNotebookPrivate *priv = GTK_NOTEBOOK_GET_PRIVATE (widget);
+
+  gtk_notebook_stop_reorder (GTK_NOTEBOOK (widget));
 
-  gtk_drag_set_icon_pixmap (context,
-                           gdk_drawable_get_colormap (pixmap),
-                           pixmap,
-                           NULL,
-                           -2, -2);
+  GTK_BIN (priv->dnd_window)->child = NULL;
+  gtk_widget_destroy (priv->dnd_window);
+  priv->dnd_window = NULL;
 
-  gtk_notebook_pages_allocate (GTK_NOTEBOOK (widget), DRAG_OPERATION_DETACH);
+  priv->operation = DRAG_OPERATION_NONE;
 }
 
 static gboolean
@@ -2871,6 +3121,18 @@ gtk_notebook_drag_motion (GtkWidget      *widget,
   GdkAtom target, tab_target;
 
   notebook = GTK_NOTEBOOK (widget);
+  arrow = gtk_notebook_get_arrow (notebook,
+                                 x + widget->allocation.x,
+                                 y + widget->allocation.y);
+  if (arrow)
+    {
+      notebook->click_child = arrow;
+      gtk_notebook_set_scroll_timer (notebook);
+      gdk_drag_status (context, 0, time);
+      return TRUE;
+    }
+
+  stop_scrolling (notebook);
   target = gtk_drag_dest_find_target (widget, context, NULL);
   tab_target = gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB");
 
@@ -2880,7 +3142,6 @@ gtk_notebook_drag_motion (GtkWidget      *widget,
       GtkWidget *source_widget;
 
       source_widget = gtk_drag_get_source_widget (context);
-
       g_assert (source_widget);
 
       widget_group = gtk_notebook_get_group_id (notebook);
@@ -2888,7 +3149,9 @@ gtk_notebook_drag_motion (GtkWidget      *widget,
 
       if (widget_group != -1 &&
          source_widget_group != -1 &&
-         widget_group == source_widget_group)
+         widget_group == source_widget_group &&
+         !(widget == GTK_NOTEBOOK (source_widget)->cur_page->child || 
+           gtk_widget_is_ancestor (widget, GTK_NOTEBOOK (source_widget)->cur_page->child)))
        {
          gdk_drag_status (context, GDK_ACTION_MOVE, time);
          return TRUE;
@@ -2913,20 +3176,13 @@ gtk_notebook_drag_motion (GtkWidget      *widget,
       priv->mouse_x = x;
       priv->mouse_y = y;
 
-      arrow = gtk_notebook_get_arrow (notebook, x, y);
-
-      if (arrow)
-       return gtk_notebook_arrow_button_press (notebook, arrow, 1);
-
-      stop_scrolling (notebook);
-
-      if (!priv->switch_tab_timer)
-       {
-          settings = gtk_widget_get_settings (widget);
+      if (!priv->switch_tab_timer)
+       {
+          settings = gtk_widget_get_settings (widget);
 
           g_object_get (settings, "gtk-timeout-expand", &timeout, NULL);
-         priv->switch_tab_timer = g_timeout_add (timeout,
-                                                 (GSourceFunc) gtk_notebook_switch_tab_timeout,
+         priv->switch_tab_timer = gdk_threads_add_timeout (timeout,
+                                                 gtk_notebook_switch_tab_timeout,
                                                  widget);
        }
     }
@@ -2984,6 +3240,7 @@ do_detach_tab (GtkNotebook     *from,
               gint             x,
               gint             y)
 {
+  GtkNotebookPrivate *priv;
   GtkWidget *tab_label, *menu_label;
   gboolean tab_expand, tab_fill, reorderable, detachable;
   GList *element;
@@ -3013,11 +3270,11 @@ do_detach_tab (GtkNotebook     *from,
 
   gtk_container_remove (GTK_CONTAINER (from), child);
 
-  element = get_drop_position (to,
-                              tab_pack,
-                              GTK_WIDGET (to)->allocation.x + x,
-                              GTK_WIDGET (to)->allocation.y + y);
+  priv = GTK_NOTEBOOK_GET_PRIVATE (to);
+  priv->mouse_x = x + GTK_WIDGET (to)->allocation.x;
+  priv->mouse_y = y + GTK_WIDGET (to)->allocation.y;
 
+  element = get_drop_position (to, tab_pack);
   page_num = g_list_position (to->children, element);
   gtk_notebook_insert_page_menu (to, child, tab_label, menu_label, page_num);
 
@@ -3048,6 +3305,7 @@ gtk_notebook_drag_data_get (GtkWidget        *widget,
                            guint             time)
 {
   GtkNotebook *dest_notebook, *notebook;
+  GtkNotebookPrivate *priv;
 
   if (data->target != gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB") &&
       (data->target != gdk_atom_intern_static_string ("application/x-rootwindow-drop") ||
@@ -3055,13 +3313,14 @@ gtk_notebook_drag_data_get (GtkWidget        *widget,
     return;
 
   notebook = GTK_NOTEBOOK (widget);
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
 
   if (data->target == gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB"))
     {
       gtk_selection_data_set (data,
                              data->target,
                              8,
-                             (void*) &notebook->cur_page->child,
+                             (void*) &priv->detached_tab->child,
                              sizeof (gpointer));
     }
   else
@@ -3073,12 +3332,11 @@ gtk_notebook_drag_data_get (GtkWidget        *widget,
       gdk_display_get_pointer (display, NULL, &x, &y, NULL);
 
       dest_notebook = (* window_creation_hook) (notebook,
-                                               notebook->cur_page->child,
+                                               priv->detached_tab->child,
                                                x, y,
                                                window_creation_hook_data);
-
       if (dest_notebook)
-       do_detach_tab (notebook, dest_notebook, notebook->cur_page->child, 0, 0);
+       do_detach_tab (notebook, dest_notebook, priv->detached_tab->child, 0, 0);
     }
 }
 
@@ -3342,11 +3600,24 @@ focus_tabs_move (GtkNotebook     *notebook,
 
   new_page = gtk_notebook_search_page (notebook, notebook->focus_tab,
                                       search_direction, TRUE);
+  if (!new_page)
+    {
+      gboolean wrap_around;
+
+      g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
+                    "gtk-keynav-wrap-around", &wrap_around,
+                    NULL);
+
+      if (wrap_around)
+        new_page = gtk_notebook_search_page (notebook, NULL,
+                                             search_direction, TRUE);
+    }
+
   if (new_page)
     gtk_notebook_switch_focus_tab (notebook, new_page);
   else
-    gdk_display_beep (gtk_widget_get_display (GTK_WIDGET (notebook)));
-  
+    gtk_widget_error_bell (GTK_WIDGET (notebook));
+
   return TRUE;
 }
 
@@ -3517,7 +3788,7 @@ gtk_notebook_set_focus_child (GtkContainer *container,
        }
     }
 
-  parent_class->set_focus_child (container, child);
+  GTK_CONTAINER_CLASS (gtk_notebook_parent_class)->set_focus_child (container, child);
 }
 
 static void
@@ -3542,6 +3813,7 @@ gtk_notebook_forall (GtkContainer *container,
       page = children->data;
       children = children->next;
       (* callback) (page->child, callback_data);
+
       if (include_internals)
        {
          if (page->tab_label)
@@ -3565,7 +3837,7 @@ page_visible_cb (GtkWidget  *page,
                  GParamSpec *arg,
                  gpointer    data)
 {
-  GtkNotebook *notebook = (GtkNotebook *)data;
+  GtkNotebook *notebook = (GtkNotebook *) data;
   GList *list;
   GList *next = NULL;
 
@@ -3707,6 +3979,7 @@ gtk_notebook_real_insert_page (GtkNotebook *notebook,
  * gtk_notebook_real_remove
  * gtk_notebook_update_labels
  * gtk_notebook_timer
+ * gtk_notebook_set_scroll_timer
  * gtk_notebook_page_compare
  * gtk_notebook_real_page_position
  * gtk_notebook_search_page
@@ -3734,30 +4007,30 @@ gtk_notebook_redraw_tabs (GtkNotebook *notebook)
   switch (tab_pos)
     {
     case GTK_POS_BOTTOM:
-      redraw_rect.y = (widget->allocation.height - border -
-                      page->allocation.height -
-                      widget->style->ythickness);
+      redraw_rect.y = widget->allocation.height - border -
+       page->allocation.height - widget->style->ythickness;
+
       if (page != notebook->cur_page)
        redraw_rect.y -= widget->style->ythickness;
       /* fall through */
     case GTK_POS_TOP:
       redraw_rect.width = widget->allocation.width - 2 * border;
-      redraw_rect.height = (page->allocation.height +
-                           widget->style->ythickness);
+      redraw_rect.height = page->allocation.height + widget->style->ythickness;
+
       if (page != notebook->cur_page)
        redraw_rect.height += widget->style->ythickness;
       break;
     case GTK_POS_RIGHT:
-      redraw_rect.x = (widget->allocation.width - border -
-                      page->allocation.width -
-                      widget->style->xthickness);
+      redraw_rect.x = widget->allocation.width - border -
+       page->allocation.width - widget->style->xthickness;
+
       if (page != notebook->cur_page)
        redraw_rect.x -= widget->style->xthickness;
       /* fall through */
     case GTK_POS_LEFT:
-      redraw_rect.width = (page->allocation.width +
-                          widget->style->xthickness);
+      redraw_rect.width = page->allocation.width + widget->style->xthickness;
       redraw_rect.height = widget->allocation.height - 2 * border;
+
       if (page != notebook->cur_page)
        redraw_rect.width += widget->style->xthickness;
       break;
@@ -3795,13 +4068,11 @@ gtk_notebook_redraw_arrows (GtkNotebook *notebook)
     }
 }
 
-static gint
+static gboolean
 gtk_notebook_timer (GtkNotebook *notebook)
 {
   gboolean retval = FALSE;
 
-  GDK_THREADS_ENTER ();
-
   if (notebook->timer)
     {
       gtk_notebook_do_arrow (notebook, notebook->click_child);
@@ -3815,7 +4086,7 @@ gtk_notebook_timer (GtkNotebook *notebook)
           g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
 
          notebook->need_timer = FALSE;
-         notebook->timer = g_timeout_add (timeout * SCROLL_DELAY_FACTOR,
+         notebook->timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
                                           (GSourceFunc) gtk_notebook_timer,
                                           (gpointer) notebook);
        }
@@ -3823,11 +4094,28 @@ gtk_notebook_timer (GtkNotebook *notebook)
        retval = TRUE;
     }
 
-  GDK_THREADS_LEAVE ();
-
   return retval;
 }
 
+static void
+gtk_notebook_set_scroll_timer (GtkNotebook *notebook)
+{
+  GtkWidget *widget = GTK_WIDGET (notebook);
+
+  if (!notebook->timer)
+    {
+      GtkSettings *settings = gtk_widget_get_settings (widget);
+      guint timeout;
+
+      g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
+
+      notebook->timer = gdk_threads_add_timeout (timeout,
+                                      (GSourceFunc) gtk_notebook_timer,
+                                      (gpointer) notebook);
+      notebook->need_timer = TRUE;
+    }
+}
+
 static gint
 gtk_notebook_page_compare (gconstpointer a,
                           gconstpointer b)
@@ -3873,12 +4161,14 @@ static void
 gtk_notebook_real_remove (GtkNotebook *notebook,
                          GList       *list)
 {
+  GtkNotebookPrivate *priv;
   GtkNotebookPage *page;
   GList * next_list;
   gint need_resize = FALSE;
 
   gboolean destroying;
 
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
   destroying = GTK_OBJECT_FLAGS (notebook) & GTK_IN_DESTRUCTION;
   
   next_list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
@@ -3892,6 +4182,9 @@ gtk_notebook_real_remove (GtkNotebook *notebook,
        gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (next_list), -1);
     }
 
+  if (priv->detached_tab == list->data)
+    priv->detached_tab = NULL;
+
   if (list == notebook->first_tab)
     notebook->first_tab = next_list;
   if (list == notebook->focus_tab && !destroying)
@@ -4043,7 +4336,9 @@ gtk_notebook_search_page (GtkNotebook *notebook,
        {
          page = list->data;
          if (page->pack == flag &&
-             (!find_visible || GTK_WIDGET_VISIBLE (page->child)))
+             (!find_visible ||
+              (GTK_WIDGET_VISIBLE (page->child) &&
+               (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
            return list;
          old_list = list;
          list = list->next;
@@ -4059,7 +4354,9 @@ gtk_notebook_search_page (GtkNotebook *notebook,
     {
       page = list->data;
       if (page->pack != flag &&
-         (!find_visible || GTK_WIDGET_VISIBLE (page->child)))
+         (!find_visible ||
+          (GTK_WIDGET_VISIBLE (page->child) &&
+           (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
        return list;
       old_list = list;
       list = list->prev;
@@ -4078,6 +4375,7 @@ gtk_notebook_paint (GtkWidget    *widget,
                    GdkRectangle *area)
 {
   GtkNotebook *notebook;
+  GtkNotebookPrivate *priv;
   GtkNotebookPage *page;
   GList *children;
   gboolean showarrow;
@@ -4095,6 +4393,7 @@ gtk_notebook_paint (GtkWidget    *widget,
     return;
 
   notebook = GTK_NOTEBOOK (widget);
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
   is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
   tab_pos = get_effective_tab_pos (notebook);
 
@@ -4116,66 +4415,68 @@ gtk_notebook_paint (GtkWidget    *widget,
       return;
     }
 
+  if (!notebook->first_tab)
+    notebook->first_tab = notebook->children;
+
+  if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, notebook->cur_page) ||
+      !GTK_WIDGET_MAPPED (notebook->cur_page->tab_label))
+    page = GTK_NOTEBOOK_PAGE (notebook->first_tab);
+  else
+    page = notebook->cur_page;
 
-  if (!GTK_WIDGET_MAPPED (notebook->cur_page->tab_label))
+  switch (tab_pos)
     {
-      page = notebook->first_tab->data;
+    case GTK_POS_TOP:
+      y += page->allocation.height;
+      /* fall thru */
+    case GTK_POS_BOTTOM:
+      height -= page->allocation.height;
+      break;
+    case GTK_POS_LEFT:
+      x += page->allocation.width;
+      /* fall thru */
+    case GTK_POS_RIGHT:
+      width -= page->allocation.width;
+      break;
+    }
 
-      switch (tab_pos)
-       {
-       case GTK_POS_TOP:
-         y += page->allocation.height + widget->style->ythickness;
-       case GTK_POS_BOTTOM:
-         height -= page->allocation.height + widget->style->ythickness;
-         break;
-       case GTK_POS_LEFT:
-         x += page->allocation.width + widget->style->xthickness;
-       case GTK_POS_RIGHT:
-         width -= page->allocation.width + widget->style->xthickness;
-         break;
-       }
-      gtk_paint_box (widget->style, widget->window,
-                    GTK_STATE_NORMAL, GTK_SHADOW_OUT,
-                    area, widget, "notebook",
-                    x, y, width, height);
+  if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, notebook->cur_page) ||
+      !GTK_WIDGET_MAPPED (notebook->cur_page->tab_label))
+    {
+      gap_x = 0;
+      gap_width = 0;
     }
   else
     {
       switch (tab_pos)
        {
        case GTK_POS_TOP:
-         y += notebook->cur_page->allocation.height;
        case GTK_POS_BOTTOM:
-         height -= notebook->cur_page->allocation.height;
-         break;
-       case GTK_POS_LEFT:
-         x += notebook->cur_page->allocation.width;
-       case GTK_POS_RIGHT:
-         width -= notebook->cur_page->allocation.width;
-         break;
-       }
+         if (priv->operation == DRAG_OPERATION_REORDER)
+           gap_x = priv->drag_window_x - widget->allocation.x - border_width;
+         else
+           gap_x = notebook->cur_page->allocation.x - widget->allocation.x - border_width;
 
-      switch (tab_pos)
-       {
-       case GTK_POS_TOP:
-       case GTK_POS_BOTTOM:
-         gap_x = (notebook->cur_page->allocation.x - widget->allocation.x - border_width);
          gap_width = notebook->cur_page->allocation.width;
          step = is_rtl ? STEP_NEXT : STEP_PREV;
          break;
        case GTK_POS_LEFT:
        case GTK_POS_RIGHT:
-         gap_x = (notebook->cur_page->allocation.y - widget->allocation.y - border_width);
+         if (priv->operation == DRAG_OPERATION_REORDER)
+           gap_x = priv->drag_window_y - border_width - widget->allocation.y;
+         else
+           gap_x = notebook->cur_page->allocation.y - widget->allocation.y - border_width;
+
          gap_width = notebook->cur_page->allocation.height;
          step = STEP_PREV;
          break;
        }
-      gtk_paint_box_gap (widget->style, widget->window,
-                        GTK_STATE_NORMAL, GTK_SHADOW_OUT,
-                        area, widget, "notebook",
-                        x, y, width, height,
-                        tab_pos, gap_x, gap_width);
     }
+  gtk_paint_box_gap (widget->style, widget->window,
+                    GTK_STATE_NORMAL, GTK_SHADOW_OUT,
+                    area, widget, "notebook",
+                    x, y, width, height,
+                    tab_pos, gap_x, gap_width);
 
   showarrow = FALSE;
   children = gtk_notebook_search_page (notebook, NULL, step, TRUE);
@@ -4211,20 +4512,31 @@ gtk_notebook_draw_tab (GtkNotebook     *notebook,
                       GtkNotebookPage *page,
                       GdkRectangle    *area)
 {
+  GtkNotebookPrivate *priv;
   GdkRectangle child_area;
   GdkRectangle page_area;
   GtkStateType state_type;
   GtkPositionType gap_side;
-  gint tab_pos = get_effective_tab_pos (notebook);
+  GdkWindow *window;
+  GtkWidget *widget;
   
   g_return_if_fail (notebook != NULL);
   g_return_if_fail (page != NULL);
   g_return_if_fail (area != NULL);
 
-  if (!GTK_WIDGET_MAPPED (page->tab_label) ||
+  if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) ||
+      !GTK_WIDGET_MAPPED (page->tab_label) ||
       (page->allocation.width == 0) || (page->allocation.height == 0))
     return;
 
+  widget = GTK_WIDGET (notebook);
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+
+  if (priv->operation == DRAG_OPERATION_REORDER && page == notebook->cur_page)
+    window = priv->drag_window;
+  else
+    window = widget->window;
+
   page_area.x = page->allocation.x;
   page_area.y = page->allocation.y;
   page_area.width = page->allocation.width;
@@ -4232,67 +4544,50 @@ gtk_notebook_draw_tab (GtkNotebook     *notebook,
 
   if (gdk_rectangle_intersect (&page_area, area, &child_area))
     {
-      GtkWidget *widget;
+      gap_side = get_tab_gap_pos (notebook);
 
-      widget = GTK_WIDGET (notebook);
-      gap_side = 0;
-      switch (tab_pos)
-       {
-       case GTK_POS_TOP:
-         gap_side = GTK_POS_BOTTOM;
-         break;
-       case GTK_POS_BOTTOM:
-         gap_side = GTK_POS_TOP;
-         break;
-       case GTK_POS_LEFT:
-         gap_side = GTK_POS_RIGHT;
-         break;
-       case GTK_POS_RIGHT:
-         gap_side = GTK_POS_LEFT;
-         break;
-       }
-      
       if (notebook->cur_page == page)
        state_type = GTK_STATE_NORMAL;
       else 
        state_type = GTK_STATE_ACTIVE;
-      gtk_paint_extension(widget->style, widget->window,
-                         state_type, GTK_SHADOW_OUT,
-                         area, widget, "tab",
-                         page_area.x, page_area.y,
-                         page_area.width, page_area.height,
-                         gap_side);
+
+      gtk_paint_extension (widget->style, window,
+                          state_type, GTK_SHADOW_OUT,
+                          area, widget, "tab",
+                          page_area.x, page_area.y,
+                          page_area.width, page_area.height,
+                          gap_side);
+
       if ((GTK_WIDGET_HAS_FOCUS (widget)) &&
          notebook->focus_tab && (notebook->focus_tab->data == page))
        {
           gint focus_width;
-         
+
          gtk_widget_style_get (widget, "focus-line-width", &focus_width, NULL);
-         
-         gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
+
+         gtk_paint_focus (widget->style, window, GTK_WIDGET_STATE (widget),
                           area, widget, "tab",
                           page->tab_label->allocation.x - focus_width,
                           page->tab_label->allocation.y - focus_width,
                           page->tab_label->allocation.width + 2 * focus_width,
                           page->tab_label->allocation.height + 2 * focus_width);
        }
+
       if (gtk_widget_intersect (page->tab_label, area, &child_area) &&
           GTK_WIDGET_DRAWABLE (page->tab_label))
         {
           GdkEvent *expose_event = gdk_event_new (GDK_EXPOSE);
 
           /* This is a lame hack since all this code needs rewriting anyhow */
-          
           expose_event->expose.window = g_object_ref (page->tab_label->window);
           expose_event->expose.area = child_area;
           expose_event->expose.region = gdk_region_rectangle (&child_area);
           expose_event->expose.send_event = TRUE;
           expose_event->expose.count = 0;
 
-         gtk_container_propagate_expose (GTK_CONTAINER (notebook), page->tab_label, (GdkEventExpose *)expose_event);
-
+         gtk_widget_send_expose (page->tab_label, expose_event);
          gdk_event_free (expose_event);
-        }
+       }
     }
 }
 
@@ -4317,6 +4612,15 @@ gtk_notebook_draw_arrow (GtkNotebook      *notebook,
 
   if (GTK_WIDGET_DRAWABLE (notebook))
     {
+      gint scroll_arrow_hlength;
+      gint scroll_arrow_vlength;
+      gint arrow_size;
+
+      gtk_widget_style_get (widget,
+                            "scroll-arrow-hlength", &scroll_arrow_hlength,
+                            "scroll-arrow-vlength", &scroll_arrow_vlength,
+                            NULL);
+
       if (notebook->in_child == nbarrow)
         {
           if (notebook->click_child == nbarrow)
@@ -4342,782 +4646,778 @@ gtk_notebook_draw_arrow (GtkNotebook      *notebook,
       
       if (notebook->tab_pos == GTK_POS_LEFT ||
          notebook->tab_pos == GTK_POS_RIGHT)
-       arrow = (ARROW_IS_LEFT (nbarrow) ? GTK_ARROW_UP : GTK_ARROW_DOWN);
+        {
+          arrow = (ARROW_IS_LEFT (nbarrow) ? GTK_ARROW_UP : GTK_ARROW_DOWN);
+          arrow_size = scroll_arrow_vlength;
+        }
       else
-       arrow = (ARROW_IS_LEFT (nbarrow) ? GTK_ARROW_LEFT : GTK_ARROW_RIGHT);
-      
+        {
+          arrow = (ARROW_IS_LEFT (nbarrow) ? GTK_ARROW_LEFT : GTK_ARROW_RIGHT);
+          arrow_size = scroll_arrow_hlength;
+        }
+     
       gtk_paint_arrow (widget->style, widget->window, state_type, 
                       shadow_type, NULL, widget, "notebook",
                       arrow, TRUE, arrow_rect.x, arrow_rect.y, 
-                      ARROW_SIZE, ARROW_SIZE);
-    }
-}
-
-static void
-get_notebook_tabs_space (GtkNotebook *notebook, gint *min, gint *max)
-{
-  GtkWidget *widget = (GtkWidget *) notebook;
-  GtkContainer *container = (GtkContainer *) notebook;
-
-  if (notebook->tab_pos == GTK_POS_TOP ||
-      notebook->tab_pos == GTK_POS_BOTTOM)
-    {
-      *min = widget->allocation.x + container->border_width;
-      *max = widget->allocation.x + widget->allocation.width - container->border_width;
-
-      if (gtk_notebook_show_arrows (notebook))
-       {
-         if (notebook->has_after_previous)
-           *max -= ARROW_SPACING + ARROW_SIZE;
-         if (notebook->has_after_next)
-           *max -= ARROW_SPACING + ARROW_SIZE;
-         if (notebook->has_before_previous)
-           *min += ARROW_SPACING + ARROW_SIZE;
-         if (notebook->has_before_next)
-           *min += ARROW_SPACING + ARROW_SIZE;
-       }
-    }
-  else
-    {
-      *min = widget->allocation.y + container->border_width;
-      *max = widget->allocation.y + widget->allocation.height - container->border_width;
-
-      if (gtk_notebook_show_arrows (notebook))
-       {
-         if (notebook->has_after_previous || notebook->has_after_next)
-           *max -= ARROW_SPACING + ARROW_SIZE;
-         if (notebook->has_before_previous || notebook->has_before_next)
-           *min += ARROW_SPACING + ARROW_SIZE;
-       }
+                      arrow_size, arrow_size);
     }
 }
 
 /* Private GtkNotebook Size Allocate Functions:
  *
+ * gtk_notebook_tab_space
+ * gtk_notebook_calculate_shown_tabs
+ * gtk_notebook_calculate_tabs_allocation
  * gtk_notebook_pages_allocate
  * gtk_notebook_page_allocate
  * gtk_notebook_calc_tabs
  */
 static void
-gtk_notebook_pages_allocate (GtkNotebook              *notebook,
-                            GtkNotebookDragOperation  operation)
+gtk_notebook_tab_space (GtkNotebook *notebook,
+                       gboolean    *show_arrows,
+                       gint        *min,
+                       gint        *max,
+                       gint        *tab_space)
 {
-  GtkWidget    *widget = GTK_WIDGET (notebook);
-  GtkContainer *container = GTK_CONTAINER (notebook);
-  GtkNotebookPrivate *priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
-  GtkNotebookPage *page = NULL;
-  GtkAllocation *allocation = &widget->allocation;
-  GtkAllocation child_allocation;
-  GList *children = NULL;
-  GList *last_child = NULL;
-  gboolean showarrow = FALSE;
-  gint tab_space = 0; 
-  gint delta; 
-  gint i;
-  gint n = 1;
-  gint old_fill = 0;
-  gint new_fill = 0;
+  GtkNotebookPrivate *priv;
+  GtkWidget *widget;
+  GList *children;
   gint tab_pos = get_effective_tab_pos (notebook);
-  gboolean is_rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL &&
-                    (tab_pos == GTK_POS_TOP || tab_pos == GTK_POS_BOTTOM));
   gint tab_overlap;
-  gint memo_x, anchor_x, anchor_y;
-  gint min, max, top_y, bottom_y, left_x, right_x;
-  gboolean packing_changed = FALSE;
-  gboolean gap_left = FALSE;
+  gint arrow_spacing;
+  gint scroll_arrow_hlength;
+  gint scroll_arrow_vlength;
 
-  if (!notebook->show_tabs || !notebook->children || !notebook->cur_page)
-    return;
-
-  gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
+  widget = GTK_WIDGET (notebook);
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+  children = notebook->children;
 
-  child_allocation.x = widget->allocation.x + container->border_width;
-  child_allocation.y = widget->allocation.y + container->border_width;
+  gtk_widget_style_get (GTK_WIDGET (notebook),
+                        "arrow-spacing", &arrow_spacing,
+                        "scroll-arrow-hlength", &scroll_arrow_hlength,
+                        "scroll-arrow-vlength", &scroll_arrow_vlength,
+                        NULL);
 
   switch (tab_pos)
     {
-    case GTK_POS_BOTTOM:
-      child_allocation.y = (widget->allocation.y +
-                           allocation->height -
-                           notebook->cur_page->requisition.height -
-                           container->border_width);
-      /* fall through */
     case GTK_POS_TOP:
-      child_allocation.height = notebook->cur_page->requisition.height;
+    case GTK_POS_BOTTOM:
+      *min = widget->allocation.x + GTK_CONTAINER (notebook)->border_width;
+      *max = widget->allocation.x + widget->allocation.width - GTK_CONTAINER (notebook)->border_width;
+
+      while (children)
+       {
+          GtkNotebookPage *page;
+
+         page = children->data;
+         children = children->next;
+
+         if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
+             GTK_WIDGET_VISIBLE (page->child))
+           *tab_space += page->requisition.width;
+       }
       break;
-      
     case GTK_POS_RIGHT:
-      child_allocation.x = (widget->allocation.x +
-                           allocation->width -
-                           notebook->cur_page->requisition.width -
-                           container->border_width);
-      /* fall through */
     case GTK_POS_LEFT:
-      child_allocation.width = notebook->cur_page->requisition.width;
+      *min = widget->allocation.y + GTK_CONTAINER (notebook)->border_width;
+      *max = widget->allocation.y + widget->allocation.height - GTK_CONTAINER (notebook)->border_width;
+
+      while (children)
+       {
+          GtkNotebookPage *page;
+
+         page = children->data;
+         children = children->next;
+
+         if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
+             GTK_WIDGET_VISIBLE (page->child))
+           *tab_space += page->requisition.height;
+       }
       break;
     }
 
-  if (notebook->scrollable) 
+  if (!notebook->scrollable)
+    *show_arrows = FALSE;
+  else
     {
-      GList *focus_tab;
-      
-      children = notebook->children;
-
-      if (notebook->focus_tab)
-       focus_tab = notebook->focus_tab;
-      else if (notebook->first_tab)
-       focus_tab = notebook->first_tab;
-      else
-       focus_tab = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, TRUE);
+      gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
 
       switch (tab_pos)
        {
        case GTK_POS_TOP:
        case GTK_POS_BOTTOM:
-         while (children)
+         if (*tab_space > *max - *min - tab_overlap)
            {
-             page = children->data;
-             children = children->next;
-
-             if (GTK_WIDGET_VISIBLE (page->child))
-               tab_space += page->requisition.width;
-           }
+             *show_arrows = TRUE;
 
-         if (tab_space >
-             allocation->width - 2 * container->border_width - tab_overlap) 
-           {
-             showarrow = TRUE;
-             page = focus_tab->data; 
+             /* take arrows into account */
+             *tab_space = widget->allocation.width - tab_overlap -
+               2 * GTK_CONTAINER (notebook)->border_width;
 
-             tab_space = allocation->width - tab_overlap -
-               page->requisition.width - 2 * container->border_width;
              if (notebook->has_after_previous)
-               tab_space -= ARROW_SPACING + ARROW_SIZE;
+               {
+                 *tab_space -= arrow_spacing + scroll_arrow_hlength;
+                 *max -= arrow_spacing + scroll_arrow_hlength;
+               }
+
              if (notebook->has_after_next)
-               tab_space -= ARROW_SPACING + ARROW_SIZE;
+               {
+                 *tab_space -= arrow_spacing + scroll_arrow_hlength;
+                 *max -= arrow_spacing + scroll_arrow_hlength;
+               }
+
              if (notebook->has_before_previous)
                {
-                 tab_space -= ARROW_SPACING + ARROW_SIZE;
-                 child_allocation.x += ARROW_SPACING + ARROW_SIZE;
+                 *tab_space -= arrow_spacing + scroll_arrow_hlength;
+                 *min += arrow_spacing + scroll_arrow_hlength;
                }
+
              if (notebook->has_before_next)
                {
-                 tab_space -= ARROW_SPACING + ARROW_SIZE;
-                 child_allocation.x += ARROW_SPACING + ARROW_SIZE;
+                 *tab_space -= arrow_spacing + scroll_arrow_hlength;
+                 *min += arrow_spacing + scroll_arrow_hlength;
                }
            }
          break;
        case GTK_POS_LEFT:
        case GTK_POS_RIGHT:
-         while (children)
+         if (*tab_space > *max - *min - tab_overlap)
            {
-             page = children->data;
-             children = children->next;
+             *show_arrows = TRUE;
+
+             /* take arrows into account */
+             *tab_space = widget->allocation.height -
+               tab_overlap - 2 * GTK_CONTAINER (notebook)->border_width;
 
-             if (GTK_WIDGET_VISIBLE (page->child))
-               tab_space += page->requisition.height;
-           }
-         if (tab_space >
-             (allocation->height - 2 * container->border_width - tab_overlap))
-           {
-             showarrow = TRUE;
-             page = focus_tab->data; 
-             tab_space = allocation->height
-               - tab_overlap - 2 * container->border_width
-               - page->requisition.height;
              if (notebook->has_after_previous || notebook->has_after_next)
-               tab_space -= ARROW_SPACING + ARROW_SIZE;
+               {
+                 *tab_space -= arrow_spacing + scroll_arrow_vlength;
+                 *max -= arrow_spacing + scroll_arrow_vlength;
+               }
+
              if (notebook->has_before_previous || notebook->has_before_next)
                {
-                 tab_space -= ARROW_SPACING + ARROW_SIZE;
-                 child_allocation.y += ARROW_SPACING + ARROW_SIZE;
+                 *tab_space -= arrow_spacing + scroll_arrow_vlength;
+                 *min += arrow_spacing + scroll_arrow_vlength;
                }
            }
          break;
        }
-      if (showarrow) /* first_tab <- focus_tab */
+    }
+}
+
+static void
+gtk_notebook_calculate_shown_tabs (GtkNotebook *notebook,
+                                  gboolean     show_arrows,
+                                  gint         min,
+                                  gint         max,
+                                  gint         tab_space,
+                                  GList      **last_child,
+                                  gint        *n,
+                                  gint        *remaining_space)
+{
+  GtkWidget *widget;
+  GtkContainer *container;
+  GList *children;
+  GtkNotebookPage *page;
+  gint tab_pos, tab_overlap;
+  
+  widget = GTK_WIDGET (notebook);
+  container = GTK_CONTAINER (notebook);
+  gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
+  tab_pos = get_effective_tab_pos (notebook);
+
+  if (show_arrows) /* first_tab <- focus_tab */
+    {
+      *remaining_space = tab_space;
+
+      if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, notebook->cur_page) &&
+         GTK_WIDGET_VISIBLE (notebook->cur_page->child))
+       {
+         gtk_notebook_calc_tabs (notebook,
+                                 notebook->focus_tab,
+                                 &(notebook->focus_tab),
+                                 remaining_space, STEP_NEXT);
+       }
+
+      if (*remaining_space <= 0)
        {
-         if (tab_space <= 0)
+         /* show 1 tab */
+         notebook->first_tab = notebook->focus_tab;
+         *last_child = gtk_notebook_search_page (notebook, notebook->focus_tab,
+                                                 STEP_NEXT, TRUE);
+       }
+      else
+       {
+         children = NULL;
+
+         if (notebook->first_tab && notebook->first_tab != notebook->focus_tab)
+           {
+             /* Is first_tab really predecessor of focus_tab? */
+             page = notebook->first_tab->data;
+             if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
+                 GTK_WIDGET_VISIBLE (page->child))
+               for (children = notebook->focus_tab;
+                    children && children != notebook->first_tab;
+                    children = gtk_notebook_search_page (notebook,
+                                                         children,
+                                                         STEP_PREV,
+                                                         TRUE));
+           }
+
+         if (!children)
            {
-             notebook->first_tab = focus_tab;
-             last_child = gtk_notebook_search_page (notebook, focus_tab,
-                                                    STEP_NEXT, TRUE);
+             if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, notebook->cur_page))
+               notebook->first_tab = notebook->focus_tab;
+             else
+               notebook->first_tab = gtk_notebook_search_page (notebook, notebook->focus_tab,
+                                                               STEP_NEXT, TRUE);
            }
          else
+           /* calculate shown tabs counting backwards from the focus tab */
+           gtk_notebook_calc_tabs (notebook,
+                                   gtk_notebook_search_page (notebook,
+                                                             notebook->focus_tab,
+                                                             STEP_PREV,
+                                                             TRUE),
+                                   &(notebook->first_tab), remaining_space,
+                                   STEP_PREV);
+
+         if (*remaining_space < 0)
            {
+             notebook->first_tab =
+               gtk_notebook_search_page (notebook, notebook->first_tab,
+                                         STEP_NEXT, TRUE);
+             if (!notebook->first_tab)
+               notebook->first_tab = notebook->focus_tab;
+
+             *last_child = gtk_notebook_search_page (notebook, notebook->focus_tab,
+                                                     STEP_NEXT, TRUE); 
+           }
+         else /* focus_tab -> end */   
+           {
+             if (!notebook->first_tab)
+               notebook->first_tab = gtk_notebook_search_page (notebook,
+                                                               NULL,
+                                                               STEP_NEXT,
+                                                               TRUE);
              children = NULL;
-             if (notebook->first_tab && notebook->first_tab != focus_tab)
-               {
-                 /* Is first_tab really predecessor of focus_tab  ? */
-                 page = notebook->first_tab->data;
-                 if (GTK_WIDGET_VISIBLE (page->child))
-                   for (children = focus_tab;
-                        children && children != notebook->first_tab;
-                        children = gtk_notebook_search_page (notebook,
-                                                             children,
-                                                             STEP_PREV,
-                                                             TRUE));
-               }
-             if (!children)
-               notebook->first_tab = focus_tab;
-             else
-               gtk_notebook_calc_tabs (notebook,
-                                       gtk_notebook_search_page (notebook,
-                                                                 focus_tab,
-                                                                 STEP_PREV,
-                                                                 TRUE), 
-                                       &(notebook->first_tab), &tab_space,
-                                       STEP_PREV);
-
-             if (tab_space <= 0)
-               {
-                 notebook->first_tab =
-                   gtk_notebook_search_page (notebook, notebook->first_tab,
-                                             STEP_NEXT, TRUE);
-                 if (!notebook->first_tab)
-                   notebook->first_tab = focus_tab;
-                 last_child = gtk_notebook_search_page (notebook, focus_tab,
-                                                        STEP_NEXT, TRUE); 
-               }
-             else /* focus_tab -> end */   
+             gtk_notebook_calc_tabs (notebook,
+                                     gtk_notebook_search_page (notebook,
+                                                               notebook->focus_tab,
+                                                               STEP_NEXT,
+                                                               TRUE),
+                                     &children, remaining_space, STEP_NEXT);
+
+             if (*remaining_space <= 0) 
+               *last_child = children;
+             else /* start <- first_tab */
                {
-                 if (!notebook->first_tab)
-                   notebook->first_tab = gtk_notebook_search_page (notebook,
-                                                                   NULL,
-                                                                   STEP_NEXT,
-                                                                   TRUE);
+                 *last_child = NULL;
                  children = NULL;
+
                  gtk_notebook_calc_tabs (notebook,
                                          gtk_notebook_search_page (notebook,
-                                                                   focus_tab,
-                                                                   STEP_NEXT,
+                                                                   notebook->first_tab,
+                                                                   STEP_PREV,
                                                                    TRUE),
-                                         &children, &tab_space, STEP_NEXT);
-
-                 if (tab_space <= 0) 
-                   last_child = children;
-                 else /* start <- first_tab */
-                   {
-                     last_child = NULL;
-                     children = NULL;
-                     gtk_notebook_calc_tabs
-                       (notebook,
-                        gtk_notebook_search_page (notebook,
-                                                  notebook->first_tab,
-                                                  STEP_PREV,
-                                                  TRUE),
-                        &children, &tab_space, STEP_PREV);
-                     notebook->first_tab = gtk_notebook_search_page(notebook,
-                                                                    children,
-                                                                    STEP_NEXT,
-                                                                    TRUE);
-                   }
+                                         &children, remaining_space, STEP_PREV);
+
+                 if (*remaining_space == 0)
+                   notebook->first_tab = children;
+                 else
+                   notebook->first_tab = gtk_notebook_search_page(notebook,
+                                                                  children,
+                                                                  STEP_NEXT,
+                                                                  TRUE);
                }
            }
+       }
 
-         if (tab_space < 0) 
-           {
-             tab_space = -tab_space;
-             n = 0;
-             for (children = notebook->first_tab;
-                  children && children != last_child;
-                  children = gtk_notebook_search_page (notebook, children,
-                                                       STEP_NEXT, TRUE))
-               n++;
-           }
-         else 
-           tab_space = 0;
+      if (*remaining_space < 0) 
+       {
+         /* calculate number of tabs */
+         *remaining_space = - (*remaining_space);
+         *n = 0;
 
-         /*unmap all non-visible tabs*/
-         for (children = gtk_notebook_search_page (notebook, NULL,
-                                                   STEP_NEXT, TRUE);
-              children && children != notebook->first_tab;
+         for (children = notebook->first_tab;
+              children && children != *last_child;
               children = gtk_notebook_search_page (notebook, children,
                                                    STEP_NEXT, TRUE))
-           {
-             page = children->data;
-             if (page->tab_label)
-               gtk_widget_set_child_visible (page->tab_label, FALSE);
-           }
-         for (children = last_child; children;
-              children = gtk_notebook_search_page (notebook, children,
-                                                   STEP_NEXT, TRUE))
-           {
-             page = children->data;
-             if (page->tab_label)
-               gtk_widget_set_child_visible (page->tab_label, FALSE);
-           }
+           (*n)++;
        }
-      else /* !showarrow */
+      else 
+       *remaining_space = 0;
+
+      /* unmap all non-visible tabs */
+      for (children = gtk_notebook_search_page (notebook, NULL,
+                                               STEP_NEXT, TRUE);
+          children && children != notebook->first_tab;
+          children = gtk_notebook_search_page (notebook, children,
+                                               STEP_NEXT, TRUE))
        {
-         notebook->first_tab = gtk_notebook_search_page (notebook, NULL,
-                                                         STEP_NEXT, TRUE);
-         tab_space = 0;
+         page = children->data;
+
+         if (page->tab_label &&
+             NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
+           gtk_widget_set_child_visible (page->tab_label, FALSE);
+       }
+
+      for (children = *last_child; children;
+          children = gtk_notebook_search_page (notebook, children,
+                                               STEP_NEXT, TRUE))
+       {
+         page = children->data;
+
+         if (page->tab_label &&
+             NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
+           gtk_widget_set_child_visible (page->tab_label, FALSE);
+       }
+    }
+  else /* !show_arrows */
+    {
+      gint c = 0;
+      *n = 0;
+
+      *remaining_space = max - min - tab_overlap - tab_space;
+      children = notebook->children;
+      notebook->first_tab = gtk_notebook_search_page (notebook, NULL,
+                                                     STEP_NEXT, TRUE);
+      while (children)
+       {
+         page = children->data;
+         children = children->next;
+
+         if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) ||
+             !GTK_WIDGET_VISIBLE (page->child))
+           continue;
+
+         c++;
+
+         if (page->expand)
+           (*n)++;
        }
+
+      /* if notebook is homogeneous, all tabs are expanded */
+      if (notebook->homogeneous && *n)
+       *n = c;
+    }
+}
+
+static gboolean
+get_allocate_at_bottom (GtkWidget *widget,
+                       gint       search_direction)
+{
+  gboolean is_rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
+  gboolean tab_pos = get_effective_tab_pos (GTK_NOTEBOOK (widget));
+
+  switch (tab_pos)
+    {
+    case GTK_POS_TOP:
+    case GTK_POS_BOTTOM:
+      if (!is_rtl)
+       return (search_direction == STEP_PREV);
+      else
+       return (search_direction == STEP_NEXT);
+
+      break;
+    case GTK_POS_RIGHT:
+    case GTK_POS_LEFT:
+      return (search_direction == STEP_PREV);
+      break;
     }
 
-  if (!showarrow)
-    {
-      gint c = 0;
+  return FALSE;
+}
 
-      n = 0;
-      children = notebook->children;
-      switch (tab_pos)
-       {
-       case GTK_POS_TOP:
-       case GTK_POS_BOTTOM:
-         while (children)
-           {
-             page = children->data;
-             children = children->next;
+static gboolean
+gtk_notebook_calculate_tabs_allocation (GtkNotebook  *notebook,
+                                       GList       **children,
+                                       GList        *last_child,
+                                       gboolean      showarrow,
+                                       gint          direction,
+                                       gint         *remaining_space,
+                                       gint         *expanded_tabs,
+                                       gint          min,
+                                       gint          max)
+{
+  GtkWidget *widget;
+  GtkContainer *container;
+  GtkNotebookPrivate *priv;
+  GtkNotebookPage *page;
+  gboolean allocate_at_bottom;
+  gint tab_overlap, tab_pos, tab_extra_space;
+  gint left_x, right_x, top_y, bottom_y, anchor;
+  gint xthickness, ythickness;
+  gboolean gap_left, packing_changed;
+  GtkAllocation child_allocation = { 0, };
+  gboolean allocation_changed = FALSE;
 
-             if (GTK_WIDGET_VISIBLE (page->child))
-               {
-                 c++;
-                 tab_space += page->requisition.width;
-                 if (page->expand)
-                   n++;
-               }
-           }
-         tab_space -= allocation->width;
-         break;
-       case GTK_POS_LEFT:
-       case GTK_POS_RIGHT:
-         while (children)
-           {
-             page = children->data;
-             children = children->next;
+  widget = GTK_WIDGET (notebook);
+  container = GTK_CONTAINER (notebook);
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+  gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
+  tab_pos = get_effective_tab_pos (notebook);
+  allocate_at_bottom = get_allocate_at_bottom (widget, direction);
+  anchor = 0;
 
-             if (GTK_WIDGET_VISIBLE (page->child))
-               {
-                 c++;
-                 tab_space += page->requisition.height;
-                 if (page->expand)
-                   n++;
-               }
-           }
-         tab_space -= allocation->height;
-       }
-      tab_space += 2 * container->border_width + tab_overlap;
-      tab_space *= -1;
-      notebook->first_tab = gtk_notebook_search_page (notebook, NULL,
-                                                     STEP_NEXT, TRUE);
-      if (notebook->homogeneous && n)
-       n = c;
-    }
-  
-  children = notebook->first_tab;
-  i = 1; 
-
-  memo_x = child_allocation.x;
-  if (notebook->children && is_rtl)
-     {
-      child_allocation.x = (allocation->x + allocation->width -
-                           container->border_width); 
-      if (showarrow) 
-       {
-         if (notebook->has_after_previous)
-           child_allocation.x -= ARROW_SPACING + ARROW_SIZE;
-         if (notebook->has_after_next)
-           child_allocation.x -= ARROW_SPACING + ARROW_SIZE;
-       }
-     }
+  child_allocation.x = widget->allocation.x + container->border_width;
+  child_allocation.y = widget->allocation.y + container->border_width;
+
+  xthickness = widget->style->xthickness;
+  ythickness = widget->style->ythickness;
 
-  get_notebook_tabs_space (notebook, &min, &max);
-  anchor_x = child_allocation.x;
-  anchor_y = child_allocation.y;
+  switch (tab_pos)
+    {
+    case GTK_POS_BOTTOM:
+      child_allocation.y = widget->allocation.y + widget->allocation.height -
+       notebook->cur_page->requisition.height - container->border_width;
+      /* fall through */
+    case GTK_POS_TOP:
+      child_allocation.x = (allocate_at_bottom) ? max : min;
+      child_allocation.height = notebook->cur_page->requisition.height;
+      anchor = child_allocation.x;
+      break;
+      
+    case GTK_POS_RIGHT:
+      child_allocation.x = widget->allocation.x + widget->allocation.width -
+       notebook->cur_page->requisition.width - container->border_width;
+      /* fall through */
+    case GTK_POS_LEFT:
+      child_allocation.y = (allocate_at_bottom) ? max : min;
+      child_allocation.width = notebook->cur_page->requisition.width;
+      anchor = child_allocation.y;
+      break;
+    }
 
-  left_x   = CLAMP (widget->allocation.x + priv->mouse_x - notebook->cur_page->allocation.width / 2,
+  left_x   = CLAMP (priv->mouse_x - priv->drag_offset_x,
                    min, max - notebook->cur_page->allocation.width);
-  top_y    = CLAMP (widget->allocation.y + priv->mouse_y - notebook->cur_page->allocation.height / 2,
+  top_y    = CLAMP (priv->mouse_y - priv->drag_offset_y,
                    min, max - notebook->cur_page->allocation.height);
   right_x  = left_x + notebook->cur_page->allocation.width;
   bottom_y = top_y + notebook->cur_page->allocation.height;
+  gap_left = packing_changed = FALSE;
 
-  while (children)
+  while (*children && *children != last_child)
     {
-      if (children == last_child)
-       {
-         /* FIXME double check */
-         goto done;
-       }
-
-      page = children->data;
+      page = (*children)->data;
 
-      if (page->pack != GTK_PACK_START)
+      if (direction == STEP_NEXT && page->pack != GTK_PACK_START)
        {
          if (!showarrow)
            break;
-         else if (operation == DRAG_OPERATION_REORDER)
+         else if (priv->operation == DRAG_OPERATION_REORDER)
            packing_changed = TRUE;
        }
 
-      children = gtk_notebook_search_page (notebook, children, STEP_NEXT,TRUE);
+      if (direction == STEP_NEXT)
+       *children = gtk_notebook_search_page (notebook, *children, direction, TRUE);
+      else
+       {
+         *children = (*children)->next;
+
+          if (page->pack != GTK_PACK_END || !GTK_WIDGET_VISIBLE (page->child))
+           continue;
+       }
+
+      if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
+       continue;
 
-      delta = 0;
-      if (n && (showarrow || page->expand || notebook->homogeneous))
+      tab_extra_space = 0;
+      if (*expanded_tabs && (showarrow || page->expand || notebook->homogeneous))
        {
-         new_fill = (tab_space * i++) / n;
-         delta = new_fill - old_fill;
-         old_fill = new_fill;
+         tab_extra_space = *remaining_space / *expanded_tabs;
+         *remaining_space -= tab_extra_space;
+         (*expanded_tabs)--;
        }
 
       switch (tab_pos)
        {
        case GTK_POS_TOP:
        case GTK_POS_BOTTOM:
-         child_allocation.width = (page->requisition.width +
-                                   tab_overlap + delta);
+         child_allocation.width = page->requisition.width + tab_overlap + tab_extra_space;
 
          /* make sure that the reordered tab doesn't go past the last position */
-         if (operation == DRAG_OPERATION_REORDER &&
+         if (priv->operation == DRAG_OPERATION_REORDER &&
              !gap_left && packing_changed)
            {
-             if (!is_rtl)
+             if (!allocate_at_bottom)
                {
-                 if ((notebook->cur_page->pack == GTK_PACK_START && left_x  >= anchor_x) ||
-                     (notebook->cur_page->pack == GTK_PACK_END && left_x < anchor_x))
+                 if ((notebook->cur_page->pack == GTK_PACK_START && left_x >= anchor) ||
+                     (notebook->cur_page->pack == GTK_PACK_END && left_x < anchor))
                    {
-                     left_x = notebook->cur_page->allocation.x = anchor_x;
-                     anchor_x += notebook->cur_page->allocation.width - tab_overlap;
+                     left_x = priv->drag_window_x = anchor;
+                     anchor += notebook->cur_page->allocation.width - tab_overlap;
                    }
                }
              else
                {
-                 if ((notebook->cur_page->pack == GTK_PACK_START && right_x <= anchor_x) ||
-                     (notebook->cur_page->pack == GTK_PACK_END && right_x > anchor_x))
+                 if ((notebook->cur_page->pack == GTK_PACK_START && right_x <= anchor) ||
+                     (notebook->cur_page->pack == GTK_PACK_END && right_x > anchor))
                    {
-                     anchor_x -= notebook->cur_page->allocation.width;
-                     left_x = notebook->cur_page->allocation.x = anchor_x;
-                     anchor_x += tab_overlap;
+                     anchor -= notebook->cur_page->allocation.width;
+                     left_x = priv->drag_window_x = anchor;
+                     anchor += tab_overlap;
                    }
                }
 
              gap_left = TRUE;
            }
 
-         if (operation == DRAG_OPERATION_REORDER && page == notebook->cur_page)
-           child_allocation.x = left_x;
+         if (priv->operation == DRAG_OPERATION_REORDER && page == notebook->cur_page)
+           {
+             priv->drag_window_x = left_x;
+             priv->drag_window_y = child_allocation.y;
+           }
          else
            {
-             if (is_rtl)
-               anchor_x -= child_allocation.width;
+             if (allocate_at_bottom)
+               anchor -= child_allocation.width;
  
-             if (operation == DRAG_OPERATION_REORDER && page->pack == notebook->cur_page->pack)
+             if (priv->operation == DRAG_OPERATION_REORDER && page->pack == notebook->cur_page->pack)
                {
-                 if (!is_rtl &&
-                     left_x >= anchor_x &&
-                     left_x <= anchor_x + child_allocation.width / 2)
-                   anchor_x += notebook->cur_page->allocation.width - tab_overlap;
-                 else if (is_rtl &&
-                          right_x >  anchor_x + child_allocation.width / 2 &&
-                          right_x <= anchor_x + child_allocation.width)
-                   anchor_x -= notebook->cur_page->allocation.width - tab_overlap;
+                 if (!allocate_at_bottom &&
+                     left_x >= anchor &&
+                     left_x <= anchor + child_allocation.width / 2)
+                   anchor += notebook->cur_page->allocation.width - tab_overlap;
+                 else if (allocate_at_bottom &&
+                          right_x >= anchor + child_allocation.width / 2 &&
+                          right_x <= anchor + child_allocation.width)
+                   anchor -= notebook->cur_page->allocation.width - tab_overlap;
                }
-             child_allocation.x = anchor_x;
+
+             child_allocation.x = anchor;
            }
 
          break;
        case GTK_POS_LEFT:
        case GTK_POS_RIGHT:
-         child_allocation.height = (page->requisition.height +
-                                    tab_overlap + delta);
+         child_allocation.height = page->requisition.height + tab_overlap + tab_extra_space;
 
          /* make sure that the reordered tab doesn't go past the last position */
-         if (operation == DRAG_OPERATION_REORDER &&
+         if (priv->operation == DRAG_OPERATION_REORDER &&
              !gap_left && packing_changed)
            {
-             if ((notebook->cur_page->pack == GTK_PACK_START && top_y >= anchor_y) ||
-                 (notebook->cur_page->pack == GTK_PACK_END && top_y < anchor_y))
+             if (!allocate_at_bottom &&
+                 ((notebook->cur_page->pack == GTK_PACK_START && top_y >= anchor) ||
+                  (notebook->cur_page->pack == GTK_PACK_END && top_y < anchor)))
                {
-                 top_y = notebook->cur_page->allocation.y = anchor_y;
-                 anchor_y += notebook->cur_page->allocation.height - tab_overlap;
+                 top_y = priv->drag_window_y = anchor;
+                 anchor += notebook->cur_page->allocation.height - tab_overlap;
                }
  
              gap_left = TRUE;
            }
 
-         if (operation == DRAG_OPERATION_REORDER && page == notebook->cur_page)
-           child_allocation.y = top_y;
+         if (priv->operation == DRAG_OPERATION_REORDER && page == notebook->cur_page)
+           {
+             priv->drag_window_x = child_allocation.x;
+             priv->drag_window_y = top_y;
+           }
          else
            {
-             if (operation == DRAG_OPERATION_REORDER &&
-                 page->pack == notebook->cur_page->pack &&
-                 top_y >= anchor_y &&
-                 top_y <= anchor_y + child_allocation.height / 2)
-               anchor_y += notebook->cur_page->allocation.height - tab_overlap;
-             child_allocation.y = anchor_y;
+             if (allocate_at_bottom)
+               anchor -= child_allocation.height;
+
+             if (priv->operation == DRAG_OPERATION_REORDER && page->pack == notebook->cur_page->pack)
+               {
+                 if (!allocate_at_bottom &&
+                     top_y >= anchor &&
+                     top_y <= anchor + child_allocation.height / 2)
+                   anchor += notebook->cur_page->allocation.height - tab_overlap;
+                 else if (allocate_at_bottom &&
+                          bottom_y >= anchor + child_allocation.height / 2 &&
+                          bottom_y <= anchor + child_allocation.height)
+                   anchor -= notebook->cur_page->allocation.height - tab_overlap;
+               }
+
+             child_allocation.y = anchor;
            }
 
          break;
        }
 
+      if ((priv->operation != DRAG_OPERATION_REORDER || page != notebook->cur_page) &&
+         (page->allocation.x != child_allocation.x ||
+          page->allocation.y != child_allocation.y ||
+          page->allocation.width != child_allocation.width ||
+          page->allocation.height != child_allocation.height))
+       allocation_changed = TRUE;
+
       page->allocation = child_allocation;
 
+      if (page == notebook->cur_page)
+       {
+         if (priv->operation == DRAG_OPERATION_REORDER ||
+             priv->operation == DRAG_OPERATION_DETACH)
+           {
+             /* needs to be allocated at 0,0
+              * to be shown in the drag window */
+             page->allocation.x = 0;
+             page->allocation.y = 0;
+           }
+       }
+      else
+       {
+         switch (tab_pos)
+           {
+           case GTK_POS_TOP:
+             page->allocation.y += ythickness;
+             /* fall through */
+           case GTK_POS_BOTTOM:
+             page->allocation.height = MAX (1, page->allocation.height - ythickness);
+             break;
+           case GTK_POS_LEFT:
+             page->allocation.x += xthickness;
+             /* fall through */
+           case GTK_POS_RIGHT:
+             page->allocation.width = MAX (1, page->allocation.width - xthickness);
+             break;
+           }
+       }
+
+      /* calculate whether to leave a gap based on reorder operation or not */
       switch (tab_pos)
        {
        case GTK_POS_TOP:
        case GTK_POS_BOTTOM:
-         if (operation != DRAG_OPERATION_REORDER ||
-             (operation == DRAG_OPERATION_REORDER && page != notebook->cur_page))
+         if (priv->operation != DRAG_OPERATION_REORDER ||
+             (priv->operation == DRAG_OPERATION_REORDER && page != notebook->cur_page))
            {
-             if (operation == DRAG_OPERATION_REORDER)
+             if (priv->operation == DRAG_OPERATION_REORDER)
                {
                  if (page->pack == notebook->cur_page->pack &&
-                     !is_rtl &&
-                     left_x >  anchor_x + child_allocation.width / 2 &&
-                     left_x <= anchor_x + child_allocation.width)
-                   anchor_x += notebook->cur_page->allocation.width - tab_overlap;
+                     !allocate_at_bottom &&
+                     left_x >  anchor + child_allocation.width / 2 &&
+                     left_x <= anchor + child_allocation.width)
+                   anchor += notebook->cur_page->allocation.width - tab_overlap;
                  else if (page->pack == notebook->cur_page->pack &&
-                          is_rtl &&
-                          right_x >= anchor_x &&
-                          right_x <= anchor_x + child_allocation.width / 2)
-                   anchor_x -= notebook->cur_page->allocation.width - tab_overlap;
+                          allocate_at_bottom &&
+                          right_x >= anchor &&
+                          right_x <= anchor + child_allocation.width / 2)
+                   anchor -= notebook->cur_page->allocation.width - tab_overlap;
                }
  
-             if (!is_rtl)
-               anchor_x += child_allocation.width - tab_overlap;
+             if (!allocate_at_bottom)
+               anchor += child_allocation.width - tab_overlap;
              else
-               anchor_x += tab_overlap;
+               anchor += tab_overlap;
            }
 
          break;
        case GTK_POS_LEFT:
        case GTK_POS_RIGHT:
-         if (operation != DRAG_OPERATION_REORDER  ||
-             (operation == DRAG_OPERATION_REORDER && page != notebook->cur_page))
+         if (priv->operation != DRAG_OPERATION_REORDER  ||
+             (priv->operation == DRAG_OPERATION_REORDER && page != notebook->cur_page))
            {
-             if (operation == DRAG_OPERATION_REORDER &&
-                 page->pack == notebook->cur_page->pack &&
-                 page != notebook->cur_page &&
-                 top_y >  anchor_y + child_allocation.height / 2 &&
-                 top_y <= anchor_y + child_allocation.height)
-               anchor_y += notebook->cur_page->allocation.height - tab_overlap;
-             anchor_y += child_allocation.height - tab_overlap;
+             if (priv->operation == DRAG_OPERATION_REORDER)
+               {
+                 if (page->pack == notebook->cur_page->pack &&
+                     !allocate_at_bottom &&
+                     top_y >= anchor + child_allocation.height / 2 &&
+                     top_y <= anchor + child_allocation.height)
+                   anchor += notebook->cur_page->allocation.height - tab_overlap;
+                 else if (page->pack == notebook->cur_page->pack &&
+                          allocate_at_bottom &&
+                          bottom_y >= anchor &&
+                          bottom_y <= anchor + child_allocation.height / 2)
+                   anchor -= notebook->cur_page->allocation.height - tab_overlap;
+               }
+
+             if (!allocate_at_bottom)
+               anchor += child_allocation.height - tab_overlap;
+             else
+               anchor += tab_overlap;
            }
 
          break;
        }
 
+      /* set child visible */
       if (page->tab_label)
        gtk_widget_set_child_visible (page->tab_label, TRUE);
     }
 
-  /* don't move the tab past the last position */
-  if (operation == DRAG_OPERATION_REORDER &&
-      notebook->cur_page->pack == GTK_PACK_START)
+  /* Don't move the current tab past the last position during tabs reordering */
+  if (children &&
+      priv->operation == DRAG_OPERATION_REORDER &&
+      ((direction == STEP_NEXT && notebook->cur_page->pack == GTK_PACK_START) ||
+       ((direction == STEP_PREV || packing_changed) && notebook->cur_page->pack == GTK_PACK_END)))
     {
       switch (tab_pos)
        {
        case GTK_POS_TOP:
        case GTK_POS_BOTTOM:
-         if ((!is_rtl && PAGE_LEFT_X (notebook->cur_page) > anchor_x) ||
-             (is_rtl && PAGE_RIGHT_X (notebook->cur_page) < anchor_x))
-           {
-             if (is_rtl)
-               anchor_x -= notebook->cur_page->allocation.width;
+         if (allocate_at_bottom)
+           anchor -= notebook->cur_page->allocation.width;
 
-             notebook->cur_page->allocation.x = anchor_x;
-           }
+         if ((!allocate_at_bottom && priv->drag_window_x > anchor) ||
+             (allocate_at_bottom && priv->drag_window_x < anchor))
+           priv->drag_window_x = anchor;
          break;
        case GTK_POS_LEFT:
        case GTK_POS_RIGHT:
-         if (PAGE_TOP_Y (notebook->cur_page) > anchor_y)
-           notebook->cur_page->allocation.y = anchor_y;
+         if (allocate_at_bottom)
+           anchor -= notebook->cur_page->allocation.height;
 
+         if ((!allocate_at_bottom && priv->drag_window_y > anchor) ||
+             (allocate_at_bottom && priv->drag_window_y < anchor))
+           priv->drag_window_y = anchor;
          break;
        }
     }
 
-  if (children)
-    {
-      children = notebook->children;
-
-      switch (tab_pos)
-       {
-       case GTK_POS_TOP:
-       case GTK_POS_BOTTOM:
-          if (!is_rtl)
-            anchor_x = (allocation->x + allocation->width -
-                        container->border_width);
-          else
-             anchor_x = memo_x; 
-         break;
-       case GTK_POS_LEFT:
-       case GTK_POS_RIGHT:
-         anchor_y = (allocation->y + allocation->height -
-                     container->border_width);
-         break;
-       }
-
-      while (children != last_child)
-       {
-         page = children->data;
-         children = children->next;
-
-          if (page->pack != GTK_PACK_END || !GTK_WIDGET_VISIBLE (page->child))
-             continue;
-
-         delta = 0;
-         if (n && (page->expand || notebook->homogeneous))
-           {
-             new_fill = (tab_space * i++) / n;
-             delta = new_fill - old_fill;
-             old_fill = new_fill;
-           }
-
-         switch (tab_pos)
-           {
-           case GTK_POS_TOP:
-           case GTK_POS_BOTTOM:
-             child_allocation.width = (page->requisition.width +
-                                       tab_overlap + delta);
-
-             if (operation == DRAG_OPERATION_REORDER && page == notebook->cur_page)
-               child_allocation.x = left_x;
-             else
-               {
-                 if (!is_rtl)
-                   anchor_x -= child_allocation.width;
-                 if (operation == DRAG_OPERATION_REORDER)
-                   {
-                     if (page->pack == notebook->cur_page->pack &&
-                         is_rtl &&
-                         left_x >= anchor_x &&
-                         left_x <= anchor_x + child_allocation.width / 2)
-                       anchor_x += notebook->cur_page->allocation.width - tab_overlap;
-                     else if (page->pack == notebook->cur_page->pack &&
-                              !is_rtl &&
-                              right_x >  anchor_x + child_allocation.width / 2 &&
-                              right_x <= anchor_x + child_allocation.width)
-                       anchor_x -= notebook->cur_page->allocation.width - tab_overlap;
-                   }
-                 child_allocation.x = anchor_x;
-               }
-
-             break;
-           case GTK_POS_LEFT:
-           case GTK_POS_RIGHT:
-             child_allocation.height = (page->requisition.height +
-                                        tab_overlap + delta);
-
-             if (operation == DRAG_OPERATION_REORDER && page == notebook->cur_page)
-               child_allocation.y = top_y;
-             else
-               {
-                 anchor_y -= child_allocation.height;
-                 if (operation == DRAG_OPERATION_REORDER &&
-                     page->pack == notebook->cur_page->pack &&
-                     bottom_y >  anchor_y + child_allocation.height / 2 &&
-                     bottom_y <= anchor_y + child_allocation.height)
-                   anchor_y -= notebook->cur_page->allocation.height - tab_overlap;
-                 child_allocation.y = anchor_y;
-               }
-
-             break;
-           }
-
-         page->allocation = child_allocation;
+  return allocation_changed;
+}
 
-         switch (tab_pos)
-           {
-           case GTK_POS_TOP:
-           case GTK_POS_BOTTOM:
-             if (operation != DRAG_OPERATION_REORDER ||
-                 (operation == DRAG_OPERATION_REORDER && page != notebook->cur_page))
-               {
-                 if (operation == DRAG_OPERATION_REORDER)
-                   {
-                     if (page->pack == notebook->cur_page->pack &&
-                         is_rtl &&
-                         left_x >  anchor_x + child_allocation.width / 2 &&
-                         left_x <= anchor_x + child_allocation.width)
-                       anchor_x += notebook->cur_page->allocation.width - tab_overlap;
-                     else if (page->pack == notebook->cur_page->pack &&
-                              !is_rtl &&
-                              right_x >= anchor_x &&
-                              right_x <= anchor_x + child_allocation.width / 2)
-                       anchor_x -= notebook->cur_page->allocation.width - tab_overlap;
-                   }
-                 if (!is_rtl)
-                   anchor_x += tab_overlap;
-                 else
-                   anchor_x += child_allocation.width - tab_overlap;
-               }
+static void
+gtk_notebook_pages_allocate (GtkNotebook *notebook)
+{
+  GList *children = NULL;
+  GList *last_child = NULL;
+  gboolean showarrow = FALSE;
+  gint tab_space, min, max, remaining_space;
+  gint expanded_tabs, operation;
 
-             break;
-           case GTK_POS_LEFT:
-           case GTK_POS_RIGHT:
-             if (operation != DRAG_OPERATION_REORDER  ||
-                 (operation == DRAG_OPERATION_REORDER && page != notebook->cur_page))
-               {
-                 if (operation == DRAG_OPERATION_REORDER &&
-                     page->pack == notebook->cur_page->pack &&
-                     page != notebook->cur_page &&
-                     bottom_y >= anchor_y &&
-                     bottom_y <= anchor_y + child_allocation.height / 2)
-                   anchor_y -= notebook->cur_page->allocation.height - tab_overlap;
-                 anchor_y += tab_overlap;
-               }
+  if (!notebook->show_tabs || !notebook->children || !notebook->cur_page)
+    return;
 
-             break;
-           }
+  min = max = tab_space = remaining_space = 0;
+  expanded_tabs = 1;
 
-         if (page->tab_label)
-           gtk_widget_set_child_visible (page->tab_label, TRUE);
-       }
+  gtk_notebook_tab_space (notebook, &showarrow,
+                         &min, &max, &tab_space);
 
-      /* don't move the tab past the last position */
-      if (operation == DRAG_OPERATION_REORDER &&
-         notebook->cur_page->pack == GTK_PACK_END)
-       {
-         switch (tab_pos)
-           {
-           case GTK_POS_TOP:
-           case GTK_POS_BOTTOM:
-             if ((!is_rtl && PAGE_RIGHT_X (notebook->cur_page) < anchor_x) ||
-                 (is_rtl && PAGE_LEFT_X (notebook->cur_page) > anchor_x))
-               {
-                 if (!is_rtl)
-                   anchor_x -= notebook->cur_page->allocation.width;
+  gtk_notebook_calculate_shown_tabs (notebook, showarrow,
+                                    min, max, tab_space, &last_child,
+                                    &expanded_tabs, &remaining_space);
 
-                 notebook->cur_page->allocation.x = anchor_x;
-               }
-             break;
-           case GTK_POS_LEFT:
-           case GTK_POS_RIGHT:
-             if (PAGE_BOTTOM_Y (notebook->cur_page) < anchor_y)
-               {
-                 anchor_y -= notebook->cur_page->allocation.height;
-                 notebook->cur_page->allocation.y = anchor_y;
-               }
-             break;
-           }
-       }
+  children = notebook->first_tab;
+  gtk_notebook_calculate_tabs_allocation (notebook, &children, last_child,
+                                         showarrow, STEP_NEXT,
+                                         &remaining_space, &expanded_tabs, min, max);
+  if (children && children != last_child)
+    {
+      children = notebook->children;
+      gtk_notebook_calculate_tabs_allocation (notebook, &children, last_child,
+                                             showarrow, STEP_PREV,
+                                             &remaining_space, &expanded_tabs, min, max);
     }
 
- done:
   children = notebook->children;
 
   while (children)
     {
-      page = children->data;
+      gtk_notebook_page_allocate (notebook, GTK_NOTEBOOK_PAGE (children));
       children = children->next;
-
-      if (GTK_WIDGET_DRAWABLE (page->tab_label))
-       gtk_notebook_page_allocate (notebook, page);
     }
 
-  gtk_notebook_redraw_tabs (notebook);  
+  operation = GTK_NOTEBOOK_GET_PRIVATE (notebook)->operation;
+
+  if (!notebook->first_tab)
+    notebook->first_tab = notebook->children;
+
+  gtk_notebook_redraw_tabs (notebook);
 }
 
 static void
@@ -5134,35 +5434,17 @@ gtk_notebook_page_allocate (GtkNotebook     *notebook,
   gint tab_curvature;
   gint tab_pos = get_effective_tab_pos (notebook);
 
-  xthickness = widget->style->xthickness;
-  ythickness = widget->style->ythickness;
-
-  if (notebook->cur_page != page)
-    {
-      switch (tab_pos)
-       {
-       case GTK_POS_TOP:
-         page->allocation.y += ythickness;
-       case GTK_POS_BOTTOM:
-         page->allocation.height = MAX (1, page->allocation.height - ythickness);
-         break;
-       case GTK_POS_LEFT:
-         page->allocation.x += xthickness;
-       case GTK_POS_RIGHT:
-         page->allocation.width = MAX (1, page->allocation.width - xthickness);
-         break;
-       }
-    }
-
   if (!page->tab_label)
     return;
 
+  xthickness = widget->style->xthickness;
+  ythickness = widget->style->ythickness;
+
   gtk_widget_get_child_requisition (page->tab_label, &tab_requisition);
   gtk_widget_style_get (widget,
                        "focus-line-width", &focus_width,
                        "tab-curvature", &tab_curvature,
                        NULL);
-
   switch (tab_pos)
     {
     case GTK_POS_TOP:
@@ -5170,24 +5452,24 @@ gtk_notebook_page_allocate (GtkNotebook     *notebook,
       padding = tab_curvature + focus_width + notebook->tab_hborder;
       if (page->fill)
        {
-         child_allocation.x = (xthickness + focus_width +
-                               notebook->tab_hborder);
-         child_allocation.width = MAX (1, (page->allocation.width -
-                                           2 * child_allocation.x));
+         child_allocation.x = xthickness + focus_width + notebook->tab_hborder;
+         child_allocation.width = MAX (1, page->allocation.width - 2 * child_allocation.x);
          child_allocation.x += page->allocation.x;
        }
       else
        {
-         child_allocation.x = (page->allocation.x +
-                               (page->allocation.width -
-                                tab_requisition.width) / 2);
+         child_allocation.x = page->allocation.x +
+           (page->allocation.width - tab_requisition.width) / 2;
+
          child_allocation.width = tab_requisition.width;
        }
-      child_allocation.y = (notebook->tab_vborder + focus_width +
-                           page->allocation.y);
+
+      child_allocation.y = notebook->tab_vborder + focus_width + page->allocation.y;
+
       if (tab_pos == GTK_POS_TOP)
        child_allocation.y += ythickness;
-      child_allocation.height = MAX (1, (((gint) page->allocation.height) - ythickness -
+
+      child_allocation.height = MAX (1, (page->allocation.height - ythickness -
                                         2 * (notebook->tab_vborder + focus_width)));
       break;
     case GTK_POS_LEFT:
@@ -5202,14 +5484,18 @@ gtk_notebook_page_allocate (GtkNotebook     *notebook,
        }
       else
        {
-         child_allocation.y = (page->allocation.y + (page->allocation.height -
-                                                     tab_requisition.height) / 2);
+         child_allocation.y = page->allocation.y +
+           (page->allocation.height - tab_requisition.height) / 2;
+
          child_allocation.height = tab_requisition.height;
        }
-      child_allocation.x = page->allocation.x + notebook->tab_hborder + focus_width;
+
+      child_allocation.x = notebook->tab_hborder + focus_width + page->allocation.x;
+
       if (tab_pos == GTK_POS_LEFT)
        child_allocation.x += xthickness;
-      child_allocation.width = MAX (1, (((gint) page->allocation.width) - xthickness -
+
+      child_allocation.width = MAX (1, (page->allocation.width - xthickness -
                                        2 * (notebook->tab_hborder + focus_width)));
       break;
     }
@@ -5218,8 +5504,8 @@ gtk_notebook_page_allocate (GtkNotebook     *notebook,
 }
 
 static void 
-gtk_notebook_calc_tabs (GtkNotebook  *notebook, 
-                       GList        *start, 
+gtk_notebook_calc_tabs (GtkNotebook  *notebook,
+                       GList        *start,
                         GList       **end,
                        gint         *tab_space,
                         guint         direction)
@@ -5227,8 +5513,10 @@ gtk_notebook_calc_tabs (GtkNotebook  *notebook,
   GtkNotebookPage *page = NULL;
   GList *children;
   GList *last_list = NULL;
+  GList *last_calculated_child = NULL;
   gboolean pack;
   gint tab_pos = get_effective_tab_pos (notebook);
+  guint real_direction;
 
   if (!start)
     return;
@@ -5236,7 +5524,9 @@ gtk_notebook_calc_tabs (GtkNotebook  *notebook,
   children = start;
   pack = GTK_NOTEBOOK_PAGE (start)->pack;
   if (pack == GTK_PACK_END)
-    direction = (direction == STEP_PREV) ? STEP_NEXT : STEP_PREV;
+    real_direction = (direction == STEP_PREV) ? STEP_NEXT : STEP_PREV;
+  else
+    real_direction = direction;
 
   while (1)
     {
@@ -5247,7 +5537,8 @@ gtk_notebook_calc_tabs (GtkNotebook  *notebook,
          while (children)
            {
              page = children->data;
-             if (GTK_WIDGET_VISIBLE (page->child))
+             if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
+                 GTK_WIDGET_VISIBLE (page->child))
                {
                  if (page->pack == pack)
                    {
@@ -5258,14 +5549,20 @@ gtk_notebook_calc_tabs (GtkNotebook  *notebook,
                            {
                              *tab_space = - (*tab_space +
                                              page->requisition.width);
+
+                             if (*tab_space == 0 && direction == STEP_PREV)
+                               children = last_calculated_child;
+
                              *end = children;
                            }
                          return;
                        }
+
+                     last_calculated_child = children;
                    }
                  last_list = children;
                }
-             if (direction == STEP_NEXT)
+             if (real_direction == STEP_NEXT)
                children = children->next;
              else
                children = children->prev;
@@ -5276,7 +5573,8 @@ gtk_notebook_calc_tabs (GtkNotebook  *notebook,
          while (children)
            {
              page = children->data;
-             if (GTK_WIDGET_VISIBLE (page->child))
+             if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
+                 GTK_WIDGET_VISIBLE (page->child))
                {
                  if (page->pack == pack)
                    {
@@ -5287,24 +5585,30 @@ gtk_notebook_calc_tabs (GtkNotebook  *notebook,
                            {
                              *tab_space = - (*tab_space +
                                              page->requisition.height);
+
+                             if (*tab_space == 0 && direction == STEP_PREV)
+                               children = last_calculated_child;
+
                              *end = children;
                            }
                          return;
                        }
+
+                     last_calculated_child = children;
                    }
                  last_list = children;
                }
-             if (direction == STEP_NEXT)
+             if (real_direction == STEP_NEXT)
                children = children->next;
              else
                children = children->prev;
            }
          break;
        }
-      if (direction == STEP_PREV)
+      if (real_direction == STEP_PREV)
        return;
       pack = (pack == GTK_PACK_END) ? GTK_PACK_START : GTK_PACK_END;
-      direction = STEP_PREV;
+      real_direction = STEP_PREV;
       children = last_list;
     }
 }
@@ -5467,7 +5771,7 @@ gtk_notebook_switch_focus_tab (GtkNotebook *notebook,
   if (GTK_WIDGET_MAPPED (page->tab_label))
     gtk_notebook_redraw_tabs (notebook);
   else
-    gtk_notebook_pages_allocate (notebook, DRAG_OPERATION_NONE);
+    gtk_notebook_pages_allocate (notebook);
   
   gtk_notebook_switch_page (notebook, page,
                            g_list_index (notebook->children, page));
@@ -5543,8 +5847,8 @@ static void
 gtk_notebook_menu_label_unparent (GtkWidget *widget, 
                                  gpointer  data)
 {
-  gtk_widget_unparent (GTK_BIN(widget)->child);
-  GTK_BIN(widget)->child = NULL;
+  gtk_widget_unparent (GTK_BIN (widget)->child);
+  GTK_BIN (widget)->child = NULL;
 }
 
 static void
@@ -6008,8 +6312,13 @@ gtk_notebook_page_num (GtkNotebook      *notebook,
  *            than the number of pages in the notebook, nothing
  *            will be done.
  *                
- * Switches to the page number @page_num.
- **/
+ * Switches to the page number @page_num. 
+ *
+ * Note that due to historical reasons, GtkNotebook refuses
+ * to switch to a page unless the child widget is visible. 
+ * Therefore, it is recommended to show child widgets before
+ * adding them to a notebook. 
+ */
 void
 gtk_notebook_set_current_page (GtkNotebook *notebook,
                               gint         page_num)
@@ -6787,7 +7096,7 @@ gtk_notebook_set_tab_label_packing (GtkNotebook *notebook,
   gtk_widget_child_notify (child, "tab-pack");
   gtk_widget_child_notify (child, "position");
   if (notebook->show_tabs)
-    gtk_notebook_pages_allocate (notebook, DRAG_OPERATION_NONE);
+    gtk_notebook_pages_allocate (notebook);
   gtk_widget_thaw_child_notify (child);
 }  
 
@@ -6883,7 +7192,7 @@ gtk_notebook_reorder_child (GtkNotebook *notebook,
   gtk_widget_child_notify (child, "position");
 
   if (notebook->show_tabs)
-    gtk_notebook_pages_allocate (notebook, DRAG_OPERATION_NONE);
+    gtk_notebook_pages_allocate (notebook);
 
   gtk_widget_thaw_child_notify (child);
 
@@ -6896,8 +7205,9 @@ gtk_notebook_reorder_child (GtkNotebook *notebook,
 
 /**
  * gtk_notebook_set_window_creation_hook:
- * @func: the #GtkNotebookWindowCreationFunc, or NULL
- * @data: user data for @func.
+ * @func: the #GtkNotebookWindowCreationFunc, or %NULL
+ * @data: user data for @func
+ * @destroy: Destroy notifier for @data, or %NULL
  *
  * Installs a global function used to create a window
  * when a detached tab is dropped in an empty area.
@@ -6906,10 +7216,15 @@ gtk_notebook_reorder_child (GtkNotebook *notebook,
  **/
 void
 gtk_notebook_set_window_creation_hook (GtkNotebookWindowCreationFunc  func,
-                                      gpointer                       data)
+                                      gpointer                       data,
+                                       GDestroyNotify                 destroy)
 {
+  if (window_creation_hook_destroy)
+    window_creation_hook_destroy (window_creation_hook_data);
+
   window_creation_hook = func;
   window_creation_hook_data = data;
+  window_creation_hook_destroy = destroy;
 }
 
 /**
@@ -6969,7 +7284,9 @@ gtk_notebook_get_group_id (GtkNotebook *notebook)
  * 
  * Gets whether the tab can be reordered via drag and drop or not.
  * 
- * Return Value: TRUE if the tab is reorderable.
+ * Return Value: %TRUE if the tab is reorderable.
+ * 
+ * Since: 2.10
  **/
 gboolean
 gtk_notebook_get_tab_reorderable (GtkNotebook *notebook,
@@ -7094,8 +7411,8 @@ gtk_notebook_get_tab_detachable (GtkNotebook *notebook,
  **/
 void
 gtk_notebook_set_tab_detachable (GtkNotebook *notebook,
-                                 GtkWidget   *child,
-                                 gboolean     detachable)
+                                GtkWidget  *child,
+                                gboolean    detachable)
 {
   GList *list;