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.Collections;
33 using System.Runtime.Serialization;
34 using System.Runtime.InteropServices;
35 using System.Runtime.Serialization.Formatters.Binary;
37 namespace System.Windows.Forms {
39 internal class X11Dnd {
46 private enum DragState {
53 private interface IDataConverter {
54 void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent);
55 void SetData (X11Dnd dnd, object data, ref XEvent xevent);
58 private delegate void MimeConverter (IntPtr dsp,
59 DataObject data, ref XEvent xevent);
61 private class MimeHandler {
64 public IntPtr NonProtocol;
65 public IDataConverter Converter;
67 public MimeHandler (string name, IDataConverter converter)
70 Converter = converter;
73 public override string ToString ()
75 return "MimeHandler {" + Name + "}";
79 private MimeHandler [] MimeHandlers = {
80 // new MimeHandler ("WCF_DIB"),
81 // new MimeHandler ("image/gif", new MimeConverter (ImageConverter)),
82 // new MimeHandler ("text/rtf", new MimeConverter (RtfConverter)),
83 // new MimeHandler ("text/richtext", new MimeConverter (RtfConverter)),
85 new MimeHandler ("text/plain", new TextConverter ()),
86 new MimeHandler ("text/html", new HtmlConverter ()),
87 new MimeHandler ("text/uri-list", new UriListConverter ()),
88 new MimeHandler ("application/x-mono-serialized-object",
89 new SerializedObjectConverter ())
92 private class SerializedObjectConverter : IDataConverter {
94 public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
96 MemoryStream stream = dnd.GetData (ref xevent);
97 BinaryFormatter bf = new BinaryFormatter ();
99 if (stream.Length == 0)
103 object obj = bf.Deserialize (stream);
107 public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
112 MemoryStream stream = new MemoryStream ();
113 BinaryFormatter bf = new BinaryFormatter ();
115 bf.Serialize (stream, data);
117 IntPtr buffer = Marshal.AllocHGlobal ((int) stream.Length);
120 for (int i = 0; i < stream.Length; i++) {
121 Marshal.WriteByte (buffer, i, (byte) stream.ReadByte ());
124 dnd.SetProperty (ref xevent, buffer, (int) stream.Length);
128 private class HtmlConverter : IDataConverter {
130 public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
132 string text = dnd.GetText (ref xevent, false);
135 data.SetData (DataFormats.Text, text);
136 data.SetData (DataFormats.UnicodeText, text);
139 public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
143 string str = data as string;
148 if (xevent.SelectionRequestEvent.target == (IntPtr)Atom.XA_STRING) {
149 byte [] bytes = Encoding.ASCII.GetBytes (str);
150 buffer = Marshal.AllocHGlobal (bytes.Length);
152 for (int i = 0; i < len; i++)
153 Marshal.WriteByte (buffer, i, bytes [i]);
155 buffer = Marshal.StringToHGlobalAnsi (str);
157 while (Marshal.ReadByte (buffer, len) != 0)
161 dnd.SetProperty (ref xevent, buffer, len);
163 Marshal.FreeHGlobal (buffer);
167 private class TextConverter : IDataConverter {
169 public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
171 string text = dnd.GetText (ref xevent, true);
174 data.SetData (DataFormats.Text, text);
175 data.SetData (DataFormats.UnicodeText, text);
178 public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
182 string str = data as string;
187 if (xevent.SelectionRequestEvent.target == (IntPtr)Atom.XA_STRING) {
188 byte [] bytes = Encoding.ASCII.GetBytes (str);
189 buffer = Marshal.AllocHGlobal (bytes.Length);
191 for (int i = 0; i < len; i++)
192 Marshal.WriteByte (buffer, i, bytes [i]);
194 buffer = Marshal.StringToHGlobalAnsi (str);
196 while (Marshal.ReadByte (buffer, len) != 0)
200 dnd.SetProperty (ref xevent, buffer, len);
202 Marshal.FreeHGlobal (buffer);
206 private class UriListConverter : IDataConverter {
208 public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
210 string text = dnd.GetText (ref xevent, false);
214 // TODO: Do this in a loop instead of just splitting
215 ArrayList uri_list = new ArrayList ();
216 string [] lines = text.Split (new char [] { '\r', '\n' });
217 foreach (string line in lines) {
218 // # is a comment line (see RFC 2483)
219 if (line.StartsWith ("#"))
222 Uri uri = new Uri (line);
223 uri_list.Add (uri.LocalPath);
227 string [] l = (string []) uri_list.ToArray (typeof (string));
230 data.SetData (DataFormats.FileDrop, l);
231 data.SetData ("FileName", l [0]);
232 data.SetData ("FileNameW", l [0]);
235 public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
237 string [] uri_list = data as string [];
239 if (uri_list == null)
242 StringBuilder res = new StringBuilder ();
243 foreach (string uri_str in uri_list) {
244 Uri uri = new Uri (uri_str);
245 res.Append (uri.ToString ());
249 IntPtr buffer = Marshal.StringToHGlobalAnsi ((string) data);
251 while (Marshal.ReadByte (buffer, len) != 0)
254 dnd.SetProperty (ref xevent, buffer, len);
258 private class DragData {
259 public IntPtr Window;
260 public DragState State;
262 public IntPtr Action;
263 public IntPtr [] SupportedTypes;
264 public MouseButtons MouseState;
266 public IntPtr LastWindow;
267 public IntPtr LastTopLevel;
269 public bool WillAccept;
273 State = DragState.None;
275 SupportedTypes = null;
280 private class DropData {
284 // This version seems to be the most common
285 private static readonly IntPtr [] XdndVersion = new IntPtr [] { new IntPtr (4) };
287 private IntPtr display;
288 private DragData drag_data;
290 private IntPtr XdndAware;
291 private IntPtr XdndSelection;
292 private IntPtr XdndEnter;
293 private IntPtr XdndLeave;
294 private IntPtr XdndPosition;
295 private IntPtr XdndDrop;
296 private IntPtr XdndFinished;
297 private IntPtr XdndStatus;
298 private IntPtr XdndTypeList;
299 private IntPtr XdndActionCopy;
300 private IntPtr XdndActionMove;
301 private IntPtr XdndActionLink;
302 //private IntPtr XdndActionPrivate;
303 //private IntPtr XdndActionList;
304 //private IntPtr XdndActionDescription;
305 //private IntPtr XdndActionAsk;
307 //private State state;
309 private int converts_pending;
310 private bool position_recieved;
311 private bool status_sent;
312 private IntPtr target;
313 private IntPtr source;
314 private IntPtr toplevel;
315 private DataObject data;
317 private Control control;
318 private int pos_x, pos_y;
319 private DragDropEffects allowed;
320 private DragEventArgs drag_event;
322 private Cursor CursorNo;
323 private Cursor CursorCopy;
324 private Cursor CursorMove;
325 private Cursor CursorLink;
326 // check out the TODO below
327 //private IntPtr CurrentCursorHandle;
329 public X11Dnd (IntPtr display)
331 this.display = display;
337 if (drag_data == null)
339 return drag_data.State != DragState.None;
342 public void SetAllowDrop (Hwnd hwnd, bool allow)
346 if (hwnd.allow_drop == allow)
349 atoms = new int[XdndVersion.Length];
350 for (int i = 0; i < XdndVersion.Length; i++) {
351 atoms[i] = XdndVersion[i].ToInt32();
354 XplatUIX11.XChangeProperty (display, hwnd.whole_window, XdndAware,
355 (IntPtr) Atom.XA_ATOM, 32,
356 PropertyMode.Replace, atoms, allow ? 1 : 0);
357 hwnd.allow_drop = allow;
360 public DragDropEffects StartDrag (IntPtr handle, object data,
361 DragDropEffects allowed_effects)
363 drag_data = new DragData ();
364 drag_data.Window = handle;
365 drag_data.State = DragState.Beginning;
366 drag_data.MouseState = XplatUIX11.MouseState;
367 drag_data.Data = data;
368 drag_data.SupportedTypes = DetermineSupportedTypes (data);
370 drag_data.Action = ActionFromEffect (allowed_effects);
372 if (CursorNo == null) {
373 // Make sure the cursors are created
374 CursorNo = new Cursor (typeof (X11Dnd), "DnDNo.cur");
375 CursorCopy = new Cursor (typeof (X11Dnd), "DnDCopy.cur");
376 CursorMove = new Cursor (typeof (X11Dnd), "DnDMove.cur");
377 CursorLink = new Cursor (typeof (X11Dnd), "DnDLink.cur");
380 drag_data.LastTopLevel = IntPtr.Zero;
381 return DragDropEffects.Copy;
384 public void HandleButtonRelease (ref XEvent xevent)
386 if (drag_data == null)
389 if (!((drag_data.MouseState == MouseButtons.Left &&
390 xevent.ButtonEvent.button == 1) ||
391 (drag_data.MouseState == MouseButtons.Right &&
392 xevent.ButtonEvent.button == 3)))
395 if (drag_data.State == DragState.Beginning) {
396 //state = State.Accepting;
397 } else if (drag_data.State != DragState.None) {
399 if (drag_data.WillAccept) {
400 SendDrop (drag_data.LastTopLevel, xevent.AnyEvent.window,
401 xevent.ButtonEvent.time);
404 XplatUIX11.XUngrabPointer (display, IntPtr.Zero);
405 drag_data.State = DragState.None;
406 // WE can't reset the drag data yet as it is still
407 // most likely going to be used by the SelectionRequest
412 public bool HandleMotionNotify (ref XEvent xevent)
414 if (drag_data == null)
417 if (drag_data.State == DragState.Beginning) {
420 drag_data.State = DragState.Dragging;
422 suc = XplatUIX11.XSetSelectionOwner (display, XdndSelection,
424 xevent.ButtonEvent.time);
427 Console.Error.WriteLine ("Could not take ownership of XdndSelection aborting drag.");
432 suc = XplatUIX11.XGrabPointer (display, xevent.AnyEvent.window,
434 EventMask.ButtonMotionMask |
435 EventMask.PointerMotionMask |
436 EventMask.ButtonPressMask |
437 EventMask.ButtonReleaseMask,
438 GrabMode.GrabModeAsync,
439 GrabMode.GrabModeAsync,
440 IntPtr.Zero, IntPtr.Zero/*CursorCopy.Handle*/, IntPtr.Zero);
443 Console.Error.WriteLine ("Could not grab pointer aborting drag.");
448 drag_data.State = DragState.Dragging;
449 } else if (drag_data.State != DragState.None) {
450 bool dnd_aware = false;
451 IntPtr toplevel = IntPtr.Zero;
452 IntPtr window = XplatUIX11.RootWindowHandle;
458 while (XplatUIX11.XQueryPointer (display,
461 out x_temp, out y_temp,
462 out xevent.MotionEvent.x,
463 out xevent.MotionEvent.y,
467 dnd_aware = IsWindowDndAware (window);
470 xevent.MotionEvent.x_root = x_temp;
471 xevent.MotionEvent.y_root = y_temp;
475 if (child == IntPtr.Zero)
481 if (window != drag_data.LastWindow && drag_data.State == DragState.Entered) {
482 drag_data.State = DragState.Dragging;
484 // TODO: Send a Leave if this is an MWF window
486 if (toplevel != drag_data.LastTopLevel)
487 SendLeave (drag_data.LastTopLevel, xevent.MotionEvent.window);
490 drag_data.State = DragState.Entered;
491 if (toplevel != drag_data.LastTopLevel) {
492 // Entering a new toplevel window
493 SendEnter (toplevel, drag_data.Window, drag_data.SupportedTypes);
495 // Already in a toplevel window, so send a position
496 SendPosition (toplevel, drag_data.Window,
498 xevent.MotionEvent.x_root,
499 xevent.MotionEvent.y_root,
500 xevent.MotionEvent.time);
503 drag_data.LastTopLevel = toplevel;
504 drag_data.LastWindow = window;
510 // return true if the event is handled here
511 public bool HandleClientMessage (ref XEvent xevent)
513 // most common so we check it first
514 if (xevent.ClientMessageEvent.message_type == XdndPosition)
515 return Accepting_HandlePositionEvent (ref xevent);
516 if (xevent.ClientMessageEvent.message_type == XdndEnter)
517 return Accepting_HandleEnterEvent (ref xevent);
518 if (xevent.ClientMessageEvent.message_type == XdndDrop)
519 return Accepting_HandleDropEvent (ref xevent);
520 if (xevent.ClientMessageEvent.message_type == XdndLeave)
521 return Accepting_HandleLeaveEvent (ref xevent);
522 if (xevent.ClientMessageEvent.message_type == XdndStatus)
523 return HandleStatusEvent (ref xevent);
528 public bool HandleSelectionNotifyEvent (ref XEvent xevent)
530 if (source != XplatUIX11.XGetSelectionOwner (display, XdndSelection))
533 MimeHandler handler = FindHandler ((IntPtr) xevent.SelectionEvent.target);
537 data = new DataObject ();
539 handler.Converter.GetData (this, data, ref xevent);
542 if (converts_pending <= 0 && position_recieved) {
543 drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
544 allowed, DragDropEffects.None);
545 control.DndEnter (drag_event);
546 SendStatus (source, drag_event.Effect);
552 public bool HandleSelectionRequestEvent (ref XEvent xevent)
554 if (xevent.SelectionRequestEvent.selection != XdndSelection)
557 MimeHandler handler = FindHandler (xevent.SelectionRequestEvent.target);
561 handler.Converter.SetData (this, drag_data.Data, ref xevent);
566 private void SetProperty (ref XEvent xevent, IntPtr data, int length)
568 XEvent sel = new XEvent();
569 sel.SelectionEvent.type = XEventName.SelectionNotify;
570 sel.SelectionEvent.send_event = true;
571 sel.SelectionEvent.display = display;
572 sel.SelectionEvent.selection = xevent.SelectionRequestEvent.selection;
573 sel.SelectionEvent.target = xevent.SelectionRequestEvent.target;
574 sel.SelectionEvent.requestor = xevent.SelectionRequestEvent.requestor;
575 sel.SelectionEvent.time = xevent.SelectionRequestEvent.time;
576 sel.SelectionEvent.property = IntPtr.Zero;
578 XplatUIX11.XChangeProperty (display, xevent.SelectionRequestEvent.requestor,
579 xevent.SelectionRequestEvent.property,
580 xevent.SelectionRequestEvent.target,
581 8, PropertyMode.Replace, data, length);
582 sel.SelectionEvent.property = xevent.SelectionRequestEvent.property;
584 XplatUIX11.XSendEvent (display, xevent.SelectionRequestEvent.requestor, false,
585 (IntPtr)EventMask.NoEventMask, ref sel);
589 private void Reset ()
595 private void ResetSourceData ()
597 converts_pending = 0;
601 private void ResetTargetData ()
603 position_recieved = false;
607 private bool Accepting_HandleEnterEvent (ref XEvent xevent)
611 source = xevent.ClientMessageEvent.ptr1;
612 toplevel = xevent.AnyEvent.window;
613 target = IntPtr.Zero;
615 ConvertData (ref xevent);
620 private bool Accepting_HandlePositionEvent (ref XEvent xevent)
622 pos_x = (int) xevent.ClientMessageEvent.ptr3 >> 16;
623 pos_y = (int) xevent.ClientMessageEvent.ptr3 & 0xFFFF;
625 // Copy is implicitly allowed
626 allowed = EffectFromAction (xevent.ClientMessageEvent.ptr5) | DragDropEffects.Copy;
628 IntPtr parent, child, new_child, last_drop_child;
629 parent = XplatUIX11.XRootWindow (display, 0);
631 last_drop_child = IntPtr.Zero;
634 new_child = IntPtr.Zero;
636 if (!XplatUIX11.XTranslateCoordinates (display,
637 parent, child, pos_x, pos_y,
638 out xd, out yd, out new_child))
640 if (new_child == IntPtr.Zero)
644 Hwnd h = Hwnd.ObjectFromHandle (child);
645 Control d = Control.FromHandle (h.client_window);
646 if (d != null && d.allow_drop)
647 last_drop_child = child;
650 if (last_drop_child != IntPtr.Zero)
651 child = last_drop_child;
653 if (target != child) {
654 // We have moved into a new control
655 // or into a control for the first time
659 Hwnd hwnd = Hwnd.ObjectFromHandle (target);
660 Control c = Control.FromHandle (hwnd.client_window);
665 SendStatus (source, DragDropEffects.None);
671 position_recieved = true;
673 if (converts_pending > 0)
676 drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
677 allowed, DragDropEffects.None);
678 control.DndEnter (drag_event);
679 SendStatus (source, drag_event.Effect);
682 drag_event.x = pos_x;
683 drag_event.y = pos_y;
684 control.DndOver (drag_event);
685 SendStatus (source, drag_event.Effect);
691 private void Finish ()
693 if (control != null) {
694 if (drag_event == null) {
696 data = new DataObject ();
697 drag_event = new DragEventArgs (data,
699 allowed, DragDropEffects.None);
701 control.DndLeave (drag_event);
706 private bool Accepting_HandleDropEvent (ref XEvent xevent)
708 if (control != null && drag_event != null) {
709 drag_event = new DragEventArgs (data,
711 allowed, DragDropEffects.None);
712 control.DndDrop (drag_event);
718 private bool Accepting_HandleLeaveEvent (ref XEvent xevent)
720 if (control != null && drag_event != null)
721 control.DndLeave (drag_event);
726 private bool HandleStatusEvent (ref XEvent xevent)
728 if (drag_data != null && drag_data.State == DragState.Entered) {
729 drag_data.WillAccept = ((int) xevent.ClientMessageEvent.ptr2 & 0x1) != 0;
730 Cursor cursor = CursorNo;
731 if (drag_data.WillAccept) {
732 // Same order as on MS
733 IntPtr action = xevent.ClientMessageEvent.ptr5;
734 if (action == XdndActionCopy)
736 else if (action == XdndActionLink)
738 else if (action == XdndActionMove)
743 // TODO: Try not to set the cursor so much
744 //if (cursor.Handle != CurrentCursorHandle) {
745 XplatUIX11.XChangeActivePointerGrab (display,
746 EventMask.ButtonMotionMask |
747 EventMask.PointerMotionMask |
748 EventMask.ButtonPressMask |
749 EventMask.ButtonReleaseMask,
750 cursor.Handle, IntPtr.Zero);
751 //CurrentCursorHandle = cursor.Handle;
757 private DragDropEffects EffectFromAction (IntPtr action)
759 DragDropEffects allowed = DragDropEffects.None;
761 if (action == XdndActionCopy)
762 allowed = DragDropEffects.Copy;
763 else if (action == XdndActionMove)
764 allowed |= DragDropEffects.Move;
765 if (action == XdndActionLink)
766 allowed |= DragDropEffects.Link;
770 private IntPtr ActionFromEffect (DragDropEffects effect)
772 IntPtr action = IntPtr.Zero;
774 // We can't OR together actions on XDND so sadly the primary
775 // is the only one shown here
776 if ((effect & DragDropEffects.Copy) != 0)
777 action = XdndActionCopy;
778 else if ((effect & DragDropEffects.Move) != 0)
779 action = XdndActionMove;
780 else if ((effect & DragDropEffects.Link) != 0)
781 action = XdndActionLink;
785 private bool ConvertData (ref XEvent xevent)
789 if (source != XplatUIX11.XGetSelectionOwner (display, XdndSelection)) {
793 Control mwfcontrol = MwfWindow (source);
795 if (mwfcontrol != null && drag_data != null) {
796 DataObject dragged = drag_data.Data as DataObject;
797 if (dragged != null) {
801 data = new DataObject ();
802 SetDataWithFormats (drag_data.Data);
807 foreach (IntPtr atom in SourceSupportedList (ref xevent)) {
808 MimeHandler handler = FindHandler (atom);
811 XplatUIX11.XConvertSelection (display, XdndSelection, handler.Type,
812 handler.NonProtocol, toplevel, IntPtr.Zero /* CurrentTime */);
819 private void SetDataWithFormats (object value)
821 if (value is string) {
822 data.SetData (DataFormats.Text, value);
823 data.SetData (DataFormats.UnicodeText, value);
826 data.SetData (value);
829 private MimeHandler FindHandler (IntPtr atom)
831 if (atom == IntPtr.Zero)
833 foreach (MimeHandler handler in MimeHandlers) {
834 if (handler.Type == atom)
840 private MimeHandler FindHandler (string name)
842 foreach (MimeHandler handler in MimeHandlers) {
843 if (handler.Name == name)
849 private void SendStatus (IntPtr source, DragDropEffects effect)
851 XEvent xevent = new XEvent ();
853 xevent.AnyEvent.type = XEventName.ClientMessage;
854 xevent.AnyEvent.display = display;
855 xevent.ClientMessageEvent.window = source;
856 xevent.ClientMessageEvent.message_type = XdndStatus;
857 xevent.ClientMessageEvent.format = 32;
858 xevent.ClientMessageEvent.ptr1 = toplevel;
859 if (effect != DragDropEffects.None)
860 xevent.ClientMessageEvent.ptr2 = (IntPtr) 1;
862 xevent.ClientMessageEvent.ptr5 = ActionFromEffect (effect);
863 XplatUIX11.XSendEvent (display, source, false, IntPtr.Zero, ref xevent);
866 private void SendEnter (IntPtr handle, IntPtr from, IntPtr [] supported)
868 XEvent xevent = new XEvent ();
870 xevent.AnyEvent.type = XEventName.ClientMessage;
871 xevent.AnyEvent.display = display;
872 xevent.ClientMessageEvent.window = handle;
873 xevent.ClientMessageEvent.message_type = XdndEnter;
874 xevent.ClientMessageEvent.format = 32;
875 xevent.ClientMessageEvent.ptr1 = from;
877 // (int) xevent.ClientMessageEvent.ptr2 & 0x1)
879 // xevent.ClientMessageEvent.ptr2 = (IntPtr) ptr2;
880 // (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~(0xFF << 24)) | ((v) << 24)
881 xevent.ClientMessageEvent.ptr2 = (IntPtr) ((long)XdndVersion [0] << 24);
883 if (supported.Length > 0)
884 xevent.ClientMessageEvent.ptr3 = supported [0];
885 if (supported.Length > 1)
886 xevent.ClientMessageEvent.ptr4 = supported [1];
887 if (supported.Length > 2)
888 xevent.ClientMessageEvent.ptr5 = supported [2];
890 XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
893 private void SendDrop (IntPtr handle, IntPtr from, IntPtr time)
895 XEvent xevent = new XEvent ();
897 xevent.AnyEvent.type = XEventName.ClientMessage;
898 xevent.AnyEvent.display = display;
899 xevent.ClientMessageEvent.window = handle;
900 xevent.ClientMessageEvent.message_type = XdndDrop;
901 xevent.ClientMessageEvent.format = 32;
902 xevent.ClientMessageEvent.ptr1 = from;
903 xevent.ClientMessageEvent.ptr3 = time;
905 XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
908 private void SendPosition (IntPtr handle, IntPtr from, IntPtr action, int x, int y, IntPtr time)
910 XEvent xevent = new XEvent ();
912 xevent.AnyEvent.type = XEventName.ClientMessage;
913 xevent.AnyEvent.display = display;
914 xevent.ClientMessageEvent.window = handle;
915 xevent.ClientMessageEvent.message_type = XdndPosition;
916 xevent.ClientMessageEvent.format = 32;
917 xevent.ClientMessageEvent.ptr1 = from;
918 xevent.ClientMessageEvent.ptr3 = (IntPtr) ((x << 16) | (y & 0xFFFF));
919 xevent.ClientMessageEvent.ptr4 = time;
920 xevent.ClientMessageEvent.ptr5 = action;
922 XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
925 private void SendLeave (IntPtr handle, IntPtr from)
927 XEvent xevent = new XEvent ();
929 xevent.AnyEvent.type = XEventName.ClientMessage;
930 xevent.AnyEvent.display = display;
931 xevent.ClientMessageEvent.window = handle;
932 xevent.ClientMessageEvent.message_type = XdndLeave;
933 xevent.ClientMessageEvent.format = 32;
934 xevent.ClientMessageEvent.ptr1 = from;
936 XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
939 private void SendFinished ()
941 XEvent xevent = new XEvent ();
943 xevent.AnyEvent.type = XEventName.ClientMessage;
944 xevent.AnyEvent.display = display;
945 xevent.ClientMessageEvent.window = source;
946 xevent.ClientMessageEvent.message_type = XdndFinished;
947 xevent.ClientMessageEvent.format = 32;
948 xevent.ClientMessageEvent.ptr1 = toplevel;
950 XplatUIX11.XSendEvent (display, source, false, IntPtr.Zero, ref xevent);
953 // There is a somewhat decent amount of overhead
954 // involved in setting up dnd so we do it lazily
955 // as a lot of applications do not even use it.
958 XdndAware = XplatUIX11.XInternAtom (display, "XdndAware", false);
959 XdndEnter = XplatUIX11.XInternAtom (display, "XdndEnter", false);
960 XdndLeave = XplatUIX11.XInternAtom (display, "XdndLeave", false);
961 XdndPosition = XplatUIX11.XInternAtom (display, "XdndPosition", false);
962 XdndStatus = XplatUIX11.XInternAtom (display, "XdndStatus", false);
963 XdndDrop = XplatUIX11.XInternAtom (display, "XdndDrop", false);
964 XdndSelection = XplatUIX11.XInternAtom (display, "XdndSelection", false);
965 XdndFinished = XplatUIX11.XInternAtom (display, "XdndFinished", false);
966 XdndTypeList = XplatUIX11.XInternAtom (display, "XdndTypeList", false);
967 XdndActionCopy = XplatUIX11.XInternAtom (display, "XdndActionCopy", false);
968 XdndActionMove = XplatUIX11.XInternAtom (display, "XdndActionMove", false);
969 XdndActionLink = XplatUIX11.XInternAtom (display, "XdndActionLink", false);
970 //XdndActionPrivate = XplatUIX11.XInternAtom (display, "XdndActionPrivate", false);
971 //XdndActionList = XplatUIX11.XInternAtom (display, "XdndActionList", false);
972 //XdndActionDescription = XplatUIX11.XInternAtom (display, "XdndActionDescription", false);
973 //XdndActionAsk = XplatUIX11.XInternAtom (display, "XdndActionAsk", false);
975 foreach (MimeHandler handler in MimeHandlers) {
976 handler.Type = XplatUIX11.XInternAtom (display, handler.Name, false);
977 handler.NonProtocol = XplatUIX11.XInternAtom (display,
978 String.Concat ("MWFNonP+", handler.Name), false);
983 private IntPtr [] SourceSupportedList (ref XEvent xevent)
988 if (((int) xevent.ClientMessageEvent.ptr2 & 0x1) == 0) {
989 res = new IntPtr [3];
990 res [0] = xevent.ClientMessageEvent.ptr3;
991 res [1] = xevent.ClientMessageEvent.ptr4;
992 res [2] = xevent.ClientMessageEvent.ptr5;
998 IntPtr data = IntPtr.Zero;
1000 XplatUIX11.XGetWindowProperty (display, source, XdndTypeList,
1001 IntPtr.Zero, new IntPtr(32), false, (IntPtr) Atom.XA_ATOM,
1002 out type, out format, out count,
1003 out remaining, ref data);
1005 res = new IntPtr [count.ToInt32()];
1006 for (int i = 0; i < count.ToInt32(); i++) {
1007 res [i] = (IntPtr) Marshal.ReadInt32 (data, i *
1008 Marshal.SizeOf (typeof (int)));
1011 XplatUIX11.XFree (data);
1017 private string GetText (ref XEvent xevent, bool unicode)
1023 StringBuilder builder = new StringBuilder ();
1027 IntPtr data = IntPtr.Zero;
1029 if (0 != XplatUIX11.XGetWindowProperty (display,
1030 xevent.AnyEvent.window,
1031 (IntPtr) xevent.SelectionEvent.property,
1032 IntPtr.Zero, new IntPtr(0xffffff), false,
1033 (IntPtr) Atom.AnyPropertyType, out actual_type,
1034 out actual_fmt, out nitems, out bytes_after,
1036 XplatUIX11.XFree (data);
1041 builder.Append (Marshal.PtrToStringUni (data));
1043 builder.Append (Marshal.PtrToStringAnsi (data));
1044 nread += nitems.ToInt32();
1046 XplatUIX11.XFree (data);
1047 } while (bytes_after.ToInt32() > 0);
1050 return builder.ToString ();
1053 private MemoryStream GetData (ref XEvent xevent)
1059 MemoryStream res = new MemoryStream ();
1063 IntPtr data = IntPtr.Zero;
1065 if (0 != XplatUIX11.XGetWindowProperty (display,
1066 xevent.AnyEvent.window,
1067 (IntPtr) xevent.SelectionEvent.property,
1068 IntPtr.Zero, new IntPtr(0xffffff), false,
1069 (IntPtr) Atom.AnyPropertyType, out actual_type,
1070 out actual_fmt, out nitems, out bytes_after,
1072 XplatUIX11.XFree (data);
1076 for (int i = 0; i < nitems.ToInt32(); i++)
1077 res.WriteByte (Marshal.ReadByte (data, i));
1078 nread += nitems.ToInt32();
1080 XplatUIX11.XFree (data);
1081 } while (bytes_after.ToInt32() > 0);
1085 private Control MwfWindow (IntPtr window)
1087 Hwnd hwnd = Hwnd.ObjectFromHandle (window);
1091 Control res = Control.FromHandle (hwnd.client_window);
1095 private bool IsWindowDndAware (IntPtr handle)
1098 // Check the version number, we need greater than 3
1103 IntPtr data = IntPtr.Zero;
1105 XplatUIX11.XGetWindowProperty (display, handle, XdndAware, IntPtr.Zero, new IntPtr(0x8000000), false,
1106 (IntPtr) Atom.XA_ATOM, out actual, out format,
1107 out count, out remaining, ref data);
1109 if (actual != (IntPtr) Atom.XA_ATOM || format != 32 ||
1110 count.ToInt32() == 0 || data == IntPtr.Zero) {
1111 if (data != IntPtr.Zero)
1112 XplatUIX11.XFree (data);
1116 int version = Marshal.ReadInt32 (data, 0);
1119 Console.Error.WriteLine ("XDND Version too old (" + version + ").");
1120 XplatUIX11.XFree (data);
1124 // First type is actually the XDND version
1125 if (count.ToInt32() > 1) {
1127 for (int i = 1; i < count.ToInt32(); i++) {
1128 IntPtr type = (IntPtr) Marshal.ReadInt32 (data, i *
1129 Marshal.SizeOf (typeof (int)));
1130 for (int j = 0; j < drag_data.SupportedTypes.Length; j++) {
1131 if (drag_data.SupportedTypes [j] == type) {
1139 XplatUIX11.XFree (data);
1143 private IntPtr [] DetermineSupportedTypes (object data)
1145 ArrayList res = new ArrayList ();
1147 if (data is string) {
1148 MimeHandler handler = FindHandler ("text/plain");
1149 if (handler != null)
1150 res.Add (handler.Type);
1151 }/* else if (data is Bitmap)
1156 if (data is IDataObject) {
1161 if (data is ISerializable) {
1162 MimeHandler handler = FindHandler ("application/x-mono-serialized-object");
1163 if (handler != null)
1164 res.Add (handler.Type);
1167 return (IntPtr []) res.ToArray (typeof (IntPtr));