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