diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/Services/ISystemClipboardService.cs b/src/AppCommon/FileTime.App.Core.Abstraction/Services/ISystemClipboardService.cs index 67a6664..dde7856 100644 --- a/src/AppCommon/FileTime.App.Core.Abstraction/Services/ISystemClipboardService.cs +++ b/src/AppCommon/FileTime.App.Core.Abstraction/Services/ISystemClipboardService.cs @@ -5,5 +5,6 @@ namespace FileTime.App.Core.Services; public interface ISystemClipboardService { Task CopyToClipboardAsync(string text); - Task> GetFiles(); + Task> GetFilesAsync(); + Task SetFilesAsync(IEnumerable files); } \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/CopyFilesToClipboardCommand.cs b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/CopyFilesToClipboardCommand.cs new file mode 100644 index 0000000..73d500c --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/CopyFilesToClipboardCommand.cs @@ -0,0 +1,14 @@ +namespace FileTime.App.Core.UserCommand; + +public class CopyFilesToClipboardCommand : IIdentifiableUserCommand +{ + public const string CommandName = "copy_to_clipboard"; + + public static readonly CopyFilesToClipboardCommand Instance = new(); + + private CopyFilesToClipboardCommand() + { + } + + public string UserCommandID => CommandName; +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/CreateElement.cs b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/CreateElement.cs deleted file mode 100644 index ea93908..0000000 --- a/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/CreateElement.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace FileTime.App.Core.UserCommand; - -public sealed class CreateElement : IIdentifiableUserCommand -{ - public const string CommandName = "create_element"; - public static CreateElement Instance { get; } = new(); - - private CreateElement() - { - } - - public string UserCommandID => CommandName; -} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/CreateElementCommand.cs b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/CreateElementCommand.cs new file mode 100644 index 0000000..99e2204 --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/CreateElementCommand.cs @@ -0,0 +1,13 @@ +namespace FileTime.App.Core.UserCommand; + +public sealed class CreateElementCommand : IIdentifiableUserCommand +{ + public const string CommandName = "create_element"; + public static CreateElementCommand Instance { get; } = new(); + + private CreateElementCommand() + { + } + + public string UserCommandID => CommandName; +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ItemManipulationUserCommandHandlerService.cs b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ItemManipulationUserCommandHandlerService.cs index 1743145..66697f6 100644 --- a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ItemManipulationUserCommandHandlerService.cs +++ b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ItemManipulationUserCommandHandlerService.cs @@ -18,6 +18,7 @@ using FileTime.Core.Timeline; using InitableService; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using CreateElementCommand = FileTime.App.Core.UserCommand.CreateElementCommand; namespace FileTime.App.Core.Services.UserCommandHandler; @@ -70,11 +71,30 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi new TypeUserCommandHandler(MarkItemAsync), new TypeUserCommandHandler(PasteAsync), new TypeUserCommandHandler(CreateContainerAsync), - new TypeUserCommandHandler(CreateElementAsync), + new TypeUserCommandHandler(CreateElementAsync), new TypeUserCommandHandler(PasteFilesFromClipboardAsync), + new TypeUserCommandHandler(CopyFilesToClipboardAsync), }); } + private async Task CopyFilesToClipboardAsync() + { + var list = new List(); + if ((_markedItems?.Collection?.Count ?? 0) > 0) + { + list.AddRange(_markedItems!.Collection!); + } + else if(_currentSelectedItem?.BaseItem?.FullName is { } selectedItemName) + { + list.Add(selectedItemName); + } + + if (list.Count > 0) + { + await _systemClipboardService.SetFilesAsync(list); + } + } + private async Task PasteFilesFromClipboardAsync(PasteFilesFromClipboardCommand command) => await (command.PasteMode switch { @@ -88,7 +108,7 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi { if (_currentLocation?.FullName is not { }) return; - var files = (await _systemClipboardService.GetFiles()).ToList(); + var files = (await _systemClipboardService.GetFilesAsync()).ToList(); var copyCommandFactory = _serviceProvider.GetRequiredService(); var copyCommand = copyCommandFactory.GenerateCommand(files, mode, _currentLocation.FullName); @@ -187,7 +207,7 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi var command = _serviceProvider .GetInitableResolver(_currentLocation.FullName, newContainerName) - .GetRequiredService(); + .GetRequiredService(); await AddCommandAsync(command); } diff --git a/src/AppCommon/FileTime.App.Core/StartupServices/DefaultIdentifiableCommandHandlerRegister.cs b/src/AppCommon/FileTime.App.Core/StartupServices/DefaultIdentifiableCommandHandlerRegister.cs index b1d4219..3016211 100644 --- a/src/AppCommon/FileTime.App.Core/StartupServices/DefaultIdentifiableCommandHandlerRegister.cs +++ b/src/AppCommon/FileTime.App.Core/StartupServices/DefaultIdentifiableCommandHandlerRegister.cs @@ -14,9 +14,10 @@ public class DefaultIdentifiableCommandHandlerRegister : IStartupHandler AddUserCommand(CloseTabCommand.Instance); AddUserCommand(CopyCommand.Instance); AddUserCommand(CopyBase64Command.Instance); + AddUserCommand(CopyFilesToClipboardCommand.Instance); AddUserCommand(CopyNativePathCommand.Instance); AddUserCommand(CreateContainer.Instance); - AddUserCommand(CreateElement.Instance); + AddUserCommand(CreateElementCommand.Instance); AddUserCommand(DeleteCommand.HardDelete); AddUserCommand(DeleteCommand.SoftDelete); AddUserCommand(EnterRapidTravelCommand.Instance); diff --git a/src/Core/FileTime.Core.Abstraction/Timeline/ITimelessContentProvider.cs b/src/Core/FileTime.Core.Abstraction/Timeline/ITimelessContentProvider.cs index 616fc4b..74a7afd 100644 --- a/src/Core/FileTime.Core.Abstraction/Timeline/ITimelessContentProvider.cs +++ b/src/Core/FileTime.Core.Abstraction/Timeline/ITimelessContentProvider.cs @@ -16,4 +16,5 @@ public interface ITimelessContentProvider Task GetItemByNativePathAsync(NativePath nativePath, PointInTime? pointInTime = null); FullName? GetFullNameByNativePath(NativePath nativePath); + NativePath? GetNativePathByFullName(FullName fullName); } \ No newline at end of file diff --git a/src/Core/FileTime.Core.Timeline/TimelessContentProvider.cs b/src/Core/FileTime.Core.Timeline/TimelessContentProvider.cs index 90c1e7a..27f8465 100644 --- a/src/Core/FileTime.Core.Timeline/TimelessContentProvider.cs +++ b/src/Core/FileTime.Core.Timeline/TimelessContentProvider.cs @@ -56,4 +56,16 @@ public class TimelessContentProvider : ITimelessContentProvider return null; } + + public NativePath? GetNativePathByFullName(FullName fullName) + { + foreach (var contentProvider in _contentProviderRegistry.ContentProviders) + { + if(!contentProvider.CanHandlePath(fullName)) continue; + + return contentProvider.GetNativePath(fullName); + } + + return null; + } } \ No newline at end of file diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/MainConfiguration.cs b/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/MainConfiguration.cs index 115fd74..654c56e 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/MainConfiguration.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/MainConfiguration.cs @@ -50,9 +50,10 @@ public static class MainConfiguration new(CopyCommand.CommandName, new[] {Key.Y, Key.Y}), //new CommandBindingConfiguration(ConfigCommand.CopyHash, new[] { Key.C, Key.H }), new(CopyNativePathCommand.CommandName, new[] {Key.C, Key.P}), + new(CopyFilesToClipboardCommand.CommandName, new[] {Key.Y, Key.C}), new(CreateContainer.CommandName, Key.F7), new(CreateContainer.CommandName, new[] {Key.C, Key.C}), - new(CreateElement.CommandName, new[] {Key.C, Key.E}), + new(CreateElementCommand.CommandName, new[] {Key.C, Key.E}), //new CommandBindingConfiguration(ConfigCommand.Cut, new[] { Key.D, Key.D }), //new CommandBindingConfiguration(ConfigCommand.Edit, new KeyConfig(Key.F4)), new(EnterRapidTravelCommand.CommandName, new KeyConfig(Key.OemComma, shift: true)), diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Services/SystemClipboardService.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Services/SystemClipboardService.cs index e3d899d..739620e 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/Services/SystemClipboardService.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Services/SystemClipboardService.cs @@ -1,5 +1,5 @@ using System.Net; -using System.Text.Encodings.Web; +using Avalonia.Input; using Avalonia.Platform.Storage; using FileTime.App.Core.Services; using FileTime.Core.Models; @@ -10,14 +10,12 @@ namespace FileTime.GuiApp.Services; public class SystemClipboardService : ISystemClipboardService { private const string ClipboardContentFiles = "Files"; - + private readonly ITimelessContentProvider _timelessContentProvider; - public IUiAccessor UiAccessor { get; internal set; } + public IUiAccessor UiAccessor { get; internal set; } = null!; public SystemClipboardService(ITimelessContentProvider timelessContentProvider) - { - _timelessContentProvider = timelessContentProvider; - } + => _timelessContentProvider = timelessContentProvider; public async Task CopyToClipboardAsync(string text) { @@ -31,7 +29,7 @@ public class SystemClipboardService : ISystemClipboardService await clipboard.SetTextAsync(text); } - public async Task> GetFiles() + public async Task> GetFilesAsync() { var clipboard = UiAccessor.GetTopLevel()?.Clipboard; @@ -48,11 +46,50 @@ public class SystemClipboardService : ISystemClipboardService if (obj is IEnumerable storageItems) { return storageItems - .Select(i => _timelessContentProvider.GetFullNameByNativePath(new NativePath(WebUtility.UrlDecode(i.Path.AbsolutePath)))) - .Where(i => i != null) - .OfType(); + .Select(i => _timelessContentProvider.GetFullNameByNativePath(new NativePath(WebUtility.UrlDecode(i.Path.AbsolutePath)))) + .Where(i => i != null) + .OfType(); } - + return Enumerable.Empty(); } + + public async Task SetFilesAsync(IEnumerable files) + { + var clipboard = UiAccessor.GetTopLevel()?.Clipboard; + + if (clipboard is null) + { + return; + } + + var topLevel = UiAccessor.GetTopLevel(); + + if (topLevel is null) + { + //TODO: + return; + } + + var fileNativePaths = files + .Select(i => _timelessContentProvider.GetNativePathByFullName(i)) + .Where(i => i != null) + .OfType(); + + var targetFiles = new List(); + foreach (var fileNativePath in fileNativePaths) + { + var file = await UiAccessor.InvokeOnUIThread(async () => await topLevel.StorageProvider.TryGetFileFromPathAsync(fileNativePath.Path)); + //TODO: Handle null + if (file != null) + { + targetFiles.Add(file); + } + } + + DataObject dataObject = new(); + dataObject.Set(ClipboardContentFiles, targetFiles); + + await clipboard.SetDataObjectAsync(dataObject); + } } \ No newline at end of file