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