]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkdnd.c
s/succesfully/successfully/g
[~andy/gtk] / gtk / gtkdnd.c
index 101e4da4cea528a09baab847af4c50451a3617b2..95a63df5e532bd4030b590693a5966019b8e4e9a 100644 (file)
@@ -2,34 +2,38 @@
  * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
  *
  * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
+ * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  */
 
 /*
- * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
+ * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
  * file for a list of people on the GTK+ Team.  See the ChangeLog
  * files for a list of changes.  These files are distributed with
  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
  */
 
-#include "gdkx.h"
+#include "gdkconfig.h"
+
+#include "gdk/gdkkeysyms.h"
 
 #include "gtkdnd.h"
+#include "gtkimage.h"
 #include "gtkinvisible.h"
 #include "gtkmain.h"
 #include "gtksignal.h"
+#include "gtkstock.h"
 #include "gtkwindow.h"
 
 static GSList *drag_widgets = NULL;
@@ -55,9 +59,18 @@ struct _GtkDragSourceSite
   GdkModifierType    start_button_mask;
   GtkTargetList     *target_list;        /* Targets for drag data */
   GdkDragAction      actions;            /* Possible actions */
+
+  /* Drag icon */
+  GtkImageType icon_type;
+  union
+  {
+    GtkImagePixmapData pixmap;
+    GtkImagePixbufData pixbuf;
+    GtkImageStockData stock;
+  } icon_data;
+  GdkBitmap *icon_mask;
+
   GdkColormap       *colormap;          /* Colormap for drag icon */
-  GdkPixmap         *pixmap;             /* Icon for drag data */
-  GdkBitmap         *mask;
 
   /* Stored button press information to detect drag beginning */
   gint               state;
@@ -171,39 +184,42 @@ static GdkCursor *   gtk_drag_get_cursor         (GdkDragAction action);
 static GtkWidget    *gtk_drag_get_ipc_widget     (void);
 static void          gtk_drag_release_ipc_widget (GtkWidget      *widget);
 
-static void          gtk_drag_highlight_paint    (GtkWidget      *widget);
 static gboolean      gtk_drag_highlight_expose   (GtkWidget      *widget,
                                                  GdkEventExpose *event,
                                                  gpointer        data);
 
-
-static GdkAtom   gtk_drag_dest_find_target    (GtkWidget          *widget,
-                                              GtkDragDestSite    *site,
-                                              GdkDragContext     *context);
-static void      gtk_drag_selection_received  (GtkWidget          *widget,
-                                              GtkSelectionData   *selection_data,
-                                              guint32             time,
-                                              gpointer            data);
-static void      gtk_drag_find_widget         (GtkWidget          *widget,
-                                              GtkDragFindData    *data);
-static void      gtk_drag_proxy_begin         (GtkWidget          *widget,
-                                              GtkDragDestInfo    *dest_info);
-static void      gtk_drag_dest_info_destroy   (gpointer            data);
-static void      gtk_drag_dest_realized       (GtkWidget          *widget);
-static void      gtk_drag_dest_site_destroy   (gpointer            data);
-static void      gtk_drag_dest_leave          (GtkWidget          *widget,
-                                              GdkDragContext     *context,
-                                              guint               time);
-static gboolean  gtk_drag_dest_motion         (GtkWidget         *widget,
-                                              GdkDragContext     *context,
-                                              gint                x,
-                                              gint                y,
-                                              guint               time);
-static gboolean  gtk_drag_dest_drop           (GtkWidget         *widget,
-                                              GdkDragContext     *context,
-                                              gint                x,
-                                              gint                y,
-                                              guint               time);
+static void     gtk_drag_selection_received     (GtkWidget        *widget,
+                                                GtkSelectionData *selection_data,
+                                                guint32           time,
+                                                gpointer          data);
+static void     gtk_drag_find_widget            (GtkWidget        *widget,
+                                                GtkDragFindData  *data);
+static void     gtk_drag_proxy_begin            (GtkWidget        *widget,
+                                                GtkDragDestInfo  *dest_info,
+                                                guint32           time);
+static void     gtk_drag_dest_realized          (GtkWidget        *widget);
+static void     gtk_drag_dest_hierarchy_changed (GtkWidget        *widget,
+                                                GtkWidget        *previous_toplevel);
+static void     gtk_drag_dest_site_destroy      (gpointer          data);
+static void     gtk_drag_dest_leave             (GtkWidget        *widget,
+                                                GdkDragContext   *context,
+                                                guint             time);
+static gboolean gtk_drag_dest_motion            (GtkWidget        *widget,
+                                                GdkDragContext   *context,
+                                                gint              x,
+                                                gint              y,
+                                                guint             time);
+static gboolean gtk_drag_dest_drop              (GtkWidget        *widget,
+                                                GdkDragContext   *context,
+                                                gint              x,
+                                                gint              y,
+                                                guint             time);
+
+static GtkDragDestInfo *  gtk_drag_get_dest_info     (GdkDragContext *context,
+                                                     gboolean        create);
+static GtkDragSourceInfo *gtk_drag_get_source_info   (GdkDragContext *context,
+                                                     gboolean        create);
+static void               gtk_drag_clear_source_info (GdkDragContext *context);
 
 static void gtk_drag_source_check_selection    (GtkDragSourceInfo *info, 
                                                GdkAtom            selection,
@@ -227,10 +243,17 @@ static void gtk_drag_selection_get             (GtkWidget         *widget,
                                                gpointer           data);
 static gint gtk_drag_anim_timeout              (gpointer           data);
 static void gtk_drag_remove_icon               (GtkDragSourceInfo *info);
-static void gtk_drag_source_info_destroy       (gpointer           data);
+static void gtk_drag_source_info_destroy       (GtkDragSourceInfo *info);
+static void gtk_drag_update                    (GtkDragSourceInfo *info,
+                                               gint               x_root,
+                                               gint               y_root,
+                                               GdkEvent          *event);
 static gint gtk_drag_motion_cb                 (GtkWidget         *widget, 
                                                GdkEventMotion    *event, 
                                                gpointer           data);
+static gint gtk_drag_key_cb                    (GtkWidget         *widget, 
+                                               GdkEventKey       *event, 
+                                               gpointer           data);
 static gint gtk_drag_button_release_cb         (GtkWidget         *widget, 
                                                GdkEventButton    *event, 
                                                gpointer           data);
@@ -242,70 +265,70 @@ static gint gtk_drag_abort_timeout             (gpointer           data);
 
 #define action_ask_width 16
 #define action_ask_height 16
-static const char action_ask_bits[] = {
+static const guchar action_ask_bits[] = {
   0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7, 
   0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb, 
   0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
 
 #define action_ask_mask_width 16
 #define action_ask_mask_height 16
-static const char action_ask_mask_bits[] = {
+static const guchar action_ask_mask_bits[] = {
   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f, 
   0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07, 
   0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
 
 #define action_copy_width 16
 #define action_copy_height 16
-static const char action_copy_bits[] = {
+static const guchar action_copy_bits[] = {
   0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb, 
   0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb, 
   0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
 
 #define action_copy_mask_width 16
 #define action_copy_mask_height 16
-static const char action_copy_mask_bits[] = {
+static const guchar action_copy_mask_bits[] = {
   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07, 
   0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07, 
   0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
 
 #define action_move_width 16
 #define action_move_height 16
-static const char action_move_bits[] = {
+static const guchar action_move_bits[] = {
   0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff, 
   0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb, 
   0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
 
 #define action_move_mask_width 16
 #define action_move_mask_height 16
-static const char action_move_mask_bits[] = {
+static const guchar action_move_mask_bits[] = {
   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00, 
   0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07, 
   0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
 
 #define action_link_width 16
 #define action_link_height 16
-static const char action_link_bits[] = {
+static const guchar action_link_bits[] = {
   0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7, 
   0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8, 
   0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
 
 #define action_link_mask_width 16
 #define action_link_mask_height 16
-static const char action_link_mask_bits[] = {
+static const guchar action_link_mask_bits[] = {
   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f, 
   0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77, 
   0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
 
 #define action_none_width 16
 #define action_none_height 16
-static const char action_none_bits[] = {
+static const guchar action_none_bits[] = {
   0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff, 
   0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 
   0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
 
 #define action_none_mask_width 16
 #define action_none_mask_height 16
-static const char action_none_mask_bits[] = {
+static const guchar action_none_mask_bits[] = {
   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00, 
   0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 
   0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
@@ -329,45 +352,6 @@ static struct {
 
 static const gint n_drag_cursors = sizeof (drag_cursors) / sizeof (drag_cursors[0]);
 
-/* XPM */
-static const char * drag_default_xpm[] = {
-"32 32 3 1",
-"      c None",
-".     c #000000",
-"+     c #FFFFFF",
-"                                ",
-"                                ",
-"                ..              ",
-"              ..+.              ",
-"             ..++..             ",
-"           ...++++.             ",
-"         ...++++++..            ",
-"       ...+++++++++.            ",
-"     ...+++++++++++..           ",
-"    ..+.++++++++++++..          ",
-"     .++.++++++++++++..         ",
-"     .+++.++++++++++++..        ",
-"     .++++.++++++++++++.        ",
-"     .+++.+++++++++++++..       ",
-"     .++.+++++++++++++++..      ",
-"     .+.+++++++++++++++++..     ",
-"     ..+++++++++++++++++++..    ",
-"     ..++++++++++++++++++++.    ",
-"     .++++++++++++++++++++..    ",
-"     ..+++++++++++++++++..      ",
-"      .++++++++++++++++..       ",
-"      ..+++++++++++++...        ",
-"       .++++++++++++..          ",
-"       ..+++++++++..            ",
-"        .++++++++..             ",
-"        ..++++++..              ",
-"         .+++++..               ",
-"          .++..                 ",
-"           ...                  ",
-"           ..                   ",
-"                                ",
-"                                "};
-
 /*********************
  * Utility functions *
  *********************/
@@ -416,6 +400,43 @@ gtk_drag_release_ipc_widget (GtkWidget *widget)
   drag_widgets = g_slist_prepend (drag_widgets, widget);
 }
 
+static guint32
+gtk_drag_get_event_time (GdkEvent *event)
+{
+  guint32 tm = GDK_CURRENT_TIME;
+  
+  if (event)
+    switch (event->type)
+      {
+      case GDK_MOTION_NOTIFY:
+       tm = event->motion.time; break;
+      case GDK_BUTTON_PRESS:
+      case GDK_2BUTTON_PRESS:
+      case GDK_3BUTTON_PRESS:
+      case GDK_BUTTON_RELEASE:
+       tm = event->button.time; break;
+      case GDK_KEY_PRESS:
+      case GDK_KEY_RELEASE:
+       tm = event->key.time; break;
+      case GDK_ENTER_NOTIFY:
+      case GDK_LEAVE_NOTIFY:
+       tm = event->crossing.time; break;
+      case GDK_PROPERTY_NOTIFY:
+       tm = event->property.time; break;
+      case GDK_SELECTION_CLEAR:
+      case GDK_SELECTION_REQUEST:
+      case GDK_SELECTION_NOTIFY:
+       tm = event->selection.time; break;
+      case GDK_PROXIMITY_IN:
+      case GDK_PROXIMITY_OUT:
+       tm = event->proximity.time; break;
+      default:                 /* use current time */
+       break;
+      }
+  
+  return tm;
+}
+
 static void
 gtk_drag_get_event_actions (GdkEvent *event, 
                            gint button, 
@@ -452,7 +473,7 @@ gtk_drag_get_event_actions (GdkEvent *event,
        default:
          break;
        }
-      
+
       if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
        {
          *suggested_action = GDK_ACTION_ASK;
@@ -501,6 +522,17 @@ gtk_drag_get_event_actions (GdkEvent *event,
            *suggested_action = GDK_ACTION_LINK;
        }
     }
+  else
+    {
+      *possible_actions = actions;
+      
+      if (actions & GDK_ACTION_COPY)
+       *suggested_action =  GDK_ACTION_COPY;
+      else if (actions & GDK_ACTION_MOVE)
+       *suggested_action = GDK_ACTION_MOVE;
+      else if (actions & GDK_ACTION_LINK)
+       *suggested_action = GDK_ACTION_LINK;
+    }
   
   return;
 }
@@ -667,25 +699,27 @@ gtk_drag_finish (GdkDragContext *context,
                             time);
     }
   
-  if (!del)
+  if (!(success && del))
     gdk_drop_finish (context, success, time);
 }
 
 /*************************************************************
- * gtk_drag_highlight_paint:
- *     Paint a highlight indicating drag status onto the widget.
+ * gtk_drag_highlight_expose:
+ *     Callback for expose_event for highlighted widgets.
  *   arguments:
  *     widget:
+ *     event:
+ *     data:
  *   results:
  *************************************************************/
 
-static void 
-gtk_drag_highlight_paint (GtkWidget  *widget)
+static gboolean
+gtk_drag_highlight_expose (GtkWidget      *widget,
+                          GdkEventExpose *event,
+                          gpointer        data)
 {
   gint x, y, width, height;
-
-  g_return_if_fail (widget != NULL);
-
+  
   if (GTK_WIDGET_DRAWABLE (widget))
     {
       if (GTK_WIDGET_NO_WINDOW (widget))
@@ -711,25 +745,8 @@ gtk_drag_highlight_paint (GtkWidget  *widget)
                          FALSE,
                          x, y, width - 1, height - 1);
     }
-}
-
-/*************************************************************
- * gtk_drag_highlight_expose:
- *     Callback for expose_event for highlighted widgets.
- *   arguments:
- *     widget:
- *     event:
- *     data:
- *   results:
- *************************************************************/
 
-static gboolean
-gtk_drag_highlight_expose (GtkWidget      *widget,
-                          GdkEventExpose *event,
-                          gpointer        data)
-{
-  gtk_drag_highlight_paint (widget);
-  return TRUE;
+  return FALSE;
 }
 
 /*************************************************************
@@ -743,9 +760,6 @@ gtk_drag_highlight_expose (GtkWidget      *widget,
 void 
 gtk_drag_highlight (GtkWidget  *widget)
 {
-  gtk_signal_connect_after (GTK_OBJECT (widget), "draw",
-                           GTK_SIGNAL_FUNC (gtk_drag_highlight_paint),
-                           NULL);
   gtk_signal_connect (GTK_OBJECT (widget), "expose_event",
                      GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
                      NULL);
@@ -766,9 +780,6 @@ gtk_drag_unhighlight (GtkWidget *widget)
 {
   g_return_if_fail (widget != NULL);
 
-  gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
-                                GTK_SIGNAL_FUNC (gtk_drag_highlight_paint),
-                                NULL);
   gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
                                 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
                                 NULL);
@@ -776,6 +787,32 @@ gtk_drag_unhighlight (GtkWidget *widget)
   gtk_widget_queue_clear (widget);
 }
 
+static void
+gtk_drag_dest_set_internal (GtkWidget       *widget,
+                           GtkDragDestSite *site)
+{
+  GtkDragDestSite *old_site;
+  
+  g_return_if_fail (widget != NULL);
+
+  /* HACK, do this in the destroy */
+  old_site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
+  if (old_site)
+    gtk_signal_disconnect_by_data (GTK_OBJECT (widget), old_site);
+
+  if (GTK_WIDGET_REALIZED (widget))
+    gtk_drag_dest_realized (widget);
+
+  gtk_signal_connect (GTK_OBJECT (widget), "realize",
+                     GTK_SIGNAL_FUNC (gtk_drag_dest_realized), site);
+  gtk_signal_connect (GTK_OBJECT (widget), "hierarchy_changed",
+                     GTK_SIGNAL_FUNC (gtk_drag_dest_hierarchy_changed), site);
+
+  gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
+                           site, gtk_drag_dest_site_destroy);
+}
+                           
+
 /*************************************************************
  * gtk_drag_dest_set:
  *     Register a drop site, and possibly add default behaviors.
@@ -799,17 +836,6 @@ gtk_drag_dest_set   (GtkWidget            *widget,
   
   g_return_if_fail (widget != NULL);
 
-  /* HACK, do this in the destroy */
-  site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
-  if (site)
-    gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
-
-  if (GTK_WIDGET_REALIZED (widget))
-    gtk_drag_dest_realized (widget);
-
-  gtk_signal_connect (GTK_OBJECT (widget), "realize",
-                     GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
-
   site = g_new (GtkDragDestSite, 1);
 
   site->flags = flags;
@@ -822,8 +848,7 @@ gtk_drag_dest_set   (GtkWidget            *widget,
   site->actions = actions;
   site->do_proxy = FALSE;
 
-  gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
-                           site, gtk_drag_dest_site_destroy);
+  gtk_drag_dest_set_internal (widget, site);
 }
 
 /*************************************************************
@@ -849,17 +874,6 @@ gtk_drag_dest_set_proxy (GtkWidget      *widget,
   
   g_return_if_fail (widget != NULL);
 
-  /* HACK, do this in the destroy */
-  site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
-  if (site)
-    gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
-
-  if (GTK_WIDGET_REALIZED (widget))
-    gtk_drag_dest_realized (widget);
-
-  gtk_signal_connect (GTK_OBJECT (widget), "realize",
-                     GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
-
   site = g_new (GtkDragDestSite, 1);
 
   site->flags = 0;
@@ -873,8 +887,7 @@ gtk_drag_dest_set_proxy (GtkWidget      *widget,
   site->proxy_protocol = protocol;
   site->proxy_coords = use_coordinates;
 
-  gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
-                           site, gtk_drag_dest_site_destroy);
+  gtk_drag_dest_set_internal (widget, site);
 }
 
 /*************************************************************
@@ -893,8 +906,60 @@ gtk_drag_dest_unset (GtkWidget *widget)
   gtk_object_set_data (GTK_OBJECT (widget), "gtk-drag-dest", NULL);
 }
 
+/**
+ * gtk_drag_dest_get_target_list:
+ * @widget: a #GtkWidget
+ * 
+ * Returns the list of targets this widget can accept from
+ * drag-and-drop.
+ * 
+ * Return value: the #GtkTargetList, or %NULL if none
+ **/
+GtkTargetList*
+gtk_drag_dest_get_target_list (GtkWidget *widget)
+{
+  GtkDragDestSite *site;
+  
+  site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
+
+  return site ? site->target_list : NULL;  
+}
+
+/**
+ * gtk_drag_dest_set_target_list:
+ * @widget: a #GtkWidget that's a drag destination
+ * @target_list: list of droppable targets, or %NULL for none
+ * 
+ * Sets the target types that this widget can accept from drag-and-drop.
+ * The widget must first be made into a drag destination with
+ * gtk_drag_dest_set().
+ **/
+void
+gtk_drag_dest_set_target_list (GtkWidget      *widget,
+                               GtkTargetList  *target_list)
+{
+  GtkDragDestSite *site;
+  
+  site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
+  
+  if (site == NULL)
+    {
+      g_warning ("can't set a target list on a widget until you've called gtk_drag_dest_set() to make the widget into a drag destination");
+      return;
+    }
+
+  if (target_list)
+    gtk_target_list_ref (target_list);
+  
+  if (site->target_list)
+    gtk_target_list_unref (site->target_list);
+
+  site->target_list = target_list;
+}
+
+
 /*************************************************************
- * gtk_drag_dest_handle_event:
+ * _gtk_drag_dest_handle_event:
  *     Called from widget event handling code on Drag events
  *     for destinations.
  *
@@ -905,7 +970,7 @@ gtk_drag_dest_unset (GtkWidget *widget)
  *************************************************************/
 
 void
-gtk_drag_dest_handle_event (GtkWidget *toplevel,
+_gtk_drag_dest_handle_event (GtkWidget *toplevel,
                            GdkEvent  *event)
 {
   GtkDragDestInfo *info;
@@ -916,21 +981,7 @@ gtk_drag_dest_handle_event (GtkWidget *toplevel,
 
   context = event->dnd.context;
 
-  info = g_dataset_get_data (context, "gtk-info");
-  if (!info)
-    {
-      info = g_new (GtkDragDestInfo, 1);
-      info->widget = NULL;
-      info->context = event->dnd.context;
-      info->proxy_source = NULL;
-      info->proxy_data = NULL;
-      info->dropped = FALSE;
-      info->proxy_drop_wait = FALSE;
-      g_dataset_set_data_full (context,
-                              "gtk-info",
-                              info,
-                              gtk_drag_dest_info_destroy);
-    }
+  info = gtk_drag_get_dest_info (context, TRUE);
 
   /* Find the widget for the event */
   switch (event->type)
@@ -953,7 +1004,17 @@ gtk_drag_dest_handle_event (GtkWidget *toplevel,
        gint tx, ty;
 
        if (event->type == GDK_DROP_START)
-         info->dropped = TRUE;
+         {
+           info->dropped = TRUE;
+           /* We send a leave here so that the widget unhighlights
+            * properly.
+            */
+           if (info->widget)
+             {
+               gtk_drag_dest_leave (info->widget, context, event->dnd.time);
+               info->widget = NULL;
+             }
+         }
 
        gdk_window_get_origin (toplevel->window, &tx, &ty);
 
@@ -969,11 +1030,7 @@ gtk_drag_dest_handle_event (GtkWidget *toplevel,
        
        gtk_drag_find_widget (toplevel, &data);
 
-       /* We send a leave here so that the widget unhighlights
-        * properly
-        */
-       if (info->widget &&
-           ((event->type == GDK_DROP_START) || (!data.found)))
+       if (info->widget && !data.found)
          {
            gtk_drag_dest_leave (info->widget, context, event->dnd.time);
            info->widget = NULL;
@@ -986,7 +1043,7 @@ gtk_drag_dest_handle_event (GtkWidget *toplevel,
            if (!data.found)
              gdk_drag_status (context, 0, event->dnd.time);
          }
-       else if (event->type == GDK_DROP_START)
+       else if (event->type == GDK_DROP_START && !info->proxy_source)
          {
            gdk_drop_reply (context, data.found, event->dnd.time);
             if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
@@ -1000,25 +1057,44 @@ gtk_drag_dest_handle_event (GtkWidget *toplevel,
     }
 }
 
-/*************************************************************
+/**
  * gtk_drag_dest_find_target:
- *     Decide on a target for the drag.
- *   arguments:
- *     site:
- *     context:
- *   results:
- *************************************************************/
-
-static GdkAtom
-gtk_drag_dest_find_target (GtkWidget       *widget,
-                          GtkDragDestSite *site,
-                          GdkDragContext  *context)
+ * @widget: drag destination widget
+ * @context: drag context
+ * @target_list: list of droppable targets, or %NULL to use
+ *    gtk_drag_dest_get_target_list (@widget).
+ * 
+ * Looks for a match between @context->targets and the
+ * @dest_target_list, returning the first matching target, otherwise
+ * returning %GDK_NONE. @dest_target_list should usually be the return
+ * value from gtk_drag_dest_get_target_list(), but some widgets may
+ * have different valid targets for different parts of the widget; in
+ * that case, they will have to implement a drag_motion handler that
+ * passes the correct target list to this function.
+ * 
+ * Return value: first target that the source offers and the dest can accept, or %GDK_NONE
+ **/
+GdkAtom
+gtk_drag_dest_find_target (GtkWidget      *widget,
+                           GdkDragContext *context,
+                           GtkTargetList  *target_list)
 {
   GList *tmp_target;
   GList *tmp_source = NULL;
-  GtkWidget *source_widget = gtk_drag_get_source_widget (context);
+  GtkWidget *source_widget;
+
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
+  g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
+
+  source_widget = gtk_drag_get_source_widget (context);
 
-  tmp_target = site->target_list->list;
+  if (target_list == NULL)
+    target_list = gtk_drag_dest_get_target_list (widget);
+  
+  if (target_list == NULL)
+    return GDK_NONE;
+  
+  tmp_target = target_list->list;
   while (tmp_target)
     {
       GtkTargetPair *pair = tmp_target->data;
@@ -1054,7 +1130,7 @@ gtk_drag_selection_received (GtkWidget        *widget,
   drop_widget = data;
 
   context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
-  info = g_dataset_get_data (context, "gtk-info");
+  info = gtk_drag_get_dest_info (context, FALSE);
 
   if (info->proxy_data && 
       info->proxy_data->target == selection_data->target)
@@ -1083,7 +1159,7 @@ gtk_drag_selection_received (GtkWidget        *widget,
 
       site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
 
-      if (site->target_list)
+      if (site && site->target_list)
        {
          guint target_info;
 
@@ -1108,7 +1184,7 @@ gtk_drag_selection_received (GtkWidget        *widget,
                                   selection_data, 0, time);
        }
       
-      if (site->flags & GTK_DEST_DEFAULT_DROP)
+      if (site && site->flags & GTK_DEST_DEFAULT_DROP)
        {
 
          gtk_drag_finish (context, 
@@ -1248,18 +1324,29 @@ gtk_drag_find_widget (GtkWidget       *widget,
 
 static void
 gtk_drag_proxy_begin (GtkWidget       *widget, 
-                     GtkDragDestInfo *dest_info)
+                     GtkDragDestInfo *dest_info,
+                     guint32          time)
 {
   GtkDragSourceInfo *source_info;
   GList *tmp_list;
+  GdkDragContext *context;
+  GtkWidget *ipc_widget;
+
+  if (dest_info->proxy_source)
+    {
+      gdk_drag_abort (dest_info->proxy_source->context, time);
+      gtk_drag_source_info_destroy (dest_info->proxy_source);
+      dest_info->proxy_source = NULL;
+    }
   
-  source_info = g_new0 (GtkDragSourceInfo, 1);
-  source_info->ipc_widget = gtk_drag_get_ipc_widget ();
-  
-  source_info->widget = widget;
-  gtk_widget_ref (source_info->widget);
-  source_info->context = gdk_drag_begin (source_info->ipc_widget->window,
-                                        dest_info->context->targets);
+  ipc_widget = gtk_drag_get_ipc_widget ();
+  context = gdk_drag_begin (ipc_widget->window,
+                           dest_info->context->targets);
+
+  source_info = gtk_drag_get_source_info (context, TRUE);
+
+  source_info->ipc_widget = ipc_widget;
+  source_info->widget = gtk_widget_ref (widget);
 
   source_info->target_list = gtk_target_list_new (NULL, 0);
   tmp_list = dest_info->context->targets;
@@ -1272,9 +1359,7 @@ gtk_drag_proxy_begin (GtkWidget       *widget,
 
   source_info->proxy_dest = dest_info;
   
-  g_dataset_set_data (source_info->context, "gtk-info", source_info);
-  
-  gtk_signal_connect (GTK_OBJECT (source_info->ipc_widget), 
+  gtk_signal_connect (GTK_OBJECT (ipc_widget), 
                      "selection_get",
                      GTK_SIGNAL_FUNC (gtk_drag_selection_get), 
                      source_info);
@@ -1290,11 +1375,76 @@ gtk_drag_dest_info_destroy (gpointer data)
   g_free (info);
 }
 
+static GtkDragDestInfo *
+gtk_drag_get_dest_info (GdkDragContext *context,
+                       gboolean        create)
+{
+  GtkDragDestInfo *info;
+  static GQuark info_quark = 0;
+  if (!info_quark)
+    info_quark = g_quark_from_static_string ("gtk-dest-info");
+  
+  info = g_object_get_qdata (G_OBJECT (context), info_quark);
+  if (!info && create)
+    {
+      info = g_new (GtkDragDestInfo, 1);
+      info->widget = NULL;
+      info->context = context;
+      info->proxy_source = NULL;
+      info->proxy_data = NULL;
+      info->dropped = FALSE;
+      info->proxy_drop_wait = FALSE;
+      g_object_set_qdata_full (G_OBJECT (context), info_quark,
+                              info, gtk_drag_dest_info_destroy);
+    }
+
+  return info;
+}
+
+static GQuark dest_info_quark = 0;
+
+static GtkDragSourceInfo *
+gtk_drag_get_source_info (GdkDragContext *context,
+                         gboolean        create)
+{
+  GtkDragSourceInfo *info;
+  if (!dest_info_quark)
+    dest_info_quark = g_quark_from_static_string ("gtk-source-info");
+  
+  info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
+  if (!info && create)
+    {
+      info = g_new0 (GtkDragSourceInfo, 1);
+      info->context = context;
+      g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
+    }
+
+  return info;
+}
+
+static void
+gtk_drag_clear_source_info (GdkDragContext *context)
+{
+  g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
+}
+
 static void
 gtk_drag_dest_realized (GtkWidget *widget)
 {
   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
-  gdk_window_register_dnd (toplevel->window);
+
+  if (GTK_WIDGET_TOPLEVEL (toplevel))
+    gdk_window_register_dnd (toplevel->window);
+}
+
+static void
+gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
+                                GtkWidget *previous_toplevel)
+{
+  GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
+
+  if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WIDGET_REALIZED (toplevel))
+    gdk_window_register_dnd (toplevel->window);
 }
 
 static void
@@ -1323,16 +1473,20 @@ gtk_drag_dest_leave (GtkWidget      *widget,
 
   if (site->do_proxy)
     {
-      GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
+      GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
 
-      if (info->proxy_source && !info->dropped)
-       gdk_drag_abort (info->proxy_source->context, time);
+      if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
+       {
+         gdk_drag_abort (info->proxy_source->context, time);
+         gtk_drag_source_info_destroy (info->proxy_source);
+         info->proxy_source = NULL;
+       }
       
       return;
     }
   else
     {
-      if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
+      if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
        gtk_drag_unhighlight (widget);
 
       if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
@@ -1364,10 +1518,10 @@ gtk_drag_dest_motion (GtkWidget      *widget,
       GdkWindow *dest_window;
       GdkDragProtocol proto;
        
-      GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
+      GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
 
-      if (!info->proxy_source)
-       gtk_drag_proxy_begin (widget, info);
+      if (!info->proxy_source || info->proxy_source->widget != widget)
+       gtk_drag_proxy_begin (widget, info, time);
 
       current_event = gtk_get_current_event ();
 
@@ -1424,7 +1578,7 @@ gtk_drag_dest_motion (GtkWidget        *widget,
            }
        }
       
-      if (action && gtk_drag_dest_find_target (widget, site, context))
+      if (action && gtk_drag_dest_find_target (widget, context, NULL))
        {
          if (!site->have_drag)
            {
@@ -1461,7 +1615,7 @@ gtk_drag_dest_drop (GtkWidget          *widget,
   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
   g_return_val_if_fail (site != NULL, FALSE);
 
-  info = g_dataset_get_data (context, "gtk-info");
+  info = gtk_drag_get_dest_info (context, FALSE);
   g_return_val_if_fail (info != NULL, FALSE);
 
   info->drop_x = x;
@@ -1485,7 +1639,7 @@ gtk_drag_dest_drop (GtkWidget          *widget,
          GdkWindow *dest_window;
          GdkDragProtocol proto;
          
-         gtk_drag_proxy_begin (widget, info);
+         gtk_drag_proxy_begin (widget, info, time);
          info->proxy_drop_wait = TRUE;
          info->proxy_drop_time = time;
          
@@ -1521,7 +1675,6 @@ gtk_drag_dest_drop (GtkWidget          *widget,
            gtk_drag_source_check_selection (info->proxy_source, selection, time);
 
          gdk_event_free (current_event);
-      
        }
 
       return TRUE;
@@ -1532,12 +1685,15 @@ gtk_drag_dest_drop (GtkWidget        *widget,
 
       if (site->flags & GTK_DEST_DEFAULT_DROP)
        {
-         GdkAtom target = gtk_drag_dest_find_target (widget, site, context);
+         GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
       
          if (target == GDK_NONE)
-           return FALSE;
-         
-         gtk_drag_get_data (widget, context, target, time);
+           {
+             gtk_drag_finish (context, FALSE, FALSE, time);
+             return TRUE;
+           }
+         else
+           gtk_drag_get_data (widget, context, target, time);
        }
 
       gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
@@ -1575,6 +1731,8 @@ gtk_drag_begin (GtkWidget         *widget,
   GList *tmp_list;
   guint32 time = GDK_CURRENT_TIME;
   GdkDragAction possible_actions, suggested_action;
+  GdkDragContext *context;
+  GtkWidget *ipc_widget;
 
   g_return_val_if_fail (widget != NULL, NULL);
   g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
@@ -1583,12 +1741,6 @@ gtk_drag_begin (GtkWidget         *widget,
   if (event)
     time = gdk_event_get_time (event);
 
-  info = g_new0 (GtkDragSourceInfo, 1);
-  info->ipc_widget = gtk_drag_get_ipc_widget ();
-  source_widgets = g_slist_prepend (source_widgets, info->ipc_widget);
-
-  gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
-
   tmp_list = g_list_last (target_list->list);
   while (tmp_list)
     {
@@ -1598,14 +1750,20 @@ gtk_drag_begin (GtkWidget         *widget,
       tmp_list = tmp_list->prev;
     }
 
-  info->widget = widget;
-  gtk_widget_ref (info->widget);
-  
-  info->context = gdk_drag_begin (info->ipc_widget->window, targets);
+  ipc_widget = gtk_drag_get_ipc_widget ();
+  source_widgets = g_slist_prepend (source_widgets, ipc_widget);
+
+  context = gdk_drag_begin (ipc_widget->window, targets);
   g_list_free (targets);
   
-  g_dataset_set_data (info->context, "gtk-info", info);
+  info = gtk_drag_get_source_info (context, TRUE);
+  
+  info->ipc_widget = ipc_widget;
+  gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
 
+  info->widget = gtk_widget_ref (widget);
+
+  
   info->button = button;
   info->target_list = target_list;
   gtk_target_list_ref (target_list);
@@ -1621,14 +1779,13 @@ gtk_drag_begin (GtkWidget         *widget,
 
   gtk_drag_get_event_actions (event, info->button, actions,
                              &suggested_action, &possible_actions);
-  
-  if (event)
-    info->cursor = gtk_drag_get_cursor (suggested_action);
+
+  info->cursor = gtk_drag_get_cursor (suggested_action);
 
   /* Set cur_x, cur_y here so if the "drag_begin" signal shows
    * the drag icon, it will be in the right place
    */
-  if (event->type == GDK_MOTION_NOTIFY)
+  if (event && event->type == GDK_MOTION_NOTIFY)
     {
       info->cur_x = event->motion.x_root;
       info->cur_y = event->motion.y_root;
@@ -1645,17 +1802,7 @@ gtk_drag_begin (GtkWidget         *widget,
   gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
                           info->context);
   
-  /* We use a GTK grab here to override any grabs that the widget
-   * we are dragging from might have held
-   */
-
-  gtk_grab_add (info->ipc_widget);
-  gdk_pointer_grab (info->ipc_widget->window, FALSE,
-                   GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
-                   GDK_BUTTON_RELEASE_MASK, NULL,
-                   info->cursor, time);
-
-  if (event->type == GDK_MOTION_NOTIFY)
+  if (event && event->type == GDK_MOTION_NOTIFY)
     gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
 
   info->start_x = info->cur_x;
@@ -1665,9 +1812,37 @@ gtk_drag_begin (GtkWidget         *widget,
                      GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
   gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
                      GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
+  gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_press_event",
+                     GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
+  gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_release_event",
+                     GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
   gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
                      GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
 
+  /* We use a GTK grab here to override any grabs that the widget
+   * we are dragging from might have held
+   */
+  gtk_grab_add (info->ipc_widget);
+  if (gdk_pointer_grab (info->ipc_widget->window, FALSE,
+                       GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
+                       GDK_BUTTON_RELEASE_MASK, NULL,
+                       info->cursor, time) == 0)
+    {
+      if (gdk_keyboard_grab (info->ipc_widget->window, FALSE, time) != 0)
+       {
+         /* FIXME: This should be cleaned up... */
+         GdkEventButton ev;
+
+         ev.time = time;
+         ev.type = GDK_BUTTON_RELEASE;
+         ev.button = info->button;
+
+         gtk_drag_button_release_cb (widget, &ev, info);
+
+         return NULL;
+       }
+    }
+
   return info->context;
 }
 
@@ -1709,6 +1884,8 @@ gtk_drag_source_set (GtkWidget            *widget,
   else
     {
       site = g_new0 (GtkDragSourceSite, 1);
+
+      site->icon_type = GTK_IMAGE_EMPTY;
       
       gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
                          GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
@@ -1757,16 +1934,48 @@ gtk_drag_source_unset (GtkWidget        *widget)
     }
 }
 
-/*************************************************************
- * gtk_drag_source_set_icon:
- *     Set an icon for drags from this source.
- *   arguments:
- *     colormap: Colormap for this icon
- *     pixmap:
- *     mask
- *   results:
- *************************************************************/
+static void
+gtk_drag_source_unset_icon (GtkDragSourceSite *site)
+{
+  switch (site->icon_type)
+    {
+    case GTK_IMAGE_EMPTY:
+      break;
+    case GTK_IMAGE_PIXMAP:
+      if (site->icon_data.pixmap.pixmap)
+       gdk_pixmap_unref (site->icon_data.pixmap.pixmap);
+      if (site->icon_mask)
+       gdk_pixmap_unref (site->icon_mask);
+      break;
+    case GTK_IMAGE_PIXBUF:
+      g_object_unref (G_OBJECT (site->icon_data.pixbuf.pixbuf));
+      break;
+    case GTK_IMAGE_STOCK:
+      g_free (G_OBJECT (site->icon_data.stock.stock_id));
+      break;
+    default:
+      g_assert_not_reached();
+      break;
+    }
+  site->icon_type = GTK_IMAGE_EMPTY;
+  
+  if (site->colormap)
+    gdk_colormap_unref (site->colormap);
+  site->colormap = NULL;
+}
 
+/**
+ * gtk_drag_source_set_icon:
+ * @widget: a #GtkWidget
+ * @colormap: the colormap of the icon
+ * @pixmap: the image data for the icon
+ * @mask: the transparency mask for an image.
+ * 
+ * Sets the icon that will be used for drags from a particular widget
+ * from a pixmap/mask. GTK+ retains a reference count for the
+ * arguments, and will release them when they are no longer needed.
+ * Use gtk_drag_source_set_icon_pixbuf() instead.
+ **/
 void 
 gtk_drag_source_set_icon (GtkWidget     *widget,
                          GdkColormap   *colormap,
@@ -1776,40 +1985,81 @@ gtk_drag_source_set_icon (GtkWidget     *widget,
   GtkDragSourceSite *site;
 
   g_return_if_fail (widget != NULL);
+  g_return_if_fail (GDK_IS_COLORMAP (colormap));
+  g_return_if_fail (GDK_IS_PIXMAP (pixmap));
+  g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
 
   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
   g_return_if_fail (site != NULL);
   
-  if (site->colormap)
-    gdk_colormap_unref (site->colormap);
-  if (site->pixmap)
-    gdk_pixmap_unref (site->pixmap);
-  if (site->mask)
-    gdk_pixmap_unref (site->mask);
-
-  site->colormap = colormap;
-  if (colormap)
-    gdk_colormap_ref (colormap);
-
-  site->pixmap = pixmap;
-  if (pixmap)
-    gdk_pixmap_ref (pixmap);
-
-  site->mask = mask;
+  gdk_colormap_ref (colormap);
+  gdk_pixmap_ref (pixmap);
   if (mask)
     gdk_pixmap_ref (mask);
-}
 
-/*************************************************************
- * gtk_drag_set_icon_window:
- *     Set a widget as the icon for a drag.
- *   arguments:
- *     context:
- *     widget:
- *     hot_x:    Hot spot
- *     hot_y:
- *   results:
- *************************************************************/
+  gtk_drag_source_unset_icon (site);
+
+  site->icon_type = GTK_IMAGE_PIXMAP;
+  
+  site->icon_data.pixmap.pixmap = pixmap;
+  site->icon_mask = mask;
+  site->colormap = colormap;
+}
+
+/**
+ * gtk_drag_source_set_icon_pixbuf:
+ * @widget: a #GtkWidget
+ * @pixbuf: the #GdkPixbuf for the drag icon
+ * 
+ * Sets the icon that will be used for drags from a particular widget
+ * from a #GdkPixbuf. GTK+ retains a reference count @pixbuf.
+ * and will release it when it is no longer needed.
+ **/
+void 
+gtk_drag_source_set_icon_pixbuf (GtkWidget   *widget,
+                                GdkPixbuf   *pixbuf)
+{
+  GtkDragSourceSite *site;
+
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
+
+  site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
+  g_return_if_fail (site != NULL);
+  
+  gdk_pixbuf_ref (pixbuf);
+
+  gtk_drag_source_unset_icon (site);
+
+  site->icon_type = GTK_IMAGE_PIXBUF;
+  site->icon_data.pixbuf.pixbuf = pixbuf;
+}
+
+/**
+ * gtk_drag_source_set_icon_stock:
+ * @widget: a #GtkWidget
+ * @stock: the ID of the stock icon to use..
+ * @size: size at which to render the stock icon
+ *
+ * Sets the icon that will be used for drags from a particular to
+ * a stock icon. 
+ **/
+void 
+gtk_drag_source_set_icon_stock (GtkWidget   *widget,
+                               const gchar *stock_id)
+{
+  GtkDragSourceSite *site;
+
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (stock_id != NULL);
+
+  site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
+  g_return_if_fail (site != NULL);
+  
+  gtk_drag_source_unset_icon (site);
+
+  site->icon_data.stock.stock_id = g_strdup (stock_id);
+}
 
 static void 
 gtk_drag_set_icon_window (GdkDragContext *context,
@@ -1823,7 +2073,7 @@ gtk_drag_set_icon_window (GdkDragContext *context,
   g_return_if_fail (context != NULL);
   g_return_if_fail (widget != NULL);
 
-  info = g_dataset_get_data (context, "gtk-info");
+  info = gtk_drag_get_source_info (context, FALSE);
   gtk_drag_remove_icon (info);
 
   info->icon_window = widget;
@@ -1843,17 +2093,19 @@ gtk_drag_set_icon_window (GdkDragContext *context,
   info->destroy_icon = destroy_on_release;
 }
 
-/*************************************************************
+/**
  * gtk_drag_set_icon_widget:
- *     Set a widget as the icon for a drag.
- *   arguments:
- *     context:
- *     widget:
- *     hot_x:    Hot spot
- *     hot_y:
- *   results:
- *************************************************************/
-
+ * @context: the context for a drag. (This must be called 
+          with a  context for the source side of a drag)
+ * @widget: a toplevel window to use as an icon.
+ * @hot_x: the X offset within @widget of the hotspot.
+ * @hot_y: the Y offset within @widget of the hotspot.
+ * 
+ * Changes the icon for a widget to a given widget. GTK+
+ * will not destroy the icon, so if you don't want
+ * it to persist, you should connect to the "drag_end" 
+ * signal and destroy it yourself.
+ **/
 void 
 gtk_drag_set_icon_widget (GdkDragContext    *context,
                          GtkWidget         *widget,
@@ -1866,19 +2118,123 @@ gtk_drag_set_icon_widget (GdkDragContext    *context,
   gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
 }
 
-/*************************************************************
- * gtk_drag_set_icon_pixmap:
- *     Set a widget as the icon for a drag.
- *   arguments:
- *     context:
- *     colormap: Colormap for the icon window.
- *     pixmap:   
- *     mask:
- *     hot_x:    Hot spot
- *     hot_y:
- *   results:
- *************************************************************/
+static void
+set_icon_stock_pixbuf (GdkDragContext    *context,
+                      const gchar       *stock_id,
+                      GdkPixbuf         *pixbuf,
+                      gint               hot_x,
+                      gint               hot_y)
+{
+  GtkWidget *window;
+  gint width, height;
+  GdkPixmap *pixmap;
+  GdkPixmap *mask;
+  
+  g_return_if_fail (context != NULL);
+  g_return_if_fail (pixbuf != NULL || stock_id != NULL);
+  g_return_if_fail (pixbuf == NULL || stock_id == NULL);
+
+  gtk_widget_push_colormap (gdk_rgb_get_colormap ());
+  window = gtk_window_new (GTK_WINDOW_POPUP);
+  gtk_widget_pop_colormap ();
+
+  gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
+  gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
+  
+  if (stock_id)
+    {
+      pixbuf = gtk_widget_render_icon (window, stock_id,
+                                      GTK_ICON_SIZE_DND, NULL);
+
+      if (!pixbuf)
+       {
+         g_warning ("Cannot load drag icon from stock_id %s", stock_id);
+         gtk_widget_destroy (window);
+         return;
+       }
+
+    }
+  
+  width = gdk_pixbuf_get_width (pixbuf);
+  height = gdk_pixbuf_get_width (pixbuf);
+
+  gtk_widget_set_usize (window,
+                       gdk_pixbuf_get_width (pixbuf),
+                       gdk_pixbuf_get_height (pixbuf));
+  gtk_widget_realize (window);
+
+  gdk_pixbuf_render_pixmap_and_mask (pixbuf, &pixmap, &mask, 128);
+  
+  gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
+  
+  if (mask)
+    gtk_widget_shape_combine_mask (window, mask, 0, 0);
 
+  g_object_unref (G_OBJECT (pixmap));
+  g_object_unref (G_OBJECT (mask));
+
+  gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
+}
+
+/**
+ * gtk_drag_set_icon_pixbuf:
+ * @context: the context for a drag. (This must be called 
+ *            with a  context for the source side of a drag)
+ * @pixbuf: the #GdkPixbuf to use as the drag icon.
+ * @hot_x: the X offset within @widget of the hotspot.
+ * @hot_y: the Y offset within @widget of the hotspot.
+ * 
+ * Sets @pixbuf as the icon for a given drag.
+ **/
+void 
+gtk_drag_set_icon_pixbuf  (GdkDragContext *context,
+                          GdkPixbuf      *pixbuf,
+                          gint            hot_x,
+                          gint            hot_y)
+{
+  g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
+  g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
+  
+  set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
+}
+
+/**
+ * gtk_drag_set_icon_pixbuf:
+ * @context: the context for a drag. (This must be called 
+ *            with a  context for the source side of a drag)
+ * @stock: the ID of the stock icon to use for the drag.
+ * @hot_x: the X offset within the icon of the hotspot.
+ * @hot_y: the Y offset within the icon of the hotspot.
+ * 
+ * Sets the the icon for a given drag from a stock ID.
+ **/
+void 
+gtk_drag_set_icon_stock  (GdkDragContext *context,
+                         const gchar    *stock_id,
+                         gint            hot_x,
+                         gint            hot_y)
+{
+  g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
+  g_return_if_fail (stock_id != NULL);
+  
+  set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
+}
+
+/**
+ * gtk_drag_set_icon_pixmap:
+ * @context: the context for a drag. (This must be called 
+ *            with a  context for the source side of a drag)
+ * @colormap: the colormap of the icon 
+ * @pixmap: the image data for the icon 
+ * @mask: the transparency mask for the icon
+ * @hot_x: the X offset within @pixmap of the hotspot.
+ * @hot_y: the Y offset within @pixmap of the hotspot.
+ * 
+ * Sets @pixmap as the icon for a given drag. GTK+ retains a
+ * reference count for the arguments, and will release them when
+ * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
+ * will be more convenient to use.
+ **/
 void 
 gtk_drag_set_icon_pixmap (GdkDragContext    *context,
                          GdkColormap       *colormap,
@@ -1896,14 +2252,12 @@ gtk_drag_set_icon_pixmap (GdkDragContext    *context,
 
   gdk_window_get_size (pixmap, &width, &height);
 
-  gtk_widget_push_visual (gdk_colormap_get_visual (colormap));
   gtk_widget_push_colormap (colormap);
 
   window = gtk_window_new (GTK_WINDOW_POPUP);
   gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
   gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
 
-  gtk_widget_pop_visual ();
   gtk_widget_pop_colormap ();
 
   gtk_widget_set_usize (window, width, height);
@@ -1917,51 +2271,43 @@ gtk_drag_set_icon_pixmap (GdkDragContext    *context,
   gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
 }
 
-/*************************************************************
+/**
  * gtk_drag_set_icon_default:
- *     Set the icon for a drag to the default icon.
- *   arguments:
- *     context:
- *   results:
- *************************************************************/
-
+ * @context: the context for a drag. (This must be called 
+             with a  context for the source side of a drag)
+ * 
+ * Sets the icon for a particular drag to the default
+ * icon.
+ **/
 void 
 gtk_drag_set_icon_default (GdkDragContext    *context)
 {
   g_return_if_fail (context != NULL);
 
   if (!default_icon_pixmap)
-    {
-      default_icon_colormap = gdk_colormap_get_system ();
-      default_icon_pixmap = 
-       gdk_pixmap_colormap_create_from_xpm_d (NULL,
-                                              default_icon_colormap,
-                                              &default_icon_mask,
-                                              NULL, drag_default_xpm);
-      default_icon_hot_x = -2;
-      default_icon_hot_y = -2;
-    }
-
-  gtk_drag_set_icon_pixmap (context, 
-                           default_icon_colormap, 
-                           default_icon_pixmap, 
-                           default_icon_mask,
-                           default_icon_hot_x,
-                           default_icon_hot_y);
+    gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
+  else
+    gtk_drag_set_icon_pixmap (context, 
+                             default_icon_colormap, 
+                             default_icon_pixmap, 
+                             default_icon_mask,
+                             default_icon_hot_x,
+                             default_icon_hot_y);
 }
 
-/*************************************************************
+/**
  * gtk_drag_set_default_icon:
- *     Set a default icon for all drags as a pixmap.
- *   arguments:
- *     colormap: Colormap for the icon window.
- *     pixmap:   
- *     mask:
- *     hot_x:    Hot spot
- *     hot_y:
- *   results:
- *************************************************************/
-
+ * @colormap: the colormap of the icon
+ * @pixmap: the image data for the icon
+ * @mask: the transparency mask for an image.
+ * @hot_x: The X offset within @widget of the hotspot.
+ * @hot_y: The Y offset within @widget of the hotspot.
+ * 
+ * Changes the default drag icon. GTK+ retains a reference count for the
+ * arguments, and will release them when they are no longer needed.
+ * This function is obsolete. The default icon should now be changed
+ * via the stock system by changing the stock pixbuf for GTK_STOCK_DND.
+ **/
 void 
 gtk_drag_set_default_icon (GdkColormap   *colormap,
                           GdkPixmap     *pixmap,
@@ -1977,7 +2323,7 @@ gtk_drag_set_default_icon (GdkColormap   *colormap,
   if (default_icon_pixmap)
     gdk_pixmap_unref (default_icon_pixmap);
   if (default_icon_mask)
-    gdk_pixmap_unref (default_icon_pixmap);
+    gdk_pixmap_unref (default_icon_mask);
 
   default_icon_colormap = colormap;
   gdk_colormap_ref (colormap);
@@ -1995,7 +2341,7 @@ gtk_drag_set_default_icon (GdkColormap   *colormap,
 
 
 /*************************************************************
- * gtk_drag_source_handle_event:
+ * _gtk_drag_source_handle_event:
  *     Called from widget event handling code on Drag events
  *     for drag sources.
  *
@@ -2006,8 +2352,8 @@ gtk_drag_set_default_icon (GdkColormap   *colormap,
  *************************************************************/
 
 void
-gtk_drag_source_handle_event (GtkWidget *widget,
-                             GdkEvent  *event)
+_gtk_drag_source_handle_event (GtkWidget *widget,
+                              GdkEvent  *event)
 {
   GtkDragSourceInfo *info;
   GdkDragContext *context;
@@ -2016,7 +2362,7 @@ gtk_drag_source_handle_event (GtkWidget *widget,
   g_return_if_fail (event != NULL);
 
   context = event->dnd.context;
-  info = g_dataset_get_data (context, "gtk-info");
+  info = gtk_drag_get_source_info (context, FALSE);
   if (!info)
     return;
 
@@ -2032,8 +2378,14 @@ gtk_drag_source_handle_event (GtkWidget *widget,
              {
                if (info->proxy_dest->proxy_drop_wait)
                  {
+                   gboolean result = context->action != 0;
+                   
                    /* Aha - we can finally pass the MOTIF DROP on... */
-                   gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
+                   gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
+                   if (result)
+                     gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
+                   else
+                     gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
                  }
                else
                  {
@@ -2048,27 +2400,20 @@ gtk_drag_source_handle_event (GtkWidget *widget,
            cursor = gtk_drag_get_cursor (event->dnd.context->action);
            if (info->cursor != cursor)
              {
-#if GDK_WINDOWING == GDK_WINDOWING_X11
-               XChangeActivePointerGrab (GDK_WINDOW_XDISPLAY (widget->window), 
-                                         PointerMotionMask | PointerMotionHintMask | ButtonReleaseMask,
-                                         ((GdkCursorPrivate *)cursor)->xcursor,
-                                         event->dnd.time);
-#elif GDK_WINDOWING == GDK_WINDOWING_WIN32
                gdk_pointer_grab (widget->window, FALSE,
                                  GDK_POINTER_MOTION_MASK |
                                  GDK_POINTER_MOTION_HINT_MASK |
                                  GDK_BUTTON_RELEASE_MASK,
                                  NULL,
-                                 info->cursor, event->dnd.time);
-#endif
+                                 cursor, event->dnd.time);
                info->cursor = cursor;
              }
            
            if (info->last_event)
              {
-               gtk_drag_motion_cb (info->widget, 
-                                   (GdkEventMotion *)info->last_event, 
-                                   info);
+               gtk_drag_update (info,
+                                info->cur_x, info->cur_y,
+                                info->last_event);
                info->last_event = NULL;
              }
          }
@@ -2189,7 +2534,7 @@ gtk_drag_drop_finished (GtkDragSourceInfo *info,
          /* Mark the context as dead, so if the destination decides
           * to respond really late, we still are OK.
           */
-         g_dataset_set_data (info->context, "gtk-info", NULL);
+         gtk_drag_clear_source_info (info->context);
          gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
        }
     }
@@ -2277,6 +2622,7 @@ gtk_drag_source_event_cb (GtkWidget      *widget,
                          gpointer        data)
 {
   GtkDragSourceSite *site;
+  gboolean retval = FALSE;
   site = (GtkDragSourceSite *)data;
 
   switch (event->type)
@@ -2292,9 +2638,7 @@ gtk_drag_source_event_cb (GtkWidget      *widget,
       
     case GDK_BUTTON_RELEASE:
       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
-       {
-         site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
-       }
+       site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
       break;
       
     case GDK_MOTION_NOTIFY:
@@ -2310,9 +2654,9 @@ gtk_drag_source_event_cb (GtkWidget      *widget,
                  GDK_BUTTON1_MASK << (i - 1))
                break;
            }
-         
-         if (MAX (ABS (site->x - event->motion.x),
-                  ABS (site->y - event->motion.y)) > 3)
+
+         if (gtk_drag_check_threshold (widget, site->x, site->y,
+                                       event->motion.x, event->motion.y))
            {
              GtkDragSourceInfo *info;
              GdkDragContext *context;
@@ -2322,21 +2666,39 @@ gtk_drag_source_event_cb (GtkWidget      *widget,
                                        site->actions, 
                                        i, event);
 
-             
-             info = g_dataset_get_data (context, "gtk-info");
+             info = gtk_drag_get_source_info (context, FALSE);
 
              if (!info->icon_window)
                {
-                 if (site->pixmap)
-                   gtk_drag_set_icon_pixmap (context,
-                                             site->colormap,
-                                             site->pixmap,
-                                             site->mask, -2, -2);
-                 else
-                   gtk_drag_set_icon_default (context);
+                 switch (site->icon_type)
+                   {
+                   case GTK_IMAGE_EMPTY:
+                     gtk_drag_set_icon_default (context);
+                     break;
+                   case GTK_IMAGE_PIXMAP:
+                     gtk_drag_set_icon_pixmap (context,
+                                               site->colormap,
+                                               site->icon_data.pixmap.pixmap,
+                                               site->icon_mask,
+                                               -2, -2);
+                     break;
+                   case GTK_IMAGE_PIXBUF:
+                     gtk_drag_set_icon_pixbuf (context,
+                                               site->icon_data.pixbuf.pixbuf,
+                                               -2, -2);
+                     break;
+                   case GTK_IMAGE_STOCK:
+                     gtk_drag_set_icon_stock (context,
+                                              site->icon_data.stock.stock_id,
+                                              -2, -2);
+                     break;
+                   default:
+                     g_assert_not_reached();
+                     break;
+                   }
                }
 
-             return TRUE;
+             retval = TRUE;
            }
        }
       break;
@@ -2344,7 +2706,8 @@ gtk_drag_source_event_cb (GtkWidget      *widget,
     default:                   /* hit for 2/3BUTTON_PRESS */
       break;
     }
-  return FALSE;
+  
+  return retval;
 }
 
 static void 
@@ -2355,12 +2718,7 @@ gtk_drag_source_site_destroy (gpointer data)
   if (site->target_list)
     gtk_target_list_unref (site->target_list);
 
-  if (site->pixmap)
-    gdk_pixmap_unref (site->pixmap);
-  
-  if (site->mask)
-    gdk_pixmap_unref (site->mask);
-  
+  gtk_drag_source_unset_icon (site);
   g_free (site);
 }
 
@@ -2477,11 +2835,9 @@ gtk_drag_remove_icon (GtkDragSourceInfo *info)
 }
 
 static void
-gtk_drag_source_info_destroy (gpointer data)
+gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
 {
-  GtkDragSourceInfo *info = data;
-
-  gtk_drag_remove_icon (data);
+  gtk_drag_remove_icon (info);
 
   if (!info->proxy_dest)
     gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end", 
@@ -2498,7 +2854,7 @@ gtk_drag_source_info_destroy (gpointer data)
 
   gtk_target_list_unref (info->target_list);
 
-  g_dataset_set_data (info->context, "gtk-info", NULL);
+  gtk_drag_clear_source_info (info->context);
   gdk_drag_context_unref (info->context);
 
   if (info->drop_timeout)
@@ -2508,41 +2864,36 @@ gtk_drag_source_info_destroy (gpointer data)
 }
 
 /*************************************************************
- * gtk_drag_motion_cb:
- *     "motion_notify_event" callback during drag.
+ * gtk_drag_update:
+ *     Function to update the status of the drag when the
+ *     cursor moves or the modifier changes
  *   arguments:
- *     
+ *     info: DragSourceInfo for the drag
+ *     x_root, y_root: position of darg
+ *     event: The event that triggered this call
  *   results:
  *************************************************************/
 
-static gint
-gtk_drag_motion_cb (GtkWidget      *widget, 
-                   GdkEventMotion *event, 
-                   gpointer        data)
+static void
+gtk_drag_update (GtkDragSourceInfo *info,
+                gint               x_root,
+                gint               y_root,
+                GdkEvent          *event)
 {
-  GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
-  GdkAtom selection;
   GdkDragAction action;
   GdkDragAction possible_actions;
   GdkWindow *window = NULL;
   GdkWindow *dest_window;
   GdkDragProtocol protocol;
-  gint x_root, y_root;
-
-  if (event->is_hint)
-    {
-      gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL);
-      event->x_root = x_root;
-      event->y_root = y_root;
-    }
+  GdkAtom selection;
+  guint32 time = gtk_drag_get_event_time (event);
 
-  gtk_drag_get_event_actions ((GdkEvent *)event, 
+  gtk_drag_get_event_actions (event,
                              info->button, 
                              info->possible_actions,
                              &action, &possible_actions);
-  
-  info->cur_x = event->x_root;
-  info->cur_y = event->y_root;
+  info->cur_x = x_root;
+  info->cur_y = y_root;
 
   if (info->icon_window)
     {
@@ -2554,79 +2905,66 @@ gtk_drag_motion_cb (GtkWidget      *widget,
     }
   
   gdk_drag_find_window (info->context,
-                       window, event->x_root, event->y_root,
+                       window, x_root, y_root,
                        &dest_window, &protocol);
-
+  
   if (gdk_drag_motion (info->context, dest_window, protocol,
-                      event->x_root, event->y_root, action, 
+                      x_root, y_root, action, 
                       possible_actions,
-                      event->time))
+                      time))
+    {
+      if (info->last_event != event) /* Paranoia, should not happen */
+       {
+         if (info->last_event)
+           gdk_event_free ((GdkEvent *)info->last_event);
+         info->last_event = gdk_event_copy ((GdkEvent *)event);
+       }
+    }
+  else
     {
       if (info->last_event)
-       gdk_event_free ((GdkEvent *)info->last_event);
-      
-      info->last_event = gdk_event_copy ((GdkEvent *)event);
+       {
+         gdk_event_free ((GdkEvent *)info->last_event);
+         info->last_event = NULL;
+       }
     }
-
+  
   if (dest_window)
     gdk_window_unref (dest_window);
 
   selection = gdk_drag_get_selection (info->context);
   if (selection)
-    gtk_drag_source_check_selection (info, selection, event->time);
-
-#if 0
-  /* We ignore the response, so we can respond precisely to the drop
-   */
-  if (event->is_hint)
-    gdk_window_get_pointer (widget->window, NULL, NULL, NULL);
-#endif  
-
-  return TRUE;
+    gtk_drag_source_check_selection (info, selection, time);
 }
 
 /*************************************************************
- * gtk_drag_motion_cb:
- *     "button_release_event" callback during drag.
+ * gtk_drag_end:
+ *     Called when the user finishes to drag, either by
+ *     releasing the mouse, or by pressing Esc.
  *   arguments:
- *     
+ *     widget: GtkInvisible widget for this drag
+ *     info: 
  *   results:
  *************************************************************/
 
-static gint
-gtk_drag_button_release_cb (GtkWidget      *widget, 
-                           GdkEventButton *event, 
-                           gpointer        data)
+static void
+gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
 {
-  GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
-  GtkWidget *source_widget = info->widget;
   GdkEvent send_event;
+  GtkWidget *source_widget = info->widget;
 
-  gtk_widget_ref (source_widget);
-
-  if (event->button != info->button)
-    return FALSE;
+  gdk_pointer_ungrab (time);
+  gdk_keyboard_ungrab (time);
 
-  gdk_pointer_ungrab (event->time);
+  gtk_grab_remove (info->ipc_widget);
 
-  gtk_grab_remove (widget);
-  gtk_signal_disconnect_by_func (GTK_OBJECT (widget), 
+  gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget), 
                                 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb),
                                 info);
-  gtk_signal_disconnect_by_func (GTK_OBJECT (widget), 
+  gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget), 
                                 GTK_SIGNAL_FUNC (gtk_drag_motion_cb),
                                 info);
 
-  if ((info->context->action != 0) && (info->context->dest_window != NULL))
-    {
-      gtk_drag_drop (info, event->time);
-    }
-  else
-    {
-      gdk_drag_abort (info->context, event->time);
-      gtk_drag_drop_finished (info, FALSE, event->time);
-    }
-
   /* Send on a release pair to the the original 
    * widget to convince it to release its grab. We need to
    * call gtk_propagate_event() here, instead of 
@@ -2637,23 +2975,119 @@ gtk_drag_button_release_cb (GtkWidget      *widget,
   send_event.button.type = GDK_BUTTON_RELEASE;
   send_event.button.window = GDK_ROOT_PARENT ();
   send_event.button.send_event = TRUE;
-  send_event.button.time = event->time;
+  send_event.button.time = time;
   send_event.button.x = 0;
   send_event.button.y = 0;
-  send_event.button.pressure = 0.;
-  send_event.button.xtilt = 0.;
-  send_event.button.ytilt = 0.;
-  send_event.button.state = event->state;
-  send_event.button.button = event->button;
-  send_event.button.source = GDK_SOURCE_PEN;
-  send_event.button.deviceid = GDK_CORE_POINTER;
+  send_event.button.axes = NULL;
+  send_event.button.state = 0;
+  send_event.button.button = info->button;
+  send_event.button.device = gdk_device_get_core_pointer ();
   send_event.button.x_root = 0;
   send_event.button.y_root = 0;
 
   gtk_propagate_event (source_widget, &send_event);
+}
+
+/*************************************************************
+ * gtk_drag_motion_cb:
+ *     "motion_notify_event" callback during drag.
+ *   arguments:
+ *     
+ *   results:
+ *************************************************************/
+
+static gint
+gtk_drag_motion_cb (GtkWidget      *widget, 
+                   GdkEventMotion *event, 
+                   gpointer        data)
+{
+  GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
+  gint x_root, y_root;
+
+  if (event->is_hint)
+    {
+      gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL);
+      event->x_root = x_root;
+      event->y_root = y_root;
+    }
 
-  gtk_widget_unref (source_widget);
+  gtk_drag_update (info, event->x_root, event->y_root, (GdkEvent *)event);
+
+  return TRUE;
+}
+
+/*************************************************************
+ * gtk_drag_key_cb:
+ *     "key_press/release_event" callback during drag.
+ *   arguments:
+ *     
+ *   results:
+ *************************************************************/
+
+static gint 
+gtk_drag_key_cb (GtkWidget         *widget, 
+                GdkEventKey       *event, 
+                gpointer           data)
+{
+  GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
+  GdkModifierType state;
   
+  if (event->type == GDK_KEY_PRESS)
+    {
+      if (event->keyval == GDK_Escape)
+       {
+         gtk_drag_end (info, event->time);
+         gdk_drag_abort (info->context, event->time);
+         gtk_drag_drop_finished (info, FALSE, event->time);
+
+         return TRUE;
+       }
+    }
+
+  /* Now send a "motion" so that the modifier state is updated */
+
+  /* The state is not yet updated in the event, so we need
+   * to query it here. We could use XGetModifierMapping, but
+   * that would be overkill.
+   */
+  gdk_window_get_pointer (GDK_ROOT_PARENT(), NULL, NULL, &state);
+
+  event->state = state;
+  gtk_drag_update (info, info->cur_x, info->cur_y, (GdkEvent *)event);
+
+  return TRUE;
+}
+
+/*************************************************************
+ * gtk_drag_button_release_cb:
+ *     "button_release_event" callback during drag.
+ *   arguments:
+ *     
+ *   results:
+ *************************************************************/
+
+static gint
+gtk_drag_button_release_cb (GtkWidget      *widget, 
+                           GdkEventButton *event, 
+                           gpointer        data)
+{
+  GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
+
+  if (event->button != info->button)
+    return FALSE;
+
+  gtk_drag_end (info, event->time);
+
+  if ((info->context->action != 0) && (info->context->dest_window != NULL))
+    {
+      gtk_drag_drop (info, event->time);
+    }
+  else
+    {
+      gdk_drag_abort (info->context, event->time);
+      gtk_drag_drop_finished (info, FALSE, event->time);
+    }
+
   return TRUE;
 }
 
@@ -2663,11 +3097,42 @@ gtk_drag_abort_timeout (gpointer data)
   GtkDragSourceInfo *info = data;
   guint32 time = GDK_CURRENT_TIME;
 
+  GDK_THREADS_ENTER ();
+
   if (info->proxy_dest)
     time = info->proxy_dest->proxy_drop_time;
 
   info->drop_timeout = 0;
   gtk_drag_drop_finished (info, FALSE, time);
   
+  GDK_THREADS_LEAVE ();
+  
   return FALSE;
 }
+
+/**
+ * gtk_drag_check_threshold:
+ * @widget: a #GtkWidget
+ * @start_x: X coordinate of start of drag
+ * @start_y: Y coordinate of start of drag
+ * @current_x: current X coordinate
+ * @current_y: current Y coordinate
+ * 
+ * Checks to see if a mouse drag starting at (start_x, start_y) and ending
+ * at (current_x, current_y) has passed the GTK drag threshhold, and thus
+ * should trigger the beginning of a drag-and-drop operation.
+ *
+ * Return Value: If the drag threshold has been passed.
+ **/
+gboolean
+gtk_drag_check_threshold (GtkWidget *widget,
+                         gint       start_x,
+                         gint       start_y,
+                         gint       current_x,
+                         gint       current_y)
+{
+#define DRAG_THRESHOLD 8
+
+  return (ABS (current_x - start_x) > DRAG_THRESHOLD ||
+         ABS (current_y - start_y) > DRAG_THRESHOLD);
+}