| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- using System.Diagnostics;
- using System.Runtime.InteropServices;
- using Avalonia;
- using Avalonia.Controls.ApplicationLifetimes;
- using Avalonia.Input;
- using Avalonia.Input.Platform;
- using Avalonia.Media.Imaging;
- using Avalonia.Platform.Storage;
- using PicView.Avalonia.Animations;
- using PicView.Avalonia.ImageHandling;
- using PicView.Avalonia.Navigation;
- using PicView.Avalonia.ViewModels;
- using PicView.Core.FileHandling;
- using PicView.Core.Localization;
- using PicView.Core.ProcessHandling;
- namespace PicView.Avalonia.Clipboard;
- /// <summary>
- /// Helper class for clipboard operations
- /// </summary>
- public static class ClipboardHelper
- {
- /// <summary>
- /// Duplicates the current file and navigates to it
- /// </summary>
- /// <param name="vm">The main view model</param>
- public static async Task Duplicate(MainViewModel vm)
- {
- if (!NavigationManager.CanNavigate(vm))
- {
- return;
- }
- vm.IsLoading = true;
- var oldPath = vm.FileInfo.FullName;
- var duplicatedPath = await FileHelper.DuplicateAndReturnFileNameAsync(oldPath, vm.FileInfo);
- if (string.IsNullOrWhiteSpace(duplicatedPath) || !File.Exists(duplicatedPath))
- {
- return;
- }
- await Task.WhenAll(AnimationsHelper.CopyAnimation(), NavigationManager.LoadPicFromFile(duplicatedPath, vm));
- }
- /// <summary>
- /// Copies text to the clipboard
- /// </summary>
- /// <param name="text">The text to copy</param>
- /// <returns>A task representing the asynchronous operation</returns>
- public static async Task<bool> CopyTextToClipboard(string text)
- {
- var clipboard = GetClipboard();
- if (clipboard == null || string.IsNullOrWhiteSpace(text))
- {
- return false;
- }
- try
- {
- await Task.WhenAll(clipboard.SetTextAsync(text), AnimationsHelper.CopyAnimation());
- return true;
- }
- catch (Exception)
- {
- return false;
- }
- }
- /// <summary>
- /// Copies a file to the clipboard
- /// </summary>
- /// <param name="file">Path to the file</param>
- /// <param name="vm">The main view model</param>
- /// <returns>A task representing the asynchronous operation</returns>
- public static async Task<bool> CopyFileToClipboard(string? file, MainViewModel vm)
- {
- if (string.IsNullOrWhiteSpace(file))
- {
- return false;
- }
- try
- {
- var success = await Task.Run(() => vm.PlatformService.CopyFile(file));
- if (success)
- {
- await AnimationsHelper.CopyAnimation();
- }
- return success;
- }
- catch (Exception)
- {
- return false;
- }
- }
- /// <summary>
- /// Copies the current image to the clipboard
- /// </summary>
- /// <param name="vm">The main view model</param>
- /// <returns>A task representing the asynchronous operation</returns>
- public static async Task<bool> CopyImageToClipboard(MainViewModel vm)
- {
- var clipboard = GetClipboard();
- if (clipboard == null || vm.ImageSource is not Bitmap bitmap)
- {
- return false;
- }
- try
- {
- await clipboard.ClearAsync();
- // Handle for Windows
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- await Task.WhenAll(vm.PlatformService.CopyImageToClipboard(bitmap), AnimationsHelper.CopyAnimation());
- return false;
- }
- using var ms = new MemoryStream();
- bitmap.Save(ms);
- var dataObject = new DataObject();
- dataObject.Set("image/png", ms.ToArray());
- await Task.WhenAll(clipboard.SetDataObjectAsync(dataObject), AnimationsHelper.CopyAnimation());
- }
- catch (Exception)
- {
- return false;
- }
- return true;
- }
- /// <summary>
- /// Copies an image as base64 string to the clipboard
- /// </summary>
- /// <param name="path">Optional path to the image file</param>
- /// <param name="vm">The main view model</param>
- /// <returns>A task representing the asynchronous operation</returns>
- public static async Task<bool> CopyBase64ToClipboard(string path, MainViewModel vm)
- {
- var clipboard = GetClipboard();
- if (clipboard == null)
- {
- return false;
- }
- try
- {
- string base64;
- if (string.IsNullOrWhiteSpace(path))
- {
- switch (vm.ImageType)
- {
- case ImageType.AnimatedGif:
- case ImageType.AnimatedWebp:
- throw new ArgumentOutOfRangeException(nameof(vm.ImageType), "Animated images are not supported");
- case ImageType.Bitmap:
- if (vm.ImageSource is not Bitmap bitmap)
- {
- return false;
- }
- using (var stream = new MemoryStream())
- {
- bitmap.Save(stream, quality: 100);
- base64 = Convert.ToBase64String(stream.ToArray());
- }
- break;
- case ImageType.Svg:
- return false;
- default:
- throw new ArgumentOutOfRangeException(nameof(vm.ImageType), $"Unsupported image type: {vm.ImageType}");
- }
- }
- else
- {
- base64 = Convert.ToBase64String(await File.ReadAllBytesAsync(path));
- }
- if (string.IsNullOrEmpty(base64))
- {
- return false;
- }
- await Task.WhenAll(clipboard.SetTextAsync(base64), AnimationsHelper.CopyAnimation());
- return true;
- }
- catch (Exception)
- {
- return false;
- }
- }
- /// <summary>
- /// Cuts a file to the clipboard (copy + mark for deletion on paste)
- /// </summary>
- /// <param name="path">Path to the file</param>
- /// <param name="vm">The main view model</param>
- /// <returns>A task representing the asynchronous operation</returns>
- public static async Task<bool> CutFile(string path, MainViewModel vm)
- {
- if (string.IsNullOrWhiteSpace(path))
- {
- return false;
- }
- try
- {
- var success = await Task.Run(() => vm.PlatformService.CutFile(path));
- if (success)
- {
- await AnimationsHelper.CopyAnimation();
- }
- return success;
- }
- catch (Exception)
- {
- return false;
- }
- }
- /// <summary>
- /// Pastes content from the clipboard
- /// </summary>
- /// <param name="vm">The main view model</param>
- /// <returns>A task representing the asynchronous operation</returns>
- public static async Task Paste(MainViewModel vm)
- {
- var clipboard = GetClipboard();
- if (clipboard == null)
- {
- return;
- }
- try
- {
- // Try to paste files first
- var files = await clipboard.GetDataAsync(DataFormats.Files);
- if (files != null)
- {
- await PasteFiles(files, vm);
- return;
- }
- // Try to paste text (URLs, file paths)
- var text = await clipboard.GetTextAsync();
- if (!string.IsNullOrWhiteSpace(text))
- {
- await NavigationManager.LoadPicFromStringAsync(text, vm).ConfigureAwait(false);
- return;
- }
- // Try to paste image data
- await PasteClipboardImage(vm, clipboard);
- }
- catch (Exception ex)
- {
- // Log or handle the exception
- Debug.WriteLine($"Paste operation failed: {ex.Message}");
- }
- }
- /// <summary>
- /// Pastes an image from the clipboard
- /// </summary>
- /// <param name="vm">The main view model</param>
- /// <param name="clipboard">The clipboard instance</param>
- /// <returns>A task representing the asynchronous operation</returns>
- public static async Task PasteClipboardImage(MainViewModel vm, IClipboard clipboard)
- {
- var name = TranslationHelper.Translation.ClipboardImage;
- var imageType = ImageType.Bitmap;
- // List of formats to try
- string[] formats = new[]
- {
- "PNG", "image/jpeg", "image/png", "image/bmp", "BMP",
- "JPG", "JPEG", "image/tiff", "GIF", "image/gif"
- };
- foreach (var format in formats)
- {
- var bitmap = await GetBitmapFromBytes(format);
- if (bitmap != null)
- {
- await UpdateImage.SetSingleImageAsync(bitmap, imageType, name, vm);
- return;
- }
- }
- // Windows-specific clipboard handling
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- var bitmap = await vm.PlatformService.GetImageFromClipboard();
- if (bitmap != null)
- {
- await UpdateImage.SetSingleImageAsync(bitmap, imageType, name, vm);
- }
- }
- async Task<Bitmap?> GetBitmapFromBytes(string format)
- {
- try
- {
- var data = await clipboard.GetDataAsync(format);
- if (data is byte[] dataBytes)
- {
- using var memoryStream = new MemoryStream(dataBytes);
- return new Bitmap(memoryStream);
- }
- }
- catch (Exception)
- {
- // Ignore format errors and try next format
- }
- return null;
- }
- }
- /// <summary>
- /// Handles pasting files from the clipboard
- /// </summary>
- private static async Task PasteFiles(object files, MainViewModel vm)
- {
- if (files is IEnumerable<IStorageItem> items)
- {
- var storageItems = items.ToArray();
- if (storageItems.Length > 0)
- {
- // Load the first file
- var firstFile = storageItems[0];
- var firstPath = RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
- ? firstFile.Path.AbsolutePath
- : firstFile.Path.LocalPath;
- await NavigationManager.LoadPicFromStringAsync(firstPath, vm).ConfigureAwait(false);
- // Open consecutive files in a new process
- foreach (var file in storageItems.Skip(1))
- {
- var path = RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
- ? file.Path.AbsolutePath
- : file.Path.LocalPath;
- ProcessHelper.StartNewProcess(path);
- }
- }
- }
- else if (files is IStorageItem singleFile)
- {
- var path = RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
- ? singleFile.Path.AbsolutePath
- : singleFile.Path.LocalPath;
- await NavigationManager.LoadPicFromStringAsync(path, vm).ConfigureAwait(false);
- }
- }
- /// <summary>
- /// Gets the clipboard instance from the current application
- /// </summary>
- /// <returns>The clipboard instance or null if not available</returns>
- private static IClipboard? GetClipboard()
- {
- if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- {
- return desktop.MainWindow.Clipboard;
- }
- return null;
- }
- }
|