A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

142 lines
4.8 KiB

using System;
using System.IO;
using System.Linq;
using Avalonia.Input;
using Avalonia.Logging;
using Avalonia.Native.Interop;
namespace Avalonia.Native;
/// <summary>
/// Wraps a <see cref="IDataTransferItem"/> into a <see cref="IAvnClipboardDataItem"/>.
/// This class is called by native code.
/// </summary>
/// <param name="item">The item to wrap.</param>
internal sealed class DataTransferItemToAvnClipboardDataItemWrapper(IDataTransferItem item)
: NativeOwned, IAvnClipboardDataItem
{
private readonly IDataTransferItem _item = item;
IAvnStringArray IAvnClipboardDataItem.ProvideFormats()
=> new AvnStringArray(_item.Formats.Where(f => f.Kind != DataFormatKind.InProcess).Select(ClipboardDataFormatHelper.ToNativeFormat));
IAvnClipboardDataValue? IAvnClipboardDataItem.GetValue(string format)
{
if (FindDataFormat(format) is { } dataFormat)
{
if (DataFormat.Text.Equals(dataFormat))
return new StringValue(_item.TryGetValue(DataFormat.Text) ?? string.Empty);
if (DataFormat.File.Equals(dataFormat))
return _item.TryGetValue(DataFormat.File) is { } file ? new StringValue(file.Path.AbsoluteUri) : null;
if (DataFormat.Bitmap.Equals(dataFormat))
{
if (_item.TryGetValue(DataFormat.Bitmap) is { } bitmap)
{
var memoryStream = new MemoryStream();
bitmap.Save(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
return new StreamValue(memoryStream);
}
return null;
}
if (dataFormat is DataFormat<string> stringFormat)
return _item.TryGetValue(stringFormat) is { } stringValue ? new StringValue(stringValue) : null;
if (dataFormat is DataFormat<byte[]> bytesFormat)
return _item.TryGetValue(bytesFormat) is { } bytes ? new BytesValue(bytes) : null;
}
Logger.TryGet(LogEventLevel.Warning, LogArea.macOSPlatform)
?.Log(this, "Unsupported data format {Format}", format);
return null;
}
private DataFormat? FindDataFormat(string nativeFormat)
{
var formats = _item.Formats;
var count = formats.Count;
for (var i = 0; i < count; i++)
{
var format = formats[i];
if (format.Kind == DataFormatKind.InProcess)
continue;
if (ClipboardDataFormatHelper.ToNativeFormat(format) == nativeFormat)
return format;
}
return null;
}
private sealed class StringValue(string value) : NativeOwned, IAvnClipboardDataValue
{
private readonly string _value = value;
int IAvnClipboardDataValue.IsString()
=> true.AsComBool();
IAvnString IAvnClipboardDataValue.AsString()
=> new AvnString(_value);
IntPtr IAvnClipboardDataValue.ByteLength
=> throw new InvalidOperationException();
unsafe void IAvnClipboardDataValue.CopyBytesTo(void* buffer)
=> throw new InvalidOperationException();
}
private sealed class BytesValue(ReadOnlyMemory<byte> value) : NativeOwned, IAvnClipboardDataValue
{
private readonly ReadOnlyMemory<byte> _value = value;
int IAvnClipboardDataValue.IsString()
=> false.AsComBool();
IAvnString IAvnClipboardDataValue.AsString()
=> throw new InvalidOperationException();
IntPtr IAvnClipboardDataValue.ByteLength
=> new(_value.Length);
unsafe void IAvnClipboardDataValue.CopyBytesTo(void* buffer)
=> _value.Span.CopyTo(new Span<byte>(buffer, _value.Length));
}
private sealed class StreamValue(MemoryStream value) : NativeOwned, IAvnClipboardDataValue
{
private readonly MemoryStream _value = value;
private readonly byte[] _buffer = new byte[1024 * 1024];
int IAvnClipboardDataValue.IsString()
=> false.AsComBool();
IAvnString IAvnClipboardDataValue.AsString()
=> throw new InvalidOperationException();
IntPtr IAvnClipboardDataValue.ByteLength
#pragma warning disable CA2020 // overflow in unchecked context
=> (IntPtr)_value.Length;
#pragma warning restore CA2020
unsafe void IAvnClipboardDataValue.CopyBytesTo(void* output)
{
long totalCopied = 0;
while (true)
{
var read = _value.Read(_buffer, 0, _buffer.Length);
if (read == 0)
break;
var destinationSpan = new Span<byte>((byte*)output + totalCopied, read);
_buffer.AsSpan(0, read).CopyTo(destinationSpan);
totalCopied += read;
}
}
}
}