merge from trunk revisions 58933, 58935, 58936
[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 == (int) 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 == (int) 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 uint [] XdndVersion = new uint [] { 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 IntPtr drag_action;
318                 private Control control;
319                 private int pos_x, pos_y;
320                 private DragDropEffects allowed;
321                 private DragEventArgs drag_event;
322
323                 private Cursor CursorNo;
324                 private Cursor CursorCopy;
325                 private Cursor CursorMove;
326                 private Cursor CursorLink;
327                 private IntPtr CurrentCursorHandle;
328                 
329                 public X11Dnd (IntPtr display)
330                 {
331                         this.display = display;
332                         Init ();
333                 }
334
335                 public void SetAllowDrop (Hwnd hwnd, bool allow)
336                 {
337                         if (hwnd.allow_drop == allow)
338                                 return;
339
340                         XChangeProperty (display, hwnd.whole_window, XdndAware,
341                                         (IntPtr) Atom.XA_ATOM, 32,
342                                         PropertyMode.Replace, XdndVersion, allow ? 1 : 0);
343                         hwnd.allow_drop = allow;
344                 }
345
346                 public DragDropEffects StartDrag (IntPtr handle, object data,
347                                 DragDropEffects allowed_effects)
348                 {
349                         drag_data = new DragData ();
350                         drag_data.Window = handle;
351                         drag_data.State = DragState.Beginning;
352                         drag_data.MouseState = XplatUIX11.MouseState;
353                         drag_data.Data = data;
354                         drag_data.SupportedTypes = DetermineSupportedTypes (data);
355
356                         drag_data.Action = ActionFromEffect (allowed_effects);
357
358                         if (CursorNo == null) {
359                                 // Make sure the cursors are created
360                                 CursorNo = new Cursor (typeof (X11Dnd), "DnDNo.cur");
361                                 CursorCopy = new Cursor (typeof (X11Dnd), "DnDCopy.cur");
362                                 CursorMove = new Cursor (typeof (X11Dnd), "DnDMove.cur");
363                                 CursorLink = new Cursor (typeof (X11Dnd), "DnDLink.cur");
364                         }
365
366                         drag_data.LastTopLevel = IntPtr.Zero;
367                         return DragDropEffects.Copy;
368                 }
369
370                 public void HandleButtonRelease (ref XEvent xevent)
371                 {
372                         if (drag_data == null)
373                                 return;
374
375                         if (!((drag_data.MouseState == MouseButtons.Left &&
376                                               xevent.ButtonEvent.button == 1) ||
377                                             (drag_data.MouseState == MouseButtons.Right &&
378                                                             xevent.ButtonEvent.button == 3)))
379                                 return;
380
381                         if (drag_data.State == DragState.Beginning) {
382                                 state = State.Accepting;
383                         } else if (drag_data.State != DragState.None) {
384
385                                 if (drag_data.WillAccept) {
386                                         SendDrop (drag_data.LastTopLevel, xevent.AnyEvent.window,
387                                                         xevent.ButtonEvent.time);
388                                 }
389
390                                 XplatUIX11.XUngrabPointer (display, 0);
391                                 drag_data.State = DragState.None;
392                                 // WE can't reset the drag data yet as it is still
393                                 // most likely going to be used by the SelectionRequest
394                                 // handlers
395                         }
396                 }
397
398                 public bool HandleMotionNotify (ref XEvent xevent)
399                 {
400                         if (drag_data == null)
401                                 return false;
402
403                         if (drag_data.State == DragState.Beginning) {
404                                 int suc;
405
406                                 drag_data.State = DragState.Dragging;
407
408                                 suc = XplatUIX11.XSetSelectionOwner (display, (int) XdndSelection,
409                                                 drag_data.Window,
410                                                 xevent.ButtonEvent.time);
411
412                                 if (suc == 0) {
413                                         Console.Error.WriteLine ("Could not take ownership of XdndSelection aborting drag.");
414                                         drag_data.Reset ();
415                                         return false;
416                                 }
417
418                                 suc = XGrabPointer (display, xevent.AnyEvent.window,
419                                                 false,
420                                                 EventMask.ButtonMotionMask |
421                                                 EventMask.PointerMotionMask |
422                                                 EventMask.ButtonPressMask |
423                                                 EventMask.ButtonReleaseMask,
424                                                 GrabMode.GrabModeAsync,
425                                                 GrabMode.GrabModeAsync,
426                                                 IntPtr.Zero, IntPtr.Zero/*CursorCopy.Handle*/, 0);
427
428                                 if (suc != 0) {
429                                         Console.Error.WriteLine ("Could not grab pointer aborting drag.");
430                                         drag_data.Reset ();
431                                         return false;
432                                 }
433
434                                 drag_data.State = DragState.Dragging;
435                         } else if (drag_data.State != DragState.None) {
436                                 bool dnd_aware = false;
437                                 IntPtr toplevel = IntPtr.Zero;
438                                 IntPtr window = XplatUIX11.RootWindowHandle;
439
440                                 IntPtr root, child;
441                                 int x_temp, y_temp;
442                                 int mask_return;
443
444                                 while (XQueryPointer (display,
445                                                        window,
446                                                        out root, out child,
447                                                        out x_temp, out y_temp,
448                                                        out xevent.MotionEvent.x,
449                                                        out xevent.MotionEvent.y,
450                                                        out mask_return)) {
451                                         
452                                         if (!dnd_aware) {
453                                                 dnd_aware = IsWindowDndAware (window);
454                                                 if (dnd_aware) {
455                                                         toplevel = window;
456                                                         xevent.MotionEvent.x_root = x_temp;
457                                                         xevent.MotionEvent.y_root = y_temp;
458                                                 }
459                                         }
460                                         
461                                         if (child == IntPtr.Zero)
462                                                 break;
463                                         
464                                         window = child;
465                                 }
466
467                                 if (window != drag_data.LastWindow && drag_data.State == DragState.Entered) {
468                                         drag_data.State = DragState.Dragging;
469
470                                         // TODO: Send a Leave if this is an MWF window
471
472                                         if (toplevel != drag_data.LastTopLevel)
473                                                 SendLeave (drag_data.LastTopLevel, xevent.MotionEvent.window);
474                                 }
475
476                                 drag_data.State = DragState.Entered;
477                                 if (toplevel != drag_data.LastTopLevel) {
478                                         // Entering a new toplevel window
479                                         SendEnter (toplevel, drag_data.Window, drag_data.SupportedTypes);
480                                 } else {
481                                         // Already in a toplevel window, so send a position
482                                         SendPosition (toplevel, drag_data.Window,
483                                                         drag_data.Action,
484                                                         xevent.MotionEvent.x_root,
485                                                         xevent.MotionEvent.y_root,
486                                                         xevent.MotionEvent.time);
487                                 }
488
489                                 drag_data.LastTopLevel = toplevel;
490                                 drag_data.LastWindow = window;
491                                 return true;
492                         }
493                         return false;
494                 }
495
496                 // DEBUG CODE REMOVE
497                 private string GetText (IntPtr handle) {
498                         string text = String.Empty;
499                         IntPtr  textptr;
500
501                         textptr = IntPtr.Zero;
502
503                         XFetchName (display, handle, ref textptr);
504                         if (textptr != IntPtr.Zero) {
505                                 text = Marshal.PtrToStringAnsi(textptr);
506                                 XFree (textptr);
507                         }
508
509                         return text;
510                 }
511
512                         
513                 // return true if the event is handled here
514                 public bool HandleClientMessage (ref XEvent xevent)
515                 {
516                         // most common so we check it first
517                         if (xevent.ClientMessageEvent.message_type == XdndPosition)
518                                 return Accepting_HandlePositionEvent (ref xevent);
519                         if (xevent.ClientMessageEvent.message_type == XdndEnter)
520                                 return Accepting_HandleEnterEvent (ref xevent);
521                         if (xevent.ClientMessageEvent.message_type == XdndDrop)
522                                 return Accepting_HandleDropEvent (ref xevent);
523                         if (xevent.ClientMessageEvent.message_type == XdndLeave)
524                                 return Accepting_HandleLeaveEvent (ref xevent);
525                         if (xevent.ClientMessageEvent.message_type == XdndStatus)
526                                 return HandleStatusEvent (ref xevent);
527
528                         return false;
529                 }
530
531                 public bool HandleSelectionNotifyEvent (ref XEvent xevent)
532                 {
533                         if (source != XGetSelectionOwner (display, XdndSelection))
534                                 return false;
535
536                         MimeHandler handler = FindHandler ((IntPtr) xevent.SelectionEvent.target);
537                         if (handler == null)
538                                 return false;
539                         if (data == null)
540                                 data = new DataObject ();
541
542                         handler.Converter.GetData (this, data, ref xevent);
543
544                         converts_pending--;
545                         if (converts_pending <= 0 && position_recieved) {
546                                 drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
547                                         allowed, DragDropEffects.None);
548                                 control.DndEnter (drag_event);
549                                 SendStatus (source, drag_event.Effect);
550                                 status_sent = true;
551                         }
552                         return true;
553                 }
554
555                 public bool HandleSelectionRequestEvent (ref XEvent xevent)
556                 {
557                         if (xevent.SelectionRequestEvent.selection != (int) XdndSelection)
558                                 return false;
559
560                         MimeHandler handler = FindHandler ((IntPtr) xevent.SelectionRequestEvent.target);
561                         if (handler == null)
562                                 return false;
563
564                         handler.Converter.SetData (this, drag_data.Data, ref xevent);
565
566                         return true;
567                 }
568
569                 private void SetProperty (ref XEvent xevent, IntPtr data, int length)
570                 {
571                         XEvent sel = new XEvent();
572                         sel.SelectionEvent.type = XEventName.SelectionNotify;
573                         sel.SelectionEvent.send_event = true;
574                         sel.SelectionEvent.display = display;
575                         sel.SelectionEvent.selection = xevent.SelectionRequestEvent.selection;
576                         sel.SelectionEvent.target = xevent.SelectionRequestEvent.target;
577                         sel.SelectionEvent.requestor = xevent.SelectionRequestEvent.requestor;
578                         sel.SelectionEvent.time = xevent.SelectionRequestEvent.time;
579                         sel.SelectionEvent.property = 0;
580
581                         XplatUIX11.XChangeProperty (display, xevent.SelectionRequestEvent.requestor,
582                                         xevent.SelectionRequestEvent.property,
583                                         xevent.SelectionRequestEvent.target,
584                                         8, PropertyMode.Replace, data, length);
585                         sel.SelectionEvent.property = xevent.SelectionRequestEvent.property;
586
587                         XSendEvent (display, xevent.SelectionRequestEvent.requestor, false,
588                                         EventMask.NoEventMask, ref sel);
589                         return;
590                 }
591
592                 private void Reset ()
593                 {
594                         ResetSourceData ();
595                         ResetTargetData ();
596                 }
597
598                 private void ResetSourceData ()
599                 {
600                         converts_pending = 0;
601                         data = null;
602                 }
603
604                 private void ResetTargetData ()
605                 {
606                         position_recieved = false;
607                         status_sent = false;
608                 }
609                 
610                 private bool Accepting_HandleEnterEvent (ref XEvent xevent)
611                 {
612                         Reset ();
613
614                         source = xevent.ClientMessageEvent.ptr1;
615                         toplevel = xevent.AnyEvent.window;
616                         target = IntPtr.Zero;
617
618                         ConvertData (ref xevent);
619
620                         return true;
621                 }
622
623                 private bool Accepting_HandlePositionEvent (ref XEvent xevent)
624                 {
625                         pos_x = (int) xevent.ClientMessageEvent.ptr3 >> 16;
626                         pos_y = (int) xevent.ClientMessageEvent.ptr3 & 0xFFFF;
627
628                         allowed = EffectFromAction (xevent.ClientMessageEvent.ptr5);
629
630                         IntPtr parent, child, new_child;
631                         parent = XplatUIX11.XRootWindow (display, 0);
632                         child = toplevel;
633                         while (true) {
634                                 int xd, yd;
635                                 new_child = IntPtr.Zero;
636                                 
637                                 if (!XplatUIX11.XTranslateCoordinates (display,
638                                                     parent, child, pos_x, pos_y,
639                                                     out xd, out yd, out new_child))
640                                         break;
641                                 if (new_child == IntPtr.Zero)
642                                         break;
643                                 child = new_child;
644                         }
645
646                         if (target != child) {
647                                 // We have moved into a new control 
648                                 // or into a control for the first time
649                                 Finish ();
650                         }
651                         target = child;
652                         Hwnd hwnd = Hwnd.ObjectFromHandle (target);
653                         Control c = Control.FromHandle (hwnd.client_window);
654
655                         if (c == null)
656                                 return true;
657                         if (!c.allow_drop) {
658                                 SendStatus (source, DragDropEffects.None);
659                                 Finish ();
660                                 return true;
661                         }
662
663                         control = c;
664                         position_recieved = true;                       
665
666                         if (converts_pending > 0)
667                                 return true;
668                         if (!status_sent) {
669                                 drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
670                                         allowed, DragDropEffects.None);
671                                 control.DndEnter (drag_event);
672                                 SendStatus (source, drag_event.Effect);
673                                 status_sent = true;
674                         } else {
675                                 drag_event.x = pos_x;
676                                 drag_event.y = pos_y;
677                                 SendStatus (source, drag_event.Effect);
678                                 control.DndOver (drag_event);
679                         }
680                         
681                         return true;
682                 }
683
684                 private void Finish ()
685                 {
686                         if (control != null) {
687                                 if (drag_event == null) {
688                                         if (data == null)
689                                                 data = new DataObject ();
690                                         drag_event = new DragEventArgs (data,
691                                                         0, pos_x, pos_y,
692                                         allowed, DragDropEffects.None);
693                                 }
694                                 control.DndLeave (drag_event);
695                         }
696                         ResetTargetData ();
697                 }
698
699                 private bool Accepting_HandleDropEvent (ref XEvent xevent)
700                 {
701                         if (control != null && drag_event != null) {
702                                 drag_event = new DragEventArgs (data,
703                                                 0, pos_x, pos_y,
704                                         allowed, DragDropEffects.None);
705                                 control.DndDrop (drag_event);
706                         }
707                         SendFinished ();
708                         return true;
709                 }
710
711                 private bool Accepting_HandleLeaveEvent (ref XEvent xevent)
712                 {
713                         if (control != null && drag_event != null)
714                                 control.DndLeave (drag_event);
715                         // Reset ();
716                         return true;
717                 }
718
719                 private bool HandleStatusEvent (ref XEvent xevent)
720                 {
721                         if (drag_data != null && drag_data.State == DragState.Entered) {
722                                 drag_data.WillAccept = ((int) xevent.ClientMessageEvent.ptr2 & 0x1) != 0;
723                                 Cursor cursor = CursorNo;
724                                 if (drag_data.WillAccept) {
725                                         // Same order as on MS
726                                         IntPtr action = xevent.ClientMessageEvent.ptr5;
727                                         if (action == XdndActionCopy)
728                                                 cursor = CursorCopy;
729                                         else if (action == XdndActionLink)
730                                                 cursor = CursorLink;
731                                         else if (action == XdndActionMove)
732                                                 cursor = CursorMove;
733
734                                 }
735
736                                         // TODO: Try not to set the cursor so much
737                                 //if (cursor.Handle != CurrentCursorHandle) {
738                                         XChangeActivePointerGrab (display,
739                                                         EventMask.ButtonMotionMask |
740                                                         EventMask.PointerMotionMask |
741                                                         EventMask.ButtonPressMask |
742                                                         EventMask.ButtonReleaseMask,
743                                                         cursor.Handle, IntPtr.Zero);
744                                         CurrentCursorHandle = cursor.Handle;
745                                         //}     
746                         }
747                         return true;
748                 }
749
750                 private DragDropEffects EffectFromAction (IntPtr action)
751                 {
752                         DragDropEffects allowed = DragDropEffects.None;
753
754                         if (action == XdndActionCopy)
755                                 allowed = DragDropEffects.Copy;
756                         else if (action == XdndActionMove)
757                                 allowed |= DragDropEffects.Move;
758                         if (action == XdndActionLink)
759                                 allowed |= DragDropEffects.Link;
760                         return allowed;
761                 }
762
763                 private IntPtr ActionFromEffect (DragDropEffects effect)
764                 {
765                         IntPtr action = IntPtr.Zero;
766
767                         // We can't OR together actions on XDND so sadly the primary
768                         // is the only one shown here
769                         if ((effect & DragDropEffects.Copy) != 0)
770                                 action = XdndActionCopy;
771                         else if ((effect & DragDropEffects.Move) != 0)
772                                 action = XdndActionMove;
773                         else if ((effect & DragDropEffects.Link) != 0)
774                                 action = XdndActionLink;
775                         return action;
776                 }
777
778                 private bool ConvertData (ref XEvent xevent)
779                 {
780                         bool match = false;
781
782                         if (source != XplatUIX11.XGetSelectionOwner (display, (int) XdndSelection)) {
783                                 return false;
784                         }
785
786                         Control mwfcontrol = MwfWindow (source);
787
788                         if (mwfcontrol != null && drag_data != null) {
789                                 DataObject dragged = drag_data.Data as DataObject;
790                                 if (dragged != null) {
791                                         data = dragged;
792                                 } else {
793                                         if (data == null)
794                                                 data = new DataObject ();
795                                         data.SetData (drag_data.Data);
796                                 }
797                                 return true;
798                         }
799
800                         foreach (IntPtr atom in SourceSupportedList (ref xevent)) {
801                                 MimeHandler handler = FindHandler (atom);
802                                 if (handler == null)
803                                         continue;
804                                 XConvertSelection (display, XdndSelection, handler.Type,
805                                         handler.NonProtocol, toplevel, 0 /* CurrentTime */);
806                                 converts_pending++;
807                                 match = true;
808                         }
809                         return match;
810                 }
811
812                 private MimeHandler FindHandler (IntPtr atom)
813                 {
814                         if (atom == IntPtr.Zero)
815                                 return null;
816                         foreach (MimeHandler handler in MimeHandlers) {
817                                 if (handler.Type == atom)
818                                         return handler;
819                         }
820                         return null;
821                 }
822
823                 private MimeHandler FindHandler (string name)
824                 {
825                         foreach (MimeHandler handler in MimeHandlers) {
826                                 if (handler.Name == name)
827                                         return handler;
828                         }
829                         return null;
830                 }
831
832                 private void SendStatus (IntPtr source, DragDropEffects effect)
833                 {
834                         XEvent xevent = new XEvent ();
835
836                         xevent.AnyEvent.type = XEventName.ClientMessage;
837                         xevent.AnyEvent.display = display;
838                         xevent.ClientMessageEvent.window = source;
839                         xevent.ClientMessageEvent.message_type = XdndStatus;
840                         xevent.ClientMessageEvent.format = 32;
841                         xevent.ClientMessageEvent.ptr1 = toplevel;
842                         if (effect != DragDropEffects.None)
843                                 xevent.ClientMessageEvent.ptr2 = (IntPtr) 1;
844
845                         xevent.ClientMessageEvent.ptr5 = ActionFromEffect (effect);
846                         XSendEvent (display, source, false, 0, ref xevent);
847                 }
848
849                 private void SendEnter (IntPtr handle, IntPtr from, IntPtr [] supported)
850                 {
851                         XEvent xevent = new XEvent ();
852
853                         xevent.AnyEvent.type = XEventName.ClientMessage;
854                         xevent.AnyEvent.display = display;
855                         xevent.ClientMessageEvent.window = handle;
856                         xevent.ClientMessageEvent.message_type = XdndEnter;
857                         xevent.ClientMessageEvent.format = 32;
858                         xevent.ClientMessageEvent.ptr1 = from;
859
860                         // (int) xevent.ClientMessageEvent.ptr2 & 0x1)
861                         // int ptr2 = 0x1;
862                         // xevent.ClientMessageEvent.ptr2 = (IntPtr) ptr2;
863                         // (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~(0xFF << 24)) | ((v) << 24)
864                         xevent.ClientMessageEvent.ptr2 = (IntPtr) (XdndVersion [0] << 24);
865                         
866                         if (supported.Length > 0)
867                                 xevent.ClientMessageEvent.ptr3 = supported [0];
868                         if (supported.Length > 1)
869                                 xevent.ClientMessageEvent.ptr4 = supported [1];
870                         if (supported.Length > 2)
871                                 xevent.ClientMessageEvent.ptr5 = supported [2];
872
873                         XSendEvent (display, handle, false, 0, ref xevent);
874                 }
875
876                 private void SendDrop (IntPtr handle, IntPtr from, IntPtr time)
877                 {
878                         XEvent xevent = new XEvent ();
879
880                         xevent.AnyEvent.type = XEventName.ClientMessage;
881                         xevent.AnyEvent.display = display;
882                         xevent.ClientMessageEvent.window = handle;
883                         xevent.ClientMessageEvent.message_type = XdndDrop;
884                         xevent.ClientMessageEvent.format = 32;
885                         xevent.ClientMessageEvent.ptr1 = from;
886                         xevent.ClientMessageEvent.ptr3 = time;
887                         
888                         XSendEvent (display, handle, false, 0, ref xevent);
889                 }
890
891                 private void SendPosition (IntPtr handle, IntPtr from, IntPtr action, int x, int y, IntPtr time)
892                 {
893                         XEvent xevent = new XEvent ();
894
895                         xevent.AnyEvent.type = XEventName.ClientMessage;
896                         xevent.AnyEvent.display = display;
897                         xevent.ClientMessageEvent.window = handle;
898                         xevent.ClientMessageEvent.message_type = XdndPosition;
899                         xevent.ClientMessageEvent.format = 32;
900                         xevent.ClientMessageEvent.ptr1 = from;
901                         xevent.ClientMessageEvent.ptr3 = (IntPtr) ((x << 16) | (y & 0xFFFF));
902                         xevent.ClientMessageEvent.ptr4 = time;
903                         xevent.ClientMessageEvent.ptr5 = action;
904                         
905                         XSendEvent (display, handle, false, 0, ref xevent);
906                 }
907
908                 private void SendLeave (IntPtr handle, IntPtr from)
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 = XdndLeave;
916                         xevent.ClientMessageEvent.format = 32;
917                         xevent.ClientMessageEvent.ptr1 = from;
918
919                         XSendEvent (display, handle, false, 0, ref xevent);
920                 }
921
922                 private void SendFinished ()
923                 {
924                         XEvent xevent = new XEvent ();
925
926                         xevent.AnyEvent.type = XEventName.ClientMessage;
927                         xevent.AnyEvent.display = display;
928                         xevent.ClientMessageEvent.window = source;
929                         xevent.ClientMessageEvent.message_type = XdndFinished;
930                         xevent.ClientMessageEvent.format = 32;
931                         xevent.ClientMessageEvent.ptr1 = toplevel;
932
933                         XSendEvent (display, source, false, 0, ref xevent);
934                 }
935
936                 // There is a somewhat decent amount of overhead
937                 // involved in setting up dnd so we do it lazily
938                 // as a lot of applications do not even use it.
939                 private void Init ()
940                 {
941                         XdndAware = XInternAtom (display, "XdndAware", false);
942                         XdndEnter = XInternAtom (display, "XdndEnter", false);
943                         XdndLeave = XInternAtom (display, "XdndLeave", false);
944                         XdndPosition = XInternAtom (display, "XdndPosition", false);
945                         XdndStatus = XInternAtom (display, "XdndStatus", false);
946                         XdndDrop = XInternAtom (display, "XdndDrop", false);
947                         XdndSelection = XInternAtom (display, "XdndSelection", false);
948                         XdndFinished = XInternAtom (display, "XdndFinished", false);
949                         XdndTypeList = XInternAtom (display, "XdndTypeList", false);
950                         XdndActionCopy = XInternAtom (display, "XdndActionCopy", false);
951                         XdndActionMove = XInternAtom (display, "XdndActionMove", false);
952                         XdndActionLink = XInternAtom (display, "XdndActionLink", false);
953                         XdndActionPrivate = XInternAtom (display, "XdndActionPrivate", false);
954                         XdndActionList = XInternAtom (display, "XdndActionList", false);
955                         XdndActionDescription = XInternAtom (display, "XdndActionDescription", false);
956                         XdndActionAsk = XInternAtom (display, "XdndActionAsk", false);
957
958                 foreach (MimeHandler handler in MimeHandlers) {
959                                 handler.Type = XInternAtom (display, handler.Name, false);
960                                 handler.NonProtocol = XInternAtom (display,
961                                                 String.Concat ("MWFNonP+", handler.Name), false);
962                         }
963
964                 }
965
966                 private IntPtr [] SourceSupportedList (ref XEvent xevent)
967                 {
968                         IntPtr [] res;
969
970                         
971                         if (((int) xevent.ClientMessageEvent.ptr2 & 0x1) == 0) {
972                                 res = new IntPtr [3];
973                                 res [0] = xevent.ClientMessageEvent.ptr3;
974                                 res [1] = xevent.ClientMessageEvent.ptr4;
975                                 res [2] = xevent.ClientMessageEvent.ptr5;
976                         } else {
977                                 IntPtr type;
978                                 int format, count, remaining;
979                                 IntPtr data = IntPtr.Zero;
980
981                                 XGetWindowProperty (display, source, XdndTypeList,
982                                                 0, 32, false, (IntPtr) Atom.XA_ATOM,
983                                                 out type, out format, out count,
984                                                 out remaining, out data);
985
986                                 res = new IntPtr [count];
987                                 for (int i = 0; i < count; i++) {
988                                         res [i] = (IntPtr) Marshal.ReadInt32 (data, i *
989                                                         Marshal.SizeOf (typeof (int)));
990                                 }
991
992                                 XFree (data);
993                         }
994
995                         return res;
996                 }
997
998                 private string GetText (ref XEvent xevent, bool unicode)
999                 {
1000                         int nread = 0;
1001                         int nitems;
1002                         int bytes_after;
1003
1004                         StringBuilder builder = new StringBuilder ();
1005                         do {
1006                                 IntPtr actual_type;
1007                                 int actual_fmt;
1008                                 IntPtr data = IntPtr.Zero;
1009
1010                                 if (0 != XGetWindowProperty (display,
1011                                                     xevent.AnyEvent.window,
1012                                                     (IntPtr) xevent.SelectionEvent.property,
1013                                                     0, 0xffffff, false,
1014                                                     (IntPtr) Atom.AnyPropertyType, out actual_type,
1015                                                     out actual_fmt, out nitems, out bytes_after,
1016                                                     out data)) {
1017                                         XFree (data);
1018                                         break;
1019                                 }
1020
1021                                 if (unicode)
1022                                         builder.Append (Marshal.PtrToStringUni (data));
1023                                 else
1024                                         builder.Append (Marshal.PtrToStringAnsi (data));
1025                                 nread += nitems;
1026
1027                                 XFree (data);
1028                         } while (bytes_after > 0);
1029                         if (nread == 0)
1030                                 return null;
1031                         return builder.ToString ();
1032                 }
1033
1034                 private MemoryStream GetData (ref XEvent xevent)
1035                 {
1036                         int nread = 0;
1037                         int nitems;
1038                         int bytes_after;
1039
1040                         MemoryStream res = new MemoryStream ();
1041                         do {
1042                                 IntPtr actual_type;
1043                                 int actual_fmt;
1044                                 IntPtr data = IntPtr.Zero;
1045
1046                                 if (0 != XGetWindowProperty (display,
1047                                                     xevent.AnyEvent.window,
1048                                                     (IntPtr) xevent.SelectionEvent.property,
1049                                                     0, 0xffffff, false,
1050                                                     (IntPtr) Atom.AnyPropertyType, out actual_type,
1051                                                     out actual_fmt, out nitems, out bytes_after,
1052                                                     out data)) {
1053                                         XFree (data);
1054                                         break;
1055                                 }
1056
1057                                 for (int i = 0; i < nitems; i++)
1058                                         res.WriteByte (Marshal.ReadByte (data, i));
1059                                 nread += nitems;
1060
1061                                 XFree (data);
1062                         } while (bytes_after > 0);
1063                         return res;
1064                 }
1065
1066                 private Control MwfWindow (IntPtr window)
1067                 {
1068                         Hwnd hwnd = Hwnd.ObjectFromHandle (window);
1069                         if (hwnd == null)
1070                                 return null;
1071
1072                         Control res = Control.FromHandle (hwnd.client_window);
1073                         return res;
1074                 }
1075
1076                 private bool IsWindowDndAware (IntPtr handle)
1077                 {
1078                         bool res = true;
1079                         // Check the version number, we need greater than 3
1080                         IntPtr actual;
1081                         int format, count, remaining;
1082                         IntPtr data = IntPtr.Zero;
1083                         
1084                         XGetWindowProperty (display, handle, XdndAware, 0, 0x8000000, false,
1085                                         (IntPtr) Atom.XA_ATOM, out actual, out format,
1086                                         out count, out remaining, out data);
1087                         
1088                         if (actual != (IntPtr) Atom.XA_ATOM || format != 32 ||
1089                                         count == 0 || data == IntPtr.Zero) {
1090                                 if (data != IntPtr.Zero)
1091                                         XFree (data);
1092                                 return false;
1093                         }
1094
1095                         int version = Marshal.ReadInt32 (data, 0);
1096
1097                         if (version < 3) {
1098                                 Console.Error.WriteLine ("XDND Version too old (" + version + ").");
1099                                 XFree (data);
1100                                 return false;
1101                         }
1102
1103                         // First type is actually the XDND version
1104                         if (count > 1) {
1105                                 res = false;
1106                                 for (int i = 1; i < count; i++) {
1107                                         IntPtr type = (IntPtr) Marshal.ReadInt32 (data, i *
1108                                                         Marshal.SizeOf (typeof (int)));
1109                                         for (int j = 0; j < drag_data.SupportedTypes.Length; j++) {
1110                                                 if (drag_data.SupportedTypes [j] == type) {
1111                                                         res = true;
1112                                                         break;
1113                                                 }
1114                                         }
1115                                 }
1116                         }
1117
1118                         XFree (data);
1119                         return res;
1120                 }
1121
1122                 private IntPtr [] DetermineSupportedTypes (object data)
1123                 {
1124                         ArrayList res = new ArrayList ();
1125
1126                         if (data is string) {
1127                                 MimeHandler handler = FindHandler ("text/plain");
1128                                 if (handler != null)
1129                                         res.Add (handler.Type);
1130                         }/* else if (data is Bitmap)
1131                                 res.Add (data);
1132
1133                          */
1134
1135                         if (data is IDataObject) {
1136                                 
1137
1138                         }
1139
1140                         if (data is ISerializable) {
1141                                 MimeHandler handler = FindHandler ("application/x-mono-serialized-object");
1142                                 if (handler != null)
1143                                         res.Add (handler.Type);
1144                         }
1145
1146                         return (IntPtr []) res.ToArray (typeof (IntPtr));
1147                 }
1148
1149                 [DllImport ("libX11")]
1150                 private extern static string XGetAtomName (IntPtr display, IntPtr atom);
1151
1152                 [DllImport ("libX11")]
1153                 private extern static IntPtr XInternAtom (IntPtr display, string atom_name, bool only_if_exists);
1154
1155                 [DllImport ("libX11")]
1156                 private extern static int XChangeProperty (IntPtr display, IntPtr window, IntPtr property,
1157                                 IntPtr format, int type, PropertyMode  mode, uint [] atoms, int nelements);
1158
1159                 [DllImport ("libX11")]
1160                 private extern static int XGetWindowProperty (IntPtr display, IntPtr window,
1161                                 IntPtr atom, int long_offset, int long_length, bool delete,
1162                                 IntPtr req_type, out IntPtr actual_type, out int actual_format,
1163                                 out int nitems, out int bytes_after, out IntPtr prop);
1164
1165                 [DllImport ("libX11")]
1166                 internal extern static int XSendEvent (IntPtr display, IntPtr window,
1167                                 bool propagate, EventMask event_mask, ref XEvent send_event);
1168
1169                 [DllImport ("libX11")]
1170                 internal extern static int XConvertSelection (IntPtr display, IntPtr selection,
1171                                 IntPtr target, IntPtr property, IntPtr requestor, int time);
1172
1173                 [DllImport ("libX11")]
1174                 internal extern static IntPtr XGetSelectionOwner (IntPtr display, IntPtr selection);
1175
1176                 [DllImport ("libX11")]
1177                 internal extern static int XGrabPointer (IntPtr display, IntPtr window,
1178                                 bool owner_events, EventMask event_mask, GrabMode pointer_mode,
1179                                 GrabMode keyboard_mode, IntPtr confine_to, IntPtr cursor, uint timestamp);
1180
1181                 [DllImport ("libX11")]
1182                 internal extern static bool XQueryPointer (IntPtr display, IntPtr window, out IntPtr root,
1183                                 out IntPtr child, out int root_x, out int root_y, out int win_x,
1184                                 out int win_y, out int keys_buttons);
1185
1186                 [DllImport ("libX11")]
1187                 internal extern static int XAllowEvents (IntPtr display, int event_mode, IntPtr time);
1188
1189                 [DllImport ("libX11")]
1190                 internal extern static int XFree(IntPtr data);
1191
1192                 [DllImport ("libX11")]
1193                 internal extern static int XFetchName (IntPtr display, IntPtr window, ref IntPtr window_name);
1194                 [DllImport ("libX11")]
1195                 internal extern static int XChangeActivePointerGrab (IntPtr display, EventMask event_mask, IntPtr cursor, IntPtr time);
1196         }
1197
1198 }