2006-12-26 Jonathan Pobst <monkey@jpobst.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / X11Dnd.cs
index d9822d2d82350e0647ea75bec14ec03873759905..2cb4a9cfb464abd9a6e5c634d5932a69d56297ab 100644 (file)
 
 
 using System;
+using System.IO;
 using System.Text;
+using System.Drawing;
 using System.Collections;
+using System.Runtime.Serialization;
 using System.Runtime.InteropServices;
-
+using System.Runtime.Serialization.Formatters.Binary;
 
 namespace System.Windows.Forms {
 
        internal class X11Dnd {
 
+               private enum State {
+                       Accepting,
+                       Dragging
+               }
+
+               private enum DragState {
+                       None,
+                       Beginning,
+                       Dragging,
+                       Entered
+               }
+
+               private interface IDataConverter {
+                       void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent);
+                       void SetData (X11Dnd dnd, object data, ref XEvent xevent);
+               }
+
                private delegate void MimeConverter (IntPtr dsp,
                                DataObject data, ref XEvent xevent);
 
@@ -42,35 +62,231 @@ namespace System.Windows.Forms {
                        public string Name;
                        public IntPtr Type;
                        public IntPtr NonProtocol;
-                       public MimeConverter Convert;
+                       public IDataConverter Converter;
                        
-                       public MimeHandler (string name, MimeConverter converter)
+                       public MimeHandler (string name, IDataConverter converter)
                        {
                                Name = name;
-                               Convert = converter;
+                               Converter = converter;
+                       }
+
+                       public override string ToString ()
+                       {
+                               return "MimeHandler {" + Name + "}";
                        }
                }
 
                private MimeHandler [] MimeHandlers = {
 //                       new MimeHandler ("WCF_DIB"),
 //                       new MimeHandler ("image/gif", new MimeConverter (ImageConverter)),
+//                     new MimeHandler ("text/rtf", new MimeConverter (RtfConverter)),
+//                     new MimeHandler ("text/richtext", new MimeConverter (RtfConverter)),
+
+                       new MimeHandler ("text/plain", new TextConverter ()),
+                       new MimeHandler ("text/html", new HtmlConverter ()),
+                       new MimeHandler ("text/uri-list", new UriListConverter ()),
+                       new MimeHandler ("application/x-mono-serialized-object",
+                                       new SerializedObjectConverter ())
+               };
+
+               private class SerializedObjectConverter : IDataConverter {
+
+                       public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
+                       {
+                               MemoryStream stream = dnd.GetData (ref xevent);
+                               BinaryFormatter bf = new BinaryFormatter ();
+
+                               if (stream.Length == 0)
+                                       return;
+
+                               stream.Seek (0, 0);
+                               object obj = bf.Deserialize (stream);
+                               data.SetData (obj);
+                       }
+
+                       public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
+                       {
+                               if (data == null)
+                                       return;
+
+                               MemoryStream stream = new MemoryStream ();
+                               BinaryFormatter bf = new BinaryFormatter ();
+
+                               bf.Serialize (stream, data);
+
+                               IntPtr buffer = Marshal.AllocHGlobal ((int) stream.Length);
+                               stream.Seek (0, 0);
+
+                               for (int i = 0; i < stream.Length; i++) {
+                                       Marshal.WriteByte (buffer, i, (byte) stream.ReadByte ());
+                               }
+
+                               dnd.SetProperty (ref xevent, buffer, (int) stream.Length);
+                       }
+               }
+
+               private class HtmlConverter : IDataConverter {
+
+                       public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
+                       {
+                               string text = dnd.GetText (ref xevent, false);
+                               if (text == null)
+                                       return;
+                               data.SetData (DataFormats.Text, text);
+                               data.SetData (DataFormats.UnicodeText, text);
+                       }
+
+                       public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
+                       {
+                               IntPtr buffer;
+                               int len;
+                               string str = data as string;
+
+                               if (str == null)
+                                       return;
+
+                               if (xevent.SelectionRequestEvent.target == (IntPtr)Atom.XA_STRING) {
+                                       byte [] bytes = Encoding.ASCII.GetBytes (str);
+                                       buffer = Marshal.AllocHGlobal (bytes.Length);
+                                       len = bytes.Length;
+                                       for (int i = 0; i < len; i++)
+                                               Marshal.WriteByte (buffer, i, bytes [i]);
+                               } else {
+                                       buffer = Marshal.StringToHGlobalAnsi (str);
+                                       len = 0;
+                                       while (Marshal.ReadByte (buffer, len) != 0)
+                                               len++;
+                               }
+
+                               dnd.SetProperty (ref xevent, buffer, len);
+
+                               Marshal.FreeHGlobal (buffer);
+                       }
+               }
+
+               private class TextConverter : IDataConverter {
+
+                       public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
+                       {
+                               string text = dnd.GetText (ref xevent, true);
+                               if (text == null)
+                                       return;
+                               data.SetData (DataFormats.Text, text);
+                               data.SetData (DataFormats.UnicodeText, text);
+                       }
+
+                       public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
+                       {
+                               IntPtr buffer;
+                               int len;
+                               string str = data as string;
+
+                               if (data == null)
+                                       return;
+                               
+                               if (xevent.SelectionRequestEvent.target == (IntPtr)Atom.XA_STRING) {
+                                       byte [] bytes = Encoding.ASCII.GetBytes (str);
+                                       buffer = Marshal.AllocHGlobal (bytes.Length);
+                                       len = bytes.Length;
+                                       for (int i = 0; i < len; i++)
+                                               Marshal.WriteByte (buffer, i, bytes [i]);
+                               } else {
+                                       buffer = Marshal.StringToHGlobalAnsi (str);
+                                       len = 0;
+                                       while (Marshal.ReadByte (buffer, len) != 0)
+                                               len++;
+                               }
+
+                               dnd.SetProperty (ref xevent, buffer, len);
+
+                               Marshal.FreeHGlobal (buffer);
+                       }
+               }
+
+               private class UriListConverter : IDataConverter {
+
+                       public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
+                       {
+                               string text = dnd.GetText (ref xevent, false);
+                               if (text == null)
+                                       return;
+
+                               // TODO: Do this in a loop instead of just splitting
+                               ArrayList uri_list = new ArrayList ();
+                               string [] lines = text.Split (new char [] { '\r', '\n' });
+                               foreach (string line in lines) {
+                                       // # is a comment line (see RFC 2483)
+                                       if (line.StartsWith ("#"))
+                                               continue;
+                                       try {
+                                               Uri uri = new Uri (line);
+                                               uri_list.Add (uri.LocalPath);
+                                       } catch { }
+                               }
+
+                               string [] l = (string []) uri_list.ToArray (typeof (string));
+                               if (l.Length < 1)
+                                       return;
+                               data.SetData (DataFormats.FileDrop, l);
+                               data.SetData ("FileName", l [0]);
+                               data.SetData ("FileNameW", l [0]);
+                       }
+
+                       public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
+                       {
+                               string [] uri_list = data as string [];
+
+                               if (uri_list == null)
+                                       return;
+
+                               StringBuilder res = new StringBuilder ();
+                               foreach (string uri_str in uri_list) {
+                                       Uri uri = new Uri (uri_str);
+                                       res.Append (uri.ToString ());
+                                       res.Append ("\r\n");
+                               }
+
+                               IntPtr buffer = Marshal.StringToHGlobalAnsi ((string) data);
+                               int len = 0;
+                               while (Marshal.ReadByte (buffer, len) != 0)
+                                       len++;
 
+                               dnd.SetProperty (ref xevent, buffer, len);
+                       }
+               }
+
+               private class DragData {
+                       public IntPtr Window;
+                       public DragState State;
+                       public object Data;
+                       public IntPtr Action;
+                       public IntPtr [] SupportedTypes;
+                       public MouseButtons MouseState;
                        
-                       new MimeHandler ("text/rtf", new MimeConverter (RtfConverter)),
-                       new MimeHandler ("text/richtext", new MimeConverter (RtfConverter)),
-                       new MimeHandler ("text/plain", new MimeConverter (TextConverter)),
-                       new MimeHandler ("text/html", new MimeConverter (HtmlConverter)),
-                       new MimeHandler ("text/uri-list", new MimeConverter (UriListConverter)),
-               };
+                       public IntPtr LastWindow;
+                       public IntPtr LastTopLevel;
+
+                       public bool WillAccept;
+                       
+                       public void Reset ()
+                       {
+                               State = DragState.None;
+                               Data = null;
+                               SupportedTypes = null;
+                               WillAccept = false;
+                       }
+               }
+               
+               private class DropData {
+
+               }
 
                // This version seems to be the most common
-               private static readonly uint [] XdndVersion = new uint [] { 4 }; 
+               private static readonly IntPtr [] XdndVersion = new IntPtr [] { new IntPtr (4) }; 
 
                private IntPtr display;
+               private DragData drag_data;
                
-            
-               private bool initialized;
-
                private IntPtr XdndAware;
                private IntPtr XdndSelection;
                private IntPtr XdndEnter;
@@ -83,10 +299,12 @@ namespace System.Windows.Forms {
                private IntPtr XdndActionCopy;
                private IntPtr XdndActionMove;
                private IntPtr XdndActionLink;
-               private IntPtr XdndActionPrivate;
-               private IntPtr XdndActionList;
-               private IntPtr XdndActionDescription;
-               private IntPtr XdndActionAsk;
+               //private IntPtr XdndActionPrivate;
+               //private IntPtr XdndActionList;
+               //private IntPtr XdndActionDescription;
+               //private IntPtr XdndActionAsk;
+
+               //private State state;
 
                private int converts_pending;
                private bool position_recieved;
@@ -95,52 +313,221 @@ namespace System.Windows.Forms {
                private IntPtr source;
                private IntPtr toplevel;
                private DataObject data;
+
                private Control control;
                private int pos_x, pos_y;
                private DragDropEffects allowed;
                private DragEventArgs drag_event;
 
+               private Cursor CursorNo;
+               private Cursor CursorCopy;
+               private Cursor CursorMove;
+               private Cursor CursorLink;
+               // check out the TODO below
+               //private IntPtr CurrentCursorHandle;
+               
                public X11Dnd (IntPtr display)
                {
                        this.display = display;
+                       Init ();
                }
 
+               public bool InDrag()
+               {
+                       if (drag_data == null)
+                               return false;
+                       return drag_data.State != DragState.None;
+               }
+               
                public void SetAllowDrop (Hwnd hwnd, bool allow)
                {
-                       if (!initialized)
-                               Init ();
+                       int[] atoms;
 
-//                       if (hwnd.allow_drop == allow)
-//                               return;
+                       if (hwnd.allow_drop == allow)
+                               return;
+
+                       atoms = new int[XdndVersion.Length];
+                       for (int i = 0; i < XdndVersion.Length; i++) {
+                               atoms[i] = XdndVersion[i].ToInt32();
+                       }
 
-                       XChangeProperty (display, hwnd.whole_window, XdndAware,
+                       XplatUIX11.XChangeProperty (display, hwnd.whole_window, XdndAware,
                                        (IntPtr) Atom.XA_ATOM, 32,
-                                       PropertyMode.Replace, XdndVersion, allow ? 1 : 0);
-//                       hwnd.allow_drop = allow;
+                                       PropertyMode.Replace, atoms, allow ? 1 : 0);
+                       hwnd.allow_drop = allow;
+               }
+
+               public DragDropEffects StartDrag (IntPtr handle, object data,
+                               DragDropEffects allowed_effects)
+               {
+                       drag_data = new DragData ();
+                       drag_data.Window = handle;
+                       drag_data.State = DragState.Beginning;
+                       drag_data.MouseState = XplatUIX11.MouseState;
+                       drag_data.Data = data;
+                       drag_data.SupportedTypes = DetermineSupportedTypes (data);
+
+                       drag_data.Action = ActionFromEffect (allowed_effects);
+
+                       if (CursorNo == null) {
+                               // Make sure the cursors are created
+                               CursorNo = new Cursor (typeof (X11Dnd), "DnDNo.cur");
+                               CursorCopy = new Cursor (typeof (X11Dnd), "DnDCopy.cur");
+                               CursorMove = new Cursor (typeof (X11Dnd), "DnDMove.cur");
+                               CursorLink = new Cursor (typeof (X11Dnd), "DnDLink.cur");
+                       }
+
+                       drag_data.LastTopLevel = IntPtr.Zero;
+                       return DragDropEffects.Copy;
+               }
+
+               public void HandleButtonRelease (ref XEvent xevent)
+               {
+                       if (drag_data == null)
+                               return;
+
+                       if (!((drag_data.MouseState == MouseButtons.Left &&
+                                             xevent.ButtonEvent.button == 1) ||
+                                           (drag_data.MouseState == MouseButtons.Right &&
+                                                           xevent.ButtonEvent.button == 3)))
+                               return;
+
+                       if (drag_data.State == DragState.Beginning) {
+                               //state = State.Accepting;
+                       } else if (drag_data.State != DragState.None) {
+
+                               if (drag_data.WillAccept) {
+                                       SendDrop (drag_data.LastTopLevel, xevent.AnyEvent.window,
+                                                       xevent.ButtonEvent.time);
+                               }
+
+                               XplatUIX11.XUngrabPointer (display, IntPtr.Zero);
+                               drag_data.State = DragState.None;
+                               // WE can't reset the drag data yet as it is still
+                               // most likely going to be used by the SelectionRequest
+                               // handlers
+                       }
+               }
+
+               public bool HandleMotionNotify (ref XEvent xevent)
+               {
+                       if (drag_data == null)
+                               return false;
+
+                       if (drag_data.State == DragState.Beginning) {
+                               int suc;
+
+                               drag_data.State = DragState.Dragging;
+
+                               suc = XplatUIX11.XSetSelectionOwner (display, XdndSelection,
+                                               drag_data.Window,
+                                               xevent.ButtonEvent.time);
+
+                               if (suc == 0) {
+                                       Console.Error.WriteLine ("Could not take ownership of XdndSelection aborting drag.");
+                                       drag_data.Reset ();
+                                       return false;
+                               }
+
+                               suc = XplatUIX11.XGrabPointer (display, xevent.AnyEvent.window,
+                                               false,
+                                               EventMask.ButtonMotionMask |
+                                               EventMask.PointerMotionMask |
+                                               EventMask.ButtonPressMask |
+                                               EventMask.ButtonReleaseMask,
+                                               GrabMode.GrabModeAsync,
+                                               GrabMode.GrabModeAsync,
+                                               IntPtr.Zero, IntPtr.Zero/*CursorCopy.Handle*/, IntPtr.Zero);
+
+                               if (suc != 0) {
+                                       Console.Error.WriteLine ("Could not grab pointer aborting drag.");
+                                       drag_data.Reset ();
+                                       return false;
+                               }
+
+                               drag_data.State = DragState.Dragging;
+                       } else if (drag_data.State != DragState.None) {
+                               bool dnd_aware = false;
+                               IntPtr toplevel = IntPtr.Zero;
+                               IntPtr window = XplatUIX11.RootWindowHandle;
+
+                               IntPtr root, child;
+                               int x_temp, y_temp;
+                               int mask_return;
+
+                               while (XplatUIX11.XQueryPointer (display,
+                                                      window,
+                                                      out root, out child,
+                                                      out x_temp, out y_temp,
+                                                      out xevent.MotionEvent.x,
+                                                      out xevent.MotionEvent.y,
+                                                      out mask_return)) {
+                                       
+                                       if (!dnd_aware) {
+                                               dnd_aware = IsWindowDndAware (window);
+                                               if (dnd_aware) {
+                                                       toplevel = window;
+                                                       xevent.MotionEvent.x_root = x_temp;
+                                                       xevent.MotionEvent.y_root = y_temp;
+                                               }
+                                       }
+
+                                       if (child == IntPtr.Zero)
+                                               break;
+                                       
+                                       window = child;
+                               }
+
+                               if (window != drag_data.LastWindow && drag_data.State == DragState.Entered) {
+                                       drag_data.State = DragState.Dragging;
+
+                                       // TODO: Send a Leave if this is an MWF window
+
+                                       if (toplevel != drag_data.LastTopLevel)
+                                               SendLeave (drag_data.LastTopLevel, xevent.MotionEvent.window);
+                               }
+
+                               drag_data.State = DragState.Entered;
+                               if (toplevel != drag_data.LastTopLevel) {
+                                       // Entering a new toplevel window
+                                       SendEnter (toplevel, drag_data.Window, drag_data.SupportedTypes);
+                               } else {
+                                       // Already in a toplevel window, so send a position
+                                       SendPosition (toplevel, drag_data.Window,
+                                                       drag_data.Action,
+                                                       xevent.MotionEvent.x_root,
+                                                       xevent.MotionEvent.y_root,
+                                                       xevent.MotionEvent.time);
+                               }
+
+                               drag_data.LastTopLevel = toplevel;
+                               drag_data.LastWindow = window;
+                               return true;
+                       }
+                       return false;
                }
 
                // return true if the event is handled here
                public bool HandleClientMessage (ref XEvent xevent)
                {
-                       if (!initialized)
-                               Init ();
-
                        // most common so we check it first
                        if (xevent.ClientMessageEvent.message_type == XdndPosition)
-                               return HandlePositionEvent (ref xevent);
+                               return Accepting_HandlePositionEvent (ref xevent);
                        if (xevent.ClientMessageEvent.message_type == XdndEnter)
-                               return HandleEnterEvent (ref xevent);
+                               return Accepting_HandleEnterEvent (ref xevent);
                        if (xevent.ClientMessageEvent.message_type == XdndDrop)
-                               return HandleDropEvent (ref xevent);
+                               return Accepting_HandleDropEvent (ref xevent);
                        if (xevent.ClientMessageEvent.message_type == XdndLeave)
-                               return HandleLeaveEvent (ref xevent);
-                       
+                               return Accepting_HandleLeaveEvent (ref xevent);
+                       if (xevent.ClientMessageEvent.message_type == XdndStatus)
+                               return HandleStatusEvent (ref xevent);
+
                        return false;
                }
 
                public bool HandleSelectionNotifyEvent (ref XEvent xevent)
                {
-                       if (source != XGetSelectionOwner (display, XdndSelection))
+                       if (source != XplatUIX11.XGetSelectionOwner (display, XdndSelection))
                                return false;
 
                        MimeHandler handler = FindHandler ((IntPtr) xevent.SelectionEvent.target);
@@ -149,14 +536,56 @@ namespace System.Windows.Forms {
                        if (data == null)
                                data = new DataObject ();
 
-                       handler.Convert (display, data, ref xevent);
+                       handler.Converter.GetData (this, data, ref xevent);
 
                        converts_pending--;
-                       if (converts_pending <= 0 && position_recieved)
-                               SendEnterStatus ();
+                       if (converts_pending <= 0 && position_recieved) {
+                               drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
+                                       allowed, DragDropEffects.None);
+                               control.DndEnter (drag_event);
+                               SendStatus (source, drag_event.Effect);
+                               status_sent = true;
+                       }
+                       return true;
+               }
+
+               public bool HandleSelectionRequestEvent (ref XEvent xevent)
+               {
+                       if (xevent.SelectionRequestEvent.selection != XdndSelection)
+                               return false;
+
+                       MimeHandler handler = FindHandler (xevent.SelectionRequestEvent.target);
+                       if (handler == null)
+                               return false;
+
+                       handler.Converter.SetData (this, drag_data.Data, ref xevent);
+
                        return true;
                }
 
+               private void SetProperty (ref XEvent xevent, IntPtr data, int length)
+               {
+                       XEvent sel = new XEvent();
+                       sel.SelectionEvent.type = XEventName.SelectionNotify;
+                       sel.SelectionEvent.send_event = true;
+                       sel.SelectionEvent.display = display;
+                       sel.SelectionEvent.selection = xevent.SelectionRequestEvent.selection;
+                       sel.SelectionEvent.target = xevent.SelectionRequestEvent.target;
+                       sel.SelectionEvent.requestor = xevent.SelectionRequestEvent.requestor;
+                       sel.SelectionEvent.time = xevent.SelectionRequestEvent.time;
+                       sel.SelectionEvent.property = IntPtr.Zero;
+
+                       XplatUIX11.XChangeProperty (display, xevent.SelectionRequestEvent.requestor,
+                                       xevent.SelectionRequestEvent.property,
+                                       xevent.SelectionRequestEvent.target,
+                                       8, PropertyMode.Replace, data, length);
+                       sel.SelectionEvent.property = xevent.SelectionRequestEvent.property;
+
+                       XplatUIX11.XSendEvent (display, xevent.SelectionRequestEvent.requestor, false,
+                                       (IntPtr)EventMask.NoEventMask, ref sel);
+                       return;
+               }
+
                private void Reset ()
                {
                        ResetSourceData ();
@@ -175,7 +604,7 @@ namespace System.Windows.Forms {
                        status_sent = false;
                }
                
-               private bool HandleEnterEvent (ref XEvent xevent)
+               private bool Accepting_HandleEnterEvent (ref XEvent xevent)
                {
                        Reset ();
 
@@ -188,29 +617,39 @@ namespace System.Windows.Forms {
                        return true;
                }
 
-               private bool HandlePositionEvent (ref XEvent xevent)
+               private bool Accepting_HandlePositionEvent (ref XEvent xevent)
                {
-                       int x = (int) xevent.ClientMessageEvent.ptr3 >> 16;
-                       int y = (int) xevent.ClientMessageEvent.ptr3 & 0xFFFF;
+                       pos_x = (int) xevent.ClientMessageEvent.ptr3 >> 16;
+                       pos_y = (int) xevent.ClientMessageEvent.ptr3 & 0xFFFF;
 
-                       allowed = EffectFromAction (xevent.ClientMessageEvent.ptr5);
+                       // Copy is implicitly allowed
+                       allowed = EffectFromAction (xevent.ClientMessageEvent.ptr5) | DragDropEffects.Copy;
 
-                       IntPtr parent, child, new_child;
+                       IntPtr parent, child, new_child, last_drop_child;
                        parent = XplatUIX11.XRootWindow (display, 0);
                        child = toplevel;
+                       last_drop_child = IntPtr.Zero;
                        while (true) {
                                int xd, yd;
                                new_child = IntPtr.Zero;
                                
                                if (!XplatUIX11.XTranslateCoordinates (display,
-                                                   parent, child, x, y,
+                                                   parent, child, pos_x, pos_y,
                                                    out xd, out yd, out new_child))
                                        break;
                                if (new_child == IntPtr.Zero)
                                        break;
                                child = new_child;
+
+                               Hwnd h = Hwnd.ObjectFromHandle (child);
+                               Control d = Control.FromHandle (h.client_window);
+                               if (d != null && d.allow_drop)
+                                       last_drop_child = child;
                        }
 
+                       if (last_drop_child != IntPtr.Zero)
+                               child = last_drop_child;
+
                        if (target != child) {
                                // We have moved into a new control 
                                // or into a control for the first time
@@ -222,10 +661,11 @@ namespace System.Windows.Forms {
 
                        if (c == null)
                                return true;
-//                     if (!c.AllowDrop) {
-//                             Finish ();
-//                             return true;
-//                     }
+                       if (!c.allow_drop) {
+                               SendStatus (source, DragDropEffects.None);
+                               Finish ();
+                               return true;
+                       }
 
                        control = c;
                        position_recieved = true;                       
@@ -233,10 +673,16 @@ namespace System.Windows.Forms {
                        if (converts_pending > 0)
                                return true;
                        if (!status_sent) {
-                               SendEnterStatus ();
+                               drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
+                                       allowed, DragDropEffects.None);
+                               control.DndEnter (drag_event);
+                               SendStatus (source, drag_event.Effect);
+                               status_sent = true;
                        } else {
-                               SendStatus ();
+                               drag_event.x = pos_x;
+                               drag_event.y = pos_y;
                                control.DndOver (drag_event);
+                               SendStatus (source, drag_event.Effect);
                        }
                        
                        return true;
@@ -257,43 +703,81 @@ namespace System.Windows.Forms {
                        ResetTargetData ();
                }
 
-               private bool HandleDropEvent (ref XEvent xevent)
+               private bool Accepting_HandleDropEvent (ref XEvent xevent)
                {
-                       Console.WriteLine ("DROPPING EVENT");
-                       if (control != null && drag_event != null)
+                       if (control != null && drag_event != null) {
+                               drag_event = new DragEventArgs (data,
+                                               0, pos_x, pos_y,
+                                       allowed, DragDropEffects.None);
                                control.DndDrop (drag_event);
+                       }
                        SendFinished ();
                        return true;
                }
 
-               private bool HandleLeaveEvent (ref XEvent xevent)
+               private bool Accepting_HandleLeaveEvent (ref XEvent xevent)
                {
                        if (control != null && drag_event != null)
                                control.DndLeave (drag_event);
-                       Reset ();
+                       // Reset ();
+                       return true;
+               }
+
+               private bool HandleStatusEvent (ref XEvent xevent)
+               {
+                       if (drag_data != null && drag_data.State == DragState.Entered) {
+                               drag_data.WillAccept = ((int) xevent.ClientMessageEvent.ptr2 & 0x1) != 0;
+                               Cursor cursor = CursorNo;
+                               if (drag_data.WillAccept) {
+                                       // Same order as on MS
+                                       IntPtr action = xevent.ClientMessageEvent.ptr5;
+                                       if (action == XdndActionCopy)
+                                               cursor = CursorCopy;
+                                       else if (action == XdndActionLink)
+                                               cursor = CursorLink;
+                                       else if (action == XdndActionMove)
+                                               cursor = CursorMove;
+
+                               }
+
+                                       // TODO: Try not to set the cursor so much
+                               //if (cursor.Handle != CurrentCursorHandle) {
+                                       XplatUIX11.XChangeActivePointerGrab (display,
+                                                       EventMask.ButtonMotionMask |
+                                                       EventMask.PointerMotionMask |
+                                                       EventMask.ButtonPressMask |
+                                                       EventMask.ButtonReleaseMask,
+                                                       cursor.Handle, IntPtr.Zero);
+                                       //CurrentCursorHandle = cursor.Handle;
+                                       //}     
+                       }
                        return true;
                }
 
                private DragDropEffects EffectFromAction (IntPtr action)
                {
                        DragDropEffects allowed = DragDropEffects.None;
+
                        if (action == XdndActionCopy)
                                allowed = DragDropEffects.Copy;
-                       if (action == XdndActionMove)
-                               allowed = DragDropEffects.Move;
+                       else if (action == XdndActionMove)
+                               allowed |= DragDropEffects.Move;
                        if (action == XdndActionLink)
-                               allowed = DragDropEffects.Link;
+                               allowed |= DragDropEffects.Link;
                        return allowed;
                }
 
                private IntPtr ActionFromEffect (DragDropEffects effect)
                {
                        IntPtr action = IntPtr.Zero;
-                       if (effect == DragDropEffects.Copy)
+
+                       // We can't OR together actions on XDND so sadly the primary
+                       // is the only one shown here
+                       if ((effect & DragDropEffects.Copy) != 0)
                                action = XdndActionCopy;
-                       if (effect == DragDropEffects.Move)
+                       else if ((effect & DragDropEffects.Move) != 0)
                                action = XdndActionMove;
-                       if (effect == DragDropEffects.Link)
+                       else if ((effect & DragDropEffects.Link) != 0)
                                action = XdndActionLink;
                        return action;
                }
@@ -302,18 +786,46 @@ namespace System.Windows.Forms {
                {
                        bool match = false;
 
+                       if (source != XplatUIX11.XGetSelectionOwner (display, XdndSelection)) {
+                               return false;
+                       }
+
+                       Control mwfcontrol = MwfWindow (source);
+
+                       if (mwfcontrol != null && drag_data != null) {
+                               DataObject dragged = drag_data.Data as DataObject;
+                               if (dragged != null) {
+                                       data = dragged;
+                               } else {
+                                       if (data == null)
+                                               data = new DataObject ();
+                                       SetDataWithFormats (drag_data.Data);
+                               }
+                               return true;
+                       }
+
                        foreach (IntPtr atom in SourceSupportedList (ref xevent)) {
                                MimeHandler handler = FindHandler (atom);
                                if (handler == null)
                                        continue;
-                               XConvertSelection (display, XdndSelection, handler.Type,
-                                       handler.NonProtocol, toplevel, 0 /* CurrentTime */);
+                               XplatUIX11.XConvertSelection (display, XdndSelection, handler.Type,
+                                       handler.NonProtocol, toplevel, IntPtr.Zero /* CurrentTime */);
                                converts_pending++;
                                match = true;
                        }
                        return match;
                }
 
+               private void SetDataWithFormats (object value)
+               {
+                       if (value is string) {
+                               data.SetData (DataFormats.Text, value);
+                               data.SetData (DataFormats.UnicodeText, value);
+                       }
+
+                       data.SetData (value);
+               }
+
                private MimeHandler FindHandler (IntPtr atom)
                {
                        if (atom == IntPtr.Zero)
@@ -325,9 +837,17 @@ namespace System.Windows.Forms {
                        return null;
                }
 
-               private void SendStatus ()
+               private MimeHandler FindHandler (string name)
+               {
+                       foreach (MimeHandler handler in MimeHandlers) {
+                               if (handler.Name == name)
+                                       return handler;
+                       }
+                       return null;
+               }
+
+               private void SendStatus (IntPtr source, DragDropEffects effect)
                {
-                       DragDropEffects action = drag_event.Effect;
                        XEvent xevent = new XEvent ();
 
                        xevent.AnyEvent.type = XEventName.ClientMessage;
@@ -336,34 +856,84 @@ namespace System.Windows.Forms {
                        xevent.ClientMessageEvent.message_type = XdndStatus;
                        xevent.ClientMessageEvent.format = 32;
                        xevent.ClientMessageEvent.ptr1 = toplevel;
-                       if (drag_event.Effect != DragDropEffects.None)
+                       if (effect != DragDropEffects.None)
                                xevent.ClientMessageEvent.ptr2 = (IntPtr) 1;
 
-                       xevent.ClientMessageEvent.ptr5 = ActionFromEffect (action);
-                       XSendEvent (display, source, false, 0, ref xevent);
+                       xevent.ClientMessageEvent.ptr5 = ActionFromEffect (effect);
+                       XplatUIX11.XSendEvent (display, source, false, IntPtr.Zero, ref xevent);
                }
 
-               private void SendEnterStatus ()
+               private void SendEnter (IntPtr handle, IntPtr from, IntPtr [] supported)
                {
-                       drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
-                                       allowed, DragDropEffects.None);
-                       control.DndEnter (drag_event);
+                       XEvent xevent = new XEvent ();
+
+                       xevent.AnyEvent.type = XEventName.ClientMessage;
+                       xevent.AnyEvent.display = display;
+                       xevent.ClientMessageEvent.window = handle;
+                       xevent.ClientMessageEvent.message_type = XdndEnter;
+                       xevent.ClientMessageEvent.format = 32;
+                       xevent.ClientMessageEvent.ptr1 = from;
+
+                       // (int) xevent.ClientMessageEvent.ptr2 & 0x1)
+                       // int ptr2 = 0x1;
+                       // xevent.ClientMessageEvent.ptr2 = (IntPtr) ptr2;
+                       // (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~(0xFF << 24)) | ((v) << 24)
+                       xevent.ClientMessageEvent.ptr2 = (IntPtr) ((long)XdndVersion [0] << 24);
+                       
+                       if (supported.Length > 0)
+                               xevent.ClientMessageEvent.ptr3 = supported [0];
+                       if (supported.Length > 1)
+                               xevent.ClientMessageEvent.ptr4 = supported [1];
+                       if (supported.Length > 2)
+                               xevent.ClientMessageEvent.ptr5 = supported [2];
+
+                       XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
+               }
 
+               private void SendDrop (IntPtr handle, IntPtr from, IntPtr time)
+               {
                        XEvent xevent = new XEvent ();
 
                        xevent.AnyEvent.type = XEventName.ClientMessage;
                        xevent.AnyEvent.display = display;
-                       xevent.ClientMessageEvent.window = source;
-                       xevent.ClientMessageEvent.message_type = XdndStatus;
+                       xevent.ClientMessageEvent.window = handle;
+                       xevent.ClientMessageEvent.message_type = XdndDrop;
                        xevent.ClientMessageEvent.format = 32;
-                       xevent.ClientMessageEvent.ptr1 = toplevel;
-                       if (drag_event.Effect != DragDropEffects.None)
-                               xevent.ClientMessageEvent.ptr2 = (IntPtr) 1;
+                       xevent.ClientMessageEvent.ptr1 = from;
+                       xevent.ClientMessageEvent.ptr3 = time;
+                       
+                       XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
+               }
+
+               private void SendPosition (IntPtr handle, IntPtr from, IntPtr action, int x, int y, IntPtr time)
+               {
+                       XEvent xevent = new XEvent ();
+
+                       xevent.AnyEvent.type = XEventName.ClientMessage;
+                       xevent.AnyEvent.display = display;
+                       xevent.ClientMessageEvent.window = handle;
+                       xevent.ClientMessageEvent.message_type = XdndPosition;
+                       xevent.ClientMessageEvent.format = 32;
+                       xevent.ClientMessageEvent.ptr1 = from;
+                       xevent.ClientMessageEvent.ptr3 = (IntPtr) ((x << 16) | (y & 0xFFFF));
+                       xevent.ClientMessageEvent.ptr4 = time;
+                       xevent.ClientMessageEvent.ptr5 = action;
+                       
+                       XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
+               }
 
-                       xevent.ClientMessageEvent.ptr5 = ActionFromEffect (drag_event.Effect);
-                       XSendEvent (display, source, false, 0, ref xevent);
+               private void SendLeave (IntPtr handle, IntPtr from)
+               {
+                       XEvent xevent = new XEvent ();
+
+                       xevent.AnyEvent.type = XEventName.ClientMessage;
+                       xevent.AnyEvent.display = display;
+                       xevent.ClientMessageEvent.window = handle;
+                       xevent.ClientMessageEvent.message_type = XdndLeave;
+                       xevent.ClientMessageEvent.format = 32;
+                       xevent.ClientMessageEvent.ptr1 = from;
 
-                       status_sent = true;
+                       XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
                }
 
                private void SendFinished ()
@@ -377,7 +947,7 @@ namespace System.Windows.Forms {
                        xevent.ClientMessageEvent.format = 32;
                        xevent.ClientMessageEvent.ptr1 = toplevel;
 
-                       XSendEvent (display, source, false, 0, ref xevent);
+                       XplatUIX11.XSendEvent (display, source, false, IntPtr.Zero, ref xevent);
                }
 
                // There is a somewhat decent amount of overhead
@@ -385,36 +955,36 @@ namespace System.Windows.Forms {
                // as a lot of applications do not even use it.
                private void Init ()
                {
-                       XdndAware = XInternAtom (display, "XdndAware", false);
-                       XdndEnter = XInternAtom (display, "XdndEnter", false);
-                       XdndLeave = XInternAtom (display, "XdndLeave", false);
-                       XdndPosition = XInternAtom (display, "XdndPosition", false);
-                       XdndStatus = XInternAtom (display, "XdndStatus", false);
-                       XdndDrop = XInternAtom (display, "XdndDrop", false);
-                       XdndSelection = XInternAtom (display, "XdndSelection", false);
-                       XdndFinished = XInternAtom (display, "XdndFinished", false);
-                       XdndTypeList = XInternAtom (display, "XdndTypeList", false);
-                       XdndActionCopy = XInternAtom (display, "XdndActionCopy", false);
-                       XdndActionMove = XInternAtom (display, "XdndActionMove", false);
-                       XdndActionLink = XInternAtom (display, "XdndActionLink", false);
-                       XdndActionPrivate = XInternAtom (display, "XdndActionPrivate", false);
-                       XdndActionList = XInternAtom (display, "XdndActionList", false);
-                       XdndActionDescription = XInternAtom (display, "XdndActionDescription", false);
-                       XdndActionAsk = XInternAtom (display, "XdndActionAsk", false);
+                       XdndAware = XplatUIX11.XInternAtom (display, "XdndAware", false);
+                       XdndEnter = XplatUIX11.XInternAtom (display, "XdndEnter", false);
+                       XdndLeave = XplatUIX11.XInternAtom (display, "XdndLeave", false);
+                       XdndPosition = XplatUIX11.XInternAtom (display, "XdndPosition", false);
+                       XdndStatus = XplatUIX11.XInternAtom (display, "XdndStatus", false);
+                       XdndDrop = XplatUIX11.XInternAtom (display, "XdndDrop", false);
+                       XdndSelection = XplatUIX11.XInternAtom (display, "XdndSelection", false);
+                       XdndFinished = XplatUIX11.XInternAtom (display, "XdndFinished", false);
+                       XdndTypeList = XplatUIX11.XInternAtom (display, "XdndTypeList", false);
+                       XdndActionCopy = XplatUIX11.XInternAtom (display, "XdndActionCopy", false);
+                       XdndActionMove = XplatUIX11.XInternAtom (display, "XdndActionMove", false);
+                       XdndActionLink = XplatUIX11.XInternAtom (display, "XdndActionLink", false);
+                       //XdndActionPrivate = XplatUIX11.XInternAtom (display, "XdndActionPrivate", false);
+                       //XdndActionList = XplatUIX11.XInternAtom (display, "XdndActionList", false);
+                       //XdndActionDescription = XplatUIX11.XInternAtom (display, "XdndActionDescription", false);
+                       //XdndActionAsk = XplatUIX11.XInternAtom (display, "XdndActionAsk", false);
 
                        foreach (MimeHandler handler in MimeHandlers) {
-                               handler.Type = XInternAtom (display, handler.Name, false);
-                               handler.NonProtocol = XInternAtom (display,
+                               handler.Type = XplatUIX11.XInternAtom (display, handler.Name, false);
+                               handler.NonProtocol = XplatUIX11.XInternAtom (display,
                                                String.Concat ("MWFNonP+", handler.Name), false);
                        }
 
-                       initialized = true;
                }
 
                private IntPtr [] SourceSupportedList (ref XEvent xevent)
                {
                        IntPtr [] res;
 
+                       
                        if (((int) xevent.ClientMessageEvent.ptr2 & 0x1) == 0) {
                                res = new IntPtr [3];
                                res [0] = xevent.ClientMessageEvent.ptr3;
@@ -422,81 +992,33 @@ namespace System.Windows.Forms {
                                res [2] = xevent.ClientMessageEvent.ptr5;
                        } else {
                                IntPtr type;
-                               int format, count, remaining;
+                               int format;
+                               IntPtr count;
+                               IntPtr remaining;
                                IntPtr data = IntPtr.Zero;
 
-                               XGetWindowProperty (display, source, XdndTypeList,
-                                               0, 32, false, (IntPtr) Atom.XA_ATOM,
+                               XplatUIX11.XGetWindowProperty (display, source, XdndTypeList,
+                                               IntPtr.Zero, new IntPtr(32), false, (IntPtr) Atom.XA_ATOM,
                                                out type, out format, out count,
-                                               out remaining, out data);
+                                               out remaining, ref data);
 
-                               res = new IntPtr [count];
-                               for (int i = 0; i < count; i++) {
-                                       res [i] = (IntPtr) Marshal.ReadInt32 (data, i * sizeof (int));
+                               res = new IntPtr [count.ToInt32()];
+                               for (int i = 0; i < count.ToInt32(); i++) {
+                                       res [i] = (IntPtr) Marshal.ReadInt32 (data, i *
+                                                       Marshal.SizeOf (typeof (int)));
                                }
-                       }
-
-                       return res;
-               }
 
-               private static void UriListConverter (IntPtr display, DataObject data,
-                               ref XEvent xevent)
-               {
-                       string text = GetText (display, ref xevent, false);
-                       if (text == null)
-                               return;
-
-                       // TODO: Do this in a loop instead of just splitting
-                       ArrayList uri_list = new ArrayList ();
-                       string [] lines = text.Split (new char [] { '\r', '\n' });
-                       foreach (string line in lines) {
-                               // # is a comment line (see RFC 2483)
-                               if (line.StartsWith ("#"))
-                                       continue;
-                               try {
-                                       Uri uri = new Uri (line);
-                                       uri_list.Add (uri.LocalPath);
-                               } catch { }
+                               XplatUIX11.XFree (data);
                        }
 
-                       string [] l = (string []) uri_list.ToArray (typeof (string));
-                       if (l.Length < 1)
-                               return;
-                       data.SetData (DataFormats.FileDrop, l);
-                       data.SetData ("FileName", l [0]);
-                       data.SetData ("FileNameW", l [0]);
-               }
-
-               private static void TextConverter (IntPtr display, DataObject data, ref XEvent xevent)
-               {
-                       string text = GetText (display, ref xevent, false);
-                       if (text == null)
-                               return;
-                       data.SetData (DataFormats.Text, text);
-                       data.SetData (DataFormats.UnicodeText, text);
-               }
-
-               private static void HtmlConverter (IntPtr display, DataObject data, ref XEvent xevent)
-               {
-                       string html = GetText (display, ref xevent, true);
-                       if (html == null)
-                               return;
-                       data.SetData (DataFormats.Html, html);
-               }
-
-               private static void ImageConverter (IntPtr display, DataObject data, ref XEvent xevent)
-               {
-               }
-
-               private static void RtfConverter (IntPtr display, DataObject data, ref XEvent xevent)
-               {
+                       return res;
                }
 
-               private static string GetText (IntPtr display, ref XEvent xevent, bool unicode)
+               private string GetText (ref XEvent xevent, bool unicode)
                {
                        int nread = 0;
-                       int nitems;
-                       int bytes_after;
+                       IntPtr nitems;
+                       IntPtr bytes_after;
 
                        StringBuilder builder = new StringBuilder ();
                        do {
@@ -504,14 +1026,14 @@ namespace System.Windows.Forms {
                                int actual_fmt;
                                IntPtr data = IntPtr.Zero;
 
-                               if (0 != XGetWindowProperty (display,
+                               if (0 != XplatUIX11.XGetWindowProperty (display,
                                                    xevent.AnyEvent.window,
                                                    (IntPtr) xevent.SelectionEvent.property,
-                                                   0, 0xffffff, false,
+                                                   IntPtr.Zero, new IntPtr(0xffffff), false,
                                                    (IntPtr) Atom.AnyPropertyType, out actual_type,
                                                    out actual_fmt, out nitems, out bytes_after,
-                                                   out data)) {
-                                       XFree (data);
+                                                   ref data)) {
+                                       XplatUIX11.XFree (data);
                                        break;
                                }
 
@@ -519,44 +1041,130 @@ namespace System.Windows.Forms {
                                        builder.Append (Marshal.PtrToStringUni (data));
                                else
                                        builder.Append (Marshal.PtrToStringAnsi (data));
-                               nread += nitems;
+                               nread += nitems.ToInt32();
 
-                               XFree (data);
-                       } while (bytes_after > 0);
+                               XplatUIX11.XFree (data);
+                       } while (bytes_after.ToInt32() > 0);
                        if (nread == 0)
                                return null;
                        return builder.ToString ();
                }
 
-               [DllImport ("libX11")]
-               private extern static string XGetAtomName (IntPtr display, IntPtr atom);
+               private MemoryStream GetData (ref XEvent xevent)
+               {
+                       int nread = 0;
+                       IntPtr nitems;
+                       IntPtr bytes_after;
 
-               [DllImport ("libX11")]
-               private extern static IntPtr XInternAtom (IntPtr display, string atom_name, bool only_if_exists);
+                       MemoryStream res = new MemoryStream ();
+                       do {
+                               IntPtr actual_type;
+                               int actual_fmt;
+                               IntPtr data = IntPtr.Zero;
 
-               [DllImport ("libX11")]
-               private extern static int XChangeProperty (IntPtr display, IntPtr window, IntPtr property,
-                               IntPtr format, int type, PropertyMode  mode, uint [] atoms, int nelements);
+                               if (0 != XplatUIX11.XGetWindowProperty (display,
+                                                   xevent.AnyEvent.window,
+                                                   (IntPtr) xevent.SelectionEvent.property,
+                                                   IntPtr.Zero, new IntPtr(0xffffff), false,
+                                                   (IntPtr) Atom.AnyPropertyType, out actual_type,
+                                                   out actual_fmt, out nitems, out bytes_after,
+                                                   ref data)) {
+                                       XplatUIX11.XFree (data);
+                                       break;
+                               }
 
-               [DllImport ("libX11")]
-               private extern static int XGetWindowProperty (IntPtr display, IntPtr window,
-                               IntPtr atom, int long_offset, int long_length, bool delete,
-                               IntPtr req_type, out IntPtr actual_type, out int actual_format,
-                               out int nitems, out int bytes_after, out IntPtr prop);
+                               for (int i = 0; i < nitems.ToInt32(); i++)
+                                       res.WriteByte (Marshal.ReadByte (data, i));
+                               nread += nitems.ToInt32();
 
-               [DllImport ("libX11")]
-               internal extern static int XSendEvent (IntPtr display, IntPtr window,
-                               bool propagate, EventMask event_mask, ref XEvent send_event);
+                               XplatUIX11.XFree (data);
+                       } while (bytes_after.ToInt32() > 0);
+                       return res;
+               }
 
-               [DllImport ("libX11")]
-               internal extern static int XConvertSelection (IntPtr display, IntPtr selection,
-                               IntPtr target, IntPtr property, IntPtr requestor, int time);
+               private Control MwfWindow (IntPtr window)
+               {
+                       Hwnd hwnd = Hwnd.ObjectFromHandle (window);
+                       if (hwnd == null)
+                               return null;
 
-               [DllImport ("libX11")]
-               internal extern static IntPtr XGetSelectionOwner(IntPtr display, IntPtr selection);
+                       Control res = Control.FromHandle (hwnd.client_window);
+                       return res;
+               }
 
-               [DllImport ("libX11")]
-               internal extern static int XFree(IntPtr data);
-       }
+               private bool IsWindowDndAware (IntPtr handle)
+               {
+                       bool res = true;
+                       // Check the version number, we need greater than 3
+                       IntPtr actual;
+                       int format;
+                       IntPtr count;
+                       IntPtr remaining;
+                       IntPtr data = IntPtr.Zero;
+                       
+                       XplatUIX11.XGetWindowProperty (display, handle, XdndAware, IntPtr.Zero, new IntPtr(0x8000000), false,
+                                       (IntPtr) Atom.XA_ATOM, out actual, out format,
+                                       out count, out remaining, ref data);
+                       
+                       if (actual != (IntPtr) Atom.XA_ATOM || format != 32 ||
+                                       count.ToInt32() == 0 || data == IntPtr.Zero) {
+                               if (data != IntPtr.Zero)
+                                       XplatUIX11.XFree (data);
+                               return false;
+                       }
+
+                       int version = Marshal.ReadInt32 (data, 0);
 
+                       if (version < 3) {
+                               Console.Error.WriteLine ("XDND Version too old (" + version + ").");
+                               XplatUIX11.XFree (data);
+                               return false;
+                       }
+
+                       // First type is actually the XDND version
+                       if (count.ToInt32() > 1) {
+                               res = false;
+                               for (int i = 1; i < count.ToInt32(); i++) {
+                                       IntPtr type = (IntPtr) Marshal.ReadInt32 (data, i *
+                                                       Marshal.SizeOf (typeof (int)));
+                                       for (int j = 0; j < drag_data.SupportedTypes.Length; j++) {
+                                               if (drag_data.SupportedTypes [j] == type) {
+                                                       res = true;
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+
+                       XplatUIX11.XFree (data);
+                       return res;
+               }
+
+               private IntPtr [] DetermineSupportedTypes (object data)
+               {
+                       ArrayList res = new ArrayList ();
+
+                       if (data is string) {
+                               MimeHandler handler = FindHandler ("text/plain");
+                               if (handler != null)
+                                       res.Add (handler.Type);
+                       }/* else if (data is Bitmap)
+                               res.Add (data);
+
+                        */
+
+                       if (data is IDataObject) {
+                               
+
+                       }
+
+                       if (data is ISerializable) {
+                               MimeHandler handler = FindHandler ("application/x-mono-serialized-object");
+                               if (handler != null)
+                                       res.Add (handler.Type);
+                       }
+
+                       return (IntPtr []) res.ToArray (typeof (IntPtr));
+               }
+       }
 }