Fix bug #395
[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 // NOTE: We have some tests in Test/System.Windows.Forms/DragAndDropTest.cs, which I *highly* recommend
26 // to run after any change made here, since those tests are interactive, and thus are not part of
27 // the common tests.
28 //
29
30
31 using System;
32 using System.IO;
33 using System.Text;
34 using System.Drawing;
35 using System.Threading;
36 using System.Collections;
37 using System.Runtime.Serialization;
38 using System.Runtime.InteropServices;
39 using System.Runtime.Serialization.Formatters.Binary;
40
41 namespace System.Windows.Forms {
42
43         internal class X11Dnd {
44
45                 private enum State {
46                         Accepting,
47                         Dragging
48                 }
49
50                 private enum DragState {
51                         None,
52                         Beginning,
53                         Dragging,
54                         Entered
55                 }
56
57                 private interface IDataConverter {
58                         void GetData (X11Dnd dnd, IDataObject data, ref XEvent xevent);
59                         void SetData (X11Dnd dnd, object data, ref XEvent xevent);
60                 }
61
62                 private delegate void MimeConverter (IntPtr dsp,
63                                 IDataObject data, ref XEvent xevent);
64
65                 private class MimeHandler {
66                         public string Name;
67                         public string [] Aliases;
68                         public IntPtr Type;
69                         public IntPtr NonProtocol;
70                         public IDataConverter Converter;
71                         
72                         public MimeHandler (string name, IDataConverter converter) : this (name, converter, name)
73                         {
74                         }
75
76                         public MimeHandler (string name, IDataConverter converter, params string [] aliases)
77                         {
78                                 Name = name;
79                                 Converter = converter;
80                                 Aliases = aliases;
81                         }
82
83                         public override string ToString ()
84                         {
85                                 return "MimeHandler {" + Name + "}";
86                         }
87                 }
88
89                 private MimeHandler [] MimeHandlers = {
90 //                        new MimeHandler ("WCF_DIB"),
91 //                        new MimeHandler ("image/gif", new MimeConverter (ImageConverter)),
92 //                      new MimeHandler ("text/rtf", new MimeConverter (RtfConverter)),
93 //                      new MimeHandler ("text/richtext", new MimeConverter (RtfConverter)),
94
95                         new MimeHandler ("text/plain", new TextConverter ()),
96                         new MimeHandler ("text/plain", new TextConverter (), "System.String", DataFormats.Text),
97                         new MimeHandler ("text/html", new HtmlConverter (), DataFormats.Html),
98                         new MimeHandler ("text/uri-list", new UriListConverter (), DataFormats.FileDrop),
99                         new MimeHandler ("application/x-mono-serialized-object",
100                                         new SerializedObjectConverter ())
101                 };
102
103                 private class SerializedObjectConverter : IDataConverter {
104
105                         public void GetData (X11Dnd dnd, IDataObject data, ref XEvent xevent)
106                         {
107                                 MemoryStream stream = dnd.GetData (ref xevent);
108                                 BinaryFormatter bf = new BinaryFormatter ();
109
110                                 if (stream.Length == 0)
111                                         return;
112
113                                 stream.Seek (0, 0);
114                                 object obj = bf.Deserialize (stream);
115                                 data.SetData (obj);
116                         }
117
118                         public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
119                         {
120                                 if (data == null)
121                                         return;
122
123                                 MemoryStream stream = new MemoryStream ();
124                                 BinaryFormatter bf = new BinaryFormatter ();
125
126                                 bf.Serialize (stream, data);
127
128                                 IntPtr buffer = Marshal.AllocHGlobal ((int) stream.Length);
129                                 stream.Seek (0, 0);
130
131                                 for (int i = 0; i < stream.Length; i++) {
132                                         Marshal.WriteByte (buffer, i, (byte) stream.ReadByte ());
133                                 }
134
135                                 dnd.SetProperty (ref xevent, buffer, (int) stream.Length);
136                         }
137                 }
138
139                 private class HtmlConverter : IDataConverter {
140
141                         public void GetData (X11Dnd dnd, IDataObject data, ref XEvent xevent)
142                         {
143                                 string text = dnd.GetText (ref xevent, false);
144                                 if (text == null)
145                                         return;
146                                 data.SetData (DataFormats.Text, text);
147                                 data.SetData (DataFormats.UnicodeText, text);
148                         }
149
150                         public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
151                         {
152                                 IntPtr buffer;
153                                 int len;
154                                 string str = data as string;
155
156                                 if (str == null)
157                                         return;
158
159                                 if (xevent.SelectionRequestEvent.target == (IntPtr)Atom.XA_STRING) {
160                                         byte [] bytes = Encoding.ASCII.GetBytes (str);
161                                         buffer = Marshal.AllocHGlobal (bytes.Length);
162                                         len = bytes.Length;
163                                         for (int i = 0; i < len; i++)
164                                                 Marshal.WriteByte (buffer, i, bytes [i]);
165                                 } else {
166                                         buffer = Marshal.StringToHGlobalAnsi (str);
167                                         len = 0;
168                                         while (Marshal.ReadByte (buffer, len) != 0)
169                                                 len++;
170                                 }
171
172                                 dnd.SetProperty (ref xevent, buffer, len);
173
174                                 Marshal.FreeHGlobal (buffer);
175                         }
176                 }
177
178                 private class TextConverter : IDataConverter {
179
180                         public void GetData (X11Dnd dnd, IDataObject data, ref XEvent xevent)
181                         {
182                                 string text = dnd.GetText (ref xevent, true);
183                                 if (text == null)
184                                         return;
185                                 data.SetData (DataFormats.Text, text);
186                                 data.SetData (DataFormats.UnicodeText, text);
187                         }
188
189                         public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
190                         {
191                                 IntPtr buffer;
192                                 int len;
193                                 string str = data as string;
194
195                                 if (str == null) {
196                                         IDataObject dobj = data as IDataObject;
197                                         if (dobj == null)
198                                                 return;
199                                         str = (string) dobj.GetData ("System.String", true);
200                                 }
201
202                                 if (xevent.SelectionRequestEvent.target == (IntPtr)Atom.XA_STRING) {
203                                         byte [] bytes = Encoding.ASCII.GetBytes (str);
204                                         buffer = Marshal.AllocHGlobal (bytes.Length);
205                                         len = bytes.Length;
206                                         for (int i = 0; i < len; i++)
207                                                 Marshal.WriteByte (buffer, i, bytes [i]);
208                                 } else {
209                                         buffer = Marshal.StringToHGlobalAnsi (str);
210                                         len = 0;
211                                         while (Marshal.ReadByte (buffer, len) != 0)
212                                                 len++;
213                                 }
214
215                                 dnd.SetProperty (ref xevent, buffer, len);
216
217                                 Marshal.FreeHGlobal (buffer);
218                         }
219                 }
220
221                 private class UriListConverter : IDataConverter {
222
223                         public void GetData (X11Dnd dnd, IDataObject data, ref XEvent xevent)
224                         {
225                                 string text = dnd.GetText (ref xevent, false);
226                                 if (text == null)
227                                         return;
228
229                                 // TODO: Do this in a loop instead of just splitting
230                                 ArrayList uri_list = new ArrayList ();
231                                 string [] lines = text.Split (new char [] { '\r', '\n' });
232                                 foreach (string line in lines) {
233                                         // # is a comment line (see RFC 2483)
234                                         if (line.StartsWith ("#"))
235                                                 continue;
236                                         try {
237                                                 Uri uri = new Uri (line);
238                                                 uri_list.Add (uri.LocalPath);
239                                         } catch { }
240                                 }
241
242                                 string [] l = (string []) uri_list.ToArray (typeof (string));
243                                 if (l.Length < 1)
244                                         return;
245                                 data.SetData (DataFormats.FileDrop, l);
246                                 data.SetData ("FileName", l [0]);
247                                 data.SetData ("FileNameW", l [0]);
248                         }
249
250                         public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
251                         {
252                                 string [] uri_list = data as string [];
253
254                                 if (uri_list == null) {
255                                         IDataObject dobj = data as IDataObject;
256                                         if (dobj == null)
257                                                 return;
258                                         uri_list = dobj.GetData (DataFormats.FileDrop, true) as string [];
259                                 }
260
261                                 if (uri_list == null)
262                                         return;
263
264                                 StringBuilder res = new StringBuilder ();
265                                 foreach (string uri_str in uri_list) {
266                                         Uri uri = new Uri (uri_str);
267                                         res.Append (uri.ToString ());
268                                         res.Append ("\r\n");
269                                 }
270
271                                 IntPtr buffer = Marshal.StringToHGlobalAnsi ((string) res.ToString ());
272                                 int len = 0;
273                                 while (Marshal.ReadByte (buffer, len) != 0)
274                                         len++;
275
276                                 dnd.SetProperty (ref xevent, buffer, len);
277                         }
278                 }
279
280                 private class DragData {
281                         public IntPtr Window;
282                         public DragState State;
283                         public object Data;
284                         public IntPtr Action;
285                         public IntPtr [] SupportedTypes;
286                         public MouseButtons MouseState;
287                         public DragDropEffects AllowedEffects;
288                         public Point CurMousePos;
289                         
290                         public IntPtr LastWindow;
291                         public IntPtr LastTopLevel;
292
293                         public bool WillAccept;
294                         
295                         public void Reset ()
296                         {
297                                 State = DragState.None;
298                                 Data = null;
299                                 SupportedTypes = null;
300                                 WillAccept = false;
301                         }
302                 }
303
304                 // This version seems to be the most common
305                 private static readonly IntPtr [] XdndVersion = new IntPtr [] { new IntPtr (4) }; 
306
307                 private IntPtr display;
308                 private DragData drag_data;
309                 
310                 private IntPtr XdndAware;
311                 private IntPtr XdndSelection;
312                 private IntPtr XdndEnter;
313                 private IntPtr XdndLeave;
314                 private IntPtr XdndPosition;
315                 private IntPtr XdndDrop;
316                 private IntPtr XdndFinished;
317                 private IntPtr XdndStatus;
318                 private IntPtr XdndTypeList;
319                 private IntPtr XdndActionCopy;
320                 private IntPtr XdndActionMove;
321                 private IntPtr XdndActionLink;
322                 //private IntPtr XdndActionPrivate;
323                 private IntPtr XdndActionList;
324                 //private IntPtr XdndActionDescription;
325                 //private IntPtr XdndActionAsk;
326
327                 //private State state;
328
329                 private int converts_pending;
330                 private bool position_recieved;
331                 private bool status_sent;
332                 private IntPtr target;
333                 private IntPtr source;
334                 private IntPtr toplevel;
335                 private IDataObject data;
336
337                 private Control control;
338                 private int pos_x, pos_y;
339                 private DragDropEffects allowed;
340                 private DragEventArgs drag_event;
341
342                 private Cursor CursorNo;
343                 private Cursor CursorCopy;
344                 private Cursor CursorMove;
345                 private Cursor CursorLink;
346                 // check out the TODO below
347                 //private IntPtr CurrentCursorHandle;
348
349                 private bool tracking = false;
350                 private bool dropped = false;
351                 private int motion_poll;
352                 //private X11Keyboard keyboard;
353
354                 public X11Dnd (IntPtr display, X11Keyboard keyboard)
355                 {
356                         this.display = display;
357                         //this.keyboard = keyboard;
358
359                         Init ();
360                 }
361
362                 public bool InDrag()
363                 {
364                         if (drag_data == null)
365                                 return false;
366                         return drag_data.State != DragState.None;
367                 }
368                 
369                 public void SetAllowDrop (Hwnd hwnd, bool allow)
370                 {
371                         int[] atoms;
372
373                         if (hwnd.allow_drop == allow)
374                                 return;
375
376                         atoms = new int[XdndVersion.Length];
377                         for (int i = 0; i < XdndVersion.Length; i++) {
378                                 atoms[i] = XdndVersion[i].ToInt32();
379                         }
380
381                         XplatUIX11.XChangeProperty (display, hwnd.whole_window, XdndAware,
382                                         (IntPtr) Atom.XA_ATOM, 32,
383                                         PropertyMode.Replace, atoms, allow ? 1 : 0);
384                         hwnd.allow_drop = allow;
385                 }
386
387                 public DragDropEffects StartDrag (IntPtr handle, object data,
388                                 DragDropEffects allowed_effects)
389                 {
390                         drag_data = new DragData ();
391                         drag_data.Window = handle;
392                         drag_data.State = DragState.Beginning;
393                         drag_data.MouseState = XplatUIX11.MouseState;
394                         drag_data.Data = data;
395                         drag_data.SupportedTypes = DetermineSupportedTypes (data);
396                         drag_data.AllowedEffects = allowed_effects;
397                         drag_data.Action = ActionFromEffect (allowed_effects);
398
399                         if (CursorNo == null) {
400                                 // Make sure the cursors are created
401                                 CursorNo = new Cursor (typeof (X11Dnd), "DnDNo.cur");
402                                 CursorCopy = new Cursor (typeof (X11Dnd), "DnDCopy.cur");
403                                 CursorMove = new Cursor (typeof (X11Dnd), "DnDMove.cur");
404                                 CursorLink = new Cursor (typeof (X11Dnd), "DnDLink.cur");
405                         }
406
407                         drag_data.LastTopLevel = IntPtr.Zero;
408                         control = null;
409
410                         System.Windows.Forms.MSG msg = new MSG();
411                         object queue_id = XplatUI.StartLoop (Thread.CurrentThread);
412
413                         Timer timer = new Timer ();
414                         timer.Tick += new EventHandler (DndTickHandler);
415                         timer.Interval = 100;
416
417                         int suc;
418                         drag_data.State = DragState.Dragging;
419
420                         suc = XplatUIX11.XSetSelectionOwner (display, XdndSelection,
421                                         drag_data.Window, IntPtr.Zero);
422
423                         if (suc == 0) {
424                                 Console.Error.WriteLine ("Could not take ownership of XdndSelection aborting drag.");
425                                 drag_data.Reset ();
426                                 return DragDropEffects.None;
427                         }
428
429                         drag_data.State = DragState.Dragging;
430                         drag_data.CurMousePos = new Point ();
431                         source = toplevel = target = IntPtr.Zero;
432                         dropped = false;
433                         tracking = true;
434                         motion_poll = -1;
435                         timer.Start ();
436
437                         // Send Enter to the window initializing the dnd operation - which initializes the data
438                         SendEnter (drag_data.Window, drag_data.Window, drag_data.SupportedTypes);
439                         drag_data.LastTopLevel = toplevel;
440
441                         while (tracking && XplatUI.GetMessage (queue_id, ref msg, IntPtr.Zero, 0, 0)) {
442
443                                 if (msg.message >= Msg.WM_KEYFIRST && msg.message <= Msg.WM_KEYLAST) {
444                                         HandleKeyMessage (msg);
445                                 } else {
446                                         switch (msg.message) {
447                                         case Msg.WM_LBUTTONUP:
448                                         case Msg.WM_RBUTTONUP:
449                                         case Msg.WM_MBUTTONUP:
450                                                 if (msg.message == Msg.WM_LBUTTONDOWN && drag_data.MouseState != MouseButtons.Left)
451                                                         break;;
452                                                 if (msg.message == Msg.WM_RBUTTONDOWN && drag_data.MouseState != MouseButtons.Right)
453                                                         break;
454                                                 if (msg.message == Msg.WM_MBUTTONDOWN && drag_data.MouseState != MouseButtons.Middle)
455                                                         break;
456                                                 
457                                                 HandleButtonUpMsg ();
458
459                                                 // We don't want to dispatch button up neither (Match .Net)
460                                                 // Thus we have to remove capture by ourselves
461                                                 RemoveCapture (msg.hwnd);
462                                                 continue;
463                                         case Msg.WM_MOUSEMOVE:
464                                                 motion_poll = 0;
465
466                                                 drag_data.CurMousePos.X = Control.LowOrder ((int) msg.lParam.ToInt32 ());
467                                                 drag_data.CurMousePos.Y = Control.HighOrder ((int) msg.lParam.ToInt32 ());
468
469                                                 HandleMouseOver ();
470                                                 // We don't want to dispatch mouse move
471                                                 continue;
472                                         }
473
474                                         XplatUI.DispatchMessage (ref msg);
475                                 }
476                         }
477
478                         timer.Stop ();
479
480                         // If the target is a mwf control, return until DragEnter/DragLeave has been fired,
481                         // which means the respective -already sent- dnd ClientMessages have been received and handled.
482                         if (control != null)
483                                 Application.DoEvents ();
484
485                         if (!dropped)
486                                 return DragDropEffects.None;
487                         if (drag_event != null)
488                                 return drag_event.Effect;
489
490                         // Fallback.
491                         return DragDropEffects.None;
492                 }
493
494                 private void DndTickHandler (object sender, EventArgs e)
495                 {
496                         // This is to make sure we don't get stuck in a loop if another
497                         // app doesn't finish the DND operation
498                         if (dropped) {
499                                 Timer t = (Timer) sender;
500                                 if (t.Interval == 500)
501                                         tracking = false;
502                                 else
503                                         t.Interval = 500;
504                         }
505
506
507                         // If motion_poll is -1, there hasn't been motion at all, so don't simulate motion yet.
508                         // Otherwise if more than 100 milliseconds have lapsed, we assume the pointer is not
509                         // in motion anymore, and we simulate the mouse over operation, like .Net does.
510                         if (motion_poll > 1)
511                                 HandleMouseOver ();
512                         else if (motion_poll > -1)
513                                 motion_poll++;
514                 }
515
516                 // This routines helps us to have a DndEnter/DndLeave fallback when there wasn't any mouse movement
517                 // as .Net does
518                 private void DefaultEnterLeave (object user_data)
519                 {
520                         IntPtr toplevel, window;
521                         int x_root, y_root;
522
523                         // The window generating the operation could be a different than the one under pointer
524                         GetWindowsUnderPointer (out window, out toplevel, out x_root, out y_root);
525                         Control source_control = Control.FromHandle (window);
526                         if (source_control == null || !source_control.AllowDrop)
527                                 return;
528
529                         // `data' and other members are already available
530                         Point pos = Control.MousePosition;
531                         DragEventArgs drag_args = new DragEventArgs (data, 0, pos.X, pos.Y, drag_data.AllowedEffects, DragDropEffects.None);
532
533                         source_control.DndEnter (drag_args);
534                         if ((drag_args.Effect & drag_data.AllowedEffects) != 0)
535                                 source_control.DndDrop (drag_args);
536                         else
537                                 source_control.DndLeave (EventArgs.Empty);
538                 }
539
540                 public void HandleButtonUpMsg ()
541                 {
542                         if (drag_data.State == DragState.Beginning) {
543                                 //state = State.Accepting;
544                         } else if (drag_data.State != DragState.None) {
545
546                                 if (drag_data.WillAccept) {
547
548                                         if (QueryContinue (false, DragAction.Drop))
549                                                 return;                                 
550                                 } else {
551
552                                         if (QueryContinue (false, DragAction.Cancel))
553                                                 return;
554
555                                         // fallback if no movement was detected, as .net does.
556                                         if (motion_poll == -1)
557                                                 DefaultEnterLeave (drag_data.Data);
558                                 }
559
560                                 drag_data.State = DragState.None;
561                                 // WE can't reset the drag data yet as it is still
562                                 // most likely going to be used by the SelectionRequest
563                                 // handlers
564                         }
565
566                         return;
567                 }
568
569                 private void RemoveCapture (IntPtr handle)
570                 {
571                         Control c = MwfWindow (handle);
572                         if (c.InternalCapture)
573                                 c.InternalCapture = false;
574                 }
575
576                 public bool HandleMouseOver ()
577                 {
578                         IntPtr toplevel, window;
579                         int x_root, y_root;
580
581                         GetWindowsUnderPointer (out window, out toplevel, out x_root, out y_root);
582
583                         if (window != drag_data.LastWindow && drag_data.State == DragState.Entered) {
584                                 drag_data.State = DragState.Dragging;
585
586                                 // TODO: Send a Leave if this is an MWF window
587
588                                 if (toplevel != drag_data.LastTopLevel)
589                                         SendLeave (drag_data.LastTopLevel, toplevel);
590                         }
591
592                         drag_data.State = DragState.Entered;
593                         if (toplevel != drag_data.LastTopLevel) {
594                                 // Entering a new toplevel window
595                                 SendEnter (toplevel, drag_data.Window, drag_data.SupportedTypes);
596                         } else {
597                                 // Already in a toplevel window, so send a position
598                                 SendPosition (toplevel, drag_data.Window,
599                                                 drag_data.Action,
600                                                 x_root, y_root,
601                                                 IntPtr.Zero);
602                         }
603
604                         drag_data.LastTopLevel = toplevel;
605                         drag_data.LastWindow = window;
606                         return true;
607                 }
608
609                 void GetWindowsUnderPointer (out IntPtr window, out IntPtr toplevel, out int x_root, out int y_root)
610                 {
611                         toplevel = IntPtr.Zero;
612                         window = XplatUIX11.RootWindowHandle;
613
614                         IntPtr root, child;
615                         bool dnd_aware = false;
616                         int x_temp, y_temp;
617                         int mask_return;
618                         int x = x_root = drag_data.CurMousePos.X;
619                         int y = y_root = drag_data.CurMousePos.Y;
620
621                         while (XplatUIX11.XQueryPointer (display, window, out root, out child,
622                                                out x_temp, out y_temp, out x, out y, out mask_return)) {
623                                         
624                                 if (!dnd_aware) {
625                                         dnd_aware = IsWindowDndAware (window);
626                                         if (dnd_aware) {
627                                                 toplevel = window;
628                                                 x_root = x_temp;
629                                                 y_root = y_temp;
630                                         }
631                                 }
632
633                                 if (child == IntPtr.Zero)
634                                         break;
635                                         
636                                 window = child;
637                         }
638                 }
639
640                 public void HandleKeyMessage (MSG msg)
641                 {
642                         if (VirtualKeys.VK_ESCAPE == (VirtualKeys) msg.wParam.ToInt32()) {
643                                 QueryContinue (true, DragAction.Cancel);
644                         }
645                 }
646                 
647                 // return true if the event is handled here
648                 public bool HandleClientMessage (ref XEvent xevent)
649                 {
650                         // most common so we check it first
651                         if (xevent.ClientMessageEvent.message_type == XdndPosition)
652                                 return Accepting_HandlePositionEvent (ref xevent);
653                         if (xevent.ClientMessageEvent.message_type == XdndEnter)
654                                 return Accepting_HandleEnterEvent (ref xevent);
655                         if (xevent.ClientMessageEvent.message_type == XdndDrop)
656                                 return Accepting_HandleDropEvent (ref xevent);
657                         if (xevent.ClientMessageEvent.message_type == XdndLeave)
658                                 return Accepting_HandleLeaveEvent (ref xevent);
659                         if (xevent.ClientMessageEvent.message_type == XdndStatus)
660                                 return HandleStatusEvent (ref xevent);
661                         if (xevent.ClientMessageEvent.message_type == XdndFinished)
662                                 return HandleFinishedEvent (ref xevent);
663
664                         return false;
665                 }
666
667                 public bool HandleSelectionNotifyEvent (ref XEvent xevent)
668                 {
669                         MimeHandler handler = FindHandler ((IntPtr) xevent.SelectionEvent.target);
670                         if (handler == null)
671                                 return false;
672                         if (data == null)
673                                 data = new DataObject ();
674
675                         handler.Converter.GetData (this, data, ref xevent);
676
677                         converts_pending--;
678                         if (converts_pending <= 0 && position_recieved) {
679                                 drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
680                                         allowed, DragDropEffects.None);
681                                 control.DndEnter (drag_event);
682                                 SendStatus (source, drag_event.Effect);
683                                 status_sent = true;
684                         }
685                         return true;
686                 }
687
688                 public bool HandleSelectionRequestEvent (ref XEvent xevent)
689                 {
690                         if (xevent.SelectionRequestEvent.selection != XdndSelection)
691                                 return false;
692
693                         MimeHandler handler = FindHandler (xevent.SelectionRequestEvent.target);
694                         if (handler == null)
695                                 return false;
696
697                         handler.Converter.SetData (this, drag_data.Data, ref xevent);
698
699                         return true;
700                 }
701
702                 private bool QueryContinue (bool escape, DragAction action)
703                 {
704                         QueryContinueDragEventArgs qce = new QueryContinueDragEventArgs ((int) XplatUI.State.ModifierKeys,
705                                         escape, action);
706
707                         Control c = MwfWindow (source);
708                         
709                         if (c == null) {
710                                 tracking = false;
711                                 return false;
712                         }
713                         
714                         c.DndContinueDrag (qce);
715
716                         switch (qce.Action) {
717                         case DragAction.Continue:
718                                 return true;
719                         case DragAction.Drop:
720                                 SendDrop (drag_data.LastTopLevel, source, IntPtr.Zero);
721                                 tracking = false;
722                                 return true;
723                         case DragAction.Cancel:
724                                 drag_data.Reset ();
725                                 c.InternalCapture = false;
726                                 break;
727                         }
728
729                         SendLeave (drag_data.LastTopLevel, toplevel);
730
731                         RestoreDefaultCursor ();
732                         tracking = false;
733                         return false;
734                 }
735
736                 private void RestoreDefaultCursor ()
737                 {
738                         // Releasing the mouse buttons should automatically restore the default cursor,
739                         // but canceling the operation using QueryContinue should restore it even if the
740                         // mouse buttons are not released yet.
741                         XplatUIX11.XChangeActivePointerGrab (display,
742                                         EventMask.ButtonMotionMask |
743                                         EventMask.PointerMotionMask |
744                                         EventMask.ButtonPressMask |
745                                         EventMask.ButtonReleaseMask,
746                                         Cursors.Default.Handle, IntPtr.Zero);
747
748                 }
749
750                 private void GiveFeedback (IntPtr action)
751                 {
752                         GiveFeedbackEventArgs gfe = new GiveFeedbackEventArgs (EffectFromAction (drag_data.Action), true);
753
754                         Control c = MwfWindow (source);
755                         c.DndFeedback (gfe);
756
757                         if (gfe.UseDefaultCursors) {
758                                 Cursor cursor = CursorNo;
759                                 if (drag_data.WillAccept) {
760                                         // Same order as on MS
761                                         if (action == XdndActionCopy)
762                                                 cursor = CursorCopy;
763                                         else if (action == XdndActionLink)
764                                                 cursor = CursorLink;
765                                         else if (action == XdndActionMove)
766                                                 cursor = CursorMove;
767                                 }
768                                 // TODO: Try not to set the cursor so much
769                                 //if (cursor.Handle != CurrentCursorHandle) {
770                                 XplatUIX11.XChangeActivePointerGrab (display,
771                                                 EventMask.ButtonMotionMask |
772                                                 EventMask.PointerMotionMask |
773                                                 EventMask.ButtonPressMask |
774                                                 EventMask.ButtonReleaseMask,
775                                                 cursor.Handle, IntPtr.Zero);
776                                 //CurrentCursorHandle = cursor.Handle;
777                                 //}
778                         }
779                 }
780
781                 private void SetProperty (ref XEvent xevent, IntPtr data, int length)
782                 {
783                         XEvent sel = new XEvent();
784                         sel.SelectionEvent.type = XEventName.SelectionNotify;
785                         sel.SelectionEvent.send_event = true;
786                         sel.SelectionEvent.display = display;
787                         sel.SelectionEvent.selection = xevent.SelectionRequestEvent.selection;
788                         sel.SelectionEvent.target = xevent.SelectionRequestEvent.target;
789                         sel.SelectionEvent.requestor = xevent.SelectionRequestEvent.requestor;
790                         sel.SelectionEvent.time = xevent.SelectionRequestEvent.time;
791                         sel.SelectionEvent.property = IntPtr.Zero;
792
793                         XplatUIX11.XChangeProperty (display, xevent.SelectionRequestEvent.requestor,
794                                         xevent.SelectionRequestEvent.property,
795                                         xevent.SelectionRequestEvent.target,
796                                         8, PropertyMode.Replace, data, length);
797                         sel.SelectionEvent.property = xevent.SelectionRequestEvent.property;
798
799                         XplatUIX11.XSendEvent (display, xevent.SelectionRequestEvent.requestor, false,
800                                         (IntPtr)EventMask.NoEventMask, ref sel);
801                         return;
802                 }
803
804                 private void Reset ()
805                 {
806                         ResetSourceData ();
807                         ResetTargetData ();
808                 }
809
810                 private void ResetSourceData ()
811                 {
812                         converts_pending = 0;
813                         data = null;
814                 }
815
816                 private void ResetTargetData ()
817                 {
818                         position_recieved = false;
819                         status_sent = false;
820                 }
821                 
822                 private bool Accepting_HandleEnterEvent (ref XEvent xevent)
823                 {
824                         Reset ();
825
826                         source = xevent.ClientMessageEvent.ptr1;
827                         toplevel = xevent.AnyEvent.window;
828                         target = IntPtr.Zero;
829
830                         ConvertData (ref xevent);
831
832                         return true;
833                 }
834
835                 private bool Accepting_HandlePositionEvent (ref XEvent xevent)
836                 {
837                         pos_x = (int) xevent.ClientMessageEvent.ptr3 >> 16;
838                         pos_y = (int) xevent.ClientMessageEvent.ptr3 & 0xFFFF;
839
840                         // Copy is implicitly allowed
841                         Control source_control = MwfWindow (source);
842                         if (source_control == null)
843                                 allowed = EffectsFromX11Source (source, xevent.ClientMessageEvent.ptr5) | DragDropEffects.Copy;
844                         else
845                                 allowed = drag_data.AllowedEffects;
846
847                         IntPtr parent, child, new_child, last_drop_child;
848                         parent = XplatUIX11.XRootWindow (display, 0);
849                         child = toplevel;
850                         last_drop_child = IntPtr.Zero;
851                         while (true) {
852                                 int xd, yd;
853                                 new_child = IntPtr.Zero;
854                                 
855                                 if (!XplatUIX11.XTranslateCoordinates (display,
856                                                     parent, child, pos_x, pos_y,
857                                                     out xd, out yd, out new_child))
858                                         break;
859                                 if (new_child == IntPtr.Zero)
860                                         break;
861                                 child = new_child;
862
863                                 Hwnd h = Hwnd.ObjectFromHandle (child);
864                                 if (h != null) {
865                                         Control d = Control.FromHandle (h.client_window);
866                                         if (d != null && d.allow_drop)
867                                                 last_drop_child = child;
868                                 }
869                         }
870
871                         if (last_drop_child != IntPtr.Zero)
872                                 child = last_drop_child;
873
874                         if (target != child) {
875                                 // We have moved into a new control 
876                                 // or into a control for the first time
877                                 Finish ();
878                         }
879                         target = child;
880                         Hwnd hwnd = Hwnd.ObjectFromHandle (target);
881                         if (hwnd == null)
882                                 return true;
883
884                         Control c = Control.FromHandle (hwnd.client_window);
885
886                         if (c == null)
887                                 return true;
888                         if (!c.allow_drop) {
889                                 SendStatus (source, DragDropEffects.None);
890                                 Finish ();
891                                 return true;
892                         }
893
894                         control = c;
895                         position_recieved = true;                       
896
897                         if (converts_pending > 0)
898                                 return true;
899
900                         if (!status_sent) {
901                                 drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
902                                         allowed, DragDropEffects.None);
903                                 control.DndEnter (drag_event);
904                                 
905                                 SendStatus (source, drag_event.Effect);
906                                 status_sent = true;
907                         } else {
908                                 drag_event.x = pos_x;
909                                 drag_event.y = pos_y;
910                                 control.DndOver (drag_event);
911
912                                 SendStatus (source, drag_event.Effect);
913                         }
914                         
915                         return true;
916                 }
917
918                 private void Finish ()
919                 {
920                         if (control != null) {
921                                 if (drag_event == null) {
922                                         if (data == null)
923                                                 data = new DataObject ();
924                                         drag_event = new DragEventArgs (data,
925                                                         0, pos_x, pos_y,
926                                         allowed, DragDropEffects.None);
927                                 }
928                                 control.DndLeave (drag_event);
929                                 control = null;
930                         }
931                         ResetTargetData ();
932                 }
933
934                 private bool Accepting_HandleDropEvent (ref XEvent xevent)
935                 {
936                         if (control != null && drag_event != null) {
937                                 drag_event = new DragEventArgs (data,
938                                                 0, pos_x, pos_y,
939                                         allowed, drag_event.Effect);
940                                 control.DndDrop (drag_event);
941                         }
942                         SendFinished ();
943                         return true;
944                 }
945
946                 private bool Accepting_HandleLeaveEvent (ref XEvent xevent)
947                 {
948                         if (control != null && drag_event != null)
949                                 control.DndLeave (drag_event);
950                         // Reset ();
951                         return true;
952                 }
953
954                 private bool HandleStatusEvent (ref XEvent xevent)
955                 {
956                         if (drag_data != null && drag_data.State == DragState.Entered) {
957
958                                 if (!QueryContinue (false, DragAction.Continue))
959                                         return true;
960
961                                 drag_data.WillAccept = ((int) xevent.ClientMessageEvent.ptr2 & 0x1) != 0;
962                                 
963                                 GiveFeedback (xevent.ClientMessageEvent.ptr5);
964                         }
965                         return true;
966                 }
967
968                 private bool HandleFinishedEvent (ref XEvent xevent)
969                 {
970                         return true;
971                 }
972
973                 private DragDropEffects EffectsFromX11Source (IntPtr source, IntPtr action_atom)
974                 {
975                         DragDropEffects allowed = DragDropEffects.None;
976                         IntPtr type, count, remaining, data = IntPtr.Zero;
977                         int format;
978
979                         XplatUIX11.XGetWindowProperty (display, source, XdndActionList,
980                                         IntPtr.Zero, new IntPtr (32), false, (IntPtr) Atom.AnyPropertyType,
981                                         out type, out format, out count, out remaining, ref data);
982
983                         int intptr_size = Marshal.SizeOf (typeof (IntPtr));
984                         for (int i = 0; i < count.ToInt32 (); i++) {
985                                 IntPtr current_atom = Marshal.ReadIntPtr (data, i * intptr_size);
986                                 allowed |= EffectFromAction (current_atom);
987                         }
988
989                         // if source is not providing the action list, use the
990                         // default action passed in the x11 dnd position message
991                         if (allowed == DragDropEffects.None)
992                                 allowed = EffectFromAction (action_atom);
993
994                         return allowed;
995                 }
996
997                 private DragDropEffects EffectFromAction (IntPtr action)
998                 {
999                         if (action == XdndActionCopy)
1000                                 return DragDropEffects.Copy;
1001                         else if (action == XdndActionMove)
1002                                 return DragDropEffects.Move;
1003                         if (action == XdndActionLink)
1004                                 return DragDropEffects.Link;
1005
1006                         return DragDropEffects.None;
1007                 }
1008
1009                 private IntPtr ActionFromEffect (DragDropEffects effect)
1010                 {
1011                         IntPtr action = IntPtr.Zero;
1012
1013                         // We can't OR together actions on XDND so sadly the primary
1014                         // is the only one shown here
1015                         if ((effect & DragDropEffects.Copy) != 0)
1016                                 action = XdndActionCopy;
1017                         else if ((effect & DragDropEffects.Move) != 0)
1018                                 action = XdndActionMove;
1019                         else if ((effect & DragDropEffects.Link) != 0)
1020                                 action = XdndActionLink;
1021                         return action;
1022                 }
1023
1024                 private bool ConvertData (ref XEvent xevent)
1025                 {
1026                         bool match = false;
1027
1028                         Control mwfcontrol = MwfWindow (source);
1029
1030                         /* To take advantage of the mwfcontrol, we have to be sure
1031                            that the dnd operation is still happening (since messages are asynchronous) */
1032                         if (mwfcontrol != null && drag_data != null) {
1033                                 if (!tracking)
1034                                         return false;
1035
1036                                 IDataObject dragged = drag_data.Data as IDataObject;
1037                                 if (dragged != null) {
1038                                         data = dragged;
1039                                 } else {
1040                                         if (data == null)
1041                                                 data = new DataObject ();
1042                                         SetDataWithFormats (drag_data.Data);
1043                                 }
1044                                 return true;
1045                         }
1046
1047                         foreach (IntPtr atom in SourceSupportedList (ref xevent)) {
1048                                 MimeHandler handler = FindHandler (atom);
1049                                 if (handler == null)
1050                                         continue;
1051                                 XplatUIX11.XConvertSelection (display, XdndSelection, handler.Type,
1052                                         handler.NonProtocol, toplevel, IntPtr.Zero /* CurrentTime */);
1053                                 converts_pending++;
1054                                 match = true;
1055                         }
1056                         return match;
1057                 }
1058
1059                 private void SetDataWithFormats (object value)
1060                 {
1061                         if (value is string) {
1062                                 data.SetData (DataFormats.Text, value);
1063                                 data.SetData (DataFormats.UnicodeText, value);
1064                         }
1065
1066                         data.SetData (value);
1067                 }
1068
1069                 private MimeHandler FindHandler (IntPtr atom)
1070                 {
1071                         if (atom == IntPtr.Zero)
1072                                 return null;
1073                         foreach (MimeHandler handler in MimeHandlers) {
1074                                 if (handler.Type == atom)
1075                                         return handler;
1076                         }
1077                         return null;
1078                 }
1079
1080                 private MimeHandler FindHandler (string name)
1081                 {
1082                         foreach (MimeHandler handler in MimeHandlers) {
1083                                 foreach (string alias in handler.Aliases) {
1084                                         if (alias == name)
1085                                                 return handler;
1086                                 }
1087                         }
1088                         return null;
1089                 }
1090
1091                 private void SendStatus (IntPtr source, DragDropEffects effect)
1092                 {
1093                         XEvent xevent = new XEvent ();
1094
1095                         xevent.AnyEvent.type = XEventName.ClientMessage;
1096                         xevent.AnyEvent.display = display;
1097                         xevent.ClientMessageEvent.window = source;
1098                         xevent.ClientMessageEvent.message_type = XdndStatus;
1099                         xevent.ClientMessageEvent.format = 32;
1100                         xevent.ClientMessageEvent.ptr1 = toplevel;
1101                         if (effect != DragDropEffects.None && (effect & allowed) != 0)
1102                                 xevent.ClientMessageEvent.ptr2 = (IntPtr) 1;
1103
1104                         xevent.ClientMessageEvent.ptr5 = ActionFromEffect (effect);
1105                         XplatUIX11.XSendEvent (display, source, false, IntPtr.Zero, ref xevent);
1106                 }
1107
1108                 private void SendEnter (IntPtr handle, IntPtr from, IntPtr [] supported)
1109                 {
1110                         XEvent xevent = new XEvent ();
1111
1112                         xevent.AnyEvent.type = XEventName.ClientMessage;
1113                         xevent.AnyEvent.display = display;
1114                         xevent.ClientMessageEvent.window = handle;
1115                         xevent.ClientMessageEvent.message_type = XdndEnter;
1116                         xevent.ClientMessageEvent.format = 32;
1117                         xevent.ClientMessageEvent.ptr1 = from;
1118
1119                         // (int) xevent.ClientMessageEvent.ptr2 & 0x1)
1120                         // int ptr2 = 0x1;
1121                         // xevent.ClientMessageEvent.ptr2 = (IntPtr) ptr2;
1122                         // (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~(0xFF << 24)) | ((v) << 24)
1123                         xevent.ClientMessageEvent.ptr2 = (IntPtr) ((long)XdndVersion [0] << 24);
1124                         
1125                         if (supported.Length > 0)
1126                                 xevent.ClientMessageEvent.ptr3 = supported [0];
1127                         if (supported.Length > 1)
1128                                 xevent.ClientMessageEvent.ptr4 = supported [1];
1129                         if (supported.Length > 2)
1130                                 xevent.ClientMessageEvent.ptr5 = supported [2];
1131
1132                         XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
1133                 }
1134
1135                 private void SendDrop (IntPtr handle, IntPtr from, IntPtr time)
1136                 {
1137                         XEvent xevent = new XEvent ();
1138
1139                         xevent.AnyEvent.type = XEventName.ClientMessage;
1140                         xevent.AnyEvent.display = display;
1141                         xevent.ClientMessageEvent.window = handle;
1142                         xevent.ClientMessageEvent.message_type = XdndDrop;
1143                         xevent.ClientMessageEvent.format = 32;
1144                         xevent.ClientMessageEvent.ptr1 = from;
1145                         xevent.ClientMessageEvent.ptr3 = time;
1146                         
1147                         XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
1148                         dropped = true;
1149                 }
1150
1151                 private void SendPosition (IntPtr handle, IntPtr from, IntPtr action, int x, int y, IntPtr time)
1152                 {
1153                         XEvent xevent = new XEvent ();
1154
1155                         xevent.AnyEvent.type = XEventName.ClientMessage;
1156                         xevent.AnyEvent.display = display;
1157                         xevent.ClientMessageEvent.window = handle;
1158                         xevent.ClientMessageEvent.message_type = XdndPosition;
1159                         xevent.ClientMessageEvent.format = 32;
1160                         xevent.ClientMessageEvent.ptr1 = from;
1161                         xevent.ClientMessageEvent.ptr3 = (IntPtr) ((x << 16) | (y & 0xFFFF));
1162                         xevent.ClientMessageEvent.ptr4 = time;
1163                         xevent.ClientMessageEvent.ptr5 = action;
1164                         
1165                         XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
1166                 }
1167
1168                 private void SendLeave (IntPtr handle, IntPtr from)
1169                 {
1170                         XEvent xevent = new XEvent ();
1171
1172                         xevent.AnyEvent.type = XEventName.ClientMessage;
1173                         xevent.AnyEvent.display = display;
1174                         xevent.ClientMessageEvent.window = handle;
1175                         xevent.ClientMessageEvent.message_type = XdndLeave;
1176                         xevent.ClientMessageEvent.format = 32;
1177                         xevent.ClientMessageEvent.ptr1 = from;
1178
1179                         XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
1180                 }
1181
1182                 private void SendFinished ()
1183                 {
1184                         XEvent xevent = new XEvent ();
1185
1186                         xevent.AnyEvent.type = XEventName.ClientMessage;
1187                         xevent.AnyEvent.display = display;
1188                         xevent.ClientMessageEvent.window = source;
1189                         xevent.ClientMessageEvent.message_type = XdndFinished;
1190                         xevent.ClientMessageEvent.format = 32;
1191                         xevent.ClientMessageEvent.ptr1 = toplevel;
1192
1193                         XplatUIX11.XSendEvent (display, source, false, IntPtr.Zero, ref xevent);
1194                 }
1195
1196                 // There is a somewhat decent amount of overhead
1197                 // involved in setting up dnd so we do it lazily
1198                 // as a lot of applications do not even use it.
1199                 private void Init ()
1200                 {
1201                         XdndAware = XplatUIX11.XInternAtom (display, "XdndAware", false);
1202                         XdndEnter = XplatUIX11.XInternAtom (display, "XdndEnter", false);
1203                         XdndLeave = XplatUIX11.XInternAtom (display, "XdndLeave", false);
1204                         XdndPosition = XplatUIX11.XInternAtom (display, "XdndPosition", false);
1205                         XdndStatus = XplatUIX11.XInternAtom (display, "XdndStatus", false);
1206                         XdndDrop = XplatUIX11.XInternAtom (display, "XdndDrop", false);
1207                         XdndSelection = XplatUIX11.XInternAtom (display, "XdndSelection", false);
1208                         XdndFinished = XplatUIX11.XInternAtom (display, "XdndFinished", false);
1209                         XdndTypeList = XplatUIX11.XInternAtom (display, "XdndTypeList", false);
1210                         XdndActionCopy = XplatUIX11.XInternAtom (display, "XdndActionCopy", false);
1211                         XdndActionMove = XplatUIX11.XInternAtom (display, "XdndActionMove", false);
1212                         XdndActionLink = XplatUIX11.XInternAtom (display, "XdndActionLink", false);
1213                         //XdndActionPrivate = XplatUIX11.XInternAtom (display, "XdndActionPrivate", false);
1214                         XdndActionList = XplatUIX11.XInternAtom (display, "XdndActionList", false);
1215                         //XdndActionDescription = XplatUIX11.XInternAtom (display, "XdndActionDescription", false);
1216                         //XdndActionAsk = XplatUIX11.XInternAtom (display, "XdndActionAsk", false);
1217
1218                         foreach (MimeHandler handler in MimeHandlers) {
1219                                 handler.Type = XplatUIX11.XInternAtom (display, handler.Name, false);
1220                                 handler.NonProtocol = XplatUIX11.XInternAtom (display,
1221                                                 String.Concat ("MWFNonP+", handler.Name), false);
1222                         }
1223
1224                 }
1225
1226                 private IntPtr [] SourceSupportedList (ref XEvent xevent)
1227                 {
1228                         IntPtr [] res;
1229
1230                         
1231                         if (((int) xevent.ClientMessageEvent.ptr2 & 0x1) == 0) {
1232                                 res = new IntPtr [3];
1233                                 res [0] = xevent.ClientMessageEvent.ptr3;
1234                                 res [1] = xevent.ClientMessageEvent.ptr4;
1235                                 res [2] = xevent.ClientMessageEvent.ptr5;
1236                         } else {
1237                                 IntPtr type;
1238                                 int format;
1239                                 IntPtr count;
1240                                 IntPtr remaining;
1241                                 IntPtr data = IntPtr.Zero;
1242
1243                                 XplatUIX11.XGetWindowProperty (display, source, XdndTypeList,
1244                                                 IntPtr.Zero, new IntPtr(32), false, (IntPtr) Atom.XA_ATOM,
1245                                                 out type, out format, out count,
1246                                                 out remaining, ref data);
1247
1248                                 res = new IntPtr [count.ToInt32()];
1249                                 for (int i = 0; i < count.ToInt32(); i++) {
1250                                         res [i] = (IntPtr) Marshal.ReadInt32 (data, i *
1251                                                         Marshal.SizeOf (typeof (int)));
1252                                 }
1253
1254                                 XplatUIX11.XFree (data);
1255                         }
1256
1257                         return res;
1258                 }
1259
1260                 private string GetText (ref XEvent xevent, bool unicode)
1261                 {
1262                         int nread = 0;
1263                         IntPtr nitems;
1264                         IntPtr bytes_after;
1265
1266                         StringBuilder builder = new StringBuilder ();
1267                         do {
1268                                 IntPtr actual_type;
1269                                 int actual_fmt;
1270                                 IntPtr data = IntPtr.Zero;
1271
1272                                 if (0 != XplatUIX11.XGetWindowProperty (display,
1273                                                     xevent.AnyEvent.window,
1274                                                     (IntPtr) xevent.SelectionEvent.property,
1275                                                     IntPtr.Zero, new IntPtr(0xffffff), false,
1276                                                     (IntPtr) Atom.AnyPropertyType, out actual_type,
1277                                                     out actual_fmt, out nitems, out bytes_after,
1278                                                     ref data)) {
1279                                         XplatUIX11.XFree (data);
1280                                         break;
1281                                 }
1282
1283                                 if (unicode)
1284                                         builder.Append (Marshal.PtrToStringUni (data));
1285                                 else
1286                                         builder.Append (Marshal.PtrToStringAnsi (data));
1287                                 nread += nitems.ToInt32();
1288
1289                                 XplatUIX11.XFree (data);
1290                         } while (bytes_after.ToInt32() > 0);
1291                         if (nread == 0)
1292                                 return null;
1293                         return builder.ToString ();
1294                 }
1295
1296                 private MemoryStream GetData (ref XEvent xevent)
1297                 {
1298                         int nread = 0;
1299                         IntPtr nitems;
1300                         IntPtr bytes_after;
1301
1302                         MemoryStream res = new MemoryStream ();
1303                         do {
1304                                 IntPtr actual_type;
1305                                 int actual_fmt;
1306                                 IntPtr data = IntPtr.Zero;
1307
1308                                 if (0 != XplatUIX11.XGetWindowProperty (display,
1309                                                     xevent.AnyEvent.window,
1310                                                     (IntPtr) xevent.SelectionEvent.property,
1311                                                     IntPtr.Zero, new IntPtr(0xffffff), false,
1312                                                     (IntPtr) Atom.AnyPropertyType, out actual_type,
1313                                                     out actual_fmt, out nitems, out bytes_after,
1314                                                     ref data)) {
1315                                         XplatUIX11.XFree (data);
1316                                         break;
1317                                 }
1318
1319                                 for (int i = 0; i < nitems.ToInt32(); i++)
1320                                         res.WriteByte (Marshal.ReadByte (data, i));
1321                                 nread += nitems.ToInt32();
1322
1323                                 XplatUIX11.XFree (data);
1324                         } while (bytes_after.ToInt32() > 0);
1325                         return res;
1326                 }
1327
1328                 private Control MwfWindow (IntPtr window)
1329                 {
1330                         Hwnd hwnd = Hwnd.ObjectFromHandle (window);
1331                         if (hwnd == null)
1332                                 return null;
1333
1334                         Control res = Control.FromHandle (hwnd.client_window);
1335                         
1336                         if (res == null)
1337                                 res = Control.FromHandle (window);
1338                                 
1339                         return res;
1340                 }
1341
1342                 private bool IsWindowDndAware (IntPtr handle)
1343                 {
1344                         bool res = true;
1345                         // Check the version number, we need greater than 3
1346                         IntPtr actual;
1347                         int format;
1348                         IntPtr count;
1349                         IntPtr remaining;
1350                         IntPtr data = IntPtr.Zero;
1351                         
1352                         XplatUIX11.XGetWindowProperty (display, handle, XdndAware, IntPtr.Zero, new IntPtr(0x8000000), false,
1353                                         (IntPtr) Atom.XA_ATOM, out actual, out format,
1354                                         out count, out remaining, ref data);
1355                         
1356                         if (actual != (IntPtr) Atom.XA_ATOM || format != 32 ||
1357                                         count.ToInt32() == 0 || data == IntPtr.Zero) {
1358                                 if (data != IntPtr.Zero)
1359                                         XplatUIX11.XFree (data);
1360                                 return false;
1361                         }
1362
1363                         int version = Marshal.ReadInt32 (data, 0);
1364
1365                         if (version < 3) {
1366                                 Console.Error.WriteLine ("XDND Version too old (" + version + ").");
1367                                 XplatUIX11.XFree (data);
1368                                 return false;
1369                         }
1370
1371                         // First type is actually the XDND version
1372                         if (count.ToInt32() > 1) {
1373                                 res = false;
1374                                 for (int i = 1; i < count.ToInt32(); i++) {
1375                                         IntPtr type = (IntPtr) Marshal.ReadInt32 (data, i *
1376                                                         Marshal.SizeOf (typeof (int)));
1377                                         for (int j = 0; j < drag_data.SupportedTypes.Length; j++) {
1378                                                 if (drag_data.SupportedTypes [j] == type) {
1379                                                         res = true;
1380                                                         break;
1381                                                 }
1382                                         }
1383                                 }
1384                         }
1385
1386                         XplatUIX11.XFree (data);
1387                         return res;
1388                 }
1389
1390                 private IntPtr [] DetermineSupportedTypes (object data)
1391                 {
1392                         ArrayList res = new ArrayList ();
1393
1394                         if (data is string) {
1395                                 MimeHandler handler = FindHandler ("text/plain");
1396                                 if (handler != null)
1397                                         res.Add (handler.Type);
1398                         }/* else if (data is Bitmap)
1399                                 res.Add (data);
1400
1401                          */
1402
1403                         IDataObject data_object = data as IDataObject;
1404                         if (data_object != null) {
1405                                 foreach (string format in data_object.GetFormats (true)) {
1406                                         MimeHandler handler = FindHandler (format);
1407                                         if (handler != null && !res.Contains (handler.Type))
1408                                                 res.Add (handler.Type);
1409                                 }
1410                         }
1411
1412                         if (data is ISerializable) {
1413                                 MimeHandler handler = FindHandler ("application/x-mono-serialized-object");
1414                                 if (handler != null)
1415                                         res.Add (handler.Type);
1416                         }
1417
1418                         return (IntPtr []) res.ToArray (typeof (IntPtr));
1419                 }
1420         }
1421 }