Merge branch 'dev'

This commit is contained in:
Jeremy Wu 2020-06-24 13:27:59 +10:00
commit 9326d093b8
96 changed files with 2317 additions and 1288 deletions

View file

@ -57,10 +57,7 @@
<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="3.2.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="PropertyChanged.Fody" Version="2.5.13" />
<PackageReference Include="SharpZipLib" Version="1.2.0" />
<PackageReference Include="System.Runtime" Version="4.3.1" />
</ItemGroup>

View file

@ -1,11 +1,12 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Threading.Tasks;
using System.Windows.Forms;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Exception;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
@ -29,6 +30,8 @@ namespace Flow.Launcher.Core.Plugin
public static IEnumerable<PluginPair> DotNetPlugins(List<PluginMetadata> source)
{
var erroredPlugins = new List<string>();
var plugins = new List<PluginPair>();
var metadatas = source.Where(o => AllowedLanguage.IsDotNet(o.Language));
@ -50,20 +53,34 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for {metadata.Name}", e);
erroredPlugins.Add(metadata.Name);
Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for the plugin: {metadata.Name}", e);
return;
}
var types = assembly.GetTypes();
Type type;
try
{
var types = assembly.GetTypes();
type = types.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin)));
}
catch (InvalidOperationException e)
{
Log.Exception($"|PluginsLoader.DotNetPlugins|Can't find class implement IPlugin for <{metadata.Name}>", 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
{
@ -71,7 +88,9 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
Log.Exception($"|PluginsLoader.DotNetPlugins|Can't create instance for <{metadata.Name}>", e);
erroredPlugins.Add(metadata.Name);
Log.Exception($"|PluginsLoader.DotNetPlugins|The following plugin has errored and can not be loaded: <{metadata.Name}>", e);
return;
}
#endif
@ -85,6 +104,26 @@ namespace Flow.Launcher.Core.Plugin
metadata.InitTime += milliseconds;
}
if (erroredPlugins.Count > 0)
{
var errorPluginString = "";
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}" +
$"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" +
$"Please refer to the logs for more information","",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
});
}
return plugins;
}

View file

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
@ -12,7 +12,26 @@
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup>
<Version>1.0.0</Version>
<PackageVersion>1.0.0-beta3</PackageVersion>
<AssemblyVersion>1.0.0</AssemblyVersion>
<FileVersion>1.0.0</FileVersion>
<PackageId>Flow.Launcher.Plugin</PackageId>
<Authors>Flow-Launcher</Authors>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryUrl>https://github.com/Flow-Launcher/Flow.Launcher</RepositoryUrl>
<PackageDescription>Reference this library if you want to develop a Flow Launcher plugin</PackageDescription>
<PackageTags>flowlauncher</PackageTags>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
</PropertyGroup>
<PropertyGroup Condition="'$(APPVEYOR)' == 'True'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
@ -26,7 +45,7 @@
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<DebugType>embedded</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\Output\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
@ -37,28 +56,15 @@
<ItemGroup>
<None Include="README.md" />
</ItemGroup>
<ItemGroup>
<None Remove="FodyWeavers.xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\SolutionAssemblyInfo.cs" Link="Properties\SolutionAssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="FodyWeavers.xml" />
<None Include="FodyWeavers.xml" />
</ItemGroup>
<ItemGroup>
<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="3.2.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="PropertyChanged.Fody" Version="2.5.13" />
<PackageReference Include="System.Runtime" Version="4.3.1" />
</ItemGroup>

View file

@ -127,5 +127,81 @@ namespace Flow.Launcher.Plugin.SharedCommands
#endif
}
}
///<summary>
/// This checks whether a given string is a directory path or network location string.
/// It does not check if location actually exists.
///</summary>
public static bool IsLocationPathString(string querySearchString)
{
if (string.IsNullOrEmpty(querySearchString))
return false;
// // shared folder location, and not \\\location\
if (querySearchString.Length >= 3
&& querySearchString.StartsWith(@"\\")
&& char.IsLetter(querySearchString[2]))
return true;
// c:\
if (querySearchString.Length == 3
&& char.IsLetter(querySearchString[0])
&& querySearchString[1] == ':'
&& querySearchString[2] == '\\')
return true;
// c:\\
if (querySearchString.Length >= 4
&& char.IsLetter(querySearchString[0])
&& querySearchString[1] == ':'
&& querySearchString[2] == '\\'
&& char.IsLetter(querySearchString[3]))
return true;
return false;
}
///<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
///</summary>
public static string GetPreviousExistingDirectory(Func<string, bool> locationExists, string path)
{
var previousDirectoryPath = "";
var index = path.LastIndexOf('\\');
if (index > 0 && index < (path.Length - 1))
{
previousDirectoryPath = path.Substring(0, index + 1);
if (!locationExists(previousDirectoryPath))
{
return "";
}
}
else
{
return "";
}
return previousDirectoryPath;
}
///<summary>
/// Returns the previous level directory if path incomplete (does not end with '\').
/// Does not check if previous level directory exists.
/// Returns passed in string if is complete path
///</summary>
public static string ReturnPreviousDirectoryIfIncompleteString(string path)
{
if (!path.EndsWith("\\"))
{
// not full path, get previous level directory string
var indexOfSeparator = path.LastIndexOf('\\');
return path.Substring(0, indexOfSeparator + 1);
}
return path;
}
}
}

View file

@ -39,6 +39,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Plugins\Flow.Launcher.Plugin.Explorer\Flow.Launcher.Plugin.Explorer.csproj" />
<ProjectReference Include="..\Plugins\Flow.Launcher.Plugin.Program\Flow.Launcher.Plugin.Program.csproj" />
<ProjectReference Include="..\Plugins\Flow.Launcher.Plugin.Url\Flow.Launcher.Plugin.Url.csproj" />
<ProjectReference Include="..\Flow.Launcher.Core\Flow.Launcher.Core.csproj" />
@ -56,8 +57,4 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,279 @@
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.Explorer;
using Flow.Launcher.Plugin.Explorer.Search;
using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo;
using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex;
using Flow.Launcher.Plugin.SharedCommands;
using NUnit.Framework;
using System;
using System.Collections.Generic;
namespace Flow.Launcher.Test.Plugins
{
/// <summary>
/// These tests require the use of CSearchManager class from Microsoft.Search.Interop.
/// Windows Search service needs to be running to complete the tests
/// </summary>
[TestFixture]
public class ExplorerTest
{
private List<Result> MethodWindowsIndexSearchReturnsZeroResults(Query dummyQuery, string dummyString)
{
return new List<Result>();
}
private List<Result> MethodDirectoryInfoClassSearchReturnsTwoResults(Query dummyQuery, string dummyString)
{
return new List<Result>
{
new Result
{
Title="Result 1"
},
new Result
{
Title="Result 2"
}
};
}
private bool PreviousLocationExistsReturnsTrue(string dummyString) => true;
private bool PreviousLocationNotExistReturnsFalse(string dummyString) => false;
[TestCase("C:\\SomeFolder\\", "directory='file:C:\\SomeFolder\\'")]
public void GivenWindowsIndexSearch_WhenProvidedFolderPath_ThenQueryWhereRestrictionsShouldUseDirectoryString(string path, string expectedString)
{
// Given
var queryConstructor = new QueryConstructor(new Settings());
// When
var folderPath = path;
var result = queryConstructor.QueryWhereRestrictionsForTopLevelDirectorySearch(folderPath);
// Then
Assert.IsTrue(result == expectedString,
$"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " +
$"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\\'")]
public void GivenWindowsIndexSearch_WhenSearchTypeIsTopLevelDirectorySearch_ThenQueryShouldUseExpectedString(string folderPath, string expectedString)
{
// Given
var queryConstructor = new QueryConstructor(new Settings());
//When
var queryString = queryConstructor.QueryForTopLevelDirectorySearch(folderPath);
// Then
Assert.IsTrue(queryString == expectedString,
$"Expected string: {expectedString}{Environment.NewLine} " +
$"Actual string was: {queryString}{Environment.NewLine}");
}
[TestCase("C:\\SomeFolder\\flow.launcher.sln", "SELECT TOP 100 System.FileName, System.ItemPathDisplay, System.ItemType " +
"FROM SystemIndex WHERE (System.FileName LIKE 'flow.launcher.sln%' " +
"OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033))" +
" AND directory='file:C:\\SomeFolder'")]
public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryShouldUseExpectedString(
string userSearchString, string expectedString)
{
// Given
var queryConstructor = new QueryConstructor(new Settings());
//When
var queryString = queryConstructor.QueryForTopLevelDirectorySearch(userSearchString);
// Then
Assert.IsTrue(queryString == expectedString,
$"Expected string: {expectedString}{Environment.NewLine} " +
$"Actual string was: {queryString}{Environment.NewLine}");
}
[TestCase("C:\\SomeFolder\\SomeApp", "(System.FileName LIKE 'SomeApp%' " +
"OR CONTAINS(System.FileName,'\"SomeApp*\"',1033))" +
" AND directory='file:C:\\SomeFolder'")]
public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryWhereRestrictionsShouldUseDirectoryString(
string userSearchString, string expectedString)
{
// Given
var queryConstructor = new QueryConstructor(new Settings());
//When
var queryString = queryConstructor.QueryWhereRestrictionsForTopLevelDirectorySearch(userSearchString);
// Then
Assert.IsTrue(queryString == expectedString,
$"Expected string: {expectedString}{Environment.NewLine} " +
$"Actual string was: {queryString}{Environment.NewLine}");
}
[TestCase("scope='file:'")]
public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryWhereRestrictionsShouldUseScopeString(string expectedString)
{
// Given
var queryConstructor = new QueryConstructor(new Settings());
//When
var resultString = queryConstructor.QueryWhereRestrictionsForAllFilesAndFoldersSearch();
// Then
Assert.IsTrue(resultString == expectedString,
$"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " +
$"Actual string was: {resultString}{Environment.NewLine}");
}
[TestCase("flow.launcher.sln", "SELECT TOP 100 \"System.FileName\", \"System.ItemPathDisplay\", \"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(
string userSearchString, string expectedString)
{
// Given
var queryConstructor = new QueryConstructor(new Settings());
//When
var resultString = queryConstructor.QueryForAllFilesAndFolders(userSearchString);
// Then
Assert.IsTrue(resultString == expectedString,
$"Expected query string: {expectedString}{Environment.NewLine} " +
$"Actual string was: {resultString}{Environment.NewLine}");
}
[TestCase]
public void GivenTopLevelDirectorySearch_WhenIndexSearchNotRequired_ThenSearchMethodShouldContinueDirectoryInfoClassSearch()
{
// Given
var searchManager = new SearchManager(new Settings(), new PluginInitContext());
// When
var results = searchManager.TopLevelDirectorySearchBehaviour(
MethodWindowsIndexSearchReturnsZeroResults,
MethodDirectoryInfoClassSearchReturnsTwoResults,
false,
new Query(),
"string not used");
// Then
Assert.IsTrue(results.Count == 2,
$"Expected to have 2 results from DirectoryInfoClassSearch {Environment.NewLine} " +
$"Actual number of results is {results.Count} {Environment.NewLine}");
}
[TestCase]
public void GivenTopLevelDirectorySearch_WhenIndexSearchNotRequired_ThenSearchMethodShouldNotContinueDirectoryInfoClassSearch()
{
// Given
var searchManager = new SearchManager(new Settings(), new PluginInitContext());
// When
var results = searchManager.TopLevelDirectorySearchBehaviour(
MethodWindowsIndexSearchReturnsZeroResults,
MethodDirectoryInfoClassSearchReturnsTwoResults,
true,
new Query(),
"string not used");
// Then
Assert.IsTrue(results.Count == 0,
$"Expected to have 0 results because location is indexed {Environment.NewLine} " +
$"Actual number of results is {results.Count} {Environment.NewLine}");
}
[TestCase(@"c:\\", false)]
[TestCase(@"i:\", true)]
[TestCase(@"\c:\", false)]
[TestCase(@"cc:\", false)]
[TestCase(@"\\\SomeNetworkLocation\", false)]
[TestCase("RandomFile", false)]
public void WhenGivenQuerySearchString_ThenShouldIndicateIfIsLocationPathString(string querySearchString, bool expectedResult)
{
// When, Given
var result = FilesFolders.IsLocationPathString(querySearchString);
//Then
Assert.IsTrue(result == expectedResult,
$"Expected query search string check result is: {expectedResult} {Environment.NewLine} " +
$"Actual check result is {result} {Environment.NewLine}");
}
[TestCase(@"C:\SomeFolder\SomeApp", true, @"C:\SomeFolder\")]
[TestCase(@"C:\SomeFolder\SomeApp\SomeFile", true, @"C:\SomeFolder\SomeApp\")]
[TestCase(@"C:\NonExistentFolder\SomeApp", false, "")]
public void GivenAPartialPath_WhenPreviousLevelDirectoryExists_ThenShouldReturnThePreviousDirectoryPathString(
string path, bool previousDirectoryExists, string expectedString)
{
// When
Func<string, bool> previousLocationExists = null;
if (previousDirectoryExists)
{
previousLocationExists = PreviousLocationExistsReturnsTrue;
}
else
{
previousLocationExists = PreviousLocationNotExistReturnsFalse;
}
// Given
var previousDirectoryPath = FilesFolders.GetPreviousExistingDirectory(previousLocationExists, path);
//Then
Assert.IsTrue(previousDirectoryPath == expectedString,
$"Expected path string: {expectedString} {Environment.NewLine} " +
$"Actual path string is {previousDirectoryPath} {Environment.NewLine}");
}
[TestCase(@"C:\NonExistentFolder\SomeApp", @"C:\NonExistentFolder\")]
[TestCase(@"C:\NonExistentFolder\SomeApp\", @"C:\NonExistentFolder\SomeApp\")]
public void WhenGivenAPath_ThenShouldReturnThePreviousDirectoryPathIfIncompleteOrOriginalString(
string path, string expectedString)
{
var returnedPath = FilesFolders.ReturnPreviousDirectoryIfIncompleteString(path);
//Then
Assert.IsTrue(returnedPath == expectedString,
$"Expected path string: {expectedString} {Environment.NewLine} " +
$"Actual path string is {returnedPath} {Environment.NewLine}");
}
[TestCase("c:\\SomeFolder\\>", "scope='file:c:\\SomeFolder'")]
[TestCase("c:\\SomeFolder\\>SomeName", "(System.FileName LIKE 'SomeName%' " +
"OR CONTAINS(System.FileName,'\"SomeName*\"',1033)) AND " +
"scope='file:c:\\SomeFolder'")]
public void GivenWindowsIndexSearch_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString(string path, string expectedString)
{
// Given
var queryConstructor = new QueryConstructor(new Settings());
//When
var resultString = queryConstructor.QueryWhereRestrictionsForTopLevelDirectoryAllFilesAndFoldersSearch(path);
// Then
Assert.IsTrue(resultString == expectedString,
$"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " +
$"Actual string was: {resultString}{Environment.NewLine}");
}
[TestCase("c:\\somefolder\\>somefile","*somefile*")]
[TestCase("c:\\somefolder\\somefile", "somefile*")]
[TestCase("c:\\somefolder\\", "*")]
public void GivenDirectoryInfoSearch_WhenSearchPatternHotKeyIsSearchAll_ThenSearchCriteriaShouldUseCriteriaString(string path, string expectedString)
{
// Given
var criteriaConstructor = new DirectoryInfoSearch(new PluginInitContext());
//When
var resultString = criteriaConstructor.ConstructSearchCriteria(path);
// Then
Assert.IsTrue(resultString == expectedString,
$"Expected criteria string: {expectedString}{Environment.NewLine} " +
$"Actual criteria string was: {resultString}{Environment.NewLine}");
}
}
}

View file

@ -1,4 +1,3 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29806.167
@ -18,9 +17,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher", "Flow.Launc
{1EE20B48-82FB-48A2-8086-675D6DDAB4F0} = {1EE20B48-82FB-48A2-8086-675D6DDAB4F0}
{0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {0B9DE348-9361-4940-ADB6-F5953BFFCCEC}
{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}
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4} = {787B8AA6-CA93-4C84-96FE-DF31110AD1C4}
{F35190AA-4758-4D9E-A193-E3BDF6AD3567} = {F35190AA-4758-4D9E-A193-E3BDF6AD3567}
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {9B130CC5-14FB-41FF-B310-0A95B6894C37}
{FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {FDED22C8-B637-42E8-824A-63B5B6E05A3A}
@ -41,8 +40,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.WebSea
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.ControlPanel", "Plugins\Flow.Launcher.Plugin.ControlPanel\Flow.Launcher.Plugin.ControlPanel.csproj", "{1EE20B48-82FB-48A2-8086-675D6DDAB4F0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Folder", "Plugins\Flow.Launcher.Plugin.Folder\Flow.Launcher.Plugin.Folder.csproj", "{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.PluginIndicator", "Plugins\Flow.Launcher.Plugin.PluginIndicator\Flow.Launcher.Plugin.PluginIndicator.csproj", "{FDED22C8-B637-42E8-824A-63B5B6E05A3A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Sys", "Plugins\Flow.Launcher.Plugin.Sys\Flow.Launcher.Plugin.Sys.csproj", "{0B9DE348-9361-4940-ADB6-F5953BFFCCEC}"
@ -58,12 +55,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.gitattributes = .gitattributes
.gitignore = .gitignore
appveyor.yml = appveyor.yml
Scripts\flowlauncher.nuspec = Scripts\flowlauncher.nuspec
LICENSE = LICENSE
Scripts\post_build.ps1 = Scripts\post_build.ps1
README.md = README.md
SolutionAssemblyInfo.cs = SolutionAssemblyInfo.cs
Scripts\flowlauncher.nuspec = Scripts\flowlauncher.nuspec
Scripts\flowlauncher.plugin.nuspec = Scripts\flowlauncher.plugin.nuspec
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HelloWorldCSharp", "Plugins\HelloWorldCSharp\HelloWorldCSharp.csproj", "{03FFA443-5F50-48D5-8869-F3DF316803AA}"
@ -76,6 +72,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Calcul
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
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -194,18 +192,6 @@ Global
{1EE20B48-82FB-48A2-8086-675D6DDAB4F0}.Release|x64.Build.0 = Release|Any CPU
{1EE20B48-82FB-48A2-8086-675D6DDAB4F0}.Release|x86.ActiveCfg = Release|Any CPU
{1EE20B48-82FB-48A2-8086-675D6DDAB4F0}.Release|x86.Build.0 = Release|Any CPU
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Debug|x64.ActiveCfg = Debug|Any CPU
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Debug|x64.Build.0 = Debug|Any CPU
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Debug|x86.ActiveCfg = Debug|Any CPU
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Debug|x86.Build.0 = Debug|Any CPU
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Release|Any CPU.Build.0 = Release|Any CPU
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Release|x64.ActiveCfg = Release|Any CPU
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Release|x64.Build.0 = Release|Any CPU
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Release|x86.ActiveCfg = Release|Any CPU
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Release|x86.Build.0 = Release|Any CPU
{FDED22C8-B637-42E8-824A-63B5B6E05A3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FDED22C8-B637-42E8-824A-63B5B6E05A3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FDED22C8-B637-42E8-824A-63B5B6E05A3A}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -327,6 +313,18 @@ Global
{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
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Debug|x64.Build.0 = Debug|Any CPU
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Debug|x86.ActiveCfg = Debug|Any CPU
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Debug|x86.Build.0 = Debug|Any CPU
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Release|Any CPU.Build.0 = Release|Any CPU
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Release|x64.ActiveCfg = Release|Any CPU
{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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -336,7 +334,6 @@ Global
{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}
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{A3DCCBCA-ACC1-421D-B16E-210896234C26} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
@ -347,6 +344,7 @@ Global
{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}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F26ACB50-3F6C-4907-B0C9-1ADACC1D0DED}

View file

@ -1,4 +1,4 @@
<Window x:Class="Flow.Launcher.ActionKeywords"
<Window x:Class="Flow.Launcher.ActionKeywords"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ActionKeywords"
@ -6,38 +6,36 @@
ResizeMode="NoResize"
Loaded="ActionKeyword_OnLoaded"
WindowStartupLocation="CenterScreen"
Height="200" Width="600">
Height="250" Width="500">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition Height="60"/>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="170" />
<ColumnDefinition Width="150" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Margin="10" FontSize="14" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"
HorizontalAlignment="Right" Text="{DynamicResource oldActionKeyword}" />
<TextBlock x:Name="tbOldActionKeyword" Margin="10" FontSize="14" Grid.Row="0" Grid.Column="1"
VerticalAlignment="Center" HorizontalAlignment="Left">
Old ActionKeywords:
</TextBlock>
<TextBlock Margin="10" FontSize="14" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center"
HorizontalAlignment="Right" Text="{DynamicResource newActionKeyword}" />
<TextBlock FontSize="14" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center"
HorizontalAlignment="Left" Text="{DynamicResource currentActionKeywords}" />
<TextBlock x:Name="tbOldActionKeyword" Grid.Row="0" Grid.Column="1" Margin="170 10 10 10" FontSize="14"
VerticalAlignment="Center" HorizontalAlignment="Left" />
<TextBlock FontSize="14" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center"
HorizontalAlignment="Left" Text="{DynamicResource newActionKeyword}" />
<StackPanel Grid.Row="1" Orientation="Horizontal" Grid.Column="1">
<TextBox x:Name="tbAction" Margin="10" Width="400" VerticalAlignment="Center" HorizontalAlignment="Left" />
<TextBox x:Name="tbAction" Margin="170 10 15 10" Width="105" VerticalAlignment="Center" HorizontalAlignment="Left" />
</StackPanel>
<TextBlock Grid.Row="2" Grid.ColumnSpan="1" Grid.Column="1" Padding="5" Foreground="Gray"
<TextBlock Grid.Row="2" Grid.ColumnSpan="1" Grid.Column="1" Foreground="Gray"
Text="{DynamicResource actionkeyword_tips}" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="3" Grid.Column="1">
<Button x:Name="btnCancel" Click="BtnCancel_OnClick" Margin="10 0 10 0" Width="80" Height="25"
<Button x:Name="btnCancel" Click="BtnCancel_OnClick" Margin="10 0 10 0" Width="80" Height="30"
Content="{DynamicResource cancel}" />
<Button x:Name="btnDone" Margin="10 0 10 0" Width="80" Height="25" Click="btnDone_OnClick">
<Button x:Name="btnDone" Margin="10 0 10 0" Width="80" Height="30" Click="btnDone_OnClick">
<TextBlock x:Name="lblAdd" Text="{DynamicResource done}" />
</Button>
</StackPanel>

View file

@ -4,30 +4,33 @@ using Flow.Launcher.Core.Resource;
using Flow.Launcher.Infrastructure.Exception;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.ViewModel;
namespace Flow.Launcher
{
public partial class ActionKeywords : Window
{
private PluginPair _plugin;
private Settings _settings;
private readonly Internationalization _translater = InternationalizationManager.Instance;
private readonly PluginPair plugin;
private Settings settings;
private readonly Internationalization translater = InternationalizationManager.Instance;
private readonly PluginViewModel pluginViewModel;
public ActionKeywords(string pluginId, Settings settings)
public ActionKeywords(string pluginId, Settings settings, PluginViewModel pluginViewModel)
{
InitializeComponent();
_plugin = PluginManager.GetPluginForId(pluginId);
_settings = settings;
if (_plugin == null)
plugin = PluginManager.GetPluginForId(pluginId);
this.settings = settings;
this.pluginViewModel = pluginViewModel;
if (plugin == null)
{
MessageBox.Show(_translater.GetTranslation("cannotFindSpecifiedPlugin"));
MessageBox.Show(translater.GetTranslation("cannotFindSpecifiedPlugin"));
Close();
}
}
private void ActionKeyword_OnLoaded(object sender, RoutedEventArgs e)
{
tbOldActionKeyword.Text = string.Join(Query.ActionKeywordSeperater, _plugin.Metadata.ActionKeywords.ToArray());
tbOldActionKeyword.Text = string.Join(Query.ActionKeywordSeperater, plugin.Metadata.ActionKeywords.ToArray());
tbAction.Focus();
}
@ -38,19 +41,17 @@ namespace Flow.Launcher
private void btnDone_OnClick(object sender, RoutedEventArgs _)
{
var oldActionKeyword = _plugin.Metadata.ActionKeywords[0];
var oldActionKeyword = plugin.Metadata.ActionKeywords[0];
var newActionKeyword = tbAction.Text.Trim();
newActionKeyword = newActionKeyword.Length > 0 ? newActionKeyword : "*";
if (!PluginManager.ActionKeywordRegistered(newActionKeyword))
if (!pluginViewModel.IsActionKeywordRegistered(newActionKeyword))
{
var id = _plugin.Metadata.ID;
PluginManager.ReplaceActionKeyword(id, oldActionKeyword, newActionKeyword);
MessageBox.Show(_translater.GetTranslation("success"));
pluginViewModel.ChangeActionKeyword(newActionKeyword, oldActionKeyword);
Close();
}
else
{
string msg = _translater.GetTranslation("newActionKeywordsHasBeenAssigned");
string msg = translater.GetTranslation("newActionKeywordsHasBeenAssigned");
MessageBox.Show(msg);
}
}

View file

@ -1,4 +1,4 @@
<Window x:Class="Flow.Launcher.CustomQueryHotkeySetting"
<Window x:Class="Flow.Launcher.CustomQueryHotkeySetting"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:flowlauncher="clr-namespace:Flow.Launcher"
@ -6,6 +6,12 @@
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen"
Title="Custom Plugin Hotkey" Height="200" Width="674.766">
<Window.InputBindings>
<KeyBinding Key="Escape" Command="Close"/>
</Window.InputBindings>
<Window.CommandBindings>
<CommandBinding Command="Close" Executed="cmdEsc_OnPress"/>
</Window.CommandBindings>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
@ -18,7 +24,7 @@
</Grid.ColumnDefinitions>
<TextBlock Margin="10" FontSize="14" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"
HorizontalAlignment="Right" Text="{DynamicResource hotkey}" />
<flowlauncher:HotkeyControl x:Name="ctlHotkey" Margin="10" Grid.Column="1" />
<flowlauncher:HotkeyControl x:Name="ctlHotkey" Margin="10,0,10,0" Grid.Column="1" VerticalAlignment="Center" Height="32" />
<TextBlock Margin="10" FontSize="14" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center"
HorizontalAlignment="Right" Text="{DynamicResource actionKeyword}" />
@ -29,9 +35,9 @@
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="2" Grid.Column="1">
<Button x:Name="btnCancel" Click="BtnCancel_OnClick" Margin="10 0 10 0" Width="80" Height="25"
<Button x:Name="btnCancel" Click="BtnCancel_OnClick" Margin="10 0 10 0" Width="80" Height="32"
Content="{DynamicResource cancel}" />
<Button x:Name="btnAdd" Margin="10 0 10 0" Width="80" Height="25" Click="btnAdd_OnClick">
<Button x:Name="btnAdd" Margin="10 0 10 0" Width="80" Height="32" Click="btnAdd_OnClick">
<TextBlock x:Name="lblAdd" Text="{DynamicResource done}" />
</Button>
</StackPanel>

View file

@ -1,13 +1,13 @@
using System;
using System.Collections.Generic;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Infrastructure.Hotkey;
using Flow.Launcher.Infrastructure.UserSettings;
using NHotkey;
using NHotkey.Wpf;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using NHotkey;
using NHotkey.Wpf;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Infrastructure.Hotkey;
using Flow.Launcher.Infrastructure.UserSettings;
using System.Windows.Input;
namespace Flow.Launcher
{
@ -125,5 +125,10 @@ namespace Flow.Launcher
MessageBox.Show(errorMsg);
}
}
private void cmdEsc_OnPress(object sender, ExecutedRoutedEventArgs e)
{
Close();
}
}
}

View file

@ -62,6 +62,14 @@
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Themes\*.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="InputSimulator" Version="1.0.4" />
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" />
@ -74,13 +82,12 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="PropertyChanged.Fody" Version="3.2.8">
<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>

View file

@ -36,8 +36,8 @@
<system:String x:Key="actionKeywords">Nøgleord</system:String>
<system:String x:Key="pluginDirectory">Plugin bibliotek</system:String>
<system:String x:Key="author">Forfatter</system:String>
<system:String x:Key="plugin_init_time">Initaliseringstid: {0}ms</system:String>
<system:String x:Key="plugin_query_time">Søgetid: {0}ms</system:String>
<system:String x:Key="plugin_init_time">Initaliseringstid:</system:String>
<system:String x:Key="plugin_query_time">Søgetid:</system:String>
<!--Setting Theme-->
<system:String x:Key="theme">Tema</system:String>

View file

@ -1,4 +1,4 @@
<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-->
@ -36,8 +36,8 @@
<system:String x:Key="actionKeywords">Aktionsschlüsselwörter</system:String>
<system:String x:Key="pluginDirectory">Pluginordner</system:String>
<system:String x:Key="author">Autor</system:String>
<system:String x:Key="plugin_init_time">Initialisierungszeit: {0}ms</system:String>
<system:String x:Key="plugin_query_time">Abfragezeit: {0}ms</system:String>
<system:String x:Key="plugin_init_time">Initialisierungszeit:</system:String>
<system:String x:Key="plugin_query_time">Abfragezeit:</system:String>
<!--Setting Theme-->
<system:String x:Key="theme">Theme</system:String>

View file

@ -1,4 +1,4 @@
<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-->
@ -40,11 +40,13 @@
<system:String x:Key="plugin">Plugin</system:String>
<system:String x:Key="browserMorePlugins">Find more plugins</system:String>
<system:String x:Key="disable">Disable</system:String>
<system:String x:Key="actionKeywords">Action keywords</system:String>
<system:String x:Key="actionKeywords">Action keyword:</system:String>
<system:String x:Key="currentActionKeywords">Current action keyword:</system:String>
<system:String x:Key="newActionKeyword">New action keyword:</system:String>
<system:String x:Key="pluginDirectory">Plugin Directory</system:String>
<system:String x:Key="author">Author</system:String>
<system:String x:Key="plugin_init_time">Init time: {0}ms</system:String>
<system:String x:Key="plugin_query_time">Query time: {0}ms</system:String>
<system:String x:Key="plugin_init_time">Init time:</system:String>
<system:String x:Key="plugin_query_time">Query time:</system:String>
<!--Setting Theme-->
<system:String x:Key="theme">Theme</system:String>

View file

@ -40,8 +40,8 @@
<system:String x:Key="actionKeywords">Mot-clé d'action :</system:String>
<system:String x:Key="pluginDirectory">Répertoire</system:String>
<system:String x:Key="author">Auteur </system:String>
<system:String x:Key="plugin_init_time">Chargement : {0}ms</system:String>
<system:String x:Key="plugin_query_time">Utilisation : {0}ms</system:String>
<system:String x:Key="plugin_init_time">Chargement :</system:String>
<system:String x:Key="plugin_query_time">Utilisation :</system:String>
<!--Setting Theme-->
<system:String x:Key="theme">Thèmes</system:String>

View file

@ -1,4 +1,4 @@
<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-->
@ -40,8 +40,8 @@
<system:String x:Key="actionKeywords">Parole chiave</system:String>
<system:String x:Key="pluginDirectory">Cartella Plugin</system:String>
<system:String x:Key="author">Autore</system:String>
<system:String x:Key="plugin_init_time">Tempo di avvio: {0}ms</system:String>
<system:String x:Key="plugin_query_time">Tempo ricerca: {0}ms</system:String>
<system:String x:Key="plugin_init_time">Tempo di avvio:</system:String>
<system:String x:Key="plugin_query_time">Tempo ricerca:</system:String>
<!--Setting Theme-->
<system:String x:Key="theme">Tema</system:String>

View file

@ -1,4 +1,4 @@
<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-->
@ -41,8 +41,8 @@
<system:String x:Key="actionKeywords">キーワード</system:String>
<system:String x:Key="pluginDirectory">プラグイン・ディレクトリ</system:String>
<system:String x:Key="author">作者</system:String>
<system:String x:Key="plugin_init_time">初期化時間: {0}ms</system:String>
<system:String x:Key="plugin_query_time">クエリ時間: {0}ms</system:String>
<system:String x:Key="plugin_init_time">初期化時間:</system:String>
<system:String x:Key="plugin_query_time">クエリ時間:</system:String>
<!--Setting Theme-->
<system:String x:Key="theme">テーマ</system:String>

View file

@ -1,4 +1,4 @@
<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-->
@ -40,8 +40,8 @@
<system:String x:Key="actionKeywords">액션 키워드</system:String>
<system:String x:Key="pluginDirectory">플러그인 디렉토리</system:String>
<system:String x:Key="author">저자</system:String>
<system:String x:Key="plugin_init_time">초기화 시간: {0}ms</system:String>
<system:String x:Key="plugin_query_time">쿼리 시간: {0}ms</system:String>
<system:String x:Key="plugin_init_time">초기화 시간:</system:String>
<system:String x:Key="plugin_query_time">쿼리 시간:</system:String>
<!--Setting Theme-->
<system:String x:Key="theme">테마</system:String>

View file

@ -40,8 +40,8 @@
<system:String x:Key="actionKeywords">Handlingsnøkkelord</system:String>
<system:String x:Key="pluginDirectory">Utvidelseskatalog</system:String>
<system:String x:Key="author">Forfatter</system:String>
<system:String x:Key="plugin_init_time">Oppstartstid: {0}ms</system:String>
<system:String x:Key="plugin_query_time">Spørringstid: {0}ms</system:String>
<system:String x:Key="plugin_init_time">Oppstartstid:</system:String>
<system:String x:Key="plugin_query_time">Spørringstid:</system:String>
<!--Setting Theme-->
<system:String x:Key="theme">Tema</system:String>

View file

@ -1,4 +1,4 @@
<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-->
@ -36,8 +36,8 @@
<system:String x:Key="actionKeywords">Action terfwoorden</system:String>
<system:String x:Key="pluginDirectory">Plugin map</system:String>
<system:String x:Key="author">Auteur</system:String>
<system:String x:Key="plugin_init_time">Init tijd: {0}ms</system:String>
<system:String x:Key="plugin_query_time">Query tijd: {0}ms</system:String>
<system:String x:Key="plugin_init_time">Init tijd:</system:String>
<system:String x:Key="plugin_query_time">Query tijd:</system:String>
<!--Setting Theme-->
<system:String x:Key="theme">Thema</system:String>

View file

@ -1,4 +1,4 @@
<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-->
@ -36,8 +36,8 @@
<system:String x:Key="actionKeywords">Wyzwalacze</system:String>
<system:String x:Key="pluginDirectory">Folder wtyczki</system:String>
<system:String x:Key="author">Autor</system:String>
<system:String x:Key="plugin_init_time">Czas ładowania: {0}ms</system:String>
<system:String x:Key="plugin_query_time">Czas zapytania: {0}ms</system:String>
<system:String x:Key="plugin_init_time">Czas ładowania:</system:String>
<system:String x:Key="plugin_query_time">Czas zapytania:</system:String>
<!--Setting Theme-->
<system:String x:Key="theme">Skórka</system:String>

View file

@ -1,4 +1,4 @@
<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-->
@ -40,8 +40,8 @@
<system:String x:Key="actionKeywords">Palavras-chave de ação</system:String>
<system:String x:Key="pluginDirectory">Diretório de Plugins</system:String>
<system:String x:Key="author">Autor</system:String>
<system:String x:Key="plugin_init_time">Tempo de inicialização: {0}ms</system:String>
<system:String x:Key="plugin_query_time">Tempo de consulta: {0}ms</system:String>
<system:String x:Key="plugin_init_time">Tempo de inicialização:</system:String>
<system:String x:Key="plugin_query_time">Tempo de consulta:</system:String>
<!--Setting Theme-->
<system:String x:Key="theme">Tema</system:String>

View file

@ -1,4 +1,4 @@
<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-->
@ -36,8 +36,8 @@
<system:String x:Key="actionKeywords">Ключевое слово</system:String>
<system:String x:Key="pluginDirectory">Папка</system:String>
<system:String x:Key="author">Автор</system:String>
<system:String x:Key="plugin_init_time">Инициализация: {0}ms</system:String>
<system:String x:Key="plugin_query_time">Запрос: {0}ms</system:String>
<system:String x:Key="plugin_init_time">Инициализация:</system:String>
<system:String x:Key="plugin_query_time">Запрос:</system:String>
<!--Setting Theme-->
<system:String x:Key="theme">Темы</system:String>

View file

@ -1,4 +1,4 @@
<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-->
@ -41,8 +41,8 @@
<system:String x:Key="actionKeywords">Skratka akcie</system:String>
<system:String x:Key="pluginDirectory">Priečinok s pluginmy</system:String>
<system:String x:Key="author">Autor</system:String>
<system:String x:Key="plugin_init_time">Čas inic.: {0}ms</system:String>
<system:String x:Key="plugin_query_time">Čas dopytu: {0}ms</system:String>
<system:String x:Key="plugin_init_time">Čas inic.:</system:String>
<system:String x:Key="plugin_query_time">Čas dopytu:</system:String>
<!--Setting Theme-->
<system:String x:Key="theme">Motív</system:String>

View file

@ -1,4 +1,4 @@
<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-->
@ -40,8 +40,8 @@
<system:String x:Key="actionKeywords">Ključne reči</system:String>
<system:String x:Key="pluginDirectory">Plugin direktorijum</system:String>
<system:String x:Key="author">Autor</system:String>
<system:String x:Key="plugin_init_time">Vreme inicijalizacije: {0}ms</system:String>
<system:String x:Key="plugin_query_time">Vreme upita: {0}ms</system:String>
<system:String x:Key="plugin_init_time">Vreme inicijalizacije:</system:String>
<system:String x:Key="plugin_query_time">Vreme upita:</system:String>
<!--Setting Theme-->
<system:String x:Key="theme">Tema</system:String>

View file

@ -1,4 +1,4 @@
<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-->
@ -42,8 +42,8 @@
<system:String x:Key="actionKeywords">Anahtar Kelimeler</system:String>
<system:String x:Key="pluginDirectory">Eklenti Klasörü</system:String>
<system:String x:Key="author">Yapımcı</system:String>
<system:String x:Key="plugin_init_time">Açılış Süresi: {0}ms</system:String>
<system:String x:Key="plugin_query_time">Sorgu Süresi: {0}ms</system:String>
<system:String x:Key="plugin_init_time">Açılış Süresi:</system:String>
<system:String x:Key="plugin_query_time">Sorgu Süresi:</system:String>
<!--Setting Theme-->
<system:String x:Key="theme">Temalar</system:String>

View file

@ -1,4 +1,4 @@
<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-->
@ -36,8 +36,8 @@
<system:String x:Key="actionKeywords">Ключове слово</system:String>
<system:String x:Key="pluginDirectory">Директорія плагіну</system:String>
<system:String x:Key="author">Автор</system:String>
<system:String x:Key="plugin_init_time">Ініціалізація: {0}ms</system:String>
<system:String x:Key="plugin_query_time">Запит: {0}ms</system:String>
<system:String x:Key="plugin_init_time">Ініціалізація:</system:String>
<system:String x:Key="plugin_query_time">Запит:</system:String>
<!--Setting Theme-->
<system:String x:Key="theme">Теми</system:String>

View file

@ -1,4 +1,4 @@
<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">
<!--主窗体-->
@ -41,8 +41,8 @@
<system:String x:Key="actionKeywords">触发关键字</system:String>
<system:String x:Key="pluginDirectory">插件目录</system:String>
<system:String x:Key="author">作者</system:String>
<system:String x:Key="plugin_init_time">加载耗时 {0}ms</system:String>
<system:String x:Key="plugin_query_time">查询耗时 {0}ms</system:String>
<system:String x:Key="plugin_init_time">加载耗时</system:String>
<system:String x:Key="plugin_query_time">查询耗时</system:String>
<!--设置,主题-->
<system:String x:Key="theme">主题</system:String>

View file

@ -1,4 +1,4 @@
<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">
<!--主視窗-->
@ -36,8 +36,8 @@
<system:String x:Key="actionKeywords">觸發關鍵字</system:String>
<system:String x:Key="pluginDirectory">外掛資料夾</system:String>
<system:String x:Key="author">作者</system:String>
<system:String x:Key="plugin_init_time">載入耗時:{0}ms</system:String>
<system:String x:Key="plugin_query_time">查詢耗時:{0}ms</system:String>
<system:String x:Key="plugin_init_time">載入耗時:</system:String>
<system:String x:Key="plugin_query_time">查詢耗時:</system:String>
<!--設定,主題-->
<system:String x:Key="theme">主題</system:String>

View file

@ -175,14 +175,16 @@
<TextBlock Text="{DynamicResource actionKeywords}"
Visibility="{Binding ActionKeywordsVisibility}"
Margin="20 0 0 0" MaxWidth="100"/>
Margin="20 0 0 0"/>
<TextBlock Text="{Binding ActionKeywordsText}"
Visibility="{Binding ActionKeywordsVisibility}"
ToolTip="Change Action Keywords"
Margin="5 0 0 0" Cursor="Hand" Foreground="Blue"
MouseUp="OnPluginActionKeywordsClick" MaxWidth="100" />
<TextBlock Text="{Binding InitilizaTime}" Margin="10 0 0 0" MaxWidth="100"/>
<TextBlock Text="{Binding QueryTime}" Margin="10 0 0 0" MaxWidth="100"/>
<TextBlock Text="{DynamicResource plugin_init_time}" Margin="10 0 0 0" MaxWidth="100"/>
<TextBlock Text="{Binding InitilizaTime}" Margin="5 0 0 0" MaxWidth="100"/>
<TextBlock Text="{DynamicResource plugin_query_time}" Margin="10 0 0 0" MaxWidth="100"/>
<TextBlock Text="{Binding QueryTime}" Margin="5 0 0 0" MaxWidth="100"/>
<TextBlock Text="{DynamicResource pluginDirectory}"
MaxWidth="100" Cursor="Hand" Margin="40 0 0 0"
MouseUp="OnPluginDirecotyClick" Foreground="Blue" />

View file

@ -1,5 +1,4 @@
using System;
using System.Diagnostics;
using System;
using System.IO;
using System.Windows;
using System.Windows.Input;
@ -21,17 +20,17 @@ namespace Flow.Launcher
{
private const string StartupPath = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
public readonly IPublicAPI _api;
private Settings _settings;
private SettingWindowViewModel _viewModel;
public readonly IPublicAPI API;
private Settings settings;
private SettingWindowViewModel viewModel;
public SettingWindow(IPublicAPI api, SettingWindowViewModel viewModel)
{
InitializeComponent();
_settings = viewModel.Settings;
settings = viewModel.Settings;
DataContext = viewModel;
_viewModel = viewModel;
_api = api;
this.viewModel = viewModel;
API = api;
}
#region General
@ -94,7 +93,7 @@ namespace Flow.Launcher
var pythonPath = Path.Combine(pythonDirectory, PluginsLoader.PythonExecutable);
if (File.Exists(pythonPath))
{
_settings.PluginSettings.PythonDirectory = pythonDirectory;
settings.PluginSettings.PythonDirectory = pythonDirectory;
MessageBox.Show("Remember to restart Flow Launcher use new Python path");
}
else
@ -111,7 +110,7 @@ namespace Flow.Launcher
private void OnHotkeyControlLoaded(object sender, RoutedEventArgs e)
{
HotkeyControl.SetHotkey(_viewModel.Settings.Hotkey, false);
HotkeyControl.SetHotkey(viewModel.Settings.Hotkey, false);
}
void OnHotkeyChanged(object sender, EventArgs e)
@ -129,8 +128,8 @@ namespace Flow.Launcher
Application.Current.MainWindow.Visibility = Visibility.Hidden;
}
});
RemoveHotkey(_settings.Hotkey);
_settings.Hotkey = HotkeyControl.CurrentHotkey.ToString();
RemoveHotkey(settings.Hotkey);
settings.Hotkey = HotkeyControl.CurrentHotkey.ToString();
}
}
@ -159,7 +158,7 @@ namespace Flow.Launcher
private void OnDeleteCustomHotkeyClick(object sender, RoutedEventArgs e)
{
var item = _viewModel.SelectedCustomPluginHotkey;
var item = viewModel.SelectedCustomPluginHotkey;
if (item == null)
{
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
@ -173,17 +172,17 @@ namespace Flow.Launcher
MessageBox.Show(deleteWarning, InternationalizationManager.Instance.GetTranslation("delete"),
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
_settings.CustomPluginHotkeys.Remove(item);
settings.CustomPluginHotkeys.Remove(item);
RemoveHotkey(item.Hotkey);
}
}
private void OnnEditCustomHotkeyClick(object sender, RoutedEventArgs e)
{
var item = _viewModel.SelectedCustomPluginHotkey;
var item = viewModel.SelectedCustomPluginHotkey;
if (item != null)
{
CustomQueryHotkeySetting window = new CustomQueryHotkeySetting(this, _settings);
CustomQueryHotkeySetting window = new CustomQueryHotkeySetting(this, settings);
window.UpdateItem(item);
window.ShowDialog();
}
@ -195,7 +194,7 @@ namespace Flow.Launcher
private void OnAddCustomeHotkeyClick(object sender, RoutedEventArgs e)
{
new CustomQueryHotkeySetting(this, _settings).ShowDialog();
new CustomQueryHotkeySetting(this, settings).ShowDialog();
}
#endregion
@ -204,17 +203,17 @@ namespace Flow.Launcher
private void OnPluginToggled(object sender, RoutedEventArgs e)
{
var id = _viewModel.SelectedPlugin.PluginPair.Metadata.ID;
var id = viewModel.SelectedPlugin.PluginPair.Metadata.ID;
// used to sync the current status from the plugin manager into the setting to keep consistency after save
_settings.PluginSettings.Plugins[id].Disabled = _viewModel.SelectedPlugin.PluginPair.Metadata.Disabled;
settings.PluginSettings.Plugins[id].Disabled = viewModel.SelectedPlugin.PluginPair.Metadata.Disabled;
}
private void OnPluginActionKeywordsClick(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
var id = _viewModel.SelectedPlugin.PluginPair.Metadata.ID;
ActionKeywords changeKeywordsWindow = new ActionKeywords(id, _settings);
var id = viewModel.SelectedPlugin.PluginPair.Metadata.ID;
ActionKeywords changeKeywordsWindow = new ActionKeywords(id, settings, viewModel.SelectedPlugin);
changeKeywordsWindow.ShowDialog();
}
}
@ -223,7 +222,7 @@ namespace Flow.Launcher
{
if (e.ChangedButton == MouseButton.Left)
{
var website = _viewModel.SelectedPlugin.PluginPair.Metadata.Website;
var website = viewModel.SelectedPlugin.PluginPair.Metadata.Website;
if (!string.IsNullOrEmpty(website))
{
var uri = new Uri(website);
@ -239,7 +238,7 @@ namespace Flow.Launcher
{
if (e.ChangedButton == MouseButton.Left)
{
var directory = _viewModel.SelectedPlugin.PluginPair.Metadata.PluginDirectory;
var directory = viewModel.SelectedPlugin.PluginPair.Metadata.PluginDirectory;
if (!string.IsNullOrEmpty(directory))
FilesFolders.OpenPath(directory);
}
@ -250,7 +249,7 @@ namespace Flow.Launcher
private void OnTestProxyClick(object sender, RoutedEventArgs e)
{ // TODO: change to command
var msg = _viewModel.TestProxy();
var msg = viewModel.TestProxy();
MessageBox.Show(msg); // TODO: add message box service
}
@ -258,7 +257,7 @@ namespace Flow.Launcher
private async void OnCheckUpdates(object sender, RoutedEventArgs e)
{
_viewModel.UpdateApp(); // TODO: change to command
viewModel.UpdateApp(); // TODO: change to command
}
private void OnRequestNavigate(object sender, RequestNavigateEventArgs e)
@ -269,7 +268,7 @@ namespace Flow.Launcher
private void OnClosed(object sender, EventArgs e)
{
_viewModel.Save();
viewModel.Save();
}
private void OnCloseExecuted(object sender, ExecutedRoutedEventArgs e)

View file

@ -1,8 +1,9 @@
using System.Windows;
using System.Windows;
using System.Windows.Media;
using Flow.Launcher.Plugin;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Infrastructure.Image;
using Flow.Launcher.Core.Plugin;
namespace Flow.Launcher.ViewModel
{
@ -22,8 +23,17 @@ namespace Flow.Launcher.ViewModel
}
}
public Visibility ActionKeywordsVisibility => PluginPair.Metadata.ActionKeywords.Count > 1 ? Visibility.Collapsed : Visibility.Visible;
public string InitilizaTime => string.Format(_translator.GetTranslation("plugin_init_time"), PluginPair.Metadata.InitTime);
public string QueryTime => string.Format(_translator.GetTranslation("plugin_query_time"), PluginPair.Metadata.AvgQueryTime);
public string InitilizaTime => PluginPair.Metadata.InitTime.ToString() + "ms";
public string QueryTime => PluginPair.Metadata.AvgQueryTime + "ms";
public string ActionKeywordsText => string.Join(Query.ActionKeywordSeperater, PluginPair.Metadata.ActionKeywords);
public void ChangeActionKeyword(string newActionKeyword, string oldActionKeyword)
{
PluginManager.ReplaceActionKeyword(PluginPair.Metadata.ID, oldActionKeyword, newActionKeyword);
OnPropertyChanged(nameof(ActionKeywordsText));
}
public bool IsActionKeywordRegistered(string newActionKeyword) => PluginManager.ActionKeywordRegistered(newActionKeyword);
}
}

View file

@ -80,8 +80,4 @@
<PackageReference Include="UnidecodeSharp" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View file

@ -99,7 +99,6 @@
<ItemGroup>
<Folder Include="Images\" />
<Folder Include="ViewModels\" />
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>

View file

@ -101,8 +101,4 @@
<PackageReference Include="System.Runtime" Version="4.3.1" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View file

@ -101,8 +101,4 @@
<PackageReference Include="System.Runtime" Version="4.3.1" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View file

@ -128,8 +128,4 @@
<PackageReference Include="System.Runtime" Version="4.3.1" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,287 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Plugin.SharedCommands;
using Flow.Launcher.Plugin.Explorer.Search;
using Flow.Launcher.Plugin.Explorer.Search.FolderLinks;
using System.Linq;
using System.Reflection;
namespace Flow.Launcher.Plugin.Explorer
{
internal class ContextMenu : IContextMenu
{
private PluginInitContext Context { get; set; }
private Settings Settings { get; set; }
public ContextMenu(PluginInitContext context, Settings settings)
{
Context = context;
Settings = settings;
}
public List<Result> LoadContextMenus(Result selectedResult)
{
var contextMenus = new List<Result>();
if (selectedResult.ContextData is SearchResult record)
{
if (record.Type == ResultType.File)
contextMenus.Add(CreateOpenWithEditorResult(record));
if (record.Type == ResultType.Folder && record.WindowsIndexed)
contextMenus.Add(CreateAddToIndexSearchExclusionListResult(record));
contextMenus.Add(CreateOpenContainingFolderResult(record));
contextMenus.Add(CreateOpenWindowsIndexingOptions());
if (record.ShowIndexState)
contextMenus.Add(new Result {Title = "From index search: " + (record.WindowsIndexed ? "Yes" : "No"),
SubTitle = "Location: " + record.FullPath,
Score = 501, IcoPath = Constants.IndexImagePath});
var icoPath = (record.Type == ResultType.File) ? Constants.FileImagePath : Constants.FolderImagePath;
var fileOrFolder = (record.Type == ResultType.File) ? "file" : "folder";
contextMenus.Add(new Result
{
Title = Context.API.GetTranslation("plugin_explorer_copypath"),
SubTitle = $"Copy the current {fileOrFolder} path to clipboard",
Action = (context) =>
{
try
{
Clipboard.SetText(record.FullPath);
return true;
}
catch (Exception e)
{
var message = "Fail to set text in clipboard";
LogException(message, e);
Context.API.ShowMsg(message);
return false;
}
},
IcoPath = Constants.CopyImagePath
});
contextMenus.Add(new Result
{
Title = Context.API.GetTranslation("plugin_explorer_copyfilefolder") + $" {fileOrFolder}",
SubTitle = $"Copy the {fileOrFolder} to clipboard",
Action = (context) =>
{
try
{
Clipboard.SetFileDropList(new System.Collections.Specialized.StringCollection { record.FullPath });
return true;
}
catch (Exception e)
{
var message = $"Fail to set {fileOrFolder} in clipboard";
LogException(message, e);
Context.API.ShowMsg(message);
return false;
}
},
IcoPath = icoPath
});
if (record.Type == ResultType.File || record.Type == ResultType.Folder)
contextMenus.Add(new Result
{
Title = Context.API.GetTranslation("plugin_explorer_deletefilefolder") + $" {fileOrFolder}",
SubTitle = Context.API.GetTranslation("plugin_explorer_deletefilefolder_subtitle") + $" {fileOrFolder}",
Action = (context) =>
{
try
{
if (record.Type == ResultType.File)
File.Delete(record.FullPath);
else
Directory.Delete(record.FullPath, true);
}
catch (Exception e)
{
var message = $"Fail to delete {fileOrFolder} at {record.FullPath}";
LogException(message, e);
Context.API.ShowMsg(message);
return false;
}
return true;
},
IcoPath = Constants.DeleteFileFolderImagePath
});
if (record.Type == ResultType.File && CanRunAsDifferentUser(record.FullPath))
contextMenus.Add(new Result
{
Title = Context.API.GetTranslation("plugin_explorer_runasdifferentuser"),
SubTitle = Context.API.GetTranslation("plugin_explorer_runasdifferentuser_subtitle"),
Action = (context) =>
{
try
{
Task.Run(() => ShellCommand.RunAsDifferentUser(record.FullPath.SetProcessStartInfo()));
}
catch (FileNotFoundException e)
{
var name = "Plugin: Folder";
var message = $"File not found: {e.Message}";
Context.API.ShowMsg(name, message);
}
return true;
},
IcoPath = Constants.DifferentUserIconImagePath
});
}
return contextMenus;
}
private Result CreateOpenContainingFolderResult(SearchResult record)
{
return new Result
{
Title = Context.API.GetTranslation("plugin_explorer_opencontainingfolder"),
SubTitle = Context.API.GetTranslation("plugin_explorer_opencontainingfolder_subtitle"),
Action = _ =>
{
try
{
Process.Start("explorer.exe", $" /select,\"{record.FullPath}\"");
}
catch (Exception e)
{
var message = $"Fail to open file at {record.FullPath}";
LogException(message, e);
Context.API.ShowMsg(message);
return false;
}
return true;
},
IcoPath = Constants.FolderImagePath
};
}
private Result CreateOpenWithEditorResult(SearchResult record)
{
string editorPath = "Notepad.exe"; // TODO add the ability to create a custom editor
var name = Context.API.GetTranslation("plugin_explorer_openwitheditor")
+ " " + Path.GetFileNameWithoutExtension(editorPath);
return new Result
{
Title = name,
Action = _ =>
{
try
{
Process.Start(editorPath, record.FullPath);
return true;
}
catch (Exception e)
{
var message = $"Failed to open editor for file at {record.FullPath}";
LogException(message, e);
Context.API.ShowMsg(message);
return false;
}
},
IcoPath = Constants.FileImagePath
};
}
private Result CreateAddToIndexSearchExclusionListResult(SearchResult record)
{
return new Result
{
Title = Context.API.GetTranslation("plugin_explorer_excludefromindexsearch"),
SubTitle = Context.API.GetTranslation("plugin_explorer_path") + " " + record.FullPath,
Action = _ =>
{
if(!Settings.IndexSearchExcludedSubdirectoryPaths.Any(x => x.Path == record.FullPath))
Settings.IndexSearchExcludedSubdirectoryPaths.Add(new FolderLink { Path = record.FullPath });
var pluginDirectory = Directory.GetParent(Assembly.GetExecutingAssembly().Location.ToString());
var iconPath = pluginDirectory + "\\" + Constants.ExplorerIconImagePath;
Task.Run(() =>
{
Context.API.ShowMsg(Context.API.GetTranslation("plugin_explorer_excludedfromindexsearch_msg"),
Context.API.GetTranslation("plugin_explorer_path") +
" " + record.FullPath, iconPath);
// so the new path can be persisted to storage and not wait till next ViewModel save.
Context.API.SaveAppAllSettings();
});
return false;
},
IcoPath = Constants.ExcludeFromIndexImagePath
};
}
private Result CreateOpenWindowsIndexingOptions()
{
return new Result
{
Title = Context.API.GetTranslation("plugin_explorer_openindexingoptions"),
SubTitle = Context.API.GetTranslation("plugin_explorer_openindexingoptions_subtitle"),
Action = _ =>
{
try
{
var psi = new ProcessStartInfo
{
FileName = "control.exe",
UseShellExecute = true,
Arguments = "srchadmin.dll"
};
Process.Start(psi);
return true;
}
catch (Exception e)
{
var message = Context.API.GetTranslation("plugin_explorer_openindexingoptions_errormsg");
LogException(message, e);
Context.API.ShowMsg(message);
return false;
}
},
IcoPath = Constants.IndexingOptionsIconImagePath
};
}
public void LogException(string message, Exception e)
{
Log.Exception($"|Flow.Launcher.Plugin.Folder.ContextMenu|{message}", e);
}
private bool CanRunAsDifferentUser(string path)
{
switch (Path.GetExtension(path))
{
case ".exe":
case ".bat":
case ".msi":
return true;
default:
return false;
}
}
}
}

View file

@ -1,37 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<ProjectGuid>{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}</ProjectGuid>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Flow.Launcher.Plugin.Folder</RootNamespace>
<AssemblyName>Flow.Launcher.Plugin.Folder</AssemblyName>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<ApplicationIcon />
<StartupObject />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.Folder\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.Explorer</OutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\Output\Release\Plugins\Flow.Launcher.Plugin.Folder\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<OutputPath>..\..\Output\Release\Plugins\Flow.Launcher.Plugin.Explorer</OutputPath>
</PropertyGroup>
<ItemGroup>
<None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@ -39,73 +25,87 @@
</ItemGroup>
<ItemGroup>
<Page Include="FolderPluginSettings.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<None Include="Images\explorer.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Images\index.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Images\excludeindexpath.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Images\copy.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Images\deletefilefolder.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<Content Include="Images\file.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="Images\user.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<None Include="Images\folder.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Images\user.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Images\windowsindexingoptions.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<Content Include="Languages\en.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Languages\zh-cn.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Languages\zh-tw.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Languages\de.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Languages\pl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Languages\tr.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Data.OleDb" Version="4.7.1" />
<PackageReference Include="tlbimp-Microsoft.Search.Interop" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="Images\folder.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="System.Runtime" Version="4.3.1" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View file

Before

Width:  |  Height:  |  Size: 501 B

After

Width:  |  Height:  |  Size: 501 B

View file

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View file

Before

Width:  |  Height:  |  Size: 290 B

After

Width:  |  Height:  |  Size: 290 B

View file

Before

Width:  |  Height:  |  Size: 468 B

After

Width:  |  Height:  |  Size: 468 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -0,0 +1,14 @@
<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">
<!--Controls-->
<system:String x:Key="plugin_explorer_delete">Löschen</system:String>
<system:String x:Key="plugin_explorer_edit">Bearbeiten</system:String>
<system:String x:Key="plugin_explorer_add">Hinzufügen</system:String>
<!--Dialogues-->
<system:String x:Key="plugin_explorer_select_folder_link_warning">Bitte wähle eine Ordnerverknüpfung</system:String>
<system:String x:Key="plugin_explorer_delete_folder_link">Bist du sicher {0} zu löschen?</system:String>
</ResourceDictionary>

View file

@ -0,0 +1,39 @@
<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">
<!--Dialogues-->
<system:String x:Key="plugin_explorer_select_folder_link_warning">Please select a folder link</system:String>
<system:String x:Key="plugin_explorer_delete_folder_link">Are you sure you want to delete {0}?</system:String>
<!--Controls-->
<system:String x:Key="plugin_explorer_delete">Delete</system:String>
<system:String x:Key="plugin_explorer_edit">Edit</system:String>
<system:String x:Key="plugin_explorer_add">Add</system:String>
<system:String x:Key="plugin_explorer_quickfolderaccess_header">Quick Folder Access Paths</system:String>
<system:String x:Key="plugin_explorer_indexsearchexcludedpaths_header">Index Search Excluded Paths</system:String>
<system:String x:Key="plugin_explorer_manageindexoptions">Indexing Options</system:String>
<!--Plugin Infos-->
<system:String x:Key="plugin_explorer_plugin_name">Explorer</system:String>
<system:String x:Key="plugin_explorer_plugin_description">Search and manage files and folders. Explorer utilises Windows Index Search</system:String>
<!--Context menu items-->
<system:String x:Key="plugin_explorer_copypath">Copy path</system:String>
<system:String x:Key="plugin_explorer_copyfilefolder">Copy</system:String>
<system:String x:Key="plugin_explorer_deletefilefolder">Delete</system:String>
<system:String x:Key="plugin_explorer_path">Path:</system:String>
<system:String x:Key="plugin_explorer_deletefilefolder_subtitle">Delete the selected</system:String>
<system:String x:Key="plugin_explorer_runasdifferentuser">Run as different user</system:String>
<system:String x:Key="plugin_explorer_runasdifferentuser_subtitle">Run the selected using a different user account</system:String>
<system:String x:Key="plugin_explorer_opencontainingfolder">Open containing folder</system:String>
<system:String x:Key="plugin_explorer_opencontainingfolder_subtitle">Opens the location that contains the file or folder</system:String>
<system:String x:Key="plugin_explorer_openwitheditor">Open With Editor:</system:String>
<system:String x:Key="plugin_explorer_excludefromindexsearch">Exclude current and sub-directories from Index Search</system:String>
<system:String x:Key="plugin_explorer_excludedfromindexsearch_msg">Excluded from Index Search</system:String>
<system:String x:Key="plugin_explorer_openindexingoptions">Open Windows Indexing Options</system:String>
<system:String x:Key="plugin_explorer_openindexingoptions_subtitle">Manage indexed files and folders</system:String>
<system:String x:Key="plugin_explorer_openindexingoptions_errormsg">Failed to open Windows Indexing Options</system:String>
</ResourceDictionary>

View file

@ -0,0 +1,14 @@
<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">
<!--Controls-->
<system:String x:Key="plugin_explorer_delete">Usuń</system:String>
<system:String x:Key="plugin_explorer_edit">Edytuj</system:String>
<system:String x:Key="plugin_explorer_add">Dodaj</system:String>
<!--Dialogues-->
<system:String x:Key="plugin_explorer_select_folder_link_warning">Musisz wybrać któryś folder z listy</system:String>
<system:String x:Key="plugin_explorer_delete_folder_link">Czy jesteś pewien że chcesz usunąć {0}?</system:String>
</ResourceDictionary>

View file

@ -0,0 +1,14 @@
<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">
<!--Controls-->
<system:String x:Key="plugin_explorer_delete">Sil</system:String>
<system:String x:Key="plugin_explorer_edit">Düzenle</system:String>
<system:String x:Key="plugin_explorer_add">Ekle</system:String>
<!--Dialogues-->
<system:String x:Key="plugin_explorer_select_folder_link_warning">Lütfen bir klasör bağlantısı seçin</system:String>
<system:String x:Key="plugin_explorer_delete_folder_link">{0} bağlantısını silmek istediğinize emin misiniz?</system:String>
</ResourceDictionary>

View file

@ -0,0 +1,14 @@
<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">
<!--Controls-->
<system:String x:Key="plugin_explorer_delete">删除</system:String>
<system:String x:Key="plugin_explorer_edit">编辑</system:String>
<system:String x:Key="plugin_explorer_add">添加</system:String>
<!--Dialogues-->
<system:String x:Key="plugin_explorer_select_folder_link_warning">请选择一个文件夹</system:String>
<system:String x:Key="plugin_explorer_delete_folder_link">你确定要删除{0}吗?</system:String>
</ResourceDictionary>

View file

@ -0,0 +1,14 @@
<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">
<!--Controls-->
<system:String x:Key="plugin_explorer_delete">刪除</system:String>
<system:String x:Key="plugin_explorer_edit">編輯</system:String>
<system:String x:Key="plugin_explorer_add">新增</system:String>
<!--Dialogues-->
<system:String x:Key="plugin_explorer_select_folder_link_warning">請選擇一個資料夾</system:String>
<system:String x:Key="plugin_explorer_delete_folder_link">你確認要刪除{0}嗎?</system:String>
</ResourceDictionary>

View file

@ -0,0 +1,58 @@
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Plugin.Explorer.Search;
using Flow.Launcher.Plugin.Explorer.ViewModels;
using Flow.Launcher.Plugin.Explorer.Views;
using System.Collections.Generic;
using System.Windows.Controls;
namespace Flow.Launcher.Plugin.Explorer
{
public class Main : ISettingProvider, IPlugin, ISavable, IContextMenu, IPluginI18n
{
internal PluginInitContext Context { get; set; }
internal Settings Settings;
private SettingsViewModel viewModel;
private IContextMenu contextMenu;
public Control CreateSettingPanel()
{
return new ExplorerSettings(viewModel);
}
public void Init(PluginInitContext context)
{
Context = context;
viewModel = new SettingsViewModel(context);
Settings = viewModel.Settings;
contextMenu = new ContextMenu(Context, Settings);
}
public List<Result> LoadContextMenus(Result selectedResult)
{
return contextMenu.LoadContextMenus(selectedResult);
}
public List<Result> Query(Query query)
{
return new SearchManager(Settings, Context).Search(query);
}
public void Save()
{
viewModel.Save();
}
public string GetTranslatedPluginTitle()
{
return Context.API.GetTranslation("plugin_explorer_plugin_name");
}
public string GetTranslatedPluginDescription()
{
return Context.API.GetTranslation("plugin_explorer_plugin_description");
}
}
}

View file

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Flow.Launcher.Plugin.Explorer.Search
{
internal static class Constants
{
internal const string FolderImagePath = "Images\\folder.png";
internal const string FileImagePath = "Images\\file.png";
internal const string DeleteFileFolderImagePath = "Images\\deletefilefolder.png";
internal const string CopyImagePath = "Images\\copy.png";
internal const string IndexImagePath = "Images\\index.png";
internal const string ExcludeFromIndexImagePath = "Images\\excludeindexpath.png";
internal const string ExplorerIconImagePath = "Images\\explorer.png";
internal const string DifferentUserIconImagePath = "Images\\user.png";
internal const string IndexingOptionsIconImagePath = "Images\\windowsindexingoptions.png";
internal const string DefaultFolderSubtitleString = "Ctrl + Enter to open the directory";
internal const char AllFilesFolderSearchWildcard = '>';
internal const char DirectorySeperator = '\\';
internal const string WindowsIndexingOptions = "srchadmin.dll";
}
}

View file

@ -0,0 +1,96 @@
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Plugin.SharedCommands;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo
{
public class DirectoryInfoSearch
{
private readonly ResultManager resultManager;
public DirectoryInfoSearch(PluginInitContext context)
{
resultManager = new ResultManager(context);
}
internal List<Result> TopLevelDirectorySearch(Query query, string search)
{
var criteria = ConstructSearchCriteria(search);
if (search.LastIndexOf(Constants.AllFilesFolderSearchWildcard) > search.LastIndexOf(Constants.DirectorySeperator))
return DirectorySearch(SearchOption.AllDirectories, query, search, criteria);
return DirectorySearch(SearchOption.TopDirectoryOnly, query, search, criteria);
}
public string ConstructSearchCriteria(string search)
{
string incompleteName = "";
if (!search.EndsWith(Constants.DirectorySeperator))
{
var indexOfSeparator = search.LastIndexOf(Constants.DirectorySeperator);
incompleteName = search.Substring(indexOfSeparator + 1).ToLower();
if (incompleteName.StartsWith(Constants.AllFilesFolderSearchWildcard))
incompleteName = "*" + incompleteName.Substring(1);
}
incompleteName += "*";
return incompleteName;
}
private List<Result> DirectorySearch(SearchOption searchOption, Query query, string search, string searchCriteria)
{
var results = new List<Result>();
var path = FilesFolders.ReturnPreviousDirectoryIfIncompleteString(search);
var folderList = new List<Result>();
var fileList = new List<Result>();
try
{
var directoryInfo = new System.IO.DirectoryInfo(path);
var fileSystemInfos = directoryInfo.GetFileSystemInfos(searchCriteria, searchOption);
foreach (var fileSystemInfo in fileSystemInfos)
{
if ((fileSystemInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) continue;
if (fileSystemInfo is System.IO.DirectoryInfo)
{
folderList.Add(resultManager.CreateFolderResult(fileSystemInfo.Name, Constants.DefaultFolderSubtitleString, fileSystemInfo.FullName, query, true, false));
}
else
{
fileList.Add(resultManager.CreateFileResult(fileSystemInfo.FullName, query, true, false));
}
}
}
catch (Exception e)
{
if (e is UnauthorizedAccessException || e is ArgumentException)
{
results.Add(new Result { Title = e.Message, Score = 501 });
return results;
}
#if DEBUG // Please investigate and handle error from DirectoryInfo search
throw e;
#else
Log.Exception($"|Flow.Launcher.Plugin.Explorer.DirectoryInfoSearch|Error from performing DirectoryInfoSearch", e);
#endif
}
// Intial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection.
return results.Concat(folderList.OrderBy(x => x.Title)).Concat(fileList.OrderBy(x => x.Title)).ToList();
}
}
}

View file

@ -0,0 +1,93 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Flow.Launcher.Plugin.Explorer.Search
{
public static class EnvironmentVariables
{
internal static bool IsEnvironmentVariableSearch(string search)
{
return LoadEnvironmentStringPaths().Count > 0
&& search.StartsWith("%")
&& search != "%%"
&& !search.Contains("\\");
}
internal static Dictionary<string, string> LoadEnvironmentStringPaths()
{
var envStringPaths = new Dictionary<string, string>();
foreach (DictionaryEntry special in Environment.GetEnvironmentVariables())
{
if (Directory.Exists(special.Value.ToString()))
{
envStringPaths.Add(special.Key.ToString().ToLower(), special.Value.ToString());
}
}
return envStringPaths;
}
internal static string TranslateEnvironmentVariablePath(string environmentVariablePath)
{
var envStringPaths = LoadEnvironmentStringPaths();
var splitSearch = environmentVariablePath.Substring(1).Split("%");
var exactEnvStringPath = splitSearch[0];
// if there are more than 2 % characters in the query, don't bother
if (splitSearch.Length == 2 && envStringPaths.ContainsKey(exactEnvStringPath))
{
var queryPartToReplace = $"%{exactEnvStringPath}%";
var expandedPath = envStringPaths[exactEnvStringPath];
// replace the %envstring% part of the query with its expanded equivalent
return environmentVariablePath.Replace(queryPartToReplace, expandedPath);
}
return environmentVariablePath;
}
internal static List<Result> GetEnvironmentStringPathSuggestions(string querySearch, Query query, PluginInitContext context)
{
var results = new List<Result>();
var environmentVariables = LoadEnvironmentStringPaths();
var search = querySearch;
if (querySearch.EndsWith("%") && search.Length > 1)
{
// query starts and ends with a %, find an exact match from env-string paths
search = querySearch.Substring(1, search.Length - 2);
if (environmentVariables.ContainsKey(search))
{
var expandedPath = environmentVariables[search];
results.Add(new ResultManager(context).CreateFolderResult($"%{search}%", expandedPath, expandedPath, query));
return results;
}
}
if (querySearch == "%")
{
search = ""; // Get all paths
}
else
{
search = search.Substring(1);
}
foreach (var p in environmentVariables)
{
if (p.Key.StartsWith(search))
{
results.Add(new ResultManager(context).CreateFolderResult($"%{p.Key}%", p.Value, p.Value, query));
}
}
return results;
}
}
}

View file

@ -0,0 +1,29 @@
using Newtonsoft.Json;
using System;
using System.Linq;
namespace Flow.Launcher.Plugin.Explorer.Search.FolderLinks
{
[JsonObject(MemberSerialization.OptIn)]
public class FolderLink
{
[JsonProperty]
public string Path { get; set; }
public string Nickname
{
get
{
var path = Path.EndsWith(Constants.DirectorySeperator) ? Path[0..^1] : Path;
if (path.EndsWith(':'))
return path[0..^1] + " Drive";
return path.Split(new[] { System.IO.Path.DirectorySeparatorChar }, StringSplitOptions.None)
.Last()
+ " (" + System.IO.Path.GetDirectoryName(Path) + ")";
}
}
}
}

View file

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Flow.Launcher.Plugin.Explorer.Search.FolderLinks
{
public class QuickFolderAccess
{
internal List<Result> FolderList(Query query, List<FolderLink> folderLinks, PluginInitContext context)
{
if (string.IsNullOrEmpty(query.Search))
return folderLinks
.Select(item =>
new ResultManager(context)
.CreateFolderResult(item.Nickname, Constants.DefaultFolderSubtitleString, item.Path, query))
.ToList();
string search = query.Search.ToLower();
var queriedFolderLinks = folderLinks.Where(x => x.Nickname.StartsWith(search, StringComparison.OrdinalIgnoreCase));
return queriedFolderLinks.Select(item =>
new ResultManager(context)
.CreateFolderResult(item.Nickname, Constants.DefaultFolderSubtitleString, item.Path, query))
.ToList();
}
}
}

View file

@ -0,0 +1,135 @@
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Plugin.SharedCommands;
using System;
using System.IO;
using System.Linq;
using System.Windows;
namespace Flow.Launcher.Plugin.Explorer.Search
{
public class ResultManager
{
private readonly PluginInitContext context;
public ResultManager(PluginInitContext context)
{
this.context = context;
}
internal Result CreateFolderResult(string title, string subtitle, string path, Query query, bool showIndexState = false, bool windowsIndexed = false)
{
return new Result
{
Title = title,
IcoPath = path,
SubTitle = subtitle,
TitleHighlightData = StringMatcher.FuzzySearch(query.Search, title).MatchData,
Action = c =>
{
if (c.SpecialKeyState.CtrlPressed)
{
try
{
FilesFolders.OpenPath(path);
return true;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Could not start " + path);
return false;
}
}
string changeTo = path.EndsWith(Constants.DirectorySeperator) ? path : path + Constants.DirectorySeperator;
context.API.ChangeQuery(string.IsNullOrEmpty(query.ActionKeyword) ?
changeTo :
query.ActionKeyword + " " + changeTo);
return false;
},
ContextData = new SearchResult { Type = ResultType.Folder, FullPath = path, ShowIndexState = showIndexState, WindowsIndexed = windowsIndexed }
};
}
internal Result CreateOpenCurrentFolderResult(string path, bool windowsIndexed = false)
{
var retrievedDirectoryPath = FilesFolders.ReturnPreviousDirectoryIfIncompleteString(path);
var folderName = retrievedDirectoryPath.TrimEnd(Constants.DirectorySeperator).Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None).Last();
if (retrievedDirectoryPath.EndsWith(":\\"))
{
var driveLetter = path.Substring(0, 1).ToUpper();
folderName = driveLetter + " drive";
}
var title = "Open current directory";
if (retrievedDirectoryPath != path)
title = "Open " + folderName;
var subtitleFolderName = folderName;
// ie. max characters can be displayed without subtitle cutting off: "Program Files (x86)"
if (folderName.Length > 19)
subtitleFolderName = "the directory";
return new Result
{
Title = title,
SubTitle = $"Use > to search within {subtitleFolderName}, " +
$"* to search for file extensions or >* to combine both searches.",
IcoPath = retrievedDirectoryPath,
Score = 500,
Action = c =>
{
FilesFolders.OpenPath(retrievedDirectoryPath);
return true;
},
ContextData = new SearchResult { Type = ResultType.Folder, FullPath = retrievedDirectoryPath, ShowIndexState = true, WindowsIndexed = windowsIndexed }
};
}
internal Result CreateFileResult(string filePath, Query query, bool showIndexState = false, bool windowsIndexed = false)
{
var result = new Result
{
Title = Path.GetFileName(filePath),
SubTitle = filePath,
IcoPath = filePath,
TitleHighlightData = StringMatcher.FuzzySearch(query.Search, Path.GetFileName(filePath)).MatchData,
Action = c =>
{
try
{
FilesFolders.OpenPath(filePath);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Could not start " + filePath);
}
return true;
},
ContextData = new SearchResult { Type = ResultType.File, FullPath = filePath, ShowIndexState = showIndexState, WindowsIndexed = windowsIndexed }
};
return result;
}
}
internal class SearchResult
{
public string FullPath { get; set; }
public ResultType Type { get; set; }
public bool WindowsIndexed { get; set; }
public bool ShowIndexState { get; set; }
}
internal enum ResultType
{
Volume,
Folder,
File
}
}

View file

@ -0,0 +1,130 @@
using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo;
using Flow.Launcher.Plugin.Explorer.Search.FolderLinks;
using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex;
using Flow.Launcher.Plugin.SharedCommands;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Flow.Launcher.Plugin.Explorer.Search
{
public class SearchManager
{
private readonly PluginInitContext context;
private readonly IndexSearch indexSearch;
private readonly QuickFolderAccess quickFolderAccess = new QuickFolderAccess();
private readonly ResultManager resultManager;
private readonly Settings settings;
public SearchManager(Settings settings, PluginInitContext context)
{
this.context = context;
indexSearch = new IndexSearch(context);
resultManager = new ResultManager(context);
this.settings = settings;
}
internal List<Result> Search(Query query)
{
var results = new List<Result>();
var querySearch = query.Search;
var quickFolderLinks = quickFolderAccess.FolderList(query, settings.QuickFolderAccessLinks, context);
if (quickFolderLinks.Count > 0)
return quickFolderLinks;
if (string.IsNullOrEmpty(querySearch))
return results;
var isEnvironmentVariable = EnvironmentVariables.IsEnvironmentVariableSearch(querySearch);
if (isEnvironmentVariable)
return EnvironmentVariables.GetEnvironmentStringPathSuggestions(querySearch, query, context);
// Query is a location path with a full environment variable, eg. %appdata%\somefolder\
var isEnvironmentVariablePath = querySearch.Substring(1).Contains("%\\");
if (!FilesFolders.IsLocationPathString(querySearch) && !isEnvironmentVariablePath)
return WindowsIndexFilesAndFoldersSearch(query, querySearch);
var locationPath = querySearch;
if (isEnvironmentVariablePath)
locationPath = EnvironmentVariables.TranslateEnvironmentVariablePath(locationPath);
if (!FilesFolders.LocationExists(FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath)))
return results;
var useIndexSearch = UseWindowsIndexForDirectorySearch(locationPath);
results.Add(resultManager.CreateOpenCurrentFolderResult(locationPath, useIndexSearch));
results.AddRange(TopLevelDirectorySearchBehaviour(WindowsIndexTopLevelFolderSearch,
DirectoryInfoClassSearch,
useIndexSearch,
query,
locationPath));
return results;
}
private List<Result> DirectoryInfoClassSearch(Query query, string querySearch)
{
var directoryInfoSearch = new DirectoryInfoSearch(context);
return directoryInfoSearch.TopLevelDirectorySearch(query, querySearch);
}
public List<Result> TopLevelDirectorySearchBehaviour(
Func<Query, string, List<Result>> windowsIndexSearch,
Func<Query, string, List<Result>> directoryInfoClassSearch,
bool useIndexSearch,
Query query,
string querySearchString)
{
if (!useIndexSearch)
return directoryInfoClassSearch(query, querySearchString);
return windowsIndexSearch(query, querySearchString);
}
private List<Result> WindowsIndexFilesAndFoldersSearch(Query query, string querySearchString)
{
var queryConstructor = new QueryConstructor(settings);
return indexSearch.WindowsIndexSearch(querySearchString,
queryConstructor.CreateQueryHelper().ConnectionString,
queryConstructor.QueryForAllFilesAndFolders,
query);
}
private List<Result> WindowsIndexTopLevelFolderSearch(Query query, string path)
{
var queryConstructor = new QueryConstructor(settings);
return indexSearch.WindowsIndexSearch(path,
queryConstructor.CreateQueryHelper().ConnectionString,
queryConstructor.QueryForTopLevelDirectorySearch,
query);
}
private bool UseWindowsIndexForDirectorySearch(string locationPath)
{
if (!settings.UseWindowsIndexForDirectorySearch)
return false;
if (settings.IndexSearchExcludedSubdirectoryPaths
.Any(x => FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath)
.StartsWith(x.Path, StringComparison.OrdinalIgnoreCase)))
return false;
return indexSearch.PathIsIndexed(locationPath);
}
}
}

View file

@ -0,0 +1,117 @@
using Flow.Launcher.Infrastructure.Logger;
using Microsoft.Search.Interop;
using System;
using System.Collections.Generic;
using System.Data.OleDb;
using System.Linq;
using System.Text.RegularExpressions;
namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
{
internal class IndexSearch
{
private readonly object _lock = new object();
private OleDbConnection conn;
private OleDbCommand command;
private OleDbDataReader dataReaderResults;
private readonly ResultManager resultManager;
// Reserved keywords in oleDB
private readonly string reservedStringPattern = @"^[\/\\\$\%]+$";
internal IndexSearch(PluginInitContext context)
{
resultManager = new ResultManager(context);
}
internal List<Result> ExecuteWindowsIndexSearch(string indexQueryString, string connectionString, Query query)
{
var folderResults = new List<Result>();
var fileResults = new List<Result>();
var results = new List<Result>();
try
{
using (conn = new OleDbConnection(connectionString))
{
conn.Open();
using (command = new OleDbCommand(indexQueryString, conn))
{
// Results return as an OleDbDataReader.
using (dataReaderResults = command.ExecuteReader())
{
if (dataReaderResults.HasRows)
{
while (dataReaderResults.Read())
{
if (dataReaderResults.GetValue(0) != DBNull.Value && dataReaderResults.GetValue(1) != DBNull.Value)
{
if (dataReaderResults.GetString(2) == "Directory")
{
folderResults.Add(resultManager.CreateFolderResult(
dataReaderResults.GetString(0),
Constants.DefaultFolderSubtitleString,
dataReaderResults.GetString(1),
query, true, true));
}
else
{
fileResults.Add(resultManager.CreateFileResult(dataReaderResults.GetString(1), query, true, true));
}
}
}
}
}
}
}
}
catch (InvalidOperationException e)
{
// Internal error from ExecuteReader(): Connection closed.
LogException("Internal error from ExecuteReader()", e);
}
catch (Exception e)
{
LogException("General error from performing index search", e);
}
// Intial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection.
return results.Concat(folderResults.OrderBy(x => x.Title)).Concat(fileResults.OrderBy(x => x.Title)).ToList(); ;
}
internal List<Result> WindowsIndexSearch(string searchString, string connectionString, Func<string, string> constructQuery, Query query)
{
var regexMatch = Regex.Match(searchString, reservedStringPattern);
if (regexMatch.Success)
return new List<Result>();
lock (_lock)
{
var constructedQuery = constructQuery(searchString);
return ExecuteWindowsIndexSearch(constructedQuery, connectionString, query);
}
}
internal bool PathIsIndexed(string path)
{
var csm = new CSearchManager();
var indexManager = csm.GetCatalog("SystemIndex").GetCrawlScopeManager();
return indexManager.IncludedInCrawlScope(path) > 0;
}
private void LogException(string message, Exception e)
{
#if DEBUG // Please investigate and handle error from index search
throw e;
#else
Log.Exception($"|Flow.Launcher.Plugin.Explorer.IndexSearch|{message}", e);
#endif
}
}
}

View file

@ -0,0 +1,121 @@
using Microsoft.Search.Interop;
namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
{
public class QueryConstructor
{
private readonly Settings settings;
private const string SystemIndex = "SystemIndex";
public QueryConstructor(Settings settings)
{
this.settings = settings;
}
public CSearchQueryHelper CreateBaseQuery()
{
var baseQuery = CreateQueryHelper();
// Set the number of results we want. Don't set this property if all results are needed.
baseQuery.QueryMaxResults = settings.MaxResult;
// Set list of columns we want to display, getting the path presently
baseQuery.QuerySelectColumns = "System.FileName, System.ItemPathDisplay, System.ItemType";
// Filter based on file name
baseQuery.QueryContentProperties = "System.FileName";
// Set sorting order
//baseQuery.QuerySorting = "System.ItemType DESC";
return baseQuery;
}
internal CSearchQueryHelper CreateQueryHelper()
{
// This uses the Microsoft.Search.Interop assembly
var manager = new CSearchManager();
// SystemIndex catalog is the default catalog in Windows
var catalogManager = manager.GetCatalog(SystemIndex);
// Get the ISearchQueryHelper which will help us to translate AQS --> SQL necessary to query the indexer
var queryHelper = catalogManager.GetQueryHelper();
return queryHelper;
}
///<summary>
/// Set the required WHERE clause restriction to search on the first level of a specified directory.
///</summary>
public string QueryWhereRestrictionsForTopLevelDirectorySearch(string path)
{
var searchDepth = $"directory='file:";
return QueryWhereRestrictionsFromLocationPath(path, searchDepth);
}
///<summary>
/// Set the required WHERE clause restriction to search all files and subfolders of a specified directory.
///</summary>
public string QueryWhereRestrictionsForTopLevelDirectoryAllFilesAndFoldersSearch(string path)
{
var searchDepth = $"scope='file:";
return QueryWhereRestrictionsFromLocationPath(path, searchDepth);
}
private string QueryWhereRestrictionsFromLocationPath(string path, string searchDepth)
{
if (path.EndsWith(Constants.DirectorySeperator))
return searchDepth + $"{path}'";
var indexOfSeparator = path.LastIndexOf(Constants.DirectorySeperator);
var itemName = path.Substring(indexOfSeparator + 1);
if (itemName.StartsWith(Constants.AllFilesFolderSearchWildcard))
itemName = itemName.Substring(1);
var previousLevelDirectory = path.Substring(0, indexOfSeparator);
if (string.IsNullOrEmpty(itemName))
return searchDepth + $"{previousLevelDirectory}'";
return $"(System.FileName LIKE '{itemName}%' " +
$"OR CONTAINS(System.FileName,'\"{itemName}*\"',1033)) AND " +
searchDepth + $"{previousLevelDirectory}'";
}
///<summary>
/// Search will be performed on all folders and files on the first level of a specified directory.
///</summary>
public string QueryForTopLevelDirectorySearch(string path)
{
string query = "SELECT TOP " + settings.MaxResult + $" {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE ";
if (path.LastIndexOf(Constants.AllFilesFolderSearchWildcard) > path.LastIndexOf(Constants.DirectorySeperator))
return query + QueryWhereRestrictionsForTopLevelDirectoryAllFilesAndFoldersSearch(path);
return query + QueryWhereRestrictionsForTopLevelDirectorySearch(path);
}
///<summary>
/// Search will be performed on all folders and files based on user's search keywords.
///</summary>
public string QueryForAllFilesAndFolders(string userSearchString)
{
// Generate SQL from constructed parameters, converting the userSearchString from AQS->WHERE clause
return CreateBaseQuery().GenerateSQLFromUserQuery(userSearchString) + " AND " + QueryWhereRestrictionsForAllFilesAndFoldersSearch();
}
///<summary>
/// Set the required WHERE clause restriction to search for all files and folders.
///</summary>
public string QueryWhereRestrictionsForAllFilesAndFoldersSearch()
{
return $"scope='file:'";
}
}
}

View file

@ -0,0 +1,21 @@
using Flow.Launcher.Plugin.Explorer.Search.FolderLinks;
using Newtonsoft.Json;
using System.Collections.Generic;
namespace Flow.Launcher.Plugin.Explorer
{
public class Settings
{
[JsonProperty]
public int MaxResult { get; set; } = 100;
[JsonProperty]
public List<FolderLink> QuickFolderAccessLinks { get; set; } = new List<FolderLink>();
[JsonProperty]
public bool UseWindowsIndexForDirectorySearch { get; set; } = true;
[JsonProperty]
public List<FolderLink> IndexSearchExcludedSubdirectoryPaths { get; set; } = new List<FolderLink>();
}
}

View file

@ -0,0 +1,44 @@
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Plugin.Explorer.Search;
using Flow.Launcher.Plugin.Explorer.Search.FolderLinks;
using System.Diagnostics;
namespace Flow.Launcher.Plugin.Explorer.ViewModels
{
public class SettingsViewModel
{
private readonly PluginJsonStorage<Settings> storage;
internal Settings Settings { get; set; }
internal PluginInitContext Context { get; set; }
public SettingsViewModel(PluginInitContext context)
{
Context = context;
storage = new PluginJsonStorage<Settings>();
Settings = storage.Load();
}
public void Save()
{
storage.Save();
}
internal void RemoveFolderLinkFromQuickFolders(FolderLink selectedRow) => Settings.QuickFolderAccessLinks.Remove(selectedRow);
internal void RemoveFolderLinkFromExcludedIndexPaths(FolderLink selectedRow) => Settings.IndexSearchExcludedSubdirectoryPaths.Remove(selectedRow);
internal void OpenWindowsIndexingOptions()
{
var psi = new ProcessStartInfo
{
FileName = "control.exe",
UseShellExecute = true,
Arguments = Constants.WindowsIndexingOptions
};
Process.Start(psi);
}
}
}

View file

@ -0,0 +1,63 @@
<UserControl x:Class="Flow.Launcher.Plugin.Explorer.Views.ExplorerSettings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:viewModels="clr-namespace:Flow.Launcher.Plugin.Explorer.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<DataTemplate x:Key="ListViewTemplateFolderLinks">
<TextBlock
Text="{Binding Nickname, Mode=OneTime}"
Margin="0,5,0,5" />
</DataTemplate>
<DataTemplate x:Key="ListViewTemplateExcludedPaths">
<TextBlock
Text="{Binding Nickname, Mode=OneTime}"
Margin="0,5,0,5" />
</DataTemplate>
</UserControl.Resources>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<ScrollViewer Margin="20 35 0 0" HorizontalScrollBarVisibility="Hidden" Grid.Row="0" VerticalScrollBarVisibility="Auto">
<StackPanel>
<Expander Name="expFolderLinks" Header="{DynamicResource plugin_explorer_quickfolderaccess_header}"
Expanded="expFolderLinks_Click" Collapsed="expFolderLinks_Click">
<ListView
x:Name="lbxFolderLinks" AllowDrop="True"
Drop="lbxFolders_Drop"
DragEnter="lbxFolders_DragEnter"
ItemTemplate="{StaticResource ListViewTemplateFolderLinks}"/>
</Expander>
<Expander x:Name="expExcludedPaths" Header="{DynamicResource plugin_explorer_indexsearchexcludedpaths_header}"
Expanded="expExcludedPaths_Click" Collapsed="expExcludedPaths_Click"
Margin="0 10 0 0">
<ListView
x:Name="lbxExcludedPaths" AllowDrop="True"
Drop="lbxFolders_Drop"
DragEnter="lbxFolders_DragEnter"
ItemTemplate="{StaticResource ListViewTemplateExcludedPaths}"/>
</Expander>
</StackPanel>
</ScrollViewer>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="350"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="1" Grid.Column="0" HorizontalAlignment="Left" Orientation="Horizontal">
<Button x:Name="btnIndexingOptions" Click="btnOpenIndexingOptions_Click" Width="130" Margin="10"
Content="{DynamicResource plugin_explorer_manageindexoptions}"/>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" Orientation="Horizontal">
<Button x:Name="btnDelete" Click="btnDelete_Click" Width="100" Margin="10" Content="{DynamicResource plugin_explorer_delete}"/>
<Button x:Name="btnEdit" Click="btnEdit_Click" Width="100" Margin="10" Content="{DynamicResource plugin_explorer_edit}"/>
<Button x:Name="btnAdd" Click="btnAdd_Click" Width="100" Margin="10" Content="{DynamicResource plugin_explorer_add}"/>
</StackPanel>
</Grid>
</Grid>
</UserControl>

View file

@ -0,0 +1,245 @@
using Flow.Launcher.Plugin.Explorer.Search.FolderLinks;
using Flow.Launcher.Plugin.Explorer.ViewModels;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Forms;
using DataFormats = System.Windows.DataFormats;
using DragDropEffects = System.Windows.DragDropEffects;
using DragEventArgs = System.Windows.DragEventArgs;
using MessageBox = System.Windows.MessageBox;
namespace Flow.Launcher.Plugin.Explorer.Views
{
/// <summary>
/// Interaction logic for ExplorerSettings.xaml
/// </summary>
public partial class ExplorerSettings
{
private readonly SettingsViewModel viewModel;
public ExplorerSettings(SettingsViewModel viewModel)
{
InitializeComponent();
this.viewModel = viewModel;
lbxFolderLinks.ItemsSource = this.viewModel.Settings.QuickFolderAccessLinks;
lbxExcludedPaths.ItemsSource = this.viewModel.Settings.IndexSearchExcludedSubdirectoryPaths;
RefreshView();
}
public void RefreshView()
{
lbxFolderLinks.Items.SortDescriptions.Add(new SortDescription("Path", ListSortDirection.Ascending));
lbxExcludedPaths.Items.SortDescriptions.Add(new SortDescription("Path", ListSortDirection.Ascending));
btnDelete.Visibility = Visibility.Hidden;
btnEdit.Visibility = Visibility.Hidden;
btnAdd.Visibility = Visibility.Hidden;
if (expFolderLinks.IsExpanded || expExcludedPaths.IsExpanded)
{
btnAdd.Visibility = Visibility.Visible;
if ((lbxFolderLinks.Items.Count == 0 && lbxExcludedPaths.Items.Count == 0)
&& btnDelete.Visibility == Visibility.Visible
&& btnEdit.Visibility == Visibility.Visible)
{
btnDelete.Visibility = Visibility.Hidden;
btnEdit.Visibility = Visibility.Hidden;
}
if (expFolderLinks.IsExpanded
&& lbxFolderLinks.Items.Count > 0
&& btnDelete.Visibility == Visibility.Hidden
&& btnEdit.Visibility == Visibility.Hidden)
{
btnDelete.Visibility = Visibility.Visible;
btnEdit.Visibility = Visibility.Visible;
}
if (expExcludedPaths.IsExpanded
&& lbxExcludedPaths.Items.Count > 0
&& btnDelete.Visibility == Visibility.Hidden
&& btnEdit.Visibility == Visibility.Hidden)
{
btnDelete.Visibility = Visibility.Visible;
btnEdit.Visibility = Visibility.Visible;
}
}
lbxFolderLinks.Items.Refresh();
lbxExcludedPaths.Items.Refresh();
}
private void expFolderLinks_Click(object sender, RoutedEventArgs e)
{
if (expFolderLinks.IsExpanded)
expFolderLinks.Height = 235;
if (!expFolderLinks.IsExpanded)
expFolderLinks.Height = Double.NaN;
if (expExcludedPaths.IsExpanded)
expExcludedPaths.IsExpanded = false;
RefreshView();
}
private void expExcludedPaths_Click(object sender, RoutedEventArgs e)
{
if (expExcludedPaths.IsExpanded)
expFolderLinks.Height = Double.NaN;
if (expFolderLinks.IsExpanded)
expFolderLinks.IsExpanded = false;
RefreshView();
}
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
var selectedRow = lbxFolderLinks.SelectedItem as FolderLink?? lbxExcludedPaths.SelectedItem as FolderLink;
if (selectedRow != null)
{
string msg = string.Format(viewModel.Context.API.GetTranslation("plugin_explorer_delete_folder_link"), selectedRow.Path);
if (MessageBox.Show(msg, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
if (expFolderLinks.IsExpanded)
viewModel.RemoveFolderLinkFromQuickFolders(selectedRow);
if (expExcludedPaths.IsExpanded)
viewModel.RemoveFolderLinkFromExcludedIndexPaths(selectedRow);
RefreshView();
}
}
else
{
string warning = viewModel.Context.API.GetTranslation("plugin_explorer_select_folder_link_warning");
MessageBox.Show(warning);
}
}
private void btnEdit_Click(object sender, RoutedEventArgs e)
{
var selectedRow = lbxFolderLinks.SelectedItem as FolderLink ?? lbxExcludedPaths.SelectedItem as FolderLink;
if (selectedRow != null)
{
var folderBrowserDialog = new FolderBrowserDialog();
folderBrowserDialog.SelectedPath = selectedRow.Path;
if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
{
if (expFolderLinks.IsExpanded)
{
var link = viewModel.Settings.QuickFolderAccessLinks.First(x => x.Path == selectedRow.Path);
link.Path = folderBrowserDialog.SelectedPath;
}
if (expExcludedPaths.IsExpanded)
{
var link = viewModel.Settings.IndexSearchExcludedSubdirectoryPaths.First(x => x.Path == selectedRow.Path);
link.Path = folderBrowserDialog.SelectedPath;
}
}
RefreshView();
}
else
{
string warning = viewModel.Context.API.GetTranslation("plugin_explorer_select_folder_link_warning");
MessageBox.Show(warning);
}
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
var folderBrowserDialog = new FolderBrowserDialog();
if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
{
var newFolderLink = new FolderLink
{
Path = folderBrowserDialog.SelectedPath
};
AddFolderLink(newFolderLink);
}
RefreshView();
}
private void lbxFolders_Drop(object sender, DragEventArgs e)
{
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
if (files != null && files.Count() > 0)
{
if (expFolderLinks.IsExpanded && viewModel.Settings.QuickFolderAccessLinks == null)
viewModel.Settings.QuickFolderAccessLinks = new List<FolderLink>();
foreach (string s in files)
{
if (Directory.Exists(s))
{
var newFolderLink = new FolderLink
{
Path = s
};
AddFolderLink(newFolderLink);
}
RefreshView();
}
}
}
private void AddFolderLink(FolderLink newFolderLink)
{
if (expFolderLinks.IsExpanded
&& !viewModel.Settings.QuickFolderAccessLinks.Any(x => x.Path == newFolderLink.Path))
{
if (viewModel.Settings.QuickFolderAccessLinks == null)
viewModel.Settings.QuickFolderAccessLinks = new List<FolderLink>();
viewModel.Settings.QuickFolderAccessLinks.Add(newFolderLink);
}
if (expExcludedPaths.IsExpanded
&& !viewModel.Settings.IndexSearchExcludedSubdirectoryPaths.Any(x => x.Path == newFolderLink.Path))
{
if (viewModel.Settings.IndexSearchExcludedSubdirectoryPaths == null)
viewModel.Settings.IndexSearchExcludedSubdirectoryPaths = new List<FolderLink>();
viewModel.Settings.IndexSearchExcludedSubdirectoryPaths.Add(newFolderLink);
}
}
private void lbxFolders_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effects = DragDropEffects.Link;
}
else
{
e.Effects = DragDropEffects.None;
}
}
private void btnOpenIndexingOptions_Click(object sender, RoutedEventArgs e)
{
viewModel.OpenWindowsIndexingOptions();
}
}
}

View file

@ -0,0 +1,12 @@
{
"ID": "572be03c74c642baae319fc283e561a8",
"ActionKeyword": "*",
"Name": "Explorer",
"Description": "Search and manage files and folders. Explorer utilises Windows Index Search",
"Author": "Jeremy Wu",
"Version": "1.0.0",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll",
"IcoPath": "Images\\explorer.png"
}

View file

@ -1,219 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.Image;
using Flow.Launcher.Plugin.SharedCommands;
namespace Flow.Launcher.Plugin.Folder
{
internal class ContextMenuLoader : IContextMenu
{
private readonly PluginInitContext _context;
public ContextMenuLoader(PluginInitContext context)
{
_context = context;
}
public List<Result> LoadContextMenus(Result selectedResult)
{
var contextMenus = new List<Result>();
if (selectedResult.ContextData is SearchResult record)
{
if (record.Type == ResultType.File)
{
contextMenus.Add(CreateOpenWithEditorResult(record));
contextMenus.Add(CreateOpenContainingFolderResult(record));
}
var icoPath = (record.Type == ResultType.File) ? Main.FileImagePath : Main.FolderImagePath;
var fileOrFolder = (record.Type == ResultType.File) ? "file" : "folder";
contextMenus.Add(new Result
{
Title = "Copy path",
SubTitle = $"Copy the current {fileOrFolder} path to clipboard",
Action = (context) =>
{
try
{
Clipboard.SetText(record.FullPath);
return true;
}
catch (Exception e)
{
var message = "Fail to set text in clipboard";
LogException(message, e);
_context.API.ShowMsg(message);
return false;
}
},
IcoPath = Main.CopyImagePath
});
contextMenus.Add(new Result
{
Title = $"Copy {fileOrFolder}",
SubTitle = $"Copy the {fileOrFolder} to clipboard",
Action = (context) =>
{
try
{
Clipboard.SetFileDropList(new System.Collections.Specialized.StringCollection { record.FullPath });
return true;
}
catch (Exception e)
{
var message = $"Fail to set {fileOrFolder} in clipboard";
LogException(message, e);
_context.API.ShowMsg(message);
return false;
}
},
IcoPath = icoPath
});
if (record.Type == ResultType.File || record.Type == ResultType.Folder)
contextMenus.Add(new Result
{
Title = $"Delete {fileOrFolder}",
SubTitle = $"Delete the selected {fileOrFolder}",
Action = (context) =>
{
try
{
if (record.Type == ResultType.File)
File.Delete(record.FullPath);
else
Directory.Delete(record.FullPath, true);
}
catch(Exception e)
{
var message = $"Fail to delete {fileOrFolder} at {record.FullPath}";
LogException(message, e);
_context.API.ShowMsg(message);
return false;
}
return true;
},
IcoPath = Main.DeleteFileFolderImagePath
});
if (record.Type == ResultType.File && CanRunAsDifferentUser(record.FullPath))
contextMenus.Add(new Result
{
Title = "Run as different user",
Action = (context) =>
{
try
{
Task.Run(()=> ShellCommand.RunAsDifferentUser(record.FullPath.SetProcessStartInfo()));
}
catch (FileNotFoundException e)
{
var name = "Plugin: Folder";
var message = $"File not found: {e.Message}";
_context.API.ShowMsg(name, message);
}
return true;
},
IcoPath = "Images/user.png"
});
}
return contextMenus;
}
private Result CreateOpenContainingFolderResult(SearchResult record)
{
return new Result
{
Title = "Open containing folder",
Action = _ =>
{
try
{
Process.Start("explorer.exe", $" /select,\"{record.FullPath}\"");
}
catch(Exception e)
{
var message = $"Fail to open file at {record.FullPath}";
LogException(message, e);
_context.API.ShowMsg(message);
return false;
}
return true;
},
IcoPath = Main.FolderImagePath
};
}
private Result CreateOpenWithEditorResult(SearchResult record)
{
string editorPath = "notepad.exe"; // TODO add the ability to create a custom editor
var name = "Open With Editor: " + Path.GetFileNameWithoutExtension(editorPath);
return new Result
{
Title = name,
Action = _ =>
{
try
{
Process.Start(editorPath, record.FullPath);
return true;
}
catch (Exception e)
{
var message = $"Fail to editor for file at {record.FullPath}";
LogException(message, e);
_context.API.ShowMsg(message);
return false;
}
},
IcoPath = editorPath
};
}
public void LogException(string message, Exception e)
{
Log.Exception($"|Flow.Launcher.Plugin.Folder.ContextMenu|{message}", e);
}
private bool CanRunAsDifferentUser(string path)
{
switch(Path.GetExtension(path))
{
case ".exe":
case ".bat":
case ".msi":
return true;
default:
return false;
}
}
}
public class SearchResult
{
public string FullPath { get; set; }
public ResultType Type { get; set; }
}
public enum ResultType
{
Volume,
Folder,
File
}
}

View file

@ -1,18 +0,0 @@
using System;
using System.Linq;
using Newtonsoft.Json;
namespace Flow.Launcher.Plugin.Folder
{
[JsonObject(MemberSerialization.OptIn)]
public class FolderLink
{
[JsonProperty]
public string Path { get; set; }
public string Nickname =>
Path.Split(new[] { System.IO.Path.DirectorySeparatorChar }, StringSplitOptions.None)
.Last()
+ " (" + System.IO.Path.GetDirectoryName(Path) + ")";
}
}

View file

@ -1,35 +0,0 @@
<UserControl x:Class="Flow.Launcher.Plugin.Folder.FolderPluginSettings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="500">
<UserControl.Resources>
<DataTemplate x:Key="ListViewTemplate">
<TextBlock
Text="{Binding Nickname, Mode=OneTime}"
Margin="0,5,0,5" />
</DataTemplate>
</UserControl.Resources>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<ScrollViewer Margin="0 35 0 0" HorizontalScrollBarVisibility="Hidden">
<StackPanel>
<ListView
x:Name="lbxFolders" Grid.Row="0" AllowDrop="True"
Drop="lbxFolders_Drop"
DragEnter="lbxFolders_DragEnter"
ItemTemplate="{StaticResource ListViewTemplate}"/>
</StackPanel>
</ScrollViewer>
<StackPanel Grid.Row="1" HorizontalAlignment="Right" Orientation="Horizontal">
<Button x:Name="btnDelete" Click="btnDelete_Click" Width="100" Margin="10" Content="{DynamicResource flowlauncher_plugin_folder_delete}"/>
<Button x:Name="btnEdit" Click="btnEdit_Click" Width="100" Margin="10" Content="{DynamicResource flowlauncher_plugin_folder_edit}"/>
<Button x:Name="btnAdd" Click="btnAdd_Click" Width="100" Margin="10" Content="{DynamicResource flowlauncher_plugin_folder_add}"/>
</StackPanel>
</Grid>
</UserControl>

View file

@ -1,156 +0,0 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Forms;
using DataFormats = System.Windows.DataFormats;
using DragDropEffects = System.Windows.DragDropEffects;
using DragEventArgs = System.Windows.DragEventArgs;
using MessageBox = System.Windows.MessageBox;
namespace Flow.Launcher.Plugin.Folder
{
public partial class FolderPluginSettings
{
private IPublicAPI flowlauncherAPI;
private Settings _settings;
public FolderPluginSettings(IPublicAPI flowlauncherAPI, Settings settings)
{
this.flowlauncherAPI = flowlauncherAPI;
InitializeComponent();
_settings = settings;
lbxFolders.ItemsSource = _settings.FolderLinks;
RefreshView();
}
public void RefreshView()
{
lbxFolders.Items.SortDescriptions.Add(new SortDescription("Path", ListSortDirection.Ascending));
if (lbxFolders.Items.Count == 0
&& btnDelete.Visibility == Visibility.Visible
&& btnEdit.Visibility == Visibility.Visible)
{
btnDelete.Visibility = Visibility.Hidden;
btnEdit.Visibility = Visibility.Hidden;
}
if (lbxFolders.Items.Count > 0
&& btnDelete.Visibility == Visibility.Hidden
&& btnEdit.Visibility == Visibility.Hidden)
{
btnDelete.Visibility = Visibility.Visible;
btnEdit.Visibility = Visibility.Visible;
}
lbxFolders.Items.Refresh();
}
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
var selectedFolder = lbxFolders.SelectedItem as FolderLink;
if (selectedFolder != null)
{
string msg = string.Format(flowlauncherAPI.GetTranslation("flowlauncher_plugin_folder_delete_folder_link"), selectedFolder.Path);
if (MessageBox.Show(msg, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
_settings.FolderLinks.Remove(selectedFolder);
RefreshView();
}
}
else
{
string warning = flowlauncherAPI.GetTranslation("flowlauncher_plugin_folder_select_folder_link_warning");
MessageBox.Show(warning);
}
}
private void btnEdit_Click(object sender, RoutedEventArgs e)
{
var selectedFolder = lbxFolders.SelectedItem as FolderLink;
if (selectedFolder != null)
{
var folderBrowserDialog = new FolderBrowserDialog();
folderBrowserDialog.SelectedPath = selectedFolder.Path;
if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
{
var link = _settings.FolderLinks.First(x => x.Path == selectedFolder.Path);
link.Path = folderBrowserDialog.SelectedPath;
}
RefreshView();
}
else
{
string warning = flowlauncherAPI.GetTranslation("flowlauncher_plugin_folder_select_folder_link_warning");
MessageBox.Show(warning);
}
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
var folderBrowserDialog = new FolderBrowserDialog();
if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
{
var newFolder = new FolderLink
{
Path = folderBrowserDialog.SelectedPath
};
if (_settings.FolderLinks == null)
{
_settings.FolderLinks = new List<FolderLink>();
}
_settings.FolderLinks.Add(newFolder);
}
RefreshView();
}
private void lbxFolders_Drop(object sender, DragEventArgs e)
{
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
if (files != null && files.Count() > 0)
{
if (_settings.FolderLinks == null)
{
_settings.FolderLinks = new List<FolderLink>();
}
foreach (string s in files)
{
if (Directory.Exists(s))
{
var newFolder = new FolderLink
{
Path = s
};
_settings.FolderLinks.Add(newFolder);
}
RefreshView();
}
}
}
private void lbxFolders_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effects = DragDropEffects.Link;
}
else
{
e.Effects = DragDropEffects.None;
}
}
}
}

View file

@ -1,15 +0,0 @@
<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">
<system:String x:Key="flowlauncher_plugin_folder_delete">Löschen</system:String>
<system:String x:Key="flowlauncher_plugin_folder_edit">Bearbeiten</system:String>
<system:String x:Key="flowlauncher_plugin_folder_add">Hinzufügen</system:String>
<system:String x:Key="flowlauncher_plugin_folder_folder_path">Ordnerpfad</system:String>
<system:String x:Key="flowlauncher_plugin_folder_select_folder_link_warning">Bitte wähle eine Ordnerverknüpfung</system:String>
<system:String x:Key="flowlauncher_plugin_folder_delete_folder_link">Bist du sicher {0} zu löschen?</system:String>
<system:String x:Key="flowlauncher_plugin_folder_plugin_name">Ordner</system:String>
<system:String x:Key="flowlauncher_plugin_folder_plugin_description">Öffne deine Lieblingsordner direkt von Flow Launcher aus</system:String>
</ResourceDictionary>

View file

@ -1,15 +0,0 @@
<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">
<system:String x:Key="flowlauncher_plugin_folder_delete">Delete</system:String>
<system:String x:Key="flowlauncher_plugin_folder_edit">Edit</system:String>
<system:String x:Key="flowlauncher_plugin_folder_add">Add</system:String>
<system:String x:Key="flowlauncher_plugin_folder_folder_path">Folder Path</system:String>
<system:String x:Key="flowlauncher_plugin_folder_select_folder_link_warning">Please select a folder link</system:String>
<system:String x:Key="flowlauncher_plugin_folder_delete_folder_link">Are you sure you want to delete {0}?</system:String>
<system:String x:Key="flowlauncher_plugin_folder_plugin_name">Folder</system:String>
<system:String x:Key="flowlauncher_plugin_folder_plugin_description">Open favorite folder from Flow Launcher directly</system:String>
</ResourceDictionary>

View file

@ -1,15 +0,0 @@
<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">
<system:String x:Key="flowlauncher_plugin_folder_delete">Usuń</system:String>
<system:String x:Key="flowlauncher_plugin_folder_edit">Edytuj</system:String>
<system:String x:Key="flowlauncher_plugin_folder_add">Dodaj</system:String>
<system:String x:Key="flowlauncher_plugin_folder_folder_path">Ścieżka folderu</system:String>
<system:String x:Key="flowlauncher_plugin_folder_select_folder_link_warning">Musisz wybrać któryś folder z listy</system:String>
<system:String x:Key="flowlauncher_plugin_folder_delete_folder_link">Czy jesteś pewien że chcesz usunąć {0}?</system:String>
<system:String x:Key="flowlauncher_plugin_folder_plugin_name">Foldery</system:String>
<system:String x:Key="flowlauncher_plugin_folder_plugin_description">Otwórz ulubione foldery bezpośrednio z poziomu Flow Launchera</system:String>
</ResourceDictionary>

View file

@ -1,15 +0,0 @@
<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">
<system:String x:Key="flowlauncher_plugin_folder_delete">Sil</system:String>
<system:String x:Key="flowlauncher_plugin_folder_edit">Düzenle</system:String>
<system:String x:Key="flowlauncher_plugin_folder_add">Ekle</system:String>
<system:String x:Key="flowlauncher_plugin_folder_folder_path">Klasör Konumu</system:String>
<system:String x:Key="flowlauncher_plugin_folder_select_folder_link_warning">Lütfen bir klasör bağlantısı seçin</system:String>
<system:String x:Key="flowlauncher_plugin_folder_delete_folder_link">{0} bağlantısını silmek istediğinize emin misiniz?</system:String>
<system:String x:Key="flowlauncher_plugin_folder_plugin_name">Klasör</system:String>
<system:String x:Key="flowlauncher_plugin_folder_plugin_description">Favori klasörünüzü doğrudan Flow Launcher'tan açın</system:String>
</ResourceDictionary>

View file

@ -1,15 +0,0 @@
<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">
<system:String x:Key="flowlauncher_plugin_folder_delete">删除</system:String>
<system:String x:Key="flowlauncher_plugin_folder_edit">编辑</system:String>
<system:String x:Key="flowlauncher_plugin_folder_add">添加</system:String>
<system:String x:Key="flowlauncher_plugin_folder_folder_path">文件夹路径</system:String>
<system:String x:Key="flowlauncher_plugin_folder_select_folder_link_warning">请选择一个文件夹</system:String>
<system:String x:Key="flowlauncher_plugin_folder_delete_folder_link">你确定要删除{0}吗?</system:String>
<system:String x:Key="flowlauncher_plugin_folder_plugin_name">文件夹</system:String>
<system:String x:Key="flowlauncher_plugin_folder_plugin_description">在Flow Launcher中直接打开收藏的文件夹</system:String>
</ResourceDictionary>

View file

@ -1,15 +0,0 @@
<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">
<system:String x:Key="flowlauncher_plugin_folder_delete">刪除</system:String>
<system:String x:Key="flowlauncher_plugin_folder_edit">編輯</system:String>
<system:String x:Key="flowlauncher_plugin_folder_add">新增</system:String>
<system:String x:Key="flowlauncher_plugin_folder_folder_path">資料夾路徑</system:String>
<system:String x:Key="flowlauncher_plugin_folder_select_folder_link_warning">請選擇一個資料夾</system:String>
<system:String x:Key="flowlauncher_plugin_folder_delete_folder_link">你確認要刪除{0}嗎?</system:String>
<system:String x:Key="flowlauncher_plugin_folder_plugin_name">資料夾</system:String>
<system:String x:Key="flowlauncher_plugin_folder_plugin_description">在 Flow Launcher 中直接開啟收藏的資料夾</system:String>
</ResourceDictionary>

View file

@ -1,396 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Plugin.SharedCommands;
namespace Flow.Launcher.Plugin.Folder
{
public class Main : IPlugin, ISettingProvider, IPluginI18n, ISavable, IContextMenu
{
public const string FolderImagePath = "Images\\folder.png";
public const string FileImagePath = "Images\\file.png";
public const string DeleteFileFolderImagePath = "Images\\deletefilefolder.png";
public const string CopyImagePath = "Images\\copy.png";
private string DefaultFolderSubtitleString = "Ctrl + Enter to open the directory";
private static List<string> _driverNames;
private PluginInitContext _context;
private readonly Settings _settings;
private readonly PluginJsonStorage<Settings> _storage;
private IContextMenu _contextMenuLoader;
private static Dictionary<string, string> _envStringPaths;
public Main()
{
_storage = new PluginJsonStorage<Settings>();
_settings = _storage.Load();
}
public void Save()
{
_storage.Save();
}
public Control CreateSettingPanel()
{
return new FolderPluginSettings(_context.API, _settings);
}
public void Init(PluginInitContext context)
{
_context = context;
_contextMenuLoader = new ContextMenuLoader(context);
InitialDriverList();
LoadEnvironmentStringPaths();
}
public List<Result> Query(Query query)
{
var results = GetUserFolderResults(query);
string search = query.Search.ToLower();
if (!IsDriveOrSharedFolder(search) && !IsEnvironmentVariableSearch(search))
{
return results;
}
if (IsEnvironmentVariableSearch(search))
{
results.AddRange(GetEnvironmentStringPathResults(search, query));
}
else
{
results.AddRange(QueryInternal_Directory_Exists(query.Search, query));
}
// todo why was this hack here?
foreach (var result in results)
{
result.Score += 10;
}
return results;
}
private static bool IsEnvironmentVariableSearch(string search)
{
return _envStringPaths != null && search.StartsWith("%");
}
private static bool IsDriveOrSharedFolder(string search)
{
if (search.StartsWith(@"\\"))
{ // shared folder
return true;
}
if (_driverNames != null && _driverNames.Any(search.StartsWith))
{ // normal drive letter
return true;
}
if (_driverNames == null && search.Length > 2 && char.IsLetter(search[0]) && search[1] == ':')
{ // when we don't have the drive letters we can try...
return true; // we don't know so let's give it the possibility
}
return false;
}
private Result CreateFolderResult(string title, string subtitle, string path, Query query)
{
return new Result
{
Title = title,
IcoPath = path,
SubTitle = subtitle,
TitleHighlightData = StringMatcher.FuzzySearch(query.Search, title).MatchData,
Action = c =>
{
if (c.SpecialKeyState.CtrlPressed)
{
try
{
FilesFolders.OpenPath(path);
return true;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Could not start " + path);
return false;
}
}
string changeTo = path.EndsWith("\\") ? path : path + "\\";
_context.API.ChangeQuery(string.IsNullOrEmpty(query.ActionKeyword) ?
changeTo :
query.ActionKeyword + " " + changeTo);
return false;
},
ContextData = new SearchResult { Type = ResultType.Folder, FullPath = path }
};
}
private List<Result> GetUserFolderResults(Query query)
{
string search = query.Search.ToLower();
var userFolderLinks = _settings.FolderLinks.Where(
x => x.Nickname.StartsWith(search, StringComparison.OrdinalIgnoreCase));
var results = userFolderLinks.Select(item =>
CreateFolderResult(item.Nickname, DefaultFolderSubtitleString, item.Path, query)).ToList();
return results;
}
private void InitialDriverList()
{
if (_driverNames == null)
{
_driverNames = new List<string>();
var allDrives = DriveInfo.GetDrives();
foreach (DriveInfo driver in allDrives)
{
_driverNames.Add(driver.Name.ToLower().TrimEnd('\\'));
}
}
}
private void LoadEnvironmentStringPaths()
{
_envStringPaths = new Dictionary<string, string>();
foreach (DictionaryEntry special in Environment.GetEnvironmentVariables())
{
if (Directory.Exists(special.Value.ToString()))
{
_envStringPaths.Add(special.Key.ToString().ToLower(), special.Value.ToString());
}
}
}
private static readonly char[] _specialSearchChars = new char[]
{
'?', '*', '>'
};
private List<Result> QueryInternal_Directory_Exists(string search, Query query)
{
var results = new List<Result>();
var hasSpecial = search.IndexOfAny(_specialSearchChars) >= 0;
string incompleteName = "";
if (hasSpecial || !Directory.Exists(search + "\\"))
{
// if folder doesn't exist, we want to take the last part and use it afterwards to help the user
// find the right folder.
int index = search.LastIndexOf('\\');
if (index > 0 && index < (search.Length - 1))
{
incompleteName = search.Substring(index + 1).ToLower();
search = search.Substring(0, index + 1);
if (!Directory.Exists(search))
{
return results;
}
}
else
{
return results;
}
}
else
{
// folder exist, add \ at the end of doesn't exist
if (!search.EndsWith("\\"))
{
search += "\\";
}
}
results.Add(CreateOpenCurrentFolderResult(incompleteName, search));
var searchOption = SearchOption.TopDirectoryOnly;
incompleteName += "*";
// give the ability to search all folder when starting with >
if (incompleteName.StartsWith(">"))
{
searchOption = SearchOption.AllDirectories;
// match everything before and after search term using supported wildcard '*', ie. *searchterm*
incompleteName = "*" + incompleteName.Substring(1);
}
var folderList = new List<Result>();
var fileList = new List<Result>();
var folderSubtitleString = DefaultFolderSubtitleString;
try
{
// search folder and add results
var directoryInfo = new DirectoryInfo(search);
var fileSystemInfos = directoryInfo.GetFileSystemInfos(incompleteName, searchOption);
foreach (var fileSystemInfo in fileSystemInfos)
{
if ((fileSystemInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) continue;
if(fileSystemInfo is DirectoryInfo)
{
if (searchOption == SearchOption.AllDirectories)
folderSubtitleString = fileSystemInfo.FullName;
folderList.Add(CreateFolderResult(fileSystemInfo.Name, folderSubtitleString, fileSystemInfo.FullName, query));
}
else
{
fileList.Add(CreateFileResult(fileSystemInfo.FullName, query));
}
}
}
catch (Exception e)
{
if (e is UnauthorizedAccessException || e is ArgumentException)
{
results.Add(new Result { Title = e.Message, Score = 501 });
return results;
}
throw;
}
// Intial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection.
return results.Concat(folderList.OrderBy(x => x.Title)).Concat(fileList.OrderBy(x => x.Title)).ToList();
}
private List<Result> GetEnvironmentStringPathSuggestions(string search, Query query)
{
var results = new List<Result>();
foreach (var p in _envStringPaths)
{
if (p.Key.StartsWith(search))
{
results.Add(CreateFolderResult($"%{p.Key}%", p.Value, p.Value, query));
}
}
return results;
}
private List<Result> GetEnvironmentStringPathResults(string envStringSearch, Query query)
{
if (envStringSearch == "%")
{ // return all environment string options as path suggestions
return GetEnvironmentStringPathSuggestions("", query);
}
var results = new List<Result>();
var search = envStringSearch.Substring(1);
if (search.EndsWith("%") && search.Length > 1)
{ // query starts and ends with a %, find an exact match from env-string paths
var exactEnvStringPath = search.Substring(0, search.Length-1);
if (_envStringPaths.ContainsKey(exactEnvStringPath))
{
var expandedPath = _envStringPaths[exactEnvStringPath];
results.Add(CreateFolderResult($"%{exactEnvStringPath}%", expandedPath, expandedPath, query));
}
}
else if (search.Contains("%"))
{ // query starts with a % and contains another % somewhere before the end
var splitSearch = search.Split("%");
var exactEnvStringPath = splitSearch[0];
// if there are more than 2 % characters in the query, don't bother
if (splitSearch.Length == 2 && _envStringPaths.ContainsKey(exactEnvStringPath))
{
var queryPartToReplace = $"%{exactEnvStringPath}%";
var expandedPath = _envStringPaths[exactEnvStringPath];
// replace the %envstring% part of the query with its expanded equivalent
var updatedSearch = envStringSearch.Replace(queryPartToReplace, expandedPath);
results.AddRange(QueryInternal_Directory_Exists(updatedSearch, query));
}
}
else
{ // query simply starts wtih a %, suggest env-string paths that match the rest of the search
results.AddRange(GetEnvironmentStringPathSuggestions(search, query));
}
return results;
}
private static Result CreateFileResult(string filePath, Query query)
{
var result = new Result
{
Title = Path.GetFileName(filePath),
SubTitle = filePath,
IcoPath = filePath,
TitleHighlightData = StringMatcher.FuzzySearch(query.Search, Path.GetFileName(filePath)).MatchData,
Action = c =>
{
try
{
FilesFolders.OpenPath(filePath);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Could not start " + filePath);
}
return true;
},
ContextData = new SearchResult { Type = ResultType.File, FullPath = filePath}
};
return result;
}
private static Result CreateOpenCurrentFolderResult(string incompleteName, string search)
{
var firstResult = "Open current directory";
if (incompleteName.Length > 0)
firstResult = "Open " + search;
var folderName = search.TrimEnd('\\').Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None).Last();
return new Result
{
Title = firstResult,
SubTitle = $"Use > to search files and subfolders within {folderName}, " +
$"* to search for file extensions in {folderName} or both >* to combine the search",
IcoPath = search,
Score = 500,
Action = c =>
{
FilesFolders.OpenPath(search);
return true;
}
};
}
public string GetTranslatedPluginTitle()
{
return _context.API.GetTranslation("flowlauncher_plugin_folder_plugin_name");
}
public string GetTranslatedPluginDescription()
{
return _context.API.GetTranslation("flowlauncher_plugin_folder_plugin_description");
}
public List<Result> LoadContextMenus(Result selectedResult)
{
return _contextMenuLoader.LoadContextMenus(selectedResult);
}
}
}

View file

@ -1,12 +0,0 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Flow.Launcher.Infrastructure.Storage;
namespace Flow.Launcher.Plugin.Folder
{
public class Settings
{
[JsonProperty]
public List<FolderLink> FolderLinks { get; set; } = new List<FolderLink>();
}
}

View file

@ -1,12 +0,0 @@
{
"ID": "B4D3B69656E14D44865C8D818EAE47C4",
"ActionKeyword": "*",
"Name": "Folder",
"Description": "Open favorite folder from Flow Launcher directorily",
"Author": "qianlifeng",
"Version": "1.0.0",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Folder.dll",
"IcoPath": "Images\\folder.png"
}

View file

@ -101,9 +101,5 @@
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" />
<PackageReference Include="System.Runtime" Version="4.3.1" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View file

@ -103,8 +103,4 @@
<PackageReference Include="System.Runtime" Version="4.3.1" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View file

@ -113,8 +113,5 @@
<PackageReference Include="NLog" Version="4.7.0" />
<PackageReference Include="System.Runtime" Version="4.3.1" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View file

@ -99,8 +99,4 @@
<PackageReference Include="System.Runtime" Version="4.3.1" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View file

@ -124,7 +124,6 @@
<ItemGroup>
<Folder Include="Images\Images\" />
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>

View file

@ -93,9 +93,5 @@
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" />
<PackageReference Include="System.Runtime" Version="4.3.1" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View file

@ -151,8 +151,4 @@
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View file

@ -51,9 +51,5 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View file

@ -2,10 +2,10 @@ Flow Launcher
=============
![Maintenance](https://img.shields.io/maintenance/yes/2020)
[![Build status](https://ci.appveyor.com/api/projects/status/32r7s2skrgm9ubva?svg=true&retina=true)](https://ci.appveyor.com/project/JohnTheGr8/flow-launcher/branch/dev)
[![Github All Releases](https://img.shields.io/github/downloads/Flow-Launcher/Flow.Launcher/total.svg)](https://github.com/Flow-Launcher/Flow.Launcher/releases)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/Flow-Launcher/Flow.Launcher)](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest)
![GitHub Release Date](https://img.shields.io/github/release-date/Flow-Launcher/Flow.Launcher)
[![Build Status](https://dev.azure.com/Flow-Launcher/Flow.Launcher/_apis/build/status/Flow.Launcher?branchName=master)](https://dev.azure.com/Flow-Launcher/Flow.Launcher/_build/latest?definitionId=1&branchName=master)
Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at being more than an app launcher, it searches, integrates and expands on functionalities. Flow will continue to evolve, designed to be open and built with the community at heart.
@ -14,7 +14,8 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be
## Features
![The Flow](https://user-images.githubusercontent.com/26427004/82151677-fa9c7100-989f-11ea-9143-81de60aaf07d.gif)
- Search everything from applications, folders, bookmarks, YouTube, Twitter and more. All from the comfort of your keyboard without ever touching the mouse.
- Search everything from applications, files, bookmarks, YouTube, Twitter and more. All from the comfort of your keyboard without ever touching the mouse.
- Support search using environment variable paths
- Run batch and PowerShell commands as Administrator or a different user.
- Support languages from Chinese to Italian and more.
- Support of wide range of plugins.
@ -51,6 +52,8 @@ We welcome all contributions. If you are unsure of a change you want to make, si
You will find the main goals of flow placed under Projects board, so feel free to contribute on that. If you would like to make small incremental changes, feel free to do so as well.
Get in touch if you like to join the Flow-Launcher Team and help build this great tool.
**Question/Suggestion**
Yes please, submit an issue to let us know.

View file

@ -4,13 +4,13 @@
<id>FlowLauncher</id>
<title>Flow Launcher</title>
<version>$version$</version>
<authors>happlebao, Jeremy Wu</authors>
<authors>Flow-Launcher Team</authors>
<projectUrl>https://github.com/Flow-Launcher/Flow.Launcher</projectUrl>
<iconUrl>https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher/master/Flow.Launcher/Images/app.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Flow Launcher - a launcher for windows</description>
</metadata>
<files>
<file src="**\*.*" target="lib\net45\" exclude="Flow.Launcher.vshost.exe;Flow.Launcher.vshost.exe.config;Flow.Launcher.vshost.exe.manifest;*.nupkg;Setup.exe;RELEASES"/>
<file src="**\*.*" target="lib\netcoreapp3.1\" exclude="Flow.Launcher.vshost.exe;Flow.Launcher.vshost.exe.config;Flow.Launcher.vshost.exe.manifest;*.nupkg;Setup.exe;RELEASES"/>
</files>
</package>

View file

@ -1,16 +0,0 @@
<?xml version="1.0"?>
<package>
<metadata>
<id>Flow.Launcher.Plugin</id>
<version>$version$</version>
<authors>qianlifeng, Jeremy Wu</authors>
<licenseUrl>https://github.com/Flow-Launcher/Flow.Launcher/blob/master/LICENSE</licenseUrl>
<projectUrl>https://github.com/Flow-Launcher/Flow.Launcher</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Reference this library if you want to develop a Flow Launcher plugin</description>
<tags>flowlauncher</tags>
</metadata>
<files>
<file src="..\Output\Release\Flow.Launcher.Plugin.dll" target="lib\net452" />
</files>
</package>

View file

@ -1,15 +1,15 @@
param(
[string]$config = "Release",
[string]$solution,
[string]$targetpath
[string]$targetpath
)
Write-Host "Config: $config"
function Build-Version {
if ([string]::IsNullOrEmpty($env:APPVEYOR_BUILD_VERSION)) {
$v = (Get-Command ${TargetPath}).FileVersionInfo.FileVersion
} else {
$v = $env:APPVEYOR_BUILD_VERSION
if ([string]::IsNullOrEmpty($env:flowVersion)) {
$v = (Get-Command ${TargetPath}).FileVersionInfo.FileVersion
} else {
$v = $env:flowVersion
}
Write-Host "Build Version: $v"
@ -35,7 +35,6 @@ function Copy-Resources ($path, $config) {
$project = "$path\Flow.Launcher"
$output = "$path\Output"
$target = "$output\$config"
Copy-Item -Recurse -Force $project\Themes\* $target\Themes\
Copy-Item -Recurse -Force $project\Images\* $target\Images\
Copy-Item -Recurse -Force $path\Plugins\HelloWorldPython $target\Plugins\HelloWorldPython
Copy-Item -Recurse -Force $path\JsonRPC $target\JsonRPC
@ -57,28 +56,13 @@ function Validate-Directory ($output) {
New-Item $output -ItemType Directory -Force
}
function Pack-Nuget ($path, $version, $output) {
Write-Host "Begin build nuget library"
$spec = "$path\Scripts\flowlauncher.plugin.nuspec"
Write-Host "nuspec path: $spec"
Write-Host "Output path: $output"
Nuget pack $spec -Version $version -OutputDirectory $output
Write-Host "End build nuget library"
}
function Zip-Release ($path, $version, $output) {
Write-Host "Begin zip release"
$input = "$path\Output\Release"
Write-Host "Input path: $input"
$file = "$output\Flow.Launcher-$version.zip"
Write-Host "Filename: $file"
$content = "$path\Output\Release\*"
$zipFile = "$output\Flow-Launcher-v$version.zip"
[Reflection.Assembly]::LoadWithPartialName("System.IO.Compression.FileSystem")
[System.IO.Compression.ZipFile]::CreateFromDirectory($input, $file)
Compress-Archive -Force -Path $content -DestinationPath $zipFile
Write-Host "End zip release"
}
@ -88,10 +72,12 @@ function Pack-Squirrel-Installer ($path, $version, $output) {
Write-Host "Begin pack squirrel installer"
$spec = "$path\Scripts\flowlauncher.nuspec"
Write-Host "nuspec path: $spec"
$input = "$path\Output\Release"
Write-Host "Packing: $spec"
Write-Host "Input path: $input"
Nuget pack $spec -Version $version -Properties Configuration=Release -BasePath $input -OutputDirectory $output
# TODO: can we use dotnet pack here?
nuget pack $spec -Version $version -BasePath $input -OutputDirectory $output -Properties Configuration=Release
$nupkg = "$output\FlowLauncher.$version.nupkg"
Write-Host "nupkg path: $nupkg"
@ -107,7 +93,7 @@ function Pack-Squirrel-Installer ($path, $version, $output) {
Move-Item $temp\* $output -Force
Remove-Item $temp
$file = "$output\Flow Launcher-$version.exe"
$file = "$output\Flow-Launcher-v$version.exe"
Write-Host "Filename: $file"
Move-Item "$output\Setup.exe" $file -Force
@ -133,7 +119,7 @@ function Main {
if(IsDotNetCoreAppSelfContainedPublishEvent) {
FixPublishLastWriteDateTimeError $p
}
}
Delete-Unused $p $config
$o = "$p\Output\Packages"
@ -144,7 +130,6 @@ function Main {
$isInCI = $env:APPVEYOR
if ($isInCI) {
Pack-Nuget $p $v $o
Zip-Release $p $v $o
}

View file

@ -1,31 +1,34 @@
version: 1.3.{build}
image: Visual Studio 2017
configuration: Release
platform: Any CPU
version: '0.9.0.{build}'
init:
- ps: |
$version = new-object System.Version $env:APPVEYOR_BUILD_VERSION
$env:flowVersion = "{0}.{1}.{2}" -f $version.Major, $version.Minor, $version.Build
- sc config WSearch start= auto # Starts Windows Search service- Needed for running ExplorerTest
- net start WSearch
assembly_info:
patch: true
file: AssemblyInfo.*
assembly_version: '{version}'
assembly_file_version: '{version}'
assembly_informational_version: '{version}'
file: SolutionAssemblyInfo.cs
assembly_version: $(flowVersion)
assembly_file_version: $(flowVersion)
assembly_informational_version: $(flowVersion)
skip_commits:
files:
- '*.md'
image: Visual Studio 2019
platform: Any CPU
configuration: Release
before_build:
- ps: nuget restore
build:
project: Flow.Launcher.sln
after_test:
verbosity: minimal
artifacts:
- path: 'Output\Packages\Flow.Launcher-*.zip'
name: zipped_binary
- path: 'Output\Packages\Flow.Launcher.Plugin.*.nupkg'
name: nuget_package
- path: 'Output\Packages\Flow.Launcher-*.*'
name: installer
- path: 'Output\Packages\RELEASES'
name: installer
deploy:
provider: NuGet
api_key:
secure: yybUOFgBuGVpbmOVZxsurC8OpkClzt9dR+/54WpMWcq6b6oyMatciaelRPnXsjRn
artifact: nuget_package
on:
branch: api
- path: 'Output\Packages\Flow-Launcher-*.zip'
name: Zip
- path: 'Output\Release\Flow.Launcher.Plugin.*.nupkg'
name: Plugin nupkg