diff --git a/gdk/quartz/GdkQuartzWindow.c b/gdk/quartz/GdkQuartzWindow.c
index 282b65b..a974bda 100644
--- a/gdk/quartz/GdkQuartzWindow.c
+++ b/gdk/quartz/GdkQuartzWindow.c
@@ -401,8 +401,29 @@ drag_operation_to_drag_action (NSDragOperation operation)
 {
   GdkDragAction result = 0;
 
+  /* GDK and Quartz drag operations do not map 1:1.
+   * This mapping represents about the best that we
+   * can come up.
+   *
+   * Note that NSDragOperationPrivate and GDK_ACTION_PRIVATE
+   * have almost opposite meanings: the GDK one means that the
+   * destination is solely responsible for the action; the Quartz
+   * one means that the source and destination will agree
+   * privately on the action. NSOperationGeneric is close in meaning
+   * to GDK_ACTION_PRIVATE but there is a problem: it will be
+   * sent for any ordinary drag, and likely not understood
+   * by any intra-widget drag (since the source & dest are the
+   * same).
+   */
+
   if (operation & NSDragOperationGeneric)
+    result |= GDK_ACTION_MOVE;
+  if (operation & NSDragOperationCopy)
     result |= GDK_ACTION_COPY;
+  if (operation & NSDragOperationMove)
+    result |= GDK_ACTION_MOVE;
+  if (operation & NSDragOperationLink)
+    result |= GDK_ACTION_LINK;
 
   return result;
 }
@@ -414,6 +435,10 @@ drag_action_to_drag_operation (GdkDragAction action)
 
   if (action & GDK_ACTION_COPY)
     result |= NSDragOperationCopy;
+  if (action & GDK_ACTION_LINK)
+    result |= NSDragOperationLink;
+  if (action & GDK_ACTION_MOVE)
+    result |= NSDragOperationMove;
 
   return result;
 }
@@ -425,6 +450,7 @@ update_context_from_dragging_info (id <NSDraggingInfo> sender)
 
   GDK_DRAG_CONTEXT_PRIVATE (current_context)->dragging_info = sender;
   current_context->suggested_action = drag_operation_to_drag_action ([sender draggingSourceOperationMask]);
+  current_context->actions = current_context->suggested_action;
 }
 
 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
@@ -450,6 +476,10 @@ update_context_from_dragging_info (id <NSDraggingInfo> sender)
 
 - (void)draggingEnded:(id <NSDraggingInfo>)sender
 {
+  /* leave a note for the source about what action was taken */
+  if (_gdk_quartz_drag_source_context && current_context)
+   _gdk_quartz_drag_source_context->action = current_context->action;
+
   if (current_context)
     g_object_unref (current_context);
   current_context = NULL;
diff --git a/gdk/quartz/gdkdnd-quartz.c b/gdk/quartz/gdkdnd-quartz.c
index 40e90af..948b4dd 100644
--- a/gdk/quartz/gdkdnd-quartz.c
+++ b/gdk/quartz/gdkdnd-quartz.c
@@ -101,6 +101,12 @@ gdk_drag_context_unref (GdkDragContext *context)
 
 GdkDragContext *_gdk_quartz_drag_source_context = NULL;
 
+GdkDragContext *
+gdk_quartz_drag_source_context ()
+{
+  return _gdk_quartz_drag_source_context;
+}
+
 GdkDragContext * 
 gdk_drag_begin (GdkWindow     *window,
 		GList         *targets)
diff --git a/gtk/gtkdnd-quartz.c b/gtk/gtkdnd-quartz.c
index fd66836..57ba2a1 100644
--- a/gtk/gtkdnd-quartz.c
+++ b/gtk/gtkdnd-quartz.c
@@ -63,6 +63,11 @@ static GtkDragDestInfo *gtk_drag_get_dest_info  (GdkDragContext   *context,
 						 gboolean          create);
 static void gtk_drag_source_site_destroy        (gpointer           data);
 
+static GtkDragSourceInfo *gtk_drag_get_source_info (GdkDragContext *context,
+						    gboolean        create);
+
+extern GdkDragContext *gdk_quartz_drag_source_context (); /* gdk/quartz/gdkdnd-quartz.c */
+
 struct _GtkDragSourceSite 
 {
   GdkModifierType    start_button_mask;
@@ -89,13 +94,16 @@ struct _GtkDragSourceSite
 
 struct _GtkDragSourceInfo 
 {
+  GtkWidget         *source_widget;
   GtkWidget         *widget;
   GtkTargetList     *target_list; /* Targets for drag data */
   GdkDragAction      possible_actions; /* Actions allowed by source */
   GdkDragContext    *context;	  /* drag context */
-
+  NSEvent           *nsevent;     /* what started it */
   gint hot_x, hot_y;		  /* Hot spot for drag */
   GdkPixbuf         *icon_pixbuf;
+  gboolean           success;
+  gboolean           delete;
 };
 
 struct _GtkDragDestSite 
@@ -233,19 +241,24 @@ gtk_drag_get_data (GtkWidget      *widget,
     }      
 }
 
-
-GtkWidget *
-gtk_drag_get_source_widget (GdkDragContext *context)
-{
-  return NULL;
-}
-
 void 
 gtk_drag_finish (GdkDragContext *context,
 		 gboolean        success,
 		 gboolean        del,
 		 guint32         time)
 {
+  GtkDragSourceInfo *info;
+  GdkDragContext* source_context = gdk_quartz_drag_source_context ();
+
+  if (source_context)
+    {
+      info = gtk_drag_get_source_info (source_context, FALSE);
+      if (info)
+        {
+          info->success = success;
+          info->delete = del;
+        }
+    }
 }
 
 static void
@@ -307,6 +320,22 @@ gtk_drag_clear_source_info (GdkDragContext *context)
   g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
 }
 
+GtkWidget *
+gtk_drag_get_source_widget (GdkDragContext *context)
+{
+  GtkDragSourceInfo *info;
+  GdkDragContext* real_source_context = gdk_quartz_drag_source_context();
+
+  if (!real_source_context)
+    return NULL;
+
+  info = gtk_drag_get_source_info (real_source_context, FALSE);
+  if (!info)
+     return NULL;
+
+  return info->source_widget;
+}
+
 /*************************************************************
  * gtk_drag_highlight_expose:
  *     Callback for expose_event for highlighted widgets.
@@ -857,6 +886,8 @@ gtk_drag_dest_drop (GtkWidget	     *widget,
 	gtk_drag_get_data (widget, context, target, time);
     }
   
+  /* leave a note for the source-side context about the action chosen */
+  
   g_signal_emit_by_name (widget, "drag-drop",
 			 context, x, y, time, &retval);
 
@@ -1031,6 +1062,45 @@ gtk_drag_dest_find_target (GtkWidget      *widget,
   return GDK_NONE;
 }
 
+static gboolean
+gtk_drag_begin_idle (gpointer arg)
+{
+  GdkDragContext* context = (GdkDragContext*) arg;
+  GtkDragSourceInfo* info = gtk_drag_get_source_info (context, FALSE);
+  NSWindow *nswindow;
+  NSPasteboard *pasteboard;
+  GtkDragSourceOwner *owner;
+  NSPoint point;
+
+  g_assert (info != NULL);
+
+  pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
+  owner = [[GtkDragSourceOwner alloc] initWithInfo:info];
+
+  [pasteboard declareTypes:_gtk_quartz_target_list_to_pasteboard_types (info->target_list) owner:owner];
+
+  if ((nswindow = get_toplevel_nswindow (info->source_widget)) == NULL)
+     return FALSE;
+  
+  /* Ref the context. It's unreffed when the drag has been aborted */
+  g_object_ref (info->context);
+
+  /* FIXME: If the event isn't a mouse event, use the global cursor position instead */
+  point = [info->nsevent locationInWindow];
+
+  [nswindow dragImage:_gtk_quartz_create_image_from_pixbuf (info->icon_pixbuf)
+                   at:point
+               offset:NSMakeSize(0, 0)
+                event:info->nsevent
+           pasteboard:pasteboard
+               source:nswindow
+            slideBack:YES];
+
+  [info->nsevent release];
+
+  return FALSE;
+}
+
 static GdkDragContext *
 gtk_drag_begin_internal (GtkWidget         *widget,
 			 GtkDragSourceSite *site,
@@ -1042,16 +1112,13 @@ gtk_drag_begin_internal (GtkWidget         *widget,
   GtkDragSourceInfo *info;
   GdkDragContext *context;
   NSWindow *nswindow;
-  NSPasteboard *pasteboard;
-  GtkDragSourceOwner *owner;
-  NSEvent *nsevent;
-  NSPoint point;
 
   context = gdk_drag_begin (NULL, NULL);
   context->is_source = TRUE;
 
   info = gtk_drag_get_source_info (context, TRUE);
   
+  info->source_widget = g_object_ref (widget);
   info->widget = g_object_ref (widget);
   info->target_list = target_list;
   gtk_target_list_ref (target_list);
@@ -1086,13 +1153,13 @@ gtk_drag_begin_internal (GtkWidget         *widget,
 		GdkPixbuf *pixbuf;
 
 		pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 1, 1);
-		gdk_pixbuf_fill (pixbuf, 0xffffff);
-	    
-		gtk_drag_set_icon_pixbuf (context,
-					  pixbuf,
+ 		gdk_pixbuf_fill (pixbuf, 0xffffff);
+
+ 		gtk_drag_set_icon_pixbuf (context,
+ 					  pixbuf,
 					  0, 0);
 
-		g_object_unref (pixbuf);
+ 		g_object_unref (pixbuf);
 	      }
 	    break;
 	  case GTK_IMAGE_PIXBUF:
@@ -1117,31 +1184,17 @@ gtk_drag_begin_internal (GtkWidget         *widget,
 	  }
     }
 
-  gdk_pointer_ungrab (0);
-  
-  pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
-  owner = [[GtkDragSourceOwner alloc] initWithInfo:info];
-
-  [pasteboard declareTypes:_gtk_quartz_target_list_to_pasteboard_types (target_list) owner:owner];
-
-  /* Ref the context. It's unreffed when the drag has been aborted */
-  g_object_ref (info->context);
-
   nswindow = get_toplevel_nswindow (widget);
+  info->nsevent = [nswindow currentEvent];
+  [info->nsevent retain];
 
-  /* FIXME: If the event isn't a mouse event, use the global cursor position instead */
-  nsevent = [nswindow currentEvent];
-  point = [nsevent locationInWindow];
+  /* drag will begin in an idle handler to avoid nested run loops */
 
-  [nswindow dragImage:_gtk_quartz_create_image_from_pixbuf (info->icon_pixbuf)
-                   at:point
-               offset:NSMakeSize(0, 0)
-                event:nsevent
-           pasteboard:pasteboard
-               source:nswindow
-            slideBack:YES];
+  g_idle_add_full (G_PRIORITY_HIGH_IDLE, gtk_drag_begin_idle, context, NULL);
 
-  return info->context;
+  gdk_pointer_ungrab (0);
+
+  return context;
 }
 
 GdkDragContext *
@@ -1773,6 +1826,9 @@ gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
   g_signal_emit_by_name (info->widget, "drag-end", 
 			 info->context);
 
+  if (info->source_widget)
+    g_object_unref (info->source_widget);
+
   if (info->widget)
     g_object_unref (info->widget);
 
@@ -1794,6 +1850,10 @@ drag_drop_finished_idle_cb (gpointer data)
 static void
 gtk_drag_drop_finished (GtkDragSourceInfo *info)
 {
+  if (info->success && info->delete)
+    g_signal_emit_by_name (info->source_widget, "drag-data-delete",
+                           info->context);
+
   /* Workaround for the fact that the NS API blocks until the drag is
    * over. This way the context is still valid when returning from
    * drag_begin, even if it will still be quite useless. See bug #501588.
