2007-01-07 Jonathan Pobst <monkey@jpobst.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / X11Dnd.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2005 Novell, Inc.
21 //
22 // Authors:
23 //      Jackson Harper (jackson@ximian.com)
24 //
25 //
26
27
28 using System;
29 using System.IO;
30 using System.Text;
31 using System.Drawing;
32 using System.Collections;
33 using System.Runtime.Serialization;
34 using System.Runtime.InteropServices;
35 using System.Runtime.Serialization.Formatters.Binary;
36
37 namespace System.Windows.Forms {
38
39         internal class X11Dnd {
40
41                 private enum State {
42                         Accepting,
43                         Dragging
44                 }
45
46                 private enum DragState {
47                         None,
48                         Beginning,
49                         Dragging,
50                         Entered
51                 }
52
53                 private interface IDataConverter {
54                         void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent);
55                         void SetData (X11Dnd dnd, object data, ref XEvent xevent);
56                 }
57
58                 private delegate void MimeConverter (IntPtr dsp,
59                                 DataObject data, ref XEvent xevent);
60
61                 private class MimeHandler {
62                         public string Name;
63                         public IntPtr Type;
64                         public IntPtr NonProtocol;
65                         public IDataConverter Converter;
66                         
67                         public MimeHandler (string name, IDataConverter converter)
68                         {
69                                 Name = name;
70                                 Converter = converter;
71                         }
72
73                         public override string ToString ()
74                         {
75                                 return "MimeHandler {" + Name + "}";
76                         }
77                 }
78
79                 private MimeHandler [] MimeHandlers = {
80 //                        new MimeHandler ("WCF_DIB"),
81 //                        new MimeHandler ("image/gif", new MimeConverter (ImageConverter)),
82 //                      new MimeHandler ("text/rtf", new MimeConverter (RtfConverter)),
83 //                      new MimeHandler ("text/richtext", new MimeConverter (RtfConverter)),
84
85                         new MimeHandler ("text/plain", new TextConverter ()),
86                         new MimeHandler ("text/html", new HtmlConverter ()),
87                         new MimeHandler ("text/uri-list", new UriListConverter ()),
88                         new MimeHandler ("application/x-mono-serialized-object",
89                                         new SerializedObjectConverter ())
90                 };
91
92                 private class SerializedObjectConverter : IDataConverter {
93
94                         public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
95                         {
96                                 MemoryStream stream = dnd.GetData (ref xevent);
97                                 BinaryFormatter bf = new BinaryFormatter ();
98
99                                 if (stream.Length == 0)
100                                         return;
101
102                                 stream.Seek (0, 0);
103                                 object obj = bf.Deserialize (stream);
104                                 data.SetData (obj);
105                         }
106
107                         public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
108                         {
109                                 if (data == null)
110                                         return;
111
112                                 MemoryStream stream = new MemoryStream ();
113                                 BinaryFormatter bf = new BinaryFormatter ();
114
115                                 bf.Serialize (stream, data);
116
117                                 IntPtr buffer = Marshal.AllocHGlobal ((int) stream.Length);
118                                 stream.Seek (0, 0);
119
120                                 for (int i = 0; i < stream.Length; i++) {
121                                         Marshal.WriteByte (buffer, i, (byte) stream.ReadByte ());
122                                 }
123
124                                 dnd.SetProperty (ref xevent, buffer, (int) stream.Length);
125                         }
126                 }
127
128                 private class HtmlConverter : IDataConverter {
129
130                         public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
131                         {
132                                 string text = dnd.GetText (ref xevent, false);
133                                 if (text == null)
134                                         return;
135                                 data.SetData (DataFormats.Text, text);
136                                 data.SetData (DataFormats.UnicodeText, text);
137                         }
138
139                         public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
140                         {
141                                 IntPtr buffer;
142                                 int len;
143                                 string str = data as string;
144
145                                 if (str == null)
146                                         return;
147
148                                 if (xevent.SelectionRequestEvent.target == (IntPtr)Atom.XA_STRING) {
149                                         byte [] bytes = Encoding.ASCII.GetBytes (str);
150                                         buffer = Marshal.AllocHGlobal (bytes.Length);
151                                         len = bytes.Length;
152                                         for (int i = 0; i < len; i++)
153                                                 Marshal.WriteByte (buffer, i, bytes [i]);
154                                 } else {
155                                         buffer = Marshal.StringToHGlobalAnsi (str);
156                                         len = 0;
157                                         while (Marshal.ReadByte (buffer, len) != 0)
158                                                 len++;
159                                 }
160
161                                 dnd.SetProperty (ref xevent, buffer, len);
162
163                                 Marshal.FreeHGlobal (buffer);
164                         }
165                 }
166
167                 private class TextConverter : IDataConverter {
168
169                         public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
170                         {
171                                 string text = dnd.GetText (ref xevent, true);
172                                 if (text == null)
173                                         return;
174                                 data.SetData (DataFormats.Text, text);
175                                 data.SetData (DataFormats.UnicodeText, text);
176                         }
177
178                         public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
179                         {
180                                 IntPtr buffer;
181                                 int len;
182                                 string str = data as string;
183
184                                 if (data == null)
185                                         return;
186                                 
187                                 if (xevent.SelectionRequestEvent.target == (IntPtr)Atom.XA_STRING) {
188                                         byte [] bytes = Encoding.ASCII.GetBytes (str);
189                                         buffer = Marshal.AllocHGlobal (bytes.Length);
190                                         len = bytes.Length;
191                                         for (int i = 0; i < len; i++)
192                                                 Marshal.WriteByte (buffer, i, bytes [i]);
193                                 } else {
194                                         buffer = Marshal.StringToHGlobalAnsi (str);
195                                         len = 0;
196                                         while (Marshal.ReadByte (buffer, len) != 0)
197                                                 len++;
198                                 }
199
200                                 dnd.SetProperty (ref xevent, buffer, len);
201
202                                 Marshal.FreeHGlobal (buffer);
203                         }
204                 }
205
206                 private class UriListConverter : IDataConverter {
207
208                         public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
209                         {
210                                 string text = dnd.GetText (ref xevent, false);
211                                 if (text == null)
212                                         return;
213
214                                 // TODO: Do this in a loop instead of just splitting
215                                 ArrayList uri_list = new ArrayList ();
216                                 string [] lines = text.Split (new char [] { '\r', '\n' });
217                                 foreach (string line in lines) {
218                                         // # is a comment line (see RFC 2483)
219                                         if (line.StartsWith ("#"))
220                                                 continue;
221                                         try {
222                                                 Uri uri = new Uri (line);
223                                                 uri_list.Add (uri.LocalPath);
224                                         } catch { }
225                                 }
226
227                                 string [] l = (string []) uri_list.ToArray (typeof (string));
228                                 if (l.Length < 1)
229                                         return;
230                                 data.SetData (DataFormats.FileDrop, l);
231                                 data.SetData ("FileName", l [0]);
232                                 data.SetData ("FileNameW", l [0]);
233                         }
234
235                         public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
236                         {
237                                 string [] uri_list = data as string [];
238
239                                 if (uri_list == null)
240                                         return;
241
242                                 StringBuilder res = new StringBuilder ();
243                                 foreach (string uri_str in uri_list) {
244                                         Uri uri = new Uri (uri_str);
245                                         res.Append (uri.ToString ());
246                                         res.Append ("\r\n");
247                                 }
248
249                                 IntPtr buffer = Marshal.StringToHGlobalAnsi ((string) data);
250                                 int len = 0;
251                                 while (Marshal.ReadByte (buffer, len) != 0)
252                                         len++;
253
254                                 dnd.SetProperty (ref xevent, buffer, len);
255                         }
256                 }
257
258                 private class DragData {
259                         public IntPtr Window;
260                         public DragState State;
261                         public object Data;
262                         public IntPtr Action;
263                         public IntPtr [] SupportedTypes;
264                         public MouseButtons MouseState;
265                         
266                         public IntPtr LastWindow;
267                         public IntPtr LastTopLevel;
268
269                         public bool WillAccept;
270                         
271                         public void Reset ()
272                         {
273                                 State = DragState.None;
274                                 Data = null;
275                                 SupportedTypes = null;
276                                 WillAccept = false;
277                         }
278                 }
279                 
280                 private class DropData {
281
282                 }
283
284                 // This version seems to be the most common
285                 private static readonly IntPtr [] XdndVersion = new IntPtr [] { new IntPtr (4) }; 
286
287                 private IntPtr display;
288                 private DragData drag_data;
289                 
290                 private IntPtr XdndAware;
291                 private IntPtr XdndSelection;
292                 private IntPtr XdndEnter;
293                 private IntPtr XdndLeave;
294                 private IntPtr XdndPosition;
295                 private IntPtr XdndDrop;
296                 private IntPtr XdndFinished;
297                 private IntPtr XdndStatus;
298                 private IntPtr XdndTypeList;
299                 private IntPtr XdndActionCopy;
300                 private IntPtr XdndActionMove;
301                 private IntPtr XdndActionLink;
302                 //private IntPtr XdndActionPrivate;
303                 //private IntPtr XdndActionList;
304                 //private IntPtr XdndActionDescription;
305                 //private IntPtr XdndActionAsk;
306
307                 //private State state;
308
309                 private int converts_pending;
310                 private bool position_recieved;
311                 private bool status_sent;
312                 private IntPtr target;
313                 private IntPtr source;
314                 private IntPtr toplevel;
315                 private DataObject data;
316
317                 private Control control;
318                 private int pos_x, pos_y;
319                 private DragDropEffects allowed;
320                 private DragEventArgs drag_event;
321
322                 private Cursor CursorNo;
323                 private Cursor CursorCopy;
324                 private Cursor CursorMove;
325                 private Cursor CursorLink;
326                 // check out the TODO below
327                 //private IntPtr CurrentCursorHandle;
328                 
329                 public X11Dnd (IntPtr display)
330                 {
331                         this.display = display;
332                         Init ();
333                 }
334
335                 public bool InDrag()
336                 {
337                         if (drag_data == null)
338                                 return false;
339                         return drag_data.State != DragState.None;
340                 }
341                 
342                 public void SetAllowDrop (Hwnd hwnd, bool allow)
343                 {
344                         int[] atoms;
345
346                         if (hwnd.allow_drop == allow)
347                                 return;
348
349                         atoms = new int[XdndVersion.Length];
350                         for (int i = 0; i < XdndVersion.Length; i++) {
351                                 atoms[i] = XdndVersion[i].ToInt32();
352                         }
353
354                         XplatUIX11.XChangeProperty (display, hwnd.whole_window, XdndAware,
355                                         (IntPtr) Atom.XA_ATOM, 32,
356                                         PropertyMode.Replace, atoms, allow ? 1 : 0);
357                         hwnd.allow_drop = allow;
358                 }
359
360                 public DragDropEffects StartDrag (IntPtr handle, object data,
361                                 DragDropEffects allowed_effects)
362                 {
363                         drag_data = new DragData ();
364                         drag_data.Window = handle;
365                         drag_data.State = DragState.Beginning;
366                         drag_data.MouseState = XplatUIX11.MouseState;
367                         drag_data.Data = data;
368                         drag_data.SupportedTypes = DetermineSupportedTypes (data);
369
370                         drag_data.Action = ActionFromEffect (allowed_effects);
371
372                         if (CursorNo == null) {
373                                 // Make sure the cursors are created
374                                 CursorNo = new Cursor (typeof (X11Dnd), "DnDNo.cur");
375                                 CursorCopy = new Cursor (typeof (X11Dnd), "DnDCopy.cur");
376                                 CursorMove = new Cursor (typeof (X11Dnd), "DnDMove.cur");
377                                 CursorLink = new Cursor (typeof (X11Dnd), "DnDLink.cur");
378                         }
379
380                         drag_data.LastTopLevel = IntPtr.Zero;
381                         return DragDropEffects.Copy;
382                 }
383
384                 public void HandleButtonRelease (ref XEvent xevent)
385                 {
386                         if (drag_data == null)
387                                 return;
388
389                         if (!((drag_data.MouseState == MouseButtons.Left &&
390                                               xevent.ButtonEvent.button == 1) ||
391                                             (drag_data.MouseState == MouseButtons.Right &&
392                                                             xevent.ButtonEvent.button == 3)))
393                                 return;
394
395                         if (drag_data.State == DragState.Beginning) {
396                                 //state = State.Accepting;
397                         } else if (drag_data.State != DragState.None) {
398
399                                 if (drag_data.WillAccept) {
400                                         SendDrop (drag_data.LastTopLevel, xevent.AnyEvent.window,
401                                                         xevent.ButtonEvent.time);
402                                 }
403
404                                 XplatUIX11.XUngrabPointer (display, IntPtr.Zero);
405                                 drag_data.State = DragState.None;
406                                 // WE can't reset the drag data yet as it is still
407                                 // most likely going to be used by the SelectionRequest
408                                 // handlers
409                         }
410                 }
411
412                 public bool HandleMotionNotify (ref XEvent xevent)
413                 {
414                         if (drag_data == null)
415                                 return false;
416
417                         if (drag_data.State == DragState.Beginning) {
418                                 int suc;
419
420                                 drag_data.State = DragState.Dragging;
421
422                                 suc = XplatUIX11.XSetSelectionOwner (display, XdndSelection,
423                                                 drag_data.Window,
424                                                 xevent.ButtonEvent.time);
425
426                                 if (suc == 0) {
427                                         Console.Error.WriteLine ("Could not take ownership of XdndSelection aborting drag.");
428                                         drag_data.Reset ();
429                                         return false;
430                                 }
431
432                                 suc = XplatUIX11.XGrabPointer (display, xevent.AnyEvent.window,
433                                                 false,
434                                                 EventMask.ButtonMotionMask |
435                                                 EventMask.PointerMotionMask |
436                                                 EventMask.ButtonPressMask |
437                                                 EventMask.ButtonReleaseMask,
438                                                 GrabMode.GrabModeAsync,
439                                                 GrabMode.GrabModeAsync,
440                                                 IntPtr.Zero, IntPtr.Zero/*CursorCopy.Handle*/, IntPtr.Zero);
441
442                                 if (suc != 0) {
443                                         Console.Error.WriteLine ("Could not grab pointer aborting drag.");
444                                         drag_data.Reset ();
445                                         return false;
446                                 }
447
448                                 drag_data.State = DragState.Dragging;
449                         } else if (drag_data.State != DragState.None) {
450                                 bool dnd_aware = false;
451                                 IntPtr toplevel = IntPtr.Zero;
452                                 IntPtr window = XplatUIX11.RootWindowHandle;
453
454                                 IntPtr root, child;
455                                 int x_temp, y_temp;
456                                 int mask_return;
457
458                                 while (XplatUIX11.XQueryPointer (display,
459                                                        window,
460                                                        out root, out child,
461                                                        out x_temp, out y_temp,
462                                                        out xevent.MotionEvent.x,
463                                                        out xevent.MotionEvent.y,
464                                                        out mask_return)) {
465                                         
466                                         if (!dnd_aware) {
467                                                 dnd_aware = IsWindowDndAware (window);
468                                                 if (dnd_aware) {
469                                                         toplevel = window;
470                                                         xevent.MotionEvent.x_root = x_temp;
471                                                         xevent.MotionEvent.y_root = y_temp;
472                                                 }
473                                         }
474
475                                         if (child == IntPtr.Zero)
476                                                 break;
477                                         
478                                         window = child;
479                                 }
480
481                                 if (window != drag_data.LastWindow && drag_data.State == DragState.Entered) {
482                                         drag_data.State = DragState.Dragging;
483
484                                         // TODO: Send a Leave if this is an MWF window
485
486                                         if (toplevel != drag_data.LastTopLevel)
487                                                 SendLeave (drag_data.LastTopLevel, xevent.MotionEvent.window);
488                                 }
489
490                                 drag_data.State = DragState.Entered;
491                                 if (toplevel != drag_data.LastTopLevel) {
492                                         // Entering a new toplevel window
493                                         SendEnter (toplevel, drag_data.Window, drag_data.SupportedTypes);
494                                 } else {
495                                         // Already in a toplevel window, so send a position
496                                         SendPosition (toplevel, drag_data.Window,
497                                                         drag_data.Action,
498                                                         xevent.MotionEvent.x_root,
499                                                         xevent.MotionEvent.y_root,
500                                                         xevent.MotionEvent.time);
501                                 }
502
503                                 drag_data.LastTopLevel = toplevel;
504                                 drag_data.LastWindow = window;
505                                 return true;
506                         }
507                         return false;
508                 }
509
510                 // return true if the event is handled here
511                 public bool HandleClientMessage (ref XEvent xevent)
512                 {
513                         // most common so we check it first
514                         if (xevent.ClientMessageEvent.message_type == XdndPosition)
515                                 return Accepting_HandlePositionEvent (ref xevent);
516                         if (xevent.ClientMessageEvent.message_type == XdndEnter)
517                                 return Accepting_HandleEnterEvent (ref xevent);
518                         if (xevent.ClientMessageEvent.message_type == XdndDrop)
519                                 return Accepting_HandleDropEvent (ref xevent);
520                         if (xevent.ClientMessageEvent.message_type == XdndLeave)
521                                 return Accepting_HandleLeaveEvent (ref xevent);
522                         if (xevent.ClientMessageEvent.message_type == XdndStatus)
523                                 return HandleStatusEvent (ref xevent);
524
525                         return false;
526                 }
527
528                 public bool HandleSelectionNotifyEvent (ref XEvent xevent)
529                 {
530                         if (source != XplatUIX11.XGetSelectionOwner (display, XdndSelection))
531                                 return false;
532
533                         MimeHandler handler = FindHandler ((IntPtr) xevent.SelectionEvent.target);
534                         if (handler == null)
535                                 return false;
536                         if (data == null)
537                                 data = new DataObject ();
538
539                         handler.Converter.GetData (this, data, ref xevent);
540
541                         converts_pending--;
542                         if (converts_pending <= 0 && position_recieved) {
543                                 drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
544                                         allowed, DragDropEffects.None);
545                                 control.DndEnter (drag_event);
546                                 SendStatus (source, drag_event.Effect);
547                                 status_sent = true;
548                         }
549                         return true;
550                 }
551
552                 public bool HandleSelectionRequestEvent (ref XEvent xevent)
553                 {
554                         if (xevent.SelectionRequestEvent.selection != XdndSelection)
555                                 return false;
556
557                         MimeHandler handler = FindHandler (xevent.SelectionRequestEvent.target);
558                         if (handler == null)
559                                 return false;
560
561                         handler.Converter.SetData (this, drag_data.Data, ref xevent);
562
563                         return true;
564                 }
565
566                 private void SetProperty (ref XEvent xevent, IntPtr data, int length)
567                 {
568                         XEvent sel = new XEvent();
569                         sel.SelectionEvent.type = XEventName.SelectionNotify;
570                         sel.SelectionEvent.send_event = true;
571                         sel.SelectionEvent.display = display;
572                         sel.SelectionEvent.selection = xevent.SelectionRequestEvent.selection;
573                         sel.SelectionEvent.target = xevent.SelectionRequestEvent.target;
574                         sel.SelectionEvent.requestor = xevent.SelectionRequestEvent.requestor;
575                         sel.SelectionEvent.time = xevent.SelectionRequestEvent.time;
576                         sel.SelectionEvent.property = IntPtr.Zero;
577
578                         XplatUIX11.XChangeProperty (display, xevent.SelectionRequestEvent.requestor,
579                                         xevent.SelectionRequestEvent.property,
580                                         xevent.SelectionRequestEvent.target,
581                                         8, PropertyMode.Replace, data, length);
582                         sel.SelectionEvent.property = xevent.SelectionRequestEvent.property;
583
584                         XplatUIX11.XSendEvent (display, xevent.SelectionRequestEvent.requestor, false,
585                                         (IntPtr)EventMask.NoEventMask, ref sel);
586                         return;
587                 }
588
589                 private void Reset ()
590                 {
591                         ResetSourceData ();
592                         ResetTargetData ();
593                 }
594
595                 private void ResetSourceData ()
596                 {
597                         converts_pending = 0;
598                         data = null;
599                 }
600
601                 private void ResetTargetData ()
602                 {
603                         position_recieved = false;
604                         status_sent = false;
605                 }
606                 
607                 private bool Accepting_HandleEnterEvent (ref XEvent xevent)
608                 {
609                         Reset ();
610
611                         source = xevent.ClientMessageEvent.ptr1;
612                         toplevel = xevent.AnyEvent.window;
613                         target = IntPtr.Zero;
614
615                         ConvertData (ref xevent);
616
617                         return true;
618                 }
619
620                 private bool Accepting_HandlePositionEvent (ref XEvent xevent)
621                 {
622                         pos_x = (int) xevent.ClientMessageEvent.ptr3 >> 16;
623                         pos_y = (int) xevent.ClientMessageEvent.ptr3 & 0xFFFF;
624
625                         // Copy is implicitly allowed
626                         allowed = EffectFromAction (xevent.ClientMessageEvent.ptr5) | DragDropEffects.Copy;
627
628                         IntPtr parent, child, new_child, last_drop_child;
629                         parent = XplatUIX11.XRootWindow (display, 0);
630                         child = toplevel;
631                         last_drop_child = IntPtr.Zero;
632                         while (true) {
633                                 int xd, yd;
634                                 new_child = IntPtr.Zero;
635                                 
636                                 if (!XplatUIX11.XTranslateCoordinates (display,
637                                                     parent, child, pos_x, pos_y,
638                                                     out xd, out yd, out new_child))
639                                         break;
640                                 if (new_child == IntPtr.Zero)
641                                         break;
642                                 child = new_child;
643
644                                 Hwnd h = Hwnd.ObjectFromHandle (child);
645                                 Control d = Control.FromHandle (h.client_window);
646                                 if (d != null && d.allow_drop)
647                                         last_drop_child = child;
648                         }
649
650                         if (last_drop_child != IntPtr.Zero)
651                                 child = last_drop_child;
652
653                         if (target != child) {
654                                 // We have moved into a new control 
655                                 // or into a control for the first time
656                                 Finish ();
657                         }
658                         target = child;
659                         Hwnd hwnd = Hwnd.ObjectFromHandle (target);
660                         Control c = Control.FromHandle (hwnd.client_window);
661
662                         if (c == null)
663                                 return true;
664                         if (!c.allow_drop) {
665                                 SendStatus (source, DragDropEffects.None);
666                                 Finish ();
667                                 return true;
668                         }
669
670                         control = c;
671                         position_recieved = true;                       
672
673                         if (converts_pending > 0)
674                                 return true;
675                         if (!status_sent) {
676                                 drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
677                                         allowed, DragDropEffects.None);
678                                 control.DndEnter (drag_event);
679                                 SendStatus (source, drag_event.Effect);
680                                 status_sent = true;
681                         } else {
682                                 drag_event.x = pos_x;
683                                 drag_event.y = pos_y;
684                                 control.DndOver (drag_event);
685                                 SendStatus (source, drag_event.Effect);
686                         }
687                         
688                         return true;
689                 }
690
691                 private void Finish ()
692                 {
693                         if (control != null) {
694                                 if (drag_event == null) {
695                                         if (data == null)
696                                                 data = new DataObject ();
697                                         drag_event = new DragEventArgs (data,
698                                                         0, pos_x, pos_y,
699                                         allowed, DragDropEffects.None);
700                                 }
701                                 control.DndLeave (drag_event);
702                         }
703                         ResetTargetData ();
704                 }
705
706                 private bool Accepting_HandleDropEvent (ref XEvent xevent)
707                 {
708                         if (control != null && drag_event != null) {
709                                 drag_event = new DragEventArgs (data,
710                                                 0, pos_x, pos_y,
711                                         allowed, DragDropEffects.None);
712                                 control.DndDrop (drag_event);
713                         }
714                         SendFinished ();
715                         return true;
716                 }
717
718                 private bool Accepting_HandleLeaveEvent (ref XEvent xevent)
719                 {
720                         if (control != null && drag_event != null)
721                                 control.DndLeave (drag_event);
722                         // Reset ();
723                         return true;
724                 }
725
726                 private bool HandleStatusEvent (ref XEvent xevent)
727                 {
728                         if (drag_data != null && drag_data.State == DragState.Entered) {
729                                 drag_data.WillAccept = ((int) xevent.ClientMessageEvent.ptr2 & 0x1) != 0;
730                                 Cursor cursor = CursorNo;
731                                 if (drag_data.WillAccept) {
732                                         // Same order as on MS
733                                         IntPtr action = xevent.ClientMessageEvent.ptr5;
734                                         if (action == XdndActionCopy)
735                                                 cursor = CursorCopy;
736                                         else if (action == XdndActionLink)
737                                                 cursor = CursorLink;
738                                         else if (action == XdndActionMove)
739                                                 cursor = CursorMove;
740
741                                 }
742
743                                         // TODO: Try not to set the cursor so much
744                                 //if (cursor.Handle != CurrentCursorHandle) {
745                                         XplatUIX11.XChangeActivePointerGrab (display,
746                                                         EventMask.ButtonMotionMask |
747                                                         EventMask.PointerMotionMask |
748                                                         EventMask.ButtonPressMask |
749                                                         EventMask.ButtonReleaseMask,
750                                                         cursor.Handle, IntPtr.Zero);
751                                         //CurrentCursorHandle = cursor.Handle;
752                                         //}     
753                         }
754                         return true;
755                 }
756
757                 private DragDropEffects EffectFromAction (IntPtr action)
758                 {
759                         DragDropEffects allowed = DragDropEffects.None;
760
761                         if (action == XdndActionCopy)
762                                 allowed = DragDropEffects.Copy;
763                         else if (action == XdndActionMove)
764                                 allowed |= DragDropEffects.Move;
765                         if (action == XdndActionLink)
766                                 allowed |= DragDropEffects.Link;
767                         return allowed;
768                 }
769
770                 private IntPtr ActionFromEffect (DragDropEffects effect)
771                 {
772                         IntPtr action = IntPtr.Zero;
773
774                         // We can't OR together actions on XDND so sadly the primary
775                         // is the only one shown here
776                         if ((effect & DragDropEffects.Copy) != 0)
777                                 action = XdndActionCopy;
778                         else if ((effect & DragDropEffects.Move) != 0)
779                                 action = XdndActionMove;
780                         else if ((effect & DragDropEffects.Link) != 0)
781                                 action = XdndActionLink;
782                         return action;
783                 }
784
785                 private bool ConvertData (ref XEvent xevent)
786                 {
787                         bool match = false;
788
789                         if (source != XplatUIX11.XGetSelectionOwner (display, XdndSelection)) {
790                                 return false;
791                         }
792
793                         Control mwfcontrol = MwfWindow (source);
794
795                         if (mwfcontrol != null && drag_data != null) {
796                                 DataObject dragged = drag_data.Data as DataObject;
797                                 if (dragged != null) {
798                                         data = dragged;
799                                 } else {
800                                         if (data == null)
801                                                 data = new DataObject ();
802                                         SetDataWithFormats (drag_data.Data);
803                                 }
804                                 return true;
805                         }
806
807                         foreach (IntPtr atom in SourceSupportedList (ref xevent)) {
808                                 MimeHandler handler = FindHandler (atom);
809                                 if (handler == null)
810                                         continue;
811                                 XplatUIX11.XConvertSelection (display, XdndSelection, handler.Type,
812                                         handler.NonProtocol, toplevel, IntPtr.Zero /* CurrentTime */);
813                                 converts_pending++;
814                                 match = true;
815                         }
816                         return match;
817                 }
818
819                 private void SetDataWithFormats (object value)
820                 {
821                         if (value is string) {
822                                 data.SetData (DataFormats.Text, value);
823                                 data.SetData (DataFormats.UnicodeText, value);
824                         }
825
826                         data.SetData (value);
827                 }
828
829                 private MimeHandler FindHandler (IntPtr atom)
830                 {
831                         if (atom == IntPtr.Zero)
832                                 return null;
833                         foreach (MimeHandler handler in MimeHandlers) {
834                                 if (handler.Type == atom)
835                                         return handler;
836                         }
837                         return null;
838                 }
839
840                 private MimeHandler FindHandler (string name)
841                 {
842                         foreach (MimeHandler handler in MimeHandlers) {
843                                 if (handler.Name == name)
844                                         return handler;
845                         }
846                         return null;
847                 }
848
849                 private void SendStatus (IntPtr source, DragDropEffects effect)
850                 {
851                         XEvent xevent = new XEvent ();
852
853                         xevent.AnyEvent.type = XEventName.ClientMessage;
854                         xevent.AnyEvent.display = display;
855                         xevent.ClientMessageEvent.window = source;
856                         xevent.ClientMessageEvent.message_type = XdndStatus;
857                         xevent.ClientMessageEvent.format = 32;
858                         xevent.ClientMessageEvent.ptr1 = toplevel;
859                         if (effect != DragDropEffects.None)
860                                 xevent.ClientMessageEvent.ptr2 = (IntPtr) 1;
861
862                         xevent.ClientMessageEvent.ptr5 = ActionFromEffect (effect);
863                         XplatUIX11.XSendEvent (display, source, false, IntPtr.Zero, ref xevent);
864                 }
865
866                 private void SendEnter (IntPtr handle, IntPtr from, IntPtr [] supported)
867                 {
868                         XEvent xevent = new XEvent ();
869
870                         xevent.AnyEvent.type = XEventName.ClientMessage;
871                         xevent.AnyEvent.display = display;
872                         xevent.ClientMessageEvent.window = handle;
873                         xevent.ClientMessageEvent.message_type = XdndEnter;
874                         xevent.ClientMessageEvent.format = 32;
875                         xevent.ClientMessageEvent.ptr1 = from;
876
877                         // (int) xevent.ClientMessageEvent.ptr2 & 0x1)
878                         // int ptr2 = 0x1;
879                         // xevent.ClientMessageEvent.ptr2 = (IntPtr) ptr2;
880                         // (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~(0xFF << 24)) | ((v) << 24)
881                         xevent.ClientMessageEvent.ptr2 = (IntPtr) ((long)XdndVersion [0] << 24);
882                         
883                         if (supported.Length > 0)
884                                 xevent.ClientMessageEvent.ptr3 = supported [0];
885                         if (supported.Length > 1)
886                                 xevent.ClientMessageEvent.ptr4 = supported [1];
887                         if (supported.Length > 2)
888                                 xevent.ClientMessageEvent.ptr5 = supported [2];
889
890                         XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
891                 }
892
893                 private void SendDrop (IntPtr handle, IntPtr from, IntPtr time)
894                 {
895                         XEvent xevent = new XEvent ();
896
897                         xevent.AnyEvent.type = XEventName.ClientMessage;
898                         xevent.AnyEvent.display = display;
899                         xevent.ClientMessageEvent.window = handle;
900                         xevent.ClientMessageEvent.message_type = XdndDrop;
901                         xevent.ClientMessageEvent.format = 32;
902                         xevent.ClientMessageEvent.ptr1 = from;
903                         xevent.ClientMessageEvent.ptr3 = time;
904                         
905                         XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
906                 }
907
908                 private void SendPosition (IntPtr handle, IntPtr from, IntPtr action, int x, int y, IntPtr time)
909                 {
910                         XEvent xevent = new XEvent ();
911
912                         xevent.AnyEvent.type = XEventName.ClientMessage;
913                         xevent.AnyEvent.display = display;
914                         xevent.ClientMessageEvent.window = handle;
915                         xevent.ClientMessageEvent.message_type = XdndPosition;
916                         xevent.ClientMessageEvent.format = 32;
917                         xevent.ClientMessageEvent.ptr1 = from;
918                         xevent.ClientMessageEvent.ptr3 = (IntPtr) ((x << 16) | (y & 0xFFFF));
919                         xevent.ClientMessageEvent.ptr4 = time;
920                         xevent.ClientMessageEvent.ptr5 = action;
921                         
922                         XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
923                 }
924
925                 private void SendLeave (IntPtr handle, IntPtr from)
926                 {
927                         XEvent xevent = new XEvent ();
928
929                         xevent.AnyEvent.type = XEventName.ClientMessage;
930                         xevent.AnyEvent.display = display;
931                         xevent.ClientMessageEvent.window = handle;
932                         xevent.ClientMessageEvent.message_type = XdndLeave;
933                         xevent.ClientMessageEvent.format = 32;
934                         xevent.ClientMessageEvent.ptr1 = from;
935
936                         XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
937                 }
938
939                 private void SendFinished ()
940                 {
941                         XEvent xevent = new XEvent ();
942
943                         xevent.AnyEvent.type = XEventName.ClientMessage;
944                         xevent.AnyEvent.display = display;
945                         xevent.ClientMessageEvent.window = source;
946                         xevent.ClientMessageEvent.message_type = XdndFinished;
947                         xevent.ClientMessageEvent.format = 32;
948                         xevent.ClientMessageEvent.ptr1 = toplevel;
949
950                         XplatUIX11.XSendEvent (display, source, false, IntPtr.Zero, ref xevent);
951                 }
952
953                 // There is a somewhat decent amount of overhead
954                 // involved in setting up dnd so we do it lazily
955                 // as a lot of applications do not even use it.
956                 private void Init ()
957                 {
958                         XdndAware = XplatUIX11.XInternAtom (display, "XdndAware", false);
959                         XdndEnter = XplatUIX11.XInternAtom (display, "XdndEnter", false);
960                         XdndLeave = XplatUIX11.XInternAtom (display, "XdndLeave", false);
961                         XdndPosition = XplatUIX11.XInternAtom (display, "XdndPosition", false);
962                         XdndStatus = XplatUIX11.XInternAtom (display, "XdndStatus", false);
963                         XdndDrop = XplatUIX11.XInternAtom (display, "XdndDrop", false);
964                         XdndSelection = XplatUIX11.XInternAtom (display, "XdndSelection", false);
965                         XdndFinished = XplatUIX11.XInternAtom (display, "XdndFinished", false);
966                         XdndTypeList = XplatUIX11.XInternAtom (display, "XdndTypeList", false);
967                         XdndActionCopy = XplatUIX11.XInternAtom (display, "XdndActionCopy", false);
968                         XdndActionMove = XplatUIX11.XInternAtom (display, "XdndActionMove", false);
969                         XdndActionLink = XplatUIX11.XInternAtom (display, "XdndActionLink", false);
970                         //XdndActionPrivate = XplatUIX11.XInternAtom (display, "XdndActionPrivate", false);
971                         //XdndActionList = XplatUIX11.XInternAtom (display, "XdndActionList", false);
972                         //XdndActionDescription = XplatUIX11.XInternAtom (display, "XdndActionDescription", false);
973                         //XdndActionAsk = XplatUIX11.XInternAtom (display, "XdndActionAsk", false);
974
975                         foreach (MimeHandler handler in MimeHandlers) {
976                                 handler.Type = XplatUIX11.XInternAtom (display, handler.Name, false);
977                                 handler.NonProtocol = XplatUIX11.XInternAtom (display,
978                                                 String.Concat ("MWFNonP+", handler.Name), false);
979                         }
980
981                 }
982
983                 private IntPtr [] SourceSupportedList (ref XEvent xevent)
984                 {
985                         IntPtr [] res;
986
987                         
988                         if (((int) xevent.ClientMessageEvent.ptr2 & 0x1) == 0) {
989                                 res = new IntPtr [3];
990                                 res [0] = xevent.ClientMessageEvent.ptr3;
991                                 res [1] = xevent.ClientMessageEvent.ptr4;
992                                 res [2] = xevent.ClientMessageEvent.ptr5;
993                         } else {
994                                 IntPtr type;
995                                 int format;
996                                 IntPtr count;
997                                 IntPtr remaining;
998                                 IntPtr data = IntPtr.Zero;
999
1000                                 XplatUIX11.XGetWindowProperty (display, source, XdndTypeList,
1001                                                 IntPtr.Zero, new IntPtr(32), false, (IntPtr) Atom.XA_ATOM,
1002                                                 out type, out format, out count,
1003                                                 out remaining, ref data);
1004
1005                                 res = new IntPtr [count.ToInt32()];
1006                                 for (int i = 0; i < count.ToInt32(); i++) {
1007                                         res [i] = (IntPtr) Marshal.ReadInt32 (data, i *
1008                                                         Marshal.SizeOf (typeof (int)));
1009                                 }
1010
1011                                 XplatUIX11.XFree (data);
1012                         }
1013
1014                         return res;
1015                 }
1016
1017                 private string GetText (ref XEvent xevent, bool unicode)
1018                 {
1019                         int nread = 0;
1020                         IntPtr nitems;
1021                         IntPtr bytes_after;
1022
1023                         StringBuilder builder = new StringBuilder ();
1024                         do {
1025                                 IntPtr actual_type;
1026                                 int actual_fmt;
1027                                 IntPtr data = IntPtr.Zero;
1028
1029                                 if (0 != XplatUIX11.XGetWindowProperty (display,
1030                                                     xevent.AnyEvent.window,
1031                                                     (IntPtr) xevent.SelectionEvent.property,
1032                                                     IntPtr.Zero, new IntPtr(0xffffff), false,
1033                                                     (IntPtr) Atom.AnyPropertyType, out actual_type,
1034                                                     out actual_fmt, out nitems, out bytes_after,
1035                                                     ref data)) {
1036                                         XplatUIX11.XFree (data);
1037                                         break;
1038                                 }
1039
1040                                 if (unicode)
1041                                         builder.Append (Marshal.PtrToStringUni (data));
1042                                 else
1043                                         builder.Append (Marshal.PtrToStringAnsi (data));
1044                                 nread += nitems.ToInt32();
1045
1046                                 XplatUIX11.XFree (data);
1047                         } while (bytes_after.ToInt32() > 0);
1048                         if (nread == 0)
1049                                 return null;
1050                         return builder.ToString ();
1051                 }
1052
1053                 private MemoryStream GetData (ref XEvent xevent)
1054                 {
1055                         int nread = 0;
1056                         IntPtr nitems;
1057                         IntPtr bytes_after;
1058
1059                         MemoryStream res = new MemoryStream ();
1060                         do {
1061                                 IntPtr actual_type;
1062                                 int actual_fmt;
1063                                 IntPtr data = IntPtr.Zero;
1064
1065                                 if (0 != XplatUIX11.XGetWindowProperty (display,
1066                                                     xevent.AnyEvent.window,
1067                                                     (IntPtr) xevent.SelectionEvent.property,
1068                                                     IntPtr.Zero, new IntPtr(0xffffff), false,
1069                                                     (IntPtr) Atom.AnyPropertyType, out actual_type,
1070                                                     out actual_fmt, out nitems, out bytes_after,
1071                                                     ref data)) {
1072                                         XplatUIX11.XFree (data);
1073                                         break;
1074                                 }
1075
1076                                 for (int i = 0; i < nitems.ToInt32(); i++)
1077                                         res.WriteByte (Marshal.ReadByte (data, i));
1078                                 nread += nitems.ToInt32();
1079
1080                                 XplatUIX11.XFree (data);
1081                         } while (bytes_after.ToInt32() > 0);
1082                         return res;
1083                 }
1084
1085                 private Control MwfWindow (IntPtr window)
1086                 {
1087                         Hwnd hwnd = Hwnd.ObjectFromHandle (window);
1088                         if (hwnd == null)
1089                                 return null;
1090
1091                         Control res = Control.FromHandle (hwnd.client_window);
1092                         return res;
1093                 }
1094
1095                 private bool IsWindowDndAware (IntPtr handle)
1096                 {
1097                         bool res = true;
1098                         // Check the version number, we need greater than 3
1099                         IntPtr actual;
1100                         int format;
1101                         IntPtr count;
1102                         IntPtr remaining;
1103                         IntPtr data = IntPtr.Zero;
1104                         
1105                         XplatUIX11.XGetWindowProperty (display, handle, XdndAware, IntPtr.Zero, new IntPtr(0x8000000), false,
1106                                         (IntPtr) Atom.XA_ATOM, out actual, out format,
1107                                         out count, out remaining, ref data);
1108                         
1109                         if (actual != (IntPtr) Atom.XA_ATOM || format != 32 ||
1110                                         count.ToInt32() == 0 || data == IntPtr.Zero) {
1111                                 if (data != IntPtr.Zero)
1112                                         XplatUIX11.XFree (data);
1113                                 return false;
1114                         }
1115
1116                         int version = Marshal.ReadInt32 (data, 0);
1117
1118                         if (version < 3) {
1119                                 Console.Error.WriteLine ("XDND Version too old (" + version + ").");
1120                                 XplatUIX11.XFree (data);
1121                                 return false;
1122                         }
1123
1124                         // First type is actually the XDND version
1125                         if (count.ToInt32() > 1) {
1126                                 res = false;
1127                                 for (int i = 1; i < count.ToInt32(); i++) {
1128                                         IntPtr type = (IntPtr) Marshal.ReadInt32 (data, i *
1129                                                         Marshal.SizeOf (typeof (int)));
1130                                         for (int j = 0; j < drag_data.SupportedTypes.Length; j++) {
1131                                                 if (drag_data.SupportedTypes [j] == type) {
1132                                                         res = true;
1133                                                         break;
1134                                                 }
1135                                         }
1136                                 }
1137                         }
1138
1139                         XplatUIX11.XFree (data);
1140                         return res;
1141                 }
1142
1143                 private IntPtr [] DetermineSupportedTypes (object data)
1144                 {
1145                         ArrayList res = new ArrayList ();
1146
1147                         if (data is string) {
1148                                 MimeHandler handler = FindHandler ("text/plain");
1149                                 if (handler != null)
1150                                         res.Add (handler.Type);
1151                         }/* else if (data is Bitmap)
1152                                 res.Add (data);
1153
1154                          */
1155
1156                         if (data is IDataObject) {
1157                                 
1158
1159                         }
1160
1161                         if (data is ISerializable) {
1162                                 MimeHandler handler = FindHandler ("application/x-mono-serialized-object");
1163                                 if (handler != null)
1164                                         res.Add (handler.Type);
1165                         }
1166
1167                         return (IntPtr []) res.ToArray (typeof (IntPtr));
1168                 }
1169         }
1170 }