Merge branch 'dev' into fix_loading_uwp
32
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
name: "\U0001F41E Bug report"
|
||||
about: Create a bug report to help us improve Flow Launcher
|
||||
title: "[Describe Your Bug]"
|
||||
labels: 'bug'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug/issue**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. ...
|
||||
2. ...
|
||||
3. ...
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Your System**
|
||||
```
|
||||
Windows build number: (run "ver" at a command prompt)
|
||||
Flow Launcher version: (Settings => About)
|
||||
```
|
||||
**Flow Launcher Error Log**
|
||||
<!--
|
||||
The latest log file can be found here: %AppData%\FlowLauncher\Logs\<version>\<date>.txt
|
||||
or for portable mode: %LocalAppData%\FlowLauncher\<App-Version>\UserData\Logs\<version>\<date>.txt
|
||||
Drag and drop the log file below this comment.
|
||||
-->
|
||||
21
.github/ISSUE_TEMPLATE/code-review.md
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
name: "\U0001F50E Code Review"
|
||||
about: Describe (bad) code that needs to be improved
|
||||
title: "[Describe Problematic Code]"
|
||||
labels: 'code review'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Point to the corresponding file and line number(s) which could be improved.**
|
||||
|
||||
Click on the line number in GitHub to create an anchored link to the corresponding code section. For example:
|
||||
|
||||
|
||||
https://github.com/Flow-Launcher/Flow.Launcher/blob/master/Flow.Launcher/Storage/TopMostRecord.cs#L8
|
||||
|
||||
Provide your alternative implementation idea:
|
||||
|
||||
```
|
||||
Code
|
||||
```
|
||||
14
.github/ISSUE_TEMPLATE/discussion.md
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
name: "⁉️ Question/Discussion"
|
||||
about: Create a report to help us improve
|
||||
title: "[Ask a question or propose a change]"
|
||||
labels: 'question/discussion'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Question**
|
||||
Describe your question.
|
||||
|
||||
**Discussion**
|
||||
Discuss a topic.
|
||||
24
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
name: "⭐ Feature request"
|
||||
about: Suggest a new idea or a feature enhacement
|
||||
title: "[Describe Your Feature]"
|
||||
labels: 'enhancement'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
|
||||
Add any other context or screenshots about the feature request here.
|
||||
6
.gitignore
vendored
|
|
@ -300,4 +300,8 @@ migrateToAutomaticPackageRestore.ps1
|
|||
*.pyc
|
||||
*.diagsession
|
||||
Output-Performance.txt
|
||||
*.diff
|
||||
*.diff
|
||||
|
||||
# vscode
|
||||
.vscode
|
||||
.history
|
||||
15
Directory.Build.targets
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<Project>
|
||||
<Target Name="ExcludePluginProjectReferenceOutput"
|
||||
AfterTargets="AssignProjectConfiguration"
|
||||
BeforeTargets="ResolveProjectReferences"
|
||||
Condition="'$(OutputType)' == 'Library' and '$(CopyLocalLockFileAssemblies)' == 'true' and $(AssemblyName.EndsWith('Tests')) == 'false' ">
|
||||
<ItemGroup>
|
||||
<ProjectReferenceWithConfiguration Update="@(ProjectReferenceWithConfiguration)" >
|
||||
<Private>false</Private>
|
||||
</ProjectReferenceWithConfiguration>
|
||||
<ProjectReference Update="@(ProjectReference)" >
|
||||
<Private>false</Private>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
BIN
Doc/Default Icons/app_missing_img.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
Doc/Default Icons/app_missing_img_01.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
44
Doc/Default Icons/app_missing_img_01.svg
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
Doc/Default Icons/app_missing_img_02.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
44
Doc/Default Icons/app_missing_img_02.svg
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
Doc/Default Icons/app_missing_img_03.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
44
Doc/Default Icons/app_missing_img_03.svg
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
Doc/Default Icons/app_missing_img_buttons.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
48
Doc/Default Icons/app_missing_img_buttons.svg
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
Doc/Default Icons/app_missing_img_fluent.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
49
Doc/Default Icons/app_missing_img_fluent.svg
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
Doc/Default Icons/app_missing_img_huge.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
42
Doc/Default Icons/app_missing_img_huge.svg
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
Doc/Default Icons/app_missing_img_lightblue.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
38
Doc/Default Icons/app_missing_img_lightblue.svg
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
Doc/Default Icons/app_missing_img_lightblue_buttons.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
42
Doc/Default Icons/app_missing_img_lightblue_buttons.svg
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
Doc/Default Icons/app_missing_img_minimal.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
40
Doc/Default Icons/app_missing_img_minimal.svg
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
Doc/Default Icons/app_missing_img_minimal_buttons.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
42
Doc/Default Icons/app_missing_img_minimal_buttons.svg
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
Doc/Logo/app_error.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
Doc/Logo/logo.ico
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
Doc/Logo/logo.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
1
Doc/Logo/logo.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 382.6 382.5"><defs><linearGradient id="a" x2="249.9" y1="191.3" y2="191.3" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#1b9de2"/><stop offset=".2" stop-color="#1595df"/><stop offset=".7" stop-color="#0680d7"/><stop offset="1" stop-color="#0078d4"/></linearGradient><linearGradient id="b" x1="697.3" x2="809.9" y1="499.2" y2="499.2" gradientTransform="rotate(49.2 809.6 -269.1)" xlink:href="#a"/><linearGradient id="c" x1="143.6" x2="393.5" y1="249.8" y2="249.8" gradientTransform="rotate(180 263.1 220.5)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#0c59a4"/><stop offset="1" stop-color="#114a8b"/></linearGradient></defs><path fill="url(#a)" d="M117 382.5H0v-189C0 90.7 84 0 186.9 0h63v120.4h-54.6c-46.8 0-78 31.2-78 78l-.1 184.1z"/><path fill="url(#b)" d="M249.9 182.6l.2-46.8h-54.7c-39 0-62.4 23.4-62.4 62.4l-.2 47 54.6-.1c39 0 62.5-23.4 62.5-62.5z"/><path fill="url(#c)" d="M265.5 0h117v189c0 102.8-84 193.5-186.8 193.5h-63V262.1h54.6c46.9 0 78.1-31.2 78.1-78L265.5 0z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Doc/Logo/logo128.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
Doc/Logo/logo16.png
Normal file
|
After Width: | Height: | Size: 409 B |
BIN
Doc/Logo/logo256.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
Doc/Logo/logo32.png
Normal file
|
After Width: | Height: | Size: 663 B |
BIN
Doc/Logo/logo48.png
Normal file
|
After Width: | Height: | Size: 1,019 B |
BIN
Doc/Logo/logo512.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
Doc/Logo/logo64.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Doc/Logo/resources/flow-header-landscape-transparent.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
Doc/Logo/resources/flow-header-landscape.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
Doc/Logo/resources/flow-header-square-transparent.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
Doc/Logo/resources/flow-header-square.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
2249
Doc/Logo/resources/flow-logo.ai
Normal file
2436
Doc/Logo/resources/preview.pdf
Normal file
BIN
Doc/app.ico
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 114 KiB |
BIN
Doc/app.png
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 15 KiB |
BIN
Doc/app_missing_img.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
|
|
@ -32,10 +32,9 @@ namespace Flow.Launcher.Core.Configuration
|
|||
try
|
||||
{
|
||||
MoveUserDataFolder(DataLocation.PortableDataPath, DataLocation.RoamingDataPath);
|
||||
#if DEBUG
|
||||
#if !DEBUG
|
||||
// Create shortcuts and uninstaller are not required in debug mode,
|
||||
// otherwise will repoint the path of the actual installed production version to the debug version
|
||||
#else
|
||||
CreateShortcuts();
|
||||
CreateUninstallerEntry();
|
||||
#endif
|
||||
|
|
@ -48,10 +47,7 @@ namespace Flow.Launcher.Core.Configuration
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
#if !DEBUG
|
||||
Log.Exception("Portable", "Error occured while disabling portable mode", e);
|
||||
#endif
|
||||
throw;
|
||||
Log.Exception("|Portable.DisablePortableMode|Error occured while disabling portable mode", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -60,10 +56,9 @@ namespace Flow.Launcher.Core.Configuration
|
|||
try
|
||||
{
|
||||
MoveUserDataFolder(DataLocation.RoamingDataPath, DataLocation.PortableDataPath);
|
||||
#if DEBUG
|
||||
#if !DEBUG
|
||||
// Remove shortcuts and uninstaller are not required in debug mode,
|
||||
// otherwise will delete the actual installed production version
|
||||
#else
|
||||
RemoveShortcuts();
|
||||
RemoveUninstallerEntry();
|
||||
#endif
|
||||
|
|
@ -76,10 +71,7 @@ namespace Flow.Launcher.Core.Configuration
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
#if !DEBUG
|
||||
Log.Exception("Portable", "Error occured while enabling portable mode", e);
|
||||
#endif
|
||||
throw;
|
||||
Log.Exception("|Portable.EnablePortableMode|Error occured while enabling portable mode", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -103,7 +95,7 @@ namespace Flow.Launcher.Core.Configuration
|
|||
|
||||
public void MoveUserDataFolder(string fromLocation, string toLocation)
|
||||
{
|
||||
FilesFolders.Copy(fromLocation, toLocation);
|
||||
FilesFolders.CopyAll(fromLocation, toLocation);
|
||||
VerifyUserDataAfterMove(fromLocation, toLocation);
|
||||
}
|
||||
|
||||
|
|
@ -125,14 +117,13 @@ namespace Flow.Launcher.Core.Configuration
|
|||
public void CreateUninstallerEntry()
|
||||
{
|
||||
var uninstallRegSubKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall";
|
||||
// NB: Sometimes the Uninstall key doesn't exist
|
||||
using (var parentKey =
|
||||
RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default)
|
||||
.CreateSubKey("Uninstall", RegistryKeyPermissionCheck.ReadWriteSubTree)) {; }
|
||||
|
||||
var key = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default)
|
||||
.CreateSubKey(uninstallRegSubKey + "\\" + Constant.FlowLauncher, RegistryKeyPermissionCheck.ReadWriteSubTree);
|
||||
key.SetValue("DisplayIcon", Constant.ApplicationDirectory + "\\app.ico", RegistryValueKind.String);
|
||||
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default))
|
||||
using (var subKey1 = baseKey.CreateSubKey(uninstallRegSubKey, RegistryKeyPermissionCheck.ReadWriteSubTree))
|
||||
using (var subKey2 = subKey1.CreateSubKey(Constant.FlowLauncher, RegistryKeyPermissionCheck.ReadWriteSubTree))
|
||||
{
|
||||
subKey2.SetValue("DisplayIcon", Path.Combine(Constant.ApplicationDirectory, "app.ico"), RegistryValueKind.String);
|
||||
}
|
||||
|
||||
using (var portabilityUpdater = NewUpdateManager())
|
||||
{
|
||||
|
|
@ -142,7 +133,10 @@ namespace Flow.Launcher.Core.Configuration
|
|||
|
||||
internal void IndicateDeletion(string filePathTodelete)
|
||||
{
|
||||
using (StreamWriter sw = File.CreateText(filePathTodelete + "\\" + DataLocation.DeletionIndicatorFile)){}
|
||||
var deleteFilePath = Path.Combine(filePathTodelete, DataLocation.DeletionIndicatorFile);
|
||||
using (var _ = File.CreateText(deleteFilePath))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
///<summary>
|
||||
|
|
@ -152,21 +146,18 @@ namespace Flow.Launcher.Core.Configuration
|
|||
public void PreStartCleanUpAfterPortabilityUpdate()
|
||||
{
|
||||
// Specify here so this method does not rely on other environment variables to initialise
|
||||
var portableDataPath = Path.Combine(Directory.GetParent(Assembly.GetExecutingAssembly().Location.NonNull()).ToString(), "UserData");
|
||||
var roamingDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FlowLauncher");
|
||||
var portableDataDir = Path.Combine(Directory.GetParent(Assembly.GetExecutingAssembly().Location.NonNull()).ToString(), "UserData");
|
||||
var roamingDataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FlowLauncher");
|
||||
|
||||
bool DataLocationPortableDeleteRequired = false;
|
||||
bool DataLocationRoamingDeleteRequired = false;
|
||||
// Get full path to the .dead files for each case
|
||||
var portableDataDeleteFilePath = Path.Combine(portableDataDir, DataLocation.DeletionIndicatorFile);
|
||||
var roamingDataDeleteFilePath = Path.Combine(roamingDataDir, DataLocation.DeletionIndicatorFile);
|
||||
|
||||
if ((roamingDataPath + "\\" + DataLocation.DeletionIndicatorFile).FileExits())
|
||||
DataLocationRoamingDeleteRequired = true;
|
||||
|
||||
if ((portableDataPath + "\\" + DataLocation.DeletionIndicatorFile).FileExits())
|
||||
DataLocationPortableDeleteRequired = true;
|
||||
|
||||
if (DataLocationRoamingDeleteRequired)
|
||||
// If the data folder in %appdata% is marked for deletion,
|
||||
// delete it and prompt the user to pick the portable data location
|
||||
if (File.Exists(roamingDataDeleteFilePath))
|
||||
{
|
||||
FilesFolders.RemoveFolderIfExists(roamingDataPath);
|
||||
FilesFolders.RemoveFolderIfExists(roamingDataDir);
|
||||
|
||||
if (MessageBox.Show("Flow Launcher has detected you enabled portable mode, " +
|
||||
"would you like to move it to a different location?", string.Empty,
|
||||
|
|
@ -176,18 +167,15 @@ namespace Flow.Launcher.Core.Configuration
|
|||
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(DataLocationPortableDeleteRequired)
|
||||
// Otherwise, if the portable data folder is marked for deletion,
|
||||
// delete it and notify the user about it.
|
||||
else if (File.Exists(portableDataDeleteFilePath))
|
||||
{
|
||||
FilesFolders.RemoveFolderIfExists(portableDataPath);
|
||||
FilesFolders.RemoveFolderIfExists(portableDataDir);
|
||||
|
||||
MessageBox.Show("Flow Launcher has detected you disabled portable mode, " +
|
||||
"the relevant shortcuts and uninstaller entry have been created");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -196,7 +184,7 @@ namespace Flow.Launcher.Core.Configuration
|
|||
var roamingLocationExists = DataLocation.RoamingDataPath.LocationExists();
|
||||
var portableLocationExists = DataLocation.PortableDataPath.LocationExists();
|
||||
|
||||
if(roamingLocationExists && portableLocationExists)
|
||||
if (roamingLocationExists && portableLocationExists)
|
||||
{
|
||||
MessageBox.Show(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 occured.",
|
||||
|
|
|
|||
|
|
@ -54,12 +54,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FSharp.Core" Version="4.7.1" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="squirrel.windows" Version="1.5.2" />
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="2.5.13" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.2.0" />
|
||||
<PackageReference Include="System.Runtime" Version="4.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
57
Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
using Flow.Launcher.Infrastructure;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
internal class PluginAssemblyLoader : AssemblyLoadContext
|
||||
{
|
||||
private readonly AssemblyDependencyResolver dependencyResolver;
|
||||
|
||||
private readonly AssemblyDependencyResolver referencedPluginPackageDependencyResolver;
|
||||
|
||||
private readonly AssemblyName assemblyName;
|
||||
|
||||
internal PluginAssemblyLoader(string assemblyFilePath)
|
||||
{
|
||||
dependencyResolver = new AssemblyDependencyResolver(assemblyFilePath);
|
||||
assemblyName = new AssemblyName(Path.GetFileNameWithoutExtension(assemblyFilePath));
|
||||
|
||||
referencedPluginPackageDependencyResolver =
|
||||
new AssemblyDependencyResolver(Path.Combine(Constant.ProgramDirectory, "Flow.Launcher.Plugin.dll"));
|
||||
}
|
||||
|
||||
internal Assembly LoadAssemblyAndDependencies()
|
||||
{
|
||||
return LoadFromAssemblyName(assemblyName);
|
||||
}
|
||||
|
||||
protected override Assembly Load(AssemblyName assemblyName)
|
||||
{
|
||||
string assemblyPath = dependencyResolver.ResolveAssemblyToPath(assemblyName);
|
||||
|
||||
// When resolving dependencies, ignore assembly depenedencies that already exits with Flow.Launcher.Plugin
|
||||
// Otherwise will get unexpected behaviour with plugins, e.g. JsonIgnore attribute not honored in WebSearch or other plugins
|
||||
// that use Newtonsoft.Json
|
||||
if (assemblyPath == null || ExistsInReferencedPluginPackage(assemblyName))
|
||||
return null;
|
||||
|
||||
return LoadFromAssemblyPath(assemblyPath);
|
||||
}
|
||||
|
||||
internal Type FromAssemblyGetTypeOfInterface(Assembly assembly, Type type)
|
||||
{
|
||||
var allTypes = assembly.ExportedTypes;
|
||||
|
||||
return allTypes.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(type));
|
||||
}
|
||||
|
||||
internal bool ExistsInReferencedPluginPackage(AssemblyName assemblyName)
|
||||
{
|
||||
return referencedPluginPackageDependencyResolver.ResolveAssemblyToPath(assemblyName) != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,9 +2,8 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Flow.Launcher.Infrastructure.Exception;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
|
|
@ -13,24 +12,16 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
internal abstract class PluginConfig
|
||||
{
|
||||
private const string PluginConfigName = "plugin.json";
|
||||
private static readonly List<PluginMetadata> PluginMetadatas = new List<PluginMetadata>();
|
||||
|
||||
/// <summary>
|
||||
/// Parse plugin metadata in giving directories
|
||||
/// Parse plugin metadata in the given directories
|
||||
/// </summary>
|
||||
/// <param name="pluginDirectories"></param>
|
||||
/// <returns></returns>
|
||||
public static List<PluginMetadata> Parse(string[] pluginDirectories)
|
||||
{
|
||||
PluginMetadatas.Clear();
|
||||
var allPluginMetadata = new List<PluginMetadata>();
|
||||
var directories = pluginDirectories.SelectMany(Directory.GetDirectories);
|
||||
ParsePluginConfigs(directories);
|
||||
return PluginMetadatas;
|
||||
}
|
||||
|
||||
private static void ParsePluginConfigs(IEnumerable<string> directories)
|
||||
{
|
||||
// todo use linq when diable plugin is implmented since parallel.foreach + list is not thread saft
|
||||
foreach (var directory in directories)
|
||||
{
|
||||
|
|
@ -50,15 +41,17 @@ namespace Flow.Launcher.Core.Plugin
|
|||
PluginMetadata metadata = GetPluginMetadata(directory);
|
||||
if (metadata != null)
|
||||
{
|
||||
PluginMetadatas.Add(metadata);
|
||||
allPluginMetadata.Add(metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allPluginMetadata;
|
||||
}
|
||||
|
||||
private static PluginMetadata GetPluginMetadata(string pluginDirectory)
|
||||
{
|
||||
string configPath = Path.Combine(pluginDirectory, PluginConfigName);
|
||||
string configPath = Path.Combine(pluginDirectory, Constant.PluginMetadataFileName);
|
||||
if (!File.Exists(configPath))
|
||||
{
|
||||
Log.Error($"|PluginConfig.GetPluginMetadata|Didn't find config file <{configPath}>");
|
||||
|
|
@ -81,7 +74,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
if (!AllowedLanguage.IsAllowed(metadata.Language))
|
||||
{
|
||||
Log.Error($"|PluginConfig.GetPluginMetadata|Invalid language <{metadata.Language}> for config <{configPath}>");
|
||||
|
|
|
|||
|
|
@ -1,202 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using Newtonsoft.Json;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
internal class PluginInstaller
|
||||
{
|
||||
internal static void Install(string path)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
string tempFoler = Path.Combine(Path.GetTempPath(), "flowlauncher\\plugins");
|
||||
if (Directory.Exists(tempFoler))
|
||||
{
|
||||
Directory.Delete(tempFoler, true);
|
||||
}
|
||||
UnZip(path, tempFoler, true);
|
||||
|
||||
string iniPath = Path.Combine(tempFoler, "plugin.json");
|
||||
if (!File.Exists(iniPath))
|
||||
{
|
||||
MessageBox.Show("Install failed: plugin config is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
PluginMetadata plugin = GetMetadataFromJson(tempFoler);
|
||||
if (plugin == null || plugin.Name == null)
|
||||
{
|
||||
MessageBox.Show("Install failed: plugin config is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
string pluginFolerPath = Infrastructure.UserSettings.DataLocation.PluginsDirectory;
|
||||
|
||||
string newPluginName = plugin.Name
|
||||
.Replace("/", "_")
|
||||
.Replace("\\", "_")
|
||||
.Replace(":", "_")
|
||||
.Replace("<", "_")
|
||||
.Replace(">", "_")
|
||||
.Replace("?", "_")
|
||||
.Replace("*", "_")
|
||||
.Replace("|", "_")
|
||||
+ "-" + Guid.NewGuid();
|
||||
string newPluginPath = Path.Combine(pluginFolerPath, newPluginName);
|
||||
string content = $"Do you want to install following plugin?{Environment.NewLine}{Environment.NewLine}" +
|
||||
$"Name: {plugin.Name}{Environment.NewLine}" +
|
||||
$"Version: {plugin.Version}{Environment.NewLine}" +
|
||||
$"Author: {plugin.Author}";
|
||||
PluginPair existingPlugin = PluginManager.GetPluginForId(plugin.ID);
|
||||
|
||||
if (existingPlugin != null)
|
||||
{
|
||||
content = $"Do you want to update following plugin?{Environment.NewLine}{Environment.NewLine}" +
|
||||
$"Name: {plugin.Name}{Environment.NewLine}" +
|
||||
$"Old Version: {existingPlugin.Metadata.Version}" +
|
||||
$"{Environment.NewLine}New Version: {plugin.Version}" +
|
||||
$"{Environment.NewLine}Author: {plugin.Author}";
|
||||
}
|
||||
|
||||
var result = MessageBox.Show(content, "Install plugin", MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||||
if (result == MessageBoxResult.Yes)
|
||||
{
|
||||
if (existingPlugin != null && Directory.Exists(existingPlugin.Metadata.PluginDirectory))
|
||||
{
|
||||
//when plugin is in use, we can't delete them. That's why we need to make plugin folder a random name
|
||||
File.Create(Path.Combine(existingPlugin.Metadata.PluginDirectory, "NeedDelete.txt")).Close();
|
||||
}
|
||||
|
||||
UnZip(path, newPluginPath, true);
|
||||
Directory.Delete(tempFoler, true);
|
||||
|
||||
//exsiting plugins may be has loaded by application,
|
||||
//if we try to delelte those kind of plugins, we will get a error that indicate the
|
||||
//file is been used now.
|
||||
//current solution is to restart Flow Launcher. Ugly.
|
||||
//if (MainWindow.Initialized)
|
||||
//{
|
||||
// Plugins.Initialize();
|
||||
//}
|
||||
if (MessageBox.Show($"You have installed plugin {plugin.Name} successfully.{Environment.NewLine}" +
|
||||
"Restart Flow Launcher to take effect?",
|
||||
"Install plugin", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
|
||||
{
|
||||
PluginManager.API.RestartApp();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static PluginMetadata GetMetadataFromJson(string pluginDirectory)
|
||||
{
|
||||
string configPath = Path.Combine(pluginDirectory, "plugin.json");
|
||||
PluginMetadata metadata;
|
||||
|
||||
if (!File.Exists(configPath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
metadata = JsonConvert.DeserializeObject<PluginMetadata>(File.ReadAllText(configPath));
|
||||
metadata.PluginDirectory = pluginDirectory;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
string error = $"Parse plugin config {configPath} failed: json format is not valid";
|
||||
#if (DEBUG)
|
||||
{
|
||||
throw new Exception(error);
|
||||
}
|
||||
#endif
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
if (!AllowedLanguage.IsAllowed(metadata.Language))
|
||||
{
|
||||
string error = $"Parse plugin config {configPath} failed: invalid language {metadata.Language}";
|
||||
#if (DEBUG)
|
||||
{
|
||||
throw new Exception(error);
|
||||
}
|
||||
#endif
|
||||
return null;
|
||||
}
|
||||
if (!File.Exists(metadata.ExecuteFilePath))
|
||||
{
|
||||
string error = $"Parse plugin config {configPath} failed: ExecuteFile {metadata.ExecuteFilePath} didn't exist";
|
||||
#if (DEBUG)
|
||||
{
|
||||
throw new Exception(error);
|
||||
}
|
||||
#endif
|
||||
return null;
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// unzip
|
||||
/// </summary>
|
||||
/// <param name="zipedFile">The ziped file.</param>
|
||||
/// <param name="strDirectory">The STR directory.</param>
|
||||
/// <param name="overWrite">overwirte</param>
|
||||
private static void UnZip(string zipedFile, string strDirectory, bool overWrite)
|
||||
{
|
||||
if (strDirectory == "")
|
||||
strDirectory = Directory.GetCurrentDirectory();
|
||||
if (!strDirectory.EndsWith("\\"))
|
||||
strDirectory = strDirectory + "\\";
|
||||
|
||||
using (ZipInputStream s = new ZipInputStream(File.OpenRead(zipedFile)))
|
||||
{
|
||||
ZipEntry theEntry;
|
||||
|
||||
while ((theEntry = s.GetNextEntry()) != null)
|
||||
{
|
||||
string directoryName = "";
|
||||
string pathToZip = "";
|
||||
pathToZip = theEntry.Name;
|
||||
|
||||
if (pathToZip != "")
|
||||
directoryName = Path.GetDirectoryName(pathToZip) + "\\";
|
||||
|
||||
string fileName = Path.GetFileName(pathToZip);
|
||||
|
||||
Directory.CreateDirectory(strDirectory + directoryName);
|
||||
|
||||
if (fileName != "")
|
||||
{
|
||||
if ((File.Exists(strDirectory + directoryName + fileName) && overWrite) || (!File.Exists(strDirectory + directoryName + fileName)))
|
||||
{
|
||||
using (FileStream streamWriter = File.Create(strDirectory + directoryName + fileName))
|
||||
{
|
||||
byte[] data = new byte[2048];
|
||||
while (true)
|
||||
{
|
||||
int size = s.Read(data, 0, data.Length);
|
||||
|
||||
if (size > 0)
|
||||
streamWriter.Write(data, 0, size);
|
||||
else
|
||||
break;
|
||||
}
|
||||
streamWriter.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,10 +19,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
{
|
||||
private static IEnumerable<PluginPair> _contextMenuPlugins;
|
||||
|
||||
/// <summary>
|
||||
/// Directories that will hold Flow Launcher plugin directory
|
||||
/// </summary>
|
||||
|
||||
public static List<PluginPair> AllPlugins { get; private set; }
|
||||
public static readonly List<PluginPair> GlobalPlugins = new List<PluginPair>();
|
||||
public static readonly Dictionary<string, PluginPair> NonGlobalPlugins = new Dictionary<string, PluginPair>();
|
||||
|
|
@ -32,27 +28,18 @@ namespace Flow.Launcher.Core.Plugin
|
|||
// todo happlebao, this should not be public, the indicator function should be embeded
|
||||
public static PluginsSettings Settings;
|
||||
private static List<PluginMetadata> _metadatas;
|
||||
private static readonly string[] Directories = { Constant.PreinstalledDirectory, DataLocation.PluginsDirectory };
|
||||
|
||||
private static void ValidateUserDirectory()
|
||||
{
|
||||
if (!Directory.Exists(DataLocation.PluginsDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(DataLocation.PluginsDirectory);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Directories that will hold Flow Launcher plugin directory
|
||||
/// </summary>
|
||||
private static readonly string[] Directories = { Constant.PreinstalledDirectory, DataLocation.PluginsDirectory };
|
||||
|
||||
private static void DeletePythonBinding()
|
||||
{
|
||||
const string binding = "flowlauncher.py";
|
||||
var directory = DataLocation.PluginsDirectory;
|
||||
foreach (var subDirectory in Directory.GetDirectories(directory))
|
||||
foreach (var subDirectory in Directory.GetDirectories(DataLocation.PluginsDirectory))
|
||||
{
|
||||
var path = Path.Combine(subDirectory, binding);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
File.Delete(Path.Combine(subDirectory, binding));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +63,8 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
static PluginManager()
|
||||
{
|
||||
ValidateUserDirectory();
|
||||
// validate user directory
|
||||
Directory.CreateDirectory(DataLocation.PluginsDirectory);
|
||||
// force old plugins use new python binding
|
||||
DeletePythonBinding();
|
||||
}
|
||||
|
|
@ -132,9 +120,10 @@ namespace Flow.Launcher.Core.Plugin
|
|||
GlobalPlugins.Add(plugin);
|
||||
|
||||
// Plugins may have multiple ActionKeywords, eg. WebSearch
|
||||
plugin.Metadata.ActionKeywords.Where(x => x != Query.GlobalPluginWildcardSign)
|
||||
.ToList()
|
||||
.ForEach(x => NonGlobalPlugins[x] = plugin);
|
||||
plugin.Metadata.ActionKeywords
|
||||
.Where(x => x != Query.GlobalPluginWildcardSign)
|
||||
.ToList()
|
||||
.ForEach(x => NonGlobalPlugins[x] = plugin);
|
||||
}
|
||||
|
||||
if (failedPlugins.Any())
|
||||
|
|
@ -144,11 +133,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
}
|
||||
|
||||
public static void InstallPlugin(string path)
|
||||
{
|
||||
PluginInstaller.Install(path);
|
||||
}
|
||||
|
||||
public static List<PluginPair> ValidPluginsForQuery(Query query)
|
||||
{
|
||||
if (NonGlobalPlugins.ContainsKey(query.ActionKeyword))
|
||||
|
|
@ -164,9 +148,9 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
public static List<Result> QueryForPlugin(PluginPair pair, Query query)
|
||||
{
|
||||
var results = new List<Result>();
|
||||
try
|
||||
{
|
||||
List<Result> results = null;
|
||||
var metadata = pair.Metadata;
|
||||
var milliseconds = Stopwatch.Debug($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}", () =>
|
||||
{
|
||||
|
|
@ -175,13 +159,12 @@ namespace Flow.Launcher.Core.Plugin
|
|||
});
|
||||
metadata.QueryCount += 1;
|
||||
metadata.AvgQueryTime = metadata.QueryCount == 1 ? milliseconds : (metadata.AvgQueryTime + milliseconds) / 2;
|
||||
return results;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>", e);
|
||||
return new List<Result>();
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public static void UpdatePluginMetadata(List<Result> results, PluginMetadata metadata, Query query)
|
||||
|
|
@ -221,47 +204,34 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
public static List<Result> GetContextMenusForPlugin(Result result)
|
||||
{
|
||||
var results = new List<Result>();
|
||||
var pluginPair = _contextMenuPlugins.FirstOrDefault(o => o.Metadata.ID == result.PluginID);
|
||||
if (pluginPair != null)
|
||||
{
|
||||
var metadata = pluginPair.Metadata;
|
||||
var plugin = (IContextMenu)pluginPair.Plugin;
|
||||
|
||||
try
|
||||
{
|
||||
var results = plugin.LoadContextMenus(result);
|
||||
results = plugin.LoadContextMenus(result);
|
||||
foreach (var r in results)
|
||||
{
|
||||
r.PluginDirectory = metadata.PluginDirectory;
|
||||
r.PluginID = metadata.ID;
|
||||
r.PluginDirectory = pluginPair.Metadata.PluginDirectory;
|
||||
r.PluginID = pluginPair.Metadata.ID;
|
||||
r.OriginQuery = result.OriginQuery;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginManager.GetContextMenusForPlugin|Can't load context menus for plugin <{metadata.Name}>", e);
|
||||
return new List<Result>();
|
||||
Log.Exception($"|PluginManager.GetContextMenusForPlugin|Can't load context menus for plugin <{pluginPair.Metadata.Name}>", e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new List<Result>();
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public static bool ActionKeywordRegistered(string actionKeyword)
|
||||
{
|
||||
if (actionKeyword != Query.GlobalPluginWildcardSign &&
|
||||
NonGlobalPlugins.ContainsKey(actionKeyword))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return actionKeyword != Query.GlobalPluginWildcardSign
|
||||
&& NonGlobalPlugins.ContainsKey(actionKeyword);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -299,7 +269,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
GlobalPlugins.Remove(plugin);
|
||||
}
|
||||
|
||||
if(oldActionkeyword != Query.GlobalPluginWildcardSign)
|
||||
if (oldActionkeyword != Query.GlobalPluginWildcardSign)
|
||||
NonGlobalPlugins.Remove(oldActionkeyword);
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
public static List<PluginPair> Plugins(List<PluginMetadata> metadatas, PluginsSettings settings)
|
||||
{
|
||||
var dotnetPlugins = DotNetPlugins(metadatas).ToList();
|
||||
var dotnetPlugins = DotNetPlugins(metadatas);
|
||||
var pythonPlugins = PythonPlugins(metadatas, settings.PythonDirectory);
|
||||
var executablePlugins = ExecutablePlugins(metadatas);
|
||||
var plugins = dotnetPlugins.Concat(pythonPlugins).Concat(executablePlugins).ToList();
|
||||
|
|
@ -41,80 +41,63 @@ namespace Flow.Launcher.Core.Plugin
|
|||
{
|
||||
|
||||
#if DEBUG
|
||||
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(metadata.ExecuteFilePath);
|
||||
var types = assembly.GetTypes();
|
||||
var type = types.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin)));
|
||||
var assemblyLoader = new PluginAssemblyLoader(metadata.ExecuteFilePath);
|
||||
var assembly = assemblyLoader.LoadAssemblyAndDependencies();
|
||||
var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin));
|
||||
var plugin = (IPlugin)Activator.CreateInstance(type);
|
||||
#else
|
||||
Assembly assembly;
|
||||
Assembly assembly = null;
|
||||
IPlugin plugin = null;
|
||||
|
||||
try
|
||||
{
|
||||
assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(metadata.ExecuteFilePath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
erroredPlugins.Add(metadata.Name);
|
||||
var assemblyLoader = new PluginAssemblyLoader(metadata.ExecuteFilePath);
|
||||
assembly = assemblyLoader.LoadAssemblyAndDependencies();
|
||||
|
||||
var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin));
|
||||
|
||||
plugin = (IPlugin)Activator.CreateInstance(type);
|
||||
}
|
||||
catch (Exception e) when (assembly == null)
|
||||
{
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for the plugin: {metadata.Name}", e);
|
||||
return;
|
||||
}
|
||||
|
||||
Type type;
|
||||
try
|
||||
{
|
||||
var types = assembly.GetTypes();
|
||||
|
||||
type = types.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin)));
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
erroredPlugins.Add(metadata.Name);
|
||||
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e);
|
||||
return;
|
||||
}
|
||||
catch (ReflectionTypeLoadException e)
|
||||
{
|
||||
erroredPlugins.Add(metadata.Name);
|
||||
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e);
|
||||
return;
|
||||
}
|
||||
|
||||
IPlugin plugin;
|
||||
try
|
||||
{
|
||||
plugin = (IPlugin)Activator.CreateInstance(type);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
erroredPlugins.Add(metadata.Name);
|
||||
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|The following plugin has errored and can not be loaded: <{metadata.Name}>", e);
|
||||
}
|
||||
|
||||
if (plugin == null)
|
||||
{
|
||||
erroredPlugins.Add(metadata.Name);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
PluginPair pair = new PluginPair
|
||||
plugins.Add(new PluginPair
|
||||
{
|
||||
Plugin = plugin,
|
||||
Metadata = metadata
|
||||
};
|
||||
plugins.Add(pair);
|
||||
});
|
||||
});
|
||||
metadata.InitTime += milliseconds;
|
||||
|
||||
}
|
||||
|
||||
if (erroredPlugins.Count > 0)
|
||||
{
|
||||
var errorPluginString = "";
|
||||
var errorPluginString = String.Join(Environment.NewLine, erroredPlugins);
|
||||
|
||||
var errorMessage = "The following "
|
||||
+ (erroredPlugins.Count > 1 ? "plugins have " : "plugin has ")
|
||||
+ "errored and cannot be loaded:";
|
||||
|
||||
erroredPlugins.ForEach(x => errorPluginString += x + Environment.NewLine);
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
MessageBox.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" +
|
||||
|
|
@ -127,65 +110,74 @@ namespace Flow.Launcher.Core.Plugin
|
|||
return plugins;
|
||||
}
|
||||
|
||||
public static IEnumerable<PluginPair> PythonPlugins(List<PluginMetadata> source, string pythonDirecotry)
|
||||
public static IEnumerable<PluginPair> PythonPlugins(List<PluginMetadata> source, string pythonDirectory)
|
||||
{
|
||||
var metadatas = source.Where(o => o.Language.ToUpper() == AllowedLanguage.Python);
|
||||
string filename;
|
||||
|
||||
if (string.IsNullOrEmpty(pythonDirecotry))
|
||||
// try to set Constant.PythonPath, either from
|
||||
// PATH or from the given pythonDirectory
|
||||
if (string.IsNullOrEmpty(pythonDirectory))
|
||||
{
|
||||
var paths = Environment.GetEnvironmentVariable(PATH);
|
||||
if (paths != null)
|
||||
{
|
||||
var pythonPaths = paths.Split(';').Where(p => p.ToLower().Contains(Python));
|
||||
if (pythonPaths.Any())
|
||||
var pythonInPath = paths
|
||||
.Split(';')
|
||||
.Where(p => p.ToLower().Contains(Python))
|
||||
.Any();
|
||||
|
||||
if (pythonInPath)
|
||||
{
|
||||
filename = PythonExecutable;
|
||||
Constant.PythonPath = PythonExecutable;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("|PluginsLoader.PythonPlugins|Python can't be found in PATH.");
|
||||
return new List<PluginPair>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("|PluginsLoader.PythonPlugins|PATH environment variable is not set.");
|
||||
return new List<PluginPair>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var path = Path.Combine(pythonDirecotry, PythonExecutable);
|
||||
var path = Path.Combine(pythonDirectory, PythonExecutable);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
filename = path;
|
||||
Constant.PythonPath = path;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("|PluginsLoader.PythonPlugins|Can't find python executable in <b ");
|
||||
return new List<PluginPair>();
|
||||
Log.Error($"|PluginsLoader.PythonPlugins|Can't find python executable in {path}");
|
||||
}
|
||||
}
|
||||
Constant.PythonPath = filename;
|
||||
var plugins = metadatas.Select(metadata => new PluginPair
|
||||
|
||||
// if we have a path to the python executable,
|
||||
// load every python plugin pair.
|
||||
if (String.IsNullOrEmpty(Constant.PythonPath))
|
||||
{
|
||||
Plugin = new PythonPlugin(filename),
|
||||
Metadata = metadata
|
||||
});
|
||||
return plugins;
|
||||
return new List<PluginPair>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return source
|
||||
.Where(o => o.Language.ToUpper() == AllowedLanguage.Python)
|
||||
.Select(metadata => new PluginPair
|
||||
{
|
||||
Plugin = new PythonPlugin(Constant.PythonPath),
|
||||
Metadata = metadata
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<PluginPair> ExecutablePlugins(IEnumerable<PluginMetadata> source)
|
||||
{
|
||||
var metadatas = source.Where(o => o.Language.ToUpper() == AllowedLanguage.Executable);
|
||||
|
||||
var plugins = metadatas.Select(metadata => new PluginPair
|
||||
{
|
||||
Plugin = new ExecutablePlugin(metadata.ExecuteFilePath),
|
||||
Metadata = metadata
|
||||
});
|
||||
return plugins;
|
||||
return source
|
||||
.Where(o => o.Language.ToUpper() == AllowedLanguage.Executable)
|
||||
.Select(metadata => new PluginPair
|
||||
{
|
||||
Plugin = new ExecutablePlugin(metadata.ExecuteFilePath),
|
||||
Metadata = metadata
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
else
|
||||
{ // non action keyword
|
||||
actionKeyword = string.Empty;
|
||||
actionParameters = terms.ToList();
|
||||
search = rawQuery;
|
||||
}
|
||||
|
||||
|
|
@ -38,10 +37,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
Terms = terms,
|
||||
RawQuery = rawQuery,
|
||||
ActionKeyword = actionKeyword,
|
||||
Search = search,
|
||||
// Obsolete value initialisation
|
||||
ActionName = actionKeyword,
|
||||
ActionParameters = actionParameters
|
||||
Search = search
|
||||
};
|
||||
|
||||
return query;
|
||||
|
|
|
|||
|
|
@ -88,7 +88,6 @@ namespace Flow.Launcher.Core.Resource
|
|||
{
|
||||
language = language.NonNull();
|
||||
|
||||
Settings.Language = language.LanguageCode;
|
||||
|
||||
RemoveOldLanguageFiles();
|
||||
if (language != AvailableLanguages.English)
|
||||
|
|
@ -96,6 +95,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
LoadLanguage(language);
|
||||
}
|
||||
UpdatePluginMetadataTranslations();
|
||||
Settings.Language = language.LanguageCode;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
private ResourceDictionary _oldResource;
|
||||
private string _oldTheme;
|
||||
public Settings Settings { get; set; }
|
||||
private const string Folder = "Themes";
|
||||
private const string Folder = Constant.Themes;
|
||||
private const string Extension = ".xaml";
|
||||
private string DirectoryPath => Path.Combine(Constant.ProgramDirectory, Folder);
|
||||
private string UserDirectoryPath => Path.Combine(DataLocation.DataDirectory(), Folder);
|
||||
|
|
@ -137,18 +137,31 @@ namespace Flow.Launcher.Core.Resource
|
|||
var dict = CurrentThemeResourceDictionary();
|
||||
|
||||
Style queryBoxStyle = dict["QueryBoxStyle"] as Style;
|
||||
if (queryBoxStyle != null)
|
||||
Style querySuggestionBoxStyle = dict["QuerySuggestionBoxStyle"] as Style;
|
||||
|
||||
if (queryBoxStyle != null && querySuggestionBoxStyle != null)
|
||||
{
|
||||
queryBoxStyle.Setters.Add(new Setter(TextBox.FontFamilyProperty, new FontFamily(Settings.QueryBoxFont)));
|
||||
queryBoxStyle.Setters.Add(new Setter(TextBox.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(Settings.QueryBoxFontStyle)));
|
||||
queryBoxStyle.Setters.Add(new Setter(TextBox.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(Settings.QueryBoxFontWeight)));
|
||||
queryBoxStyle.Setters.Add(new Setter(TextBox.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(Settings.QueryBoxFontStretch)));
|
||||
var fontFamily = new FontFamily(Settings.QueryBoxFont);
|
||||
var fontStyle = FontHelper.GetFontStyleFromInvariantStringOrNormal(Settings.QueryBoxFontStyle);
|
||||
var fontWeight = FontHelper.GetFontWeightFromInvariantStringOrNormal(Settings.QueryBoxFontWeight);
|
||||
var fontStretch = FontHelper.GetFontStretchFromInvariantStringOrNormal(Settings.QueryBoxFontStretch);
|
||||
|
||||
queryBoxStyle.Setters.Add(new Setter(TextBox.FontFamilyProperty, fontFamily));
|
||||
queryBoxStyle.Setters.Add(new Setter(TextBox.FontStyleProperty, fontStyle));
|
||||
queryBoxStyle.Setters.Add(new Setter(TextBox.FontWeightProperty, fontWeight));
|
||||
queryBoxStyle.Setters.Add(new Setter(TextBox.FontStretchProperty, fontStretch));
|
||||
|
||||
var caretBrushPropertyValue = queryBoxStyle.Setters.OfType<Setter>().Any(x => x.Property.Name == "CaretBrush");
|
||||
var foregroundPropertyValue = queryBoxStyle.Setters.OfType<Setter>().Where(x => x.Property.Name == "Foreground")
|
||||
.Select(x => x.Value).FirstOrDefault();
|
||||
if (!caretBrushPropertyValue && foregroundPropertyValue != null) //otherwise BaseQueryBoxStyle will handle styling
|
||||
queryBoxStyle.Setters.Add(new Setter(TextBox.CaretBrushProperty, foregroundPropertyValue));
|
||||
|
||||
// Query suggestion box's font style is aligned with query box
|
||||
querySuggestionBoxStyle.Setters.Add(new Setter(TextBox.FontFamilyProperty, fontFamily));
|
||||
querySuggestionBoxStyle.Setters.Add(new Setter(TextBox.FontStyleProperty, fontStyle));
|
||||
querySuggestionBoxStyle.Setters.Add(new Setter(TextBox.FontWeightProperty, fontWeight));
|
||||
querySuggestionBoxStyle.Setters.Add(new Setter(TextBox.FontStretchProperty, fontStretch));
|
||||
}
|
||||
|
||||
Style resultItemStyle = dict["ItemTitleStyle"] as Style;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ using Flow.Launcher.Infrastructure.Http;
|
|||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using System.IO;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Core
|
||||
{
|
||||
|
|
@ -28,11 +29,14 @@ namespace Flow.Launcher.Core
|
|||
GitHubRepository = gitHubRepository;
|
||||
}
|
||||
|
||||
public async Task UpdateApp(bool silentIfLatestVersion = true)
|
||||
public async Task UpdateApp(IPublicAPI api , bool silentUpdate = true)
|
||||
{
|
||||
UpdateManager updateManager;
|
||||
UpdateInfo newUpdateInfo;
|
||||
|
||||
if (!silentUpdate)
|
||||
api.ShowMsg("Please wait...", "Checking for new update");
|
||||
|
||||
try
|
||||
{
|
||||
updateManager = await GitHubUpdateManager(GitHubRepository);
|
||||
|
|
@ -62,12 +66,15 @@ namespace Flow.Launcher.Core
|
|||
|
||||
if (newReleaseVersion <= currentVersion)
|
||||
{
|
||||
if (!silentIfLatestVersion)
|
||||
if (!silentUpdate)
|
||||
MessageBox.Show("You already have the latest Flow Launcher version");
|
||||
updateManager.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!silentUpdate)
|
||||
api.ShowMsg("Update found", "Updating...");
|
||||
|
||||
try
|
||||
{
|
||||
await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply);
|
||||
|
|
@ -84,10 +91,10 @@ namespace Flow.Launcher.Core
|
|||
if (DataLocation.PortableDataLocationInUse())
|
||||
{
|
||||
var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}";
|
||||
FilesFolders.Copy(DataLocation.PortableDataPath, targetDestination);
|
||||
FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination);
|
||||
if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination))
|
||||
MessageBox.Show(string.Format("Flow Launcher was not able to move your user profile data to the new update version. Please manually" +
|
||||
"move your profile data folder from {0} to {1}", DataLocation.PortableDataPath, targetDestination));
|
||||
MessageBox.Show("Flow Launcher was not able to move your user profile data to the new update version. Please manually " +
|
||||
$"move your profile data folder from {DataLocation.PortableDataPath} to {targetDestination}");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -96,11 +103,15 @@ namespace Flow.Launcher.Core
|
|||
|
||||
var newVersionTips = NewVersinoTips(newReleaseVersion.ToString());
|
||||
|
||||
MessageBox.Show(newVersionTips);
|
||||
Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}");
|
||||
|
||||
// always dispose UpdateManager
|
||||
updateManager.Dispose();
|
||||
|
||||
if (MessageBox.Show(newVersionTips, "New Update", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||
{
|
||||
UpdateManager.RestartApp(Constant.ApplicationFileName);
|
||||
}
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 15 KiB |
|
|
@ -1,199 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using hyjiacan.util.p4n;
|
||||
using hyjiacan.util.p4n.format;
|
||||
using JetBrains.Annotations;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.Storage;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure
|
||||
{
|
||||
public interface IAlphabet
|
||||
{
|
||||
string Translate(string stringToTranslate);
|
||||
}
|
||||
|
||||
public class Alphabet : IAlphabet
|
||||
{
|
||||
private readonly HanyuPinyinOutputFormat Format = new HanyuPinyinOutputFormat();
|
||||
private ConcurrentDictionary<string, string[][]> PinyinCache;
|
||||
private BinaryStorage<Dictionary<string, string[][]>> _pinyinStorage;
|
||||
private Settings _settings;
|
||||
|
||||
public void Initialize([NotNull] Settings settings)
|
||||
{
|
||||
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
||||
InitializePinyinHelpers();
|
||||
}
|
||||
|
||||
private void InitializePinyinHelpers()
|
||||
{
|
||||
Format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
|
||||
|
||||
Stopwatch.Normal("|Flow Launcher.Infrastructure.Alphabet.Initialize|Preload pinyin cache", () =>
|
||||
{
|
||||
_pinyinStorage = new BinaryStorage<Dictionary<string, string[][]>>("Pinyin");
|
||||
|
||||
lock(_pinyinStorage)
|
||||
{
|
||||
var loaded = _pinyinStorage.TryLoad(new Dictionary<string, string[][]>());
|
||||
|
||||
PinyinCache = new ConcurrentDictionary<string, string[][]>(loaded);
|
||||
}
|
||||
|
||||
// force pinyin library static constructor initialize
|
||||
PinyinHelper.toHanyuPinyinStringArray('T', Format);
|
||||
});
|
||||
Log.Info($"|Flow Launcher.Infrastructure.Alphabet.Initialize|Number of preload pinyin combination<{PinyinCache.Count}>");
|
||||
}
|
||||
|
||||
public string Translate(string str)
|
||||
{
|
||||
return ConvertChineseCharactersToPinyin(str);
|
||||
}
|
||||
|
||||
public string ConvertChineseCharactersToPinyin(string source)
|
||||
{
|
||||
if (!_settings.ShouldUsePinyin)
|
||||
return source;
|
||||
|
||||
if (string.IsNullOrEmpty(source))
|
||||
return source;
|
||||
|
||||
if (!ContainsChinese(source))
|
||||
return source;
|
||||
|
||||
var combination = PinyinCombination(source);
|
||||
|
||||
var pinyinArray=combination.Select(x => string.Join("", x));
|
||||
var acronymArray = combination.Select(Acronym).Distinct();
|
||||
|
||||
var joinedSingleStringCombination = new StringBuilder();
|
||||
var all = acronymArray.Concat(pinyinArray);
|
||||
all.ToList().ForEach(x => joinedSingleStringCombination.Append(x));
|
||||
|
||||
return joinedSingleStringCombination.ToString();
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
if (!_settings.ShouldUsePinyin)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock(_pinyinStorage)
|
||||
{
|
||||
_pinyinStorage.Save(PinyinCache.ToDictionary(i => i.Key, i => i.Value));
|
||||
}
|
||||
}
|
||||
|
||||
private static string[] EmptyStringArray = new string[0];
|
||||
private static string[][] Empty2DStringArray = new string[0][];
|
||||
|
||||
[Obsolete("Not accurate, eg 音乐 will not return yinyue but returns yinle ")]
|
||||
/// <summary>
|
||||
/// replace chinese character with pinyin, non chinese character won't be modified
|
||||
/// <param name="word"> should be word or sentence, instead of single character. e.g. 微软 </param>
|
||||
/// </summary>
|
||||
public string[] Pinyin(string word)
|
||||
{
|
||||
if (!_settings.ShouldUsePinyin)
|
||||
{
|
||||
return EmptyStringArray;
|
||||
}
|
||||
|
||||
var pinyin = word.Select(c =>
|
||||
{
|
||||
var pinyins = PinyinHelper.toHanyuPinyinStringArray(c);
|
||||
var result = pinyins == null ? c.ToString() : pinyins[0];
|
||||
return result;
|
||||
}).ToArray();
|
||||
return pinyin;
|
||||
}
|
||||
|
||||
/// <summmary>
|
||||
/// replace chinese character with pinyin, non chinese character won't be modified
|
||||
/// Because we don't have words dictionary, so we can only return all possiblie pinyin combination
|
||||
/// e.g. 音乐 will return yinyue and yinle
|
||||
/// <param name="characters"> should be word or sentence, instead of single character. e.g. 微软 </param>
|
||||
/// </summmary>
|
||||
public string[][] PinyinCombination(string characters)
|
||||
{
|
||||
if (!_settings.ShouldUsePinyin || string.IsNullOrEmpty(characters))
|
||||
{
|
||||
return Empty2DStringArray;
|
||||
}
|
||||
|
||||
if (!PinyinCache.ContainsKey(characters))
|
||||
{
|
||||
var allPinyins = new List<string[]>();
|
||||
foreach (var c in characters)
|
||||
{
|
||||
var pinyins = PinyinHelper.toHanyuPinyinStringArray(c, Format);
|
||||
if (pinyins != null)
|
||||
{
|
||||
var r = pinyins.Distinct().ToArray();
|
||||
allPinyins.Add(r);
|
||||
}
|
||||
else
|
||||
{
|
||||
var r = new[] { c.ToString() };
|
||||
allPinyins.Add(r);
|
||||
}
|
||||
}
|
||||
|
||||
var combination = allPinyins.Aggregate(Combination).Select(c => c.Split(';')).ToArray();
|
||||
PinyinCache[characters] = combination;
|
||||
return combination;
|
||||
}
|
||||
else
|
||||
{
|
||||
return PinyinCache[characters];
|
||||
}
|
||||
}
|
||||
|
||||
public string Acronym(string[] pinyin)
|
||||
{
|
||||
var acronym = string.Join("", pinyin.Select(p => p[0]));
|
||||
return acronym;
|
||||
}
|
||||
|
||||
public bool ContainsChinese(string word)
|
||||
{
|
||||
if (!_settings.ShouldUsePinyin)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (word.Length > 40)
|
||||
{
|
||||
//Skip strings that are too long string for Pinyin conversion.
|
||||
return false;
|
||||
}
|
||||
|
||||
var chinese = word.Select(PinyinHelper.toHanyuPinyinStringArray)
|
||||
.Any(p => p != null);
|
||||
return chinese;
|
||||
}
|
||||
|
||||
private string[] Combination(string[] array1, string[] array2)
|
||||
{
|
||||
if (!_settings.ShouldUsePinyin)
|
||||
{
|
||||
return EmptyStringArray;
|
||||
}
|
||||
|
||||
var combination = (
|
||||
from a1 in array1
|
||||
from a2 in array2
|
||||
select $"{a1};{a2}"
|
||||
).ToArray();
|
||||
return combination;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ namespace Flow.Launcher.Infrastructure
|
|||
{
|
||||
public const string FlowLauncher = "Flow.Launcher";
|
||||
public const string Plugins = "Plugins";
|
||||
public const string PluginMetadataFileName = "plugin.json";
|
||||
|
||||
public const string ApplicationFileName = FlowLauncher + ".exe";
|
||||
|
||||
|
|
@ -22,14 +23,17 @@ namespace Flow.Launcher.Infrastructure
|
|||
public static readonly string Version = FileVersionInfo.GetVersionInfo(Assembly.Location.NonNull()).ProductVersion;
|
||||
|
||||
public static readonly int ThumbnailSize = 64;
|
||||
public static readonly string DefaultIcon = Path.Combine(ProgramDirectory, "Images", "app.png");
|
||||
public static readonly string ErrorIcon = Path.Combine(ProgramDirectory, "Images", "app_error.png");
|
||||
private static readonly string ImagesDirectory = Path.Combine(ProgramDirectory, "Images");
|
||||
public static readonly string DefaultIcon = Path.Combine(ImagesDirectory, "app.png");
|
||||
public static readonly string ErrorIcon = Path.Combine(ImagesDirectory, "app_error.png");
|
||||
public static readonly string MissingImgIcon = Path.Combine(ImagesDirectory, "app_missing_img.png");
|
||||
|
||||
public static string PythonPath;
|
||||
public static string EverythingSDKPath;
|
||||
|
||||
public static readonly string QueryTextBoxIconImagePath = $"{ProgramDirectory}\\Images\\mainsearch.png";
|
||||
|
||||
public const string DefaultTheme = "Darker";
|
||||
|
||||
public const string Themes = "Themes";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ namespace Flow.Launcher.Infrastructure.Exception
|
|||
sb.AppendLine($"* IntPtr Length: {IntPtr.Size}");
|
||||
sb.AppendLine($"* x64: {Environment.Is64BitOperatingSystem}");
|
||||
sb.AppendLine($"* Python Path: {Constant.PythonPath}");
|
||||
sb.AppendLine($"* Everything SDK Path: {Constant.EverythingSDKPath}");
|
||||
sb.AppendLine($"* CLR Version: {Environment.Version}");
|
||||
sb.AppendLine($"* Installed .NET Framework: ");
|
||||
foreach (var result in GetFrameworkVersionFromRegistry())
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\SolutionAssemblyInfo.cs" Link="Properties\SolutionAssemblyInfo.cs" />
|
||||
<None Include="FodyWeavers.xml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -48,13 +49,11 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="NLog.Schema" Version="4.7.0-rc1" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" Version="4.9.0" />
|
||||
<PackageReference Include="Pinyin4DotNet" Version="2016.4.23.4" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="4.7.0" />
|
||||
<PackageReference Include="System.Runtime" Version="4.3.1" />
|
||||
<PackageReference Include="ToolGood.Words.Pinyin" Version="3.0.1.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
4
Flow.Launcher.Infrastructure/FodyWeavers.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<PropertyChanged />
|
||||
</Weavers>
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure
|
||||
{
|
||||
[Obsolete("This class is obsolete and should not be used. Please use the static function StringMatcher.FuzzySearch")]
|
||||
public class FuzzyMatcher
|
||||
{
|
||||
private string query;
|
||||
private MatchOption opt;
|
||||
|
||||
private FuzzyMatcher(string query, MatchOption opt)
|
||||
{
|
||||
this.query = query.Trim();
|
||||
this.opt = opt;
|
||||
}
|
||||
|
||||
public static FuzzyMatcher Create(string query)
|
||||
{
|
||||
return new FuzzyMatcher(query, new MatchOption());
|
||||
}
|
||||
|
||||
public static FuzzyMatcher Create(string query, MatchOption opt)
|
||||
{
|
||||
return new FuzzyMatcher(query, opt);
|
||||
}
|
||||
|
||||
public MatchResult Evaluate(string str)
|
||||
{
|
||||
return StringMatcher.Instance.FuzzyMatch(query, str, opt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -66,18 +66,12 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
response = response.NonNull();
|
||||
var stream = response.GetResponseStream().NonNull();
|
||||
|
||||
using (var reader = new StreamReader(stream, Encoding.GetEncoding(encoding)))
|
||||
{
|
||||
var content = await reader.ReadToEndAsync();
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
return content;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new HttpRequestException($"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>");
|
||||
}
|
||||
}
|
||||
using var reader = new StreamReader(stream, Encoding.GetEncoding(encoding));
|
||||
var content = await reader.ReadToEndAsync();
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
throw new HttpRequestException($"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>");
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,44 +2,84 @@ using System;
|
|||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.Image
|
||||
{
|
||||
[Serializable]
|
||||
public class ImageUsage
|
||||
{
|
||||
|
||||
public int usage;
|
||||
public ImageSource imageSource;
|
||||
|
||||
public ImageUsage(int usage, ImageSource image)
|
||||
{
|
||||
this.usage = usage;
|
||||
imageSource = image;
|
||||
}
|
||||
}
|
||||
|
||||
public class ImageCache
|
||||
{
|
||||
private const int MaxCached = 5000;
|
||||
public ConcurrentDictionary<string, int> Usage = new ConcurrentDictionary<string, int>();
|
||||
private readonly ConcurrentDictionary<string, ImageSource> _data = new ConcurrentDictionary<string, ImageSource>();
|
||||
|
||||
private const int MaxCached = 50;
|
||||
public ConcurrentDictionary<string, ImageUsage> Data { get; private set; } = new ConcurrentDictionary<string, ImageUsage>();
|
||||
private const int permissibleFactor = 2;
|
||||
|
||||
public void Initialization(Dictionary<string, int> usage)
|
||||
{
|
||||
foreach (var key in usage.Keys)
|
||||
{
|
||||
Data[key] = new ImageUsage(usage[key], null);
|
||||
}
|
||||
}
|
||||
|
||||
public ImageSource this[string path]
|
||||
{
|
||||
get
|
||||
{
|
||||
Usage.AddOrUpdate(path, 1, (k, v) => v + 1);
|
||||
var i = _data[path];
|
||||
return i;
|
||||
}
|
||||
set { _data[path] = value; }
|
||||
}
|
||||
if (Data.TryGetValue(path, out var value))
|
||||
{
|
||||
value.usage++;
|
||||
return value.imageSource;
|
||||
}
|
||||
|
||||
public Dictionary<string, int> CleanupAndToDictionary()
|
||||
=> Usage
|
||||
.OrderByDescending(o => o.Value)
|
||||
.Take(MaxCached)
|
||||
.ToDictionary(i => i.Key, i => i.Value);
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
Data.AddOrUpdate(
|
||||
path,
|
||||
new ImageUsage(0, value),
|
||||
(k, v) =>
|
||||
{
|
||||
v.imageSource = value;
|
||||
v.usage++;
|
||||
return v;
|
||||
}
|
||||
);
|
||||
|
||||
// To prevent the dictionary from drastically increasing in size by caching images, the dictionary size is not allowed to grow more than the permissibleFactor * maxCached size
|
||||
// This is done so that we don't constantly perform this resizing operation and also maintain the image cache size at the same time
|
||||
if (Data.Count > permissibleFactor * MaxCached)
|
||||
{
|
||||
// To delete the images from the data dictionary based on the resizing of the Usage Dictionary.
|
||||
foreach (var key in Data.OrderBy(x => x.Value.usage).Take(Data.Count - MaxCached).Select(x => x.Key))
|
||||
Data.TryRemove(key, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsKey(string key)
|
||||
{
|
||||
var contains = _data.ContainsKey(key);
|
||||
var contains = Data.ContainsKey(key) && Data[key] != null;
|
||||
return contains;
|
||||
}
|
||||
|
||||
public int CacheSize()
|
||||
{
|
||||
return _data.Count;
|
||||
return Data.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -47,8 +87,7 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
/// </summary>
|
||||
public int UniqueImagesInCache()
|
||||
{
|
||||
return _data.Values.Distinct().Count();
|
||||
return Data.Values.Select(x => x.imageSource).Distinct().Count();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -13,12 +13,13 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
{
|
||||
public static class ImageLoader
|
||||
{
|
||||
private static readonly ImageCache _imageCache = new ImageCache();
|
||||
private static readonly ConcurrentDictionary<string, string> _guidToKey = new ConcurrentDictionary<string, string>();
|
||||
private static readonly bool _enableHashImage = true;
|
||||
|
||||
private static readonly ImageCache ImageCache = new ImageCache();
|
||||
private static BinaryStorage<Dictionary<string, int>> _storage;
|
||||
private static readonly ConcurrentDictionary<string, string> GuidToKey = new ConcurrentDictionary<string, string>();
|
||||
private static IImageHashGenerator _hashGenerator;
|
||||
private static bool EnableImageHash = true;
|
||||
public static ImageSource DefaultImage { get; } = new BitmapImage(new Uri(Constant.MissingImgIcon));
|
||||
|
||||
|
||||
private static readonly string[] ImageExtensions =
|
||||
{
|
||||
|
|
@ -36,25 +37,25 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
_storage = new BinaryStorage<Dictionary<string, int>>("Image");
|
||||
_hashGenerator = new ImageHashGenerator();
|
||||
|
||||
_imageCache.Usage = LoadStorageToConcurrentDictionary();
|
||||
var usage = LoadStorageToConcurrentDictionary();
|
||||
|
||||
foreach (var icon in new[] { Constant.DefaultIcon, Constant.ErrorIcon })
|
||||
foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon })
|
||||
{
|
||||
ImageSource img = new BitmapImage(new Uri(icon));
|
||||
img.Freeze();
|
||||
_imageCache[icon] = img;
|
||||
ImageCache[icon] = img;
|
||||
}
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
Stopwatch.Normal("|ImageLoader.Initialize|Preload images cost", () =>
|
||||
{
|
||||
_imageCache.Usage.AsParallel().ForAll(x =>
|
||||
ImageCache.Data.AsParallel().ForAll(x =>
|
||||
{
|
||||
Load(x.Key);
|
||||
});
|
||||
});
|
||||
Log.Info($"|ImageLoader.Initialize|Number of preload images is <{_imageCache.Usage.Count}>, Images Number: {_imageCache.CacheSize()}, Unique Items {_imageCache.UniqueImagesInCache()}");
|
||||
Log.Info($"|ImageLoader.Initialize|Number of preload images is <{ImageCache.CacheSize()}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -62,13 +63,13 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
{
|
||||
lock (_storage)
|
||||
{
|
||||
_storage.Save(_imageCache.CleanupAndToDictionary());
|
||||
_storage.Save(ImageCache.Data.Select(x => (x.Key, x.Value.usage)).ToDictionary(x => x.Key, x => x.usage));
|
||||
}
|
||||
}
|
||||
|
||||
private static ConcurrentDictionary<string, int> LoadStorageToConcurrentDictionary()
|
||||
{
|
||||
lock(_storage)
|
||||
lock (_storage)
|
||||
{
|
||||
var loaded = _storage.TryLoad(new Dictionary<string, int>());
|
||||
|
||||
|
|
@ -106,11 +107,11 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return new ImageResult(_imageCache[Constant.ErrorIcon], ImageType.Error);
|
||||
return new ImageResult(ImageCache[Constant.MissingImgIcon], ImageType.Error);
|
||||
}
|
||||
if (_imageCache.ContainsKey(path))
|
||||
if (ImageCache.ContainsKey(path))
|
||||
{
|
||||
return new ImageResult(_imageCache[path], ImageType.Cache);
|
||||
return new ImageResult(ImageCache[path], ImageType.Cache);
|
||||
}
|
||||
|
||||
if (path.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
|
||||
|
|
@ -139,8 +140,8 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path} on first try", e);
|
||||
Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path} on second try", e2);
|
||||
|
||||
ImageSource image = _imageCache[Constant.ErrorIcon];
|
||||
_imageCache[path] = image;
|
||||
ImageSource image = ImageCache[Constant.MissingImgIcon];
|
||||
ImageCache[path] = image;
|
||||
imageResult = new ImageResult(image, ImageType.Error);
|
||||
}
|
||||
}
|
||||
|
|
@ -156,9 +157,8 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
if (Directory.Exists(path))
|
||||
{
|
||||
/* Directories can also have thumbnails instead of shell icons.
|
||||
* Generating thumbnails for a bunch of folders while scrolling through
|
||||
* results from Everything makes a big impact on performance and
|
||||
* Flow.Launcher responsibility.
|
||||
* Generating thumbnails for a bunch of folder results while scrolling
|
||||
* could have a big impact on performance and Flow.Launcher responsibility.
|
||||
* - Solution: just load the icon
|
||||
*/
|
||||
type = ImageType.Folder;
|
||||
|
|
@ -192,8 +192,8 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
}
|
||||
else
|
||||
{
|
||||
image = _imageCache[Constant.ErrorIcon];
|
||||
path = Constant.ErrorIcon;
|
||||
image = ImageCache[Constant.MissingImgIcon];
|
||||
path = Constant.MissingImgIcon;
|
||||
}
|
||||
|
||||
if (type != ImageType.Error)
|
||||
|
|
@ -213,33 +213,37 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
option);
|
||||
}
|
||||
|
||||
public static bool CacheContainImage(string path)
|
||||
{
|
||||
return ImageCache.ContainsKey(path) && ImageCache[path] != null;
|
||||
}
|
||||
|
||||
public static ImageSource Load(string path, bool loadFullImage = false)
|
||||
{
|
||||
var imageResult = LoadInternal(path, loadFullImage);
|
||||
|
||||
var img = imageResult.ImageSource;
|
||||
if (imageResult.ImageType != ImageType.Error && imageResult.ImageType != ImageType.Cache)
|
||||
{
|
||||
// we need to get image hash
|
||||
string hash = _enableHashImage ? _hashGenerator.GetHashFromImage(img) : null;
|
||||
{ // we need to get image hash
|
||||
string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null;
|
||||
if (hash != null)
|
||||
{
|
||||
if (_guidToKey.TryGetValue(hash, out string key))
|
||||
{
|
||||
// image already exists
|
||||
img = _imageCache[key];
|
||||
|
||||
if (GuidToKey.TryGetValue(hash, out string key))
|
||||
{ // image already exists
|
||||
img = ImageCache[key] ?? img;
|
||||
}
|
||||
else
|
||||
{
|
||||
// new guid
|
||||
_guidToKey[hash] = path;
|
||||
{ // new guid
|
||||
GuidToKey[hash] = path;
|
||||
}
|
||||
}
|
||||
|
||||
// update cache
|
||||
_imageCache[path] = img;
|
||||
ImageCache[path] = img;
|
||||
}
|
||||
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
|
|
|
|||
85
Flow.Launcher.Infrastructure/PinyinAlphabet.cs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using ToolGood.Words.Pinyin;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure
|
||||
{
|
||||
public interface IAlphabet
|
||||
{
|
||||
string Translate(string stringToTranslate);
|
||||
}
|
||||
|
||||
public class PinyinAlphabet : IAlphabet
|
||||
{
|
||||
private ConcurrentDictionary<string, string> _pinyinCache = new ConcurrentDictionary<string, string>();
|
||||
private Settings _settings;
|
||||
|
||||
public void Initialize([NotNull] Settings settings)
|
||||
{
|
||||
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
||||
}
|
||||
|
||||
public string Translate(string content)
|
||||
{
|
||||
if (_settings.ShouldUsePinyin)
|
||||
{
|
||||
if (!_pinyinCache.ContainsKey(content))
|
||||
{
|
||||
if (WordsHelper.HasChinese(content))
|
||||
{
|
||||
var resultList = WordsHelper.GetPinyinList(content);
|
||||
|
||||
StringBuilder resultBuilder = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < resultList.Length; i++)
|
||||
{
|
||||
if (content[i] >= 0x3400 && content[i] <= 0x9FD5)
|
||||
resultBuilder.Append(resultList[i].First());
|
||||
}
|
||||
|
||||
resultBuilder.Append(' ');
|
||||
|
||||
bool pre = false;
|
||||
|
||||
for (int i = 0; i < resultList.Length; i++)
|
||||
{
|
||||
if (content[i] >= 0x3400 && content[i] <= 0x9FD5)
|
||||
{
|
||||
resultBuilder.Append(' ');
|
||||
resultBuilder.Append(resultList[i]);
|
||||
pre = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pre)
|
||||
{
|
||||
pre = false;
|
||||
resultBuilder.Append(' ');
|
||||
}
|
||||
resultBuilder.Append(resultList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return _pinyinCache[content] = resultBuilder.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return content;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return _pinyinCache[content];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,18 +21,6 @@ namespace Flow.Launcher.Infrastructure
|
|||
|
||||
public static StringMatcher Instance { get; internal set; }
|
||||
|
||||
[Obsolete("This method is obsolete and should not be used. Please use the static function StringMatcher.FuzzySearch")]
|
||||
public static int Score(string source, string target)
|
||||
{
|
||||
return FuzzySearch(target, source).Score;
|
||||
}
|
||||
|
||||
[Obsolete("This method is obsolete and should not be used. Please use the static function StringMatcher.FuzzySearch")]
|
||||
public static bool IsMatch(string source, string target)
|
||||
{
|
||||
return Score(source, target) > 0;
|
||||
}
|
||||
|
||||
public static MatchResult FuzzySearch(string query, string stringToCompare)
|
||||
{
|
||||
return Instance.FuzzyMatch(query, stringToCompare);
|
||||
|
|
@ -83,9 +71,18 @@ namespace Flow.Launcher.Infrastructure
|
|||
bool allSubstringsContainedInCompareString = true;
|
||||
|
||||
var indexList = new List<int>();
|
||||
List<int> spaceIndices = new List<int>();
|
||||
|
||||
for (var compareStringIndex = 0; compareStringIndex < fullStringToCompareWithoutCase.Length; compareStringIndex++)
|
||||
{
|
||||
|
||||
// To maintain a list of indices which correspond to spaces in the string to compare
|
||||
// To populate the list only for the first query substring
|
||||
if (fullStringToCompareWithoutCase[compareStringIndex].Equals(' ') && currentQuerySubstringIndex == 0)
|
||||
{
|
||||
spaceIndices.Add(compareStringIndex);
|
||||
}
|
||||
|
||||
if (fullStringToCompareWithoutCase[compareStringIndex] != currentQuerySubstring[currentQuerySubstringCharacterIndex])
|
||||
{
|
||||
matchFoundInPreviousLoop = false;
|
||||
|
|
@ -147,15 +144,31 @@ namespace Flow.Launcher.Infrastructure
|
|||
// proceed to calculate score if every char or substring without whitespaces matched
|
||||
if (allQuerySubstringsMatched)
|
||||
{
|
||||
var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex, lastMatchIndex - firstMatchIndex, allSubstringsContainedInCompareString);
|
||||
var nearestSpaceIndex = CalculateClosestSpaceIndex(spaceIndices, firstMatchIndex);
|
||||
var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1, lastMatchIndex - firstMatchIndex, allSubstringsContainedInCompareString);
|
||||
|
||||
return new MatchResult(true, UserSettingSearchPrecision, indexList, score);
|
||||
}
|
||||
|
||||
return new MatchResult (false, UserSettingSearchPrecision);
|
||||
return new MatchResult(false, UserSettingSearchPrecision);
|
||||
}
|
||||
|
||||
private static bool AllPreviousCharsMatched(int startIndexToVerify, int currentQuerySubstringCharacterIndex,
|
||||
// To get the index of the closest space which preceeds the first matching index
|
||||
private int CalculateClosestSpaceIndex(List<int> spaceIndices, int firstMatchIndex)
|
||||
{
|
||||
if (spaceIndices.Count == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int? ind = spaceIndices.OrderBy(item => (firstMatchIndex - item)).Where(item => firstMatchIndex > item).FirstOrDefault();
|
||||
int closestSpaceIndex = ind ?? -1;
|
||||
return closestSpaceIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool AllPreviousCharsMatched(int startIndexToVerify, int currentQuerySubstringCharacterIndex,
|
||||
string fullStringToCompareWithoutCase, string currentQuerySubstring)
|
||||
{
|
||||
var allMatch = true;
|
||||
|
|
@ -298,18 +311,6 @@ namespace Flow.Launcher.Infrastructure
|
|||
|
||||
public class MatchOption
|
||||
{
|
||||
/// <summary>
|
||||
/// prefix of match char, use for hightlight
|
||||
/// </summary>
|
||||
[Obsolete("this is never used")]
|
||||
public string Prefix { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// suffix of match char, use for hightlight
|
||||
/// </summary>
|
||||
[Obsolete("this is never used")]
|
||||
public string Suffix { get; set; } = "";
|
||||
|
||||
public bool IgnoreCase { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,5 +30,6 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
}
|
||||
|
||||
public static readonly string PluginsDirectory = Path.Combine(DataDirectory(), Constant.Plugins);
|
||||
public static readonly string PluginSettingsDirectory = Path.Combine(DataDirectory(), "Settings", Constant.Plugins);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,16 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
if (Plugins.ContainsKey(metadata.ID))
|
||||
{
|
||||
var settings = Plugins[metadata.ID];
|
||||
|
||||
// TODO: Remove. This is one off for 1.2.0 release.
|
||||
// Introduced a new action keyword in Explorer, so need to update plugin setting in the UserData folder.
|
||||
// This kind of plugin meta update should be handled by a dedicated method trigger by version bump.
|
||||
if (metadata.ID == "572be03c74c642baae319fc283e561a8" && metadata.ActionKeywords.Count != settings.ActionKeywords.Count)
|
||||
settings.ActionKeywords = metadata.ActionKeywords;
|
||||
|
||||
if (string.IsNullOrEmpty(settings.Version))
|
||||
settings.Version = metadata.Version;
|
||||
|
||||
if (settings.ActionKeywords?.Count > 0)
|
||||
{
|
||||
metadata.ActionKeywords = settings.ActionKeywords;
|
||||
|
|
@ -28,6 +38,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
{
|
||||
ID = metadata.ID,
|
||||
Name = metadata.Name,
|
||||
Version = metadata.Version,
|
||||
ActionKeywords = metadata.ActionKeywords,
|
||||
Disabled = metadata.Disabled
|
||||
};
|
||||
|
|
@ -39,6 +50,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
{
|
||||
public string ID { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Version { get; set; }
|
||||
public List<string> ActionKeywords { get; set; } // a reference of the action keywords from plugin manager
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -9,10 +9,18 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
{
|
||||
public class Settings : BaseModel
|
||||
{
|
||||
private string language = "en";
|
||||
|
||||
public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}";
|
||||
public string OpenResultModifiers { get; set; } = KeyConstant.Alt;
|
||||
public bool ShowOpenResultHotkey { get; set; } = true;
|
||||
public string Language { get; set; } = "en";
|
||||
public string Language
|
||||
{
|
||||
get => language; set {
|
||||
language = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
public string Theme { get; set; } = Constant.DefaultTheme;
|
||||
public bool UseDropShadowEffect { get; set; } = false;
|
||||
public string QueryBoxFont { get; set; } = FontFamily.GenericSansSerif.Name;
|
||||
|
|
@ -70,12 +78,6 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
public PluginsSettings PluginSettings { get; set; } = new PluginsSettings();
|
||||
public ObservableCollection<CustomPluginHotkey> CustomPluginHotkeys { get; set; } = new ObservableCollection<CustomPluginHotkey>();
|
||||
|
||||
[Obsolete]
|
||||
public double Opacity { get; set; } = 1;
|
||||
|
||||
[Obsolete]
|
||||
public OpacityMode OpacityMode { get; set; } = OpacityMode.Normal;
|
||||
|
||||
public bool DontPromptUpdateMsg { get; set; }
|
||||
public bool EnableUpdateLog { get; set; }
|
||||
|
||||
|
|
@ -108,12 +110,4 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
Empty,
|
||||
Preserved
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public enum OpacityMode
|
||||
{
|
||||
Normal = 0,
|
||||
LayeredWindow = 1,
|
||||
DWM = 2
|
||||
}
|
||||
}
|
||||
|
|
@ -11,23 +11,6 @@ namespace Flow.Launcher.Plugin
|
|||
List<Result> LoadContextMenus(Result selectedResult);
|
||||
}
|
||||
|
||||
[Obsolete("If a plugin has a action keyword, then it is exclusive. This interface will be remove in v1.3.0")]
|
||||
public interface IExclusiveQuery : IFeatures
|
||||
{
|
||||
[Obsolete("If a plugin has a action keyword, then it is exclusive. This method will be remove in v1.3.0")]
|
||||
bool IsExclusiveQuery(Query query);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represent plugin query will be executed in UI thread directly. Don't do long-running operation in Query method if you implement this interface
|
||||
/// <remarks>This will improve the performance of instant search like websearch or cmd plugin</remarks>
|
||||
/// </summary>
|
||||
[Obsolete("Flow Launcher is fast enough now, executed on ui thread is no longer needed")]
|
||||
public interface IInstantQuery : IFeatures
|
||||
{
|
||||
bool IsInstantQuery(string query);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represent plugins that support internationalization
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Features
|
||||
{
|
||||
[Obsolete("Delete Flow.Launcher.Plugin.Features using directive, " +
|
||||
"and use Flow.Launcher.Plugin.Feature.IContextMenu instead, " +
|
||||
"this method will be removed in v1.3.0")]
|
||||
public interface IContextMenu { }
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Features
|
||||
{
|
||||
[Obsolete("Delete Flow.Launcher.Plugin.Features using directive, " +
|
||||
"and use Flow.Launcher.Plugin.Feature.IInstantQuery instead, " +
|
||||
"this method will be removed in v1.3.0")]
|
||||
public interface IExclusiveQuery { }
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Features
|
||||
{
|
||||
[Obsolete("Delete Flow.Launcher.Plugin.Features using directive, " +
|
||||
"and use Flow.Launcher.Plugin.Feature.IInstantQuery instead, " +
|
||||
"this method will be removed in v1.3.0")]
|
||||
public interface IInstantQuery { }
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
|
|
@ -14,10 +14,10 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>1.0.0</Version>
|
||||
<PackageVersion>1.0.0-beta3</PackageVersion>
|
||||
<AssemblyVersion>1.0.0</AssemblyVersion>
|
||||
<FileVersion>1.0.0</FileVersion>
|
||||
<Version>1.3.0</Version>
|
||||
<PackageVersion>1.3.0</PackageVersion>
|
||||
<AssemblyVersion>1.3.0</AssemblyVersion>
|
||||
<FileVersion>1.3.0</FileVersion>
|
||||
<PackageId>Flow.Launcher.Plugin</PackageId>
|
||||
<Authors>Flow-Launcher</Authors>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
|
|
@ -60,12 +60,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" />
|
||||
<PackageReference Include="Mono.Cecil" Version="0.11.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="2.5.13" />
|
||||
<PackageReference Include="System.Runtime" Version="4.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -8,15 +8,6 @@ namespace Flow.Launcher.Plugin
|
|||
/// </summary>
|
||||
public interface IPublicAPI
|
||||
{
|
||||
/// <summary>
|
||||
/// Push result to query box
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <param name="plugin"></param>
|
||||
/// <param name="results"></param>
|
||||
[Obsolete("This method will be removed in Flow Launcher 1.3")]
|
||||
void PushResults(Query query, PluginMetadata plugin, List<Result> results);
|
||||
|
||||
/// <summary>
|
||||
/// Change Flow.Launcher query
|
||||
/// </summary>
|
||||
|
|
@ -27,42 +18,11 @@ namespace Flow.Launcher.Plugin
|
|||
/// </param>
|
||||
void ChangeQuery(string query, bool requery = false);
|
||||
|
||||
/// <summary>
|
||||
/// Just change the query text, this won't raise search
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
[Obsolete]
|
||||
void ChangeQueryText(string query, bool selectAll = false);
|
||||
|
||||
/// <summary>
|
||||
/// Close Flow Launcher
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
void CloseApp();
|
||||
|
||||
/// <summary>
|
||||
/// Restart Flow Launcher
|
||||
/// </summary>
|
||||
void RestartApp();
|
||||
|
||||
/// <summary>
|
||||
/// Restart Flow Launcher
|
||||
/// </summary>
|
||||
[Obsolete("Use RestartApp instead. This method will be removed in Flow Launcher 1.3")]
|
||||
void RestarApp();
|
||||
|
||||
/// <summary>
|
||||
/// Hide Flow Launcher
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
void HideApp();
|
||||
|
||||
/// <summary>
|
||||
/// Show Flow Launcher
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
void ShowApp();
|
||||
|
||||
/// <summary>
|
||||
/// Save all Flow Launcher settings
|
||||
/// </summary>
|
||||
|
|
@ -103,24 +63,6 @@ namespace Flow.Launcher.Plugin
|
|||
/// </summary>
|
||||
void OpenSettingDialog();
|
||||
|
||||
/// <summary>
|
||||
/// Show loading animation
|
||||
/// </summary>
|
||||
[Obsolete("automatically start")]
|
||||
void StartLoadingBar();
|
||||
|
||||
/// <summary>
|
||||
/// Stop loading animation
|
||||
/// </summary>
|
||||
[Obsolete("automatically stop")]
|
||||
void StopLoadingBar();
|
||||
|
||||
/// <summary>
|
||||
/// Install Flow Launcher plugin
|
||||
/// </summary>
|
||||
/// <param name="path">Plugin path (ends with .flowlauncher)</param>
|
||||
void InstallPlugin(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Get translation of current language
|
||||
/// You need to implement IPluginI18n if you want to support multiple languages for your plugin
|
||||
|
|
|
|||
|
|
@ -43,9 +43,6 @@ namespace Flow.Launcher.Plugin
|
|||
return Name;
|
||||
}
|
||||
|
||||
[Obsolete("Use IcoPath")]
|
||||
public string FullIcoPath => IcoPath;
|
||||
|
||||
/// <summary>
|
||||
/// Init time include both plugin load time and init time
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -94,14 +94,5 @@ namespace Flow.Launcher.Plugin
|
|||
}
|
||||
|
||||
public override string ToString() => RawQuery;
|
||||
|
||||
[Obsolete("Use ActionKeyword, this property will be removed in v1.3.0")]
|
||||
public string ActionName { get; internal set; }
|
||||
|
||||
[Obsolete("Use Search instead, this property will be removed in v1.3.0")]
|
||||
public List<string> ActionParameters { get; internal set; }
|
||||
|
||||
[Obsolete("Use Search instead, this method will be removed in v1.3.0")]
|
||||
public string GetAllRemainingParameter() => Search;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,21 +104,6 @@ namespace Flow.Launcher.Plugin
|
|||
return Title + SubTitle;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Context menus associate with this result
|
||||
/// </summary>
|
||||
[Obsolete("Use IContextMenu instead")]
|
||||
public List<Result> ContextMenu { get; set; }
|
||||
|
||||
[Obsolete("Use Object initializer instead")]
|
||||
public Result(string Title, string IcoPath, string SubTitle = null)
|
||||
{
|
||||
this.Title = Title;
|
||||
this.IcoPath = IcoPath;
|
||||
this.SubTitle = SubTitle;
|
||||
}
|
||||
|
||||
public Result() { }
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -130,5 +115,15 @@ namespace Flow.Launcher.Plugin
|
|||
/// Plugin ID that generated this result
|
||||
/// </summary>
|
||||
public string PluginID { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Show message as ToolTip on result Title hover over
|
||||
/// </summary>
|
||||
public string TitleToolTip { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Show message as ToolTip on result SubTitle hover over
|
||||
/// </summary>
|
||||
public string SubTitleToolTip { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,16 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
public static class FilesFolders
|
||||
{
|
||||
private const string FileExplorerProgramName = "explorer";
|
||||
public static void Copy(this string sourcePath, string targetPath)
|
||||
|
||||
private const string FileExplorerProgramEXE = "explorer.exe";
|
||||
|
||||
/// <summary>
|
||||
/// Copies the folder and all of its files and folders
|
||||
/// including subfolders to the target location
|
||||
/// </summary>
|
||||
/// <param name="sourcePath"></param>
|
||||
/// <param name="targetPath"></param>
|
||||
public static void CopyAll(this string sourcePath, string targetPath)
|
||||
{
|
||||
// Get the subdirectories for the specified directory.
|
||||
DirectoryInfo dir = new DirectoryInfo(sourcePath);
|
||||
|
|
@ -41,7 +50,7 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
foreach (DirectoryInfo subdir in dirs)
|
||||
{
|
||||
string temppath = Path.Combine(targetPath, subdir.Name);
|
||||
Copy(subdir.FullName, temppath);
|
||||
CopyAll(subdir.FullName, temppath);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
@ -105,7 +114,7 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
return Directory.Exists(path);
|
||||
}
|
||||
|
||||
public static bool FileExits(this string filePath)
|
||||
public static bool FileExists(this string filePath)
|
||||
{
|
||||
return File.Exists(filePath);
|
||||
}
|
||||
|
|
@ -115,7 +124,7 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
var psi = new ProcessStartInfo { FileName = FileExplorerProgramName, UseShellExecute = true, Arguments = fileOrFolderPath };
|
||||
try
|
||||
{
|
||||
if (LocationExists(fileOrFolderPath) || FileExits(fileOrFolderPath))
|
||||
if (LocationExists(fileOrFolderPath) || FileExists(fileOrFolderPath))
|
||||
Process.Start(psi);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
@ -128,6 +137,11 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
}
|
||||
}
|
||||
|
||||
public static void OpenContainingFolder(string path)
|
||||
{
|
||||
Process.Start(FileExplorerProgramEXE, $" /select,\"{path}\"");
|
||||
}
|
||||
|
||||
///<summary>
|
||||
/// This checks whether a given string is a directory path or network location string.
|
||||
/// It does not check if location actually exists.
|
||||
|
|
@ -164,7 +178,7 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
///<summary>
|
||||
/// Gets the previous level directory from a path string.
|
||||
/// Checks that previous level directory exists and returns it
|
||||
/// as a path string, or empty string if doesn't exit
|
||||
/// as a path string, or empty string if doesn't exist
|
||||
///</summary>
|
||||
public static string GetPreviousExistingDirectory(Func<string, bool> locationExists, string path)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
|
@ -7,12 +8,37 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
{
|
||||
public static class SearchWeb
|
||||
{
|
||||
private static string GetDefaultBrowserPath()
|
||||
{
|
||||
string name = string.Empty;
|
||||
try
|
||||
{
|
||||
using var regDefault = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice", false);
|
||||
var stringDefault = regDefault.GetValue("ProgId");
|
||||
|
||||
using var regKey = Registry.ClassesRoot.OpenSubKey(stringDefault + "\\shell\\open\\command", false);
|
||||
name = regKey.GetValue(null).ToString().ToLower().Replace("\"", "");
|
||||
|
||||
if (!name.EndsWith("exe"))
|
||||
name = name.Substring(0, name.LastIndexOf(".exe") + 4);
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens search in a new browser. If no browser path is passed in then Chrome is used.
|
||||
/// Leave browser path blank to use Chrome.
|
||||
/// </summary>
|
||||
public static void NewBrowserWindow(this string url, string browserPath = "")
|
||||
{
|
||||
browserPath = string.IsNullOrEmpty(browserPath) ? GetDefaultBrowserPath() : browserPath;
|
||||
|
||||
var browserExecutableName = browserPath?
|
||||
.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None)
|
||||
.Last();
|
||||
|
|
@ -44,7 +70,9 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
/// </summary>
|
||||
public static void NewTabInBrowser(this string url, string browserPath = "")
|
||||
{
|
||||
var psi = new ProcessStartInfo() { UseShellExecute = true};
|
||||
browserPath = string.IsNullOrEmpty(browserPath) ? GetDefaultBrowserPath() : browserPath;
|
||||
|
||||
var psi = new ProcessStartInfo() { UseShellExecute = true };
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(browserPath))
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ namespace Flow.Launcher.Test
|
|||
}
|
||||
|
||||
[TestCase("Chrome")]
|
||||
public void WhenGivenNotAllCharactersFoundInSearchStringThenShouldReturnZeroScore(string searchString)
|
||||
public void WhenNotAllCharactersFoundInSearchString_ThenShouldReturnZeroScore(string searchString)
|
||||
{
|
||||
var compareString = "Can have rum only in my glass";
|
||||
var matcher = new StringMatcher();
|
||||
|
|
@ -92,7 +92,7 @@ namespace Flow.Launcher.Test
|
|||
[TestCase("cand")]
|
||||
[TestCase("cpywa")]
|
||||
[TestCase("ccs")]
|
||||
public void WhenGivenStringsAndAppliedPrecisionFilteringThenShouldReturnGreaterThanPrecisionScoreResults(string searchTerm)
|
||||
public void GivenQueryString_WhenAppliedPrecisionFiltering_ThenShouldReturnGreaterThanPrecisionScoreResults(string searchTerm)
|
||||
{
|
||||
var results = new List<Result>();
|
||||
var matcher = new StringMatcher();
|
||||
|
|
@ -107,7 +107,10 @@ namespace Flow.Launcher.Test
|
|||
|
||||
foreach (var precisionScore in GetPrecisionScores())
|
||||
{
|
||||
var filteredResult = results.Where(result => result.Score >= precisionScore).Select(result => result).OrderByDescending(x => x.Score).ToList();
|
||||
var filteredResult = results.Where(result => result.Score >= precisionScore)
|
||||
.Select(result => result)
|
||||
.OrderByDescending(x => x.Score)
|
||||
.ToList();
|
||||
|
||||
Debug.WriteLine("");
|
||||
Debug.WriteLine("###############################################");
|
||||
|
|
@ -124,20 +127,22 @@ namespace Flow.Launcher.Test
|
|||
}
|
||||
|
||||
[TestCase(Chrome, Chrome, 157)]
|
||||
[TestCase(Chrome, LastIsChrome, 103)]
|
||||
[TestCase(Chrome, HelpCureHopeRaiseOnMindEntityChrome, 21)]
|
||||
[TestCase(Chrome, UninstallOrChangeProgramsOnYourComputer, 15)]
|
||||
[TestCase(Chrome, LastIsChrome, 147)]
|
||||
[TestCase(Chrome, HelpCureHopeRaiseOnMindEntityChrome, 25)]
|
||||
[TestCase(Chrome, UninstallOrChangeProgramsOnYourComputer, 21)]
|
||||
[TestCase(Chrome, CandyCrushSagaFromKing, 0)]
|
||||
[TestCase("sql", MicrosoftSqlServerManagementStudio, 56)]
|
||||
[TestCase("sql manag", MicrosoftSqlServerManagementStudio, 99)]//double spacing intended
|
||||
public void WhenGivenQueryStringThenShouldReturnCurrentScoring(string queryString, string compareString, int expectedScore)
|
||||
[TestCase("sql", MicrosoftSqlServerManagementStudio, 110)]
|
||||
[TestCase("sql manag", MicrosoftSqlServerManagementStudio, 121)]//double spacing intended
|
||||
public void WhenGivenQueryString_ThenShouldReturn_TheDesiredScoring(
|
||||
string queryString, string compareString, int expectedScore)
|
||||
{
|
||||
// When, Given
|
||||
var matcher = new StringMatcher();
|
||||
var rawScore = matcher.FuzzyMatch(queryString, compareString).RawScore;
|
||||
|
||||
// Should
|
||||
Assert.AreEqual(expectedScore, rawScore, $"Expected score for compare string '{compareString}': {expectedScore}, Actual: {rawScore}");
|
||||
Assert.AreEqual(expectedScore, rawScore,
|
||||
$"Expected score for compare string '{compareString}': {expectedScore}, Actual: {rawScore}");
|
||||
}
|
||||
|
||||
[TestCase("goo", "Google Chrome", StringMatcher.SearchPrecisionScore.Regular, true)]
|
||||
|
|
@ -150,7 +155,7 @@ namespace Flow.Launcher.Test
|
|||
[TestCase("ccs", "Candy Crush Saga from King", StringMatcher.SearchPrecisionScore.Low, true)]
|
||||
[TestCase("cand", "Candy Crush Saga from King",StringMatcher.SearchPrecisionScore.Regular, true)]
|
||||
[TestCase("cand", "Help cure hope raise on mind entity Chrome", StringMatcher.SearchPrecisionScore.Regular, false)]
|
||||
public void WhenGivenDesiredPrecisionThenShouldReturnAllResultsGreaterOrEqual(
|
||||
public void WhenGivenDesiredPrecision_ThenShouldReturn_AllResultsGreaterOrEqual(
|
||||
string queryString,
|
||||
string compareString,
|
||||
StringMatcher.SearchPrecisionScore expectedPrecisionScore,
|
||||
|
|
@ -185,8 +190,8 @@ namespace Flow.Launcher.Test
|
|||
[TestCase("sql manag", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
|
||||
[TestCase("sql", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
|
||||
[TestCase("sql serv", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
|
||||
[TestCase("sqlserv", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, false)]
|
||||
[TestCase("sql servman", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, false)]
|
||||
[TestCase("servez", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, false)]
|
||||
[TestCase("sql servz", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, false)]
|
||||
[TestCase("sql serv man", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
|
||||
[TestCase("sql studio", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
|
||||
[TestCase("mic", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
|
||||
|
|
@ -199,7 +204,7 @@ namespace Flow.Launcher.Test
|
|||
[TestCase("cod", VisualStudioCode, StringMatcher.SearchPrecisionScore.Regular, true)]
|
||||
[TestCase("code", VisualStudioCode, StringMatcher.SearchPrecisionScore.Regular, true)]
|
||||
[TestCase("codes", "Visual Studio Codes", StringMatcher.SearchPrecisionScore.Regular, true)]
|
||||
public void WhenGivenQueryShouldReturnResultsContainingAllQuerySubstrings(
|
||||
public void WhenGivenQuery_ShouldReturnResults_ContainingAllQuerySubstrings(
|
||||
string queryString,
|
||||
string compareString,
|
||||
StringMatcher.SearchPrecisionScore expectedPrecisionScore,
|
||||
|
|
@ -225,5 +230,60 @@ namespace Flow.Launcher.Test
|
|||
$"Raw Score: {matchResult.RawScore}{Environment.NewLine}" +
|
||||
$"Precision Score: {(int)expectedPrecisionScore}");
|
||||
}
|
||||
|
||||
[TestCase("man", "Task Manager", "eManual")]
|
||||
[TestCase("term", "Windows Terminal", "Character Map")]
|
||||
[TestCase("winterm", "Windows Terminal", "Cygwin64 Terminal")]
|
||||
public void WhenGivenAQuery_Scoring_ShouldGiveMoreWeightToStartOfNewWord(
|
||||
string queryString, string compareString1, string compareString2)
|
||||
{
|
||||
// When
|
||||
var matcher = new StringMatcher { UserSettingSearchPrecision = StringMatcher.SearchPrecisionScore.Regular };
|
||||
|
||||
// Given
|
||||
var compareString1Result = matcher.FuzzyMatch(queryString, compareString1);
|
||||
var compareString2Result = matcher.FuzzyMatch(queryString, compareString2);
|
||||
|
||||
Debug.WriteLine("");
|
||||
Debug.WriteLine("###############################################");
|
||||
Debug.WriteLine($"QueryString: \"{queryString}\"{Environment.NewLine}");
|
||||
Debug.WriteLine($"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}");
|
||||
Debug.WriteLine($"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}");
|
||||
Debug.WriteLine("###############################################");
|
||||
Debug.WriteLine("");
|
||||
|
||||
// Should
|
||||
Assert.True(compareString1Result.Score > compareString2Result.Score,
|
||||
$"Query: \"{queryString}\"{Environment.NewLine} " +
|
||||
$"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}" +
|
||||
$"Should be greater than{ Environment.NewLine}" +
|
||||
$"CompareString2: \"{compareString2}\", Score: {compareString1Result.Score}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
[TestCase("vim", "Vim", "ignoreDescription", "ignore.exe", "Vim Diff", "ignoreDescription", "ignore.exe")]
|
||||
public void WhenMultipleResults_ExactMatchingResult_ShouldHaveGreatestScore(
|
||||
string queryString, string firstName, string firstDescription, string firstExecutableName,
|
||||
string secondName, string secondDescription, string secondExecutableName)
|
||||
{
|
||||
// Act
|
||||
var matcher = new StringMatcher();
|
||||
var firstNameMatch = matcher.FuzzyMatch(queryString, firstName).RawScore;
|
||||
var firstDescriptionMatch = matcher.FuzzyMatch(queryString, firstDescription).RawScore;
|
||||
var firstExecutableNameMatch = matcher.FuzzyMatch(queryString, firstExecutableName).RawScore;
|
||||
|
||||
var secondNameMatch = matcher.FuzzyMatch(queryString, secondName).RawScore;
|
||||
var secondDescriptionMatch = matcher.FuzzyMatch(queryString, secondDescription).RawScore;
|
||||
var secondExecutableNameMatch = matcher.FuzzyMatch(queryString, secondExecutableName).RawScore;
|
||||
|
||||
var firstScore = new[] { firstNameMatch, firstDescriptionMatch, firstExecutableNameMatch }.Max();
|
||||
var secondScore = new[] { secondNameMatch, secondDescriptionMatch, secondExecutableNameMatch }.Max();
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(firstScore > secondScore,
|
||||
$"Query: \"{queryString}\"{Environment.NewLine} " +
|
||||
$"Name of first: \"{firstName}\", Final Score: {firstScore}{Environment.NewLine}" +
|
||||
$"Should be greater than{ Environment.NewLine}" +
|
||||
$"Name of second: \"{secondName}\", Final Score: {secondScore}{Environment.NewLine}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -58,8 +58,8 @@ namespace Flow.Launcher.Test.Plugins
|
|||
$"Actual: {result}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
[TestCase("C:\\", "SELECT TOP 100 System.FileName, System.ItemPathDisplay, System.ItemType FROM SystemIndex WHERE directory='file:C:\\'")]
|
||||
[TestCase("C:\\SomeFolder\\", "SELECT TOP 100 System.FileName, System.ItemPathDisplay, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\'")]
|
||||
[TestCase("C:\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\'")]
|
||||
[TestCase("C:\\SomeFolder\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\'")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchTypeIsTopLevelDirectorySearch_ThenQueryShouldUseExpectedString(string folderPath, string expectedString)
|
||||
{
|
||||
// Given
|
||||
|
|
@ -74,7 +74,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
$"Actual string was: {queryString}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
[TestCase("C:\\SomeFolder\\flow.launcher.sln", "SELECT TOP 100 System.FileName, System.ItemPathDisplay, System.ItemType " +
|
||||
[TestCase("C:\\SomeFolder\\flow.launcher.sln", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType " +
|
||||
"FROM SystemIndex WHERE (System.FileName LIKE 'flow.launcher.sln%' " +
|
||||
"OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033))" +
|
||||
" AND directory='file:C:\\SomeFolder'")]
|
||||
|
|
@ -126,7 +126,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
$"Actual string was: {resultString}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
[TestCase("flow.launcher.sln", "SELECT TOP 100 \"System.FileName\", \"System.ItemPathDisplay\", \"System.ItemType\" " +
|
||||
[TestCase("flow.launcher.sln", "SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" " +
|
||||
"FROM \"SystemIndex\" WHERE (System.FileName LIKE 'flow.launcher.sln%' " +
|
||||
"OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:'")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryShouldUseExpectedString(
|
||||
|
|
@ -184,6 +184,55 @@ namespace Flow.Launcher.Test.Plugins
|
|||
$"Actual number of results is {results.Count} {Environment.NewLine}");
|
||||
}
|
||||
|
||||
[TestCase(@"some words", @"FREETEXT('some words')")]
|
||||
public void GivenWindowsIndexSearch_WhenQueryWhereRestrictionsIsForFileContentSearch_ThenShouldReturnFreeTextString(
|
||||
string querySearchString, string expectedString)
|
||||
{
|
||||
// Given
|
||||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
|
||||
//When
|
||||
var resultString = queryConstructor.QueryWhereRestrictionsForFileContentSearch(querySearchString);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(resultString == expectedString,
|
||||
$"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " +
|
||||
$"Actual string was: {resultString}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
[TestCase("some words", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType " +
|
||||
"FROM SystemIndex WHERE FREETEXT('some words') AND scope='file:'")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchForFileContent_ThenQueryShouldUseExpectedString(
|
||||
string userSearchString, string expectedString)
|
||||
{
|
||||
// Given
|
||||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
|
||||
//When
|
||||
var resultString = queryConstructor.QueryForFileContentSearch(userSearchString);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(resultString == expectedString,
|
||||
$"Expected query string: {expectedString}{Environment.NewLine} " +
|
||||
$"Actual string was: {resultString}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
public void GivenQuery_WhenActionKeywordForFileContentSearchExists_ThenFileContentSearchRequiredShouldReturnTrue()
|
||||
{
|
||||
// Given
|
||||
var query = new Query { ActionKeyword = "doc:", Search = "search term" };
|
||||
|
||||
var searchManager = new SearchManager(new Settings(), new PluginInitContext());
|
||||
|
||||
// When
|
||||
var result = searchManager.IsFileContentSearch(query.ActionKeyword);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(result,
|
||||
$"Expected True for file content search. {Environment.NewLine} " +
|
||||
$"Actual result was: {result}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
[TestCase(@"c:\\", false)]
|
||||
[TestCase(@"i:\", true)]
|
||||
[TestCase(@"\c:\", false)]
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{3A73
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher", "Flow.Launcher\Flow.Launcher.csproj", "{DB90F671-D861-46BB-93A3-F1304F5BA1C5}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{230AE83F-E92E-4E69-8355-426B305DA9C0} = {230AE83F-E92E-4E69-8355-426B305DA9C0}
|
||||
{1EE20B48-82FB-48A2-8086-675D6DDAB4F0} = {1EE20B48-82FB-48A2-8086-675D6DDAB4F0}
|
||||
{0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {0B9DE348-9361-4940-ADB6-F5953BFFCCEC}
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217} = {4792A74A-0CEA-4173-A8B2-30E6764C6217}
|
||||
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4}
|
||||
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}
|
||||
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {59BD9891-3837-438A-958D-ADC7F91F6F7E}
|
||||
|
|
@ -24,14 +24,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher", "Flow.Launc
|
|||
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {9B130CC5-14FB-41FF-B310-0A95B6894C37}
|
||||
{FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {FDED22C8-B637-42E8-824A-63B5B6E05A3A}
|
||||
{A3DCCBCA-ACC1-421D-B16E-210896234C26} = {A3DCCBCA-ACC1-421D-B16E-210896234C26}
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE} = {049490F0-ECD2-4148-9B39-2135EC346EBE}
|
||||
{403B57F2-1856-4FC7-8A24-36AB346B763E} = {403B57F2-1856-4FC7-8A24-36AB346B763E}
|
||||
{588088F4-3262-4F9F-9663-A05DE12534C3} = {588088F4-3262-4F9F-9663-A05DE12534C3}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Infrastructure", "Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj", "{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.PluginManagement", "Plugins\Flow.Launcher.Plugin.PluginManagement\Flow.Launcher.Plugin.PluginManagement.csproj", "{049490F0-ECD2-4148-9B39-2135EC346EBE}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Core", "Flow.Launcher.Core\Flow.Launcher.Core.csproj", "{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Program", "Plugins\Flow.Launcher.Plugin.Program\Flow.Launcher.Plugin.Program.csproj", "{FDB3555B-58EF-4AE6-B5F1-904719637AB4}"
|
||||
|
|
@ -48,13 +46,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Url",
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Color", "Plugins\Flow.Launcher.Plugin.Color\Flow.Launcher.Plugin.Color.csproj", "{F35190AA-4758-4D9E-A193-E3BDF6AD3567}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Everything", "Plugins\Flow.Launcher.Plugin.Everything\Flow.Launcher.Plugin.Everything.csproj", "{230AE83F-E92E-4E69-8355-426B305DA9C0}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FFD651C7-0546-441F-BC8C-D4EE8FD01EA7}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.gitattributes = .gitattributes
|
||||
.gitignore = .gitignore
|
||||
appveyor.yml = appveyor.yml
|
||||
Directory.Build.targets = Directory.Build.targets
|
||||
Scripts\flowlauncher.nuspec = Scripts\flowlauncher.nuspec
|
||||
LICENSE = LICENSE
|
||||
Scripts\post_build.ps1 = Scripts\post_build.ps1
|
||||
|
|
@ -62,18 +59,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||
SolutionAssemblyInfo.cs = SolutionAssemblyInfo.cs
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HelloWorldCSharp", "Plugins\HelloWorldCSharp\HelloWorldCSharp.csproj", "{03FFA443-5F50-48D5-8869-F3DF316803AA}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Shell", "Plugins\Flow.Launcher.Plugin.Shell\Flow.Launcher.Plugin.Shell.csproj", "{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.BrowserBookmark", "Plugins\Flow.Launcher.Plugin.BrowserBookmark\Flow.Launcher.Plugin.BrowserBookmark.csproj", "{9B130CC5-14FB-41FF-B310-0A95B6894C37}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Calculator", "Plugins\Flow.Launcher.Plugin.Calculator\Flow.Launcher.Plugin.Calculator.csproj", "{59BD9891-3837-438A-958D-ADC7F91F6F7E}"
|
||||
EndProject
|
||||
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "HelloWorldFSharp", "Plugins\HelloWorldFSharp\HelloWorldFSharp.fsproj", "{30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Explorer", "Plugins\Flow.Launcher.Plugin.Explorer\Flow.Launcher.Plugin.Explorer.csproj", "{F9C4C081-4CC3-4146-95F1-E102B4E10A5F}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.ProcessKiller", "Plugins\Flow.Launcher.Plugin.ProcessKiller\Flow.Launcher.Plugin.ProcessKiller.csproj", "{588088F4-3262-4F9F-9663-A05DE12534C3}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.PluginsManager", "Plugins\Flow.Launcher.Plugin.PluginsManager\Flow.Launcher.Plugin.PluginsManager.csproj", "{4792A74A-0CEA-4173-A8B2-30E6764C6217}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -132,18 +129,6 @@ Global
|
|||
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x86.Build.0 = Release|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x64.Build.0 = Release|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x86.Build.0 = Release|Any CPU
|
||||
{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -241,30 +226,6 @@ Global
|
|||
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|x86.Build.0 = Release|Any CPU
|
||||
{230AE83F-E92E-4E69-8355-426B305DA9C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{230AE83F-E92E-4E69-8355-426B305DA9C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{230AE83F-E92E-4E69-8355-426B305DA9C0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{230AE83F-E92E-4E69-8355-426B305DA9C0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{230AE83F-E92E-4E69-8355-426B305DA9C0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{230AE83F-E92E-4E69-8355-426B305DA9C0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{230AE83F-E92E-4E69-8355-426B305DA9C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{230AE83F-E92E-4E69-8355-426B305DA9C0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{230AE83F-E92E-4E69-8355-426B305DA9C0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{230AE83F-E92E-4E69-8355-426B305DA9C0}.Release|x64.Build.0 = Release|Any CPU
|
||||
{230AE83F-E92E-4E69-8355-426B305DA9C0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{230AE83F-E92E-4E69-8355-426B305DA9C0}.Release|x86.Build.0 = Release|Any CPU
|
||||
{03FFA443-5F50-48D5-8869-F3DF316803AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{03FFA443-5F50-48D5-8869-F3DF316803AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{03FFA443-5F50-48D5-8869-F3DF316803AA}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{03FFA443-5F50-48D5-8869-F3DF316803AA}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{03FFA443-5F50-48D5-8869-F3DF316803AA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{03FFA443-5F50-48D5-8869-F3DF316803AA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{03FFA443-5F50-48D5-8869-F3DF316803AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{03FFA443-5F50-48D5-8869-F3DF316803AA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{03FFA443-5F50-48D5-8869-F3DF316803AA}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{03FFA443-5F50-48D5-8869-F3DF316803AA}.Release|x64.Build.0 = Release|Any CPU
|
||||
{03FFA443-5F50-48D5-8869-F3DF316803AA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{03FFA443-5F50-48D5-8869-F3DF316803AA}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -301,18 +262,6 @@ Global
|
|||
{59BD9891-3837-438A-958D-ADC7F91F6F7E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{59BD9891-3837-438A-958D-ADC7F91F6F7E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{59BD9891-3837-438A-958D-ADC7F91F6F7E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -325,12 +274,35 @@ Global
|
|||
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Release|x86.Build.0 = Release|Any CPU
|
||||
{588088F4-3262-4F9F-9663-A05DE12534C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{588088F4-3262-4F9F-9663-A05DE12534C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{588088F4-3262-4F9F-9663-A05DE12534C3}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{588088F4-3262-4F9F-9663-A05DE12534C3}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{588088F4-3262-4F9F-9663-A05DE12534C3}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{588088F4-3262-4F9F-9663-A05DE12534C3}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x64.Build.0 = Release|Any CPU
|
||||
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{403B57F2-1856-4FC7-8A24-36AB346B763E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{1EE20B48-82FB-48A2-8086-675D6DDAB4F0} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
|
|
@ -338,13 +310,12 @@ Global
|
|||
{0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{A3DCCBCA-ACC1-421D-B16E-210896234C26} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{F35190AA-4758-4D9E-A193-E3BDF6AD3567} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{230AE83F-E92E-4E69-8355-426B305DA9C0} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{03FFA443-5F50-48D5-8869-F3DF316803AA} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{588088F4-3262-4F9F-9663-A05DE12534C3} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {F26ACB50-3F6C-4907-B0C9-1ADACC1D0DED}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using System.Windows;
|
||||
|
|
@ -28,7 +29,7 @@ namespace Flow.Launcher
|
|||
private SettingWindowViewModel _settingsVM;
|
||||
private readonly Updater _updater = new Updater(Flow.Launcher.Properties.Settings.Default.GithubRepo);
|
||||
private readonly Portable _portable = new Portable();
|
||||
private readonly Alphabet _alphabet = new Alphabet();
|
||||
private readonly PinyinAlphabet _alphabet = new PinyinAlphabet();
|
||||
private StringMatcher _stringMatcher;
|
||||
|
||||
[STAThread]
|
||||
|
|
@ -85,6 +86,8 @@ namespace Flow.Launcher
|
|||
|
||||
Http.Proxy = _settings.Proxy;
|
||||
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
|
||||
RegisterExitEvents();
|
||||
|
||||
AutoStartup();
|
||||
|
|
@ -118,12 +121,12 @@ namespace Flow.Launcher
|
|||
var timer = new Timer(1000 * 60 * 60 * 5);
|
||||
timer.Elapsed += async (s, e) =>
|
||||
{
|
||||
await _updater.UpdateApp();
|
||||
await _updater.UpdateApp(API);
|
||||
};
|
||||
timer.Start();
|
||||
|
||||
// check updates on startup
|
||||
await _updater.UpdateApp();
|
||||
await _updater.UpdateApp(API);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ namespace Flow.Launcher
|
|||
App.API.ChangeQuery(pluginHotkey.ActionKeyword);
|
||||
Application.Current.MainWindow.Visibility = Visibility.Visible;
|
||||
});
|
||||
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("success"));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -76,7 +75,6 @@ namespace Flow.Launcher
|
|||
App.API.ChangeQuery(updateCustomHotkey.ActionKeyword);
|
||||
Application.Current.MainWindow.Visibility = Visibility.Visible;
|
||||
});
|
||||
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("success"));
|
||||
}
|
||||
|
||||
Close();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
|
|
@ -72,23 +72,13 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="InputSimulator" Version="1.0.4" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" />
|
||||
<PackageReference Include="Mages" Version="1.6.0" />
|
||||
<PackageReference Include="ModernWpfUI" Version="0.8.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="NHotkey.Wpf" Version="1.2.1" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" Version="4.9.0" />
|
||||
<PackageReference Include="NuGet.CommandLine" Version="5.4.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="2.5.13" />
|
||||
<PackageReference Include="System.Data.OleDb" Version="4.7.1" />
|
||||
<PackageReference Include="System.Data.SQLite" Version="1.0.112" />
|
||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.112" />
|
||||
<PackageReference Include="System.Runtime" Version="4.3.1" />
|
||||
<PackageReference Include="tlbimp-Microsoft.Search.Interop" Version="1.0.0" />
|
||||
<PackageReference Include="UnidecodeSharp" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -41,8 +41,7 @@ namespace Flow.Launcher.Helper
|
|||
|
||||
public static string DependenciesInfo()
|
||||
{
|
||||
var info = $"\nPython Path: {Constant.PythonPath}" +
|
||||
$"\nEverything SDK Path: {Constant.EverythingSDKPath}";
|
||||
var info = $"\nPython Path: {Constant.PythonPath}";
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 15 KiB |
BIN
Flow.Launcher/Images/app_missing_img.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
|
|
@ -96,7 +96,7 @@
|
|||
<system:String x:Key="version">Version</system:String>
|
||||
<system:String x:Key="about_activate_times">You have activated Flow Launcher {0} times</system:String>
|
||||
<system:String x:Key="checkUpdates">Check for Updates</system:String>
|
||||
<system:String x:Key="newVersionTips">New version {0} is available, please restart Flow Launcher.</system:String>
|
||||
<system:String x:Key="newVersionTips">New version {0} is available, would you like to restart Flow Launcher to use the update?</system:String>
|
||||
<system:String x:Key="checkUpdatesFailed">Check updates failed, please check your connection and proxy settings to api.github.com.</system:String>
|
||||
<system:String x:Key="downloadUpdatesFailed">
|
||||
Download updates failed, please check your connection and proxy settings to github-cloud.s3.amazonaws.com,
|
||||
|
|
@ -111,7 +111,7 @@
|
|||
<system:String x:Key="done">Done</system:String>
|
||||
<system:String x:Key="cannotFindSpecifiedPlugin">Can't find specified plugin</system:String>
|
||||
<system:String x:Key="newActionKeywordsCannotBeEmpty">New Action Keyword can't be empty</system:String>
|
||||
<system:String x:Key="newActionKeywordsHasBeenAssigned">New Action Keywords have been assigned to another plugin, please assign other new action keyword</system:String>
|
||||
<system:String x:Key="newActionKeywordsHasBeenAssigned">This new Action Keyword is already assigned to another plugin, please choose a different one</system:String>
|
||||
<system:String x:Key="success">Success</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">Use * if you don't want to specify an action keyword</system:String>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
<!--MainWindow-->
|
||||
<system:String x:Key="registerHotkeyFailed">Nepodarilo sa registrovať klávesovú skratku {0}</system:String>
|
||||
<system:String x:Key="couldnotStartCmd">Nepodarilo sa spustiť {0}</system:String>
|
||||
<system:String x:Key="invalidFlowLauncherPluginFileFormat">Neplatný formát súboru pre plugin Flow Launcher</system:String>
|
||||
<system:String x:Key="setAsTopMostInThisQuery">Pri tomto dopyte umiestniť navrchu</system:String>
|
||||
<system:String x:Key="cancelTopMostInThisQuery">Zrušiť umiestnenie navrchu pri tomto dopyte</system:String>
|
||||
<system:String x:Key="invalidFlowLauncherPluginFileFormat">Neplatný formát súboru pre plugin Flow Launchera</system:String>
|
||||
<system:String x:Key="setAsTopMostInThisQuery">Pri tomto zadaní umiestniť navrchu</system:String>
|
||||
<system:String x:Key="cancelTopMostInThisQuery">Zrušiť umiestnenie navrchu pri tomto zadaní</system:String>
|
||||
<system:String x:Key="executeQuery">Spustiť dopyt: {0}</system:String>
|
||||
<system:String x:Key="lastExecuteTime">Posledný čas realizácie: {0}</system:String>
|
||||
<system:String x:Key="iconTrayOpen">Otvoriť</system:String>
|
||||
|
|
@ -15,61 +15,71 @@
|
|||
<system:String x:Key="iconTrayExit">Ukončiť</system:String>
|
||||
|
||||
<!--Setting General-->
|
||||
<system:String x:Key="flowlauncher_settings">Nastavenia Flow Launcher</system:String>
|
||||
<system:String x:Key="flowlauncher_settings">Nastavenia Flow Launchera</system:String>
|
||||
<system:String x:Key="general">Všeobecné</system:String>
|
||||
<system:String x:Key="startFlowLauncherOnSystemStartup">Spustiť Flow Launcher po štarte systému</system:String>
|
||||
<system:String x:Key="hideFlowLauncherWhenLoseFocus">Schovať Flow Launcher po strate fokusu</system:String>
|
||||
<system:String x:Key="dontPromptUpdateMsg">Nezobrazovať upozornenia na novú verziu</system:String>
|
||||
<system:String x:Key="rememberLastLocation">Zapamätať si posledné umiestnenie</system:String>
|
||||
<system:String x:Key="language">Jazyk</system:String>
|
||||
<system:String x:Key="lastQueryMode">Posledný dopyt</system:String>
|
||||
<system:String x:Key="lastQueryMode">Posledné vyhľadávanie</system:String>
|
||||
<system:String x:Key="LastQueryPreserved">Ponechať</system:String>
|
||||
<system:String x:Key="LastQuerySelected">Označiť posledný dopyt</system:String>
|
||||
<system:String x:Key="LastQueryEmpty">Prázdne</system:String>
|
||||
<system:String x:Key="LastQuerySelected">Označiť</system:String>
|
||||
<system:String x:Key="LastQueryEmpty">Vymazať</system:String>
|
||||
<system:String x:Key="maxShowResults">Max. výsledkov</system:String>
|
||||
<system:String x:Key="ignoreHotkeysOnFullscreen">Ignorovať klávesové skraty v režime na celú obrazovku</system:String>
|
||||
<system:String x:Key="ignoreHotkeysOnFullscreen">Ignorovať klávesové skratky v režime na celú obrazovku</system:String>
|
||||
<system:String x:Key="pythonDirectory">Priečinok s Pythonom</system:String>
|
||||
<system:String x:Key="autoUpdates">Automatická aktualizácia</system:String>
|
||||
<system:String x:Key="selectPythonDirectory">Vybrať</system:String>
|
||||
<system:String x:Key="hideOnStartup">Schovať Flow Launcher po spustení</system:String>
|
||||
<system:String x:Key="hideNotifyIcon">Schovať ikonu v oblasti oznámení</system:String>
|
||||
<system:String x:Key="hideNotifyIcon">Schovať ikonu z oblasti oznámení</system:String>
|
||||
<system:String x:Key="querySearchPrecision">Presnosť vyhľadávania</system:String>
|
||||
<system:String x:Key="ShouldUsePinyin">Použiť Pinyin</system:String>
|
||||
|
||||
<!--Setting Plugin-->
|
||||
<system:String x:Key="plugin">Plugin</system:String>
|
||||
<system:String x:Key="browserMorePlugins">Nájsť ďalšie pluginy</system:String>
|
||||
<system:String x:Key="disable">Zakázať</system:String>
|
||||
<system:String x:Key="actionKeywords">Skratka akcie</system:String>
|
||||
<system:String x:Key="pluginDirectory">Priečinok s pluginmy</system:String>
|
||||
<system:String x:Key="currentActionKeywords">Aktuálna akcia skratky:</system:String>
|
||||
<system:String x:Key="newActionKeyword">Nová akcia skratky:</system:String>
|
||||
<system:String x:Key="pluginDirectory">Priečinok s pluginmi</system:String>
|
||||
<system:String x:Key="author">Autor</system:String>
|
||||
<system:String x:Key="plugin_init_time">Čas inic.:</system:String>
|
||||
<system:String x:Key="plugin_query_time">Čas dopytu:</system:String>
|
||||
<system:String x:Key="plugin_init_time">Príprava: {0}ms</system:String>
|
||||
<system:String x:Key="plugin_query_time">Čas dopytu: {0}ms</system:String>
|
||||
|
||||
<!--Setting Theme-->
|
||||
<system:String x:Key="theme">Motív</system:String>
|
||||
<system:String x:Key="browserMoreThemes">Prehliadať viac motívov</system:String>
|
||||
<system:String x:Key="queryBoxFont">Písmo poľa pre dopyt</system:String>
|
||||
<system:String x:Key="hiThere">Ahojte</system:String>
|
||||
<system:String x:Key="queryBoxFont">Písmo vyhľadávacieho poľa</system:String>
|
||||
<system:String x:Key="resultItemFont">Písmo výsledkov</system:String>
|
||||
<system:String x:Key="windowMode">Režim okno</system:String>
|
||||
<system:String x:Key="opacity">Nepriehľadnosť</system:String>
|
||||
<system:String x:Key="theme_load_failure_path_not_exists">Motív {0} neexistuje, návrat na predvolený motív</system:String>
|
||||
<system:String x:Key="theme_load_failure_parse_error">Nepodarilo sa nečítať motív {0}, návrat na predvolený motív</system:String>
|
||||
|
||||
<!--Setting Hotkey-->
|
||||
<system:String x:Key="hotkey">Klávesová skratka</system:String>
|
||||
<system:String x:Key="hotkey">Klávesové skratky</system:String>
|
||||
<system:String x:Key="flowlauncherHotkey">Klávesová skratka pre Flow Launcher</system:String>
|
||||
<system:String x:Key="openResultModifiers">Otvorte modifikátory výsledkov</system:String>
|
||||
<system:String x:Key="customQueryHotkey">Vlastná klávesová skratka pre dopyt</system:String>
|
||||
<system:String x:Key="openResultModifiers">Modifikáčné klávesy na otvorenie výsledkov</system:String>
|
||||
<system:String x:Key="showOpenResultHotkey">Zobraziť klávesovú skratku</system:String>
|
||||
<system:String x:Key="customQueryHotkey">Vlastná klávesová skratka na vyhľadávanie</system:String>
|
||||
<system:String x:Key="delete">Odstrániť</system:String>
|
||||
<system:String x:Key="edit">Upraviť</system:String>
|
||||
<system:String x:Key="add">Pridať</system:String>
|
||||
<system:String x:Key="pleaseSelectAnItem">Vyberte položku, prosím</system:String>
|
||||
<system:String x:Key="deleteCustomHotkeyWarning">Ste si istý, že chcete odstrániť klávesovú skratku {0} pre plugin?</system:String>
|
||||
<system:String x:Key="queryWindowShadowEffect">Tieňový efekt v poli vyhľadávania</system:String>
|
||||
<system:String x:Key="shadowEffectCPUUsage">Tieňový efekt významne využíva GPU.</system:String>
|
||||
<system:String x:Key="shadowEffectPerformance">Neodporúča sa, ak je výkon počítača obmedzený.</system:String>
|
||||
|
||||
<!--Setting Proxy-->
|
||||
<system:String x:Key="proxy">HTTP Proxy</system:String>
|
||||
<system:String x:Key="enableProxy">Povoliť HTTP Proxy</system:String>
|
||||
<system:String x:Key="server">HTTP Server</system:String>
|
||||
<system:String x:Key="port">Port</system:String>
|
||||
<system:String x:Key="userName">Používateľské meno</system:String>
|
||||
<system:String x:Key="userName">Použív. meno</system:String>
|
||||
<system:String x:Key="password">Heslo</system:String>
|
||||
<system:String x:Key="testProxy">Test Proxy</system:String>
|
||||
<system:String x:Key="save">Uložiť</system:String>
|
||||
|
|
@ -86,13 +96,13 @@
|
|||
<system:String x:Key="version">Verzia</system:String>
|
||||
<system:String x:Key="about_activate_times">Flow Launcher bol aktivovaný {0}-krát</system:String>
|
||||
<system:String x:Key="checkUpdates">Skontrolovať aktualizácie</system:String>
|
||||
<system:String x:Key="newVersionTips">Je dostupná nová verzia {0}, prosím, reštartujte Flow Launcher.</system:String>
|
||||
<system:String x:Key="newVersionTips">Je dostupná nová verzia {0}, chcete reštartovať Flow Launcher, aby sa mohol aktualizovať?</system:String>
|
||||
<system:String x:Key="checkUpdatesFailed">Kontrola aktualizácií zlyhala, prosím, skontrolujte pripojenie na internet a nastavenie proxy k api.github.com.</system:String>
|
||||
<system:String x:Key="downloadUpdatesFailed">
|
||||
Sťahovanie aktualizácií zlyhalo, skontrolujte pripojenie na internet a nastavenie proxy k github-cloud.s3.amazonaws.com,
|
||||
alebo prejdite na https://github.com/Flow-Launcher/Flow.Launcher/releases pre manuálne stiahnutie aktualizácií.
|
||||
alebo prejdite na https://github.com/Flow-Launcher/Flow.Launcher/releases pre manuálne stiahnutie aktualizácie.
|
||||
</system:String>
|
||||
<system:String x:Key="releaseNotes">Poznámky k vydaniu:</system:String>
|
||||
<system:String x:Key="releaseNotes">Poznámky k vydaniu</system:String>
|
||||
|
||||
<!--Action Keyword Setting Dialog-->
|
||||
<system:String x:Key="oldActionKeywords">Stará skratka akcie</system:String>
|
||||
|
|
@ -131,7 +141,7 @@
|
|||
<system:String x:Key="reportWindow_flowlauncher_got_an_error">Flow Launcher zaznamenal chybu</system:String>
|
||||
|
||||
<!--update-->
|
||||
<system:String x:Key="update_flowlauncher_update_new_version_available">Je dostupné nové vydanie Flow Launcher {0}</system:String>
|
||||
<system:String x:Key="update_flowlauncher_update_new_version_available">Je dostupná nová verzia Flow Launcher {0}</system:String>
|
||||
<system:String x:Key="update_flowlauncher_update_error">Počas inštalácie aktualizácií došlo k chybe</system:String>
|
||||
<system:String x:Key="update_flowlauncher_update">Aktualizovať</system:String>
|
||||
<system:String x:Key="update_flowlauncher_update_cancel">Zrušiť</system:String>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
Loaded="OnLoaded"
|
||||
Initialized="OnInitialized"
|
||||
Closing="OnClosing"
|
||||
Drop="OnDrop"
|
||||
LocationChanged="OnLocationChanged"
|
||||
Deactivated="OnDeactivated"
|
||||
PreviewKeyDown="OnKeyDown"
|
||||
|
|
@ -64,8 +63,7 @@
|
|||
<StackPanel Orientation="Vertical">
|
||||
<Grid>
|
||||
<TextBox x:Name="QueryTextSuggestionBox"
|
||||
Style="{DynamicResource QueryBoxStyle}"
|
||||
Foreground="DarkGray"
|
||||
Style="{DynamicResource QuerySuggestionBoxStyle}"
|
||||
IsEnabled="False"
|
||||
Margin="18,0,56,0">
|
||||
<TextBox.Text>
|
||||
|
|
|
|||
|
|
@ -52,12 +52,14 @@ namespace Flow.Launcher
|
|||
|
||||
private void OnInitialized(object sender, EventArgs e)
|
||||
{
|
||||
// show notify icon when flowlauncher is hided
|
||||
InitializeNotifyIcon();
|
||||
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs _)
|
||||
{
|
||||
// show notify icon when flowlauncher is hidden
|
||||
InitializeNotifyIcon();
|
||||
|
||||
// todo is there a way to set blur only once?
|
||||
ThemeManager.Instance.SetBlurForWindow();
|
||||
WindowsInteropHelper.DisableControlBox(this);
|
||||
|
|
@ -87,11 +89,17 @@ namespace Flow.Launcher
|
|||
};
|
||||
_settings.PropertyChanged += (o, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(Settings.HideNotifyIcon))
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
_notifyIcon.Visible = !_settings.HideNotifyIcon;
|
||||
case nameof(Settings.HideNotifyIcon):
|
||||
_notifyIcon.Visible = !_settings.HideNotifyIcon;
|
||||
break;
|
||||
case nameof(Settings.Language):
|
||||
UpdateNotifyIconText();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
InitializePosition();
|
||||
}
|
||||
|
||||
|
|
@ -103,6 +111,18 @@ namespace Flow.Launcher
|
|||
_settings.WindowLeft = Left;
|
||||
}
|
||||
|
||||
private void UpdateNotifyIconText()
|
||||
{
|
||||
var menu = _notifyIcon.ContextMenuStrip;
|
||||
var open = menu.Items[0];
|
||||
var setting = menu.Items[1];
|
||||
var exit = menu.Items[2];
|
||||
|
||||
open.Text = InternationalizationManager.Instance.GetTranslation("iconTrayOpen");
|
||||
setting.Text = InternationalizationManager.Instance.GetTranslation("iconTraySettings");
|
||||
exit.Text = InternationalizationManager.Instance.GetTranslation("iconTrayExit");
|
||||
}
|
||||
|
||||
private void InitializeNotifyIcon()
|
||||
{
|
||||
_notifyIcon = new NotifyIcon
|
||||
|
|
@ -179,25 +199,6 @@ namespace Flow.Launcher
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnDrop(object sender, DragEventArgs e)
|
||||
{
|
||||
if (e.Data.GetDataPresent(DataFormats.FileDrop))
|
||||
{
|
||||
// Note that you can have more than one file.
|
||||
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
|
||||
if (files[0].ToLower().EndsWith(".flowlauncher"))
|
||||
{
|
||||
PluginManager.InstallPlugin(files[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("invalidFlowLauncherPluginFileFormat"));
|
||||
}
|
||||
}
|
||||
e.Handled = false;
|
||||
}
|
||||
|
||||
private void OnPreviewDragOver(object sender, DragEventArgs e)
|
||||
{
|
||||
e.Handled = true;
|
||||
|
|
@ -294,7 +295,5 @@ namespace Flow.Launcher
|
|||
_viewModel.QueryTextCursorMovedToEnd = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||