using System;
+using System.IO;
using System.Text;
+using System.Drawing;
using System.Collections;
+using System.Runtime.Serialization;
using System.Runtime.InteropServices;
-
+using System.Runtime.Serialization.Formatters.Binary;
namespace System.Windows.Forms {
internal class X11Dnd {
+ private enum State {
+ Accepting,
+ Dragging
+ }
+
+ private enum DragState {
+ None,
+ Beginning,
+ Dragging,
+ Entered
+ }
+
+ private interface IDataConverter {
+ void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent);
+ void SetData (X11Dnd dnd, object data, ref XEvent xevent);
+ }
+
private delegate void MimeConverter (IntPtr dsp,
DataObject data, ref XEvent xevent);
public string Name;
public IntPtr Type;
public IntPtr NonProtocol;
- public MimeConverter Convert;
+ public IDataConverter Converter;
- public MimeHandler (string name, MimeConverter converter)
+ public MimeHandler (string name, IDataConverter converter)
{
Name = name;
- Convert = converter;
+ Converter = converter;
+ }
+
+ public override string ToString ()
+ {
+ return "MimeHandler {" + Name + "}";
}
}
private MimeHandler [] MimeHandlers = {
// new MimeHandler ("WCF_DIB"),
// new MimeHandler ("image/gif", new MimeConverter (ImageConverter)),
+// new MimeHandler ("text/rtf", new MimeConverter (RtfConverter)),
+// new MimeHandler ("text/richtext", new MimeConverter (RtfConverter)),
+
+ new MimeHandler ("text/plain", new TextConverter ()),
+ new MimeHandler ("text/html", new HtmlConverter ()),
+ new MimeHandler ("text/uri-list", new UriListConverter ()),
+ new MimeHandler ("application/x-mono-serialized-object",
+ new SerializedObjectConverter ())
+ };
+
+ private class SerializedObjectConverter : IDataConverter {
+
+ public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
+ {
+ MemoryStream stream = dnd.GetData (ref xevent);
+ BinaryFormatter bf = new BinaryFormatter ();
+
+ if (stream.Length == 0)
+ return;
+
+ stream.Seek (0, 0);
+ object obj = bf.Deserialize (stream);
+ data.SetData (obj);
+ }
+
+ public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
+ {
+ if (data == null)
+ return;
+
+ MemoryStream stream = new MemoryStream ();
+ BinaryFormatter bf = new BinaryFormatter ();
+
+ bf.Serialize (stream, data);
+
+ IntPtr buffer = Marshal.AllocHGlobal ((int) stream.Length);
+ stream.Seek (0, 0);
+
+ for (int i = 0; i < stream.Length; i++) {
+ Marshal.WriteByte (buffer, i, (byte) stream.ReadByte ());
+ }
+
+ dnd.SetProperty (ref xevent, buffer, (int) stream.Length);
+ }
+ }
+
+ private class HtmlConverter : IDataConverter {
+
+ public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
+ {
+ string text = dnd.GetText (ref xevent, false);
+ if (text == null)
+ return;
+ data.SetData (DataFormats.Text, text);
+ data.SetData (DataFormats.UnicodeText, text);
+ }
+
+ public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
+ {
+ IntPtr buffer;
+ int len;
+ string str = data as string;
+
+ if (str == null)
+ return;
+
+ if (xevent.SelectionRequestEvent.target == (IntPtr)Atom.XA_STRING) {
+ byte [] bytes = Encoding.ASCII.GetBytes (str);
+ buffer = Marshal.AllocHGlobal (bytes.Length);
+ len = bytes.Length;
+ for (int i = 0; i < len; i++)
+ Marshal.WriteByte (buffer, i, bytes [i]);
+ } else {
+ buffer = Marshal.StringToHGlobalAnsi (str);
+ len = 0;
+ while (Marshal.ReadByte (buffer, len) != 0)
+ len++;
+ }
+
+ dnd.SetProperty (ref xevent, buffer, len);
+
+ Marshal.FreeHGlobal (buffer);
+ }
+ }
+
+ private class TextConverter : IDataConverter {
+
+ public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
+ {
+ string text = dnd.GetText (ref xevent, true);
+ if (text == null)
+ return;
+ data.SetData (DataFormats.Text, text);
+ data.SetData (DataFormats.UnicodeText, text);
+ }
+
+ public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
+ {
+ IntPtr buffer;
+ int len;
+ string str = data as string;
+
+ if (data == null)
+ return;
+
+ if (xevent.SelectionRequestEvent.target == (IntPtr)Atom.XA_STRING) {
+ byte [] bytes = Encoding.ASCII.GetBytes (str);
+ buffer = Marshal.AllocHGlobal (bytes.Length);
+ len = bytes.Length;
+ for (int i = 0; i < len; i++)
+ Marshal.WriteByte (buffer, i, bytes [i]);
+ } else {
+ buffer = Marshal.StringToHGlobalAnsi (str);
+ len = 0;
+ while (Marshal.ReadByte (buffer, len) != 0)
+ len++;
+ }
+
+ dnd.SetProperty (ref xevent, buffer, len);
+
+ Marshal.FreeHGlobal (buffer);
+ }
+ }
+
+ private class UriListConverter : IDataConverter {
+
+ public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
+ {
+ string text = dnd.GetText (ref xevent, false);
+ if (text == null)
+ return;
+
+ // TODO: Do this in a loop instead of just splitting
+ ArrayList uri_list = new ArrayList ();
+ string [] lines = text.Split (new char [] { '\r', '\n' });
+ foreach (string line in lines) {
+ // # is a comment line (see RFC 2483)
+ if (line.StartsWith ("#"))
+ continue;
+ try {
+ Uri uri = new Uri (line);
+ uri_list.Add (uri.LocalPath);
+ } catch { }
+ }
+
+ string [] l = (string []) uri_list.ToArray (typeof (string));
+ if (l.Length < 1)
+ return;
+ data.SetData (DataFormats.FileDrop, l);
+ data.SetData ("FileName", l [0]);
+ data.SetData ("FileNameW", l [0]);
+ }
+
+ public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
+ {
+ string [] uri_list = data as string [];
+
+ if (uri_list == null)
+ return;
+
+ StringBuilder res = new StringBuilder ();
+ foreach (string uri_str in uri_list) {
+ Uri uri = new Uri (uri_str);
+ res.Append (uri.ToString ());
+ res.Append ("\r\n");
+ }
+
+ IntPtr buffer = Marshal.StringToHGlobalAnsi ((string) data);
+ int len = 0;
+ while (Marshal.ReadByte (buffer, len) != 0)
+ len++;
+ dnd.SetProperty (ref xevent, buffer, len);
+ }
+ }
+
+ private class DragData {
+ public IntPtr Window;
+ public DragState State;
+ public object Data;
+ public IntPtr Action;
+ public IntPtr [] SupportedTypes;
+ public MouseButtons MouseState;
- new MimeHandler ("text/rtf", new MimeConverter (RtfConverter)),
- new MimeHandler ("text/richtext", new MimeConverter (RtfConverter)),
- new MimeHandler ("text/plain", new MimeConverter (TextConverter)),
- new MimeHandler ("text/html", new MimeConverter (HtmlConverter)),
- new MimeHandler ("text/uri-list", new MimeConverter (UriListConverter)),
- };
+ public IntPtr LastWindow;
+ public IntPtr LastTopLevel;
+
+ public bool WillAccept;
+
+ public void Reset ()
+ {
+ State = DragState.None;
+ Data = null;
+ SupportedTypes = null;
+ WillAccept = false;
+ }
+ }
+
+ private class DropData {
+
+ }
// This version seems to be the most common
- private static readonly uint [] XdndVersion = new uint [] { 4 };
+ private static readonly IntPtr [] XdndVersion = new IntPtr [] { new IntPtr (4) };
private IntPtr display;
+ private DragData drag_data;
-
- private bool initialized;
-
private IntPtr XdndAware;
private IntPtr XdndSelection;
private IntPtr XdndEnter;
private IntPtr XdndActionCopy;
private IntPtr XdndActionMove;
private IntPtr XdndActionLink;
- private IntPtr XdndActionPrivate;
- private IntPtr XdndActionList;
- private IntPtr XdndActionDescription;
- private IntPtr XdndActionAsk;
+ //private IntPtr XdndActionPrivate;
+ //private IntPtr XdndActionList;
+ //private IntPtr XdndActionDescription;
+ //private IntPtr XdndActionAsk;
+
+ //private State state;
private int converts_pending;
private bool position_recieved;
private IntPtr source;
private IntPtr toplevel;
private DataObject data;
+
private Control control;
private int pos_x, pos_y;
private DragDropEffects allowed;
private DragEventArgs drag_event;
+ private Cursor CursorNo;
+ private Cursor CursorCopy;
+ private Cursor CursorMove;
+ private Cursor CursorLink;
+ // check out the TODO below
+ //private IntPtr CurrentCursorHandle;
+
public X11Dnd (IntPtr display)
{
this.display = display;
+ Init ();
}
+ public bool InDrag()
+ {
+ if (drag_data == null)
+ return false;
+ return drag_data.State != DragState.None;
+ }
+
public void SetAllowDrop (Hwnd hwnd, bool allow)
{
- if (!initialized)
- Init ();
+ int[] atoms;
-// if (hwnd.allow_drop == allow)
-// return;
+ if (hwnd.allow_drop == allow)
+ return;
+
+ atoms = new int[XdndVersion.Length];
+ for (int i = 0; i < XdndVersion.Length; i++) {
+ atoms[i] = XdndVersion[i].ToInt32();
+ }
- XChangeProperty (display, hwnd.whole_window, XdndAware,
+ XplatUIX11.XChangeProperty (display, hwnd.whole_window, XdndAware,
(IntPtr) Atom.XA_ATOM, 32,
- PropertyMode.Replace, XdndVersion, allow ? 1 : 0);
-// hwnd.allow_drop = allow;
+ PropertyMode.Replace, atoms, allow ? 1 : 0);
+ hwnd.allow_drop = allow;
+ }
+
+ public DragDropEffects StartDrag (IntPtr handle, object data,
+ DragDropEffects allowed_effects)
+ {
+ drag_data = new DragData ();
+ drag_data.Window = handle;
+ drag_data.State = DragState.Beginning;
+ drag_data.MouseState = XplatUIX11.MouseState;
+ drag_data.Data = data;
+ drag_data.SupportedTypes = DetermineSupportedTypes (data);
+
+ drag_data.Action = ActionFromEffect (allowed_effects);
+
+ if (CursorNo == null) {
+ // Make sure the cursors are created
+ CursorNo = new Cursor (typeof (X11Dnd), "DnDNo.cur");
+ CursorCopy = new Cursor (typeof (X11Dnd), "DnDCopy.cur");
+ CursorMove = new Cursor (typeof (X11Dnd), "DnDMove.cur");
+ CursorLink = new Cursor (typeof (X11Dnd), "DnDLink.cur");
+ }
+
+ drag_data.LastTopLevel = IntPtr.Zero;
+ return DragDropEffects.Copy;
+ }
+
+ public void HandleButtonRelease (ref XEvent xevent)
+ {
+ if (drag_data == null)
+ return;
+
+ if (!((drag_data.MouseState == MouseButtons.Left &&
+ xevent.ButtonEvent.button == 1) ||
+ (drag_data.MouseState == MouseButtons.Right &&
+ xevent.ButtonEvent.button == 3)))
+ return;
+
+ if (drag_data.State == DragState.Beginning) {
+ //state = State.Accepting;
+ } else if (drag_data.State != DragState.None) {
+
+ if (drag_data.WillAccept) {
+ SendDrop (drag_data.LastTopLevel, xevent.AnyEvent.window,
+ xevent.ButtonEvent.time);
+ }
+
+ XplatUIX11.XUngrabPointer (display, IntPtr.Zero);
+ drag_data.State = DragState.None;
+ // WE can't reset the drag data yet as it is still
+ // most likely going to be used by the SelectionRequest
+ // handlers
+ }
+ }
+
+ public bool HandleMotionNotify (ref XEvent xevent)
+ {
+ if (drag_data == null)
+ return false;
+
+ if (drag_data.State == DragState.Beginning) {
+ int suc;
+
+ drag_data.State = DragState.Dragging;
+
+ suc = XplatUIX11.XSetSelectionOwner (display, XdndSelection,
+ drag_data.Window,
+ xevent.ButtonEvent.time);
+
+ if (suc == 0) {
+ Console.Error.WriteLine ("Could not take ownership of XdndSelection aborting drag.");
+ drag_data.Reset ();
+ return false;
+ }
+
+ suc = XplatUIX11.XGrabPointer (display, xevent.AnyEvent.window,
+ false,
+ EventMask.ButtonMotionMask |
+ EventMask.PointerMotionMask |
+ EventMask.ButtonPressMask |
+ EventMask.ButtonReleaseMask,
+ GrabMode.GrabModeAsync,
+ GrabMode.GrabModeAsync,
+ IntPtr.Zero, IntPtr.Zero/*CursorCopy.Handle*/, IntPtr.Zero);
+
+ if (suc != 0) {
+ Console.Error.WriteLine ("Could not grab pointer aborting drag.");
+ drag_data.Reset ();
+ return false;
+ }
+
+ drag_data.State = DragState.Dragging;
+ } else if (drag_data.State != DragState.None) {
+ bool dnd_aware = false;
+ IntPtr toplevel = IntPtr.Zero;
+ IntPtr window = XplatUIX11.RootWindowHandle;
+
+ IntPtr root, child;
+ int x_temp, y_temp;
+ int mask_return;
+
+ while (XplatUIX11.XQueryPointer (display,
+ window,
+ out root, out child,
+ out x_temp, out y_temp,
+ out xevent.MotionEvent.x,
+ out xevent.MotionEvent.y,
+ out mask_return)) {
+
+ if (!dnd_aware) {
+ dnd_aware = IsWindowDndAware (window);
+ if (dnd_aware) {
+ toplevel = window;
+ xevent.MotionEvent.x_root = x_temp;
+ xevent.MotionEvent.y_root = y_temp;
+ }
+ }
+
+ if (child == IntPtr.Zero)
+ break;
+
+ window = child;
+ }
+
+ if (window != drag_data.LastWindow && drag_data.State == DragState.Entered) {
+ drag_data.State = DragState.Dragging;
+
+ // TODO: Send a Leave if this is an MWF window
+
+ if (toplevel != drag_data.LastTopLevel)
+ SendLeave (drag_data.LastTopLevel, xevent.MotionEvent.window);
+ }
+
+ drag_data.State = DragState.Entered;
+ if (toplevel != drag_data.LastTopLevel) {
+ // Entering a new toplevel window
+ SendEnter (toplevel, drag_data.Window, drag_data.SupportedTypes);
+ } else {
+ // Already in a toplevel window, so send a position
+ SendPosition (toplevel, drag_data.Window,
+ drag_data.Action,
+ xevent.MotionEvent.x_root,
+ xevent.MotionEvent.y_root,
+ xevent.MotionEvent.time);
+ }
+
+ drag_data.LastTopLevel = toplevel;
+ drag_data.LastWindow = window;
+ return true;
+ }
+ return false;
}
// return true if the event is handled here
public bool HandleClientMessage (ref XEvent xevent)
{
- if (!initialized)
- Init ();
-
// most common so we check it first
if (xevent.ClientMessageEvent.message_type == XdndPosition)
- return HandlePositionEvent (ref xevent);
+ return Accepting_HandlePositionEvent (ref xevent);
if (xevent.ClientMessageEvent.message_type == XdndEnter)
- return HandleEnterEvent (ref xevent);
+ return Accepting_HandleEnterEvent (ref xevent);
if (xevent.ClientMessageEvent.message_type == XdndDrop)
- return HandleDropEvent (ref xevent);
+ return Accepting_HandleDropEvent (ref xevent);
if (xevent.ClientMessageEvent.message_type == XdndLeave)
- return HandleLeaveEvent (ref xevent);
-
+ return Accepting_HandleLeaveEvent (ref xevent);
+ if (xevent.ClientMessageEvent.message_type == XdndStatus)
+ return HandleStatusEvent (ref xevent);
+
return false;
}
public bool HandleSelectionNotifyEvent (ref XEvent xevent)
{
- if (source != XGetSelectionOwner (display, XdndSelection))
+ if (source != XplatUIX11.XGetSelectionOwner (display, XdndSelection))
return false;
MimeHandler handler = FindHandler ((IntPtr) xevent.SelectionEvent.target);
if (data == null)
data = new DataObject ();
- handler.Convert (display, data, ref xevent);
+ handler.Converter.GetData (this, data, ref xevent);
converts_pending--;
- if (converts_pending <= 0 && position_recieved)
- SendEnterStatus ();
+ if (converts_pending <= 0 && position_recieved) {
+ drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
+ allowed, DragDropEffects.None);
+ control.DndEnter (drag_event);
+ SendStatus (source, drag_event.Effect);
+ status_sent = true;
+ }
+ return true;
+ }
+
+ public bool HandleSelectionRequestEvent (ref XEvent xevent)
+ {
+ if (xevent.SelectionRequestEvent.selection != XdndSelection)
+ return false;
+
+ MimeHandler handler = FindHandler (xevent.SelectionRequestEvent.target);
+ if (handler == null)
+ return false;
+
+ handler.Converter.SetData (this, drag_data.Data, ref xevent);
+
return true;
}
+ private void SetProperty (ref XEvent xevent, IntPtr data, int length)
+ {
+ XEvent sel = new XEvent();
+ sel.SelectionEvent.type = XEventName.SelectionNotify;
+ sel.SelectionEvent.send_event = true;
+ sel.SelectionEvent.display = display;
+ sel.SelectionEvent.selection = xevent.SelectionRequestEvent.selection;
+ sel.SelectionEvent.target = xevent.SelectionRequestEvent.target;
+ sel.SelectionEvent.requestor = xevent.SelectionRequestEvent.requestor;
+ sel.SelectionEvent.time = xevent.SelectionRequestEvent.time;
+ sel.SelectionEvent.property = IntPtr.Zero;
+
+ XplatUIX11.XChangeProperty (display, xevent.SelectionRequestEvent.requestor,
+ xevent.SelectionRequestEvent.property,
+ xevent.SelectionRequestEvent.target,
+ 8, PropertyMode.Replace, data, length);
+ sel.SelectionEvent.property = xevent.SelectionRequestEvent.property;
+
+ XplatUIX11.XSendEvent (display, xevent.SelectionRequestEvent.requestor, false,
+ (IntPtr)EventMask.NoEventMask, ref sel);
+ return;
+ }
+
private void Reset ()
{
ResetSourceData ();
status_sent = false;
}
- private bool HandleEnterEvent (ref XEvent xevent)
+ private bool Accepting_HandleEnterEvent (ref XEvent xevent)
{
Reset ();
return true;
}
- private bool HandlePositionEvent (ref XEvent xevent)
+ private bool Accepting_HandlePositionEvent (ref XEvent xevent)
{
- int x = (int) xevent.ClientMessageEvent.ptr3 >> 16;
- int y = (int) xevent.ClientMessageEvent.ptr3 & 0xFFFF;
+ pos_x = (int) xevent.ClientMessageEvent.ptr3 >> 16;
+ pos_y = (int) xevent.ClientMessageEvent.ptr3 & 0xFFFF;
- allowed = EffectFromAction (xevent.ClientMessageEvent.ptr5);
+ // Copy is implicitly allowed
+ allowed = EffectFromAction (xevent.ClientMessageEvent.ptr5) | DragDropEffects.Copy;
- IntPtr parent, child, new_child;
+ IntPtr parent, child, new_child, last_drop_child;
parent = XplatUIX11.XRootWindow (display, 0);
child = toplevel;
+ last_drop_child = IntPtr.Zero;
while (true) {
int xd, yd;
new_child = IntPtr.Zero;
if (!XplatUIX11.XTranslateCoordinates (display,
- parent, child, x, y,
+ parent, child, pos_x, pos_y,
out xd, out yd, out new_child))
break;
if (new_child == IntPtr.Zero)
break;
child = new_child;
+
+ Hwnd h = Hwnd.ObjectFromHandle (child);
+ Control d = Control.FromHandle (h.client_window);
+ if (d != null && d.allow_drop)
+ last_drop_child = child;
}
+ if (last_drop_child != IntPtr.Zero)
+ child = last_drop_child;
+
if (target != child) {
// We have moved into a new control
// or into a control for the first time
if (c == null)
return true;
-// if (!c.AllowDrop) {
-// Finish ();
-// return true;
-// }
+ if (!c.allow_drop) {
+ SendStatus (source, DragDropEffects.None);
+ Finish ();
+ return true;
+ }
control = c;
position_recieved = true;
if (converts_pending > 0)
return true;
if (!status_sent) {
- SendEnterStatus ();
+ drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
+ allowed, DragDropEffects.None);
+ control.DndEnter (drag_event);
+ SendStatus (source, drag_event.Effect);
+ status_sent = true;
} else {
- SendStatus ();
+ drag_event.x = pos_x;
+ drag_event.y = pos_y;
control.DndOver (drag_event);
+ SendStatus (source, drag_event.Effect);
}
return true;
ResetTargetData ();
}
- private bool HandleDropEvent (ref XEvent xevent)
+ private bool Accepting_HandleDropEvent (ref XEvent xevent)
{
- Console.WriteLine ("DROPPING EVENT");
- if (control != null && drag_event != null)
+ if (control != null && drag_event != null) {
+ drag_event = new DragEventArgs (data,
+ 0, pos_x, pos_y,
+ allowed, DragDropEffects.None);
control.DndDrop (drag_event);
+ }
SendFinished ();
return true;
}
- private bool HandleLeaveEvent (ref XEvent xevent)
+ private bool Accepting_HandleLeaveEvent (ref XEvent xevent)
{
if (control != null && drag_event != null)
control.DndLeave (drag_event);
- Reset ();
+ // Reset ();
+ return true;
+ }
+
+ private bool HandleStatusEvent (ref XEvent xevent)
+ {
+ if (drag_data != null && drag_data.State == DragState.Entered) {
+ drag_data.WillAccept = ((int) xevent.ClientMessageEvent.ptr2 & 0x1) != 0;
+ Cursor cursor = CursorNo;
+ if (drag_data.WillAccept) {
+ // Same order as on MS
+ IntPtr action = xevent.ClientMessageEvent.ptr5;
+ if (action == XdndActionCopy)
+ cursor = CursorCopy;
+ else if (action == XdndActionLink)
+ cursor = CursorLink;
+ else if (action == XdndActionMove)
+ cursor = CursorMove;
+
+ }
+
+ // TODO: Try not to set the cursor so much
+ //if (cursor.Handle != CurrentCursorHandle) {
+ XplatUIX11.XChangeActivePointerGrab (display,
+ EventMask.ButtonMotionMask |
+ EventMask.PointerMotionMask |
+ EventMask.ButtonPressMask |
+ EventMask.ButtonReleaseMask,
+ cursor.Handle, IntPtr.Zero);
+ //CurrentCursorHandle = cursor.Handle;
+ //}
+ }
return true;
}
private DragDropEffects EffectFromAction (IntPtr action)
{
DragDropEffects allowed = DragDropEffects.None;
+
if (action == XdndActionCopy)
allowed = DragDropEffects.Copy;
- if (action == XdndActionMove)
- allowed = DragDropEffects.Move;
+ else if (action == XdndActionMove)
+ allowed |= DragDropEffects.Move;
if (action == XdndActionLink)
- allowed = DragDropEffects.Link;
+ allowed |= DragDropEffects.Link;
return allowed;
}
private IntPtr ActionFromEffect (DragDropEffects effect)
{
IntPtr action = IntPtr.Zero;
- if (effect == DragDropEffects.Copy)
+
+ // We can't OR together actions on XDND so sadly the primary
+ // is the only one shown here
+ if ((effect & DragDropEffects.Copy) != 0)
action = XdndActionCopy;
- if (effect == DragDropEffects.Move)
+ else if ((effect & DragDropEffects.Move) != 0)
action = XdndActionMove;
- if (effect == DragDropEffects.Link)
+ else if ((effect & DragDropEffects.Link) != 0)
action = XdndActionLink;
return action;
}
{
bool match = false;
+ if (source != XplatUIX11.XGetSelectionOwner (display, XdndSelection)) {
+ return false;
+ }
+
+ Control mwfcontrol = MwfWindow (source);
+
+ if (mwfcontrol != null && drag_data != null) {
+ DataObject dragged = drag_data.Data as DataObject;
+ if (dragged != null) {
+ data = dragged;
+ } else {
+ if (data == null)
+ data = new DataObject ();
+ SetDataWithFormats (drag_data.Data);
+ }
+ return true;
+ }
+
foreach (IntPtr atom in SourceSupportedList (ref xevent)) {
MimeHandler handler = FindHandler (atom);
if (handler == null)
continue;
- XConvertSelection (display, XdndSelection, handler.Type,
- handler.NonProtocol, toplevel, 0 /* CurrentTime */);
+ XplatUIX11.XConvertSelection (display, XdndSelection, handler.Type,
+ handler.NonProtocol, toplevel, IntPtr.Zero /* CurrentTime */);
converts_pending++;
match = true;
}
return match;
}
+ private void SetDataWithFormats (object value)
+ {
+ if (value is string) {
+ data.SetData (DataFormats.Text, value);
+ data.SetData (DataFormats.UnicodeText, value);
+ }
+
+ data.SetData (value);
+ }
+
private MimeHandler FindHandler (IntPtr atom)
{
if (atom == IntPtr.Zero)
return null;
}
- private void SendStatus ()
+ private MimeHandler FindHandler (string name)
+ {
+ foreach (MimeHandler handler in MimeHandlers) {
+ if (handler.Name == name)
+ return handler;
+ }
+ return null;
+ }
+
+ private void SendStatus (IntPtr source, DragDropEffects effect)
{
- DragDropEffects action = drag_event.Effect;
XEvent xevent = new XEvent ();
xevent.AnyEvent.type = XEventName.ClientMessage;
xevent.ClientMessageEvent.message_type = XdndStatus;
xevent.ClientMessageEvent.format = 32;
xevent.ClientMessageEvent.ptr1 = toplevel;
- if (drag_event.Effect != DragDropEffects.None)
+ if (effect != DragDropEffects.None)
xevent.ClientMessageEvent.ptr2 = (IntPtr) 1;
- xevent.ClientMessageEvent.ptr5 = ActionFromEffect (action);
- XSendEvent (display, source, false, 0, ref xevent);
+ xevent.ClientMessageEvent.ptr5 = ActionFromEffect (effect);
+ XplatUIX11.XSendEvent (display, source, false, IntPtr.Zero, ref xevent);
}
- private void SendEnterStatus ()
+ private void SendEnter (IntPtr handle, IntPtr from, IntPtr [] supported)
{
- drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
- allowed, DragDropEffects.None);
- control.DndEnter (drag_event);
+ XEvent xevent = new XEvent ();
+
+ xevent.AnyEvent.type = XEventName.ClientMessage;
+ xevent.AnyEvent.display = display;
+ xevent.ClientMessageEvent.window = handle;
+ xevent.ClientMessageEvent.message_type = XdndEnter;
+ xevent.ClientMessageEvent.format = 32;
+ xevent.ClientMessageEvent.ptr1 = from;
+
+ // (int) xevent.ClientMessageEvent.ptr2 & 0x1)
+ // int ptr2 = 0x1;
+ // xevent.ClientMessageEvent.ptr2 = (IntPtr) ptr2;
+ // (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~(0xFF << 24)) | ((v) << 24)
+ xevent.ClientMessageEvent.ptr2 = (IntPtr) ((long)XdndVersion [0] << 24);
+
+ if (supported.Length > 0)
+ xevent.ClientMessageEvent.ptr3 = supported [0];
+ if (supported.Length > 1)
+ xevent.ClientMessageEvent.ptr4 = supported [1];
+ if (supported.Length > 2)
+ xevent.ClientMessageEvent.ptr5 = supported [2];
+
+ XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
+ }
+ private void SendDrop (IntPtr handle, IntPtr from, IntPtr time)
+ {
XEvent xevent = new XEvent ();
xevent.AnyEvent.type = XEventName.ClientMessage;
xevent.AnyEvent.display = display;
- xevent.ClientMessageEvent.window = source;
- xevent.ClientMessageEvent.message_type = XdndStatus;
+ xevent.ClientMessageEvent.window = handle;
+ xevent.ClientMessageEvent.message_type = XdndDrop;
xevent.ClientMessageEvent.format = 32;
- xevent.ClientMessageEvent.ptr1 = toplevel;
- if (drag_event.Effect != DragDropEffects.None)
- xevent.ClientMessageEvent.ptr2 = (IntPtr) 1;
+ xevent.ClientMessageEvent.ptr1 = from;
+ xevent.ClientMessageEvent.ptr3 = time;
+
+ XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
+ }
+
+ private void SendPosition (IntPtr handle, IntPtr from, IntPtr action, int x, int y, IntPtr time)
+ {
+ XEvent xevent = new XEvent ();
+
+ xevent.AnyEvent.type = XEventName.ClientMessage;
+ xevent.AnyEvent.display = display;
+ xevent.ClientMessageEvent.window = handle;
+ xevent.ClientMessageEvent.message_type = XdndPosition;
+ xevent.ClientMessageEvent.format = 32;
+ xevent.ClientMessageEvent.ptr1 = from;
+ xevent.ClientMessageEvent.ptr3 = (IntPtr) ((x << 16) | (y & 0xFFFF));
+ xevent.ClientMessageEvent.ptr4 = time;
+ xevent.ClientMessageEvent.ptr5 = action;
+
+ XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
+ }
- xevent.ClientMessageEvent.ptr5 = ActionFromEffect (drag_event.Effect);
- XSendEvent (display, source, false, 0, ref xevent);
+ private void SendLeave (IntPtr handle, IntPtr from)
+ {
+ XEvent xevent = new XEvent ();
+
+ xevent.AnyEvent.type = XEventName.ClientMessage;
+ xevent.AnyEvent.display = display;
+ xevent.ClientMessageEvent.window = handle;
+ xevent.ClientMessageEvent.message_type = XdndLeave;
+ xevent.ClientMessageEvent.format = 32;
+ xevent.ClientMessageEvent.ptr1 = from;
- status_sent = true;
+ XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent);
}
private void SendFinished ()
xevent.ClientMessageEvent.format = 32;
xevent.ClientMessageEvent.ptr1 = toplevel;
- XSendEvent (display, source, false, 0, ref xevent);
+ XplatUIX11.XSendEvent (display, source, false, IntPtr.Zero, ref xevent);
}
// There is a somewhat decent amount of overhead
// as a lot of applications do not even use it.
private void Init ()
{
- XdndAware = XInternAtom (display, "XdndAware", false);
- XdndEnter = XInternAtom (display, "XdndEnter", false);
- XdndLeave = XInternAtom (display, "XdndLeave", false);
- XdndPosition = XInternAtom (display, "XdndPosition", false);
- XdndStatus = XInternAtom (display, "XdndStatus", false);
- XdndDrop = XInternAtom (display, "XdndDrop", false);
- XdndSelection = XInternAtom (display, "XdndSelection", false);
- XdndFinished = XInternAtom (display, "XdndFinished", false);
- XdndTypeList = XInternAtom (display, "XdndTypeList", false);
- XdndActionCopy = XInternAtom (display, "XdndActionCopy", false);
- XdndActionMove = XInternAtom (display, "XdndActionMove", false);
- XdndActionLink = XInternAtom (display, "XdndActionLink", false);
- XdndActionPrivate = XInternAtom (display, "XdndActionPrivate", false);
- XdndActionList = XInternAtom (display, "XdndActionList", false);
- XdndActionDescription = XInternAtom (display, "XdndActionDescription", false);
- XdndActionAsk = XInternAtom (display, "XdndActionAsk", false);
+ XdndAware = XplatUIX11.XInternAtom (display, "XdndAware", false);
+ XdndEnter = XplatUIX11.XInternAtom (display, "XdndEnter", false);
+ XdndLeave = XplatUIX11.XInternAtom (display, "XdndLeave", false);
+ XdndPosition = XplatUIX11.XInternAtom (display, "XdndPosition", false);
+ XdndStatus = XplatUIX11.XInternAtom (display, "XdndStatus", false);
+ XdndDrop = XplatUIX11.XInternAtom (display, "XdndDrop", false);
+ XdndSelection = XplatUIX11.XInternAtom (display, "XdndSelection", false);
+ XdndFinished = XplatUIX11.XInternAtom (display, "XdndFinished", false);
+ XdndTypeList = XplatUIX11.XInternAtom (display, "XdndTypeList", false);
+ XdndActionCopy = XplatUIX11.XInternAtom (display, "XdndActionCopy", false);
+ XdndActionMove = XplatUIX11.XInternAtom (display, "XdndActionMove", false);
+ XdndActionLink = XplatUIX11.XInternAtom (display, "XdndActionLink", false);
+ //XdndActionPrivate = XplatUIX11.XInternAtom (display, "XdndActionPrivate", false);
+ //XdndActionList = XplatUIX11.XInternAtom (display, "XdndActionList", false);
+ //XdndActionDescription = XplatUIX11.XInternAtom (display, "XdndActionDescription", false);
+ //XdndActionAsk = XplatUIX11.XInternAtom (display, "XdndActionAsk", false);
foreach (MimeHandler handler in MimeHandlers) {
- handler.Type = XInternAtom (display, handler.Name, false);
- handler.NonProtocol = XInternAtom (display,
+ handler.Type = XplatUIX11.XInternAtom (display, handler.Name, false);
+ handler.NonProtocol = XplatUIX11.XInternAtom (display,
String.Concat ("MWFNonP+", handler.Name), false);
}
- initialized = true;
}
private IntPtr [] SourceSupportedList (ref XEvent xevent)
{
IntPtr [] res;
+
if (((int) xevent.ClientMessageEvent.ptr2 & 0x1) == 0) {
res = new IntPtr [3];
res [0] = xevent.ClientMessageEvent.ptr3;
res [2] = xevent.ClientMessageEvent.ptr5;
} else {
IntPtr type;
- int format, count, remaining;
+ int format;
+ IntPtr count;
+ IntPtr remaining;
IntPtr data = IntPtr.Zero;
- XGetWindowProperty (display, source, XdndTypeList,
- 0, 32, false, (IntPtr) Atom.XA_ATOM,
+ XplatUIX11.XGetWindowProperty (display, source, XdndTypeList,
+ IntPtr.Zero, new IntPtr(32), false, (IntPtr) Atom.XA_ATOM,
out type, out format, out count,
- out remaining, out data);
+ out remaining, ref data);
- res = new IntPtr [count];
- for (int i = 0; i < count; i++) {
- res [i] = (IntPtr) Marshal.ReadInt32 (data, i * Marshal.SizeOf(typeof(int)));
+ res = new IntPtr [count.ToInt32()];
+ for (int i = 0; i < count.ToInt32(); i++) {
+ res [i] = (IntPtr) Marshal.ReadInt32 (data, i *
+ Marshal.SizeOf (typeof (int)));
}
- }
-
- return res;
- }
- private static void UriListConverter (IntPtr display, DataObject data,
- ref XEvent xevent)
- {
- string text = GetText (display, ref xevent, false);
- if (text == null)
- return;
-
- // TODO: Do this in a loop instead of just splitting
- ArrayList uri_list = new ArrayList ();
- string [] lines = text.Split (new char [] { '\r', '\n' });
- foreach (string line in lines) {
- // # is a comment line (see RFC 2483)
- if (line.StartsWith ("#"))
- continue;
- try {
- Uri uri = new Uri (line);
- uri_list.Add (uri.LocalPath);
- } catch { }
+ XplatUIX11.XFree (data);
}
- string [] l = (string []) uri_list.ToArray (typeof (string));
- if (l.Length < 1)
- return;
- data.SetData (DataFormats.FileDrop, l);
- data.SetData ("FileName", l [0]);
- data.SetData ("FileNameW", l [0]);
- }
-
- private static void TextConverter (IntPtr display, DataObject data, ref XEvent xevent)
- {
- string text = GetText (display, ref xevent, false);
- if (text == null)
- return;
- data.SetData (DataFormats.Text, text);
- data.SetData (DataFormats.UnicodeText, text);
- }
-
- private static void HtmlConverter (IntPtr display, DataObject data, ref XEvent xevent)
- {
- string html = GetText (display, ref xevent, true);
- if (html == null)
- return;
- data.SetData (DataFormats.Html, html);
- }
-
- private static void ImageConverter (IntPtr display, DataObject data, ref XEvent xevent)
- {
- }
-
- private static void RtfConverter (IntPtr display, DataObject data, ref XEvent xevent)
- {
+ return res;
}
- private static string GetText (IntPtr display, ref XEvent xevent, bool unicode)
+ private string GetText (ref XEvent xevent, bool unicode)
{
int nread = 0;
- int nitems;
- int bytes_after;
+ IntPtr nitems;
+ IntPtr bytes_after;
StringBuilder builder = new StringBuilder ();
do {
int actual_fmt;
IntPtr data = IntPtr.Zero;
- if (0 != XGetWindowProperty (display,
+ if (0 != XplatUIX11.XGetWindowProperty (display,
xevent.AnyEvent.window,
(IntPtr) xevent.SelectionEvent.property,
- 0, 0xffffff, false,
+ IntPtr.Zero, new IntPtr(0xffffff), false,
(IntPtr) Atom.AnyPropertyType, out actual_type,
out actual_fmt, out nitems, out bytes_after,
- out data)) {
- XFree (data);
+ ref data)) {
+ XplatUIX11.XFree (data);
break;
}
builder.Append (Marshal.PtrToStringUni (data));
else
builder.Append (Marshal.PtrToStringAnsi (data));
- nread += nitems;
+ nread += nitems.ToInt32();
- XFree (data);
- } while (bytes_after > 0);
+ XplatUIX11.XFree (data);
+ } while (bytes_after.ToInt32() > 0);
if (nread == 0)
return null;
return builder.ToString ();
}
- [DllImport ("libX11")]
- private extern static string XGetAtomName (IntPtr display, IntPtr atom);
+ private MemoryStream GetData (ref XEvent xevent)
+ {
+ int nread = 0;
+ IntPtr nitems;
+ IntPtr bytes_after;
- [DllImport ("libX11")]
- private extern static IntPtr XInternAtom (IntPtr display, string atom_name, bool only_if_exists);
+ MemoryStream res = new MemoryStream ();
+ do {
+ IntPtr actual_type;
+ int actual_fmt;
+ IntPtr data = IntPtr.Zero;
- [DllImport ("libX11")]
- private extern static int XChangeProperty (IntPtr display, IntPtr window, IntPtr property,
- IntPtr format, int type, PropertyMode mode, uint [] atoms, int nelements);
+ if (0 != XplatUIX11.XGetWindowProperty (display,
+ xevent.AnyEvent.window,
+ (IntPtr) xevent.SelectionEvent.property,
+ IntPtr.Zero, new IntPtr(0xffffff), false,
+ (IntPtr) Atom.AnyPropertyType, out actual_type,
+ out actual_fmt, out nitems, out bytes_after,
+ ref data)) {
+ XplatUIX11.XFree (data);
+ break;
+ }
- [DllImport ("libX11")]
- private extern static int XGetWindowProperty (IntPtr display, IntPtr window,
- IntPtr atom, int long_offset, int long_length, bool delete,
- IntPtr req_type, out IntPtr actual_type, out int actual_format,
- out int nitems, out int bytes_after, out IntPtr prop);
+ for (int i = 0; i < nitems.ToInt32(); i++)
+ res.WriteByte (Marshal.ReadByte (data, i));
+ nread += nitems.ToInt32();
- [DllImport ("libX11")]
- internal extern static int XSendEvent (IntPtr display, IntPtr window,
- bool propagate, EventMask event_mask, ref XEvent send_event);
+ XplatUIX11.XFree (data);
+ } while (bytes_after.ToInt32() > 0);
+ return res;
+ }
- [DllImport ("libX11")]
- internal extern static int XConvertSelection (IntPtr display, IntPtr selection,
- IntPtr target, IntPtr property, IntPtr requestor, int time);
+ private Control MwfWindow (IntPtr window)
+ {
+ Hwnd hwnd = Hwnd.ObjectFromHandle (window);
+ if (hwnd == null)
+ return null;
- [DllImport ("libX11")]
- internal extern static IntPtr XGetSelectionOwner(IntPtr display, IntPtr selection);
+ Control res = Control.FromHandle (hwnd.client_window);
+ return res;
+ }
- [DllImport ("libX11")]
- internal extern static int XFree(IntPtr data);
- }
+ private bool IsWindowDndAware (IntPtr handle)
+ {
+ bool res = true;
+ // Check the version number, we need greater than 3
+ IntPtr actual;
+ int format;
+ IntPtr count;
+ IntPtr remaining;
+ IntPtr data = IntPtr.Zero;
+
+ XplatUIX11.XGetWindowProperty (display, handle, XdndAware, IntPtr.Zero, new IntPtr(0x8000000), false,
+ (IntPtr) Atom.XA_ATOM, out actual, out format,
+ out count, out remaining, ref data);
+
+ if (actual != (IntPtr) Atom.XA_ATOM || format != 32 ||
+ count.ToInt32() == 0 || data == IntPtr.Zero) {
+ if (data != IntPtr.Zero)
+ XplatUIX11.XFree (data);
+ return false;
+ }
+
+ int version = Marshal.ReadInt32 (data, 0);
+ if (version < 3) {
+ Console.Error.WriteLine ("XDND Version too old (" + version + ").");
+ XplatUIX11.XFree (data);
+ return false;
+ }
+
+ // First type is actually the XDND version
+ if (count.ToInt32() > 1) {
+ res = false;
+ for (int i = 1; i < count.ToInt32(); i++) {
+ IntPtr type = (IntPtr) Marshal.ReadInt32 (data, i *
+ Marshal.SizeOf (typeof (int)));
+ for (int j = 0; j < drag_data.SupportedTypes.Length; j++) {
+ if (drag_data.SupportedTypes [j] == type) {
+ res = true;
+ break;
+ }
+ }
+ }
+ }
+
+ XplatUIX11.XFree (data);
+ return res;
+ }
+
+ private IntPtr [] DetermineSupportedTypes (object data)
+ {
+ ArrayList res = new ArrayList ();
+
+ if (data is string) {
+ MimeHandler handler = FindHandler ("text/plain");
+ if (handler != null)
+ res.Add (handler.Type);
+ }/* else if (data is Bitmap)
+ res.Add (data);
+
+ */
+
+ if (data is IDataObject) {
+
+
+ }
+
+ if (data is ISerializable) {
+ MimeHandler handler = FindHandler ("application/x-mono-serialized-object");
+ if (handler != null)
+ res.Add (handler.Type);
+ }
+
+ return (IntPtr []) res.ToArray (typeof (IntPtr));
+ }
+ }
}