7 files to edit
	2006/06/13 17:57:05	1.1
+++ gtk/gtkdnd.c	2006/06/15 04:00:11
@@ -49,8 +49,10 @@ static GSList *source_widgets = NULL;
 
 typedef struct _GtkDragSourceSite GtkDragSourceSite;
 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
+typedef struct _GtkDragSourceTransfer GtkDragSourceTransfer;
 typedef struct _GtkDragDestSite GtkDragDestSite;
 typedef struct _GtkDragDestInfo GtkDragDestInfo;
+typedef struct _GtkDragDestTransfer GtkDragDestTransfer;
 typedef struct _GtkDragAnim GtkDragAnim;
 typedef struct _GtkDragFindData GtkDragFindData;
 
@@ -66,6 +68,7 @@ struct _GtkDragSourceSite 
 {
   GdkModifierType    start_button_mask;
   GtkTargetList     *target_list;        /* Targets for drag data */
+  GtkTargetList     *delayed_target_list; /* Targets for Delayed Transfer */
   GdkDragAction      actions;            /* Possible actions */
 
   /* Drag icon */
@@ -90,6 +93,7 @@ struct _GtkDragSourceInfo 
 {
   GtkWidget         *widget;
   GtkTargetList     *target_list; /* Targets for drag data */
+  GtkTargetList     *delayed_target_list; /* Targets for Delayed Transfer */
   GdkDragAction      possible_actions; /* Actions allowed by source */
   GdkDragContext    *context;	  /* drag context */
   GtkWidget         *icon_window; /* Window for drag */
@@ -125,6 +129,7 @@ struct _GtkDragDestSite 
 {
   GtkDestDefaults    flags;
   GtkTargetList     *target_list;
+  GtkTargetList     *delayed_target_list;
   GdkDragAction      actions;
   GdkWindow         *proxy_window;
   GdkDragProtocol    proxy_protocol;
@@ -149,6 +154,21 @@ struct _GtkDragDestInfo 
   gint               drop_x, drop_y; /* Position of drop */
 };
 
+struct _GtkDragDestTransfer
+{
+  GtkWidget         *widget;	   /* Widget in which drag is in */
+  guint              ordinal;
+  GdkAtom            property;
+  GtkWidget         *drop_widget;
+  GtkSelectionData  *selection_data;
+  guint              time;
+  GdkDragContext    *context;
+  GtkDragDestSite   *site;
+  GtkDragDestInfo   *info;
+  guint              delayed_transfer_progress_id;
+  guint              transfer_timeout; /* Timeout for aborting transfer */
+};
+
 #define DROP_ABORT_TIME 300000
 
 #define ANIM_STEP_TIME 50
@@ -181,7 +201,8 @@ struct _GtkDragFindData 
 enum {
   TARGET_MOTIF_SUCCESS = 0x40000000,
   TARGET_MOTIF_FAILURE,
-  TARGET_DELETE
+  TARGET_DELETE,
+  TARGET_XDND_DELAYED_TRANSFER
 };
 
 /* Drag icons */
@@ -209,10 +230,21 @@ static gboolean      gtk_drag_highlight_
 					  	  GdkEventExpose *event,
 						  gpointer        data);
 
+static void     gtk_drag_transfer_selection_received (GtkWidget   *widget,
+						 GtkSelectionData *selection_data,
+						 guint             time,
+						 gpointer          data);
 static void     gtk_drag_selection_received     (GtkWidget        *widget,
 						 GtkSelectionData *selection_data,
 						 guint             time,
 						 gpointer          data);
+static gboolean gtk_drag_selection_received_internal (GtkWidget   *widget,
+						 GtkSelectionData *selection_data,
+						 guint             time,
+						 GdkDragContext   *context,
+						 GtkDragDestInfo  *info,
+						 GtkDragDestTransfer *transfer,
+						 GtkWidget        *drop_widget);
 static void     gtk_drag_find_widget            (GtkWidget        *widget,
 						 GtkDragFindData  *data);
 static void     gtk_drag_proxy_begin            (GtkWidget        *widget,
@@ -809,6 +841,7 @@ gtk_drag_get_data (GtkWidget      *widge
 		   guint32         time)
 {
   GtkWidget *selection_widget;
+  GList *tmp_list;
 
   g_return_if_fail (GTK_IS_WIDGET (widget));
   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
@@ -824,6 +857,15 @@ gtk_drag_get_data (GtkWidget      *widge
 
   g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
 
+  for (tmp_list = context->delayed_targets; tmp_list; tmp_list = tmp_list->next)
+    if (target == GDK_POINTER_TO_ATOM (tmp_list->data))
+      {
+	context->delayed_target = target;
+	context->start_time = time;
+	target = gdk_atom_intern_static_string ("XdndDelayedTransfer");
+	break;
+      }
+
   gtk_selection_convert (selection_widget,
 			 gdk_drag_get_selection (context),
 			 target,
@@ -1082,6 +1124,7 @@ gtk_drag_dest_set   (GtkWidget          
     site->target_list = gtk_target_list_new (targets, n_targets);
   else
     site->target_list = NULL;
+  site->delayed_target_list = NULL;
   site->actions = actions;
   site->do_proxy = FALSE;
   site->proxy_window = NULL;
@@ -1119,6 +1162,7 @@ gtk_drag_dest_set_proxy (GtkWidget      
   site->flags = 0;
   site->have_drag = FALSE;
   site->target_list = NULL;
+  site->delayed_target_list = NULL;
   site->actions = 0;
   site->proxy_window = proxy_window;
   if (proxy_window)
@@ -1492,9 +1536,18 @@ gtk_drag_dest_find_target (GtkWidget    
   while (tmp_target)
     {
       GtkTargetPair *pair = tmp_target->data;
-      tmp_source = context->targets;
-      while (tmp_source)
-	{
+      if (pair->flags & GTK_TARGET_OFFER_DELAYED_TRANSFER)
+	for (tmp_source = context->delayed_targets; tmp_source; tmp_source = tmp_source->next)
+	  if (tmp_source->data == GUINT_TO_POINTER (pair->target))
+	    {
+	      if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
+		  (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
+		return pair->target;
+	      else
+		break;
+	    }
+      if (!(pair->flags & GTK_TARGET_REQUIRE_DELAYED_TRANSFER))
+	for (tmp_source = context->targets; tmp_source; tmp_source = tmp_source->next)
 	  if (tmp_source->data == GUINT_TO_POINTER (pair->target))
 	    {
 	      if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
@@ -1503,8 +1556,6 @@ gtk_drag_dest_find_target (GtkWidget    
 	      else
 		break;
 	    }
-	  tmp_source = tmp_source->next;
-	}
       tmp_target = tmp_target->next;
     }
 
@@ -1512,6 +1563,113 @@ gtk_drag_dest_find_target (GtkWidget    
 }
 
 static void
+gtk_drag_dest_transfer_destroy (gpointer data,
+				gpointer user_data)
+{
+  GtkDragDestTransfer *transfer = (GtkDragDestTransfer *) data;
+  g_free (transfer->selection_data);
+  g_signal_handler_disconnect (transfer->context, 
+			       transfer->delayed_transfer_progress_id);
+  g_object_unref (transfer->context);
+  g_free (transfer->site);
+  g_free (transfer->info);
+  g_free (transfer);
+}
+
+static void 
+gtk_drag_dest_transfer_stack_destroy (gpointer data, 
+				      GObject  *where_the_object_was)
+{
+  GList *transfer_stack = g_object_get_data (where_the_object_was, 
+					     "gtk-dnd-delayed-transfer-dest");
+  g_object_weak_unref (where_the_object_was, 
+		       gtk_drag_dest_transfer_stack_destroy, NULL);
+  g_list_foreach (transfer_stack, gtk_drag_dest_transfer_destroy, NULL);
+  g_list_free (transfer_stack);
+}
+
+static void
+gtk_drag_dest_transfer_stack_remove (GtkDragDestTransfer *transfer)
+{
+  GtkWidget *widget = transfer->widget;
+  GList *transfer_stack;
+
+  transfer_stack = g_object_get_data (G_OBJECT (widget), 
+				      "gtk-dnd-delayed-transfer-dest");
+  transfer_stack = g_list_remove (transfer_stack, transfer);
+  if (transfer_stack == NULL)
+      g_object_weak_unref (G_OBJECT (widget), 
+			   gtk_drag_dest_transfer_stack_destroy, NULL);
+  g_object_set_data (G_OBJECT (widget), "gtk-dnd-delayed-transfer-dest",
+		     transfer_stack);
+  gtk_drag_dest_transfer_destroy (transfer, NULL);
+}
+
+static gboolean
+gtk_drag_delayed_transfer_abort_timeout (gpointer data)
+{
+  GtkDragDestTransfer *transfer = (GtkDragDestTransfer *) data;
+
+  GDK_THREADS_ENTER ();
+
+  transfer->transfer_timeout = 0;
+  gtk_drag_finish (transfer->context, FALSE, FALSE, transfer->time);
+  g_object_unref (transfer->drop_widget);
+  g_signal_handlers_disconnect_by_func (transfer->widget,
+					gtk_drag_transfer_selection_received,
+					data);
+  gtk_drag_dest_transfer_stack_remove (transfer);
+
+  GDK_THREADS_LEAVE ();
+  
+  return FALSE;
+}
+
+static void
+gtk_drag_delayed_transfer_progress (GdkDragContext *context,
+				    guint           progress,
+				    gpointer        data)
+{
+  GtkDragDestTransfer *transfer = (GtkDragDestTransfer *) data;
+  
+  if (transfer->transfer_timeout)
+    {
+      g_source_remove (transfer->transfer_timeout);
+      transfer->transfer_timeout = g_timeout_add (DROP_ABORT_TIME,
+						  gtk_drag_delayed_transfer_abort_timeout,
+						  transfer);
+    }
+  gtk_selection_reset_timeout (transfer->widget, 
+			       transfer->selection_data->selection, 
+			       transfer->selection_data->target, 
+			       transfer->property);
+}
+
+static void
+gtk_drag_transfer_selection_received (GtkWidget        *widget,
+				      GtkSelectionData *selection_data,
+				      guint             time,
+				      gpointer          data)
+{
+  GtkDragDestTransfer *transfer = (GtkDragDestTransfer *) data;
+
+  if (selection_data->selection != gdk_atom_intern_static_string ("XdndDelayedTransferSelection"))
+    return;
+  
+  if (transfer->time != time)
+    return;
+
+  if (gtk_drag_selection_received_internal (widget, selection_data, time,
+					    transfer->context, 
+					    transfer->info, 
+					    transfer, 
+					    transfer->drop_widget))
+    g_signal_handlers_disconnect_by_func (widget,
+					  gtk_drag_transfer_selection_received,
+					  data);
+}
+
+static void
 gtk_drag_selection_received (GtkWidget        *widget,
 			     GtkSelectionData *selection_data,
 			     guint             time,
@@ -1521,11 +1679,32 @@ gtk_drag_selection_received (GtkWidget  
   GtkDragDestInfo *info;
   GtkWidget *drop_widget;
 
+  if (selection_data->selection == gdk_atom_intern_static_string ("XdndDelayedTransferSelection"))
+    return;
+
   drop_widget = data;
 
   context = g_object_get_data (G_OBJECT (widget), "drag-context");
   info = gtk_drag_get_dest_info (context, FALSE);
 
+  if (gtk_drag_selection_received_internal (widget, selection_data, time,
+					    context, info, NULL, drop_widget))
+    g_signal_handlers_disconnect_by_func (widget,
+					  gtk_drag_selection_received,
+					  data);
+}
+
+static gboolean
+gtk_drag_selection_received_internal (GtkWidget           *widget,
+				      GtkSelectionData    *selection_data,
+				      guint                time,
+				      GdkDragContext      *context,
+				      GtkDragDestInfo     *info,
+				      GtkDragDestTransfer *transfer,
+				      GtkWidget           *drop_widget)
+{
+  gboolean transfer_delayed;
+
   if (info->proxy_data && 
       info->proxy_data->target == selection_data->target)
     {
@@ -1535,9 +1714,11 @@ gtk_drag_selection_received (GtkWidget  
 			      selection_data->data,
 			      selection_data->length);
       gtk_main_quit ();
-      return;
+      return FALSE;
     }
 
+  transfer_delayed = (selection_data->target == gdk_atom_intern_static_string ("XdndDelayedTransfer"));
+
   if (selection_data->target == gdk_atom_intern_static_string ("DELETE"))
     {
       gtk_drag_finish (context, TRUE, FALSE, time);
@@ -1547,11 +1728,70 @@ gtk_drag_selection_received (GtkWidget  
     {
       /* Do nothing */
     }
+  else if (transfer_delayed)
+    {
+      GtkDragDestSite *site;
+      GList *transfer_stack;
+      gchar *prop;
+      
+      site = g_object_get_data (G_OBJECT (drop_widget), "gtk-drag-dest");
+
+      transfer = g_new (GtkDragDestTransfer, 1);
+      transfer_stack = g_object_get_data (G_OBJECT (widget), 
+					  "gtk-dnd-delayed-transfer-dest");
+      transfer->ordinal = transfer_stack
+	? ((GtkDragDestTransfer *) transfer_stack->data)->ordinal + 1
+	: 0;
+      prop = g_strdup_printf ("GDK_DND_DELAYED_TRANSFER_%d", 
+			     transfer->ordinal);
+      transfer->property = gdk_atom_intern (prop, TRUE);
+      g_free (prop);
+      transfer->widget = widget;
+      transfer->drop_widget = drop_widget;
+      transfer->selection_data = g_memdup (selection_data, 
+					   sizeof (*selection_data));
+      transfer->time = context->start_time;
+      transfer->context = g_object_ref (context);
+      transfer->site = g_memdup (site, sizeof (*site));
+      transfer->info = g_memdup (info, sizeof (*info));
+      if (transfer_stack == NULL)
+	g_object_weak_ref (G_OBJECT (widget), 
+			   gtk_drag_dest_transfer_stack_destroy, NULL);
+      transfer_stack = g_list_prepend (transfer_stack, transfer);
+      g_object_set_data (G_OBJECT (widget), "gtk-dnd-delayed-transfer-dest",
+			 transfer_stack);
+
+      g_signal_connect (widget, "selection_received",
+			G_CALLBACK (gtk_drag_transfer_selection_received), 
+			transfer);
+      transfer->delayed_transfer_progress_id = 
+	g_signal_connect (context, "delayed_transfer_progress",
+			  G_CALLBACK (gtk_drag_delayed_transfer_progress), 
+			  transfer);
+      transfer->transfer_timeout = g_timeout_add (DROP_ABORT_TIME,
+						  gtk_drag_delayed_transfer_abort_timeout,
+						  transfer);
+
+      gtk_selection_request (transfer->widget, 
+			     context->source_window,
+			     gdk_atom_intern_static_string ("XdndDelayedTransferSelection"), 
+			     context->delayed_target, 
+			     transfer->property, 
+			     context->start_time);
+
+      gtk_drag_finish (context, 
+		       (selection_data->length >= 0),
+		       (context->action == GDK_ACTION_MOVE),
+		       time);
+    }
   else
     {
       GtkDragDestSite *site;
 
-      site = g_object_get_data (G_OBJECT (drop_widget), "gtk-drag-dest");
+      if (context->is_delayed)
+	site = transfer->site;
+      else
+	site = g_object_get_data (G_OBJECT (drop_widget), "gtk-drag-dest");
 
       if (site && site->target_list)
 	{
@@ -1590,15 +1830,23 @@ gtk_drag_selection_received (GtkWidget  
       
       g_object_unref (drop_widget);
     }
+  
+  if (context->is_delayed)
+    {
+      gtk_drag_dest_transfer_stack_remove (transfer);
+    }
+  else
+    {
+      g_object_set_data (G_OBJECT (widget), I_("drag-context"), NULL);
+      g_object_unref (context);
 
-  g_signal_handlers_disconnect_by_func (widget,
-					gtk_drag_selection_received,
-					data);
+      gtk_drag_release_ipc_widget (widget);
+    }
   
-  g_object_set_data (G_OBJECT (widget), I_("drag-context"), NULL);
-  g_object_unref (context);
+  if (transfer_delayed)
+    context->is_delayed = TRUE;
 
-  gtk_drag_release_ipc_widget (widget);
+  return TRUE;
 }
 
 static void
@@ -2144,6 +2392,23 @@ gtk_drag_dest_drop (GtkWidget	     *widg
  * Source side *
  ***************/
 
+static void
+gtk_drag_compile_target_lists (GList  *t_list,
+			       GList **targets,
+			       GList **delayed_targets)
+{
+  for (t_list = g_list_last (t_list); t_list; t_list = t_list->prev)
+    {
+      GtkTargetPair *pair = t_list->data;
+      if (!(pair->flags & GTK_TARGET_REQUIRE_DELAYED_TRANSFER))
+	*targets = g_list_prepend (*targets, 
+				   GINT_TO_POINTER (pair->target));
+      if (pair->flags & GTK_TARGET_OFFER_DELAYED_TRANSFER)
+	*delayed_targets = g_list_prepend (*delayed_targets, 
+					   GINT_TO_POINTER (pair->target));
+    }
+}
+
 /* Like GtkDragBegin, but also takes a GtkDragSourceSite,
  * so that we can set the icon from the source site information
  */
@@ -2157,7 +2422,7 @@ gtk_drag_begin_internal (GtkWidget      
 {
   GtkDragSourceInfo *info;
   GList *targets = NULL;
-  GList *tmp_list;
+  GList *delayed_targets = NULL;
   guint32 time = GDK_CURRENT_TIME;
   GdkDragAction possible_actions, suggested_action;
   GdkDragContext *context;
@@ -2192,19 +2457,14 @@ gtk_drag_begin_internal (GtkWidget      
    */
   gtk_grab_add (ipc_widget);
   
-  tmp_list = g_list_last (target_list->list);
-  while (tmp_list)
-    {
-      GtkTargetPair *pair = tmp_list->data;
-      targets = g_list_prepend (targets, 
-				GINT_TO_POINTER (pair->target));
-      tmp_list = tmp_list->prev;
-    }
+  gtk_drag_compile_target_lists (target_list->list, &targets, &delayed_targets);
 
   source_widgets = g_slist_prepend (source_widgets, ipc_widget);
 
   context = gdk_drag_begin (ipc_widget->window, targets);
+  gdk_drag_set_targets (ipc_widget->window, context, targets, delayed_targets);
   g_list_free (targets);
+  g_list_free (delayed_targets);
   
   info = gtk_drag_get_source_info (context, TRUE);
   
@@ -2361,6 +2621,32 @@ gtk_drag_begin (GtkWidget         *widge
 				  actions, button, event);
 }
 
+/**
+ * gtk_drag_update_targets:
+ * @widget: the source widget.
+ * @context: the #GtkDragContext returned by gtk_drag_begin() 
+ * or ::drag_begin
+ * @targets: The targets (data formats) in which the source 
+ * can provide the data.
+ *
+ * While a drag is ongoing, changes the targets the source can 
+ * provide.
+ **/
+void
+gtk_drag_update_targets   (GtkWidget         *widget,
+			   GdkDragContext    *context,
+			   GtkTargetList     *target_list)
+{
+  GtkWidget *ipc_widget;
+  GList *targets = NULL, *delayed_targets = NULL;
+  
+  ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
+  gtk_drag_compile_target_lists (target_list->list, &targets, &delayed_targets);
+  gdk_drag_set_targets (ipc_widget->window, context, targets, delayed_targets);
+  g_list_free (targets);
+  g_list_free (delayed_targets);
+}
+
 /*************************************************************
  * gtk_drag_source_set:
  *     Register a drop site, and possibly add default behaviors.
@@ -3376,6 +3662,10 @@ gtk_drag_source_check_selection (GtkDrag
 			    selection,
 			    gdk_atom_intern_static_string ("DELETE"),
 			    TARGET_DELETE);
+  gtk_selection_add_target (info->ipc_widget,
+			    selection,
+			    gdk_atom_intern_static_string ("XdndDelayedTransfer"),
+			    TARGET_XDND_DELAYED_TRANSFER);
 }
 
 /*************************************************************
@@ -3633,6 +3923,17 @@ gtk_drag_selection_get (GtkWidget       
 	  gtk_main ();
 	  info->proxy_dest->proxy_data = NULL;
 	}
+      else if (sel_info == TARGET_XDND_DELAYED_TRANSFER)
+	{
+	  info->context->is_delayed = TRUE;
+	  info->context->start_time = time;
+	  gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING, 
+				  8, NULL, 0);
+	  if (info->drop_timeout)
+	    g_source_remove (info->drop_timeout);
+	  info->drop_timeout = 0;
+	  gtk_drag_source_check_selection (info, gdk_atom_intern_static_string ("XdndDelayedTransferSelection"), GDK_CURRENT_TIME);
+	}
       else
 	{
 	  if (gtk_target_list_find (info->target_list, 
--- gtk/gtkdnd.h	2006/06/13 17:57:05	1.1
+++ gtk/gtkdnd.h	2006/06/14 09:09:37
@@ -46,7 +46,10 @@ typedef enum {
  */
 typedef enum {
   GTK_TARGET_SAME_APP = 1 << 0,    /*< nick=same-app >*/
-  GTK_TARGET_SAME_WIDGET = 1 << 1  /*< nick=same-widget >*/
+  GTK_TARGET_SAME_WIDGET = 1 << 1, /*< nick=same-widget >*/
+  /* on both sides */
+  GTK_TARGET_OFFER_DELAYED_TRANSFER = 1 << 2,
+  GTK_TARGET_REQUIRE_DELAYED_TRANSFER = 1 << 3
 } GtkTargetFlags;
 
 /* Destination side */
@@ -130,6 +133,10 @@ GdkDragContext *gtk_drag_begin (GtkWidge
 				gint               button,
 				GdkEvent          *event);
 
+void gtk_drag_update_targets   (GtkWidget         *widget,
+				GdkDragContext    *context,
+				GtkTargetList     *target_list);
+
 /* Set the image being dragged around
  */
 void gtk_drag_set_icon_widget (GdkDragContext *context,
--- gtk/gtkselection.c	2006/06/13 17:57:05	1.1
+++ gtk/gtkselection.c	2006/06/15 03:55:33
@@ -142,6 +142,7 @@ struct _GtkRetrievalInfo
   GtkWidget *widget;
   GdkAtom selection;		/* Selection being retrieved. */
   GdkAtom target;		/* Form of selection that we requested */
+  GdkAtom property;		/* Property used to retrieve selection */
   guint32 idle_time;		/* Number of seconds since we last heard
 				   from selection owner */
   guchar   *buffer;		/* Buffer in which to accumulate results */
@@ -1009,6 +1010,35 @@ gtk_selection_convert (GtkWidget *widget
 		       GdkAtom	  target,
 		       guint32	  time_)
 {
+  gtk_selection_request (widget, NULL, selection, target, GDK_NONE, time_);
+}
+
+/**
+ * gtk_selection_request:
+ * @widget: The widget which acts as requestor
+ * @window: The window to request the selection from
+ * @selection: Which selection to get
+ * @target: Form of information desired (e.g., STRING)
+ * @property: Property to store the retrieved information in
+ * @time_: Time of request (usually of triggering event)
+       In emergency, you could use #GDK_CURRENT_TIME
+ * 
+ * Requests the contents of a selection from a specified
+ * window, which may not be the selection owner. When received,
+ * a "selection_received" signal will be generated.
+ * 
+ * Return value: %TRUE if requested succeeded. %FALSE if we could not process
+ *          request. (e.g., there was already a request in process for
+ *          this widget).
+ **/
+gboolean
+gtk_selection_request (GtkWidget *widget, 
+		       GdkWindow *window,
+		       GdkAtom	  selection, 
+		       GdkAtom	  target,
+		       GdkAtom	  property,
+		       guint32	  time_)
+{
   GtkRetrievalInfo *info;
   GList *tmp_list;
   GdkWindow *owner_window;
@@ -1029,13 +1059,16 @@ gtk_selection_convert (GtkWidget *widget
      we could support multiple retrievals for different selections.
      This might be useful for DND. */
   
-  tmp_list = current_retrievals;
-  while (tmp_list)
+  if (property == GDK_NONE)
     {
-      info = (GtkRetrievalInfo *)tmp_list->data;
-      if (info->widget == widget)
-	return FALSE;
-      tmp_list = tmp_list->next;
+      tmp_list = current_retrievals;
+      while (tmp_list)
+	{
+	  info = (GtkRetrievalInfo *)tmp_list->data;
+	  if (info->widget == widget)
+	    return FALSE;
+	  tmp_list = tmp_list->next;
+	}
     }
   
   info = g_new (GtkRetrievalInfo, 1);
@@ -1043,6 +1076,7 @@ gtk_selection_convert (GtkWidget *widget
   info->widget = widget;
   info->selection = selection;
   info->target = target;
+  info->property = property;
   info->idle_time = 0;
   info->buffer = NULL;
   info->offset = -1;
@@ -1051,9 +1085,13 @@ gtk_selection_convert (GtkWidget *widget
      procedure directly to avoid deadlocks with INCR. */
 
   display = gtk_widget_get_display (widget);
-  owner_window = gdk_selection_owner_get_for_display (display, selection);
+  if (window == NULL)
+    owner_window = gdk_selection_owner_get_for_display (display, selection);
+  else
+    owner_window = window;
   
-  if (owner_window != NULL)
+  if ((window == NULL && owner_window != NULL) ||
+      (window != NULL && gdk_window_get_window_type (window) != GDK_WINDOW_FOREIGN))
     {
       GtkWidget *owner_widget;
       GtkSelectionData selection_data;
@@ -1089,12 +1127,40 @@ gtk_selection_convert (GtkWidget *widget
   /* Otherwise, we need to go through X */
   
   current_retrievals = g_list_append (current_retrievals, info);
-  gdk_selection_convert (widget->window, selection, target, time_);
+  gdk_selection_request (widget->window, window, selection, target, property,
+			 time_);
   g_timeout_add (1000, (GSourceFunc) gtk_selection_retrieval_timeout, info);
   
   return TRUE;
 }
 
+/**
+ * gtk_selection_reset_timeout:
+ * @widget: The widget which acts as requestor
+ * @selection: Which selection to get
+ * @target: Form of information desired (e.g., STRING)
+ * @property: Property to store the retrieved information in
+ * 
+ * Resets the retrieval timeout on current retrievals that match the @widget,
+ * @selection, @target and @property.
+ **/
+void
+gtk_selection_reset_timeout (GtkWidget            *widget,
+			     GdkAtom               selection,
+			     GdkAtom               target,
+			     GdkAtom               property)
+{
+  GList *tmp_list = current_retrievals;
+  while (tmp_list)
+    {
+      GtkRetrievalInfo *info = (GtkRetrievalInfo *)tmp_list->data;
+      if (info->widget == widget && info->selection == selection &&
+	  info->property == property)
+	info->idle_time = 0;
+      tmp_list = tmp_list->next;
+    }
+}
+
 
 /**
  * gtk_selection_data_set:
@@ -2525,8 +2591,8 @@ _gtk_selection_notify (GtkWidget	       
     return FALSE;
 
   if (event->property != GDK_NONE)
-    length = gdk_selection_property_get (widget->window, &buffer, 
-					 &type, &format);
+    length = gdk_selection_named_property_get (widget->window, &buffer, 
+					       &type, &format, event->property);
   else
     length = 0; /* silence gcc */
   
@@ -2600,8 +2666,7 @@ _gtk_selection_property_notify (GtkWidge
   g_return_val_if_fail (event != NULL, FALSE);
 
 #if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11)
-  if ((event->state != GDK_PROPERTY_NEW_VALUE) ||  /* property was deleted */
-      (event->atom != gdk_atom_intern_static_string ("GDK_SELECTION"))) /* not the right property */
+  if (event->state != GDK_PROPERTY_NEW_VALUE)  /* property was deleted */
 #endif
     return FALSE;
   
@@ -2614,7 +2679,7 @@ _gtk_selection_property_notify (GtkWidge
   while (tmp_list)
     {
       info = (GtkRetrievalInfo *)tmp_list->data;
-      if (info->widget == widget)
+      if (info->widget == widget && info->property == event->atom)
 	break;
       tmp_list = tmp_list->next;
     }
@@ -2628,8 +2693,8 @@ _gtk_selection_property_notify (GtkWidge
   
   info->idle_time = 0;
   
-  length = gdk_selection_property_get (widget->window, &new_buffer, 
-				       &type, &format);
+  length = gdk_selection_named_property_get (widget->window, &new_buffer, 
+					     &type, &format, event->atom);
   gdk_property_delete (widget->window, event->atom);
   
   /* We could do a lot better efficiency-wise by paying attention to
--- gtk/gtkselection.h	2006/06/14 18:21:02	1.1
+++ gtk/gtkselection.h	2006/06/14 19:07:37
@@ -144,6 +144,16 @@ gboolean gtk_selection_convert       (Gt
 				      GdkAtom               selection,
 				      GdkAtom               target,
 				      guint32               time_);
+gboolean gtk_selection_request       (GtkWidget            *widget,
+				      GdkWindow            *window,
+				      GdkAtom               selection,
+				      GdkAtom               target,
+				      GdkAtom               property,
+				      guint32               time_);
+void     gtk_selection_reset_timeout (GtkWidget            *widget,
+				      GdkAtom               selection,
+				      GdkAtom               target,
+				      GdkAtom               property);
 void     gtk_selection_data_set      (GtkSelectionData     *selection_data,
 				      GdkAtom               type,
 				      gint                  format,
--- gdk/gdkselection.h	2006/06/13 17:57:05	1.1
+++ gdk/gdkselection.h	2006/06/15 03:36:19
@@ -82,10 +82,21 @@ void	   gdk_selection_convert   (GdkWind
 				    GdkAtom	  selection,
 				    GdkAtom	  target,
 				    guint32	  time_);
+void       gdk_selection_request   (GdkWindow    *requestor,
+				    GdkWindow    *window,
+				    GdkAtom	  selection,
+				    GdkAtom	  target,
+				    GdkAtom	  property,
+				    guint32	  time_);
 gboolean   gdk_selection_property_get (GdkWindow  *requestor,
 				       guchar	 **data,
 				       GdkAtom	  *prop_type,
 				       gint	  *prop_format);
+gboolean   gdk_selection_named_property_get (GdkWindow  *requestor,
+					     guchar	 **data,
+					     GdkAtom	  *prop_type,
+					     gint	  *prop_format,
+					     GdkAtom       property);
 
 #ifndef GDK_MULTIHEAD_SAFE
 void	   gdk_selection_send_notify (guint32	    requestor,
--- gdk/gdkdnd.h	2006/06/13 17:57:05	1.1
+++ gdk/gdkdnd.h	2006/06/14 07:02:59
@@ -86,6 +86,10 @@ struct _GdkDragContext {
 
   guint32 start_time;
 
+  GList *delayed_targets;
+  gboolean is_delayed;
+  GdkAtom delayed_target;
+
   /*< private >*/
   
   gpointer windowing_data;
@@ -124,6 +128,10 @@ GdkAtom          gdk_drag_get_selection 
 
 GdkDragContext * gdk_drag_begin      (GdkWindow      *window,
 				      GList          *targets);
+void		gdk_drag_set_targets (GdkWindow      *window,
+				      GdkDragContext *context,
+				      GList          *targets,
+				      GList          *delayed_targets);
 
 guint32 gdk_drag_get_protocol_for_display (GdkDisplay       *display,
 					   guint32           xid,
@@ -160,6 +168,8 @@ void            gdk_drag_drop        (Gd
 void            gdk_drag_abort       (GdkDragContext *context,
 				      guint32         time_);
 gboolean        gdk_drag_drop_succeeded (GdkDragContext *context);
+void            gdk_drag_delayed_transfer_notify (GdkDragContext *context,
+						  guint32         progress);
 
 G_END_DECLS
 
--- gdk/x11/gdkselection-x11.c	2006/06/13 17:57:05	1.1
+++ gdk/x11/gdkselection-x11.c	2006/06/15 03:35:14
@@ -227,6 +227,17 @@ gdk_selection_convert (GdkWindow *reques
 		       GdkAtom    target,
 		       guint32    time)
 {
+  gdk_selection_request (requestor, NULL, selection, target, GDK_NONE, time);
+}
+
+void
+gdk_selection_request   (GdkWindow *requestor,
+			 GdkWindow *window,
+			 GdkAtom    selection,
+			 GdkAtom    target,
+			 GdkAtom    property,
+			 guint32    time_)
+{
   GdkDisplay *display;
 
   g_return_if_fail (selection != GDK_NONE);
@@ -234,13 +245,38 @@ gdk_selection_convert (GdkWindow *reques
   if (GDK_WINDOW_DESTROYED (requestor))
     return;
 
+  if (property == GDK_NONE)
+    property = _gdk_selection_property;
+  
   display = GDK_WINDOW_DISPLAY (requestor);
 
-  XConvertSelection (GDK_WINDOW_XDISPLAY (requestor),
-		     gdk_x11_atom_to_xatom_for_display (display, selection),
-		     gdk_x11_atom_to_xatom_for_display (display, target),
-		     gdk_x11_atom_to_xatom_for_display (display, _gdk_selection_property), 
-		     GDK_WINDOW_XID (requestor), time);
+  if (window == NULL)
+    {
+      XConvertSelection (GDK_WINDOW_XDISPLAY (requestor),
+			 gdk_x11_atom_to_xatom_for_display (display, selection),
+			 gdk_x11_atom_to_xatom_for_display (display, target),
+			 gdk_x11_atom_to_xatom_for_display (display, property), 
+			 GDK_WINDOW_XID (requestor), time);
+    }
+  else
+    {
+      XSelectionRequestEvent xevent;
+
+      xevent.type = SelectionRequest;
+      xevent.serial = 0;
+      xevent.send_event = True;
+      xevent.display = GDK_WINDOW_XDISPLAY (requestor);
+      xevent.owner = GDK_WINDOW_XID (window);
+      xevent.requestor = GDK_WINDOW_XID (requestor);
+      xevent.selection = gdk_x11_atom_to_xatom_for_display (display, selection);
+      xevent.target = gdk_x11_atom_to_xatom_for_display (display, target);
+      xevent.property = gdk_x11_atom_to_xatom_for_display (display, property);
+      xevent.time = time_;
+
+      _gdk_send_xevent (gdk_drawable_get_display (GDK_DRAWABLE (requestor)), 
+			GDK_WINDOW_XID (window), False,
+			NoEventMask, (XEvent *) &xevent);
+    }
 }
 
 /**
@@ -269,6 +305,39 @@ gdk_selection_property_get (GdkWindow  *
 			    GdkAtom    *ret_type,
 			    gint       *ret_format)
 {
+  return gdk_selection_named_property_get (requestor, data, 
+					   ret_type, ret_format,
+					   _gdk_selection_property);
+}
+
+/**
+ * gdk_selection_named_property_get:
+ * @requestor: the window on which the data is stored
+ * @data: location to store a pointer to the retrieved data.
+       If the retrieval failed, %NULL we be stored here, otherwise, it
+       will be non-%NULL and the returned data should be freed with g_free()
+       when you are finished using it. The length of the
+       allocated memory is one more than the length
+       of the returned data, and the final byte will always
+       be zero, to ensure nul-termination of strings.
+ * @prop_type: location to store the type of the property.
+ * @prop_format: location to store the format of the property.
+ * @property: property the data is stored in
+ * 
+ * Retrieves selection data that was stored by the selection
+ * data in response to a call to gdk_selection_convert(). This function
+ * will not be used by applications, who should use the #GtkClipboard
+ * API instead.
+ * 
+ * Return value: the length of the retrieved data.
+ **/
+gint
+gdk_selection_named_property_get (GdkWindow  *requestor,
+				  guchar    **data,
+				  GdkAtom    *ret_type,
+				  gint       *ret_format,
+				  GdkAtom     property)
+{
   gulong nitems;
   gulong nbytes;
   gulong length = 0;		/* Quiet GCC */
@@ -293,7 +362,7 @@ gdk_selection_property_get (GdkWindow  *
      Otherwise there's no guarantee we'll win the race ... */
   if (XGetWindowProperty (GDK_DRAWABLE_XDISPLAY (requestor),
 			  GDK_DRAWABLE_XID (requestor),
-			  gdk_x11_atom_to_xatom_for_display (display, _gdk_selection_property),
+			  gdk_x11_atom_to_xatom_for_display (display, property),
 			  0, 0x1FFFFFFF /* MAXINT32 / 4 */, False, 
 			  AnyPropertyType, &prop_type, &prop_format,
 			  &nitems, &nbytes, &t) != Success)
--- gdk/x11/gdkdnd-x11.c	2006/06/13 17:57:05	1.1
+++ gdk/x11/gdkdnd-x11.c	2006/06/15 03:57:18
@@ -76,10 +76,12 @@ struct _GdkDragContextPrivateX11 {
   GdkDragAction old_action;	  /* The last action we sent to the source */
   GdkDragAction old_actions;	  /* The last actions we sent to the source */
   GdkDragAction xdnd_actions;     /* What is currently set in XdndActionList */
+  guint32 last_time;		/* Timestamp from last event */
 
   Window dest_xid;              /* The last window we looked up */
   Window drop_xid;            /* The (non-proxied) window that is receiving drops */
   guint xdnd_targets_set : 1;   /* Whether we've already set XdndTypeList */
+  guint xdnd_delayed_targets_set : 1; /* Whether we've already set XdndDelayedTransferTypeList */
   guint xdnd_actions_set : 1;   /* Whether we've already set XdndActionList */
   guint xdnd_have_actions : 1;  /* Whether an XdndActionList was provided */
   guint motif_targets_set : 1;  /* Whether we've already set motif initiator info */
@@ -118,6 +120,9 @@ static GdkFilterReturn xdnd_status_filte
 static GdkFilterReturn xdnd_finished_filter (GdkXEvent *xev,
 					     GdkEvent  *event,
 					     gpointer   data);
+static GdkFilterReturn xdnd_delayed_transfer_progress_filter (GdkXEvent *xev,
+							      GdkEvent  *event,
+							      gpointer   data);
 static GdkFilterReturn xdnd_drop_filter     (GdkXEvent *xev,
 					     GdkEvent  *event,
 					     gpointer   data);
@@ -139,9 +144,19 @@ const static struct {
   { "XdndPosition", xdnd_position_filter },
   { "XdndStatus",   xdnd_status_filter },
   { "XdndFinished", xdnd_finished_filter },
+  { "XdndDelayedTransferFinished", xdnd_finished_filter },
+  { "XdndDelayedTransferProgress", xdnd_delayed_transfer_progress_filter },
   { "XdndDrop",     xdnd_drop_filter },
 };
 	      
+enum
+{
+  DELAYED_TRANSFER_PROGRESS,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
 G_DEFINE_TYPE (GdkDragContext, gdk_drag_context, G_TYPE_OBJECT)
 
 static void
@@ -166,6 +181,13 @@ gdk_drag_context_class_init (GdkDragCont
   object_class->finalize = gdk_drag_context_finalize;
 
   g_type_class_add_private (object_class, sizeof (GdkDragContextPrivateX11));
+
+  signals[DELAYED_TRANSFER_PROGRESS] =
+    g_signal_new (g_intern_static_string ("delayed_transfer_progress"),
+		  G_OBJECT_CLASS_TYPE (object_class),
+		  G_SIGNAL_RUN_LAST, 0,
+		  NULL, NULL,
+		  g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
 }
 
 static void
@@ -245,7 +267,8 @@ static GdkDragContext *
 gdk_drag_context_find (GdkDisplay *display,
 		       gboolean    is_source,
 		       Window      source_xid,
-		       Window      dest_xid)
+		       Window      dest_xid,
+		       guint32     time_)
 {
   GList *tmp_list = contexts;
   GdkDragContext *context;
@@ -270,7 +293,8 @@ gdk_drag_context_find (GdkDisplay *displ
       if ((!context->is_source == !is_source) &&
 	  ((source_xid == None) || (context->source_window &&
 	    (GDK_DRAWABLE_XID (context->source_window) == source_xid))) &&
-	  ((dest_xid == None) || (context_dest_xid == dest_xid)))
+	  ((dest_xid == None) || (context_dest_xid == dest_xid)) && 
+	  ((time_ == GDK_CURRENT_TIME) || (context->start_time == time_)))
 	return context;
       
       tmp_list = tmp_list->next;
@@ -280,15 +304,15 @@ gdk_drag_context_find (GdkDisplay *displ
 }
 
 static void
-precache_target_list (GdkDragContext *context)
+precache_target_list_single (GdkDragContext *context, GList *list_targets)
 {
-  if (context->targets)
+  if (list_targets)
     {
       GPtrArray *targets = g_ptr_array_new ();
       GList *tmp_list;
       int i;
 
-      for (tmp_list = context->targets; tmp_list; tmp_list = tmp_list->next)
+      for (tmp_list = list_targets; tmp_list; tmp_list = tmp_list->next)
 	g_ptr_array_add (targets, gdk_atom_name (GDK_POINTER_TO_ATOM (tmp_list->data)));
 
       _gdk_x11_precache_atoms (GDK_WINDOW_DISPLAY (context->source_window),
@@ -302,6 +326,13 @@ precache_target_list (GdkDragContext *co
     }
 }
 
+static void
+precache_target_list (GdkDragContext *context)
+{
+  precache_target_list_single (context, context->targets);
+  precache_target_list_single (context, context->delayed_targets);
+}
+
 /* Utility functions */
 
 static void
@@ -1761,7 +1792,7 @@ motif_drag_status (GdkEvent *event,
   if (!display)
     return GDK_FILTER_REMOVE;
   
-  context = gdk_drag_context_find (display, TRUE, GDK_DRAWABLE_XID (event->any.window), None);
+  context = gdk_drag_context_find (display, TRUE, GDK_DRAWABLE_XID (event->any.window), None, GDK_CURRENT_TIME);
 
   if (context)
     {
@@ -1971,7 +2002,7 @@ xdnd_status_filter (GdkXEvent *xev,
 		       dest_window, action));
 
   display = gdk_drawable_get_display (event->any.window);
-  context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
+  context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window, GDK_CURRENT_TIME);
   
   if (context)
     {
@@ -2010,19 +2041,37 @@ xdnd_finished_filter (GdkXEvent *xev,
   guint32 dest_window = xevent->xclient.data.l[0];
   GdkDragContext *context;
   GdkDragContextPrivateX11 *private;
+  guint32 time_;
+  gboolean is_delayed;
 
   if (!event->any.window ||
       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
     return GDK_FILTER_CONTINUE;			/* Not for us */
   
-  GDK_NOTE (DND, 
-	    g_message ("XdndFinished: dest_window: %#x", dest_window));
-
   display = gdk_drawable_get_display (event->any.window);
-  context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
+
+  is_delayed = (gdk_x11_get_xatom_by_name_for_display (display, "XdndDelayedTransferFinished") == xevent->xclient.message_type);
+  if (is_delayed)
+    {
+      GDK_NOTE (DND, 
+		g_message ("XdndDelayedTransferFinished: dest_window: %#x", 
+			   dest_window));
+      time_ = xevent->xclient.data.l[3];
+    }
+  else
+    {
+      GDK_NOTE (DND, 
+		g_message ("XdndFinished: dest_window: %#x", dest_window));
+      time_ = GDK_CURRENT_TIME; /* FIXME? */
+    }
+
+  context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window, time_);
   
   if (context)
     {
+      if (context->is_delayed && !is_delayed)
+	return GDK_FILTER_REMOVE;
+
       private = PRIVATE_DATA (context);
       if (private->version == 5)
 	private->drop_failed = xevent->xclient.data.l[1] == 0;
@@ -2031,7 +2080,7 @@ xdnd_finished_filter (GdkXEvent *xev,
       event->dnd.context = context;
       g_object_ref (context);
 
-      event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
+      event->dnd.time = time_;
 
       return GDK_FILTER_TRANSLATE;
     }
@@ -2039,14 +2088,37 @@ xdnd_finished_filter (GdkXEvent *xev,
   return GDK_FILTER_REMOVE;
 }
 
+static GdkFilterReturn 
+xdnd_delayed_transfer_progress_filter (GdkXEvent *xev,
+				       GdkEvent  *event,
+				       gpointer   data)
+{
+  GdkDisplay *display;
+  XEvent *xevent = (XEvent *)xev;
+  guint32 dest_window = xevent->xclient.data.l[0];
+  GdkDragContext *context;
+  guint32 time_ = xevent->xclient.data.l[1];
+  guint32 progress = xevent->xclient.data.l[2];
+
+  display = gdk_drawable_get_display (event->any.window);
+
+  context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window, time_);
+  
+  if (context)
+    {
+      g_signal_emit (context, signals[DELAYED_TRANSFER_PROGRESS], 0, progress);
+    }
+
+  return GDK_FILTER_REMOVE;
+}
+
 static void
-xdnd_set_targets (GdkDragContext *context)
+xdnd_set_targets (GdkDragContext *context, GList *targets, const gchar *prop)
 {
-  GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
   Atom *atomlist;
-  GList *tmp_list = context->targets;
+  GList *tmp_list = targets;
   gint i;
-  gint n_atoms = g_list_length (context->targets);
+  gint n_atoms = g_list_length (targets);
   GdkDisplay *display = GDK_DRAWABLE_DISPLAY (context->source_window);
 
   atomlist = g_new (Atom, n_atoms);
@@ -2060,13 +2132,11 @@ xdnd_set_targets (GdkDragContext *contex
 
   XChangeProperty (GDK_DRAWABLE_XDISPLAY (context->source_window),
 		   GDK_DRAWABLE_XID (context->source_window),
-		   gdk_x11_get_xatom_by_name_for_display (display, "XdndTypeList"),
+		   gdk_x11_get_xatom_by_name_for_display (display, prop),
 		   XA_ATOM, 32, PropModeReplace,
 		   (guchar *)atomlist, n_atoms);
 
   g_free (atomlist);
-
-  private->xdnd_targets_set = 1;
 }
 
 static void
@@ -2257,10 +2327,19 @@ xdnd_send_enter (GdkDragContext *context
   GDK_NOTE(DND,
 	   g_message ("Sending enter source window %#lx XDND protocol version %d\n",
 		      GDK_DRAWABLE_XID (context->source_window), private->version));
-  if (g_list_length (context->targets) > 3)
+  if (g_list_length (context->targets) + (context->delayed_targets ? 1 : 0) > 3)
     {
       if (!private->xdnd_targets_set)
-	xdnd_set_targets (context);
+	{
+	  if (context->delayed_targets)
+	    context->targets = g_list_prepend (context->targets, 
+					       gdk_atom_intern_static_string ("XdndDelayedTransfer"));
+	  xdnd_set_targets (context, context->targets, "XdndTypeList");
+	  if (context->delayed_targets)
+	    context->targets = g_list_delete_link (context->targets, 
+						   context->targets);
+	  private->xdnd_targets_set = 1;
+	}
       xev.xclient.data.l[1] |= 1;
     }
   else
@@ -2275,6 +2354,13 @@ xdnd_send_enter (GdkDragContext *context
 	  tmp_list = tmp_list->next;
 	  i++;
 	}
+      if (context->delayed_targets)
+	xev.xclient.data.l[i] = gdk_x11_get_xatom_by_name_for_display (display, "XdndDelayedTransfer");
+    }
+  if (context->delayed_targets && !private->xdnd_delayed_targets_set)
+    {
+      xdnd_set_targets (context, context->delayed_targets, "XdndDelayedTransferTypeList");
+      private->xdnd_delayed_targets_set = 1;
     }
 
   if (!xdnd_send_xevent (context, context->dest_window,
@@ -2528,7 +2614,8 @@ xdnd_read_actions (GdkDragContext *conte
 
       source_context = gdk_drag_context_find (display, TRUE,
 					      GDK_DRAWABLE_XID (context->source_window),
-					      GDK_DRAWABLE_XID (context->dest_window));
+					      GDK_DRAWABLE_XID (context->dest_window), 
+					      GDK_CURRENT_TIME);
 
       if (source_context)
 	{
@@ -2640,7 +2727,12 @@ xdnd_precache_atoms (GdkDisplay *display
 	"XdndPosition",
 	"XdndSelection",
 	"XdndStatus",
-	"XdndTypeList"
+	"XdndTypeList",
+	"XdndDelayedTransfer",
+	"XdndDelayedTransferTypeList",
+	"XdndDelayedTransferSelection",
+	"XdndDelayedTransferProgress",
+	"XdndDelayedTransferFinished"
       };
 
       _gdk_x11_precache_atoms (display,
@@ -2666,10 +2758,13 @@ xdnd_enter_filter (GdkXEvent *xev,
   gulong nitems, after;
   guchar *data;
   Atom *atoms;
+  GdkAtom target;
 
   guint32 source_window;
   gboolean get_types;
   gint version;
+  gboolean get_delayed_types;
+  GdkAtom delayed_transfer;
 
   if (!event->any.window ||
       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
@@ -2721,6 +2816,8 @@ xdnd_enter_filter (GdkXEvent *xev,
   g_object_ref (new_context->dest_window);
 
   new_context->targets = NULL;
+  delayed_transfer = gdk_atom_intern_static_string ("XdndDelayedTransfer");
+  get_delayed_types = FALSE;
   if (get_types)
     {
       gdk_error_trap_push ();
@@ -2744,10 +2841,13 @@ xdnd_enter_filter (GdkXEvent *xev,
       atoms = (Atom *)data;
 
       for (i=0; i<nitems; i++)
-	new_context->targets = 
-	  g_list_append (new_context->targets,
-			 GDK_ATOM_TO_POINTER (gdk_x11_xatom_to_atom_for_display (display,
-										 atoms[i])));
+	{
+	  target = gdk_x11_xatom_to_atom_for_display (display, atoms[i]);
+	  new_context->targets = g_list_append (new_context->targets, 
+						GDK_ATOM_TO_POINTER (target));
+	  if (target == delayed_transfer)
+	    get_delayed_types = TRUE;
+	}
 
       XFree(atoms);
     }
@@ -2755,15 +2855,45 @@ xdnd_enter_filter (GdkXEvent *xev,
     {
       for (i=0; i<3; i++)
 	if (xevent->xclient.data.l[2+i])
-	  new_context->targets =
-	    g_list_append (new_context->targets,
-			   GDK_ATOM_TO_POINTER (gdk_x11_xatom_to_atom_for_display (display, 
-										   xevent->xclient.data.l[2+i])));
+	  {
+	    target = gdk_x11_xatom_to_atom_for_display (display, xevent->xclient.data.l[2+i]);
+	    new_context->targets = g_list_append (new_context->targets, 
+						  GDK_ATOM_TO_POINTER (target));
+	    if (target == delayed_transfer)
+	      get_delayed_types = TRUE;
+	  }
+    }
+
+  new_context->delayed_targets = NULL;
+  if (get_delayed_types)
+    {
+      gdk_error_trap_push ();
+      XGetWindowProperty (GDK_DRAWABLE_XDISPLAY (event->any.window), 
+			  source_window, 
+			  gdk_x11_get_xatom_by_name_for_display (display, "XdndDelayedTransferTypeList"),
+			  0, 65536,
+			  False, XA_ATOM, &type, &format, &nitems,
+			  &after, &data);
+
+      if (!gdk_error_trap_pop () && (format == 32) && (type == XA_ATOM))
+	{
+	  atoms = (Atom *)data;
+
+	  for (i=0; i<nitems; i++)
+	    {
+	      target = gdk_x11_xatom_to_atom_for_display (display, atoms[i]);
+	      new_context->delayed_targets = g_list_append (new_context->delayed_targets, GDK_ATOM_TO_POINTER (target));
+	    }
+	}
+      if (data)
+	XFree (data);
     }
 
 #ifdef G_ENABLE_DEBUG
   if (_gdk_debug_flags & GDK_DEBUG_DND)
     print_target_list (new_context->targets);
+  if (new_context->delayed_targets && _gdk_debug_flags & GDK_DEBUG_DND)
+    print_target_list (new_context->delayed_targets);
 #endif /* G_ENABLE_DEBUG */
 
   xdnd_manage_source_filter (new_context, new_context->source_window, TRUE);
@@ -3001,6 +3131,50 @@ gdk_drag_begin (GdkWindow     *window,
   return new_context;
 }
 
+/**
+ * gdk_drag_set_targets:
+ * @window: the source window for this drag
+ * @targets: the list of offered targets
+ * @delayed_targets: the list of targets offered for delayed transfer
+ *
+ * Set the targets offered by a drag window, updating if necessary. Note that
+ * if a destination requests a target that is no longer in the list it will be
+ * silently rejected.
+ *
+ * This function is called by the drag source.
+ **/
+void
+gdk_drag_set_targets (GdkWindow      *window,
+		      GdkDragContext *context,
+		      GList          *targets,
+		      GList          *delayed_targets)
+{
+  GdkDragContextPrivateX11 *private;
+  GdkWindow *dest_window;
+  
+  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
+  private = PRIVATE_DATA (context);
+
+  g_list_free (context->targets);
+  if (context->delayed_targets)
+    g_list_free (context->delayed_targets);
+  context->targets = g_list_copy (targets);
+  if (delayed_targets)
+    context->delayed_targets = g_list_copy (delayed_targets);
+  if ((dest_window = context->dest_window))
+    {
+      gdk_drag_do_leave (context, private->last_time);
+      private->xdnd_targets_set = 0;
+      private->xdnd_delayed_targets_set = 0;
+      private->motif_targets_set = 0;
+      gdk_drag_motion (context, dest_window, context->protocol, 
+		       private->last_x, private->last_y,
+		       context->suggested_action, private->old_actions, 
+		       private->last_time);
+    }
+}
+
 static guint32
 _gdk_drag_get_protocol_for_display (GdkDisplay      *display,
 				    guint32          xid,
@@ -3262,6 +3436,9 @@ gdk_drag_motion (GdkDragContext *context
 
   g_return_val_if_fail (context != NULL, FALSE);
 
+  if (time > private->last_time)
+    private->last_time = time;
+
   private->old_actions = context->actions;
   context->actions = possible_actions;
   
@@ -3304,7 +3481,8 @@ gdk_drag_motion (GdkDragContext *context
 		    
 	      dest_context = gdk_drag_context_find (display, FALSE,
 						    GDK_DRAWABLE_XID (context->source_window),
-						    GDK_DRAWABLE_XID (dest_window));
+						    GDK_DRAWABLE_XID (dest_window), 
+						    GDK_CURRENT_TIME);
 
 	      if (dest_context)
 		{
@@ -3525,6 +3703,9 @@ gdk_drag_status (GdkDragContext   *conte
   private = PRIVATE_DATA (context);
   display = GDK_DRAWABLE_DISPLAY (context->source_window);
   
+  if (time > private->last_time)
+    private->last_time = time;
+
   context->action = action;
 
   if (context->protocol == GDK_DRAG_PROTO_MOTIF)
@@ -3648,6 +3829,9 @@ gdk_drop_reply (GdkDragContext   *contex
 
   private = PRIVATE_DATA (context);
   
+  if (time > private->last_time)
+    private->last_time = time;
+
   if (context->protocol == GDK_DRAG_PROTO_MOTIF)
     {
       GdkDisplay *display = GDK_DRAWABLE_DISPLAY (context->source_window);
@@ -3705,7 +3889,7 @@ gdk_drop_finish (GdkDragContext   *conte
       XEvent xev;
 
       xev.xclient.type = ClientMessage;
-      xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndFinished");
+      xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, context->is_delayed ? "XdndDelayedTransferFinished" : "XdndFinished");
       xev.xclient.format = 32;
       xev.xclient.window = GDK_DRAWABLE_XID (context->source_window);
       
@@ -3721,7 +3905,7 @@ gdk_drop_finish (GdkDragContext   *conte
 	  xev.xclient.data.l[1] = 0;
 	  xev.xclient.data.l[2] = None;
 	}
-      xev.xclient.data.l[3] = 0;
+      xev.xclient.data.l[3] = context->is_delayed ? context->start_time : 0;
       xev.xclient.data.l[4] = 0;
 
       if (!xdnd_send_xevent (context, context->source_window,
@@ -3825,5 +4009,46 @@ gdk_drag_drop_succeeded (GdkDragContext 
   return !private->drop_failed;
 }
 
+/**
+ * gdk_drag_delayed_transfer_notify:
+ * @context: a #GdkDragContext
+ * @progress: the progress of the transfer, or 0 for indeterminate
+ * 
+ * Notifies the destination that delayed transfer data preparation 
+ * is ongoing.
+ *
+ * This function is called by the drag source.
+ *
+ * Since: 2.10
+ */
+void
+gdk_drag_delayed_transfer_notify (GdkDragContext *context,
+				  guint32         progress)
+{
+  XEvent xev;
+  GdkDragContextPrivateX11 *private;
+  GdkDisplay *display;
+
+  g_return_if_fail (context != NULL);
+  g_return_if_fail (context->is_delayed);
+
+  private = PRIVATE_DATA (context);
+  display = GDK_DRAWABLE_DISPLAY (context->source_window);
+ 
+  xev.xclient.type = ClientMessage;
+  xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndDelayedTransferProgress");
+  xev.xclient.format = 32;
+  xev.xclient.window = private->drop_xid ? 
+                           private->drop_xid : 
+                           GDK_DRAWABLE_XID (context->dest_window);
+  xev.xclient.data.l[0] = GDK_DRAWABLE_XID (context->source_window);
+  xev.xclient.data.l[1] = context->start_time;
+  xev.xclient.data.l[2] = progress;
+  xev.xclient.data.l[3] = 0;
+  xev.xclient.data.l[4] = 0;
+
+  xdnd_send_xevent (context, context->dest_window, FALSE, &xev);
+}
+
 #define __GDK_DND_X11_C__
 #include "gdkaliasdef.c"
