diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt index a36a6af3efa..60142b3468b 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 5b3419041a6..81b9aba97e7 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 f308ec5993a..faf1c39785c 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 37d4a8683c8..be523bfe807 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 ec8dfcd4ef8..83e830d7541 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 7498262deba..957f836bbc5 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 451bf386c92..58a877ba3b7 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 47bd66107b3..904392bf06a 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 719f2556e2e..5652eef5e16 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 fa499273c56..a5545af1248 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 7f02cef0990..721e14dcaac 100644 --- a/Flow.Launcher.Core/Configuration/Portable.cs +++ b/Flow.Launcher.Core/Configuration/Portable.cs @@ -45,8 +45,7 @@ public void DisablePortableMode() #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 @@ public void EnablePortableMode() #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 @@ public void PreStartCleanUpAfterPortabilityUpdate() { 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 @@ public void PreStartCleanUpAfterPortabilityUpdate() { 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 @@ public bool CanUpdatePortability() 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 455ee096da6..89286dfb0f9 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 @@ internal override void InstallEnvironment() // 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 12965286f47..724ae20f46c 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 @@ internal override void InstallEnvironment() { 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 6960b79c9a7..6a32664a13a 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 @@ internal override void InstallEnvironment() { 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 7ca91eaecde..1e845498ce4 100644 --- a/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs +++ b/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs @@ -4,6 +4,7 @@ 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 @@ public static class PluginsManifest 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 @@ public static async Task UpdateManifestAsync(bool usePrimaryUrlOnly = fals 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 @@ public static async Task UpdateManifestAsync(bool usePrimaryUrlOnly = fals 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 e9f199d00dd..5279500619f 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 33963c01a5b..d01b34ab6bd 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 @@ await DownloadFileAsync( } } + /// + /// 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 @@ private static bool InstallSourceKnown(string url) ); } } + +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 d88f2f050e5..a4ab8de08ae 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 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 @@ public static class PluginManager 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 @@ public static void LoadPlugins(PluginsSettings settings) _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 @@ public static async Task InitializePluginsAsync() } } - 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 (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 @@ public static async Task> QueryHomeForPluginAsync(PluginPair pair, 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 @@ public static bool IsHomePlugin(string id) 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 @@ internal static async Task UninstallPluginAsync(PluginMetadata plugin, boo 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 @@ internal static async Task UninstallPluginAsync(PluginMetadata plugin, boo 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 256c36065a9..e9e5ee367cb 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -120,17 +120,13 @@ private static IEnumerable DotNetPlugins(List source { 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 ecaecf64689..5534ea172d9 100644 --- a/Flow.Launcher.Core/Resource/AvailableLanguages.cs +++ b/Flow.Launcher.Core/Resource/AvailableLanguages.cs @@ -17,6 +17,7 @@ internal static class AvailableLanguages 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 @@ public static List GetAvailableLanguages() German, Korean, Serbian, + Serbian_Cyrillic, Portuguese_Portugal, Portuguese_Brazil, Spanish, @@ -79,7 +81,8 @@ public static string GetSystemTranslation(string languageCode) "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 7b7d6eef661..8261feab3d0 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 @@ public class Internationalization 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 @@ public static void InitSystemLanguageCode() 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 @@ public async Task InitializeLanguageAsync() // 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 @@ public void ChangeLanguage(string languageCode) 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 @@ private static Language GetLanguageByLanguageCode(string languageCode) } } - 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 @@ private async Task ChangeLanguageAsync(Language language) // 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 @@ public static void ChangeCultureInfo(string languageCode) thread.CurrentUICulture = currentCulture; } + #endregion + + #region Prompt Pinyin + public bool PromptShouldUsePinyin(string languageCodeToSet) { var languageToSet = GetLanguageByLanguageCode(languageCodeToSet); @@ -212,14 +238,18 @@ public bool PromptShouldUsePinyin(string languageCodeToSet) // 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,6 +285,40 @@ private void LoadLanguage(Language language) } } + private static string LanguageFile(string folder, string language) + { + if (Directory.Exists(folder)) + { + var path = Path.Combine(folder, language); + if (File.Exists(path)) + { + return path; + } + else + { + API.LogError(ClassName, $"Language path can't be found <{path}>"); + var english = Path.Combine(folder, DefaultFile); + if (File.Exists(english)) + { + return english; + } + else + { + API.LogError(ClassName, $"Default English Language path can't be found <{path}>"); + return string.Empty; + } + } + } + else + { + return string.Empty; + } + } + + #endregion + + #region Available Languages + public List LoadAvailableLanguages() { var list = AvailableLanguages.GetAvailableLanguages(); @@ -262,6 +326,10 @@ public List LoadAvailableLanguages() return list; } + #endregion + + #region Get Translations + public static string GetTranslation(string key) { var translation = Application.Current.TryFindResource(key); @@ -276,7 +344,11 @@ public static string GetTranslation(string key) } } - private void UpdatePluginMetadataTranslations() + #endregion + + #region Update Metadata + + public static void UpdatePluginMetadataTranslations() { // Update plugin metadata name & description foreach (var p in PluginManager.GetTranslationPlugins()) @@ -295,34 +367,6 @@ private void UpdatePluginMetadataTranslations() } } - private static string LanguageFile(string folder, string language) - { - if (Directory.Exists(folder)) - { - var path = Path.Combine(folder, language); - if (File.Exists(path)) - { - return path; - } - else - { - API.LogError(ClassName, $"Language path can't be found <{path}>"); - var english = Path.Combine(folder, DefaultFile); - if (File.Exists(english)) - { - return english; - } - else - { - API.LogError(ClassName, $"Default English Language path can't be found <{path}>"); - return string.Empty; - } - } - } - else - { - return string.Empty; - } - } + #endregion } } diff --git a/Flow.Launcher.Core/Resource/LocalizationConverter.cs b/Flow.Launcher.Core/Resource/LocalizationConverter.cs deleted file mode 100644 index fdda33926d5..00000000000 --- 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 bc3655f69e7..45275696c1d 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -49,8 +49,9 @@ public async Task UpdateAppAsync(bool silentUpdate = true) // 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 @@ public async Task UpdateAppAsync(bool silentUpdate = true) 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 @@ public async Task UpdateAppAsync(bool silentUpdate = true) _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 @@ public async Task UpdateAppAsync(bool silentUpdate = true) [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 @@ private static async Task GitHubUpdateManagerAsync(string reposit 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 @@ private string NewVersionTips(string version) 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 00000000000..5e9abc24cf4 --- /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 00000000000..65652878fc8 --- /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 00000000000..d1248eac13d --- /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 00000000000..ee4e034337b --- /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 00000000000..e9ed9dae7a5 --- /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 31547200b23..c32c3624830 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 a29f8accfb0..8afab419bbc 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -20,6 +20,10 @@ public static class 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 @@ var userName when string.IsNullOrEmpty(userName) => } 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 @@ public static async Task SendAsync(HttpRequestMessage reque 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 00000000000..d13eeb4142b --- /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 86df01a3015..64d323de6c5 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs @@ -19,14 +19,14 @@ public static class ImageLoader 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 @@ public static class ImageLoader public static async Task InitializeAsync() { - _storage = new BinaryStorage>("Image"); - _hashGenerator = new ImageHashGenerator(); + var usage = await Task.Run(() => + { + _storage = new BinaryStorage>("Image"); + _hashGenerator = new ImageHashGenerator(); - var usage = await LoadStorageToConcurrentDictionaryAsync(); - _storage.ClearData(); + var usage = LoadStorageToConcurrentDictionary(); + _storage.ClearData(); - ImageCache.Initialize(usage); + ImageCache.Initialize(usage); - foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon }) - { - ImageSource img = new BitmapImage(new Uri(icon)); - img.Freeze(); - ImageCache[icon, false] = img; - } + 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 @@ await Stopwatch.InfoAsync(ClassName, "Preload images cost", async () => }); } - public static async Task SaveAsync() + public static void Save() { - await storageLock.WaitAsync(); - - try - { - await _storage.SaveAsync(ImageCache.EnumerateEntries() - .Select(x => x.Key) - .ToList()); - } - catch (System.Exception e) + lock (storageLock) { - 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 - { - return await _storage.TryLoadAsync(new List<(string, bool)>()); - } - finally + lock (storageLock) { - storageLock.Release(); + return _storage.TryLoad([]); } } @@ -174,7 +163,7 @@ private static async ValueTask LoadInternalAsync(string path, bool 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 @@ private static ImageResult GetThumbnailResult(ref string path, bool loadFullImag } 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 edc71feef24..eb844dd7ca0 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 8eaa757bec1..1c0cc6872ff 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 + public class PinyinAlphabet : IAlphabet { - private bool constructed; - - private List originalIndexs = new List(); - private List translatedIndexs = new List(); - private int translatedLength = 0; + private readonly ConcurrentDictionary _pinyinCache = new(); + private readonly Settings _settings; + private ReadOnlyDictionary currentDoublePinyinTable; - 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) + public PinyinAlphabet() { - if (translatedIndex > translatedIndexs.Last()) - return translatedIndex - translatedLength - 1; + _settings = Ioc.Default.GetRequiredService(); + LoadDoublePinyinTable(); - 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++) + _settings.PropertyChanged += (sender, e) => { - 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]) + switch (e.PropertyName) { - int indexDef = 0; - - for (int j = 0; j < upperBound; j++) - { - indexDef += translatedIndexs[j * 2 + 1] - translatedIndexs[j * 2]; - } - - return translatedIndex - indexDef - 1; + case nameof (Settings.ShouldUsePinyin): + if (_settings.ShouldUsePinyin) + { + Reload(); + } + break; + case nameof(Settings.UseDoublePinyin): + case nameof(Settings.DoublePinyinSchema): + if (_settings.UseDoublePinyin) + { + Reload(); + } + break; } - } + }; } - public void endConstruct() + public void Reload() { - if (constructed) - throw new InvalidOperationException("Mapping has already been constructed"); - constructed = true; + LoadDoublePinyinTable(); + _pinyinCache.Clear(); } - } - /// - /// 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 void CreateDoublePinyinTableFromStream(Stream jsonStream) + { + var table = JsonSerializer.Deserialize>>(jsonStream) ?? + throw new InvalidOperationException("Failed to deserialize double pinyin table: result is null"); - private Settings _settings; + 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."); + } - public PinyinAlphabet() - { - Initialize(Ioc.Default.GetRequiredService()); + currentDoublePinyinTable = new ReadOnlyDictionary(schemaDict); } - private void Initialize([NotNull] Settings settings) + private void LoadDoublePinyinTable() { - _settings = settings ?? throw new ArgumentNullException(nameof(settings)); + 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 CanBeTranslated(string stringToTranslate) + public bool ShouldTranslate(string stringToTranslate) { - return WordsHelper.HasChinese(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 resultList = WordsHelper.GetPinyinList(content); + var resultBuilder = new StringBuilder(_settings.UseDoublePinyin ? 3 : 4); // Pre-allocate with estimated capacity + var map = new TranslationMapping(); - StringBuilder resultBuilder = new StringBuilder(); - TranslationMapping map = new TranslationMapping(); + var previousIsChinese = false; - bool pre = false; - - for (int i = 0; i < resultList.Length; i++) + for (var 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 + + map.AddNewIndex(resultBuilder.Length, translated.Length); + resultBuilder.Append(translated); + previousIsChinese = true; + } + else + { + // Add space after Chinese characters before non-Chinese characters + if (previousIsChinese) { - if (pre) - { - pre = false; - resultBuilder.Append(' '); - } - - resultBuilder.Append(resultList[i]); + previousIsChinese = false; + resultBuilder.Append(' '); } + + map.AddNewIndex(resultBuilder.Length, resultList[i].Length); + resultBuilder.Append(resultList[i]); } + } - map.endConstruct(); + map.EndConstruct(); - var key = resultBuilder.ToString(); - map.setKey(key); + var translation = resultBuilder.ToString(); + var result = (translation, map); + + return _pinyinCache[content] = result; + } - return _pinyinCache[content] = (key, map); - } - else + /// + /// 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 48e6b55238c..15200f5aa38 100644 --- a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs @@ -12,7 +12,7 @@ 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 @@ public BinaryStorage(string filename, string directoryPath = null!) 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 @@ public async ValueTask TryLoadAsync(T defaultData) 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 @@ public async ValueTask SaveAsync() 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 @@ public async ValueTask SaveAsync(T data) 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 e85c5d6f442..2882cb8f03e 100644 --- a/Flow.Launcher.Infrastructure/StringMatcher.cs +++ b/Flow.Launcher.Infrastructure/StringMatcher.cs @@ -68,7 +68,7 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption 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 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption 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 @@ private bool IsAcronym(string stringToCompare, int compareStringIndex) } // 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 00000000000..b4c6764df1a --- /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 f9504e6d926..00000000000 --- 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 3ec84ff1762..0c34020500f 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 @@ public void Save() _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 @@ public bool ShowOpenResultHotkey 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 @@ public string Language 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 @@ public string Theme } } 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 @@ public bool ShowHistoryResultsForHomePage } } } - + 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 @@ public CustomBrowserViewModel CustomBrowser } }; + 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 @@ public SearchPrecisionScore QuerySearchPrecision 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 @@ public bool HideNotifyIcon 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 @@ public List RegisteredHotkeys { 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 @@ public List RegisteredHotkeys 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 @@ public List RegisteredHotkeys 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 @@ public enum AnimationSpeeds 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 86e7b7c971c..8117339254d 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.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 @@ public static unsafe string GetWallpaperPath() #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 @@ public static bool IsForegroundWindow(Window window) 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 @@ public static unsafe bool IsForegroundWindowFullscreen() { 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 @@ public static Point TransformPixelsToDIP(Visual visual, double unitX, double uni 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 @@ internal static HWND GetWindowHandle(Window window, bool ensure = false) 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 @@ public static unsafe void SwitchToEnglishKeyboardLayout(bool backupPrevious) /// 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 @@ public static void RestorePreviousKeyboardLayout() hwnd, PInvoke.WM_INPUTLANGCHANGEREQUEST, PInvoke.INPUTLANGCHANGE_FORWARD, - _previousLayout.Value + new LPARAM((nint)_previousLayout.Value) ); _previousLayout = HKL.Null; @@ -761,6 +782,62 @@ private static bool TryGetNotoFont(string langKey, out string notoFont) #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 @@ public static unsafe void OpenFolderAndSelectFile(string filePath) } #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 00000000000..abd250f7c44 --- /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 00000000000..2c9f0c13923 --- /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 893b0ba8047..47ab24757cd 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 @@ public class FlowLauncherQueryEventArgs /// 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 1831bf46f24..ae245427947 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 00000000000..e028ebb1264 --- /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 00000000000..d81b2bd197c --- /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 00000000000..33ad9ae73da --- /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 00000000000..9a2b879d058 --- /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 bac93d090cd..cf5a8a5829c 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPlugin.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPlugin.cs @@ -32,6 +32,6 @@ public interface IPlugin : IAsyncPlugin 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 74c97fa6e4b..dd44647b42a 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -84,6 +84,15 @@ public interface IPublicAPI /// 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 @@ public interface IPublicAPI /// 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 @@ public interface IPublicAPI /// /// 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 0596691cc7f..901695a8bd8 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 f0fcd48ffc0..2c9b8d4fdbc 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -57,7 +57,10 @@ public string CopyText /// 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 @@ public string PluginDirectory /// 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 @@ public Result Clone() 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 3221708c135..6808c9c9104 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.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 @@ public static unsafe IList GetDisplayMonitors() { 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 @@ public static unsafe IList GetDisplayMonitors() /// /// 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 @@ public static unsafe MonitorInfo GetNearestDisplayMonitor(HWND hwnd) }); 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 @@ public static unsafe MonitorInfo GetNearestDisplayMonitor(HWND hwnd) 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 unsafe MonitorInfo(HMONITOR monitor, RECT* rect) /// /// 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 unsafe MonitorInfo(HMONITOR monitor, RECT* rect) /// /// 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 74a16b83d80..4257a8b2e70 100644 --- a/Flow.Launcher.Plugin/UserPlugin.cs +++ b/Flow.Launcher.Plugin/UserPlugin.cs @@ -76,5 +76,10 @@ public record UserPlugin /// 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 00000000000..af835c59870 --- /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 00000000000..1747f2b4a38 --- /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 0241a374e41..33479c5a062 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 00000000000..bd3636f0ad8 --- /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 887b13126cf..5af76f37f39 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 c1c0f96a7bc..0e3e6999620 100644 --- a/Flow.Launcher/SettingWindow.xaml.cs +++ b/Flow.Launcher/SettingWindow.xaml.cs @@ -7,10 +7,10 @@ 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 @@ private void AdjustWindowPosition(ref double top, ref double left) 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 @@ private static bool IsPositionValid(double top, double left) 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 @@ private double WindowLeft() 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 01599a05099..d492f28c58c 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -9,6 +9,7 @@ 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 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 @@ public partial class MainViewModel : BaseModel, ISavable, IDisposable private Task _resultsViewUpdateTask; private readonly IReadOnlyList _emptyResult = new List(); + private readonly IReadOnlyList _emptyDialogJumpResult = new List(); private readonly PluginMetadata _historyMetadata = new() { @@ -195,6 +199,18 @@ public MainViewModel() 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 @@ private void RegisterViewUpdate() 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 @@ public void RegisterResultsUpdatedEvent() 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 @@ public void ForwardHistory() [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 @@ private async Task OpenResultAsync(string index) 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 @@ private async Task OpenResultAsync(string index) _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 (isDialogJump) { - if (token.IsCancellationRequested) + foreach (var result in results.ToList()) { - break; + 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); + var resultCopy = result.Clone(); + resultsCopy.Add(resultCopy); + } } + return resultsCopy; } @@ -813,6 +884,12 @@ private ResultsViewModel SelectedResults } } + 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 @@ private ResultsViewModel SelectedResults 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 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b 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 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b } else { - plugins = PluginManager.ValidPluginsForQuery(query); + plugins = PluginManager.ValidPluginsForQuery(query, currentIsDialogJump); if (plugins.Count == 1) { @@ -1411,6 +1485,23 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b } // 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 @@ async Task QueryTaskAsync(PluginPair plugin, CancellationToken token) // 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 @@ public bool ShouldIgnoreHotkeys() #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 @@ public void Show() 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 @@ public void Show() } } - 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 @@ public async void Hide() 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 @@ protected virtual void Dispose(bool disposing) 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 ea222d02374..d889bdd52ab 100644 --- a/Flow.Launcher/ViewModel/PluginViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginViewModel.cs @@ -37,12 +37,17 @@ private async Task LoadIconAsync() 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 c58abae28ee..d4382fb7f16 100644 --- a/Flow.Launcher/ViewModel/ResultViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultViewModel.cs @@ -66,8 +66,6 @@ public ResultViewModel(Result result, Settings settings) 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 00000000000..32b78c3348c --- /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 6e6b2e5f4ad..b1166146670 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 @@ protected List LoadBookmarks(string browserDataPath, string name) } 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 @@ protected List LoadBookmarks(string browserDataPath, string name) 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 @@ private static void EnumerateFolderBookmark(JsonElement folderElement, ICollecti } 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 @@ ORDER BY b.width DESC } 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 758ce68ae78..b76adae93c3 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 ec3b867ea81..be83f61584f 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 @@ protected List GetBookmarksFromPath(string placesPath) } 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 @@ protected List GetBookmarksFromPath(string placesPath) 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 @@ protected List GetBookmarksFromPath(string placesPath) } 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 @@ protected List GetBookmarksFromPath(string placesPath) } 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 @@ private void LoadFaviconsFromDb(string dbPath, List bookmarks) 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 @@ private void LoadFaviconsFromDb(string dbPath, List bookmarks) // 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)) - { - faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}.svg"); - } - else + // Check if the image data is compressed (GZip) + if (imageData.Length > 2 && imageData[0] == 0x1f && imageData[1] == 0x8b) { - 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"); + + if (savedPaths.TryAdd(faviconPath, true)) + { + FaviconHelper.SaveBitmapData(webpData, faviconPath); + } - bookmark.FaviconPath = 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 49ae2656495..901dc2a374b 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 a879dcefd1b..1820a783634 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 void LoadFaviconsFromDb(string faviconCacheDir, string dbPath, Act } 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 void LoadFaviconsFromDb(string faviconCacheDir, string dbPath, Act } 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 void LoadFaviconsFromDb(string faviconCacheDir, string dbPath, Act } 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 void SaveBitmapData(byte[] imageData, string outputPath) } 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 45f8d97da3e..382418336a4 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 153b05d7646..68a8b7a6680 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 66e30855f73..68bb924113d 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 22830e7c880..56471417309 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 28524229be5..859a0ea0ec4 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 ba9efd7e0a9..e0beb7a78cf 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 39546c1024c..de10d0f19bc 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 79490928dc7..90751bb6f0b 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 eb13bf852ca..32216f3505d 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 d08d67d9867..6700bce1902 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 bb7c9fc06b1..fd381d4ee72 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 1c53a49d27d..e3650ed269e 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 407786cdde2..c83c2ec7885 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 9f92d86b153..b63c2ffc5c0 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 ce264bc5f86..c6ec5eb4c43 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 2818a0600f7..f2b3e93d180 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 bb8639d9704..b147c083b94 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 c7556e877d9..1d498a5fda9 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 00000000000..02b995517a4 --- /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 84173e616ec..5a7a18714a6 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 8c4280f63c7..15cd4f250bc 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 07ccc2ea4ce..a40370a9c69 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 662c87d49be..f52a7fe7b73 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 93454436724..778cd399211 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 7fa50a089a3..e8bc6bcb668 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 91ade206b67..07ce510fb3e 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 List Query(Query query) Score = BookmarkLoader.MatchProgram(c, param).Score, Action = _ => { - _context.API.OpenUrl(c.Url); + Context.API.OpenUrl(c.Url); return true; }, @@ -108,7 +128,7 @@ public List Query(Query query) 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 static void ReloadAllBookmarks(bool disposeFileWatchers = true) 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 List LoadContextMenus(Result selectedResult) { 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 74e0f299aff..af1e3fee496 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 string Name get => _name; set { - _name = value; - OnPropertyChanged(); + if (_name != value) + { + _name = value; + OnPropertyChanged(); + } } } @@ -21,24 +27,36 @@ public string DataDirectoryPath 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 80b004ff993..f67d359bffa 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 3219d647e85..759ba99de67 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 ed8cb3fdb66..f5dbe8e2003 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 15598118cbc..2f2777aa15a 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 d8da714dcff..46f5efe23eb 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 0e0911a70ec..b71e5d8a0e0 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 685bc136c7c..dce29cba5cd 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 657c471d55e..7f1775e2e9d 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 c462ccc432a..a6db348116d 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 9fe80bc4751..b053c890547 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 fa4651a97f2..5b724e82ebb 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 757394b4cb5..bbd06006f1b 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 ee2ae77a147..e4ca16d4142 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 527df1d21d5..9ae31e976d7 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 15598118cbc..2f2777aa15a 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 f697d2e6d83..e73298dcaf0 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 6dd903fa479..73a60d42f09 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 b751f3f397b..7ec52be8cc8 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 30c55753608..43a7d44c7d7 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 d8d3a586f53..498b6eb5021 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 00000000000..98e3aebb576 --- /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 15598118cbc..2f2777aa15a 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 345c814339f..b41fc06566b 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 01475592903..14042dffd08 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 82ebbbe9363..20717d1dbfa 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 aada17d5a84..445ed394ff9 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 83df3e7cde0..7c8acf40b2d 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 b1e4cd60601..6878c54b4a8 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 @@ public List Query(Query query) 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 @@ public List Query(Query query) 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 @@ public List Query(Query query) } catch (ExternalException) { - Context.API.ShowMsgBox("Copy failed, please try later"); + Context.API.ShowMsgBox(Localize.flowlauncher_plugin_calculator_failed_to_copy()); return false; } } @@ -116,58 +112,168 @@ public List Query(Query query) return new List(); } - private bool CanCalculate(Query query) + /// + /// 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) { - // Don't execute when user only input "e" or "i" keyword - if (query.Search.Length < 2) + 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) { - return false; + 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); + } } - if (!RegValidExpressChar.IsMatch(query.Search)) + // Case 2: Only dots + if (dotCount > 0) { - return false; + 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; + } } - if (!IsBracketComplete(query.Search)) + // Case 3: Only commas + if (commaCount > 0) { - return false; + 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); + } } - if ((query.Search.Contains(dot) && GetDecimalSeparator() != dot) || - (query.Search.Contains(comma) && GetDecimalSeparator() != comma)) - return false; + // Case 4: No separators + return numberStr; + } - return true; + 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 string ChangeDecimalSeparator(decimal value, string newDecimalSeparator) + private bool CanCalculate(Query query) { - if (String.IsNullOrEmpty(newDecimalSeparator)) + if (query.Search.Length < 2) { - return value.ToString(); + return false; } - var numberFormatInfo = new NumberFormatInfo + if (!RegValidExpressChar.IsMatch(query.Search)) { - NumberDecimalSeparator = newDecimalSeparator - }; - return value.ToString(numberFormatInfo); + return false; + } + + if (!IsBracketComplete(query.Search)) + { + return false; + } + + return true; } - 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 @@ private bool IsBracketComplete(string query) 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 00000000000..f4e2090e740 --- /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 4eacb9d349c..00000000000 --- 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 09f745669fc..87ae72fb681 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs @@ -12,6 +12,21 @@ public SettingsViewModel(Settings settings) 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 ceee3897c18..8d240ef3971 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 @@ public List LoadContextMenus(Result selectedResult) { 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 @@ public List LoadContextMenus(Result selectedResult) Context.API.GetTranslation("plugin_explorer_removefilefoldersuccess_detail"), Constants.ExplorerIconImageFullPath); - ViewModel.Save(); + return true; }, @@ -130,9 +129,8 @@ public List LoadContextMenus(Result selectedResult) } 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 @@ public List LoadContextMenus(Result selectedResult) } 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 @@ public List LoadContextMenus(Result selectedResult) } 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 @@ public List LoadContextMenus(Result selectedResult) } 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 @@ public List LoadContextMenus(Result selectedResult) } 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 @@ private Result CreateOpenContainingFolderResult(SearchResult record) } 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 93691814aee..af33f4da2ce 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 00000000000..7763d966262 --- /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 72f58f5b60c..00000000000 --- 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 ebdb0ff3518..b2bf99515b4 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 38f4f145ed7..0acdb5ca163 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 8c011814c3a..66816de93e1 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 1d8c3937ab0..8ddb958adfc 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 9f60aaa4340..16ef037ccfd 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 ae541a7457e..f80a559654b 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 10ee4a9a494..474ba9a4c0b 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 8809004c724..096cd4a0d93 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 a18a15aad4a..a4e4445ae2b 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 3afaf2f250f..f3fa1e1e6c1 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 648efe642c0..d0b045175a1 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 cf4d8b8ad7f..3195ff6e5be 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 733136c36f5..5efdead0c33 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 c4bfb639d8b..cc6d350c920 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 ddf56ab3757..c981c2832e5 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 d0a26029055..fe4fa320a24 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 98a77d06a4f..d0651cc53e8 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 44a3a9b65e8..1644745ae99 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 7c3f5deff06..3ca738b69c5 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 00000000000..19fe6dc648e --- /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 4df64e4d57c..f8effbd7cf3 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 ed327e1bced..aefe8af30d0 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 38829112c72..823c33193d8 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 de6f9a6a4c7..7b64af3d832 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 79d8fc66db9..8ad979ac8c2 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 c4e22066dda..39f260499a6 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 1c5d074a0b8..d93c6c77b9f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -10,21 +10,25 @@ 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 @@ public Task InitAsync(PluginInitContext context) 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 @@ public string GetTranslatedPluginDescription() { 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 6159c93556a..fd62566d54e 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 @@ public enum StateCode /// /// 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 @@ public static async IAsyncEnumerable SearchAsync(EverythingSearchO 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 5b80819faed..c952a980c47 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 @@ public static void Load(string directory) // 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 92b8e96238e..d8b670a0895 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 00000000000..6a3d7cb67bf --- /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 3c2fc3660ad..00000000000 --- 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 1975211f9bc..5dd6162e8c5 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 @@ public class AccessLink 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 85b595390cf..32651ecb8a7 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 @@ internal static List AccessLinkListMatched(Query query, IEnumerable 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 @@ internal static List AccessLinkListAll(Query query, IEnumerable 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 eaae3b4a972..dfa2c8d43d0 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -7,6 +7,7 @@ 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 @@ public static Result CreateResult(Query query, SearchResult result) 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 @@ internal static Result CreateFolderResult(string title, string subtitle, string { FilePath = path, }, + PreviewPanel = new Lazy(() => new PreviewPanel(Settings, path, ResultType.Folder)), Action = c => { if (c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Alt) @@ -163,12 +167,22 @@ internal static Result CreateFolderResult(string title, string subtitle, string }, 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 @@ internal static Result CreateDriveSpaceDisplayResult(string path, string actionK 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 @@ internal static Result CreateDriveSpaceDisplayResult(string path, string actionK 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 @@ internal static Result CreateOpenCurrentFolderResult(string path, string actionK 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 @@ internal static Result CreateFileResult(string filePath, Query query, int score 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 @@ internal static Result CreateFileResult(string filePath, Query query, int score { 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 @@ internal static Result CreateFileResult(string filePath, Query query, int score } else { - OpenFile(filePath, Settings.UseLocationAsWorkingDir ? Path.GetDirectoryName(filePath) : string.Empty); + OpenFile(filePath, Settings.UseLocationAsWorkingDir ? directory : string.Empty); } } catch (Exception ex) @@ -318,7 +336,7 @@ internal static Result CreateFileResult(string filePath, Query query, int score 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 @@ private static void IncrementEverythingRunCounterIfNeeded(string fileOrFolder) _ = 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 12df6c1458e..f4f87d4d4be 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -246,6 +246,18 @@ private async Task> PathSearchAsync(Query query, CancellationToken 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 4f83fc72e5b..672e81d0386 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 @@ public class Settings 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 @@ public enum ContentIndexSearchEngineOption 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 745032d2c3d..87438a881d0 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 @@ internal ActionKeywordModel(Settings.ActionKeyword actionKeyword, string descrip 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 @@ public string Keyword 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 fb33dacab01..7292697ce33 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.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 @@ public string PreviewPanelTimeFormat "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 @@ public void AppendLink(string containerName, AccessLink link) } [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 @@ private void EditLink(object commandParameter) 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 @@ private void AddLink(object commandParameter) 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 @@ public int MaxResult #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 @@ public Visibility FastSortWarningVisibility } } } + public string SortOptionWarningMessage { get @@ -568,15 +638,15 @@ public string SortOptionWarningMessage // 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 10ce28fe4ed..829a2feedd3 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 @@ public string ActionKeyword { // 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 @@ private void TextBox_Pasting(object sender, DataObjectPastingEventArgs e) 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 e24b21dcd32..00000000000 --- 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 a8097d74811..08abc3ba6d0 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 e1a957199a5..4dd0588ee60 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 static readonly string ClassName = nameof(PreviewPanel); + + 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 ImageSource PreviewImage - { - get => _previewImage; - private set - { - _previewImage = value; - OnPropertyChanged(); - } - } + private Settings Settings { get; } public Visibility FileSizeVisibility => Settings.ShowFileSizeInPreviewPanel ? Visibility.Visible @@ -50,53 +55,256 @@ private set ? 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}", + CreatedAt = type == ResultType.File ? + GetFileCreatedAt(filePath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel) : + GetFolderCreatedAt(filePath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel); + } + + if (Settings.ShowModifiedDateInPreviewPanel) + { + LastModifiedAt = type == ResultType.File ? + GetFileLastModifiedAt(filePath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel) : + GetFolderLastModifiedAt(filePath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel); + } + + _ = LoadImageAsync(); + } + + private async Task LoadImageAsync() + { + 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 ); - string result = formattedDate; - if (Settings.ShowFileAgeInPreviewPanel) result = $"{GetFileAge(createdDate)} - {formattedDate}"; - CreatedAt = result; + 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"); } + } - if (Settings.ShowModifiedDateInPreviewPanel) + public static string GetFileLastModifiedAt(string filePath, string previewPanelDateFormat, string previewPanelTimeFormat, bool showFileAgeInPreviewPanel) + { + try { - DateTime lastModifiedDate = File.GetLastWriteTime(filePath); - string formattedDate = lastModifiedDate.ToString( - $"{Settings.PreviewPanelDateFormat} {Settings.PreviewPanelTimeFormat}", + var lastModifiedDate = File.GetLastWriteTime(filePath); + var formattedDate = lastModifiedDate.ToString( + $"{previewPanelDateFormat} {previewPanelTimeFormat}", CultureInfo.CurrentCulture ); - string result = formattedDate; - if (Settings.ShowFileAgeInPreviewPanel) result = $"{GetFileAge(lastModifiedDate)} - {formattedDate}"; - LastModifiedAt = result; + + 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"); + } + } - _ = LoadImageAsync(); + 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"); + } } - private async Task LoadImageAsync() + public static string GetFolderCreatedAt(string folderPath, string previewPanelDateFormat, string previewPanelTimeFormat, bool showFileAgeInPreviewPanel) { - PreviewImage = await Main.Context.API.LoadImageAsync(FilePath, true).ConfigureAwait(false); + 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 @@ private static string GetFileAge(DateTime fileDateTime) 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 00000000000..e977a72c26e --- /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 00000000000..e6294b98b65 --- /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 1e662de9ef9..d8db0abe19e 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 00000000000..893948d3dcc --- /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 f7f4d8d7654..b2ffe60c20e 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 8ff41a7add9..6abc1a58014 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 c7ef7780198..ea8b9e14283 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 b6a3a6cbcb5..09a7c8c5e77 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 00000000000..1005a5677d4 --- /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 ed9aaf4b32c..ab87b5817fb 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 0c501b2d9c9..2da97ebbd4c 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 00000000000..0a7176d2c4f --- /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 0a7176d2c4f..c9a761d7020 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 3867829058f..cea34f7dc57 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs @@ -163,23 +163,20 @@ public unsafe string TryGetProcessFilename(Process p) 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 99c1a12e9b3..da2b19d7c86 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 00000000000..fae1abedbc0 --- /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 e75dd0fc946..1316d3fd436 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 29158b2cefb..1f7c91244a9 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 d2884599467..7c30c0c96c2 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -6,7 +6,6 @@ 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 @@ public async Task> QueryAsync(Query query, CancellationToken token) .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 @@ static void MoveFile(string sourcePath, string destinationPath) } } - // 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 @@ public static void StartProcess(Func runProcess, Proc 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 fac3ab181e7..5c2f6f9762d 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs @@ -27,28 +27,36 @@ public static unsafe string GetLocalizedName(string path) // 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 c2f4574a94a..9a8326e9aa6 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs @@ -425,7 +425,7 @@ public Result Result(string query, IPublicAPI api) } } - if (!matchResult.IsSearchPrecisionScoreMet()) + if (!matchResult.IsSearchPrecisionScoreMet() && !string.IsNullOrEmpty(query)) return null; var result = new Result @@ -462,14 +462,13 @@ public Result Result(string query, IPublicAPI api) 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 a87b002d414..7aca8f3b6a7 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -136,7 +136,7 @@ public Result Result(string query, IPublicAPI api) 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 c7ea7cdd550..5c3475133b1 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 39bd105a5d9..5bb385c3135 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 dfe9e7d4faf..fe5d30d7e28 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 90eb4931716..a013f3b5a53 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 dcfc82e0a6e..8e2078c70cf 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 645a0e14fd7..2fb9c6b67be 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 3fc6b8ba4e1..7deafcc6253 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 a3f97eac4fd..d3fcb11f7c6 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 54118e0f365..de0741c6e2b 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 1a8ebbb7fc8..d44e96df479 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 506f204469b..d4886323a61 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 781227267d7..440d33697df 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 b661e836b15..8a69bea7436 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 b00b78d6fc3..b86c82844bc 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 a429524ac36..069026a2283 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 0ec1f92fb36..bdc84917b32 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 a2e65a741c8..c39c23b540d 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 66f7aec85ea..804a89611ac 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 d3975bd9e6d..99b525bbf61 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 b1bf88a3165..1af5a882bbc 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 00000000000..a013f3b5a53 --- /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 90eb4931716..a013f3b5a53 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 d421a84a412..6fc5724f684 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 d4747478445..cdd81d5b96b 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 d21412a14eb..86a7673b023 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 0362bf6fc8f..5a25b37d9c6 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 8936c0498aa..02eb35336af 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 a51aadec7df..a86b96800c2 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 @@ private void Execute(Func startProcess, ProcessStartI } 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 dbc36ad424b..8e54e1894ff 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 ccc50678e5a..ac45e013e07 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 de35c959232..6af81c3a542 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 91230e7e3ee..4a910b38173 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 794e949ad71..469928a2979 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 ad3f8553bab..56899eef30c 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 ac4040dcad0..7fa6fdb7bee 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 5f3688ab811..0db2d60d538 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 5419bd8e2a9..3a15b69a43f 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 b02a89a0a7b..caf82072c7d 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 be31e4e5210..f1d11c15682 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 7d131d94426..398c39c9fb4 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 873e71d4433..909b4a40b7b 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 072fd623d44..4072a745412 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 9d1d540763e..e24a4b6f7df 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 33cee56d81e..8532d4a991e 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 a19ab39d630..fb0fde7306b 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 9e0e3906650..6021c96ff53 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 796cda69d62..69746e8368c 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 087ff9f0593..f4823dc577f 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 00000000000..2d2c039c32c --- /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 f5a2f1b306e..6490848e6e6 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 18f7f63f5bb..b8dcccbe270 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 c82be249adc..dd44f71fd95 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 dae12c501eb..3f2e031009e 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 d59b7714523..90666a59b51 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 573aefcbd62..3ab9c8a44a1 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 39bf4965494..77278a0545c 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs @@ -5,8 +5,6 @@ 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 @@ public class Main : IPlugin, ISettingProvider, IPluginI18n 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 @@ public List Query(Query query) 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 @@ private static unsafe bool EnableShutdownPrivilege() } } - private List Commands() + private List Commands(Query query) { var results = new List(); var recycleBinFolder = "shell:RecycleBinFolder"; @@ -332,11 +338,9 @@ private List Commands() 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 @@ private List Commands() 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 @@ private List Commands() 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 @@ private List Commands() 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 @@ private List Commands() 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 @@ private List Commands() 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 6d338733e31..fdfe03224c1 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 00000000000..4187310217a --- /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 b8ae6a93c34..fc76ee12eb6 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 03516636d43..9fa52c8da54 100644 --- a/Plugins/Flow.Launcher.Plugin.Url/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Url/Main.cs @@ -70,7 +70,7 @@ public List Query(Query query) } 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 73726ab37ab..42176376b8f 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 cefb5d1d1a8..edb44f8f445 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 ca98581c39b..2e49f49652e 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 b1113acb79a..7bc145a685a 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 2a7dca59652..41a798d44a9 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 21f836bec6c..c6a74a0470a 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 3ce22bb7809..f170a8d737e 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 7f14b59c6b3..5c956d8f84e 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 f04cfb48afb..3c9003ebfc1 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 78ee7ca7db1..583f63bdeee 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 db2a4dfeb56..f250ecf3b19 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 4112f41ff91..16c5fb3e988 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 c8f069ca754..9b81987799c 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 9f793c43fd2..93c48b54531 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 a18710324d6..25187d0b603 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 05ee397778f..bc32a1b7ed2 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 6f0d7fcc961..a581e91b275 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 1a2476a2c5f..22be076cbaa 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 1fd9aca96a9..67435b5b9b8 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 1afcdb3600d..355ce984fe9 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 00000000000..cf24862c7c1 --- /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 06707b4af17..695c88c3017 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 b37070d9340..1a9fdbf032f 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 51e1efc6eba..72cfb23c23a 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 731275c5ef3..fb99024ecfa 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 7b8a72d6bef..fff4a2c8d7d 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 727b2f4a99a..9ce6c88dba1 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 0040cffa7da..b9b8a0b19f5 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 @@ public async Task> QueryAsync(Query query, CancellationToken token) 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 @@ private async Task> SuggestionsAsync(string keyword, string 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 9eedd29a3bb..bfd95c242b2 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 @@ public class SearchSource : BaseModel /// 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 @@ public SearchSource DeepCopy() 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 746c9cf848f..c6f9b27f36f 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 f0e5ac32744..0c0ac4b8453 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs @@ -191,7 +191,19 @@ public Settings() [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 @@ public string Suggestion } } } - - 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 1ce9b70b4a0..33461e9e747 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 e53f4ec7517..71dc6ece71c 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml.cs @@ -94,7 +94,8 @@ private void SortByColumn(object sender, RoutedEventArgs e) 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 533b894b8dc..a10ea6f3366 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 73fcd9f83eb..879cea6f8c7 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 5b0a57759e7..d87cd75cbd8 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 00000000000..53715bf2339 --- /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 49c4b783929..d920e7550a1 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 9d801e0d49c..c30e6dacc11 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 4cd8e059e39..fe6ce32f48a 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 8d753bc8c83..fa12150cc7c 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 e54852d321b..8d2d14a8054 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 39e2a114c7c..911e3042333 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 1ee79d7a20e..44db98b24d2 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.*", + "version": "9.0.*", "rollForward": "latestPatch" } }