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:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
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.
20 // Copyright (c) 2005 Novell, Inc.
23 // Jackson Harper (jackson@ximian.com)
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;
38 namespace System.Windows.Forms {
40 internal class X11Dnd {
47 private enum DragState {
54 private interface IDataConverter {
55 void GetData (X11Dnd dnd, IDataObject data, ref XEvent xevent);
56 void SetData (X11Dnd dnd, object data, ref XEvent xevent);
59 private delegate void MimeConverter (IntPtr dsp,
60 IDataObject data, ref XEvent xevent);
62 private class MimeHandler {
64 public string [] Aliases;
66 public IntPtr NonProtocol;
67 public IDataConverter Converter;
69 public MimeHandler (string name, IDataConverter converter) : this (name, converter, name)
73 public MimeHandler (string name, IDataConverter converter, params string [] aliases)
76 Converter = converter;
80 public override string ToString ()
82 return "MimeHandler {" + Name + "}";
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)),
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 ())
100 private class SerializedObjectConverter : IDataConverter {
102 public void GetData (X11Dnd dnd, IDataObject data, ref XEvent xevent)
104 MemoryStream stream = dnd.GetData (ref xevent);
105 BinaryFormatter bf = new BinaryFormatter ();
107 if (stream.Length == 0)
111 object obj = bf.Deserialize (stream);
115 public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
120 MemoryStream stream = new MemoryStream ();
121 BinaryFormatter bf = new BinaryFormatter ();
123 bf.Serialize (stream, data);
125 IntPtr buffer = Marshal.AllocHGlobal ((int) stream.Length);
128 for (int i = 0; i < stream.Length; i++) {
129 Marshal.WriteByte (buffer, i, (byte) stream.ReadByte ());
132 dnd.SetProperty (ref xevent, buffer, (int) stream.Length);
136 private class HtmlConverter : IDataConverter {
138 public void GetData (X11Dnd dnd, IDataObject data, ref XEvent xevent)
140 string text = dnd.GetText (ref xevent, false);
143 data.SetData (DataFormats.Text, text);
144 data.SetData (DataFormats.UnicodeText, text);
147 public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
151 string str = data as string;
156 if (xevent.SelectionRequestEvent.target == (IntPtr)Atom.XA_STRING) {
157 byte [] bytes = Encoding.ASCII.GetBytes (str);
158 buffer = Marshal.AllocHGlobal (bytes.Length);
160 for (int i = 0; i < len; i++)
161 Marshal.WriteByte (buffer, i, bytes [i]);
163 buffer = Marshal.StringToHGlobalAnsi (str);
165 while (Marshal.ReadByte (buffer, len) != 0)
169 dnd.SetProperty (ref xevent, buffer, len);
171 Marshal.FreeHGlobal (buffer);
175 private class TextConverter : IDataConverter {
177 public void GetData (X11Dnd dnd, IDataObject data, ref XEvent xevent)
179 string text = dnd.GetText (ref xevent, true);
182 data.SetData (DataFormats.Text, text);
183 data.SetData (DataFormats.UnicodeText, text);
186 public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
190 string str = data as string;
193 IDataObject dobj = data as IDataObject;
196 str = (string) dobj.GetData ("System.String", true);
199 if (xevent.SelectionRequestEvent.target == (IntPtr)Atom.XA_STRING) {
200 byte [] bytes = Encoding.ASCII.GetBytes (str);
201 buffer = Marshal.AllocHGlobal (bytes.Length);
203 for (int i = 0; i < len; i++)
204 Marshal.WriteByte (buffer, i, bytes [i]);
206 buffer = Marshal.StringToHGlobalAnsi (str);
208 while (Marshal.ReadByte (buffer, len) != 0)
212 dnd.SetProperty (ref xevent, buffer, len);
214 Marshal.FreeHGlobal (buffer);
218 private class UriListConverter : IDataConverter {
220 public void GetData (X11Dnd dnd, IDataObject data, ref XEvent xevent)
222 string text = dnd.GetText (ref xevent, false);
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 ("#"))
234 Uri uri = new Uri (line);
235 uri_list.Add (uri.LocalPath);
239 string [] l = (string []) uri_list.ToArray (typeof (string));
242 data.SetData (DataFormats.FileDrop, l);
243 data.SetData ("FileName", l [0]);
244 data.SetData ("FileNameW", l [0]);
247 public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
249 string [] uri_list = data as string [];
251 if (uri_list == null) {
252 IDataObject dobj = data as IDataObject;
255 uri_list = dobj.GetData (DataFormats.FileDrop, true) as string [];
258 if (uri_list == null)
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 ());
268 IntPtr buffer = Marshal.StringToHGlobalAnsi ((string) res.ToString ());
270 while (Marshal.ReadByte (buffer, len) != 0)
273 dnd.SetProperty (ref xevent, buffer, len);
277 private class DragData {
278 public IntPtr Window;
279 public DragState State;
281 public IntPtr Action;
282 public IntPtr [] SupportedTypes;
283 public MouseButtons MouseState;
284 public DragDropEffects AllowedEffects;
285 public Point CurMousePos;
287 public IntPtr LastWindow;
288 public IntPtr LastTopLevel;
290 public bool WillAccept;
294 State = DragState.None;
296 SupportedTypes = null;
301 // This version seems to be the most common
302 private static readonly IntPtr [] XdndVersion = new IntPtr [] { new IntPtr (4) };
304 private IntPtr display;
305 private DragData drag_data;
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;
324 //private State state;
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;
334 private Control control;
335 private int pos_x, pos_y;
336 private DragDropEffects allowed;
337 private DragEventArgs drag_event;
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;
346 private bool tracking = false;
347 private bool dropped = false;
348 //private X11Keyboard keyboard;
350 public X11Dnd (IntPtr display, X11Keyboard keyboard)
352 this.display = display;
353 //this.keyboard = keyboard;
360 if (drag_data == null)
362 return drag_data.State != DragState.None;
365 public void SetAllowDrop (Hwnd hwnd, bool allow)
369 if (hwnd.allow_drop == allow)
372 atoms = new int[XdndVersion.Length];
373 for (int i = 0; i < XdndVersion.Length; i++) {
374 atoms[i] = XdndVersion[i].ToInt32();
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;
383 public DragDropEffects StartDrag (IntPtr handle, object data,
384 DragDropEffects allowed_effects)
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);
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");
403 drag_data.LastTopLevel = IntPtr.Zero;
405 System.Windows.Forms.MSG msg = new MSG();
406 object queue_id = XplatUI.StartLoop (Thread.CurrentThread);
408 Timer timer = new Timer ();
409 timer.Tick += new EventHandler (DndTickHandler);
414 drag_data.State = DragState.Dragging;
416 suc = XplatUIX11.XSetSelectionOwner (display, XdndSelection,
417 drag_data.Window, IntPtr.Zero);
420 Console.Error.WriteLine ("Could not take ownership of XdndSelection aborting drag.");
422 return DragDropEffects.None;
425 drag_data.State = DragState.Dragging;
426 drag_data.CurMousePos = new Point ();
430 while (tracking && XplatUI.GetMessage (queue_id, ref msg, IntPtr.Zero, 0, 0)) {
432 if (msg.message >= Msg.WM_KEYFIRST && msg.message <= Msg.WM_KEYLAST) {
433 HandleKeyMessage (msg);
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)
441 if (msg.message == Msg.WM_RBUTTONDOWN && drag_data.MouseState != MouseButtons.Right)
443 if (msg.message == Msg.WM_MBUTTONDOWN && drag_data.MouseState != MouseButtons.Middle)
446 HandleButtonUpMsg ();
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);
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 ());
457 // We don't want to dispatch mouse move
461 XplatUI.DispatchMessage (ref msg);
466 if (drag_event != null)
467 return drag_event.Effect;
469 return DragDropEffects.None;
472 private void DndTickHandler (object sender, EventArgs e)
474 // This is to make sure we don't get stuck in a loop if another
475 // app doesn't finish the DND operation
477 Timer t = (Timer) sender;
478 if (t.Interval == 500)
487 public void HandleButtonUpMsg ()
489 if (drag_data.State == DragState.Beginning) {
490 //state = State.Accepting;
491 } else if (drag_data.State != DragState.None) {
493 if (drag_data.WillAccept) {
495 if (QueryContinue (false, DragAction.Drop))
499 if (QueryContinue (false, DragAction.Cancel))
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
512 private void RemoveCapture (IntPtr handle)
514 Control c = MwfWindow (handle);
515 if (c.InternalCapture)
516 c.InternalCapture = false;
519 public bool HandleMouseOver ()
521 bool dnd_aware = false;
522 IntPtr toplevel = IntPtr.Zero;
523 IntPtr window = XplatUIX11.RootWindowHandle;
529 int x = x_root = drag_data.CurMousePos.X;
530 int y = y_root = drag_data.CurMousePos.Y;
532 while (XplatUIX11.XQueryPointer (display, window, out root, out child,
533 out x_temp, out y_temp, out x, out y, out mask_return)) {
536 dnd_aware = IsWindowDndAware (window);
544 if (child == IntPtr.Zero)
550 if (window != drag_data.LastWindow && drag_data.State == DragState.Entered) {
551 drag_data.State = DragState.Dragging;
553 // TODO: Send a Leave if this is an MWF window
555 if (toplevel != drag_data.LastTopLevel)
556 SendLeave (drag_data.LastTopLevel, toplevel);
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);
564 // Already in a toplevel window, so send a position
565 SendPosition (toplevel, drag_data.Window,
571 drag_data.LastTopLevel = toplevel;
572 drag_data.LastWindow = window;
576 public void HandleKeyMessage (MSG msg)
578 if (VirtualKeys.VK_ESCAPE == (VirtualKeys) msg.wParam.ToInt32()) {
579 QueryContinue (true, DragAction.Cancel);
583 // return true if the event is handled here
584 public bool HandleClientMessage (ref XEvent xevent)
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);
603 public bool HandleSelectionNotifyEvent (ref XEvent xevent)
605 if (source != XplatUIX11.XGetSelectionOwner (display, XdndSelection))
608 MimeHandler handler = FindHandler ((IntPtr) xevent.SelectionEvent.target);
612 data = new DataObject ();
614 handler.Converter.GetData (this, data, ref xevent);
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);
627 public bool HandleSelectionRequestEvent (ref XEvent xevent)
629 if (xevent.SelectionRequestEvent.selection != XdndSelection)
632 MimeHandler handler = FindHandler (xevent.SelectionRequestEvent.target);
636 handler.Converter.SetData (this, drag_data.Data, ref xevent);
641 private bool QueryContinue (bool escape, DragAction action)
643 QueryContinueDragEventArgs qce = new QueryContinueDragEventArgs ((int) XplatUI.State.ModifierKeys,
646 Control c = MwfWindow (source);
653 c.DndContinueDrag (qce);
655 switch (qce.Action) {
656 case DragAction.Continue:
658 case DragAction.Drop:
659 SendDrop (drag_data.LastTopLevel, source, IntPtr.Zero);
661 case DragAction.Cancel:
663 c.InternalCapture = false;
671 private void GiveFeedback (IntPtr action)
673 GiveFeedbackEventArgs gfe = new GiveFeedbackEventArgs (EffectFromAction (drag_data.Action), true);
675 Control c = MwfWindow (source);
678 if (gfe.UseDefaultCursors) {
679 Cursor cursor = CursorNo;
680 if (drag_data.WillAccept) {
681 // Same order as on MS
682 if (action == XdndActionCopy)
684 else if (action == XdndActionLink)
686 else if (action == XdndActionMove)
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;
702 private void SetProperty (ref XEvent xevent, IntPtr data, int length)
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;
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;
720 XplatUIX11.XSendEvent (display, xevent.SelectionRequestEvent.requestor, false,
721 (IntPtr)EventMask.NoEventMask, ref sel);
725 private void Reset ()
731 private void ResetSourceData ()
733 converts_pending = 0;
737 private void ResetTargetData ()
739 position_recieved = false;
743 private bool Accepting_HandleEnterEvent (ref XEvent xevent)
747 source = xevent.ClientMessageEvent.ptr1;
748 toplevel = xevent.AnyEvent.window;
749 target = IntPtr.Zero;
751 ConvertData (ref xevent);
756 private bool Accepting_HandlePositionEvent (ref XEvent xevent)
758 pos_x = (int) xevent.ClientMessageEvent.ptr3 >> 16;
759 pos_y = (int) xevent.ClientMessageEvent.ptr3 & 0xFFFF;
761 // Copy is implicitly allowed
762 Control source_control = MwfWindow (source);
763 if (source_control == null)
764 allowed = EffectFromAction (xevent.ClientMessageEvent.ptr5) | DragDropEffects.Copy;
766 allowed = drag_data.AllowedEffects;
768 IntPtr parent, child, new_child, last_drop_child;
769 parent = XplatUIX11.XRootWindow (display, 0);
771 last_drop_child = IntPtr.Zero;
774 new_child = IntPtr.Zero;
776 if (!XplatUIX11.XTranslateCoordinates (display,
777 parent, child, pos_x, pos_y,
778 out xd, out yd, out new_child))
780 if (new_child == IntPtr.Zero)
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;
790 if (last_drop_child != IntPtr.Zero)
791 child = last_drop_child;
793 if (target != child) {
794 // We have moved into a new control
795 // or into a control for the first time
799 Hwnd hwnd = Hwnd.ObjectFromHandle (target);
800 Control c = Control.FromHandle (hwnd.client_window);
805 SendStatus (source, DragDropEffects.None);
811 position_recieved = true;
813 if (converts_pending > 0)
817 drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
818 allowed, DragDropEffects.None);
819 control.DndEnter (drag_event);
821 SendStatus (source, drag_event.Effect);
824 drag_event.x = pos_x;
825 drag_event.y = pos_y;
826 control.DndOver (drag_event);
828 SendStatus (source, drag_event.Effect);
834 private void Finish ()
836 if (control != null) {
837 if (drag_event == null) {
839 data = new DataObject ();
840 drag_event = new DragEventArgs (data,
842 allowed, DragDropEffects.None);
844 control.DndLeave (drag_event);
849 private bool Accepting_HandleDropEvent (ref XEvent xevent)
851 if (control != null && drag_event != null) {
852 drag_event = new DragEventArgs (data,
854 allowed, drag_event.Effect);
855 control.DndDrop (drag_event);
861 private bool Accepting_HandleLeaveEvent (ref XEvent xevent)
863 if (control != null && drag_event != null)
864 control.DndLeave (drag_event);
869 private bool HandleStatusEvent (ref XEvent xevent)
872 if (drag_data != null && drag_data.State == DragState.Entered) {
874 if (!QueryContinue (false, DragAction.Continue))
877 drag_data.WillAccept = ((int) xevent.ClientMessageEvent.ptr2 & 0x1) != 0;
879 GiveFeedback (xevent.ClientMessageEvent.ptr5);
884 private bool HandleFinishedEvent (ref XEvent xevent)
890 private DragDropEffects EffectFromAction (IntPtr action)
892 DragDropEffects allowed = DragDropEffects.None;
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;
904 private IntPtr ActionFromEffect (DragDropEffects effect)
906 IntPtr action = IntPtr.Zero;
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;
919 private bool ConvertData (ref XEvent xevent)
923 if (source != XplatUIX11.XGetSelectionOwner (display, XdndSelection)) {
927 Control mwfcontrol = MwfWindow (source);
929 if (mwfcontrol != null && drag_data != null) {
930 IDataObject dragged = drag_data.Data as IDataObject;
931 if (dragged != null) {
935 data = new DataObject ();
936 SetDataWithFormats (drag_data.Data);
941 foreach (IntPtr atom in SourceSupportedList (ref xevent)) {
942 MimeHandler handler = FindHandler (atom);
945 XplatUIX11.XConvertSelection (display, XdndSelection, handler.Type,
946 handler.NonProtocol, toplevel, IntPtr.Zero /* CurrentTime */);
953 private void SetDataWithFormats (object value)
955 if (value is string) {
956 data.SetData (DataFormats.Text, value);
957 data.SetData (DataFormats.UnicodeText, value);
960 data.SetData (value);
963 private MimeHandler FindHandler (IntPtr atom)
965 if (atom == IntPtr.Zero)
967 foreach (MimeHandler handler in MimeHandlers) {
968 if (handler.Type == atom)
974 private MimeHandler FindHandler (string name)
976 foreach (MimeHandler handler in MimeHandlers) {
977 foreach (string alias in handler.Aliases) {
985 private void SendStatus (IntPtr source, DragDropEffects effect)
987 XEvent xevent = new XEvent ();
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;
998 xevent.ClientMessageEvent.ptr5 = ActionFromEffect (effect);
999 XplatUIX11.XSendEvent (display, source, false, IntPtr.Zero, ref xevent);
1002 private void SendEnter (IntPtr handle, IntPtr from, IntPtr [] supported)
1004 XEvent xevent = new XEvent ();
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;
1013 // (int) xevent.ClientMessageEvent.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);
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];
1026 XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
1029 private void SendDrop (IntPtr handle, IntPtr from, IntPtr time)
1031 XEvent xevent = new XEvent ();
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;
1041 XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
1045 private void SendPosition (IntPtr handle, IntPtr from, IntPtr action, int x, int y, IntPtr time)
1047 XEvent xevent = new XEvent ();
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;
1059 XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
1062 private void SendLeave (IntPtr handle, IntPtr from)
1064 XEvent xevent = new XEvent ();
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;
1073 XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
1076 private void SendFinished ()
1078 XEvent xevent = new XEvent ();
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;
1087 XplatUIX11.XSendEvent (display, source, false, IntPtr.Zero, ref xevent);
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 ()
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);
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);
1120 private IntPtr [] SourceSupportedList (ref XEvent xevent)
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;
1135 IntPtr data = IntPtr.Zero;
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);
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)));
1148 XplatUIX11.XFree (data);
1154 private string GetText (ref XEvent xevent, bool unicode)
1160 StringBuilder builder = new StringBuilder ();
1164 IntPtr data = IntPtr.Zero;
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,
1173 XplatUIX11.XFree (data);
1178 builder.Append (Marshal.PtrToStringUni (data));
1180 builder.Append (Marshal.PtrToStringAnsi (data));
1181 nread += nitems.ToInt32();
1183 XplatUIX11.XFree (data);
1184 } while (bytes_after.ToInt32() > 0);
1187 return builder.ToString ();
1190 private MemoryStream GetData (ref XEvent xevent)
1196 MemoryStream res = new MemoryStream ();
1200 IntPtr data = IntPtr.Zero;
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,
1209 XplatUIX11.XFree (data);
1213 for (int i = 0; i < nitems.ToInt32(); i++)
1214 res.WriteByte (Marshal.ReadByte (data, i));
1215 nread += nitems.ToInt32();
1217 XplatUIX11.XFree (data);
1218 } while (bytes_after.ToInt32() > 0);
1222 private Control MwfWindow (IntPtr window)
1224 Hwnd hwnd = Hwnd.ObjectFromHandle (window);
1228 Control res = Control.FromHandle (hwnd.client_window);
1231 res = Control.FromHandle (window);
1236 private bool IsWindowDndAware (IntPtr handle)
1239 // Check the version number, we need greater than 3
1244 IntPtr data = IntPtr.Zero;
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);
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);
1257 int version = Marshal.ReadInt32 (data, 0);
1260 Console.Error.WriteLine ("XDND Version too old (" + version + ").");
1261 XplatUIX11.XFree (data);
1265 // First type is actually the XDND version
1266 if (count.ToInt32() > 1) {
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) {
1280 XplatUIX11.XFree (data);
1284 private IntPtr [] DetermineSupportedTypes (object data)
1286 ArrayList res = new ArrayList ();
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)
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);
1306 if (data is ISerializable) {
1307 MimeHandler handler = FindHandler ("application/x-mono-serialized-object");
1308 if (handler != null)
1309 res.Add (handler.Type);
1312 return (IntPtr []) res.ToArray (typeof (IntPtr));