diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/Configuration/ProgramsConfigurationRoot.cs b/src/AppCommon/FileTime.App.Core.Abstraction/Configuration/ProgramsConfigurationRoot.cs new file mode 100644 index 0000000..c42d1b1 --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/Configuration/ProgramsConfigurationRoot.cs @@ -0,0 +1,7 @@ +namespace FileTime.App.Core.Configuration; + +public class ProgramsConfigurationRoot +{ + public ProgramsConfiguration Linux { get; set; } = new(); + public ProgramsConfiguration Windows { get; set; } = new(); +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/Services/IProgramsService.cs b/src/AppCommon/FileTime.App.Core.Abstraction/Services/IProgramsService.cs new file mode 100644 index 0000000..d82d6b7 --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/Services/IProgramsService.cs @@ -0,0 +1,9 @@ +using FileTime.App.Core.Configuration; + +namespace FileTime.App.Core.Services; + +public interface IProgramsService +{ + ProgramConfiguration? GetEditorProgram(bool getNext = false); + void ResetLastGoodEditor(); +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/EditCommand.cs b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/EditCommand.cs new file mode 100644 index 0000000..cca0b71 --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/EditCommand.cs @@ -0,0 +1,14 @@ +namespace FileTime.App.Core.UserCommand; + +public class EditCommand : IIdentifiableUserCommand +{ + public const string CommandName = "edit"; + public static readonly EditCommand Instance = new(); + + private EditCommand() + { + } + + public string UserCommandID => CommandName; + public string Title => "Edit"; +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core/Configuration/MainConfiguration.cs b/src/AppCommon/FileTime.App.Core/Configuration/MainConfiguration.cs index dbb98f4..3a3323d 100644 --- a/src/AppCommon/FileTime.App.Core/Configuration/MainConfiguration.cs +++ b/src/AppCommon/FileTime.App.Core/Configuration/MainConfiguration.cs @@ -6,7 +6,7 @@ namespace FileTime.App.Core.Configuration; public class MainConfiguration { - private static readonly Lazy> _defaultKeybindings = new(InitDefaultKeyBindings); + private static readonly Lazy> DefaultKeybindings = new(InitDefaultKeyBindings); public static Dictionary Configuration { get; } @@ -18,7 +18,7 @@ public class MainConfiguration }; PopulateDefaultEditorPrograms(Configuration); - PopulateDefaultKeyBindings(Configuration, _defaultKeybindings.Value, + PopulateDefaultKeyBindings(Configuration, DefaultKeybindings.Value, SectionNames.KeybindingSectionName + ":" + nameof(KeyBindingConfiguration.DefaultKeyBindings)); } @@ -60,7 +60,7 @@ public class MainConfiguration new(CreateContainer.CommandName, new[] {Keys.C, Keys.C}), new(CreateElementCommand.CommandName, new[] {Keys.C, Keys.E}), //new CommandBindingConfiguration(ConfigCommand.Cut, new[] { Keys.D, Keys.D }), - //new CommandBindingConfiguration(ConfigCommand.Edit, new KeyConfig(Keys.F4)), + new(EditCommand.CommandName, new[] {Keys.F4}), new(EnterRapidTravelCommand.CommandName, new KeyConfig(Keys.Comma, shift: true)), new(EnterRapidTravelCommand.CommandName, new KeyConfig(Keys.Question, shift: true)), new(GoBackCommand.CommandName, new KeyConfig(Keys.Left, alt: true)), @@ -120,23 +120,35 @@ public class MainConfiguration private static void PopulateDefaultEditorPrograms(Dictionary configuration) { - var editorPrograms = new List() + var linuxEditorPrograms = new List + { + new(@"gedit"), + }; + var windowsEditorPrograms = new List { - new(@"c:\Program Files\Notepad++\notepad++.exe"), new("notepad.exe"), }; + PopulateDefaultEditorPrograms(configuration, linuxEditorPrograms, nameof(ProgramsConfigurationRoot.Linux)); + PopulateDefaultEditorPrograms(configuration, windowsEditorPrograms, nameof(ProgramsConfigurationRoot.Windows)); + } + + private static void PopulateDefaultEditorPrograms( + Dictionary configuration, + List editorPrograms, + string os) + { for (var i = 0; i < editorPrograms.Count; i++) { if (editorPrograms[i].Path is not { } path) continue; configuration.Add( - $"{SectionNames.ProgramsSectionName}:{nameof(ProgramsConfiguration.DefaultEditorPrograms)}:[{i}]:{nameof(ProgramConfiguration.Path)}", + $"{SectionNames.ProgramsSectionName}:{os}:{nameof(ProgramsConfiguration.DefaultEditorPrograms)}:[{i}]:{nameof(ProgramConfiguration.Path)}", path); if (editorPrograms[i].Arguments is { } arguments) { configuration.Add( - $"{SectionNames.ProgramsSectionName}:{nameof(ProgramsConfiguration.DefaultEditorPrograms)}:[{i}]:{nameof(ProgramConfiguration.Arguments)}", + $"{SectionNames.ProgramsSectionName}:{os}:{nameof(ProgramsConfiguration.DefaultEditorPrograms)}:[{i}]:{nameof(ProgramConfiguration.Arguments)}", arguments); } } diff --git a/src/AppCommon/FileTime.App.Core/Services/ProgramsService.cs b/src/AppCommon/FileTime.App.Core/Services/ProgramsService.cs new file mode 100644 index 0000000..ab40d7f --- /dev/null +++ b/src/AppCommon/FileTime.App.Core/Services/ProgramsService.cs @@ -0,0 +1,91 @@ +using System.Runtime.InteropServices; +using FileTime.App.Core.Configuration; +using Microsoft.Extensions.Options; + +namespace FileTime.App.Core.Services; + +public class ProgramsService : IProgramsService +{ + private enum Os + { + Linux, + Windows, + } + + private readonly Os _os; + private readonly IOptionsMonitor _configuration; + private int _lastGoodEditorProgramIndex; + private readonly List _lastEditorPrograms = new(); + + public ProgramsService(IOptionsMonitor configuration) + { + _configuration = configuration; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + _os = Os.Windows; + } + else + { + _os = Os.Linux; + } + } + + public ProgramConfiguration? GetEditorProgram(bool getNext = false) + { + GeneratePrograms(); + + if (getNext) + { + _lastGoodEditorProgramIndex++; + } + + if (_lastGoodEditorProgramIndex < 0) + { + _lastGoodEditorProgramIndex = 0; + } + + if (_lastEditorPrograms.Count <= _lastGoodEditorProgramIndex) + { + ResetLastGoodEditor(); + return null; + } + + return _lastEditorPrograms[_lastGoodEditorProgramIndex]; + } + + private void GeneratePrograms() + { + var programsConfig = _os switch + { + Os.Windows => _configuration.CurrentValue.Windows, + _ => _configuration.CurrentValue.Linux + }; + var programConfigs = programsConfig.EditorPrograms.Count == 0 + ? programsConfig.DefaultEditorPrograms + : programsConfig.EditorPrograms.Concat(programsConfig.DefaultEditorPrograms).ToList(); + + var generateNew = programConfigs.Count != _lastEditorPrograms.Count; + + if (!generateNew) + { + for (var i = 0; i < programConfigs.Count; i++) + { + if (programConfigs[i].Path != _lastEditorPrograms[i].Path + || programConfigs[i].Arguments != _lastEditorPrograms[i].Arguments) + { + generateNew = true; + break; + } + } + } + + if (generateNew) + { + _lastEditorPrograms.Clear(); + _lastEditorPrograms.AddRange(programConfigs); + _lastGoodEditorProgramIndex = -1; + } + } + + public void ResetLastGoodEditor() => _lastGoodEditorProgramIndex = -1; +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs index b153af5..1ff7e98 100644 --- a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs +++ b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs @@ -9,6 +9,7 @@ using FileTime.Core.Enums; using FileTime.Core.Interactions; using FileTime.Core.Models; using FileTime.Core.Timeline; +using Microsoft.Extensions.Logging; namespace FileTime.App.Core.Services.UserCommandHandler; @@ -22,6 +23,8 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase private readonly IUserCommandHandlerService _userCommandHandlerService; private readonly IContentAccessorFactory _contentAccessorFactory; private readonly IContainerSizeScanProvider _containerSizeScanProvider; + private readonly IProgramsService _programsService; + private readonly ILogger _logger; private IDeclarativeProperty? _currentLocation; private IDeclarativeProperty? _currentSelectedItem; private ITabViewModel? _currentSelectedTab; @@ -35,7 +38,9 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase ITimelessContentProvider timelessContentProvider, IUserCommandHandlerService userCommandHandlerService, IContentAccessorFactory contentAccessorFactory, - IContainerSizeScanProvider containerSizeScanProvider) : base(appState) + IContainerSizeScanProvider containerSizeScanProvider, + IProgramsService programsService, + ILogger logger) : base(appState) { _systemClipboardService = systemClipboardService; _userCommunicationService = userCommunicationService; @@ -45,6 +50,8 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase _userCommandHandlerService = userCommandHandlerService; _contentAccessorFactory = contentAccessorFactory; _containerSizeScanProvider = containerSizeScanProvider; + _programsService = programsService; + _logger = logger; SaveCurrentLocation(l => _currentLocation = l); SaveCurrentSelectedItem(i => _currentSelectedItem = i); SaveSelectedTab(t => _currentSelectedTab = t); @@ -54,16 +61,68 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase new TypeUserCommandHandler(OpenInDefaultFileExplorer), new TypeUserCommandHandler(CopyNativePath), new TypeUserCommandHandler(CopyBase64), + new TypeUserCommandHandler(Edit), new TypeUserCommandHandler(Search), new TypeUserCommandHandler(ScanSize), new TypeUserCommandHandler(SortItems), }); } + private Task Edit() + { + if ( _currentSelectedTab?.CurrentSelectedItem.Value?.BaseItem is not IElement {NativePath: { } filePath}) + return Task.CompletedTask; + + var getNext = false; + while (true) + { + string? execPath = null; + try + { + var editorProgram = _programsService.GetEditorProgram(getNext); + if (editorProgram is null) + { + break; + } + + if (editorProgram.Path is { } executablePath) + { + execPath = executablePath; + if (string.IsNullOrWhiteSpace(editorProgram.Arguments)) + { + Process.Start(executablePath, "\"" + filePath + "\""); + } + else + { + var parts = editorProgram.Arguments.Split("%%1"); + var arguments = string.Join("%%1", parts.Select(p => p.Replace("%1", "\"" + filePath + "\""))).Replace("%%1", "%1"); + Process.Start(executablePath, arguments); + } + } + + //TODO: else + break; + } + catch (System.ComponentModel.Win32Exception e) + { + _logger.LogError(e, "Error while running editor program, possible the executable path does not exists. {ExecutablePath}", execPath); + } + catch (Exception e) + { + _logger.LogError(e, "Unknown error while running editor program"); + } + + getNext = true; + } + + //TODO: else + return Task.CompletedTask; + } + private async Task ScanSize() { if (_currentLocation?.Value is null) return; - + var searchTask = _containerSizeScanProvider.StartSizeScan(_currentLocation.Value); var openContainerCommand = new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, searchTask.SizeSizeScanContainer)); await _userCommandHandlerService.HandleCommandAsync(openContainerCommand); diff --git a/src/AppCommon/FileTime.App.Core/Startup.cs b/src/AppCommon/FileTime.App.Core/Startup.cs index c25e728..de284d8 100644 --- a/src/AppCommon/FileTime.App.Core/Startup.cs +++ b/src/AppCommon/FileTime.App.Core/Startup.cs @@ -36,6 +36,7 @@ public static class Startup serviceCollection.TryAddSingleton(); serviceCollection.TryAddSingleton(); serviceCollection.TryAddSingleton(); + serviceCollection.TryAddSingleton(); return serviceCollection .AddCommandHandlers() @@ -53,7 +54,7 @@ public static class Startup internal static IServiceCollection AddConfiguration(this IServiceCollection serviceCollection, IConfigurationRoot configuration) => serviceCollection - .Configure(configuration.GetSection(SectionNames.ProgramsSectionName)) + .Configure(configuration.GetSection(SectionNames.ProgramsSectionName)) .Configure(configuration.GetSection(SectionNames.KeybindingSectionName)) .AddSingleton(configuration); } \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core/StartupServices/DefaultIdentifiableCommandHandlerRegister.cs b/src/AppCommon/FileTime.App.Core/StartupServices/DefaultIdentifiableCommandHandlerRegister.cs index 1fd97b2..8fb3576 100644 --- a/src/AppCommon/FileTime.App.Core/StartupServices/DefaultIdentifiableCommandHandlerRegister.cs +++ b/src/AppCommon/FileTime.App.Core/StartupServices/DefaultIdentifiableCommandHandlerRegister.cs @@ -20,6 +20,7 @@ public class DefaultIdentifiableCommandHandlerRegister : IStartupHandler AddUserCommand(CreateElementCommand.Instance); AddUserCommand(DeleteCommand.HardDelete); AddUserCommand(DeleteCommand.SoftDelete); + AddUserCommand(EditCommand.Instance); AddUserCommand(EnterRapidTravelCommand.Instance); AddUserCommand(ExitRapidTravelCommand.Instance); AddUserCommand(GoBackCommand.Instance);