6 changed files with 479 additions and 1 deletions
@ -0,0 +1,80 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using Avalonia.Controls.DragDrop; |
|||
using Avalonia.Win32.Interop; |
|||
|
|||
namespace Avalonia.Win32 |
|||
{ |
|||
static class ClipboardFormats |
|||
{ |
|||
class ClipboardFormat |
|||
{ |
|||
public short Format { get; private set; } |
|||
public string Name { get; private set; } |
|||
|
|||
public ClipboardFormat(string name, short format) |
|||
{ |
|||
Format = format; |
|||
Name = name; |
|||
} |
|||
} |
|||
|
|||
private static readonly List<ClipboardFormat> FormatList = new List<ClipboardFormat>() |
|||
{ |
|||
new ClipboardFormat(DataFormats.Text, (short)UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT), |
|||
new ClipboardFormat(DataFormats.FileNames, (short)UnmanagedMethods.ClipboardFormat.CF_HDROP), |
|||
}; |
|||
|
|||
|
|||
private static string QueryFormatName(short format) |
|||
{ |
|||
int len = UnmanagedMethods.GetClipboardFormatName(format, null, 0); |
|||
if (len > 0) |
|||
{ |
|||
StringBuilder sb = new StringBuilder(len); |
|||
if (UnmanagedMethods.GetClipboardFormatName(format, sb, len) <= len) |
|||
return sb.ToString(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public static string GetFormat(short format) |
|||
{ |
|||
lock (FormatList) |
|||
{ |
|||
var pd = FormatList.FirstOrDefault(f => f.Format == format); |
|||
if (pd == null) |
|||
{ |
|||
string name = QueryFormatName(format); |
|||
if (string.IsNullOrEmpty(name)) |
|||
name = string.Format("Unknown_Format_{0}", format); |
|||
pd = new ClipboardFormat(name, format); |
|||
FormatList.Add(pd); |
|||
} |
|||
return pd.Name; |
|||
} |
|||
} |
|||
|
|||
public static short GetFormat(string format) |
|||
{ |
|||
lock (FormatList) |
|||
{ |
|||
var pd = FormatList.FirstOrDefault(f => StringComparer.OrdinalIgnoreCase.Equals(f.Name, format)); |
|||
if (pd == null) |
|||
{ |
|||
int id = UnmanagedMethods.RegisterClipboardFormat(format); |
|||
if (id == 0) |
|||
throw new Win32Exception(); |
|||
pd = new ClipboardFormat(format, (short)id); |
|||
FormatList.Add(pd); |
|||
} |
|||
return pd.Format; |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
using System; |
|||
using System.Threading; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Threading; |
|||
using Avalonia.Win32.Interop; |
|||
|
|||
namespace Zippr.UIServices.Avalonia.Windows |
|||
{ |
|||
class OleContext |
|||
{ |
|||
private static OleContext fCurrent; |
|||
|
|||
internal static OleContext Current |
|||
{ |
|||
get |
|||
{ |
|||
if (!IsValidOleThread()) |
|||
return null; |
|||
|
|||
if (fCurrent == null) |
|||
fCurrent = new OleContext(); |
|||
return fCurrent; |
|||
} |
|||
} |
|||
|
|||
|
|||
private OleContext() |
|||
{ |
|||
if (UnmanagedMethods.OleInitialize(IntPtr.Zero) != UnmanagedMethods.HRESULT.S_OK) |
|||
throw new SystemException("Failed to initialize OLE"); |
|||
} |
|||
|
|||
private static bool IsValidOleThread() |
|||
{ |
|||
return Dispatcher.UIThread.CheckAccess() && |
|||
Thread.CurrentThread.GetApartmentState() == ApartmentState.STA; |
|||
} |
|||
|
|||
internal bool RegisterDragDrop(IPlatformHandle hwnd, IDropTarget target) |
|||
{ |
|||
if (hwnd?.HandleDescriptor != "HWND" || target == null) |
|||
return false; |
|||
|
|||
return UnmanagedMethods.RegisterDragDrop(hwnd.Handle, target) == UnmanagedMethods.HRESULT.S_OK; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,141 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.InteropServices.ComTypes; |
|||
using System.Text; |
|||
using Avalonia.Controls.DragDrop; |
|||
using Avalonia.Win32.Interop; |
|||
|
|||
namespace Avalonia.Win32 |
|||
{ |
|||
class OleDataObject : IDragData |
|||
{ |
|||
private IOleDataObject _wrapped; |
|||
|
|||
public OleDataObject(IOleDataObject wrapped) |
|||
{ |
|||
_wrapped = wrapped; |
|||
} |
|||
|
|||
public bool Contains(string dataFormat) |
|||
{ |
|||
return GetDataFormatsCore().Any(df => StringComparer.OrdinalIgnoreCase.Equals(df, dataFormat)); |
|||
} |
|||
|
|||
public IEnumerable<string> GetDataFormats() |
|||
{ |
|||
return GetDataFormatsCore().Distinct(); |
|||
} |
|||
|
|||
public string GetText() |
|||
{ |
|||
return GetDataFromOleHGLOBAL(DataFormats.Text, DVASPECT.DVASPECT_CONTENT) as string; |
|||
} |
|||
|
|||
public IEnumerable<string> GetFileNames() |
|||
{ |
|||
return GetDataFromOleHGLOBAL(DataFormats.FileNames, DVASPECT.DVASPECT_CONTENT) as IEnumerable<string>; |
|||
} |
|||
|
|||
private object GetDataFromOleHGLOBAL(string format, DVASPECT aspect) |
|||
{ |
|||
FORMATETC formatEtc = new FORMATETC(); |
|||
formatEtc.cfFormat = ClipboardFormats.GetFormat(format); |
|||
formatEtc.dwAspect = aspect; |
|||
formatEtc.lindex = -1; |
|||
formatEtc.tymed = TYMED.TYMED_HGLOBAL; |
|||
if (_wrapped.QueryGetData(ref formatEtc) == 0) |
|||
{ |
|||
_wrapped.GetData(ref formatEtc, out STGMEDIUM medium); |
|||
try |
|||
{ |
|||
if (medium.unionmember != IntPtr.Zero && medium.tymed == TYMED.TYMED_HGLOBAL) |
|||
{ |
|||
if (format == DataFormats.Text) |
|||
return ReadStringFromHGlobal(medium.unionmember); |
|||
if (format == DataFormats.FileNames) |
|||
return ReadFileNamesFromHGlobal(medium.unionmember); |
|||
return ReadBytesFromHGlobal(medium.unionmember); |
|||
} |
|||
} |
|||
finally |
|||
{ |
|||
UnmanagedMethods.ReleaseStgMedium(ref medium); |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
private static IEnumerable<string> ReadFileNamesFromHGlobal(IntPtr hGlobal) |
|||
{ |
|||
List<string> files = new List<string>(); |
|||
int fileCount = UnmanagedMethods.DragQueryFile(hGlobal, -1, null, 0); |
|||
if (fileCount > 0) |
|||
{ |
|||
for (int i = 0; i < fileCount; i++) |
|||
{ |
|||
int pathLen = UnmanagedMethods.DragQueryFile(hGlobal, i, null, 0); |
|||
StringBuilder sb = new StringBuilder(pathLen+1); |
|||
|
|||
if (UnmanagedMethods.DragQueryFile(hGlobal, i, sb, sb.Capacity) == pathLen) |
|||
{ |
|||
files.Add(sb.ToString()); |
|||
} |
|||
} |
|||
} |
|||
return files; |
|||
} |
|||
|
|||
private static string ReadStringFromHGlobal(IntPtr hGlobal) |
|||
{ |
|||
IntPtr ptr = UnmanagedMethods.GlobalLock(hGlobal); |
|||
try |
|||
{ |
|||
return Marshal.PtrToStringAuto(ptr); |
|||
} |
|||
finally |
|||
{ |
|||
UnmanagedMethods.GlobalUnlock(hGlobal); |
|||
} |
|||
} |
|||
|
|||
private static byte[] ReadBytesFromHGlobal(IntPtr hGlobal) |
|||
{ |
|||
IntPtr source = UnmanagedMethods.GlobalLock(hGlobal); |
|||
try |
|||
{ |
|||
int size = (int)UnmanagedMethods.GlobalSize(hGlobal).ToInt64(); |
|||
byte[] data = new byte[size]; |
|||
Marshal.Copy(source, data, 0, size); |
|||
return data; |
|||
} |
|||
finally |
|||
{ |
|||
UnmanagedMethods.GlobalUnlock(hGlobal); |
|||
} |
|||
} |
|||
|
|||
private IEnumerable<string> GetDataFormatsCore() |
|||
{ |
|||
var enumFormat = _wrapped.EnumFormatEtc(DATADIR.DATADIR_GET); |
|||
if (enumFormat != null) |
|||
{ |
|||
enumFormat.Reset(); |
|||
FORMATETC[] formats = new FORMATETC[1]; |
|||
int[] fetched = { 1 }; |
|||
while (fetched[0] > 0) |
|||
{ |
|||
fetched[0] = 0; |
|||
if (enumFormat.Next(1, formats, fetched) == 0 && fetched[0] > 0) |
|||
{ |
|||
if (formats[0].ptd != IntPtr.Zero) |
|||
Marshal.FreeCoTaskMem(formats[0].ptd); |
|||
|
|||
yield return ClipboardFormats.GetFormat(formats[0].cfFormat); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,131 @@ |
|||
using System; |
|||
using System.Runtime.InteropServices.ComTypes; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Controls.DragDrop; |
|||
using Avalonia.Input; |
|||
using Avalonia.Interactivity; |
|||
using Avalonia.VisualTree; |
|||
using Avalonia.Win32.Interop; |
|||
|
|||
namespace Avalonia.Win32 |
|||
{ |
|||
class OleDropTarget : IDropTarget |
|||
{ |
|||
private readonly IDragDispatcher _dragDispatcher; |
|||
private readonly IInputElement _target; |
|||
|
|||
private IDragData _currentDrag = null; |
|||
|
|||
public OleDropTarget(IInputElement target) |
|||
{ |
|||
_dragDispatcher = AvaloniaLocator.Current.GetService<IDragDispatcher>(); |
|||
_target = target; |
|||
} |
|||
|
|||
static DropEffect ConvertDropEffect(DragOperation operation) |
|||
{ |
|||
DropEffect result = DropEffect.None; |
|||
if (operation.HasFlag(DragOperation.Copy)) |
|||
result |= DropEffect.Copy; |
|||
if (operation.HasFlag(DragOperation.Move)) |
|||
result |= DropEffect.Move; |
|||
if (operation.HasFlag(DragOperation.Link)) |
|||
result |= DropEffect.Link; |
|||
return result; |
|||
} |
|||
|
|||
static DragOperation ConvertDropEffect(DropEffect effect) |
|||
{ |
|||
DragOperation result = DragOperation.None; |
|||
if (effect.HasFlag(DropEffect.Copy)) |
|||
result |= DragOperation.Copy; |
|||
if (effect.HasFlag(DropEffect.Move)) |
|||
result |= DragOperation.Move; |
|||
if (effect.HasFlag(DropEffect.Link)) |
|||
result |= DragOperation.Link; |
|||
return result; |
|||
} |
|||
|
|||
UnmanagedMethods.HRESULT IDropTarget.DragEnter(IOleDataObject pDataObj, int grfKeyState, long pt, ref DropEffect pdwEffect) |
|||
{ |
|||
if (_dragDispatcher == null) |
|||
{ |
|||
pdwEffect = DropEffect.None; |
|||
return UnmanagedMethods.HRESULT.S_OK; |
|||
} |
|||
|
|||
_currentDrag = new OleDataObject(pDataObj); |
|||
var dragLocation = GetDragLocation(pt); |
|||
|
|||
var operation = ConvertDropEffect(pdwEffect); |
|||
operation = _dragDispatcher.DragEnter(_target, dragLocation, _currentDrag, operation); |
|||
pdwEffect = ConvertDropEffect(operation); |
|||
|
|||
return UnmanagedMethods.HRESULT.S_OK; |
|||
} |
|||
|
|||
UnmanagedMethods.HRESULT IDropTarget.DragOver(int grfKeyState, long pt, ref DropEffect pdwEffect) |
|||
{ |
|||
if (_dragDispatcher == null) |
|||
{ |
|||
pdwEffect = DropEffect.None; |
|||
return UnmanagedMethods.HRESULT.S_OK; |
|||
} |
|||
|
|||
var dragLocation = GetDragLocation(pt); |
|||
|
|||
var operation = ConvertDropEffect(pdwEffect); |
|||
operation = _dragDispatcher.DragOver(_target, dragLocation, _currentDrag, operation); |
|||
pdwEffect = ConvertDropEffect(operation); |
|||
|
|||
return UnmanagedMethods.HRESULT.S_OK; |
|||
} |
|||
|
|||
UnmanagedMethods.HRESULT IDropTarget.DragLeave() |
|||
{ |
|||
try |
|||
{ |
|||
_dragDispatcher?.DragLeave(_target); |
|||
return UnmanagedMethods.HRESULT.S_OK; |
|||
} |
|||
finally |
|||
{ |
|||
_currentDrag = null; |
|||
} |
|||
} |
|||
|
|||
UnmanagedMethods.HRESULT IDropTarget.Drop(IOleDataObject pDataObj, int grfKeyState, long pt, ref DropEffect pdwEffect) |
|||
{ |
|||
try |
|||
{ |
|||
if (_dragDispatcher == null) |
|||
{ |
|||
pdwEffect = DropEffect.None; |
|||
return UnmanagedMethods.HRESULT.S_OK; |
|||
} |
|||
|
|||
_currentDrag= new OleDataObject(pDataObj); |
|||
var dragLocation = GetDragLocation(pt); |
|||
|
|||
var operation = ConvertDropEffect(pdwEffect); |
|||
operation = _dragDispatcher.Drop(_target, dragLocation, _currentDrag, operation); |
|||
pdwEffect = ConvertDropEffect(operation); |
|||
|
|||
return UnmanagedMethods.HRESULT.S_OK; |
|||
} |
|||
finally |
|||
{ |
|||
_currentDrag = null; |
|||
} |
|||
} |
|||
|
|||
private Point GetDragLocation(long dragPoint) |
|||
{ |
|||
int x = (int)dragPoint; |
|||
int y = (int)(dragPoint >> 32); |
|||
|
|||
Point screenPt = new Point(x, y); |
|||
return _target.PointToClient(screenPt); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue