Index: gtknotebook.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtknotebook.c,v
retrieving revision 1.195
diff -u -r1.195 gtknotebook.c
--- gtknotebook.c	23 Feb 2006 17:59:32 -0000	1.195
+++ gtknotebook.c	1 Mar 2006 15:25:31 -0000
@@ -46,6 +46,7 @@
 #define SCROLL_DELAY_FACTOR   5
 #define SCROLL_THRESHOLD      12
 #define DND_THRESHOLD_MULTIPLIER 4
+#define SWITCH_TAB_DELAY_FACTOR  5
 
 enum {
   SWITCH_PAGE,
@@ -160,7 +161,8 @@
   gint  mouse_x;
   gint  mouse_y;
   gint  pressed_button;
-  guint dnd_timer;
+  glong dnd_timer;
+  glong switch_tab_timer;
   GtkTargetList *source_targets;
   gboolean during_detach;
   gboolean has_scrolled;
@@ -252,6 +254,9 @@
 					      gint              x,
 					      gint              y,
 					      guint             time);
+static void gtk_notebook_drag_leave          (GtkWidget        *widget,
+					      GdkDragContext   *context,
+					      guint             time);
 static gboolean gtk_notebook_drag_drop       (GtkWidget        *widget,
 					      GdkDragContext   *context,
 					      gint              x,
@@ -503,6 +508,7 @@
   widget_class->style_set = gtk_notebook_style_set;
   widget_class->drag_begin = gtk_notebook_drag_begin;
   widget_class->drag_motion = gtk_notebook_drag_motion;
+  widget_class->drag_leave = gtk_notebook_drag_leave;
   widget_class->drag_drop = gtk_notebook_drag_drop;
   widget_class->drag_data_get = gtk_notebook_drag_data_get;
   widget_class->drag_data_received = gtk_notebook_drag_data_received;
@@ -987,13 +993,12 @@
   priv->group_id = -1;
   priv->pressed_button = -1;
   priv->dnd_timer = 0;
+  priv->switch_tab_timer = 0;
   priv->source_targets = gtk_target_list_new (notebook_targets,
 					      G_N_ELEMENTS (notebook_targets));
 
-  gtk_drag_dest_set (GTK_WIDGET (notebook),
-		     GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
-		     notebook_targets, G_N_ELEMENTS (notebook_targets),
-                     GDK_ACTION_MOVE);
+  /* accept all targets, drag_motion handler determines what to do */
+  gtk_drag_dest_set (GTK_WIDGET (notebook), 0, NULL, 0, 0);
 }
 
 static gboolean
@@ -1278,7 +1283,13 @@
       gtk_target_list_unref (priv->source_targets);
       priv->source_targets = NULL;
     }
-  
+
+  if (priv->switch_tab_timer)
+    {
+      g_source_remove (priv->switch_tab_timer);
+      priv->switch_tab_timer = 0;
+    }
+
   GTK_OBJECT_CLASS (parent_class)->destroy (object);
 }
 
@@ -2207,6 +2218,30 @@
   return TRUE;
 }
 
+static GList*
+get_tab_at_pos (GtkNotebook *notebook, gint x, gint y)
+{
+  GtkNotebookPage *page;
+  GList *children = notebook->children;
+
+  while (children)
+    {
+      page = children->data;
+      
+      if (GTK_WIDGET_VISIBLE (page->child) &&
+	  page->tab_label && GTK_WIDGET_MAPPED (page->tab_label) &&
+	  (x >= page->allocation.x) &&
+	  (y >= page->allocation.y) &&
+	  (x <= (page->allocation.x + page->allocation.width)) &&
+	  (y <= (page->allocation.y + page->allocation.height)))
+	return children;
+
+      children = children->next;
+    }
+
+  return NULL;
+}
+
 static gboolean
 gtk_notebook_button_press (GtkWidget      *widget,
 			   GdkEventButton *event)
@@ -2214,9 +2249,8 @@
   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
   GtkNotebookPrivate *priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
   GtkNotebookPage *page;
-  GList *children;
+  GList *tab;
   GtkNotebookArrow arrow;
-  gint num;
   gint x, y;
 
   if (event->type != GDK_BUTTON_PRESS || !notebook->children ||
@@ -2241,41 +2275,28 @@
     return FALSE;
 
   notebook->button = event->button;
-  num = 0;
-  children = notebook->children;
 
-  while (children)
+  if ((tab = get_tab_at_pos (notebook, x, y)) != NULL)
     {
-      page = children->data;
-      
-      if (GTK_WIDGET_VISIBLE (page->child) &&
-	  page->tab_label && GTK_WIDGET_MAPPED (page->tab_label) &&
-	  (x >= page->allocation.x) &&
-	  (y >= page->allocation.y) &&
-	  (x <= (page->allocation.x + page->allocation.width)) &&
-	  (y <= (page->allocation.y + page->allocation.height)))
-	{
-	  gboolean page_changed = page != notebook->cur_page;
-	  gboolean was_focus = gtk_widget_is_focus (widget);
+      gboolean page_changed, was_focus;
+
+      page = tab->data;
+      page_changed = page != notebook->cur_page;
+      was_focus = gtk_widget_is_focus (widget);
 	  
-	  gtk_notebook_switch_focus_tab (notebook, children);
-	  gtk_widget_grab_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);
+      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->pressed_button = event->button;
-	      gtk_grab_add (widget);
-	    }
-
-	  break;
+      /* save press to possibly begin a drag */
+      if (page->reorderable || page->detachable)
+	{
+	  priv->during_detach = FALSE;
+	  priv->pressed_button = event->button;
+	  gtk_grab_add (widget);
 	}
-      children = children->next;
-      num++;
     }
 
   return TRUE;
@@ -2812,30 +2833,138 @@
 }
 
 static gboolean
+check_source_is_notebook_tab (GdkDragContext *context)
+{
+  GdkAtom target;
+  GList *targets;
+
+  /* we only want notebooks in the same app */
+  if (!gtk_drag_get_source_widget (context))
+    return FALSE;
+
+  target = gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB");
+  targets = context->targets;
+
+  while (targets)
+    {
+      if (targets->data == target)
+	return TRUE;
+
+      targets = targets->next;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+gtk_notebook_switch_tab_timeout (gpointer data)
+{
+  GtkNotebook *notebook;
+  GtkNotebookPrivate *priv;
+  GList *tab;
+  gint x, y;
+
+  notebook = GTK_NOTEBOOK (data);
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+
+  priv->switch_tab_timer = 0;
+  x = priv->mouse_x;
+  y = priv->mouse_y;
+
+  if ((tab = get_tab_at_pos (notebook, x, y)) != NULL)
+    {
+      /* FIXME: hack, we don't want the
+       * focus to move fom the source widget
+       */
+      notebook->child_has_focus = FALSE;
+      gtk_notebook_switch_focus_tab (notebook, tab);
+    }
+
+  return FALSE;
+}
+
+static gboolean
 gtk_notebook_drag_motion (GtkWidget      *widget,
 			  GdkDragContext *context,
 			  gint            x,
 			  gint            y,
 			  guint           time)
 {
-  GtkWidget *source_widget;
-  gint widget_group, source_widget_group;
-  
-  source_widget = gtk_drag_get_source_widget (context);
-  
-  g_assert (source_widget);
+  GtkNotebookPrivate *priv;
+  GdkRectangle position;
+  GtkSettings *settings;
+  guint timeout;
+
+  if (check_source_is_notebook_tab (context))
+    {
+      gint widget_group, source_widget_group;
+      GtkWidget *source_widget;
+
+      source_widget = gtk_drag_get_source_widget (context);
+
+      g_assert (source_widget);
+
+      widget_group = gtk_notebook_get_group_id (GTK_NOTEBOOK (widget));
+      source_widget_group = gtk_notebook_get_group_id (GTK_NOTEBOOK (source_widget));
 
-  widget_group = gtk_notebook_get_group_id (GTK_NOTEBOOK (widget));
-  source_widget_group = gtk_notebook_get_group_id (GTK_NOTEBOOK (source_widget));
+      if (widget_group != -1 &&
+	  source_widget_group != -1 &&
+	  widget_group == source_widget_group)
+	{
+	  gdk_drag_status (context, GDK_ACTION_MOVE, time);
+	  return TRUE;
+	}
+    }
 
-  if (widget_group != -1 &&
-      source_widget_group != -1 &&
-      widget_group == source_widget_group)
-    return TRUE;
+  priv = GTK_NOTEBOOK_GET_PRIVATE (widget);
+  x += widget->allocation.x;
+  y += widget->allocation.y;
+
+  if (gtk_notebook_get_event_window_position (GTK_NOTEBOOK (widget), &position) &&
+      x >= position.x && x <= position.x + position.width &&
+      y >= position.y && y <= position.y + position.height)
+    {
+      priv->mouse_x = x;
+      priv->mouse_y = y;
+
+      if (!priv->switch_tab_timer)
+	{
+          settings = gtk_widget_get_settings (widget);
+
+          g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
+	  priv->switch_tab_timer = g_timeout_add (timeout * SWITCH_TAB_DELAY_FACTOR,
+						  (GSourceFunc) gtk_notebook_switch_tab_timeout,
+						  widget);
+	}
+    }
+  else
+    {
+      if (priv->switch_tab_timer)
+	{
+	  g_source_remove (priv->switch_tab_timer);
+	  priv->switch_tab_timer = 0;
+	}
+    }
 
   gdk_drag_status (context, 0, time);
 
-  return FALSE;
+  return TRUE;
+}
+
+static void
+gtk_notebook_drag_leave (GtkWidget      *widget,
+			 GdkDragContext *context,
+			 guint           time)
+{
+  GtkNotebookPrivate *priv;
+
+  priv = GTK_NOTEBOOK_GET_PRIVATE (widget);
+
+  if (priv->switch_tab_timer)
+    {
+      g_source_remove (priv->switch_tab_timer);
+      priv->switch_tab_timer = 0;
+    }
 }
 
 static gboolean
@@ -2847,9 +2976,12 @@
 {
   GdkAtom target;
 
-  target = gtk_drag_dest_find_target (widget, context, NULL);
-
-  if (target == GDK_NONE)
+  if (check_source_is_notebook_tab (context))
+    {
+      target = gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB");
+      gtk_drag_get_data (widget, context, target, time);
+    }
+  else
     gtk_drag_finish (context, FALSE, FALSE, time);
 
   return TRUE;
