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
, IDataObject data
, ref XEvent xevent
);
55 void SetData (X11Dnd dnd
, object data
, ref XEvent xevent
);
58 private delegate void MimeConverter (IntPtr dsp
,
59 IDataObject data
, ref XEvent xevent
);
61 private class MimeHandler
{
63 public string [] Aliases
;
65 public IntPtr NonProtocol
;
66 public IDataConverter Converter
;
68 public MimeHandler (string name
, IDataConverter converter
) : this (name
, converter
, name
)
72 public MimeHandler (string name
, IDataConverter converter
, params string [] aliases
)
75 Converter
= converter
;
79 public override string ToString ()
81 return "MimeHandler {" + Name + "}";
85 private MimeHandler
[] MimeHandlers
= {
86 // new MimeHandler ("WCF_DIB"),
87 // new MimeHandler ("image/gif", new MimeConverter (ImageConverter)),
88 // new MimeHandler ("text/rtf", new MimeConverter (RtfConverter)),
89 // new MimeHandler ("text/richtext", new MimeConverter (RtfConverter)),
91 new MimeHandler ("text/plain", new TextConverter ()),
92 new MimeHandler ("text/plain", new TextConverter (), "System.String", DataFormats
.Text
),
93 new MimeHandler ("text/html", new HtmlConverter (), DataFormats
.Html
),
94 new MimeHandler ("text/uri-list", new UriListConverter (), DataFormats
.FileDrop
),
95 new MimeHandler ("application/x-mono-serialized-object",
96 new SerializedObjectConverter ())
99 private class SerializedObjectConverter
: IDataConverter
{
101 public void GetData (X11Dnd dnd
, IDataObject data
, ref XEvent xevent
)
103 MemoryStream stream
= dnd
.GetData (ref xevent
);
104 BinaryFormatter bf
= new BinaryFormatter ();
106 if (stream
.Length
== 0)
110 object obj
= bf
.Deserialize (stream
);
114 public void SetData (X11Dnd dnd
, object data
, ref XEvent xevent
)
119 MemoryStream stream
= new MemoryStream ();
120 BinaryFormatter bf
= new BinaryFormatter ();
122 bf
.Serialize (stream
, data
);
124 IntPtr buffer
= Marshal
.AllocHGlobal ((int) stream
.Length
);
127 for (int i
= 0; i
< stream
.Length
; i
++) {
128 Marshal
.WriteByte (buffer
, i
, (byte) stream
.ReadByte ());
131 dnd
.SetProperty (ref xevent
, buffer
, (int) stream
.Length
);
135 private class HtmlConverter
: IDataConverter
{
137 public void GetData (X11Dnd dnd
, IDataObject data
, ref XEvent xevent
)
139 string text
= dnd
.GetText (ref xevent
, false);
142 data
.SetData (DataFormats
.Text
, text
);
143 data
.SetData (DataFormats
.UnicodeText
, text
);
146 public void SetData (X11Dnd dnd
, object data
, ref XEvent xevent
)
150 string str
= data
as string;
155 if (xevent
.SelectionRequestEvent
.target
== (IntPtr
)Atom
.XA_STRING
) {
156 byte [] bytes
= Encoding
.ASCII
.GetBytes (str
);
157 buffer
= Marshal
.AllocHGlobal (bytes
.Length
);
159 for (int i
= 0; i
< len
; i
++)
160 Marshal
.WriteByte (buffer
, i
, bytes
[i
]);
162 buffer
= Marshal
.StringToHGlobalAnsi (str
);
164 while (Marshal
.ReadByte (buffer
, len
) != 0)
168 dnd
.SetProperty (ref xevent
, buffer
, len
);
170 Marshal
.FreeHGlobal (buffer
);
174 private class TextConverter
: IDataConverter
{
176 public void GetData (X11Dnd dnd
, IDataObject data
, ref XEvent xevent
)
178 string text
= dnd
.GetText (ref xevent
, true);
181 data
.SetData (DataFormats
.Text
, text
);
182 data
.SetData (DataFormats
.UnicodeText
, text
);
185 public void SetData (X11Dnd dnd
, object data
, ref XEvent xevent
)
189 string str
= data
as string;
192 IDataObject dobj
= data
as IDataObject
;
195 str
= (string) dobj
.GetData ("System.String", true);
198 if (xevent
.SelectionRequestEvent
.target
== (IntPtr
)Atom
.XA_STRING
) {
199 byte [] bytes
= Encoding
.ASCII
.GetBytes (str
);
200 buffer
= Marshal
.AllocHGlobal (bytes
.Length
);
202 for (int i
= 0; i
< len
; i
++)
203 Marshal
.WriteByte (buffer
, i
, bytes
[i
]);
205 buffer
= Marshal
.StringToHGlobalAnsi (str
);
207 while (Marshal
.ReadByte (buffer
, len
) != 0)
211 dnd
.SetProperty (ref xevent
, buffer
, len
);
213 Marshal
.FreeHGlobal (buffer
);
217 private class UriListConverter
: IDataConverter
{
219 public void GetData (X11Dnd dnd
, IDataObject data
, ref XEvent xevent
)
221 string text
= dnd
.GetText (ref xevent
, false);
225 // TODO: Do this in a loop instead of just splitting
226 ArrayList uri_list
= new ArrayList ();
227 string [] lines
= text
.Split (new char [] { '\r', '\n' }
);
228 foreach (string line
in lines
) {
229 // # is a comment line (see RFC 2483)
230 if (line
.StartsWith ("#"))
233 Uri uri
= new Uri (line
);
234 uri_list
.Add (uri
.LocalPath
);
238 string [] l
= (string []) uri_list
.ToArray (typeof (string));
241 data
.SetData (DataFormats
.FileDrop
, l
);
242 data
.SetData ("FileName", l
[0]);
243 data
.SetData ("FileNameW", l
[0]);
246 public void SetData (X11Dnd dnd
, object data
, ref XEvent xevent
)
248 string [] uri_list
= data
as string [];
250 if (uri_list
== null) {
251 IDataObject dobj
= data
as IDataObject
;
254 uri_list
= dobj
.GetData (DataFormats
.FileDrop
, true) as string [];
257 if (uri_list
== null)
260 StringBuilder res
= new StringBuilder ();
261 foreach (string uri_str
in uri_list
) {
262 Uri uri
= new Uri (uri_str
);
263 res
.Append (uri
.ToString ());
267 IntPtr buffer
= Marshal
.StringToHGlobalAnsi ((string) res
.ToString ());
269 while (Marshal
.ReadByte (buffer
, len
) != 0)
272 dnd
.SetProperty (ref xevent
, buffer
, len
);
276 private class DragData
{
277 public IntPtr Window
;
278 public DragState State
;
280 public IntPtr Action
;
281 public IntPtr
[] SupportedTypes
;
282 public MouseButtons MouseState
;
284 public IntPtr LastWindow
;
285 public IntPtr LastTopLevel
;
287 public bool WillAccept
;
291 State
= DragState
.None
;
293 SupportedTypes
= null;
298 // This version seems to be the most common
299 private static readonly IntPtr
[] XdndVersion
= new IntPtr
[] { new IntPtr (4) }
;
301 private IntPtr display
;
302 private DragData drag_data
;
304 private IntPtr XdndAware
;
305 private IntPtr XdndSelection
;
306 private IntPtr XdndEnter
;
307 private IntPtr XdndLeave
;
308 private IntPtr XdndPosition
;
309 private IntPtr XdndDrop
;
310 private IntPtr XdndFinished
;
311 private IntPtr XdndStatus
;
312 private IntPtr XdndTypeList
;
313 private IntPtr XdndActionCopy
;
314 private IntPtr XdndActionMove
;
315 private IntPtr XdndActionLink
;
316 //private IntPtr XdndActionPrivate;
317 //private IntPtr XdndActionList;
318 //private IntPtr XdndActionDescription;
319 //private IntPtr XdndActionAsk;
321 //private State state;
323 private int converts_pending
;
324 private bool position_recieved
;
325 private bool status_sent
;
326 private IntPtr target
;
327 private IntPtr source
;
328 private IntPtr toplevel
;
329 private IDataObject data
;
331 private Control control
;
332 private int pos_x
, pos_y
;
333 private DragDropEffects allowed
;
334 private DragEventArgs drag_event
;
336 private Cursor CursorNo
;
337 private Cursor CursorCopy
;
338 private Cursor CursorMove
;
339 private Cursor CursorLink
;
340 // check out the TODO below
341 //private IntPtr CurrentCursorHandle;
343 private X11Keyboard keyboard
;
345 public X11Dnd (IntPtr display
, X11Keyboard keyboard
)
347 this.display
= display
;
348 this.keyboard
= keyboard
;
355 if (drag_data
== null)
357 return drag_data
.State
!= DragState
.None
;
360 public void SetAllowDrop (Hwnd hwnd
, bool allow
)
364 if (hwnd
.allow_drop
== allow
)
367 atoms
= new int[XdndVersion
.Length
];
368 for (int i
= 0; i
< XdndVersion
.Length
; i
++) {
369 atoms
[i
] = XdndVersion
[i
].ToInt32();
372 XplatUIX11
.XChangeProperty (display
, hwnd
.whole_window
, XdndAware
,
373 (IntPtr
) Atom
.XA_ATOM
, 32,
374 PropertyMode
.Replace
, atoms
, allow
? 1 : 0);
375 hwnd
.allow_drop
= allow
;
378 public DragDropEffects
StartDrag (IntPtr handle
, object data
,
379 DragDropEffects allowed_effects
)
381 drag_data
= new DragData ();
382 drag_data
.Window
= handle
;
383 drag_data
.State
= DragState
.Beginning
;
384 drag_data
.MouseState
= XplatUIX11
.MouseState
;
385 drag_data
.Data
= data
;
386 drag_data
.SupportedTypes
= DetermineSupportedTypes (data
);
388 drag_data
.Action
= ActionFromEffect (allowed_effects
);
390 if (CursorNo
== null) {
391 // Make sure the cursors are created
392 CursorNo
= new Cursor (typeof (X11Dnd
), "DnDNo.cur");
393 CursorCopy
= new Cursor (typeof (X11Dnd
), "DnDCopy.cur");
394 CursorMove
= new Cursor (typeof (X11Dnd
), "DnDMove.cur");
395 CursorLink
= new Cursor (typeof (X11Dnd
), "DnDLink.cur");
398 drag_data
.LastTopLevel
= IntPtr
.Zero
;
399 return DragDropEffects
.Copy
;
402 public bool HandleButtonRelease (ref XEvent xevent
)
404 if (drag_data
== null)
407 if (!((drag_data
.MouseState
== MouseButtons
.Left
&&
408 xevent
.ButtonEvent
.button
== 1) ||
409 (drag_data
.MouseState
== MouseButtons
.Right
&&
410 xevent
.ButtonEvent
.button
== 3)))
413 if (drag_data
.State
== DragState
.Beginning
) {
414 //state = State.Accepting;
415 } else if (drag_data
.State
!= DragState
.None
) {
417 if (drag_data
.WillAccept
) {
419 if (QueryContinue (xevent
, false, DragAction
.Drop
))
424 drag_data
.State
= DragState
.None
;
425 // WE can't reset the drag data yet as it is still
426 // most likely going to be used by the SelectionRequest
433 public bool HandleMotionNotify (ref XEvent xevent
)
435 if (drag_data
== null)
438 if (drag_data
.State
== DragState
.Beginning
) {
441 drag_data
.State
= DragState
.Dragging
;
443 suc
= XplatUIX11
.XSetSelectionOwner (display
, XdndSelection
,
445 xevent
.ButtonEvent
.time
);
448 Console
.Error
.WriteLine ("Could not take ownership of XdndSelection aborting drag.");
453 drag_data
.State
= DragState
.Dragging
;
454 } else if (drag_data
.State
!= DragState
.None
) {
455 bool dnd_aware
= false;
456 IntPtr toplevel
= IntPtr
.Zero
;
457 IntPtr window
= XplatUIX11
.RootWindowHandle
;
463 while (XplatUIX11
.XQueryPointer (display
,
466 out x_temp
, out y_temp
,
467 out xevent
.MotionEvent
.x
,
468 out xevent
.MotionEvent
.y
,
472 dnd_aware
= IsWindowDndAware (window
);
475 xevent
.MotionEvent
.x_root
= x_temp
;
476 xevent
.MotionEvent
.y_root
= y_temp
;
480 if (child
== IntPtr
.Zero
)
486 if (window
!= drag_data
.LastWindow
&& drag_data
.State
== DragState
.Entered
) {
487 drag_data
.State
= DragState
.Dragging
;
489 // TODO: Send a Leave if this is an MWF window
491 if (toplevel
!= drag_data
.LastTopLevel
)
492 SendLeave (drag_data
.LastTopLevel
, xevent
.MotionEvent
.window
);
495 drag_data
.State
= DragState
.Entered
;
496 if (toplevel
!= drag_data
.LastTopLevel
) {
497 // Entering a new toplevel window
498 SendEnter (toplevel
, drag_data
.Window
, drag_data
.SupportedTypes
);
500 // Already in a toplevel window, so send a position
501 SendPosition (toplevel
, drag_data
.Window
,
503 xevent
.MotionEvent
.x_root
,
504 xevent
.MotionEvent
.y_root
,
505 xevent
.MotionEvent
.time
);
508 drag_data
.LastTopLevel
= toplevel
;
509 drag_data
.LastWindow
= window
;
515 public bool HandleKeyPress (ref XEvent xevent
)
517 if (VirtualKeys
.VK_ESCAPE
== (VirtualKeys
) keyboard
.EventToVkey (xevent
)) {
518 if (!QueryContinue (xevent
, true, DragAction
.Cancel
))
525 // return true if the event is handled here
526 public bool HandleClientMessage (ref XEvent xevent
)
528 // most common so we check it first
529 if (xevent
.ClientMessageEvent
.message_type
== XdndPosition
)
530 return Accepting_HandlePositionEvent (ref xevent
);
531 if (xevent
.ClientMessageEvent
.message_type
== XdndEnter
)
532 return Accepting_HandleEnterEvent (ref xevent
);
533 if (xevent
.ClientMessageEvent
.message_type
== XdndDrop
)
534 return Accepting_HandleDropEvent (ref xevent
);
535 if (xevent
.ClientMessageEvent
.message_type
== XdndLeave
)
536 return Accepting_HandleLeaveEvent (ref xevent
);
537 if (xevent
.ClientMessageEvent
.message_type
== XdndStatus
)
538 return HandleStatusEvent (ref xevent
);
543 public bool HandleSelectionNotifyEvent (ref XEvent xevent
)
545 if (source
!= XplatUIX11
.XGetSelectionOwner (display
, XdndSelection
))
548 MimeHandler handler
= FindHandler ((IntPtr
) xevent
.SelectionEvent
.target
);
552 data
= new DataObject ();
554 handler
.Converter
.GetData (this, data
, ref xevent
);
557 if (converts_pending
<= 0 && position_recieved
) {
558 drag_event
= new DragEventArgs (data
, 0, pos_x
, pos_y
,
559 allowed
, DragDropEffects
.None
);
560 control
.DndEnter (drag_event
);
561 SendStatus (source
, drag_event
.Effect
);
567 public bool HandleSelectionRequestEvent (ref XEvent xevent
)
569 if (xevent
.SelectionRequestEvent
.selection
!= XdndSelection
)
572 MimeHandler handler
= FindHandler (xevent
.SelectionRequestEvent
.target
);
576 handler
.Converter
.SetData (this, drag_data
.Data
, ref xevent
);
581 private bool QueryContinue (XEvent xevent
, bool escape
, DragAction action
)
583 QueryContinueDragEventArgs qce
= new QueryContinueDragEventArgs ((int) XplatUI
.State
.ModifierKeys
,
586 Control c
= MwfWindow (source
);
587 c
.DndContinueDrag (qce
);
589 switch (qce
.Action
) {
590 case DragAction
.Continue
:
592 case DragAction
.Drop
:
593 SendDrop (drag_data
.LastTopLevel
, source
, xevent
.ButtonEvent
.time
);
595 case DragAction
.Cancel
:
597 c
.InternalCapture
= false;
604 private void GiveFeedback (IntPtr action
)
606 GiveFeedbackEventArgs gfe
= new GiveFeedbackEventArgs (EffectFromAction (drag_data
.Action
), true);
608 Control c
= MwfWindow (source
);
611 if (gfe
.UseDefaultCursors
) {
612 Cursor cursor
= CursorNo
;
613 if (drag_data
.WillAccept
) {
614 // Same order as on MS
615 if (action
== XdndActionCopy
)
617 else if (action
== XdndActionLink
)
619 else if (action
== XdndActionMove
)
622 // TODO: Try not to set the cursor so much
623 //if (cursor.Handle != CurrentCursorHandle) {
624 XplatUIX11
.XChangeActivePointerGrab (display
,
625 EventMask
.ButtonMotionMask
|
626 EventMask
.PointerMotionMask
|
627 EventMask
.ButtonPressMask
|
628 EventMask
.ButtonReleaseMask
,
629 cursor
.Handle
, IntPtr
.Zero
);
630 //CurrentCursorHandle = cursor.Handle;
635 private void SetProperty (ref XEvent xevent
, IntPtr data
, int length
)
637 XEvent sel
= new XEvent();
638 sel
.SelectionEvent
.type
= XEventName
.SelectionNotify
;
639 sel
.SelectionEvent
.send_event
= true;
640 sel
.SelectionEvent
.display
= display
;
641 sel
.SelectionEvent
.selection
= xevent
.SelectionRequestEvent
.selection
;
642 sel
.SelectionEvent
.target
= xevent
.SelectionRequestEvent
.target
;
643 sel
.SelectionEvent
.requestor
= xevent
.SelectionRequestEvent
.requestor
;
644 sel
.SelectionEvent
.time
= xevent
.SelectionRequestEvent
.time
;
645 sel
.SelectionEvent
.property
= IntPtr
.Zero
;
647 XplatUIX11
.XChangeProperty (display
, xevent
.SelectionRequestEvent
.requestor
,
648 xevent
.SelectionRequestEvent
.property
,
649 xevent
.SelectionRequestEvent
.target
,
650 8, PropertyMode
.Replace
, data
, length
);
651 sel
.SelectionEvent
.property
= xevent
.SelectionRequestEvent
.property
;
653 XplatUIX11
.XSendEvent (display
, xevent
.SelectionRequestEvent
.requestor
, false,
654 (IntPtr
)EventMask
.NoEventMask
, ref sel
);
658 private void Reset ()
664 private void ResetSourceData ()
666 converts_pending
= 0;
670 private void ResetTargetData ()
672 position_recieved
= false;
676 private bool Accepting_HandleEnterEvent (ref XEvent xevent
)
680 source
= xevent
.ClientMessageEvent
.ptr1
;
681 toplevel
= xevent
.AnyEvent
.window
;
682 target
= IntPtr
.Zero
;
684 ConvertData (ref xevent
);
689 private bool Accepting_HandlePositionEvent (ref XEvent xevent
)
691 pos_x
= (int) xevent
.ClientMessageEvent
.ptr3
>> 16;
692 pos_y
= (int) xevent
.ClientMessageEvent
.ptr3
& 0xFFFF;
694 // Copy is implicitly allowed
695 allowed
= EffectFromAction (xevent
.ClientMessageEvent
.ptr5
) | DragDropEffects
.Copy
;
697 IntPtr parent
, child
, new_child
, last_drop_child
;
698 parent
= XplatUIX11
.XRootWindow (display
, 0);
700 last_drop_child
= IntPtr
.Zero
;
703 new_child
= IntPtr
.Zero
;
705 if (!XplatUIX11
.XTranslateCoordinates (display
,
706 parent
, child
, pos_x
, pos_y
,
707 out xd
, out yd
, out new_child
))
709 if (new_child
== IntPtr
.Zero
)
713 Hwnd h
= Hwnd
.ObjectFromHandle (child
);
714 Control d
= Control
.FromHandle (h
.client_window
);
715 if (d
!= null && d
.allow_drop
)
716 last_drop_child
= child
;
719 if (last_drop_child
!= IntPtr
.Zero
)
720 child
= last_drop_child
;
722 if (target
!= child
) {
723 // We have moved into a new control
724 // or into a control for the first time
728 Hwnd hwnd
= Hwnd
.ObjectFromHandle (target
);
729 Control c
= Control
.FromHandle (hwnd
.client_window
);
734 SendStatus (source
, DragDropEffects
.None
);
740 position_recieved
= true;
742 if (converts_pending
> 0)
745 drag_event
= new DragEventArgs (data
, 0, pos_x
, pos_y
,
746 allowed
, DragDropEffects
.None
);
747 control
.DndEnter (drag_event
);
748 SendStatus (source
, drag_event
.Effect
);
751 drag_event
.x
= pos_x
;
752 drag_event
.y
= pos_y
;
753 control
.DndOver (drag_event
);
754 SendStatus (source
, drag_event
.Effect
);
760 private void Finish ()
762 if (control
!= null) {
763 if (drag_event
== null) {
765 data
= new DataObject ();
766 drag_event
= new DragEventArgs (data
,
768 allowed
, DragDropEffects
.None
);
770 control
.DndLeave (drag_event
);
775 private bool Accepting_HandleDropEvent (ref XEvent xevent
)
777 if (control
!= null && drag_event
!= null) {
778 drag_event
= new DragEventArgs (data
,
780 allowed
, DragDropEffects
.None
);
781 control
.DndDrop (drag_event
);
787 private bool Accepting_HandleLeaveEvent (ref XEvent xevent
)
789 if (control
!= null && drag_event
!= null)
790 control
.DndLeave (drag_event
);
795 private bool HandleStatusEvent (ref XEvent xevent
)
797 if (drag_data
!= null && drag_data
.State
== DragState
.Entered
) {
799 if (!QueryContinue (xevent
, false, DragAction
.Continue
))
802 drag_data
.WillAccept
= ((int) xevent
.ClientMessageEvent
.ptr2
& 0x1) != 0;
804 GiveFeedback (xevent
.ClientMessageEvent
.ptr5
);
809 private DragDropEffects
EffectFromAction (IntPtr action
)
811 DragDropEffects allowed
= DragDropEffects
.None
;
813 if (action
== XdndActionCopy
)
814 allowed
= DragDropEffects
.Copy
;
815 else if (action
== XdndActionMove
)
816 allowed
|= DragDropEffects
.Move
;
817 if (action
== XdndActionLink
)
818 allowed
|= DragDropEffects
.Link
;
822 private IntPtr
ActionFromEffect (DragDropEffects effect
)
824 IntPtr action
= IntPtr
.Zero
;
826 // We can't OR together actions on XDND so sadly the primary
827 // is the only one shown here
828 if ((effect
& DragDropEffects
.Copy
) != 0)
829 action
= XdndActionCopy
;
830 else if ((effect
& DragDropEffects
.Move
) != 0)
831 action
= XdndActionMove
;
832 else if ((effect
& DragDropEffects
.Link
) != 0)
833 action
= XdndActionLink
;
837 private bool ConvertData (ref XEvent xevent
)
841 if (source
!= XplatUIX11
.XGetSelectionOwner (display
, XdndSelection
)) {
845 Control mwfcontrol
= MwfWindow (source
);
847 if (mwfcontrol
!= null && drag_data
!= null) {
848 IDataObject dragged
= drag_data
.Data
as IDataObject
;
849 if (dragged
!= null) {
853 data
= new DataObject ();
854 SetDataWithFormats (drag_data
.Data
);
859 foreach (IntPtr atom
in SourceSupportedList (ref xevent
)) {
860 MimeHandler handler
= FindHandler (atom
);
863 XplatUIX11
.XConvertSelection (display
, XdndSelection
, handler
.Type
,
864 handler
.NonProtocol
, toplevel
, IntPtr
.Zero
/* CurrentTime */);
871 private void SetDataWithFormats (object value)
873 if (value is string) {
874 data
.SetData (DataFormats
.Text
, value);
875 data
.SetData (DataFormats
.UnicodeText
, value);
878 data
.SetData (value);
881 private MimeHandler
FindHandler (IntPtr atom
)
883 if (atom
== IntPtr
.Zero
)
885 foreach (MimeHandler handler
in MimeHandlers
) {
886 if (handler
.Type
== atom
)
892 private MimeHandler
FindHandler (string name
)
894 foreach (MimeHandler handler
in MimeHandlers
) {
895 foreach (string alias in handler
.Aliases
) {
903 private void SendStatus (IntPtr source
, DragDropEffects effect
)
905 XEvent xevent
= new XEvent ();
907 xevent
.AnyEvent
.type
= XEventName
.ClientMessage
;
908 xevent
.AnyEvent
.display
= display
;
909 xevent
.ClientMessageEvent
.window
= source
;
910 xevent
.ClientMessageEvent
.message_type
= XdndStatus
;
911 xevent
.ClientMessageEvent
.format
= 32;
912 xevent
.ClientMessageEvent
.ptr1
= toplevel
;
913 if (effect
!= DragDropEffects
.None
)
914 xevent
.ClientMessageEvent
.ptr2
= (IntPtr
) 1;
916 xevent
.ClientMessageEvent
.ptr5
= ActionFromEffect (effect
);
917 XplatUIX11
.XSendEvent (display
, source
, false, IntPtr
.Zero
, ref xevent
);
920 private void SendEnter (IntPtr handle
, IntPtr
from, IntPtr
[] supported
)
922 XEvent xevent
= new XEvent ();
924 xevent
.AnyEvent
.type
= XEventName
.ClientMessage
;
925 xevent
.AnyEvent
.display
= display
;
926 xevent
.ClientMessageEvent
.window
= handle
;
927 xevent
.ClientMessageEvent
.message_type
= XdndEnter
;
928 xevent
.ClientMessageEvent
.format
= 32;
929 xevent
.ClientMessageEvent
.ptr1
= from;
931 // (int) xevent.ClientMessageEvent.ptr2 & 0x1)
933 // xevent.ClientMessageEvent.ptr2 = (IntPtr) ptr2;
934 // (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~(0xFF << 24)) | ((v) << 24)
935 xevent
.ClientMessageEvent
.ptr2
= (IntPtr
) ((long)XdndVersion
[0] << 24);
937 if (supported
.Length
> 0)
938 xevent
.ClientMessageEvent
.ptr3
= supported
[0];
939 if (supported
.Length
> 1)
940 xevent
.ClientMessageEvent
.ptr4
= supported
[1];
941 if (supported
.Length
> 2)
942 xevent
.ClientMessageEvent
.ptr5
= supported
[2];
944 XplatUIX11
.XSendEvent (display
, handle
, false, IntPtr
.Zero
, ref xevent
);
947 private void SendDrop (IntPtr handle
, IntPtr
from, IntPtr time
)
949 XEvent xevent
= new XEvent ();
951 xevent
.AnyEvent
.type
= XEventName
.ClientMessage
;
952 xevent
.AnyEvent
.display
= display
;
953 xevent
.ClientMessageEvent
.window
= handle
;
954 xevent
.ClientMessageEvent
.message_type
= XdndDrop
;
955 xevent
.ClientMessageEvent
.format
= 32;
956 xevent
.ClientMessageEvent
.ptr1
= from;
957 xevent
.ClientMessageEvent
.ptr3
= time
;
959 XplatUIX11
.XSendEvent (display
, handle
, false, IntPtr
.Zero
, ref xevent
);
962 private void SendPosition (IntPtr handle
, IntPtr
from, IntPtr action
, int x
, int y
, IntPtr time
)
964 XEvent xevent
= new XEvent ();
966 xevent
.AnyEvent
.type
= XEventName
.ClientMessage
;
967 xevent
.AnyEvent
.display
= display
;
968 xevent
.ClientMessageEvent
.window
= handle
;
969 xevent
.ClientMessageEvent
.message_type
= XdndPosition
;
970 xevent
.ClientMessageEvent
.format
= 32;
971 xevent
.ClientMessageEvent
.ptr1
= from;
972 xevent
.ClientMessageEvent
.ptr3
= (IntPtr
) ((x
<< 16) | (y
& 0xFFFF));
973 xevent
.ClientMessageEvent
.ptr4
= time
;
974 xevent
.ClientMessageEvent
.ptr5
= action
;
976 XplatUIX11
.XSendEvent (display
, handle
, false, IntPtr
.Zero
, ref xevent
);
979 private void SendLeave (IntPtr handle
, IntPtr
from)
981 XEvent xevent
= new XEvent ();
983 xevent
.AnyEvent
.type
= XEventName
.ClientMessage
;
984 xevent
.AnyEvent
.display
= display
;
985 xevent
.ClientMessageEvent
.window
= handle
;
986 xevent
.ClientMessageEvent
.message_type
= XdndLeave
;
987 xevent
.ClientMessageEvent
.format
= 32;
988 xevent
.ClientMessageEvent
.ptr1
= from;
990 XplatUIX11
.XSendEvent (display
, handle
, false, IntPtr
.Zero
, ref xevent
);
993 private void SendFinished ()
995 XEvent xevent
= new XEvent ();
997 xevent
.AnyEvent
.type
= XEventName
.ClientMessage
;
998 xevent
.AnyEvent
.display
= display
;
999 xevent
.ClientMessageEvent
.window
= source
;
1000 xevent
.ClientMessageEvent
.message_type
= XdndFinished
;
1001 xevent
.ClientMessageEvent
.format
= 32;
1002 xevent
.ClientMessageEvent
.ptr1
= toplevel
;
1004 XplatUIX11
.XSendEvent (display
, source
, false, IntPtr
.Zero
, ref xevent
);
1007 // There is a somewhat decent amount of overhead
1008 // involved in setting up dnd so we do it lazily
1009 // as a lot of applications do not even use it.
1010 private void Init ()
1012 XdndAware
= XplatUIX11
.XInternAtom (display
, "XdndAware", false);
1013 XdndEnter
= XplatUIX11
.XInternAtom (display
, "XdndEnter", false);
1014 XdndLeave
= XplatUIX11
.XInternAtom (display
, "XdndLeave", false);
1015 XdndPosition
= XplatUIX11
.XInternAtom (display
, "XdndPosition", false);
1016 XdndStatus
= XplatUIX11
.XInternAtom (display
, "XdndStatus", false);
1017 XdndDrop
= XplatUIX11
.XInternAtom (display
, "XdndDrop", false);
1018 XdndSelection
= XplatUIX11
.XInternAtom (display
, "XdndSelection", false);
1019 XdndFinished
= XplatUIX11
.XInternAtom (display
, "XdndFinished", false);
1020 XdndTypeList
= XplatUIX11
.XInternAtom (display
, "XdndTypeList", false);
1021 XdndActionCopy
= XplatUIX11
.XInternAtom (display
, "XdndActionCopy", false);
1022 XdndActionMove
= XplatUIX11
.XInternAtom (display
, "XdndActionMove", false);
1023 XdndActionLink
= XplatUIX11
.XInternAtom (display
, "XdndActionLink", false);
1024 //XdndActionPrivate = XplatUIX11.XInternAtom (display, "XdndActionPrivate", false);
1025 //XdndActionList = XplatUIX11.XInternAtom (display, "XdndActionList", false);
1026 //XdndActionDescription = XplatUIX11.XInternAtom (display, "XdndActionDescription", false);
1027 //XdndActionAsk = XplatUIX11.XInternAtom (display, "XdndActionAsk", false);
1029 foreach (MimeHandler handler
in MimeHandlers
) {
1030 handler
.Type
= XplatUIX11
.XInternAtom (display
, handler
.Name
, false);
1031 handler
.NonProtocol
= XplatUIX11
.XInternAtom (display
,
1032 String
.Concat ("MWFNonP+", handler
.Name
), false);
1037 private IntPtr
[] SourceSupportedList (ref XEvent xevent
)
1042 if (((int) xevent
.ClientMessageEvent
.ptr2
& 0x1) == 0) {
1043 res
= new IntPtr
[3];
1044 res
[0] = xevent
.ClientMessageEvent
.ptr3
;
1045 res
[1] = xevent
.ClientMessageEvent
.ptr4
;
1046 res
[2] = xevent
.ClientMessageEvent
.ptr5
;
1052 IntPtr data
= IntPtr
.Zero
;
1054 XplatUIX11
.XGetWindowProperty (display
, source
, XdndTypeList
,
1055 IntPtr
.Zero
, new IntPtr(32), false, (IntPtr
) Atom
.XA_ATOM
,
1056 out type
, out format
, out count
,
1057 out remaining
, ref data
);
1059 res
= new IntPtr
[count
.ToInt32()];
1060 for (int i
= 0; i
< count
.ToInt32(); i
++) {
1061 res
[i
] = (IntPtr
) Marshal
.ReadInt32 (data
, i
*
1062 Marshal
.SizeOf (typeof (int)));
1065 XplatUIX11
.XFree (data
);
1071 private string GetText (ref XEvent xevent
, bool unicode
)
1077 StringBuilder builder
= new StringBuilder ();
1081 IntPtr data
= IntPtr
.Zero
;
1083 if (0 != XplatUIX11
.XGetWindowProperty (display
,
1084 xevent
.AnyEvent
.window
,
1085 (IntPtr
) xevent
.SelectionEvent
.property
,
1086 IntPtr
.Zero
, new IntPtr(0xffffff), false,
1087 (IntPtr
) Atom
.AnyPropertyType
, out actual_type
,
1088 out actual_fmt
, out nitems
, out bytes_after
,
1090 XplatUIX11
.XFree (data
);
1095 builder
.Append (Marshal
.PtrToStringUni (data
));
1097 builder
.Append (Marshal
.PtrToStringAnsi (data
));
1098 nread
+= nitems
.ToInt32();
1100 XplatUIX11
.XFree (data
);
1101 } while (bytes_after
.ToInt32() > 0);
1104 return builder
.ToString ();
1107 private MemoryStream
GetData (ref XEvent xevent
)
1113 MemoryStream res
= new MemoryStream ();
1117 IntPtr data
= IntPtr
.Zero
;
1119 if (0 != XplatUIX11
.XGetWindowProperty (display
,
1120 xevent
.AnyEvent
.window
,
1121 (IntPtr
) xevent
.SelectionEvent
.property
,
1122 IntPtr
.Zero
, new IntPtr(0xffffff), false,
1123 (IntPtr
) Atom
.AnyPropertyType
, out actual_type
,
1124 out actual_fmt
, out nitems
, out bytes_after
,
1126 XplatUIX11
.XFree (data
);
1130 for (int i
= 0; i
< nitems
.ToInt32(); i
++)
1131 res
.WriteByte (Marshal
.ReadByte (data
, i
));
1132 nread
+= nitems
.ToInt32();
1134 XplatUIX11
.XFree (data
);
1135 } while (bytes_after
.ToInt32() > 0);
1139 private Control
MwfWindow (IntPtr window
)
1141 Hwnd hwnd
= Hwnd
.ObjectFromHandle (window
);
1145 Control res
= Control
.FromHandle (hwnd
.client_window
);
1149 private bool IsWindowDndAware (IntPtr handle
)
1152 // Check the version number, we need greater than 3
1157 IntPtr data
= IntPtr
.Zero
;
1159 XplatUIX11
.XGetWindowProperty (display
, handle
, XdndAware
, IntPtr
.Zero
, new IntPtr(0x8000000), false,
1160 (IntPtr
) Atom
.XA_ATOM
, out actual
, out format
,
1161 out count
, out remaining
, ref data
);
1163 if (actual
!= (IntPtr
) Atom
.XA_ATOM
|| format
!= 32 ||
1164 count
.ToInt32() == 0 || data
== IntPtr
.Zero
) {
1165 if (data
!= IntPtr
.Zero
)
1166 XplatUIX11
.XFree (data
);
1170 int version
= Marshal
.ReadInt32 (data
, 0);
1173 Console
.Error
.WriteLine ("XDND Version too old (" + version
+ ").");
1174 XplatUIX11
.XFree (data
);
1178 // First type is actually the XDND version
1179 if (count
.ToInt32() > 1) {
1181 for (int i
= 1; i
< count
.ToInt32(); i
++) {
1182 IntPtr type
= (IntPtr
) Marshal
.ReadInt32 (data
, i
*
1183 Marshal
.SizeOf (typeof (int)));
1184 for (int j
= 0; j
< drag_data
.SupportedTypes
.Length
; j
++) {
1185 if (drag_data
.SupportedTypes
[j
] == type
) {
1193 XplatUIX11
.XFree (data
);
1197 private IntPtr
[] DetermineSupportedTypes (object data
)
1199 ArrayList res
= new ArrayList ();
1201 if (data
is string) {
1202 MimeHandler handler
= FindHandler ("text/plain");
1203 if (handler
!= null)
1204 res
.Add (handler
.Type
);
1205 }/* else if (data is Bitmap)
1210 IDataObject data_object
= data
as IDataObject
;
1211 if (data_object
!= null) {
1212 foreach (string format
in data_object
.GetFormats (true)) {
1213 MimeHandler handler
= FindHandler (format
);
1214 if (handler
!= null && !res
.Contains (handler
.Type
))
1215 res
.Add (handler
.Type
);
1219 if (data
is ISerializable
) {
1220 MimeHandler handler
= FindHandler ("application/x-mono-serialized-object");
1221 if (handler
!= null)
1222 res
.Add (handler
.Type
);
1225 return (IntPtr
[]) res
.ToArray (typeof (IntPtr
));