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 ();
429 while (tracking && XplatUI.GetMessage (queue_id, ref msg, IntPtr.Zero, 0, 0)) {
431 if (msg.message >= Msg.WM_KEYFIRST && msg.message <= Msg.WM_KEYLAST) {
432 HandleKeyMessage (msg);
434 switch (msg.message) {
435 case Msg.WM_LBUTTONUP:
436 case Msg.WM_RBUTTONUP:
437 if (msg.message == Msg.WM_LBUTTONDOWN && drag_data.MouseState != MouseButtons.Left)
439 if (msg.message == Msg.WM_RBUTTONDOWN && drag_data.MouseState != MouseButtons.Right)
442 HandleButtonUpMsg ();
444 case Msg.WM_MOUSEMOVE:
445 drag_data.CurMousePos.X = Control.LowOrder ((int) msg.lParam.ToInt32 ());
446 drag_data.CurMousePos.Y = Control.HighOrder ((int) msg.lParam.ToInt32 ());
449 // We don't want to dispatch mouse move
453 XplatUI.DispatchMessage (ref msg);
458 if (drag_event != null)
459 return drag_event.Effect;
461 return DragDropEffects.None;
464 private void DndTickHandler (object sender, EventArgs e)
466 // This is to make sure we don't get stuck in a loop if another
467 // app doesn't finish the DND operation
469 Timer t = (Timer) sender;
470 if (t.Interval == 500)
479 public void HandleButtonUpMsg ()
483 if (drag_data.State == DragState.Beginning) {
484 //state = State.Accepting;
485 } else if (drag_data.State != DragState.None) {
487 if (drag_data.WillAccept) {
489 if (QueryContinue (false, DragAction.Drop))
494 drag_data.State = DragState.None;
495 // WE can't reset the drag data yet as it is still
496 // most likely going to be used by the SelectionRequest
503 public bool HandleMouseOver ()
505 bool dnd_aware = false;
506 IntPtr toplevel = IntPtr.Zero;
507 IntPtr window = XplatUIX11.RootWindowHandle;
513 int x = x_root = drag_data.CurMousePos.X;
514 int y = y_root = drag_data.CurMousePos.Y;
516 while (XplatUIX11.XQueryPointer (display, window, out root, out child,
517 out x_temp, out y_temp, out x, out y, out mask_return)) {
520 dnd_aware = IsWindowDndAware (window);
528 if (child == IntPtr.Zero)
534 if (window != drag_data.LastWindow && drag_data.State == DragState.Entered) {
535 drag_data.State = DragState.Dragging;
537 // TODO: Send a Leave if this is an MWF window
539 if (toplevel != drag_data.LastTopLevel)
540 SendLeave (drag_data.LastTopLevel, toplevel);
543 drag_data.State = DragState.Entered;
544 if (toplevel != drag_data.LastTopLevel) {
545 // Entering a new toplevel window
546 SendEnter (toplevel, drag_data.Window, drag_data.SupportedTypes);
548 // Already in a toplevel window, so send a position
549 SendPosition (toplevel, drag_data.Window,
555 drag_data.LastTopLevel = toplevel;
556 drag_data.LastWindow = window;
560 public void HandleKeyMessage (MSG msg)
562 if (VirtualKeys.VK_ESCAPE == (VirtualKeys) msg.wParam.ToInt32()) {
563 QueryContinue (true, DragAction.Cancel);
567 // return true if the event is handled here
568 public bool HandleClientMessage (ref XEvent xevent)
570 // most common so we check it first
571 if (xevent.ClientMessageEvent.message_type == XdndPosition)
572 return Accepting_HandlePositionEvent (ref xevent);
573 if (xevent.ClientMessageEvent.message_type == XdndEnter)
574 return Accepting_HandleEnterEvent (ref xevent);
575 if (xevent.ClientMessageEvent.message_type == XdndDrop)
576 return Accepting_HandleDropEvent (ref xevent);
577 if (xevent.ClientMessageEvent.message_type == XdndLeave)
578 return Accepting_HandleLeaveEvent (ref xevent);
579 if (xevent.ClientMessageEvent.message_type == XdndStatus)
580 return HandleStatusEvent (ref xevent);
581 if (xevent.ClientMessageEvent.message_type == XdndFinished)
582 return HandleFinishedEvent (ref xevent);
587 public bool HandleSelectionNotifyEvent (ref XEvent xevent)
589 if (source != XplatUIX11.XGetSelectionOwner (display, XdndSelection))
592 MimeHandler handler = FindHandler ((IntPtr) xevent.SelectionEvent.target);
596 data = new DataObject ();
598 handler.Converter.GetData (this, data, ref xevent);
601 if (converts_pending <= 0 && position_recieved) {
602 drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
603 allowed, DragDropEffects.None);
604 control.DndEnter (drag_event);
605 SendStatus (source, drag_event.Effect);
611 public bool HandleSelectionRequestEvent (ref XEvent xevent)
613 if (xevent.SelectionRequestEvent.selection != XdndSelection)
616 MimeHandler handler = FindHandler (xevent.SelectionRequestEvent.target);
620 handler.Converter.SetData (this, drag_data.Data, ref xevent);
625 private bool QueryContinue (bool escape, DragAction action)
627 QueryContinueDragEventArgs qce = new QueryContinueDragEventArgs ((int) XplatUI.State.ModifierKeys,
630 Control c = MwfWindow (source);
631 c.DndContinueDrag (qce);
633 switch (qce.Action) {
634 case DragAction.Continue:
636 case DragAction.Drop:
637 SendDrop (drag_data.LastTopLevel, source, IntPtr.Zero);
639 case DragAction.Cancel:
641 c.InternalCapture = false;
649 private void GiveFeedback (IntPtr action)
651 GiveFeedbackEventArgs gfe = new GiveFeedbackEventArgs (EffectFromAction (drag_data.Action), true);
653 Control c = MwfWindow (source);
656 if (gfe.UseDefaultCursors) {
657 Cursor cursor = CursorNo;
658 if (drag_data.WillAccept) {
659 // Same order as on MS
660 if (action == XdndActionCopy)
662 else if (action == XdndActionLink)
664 else if (action == XdndActionMove)
667 // TODO: Try not to set the cursor so much
668 //if (cursor.Handle != CurrentCursorHandle) {
669 XplatUIX11.XChangeActivePointerGrab (display,
670 EventMask.ButtonMotionMask |
671 EventMask.PointerMotionMask |
672 EventMask.ButtonPressMask |
673 EventMask.ButtonReleaseMask,
674 cursor.Handle, IntPtr.Zero);
675 //CurrentCursorHandle = cursor.Handle;
680 private void SetProperty (ref XEvent xevent, IntPtr data, int length)
682 XEvent sel = new XEvent();
683 sel.SelectionEvent.type = XEventName.SelectionNotify;
684 sel.SelectionEvent.send_event = true;
685 sel.SelectionEvent.display = display;
686 sel.SelectionEvent.selection = xevent.SelectionRequestEvent.selection;
687 sel.SelectionEvent.target = xevent.SelectionRequestEvent.target;
688 sel.SelectionEvent.requestor = xevent.SelectionRequestEvent.requestor;
689 sel.SelectionEvent.time = xevent.SelectionRequestEvent.time;
690 sel.SelectionEvent.property = IntPtr.Zero;
692 XplatUIX11.XChangeProperty (display, xevent.SelectionRequestEvent.requestor,
693 xevent.SelectionRequestEvent.property,
694 xevent.SelectionRequestEvent.target,
695 8, PropertyMode.Replace, data, length);
696 sel.SelectionEvent.property = xevent.SelectionRequestEvent.property;
698 XplatUIX11.XSendEvent (display, xevent.SelectionRequestEvent.requestor, false,
699 (IntPtr)EventMask.NoEventMask, ref sel);
703 private void Reset ()
709 private void ResetSourceData ()
711 converts_pending = 0;
715 private void ResetTargetData ()
717 position_recieved = false;
721 private bool Accepting_HandleEnterEvent (ref XEvent xevent)
725 source = xevent.ClientMessageEvent.ptr1;
726 toplevel = xevent.AnyEvent.window;
727 target = IntPtr.Zero;
729 ConvertData (ref xevent);
734 private bool Accepting_HandlePositionEvent (ref XEvent xevent)
736 pos_x = (int) xevent.ClientMessageEvent.ptr3 >> 16;
737 pos_y = (int) xevent.ClientMessageEvent.ptr3 & 0xFFFF;
739 // Copy is implicitly allowed
740 Control source_control = MwfWindow (source);
741 if (source_control == null)
742 allowed = EffectFromAction (xevent.ClientMessageEvent.ptr5) | DragDropEffects.Copy;
744 allowed = drag_data.AllowedEffects;
746 IntPtr parent, child, new_child, last_drop_child;
747 parent = XplatUIX11.XRootWindow (display, 0);
749 last_drop_child = IntPtr.Zero;
752 new_child = IntPtr.Zero;
754 if (!XplatUIX11.XTranslateCoordinates (display,
755 parent, child, pos_x, pos_y,
756 out xd, out yd, out new_child))
758 if (new_child == IntPtr.Zero)
762 Hwnd h = Hwnd.ObjectFromHandle (child);
763 Control d = Control.FromHandle (h.client_window);
764 if (d != null && d.allow_drop)
765 last_drop_child = child;
768 if (last_drop_child != IntPtr.Zero)
769 child = last_drop_child;
771 if (target != child) {
772 // We have moved into a new control
773 // or into a control for the first time
777 Hwnd hwnd = Hwnd.ObjectFromHandle (target);
778 Control c = Control.FromHandle (hwnd.client_window);
783 SendStatus (source, DragDropEffects.None);
789 position_recieved = true;
791 if (converts_pending > 0)
795 drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
796 allowed, DragDropEffects.None);
797 control.DndEnter (drag_event);
799 SendStatus (source, drag_event.Effect);
802 drag_event.x = pos_x;
803 drag_event.y = pos_y;
804 control.DndOver (drag_event);
806 SendStatus (source, drag_event.Effect);
812 private void Finish ()
814 if (control != null) {
815 if (drag_event == null) {
817 data = new DataObject ();
818 drag_event = new DragEventArgs (data,
820 allowed, DragDropEffects.None);
822 control.DndLeave (drag_event);
827 private bool Accepting_HandleDropEvent (ref XEvent xevent)
829 if (control != null && drag_event != null) {
830 drag_event = new DragEventArgs (data,
832 allowed, drag_event.Effect);
833 control.DndDrop (drag_event);
839 private bool Accepting_HandleLeaveEvent (ref XEvent xevent)
841 if (control != null && drag_event != null)
842 control.DndLeave (drag_event);
847 private bool HandleStatusEvent (ref XEvent xevent)
850 if (drag_data != null && drag_data.State == DragState.Entered) {
852 if (!QueryContinue (false, DragAction.Continue))
855 drag_data.WillAccept = ((int) xevent.ClientMessageEvent.ptr2 & 0x1) != 0;
857 GiveFeedback (xevent.ClientMessageEvent.ptr5);
862 private bool HandleFinishedEvent (ref XEvent xevent)
868 private DragDropEffects EffectFromAction (IntPtr action)
870 DragDropEffects allowed = DragDropEffects.None;
872 if (action == XdndActionCopy)
873 allowed = DragDropEffects.Copy;
874 else if (action == XdndActionMove)
875 allowed |= DragDropEffects.Move;
876 if (action == XdndActionLink)
877 allowed |= DragDropEffects.Link;
882 private IntPtr ActionFromEffect (DragDropEffects effect)
884 IntPtr action = IntPtr.Zero;
886 // We can't OR together actions on XDND so sadly the primary
887 // is the only one shown here
888 if ((effect & DragDropEffects.Copy) != 0)
889 action = XdndActionCopy;
890 else if ((effect & DragDropEffects.Move) != 0)
891 action = XdndActionMove;
892 else if ((effect & DragDropEffects.Link) != 0)
893 action = XdndActionLink;
897 private bool ConvertData (ref XEvent xevent)
901 if (source != XplatUIX11.XGetSelectionOwner (display, XdndSelection)) {
905 Control mwfcontrol = MwfWindow (source);
907 if (mwfcontrol != null && drag_data != null) {
908 IDataObject dragged = drag_data.Data as IDataObject;
909 if (dragged != null) {
913 data = new DataObject ();
914 SetDataWithFormats (drag_data.Data);
919 foreach (IntPtr atom in SourceSupportedList (ref xevent)) {
920 MimeHandler handler = FindHandler (atom);
923 XplatUIX11.XConvertSelection (display, XdndSelection, handler.Type,
924 handler.NonProtocol, toplevel, IntPtr.Zero /* CurrentTime */);
931 private void SetDataWithFormats (object value)
933 if (value is string) {
934 data.SetData (DataFormats.Text, value);
935 data.SetData (DataFormats.UnicodeText, value);
938 data.SetData (value);
941 private MimeHandler FindHandler (IntPtr atom)
943 if (atom == IntPtr.Zero)
945 foreach (MimeHandler handler in MimeHandlers) {
946 if (handler.Type == atom)
952 private MimeHandler FindHandler (string name)
954 foreach (MimeHandler handler in MimeHandlers) {
955 foreach (string alias in handler.Aliases) {
963 private void SendStatus (IntPtr source, DragDropEffects effect)
965 XEvent xevent = new XEvent ();
967 xevent.AnyEvent.type = XEventName.ClientMessage;
968 xevent.AnyEvent.display = display;
969 xevent.ClientMessageEvent.window = source;
970 xevent.ClientMessageEvent.message_type = XdndStatus;
971 xevent.ClientMessageEvent.format = 32;
972 xevent.ClientMessageEvent.ptr1 = toplevel;
973 if (effect != DragDropEffects.None)
974 xevent.ClientMessageEvent.ptr2 = (IntPtr) 1;
976 xevent.ClientMessageEvent.ptr5 = ActionFromEffect (effect);
977 XplatUIX11.XSendEvent (display, source, false, IntPtr.Zero, ref xevent);
980 private void SendEnter (IntPtr handle, IntPtr from, IntPtr [] supported)
982 XEvent xevent = new XEvent ();
984 xevent.AnyEvent.type = XEventName.ClientMessage;
985 xevent.AnyEvent.display = display;
986 xevent.ClientMessageEvent.window = handle;
987 xevent.ClientMessageEvent.message_type = XdndEnter;
988 xevent.ClientMessageEvent.format = 32;
989 xevent.ClientMessageEvent.ptr1 = from;
991 // (int) xevent.ClientMessageEvent.ptr2 & 0x1)
993 // xevent.ClientMessageEvent.ptr2 = (IntPtr) ptr2;
994 // (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~(0xFF << 24)) | ((v) << 24)
995 xevent.ClientMessageEvent.ptr2 = (IntPtr) ((long)XdndVersion [0] << 24);
997 if (supported.Length > 0)
998 xevent.ClientMessageEvent.ptr3 = supported [0];
999 if (supported.Length > 1)
1000 xevent.ClientMessageEvent.ptr4 = supported [1];
1001 if (supported.Length > 2)
1002 xevent.ClientMessageEvent.ptr5 = supported [2];
1004 XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
1007 private void SendDrop (IntPtr handle, IntPtr from, IntPtr time)
1009 XEvent xevent = new XEvent ();
1011 xevent.AnyEvent.type = XEventName.ClientMessage;
1012 xevent.AnyEvent.display = display;
1013 xevent.ClientMessageEvent.window = handle;
1014 xevent.ClientMessageEvent.message_type = XdndDrop;
1015 xevent.ClientMessageEvent.format = 32;
1016 xevent.ClientMessageEvent.ptr1 = from;
1017 xevent.ClientMessageEvent.ptr3 = time;
1019 XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
1023 private void SendPosition (IntPtr handle, IntPtr from, IntPtr action, int x, int y, IntPtr time)
1025 XEvent xevent = new XEvent ();
1027 xevent.AnyEvent.type = XEventName.ClientMessage;
1028 xevent.AnyEvent.display = display;
1029 xevent.ClientMessageEvent.window = handle;
1030 xevent.ClientMessageEvent.message_type = XdndPosition;
1031 xevent.ClientMessageEvent.format = 32;
1032 xevent.ClientMessageEvent.ptr1 = from;
1033 xevent.ClientMessageEvent.ptr3 = (IntPtr) ((x << 16) | (y & 0xFFFF));
1034 xevent.ClientMessageEvent.ptr4 = time;
1035 xevent.ClientMessageEvent.ptr5 = action;
1037 XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
1040 private void SendLeave (IntPtr handle, IntPtr from)
1042 XEvent xevent = new XEvent ();
1044 xevent.AnyEvent.type = XEventName.ClientMessage;
1045 xevent.AnyEvent.display = display;
1046 xevent.ClientMessageEvent.window = handle;
1047 xevent.ClientMessageEvent.message_type = XdndLeave;
1048 xevent.ClientMessageEvent.format = 32;
1049 xevent.ClientMessageEvent.ptr1 = from;
1051 XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
1054 private void SendFinished ()
1056 XEvent xevent = new XEvent ();
1058 xevent.AnyEvent.type = XEventName.ClientMessage;
1059 xevent.AnyEvent.display = display;
1060 xevent.ClientMessageEvent.window = source;
1061 xevent.ClientMessageEvent.message_type = XdndFinished;
1062 xevent.ClientMessageEvent.format = 32;
1063 xevent.ClientMessageEvent.ptr1 = toplevel;
1065 XplatUIX11.XSendEvent (display, source, false, IntPtr.Zero, ref xevent);
1068 // There is a somewhat decent amount of overhead
1069 // involved in setting up dnd so we do it lazily
1070 // as a lot of applications do not even use it.
1071 private void Init ()
1073 XdndAware = XplatUIX11.XInternAtom (display, "XdndAware", false);
1074 XdndEnter = XplatUIX11.XInternAtom (display, "XdndEnter", false);
1075 XdndLeave = XplatUIX11.XInternAtom (display, "XdndLeave", false);
1076 XdndPosition = XplatUIX11.XInternAtom (display, "XdndPosition", false);
1077 XdndStatus = XplatUIX11.XInternAtom (display, "XdndStatus", false);
1078 XdndDrop = XplatUIX11.XInternAtom (display, "XdndDrop", false);
1079 XdndSelection = XplatUIX11.XInternAtom (display, "XdndSelection", false);
1080 XdndFinished = XplatUIX11.XInternAtom (display, "XdndFinished", false);
1081 XdndTypeList = XplatUIX11.XInternAtom (display, "XdndTypeList", false);
1082 XdndActionCopy = XplatUIX11.XInternAtom (display, "XdndActionCopy", false);
1083 XdndActionMove = XplatUIX11.XInternAtom (display, "XdndActionMove", false);
1084 XdndActionLink = XplatUIX11.XInternAtom (display, "XdndActionLink", false);
1085 //XdndActionPrivate = XplatUIX11.XInternAtom (display, "XdndActionPrivate", false);
1086 //XdndActionList = XplatUIX11.XInternAtom (display, "XdndActionList", false);
1087 //XdndActionDescription = XplatUIX11.XInternAtom (display, "XdndActionDescription", false);
1088 //XdndActionAsk = XplatUIX11.XInternAtom (display, "XdndActionAsk", false);
1090 foreach (MimeHandler handler in MimeHandlers) {
1091 handler.Type = XplatUIX11.XInternAtom (display, handler.Name, false);
1092 handler.NonProtocol = XplatUIX11.XInternAtom (display,
1093 String.Concat ("MWFNonP+", handler.Name), false);
1098 private IntPtr [] SourceSupportedList (ref XEvent xevent)
1103 if (((int) xevent.ClientMessageEvent.ptr2 & 0x1) == 0) {
1104 res = new IntPtr [3];
1105 res [0] = xevent.ClientMessageEvent.ptr3;
1106 res [1] = xevent.ClientMessageEvent.ptr4;
1107 res [2] = xevent.ClientMessageEvent.ptr5;
1113 IntPtr data = IntPtr.Zero;
1115 XplatUIX11.XGetWindowProperty (display, source, XdndTypeList,
1116 IntPtr.Zero, new IntPtr(32), false, (IntPtr) Atom.XA_ATOM,
1117 out type, out format, out count,
1118 out remaining, ref data);
1120 res = new IntPtr [count.ToInt32()];
1121 for (int i = 0; i < count.ToInt32(); i++) {
1122 res [i] = (IntPtr) Marshal.ReadInt32 (data, i *
1123 Marshal.SizeOf (typeof (int)));
1126 XplatUIX11.XFree (data);
1132 private string GetText (ref XEvent xevent, bool unicode)
1138 StringBuilder builder = new StringBuilder ();
1142 IntPtr data = IntPtr.Zero;
1144 if (0 != XplatUIX11.XGetWindowProperty (display,
1145 xevent.AnyEvent.window,
1146 (IntPtr) xevent.SelectionEvent.property,
1147 IntPtr.Zero, new IntPtr(0xffffff), false,
1148 (IntPtr) Atom.AnyPropertyType, out actual_type,
1149 out actual_fmt, out nitems, out bytes_after,
1151 XplatUIX11.XFree (data);
1156 builder.Append (Marshal.PtrToStringUni (data));
1158 builder.Append (Marshal.PtrToStringAnsi (data));
1159 nread += nitems.ToInt32();
1161 XplatUIX11.XFree (data);
1162 } while (bytes_after.ToInt32() > 0);
1165 return builder.ToString ();
1168 private MemoryStream GetData (ref XEvent xevent)
1174 MemoryStream res = new MemoryStream ();
1178 IntPtr data = IntPtr.Zero;
1180 if (0 != XplatUIX11.XGetWindowProperty (display,
1181 xevent.AnyEvent.window,
1182 (IntPtr) xevent.SelectionEvent.property,
1183 IntPtr.Zero, new IntPtr(0xffffff), false,
1184 (IntPtr) Atom.AnyPropertyType, out actual_type,
1185 out actual_fmt, out nitems, out bytes_after,
1187 XplatUIX11.XFree (data);
1191 for (int i = 0; i < nitems.ToInt32(); i++)
1192 res.WriteByte (Marshal.ReadByte (data, i));
1193 nread += nitems.ToInt32();
1195 XplatUIX11.XFree (data);
1196 } while (bytes_after.ToInt32() > 0);
1200 private Control MwfWindow (IntPtr window)
1202 Hwnd hwnd = Hwnd.ObjectFromHandle (window);
1206 Control res = Control.FromHandle (hwnd.client_window);
1210 private bool IsWindowDndAware (IntPtr handle)
1213 // Check the version number, we need greater than 3
1218 IntPtr data = IntPtr.Zero;
1220 XplatUIX11.XGetWindowProperty (display, handle, XdndAware, IntPtr.Zero, new IntPtr(0x8000000), false,
1221 (IntPtr) Atom.XA_ATOM, out actual, out format,
1222 out count, out remaining, ref data);
1224 if (actual != (IntPtr) Atom.XA_ATOM || format != 32 ||
1225 count.ToInt32() == 0 || data == IntPtr.Zero) {
1226 if (data != IntPtr.Zero)
1227 XplatUIX11.XFree (data);
1231 int version = Marshal.ReadInt32 (data, 0);
1234 Console.Error.WriteLine ("XDND Version too old (" + version + ").");
1235 XplatUIX11.XFree (data);
1239 // First type is actually the XDND version
1240 if (count.ToInt32() > 1) {
1242 for (int i = 1; i < count.ToInt32(); i++) {
1243 IntPtr type = (IntPtr) Marshal.ReadInt32 (data, i *
1244 Marshal.SizeOf (typeof (int)));
1245 for (int j = 0; j < drag_data.SupportedTypes.Length; j++) {
1246 if (drag_data.SupportedTypes [j] == type) {
1254 XplatUIX11.XFree (data);
1258 private IntPtr [] DetermineSupportedTypes (object data)
1260 ArrayList res = new ArrayList ();
1262 if (data is string) {
1263 MimeHandler handler = FindHandler ("text/plain");
1264 if (handler != null)
1265 res.Add (handler.Type);
1266 }/* else if (data is Bitmap)
1271 IDataObject data_object = data as IDataObject;
1272 if (data_object != null) {
1273 foreach (string format in data_object.GetFormats (true)) {
1274 MimeHandler handler = FindHandler (format);
1275 if (handler != null && !res.Contains (handler.Type))
1276 res.Add (handler.Type);
1280 if (data is ISerializable) {
1281 MimeHandler handler = FindHandler ("application/x-mono-serialized-object");
1282 if (handler != null)
1283 res.Add (handler.Type);
1286 return (IntPtr []) res.ToArray (typeof (IntPtr));