using System; using Avalonia.Media.Imaging; using Avalonia.Metadata; using Avalonia.Platform.Storage; using Avalonia.Utilities; namespace Avalonia.Input; /// /// Represents a format usable with the clipboard and drag-and-drop. /// public abstract class DataFormat : IEquatable { private protected DataFormat(DataFormatKind kind, string identifier) { Kind = kind; Identifier = identifier; } /// /// Gets the kind of the data format. /// public DataFormatKind Kind { get; } /// /// Gets the identifier of the data format. /// public string Identifier { get; } /// /// Gets a data format representing plain text. /// Its data type is . /// public static DataFormat Text { get; } = CreateUniversalFormat("Text"); /// /// Gets a data format representing a bitmap. /// Its data type is . /// public static DataFormat Bitmap { get; } = CreateUniversalFormat("Bitmap"); /// /// Gets a data format representing a single file. /// Its data type is . /// public static DataFormat File { get; } = CreateUniversalFormat("File"); /// /// Creates a name for this format, usable by the underlying platform. /// /// The system prefix used to recognize the name as an application format. /// A system name for the format. /// /// This method can only be called if is /// or . /// public string ToSystemName(string applicationPrefix) { ThrowHelper.ThrowIfNull(applicationPrefix); return Kind switch { DataFormatKind.Application => applicationPrefix + Identifier, DataFormatKind.Platform => Identifier, _ => throw new InvalidOperationException($"Cannot get system name for universal format {Identifier}") }; } /// public bool Equals(DataFormat? other) { if (other is null) return false; if (ReferenceEquals(this, other)) return true; return Kind == other.Kind && Identifier == other.Identifier; } /// public override bool Equals(object? obj) => Equals(obj as DataFormat); /// public override int GetHashCode() => ((int)Kind * 397) ^ Identifier.GetHashCode(); /// /// Compares two instances of for equality. /// /// The first instance. /// The second instance. /// true if the two instances are equal; otherwise false. public static bool operator ==(DataFormat? left, DataFormat? right) => Equals(left, right); /// /// Compares two instances of for inequality. /// /// The first instance. /// The second instance. /// true if the two instances are not equal; otherwise false. public static bool operator !=(DataFormat? left, DataFormat? right) => !Equals(left, right); private static DataFormat CreateUniversalFormat(string identifier) where T : class => new(DataFormatKind.Universal, identifier); /// /// Creates a new format specific to the application that returns an array of . /// /// /// /// The format identifier. To avoid conflicts with system identifiers, this value isn't passed to the underlying /// platform directly. However, two different applications using the same identifier /// with or /// are able to share data using this format. /// /// Only ASCII letters (A-Z, a-z), digits (0-9), the dot (.) and the hyphen (-) are accepted. /// /// A new . public static DataFormat CreateBytesApplicationFormat(string identifier) => CreateApplicationFormat(identifier); /// /// Creates a new format specific to the application that returns a . /// /// /// /// The format identifier. To avoid conflicts with system identifiers, this value isn't passed to the underlying /// platform directly. However, two different applications using the same identifier /// with or /// are able to share data using this format. /// /// Only ASCII letters (A-Z, a-z), digits (0-9), the dot (.) and the hyphen (-) are accepted. /// /// A new . public static DataFormat CreateStringApplicationFormat(string identifier) => CreateApplicationFormat(identifier); private static DataFormat CreateApplicationFormat(string identifier) where T : class { if (!IsValidApplicationFormatIdentifier(identifier)) throw new ArgumentException("Invalid application identifier", nameof(identifier)); return new(DataFormatKind.Application, identifier); } /// /// Creates a new format for the current platform that returns an array of . /// /// /// The format identifier. This value is not validated and is passed AS IS to the underlying platform. /// Most systems use mime types, but macOS requires Uniform Type Identifiers (UTI). /// /// A new . public static DataFormat CreateBytesPlatformFormat(string identifier) => CreatePlatformFormat(identifier); /// /// Creates a new format for the current platform that returns a . /// /// /// The format identifier. This value is not validated and is passed AS IS to the underlying platform. /// Most systems use mime types, but macOS requires Uniform Type Identifiers (UTI). /// /// A new . public static DataFormat CreateStringPlatformFormat(string identifier) => CreatePlatformFormat(identifier); private static DataFormat CreatePlatformFormat(string identifier) where T : class { ThrowHelper.ThrowIfNullOrEmpty(identifier); return new(DataFormatKind.Platform, identifier); } /// /// Creates a from a name coming from the underlying platform. /// /// The name. /// The system prefix used to recognize the name as an application format. /// A corresponding to . [PrivateApi] public static DataFormat FromSystemName(string systemName, string applicationPrefix) where T : class { ThrowHelper.ThrowIfNull(systemName); ThrowHelper.ThrowIfNull(applicationPrefix); if (systemName.StartsWith(applicationPrefix, StringComparison.OrdinalIgnoreCase)) { var identifier = systemName.Substring(applicationPrefix.Length); if (IsValidApplicationFormatIdentifier(identifier)) return new(DataFormatKind.Application, identifier); } return new(DataFormatKind.Platform, systemName); } private static bool IsValidApplicationFormatIdentifier(string identifier) { if (string.IsNullOrEmpty(identifier)) return false; foreach (var c in identifier) { if (!IsValidChar(c)) return false; } return true; static bool IsValidChar(char c) => char.IsAsciiLetterOrDigit(c) || c == '.' || c == '-'; } /// public override string ToString() => $"{Kind}: {Identifier}"; }