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 ()
481 if (drag_data.State == DragState.Beginning) {
482 //state = State.Accepting;
483 } else if (drag_data.State != DragState.None) {
485 if (drag_data.WillAccept) {
487 if (QueryContinue (false, DragAction.Drop))
491 if (QueryContinue (false, DragAction.Cancel))
495 drag_data.State = DragState.None;
496 // WE can't reset the drag data yet as it is still
497 // most likely going to be used by the SelectionRequest
504 public bool HandleMouseOver ()
506 bool dnd_aware = false;
507 IntPtr toplevel = IntPtr.Zero;
508 IntPtr window = XplatUIX11.RootWindowHandle;
514 int x = x_root = drag_data.CurMousePos.X;
515 int y = y_root = drag_data.CurMousePos.Y;
517 while (XplatUIX11.XQueryPointer (display, window, out root, out child,
518 out x_temp, out y_temp, out x, out y, out mask_return)) {
521 dnd_aware = IsWindowDndAware (window);
529 if (child == IntPtr.Zero)
535 if (window != drag_data.LastWindow && drag_data.State == DragState.Entered) {
536 drag_data.State = DragState.Dragging;
538 // TODO: Send a Leave if this is an MWF window
540 if (toplevel != drag_data.LastTopLevel)
541 SendLeave (drag_data.LastTopLevel, toplevel);
544 drag_data.State = DragState.Entered;
545 if (toplevel != drag_data.LastTopLevel) {
546 // Entering a new toplevel window
547 SendEnter (toplevel, drag_data.Window, drag_data.SupportedTypes);
549 // Already in a toplevel window, so send a position
550 SendPosition (toplevel, drag_data.Window,
556 drag_data.LastTopLevel = toplevel;
557 drag_data.LastWindow = window;
561 public void HandleKeyMessage (MSG msg)
563 if (VirtualKeys.VK_ESCAPE == (VirtualKeys) msg.wParam.ToInt32()) {
564 QueryContinue (true, DragAction.Cancel);
568 // return true if the event is handled here
569 public bool HandleClientMessage (ref XEvent xevent)
571 // most common so we check it first
572 if (xevent.ClientMessageEvent.message_type == XdndPosition)
573 return Accepting_HandlePositionEvent (ref xevent);
574 if (xevent.ClientMessageEvent.message_type == XdndEnter)
575 return Accepting_HandleEnterEvent (ref xevent);
576 if (xevent.ClientMessageEvent.message_type == XdndDrop)
577 return Accepting_HandleDropEvent (ref xevent);
578 if (xevent.ClientMessageEvent.message_type == XdndLeave)
579 return Accepting_HandleLeaveEvent (ref xevent);
580 if (xevent.ClientMessageEvent.message_type == XdndStatus)
581 return HandleStatusEvent (ref xevent);
582 if (xevent.ClientMessageEvent.message_type == XdndFinished)
583 return HandleFinishedEvent (ref xevent);
588 public bool HandleSelectionNotifyEvent (ref XEvent xevent)
590 if (source != XplatUIX11.XGetSelectionOwner (display, XdndSelection))
593 MimeHandler handler = FindHandler ((IntPtr) xevent.SelectionEvent.target);
597 data = new DataObject ();
599 handler.Converter.GetData (this, data, ref xevent);
602 if (converts_pending <= 0 && position_recieved) {
603 drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
604 allowed, DragDropEffects.None);
605 control.DndEnter (drag_event);
606 SendStatus (source, drag_event.Effect);
612 public bool HandleSelectionRequestEvent (ref XEvent xevent)
614 if (xevent.SelectionRequestEvent.selection != XdndSelection)
617 MimeHandler handler = FindHandler (xevent.SelectionRequestEvent.target);
621 handler.Converter.SetData (this, drag_data.Data, ref xevent);
626 private bool QueryContinue (bool escape, DragAction action)
628 QueryContinueDragEventArgs qce = new QueryContinueDragEventArgs ((int) XplatUI.State.ModifierKeys,
631 Control c = MwfWindow (source);
638 c.DndContinueDrag (qce);
640 switch (qce.Action) {
641 case DragAction.Continue:
643 case DragAction.Drop:
644 SendDrop (drag_data.LastTopLevel, source, IntPtr.Zero);
646 case DragAction.Cancel:
648 c.InternalCapture = false;
656 private void GiveFeedback (IntPtr action)
658 GiveFeedbackEventArgs gfe = new GiveFeedbackEventArgs (EffectFromAction (drag_data.Action), true);
660 Control c = MwfWindow (source);
663 if (gfe.UseDefaultCursors) {
664 Cursor cursor = CursorNo;
665 if (drag_data.WillAccept) {
666 // Same order as on MS
667 if (action == XdndActionCopy)
669 else if (action == XdndActionLink)
671 else if (action == XdndActionMove)
674 // TODO: Try not to set the cursor so much
675 //if (cursor.Handle != CurrentCursorHandle) {
676 XplatUIX11.XChangeActivePointerGrab (display,
677 EventMask.ButtonMotionMask |
678 EventMask.PointerMotionMask |
679 EventMask.ButtonPressMask |
680 EventMask.ButtonReleaseMask,
681 cursor.Handle, IntPtr.Zero);
682 //CurrentCursorHandle = cursor.Handle;
687 private void SetProperty (ref XEvent xevent, IntPtr data, int length)
689 XEvent sel = new XEvent();
690 sel.SelectionEvent.type = XEventName.SelectionNotify;
691 sel.SelectionEvent.send_event = true;
692 sel.SelectionEvent.display = display;
693 sel.SelectionEvent.selection = xevent.SelectionRequestEvent.selection;
694 sel.SelectionEvent.target = xevent.SelectionRequestEvent.target;
695 sel.SelectionEvent.requestor = xevent.SelectionRequestEvent.requestor;
696 sel.SelectionEvent.time = xevent.SelectionRequestEvent.time;
697 sel.SelectionEvent.property = IntPtr.Zero;
699 XplatUIX11.XChangeProperty (display, xevent.SelectionRequestEvent.requestor,
700 xevent.SelectionRequestEvent.property,
701 xevent.SelectionRequestEvent.target,
702 8, PropertyMode.Replace, data, length);
703 sel.SelectionEvent.property = xevent.SelectionRequestEvent.property;
705 XplatUIX11.XSendEvent (display, xevent.SelectionRequestEvent.requestor, false,
706 (IntPtr)EventMask.NoEventMask, ref sel);
710 private void Reset ()
716 private void ResetSourceData ()
718 converts_pending = 0;
722 private void ResetTargetData ()
724 position_recieved = false;
728 private bool Accepting_HandleEnterEvent (ref XEvent xevent)
732 source = xevent.ClientMessageEvent.ptr1;
733 toplevel = xevent.AnyEvent.window;
734 target = IntPtr.Zero;
736 ConvertData (ref xevent);
741 private bool Accepting_HandlePositionEvent (ref XEvent xevent)
743 pos_x = (int) xevent.ClientMessageEvent.ptr3 >> 16;
744 pos_y = (int) xevent.ClientMessageEvent.ptr3 & 0xFFFF;
746 // Copy is implicitly allowed
747 Control source_control = MwfWindow (source);
748 if (source_control == null)
749 allowed = EffectFromAction (xevent.ClientMessageEvent.ptr5) | DragDropEffects.Copy;
751 allowed = drag_data.AllowedEffects;
753 IntPtr parent, child, new_child, last_drop_child;
754 parent = XplatUIX11.XRootWindow (display, 0);
756 last_drop_child = IntPtr.Zero;
759 new_child = IntPtr.Zero;
761 if (!XplatUIX11.XTranslateCoordinates (display,
762 parent, child, pos_x, pos_y,
763 out xd, out yd, out new_child))
765 if (new_child == IntPtr.Zero)
769 Hwnd h = Hwnd.ObjectFromHandle (child);
770 Control d = Control.FromHandle (h.client_window);
771 if (d != null && d.allow_drop)
772 last_drop_child = child;
775 if (last_drop_child != IntPtr.Zero)
776 child = last_drop_child;
778 if (target != child) {
779 // We have moved into a new control
780 // or into a control for the first time
784 Hwnd hwnd = Hwnd.ObjectFromHandle (target);
785 Control c = Control.FromHandle (hwnd.client_window);
790 SendStatus (source, DragDropEffects.None);
796 position_recieved = true;
798 if (converts_pending > 0)
802 drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
803 allowed, DragDropEffects.None);
804 control.DndEnter (drag_event);
806 SendStatus (source, drag_event.Effect);
809 drag_event.x = pos_x;
810 drag_event.y = pos_y;
811 control.DndOver (drag_event);
813 SendStatus (source, drag_event.Effect);
819 private void Finish ()
821 if (control != null) {
822 if (drag_event == null) {
824 data = new DataObject ();
825 drag_event = new DragEventArgs (data,
827 allowed, DragDropEffects.None);
829 control.DndLeave (drag_event);
834 private bool Accepting_HandleDropEvent (ref XEvent xevent)
836 if (control != null && drag_event != null) {
837 drag_event = new DragEventArgs (data,
839 allowed, drag_event.Effect);
840 control.DndDrop (drag_event);
846 private bool Accepting_HandleLeaveEvent (ref XEvent xevent)
848 if (control != null && drag_event != null)
849 control.DndLeave (drag_event);
854 private bool HandleStatusEvent (ref XEvent xevent)
857 if (drag_data != null && drag_data.State == DragState.Entered) {
859 if (!QueryContinue (false, DragAction.Continue))
862 drag_data.WillAccept = ((int) xevent.ClientMessageEvent.ptr2 & 0x1) != 0;
864 GiveFeedback (xevent.ClientMessageEvent.ptr5);
869 private bool HandleFinishedEvent (ref XEvent xevent)
875 private DragDropEffects EffectFromAction (IntPtr action)
877 DragDropEffects allowed = DragDropEffects.None;
879 if (action == XdndActionCopy)
880 allowed = DragDropEffects.Copy;
881 else if (action == XdndActionMove)
882 allowed |= DragDropEffects.Move;
883 if (action == XdndActionLink)
884 allowed |= DragDropEffects.Link;
889 private IntPtr ActionFromEffect (DragDropEffects effect)
891 IntPtr action = IntPtr.Zero;
893 // We can't OR together actions on XDND so sadly the primary
894 // is the only one shown here
895 if ((effect & DragDropEffects.Copy) != 0)
896 action = XdndActionCopy;
897 else if ((effect & DragDropEffects.Move) != 0)
898 action = XdndActionMove;
899 else if ((effect & DragDropEffects.Link) != 0)
900 action = XdndActionLink;
904 private bool ConvertData (ref XEvent xevent)
908 if (source != XplatUIX11.XGetSelectionOwner (display, XdndSelection)) {
912 Control mwfcontrol = MwfWindow (source);
914 if (mwfcontrol != null && drag_data != null) {
915 IDataObject dragged = drag_data.Data as IDataObject;
916 if (dragged != null) {
920 data = new DataObject ();
921 SetDataWithFormats (drag_data.Data);
926 foreach (IntPtr atom in SourceSupportedList (ref xevent)) {
927 MimeHandler handler = FindHandler (atom);
930 XplatUIX11.XConvertSelection (display, XdndSelection, handler.Type,
931 handler.NonProtocol, toplevel, IntPtr.Zero /* CurrentTime */);
938 private void SetDataWithFormats (object value)
940 if (value is string) {
941 data.SetData (DataFormats.Text, value);
942 data.SetData (DataFormats.UnicodeText, value);
945 data.SetData (value);
948 private MimeHandler FindHandler (IntPtr atom)
950 if (atom == IntPtr.Zero)
952 foreach (MimeHandler handler in MimeHandlers) {
953 if (handler.Type == atom)
959 private MimeHandler FindHandler (string name)
961 foreach (MimeHandler handler in MimeHandlers) {
962 foreach (string alias in handler.Aliases) {
970 private void SendStatus (IntPtr source, DragDropEffects effect)
972 XEvent xevent = new XEvent ();
974 xevent.AnyEvent.type = XEventName.ClientMessage;
975 xevent.AnyEvent.display = display;
976 xevent.ClientMessageEvent.window = source;
977 xevent.ClientMessageEvent.message_type = XdndStatus;
978 xevent.ClientMessageEvent.format = 32;
979 xevent.ClientMessageEvent.ptr1 = toplevel;
980 if (effect != DragDropEffects.None)
981 xevent.ClientMessageEvent.ptr2 = (IntPtr) 1;
983 xevent.ClientMessageEvent.ptr5 = ActionFromEffect (effect);
984 XplatUIX11.XSendEvent (display, source, false, IntPtr.Zero, ref xevent);
987 private void SendEnter (IntPtr handle, IntPtr from, IntPtr [] supported)
989 XEvent xevent = new XEvent ();
991 xevent.AnyEvent.type = XEventName.ClientMessage;
992 xevent.AnyEvent.display = display;
993 xevent.ClientMessageEvent.window = handle;
994 xevent.ClientMessageEvent.message_type = XdndEnter;
995 xevent.ClientMessageEvent.format = 32;
996 xevent.ClientMessageEvent.ptr1 = from;
998 // (int) xevent.ClientMessageEvent.ptr2 & 0x1)
1000 // xevent.ClientMessageEvent.ptr2 = (IntPtr) ptr2;
1001 // (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~(0xFF << 24)) | ((v) << 24)
1002 xevent.ClientMessageEvent.ptr2 = (IntPtr) ((long)XdndVersion [0] << 24);
1004 if (supported.Length > 0)
1005 xevent.ClientMessageEvent.ptr3 = supported [0];
1006 if (supported.Length > 1)
1007 xevent.ClientMessageEvent.ptr4 = supported [1];
1008 if (supported.Length > 2)
1009 xevent.ClientMessageEvent.ptr5 = supported [2];
1011 XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
1014 private void SendDrop (IntPtr handle, IntPtr from, IntPtr time)
1016 XEvent xevent = new XEvent ();
1018 xevent.AnyEvent.type = XEventName.ClientMessage;
1019 xevent.AnyEvent.display = display;
1020 xevent.ClientMessageEvent.window = handle;
1021 xevent.ClientMessageEvent.message_type = XdndDrop;
1022 xevent.ClientMessageEvent.format = 32;
1023 xevent.ClientMessageEvent.ptr1 = from;
1024 xevent.ClientMessageEvent.ptr3 = time;
1026 XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
1030 private void SendPosition (IntPtr handle, IntPtr from, IntPtr action, int x, int y, IntPtr time)
1032 XEvent xevent = new XEvent ();
1034 xevent.AnyEvent.type = XEventName.ClientMessage;
1035 xevent.AnyEvent.display = display;
1036 xevent.ClientMessageEvent.window = handle;
1037 xevent.ClientMessageEvent.message_type = XdndPosition;
1038 xevent.ClientMessageEvent.format = 32;
1039 xevent.ClientMessageEvent.ptr1 = from;
1040 xevent.ClientMessageEvent.ptr3 = (IntPtr) ((x << 16) | (y & 0xFFFF));
1041 xevent.ClientMessageEvent.ptr4 = time;
1042 xevent.ClientMessageEvent.ptr5 = action;
1044 XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
1047 private void SendLeave (IntPtr handle, IntPtr from)
1049 XEvent xevent = new XEvent ();
1051 xevent.AnyEvent.type = XEventName.ClientMessage;
1052 xevent.AnyEvent.display = display;
1053 xevent.ClientMessageEvent.window = handle;
1054 xevent.ClientMessageEvent.message_type = XdndLeave;
1055 xevent.ClientMessageEvent.format = 32;
1056 xevent.ClientMessageEvent.ptr1 = from;
1058 XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
1061 private void SendFinished ()
1063 XEvent xevent = new XEvent ();
1065 xevent.AnyEvent.type = XEventName.ClientMessage;
1066 xevent.AnyEvent.display = display;
1067 xevent.ClientMessageEvent.window = source;
1068 xevent.ClientMessageEvent.message_type = XdndFinished;
1069 xevent.ClientMessageEvent.format = 32;
1070 xevent.ClientMessageEvent.ptr1 = toplevel;
1072 XplatUIX11.XSendEvent (display, source, false, IntPtr.Zero, ref xevent);
1075 // There is a somewhat decent amount of overhead
1076 // involved in setting up dnd so we do it lazily
1077 // as a lot of applications do not even use it.
1078 private void Init ()
1080 XdndAware = XplatUIX11.XInternAtom (display, "XdndAware", false);
1081 XdndEnter = XplatUIX11.XInternAtom (display, "XdndEnter", false);
1082 XdndLeave = XplatUIX11.XInternAtom (display, "XdndLeave", false);
1083 XdndPosition = XplatUIX11.XInternAtom (display, "XdndPosition", false);
1084 XdndStatus = XplatUIX11.XInternAtom (display, "XdndStatus", false);
1085 XdndDrop = XplatUIX11.XInternAtom (display, "XdndDrop", false);
1086 XdndSelection = XplatUIX11.XInternAtom (display, "XdndSelection", false);
1087 XdndFinished = XplatUIX11.XInternAtom (display, "XdndFinished", false);
1088 XdndTypeList = XplatUIX11.XInternAtom (display, "XdndTypeList", false);
1089 XdndActionCopy = XplatUIX11.XInternAtom (display, "XdndActionCopy", false);
1090 XdndActionMove = XplatUIX11.XInternAtom (display, "XdndActionMove", false);
1091 XdndActionLink = XplatUIX11.XInternAtom (display, "XdndActionLink", false);
1092 //XdndActionPrivate = XplatUIX11.XInternAtom (display, "XdndActionPrivate", false);
1093 //XdndActionList = XplatUIX11.XInternAtom (display, "XdndActionList", false);
1094 //XdndActionDescription = XplatUIX11.XInternAtom (display, "XdndActionDescription", false);
1095 //XdndActionAsk = XplatUIX11.XInternAtom (display, "XdndActionAsk", false);
1097 foreach (MimeHandler handler in MimeHandlers) {
1098 handler.Type = XplatUIX11.XInternAtom (display, handler.Name, false);
1099 handler.NonProtocol = XplatUIX11.XInternAtom (display,
1100 String.Concat ("MWFNonP+", handler.Name), false);
1105 private IntPtr [] SourceSupportedList (ref XEvent xevent)
1110 if (((int) xevent.ClientMessageEvent.ptr2 & 0x1) == 0) {
1111 res = new IntPtr [3];
1112 res [0] = xevent.ClientMessageEvent.ptr3;
1113 res [1] = xevent.ClientMessageEvent.ptr4;
1114 res [2] = xevent.ClientMessageEvent.ptr5;
1120 IntPtr data = IntPtr.Zero;
1122 XplatUIX11.XGetWindowProperty (display, source, XdndTypeList,
1123 IntPtr.Zero, new IntPtr(32), false, (IntPtr) Atom.XA_ATOM,
1124 out type, out format, out count,
1125 out remaining, ref data);
1127 res = new IntPtr [count.ToInt32()];
1128 for (int i = 0; i < count.ToInt32(); i++) {
1129 res [i] = (IntPtr) Marshal.ReadInt32 (data, i *
1130 Marshal.SizeOf (typeof (int)));
1133 XplatUIX11.XFree (data);
1139 private string GetText (ref XEvent xevent, bool unicode)
1145 StringBuilder builder = new StringBuilder ();
1149 IntPtr data = IntPtr.Zero;
1151 if (0 != XplatUIX11.XGetWindowProperty (display,
1152 xevent.AnyEvent.window,
1153 (IntPtr) xevent.SelectionEvent.property,
1154 IntPtr.Zero, new IntPtr(0xffffff), false,
1155 (IntPtr) Atom.AnyPropertyType, out actual_type,
1156 out actual_fmt, out nitems, out bytes_after,
1158 XplatUIX11.XFree (data);
1163 builder.Append (Marshal.PtrToStringUni (data));
1165 builder.Append (Marshal.PtrToStringAnsi (data));
1166 nread += nitems.ToInt32();
1168 XplatUIX11.XFree (data);
1169 } while (bytes_after.ToInt32() > 0);
1172 return builder.ToString ();
1175 private MemoryStream GetData (ref XEvent xevent)
1181 MemoryStream res = new MemoryStream ();
1185 IntPtr data = IntPtr.Zero;
1187 if (0 != XplatUIX11.XGetWindowProperty (display,
1188 xevent.AnyEvent.window,
1189 (IntPtr) xevent.SelectionEvent.property,
1190 IntPtr.Zero, new IntPtr(0xffffff), false,
1191 (IntPtr) Atom.AnyPropertyType, out actual_type,
1192 out actual_fmt, out nitems, out bytes_after,
1194 XplatUIX11.XFree (data);
1198 for (int i = 0; i < nitems.ToInt32(); i++)
1199 res.WriteByte (Marshal.ReadByte (data, i));
1200 nread += nitems.ToInt32();
1202 XplatUIX11.XFree (data);
1203 } while (bytes_after.ToInt32() > 0);
1207 private Control MwfWindow (IntPtr window)
1209 Hwnd hwnd = Hwnd.ObjectFromHandle (window);
1213 Control res = Control.FromHandle (hwnd.client_window);
1216 res = Control.FromHandle (window);
1221 private bool IsWindowDndAware (IntPtr handle)
1224 // Check the version number, we need greater than 3
1229 IntPtr data = IntPtr.Zero;
1231 XplatUIX11.XGetWindowProperty (display, handle, XdndAware, IntPtr.Zero, new IntPtr(0x8000000), false,
1232 (IntPtr) Atom.XA_ATOM, out actual, out format,
1233 out count, out remaining, ref data);
1235 if (actual != (IntPtr) Atom.XA_ATOM || format != 32 ||
1236 count.ToInt32() == 0 || data == IntPtr.Zero) {
1237 if (data != IntPtr.Zero)
1238 XplatUIX11.XFree (data);
1242 int version = Marshal.ReadInt32 (data, 0);
1245 Console.Error.WriteLine ("XDND Version too old (" + version + ").");
1246 XplatUIX11.XFree (data);
1250 // First type is actually the XDND version
1251 if (count.ToInt32() > 1) {
1253 for (int i = 1; i < count.ToInt32(); i++) {
1254 IntPtr type = (IntPtr) Marshal.ReadInt32 (data, i *
1255 Marshal.SizeOf (typeof (int)));
1256 for (int j = 0; j < drag_data.SupportedTypes.Length; j++) {
1257 if (drag_data.SupportedTypes [j] == type) {
1265 XplatUIX11.XFree (data);
1269 private IntPtr [] DetermineSupportedTypes (object data)
1271 ArrayList res = new ArrayList ();
1273 if (data is string) {
1274 MimeHandler handler = FindHandler ("text/plain");
1275 if (handler != null)
1276 res.Add (handler.Type);
1277 }/* else if (data is Bitmap)
1282 IDataObject data_object = data as IDataObject;
1283 if (data_object != null) {
1284 foreach (string format in data_object.GetFormats (true)) {
1285 MimeHandler handler = FindHandler (format);
1286 if (handler != null && !res.Contains (handler.Type))
1287 res.Add (handler.Type);
1291 if (data is ISerializable) {
1292 MimeHandler handler = FindHandler ("application/x-mono-serialized-object");
1293 if (handler != null)
1294 res.Add (handler.Type);
1297 return (IntPtr []) res.ToArray (typeof (IntPtr));