diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt index a36a6af3e..60142b346 100644 --- a/.github/actions/spelling/allow.txt +++ b/.github/actions/spelling/allow.txt @@ -6,3 +6,6 @@ runcount Firefox Português Português (Brasil) +favicons +moz +workaround diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 5b3419041..81b9aba97 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -103,3 +103,8 @@ Reloadable metadatas WMP VSTHRD +CJK +Msix +dummyprofile +browserbookmark +copyurl diff --git a/.github/actions/spelling/patterns.txt b/.github/actions/spelling/patterns.txt index f308ec599..faf1c3978 100644 --- a/.github/actions/spelling/patterns.txt +++ b/.github/actions/spelling/patterns.txt @@ -124,6 +124,10 @@ # version suffix v# (?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_])) +# Non-English +[a-zA-Z]*[ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]* + + \bjjw24\b \bappref-ms\b \bTobiasSekan\b @@ -134,3 +138,13 @@ \bčeština\b \bPortuguês\b \bIoc\b +\bXiao\s*He\b +\bZi\s*Ran\s*Ma\b +\bWei\s*Ruan\b +\bZhi\s*Neng\s*ABC\b +\bZi\s*Guang\s*Pin\s*Yin\b +\bPin\s*Yin\s*Jia\s*Jia\b +\bXing\s*Kong\s*Jian\s*Dao\b +\bDa\s*Niu\b +\bXiao\s*Lang\b +\b[Ss]ettings [Ss]ettings\b diff --git a/.github/update_release_pr.py b/.github/update_release_pr.py index 37d4a8683..be523bfe8 100644 --- a/.github/update_release_pr.py +++ b/.github/update_release_pr.py @@ -89,7 +89,7 @@ def get_prs( count += 1 print( - f"Found {count} PRs with {label if label else 'no filter on'} label, state as {state}, and milestone {pr["milestone"] if pr["milestone"] is not None else "None"}" + f"Found {count} PRs with {label if label else 'no filter on'} label, state as {state}, and milestone {milestone_title if milestone_title else "any"}" ) return pr_list diff --git a/.github/workflows/default_plugins.yml b/.github/workflows/default_plugins.yml index ec8dfcd4e..83e830d75 100644 --- a/.github/workflows/default_plugins.yml +++ b/.github/workflows/default_plugins.yml @@ -10,11 +10,11 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: - dotnet-version: 7.0.x + dotnet-version: 9.0.x - name: Update Plugins To Production Version run: | @@ -42,7 +42,7 @@ jobs: - name: Build BrowserBookmark run: | - dotnet publish 'Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.BrowserBookmark" + dotnet publish 'Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj' --framework net9.0-windows -c Release -o "Flow.Launcher.Plugin.BrowserBookmark" 7z a -tzip "Flow.Launcher.Plugin.BrowserBookmark.zip" "./Flow.Launcher.Plugin.BrowserBookmark/*" rm -r "Flow.Launcher.Plugin.BrowserBookmark" @@ -66,7 +66,7 @@ jobs: - name: Build Calculator run: | - dotnet publish 'Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.Calculator" + dotnet publish 'Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj' --framework net9.0-windows -c Release -o "Flow.Launcher.Plugin.Calculator" 7z a -tzip "Flow.Launcher.Plugin.Calculator.zip" "./Flow.Launcher.Plugin.Calculator/*" rm -r "Flow.Launcher.Plugin.Calculator" @@ -90,7 +90,7 @@ jobs: - name: Build Explorer run: | - dotnet publish 'Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.Explorer" + dotnet publish 'Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj' --framework net9.0-windows -c Release -o "Flow.Launcher.Plugin.Explorer" 7z a -tzip "Flow.Launcher.Plugin.Explorer.zip" "./Flow.Launcher.Plugin.Explorer/*" rm -r "Flow.Launcher.Plugin.Explorer" @@ -114,7 +114,7 @@ jobs: - name: Build PluginIndicator run: | - dotnet publish 'Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.PluginIndicator" + dotnet publish 'Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj' --framework net9.0-windows -c Release -o "Flow.Launcher.Plugin.PluginIndicator" 7z a -tzip "Flow.Launcher.Plugin.PluginIndicator.zip" "./Flow.Launcher.Plugin.PluginIndicator/*" rm -r "Flow.Launcher.Plugin.PluginIndicator" @@ -138,7 +138,7 @@ jobs: - name: Build PluginsManager run: | - dotnet publish 'Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.PluginsManager" + dotnet publish 'Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj' --framework net9.0-windows -c Release -o "Flow.Launcher.Plugin.PluginsManager" 7z a -tzip "Flow.Launcher.Plugin.PluginsManager.zip" "./Flow.Launcher.Plugin.PluginsManager/*" rm -r "Flow.Launcher.Plugin.PluginsManager" @@ -162,7 +162,7 @@ jobs: - name: Build ProcessKiller run: | - dotnet publish 'Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.ProcessKiller" + dotnet publish 'Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj' --framework net9.0-windows -c Release -o "Flow.Launcher.Plugin.ProcessKiller" 7z a -tzip "Flow.Launcher.Plugin.ProcessKiller.zip" "./Flow.Launcher.Plugin.ProcessKiller/*" rm -r "Flow.Launcher.Plugin.ProcessKiller" @@ -186,7 +186,7 @@ jobs: - name: Build Program run: | - dotnet publish 'Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj' --framework net7.0-windows10.0.19041.0 -c Release -o "Flow.Launcher.Plugin.Program" + dotnet publish 'Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj' --framework net9.0-windows10.0.19041.0 -c Release -o "Flow.Launcher.Plugin.Program" 7z a -tzip "Flow.Launcher.Plugin.Program.zip" "./Flow.Launcher.Plugin.Program/*" rm -r "Flow.Launcher.Plugin.Program" @@ -210,7 +210,7 @@ jobs: - name: Build Shell run: | - dotnet publish 'Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.Shell" + dotnet publish 'Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj' --framework net9.0-windows -c Release -o "Flow.Launcher.Plugin.Shell" 7z a -tzip "Flow.Launcher.Plugin.Shell.zip" "./Flow.Launcher.Plugin.Shell/*" rm -r "Flow.Launcher.Plugin.Shell" @@ -234,7 +234,7 @@ jobs: - name: Build Sys run: | - dotnet publish 'Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.Sys" + dotnet publish 'Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj' --framework net9.0-windows -c Release -o "Flow.Launcher.Plugin.Sys" 7z a -tzip "Flow.Launcher.Plugin.Sys.zip" "./Flow.Launcher.Plugin.Sys/*" rm -r "Flow.Launcher.Plugin.Sys" @@ -258,7 +258,7 @@ jobs: - name: Build Url run: | - dotnet publish 'Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.Url" + dotnet publish 'Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj' --framework net9.0-windows -c Release -o "Flow.Launcher.Plugin.Url" 7z a -tzip "Flow.Launcher.Plugin.Url.zip" "./Flow.Launcher.Plugin.Url/*" rm -r "Flow.Launcher.Plugin.Url" @@ -282,7 +282,7 @@ jobs: - name: Build WebSearch run: | - dotnet publish 'Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.WebSearch" + dotnet publish 'Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj' --framework net9.0-windows -c Release -o "Flow.Launcher.Plugin.WebSearch" 7z a -tzip "Flow.Launcher.Plugin.WebSearch.zip" "./Flow.Launcher.Plugin.WebSearch/*" rm -r "Flow.Launcher.Plugin.WebSearch" @@ -306,7 +306,7 @@ jobs: - name: Build WindowsSettings run: | - dotnet publish 'Plugins/Flow.Launcher.Plugin.WindowsSettings/Flow.Launcher.Plugin.WindowsSettings.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.WindowsSettings" + dotnet publish 'Plugins/Flow.Launcher.Plugin.WindowsSettings/Flow.Launcher.Plugin.WindowsSettings.csproj' --framework net9.0-windows -c Release -o "Flow.Launcher.Plugin.WindowsSettings" 7z a -tzip "Flow.Launcher.Plugin.WindowsSettings.zip" "./Flow.Launcher.Plugin.WindowsSettings/*" rm -r "Flow.Launcher.Plugin.WindowsSettings" diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 7498262de..957f836bb 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -16,11 +16,11 @@ jobs: runs-on: windows-latest env: - FlowVersion: 1.19.5 + FlowVersion: 1.20.2 NUGET_CERT_REVOCATION_MODE: offline BUILD_NUMBER: ${{ github.run_number }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set Flow.Launcher.csproj version id: update uses: vers-one/dotnet-project-version-updater@v1.7 @@ -29,9 +29,9 @@ jobs: "**/SolutionAssemblyInfo.cs" version: ${{ env.FlowVersion }}.${{ env.BUILD_NUMBER }} - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: - dotnet-version: 7.0.x + dotnet-version: 9.0.x # cache: true # cache-dependency-path: | # Flow.Launcher/packages.lock.json diff --git a/.github/workflows/release_pr.yml b/.github/workflows/release_pr.yml index 451bf386c..58a877ba3 100644 --- a/.github/workflows/release_pr.yml +++ b/.github/workflows/release_pr.yml @@ -11,9 +11,9 @@ jobs: update-pr: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: "3.x" diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml index 47bd66107..904392bf0 100644 --- a/.github/workflows/spelling.yml +++ b/.github/workflows/spelling.yml @@ -72,7 +72,7 @@ jobs: steps: - name: check-spelling id: spelling - uses: check-spelling/check-spelling@prerelease + uses: check-spelling/check-spelling@v0.0.25 with: suppress_push_for_open_pull_request: 1 checkout: true @@ -128,7 +128,7 @@ jobs: if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request') steps: - name: comment - uses: check-spelling/check-spelling@prerelease + uses: check-spelling/check-spelling@v0.0.25 with: checkout: true spell_check_this: check-spelling/spell-check-this@main diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 719f2556e..5652eef5e 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -18,7 +18,7 @@ jobs: issues: write pull-requests: write steps: - - uses: actions/stale@v9 + - uses: actions/stale@v10 with: stale-issue-message: 'This issue is stale because it has been open ${{ env.days-before-stale }} days with no activity. Remove stale label or comment or this will be closed in ${{ env.days-before-stale }} days.\n\nAlternatively this issue can be kept open by adding one of the following labels:\n${{ env.exempt-issue-labels }}' days-before-stale: ${{ env.days-before-stale }} diff --git a/Directory.Build.props b/Directory.Build.props index fa499273c..a5545af12 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,5 +1,7 @@ true + + false \ No newline at end of file diff --git a/Flow.Launcher.Core/Configuration/Portable.cs b/Flow.Launcher.Core/Configuration/Portable.cs index 7f02cef09..721e14dca 100644 --- a/Flow.Launcher.Core/Configuration/Portable.cs +++ b/Flow.Launcher.Core/Configuration/Portable.cs @@ -45,8 +45,7 @@ namespace Flow.Launcher.Core.Configuration #endif IndicateDeletion(DataLocation.PortableDataPath); - API.ShowMsgBox("Flow Launcher needs to restart to finish disabling portable mode, " + - "after the restart your portable data profile will be deleted and roaming data profile kept"); + API.ShowMsgBox(API.GetTranslation("restartToDisablePortableMode")); UpdateManager.RestartApp(Constant.ApplicationFileName); } @@ -69,8 +68,7 @@ namespace Flow.Launcher.Core.Configuration #endif IndicateDeletion(DataLocation.RoamingDataPath); - API.ShowMsgBox("Flow Launcher needs to restart to finish enabling portable mode, " + - "after the restart your roaming data profile will be deleted and portable data profile kept"); + API.ShowMsgBox(API.GetTranslation("restartToEnablePortableMode")); UpdateManager.RestartApp(Constant.ApplicationFileName); } @@ -154,9 +152,8 @@ namespace Flow.Launcher.Core.Configuration { FilesFolders.RemoveFolderIfExists(roamingDataDir, (s) => API.ShowMsgBox(s)); - if (API.ShowMsgBox("Flow Launcher has detected you enabled portable mode, " + - "would you like to move it to a different location?", string.Empty, - MessageBoxButton.YesNo) == MessageBoxResult.Yes) + if (API.ShowMsgBox(API.GetTranslation("moveToDifferentLocation"), + string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes) { FilesFolders.OpenPath(Constant.RootDirectory, (s) => API.ShowMsgBox(s)); @@ -169,8 +166,7 @@ namespace Flow.Launcher.Core.Configuration { FilesFolders.RemoveFolderIfExists(portableDataDir, (s) => API.ShowMsgBox(s)); - API.ShowMsgBox("Flow Launcher has detected you disabled portable mode, " + - "the relevant shortcuts and uninstaller entry have been created"); + API.ShowMsgBox(API.GetTranslation("shortcutsUninstallerCreated")); } } @@ -181,9 +177,8 @@ namespace Flow.Launcher.Core.Configuration if (roamingLocationExists && portableLocationExists) { - API.ShowMsgBox(string.Format("Flow Launcher detected your user data exists both in {0} and " + - "{1}. {2}{2}Please delete {1} in order to proceed. No changes have occurred.", - DataLocation.PortableDataPath, DataLocation.RoamingDataPath, Environment.NewLine)); + API.ShowMsgBox(string.Format(API.GetTranslation("userDataDuplicated"), + DataLocation.PortableDataPath, DataLocation.RoamingDataPath, Environment.NewLine)); return false; } diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs index 455ee096d..89286dfb0 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs @@ -11,6 +11,8 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments { internal class PythonEnvironment : AbstractPluginEnvironment { + private static readonly string ClassName = nameof(PythonEnvironment); + internal override string Language => AllowedLanguage.Python; internal override string EnvName => DataLocation.PythonEnvironmentName; @@ -39,9 +41,20 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments // Python 3.11.4 is no longer Windows 7 compatible. If user is on Win 7 and // uses Python plugin they need to custom install and use v3.8.9 - JTF.Run(() => DroplexPackage.Drop(App.python_3_11_4_embeddable, InstallPath)); + JTF.Run(async () => + { + try + { + await DroplexPackage.Drop(App.python_3_11_4_embeddable, InstallPath); - PluginsSettingsFilePath = ExecutablePath; + PluginsSettingsFilePath = ExecutablePath; + } + catch (System.Exception e) + { + API.ShowMsgError(API.GetTranslation("failToInstallPythonEnv")); + API.LogException(ClassName, "Failed to install Python environment", e); + } + }); } internal override PluginPair CreatePluginPair(string filePath, PluginMetadata metadata) diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs index 12965286f..724ae20f4 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs @@ -11,6 +11,8 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments { internal class TypeScriptEnvironment : AbstractPluginEnvironment { + private static readonly string ClassName = nameof(TypeScriptEnvironment); + internal override string Language => AllowedLanguage.TypeScript; internal override string EnvName => DataLocation.NodeEnvironmentName; @@ -34,9 +36,20 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments { FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s)); - JTF.Run(() => DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath)); + JTF.Run(async () => + { + try + { + await DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath); - PluginsSettingsFilePath = ExecutablePath; + PluginsSettingsFilePath = ExecutablePath; + } + catch (System.Exception e) + { + API.ShowMsgError(API.GetTranslation("failToInstallTypeScriptEnv")); + API.LogException(ClassName, "Failed to install TypeScript environment", e); + } + }); } internal override PluginPair CreatePluginPair(string filePath, PluginMetadata metadata) diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs index 6960b79c9..6a32664a1 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs @@ -11,6 +11,8 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments { internal class TypeScriptV2Environment : AbstractPluginEnvironment { + private static readonly string ClassName = nameof(TypeScriptV2Environment); + internal override string Language => AllowedLanguage.TypeScriptV2; internal override string EnvName => DataLocation.NodeEnvironmentName; @@ -34,9 +36,20 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments { FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s)); - JTF.Run(() => DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath)); + JTF.Run(async () => + { + try + { + await DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath); - PluginsSettingsFilePath = ExecutablePath; + PluginsSettingsFilePath = ExecutablePath; + } + catch (System.Exception e) + { + API.ShowMsgError(API.GetTranslation("failToInstallTypeScriptEnv")); + API.LogException(ClassName, "Failed to install TypeScript environment", e); + } + }); } internal override PluginPair CreatePluginPair(string filePath, PluginMetadata metadata) diff --git a/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs b/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs index 7ca91eaec..1e845498c 100644 --- a/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs +++ b/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Plugin; +using Flow.Launcher.Infrastructure; namespace Flow.Launcher.Core.ExternalPlugins { @@ -12,16 +13,20 @@ namespace Flow.Launcher.Core.ExternalPlugins private static readonly string ClassName = nameof(PluginsManifest); private static readonly CommunityPluginStore mainPluginStore = - new("https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/plugin_api_v2/plugins.json", - "https://fastly.jsdelivr.net/gh/Flow-Launcher/Flow.Launcher.PluginsManifest@plugin_api_v2/plugins.json", - "https://gcore.jsdelivr.net/gh/Flow-Launcher/Flow.Launcher.PluginsManifest@plugin_api_v2/plugins.json", - "https://cdn.jsdelivr.net/gh/Flow-Launcher/Flow.Launcher.PluginsManifest@plugin_api_v2/plugins.json"); + new("https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/main/plugins.json", + "https://fastly.jsdelivr.net/gh/Flow-Launcher/Flow.Launcher.PluginsManifest@main/plugins.json", + "https://gcore.jsdelivr.net/gh/Flow-Launcher/Flow.Launcher.PluginsManifest@main/plugins.json", + "https://cdn.jsdelivr.net/gh/Flow-Launcher/Flow.Launcher.PluginsManifest@main/plugins.json"); private static readonly SemaphoreSlim manifestUpdateLock = new(1); private static DateTime lastFetchedAt = DateTime.MinValue; private static readonly TimeSpan fetchTimeout = TimeSpan.FromMinutes(2); + // We should not initialize API in static constructor because it will create another API instance + private static IPublicAPI api = null; + private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); + public static List UserPlugins { get; private set; } public static async Task UpdateManifestAsync(bool usePrimaryUrlOnly = false, CancellationToken token = default) @@ -35,18 +40,28 @@ namespace Flow.Launcher.Core.ExternalPlugins var results = await mainPluginStore.FetchAsync(token, usePrimaryUrlOnly).ConfigureAwait(false); // If the results are empty, we shouldn't update the manifest because the results are invalid. - if (results.Count != 0) - { - UserPlugins = results; - lastFetchedAt = DateTime.Now; + if (results.Count == 0) + return false; - return true; + var updatedPluginResults = new List(); + var appVersion = SemanticVersioning.Version.Parse(Constant.Version); + + for (int i = 0; i < results.Count; i++) + { + if (IsMinimumAppVersionSatisfied(results[i], appVersion)) + updatedPluginResults.Add(results[i]); } + + UserPlugins = updatedPluginResults; + + lastFetchedAt = DateTime.Now; + + return true; } } catch (Exception e) { - Ioc.Default.GetRequiredService().LogException(ClassName, "Http request failed", e); + API.LogException(ClassName, "Http request failed", e); } finally { @@ -55,5 +70,28 @@ namespace Flow.Launcher.Core.ExternalPlugins return false; } + + private static bool IsMinimumAppVersionSatisfied(UserPlugin plugin, SemanticVersioning.Version appVersion) + { + if (string.IsNullOrEmpty(plugin.MinimumAppVersion)) + return true; + + try + { + if (appVersion >= SemanticVersioning.Version.Parse(plugin.MinimumAppVersion)) + return true; + } + catch (Exception e) + { + API.LogException(ClassName, $"Failed to parse the minimum app version {plugin.MinimumAppVersion} for plugin {plugin.Name}. " + + "Plugin excluded from manifest", e); + return false; + } + + API.LogInfo(ClassName, $"Plugin {plugin.Name} requires minimum Flow Launcher version {plugin.MinimumAppVersion}, " + + $"but current version is {Constant.Version}. Plugin excluded from manifest."); + + return false; + } } } diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index e9f199d00..527950061 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -1,7 +1,7 @@ - net7.0-windows + net9.0-windows true true Library @@ -12,6 +12,7 @@ false false en + true @@ -54,11 +55,12 @@ - - + + + - + diff --git a/Flow.Launcher.Core/Plugin/PluginInstaller.cs b/Flow.Launcher.Core/Plugin/PluginInstaller.cs index 33963c01a..d01b34ab6 100644 --- a/Flow.Launcher.Core/Plugin/PluginInstaller.cs +++ b/Flow.Launcher.Core/Plugin/PluginInstaller.cs @@ -1,4 +1,5 @@ -using System; +using System; +using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; @@ -277,6 +278,122 @@ public static class PluginInstaller } } + /// + /// Updates the plugin to the latest version available from its source. + /// + /// Action to execute when the user chooses to update all plugins. + /// If true, do not show any messages when there is no update available. + /// If true, only use the primary URL for updates. + /// Cancellation token to cancel the update operation. + /// + public static async Task CheckForPluginUpdatesAsync(Action> updateAllPlugins, bool silentUpdate = true, bool usePrimaryUrlOnly = false, CancellationToken token = default) + { + // Update the plugin manifest + await API.UpdatePluginManifestAsync(usePrimaryUrlOnly, token); + + // Get all plugins that can be updated + var resultsForUpdate = ( + from existingPlugin in API.GetAllPlugins() + join pluginUpdateSource in API.GetPluginManifest() + on existingPlugin.Metadata.ID equals pluginUpdateSource.ID + where string.Compare(existingPlugin.Metadata.Version, pluginUpdateSource.Version, + StringComparison.InvariantCulture) < + 0 // if current version precedes version of the plugin from update source (e.g. PluginsManifest) + && !API.PluginModified(existingPlugin.Metadata.ID) + select + new PluginUpdateInfo() + { + ID = existingPlugin.Metadata.ID, + Name = existingPlugin.Metadata.Name, + Author = existingPlugin.Metadata.Author, + CurrentVersion = existingPlugin.Metadata.Version, + NewVersion = pluginUpdateSource.Version, + IcoPath = existingPlugin.Metadata.IcoPath, + PluginExistingMetadata = existingPlugin.Metadata, + PluginNewUserPlugin = pluginUpdateSource + }).ToList(); + + // No updates + if (!resultsForUpdate.Any()) + { + if (!silentUpdate) + { + API.ShowMsg(API.GetTranslation("updateNoResultTitle"), API.GetTranslation("updateNoResultSubtitle")); + } + return; + } + + // If all plugins are modified, just return + if (resultsForUpdate.All(x => API.PluginModified(x.ID))) + { + return; + } + + // Show message box with button to update all plugins + API.ShowMsgWithButton( + API.GetTranslation("updateAllPluginsTitle"), + API.GetTranslation("updateAllPluginsButtonContent"), + () => + { + updateAllPlugins(resultsForUpdate); + }, + string.Join(", ", resultsForUpdate.Select(x => x.PluginExistingMetadata.Name))); + } + + /// + /// Updates all plugins that have available updates. + /// + /// + /// + public static async Task UpdateAllPluginsAsync(IEnumerable resultsForUpdate, bool restart) + { + var anyPluginSuccess = false; + await Task.WhenAll(resultsForUpdate.Select(async plugin => + { + var downloadToFilePath = Path.Combine(Path.GetTempPath(), $"{plugin.Name}-{plugin.NewVersion}.zip"); + + try + { + using var cts = new CancellationTokenSource(); + + await DownloadFileAsync( + $"{API.GetTranslation("DownloadingPlugin")} {plugin.PluginNewUserPlugin.Name}", + plugin.PluginNewUserPlugin.UrlDownload, downloadToFilePath, cts); + + // check if user cancelled download before installing plugin + if (cts.IsCancellationRequested) + { + return; + } + + if (!await API.UpdatePluginAsync(plugin.PluginExistingMetadata, plugin.PluginNewUserPlugin, downloadToFilePath)) + { + return; + } + + anyPluginSuccess = true; + } + catch (Exception e) + { + API.LogException(ClassName, "Failed to update plugin", e); + API.ShowMsgError(API.GetTranslation("ErrorUpdatingPlugin")); + } + })); + + if (!anyPluginSuccess) return; + + if (restart) + { + API.RestartApp(); + } + else + { + API.ShowMsg( + API.GetTranslation("updatebtn"), + API.GetTranslation("PluginsUpdateSuccessNoRestart")); + } + } + /// /// Downloads a file from a URL to a local path, optionally showing a progress box and handling cancellation. /// @@ -351,3 +468,15 @@ public static class PluginInstaller ); } } + +public record PluginUpdateInfo +{ + public string ID { get; init; } + public string Name { get; init; } + public string Author { get; init; } + public string CurrentVersion { get; init; } + public string NewVersion { get; init; } + public string IcoPath { get; init; } + public PluginMetadata PluginExistingMetadata { get; init; } + public UserPlugin PluginNewUserPlugin { get; init; } +} diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index d88f2f050..a4ab8de08 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; @@ -9,6 +9,7 @@ using System.Threading.Tasks; using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Core.ExternalPlugins; using Flow.Launcher.Infrastructure; +using Flow.Launcher.Infrastructure.DialogJump; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedCommands; @@ -40,10 +41,13 @@ namespace Flow.Launcher.Core.Plugin private static IEnumerable _resultUpdatePlugin; private static IEnumerable _translationPlugins; + private static readonly List _dialogJumpExplorerPlugins = new(); + private static readonly List _dialogJumpDialogPlugins = new(); + /// /// Directories that will hold Flow Launcher plugin directory /// - private static readonly string[] Directories = + public static readonly string[] Directories = { Constant.PreinstalledDirectory, DataLocation.PluginsDirectory }; @@ -186,6 +190,24 @@ namespace Flow.Launcher.Core.Plugin _homePlugins = GetPluginsForInterface(); _resultUpdatePlugin = GetPluginsForInterface(); _translationPlugins = GetPluginsForInterface(); + + // Initialize Dialog Jump plugin pairs + foreach (var pair in GetPluginsForInterface()) + { + _dialogJumpExplorerPlugins.Add(new DialogJumpExplorerPair + { + Plugin = (IDialogJumpExplorer)pair.Plugin, + Metadata = pair.Metadata + }); + } + foreach (var pair in GetPluginsForInterface()) + { + _dialogJumpDialogPlugins.Add(new DialogJumpDialogPair + { + Plugin = (IDialogJumpDialog)pair.Plugin, + Metadata = pair.Metadata + }); + } } private static void UpdatePluginDirectory(List metadatas) @@ -288,20 +310,24 @@ namespace Flow.Launcher.Core.Plugin } } - public static ICollection ValidPluginsForQuery(Query query) + public static ICollection ValidPluginsForQuery(Query query, bool dialogJump) { if (query is null) return Array.Empty(); if (!NonGlobalPlugins.TryGetValue(query.ActionKeyword, out var plugin)) { - return GlobalPlugins.Where(p => !PluginModified(p.Metadata.ID)).ToList(); + if (dialogJump) + return GlobalPlugins.Where(p => p.Plugin is IAsyncDialogJump && !PluginModified(p.Metadata.ID)).ToList(); + else + return GlobalPlugins.Where(p => !PluginModified(p.Metadata.ID)).ToList(); } - if (API.PluginModified(plugin.Metadata.ID)) - { + if (dialogJump && plugin.Plugin is not IAsyncDialogJump) + return Array.Empty(); + + if (API.PluginModified(plugin.Metadata.ID)) return Array.Empty(); - } return new List { @@ -388,6 +414,36 @@ namespace Flow.Launcher.Core.Plugin return results; } + public static async Task> QueryDialogJumpForPluginAsync(PluginPair pair, Query query, CancellationToken token) + { + var results = new List(); + var metadata = pair.Metadata; + + try + { + var milliseconds = await API.StopwatchLogDebugAsync(ClassName, $"Cost for {metadata.Name}", + async () => results = await ((IAsyncDialogJump)pair.Plugin).QueryDialogJumpAsync(query, token).ConfigureAwait(false)); + + token.ThrowIfCancellationRequested(); + if (results == null) + return null; + UpdatePluginMetadata(results, metadata, query); + + token.ThrowIfCancellationRequested(); + } + catch (OperationCanceledException) + { + // null will be fine since the results will only be added into queue if the token hasn't been cancelled + return null; + } + catch (Exception e) + { + API.LogException(ClassName, $"Failed to query Dialog Jump for plugin: {metadata.Name}", e); + return null; + } + return results; + } + public static void UpdatePluginMetadata(IReadOnlyList results, PluginMetadata metadata, Query query) { foreach (var r in results) @@ -463,6 +519,16 @@ namespace Flow.Launcher.Core.Plugin return _homePlugins.Where(p => !PluginModified(p.Metadata.ID)).Any(p => p.Metadata.ID == id); } + public static IList GetDialogJumpExplorers() + { + return _dialogJumpExplorerPlugins.Where(p => !PluginModified(p.Metadata.ID)).ToList(); + } + + public static IList GetDialogJumpDialogs() + { + return _dialogJumpDialogPlugins.Where(p => !PluginModified(p.Metadata.ID)).ToList(); + } + public static bool ActionKeywordRegistered(string actionKeyword) { // this method is only checking for action keywords (defined as not '*') registration @@ -719,7 +785,7 @@ namespace Flow.Launcher.Core.Plugin catch (Exception e) { API.LogException(ClassName, $"Failed to delete plugin settings folder for {plugin.Name}", e); - API.ShowMsg(API.GetTranslation("failedToRemovePluginSettingsTitle"), + API.ShowMsgError(API.GetTranslation("failedToRemovePluginSettingsTitle"), string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name)); } } @@ -735,7 +801,7 @@ namespace Flow.Launcher.Core.Plugin catch (Exception e) { API.LogException(ClassName, $"Failed to delete plugin cache folder for {plugin.Name}", e); - API.ShowMsg(API.GetTranslation("failedToRemovePluginCacheTitle"), + API.ShowMsgError(API.GetTranslation("failedToRemovePluginCacheTitle"), string.Format(API.GetTranslation("failedToRemovePluginCacheMessage"), plugin.Name)); } Settings.RemovePluginSettings(plugin.ID); diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 256c36065..e9e5ee367 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -120,17 +120,13 @@ namespace Flow.Launcher.Core.Plugin { var errorPluginString = string.Join(Environment.NewLine, erroredPlugins); - var errorMessage = "The following " - + (erroredPlugins.Count > 1 ? "plugins have " : "plugin has ") - + "errored and cannot be loaded:"; + var errorMessage = erroredPlugins.Count > 1 ? + API.GetTranslation("pluginsHaveErrored") : + API.GetTranslation("pluginHasErrored"); - _ = Task.Run(() => - { - Ioc.Default.GetRequiredService().ShowMsgBox($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + - $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + - $"Please refer to the logs for more information", "", - MessageBoxButton.OK, MessageBoxImage.Warning); - }); + API.ShowMsgError($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + + $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + + API.GetTranslation("referToLogs")); } return plugins; diff --git a/Flow.Launcher.Core/Resource/AvailableLanguages.cs b/Flow.Launcher.Core/Resource/AvailableLanguages.cs index ecaecf646..5534ea172 100644 --- a/Flow.Launcher.Core/Resource/AvailableLanguages.cs +++ b/Flow.Launcher.Core/Resource/AvailableLanguages.cs @@ -17,6 +17,7 @@ namespace Flow.Launcher.Core.Resource public static Language German = new Language("de", "Deutsch"); public static Language Korean = new Language("ko", "한국어"); public static Language Serbian = new Language("sr", "Srpski"); + public static Language Serbian_Cyrillic = new Language("sr-Cyrl-RS", "Српски"); public static Language Portuguese_Portugal = new Language("pt-pt", "Português"); public static Language Portuguese_Brazil = new Language("pt-br", "Português (Brasil)"); public static Language Spanish = new Language("es", "Spanish"); @@ -47,6 +48,7 @@ namespace Flow.Launcher.Core.Resource German, Korean, Serbian, + Serbian_Cyrillic, Portuguese_Portugal, Portuguese_Brazil, Spanish, @@ -79,7 +81,8 @@ namespace Flow.Launcher.Core.Resource "da" => "System", "de" => "System", "ko" => "시스템", - "sr" => "Систем", + "sr" => "Sistem", + "sr-Cyrl-RS" => "Систем", "pt-pt" => "Sistema", "pt-br" => "Sistema", "es" => "Sistema", diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs index 7b7d6eef6..8261feab3 100644 --- a/Flow.Launcher.Core/Resource/Internationalization.cs +++ b/Flow.Launcher.Core/Resource/Internationalization.cs @@ -1,9 +1,8 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -using System.Reflection; using System.Threading; using System.Threading.Tasks; using System.Windows; @@ -28,22 +27,20 @@ namespace Flow.Launcher.Core.Resource private const string DefaultFile = "en.xaml"; private const string Extension = ".xaml"; private readonly Settings _settings; - private readonly List _languageDirectories = new(); - private readonly List _oldResources = new(); + private readonly List _languageDirectories = []; + private readonly List _oldResources = []; private static string SystemLanguageCode; public Internationalization(Settings settings) { _settings = settings; - AddFlowLauncherLanguageDirectory(); } - private void AddFlowLauncherLanguageDirectory() - { - var directory = Path.Combine(Constant.ProgramDirectory, Folder); - _languageDirectories.Add(directory); - } + #region Initialization + /// + /// Initialize the system language code based on the current culture. + /// public static void InitSystemLanguageCode() { var availableLanguages = AvailableLanguages.GetAvailableLanguages(); @@ -72,35 +69,6 @@ namespace Flow.Launcher.Core.Resource SystemLanguageCode = DefaultLanguageCode; } - private void AddPluginLanguageDirectories() - { - foreach (var plugin in PluginManager.GetTranslationPlugins()) - { - var location = Assembly.GetAssembly(plugin.Plugin.GetType()).Location; - var dir = Path.GetDirectoryName(location); - if (dir != null) - { - var pluginThemeDirectory = Path.Combine(dir, Folder); - _languageDirectories.Add(pluginThemeDirectory); - } - else - { - API.LogError(ClassName, $"Can't find plugin path <{location}> for <{plugin.Metadata.Name}>"); - } - } - - LoadDefaultLanguage(); - } - - private void LoadDefaultLanguage() - { - // Removes language files loaded before any plugins were loaded. - // Prevents the language Flow started in from overwriting English if the user switches back to English - RemoveOldLanguageFiles(); - LoadLanguage(AvailableLanguages.English); - _oldResources.Clear(); - } - /// /// Initialize language. Will change app language and plugin language based on settings. /// @@ -116,13 +84,64 @@ namespace Flow.Launcher.Core.Resource // Get language by language code and change language var language = GetLanguageByLanguageCode(languageCode); + // Add Flow Launcher language directory + AddFlowLauncherLanguageDirectory(); + // Add plugin language directories first so that we can load language files from plugins AddPluginLanguageDirectories(); + // Load default language resources + LoadDefaultLanguage(); + // Change language - await ChangeLanguageAsync(language); + await ChangeLanguageAsync(language, false); } + private void AddFlowLauncherLanguageDirectory() + { + // Check if Flow Launcher language directory exists + var directory = Path.Combine(Constant.ProgramDirectory, Folder); + if (!Directory.Exists(directory)) + { + API.LogError(ClassName, $"Flow Launcher language directory can't be found <{directory}>"); + return; + } + + _languageDirectories.Add(directory); + } + + private void AddPluginLanguageDirectories() + { + foreach (var pluginsDir in PluginManager.Directories) + { + if (!Directory.Exists(pluginsDir)) continue; + + // Enumerate all top directories in the plugin directory + foreach (var dir in Directory.GetDirectories(pluginsDir)) + { + // Check if the directory contains a language folder + var pluginLanguageDir = Path.Combine(dir, Folder); + if (!Directory.Exists(pluginLanguageDir)) continue; + + // Check if the language directory contains default language file since it will be checked later + _languageDirectories.Add(pluginLanguageDir); + } + } + } + + private void LoadDefaultLanguage() + { + // Removes language files loaded before any plugins were loaded. + // Prevents the language Flow started in from overwriting English if the user switches back to English + RemoveOldLanguageFiles(); + LoadLanguage(AvailableLanguages.English); + _oldResources.Clear(); + } + + #endregion + + #region Change Language + /// /// Change language during runtime. Will change app language and plugin language & save settings. /// @@ -151,8 +170,8 @@ namespace Flow.Launcher.Core.Resource private static Language GetLanguageByLanguageCode(string languageCode) { - var lowercase = languageCode.ToLower(); - var language = AvailableLanguages.GetAvailableLanguages().FirstOrDefault(o => o.LanguageCode.ToLower() == lowercase); + var language = AvailableLanguages.GetAvailableLanguages(). + FirstOrDefault(o => o.LanguageCode.Equals(languageCode, StringComparison.OrdinalIgnoreCase)); if (language == null) { API.LogError(ClassName, $"Language code can't be found <{languageCode}>"); @@ -164,7 +183,7 @@ namespace Flow.Launcher.Core.Resource } } - private async Task ChangeLanguageAsync(Language language) + private async Task ChangeLanguageAsync(Language language, bool updateMetadata = true) { // Remove old language files and load language RemoveOldLanguageFiles(); @@ -176,8 +195,11 @@ namespace Flow.Launcher.Core.Resource // Change culture info ChangeCultureInfo(language.LanguageCode); - // Raise event for plugins after culture is set - await Task.Run(UpdatePluginMetadataTranslations); + if (updateMetadata) + { + // Raise event for plugins after culture is set + await Task.Run(UpdatePluginMetadataTranslations); + } } public static void ChangeCultureInfo(string languageCode) @@ -200,6 +222,10 @@ namespace Flow.Launcher.Core.Resource thread.CurrentUICulture = currentCulture; } + #endregion + + #region Prompt Pinyin + public bool PromptShouldUsePinyin(string languageCodeToSet) { var languageToSet = GetLanguageByLanguageCode(languageCodeToSet); @@ -212,14 +238,18 @@ namespace Flow.Launcher.Core.Resource // No other languages should show the following text so just make it hard-coded // "Do you want to search with pinyin?" - string text = languageToSet == AvailableLanguages.Chinese ? "是否启用拼音搜索?" : "是否啓用拼音搜索?" ; + string text = languageToSet == AvailableLanguages.Chinese ? "是否启用拼音搜索?" : "是否啓用拼音搜索?"; - if (Ioc.Default.GetRequiredService().ShowMsgBox(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) + if (API.ShowMsgBox(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) return false; return true; } + #endregion + + #region Language Resources Management + private void RemoveOldLanguageFiles() { var dicts = Application.Current.Resources.MergedDictionaries; @@ -255,46 +285,6 @@ namespace Flow.Launcher.Core.Resource } } - public List LoadAvailableLanguages() - { - var list = AvailableLanguages.GetAvailableLanguages(); - list.Insert(0, new Language(Constant.SystemLanguageCode, AvailableLanguages.GetSystemTranslation(SystemLanguageCode))); - return list; - } - - public static string GetTranslation(string key) - { - var translation = Application.Current.TryFindResource(key); - if (translation is string) - { - return translation.ToString(); - } - else - { - API.LogError(ClassName, $"No Translation for key {key}"); - return $"No Translation for key {key}"; - } - } - - private void UpdatePluginMetadataTranslations() - { - // Update plugin metadata name & description - foreach (var p in PluginManager.GetTranslationPlugins()) - { - if (p.Plugin is not IPluginI18n pluginI18N) return; - try - { - p.Metadata.Name = pluginI18N.GetTranslatedPluginTitle(); - p.Metadata.Description = pluginI18N.GetTranslatedPluginDescription(); - pluginI18N.OnCultureInfoChanged(CultureInfo.CurrentCulture); - } - catch (Exception e) - { - API.LogException(ClassName, $"Failed for <{p.Metadata.Name}>", e); - } - } - } - private static string LanguageFile(string folder, string language) { if (Directory.Exists(folder)) @@ -324,5 +314,59 @@ namespace Flow.Launcher.Core.Resource return string.Empty; } } + + #endregion + + #region Available Languages + + public List LoadAvailableLanguages() + { + var list = AvailableLanguages.GetAvailableLanguages(); + list.Insert(0, new Language(Constant.SystemLanguageCode, AvailableLanguages.GetSystemTranslation(SystemLanguageCode))); + return list; + } + + #endregion + + #region Get Translations + + public static string GetTranslation(string key) + { + var translation = Application.Current.TryFindResource(key); + if (translation is string) + { + return translation.ToString(); + } + else + { + API.LogError(ClassName, $"No Translation for key {key}"); + return $"No Translation for key {key}"; + } + } + + #endregion + + #region Update Metadata + + public static void UpdatePluginMetadataTranslations() + { + // Update plugin metadata name & description + foreach (var p in PluginManager.GetTranslationPlugins()) + { + if (p.Plugin is not IPluginI18n pluginI18N) return; + try + { + p.Metadata.Name = pluginI18N.GetTranslatedPluginTitle(); + p.Metadata.Description = pluginI18N.GetTranslatedPluginDescription(); + pluginI18N.OnCultureInfoChanged(CultureInfo.CurrentCulture); + } + catch (Exception e) + { + API.LogException(ClassName, $"Failed for <{p.Metadata.Name}>", e); + } + } + } + + #endregion } } diff --git a/Flow.Launcher.Core/Resource/LocalizationConverter.cs b/Flow.Launcher.Core/Resource/LocalizationConverter.cs deleted file mode 100644 index fdda33926..000000000 --- a/Flow.Launcher.Core/Resource/LocalizationConverter.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.ComponentModel; -using System.Globalization; -using System.Reflection; -using System.Windows.Data; - -namespace Flow.Launcher.Core.Resource -{ - [Obsolete("LocalizationConverter is obsolete. Use with Flow.Launcher.Localization NuGet package instead.")] - public class LocalizationConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (targetType == typeof(string) && value != null) - { - FieldInfo fi = value.GetType().GetField(value.ToString()); - if (fi != null) - { - string localizedDescription = string.Empty; - var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false); - if ((attributes.Length > 0) && (!String.IsNullOrEmpty(attributes[0].Description))) - { - localizedDescription = attributes[0].Description; - } - - return (!String.IsNullOrEmpty(localizedDescription)) ? localizedDescription : value.ToString(); - } - } - - return string.Empty; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } - } -} diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index bc3655f69..45275696c 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -49,8 +49,9 @@ namespace Flow.Launcher.Core // UpdateApp CheckForUpdate will return value only if the app is squirrel installed var newUpdateInfo = await updateManager.CheckForUpdate().NonNull().ConfigureAwait(false); - var newReleaseVersion = Version.Parse(newUpdateInfo.FutureReleaseEntry.Version.ToString()); - var currentVersion = Version.Parse(Constant.Version); + var newReleaseVersion = + SemanticVersioning.Version.Parse(newUpdateInfo.FutureReleaseEntry.Version.ToString()); + var currentVersion = SemanticVersioning.Version.Parse(Constant.Version); _api.LogInfo(ClassName, $"Future Release <{Formatted(newUpdateInfo.FutureReleaseEntry)}>"); @@ -71,10 +72,13 @@ namespace Flow.Launcher.Core if (DataLocation.PortableDataLocationInUse()) { - var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion}\\{DataLocation.PortableFolderName}"; + var targetDestination = updateManager.RootAppDirectory + + $"\\app-{newReleaseVersion}\\{DataLocation.PortableFolderName}"; FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination, (s) => _api.ShowMsgBox(s)); - if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination, (s) => _api.ShowMsgBox(s))) - _api.ShowMsgBox(string.Format(_api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"), + if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination, + (s) => _api.ShowMsgBox(s))) + _api.ShowMsgBox(string.Format( + _api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"), DataLocation.PortableDataPath, targetDestination)); } @@ -87,24 +91,27 @@ namespace Flow.Launcher.Core _api.LogInfo(ClassName, $"Update success:{newVersionTips}"); - if (_api.ShowMsgBox(newVersionTips, _api.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes) + if (_api.ShowMsgBox(newVersionTips, _api.GetTranslation("update_flowlauncher_new_update"), + MessageBoxButton.YesNo) == MessageBoxResult.Yes) { UpdateManager.RestartApp(Constant.ApplicationFileName); } } catch (Exception e) { - if (e is HttpRequestException or WebException or SocketException || e.InnerException is TimeoutException) + if (e is HttpRequestException or WebException or SocketException || + e.InnerException is TimeoutException) { - _api.LogException(ClassName, $"Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e); + _api.LogException(ClassName, + $"Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e); } else { _api.LogException(ClassName, $"Error Occurred", e); } - + if (!silentUpdate) - _api.ShowMsg(_api.GetTranslation("update_flowlauncher_fail"), + _api.ShowMsgError(_api.GetTranslation("update_flowlauncher_fail"), _api.GetTranslation("update_flowlauncher_check_connection")); } finally @@ -116,14 +123,11 @@ namespace Flow.Launcher.Core [UsedImplicitly] private class GithubRelease { - [JsonPropertyName("prerelease")] - public bool Prerelease { get; [UsedImplicitly] set; } + [JsonPropertyName("prerelease")] public bool Prerelease { get; [UsedImplicitly] set; } - [JsonPropertyName("published_at")] - public DateTime PublishedAt { get; [UsedImplicitly] set; } + [JsonPropertyName("published_at")] public DateTime PublishedAt { get; [UsedImplicitly] set; } - [JsonPropertyName("html_url")] - public string HtmlUrl { get; [UsedImplicitly] set; } + [JsonPropertyName("html_url")] public string HtmlUrl { get; [UsedImplicitly] set; } } // https://github.com/Squirrel/Squirrel.Windows/blob/master/src/Squirrel/UpdateManager.Factory.cs @@ -138,10 +142,7 @@ namespace Flow.Launcher.Core var latest = releases.Where(r => !r.Prerelease).OrderByDescending(r => r.PublishedAt).First(); var latestUrl = latest.HtmlUrl.Replace("/tag/", "/download/"); - var client = new WebClient - { - Proxy = Http.WebProxy - }; + var client = new WebClient { Proxy = Http.WebProxy }; var downloader = new FileDownloader(client); var manager = new UpdateManager(latestUrl, urlDownloader: downloader); @@ -158,10 +159,7 @@ namespace Flow.Launcher.Core private static string Formatted(T t) { - var formatted = JsonSerializer.Serialize(t, new JsonSerializerOptions - { - WriteIndented = true - }); + var formatted = JsonSerializer.Serialize(t, new JsonSerializerOptions { WriteIndented = true }); return formatted; } diff --git a/Flow.Launcher.Core/packages.lock.json b/Flow.Launcher.Core/packages.lock.json new file mode 100644 index 000000000..5e9abc24c --- /dev/null +++ b/Flow.Launcher.Core/packages.lock.json @@ -0,0 +1,277 @@ +{ + "version": 1, + "dependencies": { + "net9.0-windows7.0": { + "Droplex": { + "type": "Direct", + "requested": "[1.7.0, )", + "resolved": "1.7.0", + "contentHash": "wutfIus/Ufw/9TDsp86R1ycnIH+wWrj4UhcmrzAHWjsdyC2iM07WEQ9+APTB7pQynsDnYH1r2i58XgAJ3lxUXA==", + "dependencies": { + "YamlDotNet": "9.1.0" + } + }, + "FSharp.Core": { + "type": "Direct", + "requested": "[9.0.300, )", + "resolved": "9.0.300", + "contentHash": "TVt2J7RCE1KCS2IaONF+p8/KIZ1eHNbW+7qmKF6hGoD4tXl+o07ja1mPtFjMqRa5uHMFaTrGTPn/m945WnDLiQ==" + }, + "Meziantou.Framework.Win32.Jobs": { + "type": "Direct", + "requested": "[3.4.3, )", + "resolved": "3.4.3", + "contentHash": "REjInKnQ0OrhjjtSMPQtLtdURctCroB4L8Sd2gjTOYDysklvsdnrStx1tHS7uLv+fSyFF3aazZmo5Ka0v1oz/w==" + }, + "Microsoft.IO.RecyclableMemoryStream": { + "type": "Direct", + "requested": "[3.0.1, )", + "resolved": "3.0.1", + "contentHash": "s/s20YTVY9r9TPfTrN5g8zPF1YhwxyqO6PxUkrYTGI2B+OGPe9AdajWZrLhFqXIvqIW23fnUE4+ztrUWNU1+9g==" + }, + "SemanticVersioning": { + "type": "Direct", + "requested": "[3.0.0, )", + "resolved": "3.0.0", + "contentHash": "RR+8GbPQ/gjDqov/1QN1OPoUlbUruNwcL3WjWCeLw+MY7+od/ENhnkYxCfAC6rQLIu3QifaJt3kPYyP3RumqMQ==" + }, + "squirrel.windows": { + "type": "Direct", + "requested": "[1.5.2, )", + "resolved": "1.5.2", + "contentHash": "89Y/CFxWm7SEOjvuV2stVa8p+SNM9GOLk4tUNm2nUF792nfkimAgwRA/umVsdyd/OXBH8byXSh4V1qck88ZAyQ==", + "dependencies": { + "DeltaCompressionDotNet": "[1.0.0, 2.0.0)", + "Mono.Cecil": "0.9.6.1", + "Splat": "1.6.2" + } + }, + "StreamJsonRpc": { + "type": "Direct", + "requested": "[2.22.11, )", + "resolved": "2.22.11", + "contentHash": "TQcqBFswLNpdSJANjhxZmIIe0Yl0kGqzjZ+uHLdhrkxntofvNu6C53XPEEYQ3Wkj8AorKumkuv/VMvTH4BHOZw==", + "dependencies": { + "MessagePack": "2.5.192", + "Microsoft.VisualStudio.Threading.Only": "17.13.61", + "Microsoft.VisualStudio.Validation": "17.8.8", + "Nerdbank.Streams": "2.12.87", + "Newtonsoft.Json": "13.0.3", + "System.IO.Pipelines": "8.0.0" + } + }, + "Ben.Demystifier": { + "type": "Transitive", + "resolved": "0.4.1", + "contentHash": "axFeEMfmEORy3ipAzOXG/lE+KcNptRbei3F0C4kQCdeiQtW+qJW90K5iIovITGrdLt8AjhNCwk5qLSX9/rFpoA==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0" + } + }, + "BitFaster.Caching": { + "type": "Transitive", + "resolved": "2.5.4", + "contentHash": "1QroTY1PVCZOSG9FnkkCrmCKk/+bZCgI/YXq376HnYwUDJ4Ho0EaV4YaA/5v5WYLnwIwIO7RZkdWbg9pxIpueQ==" + }, + "CommunityToolkit.Mvvm": { + "type": "Transitive", + "resolved": "8.4.0", + "contentHash": "tqVU8yc/ADO9oiTRyTnwhFN68hCwvkliMierptWOudIAvWY1mWCh5VFh+guwHJmpMwfg0J0rY+yyd5Oy7ty9Uw==" + }, + "DeltaCompressionDotNet": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "nwbZAYd+DblXAIzlnwDSnl0CiCm8jWLfHSYnoN4wYhtIav6AegB3+T/vKzLbU2IZlPB8Bvl8U3NXpx3eaz+N5w==" + }, + "InputSimulator": { + "type": "Transitive", + "resolved": "1.0.4", + "contentHash": "D0LvRCPQMX6/FJHBjng+RO+wRDuHTJrfo7IAc7rmkPvRqchdVGJWg3y70peOtDy3OLNK+HSOwVkH4GiuLnkKgA==" + }, + "JetBrains.Annotations": { + "type": "Transitive", + "resolved": "2024.3.0", + "contentHash": "ox5pkeLQXjvJdyAB4b2sBYAlqZGLh3PjSnP1bQNVx72ONuTJ9+34/+Rq91Fc0dG29XG9RgZur9+NcP4riihTug==" + }, + "MemoryPack": { + "type": "Transitive", + "resolved": "1.21.4", + "contentHash": "wy3JTBNBsO8LfQcBvvYsWr3lm2Oakolrfu0UQ3oSJSEiD+7ye0GUhYTaXuYYBowqsXBXWD9gf2218ae0JRiYVQ==", + "dependencies": { + "MemoryPack.Core": "1.21.4", + "MemoryPack.Generator": "1.21.4" + } + }, + "MemoryPack.Core": { + "type": "Transitive", + "resolved": "1.21.4", + "contentHash": "6RszGorZ0ejNmp37ZcboPBMvvPCuNW2jlrdQfcs/lMzE5b3pmPF6hsm/laDc34hRlbAST1ZxaX/DvYu2DF5sBQ==" + }, + "MemoryPack.Generator": { + "type": "Transitive", + "resolved": "1.21.4", + "contentHash": "g14EsSS85yn0lHTi0J9ivqlZMf09A2iI51fmI+0KkzIzyCbWOBWPi5mdaY7YWmXprk12aYh9u/qfWHQUYthlwg==" + }, + "MessagePack": { + "type": "Transitive", + "resolved": "2.5.192", + "contentHash": "Jtle5MaFeIFkdXtxQeL9Tu2Y3HsAQGoSntOzrn6Br/jrl6c8QmG22GEioT5HBtZJR0zw0s46OnKU8ei2M3QifA==", + "dependencies": { + "MessagePack.Annotations": "2.5.192", + "Microsoft.NET.StringTools": "17.6.3" + } + }, + "MessagePack.Annotations": { + "type": "Transitive", + "resolved": "2.5.192", + "contentHash": "jaJuwcgovWIZ8Zysdyf3b7b34/BrADw4v82GaEZymUhDd3ScMPrYd/cttekeDteJJPXseJxp04yTIcxiVUjTWg==" + }, + "Microsoft.NET.StringTools": { + "type": "Transitive", + "resolved": "17.6.3", + "contentHash": "N0ZIanl1QCgvUumEL1laasU0a7sOE5ZwLZVTn0pAePnfhq8P7SvTjF8Axq+CnavuQkmdQpGNXQ1efZtu5kDFbA==" + }, + "Microsoft.VisualStudio.Threading": { + "type": "Transitive", + "resolved": "17.14.15", + "contentHash": "1DrCusT3xNLSlaJg77BsUSAzrhjdZBAvvsS0PMzyPM+fGais6SnISOhqdZQop8VVMIBLsYm2gyF9W7THjgavwA==", + "dependencies": { + "Microsoft.VisualStudio.Threading.Analyzers": "17.14.15", + "Microsoft.VisualStudio.Threading.Only": "17.14.15", + "Microsoft.VisualStudio.Validation": "17.8.8" + } + }, + "Microsoft.VisualStudio.Threading.Analyzers": { + "type": "Transitive", + "resolved": "17.14.15", + "contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw==" + }, + "Microsoft.VisualStudio.Threading.Only": { + "type": "Transitive", + "resolved": "17.14.15", + "contentHash": "NqONyw1RXyj9P3k5e1uU2k9kc1ptwuU5NJQzG+MPq7vQVHUzBY8HLuJf/N2Rw5H/myD96CVxziDxmjawPuzntw==", + "dependencies": { + "Microsoft.VisualStudio.Validation": "17.8.8" + } + }, + "Microsoft.VisualStudio.Validation": { + "type": "Transitive", + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" + }, + "Microsoft.Win32.SystemEvents": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "2nXPrhdAyAzir0gLl8Yy8S5Mnm/uBSQQA7jEsILOS1MTyS7DbmV1NgViMtvV1sfCD1ebITpNwb1NIinKeJgUVQ==" + }, + "Mono.Cecil": { + "type": "Transitive", + "resolved": "0.9.6.1", + "contentHash": "yMsurNaOxxKIjyW9pEB+tRrR1S3DFnN1+iBgKvYvXG8kW0Y6yknJeMAe/tl3+P78/2C6304TgF7aVqpqXgEQ9Q==" + }, + "Nerdbank.Streams": { + "type": "Transitive", + "resolved": "2.12.87", + "contentHash": "oDKOeKZ865I5X8qmU3IXMyrAnssYEiYWTobPGdrqubN3RtTzEHIv+D6fwhdcfrdhPJzHjCkK/ORztR/IsnmA6g==", + "dependencies": { + "Microsoft.VisualStudio.Threading.Only": "17.13.61", + "Microsoft.VisualStudio.Validation": "17.8.8", + "System.IO.Pipelines": "8.0.0" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "NHotkey": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "IEghs0QqWsQYH0uUmvIl0Ye6RaebWRh38eB6ToOkDnQucTYRGFOgtig0gSxlwCszTilYFz3n1ZuY762x+kDR3A==" + }, + "NHotkey.Wpf": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "BIUKlhTG5KtFf9OQzWvkmVmktt5/FFj6AOEgag8Uf0R2YdZt5ajUzs3sVskcJcT2TztWlEHKQr1jFj3KQ0D9Nw==", + "dependencies": { + "NHotkey": "3.0.0" + } + }, + "NLog": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "qDWiqy8/xdpZKtHna/645KbalwP86N2NFJEzfqhcv+Si4V2iNaEfR/dCneuF/4+Dcwl3f7jHMXj3ndWYftV3Ug==" + }, + "NLog.OutputDebugString": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "wwJCQLaHVzuRf8TsXB+EEdrzVvE3dnzCSMQMDgwkw3AXp8VSp3JSVF/Q/H0oEqggKgKhPs13hh3a7svyQr4s3A==", + "dependencies": { + "NLog": "6.0.1" + } + }, + "SharpVectors.Wpf": { + "type": "Transitive", + "resolved": "1.8.4.2", + "contentHash": "PNxLkMBJnV8A+6yH9OqOlhLJegvWP/dvh0rAJp2l0kcrR+rB4R2tQ9vhUqka+UilH4atN8T6zvjDOizVyfz2Ng==" + }, + "Splat": { + "type": "Transitive", + "resolved": "1.6.2", + "contentHash": "DeH0MxPU+D4JchkIDPYG4vUT+hsWs9S41cFle0/4K5EJMXWurx5DzAkj2366DfK14/XKNhsu6tCl4dZXJ3CD4w==" + }, + "System.Drawing.Common": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "KIX+oBU38pxkKPxvLcLfIkOV5Ien8ReN78wro7OF5/erwcmortzeFx+iBswlh2Vz6gVne0khocQudGwaO1Ey6A==", + "dependencies": { + "Microsoft.Win32.SystemEvents": "7.0.0" + } + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "FHNOatmUq0sqJOkTx+UF/9YK1f180cnW5FVqnQMvYUN0elp6wFzbtPSiqbo1/ru8ICp43JM1i7kKkk6GsNGHlA==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==" + }, + "ToolGood.Words.Pinyin": { + "type": "Transitive", + "resolved": "3.1.0.3", + "contentHash": "VKcf8sUq/+LyY99WgLhOu7Q32ROEyR30/2xCCj9ADRi45wVC7kpXrYCf9vH1qirkmrIfpL8inoxAbrqAlfXxsQ==" + }, + "YamlDotNet": { + "type": "Transitive", + "resolved": "9.1.0", + "contentHash": "fuvGXU4Ec5HrsmEc+BiFTNPCRf1cGBI2kh/3RzMWgddM2M4ALhbSPoI3X3mhXZUD1qqQd9oSkFAtWjpz8z9eRg==" + }, + "flow.launcher.infrastructure": { + "type": "Project", + "dependencies": { + "Ben.Demystifier": "[0.4.1, )", + "BitFaster.Caching": "[2.5.4, )", + "CommunityToolkit.Mvvm": "[8.4.0, )", + "Flow.Launcher.Plugin": "[4.7.0, )", + "InputSimulator": "[1.0.4, )", + "MemoryPack": "[1.21.4, )", + "Microsoft.VisualStudio.Threading": "[17.14.15, )", + "NHotkey.Wpf": "[3.0.0, )", + "NLog": "[6.0.1, )", + "NLog.OutputDebugString": "[6.0.1, )", + "SharpVectors.Wpf": "[1.8.4.2, )", + "System.Drawing.Common": "[7.0.0, )", + "ToolGood.Words.Pinyin": "[3.1.0.3, )" + } + }, + "flow.launcher.plugin": { + "type": "Project", + "dependencies": { + "JetBrains.Annotations": "[2024.3.0, )" + } + } + } + } +} \ No newline at end of file diff --git a/Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs b/Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs new file mode 100644 index 000000000..65652878f --- /dev/null +++ b/Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs @@ -0,0 +1,1079 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Threading; +using CommunityToolkit.Mvvm.DependencyInjection; +using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Infrastructure.DialogJump.Models; +using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Plugin; +using NHotkey; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.Accessibility; + +namespace Flow.Launcher.Infrastructure.DialogJump +{ + public static class DialogJump + { + #region Public Properties + + public static Func ShowDialogJumpWindowAsync { get; set; } = null; + + public static Action UpdateDialogJumpWindow { get; set; } = null; + + public static Action ResetDialogJumpWindow { get; set; } = null; + + public static Action HideDialogJumpWindow { get; set; } = null; + + public static DialogJumpWindowPositions DialogJumpWindowPosition { get; private set; } + + public static DialogJumpExplorerPair WindowsDialogJumpExplorer { get; } = new() + { + Metadata = new() + { + ID = "298b197c08a24e90ab66ac060ee2b6b8", // ID is for calculating the hash id of the Dialog Jump pairs + Disabled = false // Disabled is for enabling the Windows DialogJump explorers & dialogs + }, + Plugin = new WindowsExplorer() + }; + + public static DialogJumpDialogPair WindowsDialogJumpDialog { get; } = new() + { + Metadata = new() + { + ID = "a4a113dc51094077ab4abb391e866c7b", // ID is for calculating the hash id of the Dialog Jump pairs + Disabled = false // Disabled is for enabling the Windows DialogJump explorers & dialogs + }, + Plugin = new WindowsDialog() + }; + + #endregion + + #region Private Fields + + private static readonly string ClassName = nameof(DialogJump); + + private static readonly Settings _settings = Ioc.Default.GetRequiredService(); + + // We should not initialize API in static constructor because it will create another API instance + private static IPublicAPI api = null; + private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); + + private static HWND _mainWindowHandle = HWND.Null; + + private static readonly Dictionary _dialogJumpExplorers = new(); + + private static DialogJumpExplorerPair _lastExplorer = null; + private static readonly object _lastExplorerLock = new(); + + private static readonly Dictionary _dialogJumpDialogs = new(); + + private static IDialogJumpDialogWindow _dialogWindow = null; + private static readonly object _dialogWindowLock = new(); + + private static HWINEVENTHOOK _foregroundChangeHook = HWINEVENTHOOK.Null; + private static HWINEVENTHOOK _locationChangeHook = HWINEVENTHOOK.Null; + private static HWINEVENTHOOK _destroyChangeHook = HWINEVENTHOOK.Null; + private static HWINEVENTHOOK _hideChangeHook = HWINEVENTHOOK.Null; + private static HWINEVENTHOOK _dialogEndChangeHook = HWINEVENTHOOK.Null; + + private static readonly WINEVENTPROC _fgProc = ForegroundChangeCallback; + private static readonly WINEVENTPROC _locProc = LocationChangeCallback; + private static readonly WINEVENTPROC _desProc = DestroyChangeCallback; + private static readonly WINEVENTPROC _hideProc = HideChangeCallback; + private static readonly WINEVENTPROC _dialogEndProc = DialogEndChangeCallback; + + private static DispatcherTimer _dragMoveTimer = null; + + // A list of all file dialog windows that are auto switched already + private static readonly List _autoSwitchedDialogs = new(); + private static readonly object _autoSwitchedDialogsLock = new(); + + private static HWINEVENTHOOK _moveSizeHook = HWINEVENTHOOK.Null; + private static readonly WINEVENTPROC _moveProc = MoveSizeCallBack; + + private static readonly SemaphoreSlim _foregroundChangeLock = new(1, 1); + private static readonly SemaphoreSlim _navigationLock = new(1, 1); + + private static bool _initialized = false; + private static bool _enabled = false; + + #endregion + + #region Initialize & Setup + + public static void InitializeDialogJump(IList dialogJumpExplorers, + IList dialogJumpDialogs) + { + if (_initialized) return; + + // Initialize Dialog Jump explorers & dialogs + _dialogJumpExplorers.Add(WindowsDialogJumpExplorer, null); + foreach (var explorer in dialogJumpExplorers) + { + _dialogJumpExplorers.Add(explorer, null); + } + _dialogJumpDialogs.Add(WindowsDialogJumpDialog, null); + foreach (var dialog in dialogJumpDialogs) + { + _dialogJumpDialogs.Add(dialog, null); + } + + // Initialize main window handle + _mainWindowHandle = Win32Helper.GetMainWindowHandle(); + + // Initialize timer + _dragMoveTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(10) }; + _dragMoveTimer.Tick += (s, e) => InvokeUpdateDialogJumpWindow(); + + // Initialize Dialog Jump window position + DialogJumpWindowPosition = _settings.DialogJumpWindowPosition; + + _initialized = true; + } + + public static void SetupDialogJump(bool enabled) + { + if (enabled == _enabled) return; + + if (enabled) + { + // Check if there are explorer windows and get the topmost one + try + { + if (RefreshLastExplorer()) + { + Log.Debug(ClassName, $"Explorer window found"); + } + } + catch (System.Exception) + { + // Ignored + } + + // Unhook events + if (!_foregroundChangeHook.IsNull) + { + PInvoke.UnhookWinEvent(_foregroundChangeHook); + _foregroundChangeHook = HWINEVENTHOOK.Null; + } + if (!_locationChangeHook.IsNull) + { + PInvoke.UnhookWinEvent(_locationChangeHook); + _locationChangeHook = HWINEVENTHOOK.Null; + } + if (!_destroyChangeHook.IsNull) + { + PInvoke.UnhookWinEvent(_destroyChangeHook); + _destroyChangeHook = HWINEVENTHOOK.Null; + } + if (!_hideChangeHook.IsNull) + { + PInvoke.UnhookWinEvent(_hideChangeHook); + _hideChangeHook = HWINEVENTHOOK.Null; + } + if (!_dialogEndChangeHook.IsNull) + { + PInvoke.UnhookWinEvent(_dialogEndChangeHook); + _dialogEndChangeHook = HWINEVENTHOOK.Null; + } + + // Hook events + _foregroundChangeHook = PInvoke.SetWinEventHook( + PInvoke.EVENT_SYSTEM_FOREGROUND, + PInvoke.EVENT_SYSTEM_FOREGROUND, + PInvoke.GetModuleHandle((PCWSTR)null), + _fgProc, + 0, + 0, + PInvoke.WINEVENT_OUTOFCONTEXT); + _locationChangeHook = PInvoke.SetWinEventHook( + PInvoke.EVENT_OBJECT_LOCATIONCHANGE, + PInvoke.EVENT_OBJECT_LOCATIONCHANGE, + PInvoke.GetModuleHandle((PCWSTR)null), + _locProc, + 0, + 0, + PInvoke.WINEVENT_OUTOFCONTEXT); + _destroyChangeHook = PInvoke.SetWinEventHook( + PInvoke.EVENT_OBJECT_DESTROY, + PInvoke.EVENT_OBJECT_DESTROY, + PInvoke.GetModuleHandle((PCWSTR)null), + _desProc, + 0, + 0, + PInvoke.WINEVENT_OUTOFCONTEXT); + _hideChangeHook = PInvoke.SetWinEventHook( + PInvoke.EVENT_OBJECT_HIDE, + PInvoke.EVENT_OBJECT_HIDE, + PInvoke.GetModuleHandle((PCWSTR)null), + _hideProc, + 0, + 0, + PInvoke.WINEVENT_OUTOFCONTEXT); + _dialogEndChangeHook = PInvoke.SetWinEventHook( + PInvoke.EVENT_SYSTEM_DIALOGEND, + PInvoke.EVENT_SYSTEM_DIALOGEND, + PInvoke.GetModuleHandle((PCWSTR)null), + _dialogEndProc, + 0, + 0, + PInvoke.WINEVENT_OUTOFCONTEXT); + + if (_foregroundChangeHook.IsNull || + _locationChangeHook.IsNull || + _destroyChangeHook.IsNull || + _hideChangeHook.IsNull || + _dialogEndChangeHook.IsNull) + { + Log.Error(ClassName, "Failed to enable DialogJump"); + return; + } + } + else + { + // Remove explorer windows + foreach (var explorer in _dialogJumpExplorers.Keys) + { + _dialogJumpExplorers[explorer] = null; + } + + // Remove dialog windows + foreach (var dialog in _dialogJumpDialogs.Keys) + { + _dialogJumpDialogs[dialog] = null; + } + + // Remove dialog window handle + var dialogWindowExists = false; + lock (_dialogWindowLock) + { + if (_dialogWindow != null) + { + _dialogWindow = null; + dialogWindowExists = true; + } + } + + // Remove auto switched dialogs + lock (_autoSwitchedDialogsLock) + { + _autoSwitchedDialogs.Clear(); + } + + // Unhook events + if (!_foregroundChangeHook.IsNull) + { + PInvoke.UnhookWinEvent(_foregroundChangeHook); + _foregroundChangeHook = HWINEVENTHOOK.Null; + } + if (!_locationChangeHook.IsNull) + { + PInvoke.UnhookWinEvent(_locationChangeHook); + _locationChangeHook = HWINEVENTHOOK.Null; + } + if (!_destroyChangeHook.IsNull) + { + PInvoke.UnhookWinEvent(_destroyChangeHook); + _destroyChangeHook = HWINEVENTHOOK.Null; + } + if (!_hideChangeHook.IsNull) + { + PInvoke.UnhookWinEvent(_hideChangeHook); + _hideChangeHook = HWINEVENTHOOK.Null; + } + if (!_dialogEndChangeHook.IsNull) + { + PInvoke.UnhookWinEvent(_dialogEndChangeHook); + _dialogEndChangeHook = HWINEVENTHOOK.Null; + } + + // Stop drag move timer + _dragMoveTimer?.Stop(); + + // Reset Dialog Jump window + if (dialogWindowExists) + { + InvokeResetDialogJumpWindow(); + } + } + + _enabled = enabled; + } + + private static bool RefreshLastExplorer() + { + var found = false; + + lock (_lastExplorerLock) + { + // Enum windows from the top to the bottom + PInvoke.EnumWindows((hWnd, _) => + { + foreach (var explorer in _dialogJumpExplorers.Keys) + { + if (API.PluginModified(explorer.Metadata.ID) || // Plugin is modified + explorer.Metadata.Disabled) continue; // Plugin is disabled + + var explorerWindow = explorer.Plugin.CheckExplorerWindow(hWnd); + if (explorerWindow != null) + { + _dialogJumpExplorers[explorer] = explorerWindow; + _lastExplorer = explorer; + found = true; + return false; + } + } + + // If we reach here, it means that the window is not a file explorer + return true; + }, IntPtr.Zero); + } + + return found; + } + + #endregion + + #region Active Explorer + + public static string GetActiveExplorerPath() + { + return RefreshLastExplorer() ? _dialogJumpExplorers[_lastExplorer].GetExplorerPath() : string.Empty; + } + + #endregion + + #region Events + + #region Invoke Property Events + + private static async Task InvokeShowDialogJumpWindowAsync(bool dialogWindowChanged) + { + // Show Dialog Jump window + if (_settings.ShowDialogJumpWindow) + { + // Save Dialog Jump window position for one file dialog + if (dialogWindowChanged) + { + DialogJumpWindowPosition = _settings.DialogJumpWindowPosition; + } + + // Call show Dialog Jump window + IDialogJumpDialogWindow dialogWindow; + lock (_dialogWindowLock) + { + dialogWindow = _dialogWindow; + } + if (dialogWindow != null && ShowDialogJumpWindowAsync != null) + { + await ShowDialogJumpWindowAsync.Invoke(dialogWindow.Handle); + } + + // Hook move size event if Dialog Jump window is under dialog & dialog window changed + if (DialogJumpWindowPosition == DialogJumpWindowPositions.UnderDialog) + { + if (dialogWindowChanged) + { + HWND dialogWindowHandle = HWND.Null; + lock (_dialogWindowLock) + { + if (_dialogWindow != null) + { + dialogWindowHandle = new(_dialogWindow.Handle); + } + } + + if (dialogWindowHandle == HWND.Null) return; + + if (!_moveSizeHook.IsNull) + { + PInvoke.UnhookWinEvent(_moveSizeHook); + _moveSizeHook = HWINEVENTHOOK.Null; + } + + // Call _moveProc when the window is moved or resized + SetMoveProc(dialogWindowHandle); + } + } + } + + static unsafe void SetMoveProc(HWND handle) + { + uint processId; + var threadId = PInvoke.GetWindowThreadProcessId(handle, &processId); + _moveSizeHook = PInvoke.SetWinEventHook( + PInvoke.EVENT_SYSTEM_MOVESIZESTART, + PInvoke.EVENT_SYSTEM_MOVESIZEEND, + PInvoke.GetModuleHandle((PCWSTR)null), + _moveProc, + processId, + threadId, + PInvoke.WINEVENT_OUTOFCONTEXT); + } + } + + private static void InvokeUpdateDialogJumpWindow() + { + UpdateDialogJumpWindow?.Invoke(); + } + + private static void InvokeResetDialogJumpWindow() + { + lock (_dialogWindowLock) + { + _dialogWindow = null; + } + + // Reset Dialog Jump window + ResetDialogJumpWindow?.Invoke(); + + // Stop drag move timer + _dragMoveTimer?.Stop(); + + // Unhook move size event + if (!_moveSizeHook.IsNull) + { + PInvoke.UnhookWinEvent(_moveSizeHook); + _moveSizeHook = HWINEVENTHOOK.Null; + } + } + + private static void InvokeHideDialogJumpWindow() + { + // Hide Dialog Jump window + HideDialogJumpWindow?.Invoke(); + + // Stop drag move timer + _dragMoveTimer?.Stop(); + } + + #endregion + + #region Hotkey + + public static void OnToggleHotkey(object sender, HotkeyEventArgs args) + { + _ = Task.Run(async () => + { + try + { + await NavigateDialogPathAsync(PInvoke.GetForegroundWindow()); + } + catch (System.Exception ex) + { + Log.Exception(ClassName, "Failed to navigate dialog path", ex); + } + }); + } + + #endregion + + #region Windows Events + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "")] + private static async void ForegroundChangeCallback( + HWINEVENTHOOK hWinEventHook, + uint eventType, + HWND hwnd, + int idObject, + int idChild, + uint dwEventThread, + uint dwmsEventTime + ) + { + await _foregroundChangeLock.WaitAsync(); + try + { + // Check if it is a file dialog window + var isDialogWindow = false; + var dialogWindowChanged = false; + foreach (var dialog in _dialogJumpDialogs.Keys) + { + if (API.PluginModified(dialog.Metadata.ID) || // Plugin is modified + dialog.Metadata.Disabled) continue; // Plugin is disabled + + IDialogJumpDialogWindow dialogWindow; + var existingDialogWindow = _dialogJumpDialogs[dialog]; + if (existingDialogWindow != null && existingDialogWindow.Handle == hwnd) + { + // If the dialog window is already in the list, no need to check again + dialogWindow = existingDialogWindow; + } + else + { + dialogWindow = dialog.Plugin.CheckDialogWindow(hwnd); + } + + // If the dialog window is found, set it + if (dialogWindow != null) + { + lock (_dialogWindowLock) + { + dialogWindowChanged = _dialogWindow == null || _dialogWindow.Handle != hwnd; + _dialogWindow = dialogWindow; + } + + isDialogWindow = true; + break; + } + } + + // Handle window based on its type + if (isDialogWindow) + { + Log.Debug(ClassName, $"Dialog Window: {hwnd}"); + // Navigate to path + if (_settings.AutoDialogJump) + { + // Check if we have already switched for this dialog + bool alreadySwitched; + lock (_autoSwitchedDialogsLock) + { + alreadySwitched = _autoSwitchedDialogs.Contains(hwnd); + } + + // Just show Dialog Jump window + if (alreadySwitched) + { + await InvokeShowDialogJumpWindowAsync(dialogWindowChanged); + } + // Show Dialog Jump window after navigating the path + else + { + if (!await Task.Run(async () => + { + try + { + return await NavigateDialogPathAsync(hwnd, true); + } + catch (System.Exception ex) + { + Log.Exception(ClassName, "Failed to navigate dialog path", ex); + return false; + } + })) + { + await InvokeShowDialogJumpWindowAsync(dialogWindowChanged); + } + } + } + else + { + await InvokeShowDialogJumpWindowAsync(dialogWindowChanged); + } + } + // Dialog jump window + else if (hwnd == _mainWindowHandle) + { + Log.Debug(ClassName, $"Main Window: {hwnd}"); + } + // Other window + else + { + Log.Debug(ClassName, $"Other Window: {hwnd}"); + var dialogWindowExist = false; + lock (_dialogWindowLock) + { + if (_dialogWindow != null) + { + dialogWindowExist = true; + } + } + if (dialogWindowExist) // Neither Dialog Jump window nor file dialog window is foreground + { + // Hide Dialog Jump window until the file dialog window is brought to the foreground + InvokeHideDialogJumpWindow(); + } + + // Check if there are foreground explorer windows + try + { + lock (_lastExplorerLock) + { + foreach (var explorer in _dialogJumpExplorers.Keys) + { + if (API.PluginModified(explorer.Metadata.ID) || // Plugin is modified + explorer.Metadata.Disabled) continue; // Plugin is disabled + + var explorerWindow = explorer.Plugin.CheckExplorerWindow(hwnd); + if (explorerWindow != null) + { + Log.Debug(ClassName, $"Explorer window: {hwnd}"); + _dialogJumpExplorers[explorer] = explorerWindow; + _lastExplorer = explorer; + break; + } + } + } + } + catch (System.Exception ex) + { + Log.Exception(ClassName, "An error occurred while checking foreground explorer windows", ex); + } + } + } + catch (System.Exception ex) + { + Log.Exception(ClassName, "Failed to invoke ForegroundChangeCallback", ex); + } + finally + { + _foregroundChangeLock.Release(); + } + } + + private static void LocationChangeCallback( + HWINEVENTHOOK hWinEventHook, + uint eventType, + HWND hwnd, + int idObject, + int idChild, + uint dwEventThread, + uint dwmsEventTime + ) + { + // If the dialog window is moved, update the Dialog Jump window position + var dialogWindowExist = false; + lock (_dialogWindowLock) + { + if (_dialogWindow != null && _dialogWindow.Handle == hwnd) + { + dialogWindowExist = true; + } + } + if (dialogWindowExist) + { + InvokeUpdateDialogJumpWindow(); + } + } + + private static void MoveSizeCallBack( + HWINEVENTHOOK hWinEventHook, + uint eventType, + HWND hwnd, + int idObject, + int idChild, + uint dwEventThread, + uint dwmsEventTime + ) + { + // If the dialog window is moved or resized, update the Dialog Jump window position + if (_dragMoveTimer != null) + { + switch (eventType) + { + case PInvoke.EVENT_SYSTEM_MOVESIZESTART: + _dragMoveTimer.Start(); // Start dragging position + break; + case PInvoke.EVENT_SYSTEM_MOVESIZEEND: + _dragMoveTimer.Stop(); // Stop dragging + break; + } + } + } + + private static void DestroyChangeCallback( + HWINEVENTHOOK hWinEventHook, + uint eventType, + HWND hwnd, + int idObject, + int idChild, + uint dwEventThread, + uint dwmsEventTime + ) + { + // If the dialog window is destroyed, set _dialogWindowHandle to null + var dialogWindowExist = false; + lock (_dialogWindowLock) + { + if (_dialogWindow != null && _dialogWindow.Handle == hwnd) + { + Log.Debug(ClassName, $"Destory dialog: {hwnd}"); + _dialogWindow = null; + dialogWindowExist = true; + } + } + if (dialogWindowExist) + { + lock (_autoSwitchedDialogsLock) + { + _autoSwitchedDialogs.Remove(hwnd); + } + InvokeResetDialogJumpWindow(); + } + } + + private static void HideChangeCallback( + HWINEVENTHOOK hWinEventHook, + uint eventType, + HWND hwnd, + int idObject, + int idChild, + uint dwEventThread, + uint dwmsEventTime + ) + { + // If the dialog window is hidden, set _dialogWindowHandle to null + var dialogWindowExist = false; + lock (_dialogWindowLock) + { + if (_dialogWindow != null && _dialogWindow.Handle == hwnd) + { + Log.Debug(ClassName, $"Hide dialog: {hwnd}"); + _dialogWindow = null; + dialogWindowExist = true; + } + } + if (dialogWindowExist) + { + lock (_autoSwitchedDialogsLock) + { + _autoSwitchedDialogs.Remove(hwnd); + } + InvokeResetDialogJumpWindow(); + } + } + + private static void DialogEndChangeCallback( + HWINEVENTHOOK hWinEventHook, + uint eventType, + HWND hwnd, + int idObject, + int idChild, + uint dwEventThread, + uint dwmsEventTime + ) + { + // If the dialog window is ended, set _dialogWindowHandle to null + var dialogWindowExist = false; + lock (_dialogWindowLock) + { + if (_dialogWindow != null && _dialogWindow.Handle == hwnd) + { + Log.Debug(ClassName, $"End dialog: {hwnd}"); + _dialogWindow = null; + dialogWindowExist = true; + } + } + if (dialogWindowExist) + { + lock (_autoSwitchedDialogsLock) + { + _autoSwitchedDialogs.Remove(hwnd); + } + InvokeResetDialogJumpWindow(); + } + } + + #endregion + + #endregion + + #region Path Navigation + + // Edited from: https://github.com/idkidknow/Flow.Launcher.Plugin.DirQuickJump + + public static async Task JumpToPathAsync(nint hwnd, string path) + { + // Check handle + if (hwnd == nint.Zero) return false; + + // Check path null or empty + if (string.IsNullOrEmpty(path)) return false; + + // Check path + if (!CheckPath(path, out var isFile)) return false; + + // Get dialog tab + var dialogWindowTab = GetDialogWindowTab(new(hwnd)); + if (dialogWindowTab == null) return false; + + return await JumpToPathAsync(dialogWindowTab, path, isFile, false); + } + + private static async Task NavigateDialogPathAsync(HWND hwnd, bool auto = false) + { + // Check handle + if (hwnd == HWND.Null) return false; + + // Get explorer path + string path; + lock (_lastExplorerLock) + { + path = _dialogJumpExplorers[_lastExplorer]?.GetExplorerPath(); + } + + // Check path null or empty + if (string.IsNullOrEmpty(path)) return false; + + // Check path + if (!CheckPath(path, out var isFile)) return false; + + // Get dialog tab + var dialogWindowTab = GetDialogWindowTab(hwnd); + if (dialogWindowTab == null) return false; + + // Jump to path + return await JumpToPathAsync(dialogWindowTab, path, isFile, auto); + } + + private static bool CheckPath(string path, out bool file) + { + file = false; + try + { + // shell: and shell::: paths + if (path.StartsWith("shell:", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + // file: URI paths + var localPath = path.StartsWith("file:", StringComparison.OrdinalIgnoreCase) + ? new Uri(path).LocalPath + : path; + // Is folder? + var isFolder = Directory.Exists(localPath); + // Is file? + var isFile = File.Exists(localPath); + file = isFile; + return isFolder || isFile; + } + catch (System.Exception e) + { + Log.Exception(ClassName, "Failed to check path", e); + return false; + } + } + + private static IDialogJumpDialogWindowTab GetDialogWindowTab(HWND hwnd) + { + var dialogWindow = GetDialogWindow(hwnd); + if (dialogWindow == null) return null; + var dialogWindowTab = dialogWindow.GetCurrentTab(); + return dialogWindowTab; + } + + private static IDialogJumpDialogWindow GetDialogWindow(HWND hwnd) + { + // First check dialog window + lock (_dialogWindowLock) + { + if (_dialogWindow != null && _dialogWindow.Handle == hwnd) + { + return _dialogWindow; + } + } + + // Then check all dialog windows + foreach (var dialog in _dialogJumpDialogs.Keys) + { + if (API.PluginModified(dialog.Metadata.ID) || // Plugin is modified + dialog.Metadata.Disabled) continue; // Plugin is disabled + + var dialogWindow = _dialogJumpDialogs[dialog]; + if (dialogWindow != null && dialogWindow.Handle == hwnd) + { + return dialogWindow; + } + } + + // Finally search for the dialog window again + foreach (var dialog in _dialogJumpDialogs.Keys) + { + if (API.PluginModified(dialog.Metadata.ID) || // Plugin is modified + dialog.Metadata.Disabled) continue; // Plugin is disabled + + IDialogJumpDialogWindow dialogWindow; + var existingDialogWindow = _dialogJumpDialogs[dialog]; + if (existingDialogWindow != null && existingDialogWindow.Handle == hwnd) + { + // If the dialog window is already in the list, no need to check again + dialogWindow = existingDialogWindow; + } + else + { + dialogWindow = dialog.Plugin.CheckDialogWindow(hwnd); + } + + // Update dialog window if found + if (dialogWindow != null) + { + _dialogJumpDialogs[dialog] = dialogWindow; + return dialogWindow; + } + } + + return null; + } + + private static async Task JumpToPathAsync(IDialogJumpDialogWindowTab dialog, string path, bool isFile, bool auto = false) + { + // Jump after flow launcher window vanished (after JumpAction returned true) + // and the dialog had been in the foreground. + var dialogHandle = dialog.Handle; + var timeOut = !SpinWait.SpinUntil(() => Win32Helper.IsForegroundWindow(dialogHandle), 1000); + if (timeOut) return false; + + // Assume that the dialog is in the foreground now + await _navigationLock.WaitAsync(); + try + { + bool result; + if (isFile) + { + switch (_settings.DialogJumpFileResultBehaviour) + { + case DialogJumpFileResultBehaviours.FullPath: + Log.Debug(ClassName, $"File Jump FullPath: {path}"); + result = FileJump(path, dialog); + break; + case DialogJumpFileResultBehaviours.FullPathOpen: + Log.Debug(ClassName, $"File Jump FullPathOpen: {path}"); + result = FileJump(path, dialog, openFile: true); + break; + case DialogJumpFileResultBehaviours.Directory: + Log.Debug(ClassName, $"File Jump Directory (Auto: {auto}): {path}"); + result = DirJump(Path.GetDirectoryName(path), dialog, auto); + break; + default: + return false; + } + } + else + { + Log.Debug(ClassName, $"Dir Jump: {path}"); + result = DirJump(path, dialog, auto); + } + + if (result) + { + lock (_autoSwitchedDialogsLock) + { + _autoSwitchedDialogs.Add(new(dialogHandle)); + } + } + + return result; + } + catch (System.Exception e) + { + Log.Exception(ClassName, "Failed to jump to path", e); + return false; + } + finally + { + _navigationLock.Release(); + } + } + + private static bool FileJump(string filePath, IDialogJumpDialogWindowTab dialog, bool openFile = false) + { + if (!dialog.JumpFile(filePath)) + { + Log.Error(ClassName, "Failed to jump file"); + return false; + } + + if (openFile && !dialog.Open()) + { + Log.Error(ClassName, "Failed to open file"); + return false; + } + + return true; + } + + private static bool DirJump(string dirPath, IDialogJumpDialogWindowTab dialog, bool auto = false) + { + if (!dialog.JumpFolder(dirPath, auto)) + { + Log.Error(ClassName, "Failed to jump folder"); + return false; + } + + return true; + } + + #endregion + + #region Dispose + + public static void Dispose() + { + // Reset flags + _enabled = false; + _initialized = false; + + // Unhook events + if (!_foregroundChangeHook.IsNull) + { + PInvoke.UnhookWinEvent(_foregroundChangeHook); + _foregroundChangeHook = HWINEVENTHOOK.Null; + } + if (!_locationChangeHook.IsNull) + { + PInvoke.UnhookWinEvent(_locationChangeHook); + _locationChangeHook = HWINEVENTHOOK.Null; + } + if (!_moveSizeHook.IsNull) + { + PInvoke.UnhookWinEvent(_moveSizeHook); + _moveSizeHook = HWINEVENTHOOK.Null; + } + if (!_destroyChangeHook.IsNull) + { + PInvoke.UnhookWinEvent(_destroyChangeHook); + _destroyChangeHook = HWINEVENTHOOK.Null; + } + if (!_hideChangeHook.IsNull) + { + PInvoke.UnhookWinEvent(_hideChangeHook); + _hideChangeHook = HWINEVENTHOOK.Null; + } + if (!_dialogEndChangeHook.IsNull) + { + PInvoke.UnhookWinEvent(_dialogEndChangeHook); + _dialogEndChangeHook = HWINEVENTHOOK.Null; + } + + // Dispose explorers + foreach (var explorer in _dialogJumpExplorers.Keys) + { + _dialogJumpExplorers[explorer]?.Dispose(); + } + _dialogJumpExplorers.Clear(); + lock (_lastExplorerLock) + { + _lastExplorer = null; + } + + // Dispose dialogs + foreach (var dialog in _dialogJumpDialogs.Keys) + { + _dialogJumpDialogs[dialog]?.Dispose(); + } + _dialogJumpDialogs.Clear(); + lock (_dialogWindowLock) + { + _dialogWindow = null; + } + + // Dispose locks + _foregroundChangeLock.Dispose(); + _navigationLock.Dispose(); + + // Stop drag move timer + if (_dragMoveTimer != null) + { + _dragMoveTimer.Stop(); + _dragMoveTimer = null; + } + } + + #endregion + } +} diff --git a/Flow.Launcher.Infrastructure/DialogJump/DialogJumpPair.cs b/Flow.Launcher.Infrastructure/DialogJump/DialogJumpPair.cs new file mode 100644 index 000000000..d1248eac1 --- /dev/null +++ b/Flow.Launcher.Infrastructure/DialogJump/DialogJumpPair.cs @@ -0,0 +1,63 @@ +using Flow.Launcher.Plugin; + +namespace Flow.Launcher.Infrastructure.DialogJump; + +public class DialogJumpExplorerPair +{ + public IDialogJumpExplorer Plugin { get; init; } + + public PluginMetadata Metadata { get; init; } + + public override string ToString() + { + return Metadata.Name; + } + + public override bool Equals(object obj) + { + if (obj is DialogJumpExplorerPair r) + { + return string.Equals(r.Metadata.ID, Metadata.ID); + } + else + { + return false; + } + } + + public override int GetHashCode() + { + var hashcode = Metadata.ID?.GetHashCode() ?? 0; + return hashcode; + } +} + +public class DialogJumpDialogPair +{ + public IDialogJumpDialog Plugin { get; init; } + + public PluginMetadata Metadata { get; init; } + + public override string ToString() + { + return Metadata.Name; + } + + public override bool Equals(object obj) + { + if (obj is DialogJumpDialogPair r) + { + return string.Equals(r.Metadata.ID, Metadata.ID); + } + else + { + return false; + } + } + + public override int GetHashCode() + { + var hashcode = Metadata.ID?.GetHashCode() ?? 0; + return hashcode; + } +} diff --git a/Flow.Launcher.Infrastructure/DialogJump/Models/WindowsDialog.cs b/Flow.Launcher.Infrastructure/DialogJump/Models/WindowsDialog.cs new file mode 100644 index 000000000..ee4e03433 --- /dev/null +++ b/Flow.Launcher.Infrastructure/DialogJump/Models/WindowsDialog.cs @@ -0,0 +1,345 @@ +using System; +using System.Threading; +using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Plugin; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.WindowsAndMessaging; +using WindowsInput; +using WindowsInput.Native; + +namespace Flow.Launcher.Infrastructure.DialogJump.Models +{ + /// + /// Class for handling Windows File Dialog instances in DialogJump. + /// + public class WindowsDialog : IDialogJumpDialog + { + private const string WindowsDialogClassName = "#32770"; + + public IDialogJumpDialogWindow CheckDialogWindow(IntPtr hwnd) + { + // Is it a Win32 dialog box? + if (GetClassName(new(hwnd)) == WindowsDialogClassName) + { + // Is it a windows file dialog? + var dialogType = GetFileDialogType(new(hwnd)); + if (dialogType != DialogType.Others) + { + return new WindowsDialogWindow(hwnd, dialogType); + } + } + + return null; + } + + public void Dispose() + { + + } + + #region Help Methods + + private static unsafe string GetClassName(HWND handle) + { + fixed (char* buf = new char[256]) + { + return PInvoke.GetClassName(handle, buf, 256) switch + { + 0 => string.Empty, + _ => new string(buf), + }; + } + } + + private static DialogType GetFileDialogType(HWND handle) + { + // Is it a Windows Open file dialog? + var fileEditor = PInvoke.GetDlgItem(handle, 0x047C); + if (fileEditor != HWND.Null && GetClassName(fileEditor) == "ComboBoxEx32") return DialogType.Open; + + // Is it a Windows Save or Save As file dialog? + fileEditor = PInvoke.GetDlgItem(handle, 0x0000); + if (fileEditor != HWND.Null && GetClassName(fileEditor) == "DUIViewWndClassName") return DialogType.SaveOrSaveAs; + + return DialogType.Others; + } + + #endregion + } + + public class WindowsDialogWindow : IDialogJumpDialogWindow + { + public IntPtr Handle { get; private set; } = IntPtr.Zero; + + // After jumping folder, file editor handle of Save / SaveAs file dialogs cannot be found anymore + // So we need to cache the current tab and use the original handle + private IDialogJumpDialogWindowTab _currentTab { get; set; } = null; + + private readonly DialogType _dialogType; + + internal WindowsDialogWindow(IntPtr handle, DialogType dialogType) + { + Handle = handle; + _dialogType = dialogType; + } + + public IDialogJumpDialogWindowTab GetCurrentTab() + { + return _currentTab ??= new WindowsDialogTab(Handle, _dialogType); + } + + public void Dispose() + { + + } + } + + public class WindowsDialogTab : IDialogJumpDialogWindowTab + { + #region Public Properties + + public IntPtr Handle { get; private set; } = IntPtr.Zero; + + #endregion + + #region Private Fields + + private static readonly string ClassName = nameof(WindowsDialogTab); + + private static readonly InputSimulator _inputSimulator = new(); + + private readonly DialogType _dialogType; + + private bool _legacy { get; set; } = false; + private HWND _pathControl { get; set; } = HWND.Null; + private HWND _pathEditor { get; set; } = HWND.Null; + private HWND _fileEditor { get; set; } = HWND.Null; + private HWND _openButton { get; set; } = HWND.Null; + + #endregion + + #region Constructor + + internal WindowsDialogTab(IntPtr handle, DialogType dialogType) + { + Handle = handle; + _dialogType = dialogType; + Log.Debug(ClassName, $"File dialog type: {dialogType}"); + } + + #endregion + + #region Public Methods + + public string GetCurrentFolder() + { + if (_pathEditor.IsNull && !GetPathControlEditor()) return string.Empty; + return GetWindowText(_pathEditor); + } + + public string GetCurrentFile() + { + if (_fileEditor.IsNull && !GetFileEditor()) return string.Empty; + return GetWindowText(_fileEditor); + } + + public bool JumpFolder(string path, bool auto) + { + if (auto) + { + // Use legacy jump folder method for auto Dialog Jump because file editor is default value. + // After setting path using file editor, we do not need to revert its value. + return JumpFolderWithFileEditor(path, false); + } + + // Alt-D or Ctrl-L to focus on the path input box + // "ComboBoxEx32" is not visible when the path editor is not with the keyboard focus + _inputSimulator.Keyboard.ModifiedKeyStroke(VirtualKeyCode.LMENU, VirtualKeyCode.VK_D); + // _inputSimulator.Keyboard.ModifiedKeyStroke(VirtualKeyCode.LCONTROL, VirtualKeyCode.VK_L); + + if (_pathControl.IsNull && !GetPathControlEditor()) + { + // https://github.com/idkidknow/Flow.Launcher.Plugin.DirQuickJump/issues/1 + // The dialog is a legacy one, so we can only edit file editor directly. + Log.Debug(ClassName, "Legacy dialog, using legacy jump folder method"); + return JumpFolderWithFileEditor(path, true); + } + + var timeOut = !SpinWait.SpinUntil(() => + { + var style = PInvoke.GetWindowLongPtr(_pathControl, WINDOW_LONG_PTR_INDEX.GWL_STYLE); + return (style & (int)WINDOW_STYLE.WS_VISIBLE) != 0; + }, 1000); + if (timeOut) + { + // Path control is not visible, so we can only edit file editor directly. + Log.Debug(ClassName, "Path control is not visible, using legacy jump folder method"); + return JumpFolderWithFileEditor(path, true); + } + + if (_pathEditor.IsNull) + { + // Path editor cannot be found, so we can only edit file editor directly. + Log.Debug(ClassName, "Path editor cannot be found, using legacy jump folder method"); + return JumpFolderWithFileEditor(path, true); + } + SetWindowText(_pathEditor, path); + + _inputSimulator.Keyboard.KeyPress(VirtualKeyCode.RETURN); + + return true; + } + + public bool JumpFile(string path) + { + if (_fileEditor.IsNull && !GetFileEditor()) return false; + SetWindowText(_fileEditor, path); + + return true; + } + + public bool Open() + { + if (_openButton.IsNull && !GetOpenButton()) return false; + PInvoke.PostMessage(_openButton, PInvoke.BM_CLICK, 0, 0); + + return true; + } + + public void Dispose() + { + + } + + #endregion + + #region Helper Methods + + #region Get Handles + + private bool GetPathControlEditor() + { + // Get the handle of the path editor + // Must use PInvoke.FindWindowEx because PInvoke.GetDlgItem(Handle, 0x0000) will get another control + _pathControl = PInvoke.FindWindowEx(new(Handle), HWND.Null, "WorkerW", null); // 0x0000 + _pathControl = PInvoke.FindWindowEx(_pathControl, HWND.Null, "ReBarWindow32", null); // 0xA005 + _pathControl = PInvoke.FindWindowEx(_pathControl, HWND.Null, "Address Band Root", null); // 0xA205 + _pathControl = PInvoke.FindWindowEx(_pathControl, HWND.Null, "msctls_progress32", null); // 0x0000 + _pathControl = PInvoke.FindWindowEx(_pathControl, HWND.Null, "ComboBoxEx32", null); // 0xA205 + if (_pathControl == HWND.Null) + { + _pathEditor = HWND.Null; + _legacy = true; + Log.Info(ClassName, "Legacy dialog"); + } + else + { + _pathEditor = PInvoke.GetDlgItem(_pathControl, 0xA205); // ComboBox + _pathEditor = PInvoke.GetDlgItem(_pathEditor, 0xA205); // Edit + if (_pathEditor == HWND.Null) + { + _legacy = true; + Log.Error(ClassName, "Failed to find path editor handle"); + } + } + + return !_legacy; + } + + private bool GetFileEditor() + { + if (_dialogType == DialogType.Open) + { + // Get the handle of the file name editor of Open file dialog + _fileEditor = PInvoke.GetDlgItem(new(Handle), 0x047C); // ComboBoxEx32 + _fileEditor = PInvoke.GetDlgItem(_fileEditor, 0x047C); // ComboBox + _fileEditor = PInvoke.GetDlgItem(_fileEditor, 0x047C); // Edit + } + else + { + // Get the handle of the file name editor of Save / SaveAs file dialog + _fileEditor = PInvoke.GetDlgItem(new(Handle), 0x0000); // DUIViewWndClassName + _fileEditor = PInvoke.GetDlgItem(_fileEditor, 0x0000); // DirectUIHWND + _fileEditor = PInvoke.GetDlgItem(_fileEditor, 0x0000); // FloatNotifySink + _fileEditor = PInvoke.GetDlgItem(_fileEditor, 0x0000); // ComboBox + _fileEditor = PInvoke.GetDlgItem(_fileEditor, 0x03E9); // Edit + } + + if (_fileEditor == HWND.Null) + { + Log.Error(ClassName, "Failed to find file name editor handle"); + return false; + } + + return true; + } + + private bool GetOpenButton() + { + // Get the handle of the open button + _openButton = PInvoke.GetDlgItem(new(Handle), 0x0001); // Open/Save/SaveAs Button + if (_openButton == HWND.Null) + { + Log.Error(ClassName, "Failed to find open button handle"); + return false; + } + + return true; + } + + #endregion + + #region Windows Text + + private static unsafe string GetWindowText(HWND handle) + { + int length; + Span buffer = stackalloc char[1000]; + fixed (char* pBuffer = buffer) + { + // If the control has no title bar or text, or if the control handle is invalid, the return value is zero. + length = (int)PInvoke.SendMessage(handle, PInvoke.WM_GETTEXT, 1000, (nint)pBuffer); + } + + return buffer[..length].ToString(); + } + + private static unsafe nint SetWindowText(HWND handle, string text) + { + fixed (char* textPtr = text + '\0') + { + return PInvoke.SendMessage(handle, PInvoke.WM_SETTEXT, 0, (nint)textPtr).Value; + } + } + + #endregion + + #region Legacy Jump Folder + + private bool JumpFolderWithFileEditor(string path, bool resetFocus) + { + // For Save / Save As dialog, the default value in file editor is not null and it can cause strange behaviors. + if (resetFocus && _dialogType == DialogType.SaveOrSaveAs) return false; + + if (_fileEditor.IsNull && !GetFileEditor()) return false; + SetWindowText(_fileEditor, path); + + if (_openButton.IsNull && !GetOpenButton()) return false; + PInvoke.SendMessage(_openButton, PInvoke.BM_CLICK, 0, 0); + + return true; + } + + #endregion + + #endregion + } + + internal enum DialogType + { + Others, + Open, + SaveOrSaveAs + } +} diff --git a/Flow.Launcher.Infrastructure/DialogJump/Models/WindowsExplorer.cs b/Flow.Launcher.Infrastructure/DialogJump/Models/WindowsExplorer.cs new file mode 100644 index 000000000..e9ed9dae7 --- /dev/null +++ b/Flow.Launcher.Infrastructure/DialogJump/Models/WindowsExplorer.cs @@ -0,0 +1,260 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; +using Flow.Launcher.Plugin; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.System.Com; +using Windows.Win32.UI.Shell; + +namespace Flow.Launcher.Infrastructure.DialogJump.Models +{ + /// + /// Class for handling Windows Explorer instances in DialogJump. + /// + public class WindowsExplorer : IDialogJumpExplorer + { + public IDialogJumpExplorerWindow CheckExplorerWindow(IntPtr hwnd) + { + IDialogJumpExplorerWindow explorerWindow = null; + + // Is it from Explorer? + var processName = Win32Helper.GetProcessNameFromHwnd(new(hwnd)); + if (processName.Equals("explorer.exe", StringComparison.OrdinalIgnoreCase)) + { + EnumerateShellWindows((shellWindow) => + { + try + { + if (shellWindow is not IWebBrowser2 explorer) return true; + + if (explorer.HWND != hwnd) return true; + + explorerWindow = new WindowsExplorerWindow(hwnd); + return false; + } + catch + { + // Ignored + } + + return true; + }); + } + return explorerWindow; + } + + internal static unsafe void EnumerateShellWindows(Func action) + { + // Create an instance of ShellWindows + var clsidShellWindows = new Guid("9BA05972-F6A8-11CF-A442-00A0C90A8F39"); // ShellWindowsClass + var iidIShellWindows = typeof(IShellWindows).GUID; // IShellWindows + + var result = PInvoke.CoCreateInstance( + &clsidShellWindows, + null, + CLSCTX.CLSCTX_ALL, + &iidIShellWindows, + out var shellWindowsObj); + + if (result.Failed) return; + + var shellWindows = (IShellWindows)shellWindowsObj; + + // Enumerate the shell windows + var count = shellWindows.Count; + for (var i = 0; i < count; i++) + { + if (!action(shellWindows.Item(i))) + { + return; + } + } + } + + public void Dispose() + { + + } + } + + public class WindowsExplorerWindow : IDialogJumpExplorerWindow + { + public IntPtr Handle { get; } + + private static Guid _shellBrowserGuid = typeof(IShellBrowser).GUID; + + internal WindowsExplorerWindow(IntPtr handle) + { + Handle = handle; + } + + public string GetExplorerPath() + { + if (Handle == IntPtr.Zero) return null; + + var activeTabHandle = GetActiveTabHandle(new(Handle)); + if (activeTabHandle.IsNull) return null; + + var window = GetExplorerByTabHandle(activeTabHandle); + if (window == null) return null; + + var path = GetLocation(window); + return path; + } + + public void Dispose() + { + + } + + #region Helper Methods + + // Inspired by: https://github.com/w4po/ExplorerTabUtility + + private static HWND GetActiveTabHandle(HWND windowHandle) + { + // Active tab always at the top of the z-index, so it is the first child of the ShellTabWindowClass. + var activeTab = PInvoke.FindWindowEx(windowHandle, HWND.Null, "ShellTabWindowClass", null); + return activeTab; + } + + private static IWebBrowser2 GetExplorerByTabHandle(HWND tabHandle) + { + if (tabHandle.IsNull) return null; + + IWebBrowser2 window = null; + WindowsExplorer.EnumerateShellWindows((shellWindow) => + { + try + { + return StartSTAThread(() => + { + if (shellWindow is not IWebBrowser2 explorer) return true; + + if (explorer is not IServiceProvider sp) return true; + + sp.QueryService(ref _shellBrowserGuid, ref _shellBrowserGuid, out var shellBrowser); + if (shellBrowser == null) return true; + + try + { + shellBrowser.GetWindow(out var hWnd); // Must execute in STA thread to get this hWnd + + if (hWnd == tabHandle) + { + window = explorer; + return false; + } + } + catch + { + // Ignored + } + finally + { + Marshal.ReleaseComObject(shellBrowser); + } + + return true; + }) ?? true; + } + catch + { + // Ignored + } + + return true; + }); + + return window; + } + + private static bool? StartSTAThread(Func action) + { + bool? result = null; + var thread = new Thread(() => + { + result = action(); + }) + { + IsBackground = true + }; + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + thread.Join(); + return result; + } + + private static string GetLocation(IWebBrowser2 window) + { + var path = window.LocationURL.ToString(); + if (!string.IsNullOrWhiteSpace(path)) return NormalizeLocation(path); + + // Recycle Bin, This PC, etc + if (window.Document is not IShellFolderViewDual folderView) return null; + + // Attempt to get the path from the folder view + try + { + // CSWin32 Folder does not have Self, so we need to use dynamic type here + // Use dynamic to bypass static typing + dynamic folder = folderView.Folder; + + // Access the Self property via dynamic binding + dynamic folderItem = folder.Self; + + // Get path from the folder item + path = folderItem.Path; + } + catch + { + return null; + } + + return NormalizeLocation(path); + } + + private static string NormalizeLocation(string location) + { + if (location.IndexOf('%') > -1) + location = Environment.ExpandEnvironmentVariables(location); + + if (location.StartsWith("::", StringComparison.Ordinal)) + location = $"shell:{location}"; + + else if (location.StartsWith("{", StringComparison.Ordinal)) + location = $"shell:::{location}"; + + location = location.Trim(' ', '/', '\\', '\n', '\'', '"'); + + return location.Replace('/', '\\'); + } + + #endregion + } + + #region COM Interfaces + + // Inspired by: https://github.com/w4po/ExplorerTabUtility + + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("6d5140c1-7436-11ce-8034-00aa006009fa")] + [ComImport] + public interface IServiceProvider + { + [PreserveSig] + int QueryService(ref Guid guidService, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellBrowser ppvObject); + } + + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("000214E2-0000-0000-C000-000000000046")] + [ComImport] + public interface IShellBrowser + { + [PreserveSig] + int GetWindow(out nint handle); + } + + #endregion +} diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index 31547200b..c32c36248 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -1,7 +1,7 @@ - + - net7.0-windows + net9.0-windows {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3} Library true @@ -12,6 +12,7 @@ false false true + true @@ -53,28 +54,30 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + + all + + - - - + - \ No newline at end of file diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index a29f8accf..8afab419b 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -20,6 +20,10 @@ namespace Flow.Launcher.Infrastructure.Http private static readonly HttpClient client = new(); + // We should not initialize API in static constructor because it will create another API instance + private static IPublicAPI api = null; + private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); + static Http() { // need to be added so it would work on a win10 machine @@ -78,7 +82,7 @@ namespace Flow.Launcher.Infrastructure.Http } catch (UriFormatException e) { - Ioc.Default.GetRequiredService().ShowMsg("Please try again", "Unable to parse Http Proxy"); + API.ShowMsgError(API.GetTranslation("pleaseTryAgain"), API.GetTranslation("parseProxyFailed")); Log.Exception(ClassName, "Unable to parse Uri", e); } } @@ -220,5 +224,18 @@ namespace Flow.Launcher.Infrastructure.Http return new HttpResponseMessage(HttpStatusCode.InternalServerError); } } + + public static async Task GetStringAsync(string url, CancellationToken token = default) + { + try + { + Log.Debug(ClassName, $"Url <{url}>"); + return await client.GetStringAsync(url, token); + } + catch (System.Exception e) + { + return string.Empty; + } + } } } diff --git a/Flow.Launcher.Infrastructure/IAlphabet.cs b/Flow.Launcher.Infrastructure/IAlphabet.cs new file mode 100644 index 000000000..d13eeb414 --- /dev/null +++ b/Flow.Launcher.Infrastructure/IAlphabet.cs @@ -0,0 +1,22 @@ +namespace Flow.Launcher.Infrastructure +{ + /// + /// Translate a language to English letters using a given rule. + /// + public interface IAlphabet + { + /// + /// Translate a string to English letters, using a given rule. + /// + /// String to translate. + /// + public (string translation, TranslationMapping map) Translate(string stringToTranslate); + + /// + /// Determine if a string should be translated to English letter with this Alphabet. + /// + /// String to translate. + /// + public bool ShouldTranslate(string stringToTranslate); + } +} diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs index 86df01a30..64d323de6 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs @@ -19,14 +19,14 @@ namespace Flow.Launcher.Infrastructure.Image private static readonly string ClassName = nameof(ImageLoader); private static readonly ImageCache ImageCache = new(); - private static SemaphoreSlim storageLock { get; } = new SemaphoreSlim(1, 1); + private static Lock storageLock { get; } = new(); private static BinaryStorage> _storage; private static readonly ConcurrentDictionary GuidToKey = new(); private static IImageHashGenerator _hashGenerator; private static readonly bool EnableImageHash = true; - public static ImageSource Image { get; } = new BitmapImage(new Uri(Constant.ImageIcon)); - public static ImageSource MissingImage { get; } = new BitmapImage(new Uri(Constant.MissingImgIcon)); - public static ImageSource LoadingImage { get; } = new BitmapImage(new Uri(Constant.LoadingImgIcon)); + public static ImageSource Image => ImageCache[Constant.ImageIcon, false]; + public static ImageSource MissingImage => ImageCache[Constant.MissingImgIcon, false]; + public static ImageSource LoadingImage => ImageCache[Constant.LoadingImgIcon, false]; public const int SmallIconSize = 64; public const int FullIconSize = 256; public const int FullImageSize = 320; @@ -36,20 +36,25 @@ namespace Flow.Launcher.Infrastructure.Image public static async Task InitializeAsync() { - _storage = new BinaryStorage>("Image"); - _hashGenerator = new ImageHashGenerator(); - - var usage = await LoadStorageToConcurrentDictionaryAsync(); - _storage.ClearData(); - - ImageCache.Initialize(usage); - - foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon }) + var usage = await Task.Run(() => { - ImageSource img = new BitmapImage(new Uri(icon)); - img.Freeze(); - ImageCache[icon, false] = img; - } + _storage = new BinaryStorage>("Image"); + _hashGenerator = new ImageHashGenerator(); + + var usage = LoadStorageToConcurrentDictionary(); + _storage.ClearData(); + + ImageCache.Initialize(usage); + + foreach (var icon in new[] { Constant.DefaultIcon, Constant.ImageIcon, Constant.MissingImgIcon, Constant.LoadingImgIcon }) + { + ImageSource img = new BitmapImage(new Uri(icon)); + img.Freeze(); + ImageCache[icon, false] = img; + } + + return usage; + }); _ = Task.Run(async () => { @@ -64,42 +69,26 @@ namespace Flow.Launcher.Infrastructure.Image }); } - public static async Task SaveAsync() + public static void Save() { - await storageLock.WaitAsync(); - - try + lock (storageLock) { - await _storage.SaveAsync(ImageCache.EnumerateEntries() - .Select(x => x.Key) - .ToList()); - } - catch (System.Exception e) - { - Log.Exception(ClassName, "Failed to save image cache to file", e); - } - finally - { - storageLock.Release(); + try + { + _storage.Save([.. ImageCache.EnumerateEntries().Select(x => x.Key)]); + } + catch (System.Exception e) + { + Log.Exception(ClassName, "Failed to save image cache to file", e); + } } } - public static async Task WaitSaveAsync() + private static List<(string, bool)> LoadStorageToConcurrentDictionary() { - await storageLock.WaitAsync(); - storageLock.Release(); - } - - private static async Task> LoadStorageToConcurrentDictionaryAsync() - { - await storageLock.WaitAsync(); - try + lock (storageLock) { - return await _storage.TryLoadAsync(new List<(string, bool)>()); - } - finally - { - storageLock.Release(); + return _storage.TryLoad([]); } } @@ -174,7 +163,7 @@ namespace Flow.Launcher.Infrastructure.Image Log.Exception(ClassName, $"Failed to get thumbnail for {path} on first try", e); Log.Exception(ClassName, $"Failed to get thumbnail for {path} on second try", e2); - ImageSource image = ImageCache[Constant.MissingImgIcon, false]; + ImageSource image = MissingImage; ImageCache[path, false] = image; imageResult = new ImageResult(image, ImageType.Error); } @@ -273,7 +262,7 @@ namespace Flow.Launcher.Infrastructure.Image } else { - image = ImageCache[Constant.MissingImgIcon, false]; + image = MissingImage; path = Constant.MissingImgIcon; } diff --git a/Flow.Launcher.Infrastructure/NativeMethods.txt b/Flow.Launcher.Infrastructure/NativeMethods.txt index edc71feef..eb844dd7c 100644 --- a/Flow.Launcher.Infrastructure/NativeMethods.txt +++ b/Flow.Launcher.Infrastructure/NativeMethods.txt @@ -34,12 +34,6 @@ WINDOW_STYLE SetLastError WINDOW_EX_STYLE -GetSystemMetrics -EnumDisplayMonitors -MonitorFromWindow -GetMonitorInfo -MONITORINFOEXW - WM_ENTERSIZEMOVE WM_EXITSIZEMOVE WM_NCLBUTTONDBLCLK @@ -66,3 +60,30 @@ LOCALE_TRANSIENT_KEYBOARD4 SHParseDisplayName SHOpenFolderAndSelectItems CoTaskMemFree + +SetWinEventHook +UnhookWinEvent +SendMessage +EVENT_SYSTEM_FOREGROUND +WINEVENT_OUTOFCONTEXT +WM_SETTEXT +IShellFolderViewDual2 +CoCreateInstance +CLSCTX +IShellWindows +IWebBrowser2 +EVENT_OBJECT_DESTROY +EVENT_OBJECT_LOCATIONCHANGE +EVENT_SYSTEM_MOVESIZESTART +EVENT_SYSTEM_MOVESIZEEND +GetDlgItem +PostMessage +BM_CLICK +WM_GETTEXT +OpenProcess +QueryFullProcessImageName +EVENT_OBJECT_HIDE +EVENT_SYSTEM_DIALOGEND + +WM_POWERBROADCAST +PBT_APMRESUMEAUTOMATIC \ No newline at end of file diff --git a/Flow.Launcher.Infrastructure/PinyinAlphabet.cs b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs index 8eaa757be..1c0cc6872 100644 --- a/Flow.Launcher.Infrastructure/PinyinAlphabet.cs +++ b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs @@ -1,209 +1,193 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Linq; +using System.Collections.ObjectModel; +using System.IO; using System.Text; -using JetBrains.Annotations; +using System.Text.Json; +using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Infrastructure.UserSettings; using ToolGood.Words.Pinyin; -using CommunityToolkit.Mvvm.DependencyInjection; +using Flow.Launcher.Infrastructure.Logger; namespace Flow.Launcher.Infrastructure { - public class TranslationMapping - { - private bool constructed; - - private List originalIndexs = new List(); - private List translatedIndexs = new List(); - private int translatedLength = 0; - - public string key { get; private set; } - - public void setKey(string key) - { - this.key = key; - } - - public void AddNewIndex(int originalIndex, int translatedIndex, int length) - { - if (constructed) - throw new InvalidOperationException("Mapping shouldn't be changed after constructed"); - - originalIndexs.Add(originalIndex); - translatedIndexs.Add(translatedIndex); - translatedIndexs.Add(translatedIndex + length); - translatedLength += length - 1; - } - - public int MapToOriginalIndex(int translatedIndex) - { - if (translatedIndex > translatedIndexs.Last()) - return translatedIndex - translatedLength - 1; - - int lowerBound = 0; - int upperBound = originalIndexs.Count - 1; - - int count = 0; - - // Corner case handle - if (translatedIndex < translatedIndexs[0]) - return translatedIndex; - if (translatedIndex > translatedIndexs.Last()) - { - int indexDef = 0; - for (int k = 0; k < originalIndexs.Count; k++) - { - indexDef += translatedIndexs[k * 2 + 1] - translatedIndexs[k * 2]; - } - - return translatedIndex - indexDef - 1; - } - - // Binary Search with Range - for (int i = originalIndexs.Count / 2;; count++) - { - if (translatedIndex < translatedIndexs[i * 2]) - { - // move to lower middle - upperBound = i; - i = (i + lowerBound) / 2; - } - else if (translatedIndex > translatedIndexs[i * 2 + 1] - 1) - { - lowerBound = i; - // move to upper middle - // due to floor of integer division, move one up on corner case - i = (i + upperBound + 1) / 2; - } - else - return originalIndexs[i]; - - if (upperBound - lowerBound <= 1 && - translatedIndex > translatedIndexs[lowerBound * 2 + 1] && - translatedIndex < translatedIndexs[upperBound * 2]) - { - int indexDef = 0; - - for (int j = 0; j < upperBound; j++) - { - indexDef += translatedIndexs[j * 2 + 1] - translatedIndexs[j * 2]; - } - - return translatedIndex - indexDef - 1; - } - } - } - - public void endConstruct() - { - if (constructed) - throw new InvalidOperationException("Mapping has already been constructed"); - constructed = true; - } - } - - /// - /// Translate a language to English letters using a given rule. - /// - public interface IAlphabet - { - /// - /// Translate a string to English letters, using a given rule. - /// - /// String to translate. - /// - public (string translation, TranslationMapping map) Translate(string stringToTranslate); - - /// - /// Determine if a string can be translated to English letter with this Alphabet. - /// - /// String to translate. - /// - public bool CanBeTranslated(string stringToTranslate); - } - public class PinyinAlphabet : IAlphabet { - private ConcurrentDictionary _pinyinCache = - new ConcurrentDictionary(); - - private Settings _settings; + private readonly ConcurrentDictionary _pinyinCache = new(); + private readonly Settings _settings; + private ReadOnlyDictionary currentDoublePinyinTable; public PinyinAlphabet() { - Initialize(Ioc.Default.GetRequiredService()); + _settings = Ioc.Default.GetRequiredService(); + LoadDoublePinyinTable(); + + _settings.PropertyChanged += (sender, e) => + { + switch (e.PropertyName) + { + case nameof (Settings.ShouldUsePinyin): + if (_settings.ShouldUsePinyin) + { + Reload(); + } + break; + case nameof(Settings.UseDoublePinyin): + case nameof(Settings.DoublePinyinSchema): + if (_settings.UseDoublePinyin) + { + Reload(); + } + break; + } + }; } - private void Initialize([NotNull] Settings settings) + public void Reload() { - _settings = settings ?? throw new ArgumentNullException(nameof(settings)); + LoadDoublePinyinTable(); + _pinyinCache.Clear(); } - public bool CanBeTranslated(string stringToTranslate) + private void CreateDoublePinyinTableFromStream(Stream jsonStream) { - return WordsHelper.HasChinese(stringToTranslate); + var table = JsonSerializer.Deserialize>>(jsonStream) ?? + throw new InvalidOperationException("Failed to deserialize double pinyin table: result is null"); + + var schemaKey = _settings.DoublePinyinSchema.ToString(); + if (!table.TryGetValue(schemaKey, out var schemaDict)) + { + throw new ArgumentException($"DoublePinyinSchema '{schemaKey}' is invalid or double pinyin table is broken."); + } + + currentDoublePinyinTable = new ReadOnlyDictionary(schemaDict); + } + + private void LoadDoublePinyinTable() + { + if (!_settings.UseDoublePinyin) + { + currentDoublePinyinTable = new ReadOnlyDictionary(new Dictionary()); + return; + } + + var tablePath = Path.Combine(AppContext.BaseDirectory, "Resources", "double_pinyin.json"); + try + { + using var fs = File.OpenRead(tablePath); + CreateDoublePinyinTableFromStream(fs); + } + catch (FileNotFoundException e) + { + Log.Exception(nameof(PinyinAlphabet), $"Double pinyin table file not found: {tablePath}", e); + currentDoublePinyinTable = new ReadOnlyDictionary(new Dictionary()); + } + catch (DirectoryNotFoundException e) + { + Log.Exception(nameof(PinyinAlphabet), $"Directory not found for double pinyin table: {tablePath}", e); + currentDoublePinyinTable = new ReadOnlyDictionary(new Dictionary()); + } + catch (UnauthorizedAccessException e) + { + Log.Exception(nameof(PinyinAlphabet), $"Access denied to double pinyin table: {tablePath}", e); + currentDoublePinyinTable = new ReadOnlyDictionary(new Dictionary()); + } + catch (System.Exception e) + { + Log.Exception(nameof(PinyinAlphabet), $"Failed to load double pinyin table from file: {tablePath}", e); + currentDoublePinyinTable = new ReadOnlyDictionary(new Dictionary()); + } + } + + public bool ShouldTranslate(string stringToTranslate) + { + // If the query (stringToTranslate) does NOT contain Chinese characters, + // we should translate the target string to pinyin for matching + return _settings.ShouldUsePinyin && !ContainsChinese(stringToTranslate); } public (string translation, TranslationMapping map) Translate(string content) { - if (_settings.ShouldUsePinyin) - { - if (!_pinyinCache.ContainsKey(content)) - { - return BuildCacheFromContent(content); - } - else - { - return _pinyinCache[content]; - } - } - return (content, null); + if (!_settings.ShouldUsePinyin || !ContainsChinese(content)) + return (content, null); + + return _pinyinCache.TryGetValue(content, out var cached) ? cached : BuildCacheFromContent(content); } private (string translation, TranslationMapping map) BuildCacheFromContent(string content) { - if (WordsHelper.HasChinese(content)) + var resultList = WordsHelper.GetPinyinList(content); + var resultBuilder = new StringBuilder(_settings.UseDoublePinyin ? 3 : 4); // Pre-allocate with estimated capacity + var map = new TranslationMapping(); + + var previousIsChinese = false; + + for (var i = 0; i < resultList.Length; i++) { - var resultList = WordsHelper.GetPinyinList(content); - - StringBuilder resultBuilder = new StringBuilder(); - TranslationMapping map = new TranslationMapping(); - - bool pre = false; - - for (int i = 0; i < resultList.Length; i++) + if (IsChineseCharacter(content[i])) { - if (content[i] >= 0x3400 && content[i] <= 0x9FD5) + var translated = _settings.UseDoublePinyin ? ToDoublePinyin(resultList[i]) : resultList[i]; + + if (i > 0) { - map.AddNewIndex(i, resultBuilder.Length, resultList[i].Length + 1); resultBuilder.Append(' '); - resultBuilder.Append(resultList[i]); - pre = true; - } - else - { - if (pre) - { - pre = false; - resultBuilder.Append(' '); - } - - resultBuilder.Append(resultList[i]); } + + map.AddNewIndex(resultBuilder.Length, translated.Length); + resultBuilder.Append(translated); + previousIsChinese = true; + } + else + { + // Add space after Chinese characters before non-Chinese characters + if (previousIsChinese) + { + previousIsChinese = false; + resultBuilder.Append(' '); + } + + map.AddNewIndex(resultBuilder.Length, resultList[i].Length); + resultBuilder.Append(resultList[i]); } - - map.endConstruct(); - - var key = resultBuilder.ToString(); - map.setKey(key); - - return _pinyinCache[content] = (key, map); } - else + + map.EndConstruct(); + + var translation = resultBuilder.ToString(); + var result = (translation, map); + + return _pinyinCache[content] = result; + } + + /// + /// Optimized Chinese character detection using the comprehensive CJK Unicode ranges + /// + private static bool ContainsChinese(ReadOnlySpan text) + { + foreach (var c in text) { - return (content, null); + if (IsChineseCharacter(c)) + return true; } + return false; + } + + /// + /// Check if a character is a Chinese character using comprehensive Unicode ranges + /// Covers CJK Unified Ideographs, Extension A + /// + private static bool IsChineseCharacter(char c) + { + return (c >= 0x4E00 && c <= 0x9FFF) || // CJK Unified Ideographs + (c >= 0x3400 && c <= 0x4DBF); // CJK Extension A + } + + private string ToDoublePinyin(string fullPinyin) + { + return currentDoublePinyinTable.TryGetValue(fullPinyin, out var doublePinyinValue) + ? doublePinyinValue + : fullPinyin; } } } diff --git a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs index 48e6b5523..15200f5aa 100644 --- a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs @@ -12,7 +12,7 @@ using MemoryPack; namespace Flow.Launcher.Infrastructure.Storage { /// - /// Stroage object using binary data + /// Storage object using binary data /// Normally, it has better performance, but not readable /// /// @@ -53,6 +53,45 @@ namespace Flow.Launcher.Infrastructure.Storage FilePath = Path.Combine(DirectoryPath, $"{filename}{FileSuffix}"); } + public T TryLoad(T defaultData) + { + if (Data != null) return Data; + + if (File.Exists(FilePath)) + { + if (new FileInfo(FilePath).Length == 0) + { + Log.Error(ClassName, $"Zero length cache file <{FilePath}>"); + Data = defaultData; + Save(); + } + + var bytes = File.ReadAllBytes(FilePath); + Data = Deserialize(bytes, defaultData); + } + else + { + Log.Info(ClassName, "Cache file not exist, load default data"); + Data = defaultData; + Save(); + } + return Data; + } + + private T Deserialize(ReadOnlySpan bytes, T defaultData) + { + try + { + var t = MemoryPackSerializer.Deserialize(bytes); + return t ?? defaultData; + } + catch (System.Exception e) + { + Log.Exception(ClassName, $"Deserialize error for file <{FilePath}>", e); + return defaultData; + } + } + public async ValueTask TryLoadAsync(T defaultData) { if (Data != null) return Data; @@ -79,26 +118,31 @@ namespace Flow.Launcher.Infrastructure.Storage return Data; } - private static async ValueTask DeserializeAsync(Stream stream, T defaultData) + private async ValueTask DeserializeAsync(Stream stream, T defaultData) { try { var t = await MemoryPackSerializer.DeserializeAsync(stream); return t ?? defaultData; } - catch (System.Exception) + catch (System.Exception e) { - // Log.Exception($"|BinaryStorage.Deserialize|Deserialize error for file <{FilePath}>", e); + Log.Exception(ClassName, $"Deserialize error for file <{FilePath}>", e); return defaultData; } } public void Save() + { + Save(Data.NonNull()); + } + + public void Save(T data) { // User may delete the directory, so we need to check it FilesFolders.ValidateDirectory(DirectoryPath); - var serialized = MemoryPackSerializer.Serialize(Data); + var serialized = MemoryPackSerializer.Serialize(data); File.WriteAllBytes(FilePath, serialized); } @@ -107,15 +151,6 @@ namespace Flow.Launcher.Infrastructure.Storage await SaveAsync(Data.NonNull()); } - // ImageCache need to convert data into concurrent dictionary for usage, - // so we would better to clear the data - public void ClearData() - { - Data = default; - } - - // ImageCache storages data in its class, - // so we need to pass it to SaveAsync public async ValueTask SaveAsync(T data) { // User may delete the directory, so we need to check it @@ -124,5 +159,12 @@ namespace Flow.Launcher.Infrastructure.Storage await using var stream = new FileStream(FilePath, FileMode.Create); await MemoryPackSerializer.SerializeAsync(stream, data); } + + // ImageCache need to convert data into concurrent dictionary for usage, + // so we would better to clear the data + public void ClearData() + { + Data = default; + } } } diff --git a/Flow.Launcher.Infrastructure/StringMatcher.cs b/Flow.Launcher.Infrastructure/StringMatcher.cs index e85c5d6f4..2882cb8f0 100644 --- a/Flow.Launcher.Infrastructure/StringMatcher.cs +++ b/Flow.Launcher.Infrastructure/StringMatcher.cs @@ -68,7 +68,7 @@ namespace Flow.Launcher.Infrastructure query = query.Trim(); TranslationMapping translationMapping = null; - if (_alphabet is not null && !_alphabet.CanBeTranslated(query)) + if (_alphabet is not null && _alphabet.ShouldTranslate(query)) { // We assume that if a query can be translated (containing characters of a language, like Chinese) // it actually means user doesn't want it to be translated to English letters. @@ -228,7 +228,7 @@ namespace Flow.Launcher.Infrastructure return new MatchResult(false, UserSettingSearchPrecision); } - private bool IsAcronym(string stringToCompare, int compareStringIndex) + private static bool IsAcronym(string stringToCompare, int compareStringIndex) { if (IsAcronymChar(stringToCompare, compareStringIndex) || IsAcronymNumber(stringToCompare, compareStringIndex)) return true; @@ -237,7 +237,7 @@ namespace Flow.Launcher.Infrastructure } // When counting acronyms, treat a set of numbers as one acronym ie. Visual 2019 as 2 acronyms instead of 5 - private bool IsAcronymCount(string stringToCompare, int compareStringIndex) + private static bool IsAcronymCount(string stringToCompare, int compareStringIndex) { if (IsAcronymChar(stringToCompare, compareStringIndex)) return true; diff --git a/Flow.Launcher.Infrastructure/TranslationMapping.cs b/Flow.Launcher.Infrastructure/TranslationMapping.cs new file mode 100644 index 000000000..b4c6764df --- /dev/null +++ b/Flow.Launcher.Infrastructure/TranslationMapping.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; + +namespace Flow.Launcher.Infrastructure +{ + public class TranslationMapping + { + private bool _isConstructed; + + // Assuming one original item maps to multi translated items + // list[i] is the last translated index + 1 of original index i + private readonly List _originalToTranslated = new(); + + public void AddNewIndex(int translatedIndex, int length) + { + if (_isConstructed) + throw new InvalidOperationException("Mapping shouldn't be changed after construction"); + _originalToTranslated.Add(translatedIndex + length); + } + + public int MapToOriginalIndex(int translatedIndex) + { + var searchResult = _originalToTranslated.BinarySearch(translatedIndex); + return searchResult >= 0 ? searchResult : ~searchResult; + } + + public void EndConstruct() + { + if (_isConstructed) + throw new InvalidOperationException("Mapping has already been constructed"); + _isConstructed = true; + } + } +} diff --git a/Flow.Launcher.Infrastructure/UI/EnumBindingSource.cs b/Flow.Launcher.Infrastructure/UI/EnumBindingSource.cs deleted file mode 100644 index f9504e6d9..000000000 --- a/Flow.Launcher.Infrastructure/UI/EnumBindingSource.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Windows.Markup; - -namespace Flow.Launcher.Infrastructure.UI -{ - [Obsolete("EnumBindingSourceExtension is obsolete. Use with Flow.Launcher.Localization NuGet package instead.")] - public class EnumBindingSourceExtension : MarkupExtension - { - private Type _enumType; - public Type EnumType - { - get { return _enumType; } - set - { - if (value != _enumType) - { - if (value != null) - { - Type enumType = Nullable.GetUnderlyingType(value) ?? value; - if (!enumType.IsEnum) - { - throw new ArgumentException("Type must represent an enum."); - } - } - - _enumType = value; - } - } - } - - public EnumBindingSourceExtension() { } - - public EnumBindingSourceExtension(Type enumType) - { - EnumType = enumType; - } - - public override object ProvideValue(IServiceProvider serviceProvider) - { - if (_enumType == null) - { - throw new InvalidOperationException("The EnumType must be specified."); - } - - Type actualEnumType = Nullable.GetUnderlyingType(_enumType) ?? _enumType; - Array enumValues = Enum.GetValues(actualEnumType); - - if (actualEnumType == _enumType) - { - return enumValues; - } - - Array tempArray = Array.CreateInstance(actualEnumType, enumValues.Length + 1); - enumValues.CopyTo(tempArray, 1); - return tempArray; - } - } -} diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 3ec84ff17..0c3402050 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text.Json.Serialization; using System.Windows; @@ -39,7 +39,6 @@ namespace Flow.Launcher.Infrastructure.UserSettings _storage.Save(); } - private string _theme = Constant.DefaultTheme; public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}"; private string _openResultModifiers = KeyConstant.Alt; @@ -87,6 +86,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings public string OpenHistoryHotkey { get; set; } = $"Ctrl+H"; public string CycleHistoryUpHotkey { get; set; } = $"{KeyConstant.Alt} + Up"; public string CycleHistoryDownHotkey { get; set; } = $"{KeyConstant.Alt} + Down"; + public string DialogJumpHotkey { get; set; } = $"{KeyConstant.Alt} + G"; private string _language = Constant.SystemLanguageCode; public string Language @@ -94,16 +94,20 @@ namespace Flow.Launcher.Infrastructure.UserSettings get => _language; set { - _language = value; - OnPropertyChanged(); + if (_language != value) + { + _language = value; + OnPropertyChanged(); + } } } + private string _theme = Constant.DefaultTheme; public string Theme { get => _theme; set { - if (value != _theme) + if (_theme != value) { _theme = value; OnPropertyChanged(); @@ -112,7 +116,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings } } public bool UseDropShadowEffect { get; set; } = true; - public BackdropTypes BackdropType{ get; set; } = BackdropTypes.None; + public BackdropTypes BackdropType { get; set; } = BackdropTypes.None; + public string ReleaseNotesVersion { get; set; } = string.Empty; /* Appearance Settings. It should be separated from the setting later.*/ public double WindowHeightSize { get; set; } = 42; @@ -224,11 +229,12 @@ namespace Flow.Launcher.Infrastructure.UserSettings } } } - + public int MaxHistoryResultsToShowForHomePage { get; set; } = 5; public bool AutoRestartAfterChanging { get; set; } = false; public bool ShowUnknownSourceWarning { get; set; } = true; + public bool AutoUpdatePlugins { get; set; } = true; public int CustomExplorerIndex { get; set; } = 0; @@ -318,13 +324,70 @@ namespace Flow.Launcher.Infrastructure.UserSettings } }; + public bool EnableDialogJump { get; set; } = true; + + public bool AutoDialogJump { get; set; } = false; + + public bool ShowDialogJumpWindow { get; set; } = false; + + [JsonConverter(typeof(JsonStringEnumConverter))] + public DialogJumpWindowPositions DialogJumpWindowPosition { get; set; } = DialogJumpWindowPositions.UnderDialog; + + [JsonConverter(typeof(JsonStringEnumConverter))] + public DialogJumpResultBehaviours DialogJumpResultBehaviour { get; set; } = DialogJumpResultBehaviours.LeftClick; + + [JsonConverter(typeof(JsonStringEnumConverter))] + public DialogJumpFileResultBehaviours DialogJumpFileResultBehaviour { get; set; } = DialogJumpFileResultBehaviours.FullPath; + [JsonConverter(typeof(JsonStringEnumConverter))] public LOGLEVEL LogLevel { get; set; } = LOGLEVEL.INFO; /// /// when false Alphabet static service will always return empty results /// - public bool ShouldUsePinyin { get; set; } = false; + private bool _useAlphabet = true; + public bool ShouldUsePinyin + { + get => _useAlphabet; + set + { + if (_useAlphabet != value) + { + _useAlphabet = value; + OnPropertyChanged(); + } + } + } + + private bool _useDoublePinyin = false; + public bool UseDoublePinyin + { + get => _useDoublePinyin; + set + { + if (_useDoublePinyin != value) + { + _useDoublePinyin = value; + OnPropertyChanged(); + } + } + } + + private DoublePinyinSchemas _doublePinyinSchema = DoublePinyinSchemas.XiaoHe; + + [JsonInclude, JsonConverter(typeof(JsonStringEnumConverter))] + public DoublePinyinSchemas DoublePinyinSchema + { + get => _doublePinyinSchema; + set + { + if (_doublePinyinSchema != value) + { + _doublePinyinSchema = value; + OnPropertyChanged(); + } + } + } public bool AlwaysPreview { get; set; } = false; @@ -337,9 +400,12 @@ namespace Flow.Launcher.Infrastructure.UserSettings get => _querySearchPrecision; set { - _querySearchPrecision = value; - if (_stringMatcher != null) - _stringMatcher.UserSettingSearchPrecision = value; + if (_querySearchPrecision != value) + { + _querySearchPrecision = value; + if (_stringMatcher != null) + _stringMatcher.UserSettingSearchPrecision = value; + } } } @@ -406,13 +472,30 @@ namespace Flow.Launcher.Infrastructure.UserSettings get => _hideNotifyIcon; set { - _hideNotifyIcon = value; - OnPropertyChanged(); + if (_hideNotifyIcon != value) + { + _hideNotifyIcon = value; + OnPropertyChanged(); + } } } public bool LeaveCmdOpen { get; set; } public bool HideWhenDeactivated { get; set; } = true; + private bool _showAtTopmost = false; + public bool ShowAtTopmost + { + get => _showAtTopmost; + set + { + if (_showAtTopmost != value) + { + _showAtTopmost = value; + OnPropertyChanged(); + } + } + } + public bool SearchQueryResultsWithDelay { get; set; } public int SearchDelayTime { get; set; } = 150; @@ -448,7 +531,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings { var list = FixedHotkeys(); - // Customizeable hotkeys + // Customizable hotkeys if (!string.IsNullOrEmpty(Hotkey)) list.Add(new(Hotkey, "flowlauncherHotkey", () => Hotkey = "")); if (!string.IsNullOrEmpty(PreviewHotkey)) @@ -468,7 +551,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings if (!string.IsNullOrEmpty(SettingWindowHotkey)) list.Add(new(SettingWindowHotkey, "SettingWindowHotkey", () => SettingWindowHotkey = "")); if (!string.IsNullOrEmpty(OpenHistoryHotkey)) - list.Add(new(OpenHistoryHotkey, "OpenHistoryHotkey", () => OpenHistoryHotkey = "")); + list.Add(new(OpenHistoryHotkey, "OpenHistoryHotkey", () => OpenHistoryHotkey = "")); if (!string.IsNullOrEmpty(OpenContextMenuHotkey)) list.Add(new(OpenContextMenuHotkey, "OpenContextMenuHotkey", () => OpenContextMenuHotkey = "")); if (!string.IsNullOrEmpty(SelectNextPageHotkey)) @@ -479,6 +562,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings list.Add(new(CycleHistoryUpHotkey, "CycleHistoryUpHotkey", () => CycleHistoryUpHotkey = "")); if (!string.IsNullOrEmpty(CycleHistoryDownHotkey)) list.Add(new(CycleHistoryDownHotkey, "CycleHistoryDownHotkey", () => CycleHistoryDownHotkey = "")); + if (!string.IsNullOrEmpty(DialogJumpHotkey)) + list.Add(new(DialogJumpHotkey, "dialogJumpHotkey", () => DialogJumpHotkey = "")); // Custom Query Hotkeys foreach (var customPluginHotkey in CustomPluginHotkeys) @@ -574,9 +659,41 @@ namespace Flow.Launcher.Infrastructure.UserSettings public enum BackdropTypes { - None, + None, Acrylic, Mica, MicaAlt } + + public enum DoublePinyinSchemas + { + XiaoHe, + ZiRanMa, + WeiRuan, + ZhiNengABC, + ZiGuangPinYin, + PinYinJiaJia, + XingKongJianDao, + DaNiu, + XiaoLang + } + + public enum DialogJumpWindowPositions + { + UnderDialog, + FollowDefault + } + + public enum DialogJumpResultBehaviours + { + LeftClick, + RightClick + } + + public enum DialogJumpFileResultBehaviours + { + FullPath, + FullPathOpen, + Directory + } } diff --git a/Flow.Launcher.Infrastructure/Win32Helper.cs b/Flow.Launcher.Infrastructure/Win32Helper.cs index 86e7b7c97..811733925 100644 --- a/Flow.Launcher.Infrastructure/Win32Helper.cs +++ b/Flow.Launcher.Infrastructure/Win32Helper.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; @@ -13,10 +13,13 @@ using System.Windows.Interop; using System.Windows.Markup; using System.Windows.Media; using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Plugin.SharedModels; using Microsoft.Win32; +using Microsoft.Win32.SafeHandles; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.Graphics.Dwm; +using Windows.Win32.System.Threading; using Windows.Win32.UI.Input.KeyboardAndMouse; using Windows.Win32.UI.Shell.Common; using Windows.Win32.UI.WindowsAndMessaging; @@ -118,9 +121,9 @@ namespace Flow.Launcher.Infrastructure #region Window Foreground - public static nint GetForegroundWindow() + public static unsafe nint GetForegroundWindow() { - return PInvoke.GetForegroundWindow().Value; + return (nint)PInvoke.GetForegroundWindow().Value; } public static bool SetForegroundWindow(Window window) @@ -138,6 +141,11 @@ namespace Flow.Launcher.Infrastructure return IsForegroundWindow(GetWindowHandle(window)); } + public static bool IsForegroundWindow(nint handle) + { + return IsForegroundWindow(new HWND(handle)); + } + internal static bool IsForegroundWindow(HWND handle) { return handle.Equals(PInvoke.GetForegroundWindow()); @@ -279,15 +287,15 @@ namespace Flow.Launcher.Infrastructure { var hWndDesktop = PInvoke.FindWindowEx(hWnd, HWND.Null, "SHELLDLL_DefView", null); hWndDesktop = PInvoke.FindWindowEx(hWndDesktop, HWND.Null, "SysListView32", "FolderView"); - if (hWndDesktop.Value != IntPtr.Zero) + if (hWndDesktop != HWND.Null) { return false; } } var monitorInfo = MonitorInfo.GetNearestDisplayMonitor(hWnd); - return (appBounds.bottom - appBounds.top) == monitorInfo.RectMonitor.Height && - (appBounds.right - appBounds.left) == monitorInfo.RectMonitor.Width; + return (appBounds.bottom - appBounds.top) == monitorInfo.Bounds.Height && + (appBounds.right - appBounds.left) == monitorInfo.Bounds.Width; } #endregion @@ -330,6 +338,9 @@ namespace Flow.Launcher.Infrastructure public const int SC_MAXIMIZE = (int)PInvoke.SC_MAXIMIZE; public const int SC_MINIMIZE = (int)PInvoke.SC_MINIMIZE; + public const int WM_POWERBROADCAST = (int)PInvoke.WM_POWERBROADCAST; + public const int PBT_APMRESUMEAUTOMATIC = (int)PInvoke.PBT_APMRESUMEAUTOMATIC; + #endregion #region Window Handle @@ -344,6 +355,16 @@ namespace Flow.Launcher.Infrastructure return new(windowHelper.Handle); } + internal static HWND GetMainWindowHandle() + { + // When application is exiting, the Application.Current will be null + if (Application.Current == null) return HWND.Null; + + // Get the FL main window + var hwnd = GetWindowHandle(Application.Current.MainWindow, true); + return hwnd; + } + #endregion #region STA Thread @@ -480,7 +501,7 @@ namespace Flow.Launcher.Infrastructure /// Restores the previously backed-up keyboard layout. /// If it wasn't backed up or has already been restored, this method does nothing. /// - public static void RestorePreviousKeyboardLayout() + public unsafe static void RestorePreviousKeyboardLayout() { if (_previousLayout == HKL.Null) return; @@ -491,7 +512,7 @@ namespace Flow.Launcher.Infrastructure hwnd, PInvoke.WM_INPUTLANGCHANGEREQUEST, PInvoke.INPUTLANGCHANGE_FORWARD, - _previousLayout.Value + new LPARAM((nint)_previousLayout.Value) ); _previousLayout = HKL.Null; @@ -761,6 +782,62 @@ namespace Flow.Launcher.Infrastructure #endregion + #region Window Rect + + public static unsafe bool GetWindowRect(nint handle, out Rect outRect) + { + var rect = new RECT(); + var result = PInvoke.GetWindowRect(new(handle), &rect); + if (!result) + { + outRect = new Rect(); + return false; + } + + // Convert RECT to Rect + outRect = new Rect( + rect.left, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top + ); + return true; + } + + #endregion + + #region Window Process + + internal static unsafe string GetProcessNameFromHwnd(HWND hWnd) + { + return Path.GetFileName(GetProcessPathFromHwnd(hWnd)); + } + + internal static unsafe string GetProcessPathFromHwnd(HWND hWnd) + { + uint pid; + var threadId = PInvoke.GetWindowThreadProcessId(hWnd, &pid); + if (threadId == 0) return string.Empty; + + var process = PInvoke.OpenProcess(PROCESS_ACCESS_RIGHTS.PROCESS_QUERY_LIMITED_INFORMATION, false, pid); + if (process != HWND.Null) + { + using var safeHandle = new SafeProcessHandle((nint)process.Value, true); + uint capacity = 2000; + Span buffer = new char[capacity]; + if (!PInvoke.QueryFullProcessImageName(safeHandle, PROCESS_NAME_FORMAT.PROCESS_NAME_WIN32, buffer, ref capacity)) + { + return string.Empty; + } + + return buffer[..(int)capacity].ToString(); + } + + return string.Empty; + } + + #endregion + #region Explorer // https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shopenfolderandselectitems @@ -791,5 +868,41 @@ namespace Flow.Launcher.Infrastructure } #endregion + + #region Win32 Dark Mode + + /* + * Inspired by https://github.com/ysc3839/win32-darkmode + */ + + [DllImport("uxtheme.dll", EntryPoint = "#135", SetLastError = true)] + private static extern int SetPreferredAppMode(int appMode); + + public static void EnableWin32DarkMode(string colorScheme) + { + try + { + // Undocumented API from Windows 10 1809 + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && + Environment.OSVersion.Version.Build >= 17763) + { + var flag = colorScheme switch + { + Constant.Light => 3, // ForceLight + Constant.Dark => 2, // ForceDark + Constant.System => 1, // AllowDark + _ => 0 // Default + }; + _ = SetPreferredAppMode(flag); + } + + } + catch + { + // Ignore errors on unsupported OS + } + } + + #endregion } } diff --git a/Flow.Launcher.Infrastructure/packages.lock.json b/Flow.Launcher.Infrastructure/packages.lock.json new file mode 100644 index 000000000..abd250f7c --- /dev/null +++ b/Flow.Launcher.Infrastructure/packages.lock.json @@ -0,0 +1,198 @@ +{ + "version": 1, + "dependencies": { + "net9.0-windows7.0": { + "Ben.Demystifier": { + "type": "Direct", + "requested": "[0.4.1, )", + "resolved": "0.4.1", + "contentHash": "axFeEMfmEORy3ipAzOXG/lE+KcNptRbei3F0C4kQCdeiQtW+qJW90K5iIovITGrdLt8AjhNCwk5qLSX9/rFpoA==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0" + } + }, + "BitFaster.Caching": { + "type": "Direct", + "requested": "[2.5.4, )", + "resolved": "2.5.4", + "contentHash": "1QroTY1PVCZOSG9FnkkCrmCKk/+bZCgI/YXq376HnYwUDJ4Ho0EaV4YaA/5v5WYLnwIwIO7RZkdWbg9pxIpueQ==" + }, + "CommunityToolkit.Mvvm": { + "type": "Direct", + "requested": "[8.4.0, )", + "resolved": "8.4.0", + "contentHash": "tqVU8yc/ADO9oiTRyTnwhFN68hCwvkliMierptWOudIAvWY1mWCh5VFh+guwHJmpMwfg0J0rY+yyd5Oy7ty9Uw==" + }, + "Fody": { + "type": "Direct", + "requested": "[6.9.2, )", + "resolved": "6.9.2", + "contentHash": "YBHobPGogb0vYhGYIxn/ndWqTjNWZveDi5jdjrcshL2vjwU3gQGyDeI7vGgye+2rAM5fGRvlLgNWLW3DpviS/w==" + }, + "InputSimulator": { + "type": "Direct", + "requested": "[1.0.4, )", + "resolved": "1.0.4", + "contentHash": "D0LvRCPQMX6/FJHBjng+RO+wRDuHTJrfo7IAc7rmkPvRqchdVGJWg3y70peOtDy3OLNK+HSOwVkH4GiuLnkKgA==" + }, + "MemoryPack": { + "type": "Direct", + "requested": "[1.21.4, )", + "resolved": "1.21.4", + "contentHash": "wy3JTBNBsO8LfQcBvvYsWr3lm2Oakolrfu0UQ3oSJSEiD+7ye0GUhYTaXuYYBowqsXBXWD9gf2218ae0JRiYVQ==", + "dependencies": { + "MemoryPack.Core": "1.21.4", + "MemoryPack.Generator": "1.21.4" + } + }, + "Microsoft.VisualStudio.Threading": { + "type": "Direct", + "requested": "[17.14.15, )", + "resolved": "17.14.15", + "contentHash": "1DrCusT3xNLSlaJg77BsUSAzrhjdZBAvvsS0PMzyPM+fGais6SnISOhqdZQop8VVMIBLsYm2gyF9W7THjgavwA==", + "dependencies": { + "Microsoft.VisualStudio.Threading.Analyzers": "17.14.15", + "Microsoft.VisualStudio.Threading.Only": "17.14.15", + "Microsoft.VisualStudio.Validation": "17.8.8" + } + }, + "Microsoft.Windows.CsWin32": { + "type": "Direct", + "requested": "[0.3.183, )", + "resolved": "0.3.183", + "contentHash": "Ze3aE2y7xgzKxEWtNb4SH0CExXpCHr3sbmwnvMiWMzJhWDX/G4Rs5wgg2UNs3VN+qVHh/DkDWLCPaVQv/b//Nw==", + "dependencies": { + "Microsoft.Windows.SDK.Win32Docs": "0.1.42-alpha", + "Microsoft.Windows.SDK.Win32Metadata": "61.0.15-preview", + "Microsoft.Windows.WDK.Win32Metadata": "0.12.8-experimental" + } + }, + "NHotkey.Wpf": { + "type": "Direct", + "requested": "[3.0.0, )", + "resolved": "3.0.0", + "contentHash": "BIUKlhTG5KtFf9OQzWvkmVmktt5/FFj6AOEgag8Uf0R2YdZt5ajUzs3sVskcJcT2TztWlEHKQr1jFj3KQ0D9Nw==", + "dependencies": { + "NHotkey": "3.0.0" + } + }, + "NLog": { + "type": "Direct", + "requested": "[6.0.1, )", + "resolved": "6.0.1", + "contentHash": "qDWiqy8/xdpZKtHna/645KbalwP86N2NFJEzfqhcv+Si4V2iNaEfR/dCneuF/4+Dcwl3f7jHMXj3ndWYftV3Ug==" + }, + "NLog.OutputDebugString": { + "type": "Direct", + "requested": "[6.0.1, )", + "resolved": "6.0.1", + "contentHash": "wwJCQLaHVzuRf8TsXB+EEdrzVvE3dnzCSMQMDgwkw3AXp8VSp3JSVF/Q/H0oEqggKgKhPs13hh3a7svyQr4s3A==", + "dependencies": { + "NLog": "6.0.1" + } + }, + "PropertyChanged.Fody": { + "type": "Direct", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "6v+f9cI8YjnZH2WBHuOqWEAo8DFFNGFIdU8xD3AsL6fhV6Y8oAmVWd7XKk49t8DpeUBwhR/X+97+6Epvek0Y3A==", + "dependencies": { + "Fody": "6.6.4" + } + }, + "SharpVectors.Wpf": { + "type": "Direct", + "requested": "[1.8.4.2, )", + "resolved": "1.8.4.2", + "contentHash": "PNxLkMBJnV8A+6yH9OqOlhLJegvWP/dvh0rAJp2l0kcrR+rB4R2tQ9vhUqka+UilH4atN8T6zvjDOizVyfz2Ng==" + }, + "System.Drawing.Common": { + "type": "Direct", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "KIX+oBU38pxkKPxvLcLfIkOV5Ien8ReN78wro7OF5/erwcmortzeFx+iBswlh2Vz6gVne0khocQudGwaO1Ey6A==", + "dependencies": { + "Microsoft.Win32.SystemEvents": "7.0.0" + } + }, + "ToolGood.Words.Pinyin": { + "type": "Direct", + "requested": "[3.1.0.3, )", + "resolved": "3.1.0.3", + "contentHash": "VKcf8sUq/+LyY99WgLhOu7Q32ROEyR30/2xCCj9ADRi45wVC7kpXrYCf9vH1qirkmrIfpL8inoxAbrqAlfXxsQ==" + }, + "JetBrains.Annotations": { + "type": "Transitive", + "resolved": "2024.3.0", + "contentHash": "ox5pkeLQXjvJdyAB4b2sBYAlqZGLh3PjSnP1bQNVx72ONuTJ9+34/+Rq91Fc0dG29XG9RgZur9+NcP4riihTug==" + }, + "MemoryPack.Core": { + "type": "Transitive", + "resolved": "1.21.4", + "contentHash": "6RszGorZ0ejNmp37ZcboPBMvvPCuNW2jlrdQfcs/lMzE5b3pmPF6hsm/laDc34hRlbAST1ZxaX/DvYu2DF5sBQ==" + }, + "MemoryPack.Generator": { + "type": "Transitive", + "resolved": "1.21.4", + "contentHash": "g14EsSS85yn0lHTi0J9ivqlZMf09A2iI51fmI+0KkzIzyCbWOBWPi5mdaY7YWmXprk12aYh9u/qfWHQUYthlwg==" + }, + "Microsoft.VisualStudio.Threading.Analyzers": { + "type": "Transitive", + "resolved": "17.14.15", + "contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw==" + }, + "Microsoft.VisualStudio.Threading.Only": { + "type": "Transitive", + "resolved": "17.14.15", + "contentHash": "NqONyw1RXyj9P3k5e1uU2k9kc1ptwuU5NJQzG+MPq7vQVHUzBY8HLuJf/N2Rw5H/myD96CVxziDxmjawPuzntw==", + "dependencies": { + "Microsoft.VisualStudio.Validation": "17.8.8" + } + }, + "Microsoft.VisualStudio.Validation": { + "type": "Transitive", + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" + }, + "Microsoft.Win32.SystemEvents": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "2nXPrhdAyAzir0gLl8Yy8S5Mnm/uBSQQA7jEsILOS1MTyS7DbmV1NgViMtvV1sfCD1ebITpNwb1NIinKeJgUVQ==" + }, + "Microsoft.Windows.SDK.Win32Docs": { + "type": "Transitive", + "resolved": "0.1.42-alpha", + "contentHash": "Z/9po23gUA9aoukirh2ItMU2ZS9++Js9Gdds9fu5yuMojDrmArvY2y+tq9985tR3cxFxpZO1O35Wjfo0khj5HA==" + }, + "Microsoft.Windows.SDK.Win32Metadata": { + "type": "Transitive", + "resolved": "61.0.15-preview", + "contentHash": "cysex3dazKtCPALCluC2XX3f5Aedy9H2pw5jb+TW5uas2rkem1Z7FRnbUrg2vKx0pk0Qz+4EJNr37HdYTEcvEQ==" + }, + "Microsoft.Windows.WDK.Win32Metadata": { + "type": "Transitive", + "resolved": "0.12.8-experimental", + "contentHash": "3n8R44/Z96Ly+ty4eYVJfESqbzvpw96lRLs3zOzyDmr1x1Kw7FNn5CyE416q+bZQV3e1HRuMUvyegMeRE/WedA==", + "dependencies": { + "Microsoft.Windows.SDK.Win32Metadata": "61.0.15-preview" + } + }, + "NHotkey": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "IEghs0QqWsQYH0uUmvIl0Ye6RaebWRh38eB6ToOkDnQucTYRGFOgtig0gSxlwCszTilYFz3n1ZuY762x+kDR3A==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==" + }, + "flow.launcher.plugin": { + "type": "Project", + "dependencies": { + "JetBrains.Annotations": "[2024.3.0, )" + } + } + } + } +} \ No newline at end of file diff --git a/Flow.Launcher.Plugin/DialogJumpResult.cs b/Flow.Launcher.Plugin/DialogJumpResult.cs new file mode 100644 index 000000000..2c9f0c139 --- /dev/null +++ b/Flow.Launcher.Plugin/DialogJumpResult.cs @@ -0,0 +1,92 @@ +namespace Flow.Launcher.Plugin +{ + /// + /// Describes a result of a executed by a plugin in Dialog Jump window + /// + public class DialogJumpResult : Result + { + /// + /// This holds the path which can be provided by plugin to be navigated to the + /// file dialog when records in Dialog Jump window is right clicked on a result. + /// + public required string DialogJumpPath { get; init; } + + /// + /// Clones the current Dialog Jump result + /// + public new DialogJumpResult Clone() + { + return new DialogJumpResult + { + Title = Title, + SubTitle = SubTitle, + ActionKeywordAssigned = ActionKeywordAssigned, + CopyText = CopyText, + AutoCompleteText = AutoCompleteText, + IcoPath = IcoPath, + BadgeIcoPath = BadgeIcoPath, + RoundedIcon = RoundedIcon, + Icon = Icon, + BadgeIcon = BadgeIcon, + Glyph = Glyph, + Action = Action, + AsyncAction = AsyncAction, + Score = Score, + TitleHighlightData = TitleHighlightData, + OriginQuery = OriginQuery, + PluginDirectory = PluginDirectory, + ContextData = ContextData, + PluginID = PluginID, + TitleToolTip = TitleToolTip, + SubTitleToolTip = SubTitleToolTip, + PreviewPanel = PreviewPanel, + ProgressBar = ProgressBar, + ProgressBarColor = ProgressBarColor, + Preview = Preview, + AddSelectedCount = AddSelectedCount, + RecordKey = RecordKey, + ShowBadge = ShowBadge, + DialogJumpPath = DialogJumpPath + }; + } + + /// + /// Convert to . + /// + public static DialogJumpResult From(Result result, string dialogJumpPath) + { + return new DialogJumpResult + { + Title = result.Title, + SubTitle = result.SubTitle, + ActionKeywordAssigned = result.ActionKeywordAssigned, + CopyText = result.CopyText, + AutoCompleteText = result.AutoCompleteText, + IcoPath = result.IcoPath, + BadgeIcoPath = result.BadgeIcoPath, + RoundedIcon = result.RoundedIcon, + Icon = result.Icon, + BadgeIcon = result.BadgeIcon, + Glyph = result.Glyph, + Action = result.Action, + AsyncAction = result.AsyncAction, + Score = result.Score, + TitleHighlightData = result.TitleHighlightData, + OriginQuery = result.OriginQuery, + PluginDirectory = result.PluginDirectory, + ContextData = result.ContextData, + PluginID = result.PluginID, + TitleToolTip = result.TitleToolTip, + SubTitleToolTip = result.SubTitleToolTip, + PreviewPanel = result.PreviewPanel, + ProgressBar = result.ProgressBar, + ProgressBarColor = result.ProgressBarColor, + Preview = result.Preview, + AddSelectedCount = result.AddSelectedCount, + RecordKey = result.RecordKey, + ShowBadge = result.ShowBadge, + DialogJumpPath = dialogJumpPath + }; + } + } +} diff --git a/Flow.Launcher.Plugin/EventHandler.cs b/Flow.Launcher.Plugin/EventHandler.cs index 893b0ba80..47ab24757 100644 --- a/Flow.Launcher.Plugin/EventHandler.cs +++ b/Flow.Launcher.Plugin/EventHandler.cs @@ -39,7 +39,14 @@ namespace Flow.Launcher.Plugin /// /// public delegate void VisibilityChangedEventHandler(object sender, VisibilityChangedEventArgs args); - + + /// + /// A delegate for when the actual application theme is changed + /// + /// + /// + public delegate void ActualApplicationThemeChangedEventHandler(object sender, ActualApplicationThemeChangedEventArgs args); + /// /// The event args for /// @@ -77,4 +84,15 @@ namespace Flow.Launcher.Plugin /// public Query Query { get; set; } } + + /// + /// The event args for + /// + public class ActualApplicationThemeChangedEventArgs : EventArgs + { + /// + /// if the application has changed actual theme + /// + public bool IsDark { get; init; } + } } diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 1831bf46f..ae2454279 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -1,7 +1,7 @@ - net7.0-windows + net9.0-windows {8451ECDD-2EA4-4966-BB0A-7BBC40138E80} true Library @@ -11,13 +11,14 @@ false false false + true - 4.7.0 - 4.7.0 - 4.7.0 - 4.7.0 + 5.0.0 + 5.0.0 + 5.0.0 + 5.0.0 Flow.Launcher.Plugin Flow-Launcher MIT @@ -67,17 +68,17 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all diff --git a/Flow.Launcher.Plugin/Interfaces/IAsyncDialogJump.cs b/Flow.Launcher.Plugin/Interfaces/IAsyncDialogJump.cs new file mode 100644 index 000000000..e028ebb12 --- /dev/null +++ b/Flow.Launcher.Plugin/Interfaces/IAsyncDialogJump.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Threading; + +namespace Flow.Launcher.Plugin +{ + /// + /// Asynchronous Dialog Jump Model + /// + public interface IAsyncDialogJump : IFeatures + { + /// + /// Asynchronous querying for Dialog Jump window + /// + /// + /// If the Querying method requires high IO transmission + /// or performing CPU intense jobs (performing better with cancellation), please use this IAsyncDialogJump interface + /// + /// Query to search + /// Cancel when querying job is obsolete + /// + Task> QueryDialogJumpAsync(Query query, CancellationToken token); + } +} diff --git a/Flow.Launcher.Plugin/Interfaces/IDialogJump.cs b/Flow.Launcher.Plugin/Interfaces/IDialogJump.cs new file mode 100644 index 000000000..d81b2bd19 --- /dev/null +++ b/Flow.Launcher.Plugin/Interfaces/IDialogJump.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Flow.Launcher.Plugin +{ + /// + /// Synchronous Dialog Jump Model + /// + /// If the Querying method requires high IO transmission + /// or performing CPU intense jobs (performing better with cancellation), please try the IAsyncDialogJump interface + /// + /// + public interface IDialogJump : IAsyncDialogJump + { + /// + /// Querying for Dialog Jump window + /// + /// This method will be called within a Task.Run, + /// so please avoid synchrously wait for long. + /// + /// + /// Query to search + /// + List QueryDialogJump(Query query); + + Task> IAsyncDialogJump.QueryDialogJumpAsync(Query query, CancellationToken token) => Task.Run(() => QueryDialogJump(query), token); + } +} diff --git a/Flow.Launcher.Plugin/Interfaces/IDialogJumpDialog.cs b/Flow.Launcher.Plugin/Interfaces/IDialogJumpDialog.cs new file mode 100644 index 000000000..33ad9ae73 --- /dev/null +++ b/Flow.Launcher.Plugin/Interfaces/IDialogJumpDialog.cs @@ -0,0 +1,96 @@ +using System; + +#nullable enable + +namespace Flow.Launcher.Plugin +{ + /// + /// Interface for handling file dialog instances in DialogJump. + /// + public interface IDialogJumpDialog : IFeatures, IDisposable + { + /// + /// Check if the foreground window is a file dialog instance. + /// + /// + /// The handle of the foreground window to check. + /// + /// + /// The window if the foreground window is a file dialog instance. Null if it is not. + /// + IDialogJumpDialogWindow? CheckDialogWindow(IntPtr hwnd); + } + + /// + /// Interface for handling a specific file dialog window in DialogJump. + /// + public interface IDialogJumpDialogWindow : IDisposable + { + /// + /// The handle of the dialog window. + /// + IntPtr Handle { get; } + + /// + /// Get the current tab of the dialog window. + /// + /// + IDialogJumpDialogWindowTab GetCurrentTab(); + } + + /// + /// Interface for handling a specific tab in a file dialog window in DialogJump. + /// + public interface IDialogJumpDialogWindowTab : IDisposable + { + /// + /// The handle of the dialog tab. + /// + IntPtr Handle { get; } + + /// + /// Get the current folder path of the dialog tab. + /// + /// + string GetCurrentFolder(); + + /// + /// Get the current file of the dialog tab. + /// + /// + string GetCurrentFile(); + + /// + /// Jump to a folder in the dialog tab. + /// + /// + /// The path to the folder to jump to. + /// + /// + /// Whether folder jump is under automatical mode. + /// + /// + /// True if the jump was successful, false otherwise. + /// + bool JumpFolder(string path, bool auto); + + /// + /// Jump to a file in the dialog tab. + /// + /// + /// The path to the file to jump to. + /// + /// + /// True if the jump was successful, false otherwise. + /// + bool JumpFile(string path); + + /// + /// Open the file in the dialog tab. + /// + /// + /// True if the file was opened successfully, false otherwise. + /// + bool Open(); + } +} diff --git a/Flow.Launcher.Plugin/Interfaces/IDialogJumpExplorer.cs b/Flow.Launcher.Plugin/Interfaces/IDialogJumpExplorer.cs new file mode 100644 index 000000000..9a2b879d0 --- /dev/null +++ b/Flow.Launcher.Plugin/Interfaces/IDialogJumpExplorer.cs @@ -0,0 +1,40 @@ +using System; + +#nullable enable + +namespace Flow.Launcher.Plugin +{ + /// + /// Interface for handling file explorer instances in DialogJump. + /// + public interface IDialogJumpExplorer : IFeatures, IDisposable + { + /// + /// Check if the foreground window is a Windows Explorer instance. + /// + /// + /// The handle of the foreground window to check. + /// + /// + /// The window if the foreground window is a file explorer instance. Null if it is not. + /// + IDialogJumpExplorerWindow? CheckExplorerWindow(IntPtr hwnd); + } + + /// + /// Interface for handling a specific file explorer window in DialogJump. + /// + public interface IDialogJumpExplorerWindow : IDisposable + { + /// + /// The handle of the explorer window. + /// + IntPtr Handle { get; } + + /// + /// Get the current folder path of the explorer window. + /// + /// + string? GetExplorerPath(); + } +} diff --git a/Flow.Launcher.Plugin/Interfaces/IPlugin.cs b/Flow.Launcher.Plugin/Interfaces/IPlugin.cs index bac93d090..cf5a8a582 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPlugin.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPlugin.cs @@ -32,6 +32,6 @@ namespace Flow.Launcher.Plugin Task IAsyncPlugin.InitAsync(PluginInitContext context) => Task.Run(() => Init(context)); - Task> IAsyncPlugin.QueryAsync(Query query, CancellationToken token) => Task.Run(() => Query(query)); + Task> IAsyncPlugin.QueryAsync(Query query, CancellationToken token) => Task.Run(() => Query(query), token); } } diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index 74c97fa6e..dd44647b4 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -84,6 +84,15 @@ namespace Flow.Launcher.Plugin /// Optional message subtitle void ShowMsgError(string title, string subTitle = ""); + /// + /// Show the error message using Flow's standard error icon. + /// + /// Message title + /// Message button content + /// Message button action + /// Optional message subtitle + void ShowMsgErrorWithButton(string title, string buttonText, Action buttonAction, string subTitle = ""); + /// /// Show the MainWindow when hiding /// @@ -127,6 +136,27 @@ namespace Flow.Launcher.Plugin /// when true will use main windows as the owner void ShowMsg(string title, string subTitle, string iconPath, bool useMainWindowAsOwner = true); + /// + /// Show message box with button + /// + /// Message title + /// Message button content + /// Message button action + /// Message subtitle + /// Message icon path (relative path to your plugin folder) + void ShowMsgWithButton(string title, string buttonText, Action buttonAction, string subTitle = "", string iconPath = ""); + + /// + /// Show message box with button + /// + /// Message title + /// Message button content + /// Message button action + /// Message subtitle + /// Message icon path (relative path to your plugin folder) + /// when true will use main windows as the owner + void ShowMsgWithButton(string title, string buttonText, Action buttonAction, string subTitle, string iconPath, bool useMainWindowAsOwner = true); + /// /// Open setting dialog /// @@ -572,5 +602,28 @@ namespace Flow.Launcher.Plugin /// /// The time taken to execute the method in milliseconds public Task StopwatchLogInfoAsync(string className, string message, Func action, [CallerMemberName] string methodName = ""); + + /// + /// Representing whether the application is using a dark theme + /// + /// + bool IsApplicationDarkTheme(); + + /// + /// Invoked when the actual theme of the application has changed. Currently, the plugin will continue to be subscribed even if it is turned off. + /// + event ActualApplicationThemeChangedEventHandler ActualApplicationThemeChanged; + + /// + /// Get the user data directory of Flow Launcher. + /// + /// + string GetDataDirectory(); + + /// + /// Get the log directory of Flow Launcher. + /// + /// + string GetLogDirectory(); } } diff --git a/Flow.Launcher.Plugin/NativeMethods.txt b/Flow.Launcher.Plugin/NativeMethods.txt index 0596691cc..901695a8b 100644 --- a/Flow.Launcher.Plugin/NativeMethods.txt +++ b/Flow.Launcher.Plugin/NativeMethods.txt @@ -5,4 +5,12 @@ GetWindowTextLength WM_KEYDOWN WM_KEYUP WM_SYSKEYDOWN -WM_SYSKEYUP \ No newline at end of file +WM_SYSKEYUP + +GetSystemMetrics +EnumDisplayMonitors +MonitorFromWindow +GetMonitorInfo +MONITORINFOEXW +GetCursorPos +MonitorFromPoint \ No newline at end of file diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index f0fcd48ff..2c9b8d4fd 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -57,7 +57,10 @@ namespace Flow.Launcher.Plugin /// for user on the plugin result. If autocomplete action for example is tab, pressing tab will have /// the default constructed autocomplete text (result's Title), or the text provided here if not empty. /// - /// When a value is not set, the will be used. + /// + /// When a value is not set, the will be used. + /// Please include the action keyword prefix when necessary because Flow does not prepend it automatically. + /// public string AutoCompleteText { get; set; } /// @@ -257,6 +260,17 @@ namespace Flow.Launcher.Plugin /// public bool ShowBadge { get; set; } = false; + /// + /// This holds the text which can be shown as a query suggestion. + /// + /// + /// When a value is not set, the will be used. + /// Do not include the action keyword prefix because Flow prepends it automatically. + /// If the it does not start with the query text, it will not be shown as a suggestion. + /// So make sure to set this value to start with the query text. + /// + public string QuerySuggestionText { get; set; } + /// /// Run this result, asynchronously /// @@ -308,6 +322,7 @@ namespace Flow.Launcher.Plugin AddSelectedCount = AddSelectedCount, RecordKey = RecordKey, ShowBadge = ShowBadge, + QuerySuggestionText = QuerySuggestionText }; } diff --git a/Flow.Launcher.Infrastructure/MonitorInfo.cs b/Flow.Launcher.Plugin/SharedModels/MonitorInfo.cs similarity index 52% rename from Flow.Launcher.Infrastructure/MonitorInfo.cs rename to Flow.Launcher.Plugin/SharedModels/MonitorInfo.cs index 3221708c1..6808c9c91 100644 --- a/Flow.Launcher.Infrastructure/MonitorInfo.cs +++ b/Flow.Launcher.Plugin/SharedModels/MonitorInfo.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System; using System.Runtime.InteropServices; using System.Windows; using Windows.Win32; @@ -7,13 +6,16 @@ using Windows.Win32.Foundation; using Windows.Win32.Graphics.Gdi; using Windows.Win32.UI.WindowsAndMessaging; -namespace Flow.Launcher.Infrastructure; +namespace Flow.Launcher.Plugin.SharedModels; /// /// Contains full information about a display monitor. -/// Codes are edited from: . +/// Inspired from: https://github.com/Jack251970/DesktopWidgets3. /// -internal class MonitorInfo +/// +/// Use this class to replace the System.Windows.Forms.Screen class which can cause possible System.PlatformNotSupportedException. +/// +public class MonitorInfo { /// /// Gets the display monitors (including invisible pseudo-monitors associated with the mirroring drivers). @@ -23,14 +25,14 @@ internal class MonitorInfo { var monitorCount = PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CMONITORS); var list = new List(monitorCount); - var callback = new MONITORENUMPROC((HMONITOR monitor, HDC deviceContext, RECT* rect, LPARAM data) => + var callback = new MONITORENUMPROC((monitor, deviceContext, rect, data) => { list.Add(new MonitorInfo(monitor, rect)); return true; }); var dwData = new LPARAM(); var hdc = new HDC(); - bool ok = PInvoke.EnumDisplayMonitors(hdc, (RECT?)null, callback, dwData); + bool ok = PInvoke.EnumDisplayMonitors(hdc, null, callback, dwData); if (!ok) { Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error()); @@ -43,11 +45,11 @@ internal class MonitorInfo /// /// Window handle /// The display monitor that is nearest to a given window, or null if no monitor is found. - public static unsafe MonitorInfo GetNearestDisplayMonitor(HWND hwnd) + public static unsafe MonitorInfo GetNearestDisplayMonitor(nint hwnd) { - var nearestMonitor = PInvoke.MonitorFromWindow(hwnd, MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST); + var nearestMonitor = PInvoke.MonitorFromWindow(new(hwnd), MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST); MonitorInfo nearestMonitorInfo = null; - var callback = new MONITORENUMPROC((HMONITOR monitor, HDC deviceContext, RECT* rect, LPARAM data) => + var callback = new MONITORENUMPROC((monitor, deviceContext, rect, data) => { if (monitor == nearestMonitor) { @@ -58,7 +60,7 @@ internal class MonitorInfo }); var dwData = new LPARAM(); var hdc = new HDC(); - bool ok = PInvoke.EnumDisplayMonitors(hdc, (RECT?)null, callback, dwData); + bool ok = PInvoke.EnumDisplayMonitors(hdc, null, callback, dwData); if (!ok) { Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error()); @@ -66,17 +68,75 @@ internal class MonitorInfo return nearestMonitorInfo; } + /// + /// Gets the primary display monitor (the one that contains the taskbar). + /// + /// The primary display monitor, or null if no monitor is found. + public static unsafe MonitorInfo GetPrimaryDisplayMonitor() + { + var primaryMonitor = PInvoke.MonitorFromWindow(new HWND(nint.Zero), MONITOR_FROM_FLAGS.MONITOR_DEFAULTTOPRIMARY); + MonitorInfo primaryMonitorInfo = null; + var callback = new MONITORENUMPROC((monitor, deviceContext, rect, data) => + { + if (monitor == primaryMonitor) + { + primaryMonitorInfo = new MonitorInfo(monitor, rect); + return false; + } + return true; + }); + var dwData = new LPARAM(); + var hdc = new HDC(); + bool ok = PInvoke.EnumDisplayMonitors(hdc, null, callback, dwData); + if (!ok) + { + Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error()); + } + return primaryMonitorInfo; + } + + /// + /// Gets the display monitor that contains the cursor. + /// + /// The display monitor that contains the cursor, or null if no monitor is found. + public static unsafe MonitorInfo GetCursorDisplayMonitor() + { + if (!PInvoke.GetCursorPos(out var pt)) + { + Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error()); + } + var cursorMonitor = PInvoke.MonitorFromPoint(pt, MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST); + MonitorInfo cursorMonitorInfo = null; + var callback = new MONITORENUMPROC((monitor, deviceContext, rect, data) => + { + if (monitor == cursorMonitor) + { + cursorMonitorInfo = new MonitorInfo(monitor, rect); + return false; + } + return true; + }); + var dwData = new LPARAM(); + var hdc = new HDC(); + bool ok = PInvoke.EnumDisplayMonitors(hdc, null, callback, dwData); + if (!ok) + { + Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error()); + } + return cursorMonitorInfo; + } + private readonly HMONITOR _monitor; internal unsafe MonitorInfo(HMONITOR monitor, RECT* rect) { - RectMonitor = + Bounds = new Rect(new Point(rect->left, rect->top), new Point(rect->right, rect->bottom)); _monitor = monitor; var info = new MONITORINFOEXW() { monitorInfo = new MONITORINFO() { cbSize = (uint)sizeof(MONITORINFOEXW) } }; GetMonitorInfo(monitor, ref info); - RectWork = + WorkingArea = new Rect(new Point(info.monitorInfo.rcWork.left, info.monitorInfo.rcWork.top), new Point(info.monitorInfo.rcWork.right, info.monitorInfo.rcWork.bottom)); Name = new string(info.szDevice.AsSpan()).Replace("\0", "").Trim(); @@ -93,7 +153,7 @@ internal class MonitorInfo /// /// If the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values. /// - public Rect RectMonitor { get; } + public Rect Bounds { get; } /// /// Gets the work area rectangle of the display monitor, expressed in virtual-screen coordinates. @@ -101,15 +161,15 @@ internal class MonitorInfo /// /// If the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values. /// - public Rect RectWork { get; } + public Rect WorkingArea { get; } /// - /// Gets if the monitor is the the primary display monitor. + /// Gets if the monitor is the primary display monitor. /// - public bool IsPrimary => _monitor == PInvoke.MonitorFromWindow(new(IntPtr.Zero), MONITOR_FROM_FLAGS.MONITOR_DEFAULTTOPRIMARY); + public bool IsPrimary => _monitor == PInvoke.MonitorFromWindow(new(nint.Zero), MONITOR_FROM_FLAGS.MONITOR_DEFAULTTOPRIMARY); /// - public override string ToString() => $"{Name} {RectMonitor.Width}x{RectMonitor.Height}"; + public override string ToString() => $"{Name} {Bounds.Width}x{Bounds.Height}"; private static unsafe bool GetMonitorInfo(HMONITOR hMonitor, ref MONITORINFOEXW lpmi) { diff --git a/Flow.Launcher.Plugin/UserPlugin.cs b/Flow.Launcher.Plugin/UserPlugin.cs index 74a16b83d..4257a8b2e 100644 --- a/Flow.Launcher.Plugin/UserPlugin.cs +++ b/Flow.Launcher.Plugin/UserPlugin.cs @@ -76,5 +76,10 @@ namespace Flow.Launcher.Plugin /// Indicates whether the plugin is installed from a local path /// public bool IsFromLocalInstallPath => !string.IsNullOrEmpty(LocalInstallPath); + + /// + /// The minimum Flow Launcher version required for this plugin. Default is "". + /// + public string MinimumAppVersion { get; set; } = string.Empty; } } diff --git a/Flow.Launcher.Plugin/packages.lock.json b/Flow.Launcher.Plugin/packages.lock.json new file mode 100644 index 000000000..af835c598 --- /dev/null +++ b/Flow.Launcher.Plugin/packages.lock.json @@ -0,0 +1,77 @@ +{ + "version": 1, + "dependencies": { + "net9.0-windows7.0": { + "Fody": { + "type": "Direct", + "requested": "[6.9.2, )", + "resolved": "6.9.2", + "contentHash": "YBHobPGogb0vYhGYIxn/ndWqTjNWZveDi5jdjrcshL2vjwU3gQGyDeI7vGgye+2rAM5fGRvlLgNWLW3DpviS/w==" + }, + "JetBrains.Annotations": { + "type": "Direct", + "requested": "[2024.3.0, )", + "resolved": "2024.3.0", + "contentHash": "ox5pkeLQXjvJdyAB4b2sBYAlqZGLh3PjSnP1bQNVx72ONuTJ9+34/+Rq91Fc0dG29XG9RgZur9+NcP4riihTug==" + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "Microsoft.Windows.CsWin32": { + "type": "Direct", + "requested": "[0.3.183, )", + "resolved": "0.3.183", + "contentHash": "Ze3aE2y7xgzKxEWtNb4SH0CExXpCHr3sbmwnvMiWMzJhWDX/G4Rs5wgg2UNs3VN+qVHh/DkDWLCPaVQv/b//Nw==", + "dependencies": { + "Microsoft.Windows.SDK.Win32Docs": "0.1.42-alpha", + "Microsoft.Windows.SDK.Win32Metadata": "61.0.15-preview", + "Microsoft.Windows.WDK.Win32Metadata": "0.12.8-experimental" + } + }, + "PropertyChanged.Fody": { + "type": "Direct", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "6v+f9cI8YjnZH2WBHuOqWEAo8DFFNGFIdU8xD3AsL6fhV6Y8oAmVWd7XKk49t8DpeUBwhR/X+97+6Epvek0Y3A==", + "dependencies": { + "Fody": "6.6.4" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Microsoft.Windows.SDK.Win32Docs": { + "type": "Transitive", + "resolved": "0.1.42-alpha", + "contentHash": "Z/9po23gUA9aoukirh2ItMU2ZS9++Js9Gdds9fu5yuMojDrmArvY2y+tq9985tR3cxFxpZO1O35Wjfo0khj5HA==" + }, + "Microsoft.Windows.SDK.Win32Metadata": { + "type": "Transitive", + "resolved": "61.0.15-preview", + "contentHash": "cysex3dazKtCPALCluC2XX3f5Aedy9H2pw5jb+TW5uas2rkem1Z7FRnbUrg2vKx0pk0Qz+4EJNr37HdYTEcvEQ==" + }, + "Microsoft.Windows.WDK.Win32Metadata": { + "type": "Transitive", + "resolved": "0.12.8-experimental", + "contentHash": "3n8R44/Z96Ly+ty4eYVJfESqbzvpw96lRLs3zOzyDmr1x1Kw7FNn5CyE416q+bZQV3e1HRuMUvyegMeRE/WedA==", + "dependencies": { + "Microsoft.Windows.SDK.Win32Metadata": "61.0.15-preview" + } + } + } + } +} \ No newline at end of file diff --git a/Flow.Launcher.Test/ChineseDetectionPerformanceTest.cs b/Flow.Launcher.Test/ChineseDetectionPerformanceTest.cs new file mode 100644 index 000000000..1747f2b4a --- /dev/null +++ b/Flow.Launcher.Test/ChineseDetectionPerformanceTest.cs @@ -0,0 +1,265 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using NUnit.Framework.Legacy; +using Flow.Launcher.Infrastructure; +using ToolGood.Words.Pinyin; + +namespace Flow.Launcher.Test +{ + /// + /// Performance test comparing ContainsChinese() vs WordsHelper.HasChinese() + /// + /// This test verifies: + /// 1. Both methods produce identical results (correctness) + /// 2. Performance characteristics of both implementations + /// 3. Memory allocation patterns + /// + /// The ContainsChinese() method uses optimized Unicode range checking with ReadOnlySpan + /// while WordsHelper.HasChinese() uses the ToolGood.Words library implementation. + /// + [TestFixture] + public class ChineseDetectionPerformanceTest + { + private readonly List _testStrings = new() + { + // Pure English - should return false + "Hello World", + "Visual Studio Code", + "Microsoft Office 2023", + "Adobe Photoshop Creative Suite", + "Google Chrome Browser Application", + + // Pure Chinese - should return true + "你好世界", + "微软办公软件", + "谷歌浏览器", + "北京大学计算机科学与技术学院", + "中华人民共和国国家发展和改革委员会", + + // Mixed content - should return true + "Hello 世界", + "Visual Studio 代码编辑器", + "QQ音乐 Music Player", + "Windows 10 操作系统", + "GitHub 代码仓库管理平台", + + // Edge cases + "", + " ", + "123456", + "!@#$%^&*()", + "café résumé naïve", // Accented characters (not Chinese) + + // Long strings for performance testing + "This is a very long English string that contains no Chinese characters but is designed to test performance with longer text content that might appear in file names or application descriptions", + "这是一个非常长的中文字符串,包含了很多汉字,用来测试在处理较长中文文本时的性能表现,比如可能出现在文件名或应用程序描述中的文本内容", + "This is a mixed 混合内容的字符串 that contains both English and Chinese characters 中英文混合 to test performance with 复杂的文本内容 in real-world scenarios 真实场景中的应用" + }; + + [Test] + public void ContainsChinese_CorrectnessTest() + { + // Verify ContainsChinese works correctly for known cases + ClassicAssert.IsFalse(ContainsChinese("Hello World"), "Pure English should return false"); + ClassicAssert.IsTrue(ContainsChinese("你好世界"), "Pure Chinese should return true"); + ClassicAssert.IsTrue(ContainsChinese("Hello 世界"), "Mixed content should return true"); + ClassicAssert.IsFalse(ContainsChinese(""), "Empty string should return false"); + ClassicAssert.IsFalse(ContainsChinese("123456"), "Numbers should return false"); + ClassicAssert.IsFalse(ContainsChinese("café résumé"), "Accented characters should return false"); + } + + [Test] + public void WordsHelper_CorrectnessTest() + { + // Verify WordsHelper.HasChinese works correctly for known cases + ClassicAssert.IsFalse(WordsHelper.HasChinese("Hello World"), "Pure English should return false"); + ClassicAssert.IsTrue(WordsHelper.HasChinese("你好世界"), "Pure Chinese should return true"); + ClassicAssert.IsTrue(WordsHelper.HasChinese("Hello 世界"), "Mixed content should return true"); + ClassicAssert.IsFalse(WordsHelper.HasChinese(""), "Empty string should return false"); + ClassicAssert.IsFalse(WordsHelper.HasChinese("123456"), "Numbers should return false"); + ClassicAssert.IsFalse(WordsHelper.HasChinese("café résumé"), "Accented characters should return false"); + } + + [Test] + public void BothMethods_ShouldProduceSameResults() + { + // Critical test: verify both methods produce identical results for all test cases + foreach (var testString in _testStrings) + { + var wordsHelperResult = WordsHelper.HasChinese(testString); + var containsChineseResult = ContainsChinese(testString); + + ClassicAssert.AreEqual(wordsHelperResult, containsChineseResult, + $"Results differ for string: '{testString}'. WordsHelper: {wordsHelperResult}, ContainsChinese: {containsChineseResult}"); + } + + Console.WriteLine($"✓ Both methods produce identical results for all {_testStrings.Count} test cases"); + } + + [Test] + public void PerformanceComparison_BasicBenchmark() + { + const int iterations = 1000000; + + Console.WriteLine("=== CHINESE CHARACTER DETECTION PERFORMANCE TEST ==="); + Console.WriteLine($"Test iterations: {iterations:N0}"); + Console.WriteLine($"Test strings: {_testStrings.Count}"); + Console.WriteLine($"Total operations: {iterations * _testStrings.Count:N0}"); + Console.WriteLine(); + + // Warmup to ensure JIT compilation + Console.WriteLine("Warming up..."); + for (int i = 0; i < 1000; i++) + { + foreach (var testString in _testStrings) + { + _ = ContainsChinese(testString); + _ = WordsHelper.HasChinese(testString); + } + } + + // Benchmark ContainsChinese method + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + + var sw1 = System.Diagnostics.Stopwatch.StartNew(); + for (int i = 0; i < iterations; i++) + { + foreach (var testString in _testStrings) + { + _ = ContainsChinese(testString); + } + } + sw1.Stop(); + + // Benchmark WordsHelper.HasChinese method + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + + var sw2 = System.Diagnostics.Stopwatch.StartNew(); + for (int i = 0; i < iterations; i++) + { + foreach (var testString in _testStrings) + { + _ = WordsHelper.HasChinese(testString); + } + } + sw2.Stop(); + + // Calculate and display results + var containsChineseMs = sw1.Elapsed.TotalMilliseconds; + var wordsHelperMs = sw2.Elapsed.TotalMilliseconds; + var speedRatio = wordsHelperMs / containsChineseMs; + var timeDifference = wordsHelperMs - containsChineseMs; + + Console.WriteLine("RESULTS:"); + Console.WriteLine($"ContainsChinese(): {containsChineseMs:F3} ms"); + Console.WriteLine($"WordsHelper.HasChinese(): {wordsHelperMs:F3} ms"); + Console.WriteLine($"Time difference: {timeDifference:F3} ms"); + Console.WriteLine($"Speed improvement: {speedRatio:F2}x"); + Console.WriteLine($"Performance gain: {((speedRatio - 1) * 100):F1}%"); + Console.WriteLine(); + + if (speedRatio > 1.0) + { + Console.WriteLine($"✓ ContainsChinese() is {speedRatio:F2}x faster than WordsHelper.HasChinese()"); + } + else + { + Console.WriteLine($"⚠ WordsHelper.HasChinese() is {(1/speedRatio):F2}x faster than ContainsChinese()"); + } + + // Test always passes - this is a measurement test + ClassicAssert.IsTrue(true); + } + + [Test] + public void PerformanceComparison_ByStringType() + { + Console.WriteLine("=== PERFORMANCE BY STRING TYPE ==="); + + var categories = new Dictionary> + { + ["Pure English"] = _testStrings.Where(s => !ContainsChinese(s) && s.All(c => c <= 127)).ToList(), + ["Pure Chinese"] = _testStrings.Where(s => ContainsChinese(s) && s.All(c => IsChineseCharacter(c) || char.IsWhiteSpace(c))).ToList(), + ["Mixed Content"] = _testStrings.Where(s => ContainsChinese(s) && s.Any(c => c <= 127 && char.IsLetter(c))).ToList(), + ["Edge Cases"] = _testStrings.Where(s => string.IsNullOrWhiteSpace(s) || s.All(c => !char.IsLetter(c))).ToList() + }; + + foreach (var category in categories) + { + if (category.Value.Count == 0) continue; + + Console.WriteLine($"\n{category.Key} ({category.Value.Count} strings):"); + + var sample = category.Value.First(); + var displayText = sample.Length > 40 ? sample.Substring(0, 40) + "..." : sample; + Console.WriteLine($" Sample: '{displayText}'"); + + const int categoryIterations = 5000; + + // Test each method + var sw1 = System.Diagnostics.Stopwatch.StartNew(); + for (int i = 0; i < categoryIterations; i++) + { + foreach (var str in category.Value) + { + _ = ContainsChinese(str); + } + } + sw1.Stop(); + + var sw2 = System.Diagnostics.Stopwatch.StartNew(); + for (int i = 0; i < categoryIterations; i++) + { + foreach (var str in category.Value) + { + _ = WordsHelper.HasChinese(str); + } + } + sw2.Stop(); + + var ratio = (double)sw2.ElapsedTicks / sw1.ElapsedTicks; + Console.WriteLine($" Performance: ContainsChinese is {ratio:F2}x faster"); + } + + ClassicAssert.IsTrue(true); + } + + /// + /// Optimized Chinese character detection using comprehensive CJK Unicode ranges + /// This method uses ReadOnlySpan for better performance and covers all CJK character ranges + /// + private static bool ContainsChinese(ReadOnlySpan text) + { + foreach (var c in text) + { + if (IsChineseCharacter(c)) + return true; + } + return false; + } + + /// + /// Check if a character is a Chinese character using comprehensive Unicode ranges + /// Covers CJK Unified Ideographs and all extension blocks + /// + private static bool IsChineseCharacter(char c) + { + return (c >= 0x4E00 && c <= 0x9FFF) || // CJK Unified Ideographs (most common Chinese characters) + (c >= 0x3400 && c <= 0x4DBF) || // CJK Extension A + (c >= 0x20000 && c <= 0x2A6DF) || // CJK Extension B + (c >= 0x2A700 && c <= 0x2B73F) || // CJK Extension C + (c >= 0x2B740 && c <= 0x2B81F) || // CJK Extension D + (c >= 0x2B820 && c <= 0x2CEAF) || // CJK Extension E + (c >= 0x2CEB0 && c <= 0x2EBEF) || // CJK Extension F + (c >= 0xF900 && c <= 0xFAFF) || // CJK Compatibility Ideographs + (c >= 0x2F800 && c <= 0x2FA1F); // CJK Compatibility Supplement + } + } +} diff --git a/Flow.Launcher.Test/Flow.Launcher.Test.csproj b/Flow.Launcher.Test/Flow.Launcher.Test.csproj index 0241a374e..33479c5a0 100644 --- a/Flow.Launcher.Test/Flow.Launcher.Test.csproj +++ b/Flow.Launcher.Test/Flow.Launcher.Test.csproj @@ -1,7 +1,7 @@ - net7.0-windows10.0.19041.0 + net9.0-windows10.0.19041.0 {FF742965-9A80-41A5-B042-D6C7D3A21708} Library Properties @@ -48,13 +48,13 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + \ No newline at end of file diff --git a/Flow.Launcher.Test/TranslationMappingTest.cs b/Flow.Launcher.Test/TranslationMappingTest.cs new file mode 100644 index 000000000..bd3636f0a --- /dev/null +++ b/Flow.Launcher.Test/TranslationMappingTest.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.Reflection; +using Flow.Launcher.Infrastructure; +using NUnit.Framework; +using NUnit.Framework.Legacy; + +namespace Flow.Launcher.Test +{ + [TestFixture] + public class TranslationMappingTest + { + [Test] + public void AddNewIndex_ShouldAddTranslatedIndexPlusLength() + { + var mapping = new TranslationMapping(); + mapping.AddNewIndex(5, 3); + mapping.AddNewIndex(8, 2); + + // 5+3=8, 8+2=10 + ClassicAssert.AreEqual(2, GetOriginalToTranslatedCount(mapping)); + ClassicAssert.AreEqual(8, GetOriginalToTranslatedAt(mapping, 0)); + ClassicAssert.AreEqual(10, GetOriginalToTranslatedAt(mapping, 1)); + } + + [TestCase(0, 0)] + [TestCase(2, 1)] + [TestCase(3, 1)] + [TestCase(5, 2)] + [TestCase(6, 2)] + public void MapToOriginalIndex_ShouldReturnExpectedIndex(int translatedIndex, int expectedOriginalIndex) + { + var mapping = new TranslationMapping(); + // a测试 + // a Ce Shi + mapping.AddNewIndex(0, 1); + mapping.AddNewIndex(2, 2); + mapping.AddNewIndex(5, 3); + + var result = mapping.MapToOriginalIndex(translatedIndex); + ClassicAssert.AreEqual(expectedOriginalIndex, result); + } + + private static int GetOriginalToTranslatedCount(TranslationMapping mapping) + { + var field = typeof(TranslationMapping).GetField("_originalToTranslated", BindingFlags.NonPublic | BindingFlags.Instance); + var list = (List)field.GetValue(mapping); + return list.Count; + } + + private static int GetOriginalToTranslatedAt(TranslationMapping mapping, int index) + { + var field = typeof(TranslationMapping).GetField("_originalToTranslated", BindingFlags.NonPublic | BindingFlags.Instance); + var list = (List)field.GetValue(mapping); + return list[index]; + } + } +} diff --git a/Flow.Launcher/ActionKeywords.xaml b/Flow.Launcher/ActionKeywords.xaml index 887b13126..5af76f37f 100644 --- a/Flow.Launcher/ActionKeywords.xaml +++ b/Flow.Launcher/ActionKeywords.xaml @@ -14,102 +14,102 @@ + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + @@ -117,15 +117,15 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Flow.Launcher/SettingWindow.xaml.cs b/Flow.Launcher/SettingWindow.xaml.cs index c1c0f96a7..0e3e69996 100644 --- a/Flow.Launcher/SettingWindow.xaml.cs +++ b/Flow.Launcher/SettingWindow.xaml.cs @@ -7,10 +7,10 @@ using System.Windows.Interop; using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Plugin.SharedModels; using Flow.Launcher.SettingPages.Views; using Flow.Launcher.ViewModel; using ModernWpf.Controls; -using Screen = System.Windows.Forms.Screen; namespace Flow.Launcher; @@ -202,7 +202,7 @@ public partial class SettingWindow private static bool IsPositionValid(double top, double left) { - foreach (var screen in Screen.AllScreens) + foreach (var screen in MonitorInfo.GetDisplayMonitors()) { var workingArea = screen.WorkingArea; @@ -217,7 +217,7 @@ public partial class SettingWindow private double WindowLeft() { - var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); + var screen = MonitorInfo.GetCursorDisplayMonitor(); var dip1 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0); var dip2 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0); var left = (dip2.X - ActualWidth) / 2 + dip1.X; @@ -226,7 +226,7 @@ public partial class SettingWindow private double WindowTop() { - var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); + var screen = MonitorInfo.GetCursorDisplayMonitor(); var dip1 = Win32Helper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y); var dip2 = Win32Helper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height); var top = (dip2.Y - ActualHeight) / 2 + dip1.Y - 20; diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 01599a050..d492f28c5 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -9,6 +9,7 @@ using System.Threading.Channels; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; +using System.Windows.Controls; using System.Windows.Media; using System.Windows.Threading; using CommunityToolkit.Mvvm.DependencyInjection; @@ -16,12 +17,14 @@ using CommunityToolkit.Mvvm.Input; using Flow.Launcher.Core.Plugin; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Hotkey; +using Flow.Launcher.Infrastructure.DialogJump; using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.Storage; using Microsoft.VisualStudio.Threading; +using ModernWpf; namespace Flow.Launcher.ViewModel { @@ -51,6 +54,7 @@ namespace Flow.Launcher.ViewModel private Task _resultsViewUpdateTask; private readonly IReadOnlyList _emptyResult = new List(); + private readonly IReadOnlyList _emptyDialogJumpResult = new List(); private readonly PluginMetadata _historyMetadata = new() { @@ -195,6 +199,18 @@ namespace Flow.Launcher.ViewModel RegisterViewUpdate(); _ = RegisterClockAndDateUpdateAsync(); + + ThemeManager.Current.ActualApplicationThemeChanged += ThemeManager_ActualApplicationThemeChanged; + } + + private void ThemeManager_ActualApplicationThemeChanged(ThemeManager sender, object args) + { + ActualApplicationThemeChanged?.Invoke( + Application.Current, + new ActualApplicationThemeChangedEventArgs() + { + IsDark = sender.ActualApplicationTheme == ApplicationTheme.Dark + }); } private void RegisterViewUpdate() @@ -202,7 +218,8 @@ namespace Flow.Launcher.ViewModel var resultUpdateChannel = Channel.CreateUnbounded(); _resultsUpdateChannelWriter = resultUpdateChannel.Writer; _resultsViewUpdateTask = - Task.Run(UpdateActionAsync).ContinueWith(continueAction, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default); + Task.Run(UpdateActionAsync).ContinueWith(continueAction, + CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default); async Task UpdateActionAsync() { @@ -272,8 +289,16 @@ namespace Flow.Launcher.ViewModel var token = e.Token == default ? _updateToken : e.Token; - // make a clone to avoid possible issue that plugin will also change the list and items when updating view model - var resultsCopy = DeepCloneResults(e.Results, token); + IReadOnlyList resultsCopy; + if (e.Results == null) + { + resultsCopy = _emptyResult; + } + else + { + // make a clone to avoid possible issue that plugin will also change the list and items when updating view model + resultsCopy = DeepCloneResults(e.Results, false, token); + } foreach (var result in resultsCopy) { @@ -381,12 +406,30 @@ namespace Flow.Launcher.ViewModel [RelayCommand] private void LoadContextMenu() { + // For Dialog Jump and right click mode, we need to navigate to the path + if (_isDialogJump && Settings.DialogJumpResultBehaviour == DialogJumpResultBehaviours.RightClick) + { + if (SelectedResults.SelectedItem != null && DialogWindowHandle != nint.Zero) + { + var result = SelectedResults.SelectedItem.Result; + if (result is DialogJumpResult dialogJumpResult) + { + Win32Helper.SetForegroundWindow(DialogWindowHandle); + _ = Task.Run(() => DialogJump.JumpToPathAsync(DialogWindowHandle, dialogJumpResult.DialogJumpPath)); + } + } + return; + } + + // For query mode, we load context menu if (QueryResultsSelected()) { // When switch to ContextMenu from QueryResults, but no item being chosen, should do nothing // i.e. Shift+Enter/Ctrl+O right after Alt + Space should do nothing if (SelectedResults.SelectedItem != null) + { SelectedResults = ContextMenu; + } } else { @@ -456,12 +499,33 @@ namespace Flow.Launcher.ViewModel return; } - var hideWindow = await result.ExecuteAsync(new ActionContext + // For Dialog Jump and left click mode, we need to navigate to the path + if (_isDialogJump && Settings.DialogJumpResultBehaviour == DialogJumpResultBehaviours.LeftClick) { - // not null means pressing modifier key + number, should ignore the modifier key - SpecialKeyState = index is not null ? SpecialKeyState.Default : GlobalHotkey.CheckModifiers() - }) - .ConfigureAwait(false); + if (result is DialogJumpResult dialogJumpResult) + { + Win32Helper.SetForegroundWindow(DialogWindowHandle); + _ = Task.Run(() => DialogJump.JumpToPathAsync(DialogWindowHandle, dialogJumpResult.DialogJumpPath)); + } + else + { + App.API.LogError(ClassName, "DialogJumpResult expected but got a different result type."); + } + } + // For query mode, we execute the result + else + { + var hideWindow = await result.ExecuteAsync(new ActionContext + { + // not null means pressing modifier key + number, should ignore the modifier key + SpecialKeyState = index is not null ? SpecialKeyState.Default : GlobalHotkey.CheckModifiers() + }).ConfigureAwait(false); + + if (hideWindow) + { + Hide(); + } + } if (QueryResultsSelected()) { @@ -469,26 +533,33 @@ namespace Flow.Launcher.ViewModel _history.Add(result.OriginQuery.RawQuery); lastHistoryIndex = 1; } - - if (hideWindow) - { - Hide(); - } } - private static IReadOnlyList DeepCloneResults(IReadOnlyList results, CancellationToken token = default) + private static IReadOnlyList DeepCloneResults(IReadOnlyList results, bool isDialogJump, CancellationToken token = default) { var resultsCopy = new List(); - foreach (var result in results.ToList()) - { - if (token.IsCancellationRequested) - { - break; - } - var resultCopy = result.Clone(); - resultsCopy.Add(resultCopy); + if (isDialogJump) + { + foreach (var result in results.ToList()) + { + if (token.IsCancellationRequested) break; + + var resultCopy = ((DialogJumpResult)result).Clone(); + resultsCopy.Add(resultCopy); + } } + else + { + foreach (var result in results.ToList()) + { + if (token.IsCancellationRequested) break; + + var resultCopy = result.Clone(); + resultsCopy.Add(resultCopy); + } + } + return resultsCopy; } @@ -813,6 +884,12 @@ namespace Flow.Launcher.ViewModel } } + public Visibility ShowCustomizedPreview + => InternalPreviewVisible && PreviewSelectedItem?.Result.PreviewPanel != null ? Visibility.Visible : Visibility.Collapsed; + + public UserControl CustomizedPreviewControl + => ShowCustomizedPreview == Visibility.Visible ? PreviewSelectedItem?.Result.PreviewPanel.Value : null; + public Visibility ProgressBarVisibility { get; set; } public Visibility MainWindowVisibility { get; set; } @@ -821,6 +898,7 @@ namespace Flow.Launcher.ViewModel public bool MainWindowVisibilityStatus { get; set; } = true; public event VisibilityChangedEventHandler VisibilityChanged; + public event ActualApplicationThemeChangedEventHandler ActualApplicationThemeChanged; public Visibility ClockPanelVisibility { get; set; } public Visibility SearchIconVisibility { get; set; } @@ -1265,25 +1343,21 @@ namespace Flow.Launcher.ViewModel if (query == null) // shortcut expanded { - App.API.LogDebug(ClassName, $"Clear query results"); - - // Hide and clear results again because running query may show and add some results - Results.Visibility = Visibility.Collapsed; - Results.Clear(); - - // Reset plugin icon - PluginIconPath = null; - PluginIconSource = null; - SearchIconVisibility = Visibility.Visible; - - // Hide progress bar again because running query may set this to visible - ProgressBarVisibility = Visibility.Hidden; + ClearResults(); return; } App.API.LogDebug(ClassName, $"Start query with ActionKeyword <{query.ActionKeyword}> and RawQuery <{query.RawQuery}>"); var currentIsHomeQuery = query.IsHomeQuery; + var currentIsDialogJump = _isDialogJump; + + // Do not show home page for Dialog Jump window + if (currentIsHomeQuery && currentIsDialogJump) + { + ClearResults(); + return; + } _updateSource?.Dispose(); @@ -1317,7 +1391,7 @@ namespace Flow.Launcher.ViewModel } else { - plugins = PluginManager.ValidPluginsForQuery(query); + plugins = PluginManager.ValidPluginsForQuery(query, currentIsDialogJump); if (plugins.Count == 1) { @@ -1411,6 +1485,23 @@ namespace Flow.Launcher.ViewModel } // Local function + void ClearResults() + { + App.API.LogDebug(ClassName, $"Clear query results"); + + // Hide and clear results again because running query may show and add some results + Results.Visibility = Visibility.Collapsed; + Results.Clear(); + + // Reset plugin icon + PluginIconPath = null; + PluginIconSource = null; + SearchIconVisibility = Visibility.Visible; + + // Hide progress bar again because running query may set this to visible + ProgressBarVisibility = Visibility.Hidden; + } + async Task QueryTaskAsync(PluginPair plugin, CancellationToken token) { App.API.LogDebug(ClassName, $"Wait for querying plugin <{plugin.Metadata.Name}>"); @@ -1428,21 +1519,23 @@ namespace Flow.Launcher.ViewModel // Task.Yield will force it to run in ThreadPool await Task.Yield(); - var results = currentIsHomeQuery ? - await PluginManager.QueryHomeForPluginAsync(plugin, query, token) : - await PluginManager.QueryForPluginAsync(plugin, query, token); + IReadOnlyList results = currentIsDialogJump ? + await PluginManager.QueryDialogJumpForPluginAsync(plugin, query, token) : + currentIsHomeQuery ? + await PluginManager.QueryHomeForPluginAsync(plugin, query, token) : + await PluginManager.QueryForPluginAsync(plugin, query, token); if (token.IsCancellationRequested) return; IReadOnlyList resultsCopy; if (results == null) { - resultsCopy = _emptyResult; + resultsCopy = currentIsDialogJump ? _emptyDialogJumpResult : _emptyResult; } else { // make a copy of results to avoid possible issue that FL changes some properties of the records, like score, etc. - resultsCopy = DeepCloneResults(results, token); + resultsCopy = DeepCloneResults(results, currentIsDialogJump, token); } foreach (var result in resultsCopy) @@ -1737,6 +1830,208 @@ namespace Flow.Launcher.ViewModel #endregion + #region Dialog Jump + + public nint DialogWindowHandle { get; private set; } = nint.Zero; + + private bool _isDialogJump = false; + + private bool _previousMainWindowVisibilityStatus; + + private CancellationTokenSource _dialogJumpSource; + + public void InitializeVisibilityStatus(bool visibilityStatus) + { + _previousMainWindowVisibilityStatus = visibilityStatus; + } + + public bool IsDialogJumpWindowUnderDialog() + { + return _isDialogJump && DialogJump.DialogJumpWindowPosition == DialogJumpWindowPositions.UnderDialog; + } + + public async Task SetupDialogJumpAsync(nint handle) + { + if (handle == nint.Zero) return; + + // Only set flag & reset window once for one file dialog + var dialogWindowHandleChanged = false; + if (DialogWindowHandle != handle) + { + DialogWindowHandle = handle; + _previousMainWindowVisibilityStatus = MainWindowVisibilityStatus; + _isDialogJump = true; + + dialogWindowHandleChanged = true; + + // If don't give a time, Positioning will be weird + await Task.Delay(300); + } + + // If handle is cleared, which means the dialog is closed, clear Dialog Jump state + if (DialogWindowHandle == nint.Zero) + { + _isDialogJump = false; + return; + } + + // Initialize Dialog Jump window + if (MainWindowVisibilityStatus) + { + if (dialogWindowHandleChanged) + { + // Only update the position + Application.Current?.Dispatcher.Invoke(() => + { + (Application.Current?.MainWindow as MainWindow)?.UpdatePosition(); + }); + + _ = ResetWindowAsync(); + } + } + else + { + if (DialogJump.DialogJumpWindowPosition == DialogJumpWindowPositions.UnderDialog) + { + // We wait for window to be reset before showing it because if window has results, + // showing it before resetting will cause flickering when results are clearing + if (dialogWindowHandleChanged) + { + await ResetWindowAsync(); + } + + Show(); + } + else + { + if (dialogWindowHandleChanged) + { + _ = ResetWindowAsync(); + } + } + } + + if (DialogJump.DialogJumpWindowPosition == DialogJumpWindowPositions.UnderDialog) + { + // Cancel the previous Dialog Jump task + _dialogJumpSource?.Cancel(); + + // Create a new cancellation token source + _dialogJumpSource = new CancellationTokenSource(); + + _ = Task.Run(() => + { + try + { + // Check task cancellation + if (_dialogJumpSource.Token.IsCancellationRequested) return; + + // Check dialog handle + if (DialogWindowHandle == nint.Zero) return; + + // Wait 150ms to check if Dialog Jump window gets the focus + var timeOut = !SpinWait.SpinUntil(() => !Win32Helper.IsForegroundWindow(DialogWindowHandle), 150); + if (timeOut) return; + + // Bring focus back to the dialog + Win32Helper.SetForegroundWindow(DialogWindowHandle); + } + catch (Exception e) + { + App.API.LogException(ClassName, "Failed to focus on dialog window", e); + } + }); + } + } + +#pragma warning disable VSTHRD100 // Avoid async void methods + + public async void ResetDialogJump() + { + // Cache original dialog window handle + var dialogWindowHandle = DialogWindowHandle; + + // Reset the Dialog Jump state + DialogWindowHandle = nint.Zero; + _isDialogJump = false; + + // If dialog window handle is not set, we should not reset the main window visibility + if (dialogWindowHandle == nint.Zero) return; + + if (_previousMainWindowVisibilityStatus != MainWindowVisibilityStatus) + { + // We wait for window to be reset before showing it because if window has results, + // showing it before resetting will cause flickering when results are clearing + await ResetWindowAsync(); + + // Show or hide to change visibility + if (_previousMainWindowVisibilityStatus) + { + Show(); + } + else + { + Hide(false); + } + } + else + { + if (_previousMainWindowVisibilityStatus) + { + // Only update the position + Application.Current?.Dispatcher.Invoke(() => + { + (Application.Current?.MainWindow as MainWindow)?.UpdatePosition(); + }); + + _ = ResetWindowAsync(); + } + else + { + _ = ResetWindowAsync(); + } + } + } + +#pragma warning restore VSTHRD100 // Avoid async void methods + + public void HideDialogJump() + { + if (DialogWindowHandle != nint.Zero) + { + if (DialogJump.DialogJumpWindowPosition == DialogJumpWindowPositions.UnderDialog) + { + // Warning: Main window is already in foreground + // This is because if you click popup menus in other applications to hide Dialog Jump window, + // they can steal focus before showing main window + if (MainWindowVisibilityStatus) + { + Hide(); + } + } + } + } + + // Reset index & preview & selected results & query text + private async Task ResetWindowAsync() + { + lastHistoryIndex = 1; + + if (ExternalPreviewVisible) + { + await CloseExternalPreviewAsync(); + } + + if (!QueryResultsSelected()) + { + SelectedResults = Results; + } + + await ChangeQueryTextAsync(string.Empty, true); + } + + #endregion + #region Public Methods #pragma warning disable VSTHRD100 // Avoid async void methods @@ -1756,7 +2051,7 @@ namespace Flow.Launcher.ViewModel Win32Helper.DWMSetCloakForWindow(mainWindow, false); // Set clock and search icon opacity - var opacity = Settings.UseAnimation ? 0.0 : 1.0; + var opacity = (Settings.UseAnimation && !_isDialogJump) ? 0.0 : 1.0; ClockPanelOpacity = opacity; SearchIconOpacity = opacity; @@ -1785,37 +2080,40 @@ namespace Flow.Launcher.ViewModel } } - public async void Hide() + public async void Hide(bool reset = true) { - lastHistoryIndex = 1; - - if (ExternalPreviewVisible) + if (reset) { - await CloseExternalPreviewAsync(); - } + lastHistoryIndex = 1; - BackToQueryResults(); + if (ExternalPreviewVisible) + { + await CloseExternalPreviewAsync(); + } - switch (Settings.LastQueryMode) - { - case LastQueryMode.Empty: - await ChangeQueryTextAsync(string.Empty); - break; - case LastQueryMode.Preserved: - case LastQueryMode.Selected: - LastQuerySelected = Settings.LastQueryMode == LastQueryMode.Preserved; - break; - case LastQueryMode.ActionKeywordPreserved: - case LastQueryMode.ActionKeywordSelected: - var newQuery = _lastQuery?.ActionKeyword; + BackToQueryResults(); - if (!string.IsNullOrEmpty(newQuery)) - newQuery += " "; - await ChangeQueryTextAsync(newQuery); + switch (Settings.LastQueryMode) + { + case LastQueryMode.Empty: + await ChangeQueryTextAsync(string.Empty); + break; + case LastQueryMode.Preserved: + case LastQueryMode.Selected: + LastQuerySelected = Settings.LastQueryMode == LastQueryMode.Preserved; + break; + case LastQueryMode.ActionKeywordPreserved: + case LastQueryMode.ActionKeywordSelected: + var newQuery = _lastQuery.ActionKeyword; - if (Settings.LastQueryMode == LastQueryMode.ActionKeywordSelected) - LastQuerySelected = false; - break; + if (!string.IsNullOrEmpty(newQuery)) + newQuery += " "; + await ChangeQueryTextAsync(newQuery); + + if (Settings.LastQueryMode == LastQueryMode.ActionKeywordSelected) + LastQuerySelected = false; + break; + } } // When application is exiting, the Application.Current will be null @@ -1825,7 +2123,7 @@ namespace Flow.Launcher.ViewModel if (Application.Current?.MainWindow is MainWindow mainWindow) { // Set clock and search icon opacity - var opacity = Settings.UseAnimation ? 0.0 : 1.0; + var opacity = (Settings.UseAnimation && !_isDialogJump) ? 0.0 : 1.0; ClockPanelOpacity = opacity; SearchIconOpacity = opacity; @@ -1970,11 +2268,13 @@ namespace Flow.Launcher.ViewModel if (disposing) { _updateSource?.Dispose(); + _dialogJumpSource?.Dispose(); _resultsUpdateChannelWriter?.Complete(); if (_resultsViewUpdateTask?.IsCompleted == true) { _resultsViewUpdateTask.Dispose(); } + ThemeManager.Current.ActualApplicationThemeChanged -= ThemeManager_ActualApplicationThemeChanged; _disposed = true; } } diff --git a/Flow.Launcher/ViewModel/PluginViewModel.cs b/Flow.Launcher/ViewModel/PluginViewModel.cs index ea222d023..d889bdd52 100644 --- a/Flow.Launcher/ViewModel/PluginViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginViewModel.cs @@ -37,12 +37,17 @@ namespace Flow.Launcher.ViewModel OnPropertyChanged(nameof(Image)); } + private bool _imageLoaded = false; + public ImageSource Image { get { - if (_image == ImageLoader.MissingImage) + if (!_imageLoaded) + { + _imageLoaded = true; _ = LoadIconAsync(); + } return _image; } diff --git a/Flow.Launcher/ViewModel/ResultViewModel.cs b/Flow.Launcher/ViewModel/ResultViewModel.cs index c58abae28..d4382fb7f 100644 --- a/Flow.Launcher/ViewModel/ResultViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultViewModel.cs @@ -66,8 +66,6 @@ namespace Flow.Launcher.ViewModel public Visibility ShowDefaultPreview => Result.PreviewPanel == null ? Visibility.Visible : Visibility.Collapsed; - public Visibility ShowCustomizedPreview => Result.PreviewPanel == null ? Visibility.Collapsed : Visibility.Visible; - public Visibility ShowIcon { get diff --git a/Flow.Launcher/packages.lock.json b/Flow.Launcher/packages.lock.json new file mode 100644 index 000000000..32b78c334 --- /dev/null +++ b/Flow.Launcher/packages.lock.json @@ -0,0 +1,877 @@ +{ + "version": 1, + "dependencies": { + "net9.0-windows10.0.19041": { + "ChefKeys": { + "type": "Direct", + "requested": "[0.1.2, )", + "resolved": "0.1.2", + "contentHash": "hnayWejg57tg8+lZ1Q/zPR8tj9ezUtB1sY8aCv9jiZ+3wcqK0eGL+Skt9OzT9mjSsBIg4o9Jv1HdQdzjd1lkQw==" + }, + "CommunityToolkit.Mvvm": { + "type": "Direct", + "requested": "[8.4.0, )", + "resolved": "8.4.0", + "contentHash": "tqVU8yc/ADO9oiTRyTnwhFN68hCwvkliMierptWOudIAvWY1mWCh5VFh+guwHJmpMwfg0J0rY+yyd5Oy7ty9Uw==" + }, + "Fody": { + "type": "Direct", + "requested": "[6.9.2, )", + "resolved": "6.9.2", + "contentHash": "YBHobPGogb0vYhGYIxn/ndWqTjNWZveDi5jdjrcshL2vjwU3gQGyDeI7vGgye+2rAM5fGRvlLgNWLW3DpviS/w==" + }, + "MdXaml": { + "type": "Direct", + "requested": "[1.27.0, )", + "resolved": "1.27.0", + "contentHash": "VWhqhCeKVkJe8vkPmXuGZlRX01WDrTugOLeUvJn18jH/8DrGGVBvtgIlJoELHD2f1DiEWqF3lxxjV55vnzE7Tg==", + "dependencies": { + "AvalonEdit": "6.3.0.90", + "MdXaml.Plugins": "1.27.0" + } + }, + "MdXaml.AnimatedGif": { + "type": "Direct", + "requested": "[1.27.0, )", + "resolved": "1.27.0", + "contentHash": "Xrr9IgyAfqDbruqCp2Wxzthbc87QMvMR2YXQsGDyacLtowleefP1Jt3cesZCbI44YcZTGjyJNIkvRAyzzlgsOQ==", + "dependencies": { + "MdXaml.Plugins": "1.27.0", + "WpfAnimatedGif": "2.0.2" + } + }, + "MdXaml.Html": { + "type": "Direct", + "requested": "[1.27.0, )", + "resolved": "1.27.0", + "contentHash": "3AI0g7EwsTuvhhNd9bjb3J7v5aXFk1dLaf1CNbLjkcZs/MwnEUHNgzF+sLQBYYVdG2DqfV1BsuFoPWSG7IdHvg==", + "dependencies": { + "AvalonEdit": "6.3.0.90", + "HtmlAgilityPack": "1.11.42", + "MdXaml": "1.27.0", + "MdXaml.Plugins": "1.27.0" + } + }, + "MdXaml.Plugins": { + "type": "Direct", + "requested": "[1.27.0, )", + "resolved": "1.27.0", + "contentHash": "We7LtBdoukRg9mqTfa1f5n8z/GQPMKBRj3URk9DiMuqzIHkW1lTgK5njVPSScxsRt4YzW22423tSnLWNm2MJKg==" + }, + "MdXaml.Svg": { + "type": "Direct", + "requested": "[1.27.0, )", + "resolved": "1.27.0", + "contentHash": "zHtzcQrEVDoTDRvxFAccAIQG3UHCUW2cdWrGCg9yfT6344hhqc6d9t/93kBqQ6j+f580YeevtMeraz9PWmzpfw==", + "dependencies": { + "MdXaml": "1.27.0", + "MdXaml.Plugins": "1.27.0", + "Svg": "3.0.84" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Direct", + "requested": "[9.0.7, )", + "resolved": "9.0.7", + "contentHash": "i05AYA91vgq0as84ROVCyltD2gnxaba/f1Qw2rG7mUsS0gv8cPTr1Gm7jPQHq7JTr4MJoQUcanLVs16tIOUJaQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7" + } + }, + "Microsoft.Extensions.Hosting": { + "type": "Direct", + "requested": "[9.0.7, )", + "resolved": "9.0.7", + "contentHash": "Dkv55VfitwJjPUk9mFHxT9MJAd8su7eJNaCHhBU/Y9xFqw3ZNHwrpeptXeaXiaPtfQq+alMmawIz1Impk5pHkQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.7", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.7", + "Microsoft.Extensions.Configuration.Binder": "9.0.7", + "Microsoft.Extensions.Configuration.CommandLine": "9.0.7", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "9.0.7", + "Microsoft.Extensions.Configuration.FileExtensions": "9.0.7", + "Microsoft.Extensions.Configuration.Json": "9.0.7", + "Microsoft.Extensions.Configuration.UserSecrets": "9.0.7", + "Microsoft.Extensions.DependencyInjection": "9.0.7", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7", + "Microsoft.Extensions.Diagnostics": "9.0.7", + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.7", + "Microsoft.Extensions.FileProviders.Physical": "9.0.7", + "Microsoft.Extensions.Hosting.Abstractions": "9.0.7", + "Microsoft.Extensions.Logging": "9.0.7", + "Microsoft.Extensions.Logging.Abstractions": "9.0.7", + "Microsoft.Extensions.Logging.Configuration": "9.0.7", + "Microsoft.Extensions.Logging.Console": "9.0.7", + "Microsoft.Extensions.Logging.Debug": "9.0.7", + "Microsoft.Extensions.Logging.EventLog": "9.0.7", + "Microsoft.Extensions.Logging.EventSource": "9.0.7", + "Microsoft.Extensions.Options": "9.0.7" + } + }, + "Microsoft.Toolkit.Uwp.Notifications": { + "type": "Direct", + "requested": "[7.1.3, )", + "resolved": "7.1.3", + "contentHash": "A1dglAzb24gjehmb7DwGd07mfyZ1gacAK7ObE0KwDlRc3mayH2QW7cSOy3TkkyELjLg19OQBuhPOj4SpXET9lg==", + "dependencies": { + "Microsoft.Win32.Registry": "4.7.0", + "System.Drawing.Common": "4.7.0", + "System.Reflection.Emit": "4.7.0", + "System.ValueTuple": "4.5.0" + } + }, + "ModernWpfUI": { + "type": "Direct", + "requested": "[0.9.4, )", + "resolved": "0.9.4", + "contentHash": "HJ07Be9KOiGKGcMLz/AwY+84h3yGHRPuYpYXCE6h1yPtaFwGMWfanZ70jX7W5XWx8+Qk1vGox+WGKgxxsy6EHw==" + }, + "PropertyChanged.Fody": { + "type": "Direct", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "6v+f9cI8YjnZH2WBHuOqWEAo8DFFNGFIdU8xD3AsL6fhV6Y8oAmVWd7XKk49t8DpeUBwhR/X+97+6Epvek0Y3A==", + "dependencies": { + "Fody": "6.6.4" + } + }, + "SemanticVersioning": { + "type": "Direct", + "requested": "[3.0.0, )", + "resolved": "3.0.0", + "contentHash": "RR+8GbPQ/gjDqov/1QN1OPoUlbUruNwcL3WjWCeLw+MY7+od/ENhnkYxCfAC6rQLIu3QifaJt3kPYyP3RumqMQ==" + }, + "TaskScheduler": { + "type": "Direct", + "requested": "[2.12.2, )", + "resolved": "2.12.2", + "contentHash": "glpAb3VrwfdAofp6PIyAzL0ZeTV7XUJ8muu0oZoTeyU5jtk2sMJ6QAMRRuFbovcaj+SBJiEUGklxIWOqQoxshA==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0", + "System.Diagnostics.EventLog": "9.0.2", + "System.Security.AccessControl": "6.0.1" + } + }, + "VirtualizingWrapPanel": { + "type": "Direct", + "requested": "[2.3.0, )", + "resolved": "2.3.0", + "contentHash": "Dpmtcpn2HqAWZR0NkN7Qd4YCjf+sdQcemIMKm2suZVbOIB9NsmKZnYaQDIpXWTh87a9+nArVto6Od1cM2ohzCQ==" + }, + "AvalonEdit": { + "type": "Transitive", + "resolved": "6.3.0.90", + "contentHash": "WVTb5MxwGqKdeasd3nG5udlV4t6OpvkFanziwI133K0/QJ5FvZmfzRQgpAjGTJhQfIA8GP7AzKQ3sTY9JOFk8Q==" + }, + "Ben.Demystifier": { + "type": "Transitive", + "resolved": "0.4.1", + "contentHash": "axFeEMfmEORy3ipAzOXG/lE+KcNptRbei3F0C4kQCdeiQtW+qJW90K5iIovITGrdLt8AjhNCwk5qLSX9/rFpoA==", + "dependencies": { + "System.Reflection.Metadata": "5.0.0" + } + }, + "BitFaster.Caching": { + "type": "Transitive", + "resolved": "2.5.4", + "contentHash": "1QroTY1PVCZOSG9FnkkCrmCKk/+bZCgI/YXq376HnYwUDJ4Ho0EaV4YaA/5v5WYLnwIwIO7RZkdWbg9pxIpueQ==" + }, + "DeltaCompressionDotNet": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "nwbZAYd+DblXAIzlnwDSnl0CiCm8jWLfHSYnoN4wYhtIav6AegB3+T/vKzLbU2IZlPB8Bvl8U3NXpx3eaz+N5w==" + }, + "Droplex": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "wutfIus/Ufw/9TDsp86R1ycnIH+wWrj4UhcmrzAHWjsdyC2iM07WEQ9+APTB7pQynsDnYH1r2i58XgAJ3lxUXA==", + "dependencies": { + "YamlDotNet": "9.1.0" + } + }, + "Fizzler": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "jH8KFyDJtgqLl3jZwbwgR3nA9dyebKPBgLwBx0bjPvxvvoCqHPD5IPedRGYzki8RYFpjxCFMlLtnNFPYq2OgmQ==" + }, + "FSharp.Core": { + "type": "Transitive", + "resolved": "9.0.300", + "contentHash": "TVt2J7RCE1KCS2IaONF+p8/KIZ1eHNbW+7qmKF6hGoD4tXl+o07ja1mPtFjMqRa5uHMFaTrGTPn/m945WnDLiQ==" + }, + "HtmlAgilityPack": { + "type": "Transitive", + "resolved": "1.11.42", + "contentHash": "LDc1bEfF14EY2DZzak4xvzWvbpNXK3vi1u0KQbBpLUN4+cx/VrvXhgCAMSJhSU5vz0oMfW9JZIR20vj/PkDHPA==" + }, + "InputSimulator": { + "type": "Transitive", + "resolved": "1.0.4", + "contentHash": "D0LvRCPQMX6/FJHBjng+RO+wRDuHTJrfo7IAc7rmkPvRqchdVGJWg3y70peOtDy3OLNK+HSOwVkH4GiuLnkKgA==" + }, + "JetBrains.Annotations": { + "type": "Transitive", + "resolved": "2024.3.0", + "contentHash": "ox5pkeLQXjvJdyAB4b2sBYAlqZGLh3PjSnP1bQNVx72ONuTJ9+34/+Rq91Fc0dG29XG9RgZur9+NcP4riihTug==" + }, + "MemoryPack": { + "type": "Transitive", + "resolved": "1.21.4", + "contentHash": "wy3JTBNBsO8LfQcBvvYsWr3lm2Oakolrfu0UQ3oSJSEiD+7ye0GUhYTaXuYYBowqsXBXWD9gf2218ae0JRiYVQ==", + "dependencies": { + "MemoryPack.Core": "1.21.4", + "MemoryPack.Generator": "1.21.4" + } + }, + "MemoryPack.Core": { + "type": "Transitive", + "resolved": "1.21.4", + "contentHash": "6RszGorZ0ejNmp37ZcboPBMvvPCuNW2jlrdQfcs/lMzE5b3pmPF6hsm/laDc34hRlbAST1ZxaX/DvYu2DF5sBQ==" + }, + "MemoryPack.Generator": { + "type": "Transitive", + "resolved": "1.21.4", + "contentHash": "g14EsSS85yn0lHTi0J9ivqlZMf09A2iI51fmI+0KkzIzyCbWOBWPi5mdaY7YWmXprk12aYh9u/qfWHQUYthlwg==" + }, + "MessagePack": { + "type": "Transitive", + "resolved": "2.5.192", + "contentHash": "Jtle5MaFeIFkdXtxQeL9Tu2Y3HsAQGoSntOzrn6Br/jrl6c8QmG22GEioT5HBtZJR0zw0s46OnKU8ei2M3QifA==", + "dependencies": { + "MessagePack.Annotations": "2.5.192", + "Microsoft.NET.StringTools": "17.6.3" + } + }, + "MessagePack.Annotations": { + "type": "Transitive", + "resolved": "2.5.192", + "contentHash": "jaJuwcgovWIZ8Zysdyf3b7b34/BrADw4v82GaEZymUhDd3ScMPrYd/cttekeDteJJPXseJxp04yTIcxiVUjTWg==" + }, + "Meziantou.Framework.Win32.Jobs": { + "type": "Transitive", + "resolved": "3.4.3", + "contentHash": "REjInKnQ0OrhjjtSMPQtLtdURctCroB4L8Sd2gjTOYDysklvsdnrStx1tHS7uLv+fSyFF3aazZmo5Ka0v1oz/w==" + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "oxGR51+w5cXm5B9gU6XwpAB2sTiyPSmZm7hjvv0rzRnmL5o/KZzE103AuQj7sK26OBupjVzU/bZxDWvvU4nhEg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.7", + "Microsoft.Extensions.Primitives": "9.0.7" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "lut/kiVvNsQ120VERMUYSFhpXPpKjjql+giy03LesASPBBcC0o6+aoFdzJH9GaYpFTQ3fGVhVjKjvJDoAW5/IQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "9.0.7" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "ExY+zXHhU4o9KC2alp3ZdLWyVWVRSn5INqax5ABk+HEOHlAHzomhJ7ek9HHliyOMiVGoYWYaMFOGr9q59mSAGA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.7" + } + }, + "Microsoft.Extensions.Configuration.CommandLine": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "LqwdkMNFeRyuqExewBSaWj8roEgZH8JQ9zEAmHl5ZFcnhCvjAdHICdYVRIiSEq9RWGB731LL8kZJM8tdTKEscA==", + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.7", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.7" + } + }, + "Microsoft.Extensions.Configuration.EnvironmentVariables": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "R8kgazVpDr4k1K7MeWPLAwsi5VpwrhE3ubXK38D9gpHEvf9XhZhJ8kWHKK00LDg5hJ7pMQLggdZ7XFdQ5182Ug==", + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.7", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.7" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "3LVg32iMfR9ENeegXAo73L+877iOcQauLJsXlKZNVSsLA/HbPgClZdeMGdjLSkaidYw3l02XbXTlOdGYNgu91Q==", + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.7", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.7", + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.7", + "Microsoft.Extensions.FileProviders.Physical": "9.0.7", + "Microsoft.Extensions.Primitives": "9.0.7" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "3HQV326liEInT9UKEc+k73f1ECwNhvDS/DJAe5WvtMKDJTJqTH2ujrUC2ZlK/j6pXyPbV9f0Ku8JB20JveGImg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.7", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.7", + "Microsoft.Extensions.Configuration.FileExtensions": "9.0.7", + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.7" + } + }, + "Microsoft.Extensions.Configuration.UserSecrets": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "ouDuPgRdeF4TJXKUh+lbm6QwyWwnCy+ijiqfFM2cI5NmW83MwKg1WNp2nCdMVcwQW8wJXteF/L9lA6ZPS3bCIQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.7", + "Microsoft.Extensions.Configuration.Json": "9.0.7", + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.7", + "Microsoft.Extensions.FileProviders.Physical": "9.0.7" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "iPK1FxbGFr2Xb+4Y+dTYI8Gupu9pOi8I3JPuPsrogUmEhe2hzZ9LpCmolMEBhVDo2ikcSr7G5zYiwaapHSQTew==" + }, + "Microsoft.Extensions.Diagnostics": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "6ykfInm6yw7pPHJACgnrPUXxUWVslFnzad44K/siXk6Ovan6fNMnXxI5X9vphHJuZ4JbMOdPIgsfTmLD+Dyxug==", + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.7", + "Microsoft.Extensions.Diagnostics.Abstractions": "9.0.7", + "Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.7" + } + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "d39Ov1JpeWCGLCOTinlaDkujhrSAQ0HFxb7Su1BjhCKBfmDcQ6Ia1i3JI6kd3NFgwi1dexTunu82daDNwt7E6w==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7", + "Microsoft.Extensions.Options": "9.0.7" + } + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "y9djCca1cz/oz/J8jTxtoecNiNvaiGBJeWd7XOPxonH+FnfHqcfslJMcSr5JMinmWFyS7eh3C9L6m6oURZ5lSA==", + "dependencies": { + "Microsoft.Extensions.Primitives": "9.0.7" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "JYEPYrb+YBpFTCdmSBrk8cg3wAi1V4so7ccq04qbhg3FQHQqgJk28L3heEOKMXcZobOBUjTnGCFJD49Ez9kG5w==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.7", + "Microsoft.Extensions.FileSystemGlobbing": "9.0.7", + "Microsoft.Extensions.Primitives": "9.0.7" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "5VKpTH2ME0SSs0lrtkpKgjCeHzXR5ka/H+qThPwuWi78wHubApZ/atD7w69FDt0OOM7UMV6LIbkqEQgoby4IXA==" + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "yG2JCXAR+VqI1mKqynLPNJlNlrUJeEISEpX4UznOp2uM4IEFz3pDDauzyMvTjICutEJtOigJ1yWBvxbaIlibBw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.7", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7", + "Microsoft.Extensions.Diagnostics.Abstractions": "9.0.7", + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.7", + "Microsoft.Extensions.Logging.Abstractions": "9.0.7" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "fdIeQpXYV8yxSWG03cCbU2Otdrq4NWuhnQLXokWLv3L9YcK055E7u8WFJvP+uuP4CFeCEoqZQL4yPcjuXhCZrg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "9.0.7", + "Microsoft.Extensions.Logging.Abstractions": "9.0.7", + "Microsoft.Extensions.Options": "9.0.7" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "sMM6NEAdUTE/elJ2wqjOi0iBWqZmSyaTByLF9e8XHv6DRJFFnOe0N+s8Uc6C91E4SboQCfLswaBIZ+9ZXA98AA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7" + } + }, + "Microsoft.Extensions.Logging.Configuration": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "AEBty9rvFGvdFRqgIDEhQmiCnIfQWyzVoOZrO244cfu+n9M+wI1QLDpuROVILlplIBtLVmOezAF7d1H3Qog6Xw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.7", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.7", + "Microsoft.Extensions.Configuration.Binder": "9.0.7", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7", + "Microsoft.Extensions.Logging": "9.0.7", + "Microsoft.Extensions.Logging.Abstractions": "9.0.7", + "Microsoft.Extensions.Options": "9.0.7", + "Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.7" + } + }, + "Microsoft.Extensions.Logging.Console": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "pEHlNa8iCfKsBFA3YVDn/8EicjSU/m8uDfyoR0i4svONDss4Yu9Kznw53E/TyI+TveTo7CwRid4kfd4pLYXBig==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7", + "Microsoft.Extensions.Logging": "9.0.7", + "Microsoft.Extensions.Logging.Abstractions": "9.0.7", + "Microsoft.Extensions.Logging.Configuration": "9.0.7", + "Microsoft.Extensions.Options": "9.0.7" + } + }, + "Microsoft.Extensions.Logging.Debug": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "MxzZj7XbsYJwfjclVTjJym2/nVIkksu7l7tC/4HYy+YRdDmpE4B+hTzCXu3BNfLNhdLPZsWpyXuYe6UGgWDm3g==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7", + "Microsoft.Extensions.Logging": "9.0.7", + "Microsoft.Extensions.Logging.Abstractions": "9.0.7" + } + }, + "Microsoft.Extensions.Logging.EventLog": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "usrMVsY7c8M8fESt34Y3eEIQIlRlKXfPDlI+vYEb6xT7SUjhua2ey3NpHgQktiTgz8Uo5RiWqGD8ieiyo2WaDA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7", + "Microsoft.Extensions.Logging": "9.0.7", + "Microsoft.Extensions.Logging.Abstractions": "9.0.7", + "Microsoft.Extensions.Options": "9.0.7", + "System.Diagnostics.EventLog": "9.0.7" + } + }, + "Microsoft.Extensions.Logging.EventSource": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "/wwi6ckTEegCExFV6gVToCO7CvysZnmE50fpdkYUsSMh0ue9vRkQ7uOqkHyHol93ASYTEahrp+guMtS/+fZKaA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7", + "Microsoft.Extensions.Logging": "9.0.7", + "Microsoft.Extensions.Logging.Abstractions": "9.0.7", + "Microsoft.Extensions.Options": "9.0.7", + "Microsoft.Extensions.Primitives": "9.0.7" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "trJnF6cRWgR5uMmHpGoHmM1wOVFdIYlELlkO9zX+RfieK0321Y55zrcs4AaEymKup7dxgEN/uJU25CAcMNQRXw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7", + "Microsoft.Extensions.Primitives": "9.0.7" + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "pE/jeAWHEIy/8HsqYA+I1+toTsdvsv+WywAcRoNSvPoFwjOREa8Fqn7D0/i0PbiXsDLFupltTTctliePx8ib4w==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.7", + "Microsoft.Extensions.Configuration.Binder": "9.0.7", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7", + "Microsoft.Extensions.Options": "9.0.7", + "Microsoft.Extensions.Primitives": "9.0.7" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "ti/zD9BuuO50IqlvhWQs9GHxkCmoph5BHjGiWKdg2t6Or8XoyAfRJiKag+uvd/fpASnNklfsB01WpZ4fhAe0VQ==" + }, + "Microsoft.IO.RecyclableMemoryStream": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "s/s20YTVY9r9TPfTrN5g8zPF1YhwxyqO6PxUkrYTGI2B+OGPe9AdajWZrLhFqXIvqIW23fnUE4+ztrUWNU1+9g==" + }, + "Microsoft.NET.StringTools": { + "type": "Transitive", + "resolved": "17.6.3", + "contentHash": "N0ZIanl1QCgvUumEL1laasU0a7sOE5ZwLZVTn0pAePnfhq8P7SvTjF8Axq+CnavuQkmdQpGNXQ1efZtu5kDFbA==" + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + }, + "Microsoft.NETCore.Targets": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + }, + "Microsoft.VisualStudio.Threading": { + "type": "Transitive", + "resolved": "17.14.15", + "contentHash": "1DrCusT3xNLSlaJg77BsUSAzrhjdZBAvvsS0PMzyPM+fGais6SnISOhqdZQop8VVMIBLsYm2gyF9W7THjgavwA==", + "dependencies": { + "Microsoft.VisualStudio.Threading.Analyzers": "17.14.15", + "Microsoft.VisualStudio.Threading.Only": "17.14.15", + "Microsoft.VisualStudio.Validation": "17.8.8" + } + }, + "Microsoft.VisualStudio.Threading.Analyzers": { + "type": "Transitive", + "resolved": "17.14.15", + "contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw==" + }, + "Microsoft.VisualStudio.Threading.Only": { + "type": "Transitive", + "resolved": "17.14.15", + "contentHash": "NqONyw1RXyj9P3k5e1uU2k9kc1ptwuU5NJQzG+MPq7vQVHUzBY8HLuJf/N2Rw5H/myD96CVxziDxmjawPuzntw==", + "dependencies": { + "Microsoft.VisualStudio.Validation": "17.8.8" + } + }, + "Microsoft.VisualStudio.Validation": { + "type": "Transitive", + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "Microsoft.Win32.SystemEvents": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "2nXPrhdAyAzir0gLl8Yy8S5Mnm/uBSQQA7jEsILOS1MTyS7DbmV1NgViMtvV1sfCD1ebITpNwb1NIinKeJgUVQ==" + }, + "Mono.Cecil": { + "type": "Transitive", + "resolved": "0.9.6.1", + "contentHash": "yMsurNaOxxKIjyW9pEB+tRrR1S3DFnN1+iBgKvYvXG8kW0Y6yknJeMAe/tl3+P78/2C6304TgF7aVqpqXgEQ9Q==" + }, + "Nerdbank.Streams": { + "type": "Transitive", + "resolved": "2.12.87", + "contentHash": "oDKOeKZ865I5X8qmU3IXMyrAnssYEiYWTobPGdrqubN3RtTzEHIv+D6fwhdcfrdhPJzHjCkK/ORztR/IsnmA6g==", + "dependencies": { + "Microsoft.VisualStudio.Threading.Only": "17.13.61", + "Microsoft.VisualStudio.Validation": "17.8.8", + "System.IO.Pipelines": "8.0.0" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "NHotkey": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "IEghs0QqWsQYH0uUmvIl0Ye6RaebWRh38eB6ToOkDnQucTYRGFOgtig0gSxlwCszTilYFz3n1ZuY762x+kDR3A==" + }, + "NHotkey.Wpf": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "BIUKlhTG5KtFf9OQzWvkmVmktt5/FFj6AOEgag8Uf0R2YdZt5ajUzs3sVskcJcT2TztWlEHKQr1jFj3KQ0D9Nw==", + "dependencies": { + "NHotkey": "3.0.0" + } + }, + "NLog": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "qDWiqy8/xdpZKtHna/645KbalwP86N2NFJEzfqhcv+Si4V2iNaEfR/dCneuF/4+Dcwl3f7jHMXj3ndWYftV3Ug==" + }, + "NLog.OutputDebugString": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "wwJCQLaHVzuRf8TsXB+EEdrzVvE3dnzCSMQMDgwkw3AXp8VSp3JSVF/Q/H0oEqggKgKhPs13hh3a7svyQr4s3A==", + "dependencies": { + "NLog": "6.0.1" + } + }, + "runtime.osx.10.10-x64.CoreCompat.System.Drawing": { + "type": "Transitive", + "resolved": "5.8.64", + "contentHash": "Ey7xQgWwixxdrmhzEUvaR4kxZDSQMWQScp8ViLvmL5xCBKG6U3TaMv/jzHilpfQXpHmJ4IylKGzzMvnYX2FwHQ==" + }, + "SharpVectors.Wpf": { + "type": "Transitive", + "resolved": "1.8.4.2", + "contentHash": "PNxLkMBJnV8A+6yH9OqOlhLJegvWP/dvh0rAJp2l0kcrR+rB4R2tQ9vhUqka+UilH4atN8T6zvjDOizVyfz2Ng==" + }, + "Splat": { + "type": "Transitive", + "resolved": "1.6.2", + "contentHash": "DeH0MxPU+D4JchkIDPYG4vUT+hsWs9S41cFle0/4K5EJMXWurx5DzAkj2366DfK14/XKNhsu6tCl4dZXJ3CD4w==" + }, + "squirrel.windows": { + "type": "Transitive", + "resolved": "1.5.2", + "contentHash": "89Y/CFxWm7SEOjvuV2stVa8p+SNM9GOLk4tUNm2nUF792nfkimAgwRA/umVsdyd/OXBH8byXSh4V1qck88ZAyQ==", + "dependencies": { + "DeltaCompressionDotNet": "[1.0.0, 2.0.0)", + "Mono.Cecil": "0.9.6.1", + "Splat": "1.6.2" + } + }, + "StreamJsonRpc": { + "type": "Transitive", + "resolved": "2.22.11", + "contentHash": "TQcqBFswLNpdSJANjhxZmIIe0Yl0kGqzjZ+uHLdhrkxntofvNu6C53XPEEYQ3Wkj8AorKumkuv/VMvTH4BHOZw==", + "dependencies": { + "MessagePack": "2.5.192", + "Microsoft.VisualStudio.Threading.Only": "17.13.61", + "Microsoft.VisualStudio.Validation": "17.8.8", + "Nerdbank.Streams": "2.12.87", + "Newtonsoft.Json": "13.0.3", + "System.IO.Pipelines": "8.0.0" + } + }, + "Svg": { + "type": "Transitive", + "resolved": "3.0.84", + "contentHash": "QI35/+zRerIuOTBAw0GhbEQhuesSd3nYha9ibgyv6ofe4XRpFSjaXyQJJGssaUVZaBb2vwMAFKqb5uWnTAB2Sg==", + "dependencies": { + "Fizzler": "1.1.0", + "System.Drawing.Common": "4.5.1", + "System.ObjectModel": "4.3.0", + "runtime.osx.10.10-x64.CoreCompat.System.Drawing": "5.8.64" + } + }, + "System.Collections": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Diagnostics.Debug": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "9.0.7", + "contentHash": "AJ+9fyCtQUImntxAJ9l4PZiCd4iepuk4pm7Qcno7PBIWQnfXlvwKuFsGk2H+QyY69GUVzDP2heELW6ho5BCXUg==" + }, + "System.Drawing.Common": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "KIX+oBU38pxkKPxvLcLfIkOV5Ien8ReN78wro7OF5/erwcmortzeFx+iBswlh2Vz6gVne0khocQudGwaO1Ey6A==", + "dependencies": { + "Microsoft.Win32.SystemEvents": "7.0.0" + } + }, + "System.Globalization": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "FHNOatmUq0sqJOkTx+UF/9YK1f180cnW5FVqnQMvYUN0elp6wFzbtPSiqbo1/ru8ICp43JM1i7kKkk6GsNGHlA==" + }, + "System.ObjectModel": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Reflection": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Emit": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VR4kk8XLKebQ4MZuKuIni/7oh+QGFmZW3qORd1GvBq/8026OpW501SzT/oypwiQl4TvT8ErnReh/NzY9u+C6wQ==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==" + }, + "System.Reflection.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Resources.ResourceManager": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Globalization": "4.3.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "IQ4NXP/B3Ayzvw0rDQzVTYsCKyy0Jp9KI6aYcK7UnGVlR9+Awz++TIPCQtPYfLJfOpm8ajowMR09V7quD3sEHw==" + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Threading": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", + "dependencies": { + "System.Runtime": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.Threading.Tasks": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "ToolGood.Words.Pinyin": { + "type": "Transitive", + "resolved": "3.1.0.3", + "contentHash": "VKcf8sUq/+LyY99WgLhOu7Q32ROEyR30/2xCCj9ADRi45wVC7kpXrYCf9vH1qirkmrIfpL8inoxAbrqAlfXxsQ==" + }, + "WpfAnimatedGif": { + "type": "Transitive", + "resolved": "2.0.2", + "contentHash": "B0j9SqtThyHVTiOPvu6yR+39Te0g3o+7Jjb+qEm7+Iz1HRqbE5/4QV+ntHWOYYBPOUFr9x1mdzGl/EzWP+nKiA==" + }, + "YamlDotNet": { + "type": "Transitive", + "resolved": "9.1.0", + "contentHash": "fuvGXU4Ec5HrsmEc+BiFTNPCRf1cGBI2kh/3RzMWgddM2M4ALhbSPoI3X3mhXZUD1qqQd9oSkFAtWjpz8z9eRg==" + }, + "flow.launcher.core": { + "type": "Project", + "dependencies": { + "Droplex": "[1.7.0, )", + "FSharp.Core": "[9.0.300, )", + "Flow.Launcher.Infrastructure": "[1.0.0, )", + "Flow.Launcher.Plugin": "[4.7.0, )", + "Meziantou.Framework.Win32.Jobs": "[3.4.3, )", + "Microsoft.IO.RecyclableMemoryStream": "[3.0.1, )", + "SemanticVersioning": "[3.0.0, )", + "StreamJsonRpc": "[2.22.11, )", + "squirrel.windows": "[1.5.2, )" + } + }, + "flow.launcher.infrastructure": { + "type": "Project", + "dependencies": { + "Ben.Demystifier": "[0.4.1, )", + "BitFaster.Caching": "[2.5.4, )", + "CommunityToolkit.Mvvm": "[8.4.0, )", + "Flow.Launcher.Plugin": "[4.7.0, )", + "InputSimulator": "[1.0.4, )", + "MemoryPack": "[1.21.4, )", + "Microsoft.VisualStudio.Threading": "[17.14.15, )", + "NHotkey.Wpf": "[3.0.0, )", + "NLog": "[6.0.1, )", + "NLog.OutputDebugString": "[6.0.1, )", + "SharpVectors.Wpf": "[1.8.4.2, )", + "System.Drawing.Common": "[7.0.0, )", + "ToolGood.Words.Pinyin": "[3.1.0.3, )" + } + }, + "flow.launcher.plugin": { + "type": "Project", + "dependencies": { + "JetBrains.Annotations": "[2024.3.0, )" + } + } + } + } +} \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs index 6e6b2e5f4..b11661466 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; @@ -45,7 +45,7 @@ public abstract class ChromiumBookmarkLoader : IBookmarkLoader } catch (Exception ex) { - Main._context.API.LogException(ClassName, $"Failed to register bookmark file monitoring: {bookmarkPath}", ex); + Main.Context.API.LogException(ClassName, $"Failed to register bookmark file monitoring: {bookmarkPath}", ex); continue; } @@ -58,7 +58,7 @@ public abstract class ChromiumBookmarkLoader : IBookmarkLoader var faviconDbPath = Path.Combine(profile, "Favicons"); if (File.Exists(faviconDbPath)) { - Main._context.API.StopwatchLogInfo(ClassName, $"Load {profileBookmarks.Count} favicons cost", () => + Main.Context.API.StopwatchLogInfo(ClassName, $"Load {profileBookmarks.Count} favicons cost", () => { LoadFaviconsFromDb(faviconDbPath, profileBookmarks); }); @@ -125,7 +125,7 @@ public abstract class ChromiumBookmarkLoader : IBookmarkLoader } else { - Main._context.API.LogError(ClassName, $"type property not found for {subElement.GetString()}"); + Main.Context.API.LogError(ClassName, $"type property not found for {subElement.GetString()}"); } } } @@ -190,7 +190,7 @@ public abstract class ChromiumBookmarkLoader : IBookmarkLoader } catch (Exception ex) { - Main._context.API.LogException(ClassName, $"Failed to extract bookmark favicon: {bookmark.Url}", ex); + Main.Context.API.LogException(ClassName, $"Failed to extract bookmark favicon: {bookmark.Url}", ex); } finally { diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Commands/BookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Commands/BookmarkLoader.cs index 758ce68ae..b76adae93 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Commands/BookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Commands/BookmarkLoader.cs @@ -9,11 +9,11 @@ internal static class BookmarkLoader { internal static MatchResult MatchProgram(Bookmark bookmark, string queryString) { - var match = Main._context.API.FuzzySearch(queryString, bookmark.Name); + var match = Main.Context.API.FuzzySearch(queryString, bookmark.Name); if (match.IsSearchPrecisionScoreMet()) return match; - return Main._context.API.FuzzySearch(queryString, bookmark.Url); + return Main.Context.API.FuzzySearch(queryString, bookmark.Url); } internal static List LoadAllBookmarks(Settings setting) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index ec3b867ea..be83f6158 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -1,7 +1,8 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; +using System.IO.Compression; using System.Linq; using System.Threading.Tasks; using Flow.Launcher.Plugin.BrowserBookmark.Helper; @@ -49,7 +50,7 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader } catch (Exception ex) { - Main._context.API.LogException(ClassName, $"Failed to register Firefox bookmark file monitoring: {placesPath}", ex); + Main.Context.API.LogException(ClassName, $"Failed to register Firefox bookmark file monitoring: {placesPath}", ex); return bookmarks; } @@ -84,7 +85,7 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader var faviconDbPath = Path.Combine(Path.GetDirectoryName(placesPath), "favicons.sqlite"); if (File.Exists(faviconDbPath)) { - Main._context.API.StopwatchLogInfo(ClassName, $"Load {bookmarks.Count} favicons cost", () => + Main.Context.API.StopwatchLogInfo(ClassName, $"Load {bookmarks.Count} favicons cost", () => { LoadFaviconsFromDb(faviconDbPath, bookmarks); }); @@ -98,7 +99,7 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader } catch (Exception ex) { - Main._context.API.LogException(ClassName, $"Failed to load Firefox bookmarks: {placesPath}", ex); + Main.Context.API.LogException(ClassName, $"Failed to load Firefox bookmarks: {placesPath}", ex); } // Delete temporary file @@ -111,7 +112,7 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader } catch (Exception ex) { - Main._context.API.LogException(ClassName, $"Failed to delete temporary favicon DB: {tempDbPath}", ex); + Main.Context.API.LogException(ClassName, $"Failed to delete temporary favicon DB: {tempDbPath}", ex); } return bookmarks; @@ -134,10 +135,6 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader try { - if (string.IsNullOrEmpty(bookmark.Url)) - return; - - // Extract domain from URL if (!Uri.TryCreate(bookmark.Url, UriKind.Absolute, out Uri uri)) return; @@ -146,47 +143,53 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader // Query for latest Firefox version favicon structure using var cmd = connection.CreateCommand(); cmd.CommandText = @" - SELECT i.data + SELECT i.id, i.data FROM moz_icons i JOIN moz_icons_to_pages ip ON i.id = ip.icon_id JOIN moz_pages_w_icons p ON ip.page_id = p.id - WHERE p.page_url LIKE @url - AND i.data IS NOT NULL - ORDER BY i.width DESC -- Select largest icon available + WHERE p.page_url LIKE @domain + ORDER BY i.width DESC LIMIT 1"; - cmd.Parameters.AddWithValue("@url", $"%{domain}%"); + cmd.Parameters.AddWithValue("@domain", $"%{domain}%"); using var reader = cmd.ExecuteReader(); - if (!reader.Read() || reader.IsDBNull(0)) + if (!reader.Read() || reader.IsDBNull(1)) return; + var iconId = reader.GetInt64(0).ToString(); var imageData = (byte[])reader["data"]; if (imageData is not { Length: > 0 }) return; - string faviconPath; - if (FaviconHelper.IsSvgData(imageData)) + // Check if the image data is compressed (GZip) + if (imageData.Length > 2 && imageData[0] == 0x1f && imageData[1] == 0x8b) { - faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}.svg"); - } - else - { - faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}.png"); + using var inputStream = new MemoryStream(imageData); + using var gZipStream = new GZipStream(inputStream, CompressionMode.Decompress); + using var outputStream = new MemoryStream(); + gZipStream.CopyTo(outputStream); + imageData = outputStream.ToArray(); } - // Filter out duplicate favicons - if (savedPaths.TryAdd(faviconPath, true)) + // Convert the image data to WebP format + var webpData = FaviconHelper.TryConvertToWebp(imageData); + if (webpData != null) { - FaviconHelper.SaveBitmapData(imageData, faviconPath); - } + var faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}_{iconId}.webp"); - bookmark.FaviconPath = faviconPath; + if (savedPaths.TryAdd(faviconPath, true)) + { + FaviconHelper.SaveBitmapData(webpData, faviconPath); + } + + bookmark.FaviconPath = faviconPath; + } } catch (Exception ex) { - Main._context.API.LogException(ClassName, $"Failed to extract Firefox favicon: {bookmark.Url}", ex); + Main.Context.API.LogException(ClassName, $"Failed to extract Firefox favicon: {bookmark.Url}", ex); } finally { diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj index 49ae26564..901dc2a37 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj @@ -2,7 +2,7 @@ Library - net7.0-windows + net9.0-windows true {9B130CC5-14FB-41FF-B310-0A95B6894C37} Properties @@ -45,14 +45,18 @@ $(OutputPath)runtimes\linux-musl-arm; $(OutputPath)runtimes\linux-musl-arm64; $(OutputPath)runtimes\linux-musl-x64; + $(OutputPath)runtimes\linux-musl-s390x; $(OutputPath)runtimes\linux-ppc64le; $(OutputPath)runtimes\linux-s390x; $(OutputPath)runtimes\linux-x64; $(OutputPath)runtimes\linux-x86; $(OutputPath)runtimes\maccatalyst-arm64; $(OutputPath)runtimes\maccatalyst-x64; + $(OutputPath)runtimes\osx; $(OutputPath)runtimes\osx-arm64; - $(OutputPath)runtimes\osx-x64"/> + $(OutputPath)runtimes\osx-x64; + $(OutputPath)runtimes\win-arm; + $(OutputPath)runtimes\win-arm64;"/> @@ -64,14 +68,18 @@ $(PublishDir)runtimes\linux-musl-arm; $(PublishDir)runtimes\linux-musl-arm64; $(PublishDir)runtimes\linux-musl-x64; + $(PublishDir)runtimes\linux-musl-s390x; $(PublishDir)runtimes\linux-ppc64le; $(PublishDir)runtimes\linux-s390x; $(PublishDir)runtimes\linux-x64; $(PublishDir)runtimes\linux-x86; $(PublishDir)runtimes\maccatalyst-arm64; $(PublishDir)runtimes\maccatalyst-x64; + $(PublishDir)runtimes\osx; $(PublishDir)runtimes\osx-arm64; - $(PublishDir)runtimes\osx-x64"/> + $(PublishDir)runtimes\osx-x64; + $(PublishDir)runtimes\win-arm; + $(PublishDir)runtimes\win-arm64;"/> @@ -81,7 +89,6 @@ - @@ -95,7 +102,11 @@ - + + + + + diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs index a879dcefd..1820a7836 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs @@ -1,5 +1,7 @@ -using System; +using System; using System.IO; +using SkiaSharp; +using Svg.Skia; namespace Flow.Launcher.Plugin.BrowserBookmark.Helper; @@ -27,9 +29,9 @@ public static class FaviconHelper } catch (Exception ex1) { - Main._context.API.LogException(ClassName, $"Failed to delete temporary favicon DB: {tempDbPath}", ex1); + Main.Context.API.LogException(ClassName, $"Failed to delete temporary favicon DB: {tempDbPath}", ex1); } - Main._context.API.LogException(ClassName, $"Failed to copy favicon DB: {dbPath}", ex); + Main.Context.API.LogException(ClassName, $"Failed to copy favicon DB: {dbPath}", ex); return; } @@ -39,7 +41,7 @@ public static class FaviconHelper } catch (Exception ex) { - Main._context.API.LogException(ClassName, $"Failed to connect to SQLite: {tempDbPath}", ex); + Main.Context.API.LogException(ClassName, $"Failed to connect to SQLite: {tempDbPath}", ex); } // Delete temporary file @@ -49,7 +51,7 @@ public static class FaviconHelper } catch (Exception ex) { - Main._context.API.LogException(ClassName, $"Failed to delete temporary favicon DB: {tempDbPath}", ex); + Main.Context.API.LogException(ClassName, $"Failed to delete temporary favicon DB: {tempDbPath}", ex); } } @@ -61,16 +63,62 @@ public static class FaviconHelper } catch (Exception ex) { - Main._context.API.LogException(ClassName, $"Failed to save image: {outputPath}", ex); + Main.Context.API.LogException(ClassName, $"Failed to save image: {outputPath}", ex); } } - public static bool IsSvgData(byte[] data) + public static byte[] TryConvertToWebp(byte[] data) { - if (data.Length < 5) - return false; - string start = System.Text.Encoding.ASCII.GetString(data, 0, Math.Min(100, data.Length)); - return start.Contains("إشارات المتصفح ابحث في إشارات المتصفح + + Failed to set url in clipboard + بيانات الإشارات المرجعية فتح الإشارات المرجعية في: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/cs.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/cs.xaml index 45f8d97da..382418336 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/cs.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/cs.xaml @@ -5,6 +5,9 @@ Záložky prohlížeče Hledat záložky v prohlížeči + + Failed to set url in clipboard + Data záložek Otevřít záložky v: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/da.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/da.xaml index 153b05d76..68a8b7a66 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/da.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/da.xaml @@ -5,6 +5,9 @@ Browser Bookmarks Search your browser bookmarks + + Failed to set url in clipboard + Bookmark Data Open bookmarks in: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/de.xaml index 66e30855f..68bb92411 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/de.xaml @@ -5,6 +5,9 @@ Browser-Lesezeichen Ihre Browser-Lesezeichen durchsuchen + + URL in Zwischenablage konnte nicht festgelegt werden + Lesezeichen-Daten Lesezeichen öffnen in: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml index 22830e7c8..564714173 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml @@ -7,6 +7,9 @@ Browser Bookmarks Search your browser bookmarks + + Failed to set url in clipboard + Bookmark Data Open bookmarks in: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/es-419.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/es-419.xaml index 28524229b..859a0ea0e 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/es-419.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/es-419.xaml @@ -5,6 +5,9 @@ Marcadores del Navegador Busca en los marcadores de tu navegador + + Failed to set url in clipboard + Datos de Marcadores Abrir marcadores en: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/es.xaml index ba9efd7e0..e0beb7a78 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/es.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/es.xaml @@ -5,6 +5,9 @@ Marcadores del navegador Busca en los marcadores del navegador + + No se ha podido establecer la url en el portapeles + Datos del marcador Abrir marcadores en: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/fr.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/fr.xaml index 39546c102..de10d0f19 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/fr.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/fr.xaml @@ -5,6 +5,9 @@ Favoris du Navigateur Rechercher dans les favoris de votre navigateur + + Impossible de mettre l'url dans le presse-papiers + Données des favoris Ouvrir les favoris dans : diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/he.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/he.xaml index 79490928d..90751bb6f 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/he.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/he.xaml @@ -5,6 +5,9 @@ סימניות דפדפן חפש בסימניות הדפדפן שלך + + Failed to set url in clipboard + נתוני סימניות פתח סימניות ב: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/it.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/it.xaml index eb13bf852..32216f350 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/it.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/it.xaml @@ -5,6 +5,9 @@ Segnalibri del Browser Cerca nei segnalibri del tuo browser + + Failed to set url in clipboard + Dati del segnalibro Apri preferiti in: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ja.xaml index d08d67d98..6700bce19 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ja.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ja.xaml @@ -5,6 +5,9 @@ ブラウザブックマーク ブラウザのブックマークを検索します + + Failed to set url in clipboard + Bookmark Data Open bookmarks in: @@ -14,17 +17,17 @@ Choose URLをコピー ブックマークのURLをクリップボードにコピー - Load Browser From: - Browser Name - Data Directory Path + 次のブラウザから読み込む: + ブラウザ名 + データフォルダのパス 追加 編集 削除 - Browse + 選択 その他のブラウザ - Browser Engine - If you are not using Chrome, Firefox or Edge, or you are using their portable version, you need to add bookmarks data directory and select correct browser engine to make this plugin work. - For example: Brave's engine is Chromium; and its default bookmarks data location is: "%LOCALAPPDATA%\BraveSoftware\Brave-Browser\UserData". For Firefox engine, the bookmarks directory is the userdata folder contains the places.sqlite file. - Load favicons (can be time consuming during startup) + ブラウザー エンジン + Chrome、Firefox、Edgeを使用していない場合、またはそれらのポータブル版を使用している場合、 このプラグインを動作させるには、ブックマークデータのフォルダを指定し、正しいブラウザエンジンを選択する必要があります。 + 例: BraveのエンジンはChromiumで、デフォルトのブックマークデータは以下の場所にあります: "%LOCALAPPDATA%\BraveSoftware\Brave-Browser\UserData"。 Firefox エンジンの場合、bookmarks ディレクトリは userdata フォルダにあって、places.sqlite ファイルが格納されています。 + ファビコンを読み込む (起動時に時間がかかる場合があります) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ko.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ko.xaml index bb7c9fc06..fd381d4ee 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ko.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ko.xaml @@ -5,6 +5,9 @@ 브라우저 북마크 브라우저의 북마크 검색 + + Failed to set url in clipboard + 북마크 데이터 Open bookmarks in: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/nb.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/nb.xaml index 1c53a49d2..e3650ed26 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/nb.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/nb.xaml @@ -5,6 +5,9 @@ Nettleserbokmerker Søk i nettleserbokmerker + + Failed to set url in clipboard + Bokmerkedata Åpne bokmerker i: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/nl.xaml index 407786cdd..c83c2ec78 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/nl.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/nl.xaml @@ -5,6 +5,9 @@ Browser Bookmarks Search your browser bookmarks + + Failed to set url in clipboard + Bookmark Data Open bookmarks in: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pl.xaml index 9f92d86b1..b63c2ffc5 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pl.xaml @@ -5,6 +5,9 @@ Zakładki przeglądarki Przeszukaj zakładki przeglądarki + + Failed to set url in clipboard + Dane zakładek Otwórz zakładki w: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pt-br.xaml index ce264bc5f..c6ec5eb4c 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pt-br.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pt-br.xaml @@ -5,6 +5,9 @@ Favoritos do Navegador Pesquisar favoritos do seu navegador + + Failed to set url in clipboard + Dados de Favoritos Abrir favoritos em: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pt-pt.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pt-pt.xaml index 2818a0600..f2b3e93d1 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pt-pt.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pt-pt.xaml @@ -5,6 +5,9 @@ Marcadores do navegador Pesquisar nos marcadores do navegador + + Falha ao definir o URL na área de transferência + Dados do marcador Abrir marcadores em: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ru.xaml index bb8639d97..b147c083b 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ru.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ru.xaml @@ -5,6 +5,9 @@ Закладки браузера Поиск закладок в браузере + + Failed to set url in clipboard + Данные закладок Открыть закладки в: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/sk.xaml index c7556e877..1d498a5fd 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/sk.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/sk.xaml @@ -5,6 +5,9 @@ Záložky prehliadača Vyhľadáva záložky prehliadača + + Nepodarilo sa nastaviť URL v schránke + Nastavenia pluginu Otvoriť záložky v: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/sr-Cyrl-RS.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/sr-Cyrl-RS.xaml new file mode 100644 index 000000000..02b995517 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/sr-Cyrl-RS.xaml @@ -0,0 +1,33 @@ + + + + + Browser Bookmarks + Search your browser bookmarks + + + Failed to set url in clipboard + + + Bookmark Data + Open bookmarks in: + New window + New tab + Set browser from path: + Choose + Copy url + Copy the bookmark's url to clipboard + Load Browser From: + Browser Name + Data Directory Path + Add + Edit + Delete + Browse + Others + Browser Engine + If you are not using Chrome, Firefox or Edge, or you are using their portable version, you need to add bookmarks data directory and select correct browser engine to make this plugin work. + For example: Brave's engine is Chromium; and its default bookmarks data location is: "%LOCALAPPDATA%\BraveSoftware\Brave-Browser\UserData". For Firefox engine, the bookmarks directory is the userdata folder contains the places.sqlite file. + Load favicons (can be time consuming during startup) + + diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/sr.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/sr.xaml index 84173e616..5a7a18714 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/sr.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/sr.xaml @@ -5,6 +5,9 @@ Browser Bookmarks Search your browser bookmarks + + Failed to set url in clipboard + Bookmark Data Open bookmarks in: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/tr.xaml index 8c4280f63..15cd4f250 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/tr.xaml @@ -5,6 +5,9 @@ Yer İmleri Tarayıcınızdaki yer işaretlerini arayın + + Panoya url ayarlanamadı + Yer İmleri Verisi Yer imlerini şurada aç: @@ -25,6 +28,6 @@ Tarayıcı Motoru Eğer Chrome, Firefox veya Edge kullanmıyor veya bu tarayıcıların taşınabilir sürümlerini kullanıyorsanız tarayıcı motorunu ve yer imlerinin saklandığı dizini elle girmeniz gerekir. Örneğin: Brave tarayıcısı Chromium tabanlıdır ve yer imleri varsayılan olarak "%LOCALAPPDATA%\BraveSoftware\Brave-Browser\UserData" klasöründe saklanır. Firefox tabanlı tarayıcılar için bu places.sqlite dosyasının bulunduğu kullanıcı verisi klasörüdür. - Load favicons (can be time consuming during startup) + Site simgelerini yükle (başlangıç sırasında zaman alabilir) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/uk-UA.xaml index 07ccc2ea4..a40370a9c 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/uk-UA.xaml @@ -5,6 +5,9 @@ Закладки браузера Пошук у закладках браузера + + Не вдалося вставити Url-адресу в буфер обміну + Дані закладок Відкрити закладки в: @@ -24,7 +27,7 @@ Інші Браузерний рушій Якщо ви не використовуєте Chrome, Firefox або Edge, або використовуєте їхні портативні версії, вам потрібно додати каталог даних закладок і вибрати правильний рушій браузера, щоб цей плагін працював. - Наприклад: Рушій Brave - Chromium, і за замовчуванням розташування даних закладок: "%LOCALAPPDATA%\BraveSoftware\Brave-Browser\UserData". Для браузера Firefox директорія закладок - це папка userdata, що містить файл places.sqlite. + Наприклад: рушій Brave — Chromium, і типово розташування даних закладок: «%LOCALAPPDATA%\BraveSoftware\Brave-Browser\UserData». Для браузера Firefox директорія закладок — це тека userdata, що містить файл places.sqlite. Завантажити піктограми (може зайняти багато часу під час запуску) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/vi.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/vi.xaml index 662c87d49..f52a7fe7b 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/vi.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/vi.xaml @@ -5,6 +5,9 @@ Dấu trang trình duyệt Tìm kiếm dấu trang trình duyệt của bạn + + Failed to set url in clipboard + Dữ liệu đánh dấu Mở dấu trang trong: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/zh-cn.xaml index 934544367..778cd3992 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/zh-cn.xaml @@ -5,6 +5,9 @@ 浏览器书签 搜索您的浏览器书签 + + 无法复制 URL 到剪切板 + 书签数据 在以下位置打开书签: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/zh-tw.xaml index 7fa50a089..e8bc6bcb6 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/zh-tw.xaml @@ -5,6 +5,9 @@ 瀏覽器書籤 搜尋你的瀏覽器書籤 + + Failed to set url in clipboard + 書籤資料 載入書籤至: diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs index 91ade206b..07ce510fb 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs @@ -19,7 +19,7 @@ public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, IContex internal static string _faviconCacheDir; - internal static PluginInitContext _context; + internal static PluginInitContext Context { get; set; } internal static Settings _settings; @@ -29,20 +29,40 @@ public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, IContex public void Init(PluginInitContext context) { - _context = context; + Context = context; _settings = context.API.LoadSettingJsonStorage(); _faviconCacheDir = Path.Combine( context.CurrentPluginMetadata.PluginCacheDirectoryPath, "FaviconCache"); + + try + { + if (Directory.Exists(_faviconCacheDir)) + { + var files = Directory.GetFiles(_faviconCacheDir); + foreach (var file in files) + { + var extension = Path.GetExtension(file); + if (extension is ".db-shm" or ".db-wal" or ".sqlite-shm" or ".sqlite-wal") + { + File.Delete(file); + } + } + } + } + catch (Exception e) + { + Context.API.LogException(ClassName, "Failed to clean up orphaned cache files.", e); + } LoadBookmarksIfEnabled(); } private static void LoadBookmarksIfEnabled() { - if (_context.CurrentPluginMetadata.Disabled) + if (Context.CurrentPluginMetadata.Disabled) { // Don't load or monitor files if disabled return; @@ -84,7 +104,7 @@ public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, IContex Score = BookmarkLoader.MatchProgram(c, param).Score, Action = _ => { - _context.API.OpenUrl(c.Url); + Context.API.OpenUrl(c.Url); return true; }, @@ -108,7 +128,7 @@ public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, IContex Score = 5, Action = _ => { - _context.API.OpenUrl(c.Url); + Context.API.OpenUrl(c.Url); return true; }, ContextData = new BookmarkAttributes { Url = c.Url } @@ -192,12 +212,12 @@ public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, IContex public string GetTranslatedPluginTitle() { - return _context.API.GetTranslation("flowlauncher_plugin_browserbookmark_plugin_name"); + return Localize.flowlauncher_plugin_browserbookmark_plugin_name(); } public string GetTranslatedPluginDescription() { - return _context.API.GetTranslation("flowlauncher_plugin_browserbookmark_plugin_description"); + return Localize.flowlauncher_plugin_browserbookmark_plugin_description(); } public Control CreateSettingPanel() @@ -211,23 +231,20 @@ public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, IContex { new() { - Title = _context.API.GetTranslation("flowlauncher_plugin_browserbookmark_copyurl_title"), - SubTitle = _context.API.GetTranslation("flowlauncher_plugin_browserbookmark_copyurl_subtitle"), + Title = Localize.flowlauncher_plugin_browserbookmark_copyurl_title(), + SubTitle = Localize.flowlauncher_plugin_browserbookmark_copyurl_subtitle(), Action = _ => { try { - _context.API.CopyToClipboard(((BookmarkAttributes)selectedResult.ContextData).Url); + Context.API.CopyToClipboard(((BookmarkAttributes)selectedResult.ContextData).Url); return true; } catch (Exception e) { - var message = "Failed to set url in clipboard"; - _context.API.LogException(ClassName, message, e); - - _context.API.ShowMsg(message); - + Context.API.LogException(ClassName, "Failed to set url in clipboard", e); + Context.API.ShowMsgError(Localize.flowlauncher_plugin_browserbookmark_copy_failed()); return false; } }, diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/CustomBrowser.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/CustomBrowser.cs index 74e0f299a..af1e3fee4 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/CustomBrowser.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/CustomBrowser.cs @@ -1,4 +1,7 @@ -namespace Flow.Launcher.Plugin.BrowserBookmark.Models; +using System.Collections.Generic; +using Flow.Launcher.Localization.Attributes; + +namespace Flow.Launcher.Plugin.BrowserBookmark.Models; public class CustomBrowser : BaseModel { @@ -11,8 +14,11 @@ public class CustomBrowser : BaseModel get => _name; set { - _name = value; - OnPropertyChanged(); + if (_name != value) + { + _name = value; + OnPropertyChanged(); + } } } @@ -21,24 +27,36 @@ public class CustomBrowser : BaseModel get => _dataDirectoryPath; set { - _dataDirectoryPath = value; - OnPropertyChanged(); + if (_dataDirectoryPath != value) + { + _dataDirectoryPath = value; + OnPropertyChanged(); + } } } + public List AllBrowserTypes { get; } = BrowserTypeLocalized.GetValues(); + public BrowserType BrowserType { get => _browserType; set { - _browserType = value; - OnPropertyChanged(); + if (_browserType != value) + { + _browserType = value; + OnPropertyChanged(); + } } } } +[EnumLocalize] public enum BrowserType { + [EnumLocalizeValue("Chromium")] Chromium, + + [EnumLocalizeValue("Firefox")] Firefox, } diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/CustomBrowserSetting.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/CustomBrowserSetting.xaml index 80b004ff9..f67d359bf 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/CustomBrowserSetting.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/CustomBrowserSetting.xaml @@ -5,7 +5,6 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Flow.Launcher.Plugin.BrowserBookmark.Models" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:ui="clr-namespace:Flow.Launcher.Infrastructure.UI;assembly=Flow.Launcher.Infrastructure" Title="{DynamicResource flowlauncher_plugin_browserbookmark_bookmarkDataSetting}" Width="550" Background="{DynamicResource PopuBGColor}" @@ -142,8 +141,10 @@ Margin="5 10 10 0" HorizontalAlignment="Left" VerticalAlignment="Center" - ItemsSource="{Binding Source={ui:EnumBindingSource {x:Type local:BrowserType}}}" - SelectedItem="{Binding BrowserType}" /> + DisplayMemberPath="Display" + ItemsSource="{Binding AllBrowserTypes}" + SelectedValue="{Binding BrowserType}" + SelectedValuePath="Value" /> Library - net7.0-windows + net9.0-windows {59BD9891-3837-438A-958D-ADC7F91F6F7E} Properties Flow.Launcher.Plugin.Calculator @@ -42,7 +42,6 @@ - @@ -63,6 +62,7 @@ + diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ar.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ar.xaml index 3219d647e..759ba99de 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ar.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ar.xaml @@ -1,15 +1,16 @@ - + - آلة حاسبة - تمكنك من إجراء العمليات الحسابية. (جرب 5*3-2 في Flow Launcher) + آلة حاسبة + تمكنك من إجراء العمليات الحسابية. (جرب 5*3-2 في Flow Launcher) ليست رقمًا (NaN) التعبير خاطئ أو غير مكتمل (هل نسيت بعض الأقواس؟) نسخ هذا الرقم إلى الحافظة - فاصل عشري - الفاصل العشري الذي سيتم استخدامه في الناتج. - استخدام إعدادات النظام المحلية - فاصلة (,) - نقطة (.) + فاصل عشري + الفاصل العشري الذي سيتم استخدامه في الناتج. + استخدام الإعدادات المحلية للنظام + فاصلة (,) + نقطة (.) أقصى عدد من المنازل العشرية + Copy failed, please try later diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/cs.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/cs.xaml index ed8cb3fdb..f5dbe8e20 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/cs.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/cs.xaml @@ -1,15 +1,16 @@ - + - Kalkulačka - Umožňuje provádět matematické výpočty.(Try 5*3-2 v průtokovém spouštěči) + Kalkulačka + Umožňuje provádět matematické výpočty.(Try 5*3-2 v průtokovém spouštěči) Není číslo (NaN) Nesprávný nebo neúplný výraz (Nezapomněli jste na závorky?) Kopírování výsledku do schránky - Oddělovač desetinných míst - Oddělovač desetinných míst použitý ve výsledku. - Použít podle systému - Čárka (,) - Tečka (.) + Oddělovač desetinných míst + Oddělovač desetinných míst použitý ve výsledku. + Použít podle systému + Čárka (,) + Tečka (.) Desetinná místa + Copy failed, please try later diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/da.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/da.xaml index 15598118c..2f2777aa1 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/da.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/da.xaml @@ -1,15 +1,16 @@ - + - Calculator - Allows to do mathematical calculations.(Try 5*3-2 in Flow Launcher) + Calculator + Perform mathematical calculations (including hexadecimal values). Use ',' or '.' as thousand separator or decimal place. Not a number (NaN) Expression wrong or incomplete (Did you forget some parentheses?) Copy this number to the clipboard - Decimal separator - The decimal separator to be used in the output. - Use system locale - Comma (,) - Dot (.) + Decimal separator + The decimal separator to be used in the output. + Use system locale + Comma (,) + Dot (.) Max. decimal places + Copy failed, please try later diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/de.xaml index d8da714dc..46f5efe23 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/de.xaml @@ -1,15 +1,16 @@ - + - Rechner - Ermöglicht mathematische Berechnungen. (Versuchen Sie 5*3-2 in Flow Launcher) + Rechner + Ermöglicht mathematische Berechnungen. (Versuchen Sie 5*3-2 in Flow Launcher) Nicht eine Zahl (NaN) Ausdruck falsch oder unvollständig (Haben Sie einige Klammern vergessen?) Diese Zahl in die Zwischenablage kopieren - Dezimaltrennzeichen - Das Dezimaltrennzeichen, das in der Ausgabe verwendet werden soll. - Systemgebietsschema verwenden - Komma (,) - Punkt (.) + Dezimaltrennzeichen + Das Dezimaltrennzeichen, welches bei der Ausgabe verwendet werden soll. + Systemeinstellung nutzen + Komma (,) + Punkt (.) Max. Dezimalstellen + Copy failed, please try later diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml index 0e0911a70..b71e5d8a0 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml @@ -1,16 +1,18 @@ - + - Calculator - Allows to do mathematical calculations.(Try 5*3-2 in Flow Launcher) + Calculator + Perform mathematical calculations (including hexadecimal values). Use ',' or '.' as thousand separator or decimal place. Not a number (NaN) Expression wrong or incomplete (Did you forget some parentheses?) Copy this number to the clipboard - Decimal separator - The decimal separator to be used in the output. - Use system locale - Comma (,) - Dot (.) + Decimal separator + The decimal separator to be used in the output. + Use system locale + Comma (,) + Dot (.) Max. decimal places + Copy failed, please try later \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es-419.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es-419.xaml index 685bc136c..dce29cba5 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es-419.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es-419.xaml @@ -1,15 +1,16 @@ - + - Calculadora - Permite hacer cálculos matemáticos. (Pruebe con 5*3-2 en Flow Launcher) + Calculadora + Permite hacer cálculos matemáticos. (Pruebe con 5*3-2 en Flow Launcher) No es un número (NaN) Expresión incorrecta o incompleta (¿Olvidó algún paréntesis?) Copiar este número al portapapeles - Separador decimal - El separador decimal que se usará en el resultado. - Usar configuración del sistema - Coma (,) - Punto (.) + Separador decimal + El separador decimal que se usará en el resultado. + Usar configuración del sistema + Coma (,) + Punto (.) Número máximo de decimales + Copy failed, please try later diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es.xaml index 657c471d5..7f1775e2e 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es.xaml @@ -1,15 +1,16 @@ - + - Calculadora - Permite hacer cálculos matemáticos. (Pruebe 5*3-2 en Flow Launcher) + Calculadora + Realiza cálculos matemáticos (incluyendo valores hexadecimales). Utilizar ',' o '.' como separador de miles o decimal. No es un número (NaN) Expresión incorrecta o incompleta (¿Ha olvidado algunos paréntesis?) Copiar este número al portapapeles - Separador decimal - El separador decimal que se utilizará en la salida. - Usar configuración regional del sistema - Coma (,) - Punto (.) + Separador decimal + Separador decimal que se utilizará en la salida. + Utilizar configuración regional del sistema + Coma (,) + Punto (.) Número máximo de decimales + Ha fallado la copia, inténtelo más tarde diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/fr.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/fr.xaml index c462ccc43..a6db34811 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/fr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/fr.xaml @@ -1,15 +1,16 @@ - + - Calculatrice - Permet de faire des calculs mathématiques.(Essayez 5*3-2 dans Flow Launcher) + Calculatrice + Effectuer des calculs mathématiques (y compris les valeurs hexadécimales). Utilisez ',' ou '.' comme séparateur de milliers ou décimaux. Pas un nombre (NaN) Expression incorrecte ou incomplète (avez-vous oublié certaines parenthèses ?) Copier ce chiffre dans le presse-papiers - Séparateur décimal - Le séparateur décimal à utiliser dans la sortie. - Utiliser les paramètres régionaux du système - Virgule (,) - Point (.) + Séparateur de décimales + Le séparateur de décimale à utiliser dans la sortie. + Utiliser les paramètres régionaux du système + Virgule (,) + Point (.) Décimales max. + Échec de la copie, réessayer plus tard diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/he.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/he.xaml index 9fe80bc47..b053c8905 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/he.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/he.xaml @@ -1,15 +1,16 @@ - + - מחשבון - מאפשר לבצע חישובים מתמטיים. (נסה 5*3-2 ב-Flow Launcher) + מחשבו + מאפשר לבצע חישובים מתמטיים. (נסה 5*3-2 ב-Flow Launcher) לא מספר (NaN) הביטוי שגוי או לא שלם (האם שכחת סוגריים?) העתק מספר זה ללוח - מפריד עשרוני - מפריד עשרוני שישמש בתוצאה. - השתמש בהגדרת מערכת - פסיק (,) - נקודה (.) + מפריד עשרוני + מפריד העשרוני שישמש בפלט. + השתמש בהגדרות מערכת + פסיק (,) + נקודה (.) מספר מקסימלי של מקומות עשרוניים + Copy failed, please try later diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/it.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/it.xaml index fa4651a97..5b724e82e 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/it.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/it.xaml @@ -1,15 +1,16 @@ - + - Calcolatrice - Consente di eseguire calcoli matematici (provare 5*3-2 in Flow Launcher) + Calcolatrice + Consente di eseguire calcoli matematici (provare 5*3-2 in Flow Launcher) Non è un numero (NaN) Espressione sbagliata o incompleta (avete dimenticato delle parentesi?) Copiare questo numero negli appunti - Separatore decimale - Il separatore decimale da usare nell'output. - Usa il locale del sistema - Virgola (,) - Punto (.) + Separatore decimale + Il separatore decimale da usare nell'output. + Usa il locale del sistema + Virgola (,) + Punto (.) Max. cifre decimali + Copy failed, please try later diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ja.xaml index 757394b4c..bbd06006f 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ja.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ja.xaml @@ -1,15 +1,16 @@  - 電卓 - 数式の計算ができます(Flow Launcherで「5*3-2」と入力してみてください) + Calculator + Perform mathematical calculations (including hexadecimal values). Use ',' or '.' as thousand separator or decimal place. Not a number (NaN) Expression wrong or incomplete (Did you forget some parentheses?) この数字をクリップボードにコピーします - Decimal separator - The decimal separator to be used in the output. - Use system locale - Comma (,) - Dot (.) - Max. decimal places + 小数点の区切り記号 + The decimal separator to be used in the output. + システムのロケールを使用 + コンマ(,) + ドット (.) + 小数点以下の最大桁数 + Copy failed, please try later diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ko.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ko.xaml index ee2ae77a1..e4ca16d41 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ko.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ko.xaml @@ -1,15 +1,16 @@ - + - 계산기 - 수학 계산을 할 수 있습니다. Flow Launcher에서 5*3-2를 입력해보세요. + 계산기 + 수학 계산을 할 수 있습니다. Flow Launcher에서 5*3-2를 입력해보세요. 숫자가 아님 (NaN) 표현식이 잘못되었거나 불완전합니다. (괄호를 깜빡하셨나요?) 해당 숫자를 클립보드에 복사 - 소수 구분자 - 출력에 사용할 소수점 구분자 - 시스템 설정 사용 - 쉼표 (,) - 마침표 (.) + 소수 구분자 + 출력에 사용할 소수점 구분자 + 시스템 설정 사용 + 쉼표 (,) + 마침표 (.) 최대 소수점 아래 자릿 수 + Copy failed, please try later diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nb.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nb.xaml index 527df1d21..9ae31e976 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nb.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nb.xaml @@ -1,15 +1,16 @@ - + - Kalkulator - Lar deg gjøre matematiske beregninger. (Prøv 5*3-2 i Flow Launcher) + Kalkulator + Lar deg gjøre matematiske beregninger. (Prøv 5*3-2 i Flow Launcher) Ikke et tall (NaN) Uttrykk feil eller ufullstendig (glem noen parenteser?) Kopier dette nummeret til utklippstavlen - Desimalskille - Desimalskilletegnet som skal brukes i utdataene. - Bruk systemets nasjonale innstilling - Komma (,) - Prikk (.) + Desimalskille + Desimalskilletegnet som skal brukes i utdataene. + Bruk systemets nasjonale innstilling + Komma (,) + Prikk (.) Maks. desimaler + Copy failed, please try later diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nl.xaml index 15598118c..2f2777aa1 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nl.xaml @@ -1,15 +1,16 @@ - + - Calculator - Allows to do mathematical calculations.(Try 5*3-2 in Flow Launcher) + Calculator + Perform mathematical calculations (including hexadecimal values). Use ',' or '.' as thousand separator or decimal place. Not a number (NaN) Expression wrong or incomplete (Did you forget some parentheses?) Copy this number to the clipboard - Decimal separator - The decimal separator to be used in the output. - Use system locale - Comma (,) - Dot (.) + Decimal separator + The decimal separator to be used in the output. + Use system locale + Comma (,) + Dot (.) Max. decimal places + Copy failed, please try later diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pl.xaml index f697d2e6d..e73298dca 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pl.xaml @@ -1,15 +1,16 @@ - + - Kalkulator - Szybkie wykonywanie obliczeń matematycznych. (Spróbuj wpisać 5*3-2 w oknie Flow Launchera) + Kalkulator + Szybkie wykonywanie obliczeń matematycznych. (Spróbuj wpisać 5*3-2 w oknie Flow Launchera) Nie liczba (NaN) Wyrażenie niepoprawne lub niekompletne (Czy zapomniałeś o nawiasach?) Skopiuj ten numer do schowka - Separator dziesiętny - Separator dziesiętny używany w wyniku. - Użyj ustawień regionalnych systemu - Przecinek (,) - Kropka (.) + Separator dziesiętny + Separator dziesiętny używany w wyniku. + Użyj ustawień regionalnych systemu + Przecinek (,) + Kropka (.) Maks. liczba miejsc po przecinku + Copy failed, please try later diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-br.xaml index 6dd903fa4..73a60d42f 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-br.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-br.xaml @@ -1,15 +1,16 @@ - + - Calculadora - Permite fazer cálculos matemáticos.(Tente 5*3-2 no Flow Launcher) + Calculadora + Permite fazer cálculos matemáticos.(Tente 5*3-2 no Flow Launcher) Não é um número (NaN) Expressão errada ou incompleta (Você esqueceu de adicionar parênteses?) Copiar este numero para a área de transferência - Separador decimal - O separador decimal a ser usado no resultado. - Use system locale - Vírgula (,) - Ponto (.) + Separador decimal + O separador decimal a ser usado no resultado. + Use system locale + Vírgula (,) + Ponto (.) Max. decimal places + Copy failed, please try later diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-pt.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-pt.xaml index b751f3f39..7ec52be8c 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-pt.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-pt.xaml @@ -1,15 +1,16 @@ - + - Calculadora - Permite a execução de cálculos matemáticos (experimente 5*3-2) + Calculadora + Execução de cálculos matemáticos (incluindo valores hexadecimais). Utilize ',' ou '.' como separador de milhares ou de casas decimais. Não é número (NN) Expressão errada ou incompleta (esqueceu-se de algum parêntese?) Copiar número para a área de transferência - Separador decimal - O separador decimal para utilizar no resultado. - Utilizar definições do sistema - Vírgula (,) - Ponto (.) + Separador decimal + O separador decimal a ser usado no resultado. + Utilizar definições do sistema + Vírgula (,) + Ponto (.) Número máximo de casas decimais + Falha ao copiar. Por favor tente mais tarde. diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ru.xaml index 30c557536..43a7d44c7 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ru.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ru.xaml @@ -1,15 +1,16 @@ - + - Калькулятор - Позволяет выполнять математические вычисления. (Попробуйте 5*3-2 в Flow Launcher) + Калькулятор + Позволяет выполнять математические вычисления. (Попробуйте 5*3-2 в Flow Launcher) Не является числом (NaN) Выражение неправильное или неполное (Вы забыли скобки?) Скопировать этот номер в буфер обмена - Десятичный разделитель - Десятичный разделитель, который будет использоваться в результате. - Использовать системный язык - Запятая (,) - Точка (.) + Десятичный разделитель + The decimal separator to be used in the output. + Использовать системный формат + Запятая (,) + Точка (.) Макс. число знаков после запятой + Copy failed, please try later diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sk.xaml index d8d3a586f..498b6eb50 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sk.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sk.xaml @@ -1,15 +1,16 @@ - + - Kalkulačka - Spracúva matematické operácie. (Skúste 5*3-2 vo Flow Launcheri) + Kalkulačka + Vykonávanie matematických výpočtov (vrátane hexadecimálnych hodnôt). Ako oddeľovač tisícov alebo desatinného miesta použite ',' alebo '.'. Nie je číslo (NaN) Nesprávny alebo neúplný výraz (Nezabudli ste na zátvorky?) Kopírovať výsledok do schránky - Oddeľovač desatinných miest - Oddeľovač desatinných miest použitý vo výsledku. - Použiť podľa systému - Čiarka (,) - Bodka (.) + Oddeľovač desatinných miest + Oddeľovač desatinných miest použitý vo výsledku. + Použiť podľa systému + Čiarka (,) + Bodka (.) Desatinné miesta + Kopírovanie zlyhalo, skúste to neskôr diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sr-Cyrl-RS.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sr-Cyrl-RS.xaml new file mode 100644 index 000000000..98e3aebb5 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sr-Cyrl-RS.xaml @@ -0,0 +1,16 @@ + + + + Calculator + Perform mathematical calculations (including hexadecimal values). Use ',' or '.' as thousand separator or decimal place. + Not a number (NaN) + Expression wrong or incomplete (Did you forget some parentheses?) + Copy this number to the clipboard + Decimal separator + The decimal separator to be used in the output. + Use system locale + Comma (,) + Dot (.) + Max. decimal places + Copy failed, please try later + diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sr.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sr.xaml index 15598118c..2f2777aa1 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sr.xaml @@ -1,15 +1,16 @@ - + - Calculator - Allows to do mathematical calculations.(Try 5*3-2 in Flow Launcher) + Calculator + Perform mathematical calculations (including hexadecimal values). Use ',' or '.' as thousand separator or decimal place. Not a number (NaN) Expression wrong or incomplete (Did you forget some parentheses?) Copy this number to the clipboard - Decimal separator - The decimal separator to be used in the output. - Use system locale - Comma (,) - Dot (.) + Decimal separator + The decimal separator to be used in the output. + Use system locale + Comma (,) + Dot (.) Max. decimal places + Copy failed, please try later diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/tr.xaml index 345c81433..b41fc0656 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/tr.xaml @@ -1,15 +1,16 @@ - + - Hesap Makinesi - Matematiksel hesaplamalar yapmaya yarar. (5*3-2 yazmayı deneyin) + Hesap Makinesi + Matematiksel hesaplamalar yapmaya yarar. (5*3-2 yazmayı deneyin) Sayı değil (NaN) İfade hatalı ya da eksik. (Parantez koymayı mı unuttunuz?) Bu sayıyı panoya kopyala - Ondalık ayracı - Ondalık kısımları ayırmak için kullanılacak işaret. - Sistem yerelleştirme ayarını kullan - Virgül (,) - Nokta (.) + Ondalık ayracı + Ondalık kısımları ayırmak için kullanılacak işaret. + Sistem yerelleştirme ayarını kullan + Virgül (,) + Nokta (.) Maks. ondalık basamak + Kopyalama başarısız oldu, lütfen daha sonra deneyin diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/uk-UA.xaml index 014755929..14042dffd 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/uk-UA.xaml @@ -1,15 +1,16 @@ - + - Калькулятор - Дозволяє виконувати математичні обчислення (спробуйте 5*3-2 у Flow Launcher) + Калькулятор + Виконуйте математичні обчислення (включаючи шістнадцяткові значення). Використовуйте «,» або «.» як роздільник тисяч або десяткових знаків. Не є числом (NaN) Вираз неправильний або неповний (Ви забули якісь дужки?) Скопіюйте це число в буфер обміну - Десятковий роздільник - Десятковий роздільник, який буде використовуватися у виведенні. - Використовувати системну локаль - Кома (,) - Крапка (.) + Десятковий роздільник + Десятковий роздільник, який буде використовуватися у виведенні. + Використовувати системну локаль + Кома (,) + Крапка (.) Макс. кількість знаків після коми + Копіювання не вдалося, спробуйте пізніше diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/vi.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/vi.xaml index 82ebbbe93..20717d1db 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/vi.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/vi.xaml @@ -1,15 +1,16 @@ - + - Máy tính - Cho phép thực hiện các phép tính toán học. (Thử 5*3-2 trong Flow Launcher) + Máy tính + Cho phép thực hiện các phép tính toán học. (Thử 5*3-2 trong Flow Launcher) Không phải là số (NaN) Biểu thức sai hoặc không đầy đủ (Bạn có quên một số dấu ngoặc đơn không?) Sao chép số này vào clipboard - Dấu tách thập phân - Dấu phân cách thập phân được sử dụng ở đầu ra. - Sử dụng ngôn ngữ hệ thống - Dấu phẩy (,) - dấu chấm (.) + Dấu tách thập phân + Dấu phân cách thập phân được sử dụng ở đầu ra. + Sử dụng ngôn ngữ hệ thống + Dấu phẩy (,) + dấu chấm (.) Tối đa. chữ số thập phân + Copy failed, please try later diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-cn.xaml index aada17d5a..445ed394f 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-cn.xaml @@ -1,15 +1,16 @@ - + - 计算器 - 为 Flow Launcher 提供数学计算能力。(试着在 Flow Launcher 输入 5*3-2) + 计算器 + 执行数学计算(包括十六进制值)。使用 , 或 . 作为分隔符或小数点。 请输入数字 表达错误或不完整(您是否忘记了一些括号?) 将结果复制到剪贴板 - 十进制分隔符 - 在输出中使用的十进制分隔符 - 使用系统区域设置 - 逗号(,) - 点(.) + 十进制分隔符 + 在输出中使用的十进制分隔符 + 使用系统区域设置 + 逗号(,) + 点(.) 小数点后最大位数 + 复制失败,请稍后再试 diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-tw.xaml index 83df3e7cd..7c8acf40b 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-tw.xaml @@ -1,15 +1,16 @@ - + - 計算機 - 為 Flow Launcher 提供數學計算功能。(試著在 Flow Launcher 輸入 5*3-2) + 計算機 + 為 Flow Launcher 提供數學計算功能。(試著在 Flow Launcher 輸入 5*3-2) 不是一個數 (NaN) Expression wrong or incomplete (Did you forget some parentheses?) 複製此數至剪貼簿 - 小數點分隔符號 - The decimal separator to be used in the output. - 使用系統區域設定 - 逗號 (,) - 點 (.) + 小數點分隔符號 + The decimal separator to be used in the output. + 使用系統區域設定 + 逗號 (,) + 點 (.) 小數點後最大位數 + Copy failed, please try later diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs index b1e4cd606..6878c54b4 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs @@ -1,36 +1,41 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Windows.Controls; using Mages.Core; -using Flow.Launcher.Plugin.Calculator.ViewModels; using Flow.Launcher.Plugin.Calculator.Views; +using Flow.Launcher.Plugin.Calculator.ViewModels; namespace Flow.Launcher.Plugin.Calculator { public class Main : IPlugin, IPluginI18n, ISettingProvider { - private static readonly Regex RegValidExpressChar = new Regex( - @"^(" + - @"ceil|floor|exp|pi|e|max|min|det|abs|log|ln|sqrt|" + - @"sin|cos|tan|arcsin|arccos|arctan|" + - @"eigval|eigvec|eig|sum|polar|plot|round|sort|real|zeta|" + - @"bin2dec|hex2dec|oct2dec|" + - @"factorial|sign|isprime|isinfty|" + - @"==|~=|&&|\|\||(?:\<|\>)=?|" + - @"[ei]|[0-9]|[\+\%\-\*\/\^\., ""]|[\(\)\|\!\[\]]" + - @")+$", RegexOptions.Compiled); - private static readonly Regex RegBrackets = new Regex(@"[\(\)\[\]]", RegexOptions.Compiled); + private static readonly Regex RegValidExpressChar = MainRegexHelper.GetRegValidExpressChar(); + private static readonly Regex RegBrackets = MainRegexHelper.GetRegBrackets(); + private static readonly Regex ThousandGroupRegex = MainRegexHelper.GetThousandGroupRegex(); + private static readonly Regex NumberRegex = MainRegexHelper.GetNumberRegex(); + private static Engine MagesEngine; - private const string comma = ","; - private const string dot = "."; + private const string Comma = ","; + private const string Dot = "."; - private PluginInitContext Context { get; set; } + internal static PluginInitContext Context { get; set; } = null!; - private static Settings _settings; - private static SettingsViewModel _viewModel; + private Settings _settings; + private SettingsViewModel _viewModel; + + /// + /// Holds the formatting information for a single query. + /// This is used to ensure thread safety by keeping query state local. + /// + private class ParsingContext + { + public string InputDecimalSeparator { get; set; } + public bool InputUsesGroupSeparators { get; set; } + } public void Init(PluginInitContext context) { @@ -54,33 +59,24 @@ namespace Flow.Launcher.Plugin.Calculator return new List(); } + var context = new ParsingContext(); + try { - string expression; - - switch (_settings.DecimalSeparator) - { - case DecimalSeparator.Comma: - case DecimalSeparator.UseSystemLocale when CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator == ",": - expression = query.Search.Replace(",", "."); - break; - default: - expression = query.Search; - break; - } + var expression = NumberRegex.Replace(query.Search, m => NormalizeNumber(m.Value, context)); var result = MagesEngine.Interpret(expression); if (result?.ToString() == "NaN") - result = Context.API.GetTranslation("flowlauncher_plugin_calculator_not_a_number"); + result = Localize.flowlauncher_plugin_calculator_not_a_number(); if (result is Function) - result = Context.API.GetTranslation("flowlauncher_plugin_calculator_expression_not_complete"); + result = Localize.flowlauncher_plugin_calculator_expression_not_complete(); if (!string.IsNullOrEmpty(result?.ToString())) { decimal roundedResult = Math.Round(Convert.ToDecimal(result), _settings.MaxDecimalPlaces, MidpointRounding.AwayFromZero); - string newResult = ChangeDecimalSeparator(roundedResult, GetDecimalSeparator()); + string newResult = FormatResult(roundedResult, context); return new List { @@ -89,7 +85,7 @@ namespace Flow.Launcher.Plugin.Calculator Title = newResult, IcoPath = "Images/calculator.png", Score = 300, - SubTitle = Context.API.GetTranslation("flowlauncher_plugin_calculator_copy_number_to_clipboard"), + SubTitle = Localize.flowlauncher_plugin_calculator_copy_number_to_clipboard(), CopyText = newResult, Action = c => { @@ -100,7 +96,7 @@ namespace Flow.Launcher.Plugin.Calculator } catch (ExternalException) { - Context.API.ShowMsgBox("Copy failed, please try later"); + Context.API.ShowMsgBox(Localize.flowlauncher_plugin_calculator_failed_to_copy()); return false; } } @@ -116,9 +112,137 @@ namespace Flow.Launcher.Plugin.Calculator return new List(); } + /// + /// Parses a string representation of a number, detecting its format. It uses structural analysis + /// and falls back to system culture for truly ambiguous cases (e.g., "1,234"). + /// It populates the provided ParsingContext with the detected format for later use. + /// + /// A normalized number string with '.' as the decimal separator for the Mages engine. + private string NormalizeNumber(string numberStr, ParsingContext context) + { + var systemGroupSep = CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator; + int dotCount = numberStr.Count(f => f == '.'); + int commaCount = numberStr.Count(f => f == ','); + + // Case 1: Unambiguous mixed separators (e.g., "1.234,56") + if (dotCount > 0 && commaCount > 0) + { + context.InputUsesGroupSeparators = true; + if (numberStr.LastIndexOf('.') > numberStr.LastIndexOf(',')) + { + context.InputDecimalSeparator = Dot; + return numberStr.Replace(Comma, string.Empty); + } + else + { + context.InputDecimalSeparator = Comma; + return numberStr.Replace(Dot, string.Empty).Replace(Comma, Dot); + } + } + + // Case 2: Only dots + if (dotCount > 0) + { + if (dotCount > 1) + { + context.InputUsesGroupSeparators = true; + return numberStr.Replace(Dot, string.Empty); + } + // A number is ambiguous if it has a single Dot in the thousands position, + // and does not start with a "0." or "." + bool isAmbiguous = numberStr.Length - numberStr.LastIndexOf('.') == 4 + && !numberStr.StartsWith("0.") + && !numberStr.StartsWith("."); + if (isAmbiguous) + { + if (systemGroupSep == Dot) + { + context.InputUsesGroupSeparators = true; + return numberStr.Replace(Dot, string.Empty); + } + else + { + context.InputDecimalSeparator = Dot; + return numberStr; + } + } + else // Unambiguous decimal (e.g., "12.34" or "0.123" or ".123") + { + context.InputDecimalSeparator = Dot; + return numberStr; + } + } + + // Case 3: Only commas + if (commaCount > 0) + { + if (commaCount > 1) + { + context.InputUsesGroupSeparators = true; + return numberStr.Replace(Comma, string.Empty); + } + // A number is ambiguous if it has a single Comma in the thousands position, + // and does not start with a "0," or "," + bool isAmbiguous = numberStr.Length - numberStr.LastIndexOf(',') == 4 + && !numberStr.StartsWith("0,") + && !numberStr.StartsWith(","); + if (isAmbiguous) + { + if (systemGroupSep == Comma) + { + context.InputUsesGroupSeparators = true; + return numberStr.Replace(Comma, string.Empty); + } + else + { + context.InputDecimalSeparator = Comma; + return numberStr.Replace(Comma, Dot); + } + } + else // Unambiguous decimal (e.g., "12,34" or "0,123" or ",123") + { + context.InputDecimalSeparator = Comma; + return numberStr.Replace(Comma, Dot); + } + } + + // Case 4: No separators + return numberStr; + } + + private string FormatResult(decimal roundedResult, ParsingContext context) + { + string decimalSeparator = context.InputDecimalSeparator ?? GetDecimalSeparator(); + string groupSeparator = GetGroupSeparator(decimalSeparator); + + string resultStr = roundedResult.ToString(CultureInfo.InvariantCulture); + + string[] parts = resultStr.Split('.'); + string integerPart = parts[0]; + string fractionalPart = parts.Length > 1 ? parts[1] : string.Empty; + + if (context.InputUsesGroupSeparators && integerPart.Length > 3) + { + integerPart = ThousandGroupRegex.Replace(integerPart, groupSeparator); + } + + if (!string.IsNullOrEmpty(fractionalPart)) + { + return integerPart + decimalSeparator + fractionalPart; + } + + return integerPart; + } + + private string GetGroupSeparator(string decimalSeparator) + { + // This logic is now independent of the system's group separator + // to ensure consistent output for unit testing. + return decimalSeparator == Dot ? Comma : Dot; + } + private bool CanCalculate(Query query) { - // Don't execute when user only input "e" or "i" keyword if (query.Search.Length < 2) { return false; @@ -134,40 +258,22 @@ namespace Flow.Launcher.Plugin.Calculator return false; } - if ((query.Search.Contains(dot) && GetDecimalSeparator() != dot) || - (query.Search.Contains(comma) && GetDecimalSeparator() != comma)) - return false; - return true; } - private string ChangeDecimalSeparator(decimal value, string newDecimalSeparator) - { - if (String.IsNullOrEmpty(newDecimalSeparator)) - { - return value.ToString(); - } - - var numberFormatInfo = new NumberFormatInfo - { - NumberDecimalSeparator = newDecimalSeparator - }; - return value.ToString(numberFormatInfo); - } - - private static string GetDecimalSeparator() + private string GetDecimalSeparator() { string systemDecimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; return _settings.DecimalSeparator switch { DecimalSeparator.UseSystemLocale => systemDecimalSeparator, - DecimalSeparator.Dot => dot, - DecimalSeparator.Comma => comma, + DecimalSeparator.Dot => Dot, + DecimalSeparator.Comma => Comma, _ => systemDecimalSeparator, }; } - private bool IsBracketComplete(string query) + private static bool IsBracketComplete(string query) { var matchs = RegBrackets.Matches(query); var leftBracketCount = 0; @@ -188,17 +294,22 @@ namespace Flow.Launcher.Plugin.Calculator public string GetTranslatedPluginTitle() { - return Context.API.GetTranslation("flowlauncher_plugin_caculator_plugin_name"); + return Localize.flowlauncher_plugin_calculator_plugin_name(); } public string GetTranslatedPluginDescription() { - return Context.API.GetTranslation("flowlauncher_plugin_caculator_plugin_description"); + return Localize.flowlauncher_plugin_calculator_plugin_description(); } public Control CreateSettingPanel() { - return new CalculatorSettings(_viewModel); + return new CalculatorSettings(_settings); + } + + public void OnCultureInfoChanged(CultureInfo newCulture) + { + DecimalSeparatorLocalized.UpdateLabels(_viewModel.AllDecimalSeparator); } } } diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs b/Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs new file mode 100644 index 000000000..f4e2090e7 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs @@ -0,0 +1,19 @@ +using System.Text.RegularExpressions; + +namespace Flow.Launcher.Plugin.Calculator; + +internal static partial class MainRegexHelper +{ + + [GeneratedRegex(@"[\(\)\[\]]", RegexOptions.Compiled)] + public static partial Regex GetRegBrackets(); + + [GeneratedRegex(@"^(ceil|floor|exp|pi|e|max|min|det|abs|log|ln|sqrt|sin|cos|tan|arcsin|arccos|arctan|eigval|eigvec|eig|sum|polar|plot|round|sort|real|zeta|bin2dec|hex2dec|oct2dec|factorial|sign|isprime|isinfty|==|~=|&&|\|\||(?:\<|\>)=?|[ei]|[0-9]|0x[\da-fA-F]+|[\+\%\-\*\/\^\., ""]|[\(\)\|\!\[\]])+$", RegexOptions.Compiled)] + public static partial Regex GetRegValidExpressChar(); + + [GeneratedRegex(@"[\d\.,]+", RegexOptions.Compiled)] + public static partial Regex GetNumberRegex(); + + [GeneratedRegex(@"\B(?=(\d{3})+(?!\d))", RegexOptions.Compiled)] + public static partial Regex GetThousandGroupRegex(); +} diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/NumberTranslator.cs b/Plugins/Flow.Launcher.Plugin.Calculator/NumberTranslator.cs deleted file mode 100644 index 4eacb9d34..000000000 --- a/Plugins/Flow.Launcher.Plugin.Calculator/NumberTranslator.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System.Globalization; -using System.Text; -using System.Text.RegularExpressions; - -namespace Flow.Launcher.Plugin.Calculator -{ - /// - /// Tries to convert all numbers in a text from one culture format to another. - /// - public class NumberTranslator - { - private readonly CultureInfo sourceCulture; - private readonly CultureInfo targetCulture; - private readonly Regex splitRegexForSource; - private readonly Regex splitRegexForTarget; - - private NumberTranslator(CultureInfo sourceCulture, CultureInfo targetCulture) - { - this.sourceCulture = sourceCulture; - this.targetCulture = targetCulture; - - this.splitRegexForSource = GetSplitRegex(this.sourceCulture); - this.splitRegexForTarget = GetSplitRegex(this.targetCulture); - } - - /// - /// Create a new - returns null if no number conversion - /// is required between the cultures. - /// - /// source culture - /// target culture - /// - public static NumberTranslator Create(CultureInfo sourceCulture, CultureInfo targetCulture) - { - bool conversionRequired = sourceCulture.NumberFormat.NumberDecimalSeparator != targetCulture.NumberFormat.NumberDecimalSeparator - || sourceCulture.NumberFormat.PercentGroupSeparator != targetCulture.NumberFormat.PercentGroupSeparator - || sourceCulture.NumberFormat.NumberGroupSizes != targetCulture.NumberFormat.NumberGroupSizes; - return conversionRequired - ? new NumberTranslator(sourceCulture, targetCulture) - : null; - } - - /// - /// Translate from source to target culture. - /// - /// - /// - public string Translate(string input) - { - return this.Translate(input, this.sourceCulture, this.targetCulture, this.splitRegexForSource); - } - - /// - /// Translate from target to source culture. - /// - /// - /// - public string TranslateBack(string input) - { - return this.Translate(input, this.targetCulture, this.sourceCulture, this.splitRegexForTarget); - } - - private string Translate(string input, CultureInfo cultureFrom, CultureInfo cultureTo, Regex splitRegex) - { - var outputBuilder = new StringBuilder(); - - string[] tokens = splitRegex.Split(input); - foreach (string token in tokens) - { - decimal number; - outputBuilder.Append( - decimal.TryParse(token, NumberStyles.Number, cultureFrom, out number) - ? number.ToString(cultureTo) - : token); - } - - return outputBuilder.ToString(); - } - - private Regex GetSplitRegex(CultureInfo culture) - { - var splitPattern = $"((?:\\d|{Regex.Escape(culture.NumberFormat.NumberDecimalSeparator)}"; - if (!string.IsNullOrEmpty(culture.NumberFormat.NumberGroupSeparator)) - { - splitPattern += $"|{Regex.Escape(culture.NumberFormat.NumberGroupSeparator)}"; - } - splitPattern += ")+)"; - return new Regex(splitPattern); - } - } -} diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs b/Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs index 09f745669..87ae72fb6 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs @@ -12,6 +12,21 @@ namespace Flow.Launcher.Plugin.Calculator.ViewModels public Settings Settings { get; init; } - public IEnumerable MaxDecimalPlacesRange => Enumerable.Range(1, 20); + public static IEnumerable MaxDecimalPlacesRange => Enumerable.Range(1, 20); + + public List AllDecimalSeparator { get; } = DecimalSeparatorLocalized.GetValues(); + + public DecimalSeparator SelectedDecimalSeparator + { + get => Settings.DecimalSeparator; + set + { + if (Settings.DecimalSeparator != value) + { + Settings.DecimalSeparator = value; + OnPropertyChanged(); + } + } + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml index ceee3897c..8d240ef39 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml @@ -3,20 +3,14 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:calculator="clr-namespace:Flow.Launcher.Plugin.Calculator" - xmlns:core="clr-namespace:Flow.Launcher.Core.Resource;assembly=Flow.Launcher.Core" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:ui="clr-namespace:Flow.Launcher.Infrastructure.UI;assembly=Flow.Launcher.Infrastructure" xmlns:viewModels="clr-namespace:Flow.Launcher.Plugin.Calculator.ViewModels" + d:DataContext="{d:DesignInstance Type=viewModels:SettingsViewModel}" d:DesignHeight="450" d:DesignWidth="800" - Loaded="CalculatorSettings_Loaded" mc:Ignorable="d"> - - - - @@ -33,7 +27,7 @@ Margin="{StaticResource SettingPanelItemRightTopBottomMargin}" VerticalAlignment="Center" FontSize="14" - Text="{DynamicResource flowlauncher_plugin_calculator_output_decimal_seperator}" /> + Text="{DynamicResource flowlauncher_plugin_calculator_output_decimal_separator}" /> - - - - - - + DisplayMemberPath="Display" + ItemsSource="{Binding AllDecimalSeparator}" + SelectedValue="{Binding SelectedDecimalSeparator, Mode=TwoWay}" + SelectedValuePath="Value" /> LoadContextMenus(Result selectedResult) @@ -75,14 +72,16 @@ namespace Flow.Launcher.Plugin.Explorer { Settings.QuickAccessLinks.Add(new AccessLink { - Path = record.FullPath, Type = record.Type + Name = record.FullPath.GetPathName(), + Path = record.FullPath, + Type = record.Type }); Context.API.ShowMsg(Context.API.GetTranslation("plugin_explorer_addfilefoldersuccess"), Context.API.GetTranslation("plugin_explorer_addfilefoldersuccess_detail"), Constants.ExplorerIconImageFullPath); - ViewModel.Save(); + return true; }, @@ -106,7 +105,7 @@ namespace Flow.Launcher.Plugin.Explorer Context.API.GetTranslation("plugin_explorer_removefilefoldersuccess_detail"), Constants.ExplorerIconImageFullPath); - ViewModel.Save(); + return true; }, @@ -130,9 +129,8 @@ namespace Flow.Launcher.Plugin.Explorer } catch (Exception e) { - var message = "Fail to set text in clipboard"; - LogException(message, e); - Context.API.ShowMsg(message); + LogException("Fail to set text in clipboard", e); + Context.API.ShowMsgError(Context.API.GetTranslation("plugin_explorer_fail_to_set_text")); return false; } }, @@ -153,9 +151,8 @@ namespace Flow.Launcher.Plugin.Explorer } catch (Exception e) { - var message = "Fail to set text in clipboard"; - LogException(message, e); - Context.API.ShowMsg(message); + LogException("Fail to set text in clipboard", e); + Context.API.ShowMsgError(Context.API.GetTranslation("plugin_explorer_fail_to_set_text")); return false; } }, @@ -176,9 +173,8 @@ namespace Flow.Launcher.Plugin.Explorer } catch (Exception e) { - var message = $"Fail to set file/folder in clipboard"; - LogException(message, e); - Context.API.ShowMsg(message); + LogException($"Fail to set file/folder in clipboard", e); + Context.API.ShowMsgError(Context.API.GetTranslation("plugin_explorer_fail_to_set_files")); return false; } @@ -219,9 +215,8 @@ namespace Flow.Launcher.Plugin.Explorer } catch (Exception e) { - var message = $"Fail to delete {record.FullPath}"; - LogException(message, e); - Context.API.ShowMsgError(message); + LogException($"Fail to delete {record.FullPath}", e); + Context.API.ShowMsgError(string.Format(Context.API.GetTranslation("plugin_explorer_fail_to_delete"), record.FullPath)); return false; } @@ -263,9 +258,9 @@ namespace Flow.Launcher.Plugin.Explorer } catch (FileNotFoundException e) { - var name = "Plugin: Folder"; - var message = $"File not found: {e.Message}"; - Context.API.ShowMsgError(name, message); + Context.API.ShowMsgError( + Context.API.GetTranslation("plugin_explorer_plugin_name"), + string.Format(Context.API.GetTranslation("plugin_explorer_file_not_found"), e.Message)); return false; } @@ -332,9 +327,8 @@ namespace Flow.Launcher.Plugin.Explorer } catch (Exception e) { - var message = $"Fail to open file at {record.FullPath}"; - LogException(message, e); - Context.API.ShowMsgError(message); + LogException($"Fail to open file at {record.FullPath}", e); + Context.API.ShowMsgError(string.Format(Context.API.GetTranslation("plugin_explorer_fail_to_open"), record.FullPath)); return false; } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj index 93691814a..af33f4da2 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -1,8 +1,8 @@ - + Library - net7.0-windows + net9.0-windows true true true @@ -47,9 +47,9 @@ - - - + + + diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/PathHelper.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/PathHelper.cs new file mode 100644 index 000000000..7763d9662 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/PathHelper.cs @@ -0,0 +1,21 @@ +using System; +using System.IO; +using System.Linq; +using Flow.Launcher.Plugin.Explorer.Search; + +namespace Flow.Launcher.Plugin.Explorer.Helper; + +public static class PathHelper +{ + public static string GetPathName(this string selectedPath) + { + if (string.IsNullOrEmpty(selectedPath)) return string.Empty; + var path = selectedPath.EndsWith(Constants.DirectorySeparator) ? selectedPath[0..^1] : selectedPath; + + if (path.EndsWith(':')) + return path[0..^1] + " Drive"; + + return path.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None) + .Last(); + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/SortOptionTranslationHelper.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/SortOptionTranslationHelper.cs deleted file mode 100644 index 72f58f5b6..000000000 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/SortOptionTranslationHelper.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Flow.Launcher.Plugin.Everything.Everything; -using JetBrains.Annotations; -using System; - -namespace Flow.Launcher.Plugin.Explorer.Helper; - -public static class SortOptionTranslationHelper -{ - [CanBeNull] - public static IPublicAPI API { get; internal set; } - - public static string GetTranslatedName(this SortOption sortOption) - { - const string prefix = "flowlauncher_plugin_everything_sort_by_"; - - ArgumentNullException.ThrowIfNull(API); - - var enumName = Enum.GetName(sortOption); - var splited = enumName!.Split('_'); - var name = string.Join('_', splited[..^1]); - var direction = splited[^1]; - - return $"{API.GetTranslation(prefix + name.ToLower())} {API.GetTranslation(prefix + direction.ToLower())}"; - } -} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ar.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ar.xaml index ebdb0ff35..b2bf99515 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ar.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ar.xaml @@ -5,6 +5,8 @@ يرجى إجراء تحديد أولاً Please select a folder path. Please choose a different name or folder path. + Are you sure you want to delete this quick access link? + Are you sure you want to delete this index search excluded path? يرجى تحديد رابط المجلد هل أنت متأكد أنك تريد حذف {0}؟ هل أنت متأكد أنك تريد حذف هذا الملف نهائيًا؟ @@ -128,6 +130,11 @@ عرض قائمة السياق في Windows فتح بواسطة اختر برنامج لفتح العنصر + Fail to delete {0} + File not found: {0} + Fail to open {0} + Fail to set text in clipboard + Fail to set files/folders in clipboard {0} متبقي من {1} @@ -141,19 +148,32 @@ تحذير: خدمة Everything غير قيد التشغيل خطأ أثناء استعلام Everything الترتيب حسب - الاسم - المسار - الحجم - الامتداد - نوع الاسم - تاريخ الإنشاء - تاريخ التعديل - السمات - اسم ملف قائمة الملفات - عدد مرات التشغيل - تاريخ التغيير الأخير - تاريخ الوصول - تاريخ التشغيل + Name ↑ + Name ↓ + Path ↑ + Path ↓ + Size ↑ + Size ↓ + Extension ↑ + Extension ↓ + Type Name ↑ + Type Name ↓ + Date Created ↑ + Date Created ↓ + Date Modified ↑ + Date Modified ↓ + Attributes ↑ + Attributes ↓ + File List FileName ↑ + File List FileName ↓ + Run Count ↑ + Run Count ↓ + Date Recently Changed ↑ + Date Recently Changed ↓ + Date Accessed ↑ + Date Accessed ↓ + Date Run ↑ + Date Run ↓ تحذير: هذا ليس خيار ترتيب سريع، قد تكون عمليات البحث بطيئة diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/cs.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/cs.xaml index 38f4f145e..0acdb5ca1 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/cs.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/cs.xaml @@ -5,6 +5,8 @@ Nejprve vyberte položku Please select a folder path. Please choose a different name or folder path. + Are you sure you want to delete this quick access link? + Are you sure you want to delete this index search excluded path? Vyberte odkaz na složku Opravdu chcete odstranit {0}? Opravdu chcete trvale odstranit tento soubor? @@ -128,6 +130,11 @@ Zobrazit kontextové menu Windows Open With Select a program to open with + Fail to delete {0} + File not found: {0} + Fail to open {0} + Fail to set text in clipboard + Fail to set files/folders in clipboard Volných {0} z {1} @@ -141,19 +148,32 @@ Upozornění: Služba Everything není spuštěna Chyba při dotazování Everything Seřadit podle - Jméno - Cesta - Velikost - Rozšíření - Typ - Datum vytvoření - Datum změny - Atributy - Seznam názvů souborů - Počet spuštění - Poslední změna data - Datum přístupu - Datum spouštění + Name ↑ + Name ↓ + Path ↑ + Path ↓ + Size ↑ + Size ↓ + Extension ↑ + Extension ↓ + Type Name ↑ + Type Name ↓ + Date Created ↑ + Date Created ↓ + Date Modified ↑ + Date Modified ↓ + Attributes ↑ + Attributes ↓ + File List FileName ↑ + File List FileName ↓ + Run Count ↑ + Run Count ↓ + Date Recently Changed ↑ + Date Recently Changed ↓ + Date Accessed ↑ + Date Accessed ↓ + Date Run ↑ + Date Run ↓ Poznámka: Toto není možnost Fast Sort, vyhledávání může být pomalé diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/da.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/da.xaml index 8c011814c..66816de93 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/da.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/da.xaml @@ -5,6 +5,8 @@ Please make a selection first Please select a folder path. Please choose a different name or folder path. + Are you sure you want to delete this quick access link? + Are you sure you want to delete this index search excluded path? Please select a folder link Are you sure you want to delete {0}? Are you sure you want to permanently delete this file? @@ -128,6 +130,11 @@ Show Windows Context Menu Open With Select a program to open with + Fail to delete {0} + File not found: {0} + Fail to open {0} + Fail to set text in clipboard + Fail to set files/folders in clipboard {0} free of {1} @@ -141,19 +148,32 @@ Warning: Everything service is not running Error while querying Everything Sort By - Name - Path - Size - Extension - Type Name - Date Created - Date Modified - Attributes - File List FileName - Run Count - Date Recently Changed - Date Accessed - Date Run + Name ↑ + Name ↓ + Path ↑ + Path ↓ + Size ↑ + Size ↓ + Extension ↑ + Extension ↓ + Type Name ↑ + Type Name ↓ + Date Created ↑ + Date Created ↓ + Date Modified ↑ + Date Modified ↓ + Attributes ↑ + Attributes ↓ + File List FileName ↑ + File List FileName ↓ + Run Count ↑ + Run Count ↓ + Date Recently Changed ↑ + Date Recently Changed ↓ + Date Accessed ↑ + Date Accessed ↓ + Date Run ↑ + Date Run ↓ Warning: This is not a Fast Sort option, searches may be slow diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/de.xaml index 1d8c3937a..8ddb958ad 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/de.xaml @@ -5,6 +5,8 @@ Bitte treffen Sie zuerst eine Auswahl Bitte wählen Sie einen Ordnerpfad aus. Bitte wählen Sie einen anderen Namen oder Ordnerpfad. + Are you sure you want to delete this quick access link? + Are you sure you want to delete this index search excluded path? Bitte wählen Sie einen Ordner-Link aus Sind Sie sicher, dass Sie {0} löschen wollen? Sind Sie sicher, dass Sie diese Datei dauerhaft löschen möchten? @@ -128,6 +130,11 @@ Windows-Kontextmenü zeigen Öffnen mit Ein Programm zum Öffnen auswählen + Löschen nicht möglich {0} + Datei nicht gefunden: {0} + Öffnen nicht möglich {0} + Fail to set text in clipboard + Fail to set files/folders in clipboard {0} frei von {1} @@ -141,19 +148,32 @@ Warnung: Everything-Dienst wird nicht ausgeführt Fehler bei Abfrage von Everything Sortieren nach - Name - Pfad - Größe - Extension - Typname - Erstellungsdatum - Änderungsdatum - Attribute - Dateilistenname - Ausführungszahl - Datum kürzlich geändert - Zugriffsdatum - Ausführungsdatum + Name ↑ + Name ↓ + Path ↑ + Path ↓ + Size ↑ + Size ↓ + Extension ↑ + Extension ↓ + Type Name ↑ + Type Name ↓ + Date Created ↑ + Date Created ↓ + Date Modified ↑ + Date Modified ↓ + Attributes ↑ + Attributes ↓ + File List FileName ↑ + File List FileName ↓ + Run Count ↑ + Run Count ↓ + Date Recently Changed ↑ + Date Recently Changed ↓ + Date Accessed ↑ + Date Accessed ↓ + Date Run ↑ + Date Run ↓ Warnung: Dies ist keine Schnellsortieroption, Suchen können langsam sein diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml index 9f60aaa43..16ef037cc 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml @@ -5,6 +5,10 @@ Please make a selection first + Please select a folder path. + Please choose a different name or folder path. + Are you sure you want to delete this quick access link? + Are you sure you want to delete this index search excluded path? Please select a folder link Are you sure you want to delete {0}? Are you sure you want to permanently delete this file? @@ -27,6 +31,7 @@ Add General Setting Customise Action Keywords + Customise Quick Access Quick Access Links Everything Setting Preview Panel @@ -43,6 +48,7 @@ Shell Path Index Search Excluded Paths Use search result's location as the working directory of the executable + Display more information like size and age in tooltips Hit Enter to open folder in Default File Manager Use Index Search For Path Search Indexing Options @@ -79,6 +85,9 @@ Ctrl + Enter to open the directory Ctrl + Enter to open the containing folder + {0}{4}Size: {1}{4}Date created: {2}{4}Date modified: {3} + Unknown + {0}{3}Space free: {1}{3}Total size: {2} Copy path @@ -91,7 +100,11 @@ Delete Permanently delete current file Permanently delete current folder - Path: + Name + Type + Path + File + Folder Delete the selected Run as different user Run the selected using a different user account @@ -119,6 +132,11 @@ Show Windows Context Menu Open With Select a program to open with + Fail to delete {0} + File not found: {0} + Fail to open {0} + Fail to set text in clipboard + Fail to set files/folders in clipboard {0} free of {1} @@ -132,19 +150,32 @@ Warning: Everything service is not running Error while querying Everything Sort By - Name - Path - Size - Extension - Type Name - Date Created - Date Modified - Attributes - File List FileName - Run Count - Date Recently Changed - Date Accessed - Date Run + Name ↑ + Name ↓ + Path ↑ + Path ↓ + Size ↑ + Size ↓ + Extension ↑ + Extension ↓ + Type Name ↑ + Type Name ↓ + Date Created ↑ + Date Created ↓ + Date Modified ↑ + Date Modified ↓ + Attributes ↑ + Attributes ↓ + File List FileName ↑ + File List FileName ↓ + Run Count ↑ + Run Count ↓ + Date Recently Changed ↑ + Date Recently Changed ↓ + Date Accessed ↑ + Date Accessed ↓ + Date Run ↑ + Date Run ↓ Warning: This is not a Fast Sort option, searches may be slow diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es-419.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es-419.xaml index ae541a745..f80a55965 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es-419.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es-419.xaml @@ -5,6 +5,8 @@ Por favor, seleccione primero Please select a folder path. Please choose a different name or folder path. + Are you sure you want to delete this quick access link? + Are you sure you want to delete this index search excluded path? Please select a folder link Are you sure you want to delete {0}? Are you sure you want to permanently delete this file? @@ -128,6 +130,11 @@ Show Windows Context Menu Open With Select a program to open with + Fail to delete {0} + File not found: {0} + Fail to open {0} + Fail to set text in clipboard + Fail to set files/folders in clipboard {0} free of {1} @@ -141,19 +148,32 @@ Advertencia: El servicio de Everything no se está ejecutando Error al consultar Everything Ordenar por - Name - Ruta - Size - Extensión - Tipo de nombre - Fecha de creación - Fecha de modificación - Atributos - Lista de archivos Nombre del Archivo - Ejecutar cuenta - Fecha de cambio reciente - Fecha de acceso - Fecha de ejecución + Name ↑ + Name ↓ + Path ↑ + Path ↓ + Size ↑ + Size ↓ + Extension ↑ + Extension ↓ + Type Name ↑ + Type Name ↓ + Date Created ↑ + Date Created ↓ + Date Modified ↑ + Date Modified ↓ + Attributes ↑ + Attributes ↓ + File List FileName ↑ + File List FileName ↓ + Run Count ↑ + Run Count ↓ + Date Recently Changed ↑ + Date Recently Changed ↓ + Date Accessed ↑ + Date Accessed ↓ + Date Run ↑ + Date Run ↓ Advertencia: No es una opción de orden rápido, las búsquedas pueden ser lentas diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es.xaml index 10ee4a9a4..474ba9a4c 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es.xaml @@ -5,6 +5,8 @@ Por favor haga una selección primero Por favor, seleccione una ruta de carpeta. Por favor, elija un nombre o ruta de carpeta diferente. + ¿Está seguro de que desea eliminar este enlace de acceso rápido? + ¿Está seguro de que desea eliminar esta ruta excluida de la búsqueda de índices? Por favor, seleccione un enlace de carpeta ¿Está seguro de que desea eliminar {0}? ¿Está seguro de que desea eliminar permanentemente este archivo? @@ -107,9 +109,9 @@ Abrir carpeta contenedora Abre la ubicación que contiene al elemento actual Abrir con el editor: - No se pudo abrir el archivo en {0} con el editor {1} en {2} + No se ha podido abrir el archivo en {0} con el editor {1} en {2} Abrir con Shell: - No se pudo abrir la carpeta {0} con Shell {1} en {2} + No se ha podido abrir la carpeta {0} con Shell {1} en {2} Excluir la carpeta actual y sus subcarpetas del índice de búsqueda Excluido del índice de búsqueda Abrir opciones de indexación de Windows @@ -128,6 +130,11 @@ Mostrar menú contextual de Windows Abrir con Seleccione un programa para abrir con + No se pudo eliminar {0} + Archivo no encontrado: {0} + Fallo al abrir {0} + Fallo al establecer texto en el portapapeles + Fallo al establecer archivos/carpetas en el portapapeles {0} libre de {1} @@ -141,19 +148,32 @@ Advertencia: El servicio de Everything no se está ejecutando Error al consultar Everything Ordenar por - Nombre - Ruta - Tamaño - Extensión - Tipo - Fecha de creación - Fecha de modificación - Atributos - Nombre de la lista de archivos - Número de ejecuciones - Fecha de cambios recientes - Fecha de último acceso - Fecha de ejecución + Nombre ↑ + Nombre ↓ + Ruta ↑ + Ruta ↓ + Tamaño ↑ + Tamaño ↓ + Extensión ↑ + Extensión ↓ + Tipo ↑ + Tipo ↓ + Fecha de creación ↑ + Fecha de creación ↓ + Fecha de modificación ↑ + Fecha de modificación ↓ + Atributos ↑ + Atributos ↓ + Nombre de la lista de archivos ↑ + Nombre de la lista de archivos ↓ + Número de ejecuciones ↑ + Número de ejecuciones ↓ + Fecha modificada recientemente ↑ + Fecha modificada recientemente ↓ + Fecha de último acceso ↑ + Fecha de último acceso ↓ + Fecha de ejecución ↑ + Fecha de ejecución ↓ Advertencia: Esta no es una opción de clasificación rápida, las búsquedas pueden ser lentas diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/fr.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/fr.xaml index 8809004c7..096cd4a0d 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/fr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/fr.xaml @@ -5,6 +5,8 @@ Veuillez d'abord effectuer une sélection Veuillez sélectionner un chemin d'accès au dossier. Veuillez choisir un nom ou un chemin d'accès différent. + Êtes-vous sûr de vouloir supprimer ce lien d'accès rapide ? + Êtes-vous sûr de vouloir supprimer ce chemin de recherche d'index exclu ? Veuillez sélectionner un lien vers un dossier Êtes-vous sûr de vouloir supprimer {0} ? Êtes-vous sûr de vouloir supprimer définitivement ce fichier ? @@ -128,6 +130,11 @@ Afficher le menu contextuel de Windows Ouvrir avec Sélectionnez un programme à ouvrir avec + Échec de la suppression de {0} + Fichier introuvable : {0} + Échec de l'ouverture de {0} + Impossible de mettre le texte dans le presse-papiers + Impossible de définir les fichiers/dossiers dans le presse-papiers {0} libres sur {1} @@ -141,19 +148,32 @@ Avertissement : Le service Everything n'est pas en cours d'exécution Erreur lors de l'interrogation de Everything Trier par - Nom - Xhemin - Taille - Extension - Nom du type - Date de création - Date de modification - Attributs - Fichiers Liste Nom du fichier - Nombre d'exécutions - Date récemment modifiée - Date d'accès - Date d'exécution + Nom ↑ + Nom ↓ + Chemin ↑ + Chemin ↓ + Taille ↑ + Taille ↓ + Extension ↑ + Extension ↓ + Nom du type ↑ + Nom du type ↓ + Date de création ↑ + Date de création ↓ + Date de modification ↑ + Date de modification ↓ + Attributs ↑ + Attributs ↓ + Nom de fichier de la liste de fichiers ↑ + Nom de fichier de la liste de fichiers ↓ + Nombre d'exécutions ↑ + Nombre d'exécutions ↓ + Date récemment modifié ↑ + Date récemment modifié ↓ + Date d'accès ↑ + Date d'accès ↓ + Date d'exécution ↑ + Date d'exécution ↓ Avertissement : Il ne s'agit pas d'une option de tri rapide, les recherches peuvent être lentes. diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/he.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/he.xaml index a18a15aad..a4e4445ae 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/he.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/he.xaml @@ -5,6 +5,8 @@ אנא בצע בחירה תחילה אנא בחר נתיב לתיקייה. אנא בחר שם או נתיב תיקייה אחר. + Are you sure you want to delete this quick access link? + Are you sure you want to delete this index search excluded path? אנא בחר קישור לתיקייה האם אתה בטוח שברצונך למחוק את {0}? האם אתה בטוח שברצונך למחוק קובץ זה לצמיתות? @@ -128,6 +130,11 @@ הצג תפריט הקשר של Windows פתח באמצעות בחר תוכנית לפתיחה + Fail to delete {0} + File not found: {0} + Fail to open {0} + Fail to set text in clipboard + Fail to set files/folders in clipboard {0} פנוי מתוך {1} @@ -141,19 +148,32 @@ אזהרה: שירות Everything אינו פועל שגיאה במהלך שאילתה ל-Everything מיין לפי - שם - נתיב - גודל - סיומת - שם סוג - תאריך יצירה - תאריך שינוי - מאפיינים - מיון לפי שם קובץ ברשימה - מספר הפעלות - תאריך שינוי אחרון - תאריך גישה - תאריך הרצה + Name ↑ + Name ↓ + Path ↑ + Path ↓ + Size ↑ + Size ↓ + Extension ↑ + Extension ↓ + Type Name ↑ + Type Name ↓ + Date Created ↑ + Date Created ↓ + Date Modified ↑ + Date Modified ↓ + Attributes ↑ + Attributes ↓ + File List FileName ↑ + File List FileName ↓ + Run Count ↑ + Run Count ↓ + Date Recently Changed ↑ + Date Recently Changed ↓ + Date Accessed ↑ + Date Accessed ↓ + Date Run ↑ + Date Run ↓ אזהרה: זוהי לא אפשרות מיון מהיר, החיפושים עשויים להיות איטיים diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/it.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/it.xaml index 3afaf2f25..f3fa1e1e6 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/it.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/it.xaml @@ -5,6 +5,8 @@ Effettua prima una selezione Please select a folder path. Please choose a different name or folder path. + Are you sure you want to delete this quick access link? + Are you sure you want to delete this index search excluded path? Si prega di selezionare un collegamento alla cartella Sei sicuro di voler eliminare {0}? Sei sicuro di voler eliminare definitivamente questo file? @@ -128,6 +130,11 @@ Mostra Menu Contestuale di Windows Apri Con Seleziona un programma con cui aprire + Fail to delete {0} + File not found: {0} + Fail to open {0} + Fail to set text in clipboard + Fail to set files/folders in clipboard {0} disponibili su {1} @@ -141,19 +148,32 @@ Attenzione: Il servizio "Everything" non è in esecuzione Errore nell'interrogazione di Everything Ordina per - Nome - Percorso - Dimensioni - Estensione - Tipo - Data di creazione - Data della modifica - Attributi - Nome File Lista - Esegui Conteggio - Data di recente della modifica - Data di accesso - Data di esecuzione + Name ↑ + Name ↓ + Path ↑ + Path ↓ + Size ↑ + Size ↓ + Extension ↑ + Extension ↓ + Type Name ↑ + Type Name ↓ + Date Created ↑ + Date Created ↓ + Date Modified ↑ + Date Modified ↓ + Attributes ↑ + Attributes ↓ + File List FileName ↑ + File List FileName ↓ + Run Count ↑ + Run Count ↓ + Date Recently Changed ↑ + Date Recently Changed ↓ + Date Accessed ↑ + Date Accessed ↓ + Date Run ↑ + Date Run ↓ Attenzione: Questa non è un'opzione di ordinamento rapido, le ricerche potrebbero essere lente diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ja.xaml index 648efe642..d0b045175 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ja.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ja.xaml @@ -5,6 +5,8 @@ Please make a selection first Please select a folder path. Please choose a different name or folder path. + Are you sure you want to delete this quick access link? + Are you sure you want to delete this index search excluded path? Please select a folder link Are you sure you want to delete {0}? Are you sure you want to permanently delete this file? @@ -128,6 +130,11 @@ Windowsの右クリックメニューを表示 アプリで開く 開くためのプログラムを選択します + Fail to delete {0} + File not found: {0} + Fail to open {0} + Fail to set text in clipboard + Fail to set files/folders in clipboard {0} free of {1} @@ -141,19 +148,32 @@ Warning: Everything service is not running Error while querying Everything Sort By - 名前 - Path - サイズ - Extension - Type Name - Date Created - Date Modified - Attributes - File List FileName - Run Count - Date Recently Changed - Date Accessed - Date Run + Name ↑ + Name ↓ + Path ↑ + Path ↓ + Size ↑ + Size ↓ + Extension ↑ + Extension ↓ + Type Name ↑ + Type Name ↓ + Date Created ↑ + Date Created ↓ + Date Modified ↑ + Date Modified ↓ + Attributes ↑ + Attributes ↓ + File List FileName ↑ + File List FileName ↓ + Run Count ↑ + Run Count ↓ + Date Recently Changed ↑ + Date Recently Changed ↓ + Date Accessed ↑ + Date Accessed ↓ + Date Run ↑ + Date Run ↓ Warning: This is not a Fast Sort option, searches may be slow diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ko.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ko.xaml index cf4d8b8ad..3195ff6e5 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ko.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ko.xaml @@ -5,6 +5,8 @@ 항목을 먼저 선택하세요 Please select a folder path. Please choose a different name or folder path. + Are you sure you want to delete this quick access link? + Are you sure you want to delete this index search excluded path? 폴더 링크를 선택하세요 {0} - 삭제하시겠습니까? 이 파일을 영구적으로 삭제하시겠습니까? @@ -128,6 +130,11 @@ 우클릭 메뉴 보기 함께 열기 이 항목을 열 프로그램을 선택 + Fail to delete {0} + File not found: {0} + Fail to open {0} + Fail to set text in clipboard + Fail to set files/folders in clipboard {0} free of {1} @@ -141,19 +148,32 @@ 경고: Everything 서비스가 실행 중이 아닙니다 Error while querying Everything 정렬 기준 - Name - Path - 크기 - Extension - Type Name - Date Created - Date Modified - Attributes - File List FileName - Run Count - Date Recently Changed - Date Accessed - Date Run + Name ↑ + Name ↓ + Path ↑ + Path ↓ + Size ↑ + Size ↓ + Extension ↑ + Extension ↓ + Type Name ↑ + Type Name ↓ + Date Created ↑ + Date Created ↓ + Date Modified ↑ + Date Modified ↓ + Attributes ↑ + Attributes ↓ + File List FileName ↑ + File List FileName ↓ + Run Count ↑ + Run Count ↓ + Date Recently Changed ↑ + Date Recently Changed ↓ + Date Accessed ↑ + Date Accessed ↓ + Date Run ↑ + Date Run ↓ Warning: This is not a Fast Sort option, searches may be slow diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nb.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nb.xaml index 733136c36..5efdead0c 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nb.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nb.xaml @@ -5,6 +5,8 @@ Vennligst gjør et valg først Please select a folder path. Please choose a different name or folder path. + Are you sure you want to delete this quick access link? + Are you sure you want to delete this index search excluded path? Velg en mappekobling Er du sikker på at du ønsker å slette {0}? Er du sikker på at du vil slette denne filen permanent? @@ -128,6 +130,11 @@ Vis Windows-hurtigmeny Åpne med Velg et program for å åpne med + Fail to delete {0} + File not found: {0} + Fail to open {0} + Fail to set text in clipboard + Fail to set files/folders in clipboard {0} ledig av {1} @@ -141,19 +148,32 @@ Advarsel: Everything-tjenesten kjører ikke Feil under spørring av Everything Sorter etter - Navn - Sti - Størrelse - Utvidelse - Typenavn - Dato opprettet - Dato endret - Attributter - Filliste Filnavn - Antall kjøringer - Dato nylig endret - Dato for tilgang - Dato for kjøring + Name ↑ + Name ↓ + Path ↑ + Path ↓ + Size ↑ + Size ↓ + Extension ↑ + Extension ↓ + Type Name ↑ + Type Name ↓ + Date Created ↑ + Date Created ↓ + Date Modified ↑ + Date Modified ↓ + Attributes ↑ + Attributes ↓ + File List FileName ↑ + File List FileName ↓ + Run Count ↑ + Run Count ↓ + Date Recently Changed ↑ + Date Recently Changed ↓ + Date Accessed ↑ + Date Accessed ↓ + Date Run ↑ + Date Run ↓ Advarsel: dette er ikke et hurtigsorteringsalternativ, søk kan være trege diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nl.xaml index c4bfb639d..cc6d350c9 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nl.xaml @@ -5,6 +5,8 @@ Please make a selection first Please select a folder path. Please choose a different name or folder path. + Are you sure you want to delete this quick access link? + Are you sure you want to delete this index search excluded path? Please select a folder link Are you sure you want to delete {0}? Are you sure you want to permanently delete this file? @@ -128,6 +130,11 @@ Show Windows Context Menu Open With Select a program to open with + Fail to delete {0} + File not found: {0} + Fail to open {0} + Fail to set text in clipboard + Fail to set files/folders in clipboard {0} free of {1} @@ -141,19 +148,32 @@ Waarschuwing: Everything service is niet actief Fout bij het opvragen Everything Sorteer op - Name - Pad - Size - Uitbreiding - Type naam - Datum aangemaakt - Datum gewijzigd - Kenmerken - Bestandslijst Bestandsnaam - Aantal keer uitgevoerd - Datum onlangs gewijzigd - Datum laatst geopend - Datum uitvoering + Name ↑ + Name ↓ + Path ↑ + Path ↓ + Size ↑ + Size ↓ + Extension ↑ + Extension ↓ + Type Name ↑ + Type Name ↓ + Date Created ↑ + Date Created ↓ + Date Modified ↑ + Date Modified ↓ + Attributes ↑ + Attributes ↓ + File List FileName ↑ + File List FileName ↓ + Run Count ↑ + Run Count ↓ + Date Recently Changed ↑ + Date Recently Changed ↓ + Date Accessed ↑ + Date Accessed ↓ + Date Run ↑ + Date Run ↓ Waarschuwing: Dit is geen snelle sorteeroptie, zoekopdrachten kunnen traag zijn diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pl.xaml index ddf56ab37..c981c2832 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pl.xaml @@ -5,6 +5,8 @@ Najpierw dokonaj wyboru Wybierz ścieżkę folderu. Wybierz inną nazwę lub ścieżkę folderu. + Are you sure you want to delete this quick access link? + Are you sure you want to delete this index search excluded path? Musisz wybrać któryś folder z listy Czy jesteś pewien że chcesz usunąć {0}? Jesteś pewny, że chcesz usunąć ten plik trwale? @@ -128,6 +130,11 @@ Pokaż menu kontekstowe Windows Otwórz za pomocą Wybierz program do otwarcia + Fail to delete {0} + File not found: {0} + Fail to open {0} + Fail to set text in clipboard + Fail to set files/folders in clipboard {0} wolne z {1} @@ -141,19 +148,32 @@ Everything Service nie jest uruchomiony Wystąpił błąd podczas pobierania wyników z Everything Sortuj wg - Nazwa - Ścieżka - Rozmiar - Rozszerzenia - Nazwa typu - Data utworzenia - Data modyfikacji - Atrybuty - Nazwa pliku na liście plików - Liczba uruchomień - Data ostatniej zmiany - Data dostępu - Data uruchomienia + Name ↑ + Name ↓ + Path ↑ + Path ↓ + Size ↑ + Size ↓ + Extension ↑ + Extension ↓ + Type Name ↑ + Type Name ↓ + Date Created ↑ + Date Created ↓ + Date Modified ↑ + Date Modified ↓ + Attributes ↑ + Attributes ↓ + File List FileName ↑ + File List FileName ↓ + Run Count ↑ + Run Count ↓ + Date Recently Changed ↑ + Date Recently Changed ↓ + Date Accessed ↑ + Date Accessed ↓ + Date Run ↑ + Date Run ↓ Uwaga: To nie jest opcja Szybkiego sortowania, wyszukiwania mogą być wolne diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-br.xaml index d0a260290..fe4fa320a 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-br.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-br.xaml @@ -5,6 +5,8 @@ Please make a selection first Please select a folder path. Please choose a different name or folder path. + Are you sure you want to delete this quick access link? + Are you sure you want to delete this index search excluded path? Please select a folder link Are you sure you want to delete {0}? Are you sure you want to permanently delete this file? @@ -128,6 +130,11 @@ Show Windows Context Menu Open With Select a program to open with + Fail to delete {0} + File not found: {0} + Fail to open {0} + Fail to set text in clipboard + Fail to set files/folders in clipboard {0} free of {1} @@ -141,19 +148,32 @@ Warning: Everything service is not running Error while querying Everything Sort By - Nome - Path - Tamanho - Extension - Type Name - Date Created - Date Modified - Attributes - File List FileName - Run Count - Date Recently Changed - Date Accessed - Date Run + Name ↑ + Name ↓ + Path ↑ + Path ↓ + Size ↑ + Size ↓ + Extension ↑ + Extension ↓ + Type Name ↑ + Type Name ↓ + Date Created ↑ + Date Created ↓ + Date Modified ↑ + Date Modified ↓ + Attributes ↑ + Attributes ↓ + File List FileName ↑ + File List FileName ↓ + Run Count ↑ + Run Count ↓ + Date Recently Changed ↑ + Date Recently Changed ↓ + Date Accessed ↑ + Date Accessed ↓ + Date Run ↑ + Date Run ↓ Warning: This is not a Fast Sort option, searches may be slow diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-pt.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-pt.xaml index 98a77d06a..d0651cc53 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-pt.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-pt.xaml @@ -5,6 +5,8 @@ Tem que efetuar uma seleção Selecione o caminho da pasta. Por favor, escolha um nome ou caminho diferente. + Tem a certeza de que pretende eliminar esta ligação de acesso rápido? + Tem a certeza de que deseja eliminar este caminho do índice de pesquisas? Selecione a ligação para a pasta Tem a certeza de que deseja eliminar {0}? Tem a certeza de que pretende eliminar permanentemente este ficheiro? @@ -128,6 +130,11 @@ Mostrar menu de contexto do Windows Abrir com Selecione o programa a utilizar + Falha ao eliminar {0} + Ficheiro não encontrado: {0} + Falha ao abrir {0} + Falha ao definir texto na área de transferência + Falha ao definir ficheiros/pasta na área de transferência {0} livre de {1} no total @@ -141,19 +148,32 @@ Aviso: o serviço Everything não está em execução Erro ao consultar Everything Ordenar por - Nome - Caminho - Tamanho - Extensão - Nome do tipo - Data de criação - Data de modificação - Atributos - Por nome na lista de ficheiros - Número de execuções - Data alterada recentemente - Data de acesso - Data de execução + Nome ↑ + Nome ↓ + Caminho ↑ + Caminho ↓ + Tamanho ↑ + Tamanho ↓ + Extensão ↑ + Extensão ↓ + Tipo ↑ + Tipo ↓ + Data de criação ↑ + Data de criação ↓ + Data de modificação ↑ + Data de modificação ↓ + Atributos ↑ + Atributos ↓ + Nome de ficheiro na lista ↑ + Nome de ficheiro na lista ↓ + Número de execuções ↑ + Número de execuções ↓ + Alterado recentemente ↑ + Alterado recentemente ↓ + Data de acesso ↑ + Data de acesso ↓ + Data de execução ↑ + Data de execução ↓ Aviso: esta não é uma opção de ordenação rápida e as pesquisas podem ser demoradas diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml index 44a3a9b65..1644745ae 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml @@ -5,6 +5,8 @@ Сначала отметьте что-нибудь Please select a folder path. Please choose a different name or folder path. + Are you sure you want to delete this quick access link? + Are you sure you want to delete this index search excluded path? Пожалуйста, выберите ссылку на папку Вы уверены, что хотите удалить {0}? Вы действительно хотите безвозвратно удалить этот файл? @@ -128,6 +130,11 @@ Show Windows Context Menu Open With Select a program to open with + Fail to delete {0} + File not found: {0} + Fail to open {0} + Fail to set text in clipboard + Fail to set files/folders in clipboard {0} free of {1} @@ -141,19 +148,32 @@ Warning: Everything service is not running Error while querying Everything Sort By - Name - Path - Размер - Extension - Type Name - Date Created - Date Modified - Attributes - File List FileName - Run Count - Date Recently Changed - Date Accessed - Date Run + Name ↑ + Name ↓ + Path ↑ + Path ↓ + Size ↑ + Size ↓ + Extension ↑ + Extension ↓ + Type Name ↑ + Type Name ↓ + Date Created ↑ + Date Created ↓ + Date Modified ↑ + Date Modified ↓ + Attributes ↑ + Attributes ↓ + File List FileName ↑ + File List FileName ↓ + Run Count ↑ + Run Count ↓ + Date Recently Changed ↑ + Date Recently Changed ↓ + Date Accessed ↑ + Date Accessed ↓ + Date Run ↑ + Date Run ↓ Warning: This is not a Fast Sort option, searches may be slow diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sk.xaml index 7c3f5deff..3ca738b69 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sk.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sk.xaml @@ -5,6 +5,8 @@ Najprv vyberte položku Vyberte cestu k priečinku. Vyberte iný názov alebo cestu k priečinku. + Naozaj chcete odstrániť tento odkaz na rýchly prístup? + Naozaj chcete odstrániť vylúčenú adresu na tento vyhľadávací index? Vyberte odkaz na priečinok Naozaj chcete odstrániť {0}? Naozaj chcete natrvalo odstrániť tento súbor? @@ -128,6 +130,11 @@ Zobraziť kontextovú ponuku Windowsu Otvoriť s Vyberte program, ktorým chcete otvoriť súbor + Odstránenie {0} zlyhalo + Súbor sa nenašiel: {0} + Nepodarilo sa otvoriť {0} + Nepodarilo sa nastaviť text v schránke + Nepodarilo sa nastaviť súbory/priečinky v schránke Voľných {0} z {1} @@ -141,19 +148,32 @@ Upozornenie: Služba Everything nie je spustená Chyba pri dopytovaní Everything Zoradiť podľa - Názov - Cesta - Veľkosť - Prípona - Typ - Dátum vytvorenia - Dátum úpravy - Atribúty - Zoznam názvov súborov - Počet spustení - Nedávno zmenený dátum - Dátum prístupu - Dátum spustenia + Názov ↑ + Názov ↓ + Cesta ↑ + Cesta ↓ + Veľkosť ↑ + Veľkosť ↓ + Prípona ↑ + Prípona ↓ + Typ ↑ + Typ ↓ + Dátum vytvorenia ↑ + Dátum vytvorenia ↓ + Dátum úpravy ↑ + Dátum úpravy ↓ + Atribúty ↑ + Atribúty ↓ + Názov súboru zoznamu súborov ↑ + Názov súboru zoznamu súborov ↓ + Počet spustení ↑ + Počet spustení ↓ + Dátum nedávnej zmeny ↑ + Dátum nedávnej zmeny ↓ + Dátum prístupu ↑ + Dátum prístupu ↓ + Dátum spustenia ↑ + Dátum spustenia ↓ Upozornenie: Toto nie je voľba Fast Sort, vyhľadávanie môže byť pomalé diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr-Cyrl-RS.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr-Cyrl-RS.xaml new file mode 100644 index 000000000..19fe6dc64 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr-Cyrl-RS.xaml @@ -0,0 +1,210 @@ + + + + + Please make a selection first + Please select a folder path. + Please choose a different name or folder path. + Are you sure you want to delete this quick access link? + Are you sure you want to delete this index search excluded path? + Please select a folder link + Are you sure you want to delete {0}? + Are you sure you want to permanently delete this file? + Are you sure you want to permanently delete this file/folder? + Deletion successful + Successfully deleted {0} + Assigning the global action keyword could bring up too many results during search. Please choose a specific action keyword + Quick Access can not be set to the global action keyword when enabled. Please choose a specific action keyword + The required service for Windows Index Search does not appear to be running + To fix this, start the Windows Search service. Select here to remove this warning + The warning message has been switched off. As an alternative for searching files and folders, would you like to install Everything plugin?{0}{0}Select 'Yes' to install Everything plugin, or 'No' to return + Explorer Alternative + Error occurred during search: {0} + Could not open folder + Could not open file + + + Delete + Edit + Add + General Setting + Customise Action Keywords + Customise Quick Access + Quick Access Links + Everything Setting + Preview Panel + Size + Date Created + Date Modified + File Age + Display File Info + Date and time format + Sort Option: + Everything Path: + Launch Hidden + Editor Path + Shell Path + Index Search Excluded Paths + Use search result's location as the working directory of the executable + Display more information like size and age in tooltips + Hit Enter to open folder in Default File Manager + Use Index Search For Path Search + Indexing Options + Search: + Path Search: + File Content Search: + Index Search: + Quick Access: + Current Action Keyword + Done + Enabled + When disabled Flow will not execute this search option, and will additionally revert back to '*' to free up the action keyword + Everything + Windows Index + Direct Enumeration + File Editor Path + Folder Editor Path + Enabled + Disabled + + Content Search Engine + Directory Recursive Search Engine + Index Search Engine + Open Windows Index Option + Excluded File Types (comma seperated) + For example: exe,jpg,png + Maximum results + The maximum number of results requested from active search engine + + + Explorer + Find and manage files and folders via Windows Search or Everything + + + Ctrl + Enter to open the directory + Ctrl + Enter to open the containing folder + {0}{4}Size: {1}{4}Date created: {2}{4}Date modified: {3} + Unknown + {0}{3}Space free: {1}{3}Total size: {2} + + + Copy path + Copy path of current item to clipboard + Copy name + Copy name of current item to clipboard + Copy + Copy current file to clipboard + Copy current folder to clipboard + Delete + Permanently delete current file + Permanently delete current folder + Name + Type + Path + File + Folder + Delete the selected + Run as different user + Run the selected using a different user account + Open containing folder + Open the location that contains current item + Open With Editor: + Failed to open file at {0} with Editor {1} at {2} + Open With Shell: + Failed to open folder {0} with Shell {1} at {2} + Exclude current and sub-directories from Index Search + Excluded from Index Search + Open Windows Indexing Options + Manage indexed files and folders + Failed to open Windows Indexing Options + Add to Quick Access + Add current item to Quick Access + Successfully Added + Successfully added to Quick Access + Successfully Removed + Successfully removed from Quick Access + Add to Quick Access so it can be opened with Explorer's Search Activation action keyword + Remove from Quick Access + Remove from Quick Access + Remove current item from Quick Access + Show Windows Context Menu + Open With + Select a program to open with + Fail to delete {0} + File not found: {0} + Fail to open {0} + Fail to set text in clipboard + Fail to set files/folders in clipboard + + + {0} free of {1} + Open in Default File Manager + + Use '>' to search in this directory, '*' to search for file extensions or '>*' to combine both searches. + + + + Failed to load Everything SDK + Warning: Everything service is not running + Error while querying Everything + Sort By + Name ↑ + Name ↓ + Path ↑ + Path ↓ + Size ↑ + Size ↓ + Extension ↑ + Extension ↓ + Type Name ↑ + Type Name ↓ + Date Created ↑ + Date Created ↓ + Date Modified ↑ + Date Modified ↓ + Attributes ↑ + Attributes ↓ + File List FileName ↑ + File List FileName ↓ + Run Count ↑ + Run Count ↓ + Date Recently Changed ↑ + Date Recently Changed ↓ + Date Accessed ↑ + Date Accessed ↓ + Date Run ↑ + Date Run ↓ + + + Warning: This is not a Fast Sort option, searches may be slow + + Search Full Path + Enable File/Folder Run Count + + Click to launch or install Everything + Everything Installation + Installing Everything service. Please wait... + Successfully installed Everything service + Failed to automatically install Everything service. Please manually install it from https://www.voidtools.com + Click here to start it + Unable to find an Everything installation, would you like to manually select a location?{0}{0}Click no and Everything will be automatically installed for you + Do you want to enable content search for Everything? + It can be very slow without index (which is only supported in Everything v1.5+) + + Unable to find Everything.exe + Failed to install Everything, please install it manually + + + Native Context Menu + Display native context menu (experimental) + Below you can specify items you want to include in the context menu, they can be partial (e.g. 'pen wit') or complete ('Open with'). + Below you can specify items you want to exclude from context menu, they can be partial (e.g. 'pen wit') or complete ('Open with'). + + + Today + {0} days ago + 1 month ago + {0} months ago + 1 year ago + {0} years ago + diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr.xaml index 4df64e4d5..f8effbd7c 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr.xaml @@ -5,6 +5,8 @@ Please make a selection first Please select a folder path. Please choose a different name or folder path. + Are you sure you want to delete this quick access link? + Are you sure you want to delete this index search excluded path? Please select a folder link Are you sure you want to delete {0}? Are you sure you want to permanently delete this file? @@ -128,6 +130,11 @@ Show Windows Context Menu Open With Select a program to open with + Fail to delete {0} + File not found: {0} + Fail to open {0} + Fail to set text in clipboard + Fail to set files/folders in clipboard {0} free of {1} @@ -141,19 +148,32 @@ Warning: Everything service is not running Error while querying Everything Sort By - Name - Path - Size - Extension - Type Name - Date Created - Date Modified - Attributes - File List FileName - Run Count - Date Recently Changed - Date Accessed - Date Run + Name ↑ + Name ↓ + Path ↑ + Path ↓ + Size ↑ + Size ↓ + Extension ↑ + Extension ↓ + Type Name ↑ + Type Name ↓ + Date Created ↑ + Date Created ↓ + Date Modified ↑ + Date Modified ↓ + Attributes ↑ + Attributes ↓ + File List FileName ↑ + File List FileName ↓ + Run Count ↑ + Run Count ↓ + Date Recently Changed ↑ + Date Recently Changed ↓ + Date Accessed ↑ + Date Accessed ↓ + Date Run ↑ + Date Run ↓ Warning: This is not a Fast Sort option, searches may be slow diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/tr.xaml index ed327e1bc..aefe8af30 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/tr.xaml @@ -2,21 +2,23 @@ - Please make a selection first - Please select a folder path. - Please choose a different name or folder path. + Lütfen önce bir seçim yapın + Lütfen bir klasör yolu seçin. + Lütfen farklı bir isim veya klasör yolu seçin. + Bu hızlı erişim bağlantısını silmek istediğinizden emin misiniz? + Are you sure you want to delete this index search excluded path? Lütfen bir klasör bağlantısı seçin {0} bağlantısını silmek istediğinize emin misiniz? Bu dosyayı kalıcı olarak silmek istediğinizden emin misiniz? Bu öğeyi kalıcı olarak silmek istediğinizden emin misiniz? - Deletion successful + Silme işlemi başarılı Başarıyla silindi: {0} - Assigning the global action keyword could bring up too many results during search. Please choose a specific action keyword - Quick Access can not be set to the global action keyword when enabled. Please choose a specific action keyword - The required service for Windows Index Search does not appear to be running - To fix this, start the Windows Search service. Select here to remove this warning - The warning message has been switched off. As an alternative for searching files and folders, would you like to install Everything plugin?{0}{0}Select 'Yes' to install Everything plugin, or 'No' to return - Explorer Alternative + Genel eylem anahtar sözcüğünü atamak, arama sırasında çok fazla sonuç getirebilir. Lütfen belirli bir eylem anahtar sözcüğü seçin + Hızlı Erişim etkinleştirildiğinde genel eylem anahtar sözcüğüne ayarlanamaz. Lütfen belirli bir eylem anahtar sözcüğü seçin + Windows Dizin Araması için gerekli hizmet çalışıyor gibi görünmüyor + Bunu düzeltmek için Windows Arama hizmetini başlatın. Bu uyarıyı kaldırmak için burayı seçin + Uyarı mesajı kapatıldı. Dosya ve klasörleri aramak için bir alternatif olarak Everything eklentisini yüklemek ister misiniz?{0}{0}Everything eklentisini yüklemek için 'Evet'i veya geri dönmek için 'Hayır'ı seçin + Explorer Alternatifi Arama sırasında hata oluştu: {0} Klasör açılamadı Dosya açılamadı @@ -27,113 +29,118 @@ Ekle Genel Ayarlar Anahtar Kelimeler - Customise Quick Access - Quick Access Links + Hızlı Erişim'i Özelleştirin + Hızlı Erişim Bağlantıları Everything Ayarları Önizleme Paneli Boyut Oluşturma Tarihi Değiştirme Tarihi - File Age + Dosya Yaşı Dosya Özelliklerini Göster Tarih ve saat biçimi Sıralama Seçeneği: Everything Kurulumu: - Launch Hidden + Gizli Başlat Metin Düzenleyici Komut İstemi Hariç Tutulan Dizinler Programın çalışma klasörü olarak sonuç klasörünü kullan - Display more information like size and age in tooltips - Hit Enter to open folder in Default File Manager - Use Index Search For Path Search - Indexing Options + Araç ipuçlarında boyut ve yaş gibi daha fazla bilgi göster + Klasörü varsayılan Dosya Yöneticisi'nde açmak için Enter tuşuna basın + Yol Araması İçin Dizin Aramasını Kullan + Dizin Oluşturma Seçenekleri Ara: Yolu Ara: Dosya İçeriğini Ara: - Index Search: + Dizin Araması: Hızlı Erişim: Geçerli Anahtar Kelime Tamam Etkin - When disabled Flow will not execute this search option, and will additionally revert back to '*' to free up the action keyword + Devre dışı bırakıldığında Flow bu arama seçeneğini yürütmez ve eylem anahtar sözcüğünü serbest bırakmak için ek olarak '*' değerine geri döner Everything Windows Arama Doğrudan Sayım Metin Düzenleyici Klasör Düzenleyici - Enabled - Disabled + Aktif + Devre dışı Dosya İçeriği Arama Motoru - Directory Recursive Search Engine + Dizin Yinelemeli Arama Motoru Arama Motoru Windows Dizin Oluşturma Ayarları - Excluded File Types (comma seperated) - For example: exe,jpg,png - Maximum results - The maximum number of results requested from active search engine + Hariç Tutulan Dosya Türleri (virgülle ayrılmış) + Örneğin: exe,jpg,png + Maksimum sonuç + Etkin arama motorundan istenen maksimum sonuç sayısı Dosya Gezgini - Find and manage files and folders via Windows Search or Everything + Windows Arama veya Everything aracılığıyla dosya ve klasörleri bulun ve yönetin Dizini açmak içi Ctrl + Enter Dosya konumunu açmak için Ctrl + Enter - {0}{4}Size: {1}{4}Date created: {2}{4}Date modified: {3} - Unknown - {0}{3}Space free: {1}{3}Total size: {2} + {0}{4}Boyut: {1}{4}Oluşturulma tarihi: {2}{4}Değiştirildiği tarih: {3} + Bilinmeyen + {0}{3}Boş alan: {1}{3}Toplam boyut: {2} Yolu kopyala Mevcut dosya konumunu panoya kopyala - Copy name - Copy name of current item to clipboard + İsmi kopyala + Mevcut dosyanın adını panoya kopyala Kopyala Mevcut dosyayı panoya kopyala Mevcut klasörü panoya kopyala Sil Mevcut dosyayı kalıcı olarak sil Mevcut klasörü kalıcı olarak sil - Name - Type - Path + İsim + Tür + Yol Dosya Klasör Seçileni sil Başka bir kullanıcı olarak çalıştır - Run the selected using a different user account + Seçileni farklı bir kullanıcı hesabı kullanarak çalıştır Dosya konumunu aç - Open the location that contains current item + Geçerli öğeyi içeren konumu aç Metin Düzenleyici ile Aç: - Failed to open file at {0} with Editor {1} at {2} + {0} dosyası, {1} Düzenleyicisi ile {2} konumunda açılamadı Komut İstemi ile Aç: - Failed to open folder {0} with Shell {1} at {2} - Exclude current and sub-directories from Index Search - Excluded from Index Search + {0} klasörü, {1} Kabuğu ile {2} konumunda açılamadı + Mevcut ve alt dizinleri Dizin Araması'ndan hariç tut + Dizin Araması'ndan hariç tutuldu Windows Dizin Oluşturma Ayarları - Manage indexed files and folders + Dizinlenmiş dosya ve klasörleri yönet Windows Dizin Oluşturma ayarlarını açma başarısız oldu Hızlı Erişime Sabitle - Add current item to Quick Access + Geçerli öğeyi Hızlı Erişim'e ekle Başarıyla Eklendi - Successfully added to Quick Access + Hızlı Erişim'e başarıyla eklendi Başarıyla Kaldırıldı - Successfully removed from Quick Access + Hızlı Erişim'den başarıyla kaldırıldı Add to Quick Access so it can be opened with Explorer's Search Activation action keyword Hızlı Erişimden Kaldır Hızlı Erişimden Kaldır - Remove current item from Quick Access + Geçerli öğeyi Hızlı Erişim'den kaldır Windows Bağlam Menüsünü Aç Birlikte Aç - Select a program to open with + Birlikte açmak için bir program seç + {0} silinemedi + Dosya bulunamadı: {0} + {0} açılamadı + Panodaki metin ayarlanamıyor + Panodaki dosyalar/klasörler ayarlanamıyor - {0} free of {1} + {0} / {1} boş Dosya Yöneticisinde Aç - Use '>' to search in this directory, '*' to search for file extensions or '>*' to combine both searches. + Bu dizinde arama yapmak için '>', dosya uzantılarını aramak için '*' veya her iki aramayı birleştirmek için '>*' kullanın. @@ -141,25 +148,38 @@ Everything Servisi çalışmıyor Sorgu Everything üzerinde çalıştırılırken hata oluştu Şuna Göre Sırala - Name - Yol - Boyut - Uzantı - Tür - Oluşturma Tarihi - Değiştirme Tarihi - Özellikler - File List FileName - Erişim Sayısı - Date Recently Changed - Erişim Tarihi - Date Run + İsim ↑ + İsim ↓ + Yol ↑ + Yol ↓ + Boyut ↑ + Boyut ↓ + Uzantı ↑ + Uzantı ↓ + Tür Adı ↑ + Tür Adı ↓ + Oluşturma Tarihi ↑ + Oluşturma Tarihi ↓ + Değiştirme Tarihi ↑ + Değiştirme Tarihi ↓ + Özellikler ↑ + Özellikler ↓ + Dosya Listesi DosyaAdı ↑ + Dosya Listesi DosyaAdı ↓ + Çalıştırma Sayısı ↑ + Çalıştırma Sayısı ↓ + Son Değişiklik Tarihi ↑ + Son Değişiklik Tarihi ↓ + Erişim Tarihi ↑ + Erişim Tarihi ↓ + Çalıştırma Tarihi ↑ + Çalıştırma Tarihi ↓ - Warning: This is not a Fast Sort option, searches may be slow + Uyarı: Bu, Hızlı Sıralama seçeneği değildir, aramalar yavaş olabilir Tam Yolu Ara - Enable File/Folder Run Count + Dosya/Klasör Çalıştırma Sayısını Aç Everything'i yükle veya başlat Everything Kurulumu @@ -167,18 +187,18 @@ Everything hizmeti başarıyla yüklendi Everything hizmetini otomatik olarak yükleme başarısız oldu. Lütfen https://www.voidtools.com adresinden elle yükleyin. Başlat - Unable to find an Everything installation, would you like to manually select a location?{0}{0}Click no and Everything will be automatically installed for you - Do you want to enable content search for Everything? - It can be very slow without index (which is only supported in Everything v1.5+) + Herhangi bir Everything kurulumu bulunamadı, bir konum seçmek ister misiniz? {0}{0} Hayır'a tıkladığınızda, Everything otomatik olarak sizin için yüklenecektir + Everything için içerik aramasını açmak ister misiniz? + Dizin olmadan çok yavaş olabilir (yalnızca Everything v1.5+ sürümünde desteklenir) - Unable to find Everything.exe - Failed to install Everything, please install it manually + Everything.exe bulunamadı + Everything yüklenemedi, lütfen manuel olarak yükleyin - Native Context Menu - Display native context menu (experimental) - Below you can specify items you want to include in the context menu, they can be partial (e.g. 'pen wit') or complete ('Open with'). - Below you can specify items you want to exclude from context menu, they can be partial (e.g. 'pen wit') or complete ('Open with'). + Yerel Bağlam Menüsünü Aç + Yerel bağlam menüsünü göster (deneysel) + Aşağıda, içerik menüsüne dahil etmek istediğiniz öğeleri belirtebilirsiniz, bunlar kısmi ('irlikte a' gibi) veya tam ('Birlikte aç') olabilir. + Aşağıda, içerik menüsünden hariç tutmak istediğiniz öğeleri belirtebilirsiniz, bunlar kısmi ('irlikte a' gibi) veya tam ('Birlikte aç') olabilir. Bugün diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/uk-UA.xaml index 38829112c..823c33193 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/uk-UA.xaml @@ -5,6 +5,8 @@ Будь ласка, спочатку зробіть вибір Виберіть шлях до теки. Виберіть інше ім'я або шлях до теки. + Упевнені, що хочете видалити це посилання швидкого доступу? + Упевнені, що хочете видалити цей шлях, виключений з індексу пошуку? Будь ласка, оберіть посилання на теку Ви впевнені, що хочете видалити {0}? Ви впевнені, що хочете назавжди видалити цей файл? @@ -45,7 +47,7 @@ Виключені шляхи індексного пошуку Використовувати розташування результату пошуку як робочу директорію виконуваного файлу Показувати більше інформації, наприклад розмір і дату створення, у підказках - Натисніть Enter, щоб відкрити папку у файловому менеджері за замовчуванням + Натисніть Enter, щоб відкрити теку у типовому файловому менеджері Використовуйте індексний пошук для пошуку шляху Параметри індексації Пошук: @@ -56,7 +58,7 @@ Поточне ключове слово дії Готово Увімкнено - Якщо вимкнено, Flow не виконуватиме цю опцію пошуку і додатково повернеться до '*', щоб звільнити ключове слово дії + Якщо вимкнено, Flow не виконуватиме цей параметр пошуку і додатково повернеться до «*», щоб звільнити ключове слово дії Everything Індекс Windows Пряме перерахування @@ -116,22 +118,27 @@ Керування проіндексованими файлами та папками Не вдалося відкрити параметри індексування Windows Додати до Швидкого доступу - Додати поточний елемент до швидкого доступу + Додати поточний елемент до Швидкого доступу Успішно додано Успішно додано до швидкого доступу Успішно вилучено Успішно видалено з швидкого доступу - Додати до швидкого доступу, щоб його можна було відкрити за допомогою ключового слова дії "Активація пошуку" у Провіднику + Додати до Швидкого доступу, щоб його можна було відкрити за допомогою ключового слова дії «Активація пошуку» у Провіднику Видалити зі швидкого доступу Видалити зі швидкого доступу Видалити поточний елемент зі швидкого доступу Показати контекстне меню Windows Відкрити за допомогою Виберіть програму для відкриття + Не вдалося видалити {0} + Файл не знайдено: {0} + Не вдалося відкрити {0} + Не вдалося вставити текст у буфер обміну + Не вдалося вставити файли/теки в буфер обміну {0} не містить {1} - Відкрити у файловому менеджері за замовчуванням + Відкрити у типовому файловому менеджері Використовуйте '>' для пошуку в цьому каталозі, '*' для пошуку за розширеннями файлів або '>*' для поєднання обох варіантів пошуку. @@ -141,19 +148,32 @@ Попередження: Служба Everything не запущена Помилка при виконанні запиту Everything Сортувати за - Назва - Шлях - Розмір - Розширення - Назва типу - Дата створення - Дата останньої зміни - Атрибути - Список файлів FileName - Кількість запусків - Дата нещодавно змінена - Дата доступу - Дата запуску + Назва ↑ + Назва ↓ + Шлях ↑ + Шлях ↓ + Розмір ↑ + Розмір ↓ + Розширення ↑ + Розширення ↓ + Тип назви ↑ + Тип назви ↓ + Дата створення ↑ + Дата створення ↓ + Дата зміни ↑ + Дата зміни ↓ + Атрибути ↑ + Атрибути ↓ + Список файлів FileName ↑ + Список файлів FileName ↓ + Кількість запусків ↑ + Кількість запусків ↓ + Нещодавно змінено ↑ + Нещодавно змінено ↓ + Дата доступу ↑ + Дата доступу ↓ + Дата запуску ↑ + Дата запуску ↓ Попередження: Це не швидке сортування, пошук може бути повільним diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/vi.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/vi.xaml index de6f9a6a4..7b64af3d8 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/vi.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/vi.xaml @@ -5,6 +5,8 @@ Vui lòng lựa chọn trước Please select a folder path. Please choose a different name or folder path. + Are you sure you want to delete this quick access link? + Are you sure you want to delete this index search excluded path? Hãy chọn một thư mục. Bạn có chắc chắn muốn xóa {0} không? Bạn có chắc là muốn xóa vĩnh viễn các ảnh này? @@ -128,6 +130,11 @@ Show Windows Context Menu Mở bằng Select a program to open with + Fail to delete {0} + File not found: {0} + Fail to open {0} + Fail to set text in clipboard + Fail to set files/folders in clipboard {0} phần {1} @@ -141,19 +148,32 @@ Warning: Everything service is not running Error while querying Everything Sắp xếp theo - Tên - Đường dẫn - Kích thước - Mở rộng - Loại tên - Ngày Tạo - ngày sửa đổi - Thuộc tính - File List FileName - Số lần chạy - Date Recently Changed - Ngày truy cập - Date Run + Name ↑ + Name ↓ + Path ↑ + Path ↓ + Size ↑ + Size ↓ + Extension ↑ + Extension ↓ + Type Name ↑ + Type Name ↓ + Date Created ↑ + Date Created ↓ + Date Modified ↑ + Date Modified ↓ + Attributes ↑ + Attributes ↓ + File List FileName ↑ + File List FileName ↓ + Run Count ↑ + Run Count ↓ + Date Recently Changed ↑ + Date Recently Changed ↓ + Date Accessed ↑ + Date Accessed ↓ + Date Run ↑ + Date Run ↓ Warning: This is not a Fast Sort option, searches may be slow diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml index 79d8fc66d..8ad979ac8 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml @@ -5,6 +5,8 @@ 请先进行选择 请选择一个文件夹路径。 请选择不同的名称或文件夹路径。 + 您确定要删除此快速访问链接吗? + 您确定要删除此索引搜索排除路径吗? 请选择一个文件夹链接 您确定要删除 {0} 吗? 您确定要永久删除此文件吗? @@ -128,6 +130,11 @@ 显示 Windows 上下文菜单 打开方式 选择要打开的程序 + 无法删除 {0} + 未找到文件:{0} + 无法打开 {0} + 无法复制文本到剪切板 + 无法复制文件/文件夹到剪切板 {0} 可用,共 {1} @@ -141,19 +148,32 @@ 警告:Everything 服务未运行 Everything 插件发生了一个错误(回车拷贝具体错误信息) 排序依据 - 名称 - 路径 - 大小 - 扩展名 - 类型名称 - 创建日期 - 修改日期 - 属性 - 文件列表名 - 运行次数 - 最近更改日期 - 访问日期 - 运行日期 + 名称 ↑ + 名称 ↓ + 路径 ↑ + 路径 ↓ + 大小 ↑ + 大小 ↓ + 扩展名 ↑ + 扩展名 ↓ + 类型名称 ↑ + 类型名称 ↓ + 创建日期 ↑ + 创建日期 ↓ + 修改日期 ↑ + 修改日期 ↓ + 属性 ↑ + 属性 ↓ + 文件列表名 ↑ + 文件列表名 ↓ + 运行次数 ↑ + 运行次数 ↓ + 最近更改日期 ↑ + 最近更改日期 ↓ + 访问日期 ↑ + 访问日期 ↓ + 运行日期 ↑ + 运行日期 ↓ 警告:这不是一个快速排序选项,搜索可能较慢。 diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-tw.xaml index c4e22066d..39f260499 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-tw.xaml @@ -5,6 +5,8 @@ Please make a selection first Please select a folder path. Please choose a different name or folder path. + Are you sure you want to delete this quick access link? + Are you sure you want to delete this index search excluded path? 請選擇一個資料夾 你確認要刪除{0}嗎? Are you sure you want to permanently delete this file? @@ -128,6 +130,11 @@ Show Windows Context Menu Open With Select a program to open with + Fail to delete {0} + File not found: {0} + Fail to open {0} + Fail to set text in clipboard + Fail to set files/folders in clipboard {0} free of {1} @@ -141,19 +148,32 @@ Everything Service 尚未啟動 Everything 套件發生錯誤(Enter 複製具體錯誤訊息) 排序依據 - 名稱 - 路徑 - 大小 - 擴展程序 - 類型 - 創建日期 - 修改日期 - 屬性 - File List FileName - 執行次數 - 近期變更 - 存取日期 - Date Run + Name ↑ + Name ↓ + Path ↑ + Path ↓ + Size ↑ + Size ↓ + Extension ↑ + Extension ↓ + Type Name ↑ + Type Name ↓ + Date Created ↑ + Date Created ↓ + Date Modified ↑ + Date Modified ↓ + Attributes ↑ + Attributes ↓ + File List FileName ↑ + File List FileName ↓ + Run Count ↑ + Run Count ↓ + Date Recently Changed ↑ + Date Recently Changed ↓ + Date Accessed ↑ + Date Accessed ↓ + Date Run ↑ + Date Run ↓ Warning: This is not a Fast Sort option, searches may be slow diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs index 1c5d074a0..d93c6c77b 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -10,21 +10,25 @@ using System.Threading; using System.Threading.Tasks; using System.Windows.Controls; using Flow.Launcher.Plugin.Explorer.Exceptions; +using System.Linq; +using System.Globalization; namespace Flow.Launcher.Plugin.Explorer { - public class Main : ISettingProvider, IAsyncPlugin, IContextMenu, IPluginI18n + public class Main : ISettingProvider, IAsyncPlugin, IContextMenu, IPluginI18n, IAsyncDialogJump { internal static PluginInitContext Context { get; set; } - internal Settings Settings; + internal static Settings Settings { get; set; } private SettingsViewModel viewModel; - private IContextMenu contextMenu; + private ContextMenu contextMenu; private SearchManager searchManager; + private static readonly List _emptyDialogJumpResultList = new(); + public Control CreateSettingPanel() { return new ExplorerSettings(viewModel); @@ -35,14 +39,12 @@ namespace Flow.Launcher.Plugin.Explorer Context = context; Settings = context.API.LoadSettingJsonStorage(); + FillQuickAccessLinkNames(); viewModel = new SettingsViewModel(context, Settings); - - contextMenu = new ContextMenu(Context, Settings, viewModel); + contextMenu = new ContextMenu(Context, Settings); searchManager = new SearchManager(Settings, Context); ResultManager.Init(Context, Settings); - - SortOptionTranslationHelper.API = context.API; EverythingApiDllImport.Load(Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "EverythingSDK", Environment.Is64BitProcess ? "x64" : "x86")); @@ -95,5 +97,36 @@ namespace Flow.Launcher.Plugin.Explorer { return Context.API.GetTranslation("plugin_explorer_plugin_description"); } + + public void OnCultureInfoChanged(CultureInfo newCulture) + { + // Update labels for setting view model + EverythingSortOptionLocalized.UpdateLabels(viewModel.AllEverythingSortOptions); + } + + private static void FillQuickAccessLinkNames() + { + // Legacy version does not have names for quick access links, so we fill them with the path name. + foreach (var link in Settings.QuickAccessLinks) + { + if (string.IsNullOrWhiteSpace(link.Name)) + { + link.Name = link.Path.GetPathName(); + } + } + } + + public async Task> QueryDialogJumpAsync(Query query, CancellationToken token) + { + try + { + var results = await searchManager.SearchAsync(query, token); + return results.Select(r => DialogJumpResult.From(r, r.CopyText)).ToList(); + } + catch (Exception e) when (e is SearchException or EngineNotAvailableException) + { + return _emptyDialogJumpResultList; + } + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs index 6159c9355..fd62566d5 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs @@ -1,5 +1,4 @@ -using Flow.Launcher.Plugin.Everything.Everything; -using Flow.Launcher.Plugin.Explorer.Search.Everything.Exceptions; +using Flow.Launcher.Plugin.Explorer.Search.Everything.Exceptions; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -36,7 +35,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything /// /// Checks whether the sort option is Fast Sort. /// - public static bool IsFastSortOption(SortOption sortOption) + public static bool IsFastSortOption(EverythingSortOption sortOption) { var fastSortOptionEnabled = EverythingApiDllImport.Everything_IsFastSort(sortOption); @@ -112,7 +111,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything EverythingApiDllImport.Everything_SetSort(option.SortOption); EverythingApiDllImport.Everything_SetMatchPath(option.IsFullPathSearch); - if (option.SortOption == SortOption.RUN_COUNT_DESCENDING) + if (option.SortOption == EverythingSortOption.RUN_COUNT_DESCENDING) { EverythingApiDllImport.Everything_SetRequestFlags(EVERYTHING_REQUEST_FULL_PATH_AND_FILE_NAME | EVERYTHING_REQUEST_RUN_COUNT); } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingApiDllImport.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingApiDllImport.cs index 5b80819fa..c952a980c 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingApiDllImport.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingApiDllImport.cs @@ -1,5 +1,4 @@ -using Flow.Launcher.Plugin.Everything.Everything; -using System; +using System; using System.IO; using System.Runtime.InteropServices; using System.Text; @@ -114,11 +113,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything // Everything 1.4 [DllImport(DLL)] - public static extern void Everything_SetSort(SortOption dwSortType); + public static extern void Everything_SetSort(EverythingSortOption dwSortType); [DllImport(DLL)] - public static extern bool Everything_IsFastSort(SortOption dwSortType); + public static extern bool Everything_IsFastSort(EverythingSortOption dwSortType); [DllImport(DLL)] - public static extern SortOption Everything_GetSort(); + public static extern EverythingSortOption Everything_GetSort(); [DllImport(DLL)] public static extern uint Everything_GetResultListSort(); [DllImport(DLL)] diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchOption.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchOption.cs index 92b8e9623..d8b670a08 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchOption.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchOption.cs @@ -1,10 +1,8 @@ -using Flow.Launcher.Plugin.Everything.Everything; - -namespace Flow.Launcher.Plugin.Explorer.Search.Everything +namespace Flow.Launcher.Plugin.Explorer.Search.Everything { public record struct EverythingSearchOption( string Keyword, - SortOption SortOption, + EverythingSortOption SortOption, bool IsContentSearch = false, string ContentSearchKeyword = default, string ParentPath = default, diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSortOption.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSortOption.cs new file mode 100644 index 000000000..6a3d7cb67 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSortOption.cs @@ -0,0 +1,59 @@ +using Flow.Launcher.Localization.Attributes; + +namespace Flow.Launcher.Plugin.Explorer.Search.Everything +{ + [EnumLocalize] + public enum EverythingSortOption : uint + { + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_name_ascending))] + NAME_ASCENDING = 1u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_name_descending))] + NAME_DESCENDING = 2u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_path_ascending))] + PATH_ASCENDING = 3u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_path_descending))] + PATH_DESCENDING = 4u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_size_ascending))] + SIZE_ASCENDING = 5u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_size_descending))] + SIZE_DESCENDING = 6u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_extension_ascending))] + EXTENSION_ASCENDING = 7u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_extension_descending))] + EXTENSION_DESCENDING = 8u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_type_name_ascending))] + TYPE_NAME_ASCENDING = 9u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_type_name_descending))] + TYPE_NAME_DESCENDING = 10u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_date_created_ascending))] + DATE_CREATED_ASCENDING = 11u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_date_created_descending))] + DATE_CREATED_DESCENDING = 12u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_date_modified_ascending))] + DATE_MODIFIED_ASCENDING = 13u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_date_modified_descending))] + DATE_MODIFIED_DESCENDING = 14u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_attributes_ascending))] + ATTRIBUTES_ASCENDING = 15u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_attributes_descending))] + ATTRIBUTES_DESCENDING = 16u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_file_list_filename_ascending))] + FILE_LIST_FILENAME_ASCENDING = 17u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_file_list_filename_descending))] + FILE_LIST_FILENAME_DESCENDING = 18u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_run_count_descending))] + RUN_COUNT_DESCENDING = 20u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_date_recently_changed_ascending))] + DATE_RECENTLY_CHANGED_ASCENDING = 21u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_date_recently_changed_descending))] + DATE_RECENTLY_CHANGED_DESCENDING = 22u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_date_accessed_ascending))] + DATE_ACCESSED_ASCENDING = 23u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_date_accessed_descending))] + DATE_ACCESSED_DESCENDING = 24u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_date_run_ascending))] + DATE_RUN_ASCENDING = 25u, + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_everything_sort_by_date_run_descending))] + DATE_RUN_DESCENDING = 26u + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/SortOption.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/SortOption.cs deleted file mode 100644 index 3c2fc3660..000000000 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/SortOption.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Flow.Launcher.Plugin.Everything.Everything -{ - public enum SortOption : uint - { - NAME_ASCENDING = 1u, - NAME_DESCENDING = 2u, - PATH_ASCENDING = 3u, - PATH_DESCENDING = 4u, - SIZE_ASCENDING = 5u, - SIZE_DESCENDING = 6u, - EXTENSION_ASCENDING = 7u, - EXTENSION_DESCENDING = 8u, - TYPE_NAME_ASCENDING = 9u, - TYPE_NAME_DESCENDING = 10u, - DATE_CREATED_ASCENDING = 11u, - DATE_CREATED_DESCENDING = 12u, - DATE_MODIFIED_ASCENDING = 13u, - DATE_MODIFIED_DESCENDING = 14u, - ATTRIBUTES_ASCENDING = 15u, - ATTRIBUTES_DESCENDING = 16u, - FILE_LIST_FILENAME_ASCENDING = 17u, - FILE_LIST_FILENAME_DESCENDING = 18u, - RUN_COUNT_DESCENDING = 20u, - DATE_RECENTLY_CHANGED_ASCENDING = 21u, - DATE_RECENTLY_CHANGED_DESCENDING = 22u, - DATE_ACCESSED_ASCENDING = 23u, - DATE_ACCESSED_DESCENDING = 24u, - DATE_RUN_ASCENDING = 25u, - DATE_RUN_DESCENDING = 26u - } -} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickAccessLinks/AccessLink.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickAccessLinks/AccessLink.cs index 1975211f9..5dd6162e8 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickAccessLinks/AccessLink.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickAccessLinks/AccessLink.cs @@ -1,8 +1,4 @@ -using System; -using System.Linq; -using System.Text.Json.Serialization; - -namespace Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks +namespace Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks { public class AccessLink { @@ -10,20 +6,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks public ResultType Type { get; set; } = ResultType.Folder; - [JsonIgnore] - public string Name - { - get - { - var path = Path.EndsWith(Constants.DirectorySeparator) ? Path[0..^1] : Path; - - if (path.EndsWith(':')) - return path[0..^1] + " Drive"; - - return path.Split(new[] { System.IO.Path.DirectorySeparatorChar }, StringSplitOptions.None) - .Last(); - } - } + public string Name { get; set; } } - } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickAccessLinks/QuickAccess.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickAccessLinks/QuickAccess.cs index 85b595390..32651ecb8 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickAccessLinks/QuickAccess.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickAccessLinks/QuickAccess.cs @@ -6,7 +6,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks { internal static class QuickAccess { - private const int quickAccessResultScore = 100; + private const int QuickAccessResultScore = 100; internal static List AccessLinkListMatched(Query query, IEnumerable accessLinks) { @@ -19,8 +19,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks .ThenBy(x => x.Name) .Select(l => l.Type switch { - ResultType.Folder => ResultManager.CreateFolderResult(l.Name, l.Path, l.Path, query, quickAccessResultScore), - ResultType.File => ResultManager.CreateFileResult(l.Path, query, quickAccessResultScore), + ResultType.Volume => ResultManager.CreateDriveSpaceDisplayResult(l.Path, query.ActionKeyword, QuickAccessResultScore), + ResultType.Folder => ResultManager.CreateFolderResult(l.Name, l.Path, l.Path, query, QuickAccessResultScore), + ResultType.File => ResultManager.CreateFileResult(l.Path, query, QuickAccessResultScore), _ => throw new ArgumentOutOfRangeException() }) .ToList(); @@ -32,8 +33,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks .ThenBy(x => x.Name) .Select(l => l.Type switch { - ResultType.Folder => ResultManager.CreateFolderResult(l.Name, l.Path, l.Path, query), - ResultType.File => ResultManager.CreateFileResult(l.Path, query, quickAccessResultScore), + ResultType.Volume => ResultManager.CreateDriveSpaceDisplayResult(l.Path, query.ActionKeyword, QuickAccessResultScore), + ResultType.Folder => ResultManager.CreateFolderResult(l.Name, l.Path, l.Path, query, QuickAccessResultScore), + ResultType.File => ResultManager.CreateFileResult(l.Path, query, QuickAccessResultScore), _ => throw new ArgumentOutOfRangeException() }).ToList(); } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index eaae3b4a9..dfa2c8d43 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -7,6 +7,7 @@ using System.Windows.Input; using Flow.Launcher.Plugin.Explorer.Search.Everything; using Flow.Launcher.Plugin.Explorer.Views; using Flow.Launcher.Plugin.SharedCommands; +using Flow.Launcher.Plugin.SharedModels; using Peter; using Path = System.IO.Path; @@ -14,6 +15,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search { public static class ResultManager { + private static readonly string ClassName = nameof(ResultManager); + private static readonly string[] SizeUnits = { "B", "KB", "MB", "GB", "TB" }; private static PluginInitContext Context; private static Settings Settings { get; set; } @@ -70,10 +73,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search internal static void ShowNativeContextMenu(string path, ResultType type) { - var screenWithMouseCursor = System.Windows.Forms.Screen.FromPoint(System.Windows.Forms.Cursor.Position); + var screenWithMouseCursor = MonitorInfo.GetCursorDisplayMonitor(); var xOfScreenCenter = screenWithMouseCursor.WorkingArea.Left + screenWithMouseCursor.WorkingArea.Width / 2; var yOfScreenCenter = screenWithMouseCursor.WorkingArea.Top + screenWithMouseCursor.WorkingArea.Height / 2; - var showPosition = new System.Drawing.Point(xOfScreenCenter, yOfScreenCenter); + var showPosition = new System.Drawing.Point((int)xOfScreenCenter, (int)yOfScreenCenter); switch (type) { @@ -103,6 +106,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search { FilePath = path, }, + PreviewPanel = new Lazy(() => new PreviewPanel(Settings, path, ResultType.Folder)), Action = c => { if (c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Alt) @@ -163,12 +167,22 @@ namespace Flow.Launcher.Plugin.Explorer.Search }, Score = score, TitleToolTip = Main.Context.API.GetTranslation("plugin_explorer_plugin_ToolTipOpenDirectory"), - SubTitleToolTip = path, + SubTitleToolTip = Settings.DisplayMoreInformationInToolTip ? GetFolderMoreInfoTooltip(path) : path, ContextData = new SearchResult { Type = ResultType.Folder, FullPath = path, WindowsIndexed = windowsIndexed } }; } + internal static Result CreateDriveSpaceDisplayResult(string path, string actionKeyword, int score) + { + return CreateDriveSpaceDisplayResult(path, actionKeyword, score, SearchManager.UseIndexSearch(path)); + } + internal static Result CreateDriveSpaceDisplayResult(string path, string actionKeyword, bool windowsIndexed = false) + { + return CreateDriveSpaceDisplayResult(path, actionKeyword, 500, windowsIndexed); + } + + private static Result CreateDriveSpaceDisplayResult(string path, string actionKeyword, int score, bool windowsIndexed = false) { var progressBarColor = "#26a0da"; var title = string.Empty; // hide title when use progress bar, @@ -184,13 +198,17 @@ namespace Flow.Launcher.Plugin.Explorer.Search if (progressValue >= 90) progressBarColor = "#da2626"; + var tooltip = Settings.DisplayMoreInformationInToolTip + ? GetVolumeMoreInfoTooltip(path, freespace, totalspace) + : path; + return new Result { Title = title, SubTitle = subtitle, AutoCompleteText = GetPathWithActionKeyword(path, ResultType.Folder, actionKeyword), IcoPath = path, - Score = 500, + Score = score, ProgressBar = progressValue, ProgressBarColor = progressBarColor, Preview = new Result.PreviewInfo @@ -202,8 +220,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search OpenFolder(path); return true; }, - TitleToolTip = path, - SubTitleToolTip = path, + TitleToolTip = tooltip, + SubTitleToolTip = tooltip, ContextData = new SearchResult { Type = ResultType.Volume, FullPath = path, WindowsIndexed = windowsIndexed } }; } @@ -266,16 +284,16 @@ namespace Flow.Launcher.Plugin.Explorer.Search internal static Result CreateFileResult(string filePath, Query query, int score = 0, bool windowsIndexed = false) { - bool isMedia = IsMedia(Path.GetExtension(filePath)); - var title = Path.GetFileName(filePath); - + var isMedia = IsMedia(Path.GetExtension(filePath)); + var title = Path.GetFileName(filePath) ?? string.Empty; + var directory = Path.GetDirectoryName(filePath) ?? string.Empty; /* Preview Detail */ var result = new Result { Title = title, - SubTitle = Path.GetDirectoryName(filePath), + SubTitle = directory, IcoPath = filePath, Preview = new Result.PreviewInfo { @@ -287,7 +305,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search TitleHighlightData = Context.API.FuzzySearch(query.Search, title).MatchData, Score = score, CopyText = filePath, - PreviewPanel = new Lazy(() => new PreviewPanel(Settings, filePath)), + PreviewPanel = new Lazy(() => new PreviewPanel(Settings, filePath, ResultType.File)), Action = c => { if (c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Alt) @@ -299,7 +317,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search { if (c.SpecialKeyState.ToModifierKeys() == (ModifierKeys.Control | ModifierKeys.Shift)) { - OpenFile(filePath, Settings.UseLocationAsWorkingDir ? Path.GetDirectoryName(filePath) : string.Empty, true); + OpenFile(filePath, Settings.UseLocationAsWorkingDir ? directory : string.Empty, true); } else if (c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Control) { @@ -307,7 +325,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search } else { - OpenFile(filePath, Settings.UseLocationAsWorkingDir ? Path.GetDirectoryName(filePath) : string.Empty); + OpenFile(filePath, Settings.UseLocationAsWorkingDir ? directory : string.Empty); } } catch (Exception ex) @@ -318,7 +336,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search return true; }, TitleToolTip = Main.Context.API.GetTranslation("plugin_explorer_plugin_ToolTipOpenContainingFolder"), - SubTitleToolTip = filePath, + SubTitleToolTip = Settings.DisplayMoreInformationInToolTip ? GetFileMoreInfoTooltip(filePath) : filePath, ContextData = new SearchResult { Type = ResultType.File, FullPath = filePath, WindowsIndexed = windowsIndexed } }; return result; @@ -349,6 +367,46 @@ namespace Flow.Launcher.Plugin.Explorer.Search _ = Task.Run(() => EverythingApi.IncrementRunCounterAsync(fileOrFolder)); } + private static string GetFileMoreInfoTooltip(string filePath) + { + try + { + var fileSize = PreviewPanel.GetFileSize(filePath); + var fileCreatedAt = PreviewPanel.GetFileCreatedAt(filePath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel); + var fileModifiedAt = PreviewPanel.GetFileLastModifiedAt(filePath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel); + return string.Format(Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info"), + filePath, fileSize, fileCreatedAt, fileModifiedAt, Environment.NewLine); + } + catch (Exception e) + { + Context.API.LogException(ClassName, $"Failed to load tooltip for {filePath}", e); + return filePath; + } + } + + private static string GetFolderMoreInfoTooltip(string folderPath) + { + try + { + var folderSize = PreviewPanel.GetFolderSize(folderPath); + var folderCreatedAt = PreviewPanel.GetFolderCreatedAt(folderPath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel); + var folderModifiedAt = PreviewPanel.GetFolderLastModifiedAt(folderPath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel); + return string.Format(Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info"), + folderPath, folderSize, folderCreatedAt, folderModifiedAt, Environment.NewLine); + } + catch (Exception e) + { + Context.API.LogException(ClassName, $"Failed to load tooltip for {folderPath}", e); + return folderPath; + } + } + + private static string GetVolumeMoreInfoTooltip(string volumePath, string freespace, string totalspace) + { + return string.Format(Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_volume"), + volumePath, freespace, totalspace, Environment.NewLine); + } + private static readonly string[] MediaExtensions = { ".jpg", ".png", ".avi", ".mkv", ".bmp", ".gif", ".wmv", ".mp3", ".flac", ".mp4", diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 12df6c145..f4f87d4d4 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -246,6 +246,18 @@ namespace Flow.Launcher.Plugin.Explorer.Search public bool IsFileContentSearch(string actionKeyword) => actionKeyword == Settings.FileContentSearchActionKeyword; + public static bool UseIndexSearch(string path) + { + if (Main.Settings.IndexSearchEngine is not Settings.IndexSearchEngineOption.WindowsIndex) + return false; + + // Check if the path is using windows index search + var pathToDirectory = FilesFolders.ReturnPreviousDirectoryIfIncompleteString(path); + + return !Main.Settings.IndexSearchExcludedSubdirectoryPaths.Any( + x => FilesFolders.ReturnPreviousDirectoryIfIncompleteString(pathToDirectory).StartsWith(x.Path, StringComparison.OrdinalIgnoreCase)) + && WindowsIndex.WindowsIndex.PathIsIndexed(pathToDirectory); + } private bool UseWindowsIndexForDirectorySearch(string locationPath) { diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs index 4f83fc72e..672e81d03 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs @@ -1,5 +1,4 @@ -using Flow.Launcher.Plugin.Everything.Everything; -using Flow.Launcher.Plugin.Explorer.Search; +using Flow.Launcher.Plugin.Explorer.Search; using Flow.Launcher.Plugin.Explorer.Search.Everything; using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; @@ -37,6 +36,8 @@ namespace Flow.Launcher.Plugin.Explorer public bool DefaultOpenFolderInFileManager { get; set; } = false; + public bool DisplayMoreInformationInToolTip { get; set; } = false; + public string SearchActionKeyword { get; set; } = Query.GlobalPluginWildcardSign; public bool SearchActionKeywordEnabled { get; set; } = true; @@ -143,10 +144,7 @@ namespace Flow.Launcher.Plugin.Explorer public string EverythingInstalledPath { get; set; } - [JsonIgnore] - public SortOption[] SortOptions { get; set; } = Enum.GetValues(); - - public SortOption SortOption { get; set; } = SortOption.NAME_ASCENDING; + public EverythingSortOption SortOption { get; set; } = EverythingSortOption.NAME_ASCENDING; public bool EnableEverythingContentSearch { get; set; } = false; diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/ActionKeywordModel.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/ActionKeywordModel.cs index 745032d2c..87438a881 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/ActionKeywordModel.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/ActionKeywordModel.cs @@ -1,16 +1,11 @@ -using System.ComponentModel; -using System.Runtime.CompilerServices; - #nullable enable -namespace Flow.Launcher.Plugin.Explorer.Views +namespace Flow.Launcher.Plugin.Explorer.ViewModels { - public class ActionKeywordModel : INotifyPropertyChanged + public partial class ActionKeywordModel : BaseModel { private static Settings _settings = null!; - public event PropertyChangedEventHandler? PropertyChanged; - public static void Init(Settings settings) { _settings = settings; @@ -28,13 +23,7 @@ namespace Flow.Launcher.Plugin.Explorer.Views internal Settings.ActionKeyword KeywordProperty { get; } - private void OnPropertyChanged([CallerMemberName] string propertyName = "") - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - private string? keyword; - public string Keyword { get => keyword ??= _settings.GetActionKeyword(KeywordProperty); @@ -45,8 +34,8 @@ namespace Flow.Launcher.Plugin.Explorer.Views OnPropertyChanged(); } } - private bool? enabled; + private bool? enabled; public bool Enabled { get => enabled ??= _settings.GetActionKeywordEnabled(KeywordProperty); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs index fb33dacab..7292697ce 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Windows; using System.Windows.Forms; using CommunityToolkit.Mvvm.Input; +using Flow.Launcher.Plugin.Explorer.Helper; using Flow.Launcher.Plugin.Explorer.Search; using Flow.Launcher.Plugin.Explorer.Search.Everything; using Flow.Launcher.Plugin.Explorer.Search.Everything.Exceptions; @@ -243,6 +244,21 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels "yyyy-MM-dd", "yyyy-MM-dd ddd", "yyyy-MM-dd, dddd", + "dd/MMM/yyyy", + "dd/MMM/yyyy ddd", + "dd/MMM/yyyy, dddd", + "dd-MMM-yyyy", + "dd-MMM-yyyy ddd", + "dd-MMM-yyyy, dddd", + "dd.MMM.yyyy", + "dd.MMM.yyyy ddd", + "dd.MMM.yyyy, dddd", + "MMM/dd/yyyy", + "MMM/dd/yyyy ddd", + "MMM/dd/yyyy, dddd", + "yyyy-MMM-dd", + "yyyy-MMM-dd ddd", + "yyyy-MMM-dd, dddd", }; #endregion @@ -328,14 +344,10 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels } [RelayCommand] - private void EditLink(object commandParameter) + private void EditIndexSearchExcludePaths() { - var (selectedLink, collection) = commandParameter switch - { - "QuickAccessLink" => (SelectedQuickAccessLink, Settings.QuickAccessLinks), - "IndexSearchExcludedPaths" => (SelectedIndexSearchExcludedPath, Settings.IndexSearchExcludedSubdirectoryPaths), - _ => throw new ArgumentOutOfRangeException(nameof(commandParameter)) - }; + var selectedLink = SelectedIndexSearchExcludedPath; + var collection = Settings.IndexSearchExcludedSubdirectoryPaths; if (selectedLink is null) { @@ -354,28 +366,18 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels collection.Remove(selectedLink); collection.Add(new AccessLink { - Path = path, Type = selectedLink.Type, + Path = path, Type = selectedLink.Type, Name = path.GetPathName() }); - } - - private void ShowUnselectedMessage() - { - var warning = Context.API.GetTranslation("plugin_explorer_make_selection_warning"); - Context.API.ShowMsgBox(warning); + Save(); } [RelayCommand] - private void AddLink(object commandParameter) + private void AddIndexSearchExcludePaths() { - var container = commandParameter switch - { - "QuickAccessLink" => Settings.QuickAccessLinks, - "IndexSearchExcludedPaths" => Settings.IndexSearchExcludedSubdirectoryPaths, - _ => throw new ArgumentOutOfRangeException(nameof(commandParameter)) - }; - - ArgumentNullException.ThrowIfNull(container); + var container = Settings.IndexSearchExcludedSubdirectoryPaths; + if (container is null) return; + var folderBrowserDialog = new FolderBrowserDialog(); if (folderBrowserDialog.ShowDialog() != DialogResult.OK) @@ -383,34 +385,85 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels var newAccessLink = new AccessLink { + Name = folderBrowserDialog.SelectedPath.GetPathName(), Path = folderBrowserDialog.SelectedPath }; container.Add(newAccessLink); + Save(); } [RelayCommand] - private void RemoveLink(object obj) + private void EditQuickAccessLink() { - if (obj is not string container) return; + var selectedLink = SelectedQuickAccessLink; + var collection = Settings.QuickAccessLinks; + + if (selectedLink is null) + { + ShowUnselectedMessage(); + return; + } + + var quickAccessLinkSettings = new QuickAccessLinkSettings(collection, SelectedQuickAccessLink); + if (quickAccessLinkSettings.ShowDialog() == true) + { + Save(); + } + } + + [RelayCommand] + private void AddQuickAccessLink() + { + var quickAccessLinkSettings = new QuickAccessLinkSettings(Settings.QuickAccessLinks); + if (quickAccessLinkSettings.ShowDialog() == true) + { + Save(); + } + } + + [RelayCommand] + private void RemoveLink(object commandParameter) + { + if (commandParameter is not string container) return; switch (container) { case "QuickAccessLink": if (SelectedQuickAccessLink == null) return; + if (Context.API.ShowMsgBox( + Context.API.GetTranslation("plugin_explorer_delete_quick_access_link"), + Context.API.GetTranslation("plugin_explorer_delete"), + MessageBoxButton.OKCancel, + MessageBoxImage.Warning) + == MessageBoxResult.Cancel) + return; Settings.QuickAccessLinks.Remove(SelectedQuickAccessLink); break; case "IndexSearchExcludedPaths": if (SelectedIndexSearchExcludedPath == null) return; + if (Context.API.ShowMsgBox( + Context.API.GetTranslation("plugin_explorer_delete_index_search_excluded_path"), + Context.API.GetTranslation("plugin_explorer_delete"), + MessageBoxButton.OKCancel, + MessageBoxImage.Warning) + == MessageBoxResult.Cancel) + return; Settings.IndexSearchExcludedSubdirectoryPaths.Remove(SelectedIndexSearchExcludedPath); break; } Save(); } + + private void ShowUnselectedMessage() + { + var warning = Context.API.GetTranslation("plugin_explorer_make_selection_warning"); + Context.API.ShowMsgBox(warning); + } #endregion - private string? PromptUserSelectPath(ResultType type, string? initialDirectory = null) + private static string? PromptUserSelectPath(ResultType type, string? initialDirectory = null) { string? path = null; @@ -539,6 +592,22 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels #region Everything FastSortWarning + public List AllEverythingSortOptions = EverythingSortOptionLocalized.GetValues(); + + public EverythingSortOption SelectedEverythingSortOption + { + get => Settings.SortOption; + set + { + if (value == Settings.SortOption) + return; + Settings.SortOption = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(FastSortWarningVisibility)); + OnPropertyChanged(nameof(SortOptionWarningMessage)); + } + } + public Visibility FastSortWarningVisibility { get @@ -559,6 +628,7 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels } } } + public string SortOptionWarningMessage { get @@ -568,15 +638,15 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels // this method is used to determine if Everything service is running because as at Everything v1.4.1 // the sdk does not provide a dedicated interface to determine if it is running. return EverythingApi.IsFastSortOption(Settings.SortOption) ? string.Empty - : Context.API.GetTranslation("flowlauncher_plugin_everything_nonfastsort_warning"); + : Localize.flowlauncher_plugin_everything_nonfastsort_warning(); } catch (IPCErrorException) { - return Context.API.GetTranslation("flowlauncher_plugin_everything_is_not_running"); + return Localize.flowlauncher_plugin_everything_is_not_running(); } catch (DllNotFoundException) { - return Context.API.GetTranslation("flowlauncher_plugin_everything_sdk_issue"); + return Localize.flowlauncher_plugin_everything_sdk_issue(); } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs index 10ce28fe4..829a2feed 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs @@ -1,16 +1,13 @@ -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Runtime.CompilerServices; +using System.Linq; using System.Windows; using System.Windows.Input; +using CommunityToolkit.Mvvm.ComponentModel; +using Flow.Launcher.Plugin.Explorer.ViewModels; namespace Flow.Launcher.Plugin.Explorer.Views { - /// - /// Interaction logic for ActionKeywordSetting.xaml - /// - public partial class ActionKeywordSetting : INotifyPropertyChanged + [INotifyPropertyChanged] + public partial class ActionKeywordSetting { private ActionKeywordModel CurrentActionKeyword { get; } @@ -21,14 +18,14 @@ namespace Flow.Launcher.Plugin.Explorer.Views { // Set Enable to be true if user change ActionKeyword KeywordEnabled = true; - _ = SetField(ref actionKeyword, value); + _ = SetProperty(ref actionKeyword, value); } } public bool KeywordEnabled { get => _keywordEnabled; - set => SetField(ref _keywordEnabled, value); + set => _ = SetProperty(ref _keywordEnabled, value); } private string actionKeyword; @@ -116,20 +113,5 @@ namespace Flow.Launcher.Plugin.Explorer.Views e.CancelCommand(); } } - - public event PropertyChangedEventHandler PropertyChanged; - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - private bool SetField(ref T field, T value, [CallerMemberName] string propertyName = null) - { - if (EqualityComparer.Default.Equals(field, value)) - return false; - field = value; - OnPropertyChanged(propertyName); - return true; - } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/Converters/EverythingEnumNameConverter.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/Converters/EverythingEnumNameConverter.cs deleted file mode 100644 index e24b21dcd..000000000 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/Converters/EverythingEnumNameConverter.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Flow.Launcher.Plugin.Everything.Everything; -using Flow.Launcher.Plugin.Explorer.Helper; -using System; -using System.Globalization; -using System.Windows.Data; - -namespace Flow.Launcher.Plugin.Explorer.Views.Converters; - -public class EnumNameConverter : IValueConverter -{ - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return value is SortOption option ? option.GetTranslatedName() : value; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml index a8097d748..08abc3ba6 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml @@ -2,13 +2,11 @@ x:Class="Flow.Launcher.Plugin.Explorer.Views.ExplorerSettings" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:converters="clr-namespace:Flow.Launcher.Plugin.Explorer.Views.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:qa="clr-namespace:Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks" xmlns:ui="http://schemas.modernwpf.com/2019" xmlns:viewModels="clr-namespace:Flow.Launcher.Plugin.Explorer.ViewModels" - xmlns:views="clr-namespace:Flow.Launcher.Plugin.Explorer.Views" d:DataContext="{d:DesignInstance viewModels:SettingsViewModel}" d:DesignHeight="450" d:DesignWidth="800" @@ -18,7 +16,7 @@ - + - - - - + Source="{Binding PreviewImage, IsAsync=True, Mode=OneWay}" /> - - - + Text="{Binding FilePath, Mode=OneTime}" /> - - - + + + @@ -87,7 +70,7 @@ - + @@ -105,7 +88,7 @@ Style="{DynamicResource PreviewItemSubTitleStyle}" Text="{DynamicResource FileSize}" TextWrapping="Wrap" - Visibility="{Binding FileSizeVisibility, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}" /> + Visibility="{Binding FileSizeVisibility, Mode=OneTime}" /> + Visibility="{Binding FileSizeVisibility, Mode=OneTime}" /> + Visibility="{Binding CreatedAtVisibility, Mode=OneTime}" /> + Visibility="{Binding CreatedAtVisibility, Mode=OneTime}" /> + Visibility="{Binding LastModifiedAtVisibility, Mode=OneTime}" /> + Visibility="{Binding LastModifiedAtVisibility, Mode=OneTime}" /> diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml.cs index e1a957199..4dd0588ee 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml.cs @@ -2,36 +2,41 @@ using System.ComponentModel; using System.Globalization; using System.IO; -using System.Runtime.CompilerServices; +using System.Linq; +using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Imaging; using Flow.Launcher.Plugin.Explorer.Search; +using CommunityToolkit.Mvvm.ComponentModel; namespace Flow.Launcher.Plugin.Explorer.Views; #nullable enable -public partial class PreviewPanel : UserControl, INotifyPropertyChanged +[INotifyPropertyChanged] +public partial class PreviewPanel : UserControl { - private string FilePath { get; } - public string FileSize { get; } = ""; - public string CreatedAt { get; } = ""; - public string LastModifiedAt { get; } = ""; - private ImageSource _previewImage = new BitmapImage(); - private Settings Settings { get; } + private static readonly string ClassName = nameof(PreviewPanel); - public ImageSource PreviewImage - { - get => _previewImage; - private set - { - _previewImage = value; - OnPropertyChanged(); - } - } + public string FilePath { get; } + public string FileName { get; } + + [ObservableProperty] + private string _fileSize = Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + + [ObservableProperty] + private string _createdAt = ""; + + [ObservableProperty] + private string _lastModifiedAt = ""; + + [ObservableProperty] + private ImageSource _previewImage = new BitmapImage(); + + private Settings Settings { get; } public Visibility FileSizeVisibility => Settings.ShowFileSizeInPreviewPanel ? Visibility.Visible @@ -50,43 +55,42 @@ public partial class PreviewPanel : UserControl, INotifyPropertyChanged ? Visibility.Visible : Visibility.Collapsed; - public PreviewPanel(Settings settings, string filePath) + public PreviewPanel(Settings settings, string filePath, ResultType type) { - InitializeComponent(); - Settings = settings; - FilePath = filePath; + FileName = Path.GetFileName(filePath); + + InitializeComponent(); if (Settings.ShowFileSizeInPreviewPanel) { - var fileSize = new FileInfo(filePath).Length; - FileSize = ResultManager.ToReadableSize(fileSize, 2); + if (type == ResultType.File) + { + FileSize = GetFileSize(filePath); + } + else + { + _ = Task.Run(() => + { + FileSize = GetFolderSize(filePath); + OnPropertyChanged(nameof(FileSize)); + }).ConfigureAwait(false); + } } if (Settings.ShowCreatedDateInPreviewPanel) { - DateTime createdDate = File.GetCreationTime(filePath); - string formattedDate = createdDate.ToString( - $"{Settings.PreviewPanelDateFormat} {Settings.PreviewPanelTimeFormat}", - CultureInfo.CurrentCulture - ); - - string result = formattedDate; - if (Settings.ShowFileAgeInPreviewPanel) result = $"{GetFileAge(createdDate)} - {formattedDate}"; - CreatedAt = result; + CreatedAt = type == ResultType.File ? + GetFileCreatedAt(filePath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel) : + GetFolderCreatedAt(filePath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel); } if (Settings.ShowModifiedDateInPreviewPanel) { - DateTime lastModifiedDate = File.GetLastWriteTime(filePath); - string formattedDate = lastModifiedDate.ToString( - $"{Settings.PreviewPanelDateFormat} {Settings.PreviewPanelTimeFormat}", - CultureInfo.CurrentCulture - ); - string result = formattedDate; - if (Settings.ShowFileAgeInPreviewPanel) result = $"{GetFileAge(lastModifiedDate)} - {formattedDate}"; - LastModifiedAt = result; + LastModifiedAt = type == ResultType.File ? + GetFileLastModifiedAt(filePath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel) : + GetFolderLastModifiedAt(filePath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel); } _ = LoadImageAsync(); @@ -96,7 +100,211 @@ public partial class PreviewPanel : UserControl, INotifyPropertyChanged { PreviewImage = await Main.Context.API.LoadImageAsync(FilePath, true).ConfigureAwait(false); } - + + public static string GetFileSize(string filePath) + { + try + { + var fileInfo = new FileInfo(filePath); + return ResultManager.ToReadableSize(fileInfo.Length, 2); + } + catch (FileNotFoundException) + { + Main.Context.API.LogError(ClassName, $"File not found: {filePath}"); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + catch (UnauthorizedAccessException) + { + Main.Context.API.LogError(ClassName, $"Access denied to file: {filePath}"); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + catch (Exception e) + { + Main.Context.API.LogException(ClassName, $"Failed to get file size for {filePath}", e); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + } + + public static string GetFileCreatedAt(string filePath, string previewPanelDateFormat, string previewPanelTimeFormat, bool showFileAgeInPreviewPanel) + { + try + { + var createdDate = File.GetCreationTime(filePath); + var formattedDate = createdDate.ToString( + $"{previewPanelDateFormat} {previewPanelTimeFormat}", + CultureInfo.CurrentCulture + ); + + var result = formattedDate; + if (showFileAgeInPreviewPanel) result = $"{GetFileAge(createdDate)} - {formattedDate}"; + return result; + } + catch (FileNotFoundException) + { + Main.Context.API.LogError(ClassName, $"File not found: {filePath}"); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + catch (UnauthorizedAccessException) + { + Main.Context.API.LogError(ClassName, $"Access denied to file: {filePath}"); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + catch (Exception e) + { + Main.Context.API.LogException(ClassName, $"Failed to get file created date for {filePath}", e); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + } + + public static string GetFileLastModifiedAt(string filePath, string previewPanelDateFormat, string previewPanelTimeFormat, bool showFileAgeInPreviewPanel) + { + try + { + var lastModifiedDate = File.GetLastWriteTime(filePath); + var formattedDate = lastModifiedDate.ToString( + $"{previewPanelDateFormat} {previewPanelTimeFormat}", + CultureInfo.CurrentCulture + ); + + var result = formattedDate; + if (showFileAgeInPreviewPanel) result = $"{GetFileAge(lastModifiedDate)} - {formattedDate}"; + return result; + } + catch (FileNotFoundException) + { + Main.Context.API.LogError(ClassName, $"File not found: {filePath}"); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + catch (UnauthorizedAccessException) + { + Main.Context.API.LogError(ClassName, $"Access denied to file: {filePath}"); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + catch (Exception e) + { + Main.Context.API.LogException(ClassName, $"Failed to get file modified date for {filePath}", e); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + } + + public static string GetFolderSize(string folderPath) + { + using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(3)); + + try + { + // Use parallel enumeration for better performance + var directoryInfo = new DirectoryInfo(folderPath); + long size = directoryInfo.EnumerateFiles("*", SearchOption.AllDirectories) + .AsParallel() + .WithCancellation(timeoutCts.Token) + .Sum(file => file.Length); + + return ResultManager.ToReadableSize(size, 2); + } + catch (FileNotFoundException) + { + Main.Context.API.LogError(ClassName, $"Folder not found: {folderPath}"); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + catch (UnauthorizedAccessException) + { + Main.Context.API.LogError(ClassName, $"Access denied to folder: {folderPath}"); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + catch (OperationCanceledException) + { + Main.Context.API.LogError(ClassName, $"Operation timed out while calculating folder size for {folderPath}"); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + // For parallel operations, AggregateException may be thrown if any of the tasks fail + catch (AggregateException ae) + { + switch (ae.InnerException) + { + case FileNotFoundException: + Main.Context.API.LogError(ClassName, $"Folder not found: {folderPath}"); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + case UnauthorizedAccessException: + Main.Context.API.LogError(ClassName, $"Access denied to folder: {folderPath}"); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + case OperationCanceledException: + Main.Context.API.LogError(ClassName, $"Operation timed out while calculating folder size for {folderPath}"); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + default: + Main.Context.API.LogException(ClassName, $"Failed to get folder size for {folderPath}", ae); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + } + catch (Exception e) + { + Main.Context.API.LogException(ClassName, $"Failed to get folder size for {folderPath}", e); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + } + + public static string GetFolderCreatedAt(string folderPath, string previewPanelDateFormat, string previewPanelTimeFormat, bool showFileAgeInPreviewPanel) + { + try + { + var createdDate = Directory.GetCreationTime(folderPath); + var formattedDate = createdDate.ToString( + $"{previewPanelDateFormat} {previewPanelTimeFormat}", + CultureInfo.CurrentCulture + ); + + var result = formattedDate; + if (showFileAgeInPreviewPanel) result = $"{GetFileAge(createdDate)} - {formattedDate}"; + return result; + } + catch (FileNotFoundException) + { + Main.Context.API.LogError(ClassName, $"Folder not found: {folderPath}"); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + catch (UnauthorizedAccessException) + { + Main.Context.API.LogError(ClassName, $"Access denied to folder: {folderPath}"); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + catch (Exception e) + { + Main.Context.API.LogException(ClassName, $"Failed to get folder created date for {folderPath}", e); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + } + + public static string GetFolderLastModifiedAt(string folderPath, string previewPanelDateFormat, string previewPanelTimeFormat, bool showFileAgeInPreviewPanel) + { + try + { + var lastModifiedDate = Directory.GetLastWriteTime(folderPath); + var formattedDate = lastModifiedDate.ToString( + $"{previewPanelDateFormat} {previewPanelTimeFormat}", + CultureInfo.CurrentCulture + ); + + var result = formattedDate; + if (showFileAgeInPreviewPanel) result = $"{GetFileAge(lastModifiedDate)} - {formattedDate}"; + return result; + } + catch (FileNotFoundException) + { + Main.Context.API.LogError(ClassName, $"Folder not found: {folderPath}"); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + catch (UnauthorizedAccessException) + { + Main.Context.API.LogError(ClassName, $"Access denied to folder: {folderPath}"); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + catch (Exception e) + { + Main.Context.API.LogException(ClassName, $"Failed to get folder modified date for {folderPath}", e); + return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + } + } + private static string GetFileAge(DateTime fileDateTime) { var now = DateTime.Now; @@ -120,11 +328,4 @@ public partial class PreviewPanel : UserControl, INotifyPropertyChanged return yearsDiff == 1 ? Main.Context.API.GetTranslation("OneYearAgo") : string.Format(Main.Context.API.GetTranslation("YearsAgo"), yearsDiff); } - - public event PropertyChangedEventHandler? PropertyChanged; - - protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/QuickAccessLinkSettings.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Views/QuickAccessLinkSettings.xaml new file mode 100644 index 000000000..e977a72c2 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/QuickAccessLinkSettings.xaml @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/QuickAccessLinkSettings.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/QuickAccessLinkSettings.xaml.cs new file mode 100644 index 000000000..e6294b98b --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/QuickAccessLinkSettings.xaml.cs @@ -0,0 +1,218 @@ +using System; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Windows; +using System.Windows.Forms; +using Flow.Launcher.Plugin.Explorer.Helper; +using Flow.Launcher.Plugin.Explorer.Search; +using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace Flow.Launcher.Plugin.Explorer.Views; + +[INotifyPropertyChanged] +public partial class QuickAccessLinkSettings +{ + private static readonly string ClassName = nameof(QuickAccessLinkSettings); + + private string _selectedPath; + public string SelectedPath + { + get => _selectedPath; + set + { + if (_selectedPath != value) + { + _selectedPath = value; + OnPropertyChanged(); + if (string.IsNullOrEmpty(_selectedName)) + { + SelectedName = _selectedPath.GetPathName(); + } + if (!string.IsNullOrEmpty(_selectedPath)) + { + _accessLinkType = GetResultType(_selectedPath); + } + } + } + } + + private string _selectedName; + public string SelectedName + { + get + { + return string.IsNullOrEmpty(_selectedName) ? _selectedPath.GetPathName() : _selectedName; + } + set + { + if (_selectedName != value) + { + _selectedName = value; + OnPropertyChanged(); + } + } + } + + public bool IsFileSelected { get; set; } + public bool IsFolderSelected { get; set; } = true; // Default to Folder + + private bool IsEdit { get; } + private AccessLink SelectedAccessLink { get; } + + public ObservableCollection QuickAccessLinks { get; } + + private ResultType _accessLinkType = ResultType.Folder; // Default to Folder + + public QuickAccessLinkSettings(ObservableCollection quickAccessLinks) + { + IsEdit = false; + QuickAccessLinks = quickAccessLinks; + InitializeComponent(); + } + + public QuickAccessLinkSettings(ObservableCollection quickAccessLinks, AccessLink selectedAccessLink) + { + IsEdit = true; + _selectedName = selectedAccessLink.Name; + _selectedPath = selectedAccessLink.Path; + _accessLinkType = GetResultType(_selectedPath); // Initialize link type + IsFileSelected = selectedAccessLink.Type == ResultType.File; // Initialize default selection + IsFolderSelected = !IsFileSelected; + SelectedAccessLink = selectedAccessLink; + QuickAccessLinks = quickAccessLinks; + InitializeComponent(); + } + + private void BtnCancel_OnClick(object sender, RoutedEventArgs e) + { + DialogResult = false; + Close(); + } + + private void OnDoneButtonClick(object sender, RoutedEventArgs e) + { + // Validate the input before proceeding + if (string.IsNullOrEmpty(SelectedName) || string.IsNullOrEmpty(SelectedPath)) + { + var warning = Main.Context.API.GetTranslation("plugin_explorer_quick_access_link_no_folder_selected"); + Main.Context.API.ShowMsgBox(warning); + return; + } + + // Check if the path already exists in the quick access links + if (QuickAccessLinks.Any(x => + x.Path.Equals(SelectedPath, StringComparison.OrdinalIgnoreCase) && + x.Name.Equals(SelectedName, StringComparison.OrdinalIgnoreCase))) + { + var warning = Main.Context.API.GetTranslation("plugin_explorer_quick_access_link_path_already_exists"); + Main.Context.API.ShowMsgBox(warning); + return; + } + + // If editing, update the existing link + if (IsEdit) + { + if (SelectedAccessLink != null) + { + var index = QuickAccessLinks.IndexOf(SelectedAccessLink); + if (index >= 0) + { + var updatedLink = new AccessLink + { + Name = SelectedName, + Type = _accessLinkType, + Path = SelectedPath + }; + QuickAccessLinks[index] = updatedLink; + } + DialogResult = true; + Close(); + } + // Add a new one if the selected access link is null (should not happen in edit mode, but just in case) + else + { + AddNewAccessLink(); + } + } + // Otherwise, add a new one + else + { + AddNewAccessLink(); + } + + void AddNewAccessLink() + { + var newAccessLink = new AccessLink + { + Name = SelectedName, + Type = _accessLinkType, + Path = SelectedPath + }; + QuickAccessLinks.Add(newAccessLink); + DialogResult = true; + Close(); + } + } + + private void SelectPath_OnClick(object commandParameter, RoutedEventArgs e) + { + // Open file or folder selection dialog based on the selected radio button + if (IsFileSelected) + { + var openFileDialog = new OpenFileDialog + { + Multiselect = false, + CheckFileExists = true, + CheckPathExists = true + }; + + if (openFileDialog.ShowDialog() != System.Windows.Forms.DialogResult.OK || + string.IsNullOrEmpty(openFileDialog.FileName)) + return; + + SelectedPath = openFileDialog.FileName; + } + else // Folder selection + { + var folderBrowserDialog = new FolderBrowserDialog + { + ShowNewFolderButton = true + }; + + if (folderBrowserDialog.ShowDialog() != System.Windows.Forms.DialogResult.OK || + string.IsNullOrEmpty(folderBrowserDialog.SelectedPath)) + return; + + SelectedPath = folderBrowserDialog.SelectedPath; + } + } + + private static ResultType GetResultType(string path) + { + // Check if the path is a file or folder + if (File.Exists(path)) + { + return ResultType.File; + } + else if (Directory.Exists(path)) + { + if (string.Equals(Path.GetPathRoot(path), path, StringComparison.OrdinalIgnoreCase)) + { + return ResultType.Volume; + } + else + { + return ResultType.Folder; + } + } + else + { + // This should not happen, but just in case, we assume it's a folder + Main.Context.API.LogError(ClassName, $"The path '{path}' does not exist or is invalid. Defaulting to Folder type."); + return ResultType.Folder; + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj index 1e662de9e..d8db0abe1 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj @@ -2,7 +2,7 @@ Library - net7.0-windows + net9.0-windows {FDED22C8-B637-42E8-824A-63B5B6E05A3A} Properties Flow.Launcher.Plugin.PluginIndicator diff --git a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Languages/sr-Cyrl-RS.xaml b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Languages/sr-Cyrl-RS.xaml new file mode 100644 index 000000000..893948d3d --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Languages/sr-Cyrl-RS.xaml @@ -0,0 +1,9 @@ + + + + Activate {0} plugin action keyword + + Plugin Indicator + Provides plugins action words suggestions + + diff --git a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Languages/tr.xaml index f7f4d8d76..b2ffe60c2 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Languages/tr.xaml @@ -1,7 +1,7 @@  - Activate {0} plugin action keyword + {0} eklenti anahtar sözcüğünü etkinleştir Eklenti Göstergesi Eklenti eylemleri hakkında kelime önerileri sunar diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj b/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj index 8ff41a7ad..6abc1a580 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj @@ -1,7 +1,7 @@  Library - net7.0-windows + net9.0-windows true true true diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/de.xaml index c7ef77801..ea8b9e142 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/de.xaml @@ -43,15 +43,15 @@ Plug-in {0} erfolgreich aktualisiert. Bitte starten Sie Flow neu. {0} Plug-ins erfolgreich aktualisiert. Bitte starten Sie Flow neu. Plug-in {0} ist bereits modifiziert worden. Bitte starten Sie Flow neu, bevor Sie irgendwelche weitere Änderungen vornehmen. - {0} modified already - Please restart Flow before making any further changes + {0} bereits modifiziert + Bitte starten Sie Flow neu, bevor Sie irgendwelche weitere Änderungen vornehmen - Invalid zip installer file - Please check if there is a plugin.json in {0} + Ungültige Zip-Installer-Datei + Bitte überprüfen Sie, ob es eine plugin.json in {0} gibt Plug-ins-Manager - Install, uninstall or update Flow Launcher plugins via the search window + Flow Launcher-Plug-ins via Suchfenster installieren, deinstallieren oder aktualisieren Unbekannter Autor @@ -66,5 +66,5 @@ Warnung vor Installation aus unbekannter Quelle - Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager + Flow Launcher nach Installation/Deinstallation/Aktualisierung des Plug-ins via Plug-in-Manager automatisch neu starten diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/es.xaml index b6a3a6cbc..09a7c8c5e 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/es.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/es.xaml @@ -27,7 +27,7 @@ {0} por {1} {2}{2}¿Desea actualizar este complemento? Actualizar complemento Este complemento ya está instalado - La descarga del manifiesto del complemento ha fallado + Ha fallado la descarga del manifiesto del complemento Por favor, compruebe que puede establecer conexión con github.com. Este error significa que es posible que no pueda instalar o actualizar complementos. Actualizar todos los complementos ¿Desea actualizar todos los complementos? diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sr-Cyrl-RS.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sr-Cyrl-RS.xaml new file mode 100644 index 000000000..1005a5677 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sr-Cyrl-RS.xaml @@ -0,0 +1,70 @@ + + + + + Downloading plugin + Successfully downloaded {0} + Error: Unable to download the plugin + {0} by {1} {2}{3}Would you like to uninstall this plugin? After the uninstallation Flow will automatically restart. + {0} by {1} {2}{2}Would you like to uninstall this plugin? + {0} by {1} {2}{3}Would you like to install this plugin? After the installation Flow will automatically restart. + {0} by {1} {2}{2}Would you like to install this plugin? + Plugin Install + Installing Plugin + Download and install {0} + Plugin Uninstall + Keep plugin settings + Do you want to keep the settings of the plugin for the next usage? + Plugin {0} successfully installed. Restarting Flow, please wait... + Unable to find the plugin.json metadata file from the extracted zip file. + Error: A plugin which has the same or greater version with {0} already exists. + Error installing plugin + Error occurred while trying to install {0} + Error uninstalling plugin + No update available + All plugins are up to date + {0} by {1} {2}{3}Would you like to update this plugin? After the update Flow will automatically restart. + {0} by {1} {2}{2}Would you like to update this plugin? + Plugin Update + This plugin is already installed + Plugin Manifest Download Failed + Please check if you can connect to github.com. This error means you may not be able to install or update plugins. + Update all plugins + Would you like to update all plugins? + Would you like to update {0} plugins?{1}Flow Launcher will restart after updating all plugins. + Would you like to update {0} plugins? + {0} plugins successfully updated. Restarting Flow, please wait... + Plugin {0} successfully updated. Restarting Flow, please wait... + Installing from an unknown source + You are installing this plugin from an unknown source and it may contain potential risks!{0}{0}Please ensure you understand where this plugin is from and that it is safe.{0}{0}Would you like to continue still?{0}{0}(You can switch off this warning via settings) + + Plugin {0} successfully installed. Please restart Flow. + Plugin {0} successfully uninstalled. Please restart Flow. + Plugin {0} successfully updated. Please restart Flow. + {0} plugins successfully updated. Please restart Flow. + Plugin {0} has already been modified. Please restart Flow before making any further changes. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} + + + Plugins Manager + Install, uninstall or update Flow Launcher plugins via the search window + Unknown Author + + + Open website + Visit the plugin's website + See source code + See the plugin's source code + Suggest an enhancement or submit an issue + Suggest an enhancement or submit an issue to the plugin developer + Go to Flow's plugins repository + Visit the PluginsManifest repository to see community-made plugin submissions + + + Install from unknown source warning + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager + diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/tr.xaml index ed9aaf4b3..ab87b5817 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/tr.xaml @@ -5,66 +5,66 @@ Eklenti indiriliyor Successfully downloaded Hata: Eklenti indirilemiyor - {0} by {1} {2}{3}Would you like to uninstall this plugin? After the uninstallation Flow will automatically restart. - {0} by {1} {2}{2}Would you like to uninstall this plugin? - {0} by {1} {2}{3}Would you like to install this plugin? After the installation Flow will automatically restart. - {0} by {1} {2}{2}Would you like to install this plugin? - Plugin Install - Installing Plugin - Download and install {0} - Plugin Uninstall - Keep plugin settings - Do you want to keep the settings of the plugin for the next usage? + {0}, {1} tarafından geliştirildi {2}{3}Bu eklentiyi kaldırmak ister misiniz? Kaldırma işlemi tamamlandığında Flow kendiliğinden yeniden başlatılacak. + {0} - {1} tarafından geliştirildi {2}{2}Bu eklentiyi kaldırmak ister misiniz? + {0}, {1} tarafından geliştirildi {2}{3}Bu eklentiyi yüklemek ister misiniz? Yükleme işlemi tamamlandığında Flow kendiliğinden yeniden başlatılacak. + {0} - {1} tarafından geliştirildi {2}{2}Bu eklentiyi yüklemek ister misiniz? + Eklenti Yükleme + Eklenti Yükleniyor + {0} İndir ve yükle + Eklenti Kaldırma + Eklenti ayarlarını sakla + Eklentinin ayarlarını bir sonraki kullanım için saklamak istiyor musunuz? Plugin successfully installed. Restarting Flow, please wait... - Unable to find the plugin.json metadata file from the extracted zip file. - Error: A plugin which has the same or greater version with {0} already exists. - Error installing plugin - Error occurred while trying to install {0} - Error uninstalling plugin - No update available + Çıkarılan zip dosyasından plugin.json metadata dosyası bulunamıyor. + Hata: {0} ile aynı veya daha yüksek sürüme sahip bir eklenti zaten mevcut. + Eklenti yüklenirken bir hata oluştu + {0} yüklenmeye çalışılırken hata oluştu + Eklenti kaldırılırken bir hata oluştu + Güncelleme mevcut değil Tüm eklentiler güncel - {0} by {1} {2}{3}Would you like to update this plugin? After the update Flow will automatically restart. - {0} by {1} {2}{2}Would you like to update this plugin? - Plugin Update - This plugin is already installed - Plugin Manifest Download Failed - Please check if you can connect to github.com. This error means you may not be able to install or update plugins. + {0}, {1} tarafından geliştirildi {2}{3}Bu eklentiyi güncellemek ister misiniz? Güncelleme tamamlandığında Flow kendiliğinden yeniden başlatılacak. + {0} - {1} tarafından geliştirildi {2}{2}Bu eklentiyi güncellemek ister misiniz? + Eklenti Güncellemesi + Bu eklenti zaten yüklü + Eklenti Manifestosu İndirme Başarısız Oldu + Lütfen github.com'a bağlanıp bağlanamadığınızı kontrol edin. Bu hata, eklentileri yükleyemeyeceğiniz veya güncelleyemeyeceğiniz anlamına gelir. Tüm eklentileri güncelle - Would you like to update all plugins? - Would you like to update {0} plugins?{1}Flow Launcher will restart after updating all plugins. - Would you like to update {0} plugins? - {0} plugins successfully updated. Restarting Flow, please wait... - Plugin {0} successfully updated. Restarting Flow, please wait... - Installing from an unknown source - You are installing this plugin from an unknown source and it may contain potential risks!{0}{0}Please ensure you understand where this plugin is from and that it is safe.{0}{0}Would you like to continue still?{0}{0}(You can switch off this warning via settings) + Tüm eklentileri güncellemek ister misiniz? + {0} eklentiyi güncellemek ister misiniz? {1}Flow Launcher tüm eklentileri güncelledikten sonra yeniden başlayacaktır. + {0} eklentisini güncellemek ister misiniz? + {0} eklentileri başarıyla güncellendi. Flow yeniden başlatılıyor, lütfen bekleyin... + {0} eklentisi başarıyla güncellendi. Flow yeniden başlatılıyor, lütfen bekleyin... + Bilinmeyen bir kaynaktan yükleniyor + Bu eklentiyi bilinmeyen bir kaynaktan yüklüyorsunuz ve potansiyel riskler içerebilir!{0}{0}Lütfen bu eklentinin nereden geldiğini ve güvenli olduğunu anladığınızdan emin olun.{0}{0}Devam etmek ister misiniz?{0}{0}(Bu uyarıyı ayarlardan kapatabilirsiniz) - Plugin {0} successfully installed. Please restart Flow. - Plugin {0} successfully uninstalled. Please restart Flow. - Plugin {0} successfully updated. Please restart Flow. - {0} plugins successfully updated. Please restart Flow. - Plugin {0} has already been modified. Please restart Flow before making any further changes. - {0} modified already - Please restart Flow before making any further changes + {0} eklentisi başarıyla yüklendi. Lütfen Flow'u yeniden başlatın. + {0} eklentisi başarıyla kaldırıldı. Lütfen Flow'u yeniden başlatın. + {0} eklentisi başarıyla güncellendi. Lütfen Flow'u yeniden başlatın. + {0} eklentileri başarıyla güncellendi. Lütfen Flow'u yeniden başlatın. + {0} eklentisi zaten değiştirilmiş. Lütfen daha fazla değişiklik yapmadan önce Flow'u yeniden başlatın. + {0} zaten değiştirilmiş + Lütfen başka değişiklikler yapmadan önce Flow'u yeniden başlatın - Invalid zip installer file - Please check if there is a plugin.json in {0} + Geçersiz zip yükleyici dosyası + Lütfen {0} içerisinde bir plugin.json olup olmadığını kontrol edin - Plugins Manager - Install, uninstall or update Flow Launcher plugins via the search window + Eklenti Yöneticisi + Arama penceresi aracılığıyla Flow Launcher eklentilerini yükleyin, kaldırın veya güncelleyin Bilinmeyen Yazar - Open website - Visit the plugin's website - See source code - See the plugin's source code - Suggest an enhancement or submit an issue - Suggest an enhancement or submit an issue to the plugin developer - Go to Flow's plugins repository - Visit the PluginsManifest repository to see community-made plugin submissions + Web sitesini aç + Eklentinin web sitesini ziyaret edin + Kaynak kodunu görüntüle + Eklentinin kaynak kodunu görüntüle + Bir geliştirme öner veya bir sorun gönder + Eklenti geliştiricisine bir geliştirme önerin veya bir sorun gönderin + Flow'un eklenti deposuna gidin + Topluluk tarafından yapılan eklenti gönderimlerini görmek için PluginsManifest deposunu ziyaret edin - Install from unknown source warning - Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager + Bilinmeyen kaynaktan yükleme uyarısı + Eklenti Yöneticisi aracılığıyla eklentiyi yükledikten/kaldırdıktan/güncelleştirdikten sonra Flow Launcher'ı otomatik olarak yeniden başlatın diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj index 0c501b2d9..2da97ebbd 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj @@ -2,7 +2,7 @@ Library - net7.0-windows + net9.0-windows Flow.Launcher.Plugin.ProcessKiller Flow.Launcher.Plugin.ProcessKiller Flow-Launcher @@ -52,7 +52,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/sr-Cyrl-RS.xaml b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/sr-Cyrl-RS.xaml new file mode 100644 index 000000000..0a7176d2c --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/sr-Cyrl-RS.xaml @@ -0,0 +1,14 @@ + + + + Process Killer + Kill running processes from Flow Launcher + + kill all instances of "{0}" + kill {0} processes + kill all instances + + Show title for processes with visible windows + Put processes with visible windows on the top + + diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/tr.xaml index 0a7176d2c..c9a761d70 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/tr.xaml @@ -1,14 +1,14 @@  - Process Killer - Kill running processes from Flow Launcher + İşlem Sonlandırıcı + Flow Launcher'dan çalışan işlemleri sonlandırın - kill all instances of "{0}" - kill {0} processes - kill all instances + "{0}" için tüm işlemleri sonlandır + {0} işlemini sonlandır + tüm işlemleri sonlandır - Show title for processes with visible windows - Put processes with visible windows on the top + Görünür pencereleri olan işlemler için başlığı göster + Görünür pencereleri olan işlemleri en üste yerleştir diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs index 386782905..cea34f7dc 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs @@ -163,23 +163,20 @@ namespace Flow.Launcher.Plugin.ProcessKiller try { var handle = PInvoke.OpenProcess(PROCESS_ACCESS_RIGHTS.PROCESS_QUERY_LIMITED_INFORMATION, false, (uint)p.Id); - if (handle.Value == IntPtr.Zero) + if (handle == HWND.Null) { return string.Empty; } - using var safeHandle = new SafeProcessHandle(handle.Value, true); + using var safeHandle = new SafeProcessHandle((nint)handle.Value, true); uint capacity = 2000; Span buffer = new char[capacity]; - fixed (char* pBuffer = buffer) + if (!PInvoke.QueryFullProcessImageName(safeHandle, PROCESS_NAME_FORMAT.PROCESS_NAME_WIN32, buffer, ref capacity)) { - if (!PInvoke.QueryFullProcessImageName(safeHandle, PROCESS_NAME_FORMAT.PROCESS_NAME_WIN32, (PWSTR)pBuffer, ref capacity)) - { - return string.Empty; - } - - return buffer[..(int)capacity].ToString(); + return string.Empty; } + + return buffer[..(int)capacity].ToString(); } catch { diff --git a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj index 99c1a12e9..da2b19d7c 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj +++ b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj @@ -2,7 +2,7 @@ Library - net7.0-windows10.0.19041.0 + net9.0-windows10.0.19041.0 {FDB3555B-58EF-4AE6-B5F1-904719637AB4} Properties Flow.Launcher.Plugin.Program @@ -58,17 +58,18 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive + \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/sr-Cyrl-RS.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/sr-Cyrl-RS.xaml new file mode 100644 index 000000000..fae1abedb --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/sr-Cyrl-RS.xaml @@ -0,0 +1,99 @@ + + + + + Reset Default + Delete + Edit + Add + Name + Enable + Enabled + Disable + Status + Enabled + Disabled + Location + All Programs + File Type + Reindex + Indexing + Index Sources + Options + UWP Apps + When enabled, Flow will load UWP Applications + Start Menu + When enabled, Flow will load programs from the start menu + Registry + When enabled, Flow will load programs from the registry + PATH + When enabled, Flow will load programs from the PATH environment variable + Hide app path + For executable files such as UWP or lnk, hide the file path from being visible + Hide uninstallers + Hides programs with common uninstaller names, such as unins000.exe + Search in Program Description + Flow will search program's description + Hide duplicated apps + Hide duplicated Win32 programs that are already in the UWP list + Suffixes + Max Depth + + Directory + Browse + File Suffixes: + Maximum Search Depth (-1 is unlimited): + + Please select a program source + Are you sure you want to delete the selected program sources? + Please select program sources that are not added by you + Please select program sources that are added by you + Another program source with the same location already exists. + + Program Source + Edit directory and status of this program source. + + Update + Program Plugin will only index files with selected suffixes and .url files with selected protocols. + Successfully updated file suffixes + File suffixes can't be empty + Protocols can't be empty + + File Suffixes + URL Protocols + Steam Games + Epic Games + Http/Https + Custom URL Protocols + Custom File Suffixes + + Insert file suffixes you want to index. Suffixes should be separated by ';'. (ex>bat;py) + + + Insert protocols of .url files you want to index. Protocols should be separated by ';', and should end with "://". (ex>ftp://;mailto://) + + + Run As Different User + Run As Administrator + Open containing folder + Hide + Open target folder + + Program + Search programs in Flow Launcher + + Invalid Path + + Customized Explorer + Args + You can customize the explorer used for opening the container folder by inputing the Environmental Variable of the explorer you want to use. It will be useful to use CMD to test whether the Environmental Variable is available. + Enter the customized args you want to add for your customized explorer. %s for parent directory, %f for full path (which only works for win32). Check the explorer's website for details. + + + Success + Error + Successfully disabled this program from displaying in your query + This app is not intended to be run as administrator + Unable to run {0} + + diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/tr.xaml index e75dd0fc9..1316d3fd4 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/tr.xaml @@ -8,34 +8,34 @@ Ekle Ad Etkin - Enabled - Disable + Etkin + Devre Dışı Bırak Durum - Enabled - Disabled + Etkin + Devre dışı Konum Tüm Programlar - File Type + Dosya Tipi Yeniden İndeksle İndeksleniyor - Index Sources + Kaynak Dizinleri Seçenekler UWP Uygulamaları - When enabled, Flow will load UWP Applications + Etkinleştirildiğinde Flow, UWP Uygulamalarını yükleyecektir Başlat Menüsü - When enabled, Flow will load programs from the start menu - Registry - When enabled, Flow will load programs from the registry + Etkinleştirildiğinde Flow, programları başlat menüsünden yükleyecektir + Kayıt Defteri + Etkinleştirildiğinde Flow, programları kayıt defterinden yükleyecektir YOL - When enabled, Flow will load programs from the PATH environment variable + Etkinleştirildiğinde Flow, programları PATH ortam değişkeninden yükleyecektir Uygulama yolunu gizle - For executable files such as UWP or lnk, hide the file path from being visible - Hide uninstallers - Hides programs with common uninstaller names, such as unins000.exe - Search in Program Description - Flow will search program's description - Hide duplicated apps - Hide duplicated Win32 programs that are already in the UWP list + UWP ya da lnk gibi dosyalar için dosya yolunu gösterme + Kaldırıcıları gizle + unins000.exe gibi yaygın kaldırıcı adlarına sahip programları gizler + Program Açıklamasında Ara + Flow, programların açıklamasında arama yapar + Yinelenen uygulamaları gizle + UWP listesinde zaten bulunan yinelenen Win32 programları gizler Uzantılar Derinlik @@ -45,55 +45,55 @@ Maksimum Arama Derinliği (Limitsiz için -1): İşlem yapmak istediğiniz klasörü seçin. - Are you sure you want to delete the selected program sources? - Please select program sources that are not added by you - Please select program sources that are added by you - Another program source with the same location already exists. + Seçili program kaynaklarını silmek istediğinizden emin misiniz? + Lütfen sizin tarafınızdan eklenmemiş program kaynaklarını seçin + Lütfen sizin tarafınızdan eklenmiş program kaynaklarını seçin + Aynı konuma sahip başka bir program kaynağı zaten mevcut. - Program Source - Edit directory and status of this program source. + Program Kaynağı + Bu programın kaynağının dizinini ve durumunu düzenleyin. Güncelle - Program Plugin will only index files with selected suffixes and .url files with selected protocols. + Program eklentisi yalnızca seçilen uzantılara sahip dosyaları ve belirli protokollere sahip .url dosyalarını dizine ekler. Dosya uzantıları başarıyla güncellendi Dosya uzantıları boş olamaz - Protocols can't be empty + Protokoller boş olamaz İndekslenecek Uzantılar URL Protokolleri - Steam Games + Steam Oyunları Epic Games Http/Https - Custom URL Protocols - Custom File Suffixes + Özel URL Protokolleri + Özel Dosya Sonekleri Insert file suffixes you want to index. Suffixes should be separated by ';'. (ex>bat;py) - Insert protocols of .url files you want to index. Protocols should be separated by ';', and should end with "://". (ex>ftp://;mailto://) + İndekslemek istediğiniz .url dosyalarının protokollerini girin. Protokoller ';' ile ayrılmalı ve "://" ile bitmelidir. (örn>ftp://;mailto://) - Run As Different User + Başka Bir Kullanıcı Olarak Çalıştır Yönetici Olarak Çalıştır İçeren klasörü aç Gizle - Open target folder + Hedef klasörü aç Program Programları Flow Launcher'tan arayın Geçersiz Konum - Customized Explorer - Args + Özelleştirilmiş Gezgin + Parametreler You can customize the explorer used for opening the container folder by inputing the Environmental Variable of the explorer you want to use. It will be useful to use CMD to test whether the Environmental Variable is available. Enter the customized args you want to add for your customized explorer. %s for parent directory, %f for full path (which only works for win32). Check the explorer's website for details. Başarılı - Error - Successfully disabled this program from displaying in your query - This app is not intended to be run as administrator - Unable to run {0} + Hata + Bu programın sorgunuzda görüntülenmesi başarıyla devre dışı bırakıldı + Bu uygulama yönetici olarak çalıştırılmak için tasarlanmamıştır + {0} çalıştırılamıyor diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml index 29158b2ce..1f7c91244 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml @@ -2,7 +2,7 @@ - Скинути до значення за замовчуванням + Скинути до типового значення Видалити Редагувати Додати @@ -20,8 +20,8 @@ Індексація Джерела індексу Параметри - Додатки UWP - Якщо увімкнено, Flow буде завантажувати UWP-додатки + UWP-застосунки + Якщо увімкнено, Flow буде завантажувати UWP-застосунки Меню Пуск Якщо увімкнено, Flow завантажуватиме програми зі стартового меню Реєстр @@ -87,7 +87,7 @@ Кастомізований провідник Аргументи Ви можете налаштувати провідник, який використовується для відкриття теки контейнера, ввівши змінну середовища провідника, який ви хочете використовувати. Буде корисно використовувати CMD, аби перевірити, чи доступна змінна середовища. - Введіть спеціальні аргументи, які ви хочете додати до вашого провідника. %s для батьківського каталогу, %f для повного шляху (працює лише для win32). Докладнішу інформацію можна знайти на веб-сайті провідника. + Введіть спеціальні аргументи, які ви хочете додати до вашого провідника. %s для батьківського каталогу, %f для повного шляху (працює лише для win32). Докладнішу інформацію можна знайти на вебсайті провідника. Успішно diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index d28845994..7c30c0c96 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows.Controls; -using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin.Program.Programs; using Flow.Launcher.Plugin.Program.Views; using Flow.Launcher.Plugin.Program.Views.Models; @@ -104,7 +103,7 @@ namespace Flow.Launcher.Plugin.Program .Where(p => HideDuplicatedWindowsAppFilter(p, uwpsDirectories)) .Where(p => p.Enabled) .Select(p => p.Result(query.Search, Context.API)) - .Where(r => r?.Score > 0) + .Where(r => string.IsNullOrEmpty(query.Search) || r?.Score > 0) .ToList(); } catch (OperationCanceledException) @@ -234,13 +233,21 @@ namespace Flow.Launcher.Plugin.Program } } - // Move old cache files to the new cache directory - var oldWin32CacheFile = Path.Combine(DataLocation.CacheDirectory, $"{Win32CacheName}.cache"); - var newWin32CacheFile = Path.Combine(pluginCacheDirectory, $"{Win32CacheName}.cache"); - MoveFile(oldWin32CacheFile, newWin32CacheFile); - var oldUWPCacheFile = Path.Combine(DataLocation.CacheDirectory, $"{UwpCacheName}.cache"); - var newUWPCacheFile = Path.Combine(pluginCacheDirectory, $"{UwpCacheName}.cache"); - MoveFile(oldUWPCacheFile, newUWPCacheFile); + // If plugin cache directory is this: D:\\Data\\Cache\\Plugins\\Flow.Launcher.Plugin.Program + // then the parent directory is: D:\\Data\\Cache + // So we can use the parent of the parent directory to get the cache directory path + var directoryInfo = new DirectoryInfo(pluginCacheDirectory); + var cacheDirectory = directoryInfo.Parent?.Parent?.FullName; + // Move old cache files to the new cache directory if cache directory exists + if (!string.IsNullOrEmpty(cacheDirectory)) + { + var oldWin32CacheFile = Path.Combine(cacheDirectory, $"{Win32CacheName}.cache"); + var newWin32CacheFile = Path.Combine(pluginCacheDirectory, $"{Win32CacheName}.cache"); + MoveFile(oldWin32CacheFile, newWin32CacheFile); + var oldUWPCacheFile = Path.Combine(cacheDirectory, $"{UwpCacheName}.cache"); + var newUWPCacheFile = Path.Combine(pluginCacheDirectory, $"{UwpCacheName}.cache"); + MoveFile(oldUWPCacheFile, newUWPCacheFile); + } await _win32sLock.WaitAsync(); _win32s = await context.API.LoadCacheBinaryStorageAsync(Win32CacheName, pluginCacheDirectory, new List()); @@ -446,7 +453,7 @@ namespace Flow.Launcher.Plugin.Program var title = Context.API.GetTranslation("flowlauncher_plugin_program_disable_dlgtitle_error"); var message = string.Format(Context.API.GetTranslation("flowlauncher_plugin_program_run_failed"), info.FileName); - Context.API.ShowMsg(title, string.Format(message, info.FileName), string.Empty); + Context.API.ShowMsgError(title, message); } } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs index fac3ab181..5c2f6f976 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs @@ -27,28 +27,36 @@ namespace Flow.Launcher.Plugin.Program.Programs // If there is no resource to localize a file name the method returns a non zero value. fixed (char* bufferPtr = buffer) { - var result = PInvoke.SHGetLocalizedName(path, bufferPtr, capacity, out var id); - if (result != HRESULT.S_OK) + int id; + fixed (char* pathPtr = path) { - return string.Empty; - } + var result = PInvoke.SHGetLocalizedName(new PCWSTR(pathPtr), bufferPtr, capacity, &id); - var resourcePathStr = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(bufferPtr).ToString(); - _ = PInvoke.ExpandEnvironmentStrings(resourcePathStr, bufferPtr, capacity); - using var handle = PInvoke.LoadLibraryEx(resourcePathStr, - LOAD_LIBRARY_FLAGS.DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_FLAGS.LOAD_LIBRARY_AS_DATAFILE); - if (handle.IsInvalid) - { - return string.Empty; - } + if (result != HRESULT.S_OK) + { + return string.Empty; + } - // not sure about the behavior of Pinvoke.LoadString, so we clear the buffer before using it (so it must be a null-terminated string) - buffer.Clear(); + var resourcePathStr = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(bufferPtr).ToString(); + fixed (char* resourcePathPtr = resourcePathStr) + { + _ = PInvoke.ExpandEnvironmentStrings(new PCWSTR(resourcePathPtr), bufferPtr, capacity); + using var handle = PInvoke.LoadLibraryEx(resourcePathStr, + LOAD_LIBRARY_FLAGS.DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_FLAGS.LOAD_LIBRARY_AS_DATAFILE); + if (handle.IsInvalid) + { + return string.Empty; + } - if (PInvoke.LoadString(handle, (uint)id, bufferPtr, capacity) != 0) - { - var lString = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(bufferPtr).ToString(); - return lString; + // not sure about the behavior of Pinvoke.LoadString, so we clear the buffer before using it (so it must be a null-terminated string) + buffer.Clear(); + + if (PInvoke.LoadString(handle, (uint)id, buffer, capacity) != 0) + { + var lString = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(bufferPtr).ToString(); + return lString; + } + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs index c2f4574a9..9a8326e9a 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs @@ -425,7 +425,7 @@ namespace Flow.Launcher.Plugin.Program.Programs } } - if (!matchResult.IsSearchPrecisionScoreMet()) + if (!matchResult.IsSearchPrecisionScoreMet() && !string.IsNullOrEmpty(query)) return null; var result = new Result @@ -462,14 +462,13 @@ namespace Flow.Launcher.Plugin.Program.Programs var message = api.GetTranslation( "flowlauncher_plugin_program_run_as_administrator_not_supported_message"); - api.ShowMsg(title, message, string.Empty); + api.ShowMsgError(title, message); } return true; } }; - return result; } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index a87b002d4..7aca8f3b6 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -136,7 +136,7 @@ namespace Flow.Launcher.Plugin.Program.Programs List candidates = new List(); - if (!matchResult.IsSearchPrecisionScoreMet()) + if (!matchResult.IsSearchPrecisionScoreMet() && !string.IsNullOrEmpty(query)) { if (ExecutableName != null) // only lnk program will need this one { diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj b/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj index c7ea7cdd5..5c3475133 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj +++ b/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj @@ -2,7 +2,7 @@ Library - net7.0-windows + net9.0-windows {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} Properties Flow.Launcher.Plugin.Shell diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/ar.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/ar.xaml index 39bd105a5..5bb385c31 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/ar.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/ar.xaml @@ -15,4 +15,6 @@ التشغيل كمسؤول نسخ الأمر إظهار عدد أوامر الأكثر استخدامًا فقط: + Command not found: {0} + Error running the command: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/cs.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/cs.xaml index dfe9e7d4f..fe5d30d7e 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/cs.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/cs.xaml @@ -15,4 +15,6 @@ Spustit jako správce Kopírovat příkaz Zobrazit pouze počet nejpoužívanějších příkazů: + Command not found: {0} + Error running the command: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/da.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/da.xaml index 90eb49317..a013f3b5a 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/da.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/da.xaml @@ -15,4 +15,6 @@ Run As Administrator Copy the command Only show number of most used commands: + Command not found: {0} + Error running the command: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/de.xaml index dcfc82e0a..8e2078c70 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/de.xaml @@ -15,4 +15,6 @@ Als Administrator ausführen Kopieren des Befehls Nur Anzahl der am meisten verwendeten Befehle zeigen: + Befehl nicht gefunden: {0} + Fehler beim Ausführen des Befehls: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/en.xaml index 645a0e14f..2fb9c6b67 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/en.xaml @@ -1,6 +1,7 @@ - + Replace Win+R Close Command Prompt after pressing any key @@ -16,4 +17,6 @@ Run As Administrator Copy the command Only show number of most used commands: + Command not found: {0} + Error running the command: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/es-419.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/es-419.xaml index 3fc6b8ba4..7deafcc62 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/es-419.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/es-419.xaml @@ -15,4 +15,6 @@ Ejecutar como administrador Copiar comando Sólo mostrar el número de comandos más usados: + Command not found: {0} + Error running the command: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/es.xaml index a3f97eac4..d3fcb11f7 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/es.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/es.xaml @@ -15,4 +15,6 @@ Ejecutar como administrador Copiar el comando Mostrar solo el siguiente número de comandos más usados: + Comando no encontrado: {0} + Error al ejecutar el comando: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/fr.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/fr.xaml index 54118e0f3..de0741c6e 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/fr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/fr.xaml @@ -15,4 +15,6 @@ Exécuter en tant qu'administrateur Copier la commande Afficher uniquement le nombre de commandes les plus utilisées : + Commande introuvable : {0} + Erreur lors de l'exécution de la commande : {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/he.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/he.xaml index 1a8ebbb7f..d44e96df4 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/he.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/he.xaml @@ -15,4 +15,6 @@ הפעל כמנהל העתק את הפקודה הצג רק את מספר הפקודות הנפוצות ביותר: + Command not found: {0} + Error running the command: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/it.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/it.xaml index 506f20446..d4886323a 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/it.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/it.xaml @@ -15,4 +15,6 @@ Esegui Come Amministratore Copia il comando Mostra solo il numero di comandi più usati: + Command not found: {0} + Error running the command: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/ja.xaml index 781227267..440d33697 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/ja.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/ja.xaml @@ -15,4 +15,6 @@ 管理者として実行 Copy the command Only show number of most used commands: + Command not found: {0} + Error running the command: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/ko.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/ko.xaml index b661e836b..8a69bea74 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/ko.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/ko.xaml @@ -15,4 +15,6 @@ 관리자 권한으로 실행 명령어 복사 가장 많이 사용된 명령 표시 수 + Command not found: {0} + Error running the command: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/nb.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/nb.xaml index b00b78d6f..b86c82844 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/nb.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/nb.xaml @@ -15,4 +15,6 @@ Kjør som administrator Kopier kommandoen Vis bare antall mest brukte kommandoer: + Command not found: {0} + Error running the command: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/nl.xaml index a429524ac..069026a22 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/nl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/nl.xaml @@ -15,4 +15,6 @@ Run As Administrator Copy the command Only show number of most used commands: + Command not found: {0} + Error running the command: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/pl.xaml index 0ec1f92fb..bdc84917b 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/pl.xaml @@ -15,4 +15,6 @@ Uruchom jako administrator Skopiuj komendę Pokaż tylko liczbę najczęściej używanych poleceń: + Command not found: {0} + Error running the command: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/pt-br.xaml index a2e65a741..c39c23b54 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/pt-br.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/pt-br.xaml @@ -15,4 +15,6 @@ Run As Administrator Copiar o comando Only show number of most used commands: + Command not found: {0} + Error running the command: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/pt-pt.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/pt-pt.xaml index 66f7aec85..804a89611 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/pt-pt.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/pt-pt.xaml @@ -15,4 +15,6 @@ Executar como administrador Copiar comando Mostrar este número dos comandos mais usados: + Comando não encontrado: {0} + Erro ao executar o comando: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/ru.xaml index d3975bd9e..99b525bbf 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/ru.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/ru.xaml @@ -15,4 +15,6 @@ Run As Administrator Скопировать команду Показывать только самые используемые команды: + Command not found: {0} + Error running the command: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/sk.xaml index b1bf88a31..1af5a882b 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/sk.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/sk.xaml @@ -15,4 +15,6 @@ Spustiť ako správca Kopírovať príkaz Zobraziť iba počet najpoužívanejších príkazov: + Príkaz sa nenašiel: {0} + Neporadilo sa spustiť príkaz: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/sr-Cyrl-RS.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/sr-Cyrl-RS.xaml new file mode 100644 index 000000000..a013f3b5a --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/sr-Cyrl-RS.xaml @@ -0,0 +1,20 @@ + + + + Replace Win+R + Close Command Prompt after pressing any key + Press any key to close this window... + Do not close Command Prompt after command execution + Always run as administrator + Use Windows Terminal + Run as different user + Shell + Allows to execute system commands from Flow Launcher + this command has been executed {0} times + execute command through command shell + Run As Administrator + Copy the command + Only show number of most used commands: + Command not found: {0} + Error running the command: {0} + diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/sr.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/sr.xaml index 90eb49317..a013f3b5a 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/sr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/sr.xaml @@ -15,4 +15,6 @@ Run As Administrator Copy the command Only show number of most used commands: + Command not found: {0} + Error running the command: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/tr.xaml index d421a84a4..6fc5724f6 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/tr.xaml @@ -2,17 +2,19 @@ Win+R kısayolunu kullan - Close Command Prompt after pressing any key - Press any key to close this window... + Herhangi bir tuşa bastıktan sonra Komut İstemi'ni kapatma + Bu pencereyi kapatmak için herhangi bir tuşa basın... Çalıştırma sona erdikten sonra komut istemini kapatma - Always run as administrator - Use Windows Terminal - Run as different user + Her zaman yönetici olarak çalıştır + Windows Terminal Kullan + Başka bir kullanıcı olarak çalıştır Kabuk - Allows to execute system commands from Flow Launcher - Bu komut {0} kez çalıştırıldı - Komut isteminde çalıştır + Flow Launcher'dan sistem komutlarının yürütülmesine izin verir + bu komut {0} kez çalıştırıldı + komut isteminde çalıştır Yönetici Olarak Çalıştır - Copy the command - Only show number of most used commands: + Komutu kopyala + Sadece en çok kullanılan komutların sayısını gösterir: + Komut bulunamadı: {0} + Komut çalıştırılırken hata oluştu: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/uk-UA.xaml index d47474784..cdd81d5b9 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/uk-UA.xaml @@ -15,4 +15,6 @@ Запустити від імені адміністратора Скопіювати команду Показати лише кількість найчастіше використовуваних команд: + Команду не знайдено: {0} + Помилка запуску команди: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/vi.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/vi.xaml index d21412a14..86a7673b0 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/vi.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/vi.xaml @@ -15,4 +15,6 @@ Chạy với quyền quản trị Sao chép lệnh Chỉ hiển thị số lượng lệnh được sử dụng nhiều nhất: + Command not found: {0} + Error running the command: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-cn.xaml index 0362bf6fc..5a25b37d9 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-cn.xaml @@ -15,4 +15,6 @@ 以管理员身份运行 复制命令 显示最常用的命令个数: + 找不到命令:{0} + 运行命令时出错:{0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-tw.xaml index 8936c0498..02eb35336 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-tw.xaml @@ -15,4 +15,6 @@ 以系統管理員身分執行 Copy the command Only show number of most used commands: + Command not found: {0} + Error running the command: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs index a51aadec7..a86b96800 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; @@ -335,15 +335,17 @@ namespace Flow.Launcher.Plugin.Shell } catch (FileNotFoundException e) { - var name = "Plugin: Shell"; - var message = $"Command not found: {e.Message}"; - Context.API.ShowMsg(name, message); + Context.API.ShowMsgError(GetTranslatedPluginTitle(), + string.Format(Context.API.GetTranslation("flowlauncher_plugin_cmd_command_not_found"), e.Message)); } catch (Win32Exception e) { - var name = "Plugin: Shell"; - var message = $"Error running the command: {e.Message}"; - Context.API.ShowMsg(name, message); + Context.API.ShowMsgError(GetTranslatedPluginTitle(), + string.Format(Context.API.GetTranslation("flowlauncher_plugin_cmd_error_running_command"), e.Message)); + } + catch (Exception e) + { + Context.API.LogException(ClassName, $"Error executing command: {info.FileName} {string.Join(" ", info.ArgumentList)}", e); } } diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj index dbc36ad42..8e54e1894 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj +++ b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj @@ -2,7 +2,7 @@ Library - net7.0-windows + net9.0-windows {0B9DE348-9361-4940-ADB6-F5953BFFCCEC} Properties Flow.Launcher.Plugin.Sys @@ -37,7 +37,6 @@ - @@ -59,7 +58,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ar.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ar.xaml index ccc50678e..ac45e013e 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ar.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ar.xaml @@ -61,6 +61,8 @@ هل أنت متأكد أنك تريد إعادة تشغيل الكمبيوتر؟ هل أنت متأكد أنك تريد إعادة تشغيل الكمبيوتر مع خيارات التمهيد المتقدمة؟ هل أنت متأكد أنك تريد تسجيل الخروج؟ + خطأ + Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. Command Keyword Setting Custom Command Keyword diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/cs.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/cs.xaml index de35c9592..6af81c3a5 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/cs.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/cs.xaml @@ -61,6 +61,8 @@ Opravdu chcete počítač restartovat? Opravdu chcete restartovat počítač s rozšířenými možnostmi spouštění? Opravdu se chcete odhlásit? + Chyba + Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. Command Keyword Setting Custom Command Keyword diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/da.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/da.xaml index 91230e7e3..4a910b381 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/da.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/da.xaml @@ -61,6 +61,8 @@ Are you sure you want to restart the computer? Are you sure you want to restart the computer with Advanced Boot Options? Are you sure you want to log off? + Error + Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. Command Keyword Setting Custom Command Keyword diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/de.xaml index 794e949ad..469928a29 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/de.xaml @@ -61,6 +61,8 @@ Sind Sie sicher, dass Sie den Computer neu starten wollen? Sind Sie sicher, dass Sie den Computer mit erweiterten Boot-Optionen neu starten wollen? Sind Sie sicher, dass Sie sich ausloggen wollen? + Fehler + Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. Befehls-Schlüsselwort-Einstellung Benutzerdefiniertes Befehls-Schlüsselwort diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml index ad3f8553b..56899eef3 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml @@ -63,6 +63,8 @@ Are you sure you want to restart the computer? Are you sure you want to restart the computer with Advanced Boot Options? Are you sure you want to log off? + Error + Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. Command Keyword Setting Custom Command Keyword diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/es-419.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/es-419.xaml index ac4040dca..7fa6fdb7b 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/es-419.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/es-419.xaml @@ -61,6 +61,8 @@ Are you sure you want to restart the computer? Are you sure you want to restart the computer with Advanced Boot Options? Are you sure you want to log off? + Error + Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. Command Keyword Setting Custom Command Keyword diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/es.xaml index 5f3688ab8..0db2d60d5 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/es.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/es.xaml @@ -61,6 +61,8 @@ ¿Está seguro de que desea reiniciar el equipo? ¿Está seguro de que desea reiniciar el equipo con opciones de arranque avanzadas? ¿Está seguro de que desea cerrar la sesión? + Error + No se ha podido vaciar la papelera de reciclaje. Esto puede ocurrir si:{0}- Algunos elementos están actualmente en uso{0}- Algunos elementos no se pueden eliminar debido a permisos{0}Por favor, cierre cualquier aplicación que pueda estar utilizando estos archivos e inténtelo de nuevo. Configuración de la palabra clave de comando Palabra clave de comando personalizada diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/fr.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/fr.xaml index 5419bd8e2..3a15b69a4 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/fr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/fr.xaml @@ -61,6 +61,8 @@ Êtes-vous sûr de vouloir redémarrer l'ordinateur ? Êtes-vous sûr de vouloir redémarrer l'ordinateur avec les options de démarrage avancées ? Êtes-vous sûr de vouloir vous déconnecter ? + Erreur + Impossible de vider la corbeille. Cela peut se produire si :{0}- Certains éléments sont actuellement en cours d'utilisation{0}- Certains éléments ne peuvent pas être supprimés en raison des permissions{0}. Veuillez fermer toutes les applications qui pourraient utiliser ces fichiers et réessayer. Réglage du mot-clé de commande Mot-clé de commande personnalisé diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/he.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/he.xaml index b02a89a0a..caf82072c 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/he.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/he.xaml @@ -61,6 +61,8 @@ האם אתה בטוח שברצונך להפעיל מחדש את המחשב? האם אתה בטוח שברצונך להפעיל מחדש את המחשב עם אפשרויות אתחול מתקדמות? האם אתה בטוח שברצונך להתנתק? + שגיאה + Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. הגדרת מילת מפתח לפקודה מילת מפתח מותאמת לפקודה diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/it.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/it.xaml index be31e4e52..f1d11c156 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/it.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/it.xaml @@ -61,6 +61,8 @@ Sei sicuro di voler riavviare il computer? Sei sicuro di voler riavviare il computer con le Opzioni di Avvio Avanzate? Sei sicuro di volerti disconettere? + Error + Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. Command Keyword Setting Custom Command Keyword diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml index 7d131d944..398c39c9f 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml @@ -61,12 +61,14 @@ 本当にコンピューターを再起動しますか? 高度な起動オプションでコンピューターを再起動しますか? 本当にログオフしますか? + Error + Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. Command Keyword Setting Custom Command Keyword Enter a keyword to search for command: {0}. This keyword is used to match your query. Command Keyword - Reset + リセット 確認 キャンセル Please enter a non-empty command keyword diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ko.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ko.xaml index 873e71d44..909b4a40b 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ko.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ko.xaml @@ -61,6 +61,8 @@ 시스템을 재시작 하시겠습니까? 고급 부팅 옵션으로 시스템을 다시 시작하시겠습니까? 정말 로그아웃 하시겠습니까? + Error + Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. Command Keyword Setting Custom Command Keyword diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nb.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nb.xaml index 072fd623d..4072a7454 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nb.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nb.xaml @@ -61,6 +61,8 @@ Er du sikker på at du vil starte datamaskinen på nytt? Er du sikker på at du vil starte datamaskinen på nytt med avanserte oppstartsalternativer? Er du sikker på at du vil logge av? + Feil + Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. Command Keyword Setting Custom Command Keyword diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml index 9d1d54076..e24a4b6f7 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml @@ -61,6 +61,8 @@ Weet u zeker dat u de computer wilt herstarten? Weet u zeker dat u de computer wilt herstarten met geavanceerde opstartopties? Are you sure you want to log off? + Error + Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. Command Keyword Setting Custom Command Keyword diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml index 33cee56d8..8532d4a99 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml @@ -61,6 +61,8 @@ Czy na pewno chcesz zrestartować komputer? Czy na pewno chcesz ponownie uruchomić komputer z Zaawansowanymi opcjami rozruchu? Czy na pewno chcesz się wylogować? + Błąd + Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. Command Keyword Setting Custom Command Keyword diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-br.xaml index a19ab39d6..fb0fde730 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-br.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-br.xaml @@ -61,6 +61,8 @@ Are you sure you want to restart the computer? Are you sure you want to restart the computer with Advanced Boot Options? Are you sure you want to log off? + Error + Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. Command Keyword Setting Custom Command Keyword diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-pt.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-pt.xaml index 9e0e39066..6021c96ff 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-pt.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-pt.xaml @@ -61,6 +61,8 @@ Tem a certeza que deseja reiniciar o computador? Tem certeza de que deseja reiniciar o computador com as opções avançadas de arranque? Tem certeza de que deseja terminar a sessão? + Erro + Falha ao limpar a Reciclagem. Isto pode aparecer se: {0} - alguns itens estão a ser usados {0} - alguns itens não podem ser eliminados (permissões) {0}Experimente fechar quaisquer aplicações que possam estar a usar os ficheiros e tente novamente. Definição de palavra-chave Palavra-chave personalizada diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ru.xaml index 796cda69d..69746e836 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ru.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ru.xaml @@ -61,6 +61,8 @@ Are you sure you want to restart the computer? Are you sure you want to restart the computer with Advanced Boot Options? Are you sure you want to log off? + Ошибка + Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. Command Keyword Setting Custom Command Keyword diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sk.xaml index 087ff9f05..f4823dc57 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/sk.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sk.xaml @@ -22,7 +22,7 @@ Nastavenia Znova načítať údaje pluginov Skontrolovať aktualizácie - Otvoriť umiestnenie denníka + Otvoriť umiestnenie logu Tipy pre Flow Launcher Používateľský priečinok Flow Launchera Prepnúť herný režim @@ -46,7 +46,7 @@ Hibernovať počítač Uložiť všetky nastavenia Flow Launchera Aktualizovať všetky nové dáta pluginov - Otvoriť umiestnenie denníka Flow Launchera + Otvoriť umiestnenie logu Flow Launchera Skontrolovať aktualizácie Flow Launchera V dokumentácii k aplikácii Flow Launcher nájdete ďalšiu pomoc a tipy na používanie Otvoriť umiestnenie, kde sú uložené nastavenia Flow Launchera @@ -61,6 +61,8 @@ Naozaj chcete počítač reštartovať? Naozaj chcete počítač reštartovať s pokročilými možnosťami spúšťania? Naozaj sa chcete odhlásiť? + Chyba + Nepodarilo sa vyprázdniť kôš. Môže sa to stať, ak:{0}– Niektoré položky sa práve používajú{0}– Niektoré položky sa nedajú odstrániť pre nedostatočné oprávnenia{0}Prosím, ukončite každú aplikáciu, ktorá môže používať tieto súbory a skúste to znova. Nastavenia kľúčového slova príkazu Vlastné kľúčové slovo príkazu diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr-Cyrl-RS.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr-Cyrl-RS.xaml new file mode 100644 index 000000000..2d2c039c3 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr-Cyrl-RS.xaml @@ -0,0 +1,79 @@ + + + + + Name + Description + Command + + Shutdown + Restart + Restart With Advanced Boot Options + Log Off/Sign Out + Lock + Sleep + Hibernate + Index Option + Empty Recycle Bin + Open Recycle Bin + Exit + Save Settings + Restart Flow Launcher + Settings + Reload Plugin Data + Check For Update + Open Log Location + Flow Launcher Tips + Flow Launcher UserData Folder + Toggle Game Mode + Set the Flow Launcher Theme + + Edit + + + Shutdown Computer + Restart Computer + Restart the computer with Advanced Boot Options for Safe and Debugging modes, as well as other options + Log off + Lock this computer + Close Flow Launcher + Restart Flow Launcher + Tweak Flow Launcher's settings + Put computer to sleep + Empty recycle bin + Open recycle bin + Indexing Options + Hibernate computer + Save all Flow Launcher settings + Refreshes plugin data with new content + Open Flow Launcher's log location + Check for new Flow Launcher update + Visit Flow Launcher's documentation for more help and how to use tips + Open the location where Flow Launcher's settings are stored + Toggle Game Mode + Quickly change the Flow Launcher theme + + + Success + All Flow Launcher settings saved + Reloaded all applicable plugin data + Are you sure you want to shut the computer down? + Are you sure you want to restart the computer? + Are you sure you want to restart the computer with Advanced Boot Options? + Are you sure you want to log off? + Error + Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. + + Command Keyword Setting + Custom Command Keyword + Enter a keyword to search for command: {0}. This keyword is used to match your query. + Command Keyword + Reset + Confirm + Cancel + Please enter a non-empty command keyword + + System Commands + Provides System related commands. e.g. shutdown, lock, settings etc. + + diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr.xaml index f5a2f1b30..6490848e6 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr.xaml @@ -61,6 +61,8 @@ Are you sure you want to restart the computer? Are you sure you want to restart the computer with Advanced Boot Options? Are you sure you want to log off? + Error + Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. Command Keyword Setting Custom Command Keyword diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/tr.xaml index 18f7f63f5..b8dcccbe2 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/tr.xaml @@ -2,38 +2,38 @@ - Name + Ad Açıklama Komut - Shutdown - Restart - Restart With Advanced Boot Options - Log Off/Sign Out - Lock - Sleep - Hibernate - Index Option - Empty Recycle Bin - Open Recycle Bin - Çıkı - Save Settings + Bilgisayarı Kapat + Yeniden Başlat + Gelişmiş Önyükleme Seçenekleri ile Yeniden Başlat + Oturumu Kapat + Kilitle + Uyku Moduna Geç + Hazırda Beklet + Dizin Seçeneği + Geri Dönüşüm Kutusunu Boşalt + Geri Dönüşüm Kutusunu Aç + Çıkış + Ayarları Kaydet Flow Launcher'u Yeniden Başlat Ayarlar - Reload Plugin Data - Check For Update - Open Log Location - Flow Launcher Tips - Flow Launcher UserData Folder - Toggle Game Mode - Set the Flow Launcher Theme + Eklenti Verisini Yenile + Güncellemeleri Denetle + Günlük Konumunu Aç + Flow Launcher İpuçları + Flow Launcher UserData Klasörü + Oyun Modunu Aç/Kapat + Flow Launcher Temasını Ayarla Düzenle Bilgisayarı Kapat Yeniden Başlat - Restart the computer with Advanced Boot Options for Safe and Debugging modes, as well as other options + Gelişmiş Başlatma Seçenekleri ile bilgisayarı yeniden başlatın, Güvenli Mod ve Hata Ayıklama Modu da dahil olmak üzere diğer seçenekler için Oturumu Kapat Bilgisayarı Kilitle Flow Launcher'u Kapat @@ -41,35 +41,37 @@ Flow Launcher Ayarlarını Aç Bilgisayarı Uyku Moduna Al Geri Dönüşüm Kutusunu Boşalt - Open recycle bin - Indexing Options + Geri dönüşüm kutusunu aç + Dizin Oluşturma Seçenekleri Bilgisayarı Askıya Al Tüm Flow Launcher Ayarlarını Kaydet Eklentilerin verilerini Flow Launcher'un açılışından sonra yapılan değişiklikleri için günceller. Eklentilerin bu özelliği zaten eklemiş olması gerekir. - Open Flow Launcher's log location - Check for new Flow Launcher update - Visit Flow Launcher's documentation for more help and how to use tips - Open the location where Flow Launcher's settings are stored - Toggle Game Mode - Quickly change the Flow Launcher theme + Flow Launcher'ın günlük konumunu aç + Yeni Flow Launcher güncellemelerini kontrol et + Daha fazla yardım ve ipuçları için Flow Launcher'ın dokümantasyonunu ziyaret edin + Flow Launcher'ın ayarlarının depolandığı dosya konumunu açın + Oyun Modunu Aç/Kapat + Flow Launcher temasını hızlıca değiştir Başarılı Tüm Flow Launcher ayarları kaydedildi. - Reloaded all applicable plugin data + Geçerli tüm eklenti verileri yeniden yüklendi Bilgisayarı kapatmak istediğinize emin misiniz? Bilgisayarı yeniden başlatmak istediğinize emin misiniz? - Are you sure you want to restart the computer with Advanced Boot Options? - Are you sure you want to log off? + Gelişmiş Başlatma Seçenekleri ile bilgisayarı yeniden başlatmak istediğinizden emin misiniz? + Oturumu kapatmayı istediğinizden emin misiniz? + Hata + Geri dönüşüm kutusu boşaltılamadı. Bu durum şu durumlarda meydana gelebilir:{0}- Bazı öğeler şu anda kullanımda{0}- Bazı öğeler izinler nedeniyle silinemiyor{0}Lütfen bu dosyaları kullanıyor olabilecek tüm uygulamaları kapatın ve tekrar deneyin. - Command Keyword Setting - Custom Command Keyword - Enter a keyword to search for command: {0}. This keyword is used to match your query. - Command Keyword + Komut Anahtar Kelimesi Ayarı + Özel Komut Anahtar Kelimesi + Komut aramak için bir anahtar kelimesi girin: {0}. Bu anahtar kelime, sorgunuzu eşleştirmek için kullanılır. + Komut Anahtar Kelimesi Sıfırla Onayla İptal - Please enter a non-empty command keyword + Lütfen boş olmayan bir komut anahtar kelimesini girin Sistem Komutları Sistem ile ilgili komutlara erişim sağlar. ör. shutdown, lock, settings vb. diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml index c82be249a..dd44f71fd 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml @@ -61,6 +61,8 @@ Ви впевнені, що хочете перезавантажити комп'ютер? Ви впевнені, що хочете перезавантажити комп'ютер за допомогою додаткових параметрів завантаження? Ви впевнені, що хочете вийти з системи? + Помилка + Не вдалося очистити кошик. Це може статися, якщо:{0}- Деякі елементи зараз використовуються{0}- Деякі елементи не можна видалити через обмеження доступу{0}Закрийте всі застосунки, які можуть використовувати ці файли, і спробуйте ще раз. Налаштування ключового слова команди Власне ключове слово команди diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/vi.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/vi.xaml index dae12c501..3f2e03100 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/vi.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/vi.xaml @@ -61,6 +61,8 @@ Bạn có chắc muốn khởi động lại cấp này? Bạn có chắc chắn muốn khởi động lại máy tính bằng Advanced Boot Options không? Are you sure you want to log off? + Lỗi + Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. Command Keyword Setting Custom Command Keyword diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-cn.xaml index d59b77145..90666a59b 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-cn.xaml @@ -61,6 +61,8 @@ 您确定要重启吗? 您确定要以高级启动选项重启吗? 您确定要注销吗? + 错误 + 清空回收站失败。 可能会发生这种情况:{0}- 某些项目目前正在使用。{0}- 某些项目由于权限而无法删除。{0}请关闭任何可能使用这些文件的应用程序,然后重试。 命令关键词设置 自定义命令关键词 diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-tw.xaml index 573aefcbd..3ab9c8a44 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-tw.xaml @@ -61,6 +61,8 @@ Are you sure you want to restart the computer? Are you sure you want to restart the computer with Advanced Boot Options? Are you sure you want to log off? + Error + Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. Command Keyword Setting Custom Command Keyword diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs index 39bf49654..77278a054 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs @@ -5,8 +5,6 @@ using System.Globalization; using System.Linq; using System.Runtime.InteropServices; using System.Windows; -using Flow.Launcher.Infrastructure; -using Flow.Launcher.Infrastructure.UserSettings; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.Security; @@ -52,6 +50,8 @@ namespace Flow.Launcher.Plugin.Sys private const SHUTDOWN_REASON REASON = SHUTDOWN_REASON.SHTDN_REASON_MAJOR_OTHER | SHUTDOWN_REASON.SHTDN_REASON_FLAG_PLANNED; + private const string Documentation = "https://flowlauncher.com/docs/#/usage-tips"; + private PluginInitContext _context; private Settings _settings; private ThemeSelector _themeSelector; @@ -70,13 +70,19 @@ namespace Flow.Launcher.Plugin.Sys return _themeSelector.Query(query); } - var commands = Commands(); + var commands = Commands(query); var results = new List(); + var isEmptyQuery = string.IsNullOrWhiteSpace(query.Search); foreach (var c in commands) { var command = _settings.Commands.First(x => x.Key == c.Title); c.Title = command.Name; c.SubTitle = command.Description; + if (isEmptyQuery) + { + results.Add(c); + continue; + } // Match from localized title & localized subtitle & keyword var titleMatch = _context.API.FuzzySearch(query.Search, c.Title); @@ -188,7 +194,7 @@ namespace Flow.Launcher.Plugin.Sys } } - private List Commands() + private List Commands(Query query) { var results = new List(); var recycleBinFolder = "shell:RecycleBinFolder"; @@ -332,11 +338,9 @@ namespace Flow.Launcher.Plugin.Sys var result = PInvoke.SHEmptyRecycleBin(new(), string.Empty, 0); if (result != HRESULT.S_OK && result != HRESULT.E_UNEXPECTED) { - _context.API.ShowMsgBox("Failed to empty the recycle bin. This might happen if:\n" + - "- A file in the recycle bin is in use\n" + - "- You don't have permission to delete some items\n" + - "Please close any applications that might be using these files and try again.", - "Error", + _context.API.ShowMsgBox( + string.Format(_context.API.GetTranslation("flowlauncher_plugin_sys_dlgtext_empty_recycle_bin_failed"), Environment.NewLine), + _context.API.GetTranslation("flowlauncher_plugin_sys_dlgtitle_error"), MessageBoxButton.OK, MessageBoxImage.Error); } @@ -398,6 +402,8 @@ namespace Flow.Launcher.Plugin.Sys IcoPath = "Images\\app.png", Action = c => { + // Hide the window first then open setting dialog because main window can be topmost window which will still display on top of the setting dialog for a while + _context.API.HideMainWindow(); _context.API.OpenSettingDialog(); return true; } @@ -439,11 +445,11 @@ namespace Flow.Launcher.Plugin.Sys Glyph = new GlyphInfo (FontFamily:"/Resources/#Segoe Fluent Icons", Glyph:"\xf12b"), Title = "Open Log Location", IcoPath = "Images\\app.png", - CopyText = DataLocation.VersionLogDirectory, - AutoCompleteText = DataLocation.VersionLogDirectory, + CopyText = _context.API.GetLogDirectory(), + AutoCompleteText = _context.API.GetLogDirectory(), Action = c => { - _context.API.OpenDirectory(DataLocation.VersionLogDirectory); + _context.API.OpenDirectory(_context.API.GetLogDirectory()); return true; } }, @@ -452,11 +458,11 @@ namespace Flow.Launcher.Plugin.Sys Title = "Flow Launcher Tips", Glyph = new GlyphInfo (FontFamily:"/Resources/#Segoe Fluent Icons", Glyph:"\xe897"), IcoPath = "Images\\app.png", - CopyText = Constant.Documentation, - AutoCompleteText = Constant.Documentation, + CopyText = Documentation, + AutoCompleteText = Documentation, Action = c => { - _context.API.OpenUrl(Constant.Documentation); + _context.API.OpenUrl(Documentation); return true; } }, @@ -465,11 +471,11 @@ namespace Flow.Launcher.Plugin.Sys Title = "Flow Launcher UserData Folder", Glyph = new GlyphInfo (FontFamily:"/Resources/#Segoe Fluent Icons", Glyph:"\xf12b"), IcoPath = "Images\\app.png", - CopyText = DataLocation.DataDirectory(), - AutoCompleteText = DataLocation.DataDirectory(), + CopyText = _context.API.GetDataDirectory(), + AutoCompleteText = _context.API.GetDataDirectory(), Action = c => { - _context.API.OpenDirectory(DataLocation.DataDirectory()); + _context.API.OpenDirectory(_context.API.GetDataDirectory()); return true; } }, @@ -491,7 +497,15 @@ namespace Flow.Launcher.Plugin.Sys Glyph = new GlyphInfo (FontFamily:"/Resources/#Segoe Fluent Icons", Glyph:"\ue790"), Action = c => { - _context.API.ChangeQuery($"{ThemeSelector.Keyword} "); + if (string.IsNullOrEmpty(query.ActionKeyword)) + { + _context.API.ChangeQuery($"{ThemeSelector.Keyword}{Plugin.Query.ActionKeywordSeparator}"); + } + else + { + _context.API.ChangeQuery($"{query.ActionKeyword}{Plugin.Query.ActionKeywordSeparator}{ThemeSelector.Keyword}{Plugin.Query.ActionKeywordSeparator}"); + + } return false; } } diff --git a/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj b/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj index 6d338733e..fdfe03224 100644 --- a/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj +++ b/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj @@ -2,7 +2,7 @@ Library - net7.0-windows + net9.0-windows {A3DCCBCA-ACC1-421D-B16E-210896234C26} true Properties diff --git a/Plugins/Flow.Launcher.Plugin.Url/Languages/sr-Cyrl-RS.xaml b/Plugins/Flow.Launcher.Plugin.Url/Languages/sr-Cyrl-RS.xaml new file mode 100644 index 000000000..418731021 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Url/Languages/sr-Cyrl-RS.xaml @@ -0,0 +1,17 @@ + + + + Open search in: + New Window + New Tab + + Open url:{0} + Can't open url:{0} + + URL + Open the typed URL from Flow Launcher + + Please set your browser path: + Choose + Application(*.exe)|*.exe|All files|*.* + diff --git a/Plugins/Flow.Launcher.Plugin.Url/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.Url/Languages/tr.xaml index b8ae6a93c..fc76ee12e 100644 --- a/Plugins/Flow.Launcher.Plugin.Url/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Url/Languages/tr.xaml @@ -1,9 +1,9 @@  - Open search in: - New Window - New Tab + Aramayı şurada aç: + Yeni Pencere + Yeni Sekme URL'yi Aç: {0} URL Açılamıyor: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Url/Main.cs b/Plugins/Flow.Launcher.Plugin.Url/Main.cs index 03516636d..9fa52c8da 100644 --- a/Plugins/Flow.Launcher.Plugin.Url/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Url/Main.cs @@ -70,7 +70,7 @@ namespace Flow.Launcher.Plugin.Url } catch(Exception) { - context.API.ShowMsg(string.Format(context.API.GetTranslation("flowlauncher_plugin_url_cannot_open_url"), raw)); + context.API.ShowMsgError(string.Format(context.API.GetTranslation("flowlauncher_plugin_url_cannot_open_url"), raw)); return false; } } diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj index 73726ab37..42176376b 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj @@ -2,7 +2,7 @@ Library - net7.0-windows + net9.0-windows {403B57F2-1856-4FC7-8A24-36AB346B763E} Properties true diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ar.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ar.xaml index cefb5d1d1..edb44f8f4 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ar.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ar.xaml @@ -11,6 +11,7 @@ تعديل إضافة مفعل + الوضع الخاص مفعل مُعطّل تأكيد diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/cs.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/cs.xaml index ca98581c3..2e49f4965 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/cs.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/cs.xaml @@ -11,6 +11,7 @@ Editovat Přidat Povoleno + Soukromý režim Povoleno Deaktivován Potvrdit diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/da.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/da.xaml index b1113acb7..7bc145a68 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/da.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/da.xaml @@ -11,6 +11,7 @@ Rediger Tilføj Enabled + Privattilstand Enabled Disabled Confirm diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/de.xaml index 2a7dca596..41a798d44 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/de.xaml @@ -11,6 +11,7 @@ Bearbeiten Hinzufügen Aktiviert + Privater Modus Aktiviert Deaktiviert Bestätigen diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/en.xaml index 21f836bec..c6a74a047 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/en.xaml @@ -13,6 +13,7 @@ Edit Add Enabled + Private Mode Enabled Disabled Confirm diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/es-419.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/es-419.xaml index 3ce22bb78..f170a8d73 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/es-419.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/es-419.xaml @@ -11,6 +11,7 @@ Editar Añadir Enabled + Modo Privado Enabled Disabled Confirmar diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/es.xaml index 7f14b59c6..5c956d8f8 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/es.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/es.xaml @@ -11,6 +11,7 @@ Editar Añadir Activado + Modo privado Activado Desactivado Confirmar diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/fr.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/fr.xaml index f04cfb48a..3c9003ebf 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/fr.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/fr.xaml @@ -11,6 +11,7 @@ Modifier Ajouter Activé + Mode privé Activé Désactivé Confirmer diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/he.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/he.xaml index 78ee7ca7d..583f63bde 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/he.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/he.xaml @@ -11,6 +11,7 @@ ערו הוסף מופעל + מצב פרטי מופעל מושבת אישו diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/it.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/it.xaml index db2a4dfeb..f250ecf3b 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/it.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/it.xaml @@ -11,6 +11,7 @@ Modifica Aggiungi Abilitato + Modalità Privata Abilitato Disabilitato Conferma diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ja.xaml index 4112f41ff..16c5fb3e9 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ja.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ja.xaml @@ -11,6 +11,7 @@ 編集 追加 Enabled + Private Mode Enabled Disabled Confirm diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ko.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ko.xaml index c8f069ca7..9b8198779 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ko.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ko.xaml @@ -11,6 +11,7 @@ 편집 추가 + 사생활 보호 모드 Disabled 확인 diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/nb.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/nb.xaml index 9f793c43f..93c48b545 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/nb.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/nb.xaml @@ -11,6 +11,7 @@ Rediger Legg til Aktivert + Privat modus Aktivert Deaktivert Bekreft diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/nl.xaml index a18710324..25187d0b6 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/nl.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/nl.xaml @@ -11,6 +11,7 @@ Bewerken Toevoegen Enabled + Privémodus Enabled Disabled Confirm diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pl.xaml index 05ee39777..bc32a1b7e 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pl.xaml @@ -11,6 +11,7 @@ Edytuj Dodaj Aktywny + Tryb prywatny Aktywny Wyłączony Potwierdź diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pt-br.xaml index 6f0d7fcc9..a581e91b2 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pt-br.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pt-br.xaml @@ -11,6 +11,7 @@ Editar Adicionar Enabled + Private Mode Enabled Disabled Confirm diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pt-pt.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pt-pt.xaml index 1a2476a2c..22be076cb 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pt-pt.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pt-pt.xaml @@ -11,6 +11,7 @@ Editar Adicionar Ativo + Modo privado Ativo Inativo Confirmar diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ru.xaml index 1fd9aca96..67435b5b9 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ru.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ru.xaml @@ -11,6 +11,7 @@ Редактировать Добавить Enabled + Приватный режим Enabled Отключён Confirm diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sk.xaml index 1afcdb360..355ce984f 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sk.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sk.xaml @@ -11,6 +11,7 @@ Upraviť Pridať Povolené + Privátny režim Zapnuté Vypnuté Potvrdiť diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sr-Cyrl-RS.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sr-Cyrl-RS.xaml new file mode 100644 index 000000000..cf24862c7 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sr-Cyrl-RS.xaml @@ -0,0 +1,53 @@ + + + + Search Source Setting + Open search in: + New Window + New Tab + Set browser from path: + Choose + Delete + Edit + Add + Enabled + Private Mode + Enabled + Disabled + Confirm + Action Keyword + URL + Search + Use Search Query Autocomplete + Autocomplete Data from: + Please select a web search + Are you sure you want to delete {0}? + If you want to add a search for a particular website to Flow, first enter a dummy text string in the search bar of that website, and launch the search. Now copy the contents of the browser's address bar, and paste it in the URL field below. Replace your test string with {q}. For example, if you search for casino on Netflix, its address bar reads + https://www.netflix.com/search?q=Casino + + Now copy this entire string and paste it in the URL field below. + Then replace casino with {q}. + Thus, the generic formula for a search on Netflix is https://www.netflix.com/search?q={q} + + + Copy URL + Copy search URL to clipboard + + + Title + Status + Select Icon + Icon + Cancel + Invalid web search + Please enter a title + Please enter an action keyword + Please enter a URL + Action keyword already exists, please enter a different one + Success + Hint: You do not need to place custom images in this directory, if Flow's version is updated they will be lost. Flow will automatically copy any images outside of this directory across to WebSearch's custom image location. + + Web Searches + Allows to perform web searches + + diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sr.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sr.xaml index 06707b4af..695c88c30 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sr.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sr.xaml @@ -11,6 +11,7 @@ Izmeni Dodaj Enabled + Private Mode Enabled Disabled Confirm diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/tr.xaml index b37070d93..1a9fdbf03 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/tr.xaml @@ -1,36 +1,37 @@  - Search Source Setting - Open search in: + Arama Kaynağı Ayarı + Aramayı şurada aç: Yeni Pencere Yeni Sekme - Set browser from path: + Dizin üzerinden tarayıcı seç: Seç Sil Düzenle Ekle - Enabled - Enabled - Disabled + Etkin + Gizli Mod + Etkin + Devre dışı Onayla Anahtar Kelime URL Ara: - Use Search Query Autocomplete - Autocomplete Data from: + Arama Sorgusu Otomatik Doldurmayı Kullan + Otomatik Doldurma Verileri: Lütfen bir web araması seçin {0} bağlantısını silmek istediğinize emin misiniz? - If you want to add a search for a particular website to Flow, first enter a dummy text string in the search bar of that website, and launch the search. Now copy the contents of the browser's address bar, and paste it in the URL field below. Replace your test string with {q}. For example, if you search for casino on Netflix, its address bar reads + Flow'a belirli bir web sitesi için arama eklemek istiyorsanız, önce söz konusu web sitesinin arama çubuğuna sahte bir metin dizesi girin ve aramayı başlatın. Şimdi tarayıcının adres çubuğunun içeriğini kopyalayın ve aşağıdaki URL alanına yapıştırın. Test dizenizi {q} ile değiştirin. Örneğin, Netflix'te casino araması yaparsanız, adres çubuğunda şunlar yazar https://www.netflix.com/search?q=Casino - Now copy this entire string and paste it in the URL field below. - Then replace casino with {q}. - Thus, the generic formula for a search on Netflix is https://www.netflix.com/search?q={q} + Şimdi bu dizenin tamamını kopyalayın ve aşağıdaki URL alanına yapıştırın. + Daha sonra casino yazısını {q} ile değiştirin. + Dolayısıyla, Netflix'te arama yapmak için genel formül https://www.netflix.com/search?q={q} - Copy URL - Copy search URL to clipboard + URL'yi kopyala + Arama URL'sini panoya kopyala Başlık @@ -44,7 +45,7 @@ Lütfen bir URL giriniz Anahtar kelime zaten mevcut. Lütfen yeni bir tane seçiniz. Başarılı - Hint: You do not need to place custom images in this directory, if Flow's version is updated they will be lost. Flow will automatically copy any images outside of this directory across to WebSearch's custom image location. + İpucu: Bu dizine özel resimler yerleştirmenize gerek yoktur, Flow'un sürümü güncellenirse bunlar kaybolacaktır. Flow, bu dizinin dışındaki tüm görüntüleri otomatik olarak WebSearch'ün özel görüntü konumuna kopyalayacaktır. Web Araması Web üzerinde arama yapmanızı sağlar diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/uk-UA.xaml index 51e1efc6e..72cfb23c2 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/uk-UA.xaml @@ -11,6 +11,7 @@ Редагувати Додати Увімкнено + Приватний режим Увімкнено Вимкнено Підтвердити @@ -21,7 +22,7 @@ Автозаповнення даних з: Будь ласка, виберіть пошуковий запит в Інтернеті Ви впевнені, що хочете видалити {0}? - Якщо ви хочете додати до Flow пошук певного веб-сайту, спочатку введіть фіктивний текстовий рядок у пошуковий рядок цього веб-сайту і розпочніть пошук. Тепер скопіюйте вміст адресного рядка браузера і вставте його в поле URL нижче. Замініть тестовий рядок на {q}. Наприклад, якщо ви шукаєте казино на сайті Netflix, його адресний рядок матиме такий вигляд + Якщо ви хочете додати до Flow пошук певного вебсайту, спочатку введіть фіктивний текстовий рядок у пошуковий рядок цього вебсайту і розпочніть пошук. Тепер скопіюйте вміст адресного рядка браузера і вставте його в поле URL нижче. Замініть тестовий рядок на {q}. Наприклад, якщо ви шукаєте казино на сайті Netflix, його адресний рядок матиме такий вигляд https://www.netflix.com/search?q=Casino Тепер скопіюйте весь цей рядок і вставте його в поле URL нижче. @@ -38,7 +39,7 @@ Обрати значок Іконка Скасувати - Неправильний веб-пошук + Неправильний вебпошук Будь ласка, введіть назву Будь ласка, введіть ключове слово дії Будь ласка, введіть URL-адресу @@ -46,7 +47,7 @@ Успішно Підказка: Вам не потрібно розміщувати власні зображення в цьому каталозі, якщо версія Flow оновиться, вони будуть втрачені. Flow автоматично копіює всі зображення з цього каталогу до спеціального каталогу WebSearch. - Веб-пошук - Дозволяє здійснювати веб-пошук + Вебпошук + Дозволяє здійснювати вебпошук diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/vi.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/vi.xaml index 731275c5e..fb99024ec 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/vi.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/vi.xaml @@ -11,6 +11,7 @@ Sửa Thêm Đã bật + Chế độ riêng tư Đã bật Vô hiệu hóa Xác nhận diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/zh-cn.xaml index 7b8a72d6b..fff4a2c8d 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/zh-cn.xaml @@ -11,6 +11,7 @@ 编辑 添加 启用 + 隐身模式 已启用 已禁用 确认 diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/zh-tw.xaml index 727b2f4a9..9ce6c88db 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/zh-tw.xaml @@ -11,6 +11,7 @@ 編輯 新增 已啟用 + 無痕模式 已啟用 Disabled 確定 diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs index 0040cffa7..b9b8a0b19 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -71,7 +71,7 @@ namespace Flow.Launcher.Plugin.WebSearch Score = score, Action = c => { - _context.API.OpenWebUrl(searchSource.Url.Replace("{q}", Uri.EscapeDataString(keyword))); + _context.API.OpenWebUrl(searchSource.Url.Replace("{q}", Uri.EscapeDataString(keyword)), searchSource.IsPrivateMode); return true; }, @@ -135,7 +135,7 @@ namespace Flow.Launcher.Plugin.WebSearch ActionKeywordAssigned = searchSource.ActionKeyword == SearchSourceGlobalPluginWildCardSign ? string.Empty : searchSource.ActionKeyword, Action = c => { - _context.API.OpenWebUrl(searchSource.Url.Replace("{q}", Uri.EscapeDataString(o))); + _context.API.OpenWebUrl(searchSource.Url.Replace("{q}", Uri.EscapeDataString(o)), searchSource.IsPrivateMode); return true; }, diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSource.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSource.cs index 9eedd29a3..bfd95c242 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSource.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSource.cs @@ -7,6 +7,7 @@ namespace Flow.Launcher.Plugin.WebSearch public class SearchSource : BaseModel { public string Title { get; set; } + public string ActionKeyword { get; set; } [NotNull] @@ -19,21 +20,17 @@ namespace Flow.Launcher.Plugin.WebSearch /// Custom icons are placed in the user data directory /// [JsonIgnore] - public string IconPath - { - get - { - if (CustomIcon) - return Path.Combine(Main.CustomImagesDirectory, Icon); - - return Path.Combine(Main.DefaultImagesDirectory, Icon); - } - } + public string IconPath => CustomIcon + ? Path.Combine(Main.CustomImagesDirectory, Icon) + : Path.Combine(Main.DefaultImagesDirectory, Icon); public string Url { get; set; } [JsonIgnore] public bool Status => Enabled; + + public bool IsPrivateMode { get; set; } + public bool Enabled { get; set; } public SearchSource DeepCopy() @@ -45,8 +42,10 @@ namespace Flow.Launcher.Plugin.WebSearch Url = Url, Icon = Icon, CustomIcon = CustomIcon, + IsPrivateMode = IsPrivateMode, Enabled = Enabled }; + return webSearch; } } diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSourceSetting.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSourceSetting.xaml index 746c9cf84..c6f9b27f3 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSourceSetting.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSourceSetting.xaml @@ -101,6 +101,7 @@ + + Text="{DynamicResource flowlauncher_plugin_websearch_private_mode_label}" /> + + diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs index f0e5ac327..0c0ac4b84 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs @@ -191,7 +191,19 @@ namespace Flow.Launcher.Plugin.WebSearch [JsonIgnore] public SearchSource SelectedSearchSource { get; set; } - public bool EnableSuggestion { get; set; } + private bool enableSuggestion; + public bool EnableSuggestion + { + get => enableSuggestion; + set + { + if (enableSuggestion != value) + { + enableSuggestion = value; + OnPropertyChanged(nameof(EnableSuggestion)); + } + } + } [JsonIgnore] public SuggestionSource[] Suggestions { get; set; } = { @@ -221,9 +233,5 @@ namespace Flow.Launcher.Plugin.WebSearch } } } - - public string BrowserPath { get; set; } - - public bool OpenInNewBrowser { get; set; } = true; } } diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml index 1ce9b70b4..33461e9e7 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml @@ -11,10 +11,6 @@ mc:Ignorable="d"> - @@ -96,6 +92,20 @@ + + + + + + + @@ -138,7 +148,7 @@ Margin="{StaticResource SettingPanelItemLeftMargin}" VerticalAlignment="Center" FontSize="11" - IsEnabled="{Binding ElementName=EnableSuggestion, Path=IsChecked}" + IsEnabled="{Binding Settings.EnableSuggestion}" ItemsSource="{Binding Settings.Suggestions}" SelectedItem="{Binding Settings.SelectedSuggestion}" /> - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml.cs index e53f4ec75..71dc6ece7 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml.cs @@ -94,7 +94,8 @@ namespace Flow.Launcher.Plugin.WebSearch var columnBinding = headerClicked.Column.DisplayMemberBinding as Binding; var sortBy = columnBinding?.Path.Path ?? headerClicked.Column.Header as string; - if(sortBy != null) { + if (sortBy != null) + { Sort(sortBy, direction); if (direction == ListSortDirection.Ascending) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/setting.json b/Plugins/Flow.Launcher.Plugin.WebSearch/setting.json index 533b894b8..a10ea6f33 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/setting.json +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/setting.json @@ -141,6 +141,6 @@ "Enabled": true } ], - "EnableWebSearchSuggestion": false, - "WebSearchSuggestionSource": "Google" + "EnableSuggestion": false, + "Suggestion": "Google" } \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Flow.Launcher.Plugin.WindowsSettings.csproj b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Flow.Launcher.Plugin.WindowsSettings.csproj index 73fcd9f83..879cea6f8 100644 --- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Flow.Launcher.Plugin.WindowsSettings.csproj +++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Flow.Launcher.Plugin.WindowsSettings.csproj @@ -1,7 +1,7 @@  Library - net7.0-windows + net9.0-windows true true false diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.pt-PT.resx b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.pt-PT.resx index 5b0a57759..d87cd75cb 100644 --- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.pt-PT.resx +++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.pt-PT.resx @@ -1779,7 +1779,7 @@ Alterar definições padrão para multimédia e dispositivos - Imprimir o cartão de referência de fala + Imprimir cartão de referência de fala Calibrar ecrã @@ -2227,10 +2227,10 @@ Identificar e reparar problemas de rede - Encontrar e corrigir problemas de rede e conexão + Encontrar e corrigir problemas de rede e ligações - Reproduzir CDs ou outras mídias automaticamente + Reproduzir CDs ou outas unidades automaticamente Ver informações básicas sobre seu computador diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.sr-SP.resx b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.sr-SP.resx new file mode 100644 index 000000000..53715bf23 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.sr-SP.resx @@ -0,0 +1,2514 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + About + Area System + + + access.cpl + File name, Should not translated + + + Accessibility Options + Area Control Panel (legacy settings) + + + Accessory apps + Area Privacy + + + Access work or school + Area UserAccounts + + + Account info + Area Privacy + + + Accounts + Area SurfaceHub + + + Action Center + Area Control Panel (legacy settings) + + + Activation + Area UpdateAndSecurity + + + Activity history + Area Privacy + + + Add Hardware + Area Control Panel (legacy settings) + + + Add/Remove Programs + Area Control Panel (legacy settings) + + + Add your phone + Area Phone + + + Administrative Tools + Area System + + + Advanced display settings + Area System, only available on devices that support advanced display options + + + Advanced graphics + + + Advertising ID + Area Privacy, Deprecated in Windows 10, version 1809 and later + + + Airplane mode + Area NetworkAndInternet + + + Alt+Tab + Means the key combination "Tabulator+Alt" on the keyboard + + + Alternative names + + + Animations + + + App color + + + App diagnostics + Area Privacy + + + App features + Area Apps + + + App + Short/modern name for application + + + Apps and Features + Area Apps + + + System settings + Type of the setting is a "Modern Windows settings". We use the same term as used in start menu search at the moment. + + + Apps for websites + Area Apps + + + App volume and device preferences + Area System, Added in Windows 10, version 1903 + + + appwiz.cpl + File name, Should not translated + + + Area + Mean the settings area or settings category + + + Accounts + + + Administrative Tools + Area Control Panel (legacy settings) + + + Appearance and Personalization + + + Apps + + + Clock and Region + + + Control Panel + + + Cortana + + + Devices + + + Ease of access + + + Extras + + + Gaming + + + Hardware and Sound + + + Home page + + + Mixed reality + + + Network and Internet + + + Personalization + + + Phone + + + Privacy + + + Programs + + + SurfaceHub + + + System + + + System and Security + + + Time and language + + + Update and security + + + User accounts + + + Assigned access + + + Audio + Area EaseOfAccess + + + Audio alerts + + + Audio and speech + Area MixedReality, only available if the Mixed Reality Portal app is installed. + + + Automatic file downloads + Area Privacy + + + AutoPlay + Area Device + + + Background + Area Personalization + + + Background Apps + Area Privacy + + + Backup + Area UpdateAndSecurity + + + Backup and Restore + Area Control Panel (legacy settings) + + + Battery Saver + Area System, only available on devices that have a battery, such as a tablet + + + Battery Saver settings + Area System, only available on devices that have a battery, such as a tablet + + + Battery saver usage details + + + Battery use + Area System, only available on devices that have a battery, such as a tablet + + + Biometric Devices + Area Control Panel (legacy settings) + + + BitLocker Drive Encryption + Area Control Panel (legacy settings) + + + Blue light + + + Bluetooth + Area Device + + + Bluetooth devices + Area Control Panel (legacy settings) + + + Blue-yellow + + + Bopomofo IME + Area TimeAndLanguage + + + bpmf + Should not translated + + + Broadcasting + Area Gaming + + + Calendar + Area Privacy + + + Call history + Area Privacy + + + calling + + + Camera + Area Privacy + + + Cangjie IME + Area TimeAndLanguage + + + Caps Lock + Mean the "Caps Lock" key + + + Cellular and SIM + Area NetworkAndInternet + + + Choose which folders appear on Start + Area Personalization + + + Client service for NetWare + Area Control Panel (legacy settings) + + + Clipboard + Area System + + + Closed captions + Area EaseOfAccess + + + Color filters + Area EaseOfAccess + + + Color management + Area Control Panel (legacy settings) + + + Colors + Area Personalization + + + Command + The command to direct start a setting + + + Connected Devices + Area Device + + + Contacts + Area Privacy + + + Control Panel + Type of the setting is a "(legacy) Control Panel setting" + + + Copy command + + + Core Isolation + Means the protection of the system core + + + Cortana + Area Cortana + + + Cortana across my devices + Area Cortana + + + Cortana - Language + Area Cortana + + + Credential manager + Area Control Panel (legacy settings) + + + Crossdevice + + + Custom devices + + + Dark color + + + Dark mode + + + Data usage + Area NetworkAndInternet + + + Date and time + Area TimeAndLanguage + + + Default apps + Area Apps + + + Default camera + Area Device + + + Default location + Area Control Panel (legacy settings) + + + Default programs + Area Control Panel (legacy settings) + + + Default Save Locations + Area System + + + Delivery Optimization + Area UpdateAndSecurity + + + desk.cpl + File name, Should not translated + + + Desktop themes + Area Control Panel (legacy settings) + + + deuteranopia + Medical: Mean you don't can see red colors + + + Device manager + Area Control Panel (legacy settings) + + + Devices and printers + Area Control Panel (legacy settings) + + + DHCP + Should not translated + + + Dial-up + Area NetworkAndInternet + + + Direct access + Area NetworkAndInternet, only available if DirectAccess is enabled + + + Direct open your phone + Area EaseOfAccess + + + Display + Area EaseOfAccess + + + Display properties + Area Control Panel (legacy settings) + + + DNS + Should not translated + + + Documents + Area Privacy + + + Duplicating my display + Area System + + + During these hours + Area System + + + Ease of access center + Area Control Panel (legacy settings) + + + Edition + Means the "Windows Edition" + + + Email + Area Privacy + + + Email and app accounts + Area UserAccounts + + + Encryption + Area System + + + Environment + Area MixedReality, only available if the Mixed Reality Portal app is installed. + + + Ethernet + Area NetworkAndInternet + + + Exploit Protection + + + Extras + Area Extra, , only used for setting of 3rd-Party tools + + + Eye control + Area EaseOfAccess + + + Eye tracker + Area Privacy, requires eyetracker hardware + + + Family and other people + Area UserAccounts + + + Feedback and diagnostics + Area Privacy + + + File system + Area Privacy + + + FindFast + Area Control Panel (legacy settings) + + + findfast.cpl + File name, Should not translated + + + Find My Device + Area UpdateAndSecurity + + + Firewall + + + Focus assist - Quiet hours + Area System + + + Focus assist - Quiet moments + Area System + + + Folder options + Area Control Panel (legacy settings) + + + Fonts + Area EaseOfAccess + + + For developers + Area UpdateAndSecurity + + + Game bar + Area Gaming + + + Game controllers + Area Control Panel (legacy settings) + + + Game DVR + Area Gaming + + + Game Mode + Area Gaming + + + Gateway + Should not translated + + + General + Area Privacy + + + Get programs + Area Control Panel (legacy settings) + + + Getting started + Area Control Panel (legacy settings) + + + Glance + Area Personalization, Deprecated in Windows 10, version 1809 and later + + + Graphics settings + Area System + + + Grayscale + + + Green week + Mean you don't can see green colors + + + Headset display + Area MixedReality, only available if the Mixed Reality Portal app is installed. + + + High contrast + Area EaseOfAccess + + + Holographic audio + + + Holographic Environment + + + Holographic Headset + + + Holographic Management + + + Home group + Area Control Panel (legacy settings) + + + ID + MEans The "Windows Identifier" + + + Image + + + Indexing options + Area Control Panel (legacy settings) + + + inetcpl.cpl + File name, Should not translated + + + Infrared + Area Control Panel (legacy settings) + + + Inking and typing + Area Privacy + + + Internet options + Area Control Panel (legacy settings) + + + intl.cpl + File name, Should not translated + + + Inverted colors + + + IP + Should not translated + + + Isolated Browsing + + + Japan IME settings + Area TimeAndLanguage, available if the Microsoft Japan input method editor is installed + + + joy.cpl + File name, Should not translated + + + Joystick properties + Area Control Panel (legacy settings) + + + jpnime + Should not translated + + + Keyboard + Area EaseOfAccess + + + Keypad + + + Keys + + + Language + Area TimeAndLanguage + + + Light color + + + Light mode + + + Location + Area Privacy + + + Lock screen + Area Personalization + + + Magnifier + Area EaseOfAccess + + + Mail - Microsoft Exchange or Windows Messaging + Area Control Panel (legacy settings) + + + main.cpl + File name, Should not translated + + + Manage known networks + Area NetworkAndInternet + + + Manage optional features + Area Apps + + + Messaging + Area Privacy + + + Metered connection + + + Microphone + Area Privacy + + + Microsoft Mail Post Office + Area Control Panel (legacy settings) + + + mlcfg32.cpl + File name, Should not translated + + + mmsys.cpl + File name, Should not translated + + + Mobile devices + + + Mobile hotspot + Area NetworkAndInternet + + + modem.cpl + File name, Should not translated + + + Mono + + + More details + Area Cortana + + + Motion + Area Privacy + + + Mouse + Area EaseOfAccess + + + Mouse and touchpad + Area Device + + + Mouse, Fonts, Keyboard, and Printers properties + Area Control Panel (legacy settings) + + + Mouse pointer + Area EaseOfAccess + + + Multimedia properties + Area Control Panel (legacy settings) + + + Multitasking + Area System + + + Narrator + Area EaseOfAccess + + + Navigation bar + Area Personalization + + + netcpl.cpl + File name, Should not translated + + + netsetup.cpl + File name, Should not translated + + + Network + Area NetworkAndInternet + + + Network and sharing center + Area Control Panel (legacy settings) + + + Network connection + Area Control Panel (legacy settings) + + + Network properties + Area Control Panel (legacy settings) + + + Network Setup Wizard + Area Control Panel (legacy settings) + + + Network status + Area NetworkAndInternet + + + NFC + Area NetworkAndInternet + + + NFC Transactions + "NFC should not translated" + + + Night light + + + Night light settings + Area System + + + Note + + + Only available when you have connected a mobile device to your device. + + + Only available on devices that support advanced graphics options. + + + Only available on devices that have a battery, such as a tablet. + + + Deprecated in Windows 10, version 1809 (build 17763) and later. + + + Only available if Dial is paired. + + + Only available if DirectAccess is enabled. + + + Only available on devices that support advanced display options. + + + Only present if user is enrolled in WIP. + + + Requires eyetracker hardware. + + + Available if the Microsoft Japan input method editor is installed. + + + Available if the Microsoft Pinyin input method editor is installed. + + + Available if the Microsoft Wubi input method editor is installed. + + + Only available if the Mixed Reality Portal app is installed. + + + Only available on mobile and if the enterprise has deployed a provisioning package. + + + Added in Windows 10, version 1903 (build 18362). + + + Added in Windows 10, version 2004 (build 19041). + + + Only available if "settings apps" are installed, for example, by a 3rd party. + + + Only available if touchpad hardware is present. + + + Only available if the device has a Wi-Fi adapter. + + + Device must be Windows Anywhere-capable. + + + Only available if enterprise has deployed a provisioning package. + + + Notifications + Area Privacy + + + Notifications and actions + Area System + + + Num Lock + Mean the "Num Lock" key + + + nwc.cpl + File name, Should not translated + + + odbccp32.cpl + File name, Should not translated + + + ODBC Data Source Administrator (32-bit) + Area Control Panel (legacy settings) + + + ODBC Data Source Administrator (64-bit) + Area Control Panel (legacy settings) + + + Offline files + Area Control Panel (legacy settings) + + + Offline Maps + Area Apps + + + Offline Maps - Download maps + Area Apps + + + On-Screen + + + OS + Means the "Operating System" + + + Other devices + Area Privacy + + + Other options + Area EaseOfAccess + + + Other users + + + Parental controls + Area Control Panel (legacy settings) + + + Password + + + password.cpl + File name, Should not translated + + + Password properties + Area Control Panel (legacy settings) + + + Pen and input devices + Area Control Panel (legacy settings) + + + Pen and touch + Area Control Panel (legacy settings) + + + Pen and Windows Ink + Area Device + + + People Near Me + Area Control Panel (legacy settings) + + + Performance information and tools + Area Control Panel (legacy settings) + + + Permissions and history + Area Cortana + + + Personalization (category) + Area Personalization + + + Phone + Area Phone + + + Phone and modem + Area Control Panel (legacy settings) + + + Phone and modem - Options + Area Control Panel (legacy settings) + + + Phone calls + Area Privacy + + + Phone - Default apps + Area System + + + Picture + + + Pictures + Area Privacy + + + Pinyin IME settings + Area TimeAndLanguage, available if the Microsoft Pinyin input method editor is installed + + + Pinyin IME settings - domain lexicon + Area TimeAndLanguage + + + Pinyin IME settings - Key configuration + Area TimeAndLanguage + + + Pinyin IME settings - UDP + Area TimeAndLanguage + + + Playing a game full screen + Area Gaming + + + Plugin to search for Windows settings + + + Windows Settings + + + Power and sleep + Area System + + + powercfg.cpl + File name, Should not translated + + + Power options + Area Control Panel (legacy settings) + + + Presentation + + + Printers + Area Control Panel (legacy settings) + + + Printers and scanners + Area Device + + + Print screen + Mean the "Print screen" key + + + Problem reports and solutions + Area Control Panel (legacy settings) + + + Processor + + + Programs and features + Area Control Panel (legacy settings) + + + Projecting to this PC + Area System + + + protanopia + Medical: Mean you don't can see green colors + + + Provisioning + Area UserAccounts, only available if enterprise has deployed a provisioning package + + + Proximity + Area NetworkAndInternet + + + Proxy + Area NetworkAndInternet + + + Quickime + Area TimeAndLanguage + + + Quiet moments game + + + Radios + Area Privacy + + + RAM + Means the Read-Access-Memory (typical the used to inform about the size) + + + Recognition + + + Recovery + Area UpdateAndSecurity + + + Red eye + Mean red eye effect by over-the-night flights + + + Red-green + Mean the weakness you can't differ between red and green colors + + + Red week + Mean you don't can see red colors + + + Region + Area TimeAndLanguage + + + Regional language + Area TimeAndLanguage + + + Regional settings properties + Area Control Panel (legacy settings) + + + Region and language + Area Control Panel (legacy settings) + + + Region formatting + + + RemoteApp and desktop connections + Area Control Panel (legacy settings) + + + Remote Desktop + Area System + + + Scanners and cameras + Area Control Panel (legacy settings) + + + schedtasks + File name, Should not translated + + + Scheduled + + + Scheduled tasks + Area Control Panel (legacy settings) + + + Screen rotation + Area System + + + Scroll bars + + + Scroll Lock + Mean the "Scroll Lock" key + + + SDNS + Should not translated + + + Searching Windows + Area Cortana + + + SecureDNS + Should not translated + + + Security Center + Area Control Panel (legacy settings) + + + Security Processor + + + Session cleanup + Area SurfaceHub + + + Settings home page + Area Home, Overview-page for all areas of settings + + + Set up a kiosk + Area UserAccounts + + + Shared experiences + Area System + + + Shortcuts + + + wifi + dont translate this, is a short term to find entries + + + Sign-in options + Area UserAccounts + + + Sign-in options - Dynamic lock + Area UserAccounts + + + Size + Size for text and symbols + + + Sound + Area System + + + Speech + Area EaseOfAccess + + + Speech recognition + Area Control Panel (legacy settings) + + + Speech typing + + + Start + Area Personalization + + + Start places + + + Startup apps + Area Apps + + + sticpl.cpl + File name, Should not translated + + + Storage + Area System + + + Storage policies + Area System + + + Storage Sense + Area System + + + in + Example: Area "System" in System settings + + + Sync center + Area Control Panel (legacy settings) + + + Sync your settings + Area UserAccounts + + + sysdm.cpl + File name, Should not translated + + + System + Area Control Panel (legacy settings) + + + System properties and Add New Hardware wizard + Area Control Panel (legacy settings) + + + Tab + Means the key "Tabulator" on the keyboard + + + Tablet mode + Area System + + + Tablet PC settings + Area Control Panel (legacy settings) + + + Talk + + + Talk to Cortana + Area Cortana + + + Taskbar + Area Personalization + + + Taskbar color + + + Tasks + Area Privacy + + + Team Conferencing + Area SurfaceHub + + + Team device management + Area SurfaceHub + + + Text to speech + Area Control Panel (legacy settings) + + + Themes + Area Personalization + + + themes.cpl + File name, Should not translated + + + timedate.cpl + File name, Should not translated + + + Timeline + + + Touch + + + Touch feedback + + + Touchpad + Area Device + + + Transparency + + + tritanopia + Medical: Mean you don't can see yellow and blue colors + + + Troubleshoot + Area UpdateAndSecurity + + + TruePlay + Area Gaming + + + Typing + Area Device + + + Uninstall + Area MixedReality, only available if the Mixed Reality Portal app is installed. + + + USB + Area Device + + + User accounts + Area Control Panel (legacy settings) + + + Version + Means The "Windows Version" + + + Video playback + Area Apps + + + Videos + Area Privacy + + + Virtual Desktops + + + Virus + Means the virus in computers and software + + + Voice activation + Area Privacy + + + Volume + + + VPN + Area NetworkAndInternet + + + Wallpaper + + + Warmer color + + + Welcome center + Area Control Panel (legacy settings) + + + Welcome screen + Area SurfaceHub + + + wgpocpl.cpl + File name, Should not translated + + + Wheel + Area Device + + + Wi-Fi + Area NetworkAndInternet, only available if Wi-Fi calling is enabled + + + Wi-Fi Calling + Area NetworkAndInternet, only available if Wi-Fi calling is enabled + + + Wi-Fi settings + "Wi-Fi" should not translated + + + Window border + + + Windows Anytime Upgrade + Area Control Panel (legacy settings) + + + Windows Anywhere + Area UserAccounts, device must be Windows Anywhere-capable + + + Windows CardSpace + Area Control Panel (legacy settings) + + + Windows Defender + Area Control Panel (legacy settings) + + + Windows Firewall + Area Control Panel (legacy settings) + + + Windows Hello setup - Face + Area UserAccounts + + + Windows Hello setup - Fingerprint + Area UserAccounts + + + Windows Insider Program + Area UpdateAndSecurity + + + Windows Mobility Center + Area Control Panel (legacy settings) + + + Windows search + Area Cortana + + + Windows Security + Area UpdateAndSecurity + + + Windows Update + Area UpdateAndSecurity + + + Windows Update - Advanced options + Area UpdateAndSecurity + + + Windows Update - Check for updates + Area UpdateAndSecurity + + + Windows Update - Restart options + Area UpdateAndSecurity + + + Windows Update - View optional updates + Area UpdateAndSecurity + + + Windows Update - View update history + Area UpdateAndSecurity + + + Wireless + + + Workplace + + + Workplace provisioning + Area UserAccounts + + + Wubi IME settings + Area TimeAndLanguage, available if the Microsoft Wubi input method editor is installed + + + Wubi IME settings - UDP + Area TimeAndLanguage + + + Xbox Networking + Area Gaming + + + Your info + Area UserAccounts + + + Zoom + Mean zooming of things via a magnifier + + + Change device installation settings + + + Turn off background images + + + Navigation properties + + + Media streaming options + + + Make a file type always open in a specific program + + + Change the Narrator’s voice + + + Find and fix keyboard problems + + + Use screen reader + + + Show which workgroup this computer is on + + + Change mouse wheel settings + + + Manage computer certificates + + + Find and fix problems + + + Change settings for content received using Tap and send + + + Change default settings for media or devices + + + Print the speech reference card + + + Calibrate display colour + + + Manage file encryption certificates + + + View recent messages about your computer + + + Give other users access to this computer + + + Show hidden files and folders + + + Change Windows To Go start-up options + + + See which processes start up automatically when you start Windows + + + Tell if an RSS feed is available on a website + + + Add clocks for different time zones + + + Add a Bluetooth device + + + Customise the mouse buttons + + + Set tablet buttons to perform certain tasks + + + View installed fonts + + + Change the way currency is displayed + + + Edit group policy + + + Manage browser add-ons + + + Check processor speed + + + Check firewall status + + + Send or receive a file + + + Add or remove user accounts + + + Edit the system environment variables + + + Manage BitLocker + + + Auto-hide the taskbar + + + Change sound card settings + + + Make changes to accounts + + + Edit local users and groups + + + View network computers and devices + + + Install a program from the network + + + View scanners and cameras + + + Microsoft IME Register Word (Japanese) + + + Restore your files with File History + + + Turn On-Screen keyboard on or off + + + Block or allow third-party cookies + + + Find and fix audio recording problems + + + Create a recovery drive + + + Microsoft New Phonetic Settings + + + Generate a system health report + + + Fix problems with your computer + + + Back up and Restore (Windows 7) + + + Preview, delete, show or hide fonts + + + Microsoft Quick Settings + + + View reliability history + + + Access RemoteApp and desktops + + + Set up ODBC data sources + + + Reset Security Policies + + + Block or allow pop-ups + + + Turn autocomplete in Internet Explorer on or off + + + Microsoft Pinyin SimpleFast Options + + + Change what closing the lid does + + + Turn off unnecessary animations + + + Create a restore point + + + Turn off automatic window arrangement + + + Troubleshooting History + + + Diagnose your computer's memory problems + + + View recommended actions to keep Windows running smoothly + + + Change cursor blink rate + + + Add or remove programs + + + Create a password reset disk + + + Configure advanced user profile properties + + + Start or stop using AutoPlay for all media and devices + + + Change Automatic Maintenance settings + + + Specify single- or double-click to open + + + Select users who can use remote desktop + + + Show which programs are installed on your computer + + + Allow remote access to your computer + + + View advanced system settings + + + How to install a program + + + Change how your keyboard works + + + Automatically adjust for daylight saving time + + + Change the order of Windows SideShow gadgets + + + Check keyboard status + + + Control the computer without the mouse or keyboard + + + Change or remove a program + + + Change multi-touch gesture settings + + + Set up ODBC data sources (64-bit) + + + Configure proxy server + + + Change your homepage + + + Group similar windows on the taskbar + + + Change Windows SideShow settings + + + Use audio description for video + + + Change workgroup name + + + Find and fix printing problems + + + Change when the computer sleeps + + + Set up a virtual private network (VPN) connection + + + Accommodate learning abilities + + + Set up a dial-up connection + + + Set up a connection or network + + + How to change your Windows password + + + Make it easier to see the mouse pointer + + + Set up iSCSI initiator + + + Accommodate low vision + + + Manage offline files + + + Review your computer's status and resolve issues + + + Microsoft ChangJie Settings + + + Replace sounds with visual cues + + + Change temporary Internet file settings + + + Connect to the Internet + + + Find and fix audio playback problems + + + Change the mouse pointer display or speed + + + Back up your recovery key + + + Save backup copies of your files with File History + + + View current accessibility settings + + + Change tablet pen settings + + + Change how your mouse works + + + Show how much RAM is on this computer + + + Edit power plan + + + Adjust system volume + + + Defragment and optimise your drives + + + Set up ODBC data sources (32-bit) + + + Change Font Settings + + + Magnify portions of the screen using Magnifier + + + Change the file type associated with a file extension + + + View event logs + + + Manage Windows Credentials + + + Set up a microphone + + + Change how the mouse pointer looks + + + Change power-saving settings + + + Optimise for blindness + + + + + + + Turn Windows features on or off + + + Show which operating system your computer is running + + + View local services + + + Manage Work Folders + + + Encrypt your offline files + + + Train the computer to recognise your voice + + + Advanced printer setup + + + Change default printer + + + Edit environment variables for your account + + + Optimise visual display + + + Change mouse click settings + + + Change advanced colour management settings for displays, scanners and printers + + + Let Windows suggest Ease of Access settings + + + Clear disk space by deleting unnecessary files + + + View devices and printers + + + Private Character Editor + + + Record steps to reproduce a problem + + + Adjust the appearance and performance of Windows + + + Settings for Microsoft IME (Japanese) + + + Invite someone to connect to your PC and help you, or offer to help someone else + + + Run programs made for previous versions of Windows + + + Choose the order of how your screen rotates + + + Change how Windows searches + + + Set flicks to perform certain tasks + + + Change account type + + + Change screen saver + + + Change User Account Control settings + + + Turn on easy access keys + + + Identify and repair network problems + + + Find and fix networking and connection problems + + + Play CDs or other media automatically + + + View basic information about your computer + + + Choose how you open links + + + Allow Remote Assistance invitations to be sent from this computer + + + Task Manager + + + Turn flicks on or off + + + Add a language + + + View network status and tasks + + + Turn Magnifier on or off + + + See the name of this computer + + + View network connections + + + Perform recommended maintenance tasks automatically + + + Manage disk space used by your offline files + + + Turn High Contrast on or off + + + Change the way time is displayed + + + Change how web pages are displayed in tabs + + + Change the way dates and lists are displayed + + + Manage audio devices + + + Change security settings + + + Check security status + + + Delete cookies or temporary files + + + Specify which hand you write with + + + Change touch input settings + + + How to change the size of virtual memory + + + Hear text read aloud with Narrator + + + Set up USB game controllers + + + Show which domain your computer is on + + + View all problem reports + + + 16-Bit Application Support + + + Set up dialling rules + + + Enable or disable session cookies + + + Give administrative rights to a domain user + + + Choose when to turn off display + + + Move the pointer with the keypad using MouseKeys + + + Change Windows SideShow-compatible device settings + + + Adjust commonly used mobility settings + + + Change text-to-speech settings + + + Set the time and date + + + Change location settings + + + Change mouse settings + + + Manage Storage Spaces + + + Show or hide file extensions + + + Allow an app through Windows Firewall + + + Change system sounds + + + Adjust ClearType text + + + Turn screen saver on or off + + + Find and fix windows update problems + + + Change Bluetooth settings + + + Connect to a network + + + Change the search provider in Internet Explorer + + + Join a domain + + + Add a device + + + Find and fix problems with Windows Search + + + Choose a power plan + + + Change how the mouse pointer looks when it’s moving + + + Uninstall a program + + + Create and format hard disk partitions + + + Change date, time or number formats + + + Change PC wake-up settings + + + Manage network passwords + + + Change input methods + + + Manage advanced sharing settings + + + Change battery settings + + + Rename this computer + + + Lock or unlock the taskbar + + + Manage Web Credentials + + + Change the time zone + + + Start speech recognition + + + View installed updates + + + What's happened to the Quick Launch toolbar? + + + Change search options for files and folders + + + Adjust settings before giving a presentation + + + Scan a document or picture + + + Change the way measurements are displayed + + + Press key combinations one at a time + + + Restore data, files or computer from backup (Windows 7) + + + Set your default programs + + + Set up a broadband connection + + + Calibrate the screen for pen or touch input + + + Manage user certificates + + + Schedule tasks + + + Ignore repeated keystrokes using FilterKeys + + + Find and fix bluescreen problems + + + Hear a tone when keys are pressed + + + Delete browsing history + + + Change what the power buttons do + + + Create standard user account + + + Take speech tutorials + + + View system resource usage in Task Manager + + + Create an account + + + Get more features with a new edition of Windows + + + Control Panel + + + TaskLink + + + Unknown + + \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.tr-TR.resx b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.tr-TR.resx index 49c4b7839..d920e7550 100644 --- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.tr-TR.resx +++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.tr-TR.resx @@ -254,7 +254,7 @@ Saat ve Bölge - Control Panel + Denetim Masası Cortana @@ -468,7 +468,7 @@ Area Privacy - Control Panel + Denetim Masası Type of the setting is a "(legacy) Control Panel setting" @@ -627,7 +627,7 @@ Area NetworkAndInternet - Exploit Protection + Exploit Koruması Ek Özellikler @@ -1206,7 +1206,7 @@ Windows ayarlarını aramak için eklenti - Windows Settings + Windows Ayarları Güç ve uyku @@ -1450,7 +1450,7 @@ Area System - in + içinde Example: Area "System" in System settings @@ -1671,7 +1671,7 @@ Area UpdateAndSecurity - Windows Mobility Center + Windows Mobilite Merkezi Area Control Panel (legacy settings) @@ -1737,208 +1737,208 @@ Mean zooming of things via a magnifier - Change device installation settings + Cihaz kurulum ayarlarını değiştir - Turn off background images + Arka plan görsellerini kapat - Navigation properties + Navigasyon özellikleri - Media streaming options + Medya akış seçenekleri - Make a file type always open in a specific program + Bir dosya türünün her zaman belirli bir programda açılmasını sağla - Change the Narrator’s voice + Anlatıcının sesini değiştir - Find and fix keyboard problems + Klavye sorunlarını bul ve düzelt - Use screen reader + Ekran okuyucu kullan - Show which workgroup this computer is on + Bu bilgisayarın hangi çalışma grubunda olduğunu göster - Change mouse wheel settings + Fare tekerleği ayarlarını değiştir - Manage computer certificates + Bilgisayar sertifikalarını yönet - Find and fix problems + Sorunları bul ve düzelt Change settings for content received using Tap and send - Change default settings for media or devices + Medya veya cihazlar için varsayılan ayarları değiştir Print the speech reference card - Calibrate display colour + Ekran rengini kalibre et - Manage file encryption certificates + Dosya şifreleme sertifikalarını yönet - View recent messages about your computer + Bilgisayarınızla ilgili son mesajları görüntüleyin - Give other users access to this computer + Diğer kullanıcılara bu bilgisayara erişim izni ver - Show hidden files and folders + Gizli dosya ve klasörleri göster - Change Windows To Go start-up options + Windows To Go başlatma seçeneklerini değiştir - See which processes start up automatically when you start Windows + Windows'u başlattığınızda hangi işlemlerin otomatik olarak başladığını görün - Tell if an RSS feed is available on a website + Bir RSS beslemesinin bir web sitesinde mevcut olup olmadığını söyle - Add clocks for different time zones + Farklı zaman dilimleri için saatler ekleyin - Add a Bluetooth device + Bir Bluetooth cihazı ekle - Customise the mouse buttons + Fare düğmelerini özelleştir Set tablet buttons to perform certain tasks - View installed fonts + Yüklü yazı tiplerini görüntüle Change the way currency is displayed - Edit group policy + Grup ilkesini düzenle - Manage browser add-ons + Tarayıcı eklentilerini yönet - Check processor speed + İşlemci hızını kontrol et - Check firewall status + Güvenlik duvarı durumunu kontrol et - Send or receive a file + Dosya gönder veya al - Add or remove user accounts + Kullanıcı hesapları ekle veya kaldır - Edit the system environment variables + Sistem ortam değişkenlerini düzenle - Manage BitLocker + BitLocker'ı Yönet - Auto-hide the taskbar + Görev çubuğunu otomatik gizle - Change sound card settings + Ses kartı ayalarını değiştir - Make changes to accounts + Hesaplarda değişiklik yap - Edit local users and groups + Yerel kullanıcıları ve grupları düzenle - View network computers and devices + Ağ bilgisayarlarını ve cihazlarını görüntüle - Install a program from the network + İnternetten bir program yükle - View scanners and cameras + Tarayıcıları ve kameraları görüntüle - Microsoft IME Register Word (Japanese) + Microsoft IME Register Word (Japonca) - Restore your files with File History + Dosya Geçmişi ile dosyalarınızı geri yükleyin - Turn On-Screen keyboard on or off + Ekran klavyesini aç veya kapat - Block or allow third-party cookies + Üçüncü taraf çerezlerini engelle veya izin ver - Find and fix audio recording problems + Ses kayıt sorunlarını bul ve düzelt - Create a recovery drive + Bir kurtarma sürücüsü oluştur - Microsoft New Phonetic Settings + Microsoft Fonetik İçin Yeni Ayarlar - Generate a system health report + Sistem sağlık raporu oluştur - Fix problems with your computer + Bilgisayarla ilgili sorunları gider - Back up and Restore (Windows 7) + Yedekleme ve Geri Yükleme (Windows 7) - Preview, delete, show or hide fonts + Yazı tiplerini önizleme, silme, gösterme veya gizleme - Microsoft Quick Settings + Microsoft Hızlı Ayarlar - View reliability history + Güvenilirlik geçmişini görüntüle Access RemoteApp and desktops - Set up ODBC data sources + ODBC veri kaynaklarını ayarla - Reset Security Policies + Güvenlik Politikalarını Sıfırla - Block or allow pop-ups + Pop-up'ları engelle veya izin ver - Turn autocomplete in Internet Explorer on or off + Internet Explorer'da otomatik doldurmayı aç veya kapat - Microsoft Pinyin SimpleFast Options + Microsoft Pinyin SimpleFast Seçenekleri Change what closing the lid does - Turn off unnecessary animations + Gereksiz animasyonları kapat - Create a restore point + Geri yükleme noktası oluştur Turn off automatic window arrangement - Troubleshooting History + Sorun Giderme Geçmişi Diagnose your computer's memory problems @@ -1947,13 +1947,13 @@ View recommended actions to keep Windows running smoothly - Change cursor blink rate + İmleç yanıp sönme hızını değiştir - Add or remove programs + Program ekle veya kaldır - Create a password reset disk + Parola sıfırlama diski oluştur Configure advanced user profile properties @@ -1965,7 +1965,7 @@ Change Automatic Maintenance settings - Specify single- or double-click to open + Açmak için tek veya çift tıkla Select users who can use remote desktop @@ -1974,16 +1974,16 @@ Show which programs are installed on your computer - Allow remote access to your computer + Bilgisayarınıza uzaktan erişime izin ver - View advanced system settings + Gelişmiş sistem ayarlarını görüntüle - How to install a program + Bir program nasıl kurulur - Change how your keyboard works + Klavyenin çalışma şeklini değiştir Automatically adjust for daylight saving time @@ -1992,124 +1992,124 @@ Change the order of Windows SideShow gadgets - Check keyboard status + Klavye durumunu kontrol et Control the computer without the mouse or keyboard - Change or remove a program + Bir programı değiştir veya kaldır Change multi-touch gesture settings - Set up ODBC data sources (64-bit) + ODBC veri kaynaklarını ayarla (64-bit) - Configure proxy server + Proxy sunucusunu yapılandır - Change your homepage + Ana sayfayı değiştir - Group similar windows on the taskbar + Görev çubuğunda benzer pencereleri gruplama Change Windows SideShow settings - Use audio description for video + Video için sesli açıklama kullan - Change workgroup name + Çalışma grubu adını değiştir - Find and fix printing problems + Yazıcı sorunlarını bul ve düzelt - Change when the computer sleeps + Bilgisayarın uyku zamanını değiştir - Set up a virtual private network (VPN) connection + Bir sanal özel ağ (VPN) bağlantısı kur - Accommodate learning abilities + Öğrenme yeteneklerine uyum sağla - Set up a dial-up connection + Bir çevirmeli bağlantı kur - Set up a connection or network + Bir bağlantı veya ağ kur - How to change your Windows password + Windows parolanızı nasıl değiştirirsiniz - Make it easier to see the mouse pointer + Fare imlecini görmeyi kolaylaştırın - Set up iSCSI initiator + iSCSI başlatıcıyı ayarla - Accommodate low vision + Az görmeye uyum sağla - Manage offline files + Çevrim dışı dosyaları yönet Review your computer's status and resolve issues - Microsoft ChangJie Settings + Microsoft ChangJie Ayarları Replace sounds with visual cues - Change temporary Internet file settings + Geçici İnternet dosyası ayarlarını değiştir - Connect to the Internet + İnternete bağlan - Find and fix audio playback problems + Ses oynatma sorunlarını bul ve düzelt - Change the mouse pointer display or speed + Fare imleci görüntüsünü veya hızını değiştir - Back up your recovery key + Kurtarma anahtarınızı yedekleyin Save backup copies of your files with File History - View current accessibility settings + Geçerli erişilebilirlik ayarlarını görüntüle - Change tablet pen settings + Tablet kalemi ayarlarını değiştir - Change how your mouse works + Farenin çalışma şeklini değiştir - Show how much RAM is on this computer + Bu bilgisayarda ne kadar RAM olduğunu göster - Edit power plan + Güç planını düzenle - Adjust system volume + Sistem ses seviyesini ayarla Defragment and optimise your drives - Set up ODBC data sources (32-bit) + ODBC veri kaynaklarını ayarla (32-bit) - Change Font Settings + Yazı Tipi Ayarlarını Değiştir Magnify portions of the screen using Magnifier @@ -2118,59 +2118,59 @@ Change the file type associated with a file extension - View event logs + Olay günlüklerini görüntüle Manage Windows Credentials - Set up a microphone + Bir mikrofon ayarla - Change how the mouse pointer looks + Fare işaretçisinin görünümünü değiştir - Change power-saving settings + Güç tasarrufu ayarlarını değiştir - Optimise for blindness + Körlük için optimize et - Turn Windows features on or off + Windows özelliklerini aç veya kapat - Show which operating system your computer is running + Bilgisayarınızın hangi işletim sistemini çalıştırdığını gösterin - View local services + Yerel hizmetleri görüntüle - Manage Work Folders + Çalışma Klasörlerini Yönet - Encrypt your offline files + Çevrimdışı dosyalarınızı şifreleyin - Train the computer to recognise your voice + Bilgisayarı sesinizi tanıması için eğitin - Advanced printer setup + Gelişmiş yazıcı kurulumu - Change default printer + Varsayılan yazıcıyı değiştir - Edit environment variables for your account + Hesap için ortam değişkenlerini düzenle - Optimise visual display + Görsel ekranı optimize et - Change mouse click settings + Fare tıklama ayarlarını değiştir Change advanced colour management settings for displays, scanners and printers @@ -2179,145 +2179,145 @@ Let Windows suggest Ease of Access settings - Clear disk space by deleting unnecessary files + Gereksiz dosyaları silerek disk alanını temizle - View devices and printers + Cihazları ve yazıcıları görüntüle - Private Character Editor + Özel Karakter Düzenleyici Record steps to reproduce a problem - Adjust the appearance and performance of Windows + Windows'un görünümünü ve performansını ayarla - Settings for Microsoft IME (Japanese) + Microsoft IME için ayarlar (Japonca) - Invite someone to connect to your PC and help you, or offer to help someone else + Bilgisayarınıza bağlanıp size yardım etmesi için birini davet edin veya başka birine yardım etmeyi teklif edin - Run programs made for previous versions of Windows + Windows'un önceki sürümleri için hazırlanmış programları çalıştırma - Choose the order of how your screen rotates + Ekranınızın dönme sırasını seçin - Change how Windows searches + Windows'un arama şeklini değiştir Set flicks to perform certain tasks - Change account type + Hesap türünü değiştir - Change screen saver + Ekran koruyucuyu değiştir - Change User Account Control settings + Kullanıcı Hesabı Denetimi ayarlarını değiştir - Turn on easy access keys + Kolay erişim tuşlarını aç - Identify and repair network problems + Ağ sorunlarını belirle ve onar - Find and fix networking and connection problems + Ağ ve bağlantı sorunlarını bul ve düzelt - Play CDs or other media automatically + CD'leri veya diğer medyaları otomatik olarak çal - View basic information about your computer + Bilgisayarınız hakkında temel bilgileri görüntüleyin - Choose how you open links + Bağlantıları nasıl açacağınızı seçin Allow Remote Assistance invitations to be sent from this computer - Task Manager + Görev Yöneticisi Turn flicks on or off - Add a language + Bir dil ekleyin - View network status and tasks + Ağ durumunu ve görevlerini görüntüle Turn Magnifier on or off - See the name of this computer + Bu bilgisayarın adına bakın - View network connections + Ağ bağlantılarını görüntüle - Perform recommended maintenance tasks automatically + Önerilen bakım görevlerini otomatik olarak gerçekleştirin - Manage disk space used by your offline files + Çevrimdışı dosyalarınız tarafından kullanılan disk alanını yönetin - Turn High Contrast on or off + Yüksek Kontrast'ı aç veya kapat - Change the way time is displayed + Saatin görüntülenme şeklini değiştirme - Change how web pages are displayed in tabs + Web sayfalarının sekmelerde görüntülenme şeklini değiştirme - Change the way dates and lists are displayed + Tarihlerin ve listelerin görüntülenme şeklini değiştirme - Manage audio devices + Ses cihazlarını yönet - Change security settings + Güvenlik ayarlarını değiştir - Check security status + Güvenlik durumunu kontrol et - Delete cookies or temporary files + Çerezleri veya geçici dosyaları sil - Specify which hand you write with + Hangi elinizle yazdığınızı belirtin - Change touch input settings + Dokunmatik giriş ayarlarını değiştir - How to change the size of virtual memory + Sanal bellek boyutu nasıl değiştirilir - Hear text read aloud with Narrator + Anlatıcı ile yüksek sesle okunan metni dinleyin - Set up USB game controllers + USB oyun kumandalarını ayarla - Show which domain your computer is on + Bilgisayarınızın hangi etki alanında olduğunu gösterme - View all problem reports + Tüm sorun raporlarını görüntüle - 16-Bit Application Support + 16-Bit Uygulama Desteği - Set up dialling rules + Arama kurallarını ayarla Enable or disable session cookies @@ -2338,177 +2338,177 @@ Adjust commonly used mobility settings - Change text-to-speech settings + Metinden sese ayarlarını değiştir - Set the time and date + Saat ve tarihi ayarla - Change location settings + Konum ayarlarını değiştir - Change mouse settings + Fare ayarlarını değiştir - Manage Storage Spaces + Depolama Alanlarını Yönet - Show or hide file extensions + Dosya uzantılarını göster/gizle - Allow an app through Windows Firewall + Windows Güvenlik Duvarı üzerinden bir uygulamaya izin ver - Change system sounds + Sistem seslerini değiştir Adjust ClearType text - Turn screen saver on or off + Ekran koruyucuyu aç/kapat - Find and fix windows update problems + Windows güncelleme sorunlarını bul ve düzelt - Change Bluetooth settings + Bluetooth ayarlarını değiştir - Connect to a network + Bir ağa bağlan - Change the search provider in Internet Explorer + Internet Explorer'da arama sağlayıcısını değiştir - Join a domain + Bir etki alanına katılın - Add a device + Bir cihaz ekle - Find and fix problems with Windows Search + Windows Arama ile ilgili sorunları bul ve düzelt - Choose a power plan + Bir güç planı seç Change how the mouse pointer looks when it’s moving - Uninstall a program + Bir program kaldır - Create and format hard disk partitions + Sabit disk bölümleri oluştur ve biçimlendir - Change date, time or number formats + Tarih, saat veya sayı biçimlerini değiştir - Change PC wake-up settings + PC uyandırma ayarlarını değiştir - Manage network passwords + Ağ parolalarını yönet - Change input methods + Giriş yöntemlerini değiştir - Manage advanced sharing settings + Gelişmiş paylaşım ayarlarını yönet - Change battery settings + Pil ayarlarını değiştir - Rename this computer + Bilgisayarı yeniden adlandır - Lock or unlock the taskbar + Görev çubuğunu kilitle/kilidi aç - Manage Web Credentials + Web Kimlik Bilgilerini Yönet - Change the time zone + Saat dilimini değiştir - Start speech recognition + Konuşma tanımayı başlat - View installed updates + Yüklü güncellemeleri görüntüle - What's happened to the Quick Launch toolbar? + Hızlı Başlat araç çubuğuna ne oldu? - Change search options for files and folders + Dosya ve klasörler için arama seçeneklerini değiştir Adjust settings before giving a presentation - Scan a document or picture + Bir belgeyi veya resmi tara - Change the way measurements are displayed + Ölçümlerin görüntülenme şeklini değiştirme - Press key combinations one at a time + Tuş kombinasyonlarına teker teker basın - Restore data, files or computer from backup (Windows 7) + Verileri, dosyaları veya bilgisayarı yedekten geri yükleme (Windows 7) - Set your default programs + Varsayılan programlarınızı ayarlayın - Set up a broadband connection + Geniş bant bağlantısı kur Calibrate the screen for pen or touch input - Manage user certificates + Kullanıcı sertifikalarını yönet - Schedule tasks + Görevleri planla Ignore repeated keystrokes using FilterKeys - Find and fix bluescreen problems + Mavi ekran sorunlarını bul ve düzelt - Hear a tone when keys are pressed + Tuşlara basıldığında bir ses duy - Delete browsing history + Tarama geçmişini sil - Change what the power buttons do + Güç düğmelerinin ne yaptığını değiştir - Create standard user account + Standart kullanıcı hesabı oluştur - Take speech tutorials + Konuşma eğitimleri al - View system resource usage in Task Manager + Görev Yöneticisi'nde sistem kaynağı kullanımını görüntüle - Create an account + Bir hesap oluştur - Get more features with a new edition of Windows + Windows'un yeni sürümüyle daha fazla özellik edinin - Control Panel + Denetim Masası TaskLink - Unknown + Bilinmiyor \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.uk-UA.resx b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.uk-UA.resx index 9d801e0d4..c30e6dacc 100644 --- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.uk-UA.resx +++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.uk-UA.resx @@ -214,7 +214,7 @@ Short/modern name for application - Додатки та функції + Застосунки та функції Area Apps @@ -222,7 +222,7 @@ Type of the setting is a "Modern Windows settings". We use the same term as used in start menu search at the moment. - Додатки для веб-сайтів + Застосунки для вебсайтів Area Apps @@ -248,7 +248,7 @@ Зовнішній вигляд та персоналізація - Додатки + Застосунки Час і регіон @@ -515,23 +515,23 @@ Area TimeAndLanguage - Програми за замовчуванням + Типові програми Area Apps - Камера за замовчуванням + Типова камера Area Device - Розташування за замовчуванням + Типове розташування Area Control Panel (legacy settings) - Програми за замовчуванням + Типові програми Area Control Panel (legacy settings) - Місця збереження за замовчуванням + Типові місця збереження Area System @@ -611,7 +611,7 @@ Area Privacy - Облікові записи електронної пошти та додатків + Облікові записи пошти та застосунків Area UserAccounts @@ -1028,7 +1028,7 @@ Доступно, якщо встановлено редактор методів введення Microsoft Wubi. - Доступно лише якщо встановлено додаток Портал змішаної реальності. + Доступно лише якщо встановлено застосунок «Портал змішаної реальності». Доступно лише на мобільних пристроях або якщо на підприємстві розгорнуто пакет забезпечення. @@ -1172,7 +1172,7 @@ Area Privacy - Телефон - Програми за замовчуванням + Телефон: типові програми Area System @@ -1687,7 +1687,7 @@ Area UpdateAndSecurity - Оновлення Windows - Додаткові параметри + Оновлення Windows: додаткові параметри Area UpdateAndSecurity @@ -1699,7 +1699,7 @@ Area UpdateAndSecurity - Оновлення Windows - Перегляд додаткових оновлень + Оновлення Windows: перегляд додаткових оновлень Area UpdateAndSecurity @@ -1776,7 +1776,7 @@ Зміна налаштувань для вмісту, отриманого за допомогою функції "Доторкнутись і відправити" - Зміна налаштувань за замовчуванням для носіїв або пристроїв + Зміна типових налаштувань для носіїв або пристроїв Роздрукувати мовленнєву довідкову картку @@ -1803,7 +1803,7 @@ Переглянути, які процеси запускаються автоматично під час запуску Windows - Визначити, чи є на веб-сайті RSS-канал + Визначити, чи є на вебсайті RSS-канал Додати годинник для різних часових поясів @@ -2161,7 +2161,7 @@ Розширене налаштування принтера - Змінити принтер за замовчуванням + Змінити типовий принтер Редагування змінних середовища для вашого облікового запису @@ -2275,7 +2275,7 @@ Змінити спосіб відображення часу - Зміна способу відображення веб-сторінок у вкладках + Зміна способу відображення вебсторінок у вкладках Зміна способу відображення дат і списків @@ -2314,7 +2314,7 @@ Переглянути всі звіти про проблеми - Підтримка 16-бітних додатків + Підтримка 16-бітних застосунків Налаштуйте правила набору @@ -2425,7 +2425,7 @@ Блокування або розблокування панелі завдань - Керування веб-обліковими даними + Керування вебобліковими даними Зміна часового поясу @@ -2458,7 +2458,7 @@ Відновлення даних, файлів або комп'ютера з резервної копії (Windows 7) - Налаштування програм за замовчуванням + Налаштування типових програм Налаштування широкосмугового з'єднання diff --git a/README.md b/README.md index 4cd8e059e..fe6ce32f4 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Dedicated to making your work flow more seamless. Search everything from applica ### Installation -[Windows 7+ Installer](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest/download/Flow-Launcher-Setup.exe) or [Portable Version](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest/download/Flow-Launcher-Portable.zip) +[Windows 10+ Installer](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest/download/Flow-Launcher-Setup.exe) or [Portable Version](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest/download/Flow-Launcher-Portable.zip) #### Winget @@ -117,22 +117,29 @@ Or download the [early access version](https://github.com/Flow-Launcher/Prerelea - Save file or folder locations for quick access. -#### Drag & Drop - - - -- Drag a file/folder to File Explorer, or even Discord. -- Copy/move behavior can be change via Ctrl or Shift, and the operation is displayed on the mouse cursor. - ### Windows & Control Panel Settings - Search for Windows & Control Panel settings. +### Dialog Jump + + + +- Search for a file/folder and quickly jump to its path in the Open/Save As dialog window. +- Use Alt+G to quickly jump the Open/Save As dialog window path to the path of the active file manager. + +### Drag & Drop + + + +- Drag a file/folder to File Explorer, or even Discord. +- Copy/move behavior can be change via Ctrl or Shift, and the operation is displayed on the mouse cursor. + ### Priority - + - Prioritise the order of each plugin's results. @@ -145,19 +152,18 @@ Or download the [early access version](https://github.com/Flow-Launcher/Prerelea - Turn on preview permanently via Settings (Always Preview). - Use Ctrl++/- and Ctrl+[/] to adjust search window width and height quickly if the preview area is too narrow. - ### Customizations - + - Window size adjustment, animation, and sound - Color Scheme (aka Dark Mode) - + - There are various themes and you also can make your own. -#### Date & Time Display In Search Window +### Date & Time Display In Search Window @@ -185,6 +191,7 @@ Or download the [early access version](https://github.com/Flow-Launcher/Prerelea
  • de, Deutsch
  • ko, 한국어
  • Srpski
  • +
  • Српски
  • Português
  • Português (Brasil)
  • Spanish
  • @@ -196,6 +203,7 @@ Or download the [early access version](https://github.com/Flow-Launcher/Prerelea
  • čeština
  • اللغة العربية
  • Tiếng Việt
  • +
  • עברית
  • @@ -264,7 +272,7 @@ Or download the [early access version](https://github.com/Flow-Launcher/Prerelea ### 🛒 Plugin Store - + - You can view the full plugin list or quickly install a plugin via the Plugin Store menu inside Settings @@ -322,6 +330,7 @@ Or download the [early access version](https://github.com/Flow-Launcher/Prerelea | Flow Launcher Tips | Visit Flow Launcher's documentation for more help and how to use tips | | Flow Launcher UserData Folder | Open the location where Flow Launcher's settings are stored | | Toggle Game Mode | Toggle Game Mode | +| Set Flow Launcher Theme | Set the Flow Launcher Theme | ### 💁‍♂️ Tips @@ -389,17 +398,17 @@ Each of the pull requests will be marked with a milestone indicating the planned Contributions are very welcome, in addition to the main project(C#) there are also [documentation](https://github.com/Flow-Launcher/docs)(md), [website](https://github.com/Flow-Launcher/flow-launcher.github.io)(html/css) and [others](https://github.com/Flow-Launcher) that can be contributed to. If you are unsure of a change you want to make, let us know in the [Discussions](https://github.com/Flow-Launcher/Flow.Launcher/discussions/categories/ideas), otherwise feel free to put in a pull request. -You will find the main goals of flow placed under the [Projects board](https://github.com/Flow-Launcher/Flow.Launcher/projects), so feel free to contribute on that. If you would like to make small incremental changes, feel free to do so as well. +You will find the main goals of flow placed under the [Projects board](https://github.com/orgs/Flow-Launcher/projects/4), so feel free to contribute on that. If you would like to make small incremental changes, feel free to do so as well. Get in touch if you like to join the Flow-Launcher Team and help build this great tool. ### Developing/Debugging -- Flow Launcher's target framework is .Net 7 +- Flow Launcher's target framework is .Net 9 -- Install Visual Studio 2022 +- Install Visual Studio 2022 (v17.12+) -- Install .Net 7 SDK +- Install .Net 9 SDK - via Visual Studio installer - - via winget `winget install Microsoft.DotNet.SDK.7` - - Manually from [here](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) + - via winget `winget install Microsoft.DotNet.SDK.9` + - Manually from [here](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) diff --git a/Scripts/flowlauncher.nuspec b/Scripts/flowlauncher.nuspec index 8d753bc8c..fa12150cc 100644 --- a/Scripts/flowlauncher.nuspec +++ b/Scripts/flowlauncher.nuspec @@ -11,6 +11,6 @@ Flow Launcher - Quick file search and app launcher for Windows with community-made plugins - + diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index e54852d32..8d2d14a80 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -99,7 +99,7 @@ function Pack-Squirrel-Installer ($path, $version, $output) { function Publish-Self-Contained ($p) { $csproj = Join-Path "$p" "Flow.Launcher/Flow.Launcher.csproj" -Resolve - $profile = Join-Path "$p" "Flow.Launcher/Properties/PublishProfiles/Net7.0-SelfContained.pubxml" -Resolve + $profile = Join-Path "$p" "Flow.Launcher/Properties/PublishProfiles/Net9.0-SelfContained.pubxml" -Resolve # we call dotnet publish on the main project. # The other projects should have been built in Release at this point. diff --git a/appveyor.yml b/appveyor.yml index 39e2a114c..911e30423 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: '1.20.2.{build}' +version: '2.0.0.{build}' # Do not build on tags because we create a release on merge to master. Otherwise will upload artifacts twice changing the hash, as well as triggering duplicate GitHub release action & NuGet deployments. skip_tags: true @@ -76,7 +76,7 @@ deploy: This build includes new changes from commit: $(APPVEYOR_REPO_COMMIT_MESSAGE) - See all changes in this early access by going to the [milstones](https://github.com/Flow-Launcher/Flow.Launcher/milestones?sort=title&direction=asc) section and choosing the upcoming milestone. + See all changes in this early access by going to the [milestones](https://github.com/Flow-Launcher/Flow.Launcher/milestones?sort=title&direction=asc) section and choosing the upcoming milestone. For latest production release visit [here](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest) Please report any bugs or issues over at the [main repository](https://github.com/Flow-Launcher/Flow.Launcher/issues)' diff --git a/global.json b/global.json index 1ee79d7a2..44db98b24 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.*", + "version": "9.0.*", "rollForward": "latestPatch" } }