Index: gtknotebook.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtknotebook.c,v
retrieving revision 1.191
diff -u -r1.191 gtknotebook.c
--- gtknotebook.c	23 Nov 2005 18:06:56 -0000	1.191
+++ gtknotebook.c	15 Dec 2005 20:25:20 -0000
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
 /* GTK - The GIMP Toolkit
  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
  *
@@ -69,6 +70,13 @@
   ARROW_RIGHT_AFTER
 } GtkNotebookArrow;
 
+typedef enum
+{
+  POINTER_BEFORE,
+  POINTER_AFTER,
+  POINTER_BETWEEN
+} GtkNotebookPointerPosition;
+
 #define ARROW_IS_LEFT(arrow)  ((arrow) == ARROW_LEFT_BEFORE || (arrow) == ARROW_LEFT_AFTER)
 #define ARROW_IS_BEFORE(arrow) ((arrow) == ARROW_LEFT_BEFORE || (arrow) == ARROW_RIGHT_BEFORE)
 
@@ -83,7 +91,8 @@
   PROP_TAB_VBORDER,
   PROP_PAGE,
   PROP_ENABLE_POPUP,
-  PROP_HOMOGENEOUS
+  PROP_HOMOGENEOUS,
+  PROP_REORDERABLE
 };
 
 enum {
@@ -98,6 +107,13 @@
 
 #define GTK_NOTEBOOK_PAGE(_glist_)         ((GtkNotebookPage *)((GList *)(_glist_))->data)
 
+/* some useful defines for calculating coords */
+#define PAGE_LEFT_X(_page_)   (((GtkNotebookPage *) (_page_))->allocation.x)
+#define PAGE_RIGHT_X(_page_)  (((GtkNotebookPage *) (_page_))->allocation.x + ((GtkNotebookPage *) (_page_))->allocation.width)
+#define PAGE_MIDDLE_X(_page_) (((GtkNotebookPage *) (_page_))->allocation.x + ((GtkNotebookPage *) (_page_))->allocation.width / 2)
+#define PAGE_TOP_Y(_page_)    (((GtkNotebookPage *) (_page_))->allocation.y)
+#define PAGE_MIDDLE_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y + ((GtkNotebookPage *) (_page_))->allocation.height / 2)
+
 struct _GtkNotebookPage
 {
   GtkWidget *child;
@@ -117,6 +133,20 @@
   guint mnemonic_activate_signal;
 };
 
+#define GTK_NOTEBOOK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_NOTEBOOK, GtkNotebookPrivate))
+
+typedef struct _GtkNotebookPrivate GtkNotebookPrivate;
+
+struct _GtkNotebookPrivate
+{
+  gboolean reorderable;
+
+  gint  mouse_x;
+  gint  mouse_y;
+  gint  pressed_button;
+  guint dnd_timer;
+};
+
 #ifdef G_DISABLE_CHECKS
 #define CHECK_FIND_CHILD(notebook, child)                           \
  gtk_notebook_find_child (notebook, child, G_STRLOC)
@@ -239,7 +269,8 @@
 					      GtkNotebookArrow  arrow);
 
 /*** GtkNotebook Size Allocate Functions ***/
-static void gtk_notebook_pages_allocate      (GtkNotebook      *notebook);
+static void gtk_notebook_pages_allocate      (GtkNotebook      *notebook,
+					      gboolean          during_drag);
 static void gtk_notebook_page_allocate       (GtkNotebook      *notebook,
 					      GtkNotebookPage  *page,
 					      GtkAllocation    *allocation);
@@ -382,7 +413,7 @@
   widget_class->focus_out_event = gtk_notebook_focus_out;
   widget_class->focus = gtk_notebook_focus;
   widget_class->style_set = gtk_notebook_style_set;
-  
+
   container_class->add = gtk_notebook_add;
   container_class->remove = gtk_notebook_remove;
   container_class->forall = gtk_notebook_forall;
@@ -477,6 +508,13 @@
  							 P_("Whether tabs should have homogeneous sizes"),
  							 FALSE,
 							 GTK_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+				   PROP_REORDERABLE,
+				   g_param_spec_boolean ("reorderable",
+ 							 P_("Reorderable"),
+ 							 P_("Whether tabs can be reordered by user action"),
+ 							 FALSE,
+							 GTK_PARAM_READWRITE));
 
   gtk_container_class_install_child_property (container_class,
 					      CHILD_PROP_TAB_LABEL,
@@ -687,14 +725,20 @@
 
   add_tab_bindings (binding_set, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
   add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
+
+  g_type_class_add_private (class, sizeof (GtkNotebookPrivate));
 }
 
 static void
 gtk_notebook_init (GtkNotebook *notebook)
 {
+  GtkNotebookPrivate *priv;
+
   GTK_WIDGET_SET_FLAGS (notebook, GTK_CAN_FOCUS);
   GTK_WIDGET_SET_FLAGS (notebook, GTK_NO_WINDOW);
 
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+
   notebook->cur_page = NULL;
   notebook->children = NULL;
   notebook->first_tab = NULL;
@@ -721,6 +765,10 @@
   notebook->has_before_next     = 0;
   notebook->has_after_previous  = 0;
   notebook->has_after_next      = 1;
+
+  priv->reorderable = FALSE;
+  priv->pressed_button = -1;
+  priv->dnd_timer = 0;
 }
 
 static gboolean
@@ -947,6 +995,9 @@
     case PROP_TAB_VBORDER:
       gtk_notebook_set_tab_vborder_internal (notebook, g_value_get_uint (value));
       break;
+    case PROP_REORDERABLE:
+      gtk_notebook_set_reorderable (notebook, g_value_get_boolean (value));
+      break;
     default:
       break;
     }
@@ -959,8 +1010,10 @@
 			   GParamSpec      *pspec)
 {
   GtkNotebook *notebook;
+  GtkNotebookPrivate *priv;
 
   notebook = GTK_NOTEBOOK (object);
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
 
   switch (prop_id)
     {
@@ -991,6 +1044,9 @@
     case PROP_TAB_VBORDER:
       g_value_set_uint (value, notebook->tab_vborder);
       break;
+    case PROP_REORDERABLE:
+      g_value_set_boolean (value, priv->reorderable);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1095,7 +1151,7 @@
     gtk_widget_map (notebook->cur_page->child);
 
   if (notebook->scrollable)
-    gtk_notebook_pages_allocate (notebook);
+    gtk_notebook_pages_allocate (notebook, FALSE);
   else
     {
       children = notebook->children;
@@ -1157,6 +1213,7 @@
   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_SCROLL_MASK);
   attributes_mask = GDK_WA_X | GDK_WA_Y;
 
@@ -1517,7 +1574,7 @@
 	    }
 	}
 
-      gtk_notebook_pages_allocate (notebook);
+      gtk_notebook_pages_allocate (notebook, FALSE);
     }
 }
 
@@ -1811,6 +1868,7 @@
 			   GdkEventButton *event)
 {
   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
+  GtkNotebookPrivate *priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
   GtkNotebookPage *page;
   GList *children;
   GtkNotebookArrow arrow;
@@ -1837,9 +1895,14 @@
 
   if (event->button != 1)
     return FALSE;
-  
+
+  if (priv->reorderable)
+    gtk_grab_add (widget);
+
+  notebook->button = event->button;
   num = 0;
   children = notebook->children;
+
   while (children)
     {
       page = children->data;
@@ -1860,12 +1923,15 @@
 	  if (page_changed && !was_focus)
 	    gtk_widget_child_focus (page->child, GTK_DIR_TAB_FORWARD);
 	  
+	  /* save press to possibly begin a drag */
+	  if (priv->reorderable)
+	    priv->pressed_button = event->button;
 	  break;
 	}
       children = children->next;
       num++;
     }
-  
+
   return TRUE;
 }
 
@@ -1883,11 +1949,105 @@
   gtk_notebook_redraw_arrows (notebook);
 }
 
+static void
+move_tab_after (GtkNotebook *notebook, GList *position, GList *tab)
+{
+  GList *next;
+
+  if (position == tab)
+    return;
+
+  if (notebook->first_tab == tab)
+    notebook->first_tab = gtk_notebook_search_page (notebook, notebook->first_tab,
+						    STEP_NEXT, TRUE);
+
+  notebook->children = g_list_remove_link (notebook->children, tab);
+
+  if (!position)
+    {
+      next = notebook->children;
+      notebook->children = tab;
+    }
+  else
+    {
+      next = position->next;
+      position->next = tab;
+    }
+
+  if (next)
+    next->prev = tab;
+
+  tab->next  = next;
+  tab->prev  = position;
+}
+
+static GList*
+get_drop_position (GtkNotebook *notebook)
+{
+  GList *children, *last = NULL;
+  GtkNotebookPage *page;
+  gboolean is_rtl;
+  
+  is_rtl = gtk_widget_get_direction ((GtkWidget *) notebook) == GTK_TEXT_DIR_RTL;
+  children = notebook->children;
+
+  while (children)
+    {
+      page = children->data;
+
+      if (GTK_WIDGET_VISIBLE (page->child) &&
+	  page->tab_label && GTK_WIDGET_MAPPED (page->tab_label) &&
+	  page->pack == notebook->cur_page->pack)
+	{
+	  switch (notebook->tab_pos)
+	    {
+	    case GTK_POS_TOP:
+	    case GTK_POS_BOTTOM:
+	      if (!is_rtl)
+		{
+		  if ((page->pack == GTK_PACK_START &&
+		       PAGE_MIDDLE_X (page) > PAGE_MIDDLE_X (notebook->cur_page)) ||
+		      (page->pack == GTK_PACK_END &&
+		       PAGE_MIDDLE_X (page) < PAGE_MIDDLE_X (notebook->cur_page)))
+		    return children->prev;
+		}
+	      else
+		{
+		  if ((page->pack == GTK_PACK_START &&
+		       PAGE_MIDDLE_X (page) < PAGE_MIDDLE_X (notebook->cur_page)) ||
+		      (page->pack == GTK_PACK_END &&
+		       PAGE_MIDDLE_X (page) > PAGE_MIDDLE_X (notebook->cur_page)))
+		    return children->prev;
+		}
+
+	      break;
+	    case GTK_POS_LEFT:
+	    case GTK_POS_RIGHT:
+	      if ((page->pack == GTK_PACK_START &&
+		   PAGE_MIDDLE_Y (page) > PAGE_MIDDLE_Y (notebook->cur_page)) ||
+		  (page->pack == GTK_PACK_END &&
+		   PAGE_MIDDLE_Y (page) < PAGE_MIDDLE_Y (notebook->cur_page)))
+		return children->prev;
+
+	      break;
+	    }
+
+	  last = children;
+	}
+
+      children = children->next;
+    }
+
+  return last;
+}
+
 static gint
 gtk_notebook_button_release (GtkWidget      *widget,
 			     GdkEventButton *event)
 {
   GtkNotebook *notebook;
+  GtkNotebookPrivate *priv;
+  GList *element;
 
   g_return_val_if_fail (GTK_IS_NOTEBOOK (widget), FALSE);
   g_return_val_if_fail (event != NULL, FALSE);
@@ -1896,6 +2056,27 @@
     return FALSE;
 
   notebook = GTK_NOTEBOOK (widget);
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+
+  if (priv->reorderable)
+    {
+      gtk_grab_remove (widget);
+
+      if (priv->pressed_button == event->button)
+	{
+	  priv->pressed_button = -1;
+	  element = get_drop_position (notebook);
+	  move_tab_after (notebook, element, notebook->focus_tab);
+	}
+
+      gtk_notebook_pages_allocate (notebook, FALSE);
+
+      if (priv->dnd_timer)
+	{
+	  g_source_remove (priv->dnd_timer);
+	  priv->dnd_timer = 0;
+	}
+    }
 
   if (event->button == notebook->button)
     {
@@ -1955,28 +2136,103 @@
   return TRUE;
 }
 
+static GtkNotebookPointerPosition
+get_pointer_position (GtkNotebook *notebook)
+{
+  GtkWidget *widget = (GtkWidget *) notebook;
+  GtkNotebookPrivate *priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+  gboolean is_rtl;
+
+  if (notebook->tab_pos == GTK_POS_TOP ||
+      notebook->tab_pos == GTK_POS_BOTTOM)
+    {
+      is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
+
+      if (priv->mouse_x > widget->allocation.x + widget->allocation.width - ARROW_SIZE)
+	return (is_rtl) ? POINTER_BEFORE : POINTER_AFTER;
+      else if (priv->mouse_x < widget->allocation.x + ARROW_SIZE)
+	return (is_rtl) ? POINTER_AFTER : POINTER_BEFORE;
+      else
+	return POINTER_BETWEEN;
+    }
+  else
+    {
+      if (priv->mouse_y > widget->allocation.y + widget->allocation.height - ARROW_SIZE)
+	return POINTER_AFTER;
+      else if (priv->mouse_y < widget->allocation.y + ARROW_SIZE)
+	return POINTER_BEFORE;
+      else
+	return POINTER_BETWEEN;
+    }
+}
+
+static gboolean
+scroll_notebook_timer (gpointer data)
+{
+  GtkNotebook *notebook = (GtkNotebook *) data;
+  GtkNotebookPointerPosition pointer_position;
+  GList *element, *first_tab;
+
+  pointer_position = get_pointer_position (notebook);
+  element = get_drop_position (notebook);
+  move_tab_after (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, TRUE);
+    }
+
+  return TRUE;
+}
+
 static gint
 gtk_notebook_motion_notify (GtkWidget      *widget,
 			    GdkEventMotion *event)
 {
   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
-  GtkNotebookArrow arrow;
-  gint x, y;
-  
-  if (notebook->button)
+  GtkNotebookPrivate *priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+  GtkNotebookPointerPosition pointer_position;
+  GtkSettings *settings;
+  guint        timeout;
+
+  if (priv->pressed_button == -1)
     return FALSE;
 
-  if (!get_widget_coordinates (widget, (GdkEvent *)event, &x, &y))
+  if (!notebook->cur_page)
     return FALSE;
 
-  arrow = gtk_notebook_get_arrow (notebook, x, y);
+  priv->mouse_x = event->x;
+  priv->mouse_y = event->y;
 
-  if (arrow != notebook->in_child)
+  pointer_position = get_pointer_position (notebook);
+
+  if (pointer_position != POINTER_BETWEEN)
     {
-      notebook->in_child = arrow;
-      gtk_notebook_redraw_arrows (notebook);
+      if (!priv->dnd_timer)
+	{
+	  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, 
+					   (gpointer) notebook);
+	}
     }
-  
+  else
+    {
+      if (priv->dnd_timer)
+	{
+	  g_source_remove (priv->dnd_timer);
+	  priv->dnd_timer = 0;
+	}
+    }
+
+  gtk_notebook_pages_allocate (notebook, TRUE);
+
   return TRUE;
 }
 
@@ -2569,7 +2825,7 @@
 	{
 	  if (arrow[i] == ARROW_NONE)
 	    continue;
-	  
+
 	  gtk_notebook_get_arrow_rect (notebook, &rect, arrow[i]);
 	  gdk_window_invalidate_rect (GTK_WIDGET (notebook)->window, 
 				      &rect, FALSE);
@@ -3129,6 +3385,44 @@
     }
 }
 
+static void
+get_notebook_tabs_space (GtkNotebook *notebook, gint *min, gint *max)
+{
+  GtkWidget *widget = (GtkWidget *) notebook;
+
+  if (notebook->tab_pos == GTK_POS_TOP ||
+      notebook->tab_pos == GTK_POS_BOTTOM)
+    {
+      *min = widget->allocation.x;
+      *max = widget->allocation.x + widget->allocation.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;
+      *max = widget->allocation.y + widget->allocation.height;
+
+      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;
+	}
+    }
+}
+
 /* Private GtkNotebook Size Allocate Functions:
  *
  * gtk_notebook_pages_allocate
@@ -3136,10 +3430,12 @@
  * gtk_notebook_calc_tabs
  */
 static void
-gtk_notebook_pages_allocate (GtkNotebook   *notebook)
+gtk_notebook_pages_allocate (GtkNotebook *notebook,
+			     gboolean     during_drag)
 {
   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;
@@ -3155,7 +3451,10 @@
   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 memo_x;
+  gint memo_x, anchor_x, anchor_y;
+  gint min, max, top_y, bottom_y, left_x, right_x;
+  gboolean maybe_need_gap = FALSE;
+  gboolean gap_left = FALSE;
 
   if (!notebook->show_tabs || !notebook->children || !notebook->cur_page)
     return;
@@ -3185,13 +3484,13 @@
       child_allocation.width = notebook->cur_page->requisition.width;
       break;
     }
-  
+
   if (notebook->scrollable) 
     {
       GList *focus_tab;
       
       children = notebook->children;
-      
+
       if (notebook->focus_tab)
 	focus_tab = notebook->focus_tab;
       else if (notebook->first_tab)
@@ -3211,6 +3510,7 @@
 	      if (GTK_WIDGET_VISIBLE (page->child))
 		tab_space += page->requisition.width;
 	    }
+
 	  if (tab_space >
 	      allocation->width - 2 * container->border_width - TAB_OVERLAP) 
 	    {
@@ -3264,7 +3564,7 @@
 	  break;
 	}
       if (showarrow) /* first_tab <- focus_tab */
-	{ 
+	{
 	  if (tab_space <= 0)
 	    {
 	      notebook->first_tab = focus_tab;
@@ -3451,6 +3751,17 @@
 	}
      }
 
+  get_notebook_tabs_space (notebook, &min, &max);
+  anchor_x = child_allocation.x;
+  anchor_y = child_allocation.y;
+
+  left_x   = CLAMP (priv->mouse_x - notebook->cur_page->allocation.width / 2,
+		    min, max - notebook->cur_page->allocation.width);
+  top_y    = CLAMP (priv->mouse_y - notebook->cur_page->allocation.height / 2,
+		    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;
+
   while (children)
     {
       if (children == last_child)
@@ -3460,10 +3771,17 @@
 	}
 
       page = children->data;
-      if (!showarrow && page->pack != GTK_PACK_START) 
-	break;
+
+      if (page->pack != GTK_PACK_START)
+	{
+	  if (!showarrow)
+	    break;
+	  else if (during_drag)
+	    maybe_need_gap = TRUE;
+	}
+
       children = gtk_notebook_search_page (notebook, children, STEP_NEXT,TRUE);
-      
+
       delta = 0;
       if (n && (showarrow || page->expand || notebook->homogeneous))
 	{
@@ -3478,13 +3796,81 @@
 	case GTK_POS_BOTTOM:
 	  child_allocation.width = (page->requisition.width +
 				    TAB_OVERLAP + delta);
-          if (is_rtl)
-	      child_allocation.x -= child_allocation.width;
+
+	  if (during_drag && page == notebook->cur_page)
+	    child_allocation.x = left_x;
+	  else
+	    {
+	      if (!gap_left && maybe_need_gap)
+		{
+		  if (!is_rtl)
+		    {
+		      if ((notebook->cur_page->pack == GTK_PACK_START &&
+			   PAGE_LEFT_X (notebook->cur_page) > anchor_x) ||
+			  (notebook->cur_page->pack == GTK_PACK_END &&
+			   PAGE_LEFT_X (notebook->cur_page) <= anchor_x))
+			anchor_x += notebook->cur_page->allocation.width - TAB_OVERLAP;
+		    }
+		  else
+		    {
+		      if ((notebook->cur_page->pack == GTK_PACK_START &&
+			   PAGE_RIGHT_X (notebook->cur_page) < anchor_x) ||
+			  (notebook->cur_page->pack == GTK_PACK_END &&
+			   PAGE_RIGHT_X (notebook->cur_page) >= anchor_x))
+			anchor_x -= notebook->cur_page->allocation.width - TAB_OVERLAP;
+		    }
+
+		  gap_left = TRUE;
+		}
+
+	      if (is_rtl)
+		anchor_x -= child_allocation.width;
+
+	      if (during_drag && 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;
+		}
+
+	      child_allocation.x = anchor_x;
+	    }
+
 	  break;
 	case GTK_POS_LEFT:
 	case GTK_POS_RIGHT:
 	  child_allocation.height = (page->requisition.height +
 				     TAB_OVERLAP + delta);
+
+	  if (during_drag && page == notebook->cur_page)
+	    child_allocation.y = top_y;
+	  else
+	    {
+	      if (!gap_left && maybe_need_gap)
+		{
+		  if ((notebook->cur_page->pack == GTK_PACK_START &&
+		       PAGE_TOP_Y (notebook->cur_page) > anchor_y) ||
+		      (notebook->cur_page->pack == GTK_PACK_END &&
+		       PAGE_TOP_Y (notebook->cur_page) <= anchor_y))
+		    anchor_y += notebook->cur_page->allocation.height - TAB_OVERLAP;
+
+		  gap_left = TRUE;
+		}
+
+	      if (during_drag &&
+		  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;
+	    }
+
 	  break;
 	}
 
@@ -3494,14 +3880,43 @@
 	{
 	case GTK_POS_TOP:
 	case GTK_POS_BOTTOM:
-          if (!is_rtl)
-	     child_allocation.x += child_allocation.width - TAB_OVERLAP;
-          else
-             child_allocation.x += TAB_OVERLAP;
+	  if (!during_drag || (during_drag && page != notebook->cur_page))
+	    {
+	      if (during_drag)
+		{
+		  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 += child_allocation.width - TAB_OVERLAP;
+	      else
+		anchor_x += TAB_OVERLAP;
+	    }
+
 	  break;
 	case GTK_POS_LEFT:
 	case GTK_POS_RIGHT:
-	  child_allocation.y += child_allocation.height - TAB_OVERLAP;
+	  if (!during_drag  || (during_drag && page != notebook->cur_page))
+	    {
+	      if (during_drag &&
+		  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;
+	    }
+
 	  break;
 	}
 
@@ -3518,15 +3933,15 @@
 	case GTK_POS_TOP:
 	case GTK_POS_BOTTOM:
           if (!is_rtl)
-	     child_allocation.x = (allocation->x + allocation->width -
-	   			  container->border_width);
+	     anchor_x = (allocation->x + allocation->width -
+			 container->border_width);
           else
-             child_allocation.x = memo_x; 
+             anchor_x = memo_x; 
 	  break;
 	case GTK_POS_LEFT:
 	case GTK_POS_RIGHT:
-	  child_allocation.y = (allocation->y + allocation->height -
-				container->border_width);
+	  anchor_y = (allocation->y + allocation->height -
+		      container->border_width);
 	  break;
 	}
 
@@ -3552,14 +3967,52 @@
 	    case GTK_POS_BOTTOM:
 	      child_allocation.width = (page->requisition.width +
 					TAB_OVERLAP + delta);
-              if (!is_rtl)
-	         child_allocation.x -= child_allocation.width;
+
+	      if (during_drag && page == notebook->cur_page)
+		child_allocation.x = left_x;
+	      else
+		{
+		  if (!is_rtl)
+		    anchor_x -= child_allocation.width;
+
+		  if (during_drag)
+		    {
+		      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);
-	      child_allocation.y -= child_allocation.height;
+
+	      if (during_drag && page == notebook->cur_page)
+		child_allocation.y = top_y;
+	      else
+		{
+		  anchor_y -= child_allocation.height;
+
+		  if (during_drag &&
+		      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;
 	    }
 
@@ -3569,14 +4022,43 @@
 	    {
 	    case GTK_POS_TOP:
 	    case GTK_POS_BOTTOM:
-              if (!is_rtl)
-	         child_allocation.x += TAB_OVERLAP;
-              else
-                 child_allocation.x += child_allocation.width - TAB_OVERLAP;
+	      if (!during_drag || (during_drag && page != notebook->cur_page))
+		{
+		  if (during_drag)
+		    {
+		      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;
+		}
+
 	      break;
 	    case GTK_POS_LEFT:
 	    case GTK_POS_RIGHT:
-	      child_allocation.y += TAB_OVERLAP;
+	      if (!during_drag  || (during_drag && page != notebook->cur_page))
+		{
+		  if (during_drag &&
+		      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;
+		}
+
 	      break;
 	    }
 
@@ -3933,7 +4415,7 @@
   if (GTK_WIDGET_MAPPED (page->tab_label))
     gtk_notebook_redraw_tabs (notebook);
   else
-    gtk_notebook_pages_allocate (notebook);
+    gtk_notebook_pages_allocate (notebook, FALSE);
   
   gtk_notebook_switch_page (notebook, page,
 			    g_list_index (notebook->children, page));
@@ -5339,7 +5821,7 @@
   gtk_widget_child_notify (child, "tab-pack");
   gtk_widget_child_notify (child, "position");
   if (notebook->show_tabs)
-    gtk_notebook_pages_allocate (notebook);
+    gtk_notebook_pages_allocate (notebook, FALSE);
   gtk_widget_thaw_child_notify (child);
 }  
 
@@ -5433,11 +5915,49 @@
   gtk_notebook_child_reordered (notebook, page);
   gtk_widget_child_notify (child, "tab-pack");
   gtk_widget_child_notify (child, "position");
-  
+
   if (notebook->show_tabs)
-    gtk_notebook_pages_allocate (notebook);
+    gtk_notebook_pages_allocate (notebook, FALSE);
 
   gtk_widget_thaw_child_notify (child);
+}
+
+/**
+ * gtk_notebook_set_reorderable:
+ * @notebook: a #GtkNotebook
+ * @reorderable: %TRUE if the tabs can reordered by the user
+ * 
+ * Sets whether the notebook tabs can be reordered by the user.
+ **/
+void
+gtk_notebook_set_reorderable (GtkNotebook *notebook,
+			      gboolean     reorderable)
+{
+  GtkNotebookPrivate *priv;
+
+  g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
+
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+  priv->reorderable = reorderable;
+}
+
+/**
+ * gtk_notebook_get_reorderable:
+ * @notebook: a #GtkNotebook
+ * 
+ * Returns whether the tabs can be reordered by the user.
+ * 
+ * Return Value: TRUE if tabs can be reordered
+ **/
+gboolean
+gtk_notebook_get_reorderable (GtkNotebook *notebook)
+{
+  GtkNotebookPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
+
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+  return priv->reorderable;
 }
 
 #define __GTK_NOTEBOOK_C__
Index: gtknotebook.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtknotebook.h,v
retrieving revision 1.37
diff -u -r1.37 gtknotebook.h
--- gtknotebook.h	20 Mar 2005 07:01:22 -0000	1.37
+++ gtknotebook.h	15 Dec 2005 20:25:20 -0000
@@ -188,6 +188,10 @@
 					    gboolean         scrollable);
 gboolean gtk_notebook_get_scrollable       (GtkNotebook     *notebook);
 
+void     gtk_notebook_set_reorderable      (GtkNotebook *notebook,
+					    gboolean     reorderable);
+gboolean gtk_notebook_get_reorderable      (GtkNotebook *notebook);
+
 /***********************************************************
  *               enable/disable PopupMenu                  *
  ***********************************************************/
