long standing dnd bug fixes



hi owen,

i got annoyed enough by my dnd-bug workaround code in beast to actually
sat down and fix the mroe serious bugs in gtkdnd.c.
the patch to gtk_drag_find_widget() is appended, it adresses:
1) dnd code messing up if the widget tree changes during motion (happens
   if a target-indicator widget is added/removed under the drag icon)
2) wrong drop position calculations within scrolled windows that have
   non-0 scroll offsets (and probably other cases where parent->window !=
   gtk_widget_get_parent_window (child)).

btw, i suspect my new (like your old) code to still be broken for
handleboxes, since the position calculation relies on parent->window
and widget->window to form a chain via gdk_window_get_parent().

---
ciaoTJ

Index: gtkdnd.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkdnd.c,v
retrieving revision 1.74
diff -u -p -u -r1.74 gtkdnd.c
--- gtkdnd.c	2001/12/09 22:08:26	1.74
+++ gtkdnd.c	2002/01/02 14:06:05
@@ -1206,6 +1206,16 @@ gtk_drag_selection_received (GtkWidget  
   gtk_drag_release_ipc_widget (widget);
 }
 
+static void
+prepend_and_ref_drawable (GtkWidget *widget,
+			  gpointer   data)
+{
+  GSList **slist_p = data;
+
+  if (GTK_WIDGET_DRAWABLE (widget))
+    *slist_p = g_slist_prepend (*slist_p, gtk_widget_ref (widget));
+}
+
 /*************************************************************
  * gtk_drag_find_widget:
  *     Recursive callback used to locate widgets for 
@@ -1220,6 +1230,7 @@ gtk_drag_find_widget (GtkWidget       *w
 		      GtkDragFindData *data)
 {
   GtkAllocation new_allocation;
+  GtkDragFindData new_data;
   gint x_offset = 0;
   gint y_offset = 0;
 
@@ -1234,66 +1245,102 @@ gtk_drag_find_widget (GtkWidget       *w
    * but within the allocation are not counted. This is consistent
    * with the way we highlight drag targets.
    */
-  if (!GTK_WIDGET_NO_WINDOW (widget))
-    {
-      new_allocation.x = 0;
-      new_allocation.y = 0;
-    }
-  
+
+  /* data.x/y are relative to widget->parent->window,
+   * translate and constrain to widget->window
+   */
   if (widget->parent)
     {
-      GdkWindow *window = widget->window;
+      GSList *slist, *wlist = NULL;
+      GdkWindow *pwindow = gtk_widget_get_parent_window (widget);
+      GdkWindow *window = pwindow;
+      gint tx, ty, twidth, theight;
+
+      /* translate offset relative to widget's parent window */
       while (window != widget->parent->window)
 	{
-	  gint tx, ty, twidth, theight;
-	  gdk_window_get_size (window, &twidth, &theight);
+	  wlist = g_slist_prepend (wlist, window);
+	  window = gdk_window_get_parent (window);
+	}
+      for (slist = wlist; slist; slist = slist->next)
+	{
+	  window = slist->data;
+	  gdk_window_get_position (window, &tx, &ty);
+	  x_offset += tx;
+	  y_offset += ty;
+	}
+      g_slist_free (wlist);
+      wlist = NULL;
 
-	  if (new_allocation.x < 0)
+      /* translate and constrain offset and allocation relative to widget->window */
+      window = widget->window;
+      while (window != pwindow)
+	{
+	  wlist = g_slist_prepend (wlist, window);
+	  window = gdk_window_get_parent (window);
+	}
+      for (slist = wlist; slist; slist = slist->next)
+	{
+	  window = slist->data;
+	  gdk_window_get_position (window, &tx, &ty);
+	  gdk_window_get_size (window, &twidth, &theight);
+	  x_offset += tx;
+	  y_offset += ty;
+	  if (tx > new_allocation.x)
 	    {
-	      new_allocation.width += new_allocation.x;
+	      new_allocation.width -= tx - new_allocation.x;
 	      new_allocation.x = 0;
+	      new_allocation.width = MIN (new_allocation.width, twidth);
+	    }
+	  else
+	    {
+	      new_allocation.x -= tx;
+	      new_allocation.width = MIN (new_allocation.width, twidth - new_allocation.x);
 	    }
-	  if (new_allocation.y < 0)
+	  if (ty > new_allocation.y)
 	    {
-	      new_allocation.height += new_allocation.y;
+	      new_allocation.height -= ty - new_allocation.y;
 	      new_allocation.y = 0;
+	      new_allocation.height = MIN (new_allocation.height, theight);
 	    }
-	  if (new_allocation.x + new_allocation.width > twidth)
-	    new_allocation.width = twidth - new_allocation.x;
-	  if (new_allocation.y + new_allocation.height > theight)
-	    new_allocation.height = theight - new_allocation.y;
-
-	  gdk_window_get_position (window, &tx, &ty);
-	  new_allocation.x += tx;
-	  x_offset += tx;
-	  new_allocation.y += ty;
-	  y_offset += ty;
-	  
-	  window = gdk_window_get_parent (window);
+	  else
+	    {
+	      new_allocation.y -= ty;
+	      new_allocation.height = MIN (new_allocation.height, theight - new_allocation.y);
+	    }
 	}
+      g_slist_free (wlist);
     }
 
+  new_data = *data;
+  new_data.x -= x_offset;
+  new_data.y -= y_offset;
+  new_data.found = FALSE;
+  new_data.toplevel = FALSE;
+  
   if (data->toplevel ||
-      ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
-       (data->x < new_allocation.x + new_allocation.width) && 
-       (data->y < new_allocation.y + new_allocation.height)))
+      ((new_data.x >= new_allocation.x) && (new_data.y >= new_allocation.y) &&
+       (new_data.x < new_allocation.x + new_allocation.width) && 
+       (new_data.y < new_allocation.y + new_allocation.height)))
     {
       /* First, check if the drag is in a valid drop site in
        * one of our children 
        */
       if (GTK_IS_CONTAINER (widget))
 	{
-	  GtkDragFindData new_data = *data;
-	  
-	  new_data.x -= x_offset;
-	  new_data.y -= y_offset;
-	  new_data.found = FALSE;
-	  new_data.toplevel = FALSE;
-	  
-	  gtk_container_forall (GTK_CONTAINER (widget),
-				(GtkCallback)gtk_drag_find_widget,
-				&new_data);
-	  
+	  GSList *slist, *children = NULL;
+
+	  /* need to reference children temoprarily to cope with
+	   * widget tree changes during motion
+	   */
+	  gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_drawable, &children);
+	  for (slist = children; slist; slist = slist->next)
+	    {
+	      if (!new_data.found && GTK_WIDGET_DRAWABLE (slist->data))
+		gtk_drag_find_widget (slist->data, &new_data);
+	      gtk_widget_unref (slist->data);
+	    }
+	  g_slist_free (children);
 	  data->found = new_data.found;
 	}
 
@@ -1306,16 +1353,14 @@ gtk_drag_find_widget (GtkWidget       *w
 	{
 	  data->found = data->callback (widget,
 					data->context,
-					data->x - new_allocation.x,
-					data->y - new_allocation.y,
+					new_data.x - new_allocation.x,
+					new_data.y - new_allocation.y,
 					data->time);
 	  /* If so, send a "drag_leave" to the last widget */
 	  if (data->found)
 	    {
 	      if (data->info->widget && data->info->widget != widget)
-		{
-		  gtk_drag_dest_leave (data->info->widget, data->context, data->time);
-		}
+		gtk_drag_dest_leave (data->info->widget, data->context, data->time);
 	      data->info->widget = widget;
 	    }
 	}




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]