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