diff --git a/.pipelines/ci/ci.yml b/.pipelines/ci/ci.yml
index 47999a29db..850bb1ea08 100644
--- a/.pipelines/ci/ci.yml
+++ b/.pipelines/ci/ci.yml
@@ -4,6 +4,7 @@ trigger:
include:
- master
- dev/build-features
+ - dev/PowerLauncher
paths:
exclude:
- doc/*
@@ -15,6 +16,7 @@ pr:
include:
- master
- dev/build-features
+ - dev/PowerLauncher
# 0.0.yyMM.dd##
# 0.0.1904.0900
diff --git a/PowerToys.sln b/PowerToys.sln
index 86d29bd040..998382a7ae 100644
--- a/PowerToys.sln
+++ b/PowerToys.sln
@@ -17,8 +17,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "runner", "src\runner\runner
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D} = {D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481} = {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}
{0B593A6C-4143-4337-860E-DB5710FB87DB} = {0B593A6C-4143-4337-860E-DB5710FB87DB}
+ {E364F67B-BB12-4E91-B639-355866EBCD8B} = {E364F67B-BB12-4E91-B639-355866EBCD8B}
{DA425894-6E13-404F-8DCB-78584EC0557A} = {DA425894-6E13-404F-8DCB-78584EC0557A}
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34} = {2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}
+ {A7D5099E-F0FD-4BF3-8522-5A682759F915} = {A7D5099E-F0FD-4BF3-8522-5A682759F915}
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B} = {0B43679E-EDFA-4DA0-AD30-F4628B308B1B}
{B25AC7A5-FB9F-4789-B392-D5C85E948670} = {B25AC7A5-FB9F-4789-B392-D5C85E948670}
{E4E0D2AE-B17D-4BD4-8BEE-AFC8CC464C5F} = {E4E0D2AE-B17D-4BD4-8BEE-AFC8CC464C5F}
@@ -29,7 +31,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "runner", "src\runner\runner
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99} = {F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}
{07C389E3-6BC8-41CF-923E-307B1265FA2D} = {07C389E3-6BC8-41CF-923E-307B1265FA2D}
{89F34AF7-1C34-4A72-AA6E-534BCF972BD9} = {89F34AF7-1C34-4A72-AA6E-534BCF972BD9}
- {A7D5099E-F0FD-4BF3-8522-5A682759F915} = {A7D5099E-F0FD-4BF3-8522-5A682759F915}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "src\common\common.vcxproj", "{74485049-C722-400F-ABE5-86AC52D929B3}"
@@ -164,6 +165,50 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "keyboardmanager", "keyboard
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerCommon", "src\modules\keyboardmanager\common\KeyboardManagerCommon.vcxproj", "{8AFFA899-0B73-49EC-8C50-0FADDA57B2FC}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "launcher", "launcher", "{C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wox", "src\modules\launcher\Wox\Wox.csproj", "{DB90F671-D861-46BB-93A3-F1304F5BA1C5}"
+ ProjectSection(ProjectDependencies) = postProject
+ {FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4}
+ {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}
+ {F8B870EB-D5F5-45BA-9CF7-A5C459818820} = {F8B870EB-D5F5-45BA-9CF7-A5C459818820}
+ {74F1B9ED-F59C-4FE7-B473-7B453E30837E} = {74F1B9ED-F59C-4FE7-B473-7B453E30837E}
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wox.Core", "src\modules\launcher\Wox.Core\Wox.Core.csproj", "{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wox.Infrastructure", "src\modules\launcher\Wox.Infrastructure\Wox.Infrastructure.csproj", "{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wox.Plugin", "src\modules\launcher\Wox.Plugin\Wox.Plugin.csproj", "{8451ECDD-2EA4-4966-BB0A-7BBC40138E80}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wox.Test", "src\modules\launcher\Wox.Test\Wox.Test.csproj", "{FF742965-9A80-41A5-B042-D6C7D3A21708}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{4AFC9975-2456-4C70-94A4-84073C1CED93}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wox.Plugin.Calculator", "src\modules\launcher\Plugins\Wox.Plugin.Calculator\Wox.Plugin.Calculator.csproj", "{59BD9891-3837-438A-958D-ADC7F91F6F7E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wox.Plugin.WindowWalker", "src\modules\launcher\Plugins\Wox.Plugin.WindowWalker\Wox.Plugin.WindowWalker.csproj", "{74F1B9ED-F59C-4FE7-B473-7B453E30837E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wox.Plugin.Program", "src\modules\launcher\Plugins\Wox.Plugin.Program\Wox.Plugin.Program.csproj", "{FDB3555B-58EF-4AE6-B5F1-904719637AB4}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wox.Plugin.Shell", "src\modules\launcher\Plugins\Wox.Plugin.Shell\Wox.Plugin.Shell.csproj", "{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Plugin.Indexer", "src\modules\launcher\Plugins\Microsoft.Plugin.Indexer\Microsoft.Plugin.Indexer.csproj", "{F8B870EB-D5F5-45BA-9CF7-A5C459818820}"
+ ProjectSection(ProjectDependencies) = postProject
+ {8451ECDD-2EA4-4966-BB0A-7BBC40138E80} = {8451ECDD-2EA4-4966-BB0A-7BBC40138E80}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Launcher", "src\modules\launcher\Microsoft.Launcher\Microsoft.Launcher.vcxproj", "{E364F67B-BB12-4E91-B639-355866EBCD8B}"
+ ProjectSection(ProjectDependencies) = postProject
+ {F97E5003-F263-4D4A-A964-0F1F3C82DEF2} = {F97E5003-F263-4D4A-A964-0F1F3C82DEF2}
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerLauncher", "src\modules\launcher\PowerLauncher\PowerLauncher.csproj", "{F97E5003-F263-4D4A-A964-0F1F3C82DEF2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerLauncher.UI", "src\modules\launcher\PowerLauncher.UI\PowerLauncher.UI.csproj", "{4A3DE70C-684C-410D-B851-C23B6DAEDF16}"
+EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{E775CC2C-24CB-48D6-9C3A-BE4CCE0DB17A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "win-app-driver", "src\tests\win-app-driver\win-app-driver.csproj", "{880ED251-9E16-4713-9A70-D35FE0C01669}"
@@ -208,192 +253,654 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common-md-flag", "src\commo
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "interop-tests", "src\common\interop-tests\interop-tests.csproj", "{437AD818-3F1F-4CA5-A79B-25233A157026}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wox.Plugin.Folder", "src\modules\launcher\Plugins\Wox.Plugin.Folder\Wox.Plugin.Folder.csproj", "{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|ARM = Debug|ARM
+ Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|ARM = Release|ARM
+ Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
+ Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Debug|ARM.ActiveCfg = Debug|x64
+ {9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Debug|ARM64.ActiveCfg = Debug|x64
{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Debug|x64.ActiveCfg = Debug|x64
{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Debug|x64.Build.0 = Debug|x64
+ {9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Debug|x86.ActiveCfg = Debug|x64
+ {9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Release|ARM.ActiveCfg = Release|x64
+ {9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Release|ARM64.ActiveCfg = Release|x64
{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Release|x64.ActiveCfg = Release|x64
{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Release|x64.Build.0 = Release|x64
+ {9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Release|x86.ActiveCfg = Release|x64
+ {74485049-C722-400F-ABE5-86AC52D929B3}.Debug|ARM.ActiveCfg = Debug|x64
+ {74485049-C722-400F-ABE5-86AC52D929B3}.Debug|ARM64.ActiveCfg = Debug|x64
{74485049-C722-400F-ABE5-86AC52D929B3}.Debug|x64.ActiveCfg = Debug|x64
{74485049-C722-400F-ABE5-86AC52D929B3}.Debug|x64.Build.0 = Debug|x64
+ {74485049-C722-400F-ABE5-86AC52D929B3}.Debug|x86.ActiveCfg = Debug|x64
+ {74485049-C722-400F-ABE5-86AC52D929B3}.Release|ARM.ActiveCfg = Release|x64
+ {74485049-C722-400F-ABE5-86AC52D929B3}.Release|ARM64.ActiveCfg = Release|x64
{74485049-C722-400F-ABE5-86AC52D929B3}.Release|x64.ActiveCfg = Release|x64
{74485049-C722-400F-ABE5-86AC52D929B3}.Release|x64.Build.0 = Release|x64
+ {74485049-C722-400F-ABE5-86AC52D929B3}.Release|x86.ActiveCfg = Release|x64
+ {A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}.Debug|ARM.ActiveCfg = Debug|x64
+ {A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}.Debug|ARM64.ActiveCfg = Debug|x64
{A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}.Debug|x64.ActiveCfg = Debug|x64
{A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}.Debug|x64.Build.0 = Debug|x64
+ {A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}.Debug|x86.ActiveCfg = Debug|x64
+ {A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}.Release|ARM.ActiveCfg = Release|x64
+ {A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}.Release|ARM64.ActiveCfg = Release|x64
{A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}.Release|x64.ActiveCfg = Release|x64
{A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}.Release|x64.Build.0 = Release|x64
+ {A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}.Release|x86.ActiveCfg = Release|x64
+ {44CC9375-3E6E-4D99-8913-7FB748807EBD}.Debug|ARM.ActiveCfg = Debug|x64
+ {44CC9375-3E6E-4D99-8913-7FB748807EBD}.Debug|ARM64.ActiveCfg = Debug|x64
{44CC9375-3E6E-4D99-8913-7FB748807EBD}.Debug|x64.ActiveCfg = Debug|x64
{44CC9375-3E6E-4D99-8913-7FB748807EBD}.Debug|x64.Build.0 = Debug|x64
+ {44CC9375-3E6E-4D99-8913-7FB748807EBD}.Debug|x86.ActiveCfg = Debug|x64
+ {44CC9375-3E6E-4D99-8913-7FB748807EBD}.Release|ARM.ActiveCfg = Release|x64
+ {44CC9375-3E6E-4D99-8913-7FB748807EBD}.Release|ARM64.ActiveCfg = Release|x64
{44CC9375-3E6E-4D99-8913-7FB748807EBD}.Release|x64.ActiveCfg = Release|x64
{44CC9375-3E6E-4D99-8913-7FB748807EBD}.Release|x64.Build.0 = Release|x64
+ {44CC9375-3E6E-4D99-8913-7FB748807EBD}.Release|x86.ActiveCfg = Release|x64
+ {07C389E3-6BC8-41CF-923E-307B1265FA2D}.Debug|ARM.ActiveCfg = Debug|x64
+ {07C389E3-6BC8-41CF-923E-307B1265FA2D}.Debug|ARM64.ActiveCfg = Debug|x64
{07C389E3-6BC8-41CF-923E-307B1265FA2D}.Debug|x64.ActiveCfg = Debug|x64
{07C389E3-6BC8-41CF-923E-307B1265FA2D}.Debug|x64.Build.0 = Debug|x64
+ {07C389E3-6BC8-41CF-923E-307B1265FA2D}.Debug|x86.ActiveCfg = Debug|x64
+ {07C389E3-6BC8-41CF-923E-307B1265FA2D}.Release|ARM.ActiveCfg = Release|x64
+ {07C389E3-6BC8-41CF-923E-307B1265FA2D}.Release|ARM64.ActiveCfg = Release|x64
{07C389E3-6BC8-41CF-923E-307B1265FA2D}.Release|x64.ActiveCfg = Release|x64
{07C389E3-6BC8-41CF-923E-307B1265FA2D}.Release|x64.Build.0 = Release|x64
+ {07C389E3-6BC8-41CF-923E-307B1265FA2D}.Release|x86.ActiveCfg = Release|x64
+ {F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Debug|ARM.ActiveCfg = Debug|x64
+ {F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Debug|ARM64.ActiveCfg = Debug|x64
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Debug|x64.ActiveCfg = Debug|x64
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Debug|x64.Build.0 = Debug|x64
+ {F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Debug|x86.ActiveCfg = Debug|x64
+ {F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Release|ARM.ActiveCfg = Release|x64
+ {F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Release|ARM64.ActiveCfg = Release|x64
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Release|x64.ActiveCfg = Release|x64
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Release|x64.Build.0 = Release|x64
+ {F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Release|x86.ActiveCfg = Release|x64
+ {48804216-2A0E-4168-A6D8-9CD068D14227}.Debug|ARM.ActiveCfg = Debug|x64
+ {48804216-2A0E-4168-A6D8-9CD068D14227}.Debug|ARM64.ActiveCfg = Debug|x64
{48804216-2A0E-4168-A6D8-9CD068D14227}.Debug|x64.ActiveCfg = Debug|x64
{48804216-2A0E-4168-A6D8-9CD068D14227}.Debug|x64.Build.0 = Debug|x64
+ {48804216-2A0E-4168-A6D8-9CD068D14227}.Debug|x86.ActiveCfg = Debug|x64
+ {48804216-2A0E-4168-A6D8-9CD068D14227}.Release|ARM.ActiveCfg = Release|x64
+ {48804216-2A0E-4168-A6D8-9CD068D14227}.Release|ARM64.ActiveCfg = Release|x64
{48804216-2A0E-4168-A6D8-9CD068D14227}.Release|x64.ActiveCfg = Release|x64
{48804216-2A0E-4168-A6D8-9CD068D14227}.Release|x64.Build.0 = Release|x64
+ {48804216-2A0E-4168-A6D8-9CD068D14227}.Release|x86.ActiveCfg = Release|x64
+ {9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Debug|ARM.ActiveCfg = Debug|x64
+ {9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Debug|ARM64.ActiveCfg = Debug|x64
{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Debug|x64.ActiveCfg = Debug|x64
{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Debug|x64.Build.0 = Debug|x64
+ {9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Debug|x86.ActiveCfg = Debug|x64
+ {9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Release|ARM.ActiveCfg = Release|x64
+ {9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Release|ARM64.ActiveCfg = Release|x64
{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Release|x64.ActiveCfg = Release|x64
{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Release|x64.Build.0 = Release|x64
+ {9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Release|x86.ActiveCfg = Release|x64
+ {1A066C63-64B3-45F8-92FE-664E1CCE8077}.Debug|ARM.ActiveCfg = Debug|x64
+ {1A066C63-64B3-45F8-92FE-664E1CCE8077}.Debug|ARM64.ActiveCfg = Debug|x64
{1A066C63-64B3-45F8-92FE-664E1CCE8077}.Debug|x64.ActiveCfg = Debug|x64
{1A066C63-64B3-45F8-92FE-664E1CCE8077}.Debug|x64.Build.0 = Debug|x64
+ {1A066C63-64B3-45F8-92FE-664E1CCE8077}.Debug|x86.ActiveCfg = Debug|x64
+ {1A066C63-64B3-45F8-92FE-664E1CCE8077}.Release|ARM.ActiveCfg = Release|x64
+ {1A066C63-64B3-45F8-92FE-664E1CCE8077}.Release|ARM64.ActiveCfg = Release|x64
{1A066C63-64B3-45F8-92FE-664E1CCE8077}.Release|x64.ActiveCfg = Release|x64
{1A066C63-64B3-45F8-92FE-664E1CCE8077}.Release|x64.Build.0 = Release|x64
+ {1A066C63-64B3-45F8-92FE-664E1CCE8077}.Release|x86.ActiveCfg = Release|x64
+ {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|ARM.ActiveCfg = Debug|x64
+ {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|ARM64.ActiveCfg = Debug|x64
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|x64.ActiveCfg = Debug|x64
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|x64.Build.0 = Debug|x64
+ {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|x86.ActiveCfg = Debug|x64
+ {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|ARM.ActiveCfg = Release|x64
+ {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|ARM64.ActiveCfg = Release|x64
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|x64.ActiveCfg = Release|x64
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|x64.Build.0 = Release|x64
+ {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|x86.ActiveCfg = Release|x64
+ {B25AC7A5-FB9F-4789-B392-D5C85E948670}.Debug|ARM.ActiveCfg = Debug|Win32
+ {B25AC7A5-FB9F-4789-B392-D5C85E948670}.Debug|ARM64.ActiveCfg = Debug|Win32
{B25AC7A5-FB9F-4789-B392-D5C85E948670}.Debug|x64.ActiveCfg = Debug|x64
{B25AC7A5-FB9F-4789-B392-D5C85E948670}.Debug|x64.Build.0 = Debug|x64
+ {B25AC7A5-FB9F-4789-B392-D5C85E948670}.Debug|x86.ActiveCfg = Debug|Win32
+ {B25AC7A5-FB9F-4789-B392-D5C85E948670}.Debug|x86.Build.0 = Debug|Win32
+ {B25AC7A5-FB9F-4789-B392-D5C85E948670}.Release|ARM.ActiveCfg = Release|Win32
+ {B25AC7A5-FB9F-4789-B392-D5C85E948670}.Release|ARM64.ActiveCfg = Release|Win32
{B25AC7A5-FB9F-4789-B392-D5C85E948670}.Release|x64.ActiveCfg = Release|x64
{B25AC7A5-FB9F-4789-B392-D5C85E948670}.Release|x64.Build.0 = Release|x64
+ {B25AC7A5-FB9F-4789-B392-D5C85E948670}.Release|x86.ActiveCfg = Release|Win32
+ {B25AC7A5-FB9F-4789-B392-D5C85E948670}.Release|x86.Build.0 = Release|Win32
+ {51920F1F-C28C-4ADF-8660-4238766796C2}.Debug|ARM.ActiveCfg = Debug|Win32
+ {51920F1F-C28C-4ADF-8660-4238766796C2}.Debug|ARM64.ActiveCfg = Debug|Win32
{51920F1F-C28C-4ADF-8660-4238766796C2}.Debug|x64.ActiveCfg = Debug|x64
{51920F1F-C28C-4ADF-8660-4238766796C2}.Debug|x64.Build.0 = Debug|x64
+ {51920F1F-C28C-4ADF-8660-4238766796C2}.Debug|x86.ActiveCfg = Debug|Win32
+ {51920F1F-C28C-4ADF-8660-4238766796C2}.Debug|x86.Build.0 = Debug|Win32
+ {51920F1F-C28C-4ADF-8660-4238766796C2}.Release|ARM.ActiveCfg = Release|Win32
+ {51920F1F-C28C-4ADF-8660-4238766796C2}.Release|ARM64.ActiveCfg = Release|Win32
{51920F1F-C28C-4ADF-8660-4238766796C2}.Release|x64.ActiveCfg = Release|x64
{51920F1F-C28C-4ADF-8660-4238766796C2}.Release|x64.Build.0 = Release|x64
+ {51920F1F-C28C-4ADF-8660-4238766796C2}.Release|x86.ActiveCfg = Release|Win32
+ {51920F1F-C28C-4ADF-8660-4238766796C2}.Release|x86.Build.0 = Release|Win32
+ {0E072714-D127-460B-AFAD-B4C40B412798}.Debug|ARM.ActiveCfg = Debug|x64
+ {0E072714-D127-460B-AFAD-B4C40B412798}.Debug|ARM64.ActiveCfg = Debug|x64
{0E072714-D127-460B-AFAD-B4C40B412798}.Debug|x64.ActiveCfg = Debug|x64
{0E072714-D127-460B-AFAD-B4C40B412798}.Debug|x64.Build.0 = Debug|x64
+ {0E072714-D127-460B-AFAD-B4C40B412798}.Debug|x86.ActiveCfg = Debug|x64
+ {0E072714-D127-460B-AFAD-B4C40B412798}.Release|ARM.ActiveCfg = Release|x64
+ {0E072714-D127-460B-AFAD-B4C40B412798}.Release|ARM64.ActiveCfg = Release|x64
{0E072714-D127-460B-AFAD-B4C40B412798}.Release|x64.ActiveCfg = Release|x64
{0E072714-D127-460B-AFAD-B4C40B412798}.Release|x64.Build.0 = Release|x64
+ {0E072714-D127-460B-AFAD-B4C40B412798}.Release|x86.ActiveCfg = Release|x64
+ {A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Debug|ARM.ActiveCfg = Debug|Win32
+ {A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Debug|ARM64.ActiveCfg = Debug|Win32
{A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Debug|x64.ActiveCfg = Debug|x64
{A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Debug|x64.Build.0 = Debug|x64
+ {A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Debug|x86.ActiveCfg = Debug|Win32
+ {A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Debug|x86.Build.0 = Debug|Win32
+ {A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Release|ARM.ActiveCfg = Release|Win32
+ {A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Release|ARM64.ActiveCfg = Release|Win32
{A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Release|x64.ActiveCfg = Release|x64
{A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Release|x64.Build.0 = Release|x64
+ {A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Release|x86.ActiveCfg = Release|Win32
+ {A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Release|x86.Build.0 = Release|Win32
+ {2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Debug|ARM.ActiveCfg = Debug|Win32
+ {2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Debug|ARM64.ActiveCfg = Debug|Win32
{2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Debug|x64.ActiveCfg = Debug|x64
{2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Debug|x64.Build.0 = Debug|x64
+ {2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Debug|x86.ActiveCfg = Debug|Win32
+ {2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Debug|x86.Build.0 = Debug|Win32
+ {2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Release|ARM.ActiveCfg = Release|Win32
+ {2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Release|ARM64.ActiveCfg = Release|Win32
{2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Release|x64.ActiveCfg = Release|x64
{2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Release|x64.Build.0 = Release|x64
+ {2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Release|x86.ActiveCfg = Release|Win32
+ {2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Release|x86.Build.0 = Release|Win32
+ {64A80062-4D8B-4229-8A38-DFA1D7497749}.Debug|ARM.ActiveCfg = Debug|x64
+ {64A80062-4D8B-4229-8A38-DFA1D7497749}.Debug|ARM64.ActiveCfg = Debug|x64
{64A80062-4D8B-4229-8A38-DFA1D7497749}.Debug|x64.ActiveCfg = Debug|x64
{64A80062-4D8B-4229-8A38-DFA1D7497749}.Debug|x64.Build.0 = Debug|x64
+ {64A80062-4D8B-4229-8A38-DFA1D7497749}.Debug|x86.ActiveCfg = Debug|x64
+ {64A80062-4D8B-4229-8A38-DFA1D7497749}.Release|ARM.ActiveCfg = Release|x64
+ {64A80062-4D8B-4229-8A38-DFA1D7497749}.Release|ARM64.ActiveCfg = Release|x64
{64A80062-4D8B-4229-8A38-DFA1D7497749}.Release|x64.ActiveCfg = Release|x64
{64A80062-4D8B-4229-8A38-DFA1D7497749}.Release|x64.Build.0 = Release|x64
+ {64A80062-4D8B-4229-8A38-DFA1D7497749}.Release|x86.ActiveCfg = Release|x64
+ {0485F45C-EA7A-4BB5-804B-3E8D14699387}.Debug|ARM.ActiveCfg = Debug|x64
+ {0485F45C-EA7A-4BB5-804B-3E8D14699387}.Debug|ARM64.ActiveCfg = Debug|x64
{0485F45C-EA7A-4BB5-804B-3E8D14699387}.Debug|x64.ActiveCfg = Debug|x64
{0485F45C-EA7A-4BB5-804B-3E8D14699387}.Debug|x64.Build.0 = Debug|x64
+ {0485F45C-EA7A-4BB5-804B-3E8D14699387}.Debug|x86.ActiveCfg = Debug|x64
+ {0485F45C-EA7A-4BB5-804B-3E8D14699387}.Release|ARM.ActiveCfg = Release|x64
+ {0485F45C-EA7A-4BB5-804B-3E8D14699387}.Release|ARM64.ActiveCfg = Release|x64
{0485F45C-EA7A-4BB5-804B-3E8D14699387}.Release|x64.ActiveCfg = Release|x64
{0485F45C-EA7A-4BB5-804B-3E8D14699387}.Release|x64.Build.0 = Release|x64
+ {0485F45C-EA7A-4BB5-804B-3E8D14699387}.Release|x86.ActiveCfg = Release|x64
+ {0B593A6C-4143-4337-860E-DB5710FB87DB}.Debug|ARM.ActiveCfg = Debug|ARM
+ {0B593A6C-4143-4337-860E-DB5710FB87DB}.Debug|ARM.Build.0 = Debug|ARM
+ {0B593A6C-4143-4337-860E-DB5710FB87DB}.Debug|ARM64.ActiveCfg = Debug|Win32
{0B593A6C-4143-4337-860E-DB5710FB87DB}.Debug|x64.ActiveCfg = Debug|x64
{0B593A6C-4143-4337-860E-DB5710FB87DB}.Debug|x64.Build.0 = Debug|x64
+ {0B593A6C-4143-4337-860E-DB5710FB87DB}.Debug|x86.ActiveCfg = Debug|Win32
+ {0B593A6C-4143-4337-860E-DB5710FB87DB}.Debug|x86.Build.0 = Debug|Win32
+ {0B593A6C-4143-4337-860E-DB5710FB87DB}.Release|ARM.ActiveCfg = Release|ARM
+ {0B593A6C-4143-4337-860E-DB5710FB87DB}.Release|ARM.Build.0 = Release|ARM
+ {0B593A6C-4143-4337-860E-DB5710FB87DB}.Release|ARM64.ActiveCfg = Release|Win32
{0B593A6C-4143-4337-860E-DB5710FB87DB}.Release|x64.ActiveCfg = Release|x64
{0B593A6C-4143-4337-860E-DB5710FB87DB}.Release|x64.Build.0 = Release|x64
+ {0B593A6C-4143-4337-860E-DB5710FB87DB}.Release|x86.ActiveCfg = Release|Win32
+ {0B593A6C-4143-4337-860E-DB5710FB87DB}.Release|x86.Build.0 = Release|Win32
+ {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Debug|ARM.ActiveCfg = Debug|Win32
+ {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Debug|ARM64.ActiveCfg = Debug|Win32
{031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Debug|x64.ActiveCfg = Debug|x64
{031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Debug|x64.Build.0 = Debug|x64
+ {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Debug|x86.ActiveCfg = Debug|Win32
+ {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Debug|x86.Build.0 = Debug|Win32
+ {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Release|ARM.ActiveCfg = Release|Win32
+ {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Release|ARM64.ActiveCfg = Release|Win32
{031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Release|x64.ActiveCfg = Release|x64
{031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Release|x64.Build.0 = Release|x64
+ {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Release|x86.ActiveCfg = Release|Win32
+ {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Release|x86.Build.0 = Release|Win32
+ {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Debug|ARM.ActiveCfg = Debug|x64
+ {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Debug|ARM64.ActiveCfg = Debug|x64
{B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Debug|x64.ActiveCfg = Debug|x64
{B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Debug|x64.Build.0 = Debug|x64
+ {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Debug|x86.ActiveCfg = Debug|x64
+ {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Release|ARM.ActiveCfg = Release|x64
+ {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Release|ARM64.ActiveCfg = Release|x64
{B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Release|x64.ActiveCfg = Release|x64
{B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Release|x64.Build.0 = Release|x64
+ {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Release|x86.ActiveCfg = Release|x64
+ {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Debug|ARM.ActiveCfg = Debug|x64
+ {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Debug|ARM64.ActiveCfg = Debug|x64
{51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Debug|x64.ActiveCfg = Debug|x64
{51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Debug|x64.Build.0 = Debug|x64
+ {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Debug|x86.ActiveCfg = Debug|x64
+ {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Release|ARM.ActiveCfg = Release|x64
+ {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Release|ARM64.ActiveCfg = Release|x64
{51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Release|x64.ActiveCfg = Release|x64
{51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Release|x64.Build.0 = Release|x64
+ {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Release|x86.ActiveCfg = Release|x64
+ {89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Debug|ARM.ActiveCfg = Debug|x64
+ {89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Debug|ARM64.ActiveCfg = Debug|x64
{89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Debug|x64.ActiveCfg = Debug|x64
{89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Debug|x64.Build.0 = Debug|x64
+ {89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Debug|x86.ActiveCfg = Debug|x64
+ {89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Release|ARM.ActiveCfg = Release|x64
+ {89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Release|ARM64.ActiveCfg = Release|x64
{89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Release|x64.ActiveCfg = Release|x64
{89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Release|x64.Build.0 = Release|x64
+ {89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Release|x86.ActiveCfg = Release|x64
+ {2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Debug|ARM.ActiveCfg = Debug|x64
+ {2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Debug|ARM64.ActiveCfg = Debug|x64
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Debug|x64.ActiveCfg = Debug|x64
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Debug|x64.Build.0 = Debug|x64
+ {2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Debug|x86.ActiveCfg = Debug|x64
+ {2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Release|ARM.ActiveCfg = Release|x64
+ {2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Release|ARM64.ActiveCfg = Release|x64
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Release|x64.ActiveCfg = Release|x64
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Release|x64.Build.0 = Release|x64
+ {2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Release|x86.ActiveCfg = Release|x64
+ {0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Debug|ARM.ActiveCfg = Debug|Win32
+ {0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Debug|ARM64.ActiveCfg = Debug|Win32
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Debug|x64.ActiveCfg = Debug|x64
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Debug|x64.Build.0 = Debug|x64
+ {0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Debug|x86.ActiveCfg = Debug|Win32
+ {0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Debug|x86.Build.0 = Debug|Win32
+ {0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Release|ARM.ActiveCfg = Release|Win32
+ {0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Release|ARM64.ActiveCfg = Release|Win32
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Release|x64.ActiveCfg = Release|x64
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Release|x64.Build.0 = Release|x64
+ {0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Release|x86.ActiveCfg = Release|Win32
+ {0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Release|x86.Build.0 = Release|Win32
+ {E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Debug|ARM.ActiveCfg = Debug|x64
+ {E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Debug|ARM64.ActiveCfg = Debug|x64
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Debug|x64.ActiveCfg = Debug|x64
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Debug|x64.Build.0 = Debug|x64
+ {E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Debug|x86.ActiveCfg = Debug|x64
+ {E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Release|ARM.ActiveCfg = Release|x64
+ {E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Release|ARM64.ActiveCfg = Release|x64
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Release|x64.ActiveCfg = Release|x64
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Release|x64.Build.0 = Release|x64
+ {E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Release|x86.ActiveCfg = Release|x64
+ {EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Debug|ARM.ActiveCfg = Debug|x64
+ {EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Debug|ARM64.ActiveCfg = Debug|x64
{EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Debug|x64.ActiveCfg = Debug|x64
{EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Debug|x64.Build.0 = Debug|x64
+ {EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Debug|x86.ActiveCfg = Debug|x64
+ {EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Release|ARM.ActiveCfg = Release|x64
+ {EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Release|ARM64.ActiveCfg = Release|x64
{EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Release|x64.ActiveCfg = Release|x64
{EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Release|x64.Build.0 = Release|x64
+ {EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Release|x86.ActiveCfg = Release|x64
+ {D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Debug|ARM.ActiveCfg = Debug|Win32
+ {D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Debug|ARM64.ActiveCfg = Debug|Win32
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Debug|x64.ActiveCfg = Debug|x64
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Debug|x64.Build.0 = Debug|x64
+ {D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Debug|x86.ActiveCfg = Debug|Win32
+ {D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Debug|x86.Build.0 = Debug|Win32
+ {D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Release|ARM.ActiveCfg = Release|Win32
+ {D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Release|ARM64.ActiveCfg = Release|Win32
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Release|x64.ActiveCfg = Release|x64
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Release|x64.Build.0 = Release|x64
+ {D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Release|x86.ActiveCfg = Release|Win32
+ {D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Release|x86.Build.0 = Release|Win32
+ {17DA04DF-E393-4397-9CF0-84DABE11032E}.Debug|ARM.ActiveCfg = Debug|Win32
+ {17DA04DF-E393-4397-9CF0-84DABE11032E}.Debug|ARM64.ActiveCfg = Debug|Win32
{17DA04DF-E393-4397-9CF0-84DABE11032E}.Debug|x64.ActiveCfg = Debug|x64
{17DA04DF-E393-4397-9CF0-84DABE11032E}.Debug|x64.Build.0 = Debug|x64
+ {17DA04DF-E393-4397-9CF0-84DABE11032E}.Debug|x86.ActiveCfg = Debug|Win32
+ {17DA04DF-E393-4397-9CF0-84DABE11032E}.Debug|x86.Build.0 = Debug|Win32
+ {17DA04DF-E393-4397-9CF0-84DABE11032E}.Release|ARM.ActiveCfg = Release|Win32
+ {17DA04DF-E393-4397-9CF0-84DABE11032E}.Release|ARM64.ActiveCfg = Release|Win32
{17DA04DF-E393-4397-9CF0-84DABE11032E}.Release|x64.ActiveCfg = Release|x64
{17DA04DF-E393-4397-9CF0-84DABE11032E}.Release|x64.Build.0 = Release|x64
+ {17DA04DF-E393-4397-9CF0-84DABE11032E}.Release|x86.ActiveCfg = Release|Win32
+ {17DA04DF-E393-4397-9CF0-84DABE11032E}.Release|x86.Build.0 = Release|Win32
+ {8AFFA899-0B73-49EC-8C50-0FADDA57B2FC}.Debug|ARM.ActiveCfg = Debug|x64
+ {8AFFA899-0B73-49EC-8C50-0FADDA57B2FC}.Debug|ARM64.ActiveCfg = Debug|x64
{8AFFA899-0B73-49EC-8C50-0FADDA57B2FC}.Debug|x64.ActiveCfg = Debug|x64
{8AFFA899-0B73-49EC-8C50-0FADDA57B2FC}.Debug|x64.Build.0 = Debug|x64
+ {8AFFA899-0B73-49EC-8C50-0FADDA57B2FC}.Debug|x86.ActiveCfg = Debug|x64
+ {8AFFA899-0B73-49EC-8C50-0FADDA57B2FC}.Release|ARM.ActiveCfg = Release|x64
+ {8AFFA899-0B73-49EC-8C50-0FADDA57B2FC}.Release|ARM64.ActiveCfg = Release|x64
{8AFFA899-0B73-49EC-8C50-0FADDA57B2FC}.Release|x64.ActiveCfg = Release|x64
{8AFFA899-0B73-49EC-8C50-0FADDA57B2FC}.Release|x64.Build.0 = Release|x64
+ {8AFFA899-0B73-49EC-8C50-0FADDA57B2FC}.Release|x86.ActiveCfg = Release|x64
+ {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|ARM.ActiveCfg = Debug|x64
+ {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|ARM64.ActiveCfg = Debug|x64
+ {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x64.ActiveCfg = Debug|x64
+ {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x64.Build.0 = Debug|x64
+ {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x86.ActiveCfg = Debug|x64
+ {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|ARM.ActiveCfg = Release|x64
+ {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|ARM64.ActiveCfg = Release|x64
+ {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x64.ActiveCfg = Release|x64
+ {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x64.Build.0 = Release|x64
+ {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x86.ActiveCfg = Release|x64
+ {B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|ARM.ActiveCfg = Debug|x64
+ {B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|ARM64.ActiveCfg = Debug|x64
+ {B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|x64.ActiveCfg = Debug|x64
+ {B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|x64.Build.0 = Debug|x64
+ {B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|x86.ActiveCfg = Debug|x64
+ {B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Release|ARM.ActiveCfg = Release|x64
+ {B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Release|ARM64.ActiveCfg = Release|x64
+ {B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Release|x64.ActiveCfg = Release|x64
+ {B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Release|x64.Build.0 = Release|x64
+ {B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Release|x86.ActiveCfg = Release|x64
+ {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|ARM.ActiveCfg = Debug|x64
+ {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|ARM64.ActiveCfg = Debug|x64
+ {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|x64.ActiveCfg = Debug|x64
+ {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|x64.Build.0 = Debug|x64
+ {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|x86.ActiveCfg = Debug|x64
+ {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|ARM.ActiveCfg = Release|x64
+ {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|ARM64.ActiveCfg = Release|x64
+ {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x64.ActiveCfg = Release|x64
+ {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x64.Build.0 = Release|x64
+ {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x86.ActiveCfg = Release|x64
+ {8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Debug|ARM.ActiveCfg = Debug|x64
+ {8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Debug|ARM64.ActiveCfg = Debug|x64
+ {8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Debug|x64.ActiveCfg = Debug|x64
+ {8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Debug|x64.Build.0 = Debug|x64
+ {8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Debug|x86.ActiveCfg = Debug|x64
+ {8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|ARM.ActiveCfg = Release|x64
+ {8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|ARM64.ActiveCfg = Release|x64
+ {8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|x64.ActiveCfg = Release|x64
+ {8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|x64.Build.0 = Release|x64
+ {8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|x86.ActiveCfg = Release|x64
+ {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|ARM.ActiveCfg = Debug|x64
+ {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|ARM64.ActiveCfg = Debug|x64
+ {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x64.ActiveCfg = Debug|x64
+ {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x64.Build.0 = Debug|x64
+ {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x86.ActiveCfg = Debug|x64
+ {FF742965-9A80-41A5-B042-D6C7D3A21708}.Release|ARM.ActiveCfg = Release|x64
+ {FF742965-9A80-41A5-B042-D6C7D3A21708}.Release|ARM64.ActiveCfg = Release|x64
+ {FF742965-9A80-41A5-B042-D6C7D3A21708}.Release|x64.ActiveCfg = Release|x64
+ {FF742965-9A80-41A5-B042-D6C7D3A21708}.Release|x64.Build.0 = Release|x64
+ {FF742965-9A80-41A5-B042-D6C7D3A21708}.Release|x86.ActiveCfg = Release|x64
+ {59BD9891-3837-438A-958D-ADC7F91F6F7E}.Debug|ARM.ActiveCfg = Debug|x64
+ {59BD9891-3837-438A-958D-ADC7F91F6F7E}.Debug|ARM64.ActiveCfg = Debug|x64
+ {59BD9891-3837-438A-958D-ADC7F91F6F7E}.Debug|x64.ActiveCfg = Debug|x64
+ {59BD9891-3837-438A-958D-ADC7F91F6F7E}.Debug|x64.Build.0 = Debug|x64
+ {59BD9891-3837-438A-958D-ADC7F91F6F7E}.Debug|x86.ActiveCfg = Debug|x64
+ {59BD9891-3837-438A-958D-ADC7F91F6F7E}.Release|ARM.ActiveCfg = Release|x64
+ {59BD9891-3837-438A-958D-ADC7F91F6F7E}.Release|ARM64.ActiveCfg = Release|x64
+ {59BD9891-3837-438A-958D-ADC7F91F6F7E}.Release|x64.ActiveCfg = Release|x64
+ {59BD9891-3837-438A-958D-ADC7F91F6F7E}.Release|x64.Build.0 = Release|x64
+ {59BD9891-3837-438A-958D-ADC7F91F6F7E}.Release|x86.ActiveCfg = Release|x64
+ {74F1B9ED-F59C-4FE7-B473-7B453E30837E}.Debug|ARM.ActiveCfg = Debug|x64
+ {74F1B9ED-F59C-4FE7-B473-7B453E30837E}.Debug|ARM64.ActiveCfg = Debug|x64
+ {74F1B9ED-F59C-4FE7-B473-7B453E30837E}.Debug|x64.ActiveCfg = Debug|x64
+ {74F1B9ED-F59C-4FE7-B473-7B453E30837E}.Debug|x64.Build.0 = Debug|x64
+ {74F1B9ED-F59C-4FE7-B473-7B453E30837E}.Debug|x86.ActiveCfg = Debug|x64
+ {74F1B9ED-F59C-4FE7-B473-7B453E30837E}.Release|ARM.ActiveCfg = Release|x64
+ {74F1B9ED-F59C-4FE7-B473-7B453E30837E}.Release|ARM64.ActiveCfg = Release|x64
+ {74F1B9ED-F59C-4FE7-B473-7B453E30837E}.Release|x64.ActiveCfg = Release|x64
+ {74F1B9ED-F59C-4FE7-B473-7B453E30837E}.Release|x64.Build.0 = Release|x64
+ {74F1B9ED-F59C-4FE7-B473-7B453E30837E}.Release|x86.ActiveCfg = Release|x64
+ {FDB3555B-58EF-4AE6-B5F1-904719637AB4}.Debug|ARM.ActiveCfg = Debug|x64
+ {FDB3555B-58EF-4AE6-B5F1-904719637AB4}.Debug|ARM64.ActiveCfg = Debug|x64
+ {FDB3555B-58EF-4AE6-B5F1-904719637AB4}.Debug|x64.ActiveCfg = Debug|x64
+ {FDB3555B-58EF-4AE6-B5F1-904719637AB4}.Debug|x64.Build.0 = Debug|x64
+ {FDB3555B-58EF-4AE6-B5F1-904719637AB4}.Debug|x86.ActiveCfg = Debug|x64
+ {FDB3555B-58EF-4AE6-B5F1-904719637AB4}.Release|ARM.ActiveCfg = Release|x64
+ {FDB3555B-58EF-4AE6-B5F1-904719637AB4}.Release|ARM64.ActiveCfg = Release|x64
+ {FDB3555B-58EF-4AE6-B5F1-904719637AB4}.Release|x64.ActiveCfg = Release|x64
+ {FDB3555B-58EF-4AE6-B5F1-904719637AB4}.Release|x64.Build.0 = Release|x64
+ {FDB3555B-58EF-4AE6-B5F1-904719637AB4}.Release|x86.ActiveCfg = Release|x64
+ {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|ARM.ActiveCfg = Debug|x64
+ {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|ARM64.ActiveCfg = Debug|x64
+ {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|x64.ActiveCfg = Debug|x64
+ {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|x64.Build.0 = Debug|x64
+ {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|x86.ActiveCfg = Debug|x64
+ {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Release|ARM.ActiveCfg = Release|x64
+ {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Release|ARM64.ActiveCfg = Release|x64
+ {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Release|x64.ActiveCfg = Release|x64
+ {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Release|x64.Build.0 = Release|x64
+ {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Release|x86.ActiveCfg = Release|x64
+ {F8B870EB-D5F5-45BA-9CF7-A5C459818820}.Debug|ARM.ActiveCfg = Debug|x64
+ {F8B870EB-D5F5-45BA-9CF7-A5C459818820}.Debug|ARM64.ActiveCfg = Debug|x64
+ {F8B870EB-D5F5-45BA-9CF7-A5C459818820}.Debug|x64.ActiveCfg = Debug|x64
+ {F8B870EB-D5F5-45BA-9CF7-A5C459818820}.Debug|x64.Build.0 = Debug|x64
+ {F8B870EB-D5F5-45BA-9CF7-A5C459818820}.Debug|x86.ActiveCfg = Debug|x64
+ {F8B870EB-D5F5-45BA-9CF7-A5C459818820}.Release|ARM.ActiveCfg = Release|x64
+ {F8B870EB-D5F5-45BA-9CF7-A5C459818820}.Release|ARM64.ActiveCfg = Release|x64
+ {F8B870EB-D5F5-45BA-9CF7-A5C459818820}.Release|x64.ActiveCfg = Release|x64
+ {F8B870EB-D5F5-45BA-9CF7-A5C459818820}.Release|x64.Build.0 = Release|x64
+ {F8B870EB-D5F5-45BA-9CF7-A5C459818820}.Release|x86.ActiveCfg = Release|x64
+ {E364F67B-BB12-4E91-B639-355866EBCD8B}.Debug|ARM.ActiveCfg = Debug|x64
+ {E364F67B-BB12-4E91-B639-355866EBCD8B}.Debug|ARM64.ActiveCfg = Debug|x64
+ {E364F67B-BB12-4E91-B639-355866EBCD8B}.Debug|x64.ActiveCfg = Debug|x64
+ {E364F67B-BB12-4E91-B639-355866EBCD8B}.Debug|x64.Build.0 = Debug|x64
+ {E364F67B-BB12-4E91-B639-355866EBCD8B}.Debug|x86.ActiveCfg = Debug|x64
+ {E364F67B-BB12-4E91-B639-355866EBCD8B}.Release|ARM.ActiveCfg = Release|x64
+ {E364F67B-BB12-4E91-B639-355866EBCD8B}.Release|ARM64.ActiveCfg = Release|x64
+ {E364F67B-BB12-4E91-B639-355866EBCD8B}.Release|x64.ActiveCfg = Release|x64
+ {E364F67B-BB12-4E91-B639-355866EBCD8B}.Release|x64.Build.0 = Release|x64
+ {E364F67B-BB12-4E91-B639-355866EBCD8B}.Release|x86.ActiveCfg = Release|x64
+ {F97E5003-F263-4D4A-A964-0F1F3C82DEF2}.Debug|ARM.ActiveCfg = Debug|x64
+ {F97E5003-F263-4D4A-A964-0F1F3C82DEF2}.Debug|ARM64.ActiveCfg = Debug|x64
+ {F97E5003-F263-4D4A-A964-0F1F3C82DEF2}.Debug|x64.ActiveCfg = Debug|x64
+ {F97E5003-F263-4D4A-A964-0F1F3C82DEF2}.Debug|x64.Build.0 = Debug|x64
+ {F97E5003-F263-4D4A-A964-0F1F3C82DEF2}.Debug|x86.ActiveCfg = Debug|x64
+ {F97E5003-F263-4D4A-A964-0F1F3C82DEF2}.Release|ARM.ActiveCfg = Release|x64
+ {F97E5003-F263-4D4A-A964-0F1F3C82DEF2}.Release|ARM64.ActiveCfg = Release|x64
+ {F97E5003-F263-4D4A-A964-0F1F3C82DEF2}.Release|x64.ActiveCfg = Release|x64
+ {F97E5003-F263-4D4A-A964-0F1F3C82DEF2}.Release|x64.Build.0 = Release|x64
+ {F97E5003-F263-4D4A-A964-0F1F3C82DEF2}.Release|x86.ActiveCfg = Release|x64
+ {4A3DE70C-684C-410D-B851-C23B6DAEDF16}.Debug|ARM.ActiveCfg = Debug|x64
+ {4A3DE70C-684C-410D-B851-C23B6DAEDF16}.Debug|ARM64.ActiveCfg = Debug|x64
+ {4A3DE70C-684C-410D-B851-C23B6DAEDF16}.Debug|x64.ActiveCfg = Debug|x64
+ {4A3DE70C-684C-410D-B851-C23B6DAEDF16}.Debug|x64.Build.0 = Debug|x64
+ {4A3DE70C-684C-410D-B851-C23B6DAEDF16}.Debug|x86.ActiveCfg = Debug|x64
+ {4A3DE70C-684C-410D-B851-C23B6DAEDF16}.Release|ARM.ActiveCfg = Release|x64
+ {4A3DE70C-684C-410D-B851-C23B6DAEDF16}.Release|ARM64.ActiveCfg = Release|x64
+ {4A3DE70C-684C-410D-B851-C23B6DAEDF16}.Release|x64.ActiveCfg = Release|x64
+ {4A3DE70C-684C-410D-B851-C23B6DAEDF16}.Release|x64.Build.0 = Release|x64
+ {4A3DE70C-684C-410D-B851-C23B6DAEDF16}.Release|x86.ActiveCfg = Release|x64
+ {880ED251-9E16-4713-9A70-D35FE0C01669}.Debug|ARM.ActiveCfg = Debug|x64
+ {880ED251-9E16-4713-9A70-D35FE0C01669}.Debug|ARM64.ActiveCfg = Debug|x64
{880ED251-9E16-4713-9A70-D35FE0C01669}.Debug|x64.ActiveCfg = Debug|x64
{880ED251-9E16-4713-9A70-D35FE0C01669}.Debug|x64.Build.0 = Debug|x64
+ {880ED251-9E16-4713-9A70-D35FE0C01669}.Debug|x86.ActiveCfg = Debug|x64
+ {880ED251-9E16-4713-9A70-D35FE0C01669}.Release|ARM.ActiveCfg = Release|x64
+ {880ED251-9E16-4713-9A70-D35FE0C01669}.Release|ARM64.ActiveCfg = Release|x64
{880ED251-9E16-4713-9A70-D35FE0C01669}.Release|x64.ActiveCfg = Release|x64
{880ED251-9E16-4713-9A70-D35FE0C01669}.Release|x64.Build.0 = Release|x64
+ {880ED251-9E16-4713-9A70-D35FE0C01669}.Release|x86.ActiveCfg = Release|x64
+ {AF2349B8-E5B6-4004-9502-687C1C7730B1}.Debug|ARM.ActiveCfg = Debug|x64
+ {AF2349B8-E5B6-4004-9502-687C1C7730B1}.Debug|ARM64.ActiveCfg = Debug|x64
{AF2349B8-E5B6-4004-9502-687C1C7730B1}.Debug|x64.ActiveCfg = Debug|x64
{AF2349B8-E5B6-4004-9502-687C1C7730B1}.Debug|x64.Build.0 = Debug|x64
+ {AF2349B8-E5B6-4004-9502-687C1C7730B1}.Debug|x86.ActiveCfg = Debug|x64
+ {AF2349B8-E5B6-4004-9502-687C1C7730B1}.Release|ARM.ActiveCfg = Release|x64
+ {AF2349B8-E5B6-4004-9502-687C1C7730B1}.Release|ARM64.ActiveCfg = Release|x64
{AF2349B8-E5B6-4004-9502-687C1C7730B1}.Release|x64.ActiveCfg = Release|x64
{AF2349B8-E5B6-4004-9502-687C1C7730B1}.Release|x64.Build.0 = Release|x64
+ {AF2349B8-E5B6-4004-9502-687C1C7730B1}.Release|x86.ActiveCfg = Release|x64
+ {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Debug|ARM.ActiveCfg = Debug|x64
+ {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Debug|ARM64.ActiveCfg = Debug|x64
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Debug|x64.ActiveCfg = Debug|x64
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Debug|x64.Build.0 = Debug|x64
+ {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Debug|x86.ActiveCfg = Debug|x64
+ {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Release|ARM.ActiveCfg = Release|x64
+ {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Release|ARM64.ActiveCfg = Release|x64
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Release|x64.ActiveCfg = Release|x64
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Release|x64.Build.0 = Release|x64
+ {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Release|x86.ActiveCfg = Release|x64
+ {A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Debug|ARM.ActiveCfg = Debug|x64
+ {A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Debug|ARM64.ActiveCfg = Debug|x64
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Debug|x64.ActiveCfg = Debug|x64
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Debug|x64.Build.0 = Debug|x64
+ {A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Debug|x86.ActiveCfg = Debug|x64
+ {A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Release|ARM.ActiveCfg = Release|x64
+ {A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Release|ARM64.ActiveCfg = Release|x64
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Release|x64.ActiveCfg = Release|x64
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Release|x64.Build.0 = Release|x64
+ {A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Release|x86.ActiveCfg = Release|x64
+ {DA425894-6E13-404F-8DCB-78584EC0557A}.Debug|ARM.ActiveCfg = Debug|x64
+ {DA425894-6E13-404F-8DCB-78584EC0557A}.Debug|ARM64.ActiveCfg = Debug|x64
{DA425894-6E13-404F-8DCB-78584EC0557A}.Debug|x64.ActiveCfg = Debug|x64
{DA425894-6E13-404F-8DCB-78584EC0557A}.Debug|x64.Build.0 = Debug|x64
+ {DA425894-6E13-404F-8DCB-78584EC0557A}.Debug|x86.ActiveCfg = Debug|x64
+ {DA425894-6E13-404F-8DCB-78584EC0557A}.Release|ARM.ActiveCfg = Release|x64
+ {DA425894-6E13-404F-8DCB-78584EC0557A}.Release|ARM64.ActiveCfg = Release|x64
{DA425894-6E13-404F-8DCB-78584EC0557A}.Release|x64.ActiveCfg = Release|x64
{DA425894-6E13-404F-8DCB-78584EC0557A}.Release|x64.Build.0 = Release|x64
+ {DA425894-6E13-404F-8DCB-78584EC0557A}.Release|x86.ActiveCfg = Release|x64
+ {060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Debug|ARM.ActiveCfg = Debug|x64
+ {060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Debug|ARM64.ActiveCfg = Debug|x64
{060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Debug|x64.ActiveCfg = Debug|x64
{060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Debug|x64.Build.0 = Debug|x64
+ {060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Debug|x86.ActiveCfg = Debug|x64
+ {060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Release|ARM.ActiveCfg = Release|x64
+ {060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Release|ARM64.ActiveCfg = Release|x64
{060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Release|x64.ActiveCfg = Release|x64
{060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Release|x64.Build.0 = Release|x64
+ {060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Release|x86.ActiveCfg = Release|x64
+ {748417CA-F17E-487F-9411-CAFB6D3F4877}.Debug|ARM.ActiveCfg = Debug|x64
+ {748417CA-F17E-487F-9411-CAFB6D3F4877}.Debug|ARM64.ActiveCfg = Debug|x64
{748417CA-F17E-487F-9411-CAFB6D3F4877}.Debug|x64.ActiveCfg = Debug|x64
{748417CA-F17E-487F-9411-CAFB6D3F4877}.Debug|x64.Build.0 = Debug|x64
+ {748417CA-F17E-487F-9411-CAFB6D3F4877}.Debug|x86.ActiveCfg = Debug|x64
+ {748417CA-F17E-487F-9411-CAFB6D3F4877}.Release|ARM.ActiveCfg = Release|x64
+ {748417CA-F17E-487F-9411-CAFB6D3F4877}.Release|ARM64.ActiveCfg = Release|x64
{748417CA-F17E-487F-9411-CAFB6D3F4877}.Release|x64.ActiveCfg = Release|x64
{748417CA-F17E-487F-9411-CAFB6D3F4877}.Release|x64.Build.0 = Release|x64
+ {748417CA-F17E-487F-9411-CAFB6D3F4877}.Release|x86.ActiveCfg = Release|x64
+ {217DF501-135C-4E38-BFC8-99D4821032EA}.Debug|ARM.ActiveCfg = Debug|x64
+ {217DF501-135C-4E38-BFC8-99D4821032EA}.Debug|ARM64.ActiveCfg = Debug|x64
{217DF501-135C-4E38-BFC8-99D4821032EA}.Debug|x64.ActiveCfg = Debug|x64
{217DF501-135C-4E38-BFC8-99D4821032EA}.Debug|x64.Build.0 = Debug|x64
+ {217DF501-135C-4E38-BFC8-99D4821032EA}.Debug|x86.ActiveCfg = Debug|x64
+ {217DF501-135C-4E38-BFC8-99D4821032EA}.Release|ARM.ActiveCfg = Release|x64
+ {217DF501-135C-4E38-BFC8-99D4821032EA}.Release|ARM64.ActiveCfg = Release|x64
{217DF501-135C-4E38-BFC8-99D4821032EA}.Release|x64.ActiveCfg = Release|x64
{217DF501-135C-4E38-BFC8-99D4821032EA}.Release|x64.Build.0 = Release|x64
+ {217DF501-135C-4E38-BFC8-99D4821032EA}.Release|x86.ActiveCfg = Release|x64
+ {47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Debug|ARM.ActiveCfg = Debug|Win32
+ {47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Debug|ARM64.ActiveCfg = Debug|Win32
{47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Debug|x64.ActiveCfg = Debug|x64
{47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Debug|x64.Build.0 = Debug|x64
+ {47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Debug|x86.ActiveCfg = Debug|Win32
+ {47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Debug|x86.Build.0 = Debug|Win32
+ {47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Release|ARM.ActiveCfg = Release|Win32
+ {47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Release|ARM64.ActiveCfg = Release|Win32
{47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Release|x64.ActiveCfg = Release|x64
{47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Release|x64.Build.0 = Release|x64
+ {47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Release|x86.ActiveCfg = Release|Win32
+ {47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Release|x86.Build.0 = Release|Win32
+ {E4E0D2AE-B17D-4BD4-8BEE-AFC8CC464C5F}.Debug|ARM.ActiveCfg = Debug|x64
+ {E4E0D2AE-B17D-4BD4-8BEE-AFC8CC464C5F}.Debug|ARM64.ActiveCfg = Debug|x64
{E4E0D2AE-B17D-4BD4-8BEE-AFC8CC464C5F}.Debug|x64.ActiveCfg = Debug|x64
{E4E0D2AE-B17D-4BD4-8BEE-AFC8CC464C5F}.Debug|x64.Build.0 = Debug|x64
+ {E4E0D2AE-B17D-4BD4-8BEE-AFC8CC464C5F}.Debug|x86.ActiveCfg = Debug|x64
+ {E4E0D2AE-B17D-4BD4-8BEE-AFC8CC464C5F}.Release|ARM.ActiveCfg = Release|x64
+ {E4E0D2AE-B17D-4BD4-8BEE-AFC8CC464C5F}.Release|ARM64.ActiveCfg = Release|x64
{E4E0D2AE-B17D-4BD4-8BEE-AFC8CC464C5F}.Release|x64.ActiveCfg = Release|x64
{E4E0D2AE-B17D-4BD4-8BEE-AFC8CC464C5F}.Release|x64.Build.0 = Release|x64
+ {E4E0D2AE-B17D-4BD4-8BEE-AFC8CC464C5F}.Release|x86.ActiveCfg = Release|x64
+ {A7D5099E-F0FD-4BF3-8522-5A682759F915}.Debug|ARM.ActiveCfg = Debug|Win32
+ {A7D5099E-F0FD-4BF3-8522-5A682759F915}.Debug|ARM64.ActiveCfg = Debug|Win32
{A7D5099E-F0FD-4BF3-8522-5A682759F915}.Debug|x64.ActiveCfg = Debug|x64
{A7D5099E-F0FD-4BF3-8522-5A682759F915}.Debug|x64.Build.0 = Debug|x64
+ {A7D5099E-F0FD-4BF3-8522-5A682759F915}.Debug|x86.ActiveCfg = Debug|Win32
+ {A7D5099E-F0FD-4BF3-8522-5A682759F915}.Debug|x86.Build.0 = Debug|Win32
+ {A7D5099E-F0FD-4BF3-8522-5A682759F915}.Debug|x86.Deploy.0 = Debug|Win32
+ {A7D5099E-F0FD-4BF3-8522-5A682759F915}.Release|ARM.ActiveCfg = Release|Win32
+ {A7D5099E-F0FD-4BF3-8522-5A682759F915}.Release|ARM64.ActiveCfg = Release|Win32
{A7D5099E-F0FD-4BF3-8522-5A682759F915}.Release|x64.ActiveCfg = Release|x64
{A7D5099E-F0FD-4BF3-8522-5A682759F915}.Release|x64.Build.0 = Release|x64
+ {A7D5099E-F0FD-4BF3-8522-5A682759F915}.Release|x86.ActiveCfg = Release|x64
+ {A7D5099E-F0FD-4BF3-8522-5A682759F915}.Release|x86.Build.0 = Release|x64
+ {A7D5099E-F0FD-4BF3-8522-5A682759F915}.Release|x86.Deploy.0 = Release|x64
+ {B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Debug|ARM.Build.0 = Debug|Any CPU
+ {B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Debug|ARM64.Build.0 = Debug|Any CPU
{B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Debug|x64.ActiveCfg = Debug|x64
{B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Debug|x64.Build.0 = Debug|x64
+ {B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Debug|x86.Build.0 = Debug|Any CPU
+ {B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Release|ARM.ActiveCfg = Release|Any CPU
+ {B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Release|ARM.Build.0 = Release|Any CPU
+ {B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Release|ARM64.Build.0 = Release|Any CPU
{B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Release|x64.ActiveCfg = Release|x64
{B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Release|x64.Build.0 = Release|x64
+ {B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Release|x86.ActiveCfg = Release|x64
+ {B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Release|x86.Build.0 = Release|x64
+ {A80355C2-780D-4245-BD80-25B8DE698EE3}.Debug|ARM.ActiveCfg = Debug|ARM
+ {A80355C2-780D-4245-BD80-25B8DE698EE3}.Debug|ARM.Build.0 = Debug|ARM
+ {A80355C2-780D-4245-BD80-25B8DE698EE3}.Debug|ARM.Deploy.0 = Debug|ARM
+ {A80355C2-780D-4245-BD80-25B8DE698EE3}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {A80355C2-780D-4245-BD80-25B8DE698EE3}.Debug|ARM64.Build.0 = Debug|ARM64
+ {A80355C2-780D-4245-BD80-25B8DE698EE3}.Debug|ARM64.Deploy.0 = Debug|ARM64
{A80355C2-780D-4245-BD80-25B8DE698EE3}.Debug|x64.ActiveCfg = Debug|x64
{A80355C2-780D-4245-BD80-25B8DE698EE3}.Debug|x64.Build.0 = Debug|x64
+ {A80355C2-780D-4245-BD80-25B8DE698EE3}.Debug|x86.ActiveCfg = Debug|x86
+ {A80355C2-780D-4245-BD80-25B8DE698EE3}.Debug|x86.Build.0 = Debug|x86
+ {A80355C2-780D-4245-BD80-25B8DE698EE3}.Debug|x86.Deploy.0 = Debug|x86
+ {A80355C2-780D-4245-BD80-25B8DE698EE3}.Release|ARM.ActiveCfg = Release|ARM
+ {A80355C2-780D-4245-BD80-25B8DE698EE3}.Release|ARM.Build.0 = Release|ARM
+ {A80355C2-780D-4245-BD80-25B8DE698EE3}.Release|ARM.Deploy.0 = Release|ARM
+ {A80355C2-780D-4245-BD80-25B8DE698EE3}.Release|ARM64.ActiveCfg = Release|ARM64
+ {A80355C2-780D-4245-BD80-25B8DE698EE3}.Release|ARM64.Build.0 = Release|ARM64
+ {A80355C2-780D-4245-BD80-25B8DE698EE3}.Release|ARM64.Deploy.0 = Release|ARM64
{A80355C2-780D-4245-BD80-25B8DE698EE3}.Release|x64.ActiveCfg = Release|x64
{A80355C2-780D-4245-BD80-25B8DE698EE3}.Release|x64.Build.0 = Release|x64
+ {A80355C2-780D-4245-BD80-25B8DE698EE3}.Release|x86.ActiveCfg = Release|x64
+ {A80355C2-780D-4245-BD80-25B8DE698EE3}.Release|x86.Build.0 = Release|x64
+ {A80355C2-780D-4245-BD80-25B8DE698EE3}.Release|x86.Deploy.0 = Release|x64
+ {F055103B-F80B-4D0C-BF48-057C55620033}.Debug|ARM.ActiveCfg = Debug|x64
+ {F055103B-F80B-4D0C-BF48-057C55620033}.Debug|ARM64.ActiveCfg = Debug|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Debug|x64.ActiveCfg = Debug|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Debug|x64.Build.0 = Debug|x64
+ {F055103B-F80B-4D0C-BF48-057C55620033}.Debug|x86.ActiveCfg = Debug|x64
+ {F055103B-F80B-4D0C-BF48-057C55620033}.Release|ARM.ActiveCfg = Release|x64
+ {F055103B-F80B-4D0C-BF48-057C55620033}.Release|ARM64.ActiveCfg = Release|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x64.ActiveCfg = Release|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x64.Build.0 = Release|x64
+ {F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.ActiveCfg = Release|x64
+ {985B3F2F-CEED-4C0A-A249-69257E719145}.Debug|ARM.ActiveCfg = Debug|x64
+ {985B3F2F-CEED-4C0A-A249-69257E719145}.Debug|ARM64.ActiveCfg = Debug|x64
{985B3F2F-CEED-4C0A-A249-69257E719145}.Debug|x64.ActiveCfg = Debug|x64
{985B3F2F-CEED-4C0A-A249-69257E719145}.Debug|x64.Build.0 = Debug|x64
+ {985B3F2F-CEED-4C0A-A249-69257E719145}.Debug|x86.ActiveCfg = Debug|x64
+ {985B3F2F-CEED-4C0A-A249-69257E719145}.Release|ARM.ActiveCfg = Release|x64
+ {985B3F2F-CEED-4C0A-A249-69257E719145}.Release|ARM64.ActiveCfg = Release|x64
{985B3F2F-CEED-4C0A-A249-69257E719145}.Release|x64.ActiveCfg = Release|x64
{985B3F2F-CEED-4C0A-A249-69257E719145}.Release|x64.Build.0 = Release|x64
+ {985B3F2F-CEED-4C0A-A249-69257E719145}.Release|x86.ActiveCfg = Release|x64
+ {437AD818-3F1F-4CA5-A79B-25233A157026}.Debug|ARM.ActiveCfg = Debug|x64
+ {437AD818-3F1F-4CA5-A79B-25233A157026}.Debug|ARM64.ActiveCfg = Debug|x64
{437AD818-3F1F-4CA5-A79B-25233A157026}.Debug|x64.ActiveCfg = Debug|x64
{437AD818-3F1F-4CA5-A79B-25233A157026}.Debug|x64.Build.0 = Debug|x64
+ {437AD818-3F1F-4CA5-A79B-25233A157026}.Debug|x86.ActiveCfg = Debug|x64
+ {437AD818-3F1F-4CA5-A79B-25233A157026}.Release|ARM.ActiveCfg = Release|x64
+ {437AD818-3F1F-4CA5-A79B-25233A157026}.Release|ARM64.ActiveCfg = Release|x64
{437AD818-3F1F-4CA5-A79B-25233A157026}.Release|x64.ActiveCfg = Release|x64
{437AD818-3F1F-4CA5-A79B-25233A157026}.Release|x64.Build.0 = Release|x64
+ {437AD818-3F1F-4CA5-A79B-25233A157026}.Release|x86.ActiveCfg = Release|x64
+ {787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Debug|ARM.ActiveCfg = Debug|x64
+ {787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Debug|ARM64.ActiveCfg = Debug|x64
+ {787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Debug|x64.ActiveCfg = Debug|x64
+ {787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Debug|x64.Build.0 = Debug|x64
+ {787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Debug|x86.ActiveCfg = Debug|x64
+ {787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Release|ARM.ActiveCfg = Release|x64
+ {787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Release|ARM64.ActiveCfg = Release|x64
+ {787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Release|x64.ActiveCfg = Release|x64
+ {787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Release|x64.Build.0 = Release|x64
+ {787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Release|x86.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -431,6 +938,21 @@ Global
{17DA04DF-E393-4397-9CF0-84DABE11032E} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{38BDB927-829B-4C65-9CD9-93FB05D66D65} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{8AFFA899-0B73-49EC-8C50-0FADDA57B2FC} = {38BDB927-829B-4C65-9CD9-93FB05D66D65}
+ {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
+ {DB90F671-D861-46BB-93A3-F1304F5BA1C5} = {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68}
+ {B749F0DB-8E75-47DB-9E5E-265D16D0C0D2} = {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68}
+ {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3} = {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68}
+ {8451ECDD-2EA4-4966-BB0A-7BBC40138E80} = {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68}
+ {FF742965-9A80-41A5-B042-D6C7D3A21708} = {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68}
+ {4AFC9975-2456-4C70-94A4-84073C1CED93} = {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68}
+ {59BD9891-3837-438A-958D-ADC7F91F6F7E} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
+ {74F1B9ED-F59C-4FE7-B473-7B453E30837E} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
+ {FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
+ {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
+ {F8B870EB-D5F5-45BA-9CF7-A5C459818820} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
+ {E364F67B-BB12-4E91-B639-355866EBCD8B} = {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68}
+ {F97E5003-F263-4D4A-A964-0F1F3C82DEF2} = {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68}
+ {4A3DE70C-684C-410D-B851-C23B6DAEDF16} = {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68}
{880ED251-9E16-4713-9A70-D35FE0C01669} = {E775CC2C-24CB-48D6-9C3A-BE4CCE0DB17A}
{2F305555-C296-497E-AC20-5FA1B237996A} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{AF2349B8-E5B6-4004-9502-687C1C7730B1} = {2F305555-C296-497E-AC20-5FA1B237996A}
@@ -448,6 +970,7 @@ Global
{F055103B-F80B-4D0C-BF48-057C55620033} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{985B3F2F-CEED-4C0A-A249-69257E719145} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{437AD818-3F1F-4CA5-A79B-25233A157026} = {1AFB6476-670D-4E80-A464-657E01DFF482}
+ {787B8AA6-CA93-4C84-96FE-DF31110AD1C4} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs
index fa205592da..0a2397117a 100644
--- a/installer/PowerToysSetup/Product.wxs
+++ b/installer/PowerToysSetup/Product.wxs
@@ -32,12 +32,13 @@
= 17134)]]>
-
+
+
@@ -211,6 +212,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -285,48 +328,48 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -338,29 +381,33 @@
-
-
-
-
+
+
+
+
+
+
+
+
-
+
-
+
-
-
-
-
+
+
+
+
-
-
+
+
@@ -429,15 +476,15 @@
-
+
-
+
-
+
-
-
-
+
+
+
@@ -503,73 +550,77 @@
-
+
-
-
+
+
-
+
-
-
+
+
-
+
-
+
-
-
+
+
-
+
-
-
+
+
-
+
-
-
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -626,6 +677,7 @@
+
@@ -657,10 +709,201 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj
index 0a2a1d7c95..1bcaae0e70 100644
--- a/src/common/common.vcxproj
+++ b/src/common/common.vcxproj
@@ -29,9 +29,7 @@
common
-
-
-
+
StaticLibrary
true
@@ -179,11 +177,12 @@
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
+
\ No newline at end of file
diff --git a/src/common/notifications_winrt/notifications.vcxproj b/src/common/notifications_winrt/notifications.vcxproj
index 0599997ee5..d44d6c9e1d 100644
--- a/src/common/notifications_winrt/notifications.vcxproj
+++ b/src/common/notifications_winrt/notifications.vcxproj
@@ -1,6 +1,6 @@
-
+
true
true
@@ -147,13 +147,13 @@
-
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
+
+
\ No newline at end of file
diff --git a/src/common/notifications_winrt/notifications.vcxproj.filters b/src/common/notifications_winrt/notifications.vcxproj.filters
index 097a6072f1..fe1b061b0d 100644
--- a/src/common/notifications_winrt/notifications.vcxproj.filters
+++ b/src/common/notifications_winrt/notifications.vcxproj.filters
@@ -18,11 +18,9 @@
-
-
-
+
diff --git a/src/common/notifications_winrt/packages.config b/src/common/notifications_winrt/packages.config
index c4867c30bf..1447e714af 100644
--- a/src/common/notifications_winrt/packages.config
+++ b/src/common/notifications_winrt/packages.config
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/src/common/packages.config b/src/common/packages.config
index 09cb116327..4f733475e9 100644
--- a/src/common/packages.config
+++ b/src/common/packages.config
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/EnabledModules.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/EnabledModules.cs
index 2c0a788a91..2333ba38d1 100644
--- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/EnabledModules.cs
+++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/EnabledModules.cs
@@ -16,6 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
this.FileExplorerPreview = false;
this.PowerRename = false;
this.ShortcutGuide = false;
+ this.PowerLauncher = true;
}
[JsonPropertyName("FancyZones")]
@@ -34,6 +35,9 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
[JsonPropertyName("Keyboard Manager")]
public bool KeyboardManager { get; set; }
+
+ [JsonPropertyName("Launcher")]
+ public bool PowerLauncher { get; set; }
public string ToJsonString()
{
diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/HotkeySettings.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/HotkeySettings.cs
index f8ba5195bd..bfd4bb6dcc 100644
--- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/HotkeySettings.cs
+++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/HotkeySettings.cs
@@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using Microsoft.PowerToys.Settings.UI.Lib.Utilities;
using System.Text;
using System.Text.Json.Serialization;
@@ -61,8 +62,19 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
output.Append("Shift + ");
}
- output.Append(Key);
+ var localKey = Helper.GetKeyName((uint) Code);
+ output.Append(localKey);
return output.ToString();
}
+
+ public bool IsValid()
+ {
+ return (Alt || Ctrl || Win || Shift) && Code != 0;
+ }
+
+ public bool IsEmpty()
+ {
+ return !Alt && !Ctrl && !Win && !Shift && Code == 0;
+ }
}
}
diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerLauncherProperties.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerLauncherProperties.cs
index 01887c516f..5f61705afb 100644
--- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerLauncherProperties.cs
+++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerLauncherProperties.cs
@@ -6,8 +6,6 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
{
public class PowerLauncherProperties
{
- public bool enable_powerlauncher { get; set; }
-
public string search_result_preference { get; set; }
public string search_type_preference { get; set; }
diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerLauncherSettings.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerLauncherSettings.cs
index 2ce14acefa..fc841e928b 100644
--- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerLauncherSettings.cs
+++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerLauncherSettings.cs
@@ -8,7 +8,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
{
public class PowerLauncherSettings : BasePTModuleSettings
{
- public const string POWERTOYNAME = "PowerLauncher";
+ public const string POWERTOYNAME = "Launcher";
public PowerLauncherProperties properties { get; set; }
@@ -16,7 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
{
properties = new PowerLauncherProperties();
version = "1";
- name = "_unset_";
+ name = POWERTOYNAME;
}
public virtual void Save()
diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Controls/HotkeySettingsControl.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Controls/HotkeySettingsControl.xaml.cs
index 4277644479..36d12c671a 100644
--- a/src/core/Microsoft.PowerToys.Settings.UI/Controls/HotkeySettingsControl.xaml.cs
+++ b/src/core/Microsoft.PowerToys.Settings.UI/Controls/HotkeySettingsControl.xaml.cs
@@ -23,6 +23,7 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
null);
private HotkeySettings hotkeySettings;
+ private HotkeySettings internalSettings = new HotkeySettings();
public HotkeySettings HotkeySettings
{
@@ -45,7 +46,10 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
public HotkeySettingsControl()
{
InitializeComponent();
+ internalSettings = new HotkeySettings();
+
HotkeyTextBox.PreviewKeyDown += HotkeyTextBox_KeyDown;
+ HotkeyTextBox.LostFocus += HotkeyTextBox_LosingFocus;
}
private static bool IsDown(Windows.System.VirtualKey key)
@@ -66,6 +70,13 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
return;
}
+ if (e.Key == Windows.System.VirtualKey.Escape)
+ {
+ internalSettings = new HotkeySettings();
+ HotkeySettings = new HotkeySettings();
+ return;
+ }
+
var settings = new HotkeySettings();
// Display HotKey value
@@ -92,9 +103,19 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
settings.Key = Lib.Utilities.Helper.GetKeyName((uint)e.Key);
- // TODO: Check that e.OriginalKey is the ScanCode. It is not clear from docs.
settings.Code = (int)e.OriginalKey;
- HotkeySettings = settings;
+ internalSettings = settings;
+ HotkeyTextBox.Text = internalSettings.ToString();
+ }
+
+ private void HotkeyTextBox_LosingFocus(object sender, RoutedEventArgs e)
+ {
+ if (internalSettings.IsValid() || internalSettings.IsEmpty())
+ {
+ HotkeySettings = internalSettings;
+ }
+
+ HotkeyTextBox.Text = hotkeySettings.ToString();
}
}
}
diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw b/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw
index 47dd086c9d..67de76dd9d 100644
--- a/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw
+++ b/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw
@@ -134,7 +134,7 @@
Navigation view item name for General
- PowerLauncher [Not Functional]
+ PowerLauncher
Navigation view item name for PowerLauncher
diff --git a/src/core/Microsoft.PowerToys.Settings.UI/ViewModels/PowerLauncherViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI/ViewModels/PowerLauncherViewModel.cs
index 1c2fbd0889..fe218ef302 100644
--- a/src/core/Microsoft.PowerToys.Settings.UI/ViewModels/PowerLauncherViewModel.cs
+++ b/src/core/Microsoft.PowerToys.Settings.UI/ViewModels/PowerLauncherViewModel.cs
@@ -13,13 +13,20 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
public class PowerLauncherViewModel : Observable
{
private PowerLauncherSettings settings;
+ private GeneralSettings generalSettings;
public delegate void SendCallback(PowerLauncherSettings settings);
private readonly SendCallback callback;
public PowerLauncherViewModel()
- {
+ {
+ callback = (PowerLauncherSettings settings) =>
+ {
+ // Propagate changes to Power Launcher through IPC
+ ShellPage.DefaultSndMSGCallback(
+ string.Format("{{ \"powertoys\": {{ \"{0}\": {1} }} }}", PowerLauncherSettings.POWERTOYNAME, JsonSerializer.Serialize(settings)));
+ };
if (SettingsUtils.SettingsExists(PowerLauncherSettings.POWERTOYNAME))
{
settings = SettingsUtils.GetSettings(PowerLauncherSettings.POWERTOYNAME);
@@ -27,15 +34,20 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
else
{
settings = new PowerLauncherSettings();
+ settings.properties.open_powerlauncher.Alt = true;
+ settings.properties.open_powerlauncher.Code = (int)Windows.System.VirtualKey.Space;
+ settings.properties.maximum_number_of_results = 4;
+ callback(settings);
}
- callback = (PowerLauncherSettings settings) =>
+ if (SettingsUtils.SettingsExists())
{
- // Propagate changes to Power Launcher through IPC
- var propertiesJson = JsonSerializer.Serialize(settings.properties);
- ShellPage.DefaultSndMSGCallback(
- string.Format("{{ \"{0}\": {1} }}", PowerLauncherSettings.POWERTOYNAME, JsonSerializer.Serialize(settings.properties)));
- };
+ generalSettings = SettingsUtils.GetSettings();
+ }
+ else
+ {
+ generalSettings = new GeneralSettings();
+ }
}
public PowerLauncherViewModel(PowerLauncherSettings settings, SendCallback callback)
@@ -49,7 +61,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
// Notify UI of property change
OnPropertyChanged(propertyName);
- settings.Save();
callback(settings);
}
@@ -57,15 +68,17 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
get
{
- return settings.properties.enable_powerlauncher;
+ return generalSettings.Enabled.PowerLauncher;
}
set
{
- if (settings.properties.enable_powerlauncher != value)
+ if (generalSettings.Enabled.PowerLauncher != value)
{
- settings.properties.enable_powerlauncher = value;
- UpdateSettings();
+ generalSettings.Enabled.PowerLauncher = value;
+ OnPropertyChanged(nameof(EnablePowerLauncher));
+ OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(generalSettings);
+ ShellPage.DefaultSndMSGCallback(outgoing.ToString());
}
}
}
diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml
index 6a9b7bc194..dbed7e95c3 100644
--- a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml
+++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml
@@ -61,6 +61,7 @@
SelectedItem="{x:Bind Mode=TwoWay, Path=SelectedSearchResultPreference}"
SelectedValuePath="Item2"
DisplayMemberPath="Item1"
+ IsEnabled="False"
/>
+ Minimum="1"
+ IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.EnablePowerLauncher}"/>
+ IsChecked="{x:Bind Mode=TwoWay, Path=ViewModel.OverrideWinRKey}"
+ IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.EnablePowerLauncher}"
+ />
-
-
+ IsChecked="{x:Bind Mode=TwoWay, Path=ViewModel.OverrideWinSKey}"
+ IsEnabled="False"
+ />
-
-
+
@@ -121,16 +121,16 @@
-
+
-
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/dll/FancyZonesModule.vcxproj.filters b/src/modules/fancyzones/dll/FancyZonesModule.vcxproj.filters
index 10d25e8762..d90f9e60e7 100644
--- a/src/modules/fancyzones/dll/FancyZonesModule.vcxproj.filters
+++ b/src/modules/fancyzones/dll/FancyZonesModule.vcxproj.filters
@@ -24,10 +24,10 @@
{2f10207d-d8d1-4a42-8027-8ca597b3cb23}
-
-
-
+
+
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/dll/packages.config b/src/modules/fancyzones/dll/packages.config
index 2e822b86d0..feed8b8b86 100644
--- a/src/modules/fancyzones/dll/packages.config
+++ b/src/modules/fancyzones/dll/packages.config
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/lib/FancyZonesLib.vcxproj b/src/modules/fancyzones/lib/FancyZonesLib.vcxproj
index ee090a28ab..947117192e 100644
--- a/src/modules/fancyzones/lib/FancyZonesLib.vcxproj
+++ b/src/modules/fancyzones/lib/FancyZonesLib.vcxproj
@@ -131,12 +131,12 @@
-
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/lib/packages.config b/src/modules/fancyzones/lib/packages.config
index 2e822b86d0..feed8b8b86 100644
--- a/src/modules/fancyzones/lib/packages.config
+++ b/src/modules/fancyzones/lib/packages.config
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/tests/UnitTests/UnitTests.vcxproj b/src/modules/fancyzones/tests/UnitTests/UnitTests.vcxproj
index 864b424d3c..f23f1f7547 100644
--- a/src/modules/fancyzones/tests/UnitTests/UnitTests.vcxproj
+++ b/src/modules/fancyzones/tests/UnitTests/UnitTests.vcxproj
@@ -130,12 +130,12 @@
-
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/tests/UnitTests/packages.config b/src/modules/fancyzones/tests/UnitTests/packages.config
index 09cb116327..4f733475e9 100644
--- a/src/modules/fancyzones/tests/UnitTests/packages.config
+++ b/src/modules/fancyzones/tests/UnitTests/packages.config
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/src/modules/launcher/Directory.Build.targets b/src/modules/launcher/Directory.Build.targets
new file mode 100644
index 0000000000..cba92d64e1
--- /dev/null
+++ b/src/modules/launcher/Directory.Build.targets
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/JsonRPC/wox.py b/src/modules/launcher/JsonRPC/wox.py
new file mode 100644
index 0000000000..2e65a629d6
--- /dev/null
+++ b/src/modules/launcher/JsonRPC/wox.py
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+from __future__ import print_function
+import json
+import sys
+import inspect
+
+class Wox(object):
+ """
+ Wox python plugin base
+ """
+
+ def __init__(self):
+ rpc_request = json.loads(sys.argv[1])
+ # proxy is not working now
+ self.proxy = rpc_request.get("proxy",{})
+ request_method_name = rpc_request.get("method")
+ request_parameters = rpc_request.get("parameters")
+ methods = inspect.getmembers(self, predicate=inspect.ismethod)
+
+ request_method = dict(methods)[request_method_name]
+ results = request_method(*request_parameters)
+
+ if request_method_name == "query" or request_method_name == "context_menu":
+ print(json.dumps({"result": results}))
+
+ def query(self,query):
+ """
+ sub class need to override this method
+ """
+ return []
+
+ def context_menu(self, data):
+ """
+ optional context menu entries for a result
+ """
+ return []
+
+ def debug(self,msg):
+ """
+ alert msg
+ """
+ print("DEBUG:{}".format(msg))
+ sys.exit()
+
+class WoxAPI(object):
+
+ @classmethod
+ def change_query(cls,query,requery = False):
+ """
+ change wox query
+ """
+ print(json.dumps({"method": "Wox.ChangeQuery","parameters":[query,requery]}))
+
+ @classmethod
+ def shell_run(cls,cmd):
+ """
+ run shell commands
+ """
+ print(json.dumps({"method": "Wox.ShellRun","parameters":[cmd]}))
+
+ @classmethod
+ def close_app(cls):
+ """
+ close wox
+ """
+ print(json.dumps({"method": "Wox.CloseApp","parameters":[]}))
+
+ @classmethod
+ def hide_app(cls):
+ """
+ hide wox
+ """
+ print(json.dumps({"method": "Wox.HideApp","parameters":[]}))
+
+ @classmethod
+ def show_app(cls):
+ """
+ show wox
+ """
+ print(json.dumps({"method": "Wox.ShowApp","parameters":[]}))
+
+ @classmethod
+ def show_msg(cls,title,sub_title,ico_path=""):
+ """
+ show messagebox
+ """
+ print(json.dumps({"method": "Wox.ShowMsg","parameters":[title,sub_title,ico_path]}))
+
+ @classmethod
+ def open_setting_dialog(cls):
+ """
+ open setting dialog
+ """
+ print(json.dumps({"method": "Wox.OpenSettingDialog","parameters":[]}))
+
+ @classmethod
+ def start_loadingbar(cls):
+ """
+ start loading animation in wox
+ """
+ print(json.dumps({"method": "Wox.StartLoadingBar","parameters":[]}))
+
+ @classmethod
+ def stop_loadingbar(cls):
+ """
+ stop loading animation in wox
+ """
+ print(json.dumps({"method": "Wox.StopLoadingBar","parameters":[]}))
+
+ @classmethod
+ def reload_plugins(cls):
+ """
+ reload all wox plugins
+ """
+ print(json.dumps({"method": "Wox.ReloadPlugins","parameters":[]}))
diff --git a/src/modules/launcher/LICENSE b/src/modules/launcher/LICENSE
new file mode 100644
index 0000000000..cb4b563c0a
--- /dev/null
+++ b/src/modules/launcher/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Wox
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.rc b/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.rc
new file mode 100644
index 0000000000..ceb12a24d8
Binary files /dev/null and b/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.rc differ
diff --git a/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj b/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj
new file mode 100644
index 0000000000..b578b9c6b9
--- /dev/null
+++ b/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj
@@ -0,0 +1,123 @@
+
+
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 15.0
+ {e364f67b-bb12-4e91-b639-355866ebcd8b}
+ Win32Proj
+ Wox_Launcher
+ 10.0
+ Microsoft.Launcher
+
+
+
+ DynamicLibrary
+ true
+ v142
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v142
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(SolutionDir)$(Platform)\$(Configuration)\modules\
+
+
+ false
+ $(SolutionDir)$(Platform)\$(Configuration)\modules\
+
+
+
+ Use
+ Level3
+ Disabled
+ true
+ _DEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ pch.h
+ ..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;..\..\..\..\deps\cpprestsdk\include;%(AdditionalIncludeDirectories)
+ MultiThreadedDebug
+ stdcpplatest
+
+
+ Windows
+ true
+ $(OutDir)$(TargetName)$(TargetExt)
+
+
+
+
+ Use
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ NDEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ pch.h
+ ..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;..\..\..\..\deps\cpprestsdk\include;%(AdditionalIncludeDirectories)
+ MultiThreaded
+ stdcpplatest
+
+
+ Windows
+ true
+ true
+ true
+ $(OutDir)$(TargetName)$(TargetExt)
+
+
+
+
+
+
+
+
+
+
+ Create
+ Create
+ pch.h
+ pch.h
+
+
+
+
+
+ {74485049-c722-400f-abe5-86ac52d929b3}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj.filters b/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj.filters
new file mode 100644
index 0000000000..bb7d8ab134
--- /dev/null
+++ b/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj.filters
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Microsoft.Launcher/dllmain.cpp b/src/modules/launcher/Microsoft.Launcher/dllmain.cpp
new file mode 100644
index 0000000000..76d902a2c5
--- /dev/null
+++ b/src/modules/launcher/Microsoft.Launcher/dllmain.cpp
@@ -0,0 +1,197 @@
+#include "pch.h"
+#include
+#include
+#include
+#include
+#include "trace.h"
+#include "resource.h"
+
+extern "C" IMAGE_DOS_HEADER __ImageBase;
+
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
+ switch (ul_reason_for_call) {
+ case DLL_PROCESS_ATTACH:
+ Trace::RegisterProvider();
+ break;
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ break;
+ case DLL_PROCESS_DETACH:
+ Trace::UnregisterProvider();
+ break;
+ }
+ return TRUE;
+}
+
+
+// These are the properties shown in the Settings page.
+struct ModuleSettings {
+} g_settings;
+
+// Implement the PowerToy Module Interface and all the required methods.
+class Microsoft_Launcher : public PowertoyModuleIface {
+private:
+ // The PowerToy state.
+ bool m_enabled = false;
+
+ // Load initial settings from the persisted values.
+ void init_settings();
+
+ // Handle to launch and terminate the launcher
+ HANDLE m_hProcess;
+
+ //contains the name of the powerToys
+ std::wstring app_name;
+
+public:
+ // Constructor
+ Microsoft_Launcher() {
+ app_name = GET_RESOURCE_STRING(IDS_LAUNCHER_NAME);
+ init_settings();
+ };
+
+ ~Microsoft_Launcher() {
+ if (m_enabled)
+ {
+ TerminateProcess(m_hProcess, 1);
+ }
+ m_enabled = false;
+ }
+
+ // Destroy the powertoy and free memory
+ virtual void destroy() override {
+ delete this;
+ }
+
+ // Return the display name of the powertoy, this will be cached by the runner
+ virtual const wchar_t* get_name() override {
+ return app_name.c_str();
+ }
+
+ // Return array of the names of all events that this powertoy listens for, with
+ // nullptr as the last element of the array. Nullptr can also be retured for empty
+ // list.
+ virtual const wchar_t** get_events() override {
+ static const wchar_t* events[] = { nullptr };
+ // Available events:
+ // - ll_keyboard
+ // - win_hook_event
+ //
+ // static const wchar_t* events[] = { ll_keyboard,
+ // win_hook_event,
+ // nullptr };
+
+ return events;
+ }
+
+ // Return JSON with the configuration options.
+ virtual bool get_config(wchar_t* buffer, int* buffer_size) override {
+ HINSTANCE hinstance = reinterpret_cast(&__ImageBase);
+
+ // Create a Settings object.
+ PowerToysSettings::Settings settings(hinstance, get_name());
+ settings.set_description(GET_RESOURCE_STRING(IDS_LAUNCHER_SETTINGS_DESC));
+ return settings.serialize_to_buffer(buffer, buffer_size);
+ }
+
+ // Signal from the Settings editor to call a custom action.
+ // This can be used to spawn more complex editors.
+ virtual void call_custom_action(const wchar_t* action) override {
+ static UINT custom_action_num_calls = 0;
+ try {
+ // Parse the action values, including name.
+ PowerToysSettings::CustomActionObject action_object =
+ PowerToysSettings::CustomActionObject::from_json_string(action);
+ }
+ catch (std::exception ex) {
+ // Improper JSON.
+ }
+ }
+
+ // Called by the runner to pass the updated settings values as a serialized JSON.
+ virtual void set_config(const wchar_t* config) override {
+ try {
+ // Parse the input JSON string.
+ PowerToysSettings::PowerToyValues values =
+ PowerToysSettings::PowerToyValues::from_json_string(config);
+
+ // If you don't need to do any custom processing of the settings, proceed
+ // to persists the values calling:
+ values.save_to_settings_file();
+ // Otherwise call a custom function to process the settings before saving them to disk:
+ // save_settings();
+ }
+ catch (std::exception ex) {
+ // Improper JSON.
+ }
+ }
+
+ // Enable the powertoy
+ virtual void enable()
+ {
+ SHELLEXECUTEINFO sei{ sizeof(sei) };
+ sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
+ sei.lpFile = L"modules\\launcher\\PowerLauncher.exe";
+ sei.nShow = SW_SHOWNORMAL;
+ ShellExecuteEx(&sei);
+
+ m_hProcess = sei.hProcess;
+
+ m_enabled = true;
+ }
+
+ // Disable the powertoy
+ virtual void disable()
+ {
+ if (m_enabled)
+ {
+ TerminateProcess(m_hProcess, 1);
+ }
+
+ m_enabled = false;
+ }
+
+ // Returns if the powertoys is enabled
+ virtual bool is_enabled() override {
+ return m_enabled;
+ }
+
+ // Handle incoming event, data is event-specific
+ virtual intptr_t signal_event(const wchar_t* name, intptr_t data) override {
+ if (wcscmp(name, ll_keyboard) == 0) {
+ auto& event = *(reinterpret_cast(data));
+ // Return 1 if the keypress is to be suppressed (not forwarded to Windows),
+ // otherwise return 0.
+ return 0;
+ }
+ else if (wcscmp(name, win_hook_event) == 0) {
+ auto& event = *(reinterpret_cast(data));
+ // Return value is ignored
+ return 0;
+ }
+ return 0;
+ }
+
+ /* Register helper class to handle system menu items related actions. */
+ virtual void register_system_menu_helper(PowertoySystemMenuIface* helper) {}
+ /* Handle action on system menu item. */
+ virtual void signal_system_menu_action(const wchar_t* name) {}
+};
+
+// Load the settings file.
+void Microsoft_Launcher::init_settings() {
+ try {
+ // Load and parse the settings file for this PowerToy.
+ PowerToysSettings::PowerToyValues settings =
+ PowerToysSettings::PowerToyValues::load_from_settings_file(get_name());
+
+ }
+ catch (std::exception ex) {
+ // Error while loading from the settings file. Let default values stay as they are.
+ }
+}
+
+
+extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() {
+ return new Microsoft_Launcher();
+}
diff --git a/src/modules/launcher/Microsoft.Launcher/pch.cpp b/src/modules/launcher/Microsoft.Launcher/pch.cpp
new file mode 100644
index 0000000000..a3d1ff4225
--- /dev/null
+++ b/src/modules/launcher/Microsoft.Launcher/pch.cpp
@@ -0,0 +1,3 @@
+#include "pch.h"
+#pragma comment(lib, "windowsapp")
+#pragma comment(lib, "shlwapi.lib")
\ No newline at end of file
diff --git a/src/modules/launcher/Microsoft.Launcher/pch.h b/src/modules/launcher/Microsoft.Launcher/pch.h
new file mode 100644
index 0000000000..8cb4314b9c
--- /dev/null
+++ b/src/modules/launcher/Microsoft.Launcher/pch.h
@@ -0,0 +1,7 @@
+#pragma once
+#define WIN32_LEAN_AND_MEAN
+#include
+#include
+#include
+#include
+#include
\ No newline at end of file
diff --git a/src/modules/launcher/Microsoft.Launcher/resource.h b/src/modules/launcher/Microsoft.Launcher/resource.h
new file mode 100644
index 0000000000..aad6c4bbdc
--- /dev/null
+++ b/src/modules/launcher/Microsoft.Launcher/resource.h
@@ -0,0 +1,2 @@
+#define IDS_LAUNCHER_NAME 601
+#define IDS_LAUNCHER_SETTINGS_DESC 602
diff --git a/src/modules/launcher/Microsoft.Launcher/trace.cpp b/src/modules/launcher/Microsoft.Launcher/trace.cpp
new file mode 100644
index 0000000000..94ae6d8fd9
--- /dev/null
+++ b/src/modules/launcher/Microsoft.Launcher/trace.cpp
@@ -0,0 +1,26 @@
+#include "pch.h"
+#include "trace.h"
+
+TRACELOGGING_DEFINE_PROVIDER(
+ g_hProvider,
+ "Microsoft.PowerToys",
+ // {38e8889b-9731-53f5-e901-e8a7c1753074}
+ (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
+ TraceLoggingOptionProjectTelemetry());
+
+void Trace::RegisterProvider() {
+ TraceLoggingRegister(g_hProvider);
+}
+
+void Trace::UnregisterProvider() {
+ TraceLoggingUnregister(g_hProvider);
+}
+
+void Trace::MyEvent() {
+ TraceLoggingWrite(
+ g_hProvider,
+ "PowerToyName::Event::MyEvent",
+ ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
+ TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
+ TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
+}
diff --git a/src/modules/launcher/Microsoft.Launcher/trace.h b/src/modules/launcher/Microsoft.Launcher/trace.h
new file mode 100644
index 0000000000..92cef7fc80
--- /dev/null
+++ b/src/modules/launcher/Microsoft.Launcher/trace.h
@@ -0,0 +1,8 @@
+#pragma once
+
+class Trace {
+public:
+ static void RegisterProvider();
+ static void UnregisterProvider();
+ static void MyEvent();
+};
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/ContextMenuLoader.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/ContextMenuLoader.cs
new file mode 100644
index 0000000000..9f054cdd91
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/ContextMenuLoader.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Windows;
+using Wox.Infrastructure.Logger;
+using Wox.Plugin;
+using Microsoft.Plugin.Indexer.SearchHelper;
+
+namespace Microsoft.Plugin.Indexer
+{
+ internal class ContextMenuLoader : IContextMenu
+ {
+ private readonly PluginInitContext _context;
+
+ public enum ResultType
+ {
+ Folder,
+ File
+ }
+
+ public ContextMenuLoader(PluginInitContext context)
+ {
+ _context = context;
+ }
+
+ public List LoadContextMenus(Result selectedResult)
+ {
+ var contextMenus = new List();
+ if (selectedResult.ContextData is SearchResult record)
+ {
+ ResultType type = Path.HasExtension(record.Path) ? ResultType.File : ResultType.Folder;
+
+ if (type == ResultType.File)
+ {
+ contextMenus.Add(CreateOpenContainingFolderResult(record));
+ }
+
+ var fileOrFolder = (type == ResultType.File) ? "file" : "folder";
+ contextMenus.Add(new ContextMenuResult
+ {
+ Title = "Copy path",
+ Glyph = "\xE8C8",
+ FontFamily = "Segoe MDL2 Assets",
+ SubTitle = $"Copy the current {fileOrFolder} path to clipboard",
+ AcceleratorKey = "C",
+ AcceleratorModifiers = "Control",
+
+ Action = (context) =>
+ {
+ try
+ {
+ Clipboard.SetText(record.Path);
+ return true;
+ }
+ catch (Exception e)
+ {
+ var message = "Fail to set text in clipboard";
+ LogException(message, e);
+ _context.API.ShowMsg(message);
+ return false;
+ }
+ }
+ });
+ }
+
+ return contextMenus;
+ }
+
+ private ContextMenuResult CreateOpenContainingFolderResult(SearchResult record)
+ {
+ return new ContextMenuResult
+ {
+ Title = "Open containing folder",
+ Glyph = "\xE838",
+ FontFamily = "Segoe MDL2 Assets",
+ AcceleratorKey = "E",
+ AcceleratorModifiers = "Control,Shift",
+ Action = _ =>
+ {
+ try
+ {
+ Process.Start("explorer.exe", $" /select,\"{record.Path}\"");
+ }
+ catch(Exception e)
+ {
+ var message = $"Fail to open file at {record.Path}";
+ LogException(message, e);
+ _context.API.ShowMsg(message);
+ return false;
+ }
+
+ return true;
+ },
+ };
+ }
+
+ public void LogException(string message, Exception e)
+ {
+ Log.Exception($"|Wox.Plugin.Folder.ContextMenu|{message}", e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Images/WindowsIndexerImg.bmp b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Images/WindowsIndexerImg.bmp
new file mode 100644
index 0000000000..78a015715e
Binary files /dev/null and b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Images/WindowsIndexerImg.bmp differ
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Main.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Main.cs
new file mode 100644
index 0000000000..4ee38e52fc
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Main.cs
@@ -0,0 +1,156 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Diagnostics;
+using System.Text;
+using System.Threading.Tasks;
+using Wox.Plugin;
+using System.IO;
+using System.ComponentModel;
+using Wox.Infrastructure.Storage;
+using Microsoft.Plugin.Indexer.SearchHelper;
+using Microsoft.Search.Interop;
+using Microsoft.PowerToys.Settings.UI.Lib;
+using System.Windows.Controls;
+
+namespace Microsoft.Plugin.Indexer
+{
+ class Main : ISettingProvider, IPlugin, ISavable, IPluginI18n, IContextMenu
+ {
+
+ // This variable contains metadata about the Plugin
+ private PluginInitContext _context;
+
+ // This variable contains information about the context menus
+ private Settings _settings;
+
+ // Contains information about the plugin stored in json format
+ private PluginJsonStorage _storage;
+
+ // To access Windows Search functionalities
+ private readonly WindowsSearchAPI _api = new WindowsSearchAPI();
+
+ private IContextMenu _contextMenuLoader;
+
+ // To save the configurations of plugins
+ public void Save()
+ {
+ _storage.Save();
+ }
+
+ // This function uses the Windows indexer and returns the list of results obtained
+ public List Query(Query query)
+ {
+ var results = new List();
+ if (!string.IsNullOrEmpty(query.Search))
+ {
+ var searchQuery = query.Search;
+ if (_settings.MaxSearchCount <= 0)
+ {
+ _settings.MaxSearchCount = 50;
+ }
+
+ try
+ {
+ var searchResultsList = _api.Search(searchQuery, maxCount: _settings.MaxSearchCount).ToList();
+ foreach (var searchResult in searchResultsList)
+ {
+ var path = searchResult.Path;
+
+ string workingDir = null;
+ if (_settings.UseLocationAsWorkingDir)
+ workingDir = Path.GetDirectoryName(path);
+
+ Result r = new Result();
+ r.Title = searchResult.Title;
+ r.SubTitle = path;
+ r.IcoPath = path;
+ r.Action = c =>
+ {
+ bool hide;
+ try
+ {
+ Process.Start(new ProcessStartInfo
+ {
+ FileName = path,
+ UseShellExecute = true,
+ WorkingDirectory = workingDir
+ });
+ hide = true;
+ }
+ catch (Win32Exception)
+ {
+ var name = $"Plugin: {_context.CurrentPluginMetadata.Name}";
+ var msg = "Can't Open this file";
+ _context.API.ShowMsg(name, msg, string.Empty);
+ hide = false;
+ }
+ return hide;
+ };
+ r.ContextData = searchResult;
+
+ //If the result is a directory, then it's display should show a directory.
+ if(Directory.Exists(path))
+ {
+ r.QueryTextDisplay = path;
+ }
+
+ results.Add(r);
+ }
+ }
+ catch(InvalidOperationException)
+ {
+ //The connection has closed, internal error of ExecuteReader()
+ //Not showing this exception to the users
+ }
+ catch (Exception ex)
+ {
+ results.Add(new Result
+ {
+ Title = ex.ToString(),
+ IcoPath = "Images\\WindowsIndexerImg.bmp"
+ });
+ }
+ }
+
+ return results;
+ }
+
+ public void Init(PluginInitContext context)
+ {
+ // initialize the context of the plugin
+ _context = context;
+ _contextMenuLoader = new ContextMenuLoader(context);
+ _storage = new PluginJsonStorage();
+ _settings = _storage.Load();
+ }
+
+ // TODO: Localize the strings
+ // Set the Plugin Title
+ public string GetTranslatedPluginTitle()
+ {
+ return "Windows Indexer Plugin";
+ }
+
+ // TODO: Localize the string
+ // Set the plugin Description
+ public string GetTranslatedPluginDescription()
+ {
+ return "Returns files and folders";
+ }
+
+ public List LoadContextMenus(Result selectedResult)
+ {
+ return _contextMenuLoader.LoadContextMenus(selectedResult);
+ }
+ public void UpdateSettings(PowerLauncherSettings settings)
+ {
+ _settings.MaxSearchCount = settings.properties.maximum_number_of_results;
+ }
+ public Control CreateSettingPanel()
+ {
+ throw new NotImplementedException();
+ }
+
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj
new file mode 100644
index 0000000000..ceaf8cc904
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj
@@ -0,0 +1,59 @@
+
+
+
+ netcoreapp3.1
+ {F8B870EB-D5F5-45BA-9CF7-A5C459818820}
+ Properties
+ Microsoft.Plugin.Indexer
+ Microsoft.Plugin.Indexer
+ true
+ false
+ false
+ x64
+
+
+
+ true
+ ..\..\..\..\..\x64\Debug\modules\launcher\Plugins\Microsoft.Plugin.Indexer\
+ DEBUG;TRACE
+ full
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+ false
+
+
+
+ ..\..\..\..\..\x64\Release\modules\launcher\Plugins\Microsoft.Plugin.Indexer\
+ TRACE
+ true
+ pdbonly
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/SearchResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/SearchResult.cs
new file mode 100644
index 0000000000..048191fa23
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/SearchResult.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Microsoft.Plugin.Indexer.SearchHelper
+{
+ public class SearchResult
+ {
+ // Contains the Path of the file or folder
+ public string Path { get; set; }
+
+ // Contains the Title of the file or folder
+ public string Title { get; set; }
+
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/WindowsSearchAPI.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/WindowsSearchAPI.cs
new file mode 100644
index 0000000000..26c51f7b67
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/WindowsSearchAPI.cs
@@ -0,0 +1,115 @@
+using System;
+using System.Collections.Generic;
+using System.Data.OleDb;
+using Microsoft.Search.Interop;
+
+namespace Microsoft.Plugin.Indexer.SearchHelper
+{
+ public class WindowsSearchAPI
+ {
+ public OleDbConnection conn;
+ public OleDbCommand command;
+ public OleDbDataReader WDSResults;
+ private readonly object _lock = new object();
+
+
+ public List ExecuteQuery(ISearchQueryHelper queryHelper, string keyword)
+ {
+ List _Result = new List();
+ // Generate SQL from our parameters, converting the userQuery from AQS->WHERE clause
+ string sqlQuery = queryHelper.GenerateSQLFromUserQuery(keyword);
+
+ // --- Perform the query ---
+ // create an OleDbConnection object which connects to the indexer provider with the windows application
+ using (conn = new OleDbConnection(queryHelper.ConnectionString))
+ {
+ // open the connection
+ conn.Open();
+
+ // now create an OleDB command object with the query we built above and the connection we just opened.
+ using (command = new OleDbCommand(sqlQuery, conn))
+ {
+ // execute the command, which returns the results as an OleDbDataReader.
+ using (WDSResults = command.ExecuteReader())
+ {
+ if(WDSResults.HasRows)
+ {
+ while (WDSResults.Read())
+ {
+ if(WDSResults.GetValue(0) != DBNull.Value && WDSResults.GetValue(1) != DBNull.Value)
+ {
+ var result = new SearchResult
+ {
+ Path = WDSResults.GetString(0),
+ Title = WDSResults.GetString(1)
+ };
+ _Result.Add(result);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return _Result;
+ }
+
+
+ public void ModifyQueryHelper(ref ISearchQueryHelper queryHelper, string pattern)
+ {
+ // convert file pattern if it is not '*'. Don't create restriction for '*' as it includes all files.
+ if (pattern != "*")
+ {
+ pattern = pattern.Replace("*", "%");
+ pattern = pattern.Replace("?", "_");
+
+ if (pattern.Contains("%") || pattern.Contains("_"))
+ {
+ queryHelper.QueryWhereRestrictions += " AND System.FileName LIKE '" + pattern + "' ";
+ }
+ else
+ {
+ // if there are no wildcards we can use a contains which is much faster as it uses the index
+ queryHelper.QueryWhereRestrictions += " AND Contains(System.FileName, '" + pattern + "') ";
+ }
+ }
+ }
+
+ public void InitQueryHelper(out ISearchQueryHelper queryHelper, int maxCount)
+ {
+ // This uses the Microsoft.Search.Interop assembly
+ CSearchManager manager = new CSearchManager();
+
+ // SystemIndex catalog is the default catalog in Windows
+ ISearchCatalogManager catalogManager = manager.GetCatalog("SystemIndex");
+
+ // Get the ISearchQueryHelper which will help us to translate AQS --> SQL necessary to query the indexer
+ queryHelper = catalogManager.GetQueryHelper();
+
+ // Set the number of results we want. Don't set this property if all results are needed.
+ queryHelper.QueryMaxResults = maxCount;
+
+ // Set list of columns we want to display, getting the path presently
+ queryHelper.QuerySelectColumns = "System.ItemPathDisplay, System.FileName";
+
+ // Set additional query restriction
+ queryHelper.QueryWhereRestrictions = "AND scope='file:'";
+
+ // To filter based on title for now
+ queryHelper.QueryContentProperties = "System.FileName";
+
+ // Set sorting order
+ queryHelper.QuerySorting = "System.DateModified DESC";
+ }
+
+ public IEnumerable Search(string keyword, string pattern = "*", int maxCount = 100)
+ {
+ lock(_lock){
+ ISearchQueryHelper queryHelper;
+ InitQueryHelper(out queryHelper, maxCount);
+ ModifyQueryHelper(ref queryHelper, pattern);
+ return ExecuteQuery(queryHelper, keyword);
+ }
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Settings.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Settings.cs
new file mode 100644
index 0000000000..cdfc3600c1
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Settings.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Microsoft.Plugin.Indexer
+{
+ public class Settings
+ {
+ public List ContextMenus = new List();
+ public int MaxSearchCount { get; set; } = 100;
+ public bool UseLocationAsWorkingDir { get; set; } = false;
+ }
+
+ public class ContextMenu
+ {
+ public string Name { get; set; }
+ public string Command { get; set; }
+ public string Argument { get; set; }
+ public string ImagePath { get; set; }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/plugin.json b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/plugin.json
new file mode 100644
index 0000000000..d6d4225bff
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/plugin.json
@@ -0,0 +1,12 @@
+{
+ "ID": "2140FC9819AD43A3A616E2735815C27C",
+ "ActionKeyword": "*",
+ "Name": "Windows Indexer",
+ "Description": "Search for files and folders",
+ "Author": "Microsoft",
+ "Version": "1.0.0",
+ "Language": "csharp",
+ "Website": "http://aka.ms/PowerToys",
+ "ExecuteFileName": "Microsoft.Plugin.Indexer.dll",
+ "IcoPath": "Images\\WindowsIndexerImg.bmp"
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Images/calculator.png b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Images/calculator.png
new file mode 100644
index 0000000000..102a86bde5
Binary files /dev/null and b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Images/calculator.png differ
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Languages/de.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Languages/de.xaml
new file mode 100644
index 0000000000..286e4bc9b3
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Languages/de.xaml
@@ -0,0 +1,10 @@
+
+
+ Rechner
+ Stellt mathematische Berechnungen bereit.(Versuche 5*3-2 in Wox)
+ Keine Zahl (NaN)
+ Ausdruck falsch oder nicht vollständig (Klammern vergessen?)
+ Diese Zahl in die Zwischenablage kopieren
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Languages/en.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Languages/en.xaml
new file mode 100644
index 0000000000..3b5fa32b33
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Languages/en.xaml
@@ -0,0 +1,10 @@
+
+
+ Calculator
+ Allows to do mathematical calculations.(Try 5*3-2 in Wox)
+ Not a number (NaN)
+ Expression wrong or incomplete (Did you forget some parentheses?)
+ Copy this number to the clipboard
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Languages/pl.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Languages/pl.xaml
new file mode 100644
index 0000000000..c93c5e802b
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Languages/pl.xaml
@@ -0,0 +1,8 @@
+
+
+ Kalkulator
+ Szybkie wykonywanie obliczeń matematycznych. (Spróbuj wpisać 5*3-2 w oknie Woxa)
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Languages/tr.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Languages/tr.xaml
new file mode 100644
index 0000000000..51c5b9b2b9
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Languages/tr.xaml
@@ -0,0 +1,10 @@
+
+
+ Hesap Makinesi
+ Matematiksel hesaplamalar yapmaya yarar. (5*3-2 yazmayı deneyin)
+ Sayı değil (NaN)
+ İfade hatalı ya da eksik. (Parantez koymayı mı unuttunuz?)
+ Bu sayıyı panoya kopyala
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Languages/zh-cn.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Languages/zh-cn.xaml
new file mode 100644
index 0000000000..1385f4565e
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Languages/zh-cn.xaml
@@ -0,0 +1,8 @@
+
+
+ 计算器
+ 为Wox提供数学计算能力。(试着在Wox输入 5*3-2)
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Languages/zh-tw.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Languages/zh-tw.xaml
new file mode 100644
index 0000000000..08cda08d94
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Languages/zh-tw.xaml
@@ -0,0 +1,8 @@
+
+
+ 計算機
+ 為 Wox 提供數學計算功能。(試著在 Wox 輸入 5*3-2)
+
+
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Main.cs b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Main.cs
new file mode 100644
index 0000000000..fab0b8e030
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Main.cs
@@ -0,0 +1,115 @@
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text.RegularExpressions;
+using System.Windows;
+using Mages.Core;
+
+namespace Wox.Plugin.Caculator
+{
+ public class Main : IPlugin, IPluginI18n
+ {
+ private static readonly Regex RegValidExpressChar = new Regex(
+ @"^(" +
+ @"ceil|floor|exp|pi|e|max|min|det|abs|log|ln|sqrt|" +
+ @"sin|cos|tan|arcsin|arccos|arctan|" +
+ @"eigval|eigvec|eig|sum|polar|plot|round|sort|real|zeta|" +
+ @"bin2dec|hex2dec|oct2dec|" +
+ @"==|~=|&&|\|\||" +
+ @"[ei]|[0-9]|[\+\-\*\/\^\., ""]|[\(\)\|\!\[\]]" +
+ @")+$", RegexOptions.Compiled);
+ private static readonly Regex RegBrackets = new Regex(@"[\(\)\[\]]", RegexOptions.Compiled);
+ private static readonly Engine MagesEngine;
+ private PluginInitContext Context { get; set; }
+
+ static Main()
+ {
+ MagesEngine = new Engine();
+ }
+
+ public List Query(Query query)
+ {
+ if (query.Search.Length <= 2 // don't affect when user only input "e" or "i" keyword
+ || !RegValidExpressChar.IsMatch(query.Search)
+ || !IsBracketComplete(query.Search)) return new List();
+
+ try
+ {
+ var result = MagesEngine.Interpret(query.Search);
+
+ if (result.ToString() == "NaN")
+ result = Context.API.GetTranslation("wox_plugin_calculator_not_a_number");
+
+ if (result is Function)
+ result = Context.API.GetTranslation("wox_plugin_calculator_expression_not_complete");
+
+
+ if (!string.IsNullOrEmpty(result?.ToString()))
+ {
+ return new List
+ {
+ new Result
+ {
+ Title = result.ToString(),
+ IcoPath = "Images/calculator.png",
+ Score = 300,
+ SubTitle = Context.API.GetTranslation("wox_plugin_calculator_copy_number_to_clipboard"),
+ Action = c =>
+ {
+ try
+ {
+ Clipboard.SetText(result.ToString());
+ return true;
+ }
+ catch (ExternalException)
+ {
+ MessageBox.Show("Copy failed, please try later");
+ return false;
+ }
+ }
+ }
+ };
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+
+ return new List();
+ }
+
+ private bool IsBracketComplete(string query)
+ {
+ var matchs = RegBrackets.Matches(query);
+ var leftBracketCount = 0;
+ foreach (Match match in matchs)
+ {
+ if (match.Value == "(" || match.Value == "[")
+ {
+ leftBracketCount++;
+ }
+ else
+ {
+ leftBracketCount--;
+ }
+ }
+
+ return leftBracketCount == 0;
+ }
+
+ public void Init(PluginInitContext context)
+ {
+ Context = context;
+ }
+
+ public string GetTranslatedPluginTitle()
+ {
+ return Context.API.GetTranslation("wox_plugin_caculator_plugin_name");
+ }
+
+ public string GetTranslatedPluginDescription()
+ {
+ return Context.API.GetTranslation("wox_plugin_caculator_plugin_description");
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Calculator/NumberTranslator.cs b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/NumberTranslator.cs
new file mode 100644
index 0000000000..448301d4fb
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/NumberTranslator.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace Wox.Plugin.Caculator
+{
+ ///
+ /// Tries to convert all numbers in a text from one culture format to another.
+ ///
+ public class NumberTranslator
+ {
+ private readonly CultureInfo sourceCulture;
+ private readonly CultureInfo targetCulture;
+ private readonly Regex splitRegexForSource;
+ private readonly Regex splitRegexForTarget;
+
+ private NumberTranslator(CultureInfo sourceCulture, CultureInfo targetCulture)
+ {
+ this.sourceCulture = sourceCulture;
+ this.targetCulture = targetCulture;
+
+ this.splitRegexForSource = GetSplitRegex(this.sourceCulture);
+ this.splitRegexForTarget = GetSplitRegex(this.targetCulture);
+ }
+
+ ///
+ /// Create a new - returns null if no number conversion
+ /// is required between the cultures.
+ ///
+ /// source culture
+ /// target culture
+ ///
+ public static NumberTranslator Create(CultureInfo sourceCulture, CultureInfo targetCulture)
+ {
+ bool conversionRequired = sourceCulture.NumberFormat.NumberDecimalSeparator != targetCulture.NumberFormat.NumberDecimalSeparator
+ || sourceCulture.NumberFormat.PercentGroupSeparator != targetCulture.NumberFormat.PercentGroupSeparator
+ || sourceCulture.NumberFormat.NumberGroupSizes != targetCulture.NumberFormat.NumberGroupSizes;
+ return conversionRequired
+ ? new NumberTranslator(sourceCulture, targetCulture)
+ : null;
+ }
+
+ ///
+ /// Translate from source to target culture.
+ ///
+ ///
+ ///
+ public string Translate(string input)
+ {
+ return this.Translate(input, this.sourceCulture, this.targetCulture, this.splitRegexForSource);
+ }
+
+ ///
+ /// Translate from target to source culture.
+ ///
+ ///
+ ///
+ public string TranslateBack(string input)
+ {
+ return this.Translate(input, this.targetCulture, this.sourceCulture, this.splitRegexForTarget);
+ }
+
+ private string Translate(string input, CultureInfo cultureFrom, CultureInfo cultureTo, Regex splitRegex)
+ {
+ var outputBuilder = new StringBuilder();
+
+ string[] tokens = splitRegex.Split(input);
+ foreach (string token in tokens)
+ {
+ decimal number;
+ outputBuilder.Append(
+ decimal.TryParse(token, NumberStyles.Number, cultureFrom, out number)
+ ? number.ToString(cultureTo)
+ : token);
+ }
+
+ return outputBuilder.ToString();
+ }
+
+ private Regex GetSplitRegex(CultureInfo culture)
+ {
+ var splitPattern = $"((?:\\d|{Regex.Escape(culture.NumberFormat.NumberDecimalSeparator)}";
+ if (!string.IsNullOrEmpty(culture.NumberFormat.NumberGroupSeparator))
+ {
+ splitPattern += $"|{Regex.Escape(culture.NumberFormat.NumberGroupSeparator)}";
+ }
+ splitPattern += ")+)";
+ return new Regex(splitPattern);
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Wox.Plugin.Calculator.csproj b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Wox.Plugin.Calculator.csproj
new file mode 100644
index 0000000000..02e9cb96ac
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/Wox.Plugin.Calculator.csproj
@@ -0,0 +1,111 @@
+
+
+
+ netcoreapp3.1
+ {59BD9891-3837-438A-958D-ADC7F91F6F7E}
+ Properties
+ Wox.Plugin.Caculator
+ Wox.Plugin.Caculator
+ true
+ false
+ false
+ x64
+
+
+
+ true
+ ..\..\..\..\..\x64\Debug\modules\launcher\Plugins\Wox.Plugin.Caculator\
+ DEBUG;TRACE
+ full
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+ false
+
+
+
+ ..\..\..\..\..\x64\Release\modules\launcher\Plugins\Wox.Plugin.Caculator\
+ TRACE
+ true
+ pdbonly
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Calculator/plugin.json b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/plugin.json
new file mode 100644
index 0000000000..925099548c
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Calculator/plugin.json
@@ -0,0 +1,12 @@
+{
+ "ID":"CEA0FDFC6D3B4085823D60DC76F28855",
+ "ActionKeyword":"*",
+ "Name":"Calculator",
+ "Description":"Provide mathematical calculations.(Try 5*3-2 in Wox)",
+ "Author":"cxfksword",
+ "Version":"1.0.0",
+ "Language":"csharp",
+ "Website":"http://www.wox.one/plugin",
+ "ExecuteFileName":"Wox.Plugin.Caculator.dll",
+ "IcoPath":"Images\\calculator.png"
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Folder/ContextMenuLoader.cs b/src/modules/launcher/Plugins/Wox.Plugin.Folder/ContextMenuLoader.cs
new file mode 100644
index 0000000000..f6e83b1787
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Folder/ContextMenuLoader.cs
@@ -0,0 +1,151 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Threading.Tasks;
+using System.Windows;
+using Wox.Infrastructure.Logger;
+using Wox.Infrastructure.Image;
+using Wox.Plugin.SharedCommands;
+
+namespace Wox.Plugin.Folder
+{
+ internal class ContextMenuLoader : IContextMenu
+ {
+ private readonly PluginInitContext _context;
+
+ public ContextMenuLoader(PluginInitContext context)
+ {
+ _context = context;
+ }
+
+ public List LoadContextMenus(Result selectedResult)
+ {
+ var contextMenus = new List();
+ if (selectedResult.ContextData is SearchResult record)
+ {
+ if (record.Type == ResultType.File)
+ {
+ 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 ContextMenuResult
+ {
+ Title = "Copy path",
+ Glyph = "\xE8C8",
+ FontFamily = "Segoe MDL2 Assets",
+ SubTitle = $"Copy the current {fileOrFolder} path to clipboard",
+ AcceleratorKey = "C",
+ AcceleratorModifiers = "Control",
+ 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;
+ }
+ }
+ });
+ }
+
+ return contextMenus;
+ }
+
+ private ContextMenuResult CreateOpenContainingFolderResult(SearchResult record)
+ {
+ return new ContextMenuResult
+ {
+ Title = "Open containing folder",
+ Glyph = "\xE838",
+ FontFamily = "Segoe MDL2 Assets",
+ AcceleratorKey = "E",
+ AcceleratorModifiers = "Control,Shift",
+ 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;
+ }
+ };
+ }
+
+
+ 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($"|Wox.Plugin.Folder.ContextMenu|{message}", e);
+ }
+
+ private bool CanRunAsDifferentUser(string path)
+ {
+ switch(Path.GetExtension(path))
+ {
+ case ".exe":
+ case ".bat":
+ return true;
+
+ default:
+ return false;
+
+ }
+ }
+ }
+
+ public class SearchResult
+ {
+ public string FullPath { get; set; }
+ public ResultType Type { get; set; }
+ }
+
+ public enum ResultType
+ {
+ Volume,
+ Folder,
+ File
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Folder/FolderLink.cs b/src/modules/launcher/Plugins/Wox.Plugin.Folder/FolderLink.cs
new file mode 100644
index 0000000000..8148c45bf4
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Folder/FolderLink.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Linq;
+using Newtonsoft.Json;
+
+namespace Wox.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) + ")";
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Folder/FolderPluginSettings.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Folder/FolderPluginSettings.xaml
new file mode 100644
index 0000000000..93b9178e01
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Folder/FolderPluginSettings.xaml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Folder/FolderPluginSettings.xaml.cs b/src/modules/launcher/Plugins/Wox.Plugin.Folder/FolderPluginSettings.xaml.cs
new file mode 100644
index 0000000000..2b5b979cef
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Folder/FolderPluginSettings.xaml.cs
@@ -0,0 +1,130 @@
+using System.Collections.Generic;
+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 Wox.Plugin.Folder
+{
+
+ public partial class FileSystemSettings
+ {
+ private IPublicAPI woxAPI;
+ private Settings _settings;
+
+ public FileSystemSettings(IPublicAPI woxAPI, Settings settings)
+ {
+ this.woxAPI = woxAPI;
+ InitializeComponent();
+ _settings = settings;
+ lbxFolders.ItemsSource = _settings.FolderLinks;
+ }
+
+ private void btnDelete_Click(object sender, RoutedEventArgs e)
+ {
+ var selectedFolder = lbxFolders.SelectedItem as FolderLink;
+ if (selectedFolder != null)
+ {
+ string msg = string.Format(woxAPI.GetTranslation("wox_plugin_folder_delete_folder_link"), selectedFolder.Path);
+
+ if (MessageBox.Show(msg, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes)
+ {
+ _settings.FolderLinks.Remove(selectedFolder);
+ lbxFolders.Items.Refresh();
+ }
+ }
+ else
+ {
+ string warning = woxAPI.GetTranslation("wox_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;
+ }
+
+ lbxFolders.Items.Refresh();
+ }
+ else
+ {
+ string warning = woxAPI.GetTranslation("wox_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();
+ }
+
+ _settings.FolderLinks.Add(newFolder);
+ }
+
+ lbxFolders.Items.Refresh();
+ }
+
+ 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();
+ }
+
+ foreach (string s in files)
+ {
+ if (Directory.Exists(s))
+ {
+ var newFolder = new FolderLink
+ {
+ Path = s
+ };
+
+ _settings.FolderLinks.Add(newFolder);
+ }
+
+ lbxFolders.Items.Refresh();
+ }
+ }
+ }
+
+ private void lbxFolders_DragEnter(object sender, DragEventArgs e)
+ {
+ if (e.Data.GetDataPresent(DataFormats.FileDrop))
+ {
+ e.Effects = DragDropEffects.Link;
+ }
+ else
+ {
+ e.Effects = DragDropEffects.None;
+ }
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Folder/Images/copy.png b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Images/copy.png
new file mode 100644
index 0000000000..8f1fca752f
Binary files /dev/null and b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Images/copy.png differ
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Folder/Images/deletefilefolder.png b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Images/deletefilefolder.png
new file mode 100644
index 0000000000..024cc92915
Binary files /dev/null and b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Images/deletefilefolder.png differ
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Folder/Images/file.png b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Images/file.png
new file mode 100644
index 0000000000..36156767a6
Binary files /dev/null and b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Images/file.png differ
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Folder/Images/folder.png b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Images/folder.png
new file mode 100644
index 0000000000..569fa70491
Binary files /dev/null and b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Images/folder.png differ
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Folder/Images/user.png b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Images/user.png
new file mode 100644
index 0000000000..2d45c1ee91
Binary files /dev/null and b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Images/user.png differ
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Folder/Languages/de.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Languages/de.xaml
new file mode 100644
index 0000000000..d58189b346
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Languages/de.xaml
@@ -0,0 +1,15 @@
+
+
+ Löschen
+ Bearbeiten
+ Hinzufügen
+ Ordnerpfad
+ Bitte wähle eine Ordnerverknüpfung
+ Bist du sicher {0} zu löschen?
+
+ Ordner
+ Öffne deine Lieblingsordner direkt von Wox aus
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Folder/Languages/en.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Languages/en.xaml
new file mode 100644
index 0000000000..78190d1b89
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Languages/en.xaml
@@ -0,0 +1,15 @@
+
+
+ Delete
+ Edit
+ Add
+ Folder Path
+ Please select a folder link
+ Are you sure you want to delete {0}?
+
+ Folder
+ Open favorite folder from Wox directly
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Folder/Languages/pl.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Languages/pl.xaml
new file mode 100644
index 0000000000..454b85036d
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Languages/pl.xaml
@@ -0,0 +1,15 @@
+
+
+ Usuń
+ Edytuj
+ Dodaj
+ Ścieżka folderu
+ Musisz wybrać któryś folder z listy
+ Czy jesteś pewien że chcesz usunąć {0}?
+
+ Foldery
+ Otwórz ulubione foldery bezpośrednio z poziomu Woxa
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Folder/Languages/tr.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Languages/tr.xaml
new file mode 100644
index 0000000000..8cf0c76535
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Languages/tr.xaml
@@ -0,0 +1,15 @@
+
+
+ Sil
+ Düzenle
+ Ekle
+ Klasör Konumu
+ Lütfen bir klasör bağlantısı seçin
+ {0} bağlantısını silmek istediğinize emin misiniz?
+
+ Klasör
+ Favori klasörünüzü doğrudan Wox'tan açın
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Folder/Languages/zh-cn.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Languages/zh-cn.xaml
new file mode 100644
index 0000000000..15df3d5c6e
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Languages/zh-cn.xaml
@@ -0,0 +1,15 @@
+
+
+ 删除
+ 编辑
+ 添加
+ 文件夹路径
+ 请选择一个文件夹
+ 你确定要删除{0}吗?
+
+ 文件夹
+ 在Wox中直接打开收藏的文件夹
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Folder/Languages/zh-tw.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Languages/zh-tw.xaml
new file mode 100644
index 0000000000..f87d8c14d4
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Languages/zh-tw.xaml
@@ -0,0 +1,15 @@
+
+
+ 刪除
+ 編輯
+ 新增
+ 資料夾路徑
+ 請選擇一個資料夾
+ 你確認要刪除{0}嗎?
+
+ 資料夾
+ 在 Wox 中直接開啟收藏的資料夾
+
+
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Folder/Main.cs b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Main.cs
new file mode 100644
index 0000000000..3b0d010e3f
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Main.cs
@@ -0,0 +1,293 @@
+using Microsoft.PowerToys.Settings.UI.Lib;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using Wox.Infrastructure;
+using Wox.Infrastructure.Storage;
+
+namespace Wox.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 const string _fileExplorerProgramName = "explorer";
+ private static List _driverNames;
+ private PluginInitContext _context;
+
+ private readonly Settings _settings;
+ private readonly PluginJsonStorage _storage;
+ private IContextMenu _contextMenuLoader;
+
+ public Main()
+ {
+ _storage = new PluginJsonStorage();
+ _settings = _storage.Load();
+ }
+
+ public void Save()
+ {
+ _storage.Save();
+ }
+
+ public Control CreateSettingPanel()
+ {
+ return new FileSystemSettings(_context.API, _settings);
+ }
+
+ public void Init(PluginInitContext context)
+ {
+ _context = context;
+ _contextMenuLoader = new ContextMenuLoader(context);
+ InitialDriverList();
+ }
+
+ public List Query(Query query)
+ {
+ var results = GetUserFolderResults(query);
+
+ string search = query.Search.ToLower();
+ if (!IsDriveOrSharedFolder(search))
+ return results;
+
+ results.AddRange(QueryInternal_Directory_Exists(query));
+
+ // todo why was this hack here?
+ foreach (var result in results)
+ {
+ result.Score += 10;
+ }
+
+ return results;
+ }
+
+ private static bool IsDriveOrSharedFolder(string search)
+ {
+ if (search.StartsWith(@"\\"))
+ { // share 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,
+ QueryTextDisplay = path,
+ TitleHighlightData = StringMatcher.FuzzySearch(query.Search, title).MatchData,
+ ContextData = new SearchResult { Type = ResultType.Folder, FullPath = path },
+ Action = c =>
+ {
+ Process.Start(_fileExplorerProgramName, path);
+ return true;
+ }
+ };
+ }
+
+ private List 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, item.Path, item.Path, query)).ToList();
+ return results;
+ }
+
+ private void InitialDriverList()
+ {
+ if (_driverNames == null)
+ {
+ _driverNames = new List();
+ var allDrives = DriveInfo.GetDrives();
+ foreach (DriveInfo driver in allDrives)
+ {
+ _driverNames.Add(driver.Name.ToLower().TrimEnd('\\'));
+ }
+ }
+ }
+
+ private static readonly char[] _specialSearchChars = new char[]
+ {
+ '?', '*', '>'
+ };
+
+ private List QueryInternal_Directory_Exists(Query query)
+ {
+ var search = query.Search;
+ var results = new List();
+ 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();
+ var fileList = new List();
+
+ 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)
+ {
+ var 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 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
+ {
+ Process.Start(_fileExplorerProgramName, 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 " + search;
+
+ var folderName = search.TrimEnd('\\').Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None).Last();
+
+ return new Result
+ {
+ Title = firstResult,
+ QueryTextDisplay = search,
+ SubTitle = $"Use > to search within the directory. Use * to search for file extensions. Or use both >*.",
+ IcoPath = search,
+ Score = 500,
+ Action = c =>
+ {
+ Process.Start(_fileExplorerProgramName, search);
+ return true;
+ }
+ };
+ }
+
+ public string GetTranslatedPluginTitle()
+ {
+ return _context.API.GetTranslation("wox_plugin_folder_plugin_name");
+ }
+
+ public string GetTranslatedPluginDescription()
+ {
+ return _context.API.GetTranslation("wox_plugin_folder_plugin_description");
+ }
+
+ public List LoadContextMenus(Result selectedResult)
+ {
+ return _contextMenuLoader.LoadContextMenus(selectedResult);
+ }
+
+ public void UpdateSettings(PowerLauncherSettings settings)
+ {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Folder/Settings.cs b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Settings.cs
new file mode 100644
index 0000000000..27518258f9
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Settings.cs
@@ -0,0 +1,12 @@
+using System.Collections.Generic;
+using Newtonsoft.Json;
+using Wox.Infrastructure.Storage;
+
+namespace Wox.Plugin.Folder
+{
+ public class Settings
+ {
+ [JsonProperty]
+ public List FolderLinks { get; set; } = new List();
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Folder/Wox.Plugin.Folder.csproj b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Wox.Plugin.Folder.csproj
new file mode 100644
index 0000000000..b015529232
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Folder/Wox.Plugin.Folder.csproj
@@ -0,0 +1,113 @@
+
+
+
+ netcoreapp3.1
+ {787B8AA6-CA93-4C84-96FE-DF31110AD1C4}
+ Properties
+ Wox.Plugin.Folder
+ Wox.Plugin.Folder
+ true
+ false
+ false
+ x64
+
+
+
+ true
+ ..\..\..\..\..\x64\Debug\modules\launcher\Plugins\Wox.Plugin.Folder\
+ DEBUG;TRACE
+ full
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+ false
+
+
+
+ ..\..\..\..\..\x64\Release\modules\launcher\Plugins\Wox.Plugin.Folder\
+ TRACE
+ true
+ pdbonly
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+ PreserveNewest
+
+
+ Always
+
+
+ PreserveNewest
+
+
+ Always
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Folder/plugin.json b/src/modules/launcher/Plugins/Wox.Plugin.Folder/plugin.json
new file mode 100644
index 0000000000..662a4df6eb
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Folder/plugin.json
@@ -0,0 +1,12 @@
+{
+ "ID":"B4D3B69656E14D44865C8D818EAE47C4",
+ "ActionKeyword":"*",
+ "Name":"Folder",
+ "Description":"Open favorite folder from wox directorily",
+ "Author":"qianlifeng",
+ "Version":"1.0.0",
+ "Language":"csharp",
+ "Website":"http://www.wox.one/plugin",
+ "ExecuteFileName":"Wox.Plugin.Folder.dll",
+ "IcoPath":"Images\\folder.png"
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/AddProgramSource.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Program/AddProgramSource.xaml
new file mode 100644
index 0000000000..6e3d66ff5b
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/AddProgramSource.xaml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/AddProgramSource.xaml.cs b/src/modules/launcher/Plugins/Wox.Plugin.Program/AddProgramSource.xaml.cs
new file mode 100644
index 0000000000..c79cb46cef
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/AddProgramSource.xaml.cs
@@ -0,0 +1,76 @@
+using System.Windows;
+using System.Windows.Forms;
+using Wox.Plugin.Program.Views.Models;
+using Wox.Plugin.Program.Views;
+using System.Linq;
+
+namespace Wox.Plugin.Program
+{
+ ///
+ /// Interaction logic for AddProgramSource.xaml
+ ///
+ public partial class AddProgramSource
+ {
+ private PluginInitContext _context;
+ private Settings.ProgramSource _editing;
+ private Settings _settings;
+
+ public AddProgramSource(PluginInitContext context, Settings settings)
+ {
+ InitializeComponent();
+ _context = context;
+ _settings = settings;
+ Directory.Focus();
+ }
+
+ public AddProgramSource(Settings.ProgramSource edit, Settings settings)
+ {
+ _editing = edit;
+ _settings = settings;
+
+ InitializeComponent();
+ Directory.Text = _editing.Location;
+ }
+
+ private void BrowseButton_Click(object sender, RoutedEventArgs e)
+ {
+ var dialog = new FolderBrowserDialog();
+ DialogResult result = dialog.ShowDialog();
+ if (result == System.Windows.Forms.DialogResult.OK)
+ {
+ Directory.Text = dialog.SelectedPath;
+ }
+ }
+
+ private void ButtonAdd_OnClick(object sender, RoutedEventArgs e)
+ {
+ string s = Directory.Text;
+ if (!System.IO.Directory.Exists(s))
+ {
+ System.Windows.MessageBox.Show(_context.API.GetTranslation("wox_plugin_program_invalid_path"));
+ return;
+ }
+ if (_editing == null)
+ {
+ if (!ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == Directory.Text))
+ {
+ var source = new ProgramSource
+ {
+ Location = Directory.Text,
+ UniqueIdentifier = Directory.Text
+ };
+
+ _settings.ProgramSources.Insert(0, source);
+ ProgramSetting.ProgramSettingDisplayList.Add(source);
+ }
+ }
+ else
+ {
+ _editing.Location = Directory.Text;
+ }
+
+ DialogResult = true;
+ Close();
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/AppxPackagingTlb.dll b/src/modules/launcher/Plugins/Wox.Plugin.Program/AppxPackagingTlb.dll
new file mode 100644
index 0000000000..183cfc085c
Binary files /dev/null and b/src/modules/launcher/Plugins/Wox.Plugin.Program/AppxPackagingTlb.dll differ
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/FileChangeWatcher.cs b/src/modules/launcher/Plugins/Wox.Plugin.Program/FileChangeWatcher.cs
new file mode 100644
index 0000000000..0df361215c
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/FileChangeWatcher.cs
@@ -0,0 +1,58 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using Wox.Infrastructure.Logger;
+using Wox.Plugin.Program.Programs;
+
+namespace Wox.Plugin.Program
+{
+ //internal static class FileChangeWatcher
+ //{
+ // private static readonly List WatchedPath = new List();
+ // // todo remove previous watcher events
+ // public static void AddAll(List sources, string[] suffixes)
+ // {
+ // foreach (var s in sources)
+ // {
+ // if (Directory.Exists(s.Location))
+ // {
+ // AddWatch(s.Location, suffixes);
+ // }
+ // }
+ // }
+
+ // public static void AddWatch(string path, string[] programSuffixes, bool includingSubDirectory = true)
+ // {
+ // if (WatchedPath.Contains(path)) return;
+ // if (!Directory.Exists(path))
+ // {
+ // Log.Warn($"|FileChangeWatcher|{path} doesn't exist");
+ // return;
+ // }
+
+ // WatchedPath.Add(path);
+ // foreach (string fileType in programSuffixes)
+ // {
+ // FileSystemWatcher watcher = new FileSystemWatcher
+ // {
+ // Path = path,
+ // IncludeSubdirectories = includingSubDirectory,
+ // Filter = $"*.{fileType}",
+ // EnableRaisingEvents = true
+ // };
+ // watcher.Changed += FileChanged;
+ // watcher.Created += FileChanged;
+ // watcher.Deleted += FileChanged;
+ // watcher.Renamed += FileChanged;
+ // }
+ // }
+
+ // private static void FileChanged(object source, FileSystemEventArgs e)
+ // {
+ // Task.Run(() =>
+ // {
+ // Main.IndexPrograms();
+ // });
+ // }
+ //}
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Images/cmd.png b/src/modules/launcher/Plugins/Wox.Plugin.Program/Images/cmd.png
new file mode 100644
index 0000000000..686583653f
Binary files /dev/null and b/src/modules/launcher/Plugins/Wox.Plugin.Program/Images/cmd.png differ
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Images/disable.png b/src/modules/launcher/Plugins/Wox.Plugin.Program/Images/disable.png
new file mode 100644
index 0000000000..e9d4976bcf
Binary files /dev/null and b/src/modules/launcher/Plugins/Wox.Plugin.Program/Images/disable.png differ
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Images/folder.png b/src/modules/launcher/Plugins/Wox.Plugin.Program/Images/folder.png
new file mode 100644
index 0000000000..569fa70491
Binary files /dev/null and b/src/modules/launcher/Plugins/Wox.Plugin.Program/Images/folder.png differ
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Images/program.png b/src/modules/launcher/Plugins/Wox.Plugin.Program/Images/program.png
new file mode 100644
index 0000000000..e4c7896898
Binary files /dev/null and b/src/modules/launcher/Plugins/Wox.Plugin.Program/Images/program.png differ
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Images/user.png b/src/modules/launcher/Plugins/Wox.Plugin.Program/Images/user.png
new file mode 100644
index 0000000000..2d45c1ee91
Binary files /dev/null and b/src/modules/launcher/Plugins/Wox.Plugin.Program/Images/user.png differ
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/de.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/de.xaml
new file mode 100644
index 0000000000..cfcb43089d
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/de.xaml
@@ -0,0 +1,37 @@
+
+
+
+ Löschen
+ Bearbeiten
+ Hinzufügen
+ Speicherort
+ Indexiere Dateiendungen
+ erneut Indexieren
+ Indexieren
+ Indexierungs Startmenü
+ Indexierungsspeicher
+ Endungen
+ Maximale Tiefe
+
+ Verzeichnis:
+ Durchsuchen
+ Dateiendungen:
+ Maximale Suchtiefe (-1 ist unlimitiert):
+
+ Bitte wähle eine Programmquelle
+
+ Aktualisieren
+ Wox indexiert nur Datien mit folgenden Endungen:
+ (Jede Endung muss durch ein ; getrennt werden)
+ Dateiendungen wurden erfolgreich aktualisiert
+ Dateiendungen dürfen nicht leer sein
+
+ Als Administrator ausführen
+ Enthaltenden Ordner öffnen
+
+ Programm
+ Suche Programme mit Wox
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/en.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/en.xaml
new file mode 100644
index 0000000000..b7202d590f
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/en.xaml
@@ -0,0 +1,48 @@
+
+
+
+ Delete
+ Edit
+ Add
+ Disable
+ Location
+ All Programs
+ File Suffixes
+ Reindex
+ Indexing
+ Index Start Menu
+ Index Registry
+ Suffixes
+ Max Depth
+
+ Directory:
+ Browse
+ File Suffixes:
+ Maximum Search Depth (-1 is unlimited):
+
+ Please select a program source
+ Are you sure you want to delete the selected program sources?
+
+ Update
+ Wox will only index files that end with the following suffixes:
+ (Each suffix should split by ';' )
+ Successfully updated file suffixes
+ File suffixes can't be empty
+
+ Run As Different User
+ Run As Administrator
+ Open containing folder
+ Disable this program from displaying
+
+ Program
+ Search programs in Wox
+
+ Invalid Path
+
+
+ Success
+ Successfully disabled this program from displaying in your query
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/ja.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/ja.xaml
new file mode 100644
index 0000000000..07630a1cda
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/ja.xaml
@@ -0,0 +1,38 @@
+
+
+
+ Delete
+ Edit
+ Add
+ Location
+ Index file suffixes
+ Reindex
+ Indexing
+ Index Start Menu
+ Index Registry
+ Suffixes
+ Max Depth
+
+ Directory:
+ Browse
+ File Suffixes:
+ Maximum Search Depth (-1 is unlimited):
+
+ Please select a program source
+ Are your sure to delete {0}?
+
+ Update
+ Wox will only index files that end with following suffixes:
+ (Each suffix should split by ;)
+ Sucessfully update file suffixes
+ File suffixes can't be empty
+
+ Run As Administrator
+ Open containing folder
+
+ Program
+ Search programs in Wox
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/pl.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/pl.xaml
new file mode 100644
index 0000000000..bb82ee8d2e
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/pl.xaml
@@ -0,0 +1,37 @@
+
+
+
+ Usuń
+ Edytuj
+ Dodaj
+ Lokalizacja
+ Rozszerzenia indeksowanych plików
+ Re-indeksuj
+ Indeksowanie
+ Indeksuj Menu Start
+ Indeksuj rejestr
+ Rozszerzenia
+ Maksymalna głębokość
+
+ Katalog:
+ Przeglądaj
+ Rozszerzenia plików:
+ Maksymalna głębokość wyszukiwania (-1 to nieograniczona):
+
+ Musisz wybrać katalog programu
+
+ Aktualizuj
+ Wox będzie indeksował tylko te pliki z podanymi rozszerzeniami:
+ (Każde rozszerzenie musi być oddzielone ;)
+ Pomyślnie zaktualizowano rozszerzenia plików
+ Musisz podać rozszerzenia plików
+
+ Uruchom jako administrator
+ Otwórz folder zawierający
+
+ Programy
+ Szukaj i uruchamiaj programy z poziomu Woxa
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/tr.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/tr.xaml
new file mode 100644
index 0000000000..ef01e29fd1
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/tr.xaml
@@ -0,0 +1,39 @@
+
+
+
+ Sil
+ Düzenle
+ Ekle
+ Konum
+ İndekslenecek Uzantılar
+ Yeniden İndeksle
+ İndeksleniyor
+ Başlat Menüsünü İndeksle
+ Registry'i İndeksle
+ Uzantılar
+ Derinlik
+
+ Dizin:
+ Gözat
+ Dosya Uzantıları:
+ Maksimum Arama Derinliği (Limitsiz için -1):
+
+ İşlem yapmak istediğiniz klasörü seçin.
+
+ Güncelle
+ Wox yalnızca aşağıdaki uzantılara sahip dosyaları indeksleyecektir:
+ (Her uzantıyı ; işareti ile ayırın)
+ Dosya uzantıları başarıyla güncellendi
+ Dosya uzantıları boş olamaz
+
+ Yönetici Olarak Çalıştır
+ İçeren Klasörü Aç
+
+ Program
+ Programları Wox'tan arayın
+
+ Geçersiz Konum
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/zh-cn.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/zh-cn.xaml
new file mode 100644
index 0000000000..f41193095b
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/zh-cn.xaml
@@ -0,0 +1,39 @@
+
+
+
+ 删除
+ 编辑
+ 增加
+ 位置
+ 索引文件后缀
+ 重新索引
+ 索引中
+ 索引开始菜单
+ 索引注册表
+ 后缀
+ 最大深度
+
+ 目录
+ 浏览
+ 文件后缀
+ 最大搜索深度(-1是无限的):
+
+ 请先选择一项
+
+ 更新
+ Wox仅索引下列后缀的文件:
+ (每个后缀以英文状态下的分号分隔)
+ 成功更新索引文件后缀
+ 文件后缀不能为空
+
+ 以管理员身份运行
+ 打开所属文件夹
+
+ 程序
+ 在Wox中搜索程序
+
+ 无效路径
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/zh-tw.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/zh-tw.xaml
new file mode 100644
index 0000000000..d3c0f18cca
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/Languages/zh-tw.xaml
@@ -0,0 +1,36 @@
+
+
+
+ 刪除
+ 編輯
+ 新增
+ 路徑
+ 索引副檔名清單
+ 重新建立索引
+ 正在建立索引
+ 替開始選單建立索引
+ 替登錄檔建立索引
+ 副檔名
+ 最大深度
+
+ 目錄
+ 瀏覽
+ 副檔名
+ 最大搜尋深度(-1是無限的):
+
+ 請先選擇一項
+
+ 更新
+ Wox 僅索引下列副檔名的檔案:
+ (每個副檔名以英文的分號分隔)
+ 成功更新索引副檔名清單
+ 副檔名不能為空
+
+ 以系統管理員身分執行
+ 開啟檔案位置
+
+ 程式
+ 在 Wox 中搜尋程式
+
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/LocationConverter.cs b/src/modules/launcher/Plugins/Wox.Plugin.Program/LocationConverter.cs
new file mode 100644
index 0000000000..bd28bc9441
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/LocationConverter.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+using System.Windows.Markup;
+
+namespace Wox.Plugin.Program
+{
+ public class LocationConverter : MarkupExtension, IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ var text = value as string;
+ if (string.IsNullOrEmpty(text))
+ {
+ return string.Empty;
+ }
+ else
+ {
+ return text;
+ }
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override object ProvideValue(IServiceProvider serviceProvider)
+ {
+ return this;
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Logger/ProgramLogger.cs b/src/modules/launcher/Plugins/Wox.Plugin.Program/Logger/ProgramLogger.cs
new file mode 100644
index 0000000000..587aba6857
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/Logger/ProgramLogger.cs
@@ -0,0 +1,139 @@
+using NLog;
+using NLog.Config;
+using NLog.Targets;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Security;
+using Wox.Infrastructure;
+
+namespace Wox.Plugin.Program.Logger
+{
+ ///
+ /// The Program plugin has seen many issues recorded in the Wox repo related to various loading of Windows programs.
+ /// This is a dedicated logger for this Program plugin with the aim to output a more friendlier message and clearer
+ /// log that will allow debugging to be quicker and easier.
+ ///
+ internal static class ProgramLogger
+ {
+ public const string DirectoryName = "Logs";
+
+ static ProgramLogger()
+ {
+ var path = Path.Combine(Constant.DataDirectory, DirectoryName, Constant.Version);
+ if (!Directory.Exists(path))
+ {
+ Directory.CreateDirectory(path);
+ }
+
+ var configuration = new LoggingConfiguration();
+ var target = new FileTarget();
+ configuration.AddTarget("file", target);
+ target.FileName = path.Replace(@"\", "/") + "/${shortdate}.txt";
+#if DEBUG
+ var rule = new LoggingRule("*", LogLevel.Debug, target);
+#else
+ var rule = new LoggingRule("*", LogLevel.Error, target);
+#endif
+ configuration.LoggingRules.Add(rule);
+ LogManager.Configuration = configuration;
+ }
+
+ ///
+ /// Logs an exception
+ ///
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ internal static void LogException(string classname, string callingMethodName, string loadingProgramPath,
+ string interpretationMessage, Exception e)
+ {
+ Debug.WriteLine($"ERROR{classname}|{callingMethodName}|{loadingProgramPath}|{interpretationMessage}");
+
+ var logger = LogManager.GetLogger("");
+
+ var innerExceptionNumber = 1;
+
+ var possibleResolution = "Not yet known";
+ var errorStatus = "UNKNOWN";
+
+ logger.Error("------------- BEGIN Wox.Plugin.Program exception -------------");
+
+ do
+ {
+ if (IsKnownWinProgramError(e, callingMethodName) || IsKnownUWPProgramError(e, callingMethodName))
+ {
+ possibleResolution = "Can be ignored and Wox should still continue, however the program may not be loaded";
+ errorStatus = "KNOWN";
+ }
+
+ var calledMethod = e.TargetSite != null ? e.TargetSite.ToString() : e.StackTrace;
+
+ calledMethod = string.IsNullOrEmpty(calledMethod) ? "Not available" : calledMethod;
+
+ logger.Error($"\nException full name: {e.GetType().FullName}"
+ + $"\nError status: {errorStatus}"
+ + $"\nClass name: {classname}"
+ + $"\nCalling method: {callingMethodName}"
+ + $"\nProgram path: {loadingProgramPath}"
+ + $"\nInnerException number: {innerExceptionNumber}"
+ + $"\nException message: {e.Message}"
+ + $"\nException error type: HResult {e.HResult}"
+ + $"\nException thrown in called method: {calledMethod}"
+ + $"\nPossible interpretation of the error: {interpretationMessage}"
+ + $"\nPossible resolution: {possibleResolution}");
+
+ innerExceptionNumber++;
+ e = e.InnerException;
+ } while (e != null);
+
+ logger.Error("------------- END Wox.Plugin.Program exception -------------");
+ }
+
+ ///
+ /// Please follow exception format: |class name|calling method name|loading program path|user friendly message that explains the error
+ /// => Example: |Win32|LnkProgram|c:\..\chrome.exe|Permission denied on directory, but Wox should continue
+ ///
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ internal static void LogException(string message, Exception e)
+ {
+ //Index 0 is always empty.
+ var parts = message.Split('|');
+ if (parts.Length < 4)
+ {
+ var logger = LogManager.GetLogger("");
+ logger.Error(e, $"fail to log exception in program logger, parts length is too small: {parts.Length}, message: {message}");
+ }
+
+ var classname = parts[1];
+ var callingMethodName = parts[2];
+ var loadingProgramPath = parts[3];
+ var interpretationMessage = parts[4];
+
+ LogException(classname, callingMethodName, loadingProgramPath, interpretationMessage, e);
+ }
+
+ private static bool IsKnownWinProgramError(Exception e, string callingMethodName)
+ {
+ if (e.TargetSite?.Name == "GetDescription" && callingMethodName == "LnkProgram")
+ return true;
+
+ if (e is SecurityException || e is UnauthorizedAccessException || e is DirectoryNotFoundException)
+ return true;
+
+ return false;
+ }
+
+ private static bool IsKnownUWPProgramError(Exception e, string callingMethodName)
+ {
+ if (((e.HResult == -2147024774 || e.HResult == -2147009769) && callingMethodName == "ResourceFromPri")
+ || (e.HResult == -2147024894 && (callingMethodName == "LogoPathFromUri" || callingMethodName == "ImageFromPath"))
+ || (e.HResult == -2147024864 && callingMethodName == "InitializeAppInfo"))
+ return true;
+
+ if (callingMethodName == "XmlNamespaces")
+ return true;
+
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Main.cs b/src/modules/launcher/Plugins/Wox.Plugin.Program/Main.cs
new file mode 100644
index 0000000000..444e6525e8
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/Main.cs
@@ -0,0 +1,235 @@
+using Microsoft.PowerToys.Settings.UI.Lib;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Timers;
+using System.Windows.Controls;
+using Wox.Infrastructure.Logger;
+using Wox.Infrastructure.Storage;
+using Wox.Plugin.Program.Programs;
+using Wox.Plugin.Program.Views;
+using Stopwatch = Wox.Infrastructure.Stopwatch;
+
+namespace Wox.Plugin.Program
+{
+ public class Main : ISettingProvider, IPlugin, IPluginI18n, IContextMenu, ISavable, IReloadable
+ {
+ private static readonly object IndexLock = new object();
+ internal static Win32[] _win32s { get; set; }
+ internal static UWP.Application[] _uwps { get; set; }
+ internal static Settings _settings { get; set; }
+
+ FileSystemWatcher _watcher = null;
+ System.Timers.Timer _timer = null;
+
+ private static bool IsStartupIndexProgramsRequired => _settings.LastIndexTime.AddDays(3) < DateTime.Today;
+
+ private static PluginInitContext _context;
+
+ private static BinaryStorage _win32Storage;
+ private static BinaryStorage _uwpStorage;
+ private readonly PluginJsonStorage _settingsStorage;
+
+ public Main()
+ {
+ _settingsStorage = new PluginJsonStorage();
+ _settings = _settingsStorage.Load();
+
+ Stopwatch.Normal("|Wox.Plugin.Program.Main|Preload programs cost", () =>
+ {
+ _win32Storage = new BinaryStorage("Win32");
+ _win32s = _win32Storage.TryLoad(new Win32[] { });
+ _uwpStorage = new BinaryStorage("UWP");
+ _uwps = _uwpStorage.TryLoad(new UWP.Application[] { });
+ });
+ Log.Info($"|Wox.Plugin.Program.Main|Number of preload win32 programs <{_win32s.Length}>");
+ Log.Info($"|Wox.Plugin.Program.Main|Number of preload uwps <{_uwps.Length}>");
+
+ var a = Task.Run(() =>
+ {
+ if (IsStartupIndexProgramsRequired || !_win32s.Any())
+ Stopwatch.Normal("|Wox.Plugin.Program.Main|Win32Program index cost", IndexWin32Programs);
+ });
+
+ var b = Task.Run(() =>
+ {
+ if (IsStartupIndexProgramsRequired || !_uwps.Any())
+ Stopwatch.Normal("|Wox.Plugin.Program.Main|Win32Program index cost", IndexUWPPrograms);
+ });
+
+ Task.WaitAll(a, b);
+
+ _settings.LastIndexTime = DateTime.Today;
+
+ InitializeFileWatchers();
+ InitializeTimer();
+ }
+
+ public void Save()
+ {
+ _settingsStorage.Save();
+ _win32Storage.Save(_win32s);
+ _uwpStorage.Save(_uwps);
+ }
+
+ public List Query(Query query)
+ {
+ Win32[] win32;
+ UWP.Application[] uwps;
+
+ lock (IndexLock)
+ {
+ // just take the reference inside the lock to eliminate query time issues.
+ win32 = _win32s;
+ uwps = _uwps;
+ }
+
+ var results1 = win32.AsParallel()
+ .Where(p => p.Enabled)
+ .Select(p => p.Result(query.Search, _context.API));
+
+ var results2 = uwps.AsParallel()
+ .Where(p => p.Enabled)
+ .Select(p => p.Result(query.Search, _context.API));
+
+ var result = results1.Concat(results2).Where(r => r != null && r.Score > 0).ToList();
+ return result;
+ }
+
+ public void Init(PluginInitContext context)
+ {
+ _context = context;
+ }
+
+ public static void IndexWin32Programs()
+ {
+ var win32S = Win32.All(_settings);
+ lock (IndexLock)
+ {
+ _win32s = win32S;
+ }
+ }
+
+ public static void IndexUWPPrograms()
+ {
+ var windows10 = new Version(10, 0);
+ var support = Environment.OSVersion.Version.Major >= windows10.Major;
+
+ var applications = support ? UWP.All() : new UWP.Application[] { };
+ lock (IndexLock)
+ {
+ _uwps = applications;
+ }
+ }
+
+ public static void IndexPrograms()
+ {
+ var t1 = Task.Run(() => IndexWin32Programs());
+ var t2 = Task.Run(() => IndexUWPPrograms());
+
+ Task.WaitAll(t1, t2);
+
+ _settings.LastIndexTime = DateTime.Today;
+ }
+
+ public Control CreateSettingPanel()
+ {
+ return new ProgramSetting(_context, _settings, _win32s, _uwps);
+ }
+
+ public string GetTranslatedPluginTitle()
+ {
+ return _context.API.GetTranslation("wox_plugin_program_plugin_name");
+ }
+
+ public string GetTranslatedPluginDescription()
+ {
+ return _context.API.GetTranslation("wox_plugin_program_plugin_description");
+ }
+
+ public List LoadContextMenus(Result selectedResult)
+ {
+ var menuOptions = new List();
+ var program = selectedResult.ContextData as IProgram;
+ if (program != null)
+ {
+ menuOptions = program.ContextMenus(_context.API);
+ }
+
+ return menuOptions;
+ }
+
+ public static void StartProcess(Func runProcess, ProcessStartInfo info)
+ {
+ try
+ {
+ runProcess(info);
+ }
+ catch (Exception)
+ {
+ var name = "Plugin: Program";
+ var message = $"Unable to start: {info.FileName}";
+ _context.API.ShowMsg(name, message, string.Empty);
+ }
+ }
+
+ public void ReloadData()
+ {
+ IndexPrograms();
+ }
+
+ public void UpdateSettings(PowerLauncherSettings settings)
+ {
+ }
+ void InitializeFileWatchers()
+ {
+ // Create a new FileSystemWatcher and set its properties.
+ _watcher = new FileSystemWatcher();
+ var resolvedPath = Environment.ExpandEnvironmentVariables("%ProgramFiles%");
+ _watcher.Path = resolvedPath;
+
+ //Filter to create and deletes of 'microsoft.system.package.metadata' directories.
+ _watcher.NotifyFilter = NotifyFilters.DirectoryName | NotifyFilters.FileName;
+ _watcher.IncludeSubdirectories = true;
+
+ // Add event handlers.
+ _watcher.Created += OnChanged;
+ _watcher.Deleted += OnChanged;
+
+ // Begin watching.
+ _watcher.EnableRaisingEvents = true;
+ }
+
+ void InitializeTimer()
+ {
+ //multiple file writes occur on install / unistall. Adding a delay before actually indexing.
+ var delayInterval = 5000;
+ _timer = new System.Timers.Timer(delayInterval);
+ _timer.Enabled = true;
+ _timer.AutoReset = false;
+ _timer.Elapsed += FileWatchElapsedTimer;
+ _timer.Stop();
+ }
+
+ //When a watched directory changes then reset the timer.
+ private void OnChanged(object source, FileSystemEventArgs e)
+ {
+ Log.Debug($"|Wox.Plugin.Program.Main|Directory Changed: {e.FullPath} {e.ChangeType} - Resetting timer.");
+ _timer.Stop();
+ _timer.Start();
+ }
+
+ private void FileWatchElapsedTimer(object sender, ElapsedEventArgs e)
+ {
+ Task.Run(() =>
+ {
+ Log.Debug($"|Wox.Plugin.Program.Main| ReIndexing UWP Programs");
+ IndexUWPPrograms();
+ Log.Debug($"|Wox.Plugin.Program.Main| Done ReIndexing");
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/ProgramSuffixes.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Program/ProgramSuffixes.xaml
new file mode 100644
index 0000000000..03d399a197
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/ProgramSuffixes.xaml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/ProgramSuffixes.xaml.cs b/src/modules/launcher/Plugins/Wox.Plugin.Program/ProgramSuffixes.xaml.cs
new file mode 100644
index 0000000000..ef5783b469
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/ProgramSuffixes.xaml.cs
@@ -0,0 +1,37 @@
+using System.Windows;
+
+namespace Wox.Plugin.Program
+{
+ ///
+ /// ProgramSuffixes.xaml 的交互逻辑
+ ///
+ public partial class ProgramSuffixes
+ {
+ private PluginInitContext context;
+ private Settings _settings;
+
+ public ProgramSuffixes(PluginInitContext context, Settings settings)
+ {
+ this.context = context;
+ InitializeComponent();
+ _settings = settings;
+ tbSuffixes.Text = string.Join(Settings.SuffixSeperator.ToString(), _settings.ProgramSuffixes);
+ }
+
+ private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
+ {
+ if (string.IsNullOrEmpty(tbSuffixes.Text))
+ {
+ string warning = context.API.GetTranslation("wox_plugin_program_suffixes_cannot_empty");
+ MessageBox.Show(warning);
+ return;
+ }
+
+ _settings.ProgramSuffixes = tbSuffixes.Text.Split(Settings.SuffixSeperator);
+ string msg = context.API.GetTranslation("wox_plugin_program_update_file_suffixes");
+ MessageBox.Show(msg);
+
+ DialogResult = true;
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Programs/IProgram.cs b/src/modules/launcher/Plugins/Wox.Plugin.Program/Programs/IProgram.cs
new file mode 100644
index 0000000000..e183ecaad9
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/Programs/IProgram.cs
@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+
+namespace Wox.Plugin.Program.Programs
+{
+ public interface IProgram
+ {
+ List ContextMenus(IPublicAPI api);
+ Result Result(string query, IPublicAPI api);
+ string UniqueIdentifier { get; set; }
+ string Name { get; }
+ string Location { get; }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Programs/UWP.cs b/src/modules/launcher/Plugins/Wox.Plugin.Program/Programs/UWP.cs
new file mode 100644
index 0000000000..3b783fcda5
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/Programs/UWP.cs
@@ -0,0 +1,570 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Security.Principal;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Linq;
+using Windows.ApplicationModel;
+using Windows.Management.Deployment;
+using AppxPackaing;
+using Shell;
+using Wox.Infrastructure;
+using Wox.Plugin.Program.Logger;
+using IStream = AppxPackaing.IStream;
+using Rect = System.Windows.Rect;
+using Windows.UI.Xaml.Media.Imaging;
+using Windows.UI.Xaml.Media;
+using System.Windows.Controls;
+
+namespace Wox.Plugin.Program.Programs
+{
+ [Serializable]
+ public class UWP
+ {
+ public string Name { get; }
+ public string FullName { get; }
+ public string FamilyName { get; }
+ public string Location { get; set; }
+
+ public Application[] Apps { get; set; }
+
+ public PackageVersion Version { get; set; }
+
+ public UWP(Package package)
+ {
+ Location = package.InstalledLocation.Path;
+ Name = package.Id.Name;
+ FullName = package.Id.FullName;
+ FamilyName = package.Id.FamilyName;
+ InitializeAppInfo();
+ Apps = Apps.Where(a =>
+ {
+ var valid =
+ !string.IsNullOrEmpty(a.UserModelId) &&
+ !string.IsNullOrEmpty(a.DisplayName);
+ return valid;
+ }).ToArray();
+ }
+
+ private void InitializeAppInfo()
+ {
+ var path = Path.Combine(Location, "AppxManifest.xml");
+
+ var namespaces = XmlNamespaces(path);
+ InitPackageVersion(namespaces);
+
+ var appxFactory = new AppxFactory();
+ IStream stream;
+ const uint noAttribute = 0x80;
+ const Stgm exclusiveRead = Stgm.Read | Stgm.ShareExclusive;
+ var hResult = SHCreateStreamOnFileEx(path, exclusiveRead, noAttribute, false, null, out stream);
+
+ if (hResult == Hresult.Ok)
+ {
+ var reader = appxFactory.CreateManifestReader(stream);
+ var manifestApps = reader.GetApplications();
+ var apps = new List();
+ while (manifestApps.GetHasCurrent() != 0)
+ {
+ var manifestApp = manifestApps.GetCurrent();
+ var appListEntry = manifestApp.GetStringValue("AppListEntry");
+ if (appListEntry != "none")
+ {
+ var app = new Application(manifestApp, this);
+ apps.Add(app);
+ }
+ manifestApps.MoveNext();
+ }
+ Apps = apps.Where(a => a.AppListEntry != "none").ToArray();
+ }
+ else
+ {
+ var e = Marshal.GetExceptionForHR((int)hResult);
+ ProgramLogger.LogException($"|UWP|InitializeAppInfo|{path}" +
+ "|Error caused while trying to get the details of the UWP program", e);
+
+ Apps = new List().ToArray();
+ }
+ }
+
+
+
+ /// http://www.hanselman.com/blog/GetNamespacesFromAnXMLDocumentWithXPathDocumentAndLINQToXML.aspx
+ private string[] XmlNamespaces(string path)
+ {
+ XDocument z = XDocument.Load(path);
+ if (z.Root != null)
+ {
+ var namespaces = z.Root.Attributes().
+ Where(a => a.IsNamespaceDeclaration).
+ GroupBy(
+ a => a.Name.Namespace == XNamespace.None ? string.Empty : a.Name.LocalName,
+ a => XNamespace.Get(a.Value)
+ ).Select(
+ g => g.First().ToString()
+ ).ToArray();
+ return namespaces;
+ }
+ else
+ {
+ ProgramLogger.LogException($"|UWP|XmlNamespaces|{path}" +
+ $"|Error occured while trying to get the XML from {path}", new ArgumentNullException());
+
+ return new string[] { };
+ }
+ }
+
+ private void InitPackageVersion(string[] namespaces)
+ {
+ var versionFromNamespace = new Dictionary
+ {
+ {"http://schemas.microsoft.com/appx/manifest/foundation/windows10", PackageVersion.Windows10},
+ {"http://schemas.microsoft.com/appx/2013/manifest", PackageVersion.Windows81},
+ {"http://schemas.microsoft.com/appx/2010/manifest", PackageVersion.Windows8},
+ };
+
+ foreach (var n in versionFromNamespace.Keys)
+ {
+ if (namespaces.Contains(n))
+ {
+ Version = versionFromNamespace[n];
+ return;
+ }
+ }
+
+ ProgramLogger.LogException($"|UWP|XmlNamespaces|{Location}" +
+ "|Trying to get the package version of the UWP program, but a unknown UWP appmanifest version "
+ + $"{FullName} from location {Location} is returned.", new FormatException());
+
+ Version = PackageVersion.Unknown;
+ }
+
+ public static Application[] All()
+ {
+ var windows10 = new Version(10, 0);
+ var support = Environment.OSVersion.Version.Major >= windows10.Major;
+ if (support)
+ {
+ var applications = CurrentUserPackages().AsParallel().SelectMany(p =>
+ {
+ UWP u;
+ try
+ {
+ u = new UWP(p);
+ }
+#if !DEBUG
+ catch (Exception e)
+ {
+ ProgramLogger.LogException($"|UWP|All|{p.InstalledLocation}|An unexpected error occured and "
+ + $"unable to convert Package to UWP for {p.Id.FullName}", e);
+ return new Application[] { };
+ }
+#endif
+#if DEBUG //make developer aware and implement handling
+ catch
+ {
+ throw;
+ }
+#endif
+ return u.Apps;
+ }).ToArray();
+
+ var updatedListWithoutDisabledApps = applications
+ .Where(t1 => !Main._settings.DisabledProgramSources
+ .Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
+ .Select(x => x);
+
+ return updatedListWithoutDisabledApps.ToArray();
+ }
+ else
+ {
+ return new Application[] { };
+ }
+ }
+
+ private static IEnumerable CurrentUserPackages()
+ {
+ var u = WindowsIdentity.GetCurrent().User;
+
+ if (u != null)
+ {
+ var id = u.Value;
+ var m = new PackageManager();
+ var ps = m.FindPackagesForUser(id);
+ ps = ps.Where(p =>
+ {
+ bool valid;
+ try
+ {
+ var f = p.IsFramework;
+ var d = p.IsDevelopmentMode;
+ var path = p.InstalledLocation.Path;
+ valid = !f && !d && !string.IsNullOrEmpty(path);
+ }
+ catch (Exception e)
+ {
+ ProgramLogger.LogException("UWP" ,"CurrentUserPackages", $"id","An unexpected error occured and "
+ + $"unable to verify if package is valid", e);
+ return false;
+ }
+
+
+ return valid;
+ });
+ return ps;
+ }
+ else
+ {
+ return new Package[] { };
+ }
+ }
+
+ public override string ToString()
+ {
+ return FamilyName;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is UWP uwp)
+ {
+ return FamilyName.Equals(uwp.FamilyName);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public override int GetHashCode()
+ {
+ return FamilyName.GetHashCode();
+ }
+
+ [Serializable]
+ public class Application : IProgram
+ {
+ public string AppListEntry { get; set; }
+ public string UniqueIdentifier { get; set; }
+ public string DisplayName { get; set; }
+ public string Description { get; set; }
+ public string UserModelId { get; set; }
+ public string BackgroundColor { get; set; }
+
+ public string Name => DisplayName;
+ public string Location => Package.Location;
+
+ public bool Enabled { get; set; }
+
+ public string LogoUri { get; set; }
+ public string LogoPath { get; set; }
+ public UWP Package { get; set; }
+
+ private int Score(string query)
+ {
+ var displayNameMatch = StringMatcher.FuzzySearch(query, DisplayName);
+ var descriptionMatch = StringMatcher.FuzzySearch(query, Description);
+ var score = new[] { displayNameMatch.Score, descriptionMatch.Score }.Max();
+ return score;
+ }
+
+ public Result Result(string query, IPublicAPI api)
+ {
+ var score = Score(query);
+ if (score <= 0)
+ { // no need to create result if score is 0
+ return null;
+ }
+
+ var result = new Result
+ {
+ SubTitle = "UWP application",
+ Icon = Logo,
+ Score = score,
+ ContextData = this,
+ Action = e =>
+ {
+ Launch(api);
+ return true;
+ }
+ };
+
+ if (Description.Length >= DisplayName.Length &&
+ Description.Substring(0, DisplayName.Length) == DisplayName)
+ {
+ result.Title = Description;
+ result.TitleHighlightData = StringMatcher.FuzzySearch(query, Description).MatchData;
+ }
+ else
+ {
+ result.Title = DisplayName;
+ result.TitleHighlightData = StringMatcher.FuzzySearch(query, DisplayName).MatchData;
+ }
+ return result;
+ }
+
+ public List ContextMenus(IPublicAPI api)
+ {
+ var contextMenus = new List
+ {
+ new ContextMenuResult
+ {
+ Title = api.GetTranslation("wox_plugin_program_open_containing_folder"),
+ Glyph = "\xE838",
+ FontFamily = "Segoe MDL2 Assets",
+ AcceleratorKey = "E",
+ AcceleratorModifiers = "Control,Shift",
+ Action = _ =>
+ {
+ Main.StartProcess(Process.Start, new ProcessStartInfo("explorer", Package.Location));
+
+ return true;
+ }
+ }
+ };
+ return contextMenus;
+ }
+
+ private async void Launch(IPublicAPI api)
+ {
+ var appManager = new ApplicationActivationManager();
+ uint unusedPid;
+ const string noArgs = "";
+ const ACTIVATEOPTIONS noFlags = ACTIVATEOPTIONS.AO_NONE;
+ await Task.Run(() =>
+ {
+ try
+ {
+ appManager.ActivateApplication(UserModelId, noArgs, noFlags, out unusedPid);
+ }
+ catch (Exception)
+ {
+ var name = "Plugin: Program";
+ var message = $"Can't start UWP: {DisplayName}";
+ api.ShowMsg(name, message, string.Empty);
+ }
+ });
+ }
+
+ public Application(IAppxManifestApplication manifestApp, UWP package)
+ {
+ UserModelId = manifestApp.GetAppUserModelId();
+ UniqueIdentifier = manifestApp.GetAppUserModelId();
+ DisplayName = manifestApp.GetStringValue("DisplayName");
+ Description = manifestApp.GetStringValue("Description");
+ BackgroundColor = manifestApp.GetStringValue("BackgroundColor");
+ Package = package;
+
+ DisplayName = ResourceFromPri(package.FullName, DisplayName);
+ Description = ResourceFromPri(package.FullName, Description);
+ LogoUri = LogoUriFromManifest(manifestApp);
+ LogoPath = LogoPathFromUri(LogoUri);
+
+ Enabled = true;
+ }
+
+ internal string ResourceFromPri(string packageFullName, string resourceReference)
+ {
+ const string prefix = "ms-resource:";
+ if (!string.IsNullOrWhiteSpace(resourceReference) && resourceReference.StartsWith(prefix))
+ {
+ // magic comes from @talynone
+ // https://github.com/talynone/Wox.Plugin.WindowsUniversalAppLauncher/blob/master/StoreAppLauncher/Helpers/NativeApiHelper.cs#L139-L153
+ string key = resourceReference.Substring(prefix.Length);
+ string parsed;
+ if (key.StartsWith("//"))
+ {
+ parsed = prefix + key;
+ }
+ else if (key.StartsWith("/"))
+ {
+ parsed = prefix + "//" + key;
+ }
+ else
+ {
+ parsed = prefix + "///resources/" + key;
+ }
+
+ var outBuffer = new StringBuilder(128);
+ string source = $"@{{{packageFullName}? {parsed}}}";
+ var capacity = (uint)outBuffer.Capacity;
+ var hResult = SHLoadIndirectString(source, outBuffer, capacity, IntPtr.Zero);
+ if (hResult == Hresult.Ok)
+ {
+ var loaded = outBuffer.ToString();
+ if (!string.IsNullOrEmpty(loaded))
+ {
+ return loaded;
+ }
+ else
+ {
+ ProgramLogger.LogException($"|UWP|ResourceFromPri|{Package.Location}|Can't load null or empty result "
+ + $"pri {source} in uwp location {Package.Location}", new NullReferenceException());
+ return string.Empty;
+ }
+ }
+ else
+ {
+ // https://github.com/Wox-launcher/Wox/issues/964
+ // known hresult 2147942522:
+ // 'Microsoft Corporation' violates pattern constraint of '\bms-resource:.{1,256}'.
+ // for
+ // Microsoft.MicrosoftOfficeHub_17.7608.23501.0_x64__8wekyb3d8bbwe: ms-resource://Microsoft.MicrosoftOfficeHub/officehubintl/AppManifest_GetOffice_Description
+ // Microsoft.BingFoodAndDrink_3.0.4.336_x64__8wekyb3d8bbwe: ms-resource:AppDescription
+ var e = Marshal.GetExceptionForHR((int)hResult);
+ ProgramLogger.LogException($"|UWP|ResourceFromPri|{Package.Location}|Load pri failed {source} with HResult {hResult} and location {Package.Location}", e);
+ return string.Empty;
+ }
+ }
+ else
+ {
+ return resourceReference;
+ }
+ }
+
+
+ internal string LogoUriFromManifest(IAppxManifestApplication app)
+ {
+ var logoKeyFromVersion = new Dictionary
+ {
+ { PackageVersion.Windows10, "Square44x44Logo" },
+ { PackageVersion.Windows81, "Square30x30Logo" },
+ { PackageVersion.Windows8, "SmallLogo" },
+ };
+ if (logoKeyFromVersion.ContainsKey(Package.Version))
+ {
+ var key = logoKeyFromVersion[Package.Version];
+ var logoUri = app.GetStringValue(key);
+ return logoUri;
+ }
+ else
+ {
+ return string.Empty;
+ }
+ }
+
+ internal string LogoPathFromUri(string uri)
+ {
+ // all https://msdn.microsoft.com/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets
+ // windows 10 https://msdn.microsoft.com/en-us/library/windows/apps/dn934817.aspx
+ // windows 8.1 https://msdn.microsoft.com/en-us/library/windows/apps/hh965372.aspx#target_size
+ // windows 8 https://msdn.microsoft.com/en-us/library/windows/apps/br211475.aspx
+
+ string path;
+ if (uri.Contains("\\"))
+ {
+ path = Path.Combine(Package.Location, uri);
+ }
+ else
+ {
+ // for C:\Windows\MiracastView etc
+ path = Path.Combine(Package.Location, "Assets", uri);
+ }
+
+ var extension = Path.GetExtension(path);
+ if (extension != null)
+ {
+ var end = path.Length - extension.Length;
+ var prefix = path.Substring(0, end);
+ var paths = new List { path };
+
+ var scaleFactors = new Dictionary>
+ {
+ // scale factors on win10: https://docs.microsoft.com/en-us/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets#asset-size-tables,
+ { PackageVersion.Windows10, new List { 100, 125, 150, 200, 400 } },
+ { PackageVersion.Windows81, new List { 100, 120, 140, 160, 180 } },
+ { PackageVersion.Windows8, new List { 100 } }
+ };
+
+ if (scaleFactors.ContainsKey(Package.Version))
+ {
+ foreach (var factor in scaleFactors[Package.Version])
+ {
+ paths.Add($"{prefix}.scale-{factor}{extension}");
+ }
+ }
+
+ var selected = paths.FirstOrDefault(File.Exists);
+ if (!string.IsNullOrEmpty(selected))
+ {
+ return selected;
+ }
+ else
+ {
+ ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Package.Location}" +
+ $"|{UserModelId} can't find logo uri for {uri} in package location: {Package.Location}", new FileNotFoundException());
+ return string.Empty;
+ }
+ }
+ else
+ {
+ ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Package.Location}" +
+ $"|Unable to find extension from {uri} for {UserModelId} " +
+ $"in package location {Package.Location}", new FileNotFoundException());
+ return string.Empty;
+ }
+ }
+
+
+ public ImageSource Logo()
+ {
+ var logo = ImageFromPath(LogoPath);
+ return logo;
+ }
+
+
+ private BitmapImage ImageFromPath(string path)
+ {
+ if (File.Exists(path))
+ {
+ var image = new BitmapImage(new Uri(path));
+ return image;
+ }
+ else
+ {
+ ProgramLogger.LogException($"|UWP|ImageFromPath|{path}" +
+ $"|Unable to get logo for {UserModelId} from {path} and" +
+ $" located in {Package.Location}", new FileNotFoundException());
+ return new BitmapImage(new Uri(Constant.ErrorIcon));
+ }
+ }
+
+ public override string ToString()
+ {
+ return $"{DisplayName}: {Description}";
+ }
+ }
+
+ public enum PackageVersion
+ {
+ Windows10,
+ Windows81,
+ Windows8,
+ Unknown
+ }
+
+ [Flags]
+ private enum Stgm : uint
+ {
+ Read = 0x0,
+ ShareExclusive = 0x10,
+ }
+
+ private enum Hresult : uint
+ {
+ Ok = 0x0000,
+ }
+
+ [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
+ private static extern Hresult SHCreateStreamOnFileEx(string fileName, Stgm grfMode, uint attributes, bool create,
+ IStream reserved, out IStream stream);
+
+ [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
+ private static extern Hresult SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf,
+ IntPtr ppvReserved);
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Programs/Win32.cs b/src/modules/launcher/Plugins/Wox.Plugin.Program/Programs/Win32.cs
new file mode 100644
index 0000000000..6ac1ebdff2
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/Programs/Win32.cs
@@ -0,0 +1,500 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Win32;
+using Shell;
+using Wox.Infrastructure;
+using Wox.Plugin.Program.Logger;
+
+namespace Wox.Plugin.Program.Programs
+{
+ [Serializable]
+ public class Win32 : IProgram
+ {
+ public string Name { get; set; }
+ public string UniqueIdentifier { get; set; }
+ public string IcoPath { get; set; }
+ public string FullPath { get; set; }
+ public string LnkResolvedPath { get; set; }
+ public string ParentDirectory { get; set; }
+ public string ExecutableName { get; set; }
+ public string Description { get; set; }
+ public bool Valid { get; set; }
+ public bool Enabled { get; set; }
+ public string Location => ParentDirectory;
+
+ private const string ShortcutExtension = "lnk";
+ private const string ApplicationReferenceExtension = "appref-ms";
+ private const string ExeExtension = "exe";
+
+ private int Score(string query)
+ {
+ var nameMatch = StringMatcher.FuzzySearch(query, Name);
+ var descriptionMatch = StringMatcher.FuzzySearch(query, Description);
+ var executableNameMatch = StringMatcher.FuzzySearch(query, ExecutableName);
+ var score = new[] { nameMatch.Score, descriptionMatch.Score, executableNameMatch.Score }.Max();
+ return score;
+ }
+
+
+ public Result Result(string query, IPublicAPI api)
+ {
+ var score = Score(query);
+ if (score <= 0)
+ { // no need to create result if this is zero
+ return null;
+ }
+
+ var result = new Result
+ {
+ SubTitle = "Win32 application",
+ IcoPath = IcoPath,
+ Score = score,
+ ContextData = this,
+ Action = e =>
+ {
+ var info = new ProcessStartInfo
+ {
+ FileName = FullPath,
+ WorkingDirectory = ParentDirectory,
+ UseShellExecute = true
+ };
+
+ Main.StartProcess(Process.Start, info);
+
+ return true;
+ }
+ };
+
+ if (Description.Length >= Name.Length &&
+ Description.Substring(0, Name.Length) == Name)
+ {
+ result.Title = Description;
+ result.TitleHighlightData = StringMatcher.FuzzySearch(query, Description).MatchData;
+ }
+ else
+ {
+ result.Title = Name;
+ result.TitleHighlightData = StringMatcher.FuzzySearch(query, Name).MatchData;
+ }
+
+ return result;
+ }
+
+
+ public List ContextMenus(IPublicAPI api)
+ {
+ var contextMenus = new List
+ {
+ new ContextMenuResult
+ {
+ Title = api.GetTranslation("wox_plugin_program_run_as_administrator"),
+ Glyph = "\xE7EF",
+ FontFamily = "Segoe MDL2 Assets",
+ AcceleratorKey = "Enter",
+ AcceleratorModifiers = "Control,Shift",
+ Action = _ =>
+ {
+ var info = new ProcessStartInfo
+ {
+ FileName = FullPath,
+ WorkingDirectory = ParentDirectory,
+ Verb = "runas",
+ UseShellExecute = true
+ };
+
+ Task.Run(() => Main.StartProcess(Process.Start, info));
+
+ return true;
+ }
+ },
+ new ContextMenuResult
+ {
+ Title = api.GetTranslation("wox_plugin_program_open_containing_folder"),
+ Glyph = "\xE838",
+ FontFamily = "Segoe MDL2 Assets",
+ AcceleratorKey = "E",
+ AcceleratorModifiers = "Control,Shift",
+ Action = _ =>
+ {
+
+
+ Main.StartProcess(Process.Start, new ProcessStartInfo("explorer", ParentDirectory));
+
+ return true;
+ }
+ }
+ };
+ return contextMenus;
+ }
+
+
+
+ public override string ToString()
+ {
+ return ExecutableName;
+ }
+
+ private static Win32 Win32Program(string path)
+ {
+ try
+ {
+ var p = new Win32
+ {
+ Name = Path.GetFileNameWithoutExtension(path),
+ ExecutableName = Path.GetFileName(path),
+ IcoPath = path,
+ FullPath = path.ToLower(),
+ UniqueIdentifier = path,
+ ParentDirectory = Directory.GetParent(path).FullName,
+ Description = string.Empty,
+ Valid = true,
+ Enabled = true
+ };
+ return p;
+ }
+ catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
+ {
+ ProgramLogger.LogException($"|Win32|Win32Program|{path}" +
+ $"|Permission denied when trying to load the program from {path}", e);
+
+ return new Win32() { Valid = false, Enabled = false };
+ }
+ }
+
+ private static Win32 LnkProgram(string path)
+ {
+ var program = Win32Program(path);
+ try
+ {
+ var link = new ShellLink();
+ const uint STGM_READ = 0;
+ ((IPersistFile)link).Load(path, STGM_READ);
+ var hwnd = new _RemotableHandle();
+ link.Resolve(ref hwnd, 0);
+
+ const int MAX_PATH = 260;
+ StringBuilder buffer = new StringBuilder(MAX_PATH);
+
+ var data = new _WIN32_FIND_DATAW();
+ const uint SLGP_SHORTPATH = 1;
+ link.GetPath(buffer, buffer.Capacity, ref data, SLGP_SHORTPATH);
+ var target = buffer.ToString();
+ if (!string.IsNullOrEmpty(target))
+ {
+ var extension = Extension(target);
+ if (extension == ExeExtension && File.Exists(target))
+ {
+ program.LnkResolvedPath = program.FullPath;
+ program.FullPath = Path.GetFullPath(target).ToLower();
+ program.ExecutableName = Path.GetFileName(target);
+
+ buffer = new StringBuilder(MAX_PATH);
+ link.GetDescription(buffer, MAX_PATH);
+ var description = buffer.ToString();
+ if (!string.IsNullOrEmpty(description))
+ {
+ program.Description = description;
+ }
+ else
+ {
+ var info = FileVersionInfo.GetVersionInfo(target);
+ if (!string.IsNullOrEmpty(info.FileDescription))
+ {
+ program.Description = info.FileDescription;
+ }
+ }
+ }
+ }
+ return program;
+ }
+ catch (COMException e)
+ {
+ // C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\MiracastView.lnk always cause exception
+ ProgramLogger.LogException($"|Win32|LnkProgram|{path}" +
+ "|Error caused likely due to trying to get the description of the program", e);
+
+ program.Valid = false;
+ return program;
+ }
+#if !DEBUG //Only do a catch all in production. This is so make developer aware of any unhandled exception and add the exception handling in.
+ catch (Exception e)
+ {
+ ProgramLogger.LogException($"|Win32|LnkProgram|{path}" +
+ "|An unexpected error occurred in the calling method LnkProgram", e);
+
+ program.Valid = false;
+ return program;
+ }
+#endif
+ }
+
+ private static Win32 ExeProgram(string path)
+ {
+ try
+ {
+ var program = Win32Program(path);
+ var info = FileVersionInfo.GetVersionInfo(path);
+
+ if (!string.IsNullOrEmpty(info.FileDescription))
+ {
+ program.Description = info.FileDescription;
+ }
+
+ return program;
+ }
+ catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
+ {
+ ProgramLogger.LogException($"|Win32|ExeProgram|{path}" +
+ $"|Permission denied when trying to load the program from {path}", e);
+
+ return new Win32() { Valid = false, Enabled = false };
+ }
+ }
+
+ private static IEnumerable ProgramPaths(string directory, string[] suffixes)
+ {
+ if (!Directory.Exists(directory))
+ {
+ return new string[] { };
+ }
+
+ var files = new List();
+ var folderQueue = new Queue();
+ folderQueue.Enqueue(directory);
+
+ do
+ {
+ var currentDirectory = folderQueue.Dequeue();
+ try
+ {
+ foreach (var suffix in suffixes)
+ {
+ try
+ {
+ files.AddRange(Directory.EnumerateFiles(currentDirectory, $"*.{suffix}", SearchOption.TopDirectoryOnly));
+ }
+ catch (DirectoryNotFoundException e)
+ {
+ ProgramLogger.LogException($"|Win32|ProgramPaths|{currentDirectory}" +
+ "|The directory trying to load the program from does not exist", e);
+ }
+ }
+ }
+ catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
+ {
+ ProgramLogger.LogException($"|Win32|ProgramPaths|{currentDirectory}" +
+ $"|Permission denied when trying to load programs from {currentDirectory}", e);
+ }
+
+ try
+ {
+ foreach (var childDirectory in Directory.EnumerateDirectories(currentDirectory, "*", SearchOption.TopDirectoryOnly))
+ {
+ folderQueue.Enqueue(childDirectory);
+ }
+ }
+ catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
+ {
+ ProgramLogger.LogException($"|Win32|ProgramPaths|{currentDirectory}" +
+ $"|Permission denied when trying to load programs from {currentDirectory}", e);
+ }
+ } while (folderQueue.Any());
+
+ return files;
+ }
+
+ private static string Extension(string path)
+ {
+ var extension = Path.GetExtension(path)?.ToLower();
+
+ if (!string.IsNullOrEmpty(extension))
+ {
+ return extension.Substring(1);
+ }
+ else
+ {
+ return string.Empty;
+ }
+ }
+
+ private static ParallelQuery UnregisteredPrograms(List sources, string[] suffixes)
+ {
+ var listToAdd = new List();
+ sources.Where(s => Directory.Exists(s.Location) && s.Enabled)
+ .SelectMany(s => ProgramPaths(s.Location, suffixes))
+ .ToList()
+ .Where(t1 => !Main._settings.DisabledProgramSources.Any(x => t1 == x.UniqueIdentifier))
+ .ToList()
+ .ForEach(x => listToAdd.Add(x));
+
+ var paths = listToAdd.Distinct().ToArray();
+
+ var programs1 = paths.AsParallel().Where(p => Extension(p) == ExeExtension).Select(ExeProgram);
+ var programs2 = paths.AsParallel().Where(p => Extension(p) == ShortcutExtension).Select(ExeProgram);
+ var programs3 = from p in paths.AsParallel()
+ let e = Extension(p)
+ where e != ShortcutExtension && e != ExeExtension
+ select Win32Program(p);
+ return programs1.Concat(programs2).Concat(programs3);
+ }
+
+ private static ParallelQuery StartMenuPrograms(string[] suffixes)
+ {
+ var disabledProgramsList = Main._settings.DisabledProgramSources;
+
+ var directory1 = Environment.GetFolderPath(Environment.SpecialFolder.Programs);
+ var directory2 = Environment.GetFolderPath(Environment.SpecialFolder.CommonPrograms);
+ var paths1 = ProgramPaths(directory1, suffixes);
+ var paths2 = ProgramPaths(directory2, suffixes);
+
+ var toFilter = paths1.Concat(paths2);
+ var paths = toFilter
+ .Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1))
+ .Select(t1 => t1)
+ .Distinct()
+ .ToArray();
+
+ var programs1 = paths.AsParallel().Where(p => Extension(p) == ShortcutExtension).Select(LnkProgram);
+ var programs2 = paths.AsParallel().Where(p => Extension(p) == ApplicationReferenceExtension).Select(Win32Program);
+
+ return programs1.Concat(programs2).Where(p => p.Valid);
+ }
+
+ private static ParallelQuery AppPathsPrograms(string[] suffixes)
+ {
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121
+ const string appPaths = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths";
+ var programs = new List();
+ using (var root = Registry.LocalMachine.OpenSubKey(appPaths))
+ {
+ if (root != null)
+ {
+ programs.AddRange(GetProgramsFromRegistry(root));
+ }
+ }
+ using (var root = Registry.CurrentUser.OpenSubKey(appPaths))
+ {
+ if (root != null)
+ {
+ programs.AddRange(GetProgramsFromRegistry(root));
+ }
+ }
+
+ var disabledProgramsList = Main._settings.DisabledProgramSources;
+ var toFilter = programs.AsParallel().Where(p => suffixes.Contains(Extension(p.ExecutableName)));
+
+ var filtered = toFilter.Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier)).Select(t1 => t1);
+
+ return filtered;
+ }
+
+ private static IEnumerable GetProgramsFromRegistry(RegistryKey root)
+ {
+ return root
+ .GetSubKeyNames()
+ .Select(x => GetProgramPathFromRegistrySubKeys(root, x))
+ .Distinct()
+ .Select(x => GetProgramFromPath(x));
+ }
+
+ private static string GetProgramPathFromRegistrySubKeys(RegistryKey root, string subkey)
+ {
+ var path = string.Empty;
+ try
+ {
+ using (var key = root.OpenSubKey(subkey))
+ {
+ if (key == null)
+ return string.Empty;
+
+ var defaultValue = string.Empty;
+ path = key.GetValue(defaultValue) as string;
+ }
+
+ if (string.IsNullOrEmpty(path))
+ return string.Empty;
+
+ // fix path like this: ""\"C:\\folder\\executable.exe\""
+ return path = path.Trim('"', ' ');
+ }
+ catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
+ {
+ ProgramLogger.LogException($"|Win32|GetProgramPathFromRegistrySubKeys|{path}" +
+ $"|Permission denied when trying to load the program from {path}", e);
+
+ return string.Empty;
+ }
+ }
+
+ private static Win32 GetProgramFromPath(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ return new Win32();
+
+ path = Environment.ExpandEnvironmentVariables(path);
+
+ if (!File.Exists(path))
+ return new Win32();
+
+ var entry = Win32Program(path);
+ entry.ExecutableName = Path.GetFileName(path);
+
+ return entry;
+ }
+
+ public static Win32[] All(Settings settings)
+ {
+ try
+ {
+ var programs = new List().AsParallel();
+
+ var unregistered = UnregisteredPrograms(settings.ProgramSources, settings.ProgramSuffixes);
+ programs = programs.Concat(unregistered);
+
+ if (settings.EnableRegistrySource)
+ {
+ var appPaths = AppPathsPrograms(settings.ProgramSuffixes);
+ programs = programs.Concat(appPaths);
+ }
+
+ if (settings.EnableStartMenuSource)
+ {
+ var startMenu = StartMenuPrograms(settings.ProgramSuffixes);
+ programs = programs.Concat(startMenu);
+ }
+
+ var programsWithoutLnk = programs.Where(x => string.IsNullOrEmpty(x.LnkResolvedPath));
+ var programsAsList = programs.ToList();
+
+ foreach(var app in programsWithoutLnk)
+ {
+ programsAsList.RemoveAll(x => (x.FullPath == app.FullPath) && string.IsNullOrEmpty(x.LnkResolvedPath));
+ }
+
+ return programsAsList.ToArray();
+ }
+#if DEBUG //This is to make developer aware of any unhandled exception and add in handling.
+ catch (Exception e)
+ {
+ throw e;
+ }
+#endif
+
+#if !DEBUG //Only do a catch all in production.
+ catch (Exception e)
+ {
+ ProgramLogger.LogException("|Win32|All|Not available|An unexpected error occurred", e);
+
+ return new Win32[0];
+ }
+#endif
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Settings.cs b/src/modules/launcher/Plugins/Wox.Plugin.Program/Settings.cs
new file mode 100644
index 0000000000..1ead3f9c32
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/Settings.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Wox.Plugin.Program
+{
+ public class Settings
+ {
+ public DateTime LastIndexTime { get; set; }
+ public List ProgramSources { get; set; } = new List();
+ public List DisabledProgramSources { get; set; } = new List();
+ public string[] ProgramSuffixes { get; set; } = {"bat", "appref-ms", "exe", "lnk"};
+
+ public bool EnableStartMenuSource { get; set; } = true;
+
+ public bool EnableRegistrySource { get; set; } = true;
+
+ internal const char SuffixSeperator = ';';
+
+ ///
+ /// Contains user added folder location contents as well as all user disabled applications
+ ///
+ ///
+ /// Win32 class applications set UniqueIdentifier using their full file path
+ /// UWP class applications set UniqueIdentifier using their Application User Model ID
+ /// Custom user added program sources set UniqueIdentifier using their location
+ ///
+ public class ProgramSource
+ {
+ private string name;
+
+ public string Location { get; set; }
+ public string Name { get => name ?? new DirectoryInfo(Location).Name; set => name = value; }
+ public bool Enabled { get; set; } = true;
+ public string UniqueIdentifier { get; set; }
+ }
+
+ public class DisabledProgramSource : ProgramSource { }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/ShObjIdlTlb.dll b/src/modules/launcher/Plugins/Wox.Plugin.Program/ShObjIdlTlb.dll
new file mode 100644
index 0000000000..83815e40a4
Binary files /dev/null and b/src/modules/launcher/Plugins/Wox.Plugin.Program/ShObjIdlTlb.dll differ
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/SuffixesConverter.cs b/src/modules/launcher/Plugins/Wox.Plugin.Program/SuffixesConverter.cs
new file mode 100644
index 0000000000..5db4ec945f
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/SuffixesConverter.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+using System.Windows.Markup;
+
+namespace Wox.Plugin.Program
+{
+ public class SuffixesConvert : MarkupExtension, IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ var text = value as string[];
+ if (text != null)
+ {
+ return string.Join(";", text);
+ }
+ else
+ {
+ return string.Empty;
+ }
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override object ProvideValue(IServiceProvider serviceProvider)
+ {
+ return this;
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Views/Commands/ProgramSettingDisplay.cs b/src/modules/launcher/Plugins/Wox.Plugin.Program/Views/Commands/ProgramSettingDisplay.cs
new file mode 100644
index 0000000000..21bba677a5
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/Views/Commands/ProgramSettingDisplay.cs
@@ -0,0 +1,149 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Wox.Plugin.Program.Views.Models;
+
+namespace Wox.Plugin.Program.Views.Commands
+{
+ internal static class ProgramSettingDisplay
+ {
+ internal static List LoadProgramSources(this List programSources)
+ {
+ var list = new List();
+
+ programSources.ForEach(x => list
+ .Add(
+ new ProgramSource
+ {
+ Enabled = x.Enabled,
+ Location = x.Location,
+ Name = x.Name,
+ UniqueIdentifier = x.UniqueIdentifier
+ }
+ ));
+
+ // Even though these are disabled, we still want to display them so users can enable later on
+ Main._settings
+ .DisabledProgramSources
+ .Where(t1 => !Main._settings
+ .ProgramSources // program sourcces added above already, so exlcude
+ .Any(x => t1.UniqueIdentifier == x.UniqueIdentifier))
+ .Select(x => x)
+ .ToList()
+ .ForEach(x => list
+ .Add(
+ new ProgramSource
+ {
+ Enabled = x.Enabled,
+ Location = x.Location,
+ Name = x.Name,
+ UniqueIdentifier = x.UniqueIdentifier
+ }
+ ));
+
+ return list;
+ }
+
+ internal static void LoadAllApplications(this List list)
+ {
+ Main._win32s
+ .Where(t1 => !ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
+ .ToList()
+ .ForEach(t1 => ProgramSetting.ProgramSettingDisplayList
+ .Add(
+ new ProgramSource
+ {
+ Name = t1.Name,
+ Location = t1.ParentDirectory,
+ UniqueIdentifier = t1.UniqueIdentifier,
+ Enabled = t1.Enabled
+ }
+ ));
+
+ Main._uwps
+ .Where(t1 => !ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
+ .ToList()
+ .ForEach(t1 => ProgramSetting.ProgramSettingDisplayList
+ .Add(
+ new ProgramSource
+ {
+ Name = t1.DisplayName,
+ Location = t1.Package.Location,
+ UniqueIdentifier = t1.UniqueIdentifier,
+ Enabled = t1.Enabled
+ }
+ ));
+ }
+
+ internal static void SetProgramSourcesStatus(this List list, List selectedProgramSourcesToDisable, bool status)
+ {
+ ProgramSetting.ProgramSettingDisplayList
+ .Where(t1 => selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier && t1.Enabled != status))
+ .ToList()
+ .ForEach(t1 => t1.Enabled = status);
+
+ Main._win32s
+ .Where(t1 => selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier && t1.Enabled != status))
+ .ToList()
+ .ForEach(t1 => t1.Enabled = status);
+
+ Main._uwps
+ .Where(t1 => selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier && t1.Enabled != status))
+ .ToList()
+ .ForEach(t1 => t1.Enabled = status);
+ }
+
+ internal static void StoreDisabledInSettings(this List list)
+ {
+ Main._settings.ProgramSources
+ .Where(t1 => ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier && !x.Enabled))
+ .ToList()
+ .ForEach(t1 => t1.Enabled = false);
+
+ ProgramSetting.ProgramSettingDisplayList
+ .Where(t1 => !t1.Enabled
+ && !Main._settings.DisabledProgramSources.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
+ .ToList()
+ .ForEach(x => Main._settings.DisabledProgramSources
+ .Add(
+ new Settings.DisabledProgramSource
+ {
+ Name = x.Name,
+ Location = x.Location,
+ UniqueIdentifier = x.UniqueIdentifier,
+ Enabled = false
+ }
+ ));
+ }
+
+ internal static void RemoveDisabledFromSettings(this List list)
+ {
+ Main._settings.ProgramSources
+ .Where(t1 => ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier && x.Enabled))
+ .ToList()
+ .ForEach(t1 => t1.Enabled = true);
+
+ Main._settings.DisabledProgramSources
+ .Where(t1 => ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier && x.Enabled))
+ .ToList()
+ .ForEach(x => Main._settings.DisabledProgramSources.Remove(x));
+ }
+
+ internal static bool IsReindexRequired(this List selectedItems)
+ {
+ if (selectedItems.Where(t1 => t1.Enabled && !Main._uwps.Any(x => t1.UniqueIdentifier == x.UniqueIdentifier)).Count() > 0
+ && selectedItems.Where(t1 => t1.Enabled && !Main._win32s.Any(x => t1.UniqueIdentifier == x.UniqueIdentifier)).Count() > 0)
+ return true;
+
+ // ProgramSources holds list of user added directories,
+ // so when we enable/disable we need to reindex to show/not show the programs
+ // that are found in those directories.
+ if (selectedItems.Where(t1 => Main._settings.ProgramSources.Any(x => t1.UniqueIdentifier == x.UniqueIdentifier)).Count() > 0)
+ return true;
+
+ return false;
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Views/Models/ProgramSource.cs b/src/modules/launcher/Plugins/Wox.Plugin.Program/Views/Models/ProgramSource.cs
new file mode 100644
index 0000000000..c2995f74da
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/Views/Models/ProgramSource.cs
@@ -0,0 +1,5 @@
+
+namespace Wox.Plugin.Program.Views.Models
+{
+ public class ProgramSource : Settings.ProgramSource { }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Views/ProgramSetting.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Program/Views/ProgramSetting.xaml
new file mode 100644
index 0000000000..0fe8d3bd85
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/Views/ProgramSetting.xaml
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Views/ProgramSetting.xaml.cs b/src/modules/launcher/Plugins/Wox.Plugin.Program/Views/ProgramSetting.xaml.cs
new file mode 100644
index 0000000000..72a4f57466
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/Views/ProgramSetting.xaml.cs
@@ -0,0 +1,307 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using Wox.Plugin.Program.Views.Models;
+using Wox.Plugin.Program.Views.Commands;
+using Wox.Plugin.Program.Programs;
+using System.ComponentModel;
+using System.Windows.Data;
+
+namespace Wox.Plugin.Program.Views
+{
+ ///
+ /// Interaction logic for ProgramSetting.xaml
+ ///
+ public partial class ProgramSetting : UserControl
+ {
+ private PluginInitContext context;
+ private Settings _settings;
+ private GridViewColumnHeader _lastHeaderClicked;
+ private ListSortDirection _lastDirection;
+
+ // We do not save all program sources to settings, so using
+ // this as temporary holder for displaying all loaded programs sources.
+ internal static List ProgramSettingDisplayList { get; set; }
+
+ public ProgramSetting(PluginInitContext context, Settings settings, Win32[] win32s, UWP.Application[] uwps)
+ {
+ this.context = context;
+ InitializeComponent();
+ Loaded += Setting_Loaded;
+ _settings = settings;
+ }
+
+ private void Setting_Loaded(object sender, RoutedEventArgs e)
+ {
+ ProgramSettingDisplayList = _settings.ProgramSources.LoadProgramSources();
+ programSourceView.ItemsSource = ProgramSettingDisplayList;
+
+ StartMenuEnabled.IsChecked = _settings.EnableStartMenuSource;
+ RegistryEnabled.IsChecked = _settings.EnableRegistrySource;
+ }
+
+ private void ReIndexing()
+ {
+ programSourceView.Items.Refresh();
+ Task.Run(() =>
+ {
+ Dispatcher.Invoke(() => { indexingPanel.Visibility = Visibility.Visible; });
+ Main.IndexPrograms();
+ Dispatcher.Invoke(() => { indexingPanel.Visibility = Visibility.Hidden; });
+ });
+ }
+
+ private void btnAddProgramSource_OnClick(object sender, RoutedEventArgs e)
+ {
+ var add = new AddProgramSource(context, _settings);
+ if(add.ShowDialog() ?? false)
+ {
+ ReIndexing();
+ }
+
+ programSourceView.Items.Refresh();
+ }
+
+ private void DeleteProgramSources(List itemsToDelete)
+ {
+ itemsToDelete.ForEach(t1 => _settings.ProgramSources
+ .Remove(_settings.ProgramSources
+ .Where(x => x.UniqueIdentifier == t1.UniqueIdentifier)
+ .FirstOrDefault()));
+ itemsToDelete.ForEach(x => ProgramSettingDisplayList.Remove(x));
+
+ ReIndexing();
+ }
+
+ private void btnEditProgramSource_OnClick(object sender, RoutedEventArgs e)
+ {
+ var selectedProgramSource = programSourceView.SelectedItem as Settings.ProgramSource;
+ if (selectedProgramSource != null)
+ {
+ var add = new AddProgramSource(selectedProgramSource, _settings);
+ if (add.ShowDialog() ?? false)
+ {
+ ReIndexing();
+ }
+ }
+ else
+ {
+ string msg = context.API.GetTranslation("wox_plugin_program_pls_select_program_source");
+ MessageBox.Show(msg);
+ }
+ }
+
+ private void btnReindex_Click(object sender, RoutedEventArgs e)
+ {
+ ReIndexing();
+ }
+
+ private void BtnProgramSuffixes_OnClick(object sender, RoutedEventArgs e)
+ {
+ var p = new ProgramSuffixes(context, _settings);
+ if (p.ShowDialog() ?? false)
+ {
+ ReIndexing();
+ }
+ }
+
+ private void programSourceView_DragEnter(object sender, DragEventArgs e)
+ {
+ if (e.Data.GetDataPresent(DataFormats.FileDrop))
+ {
+ e.Effects = DragDropEffects.Link;
+ }
+ else
+ {
+ e.Effects = DragDropEffects.None;
+ }
+ }
+
+ private void programSourceView_Drop(object sender, DragEventArgs e)
+ {
+ var directories = (string[])e.Data.GetData(DataFormats.FileDrop);
+
+ var directoriesToAdd = new List();
+
+ if (directories != null && directories.Length > 0)
+ {
+ foreach (string directory in directories)
+ {
+ if (Directory.Exists(directory) && !ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == directory))
+ {
+ var source = new ProgramSource
+ {
+ Location = directory,
+ UniqueIdentifier = directory
+ };
+
+ directoriesToAdd.Add(source);
+ }
+ }
+
+ if (directoriesToAdd.Count() > 0)
+ {
+ directoriesToAdd.ForEach(x => _settings.ProgramSources.Add(x));
+ directoriesToAdd.ForEach(x => ProgramSettingDisplayList.Add(x));
+
+ programSourceView.Items.Refresh();
+ ReIndexing();
+ }
+ }
+ }
+
+ private void StartMenuEnabled_Click(object sender, RoutedEventArgs e)
+ {
+ _settings.EnableStartMenuSource = StartMenuEnabled.IsChecked ?? false;
+ ReIndexing();
+ }
+
+ private void RegistryEnabled_Click(object sender, RoutedEventArgs e)
+ {
+ _settings.EnableRegistrySource = RegistryEnabled.IsChecked ?? false;
+ ReIndexing();
+ }
+
+ private void btnLoadAllProgramSource_OnClick(object sender, RoutedEventArgs e)
+ {
+ ProgramSettingDisplayList.LoadAllApplications();
+
+ programSourceView.Items.Refresh();
+ }
+
+ private void btnProgramSourceStatus_OnClick(object sender, RoutedEventArgs e)
+ {
+ var selectedItems = programSourceView
+ .SelectedItems.Cast()
+ .ToList();
+
+ if (selectedItems.Count() == 0)
+ {
+ string msg = context.API.GetTranslation("wox_plugin_program_pls_select_program_source");
+ MessageBox.Show(msg);
+ return;
+ }
+
+ if (selectedItems
+ .Where(t1 => !_settings
+ .ProgramSources
+ .Any(x => t1.UniqueIdentifier == x.UniqueIdentifier))
+ .Count() == 0)
+ {
+ var msg = string.Format(context.API.GetTranslation("wox_plugin_program_delete_program_source"));
+
+ if (MessageBox.Show(msg, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
+ {
+ return;
+ }
+
+ DeleteProgramSources(selectedItems);
+ }
+ else if (IsSelectedRowStatusEnabledMoreOrEqualThanDisabled(selectedItems))
+ {
+ ProgramSettingDisplayList.SetProgramSourcesStatus(selectedItems, false);
+
+ ProgramSettingDisplayList.StoreDisabledInSettings();
+ }
+ else
+ {
+ ProgramSettingDisplayList.SetProgramSourcesStatus(selectedItems, true);
+
+ ProgramSettingDisplayList.RemoveDisabledFromSettings();
+ }
+
+ if (selectedItems.IsReindexRequired())
+ ReIndexing();
+
+ programSourceView.SelectedItems.Clear();
+
+ programSourceView.Items.Refresh();
+ }
+
+ private void ProgramSourceView_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e)
+ {
+ programSourceView.SelectedItems.Clear();
+ }
+
+ private void GridViewColumnHeaderClickedHandler(object sender, RoutedEventArgs e)
+ {
+ var headerClicked = e.OriginalSource as GridViewColumnHeader;
+ ListSortDirection direction;
+
+ if (headerClicked != null)
+ {
+ if (headerClicked.Role != GridViewColumnHeaderRole.Padding)
+ {
+ if (headerClicked != _lastHeaderClicked)
+ {
+ direction = ListSortDirection.Ascending;
+ }
+ else
+ {
+ if (_lastDirection == ListSortDirection.Ascending)
+ {
+ direction = ListSortDirection.Descending;
+ }
+ else
+ {
+ direction = ListSortDirection.Ascending;
+ }
+ }
+
+ var columnBinding = headerClicked.Column.DisplayMemberBinding as Binding;
+ var sortBy = columnBinding?.Path.Path ?? headerClicked.Column.Header as string;
+
+ Sort(sortBy, direction);
+
+ _lastHeaderClicked = headerClicked;
+ _lastDirection = direction;
+ }
+ }
+ }
+
+ private void Sort(string sortBy, ListSortDirection direction)
+ {
+ var dataView = CollectionViewSource.GetDefaultView(programSourceView.ItemsSource);
+
+ dataView.SortDescriptions.Clear();
+ SortDescription sd = new SortDescription(sortBy, direction);
+ dataView.SortDescriptions.Add(sd);
+ dataView.Refresh();
+ }
+
+ private bool IsSelectedRowStatusEnabledMoreOrEqualThanDisabled(List selectedItems)
+ {
+ return selectedItems.Where(x => x.Enabled).Count() >= selectedItems.Where(x => !x.Enabled).Count();
+ }
+
+ private void Row_OnClick(object sender, RoutedEventArgs e)
+ {
+ var selectedItems = programSourceView
+ .SelectedItems.Cast()
+ .ToList();
+
+ if (selectedItems
+ .Where(t1 => !_settings
+ .ProgramSources
+ .Any(x => t1.UniqueIdentifier == x.UniqueIdentifier))
+ .Count() == 0)
+ {
+ btnProgramSourceStatus.Content = context.API.GetTranslation("wox_plugin_program_delete");
+ return;
+ }
+
+ if (IsSelectedRowStatusEnabledMoreOrEqualThanDisabled(selectedItems))
+ {
+ btnProgramSourceStatus.Content = "Disable";
+ }
+ else
+ {
+ btnProgramSourceStatus.Content = "Enable";
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/Wox.Plugin.Program.csproj b/src/modules/launcher/Plugins/Wox.Plugin.Program/Wox.Plugin.Program.csproj
new file mode 100644
index 0000000000..0bed61a1ab
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/Wox.Plugin.Program.csproj
@@ -0,0 +1,122 @@
+
+
+
+ netcoreapp3.1
+ {FDB3555B-58EF-4AE6-B5F1-904719637AB4}
+ Properties
+ Wox.Plugin.Program
+ Wox.Plugin.Program
+ true
+ true
+ false
+ false
+ x64
+
+
+
+ true
+ ..\..\..\..\..\x64\Debug\modules\launcher\Plugins\Wox.Plugin.Program\
+ DEBUG;TRACE
+ full
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+ false
+
+
+
+ ..\..\..\..\..\x64\Release\modules\launcher\Plugins\Wox.Plugin.Program\
+ TRACE
+ true
+ pdbonly
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+ .\AppxPackagingTlb.dll
+ True
+
+
+ .\ShObjIdlTlb.dll
+ True
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ Always
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Program/plugin.json b/src/modules/launcher/Plugins/Wox.Plugin.Program/plugin.json
new file mode 100644
index 0000000000..86fd8ebd62
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Program/plugin.json
@@ -0,0 +1,12 @@
+{
+ "ID":"791FC278BA414111B8D1886DFE447410",
+ "ActionKeyword":"*",
+ "Name":"Program",
+ "Description":"Search programs in Wox",
+ "Author":"qianlifeng",
+ "Version":"1.0.0",
+ "Language":"csharp",
+ "Website":"http://www.wox.one/plugin",
+ "ExecuteFileName":"Wox.Plugin.Program.dll",
+ "IcoPath":"Images\\program.png"
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Shell/Images/shell.png b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Images/shell.png
new file mode 100644
index 0000000000..686583653f
Binary files /dev/null and b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Images/shell.png differ
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Shell/Images/user.png b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Images/user.png
new file mode 100644
index 0000000000..2d45c1ee91
Binary files /dev/null and b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Images/user.png differ
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Shell/Languages/de.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Languages/de.xaml
new file mode 100644
index 0000000000..73eebecdcb
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Languages/de.xaml
@@ -0,0 +1,12 @@
+
+
+ Ersetzt Win+R
+ Schließe die Kommandozeilte nicht nachdem der Befehl ausgeführt wurde
+ Kommandozeile
+ Bereitstellung der Kommandozeile in Wox. Befehle müssem mit > starten
+ Dieser Befehl wurde {0} mal ausgeführt
+ Führe Befehle mittels Kommandozeile aus
+ Als Administrator ausführen
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Shell/Languages/en.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Languages/en.xaml
new file mode 100644
index 0000000000..0d0a41b3f1
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Languages/en.xaml
@@ -0,0 +1,14 @@
+
+
+ Replace Win+R
+ Do not close Command Prompt after command execution
+ Always run as administrator
+ Run as different user
+ Shell
+ Allows to execute system commands from Wox. Commands should start with >
+ this command has been executed {0} times
+ execute command through command shell
+ Run As Administrator
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Shell/Languages/pl.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Languages/pl.xaml
new file mode 100644
index 0000000000..909e5c2586
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Languages/pl.xaml
@@ -0,0 +1,12 @@
+
+
+ Zastąp Win+R
+ Nie zamykaj wiersza poleceń po wykonaniu polecenia
+ Wiersz poleceń
+ Pozwala wykonywać komend wiersza polecania z Woxa. Polecania zaczynają się od >
+ to polecenie zostało wykonane {0} razy
+ wykonaj to polecenie w wierszu poleceń
+ Uruchom jako administrator
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Shell/Languages/tr.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Languages/tr.xaml
new file mode 100644
index 0000000000..268b882533
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Languages/tr.xaml
@@ -0,0 +1,12 @@
+
+
+ Win+R kısayolunu kullan
+ Çalıştırma sona erdikten sonra komut istemini kapatma
+ Kabuk
+ Wox üzerinden komut istemini kullanmanızı sağlar. Komutlar > işareti ile başlamalıdır.
+ Bu komut {0} kez çalıştırıldı
+ Komut isteminde çalıştır
+ Yönetici Olarak Çalıştır
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Shell/Languages/zh-cn.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Languages/zh-cn.xaml
new file mode 100644
index 0000000000..c4870a1647
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Languages/zh-cn.xaml
@@ -0,0 +1,12 @@
+
+
+ 替换 Win+R
+ 执行后不关闭命令窗口
+ 命令行
+ 提供从Wox中执行命令行的能力,命令应该以>开头
+ 此命令已经被执行了 {0} 次
+ 执行此命令
+ 以管理员身份运行
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Shell/Languages/zh-tw.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Languages/zh-tw.xaml
new file mode 100644
index 0000000000..082de6301b
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Languages/zh-tw.xaml
@@ -0,0 +1,12 @@
+
+
+ 取代 Win+R
+ 執行後不關閉命令提示字元視窗
+ 命令提示字元
+ 提供從 Wox 中執行命令提示字元的功能,指令應該以>開頭
+ 此指令已執行了 {0} 次
+ 執行指令
+ 以系統管理員身分執行
+
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Shell/Main.cs b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Main.cs
new file mode 100644
index 0000000000..5f72492c25
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Main.cs
@@ -0,0 +1,363 @@
+using Microsoft.PowerToys.Settings.UI.Lib;
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+using WindowsInput;
+using WindowsInput.Native;
+using Wox.Infrastructure.Hotkey;
+using Wox.Infrastructure.Logger;
+using Wox.Infrastructure.Storage;
+using Wox.Plugin.SharedCommands;
+using Application = System.Windows.Application;
+using Control = System.Windows.Controls.Control;
+using Keys = System.Windows.Forms.Keys;
+
+namespace Wox.Plugin.Shell
+{
+ public class Main : IPlugin, ISettingProvider, IPluginI18n, IContextMenu, ISavable
+ {
+ private const string Image = "Images/shell.png";
+ private PluginInitContext _context;
+ private bool _winRStroked;
+ private readonly KeyboardSimulator _keyboardSimulator = new KeyboardSimulator(new InputSimulator());
+
+ private readonly Settings _settings;
+ private readonly PluginJsonStorage _storage;
+
+ public Main()
+ {
+ _storage = new PluginJsonStorage();
+ _settings = _storage.Load();
+ }
+
+ public void Save()
+ {
+ _storage.Save();
+ }
+
+
+ public List Query(Query query)
+ {
+ List results = new List();
+ string cmd = query.Search;
+ if (string.IsNullOrEmpty(cmd))
+ {
+ return ResultsFromlHistory();
+ }
+ else
+ {
+ var queryCmd = GetCurrentCmd(cmd);
+ results.Add(queryCmd);
+ var history = GetHistoryCmds(cmd, queryCmd);
+ results.AddRange(history);
+
+ try
+ {
+ string basedir = null;
+ string dir = null;
+ string excmd = Environment.ExpandEnvironmentVariables(cmd);
+ if (Directory.Exists(excmd) && (cmd.EndsWith("/") || cmd.EndsWith(@"\")))
+ {
+ basedir = excmd;
+ dir = cmd;
+ }
+ else if (Directory.Exists(Path.GetDirectoryName(excmd) ?? string.Empty))
+ {
+ basedir = Path.GetDirectoryName(excmd);
+ var dirn = Path.GetDirectoryName(cmd);
+ dir = (dirn.EndsWith("/") || dirn.EndsWith(@"\")) ? dirn : cmd.Substring(0, dirn.Length + 1);
+ }
+
+ if (basedir != null)
+ {
+ var autocomplete = Directory.GetFileSystemEntries(basedir).
+ Select(o => dir + Path.GetFileName(o)).
+ Where(o => o.StartsWith(cmd, StringComparison.OrdinalIgnoreCase) &&
+ !results.Any(p => o.Equals(p.Title, StringComparison.OrdinalIgnoreCase)) &&
+ !results.Any(p => o.Equals(p.Title, StringComparison.OrdinalIgnoreCase))).ToList();
+ autocomplete.Sort();
+ results.AddRange(autocomplete.ConvertAll(m => new Result
+ {
+ Title = m,
+ IcoPath = Image,
+ Action = c =>
+ {
+ Execute(Process.Start, PrepareProcessStartInfo(m));
+ return true;
+ }
+ }));
+ }
+ }
+ catch (Exception e)
+ {
+ Log.Exception($"|Wox.Plugin.Shell.Main.Query|Exception when query for <{query}>", e);
+ }
+ return results;
+ }
+ }
+
+ private List GetHistoryCmds(string cmd, Result result)
+ {
+ IEnumerable history = _settings.Count.Where(o => o.Key.Contains(cmd))
+ .OrderByDescending(o => o.Value)
+ .Select(m =>
+ {
+ if (m.Key == cmd)
+ {
+ result.SubTitle = string.Format(_context.API.GetTranslation("wox_plugin_cmd_cmd_has_been_executed_times"), m.Value);
+ return null;
+ }
+
+ var ret = new Result
+ {
+ Title = m.Key,
+ SubTitle = string.Format(_context.API.GetTranslation("wox_plugin_cmd_cmd_has_been_executed_times"), m.Value),
+ IcoPath = Image,
+ Action = c =>
+ {
+ Execute(Process.Start, PrepareProcessStartInfo(m.Key));
+ return true;
+ }
+ };
+ return ret;
+ }).Where(o => o != null).Take(4);
+ return history.ToList();
+ }
+
+ private Result GetCurrentCmd(string cmd)
+ {
+ Result result = new Result
+ {
+ Title = cmd,
+ Score = 5000,
+ SubTitle = _context.API.GetTranslation("wox_plugin_cmd_execute_through_shell"),
+ IcoPath = Image,
+ Action = c =>
+ {
+ Execute(Process.Start, PrepareProcessStartInfo(cmd));
+ return true;
+ }
+ };
+
+ return result;
+ }
+
+ private List ResultsFromlHistory()
+ {
+ IEnumerable history = _settings.Count.OrderByDescending(o => o.Value)
+ .Select(m => new Result
+ {
+ Title = m.Key,
+ SubTitle = string.Format(_context.API.GetTranslation("wox_plugin_cmd_cmd_has_been_executed_times"), m.Value),
+ IcoPath = Image,
+ Action = c =>
+ {
+ Execute(Process.Start, PrepareProcessStartInfo(m.Key));
+ return true;
+ }
+ }).Take(5);
+ return history.ToList();
+ }
+
+ private ProcessStartInfo PrepareProcessStartInfo(string command, bool runAsAdministrator = false)
+ {
+ command = command.Trim();
+ command = Environment.ExpandEnvironmentVariables(command);
+ var workingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
+ var runAsAdministratorArg = !runAsAdministrator && !_settings.RunAsAdministrator ? "" : "runas";
+
+ ProcessStartInfo info;
+ if (_settings.Shell == Shell.Cmd)
+ {
+ var arguments = _settings.LeaveShellOpen ? $"/k \"{command}\"" : $"/c \"{command}\" & pause";
+
+ info = ShellCommand.SetProcessStartInfo("cmd.exe", workingDirectory, arguments, runAsAdministratorArg);
+ }
+ else if (_settings.Shell == Shell.Powershell)
+ {
+ string arguments;
+ if (_settings.LeaveShellOpen)
+ {
+ arguments = $"-NoExit \"{command}\"";
+ }
+ else
+ {
+ arguments = $"\"{command} ; Read-Host -Prompt \\\"Press Enter to continue\\\"\"";
+ }
+
+ info = ShellCommand.SetProcessStartInfo("powershell.exe", workingDirectory, arguments, runAsAdministratorArg);
+ }
+ else if (_settings.Shell == Shell.RunCommand)
+ {
+ //Open explorer if the path is a file or directory
+ if(Directory.Exists(command) || File.Exists(command))
+ {
+ info = ShellCommand.SetProcessStartInfo("explorer.exe", arguments: command, verb: runAsAdministratorArg);
+ }
+ else
+ {
+ var parts = command.Split(new[] { ' ' }, 2);
+ if (parts.Length == 2)
+ {
+ var filename = parts[0];
+ if (ExistInPath(filename))
+ {
+ var arguments = parts[1];
+ info = ShellCommand.SetProcessStartInfo(filename, workingDirectory, arguments, runAsAdministratorArg);
+ }
+ else
+ {
+ info = ShellCommand.SetProcessStartInfo(command, verb: runAsAdministratorArg);
+ }
+ }
+ else
+ {
+ info = ShellCommand.SetProcessStartInfo(command, verb: runAsAdministratorArg);
+ }
+ }
+ }
+ else
+ {
+ throw new NotImplementedException();
+ }
+
+ info.UseShellExecute = true;
+
+ _settings.AddCmdHistory(command);
+
+ return info;
+ }
+
+ private void Execute(Func startProcess,ProcessStartInfo info)
+ {
+ try
+ {
+ startProcess(info);
+ }
+ catch (FileNotFoundException e)
+ {
+ var name = "Plugin: Shell";
+ var message = $"Command not found: {e.Message}";
+ _context.API.ShowMsg(name, message);
+ }
+ catch(Win32Exception e)
+ {
+ var name = "Plugin: Shell";
+ var message = $"Error running the command: {e.Message}";
+ _context.API.ShowMsg(name, message);
+ }
+ }
+
+ private bool ExistInPath(string filename)
+ {
+ if (File.Exists(filename))
+ {
+ return true;
+ }
+ else
+ {
+ var values = Environment.GetEnvironmentVariable("PATH");
+ if (values != null)
+ {
+ foreach (var path in values.Split(';'))
+ {
+ var path1 = Path.Combine(path, filename);
+ var path2 = Path.Combine(path, filename + ".exe");
+ if (File.Exists(path1) || File.Exists(path2))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ public void Init(PluginInitContext context)
+ {
+ this._context = context;
+ context.API.GlobalKeyboardEvent += API_GlobalKeyboardEvent;
+ }
+
+ bool API_GlobalKeyboardEvent(int keyevent, int vkcode, SpecialKeyState state)
+ {
+ // not overriding Win+R
+ // crutkas we need to earn the right for Win+R override
+
+ if (_settings.ReplaceWinR)
+ {
+ if (keyevent == (int)KeyEvent.WM_KEYDOWN && vkcode == (int)Keys.R && state.WinPressed)
+ {
+ _winRStroked = true;
+ OnWinRPressed();
+ return false;
+ }
+ if (keyevent == (int)KeyEvent.WM_KEYUP && _winRStroked && vkcode == (int)Keys.LWin)
+ {
+ _winRStroked = false;
+ _keyboardSimulator.ModifiedKeyStroke(VirtualKeyCode.LWIN, VirtualKeyCode.BACK);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void OnWinRPressed()
+ {
+ _context.API.ChangeQuery($"{_context.CurrentPluginMetadata.ActionKeywords[0]}{Plugin.Query.TermSeperater}");
+ Application.Current.MainWindow.Visibility = Visibility.Visible;
+ }
+
+ public Control CreateSettingPanel()
+ {
+ return new CMDSetting(_settings);
+ }
+
+ public string GetTranslatedPluginTitle()
+ {
+ return _context.API.GetTranslation("wox_plugin_cmd_plugin_name");
+ }
+
+ public string GetTranslatedPluginDescription()
+ {
+ return _context.API.GetTranslation("wox_plugin_cmd_plugin_description");
+ }
+
+ public List LoadContextMenus(Result selectedResult)
+ {
+ var resultlist = new List
+ {
+ new ContextMenuResult
+ {
+ Title = _context.API.GetTranslation("wox_plugin_cmd_run_as_administrator"),
+ Glyph = "\xE7EF",
+ FontFamily = "Segoe MDL2 Assets",
+ AcceleratorKey = "Enter",
+ AcceleratorModifiers = "Control,Shift",
+ Action = c =>
+ {
+ Execute(Process.Start, PrepareProcessStartInfo(selectedResult.Title, true));
+ return true;
+ }
+ }
+ };
+
+ return resultlist;
+ }
+
+ public void UpdateSettings(PowerLauncherSettings settings)
+ {
+ _settings.ReplaceWinR = settings.properties.override_win_r_key;
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Shell/Settings.cs b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Settings.cs
new file mode 100644
index 0000000000..e4e1cd43fc
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Settings.cs
@@ -0,0 +1,37 @@
+using System.Collections.Generic;
+
+namespace Wox.Plugin.Shell
+{
+ public class Settings
+ {
+ public Shell Shell { get; set; } = Shell.RunCommand;
+
+ // not overriding Win+R
+ // crutkas we need to earn the right for Win+R override
+ public bool ReplaceWinR { get; set; } = false;
+ public bool LeaveShellOpen { get; set; }
+ public bool RunAsAdministrator { get; set; } = false;
+
+ public Dictionary Count = new Dictionary();
+
+ public void AddCmdHistory(string cmdName)
+ {
+ if (Count.ContainsKey(cmdName))
+ {
+ Count[cmdName] += 1;
+ }
+ else
+ {
+ Count.Add(cmdName, 1);
+ }
+ }
+ }
+
+ public enum Shell
+ {
+ Cmd = 0,
+ Powershell = 1,
+ RunCommand = 2,
+
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Shell/ShellSetting.xaml b/src/modules/launcher/Plugins/Wox.Plugin.Shell/ShellSetting.xaml
new file mode 100644
index 0000000000..dc6de53bac
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Shell/ShellSetting.xaml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CMD
+ PowerShell
+ RunCommand
+
+
+
+
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Shell/ShellSetting.xaml.cs b/src/modules/launcher/Plugins/Wox.Plugin.Shell/ShellSetting.xaml.cs
new file mode 100644
index 0000000000..ffa3b58568
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Shell/ShellSetting.xaml.cs
@@ -0,0 +1,60 @@
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Wox.Plugin.Shell
+{
+ public partial class CMDSetting : UserControl
+ {
+ private readonly Settings _settings;
+
+ public CMDSetting(Settings settings)
+ {
+ InitializeComponent();
+ _settings = settings;
+ }
+
+ private void CMDSetting_OnLoaded(object sender, RoutedEventArgs re)
+ {
+ ReplaceWinR.IsChecked = _settings.ReplaceWinR;
+ LeaveShellOpen.IsChecked = _settings.LeaveShellOpen;
+ AlwaysRunAsAdministrator.IsChecked = _settings.RunAsAdministrator;
+ LeaveShellOpen.IsEnabled = _settings.Shell != Shell.RunCommand;
+
+ LeaveShellOpen.Checked += (o, e) =>
+ {
+ _settings.LeaveShellOpen = true;
+ };
+
+ LeaveShellOpen.Unchecked += (o, e) =>
+ {
+ _settings.LeaveShellOpen = false;
+ };
+
+ AlwaysRunAsAdministrator.Checked += (o, e) =>
+ {
+ _settings.RunAsAdministrator = true;
+ };
+
+ AlwaysRunAsAdministrator.Unchecked += (o, e) =>
+ {
+ _settings.RunAsAdministrator = false;
+ };
+
+ ReplaceWinR.Checked += (o, e) =>
+ {
+ _settings.ReplaceWinR = true;
+ };
+ ReplaceWinR.Unchecked += (o, e) =>
+ {
+ _settings.ReplaceWinR = false;
+ };
+
+ ShellComboBox.SelectedIndex = (int) _settings.Shell;
+ ShellComboBox.SelectionChanged += (o, e) =>
+ {
+ _settings.Shell = (Shell) ShellComboBox.SelectedIndex;
+ LeaveShellOpen.IsEnabled = _settings.Shell != Shell.RunCommand;
+ };
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Shell/Wox.Plugin.Shell.csproj b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Wox.Plugin.Shell.csproj
new file mode 100644
index 0000000000..9c3951afd6
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Shell/Wox.Plugin.Shell.csproj
@@ -0,0 +1,107 @@
+
+
+
+ netcoreapp3.1
+ {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}
+ Properties
+ Wox.Plugin.Shell
+ Wox.Plugin.Shell
+ true
+ false
+ false
+ x64
+
+
+
+ true
+ ..\..\..\..\..\x64\Debug\modules\launcher\Plugins\Wox.Plugin.Shell\
+ DEBUG;TRACE
+ full
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+ false
+
+
+
+ ..\..\..\..\..\x64\Release\modules\launcher\Plugins\Wox.Plugin.Shell\
+ TRACE
+ true
+ pdbonly
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+
+
+
+
+ Always
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+ MSBuild:Compile
+ Designer
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Shell/plugin.json b/src/modules/launcher/Plugins/Wox.Plugin.Shell/plugin.json
new file mode 100644
index 0000000000..a09636e390
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.Shell/plugin.json
@@ -0,0 +1,12 @@
+{
+ "ID":"D409510CD0D2481F853690A07E6DC426",
+ "ActionKeyword":">",
+ "Name":"Shell",
+ "Description":"Provide executing commands from Wox. Commands should start with >",
+ "Author":"qianlifeng",
+ "Version":"1.0.0",
+ "Language":"csharp",
+ "Website":"http://www.wox.one/plugin",
+ "ExecuteFileName":"Wox.Plugin.Shell.dll",
+ "IcoPath":"Images\\shell.png"
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/FuzzyMatching.cs b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/FuzzyMatching.cs
new file mode 100644
index 0000000000..be032cb215
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/FuzzyMatching.cs
@@ -0,0 +1,123 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/
+
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Wox.Plugin.WindowWalker.Components
+{
+ ///
+ /// Class housing fuzzy matching methods
+ ///
+ public class FuzzyMatching
+ {
+ ///
+ /// Finds the best match (the one with the most
+ /// number of letters adjecent to each other) and
+ /// returns the index location of each of the letters
+ /// of the matches
+ ///
+ /// The text to search inside of
+ /// the text to search for
+ /// returns the index location of each of the letters of the matches
+ public static List FindBestFuzzyMatch(string text, string searchText)
+ {
+ searchText = searchText.ToLower();
+ text = text.ToLower();
+
+ // Create a grid to march matches like
+ // eg.
+ // a b c a d e c f g
+ // a x x
+ // c x x
+ bool[,] matches = new bool[text.Length, searchText.Length];
+ for (int firstIndex = 0; firstIndex < text.Length; firstIndex++)
+ {
+ for (int secondIndex = 0; secondIndex < searchText.Length; secondIndex++)
+ {
+ matches[firstIndex, secondIndex] =
+ searchText[secondIndex] == text[firstIndex] ?
+ true :
+ false;
+ }
+ }
+
+ // use this table to get all the possible matches
+ List> allMatches = GetAllMatchIndexes(matches);
+
+ // return the score that is the max
+ int maxScore = allMatches.Count > 0 ? CalculateScoreForMatches(allMatches[0]) : 0;
+ List bestMatch = allMatches.Count > 0 ? allMatches[0] : new List();
+
+ foreach (var match in allMatches)
+ {
+ int score = CalculateScoreForMatches(match);
+ if (score > maxScore)
+ {
+ bestMatch = match;
+ maxScore = score;
+ }
+ }
+
+ return bestMatch;
+ }
+
+ ///
+ /// Gets all the possible matches to the search string with in the text
+ ///
+ /// a table showing the matches as generated by
+ /// a two dimentional array with the first dimention the text and the second
+ /// one the search string and each cell marked as an intersection between the two
+ /// a list of the possible combinations that match the search text
+ public static List> GetAllMatchIndexes(bool[,] matches)
+ {
+ List> results = new List>();
+
+ for (int secondIndex = 0; secondIndex < matches.GetLength(1); secondIndex++)
+ {
+ for (int firstIndex = 0; firstIndex < matches.GetLength(0); firstIndex++)
+ {
+ if (secondIndex == 0 && matches[firstIndex, secondIndex])
+ {
+ results.Add(new List { firstIndex });
+ }
+ else if (matches[firstIndex, secondIndex])
+ {
+ var tempList = results.Where(x => x.Count == secondIndex && x[x.Count - 1] < firstIndex).Select(x => x.ToList()).ToList();
+
+ foreach (var pathSofar in tempList)
+ {
+ pathSofar.Add(firstIndex);
+ }
+
+ results.AddRange(tempList);
+ }
+ }
+
+ results = results.Where(x => x.Count == secondIndex + 1).ToList();
+ }
+
+ return results.Where(x => x.Count == matches.GetLength(1)).ToList();
+ }
+
+ ///
+ /// Calculates the score for a string
+ ///
+ /// the index of the matches
+ /// an integer representing the score
+ public static int CalculateScoreForMatches(List matches)
+ {
+ var score = 0;
+
+ for (int currentIndex = 1; currentIndex < matches.Count; currentIndex++)
+ {
+ var previousIndex = currentIndex - 1;
+
+ score -= matches[currentIndex] - matches[previousIndex];
+ }
+
+ return score == 0 ? -10000 : score;
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/InteropAndHelpers.cs b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/InteropAndHelpers.cs
new file mode 100644
index 0000000000..8bd6266adf
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/InteropAndHelpers.cs
@@ -0,0 +1,842 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/
+
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Wox.Plugin.WindowWalker.Components
+{
+ ///
+ /// Interop calls with helper layers
+ ///
+ internal class InteropAndHelpers
+ {
+ public delegate bool CallBackPtr(IntPtr hwnd, IntPtr lParam);
+
+ ///
+ /// Some flags for interop calls to SetWindowPosition
+ ///
+ [Flags]
+ public enum SetWindowPosFlags : uint
+ {
+ ///
+ /// If the calling thread and the thread that owns the window are attached to different input queues, the system posts the request to the thread that owns the window. This prevents the calling thread from blocking its execution while other threads process the request.
+ ///
+ SWP_ASYNCWINDOWPOS = 0x4000,
+
+ ///
+ /// Prevents generation of the WM_SYNCPAINT message.
+ ///
+ SWP_DEFERERASE = 0x2000,
+
+ ///
+ /// Draws a frame (defined in the window's class description) around the window.
+ ///
+ SWP_DRAWFRAME = 0x0020,
+
+ ///
+ /// Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE is sent only when the window's size is being changed.
+ ///
+ SWP_FRAMECHANGED = 0x0020,
+
+ ///
+ /// Hides the window.
+ ///
+ SWP_HIDEWINDOW = 0x0080,
+
+ ///
+ /// Does not activate the window. If this flag is not set, the window is activated and moved to the top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter parameter).
+ ///
+ SWP_NOACTIVATE = 0x0010,
+
+ ///
+ /// Discards the entire contents of the client area. If this flag is not specified, the valid contents of the client area are saved and copied back into the client area after the window is sized or repositioned.
+ ///
+ SWP_NOCOPYBITS = 0x0100,
+
+ ///
+ /// Retains the current position (ignores X and Y parameters).
+ ///
+ SWP_NOMOVE = 0x0002,
+
+ ///
+ /// Does not change the owner window's position in the Z order.
+ ///
+ SWP_NOOWNERZORDER = 0x0200,
+
+ ///
+ /// Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent window uncovered as a result of the window being moved. When this flag is set, the application must explicitly invalidate or redraw any parts of the window and parent window that need redrawing.
+ ///
+ SWP_NOREDRAW = 0x0008,
+
+ ///
+ /// Same as the SWP_NOOWNERZORDER flag.
+ ///
+ SWP_NOREPOSITION = 0x0200,
+
+ ///
+ /// Prevents the window from receiving the WM_WINDOWPOSCHANGING message.
+ ///
+ SWP_NOSENDCHANGING = 0x0400,
+
+ ///
+ /// Retains the current size (ignores the cx and cy parameters).
+ ///
+ SWP_NOSIZE = 0x0001,
+
+ ///
+ /// Retains the current Z order (ignores the hWndInsertAfter parameter).
+ ///
+ SWP_NOZORDER = 0x0004,
+
+ ///
+ /// Displays the window.
+ ///
+ SWP_SHOWWINDOW = 0x0040,
+ }
+
+ ///
+ /// Flags for setting hotkeys
+ ///
+ [Flags]
+ public enum Modifiers
+ {
+ NoMod = 0x0000,
+ Alt = 0x0001,
+ Ctrl = 0x0002,
+ Shift = 0x0004,
+ Win = 0x0008,
+ }
+
+ ///
+ /// Options for DwmpActivateLivePreview
+ ///
+ public enum LivePreviewTrigger
+ {
+ ///
+ /// Show Desktop button
+ ///
+ ShowDesktop = 1,
+
+ ///
+ /// WIN+SPACE hotkey
+ ///
+ WinSpace,
+
+ ///
+ /// Hover-over Superbar thumbnails
+ ///
+ Superbar,
+
+ ///
+ /// Alt-Tab
+ ///
+ AltTab,
+
+ ///
+ /// Press and hold on Superbar thumbnails
+ ///
+ SuperbarTouch,
+
+ ///
+ /// Press and hold on Show desktop
+ ///
+ ShowDesktopTouch,
+ }
+
+ ///
+ /// Show Window Enums
+ ///
+ public enum ShowWindowCommands
+ {
+ ///
+ /// Hides the window and activates another window.
+ ///
+ Hide = 0,
+
+ ///
+ /// Activates and displays a window. If the window is minimized or
+ /// maximized, the system restores it to its original size and position.
+ /// An application should specify this flag when displaying the window
+ /// for the first time.
+ ///
+ Normal = 1,
+
+ ///
+ /// Activates the window and displays it as a minimized window.
+ ///
+ ShowMinimized = 2,
+
+ ///
+ /// Maximizes the specified window.
+ ///
+ Maximize = 3, // is this the right value?
+
+ ///
+ /// Activates the window and displays it as a maximized window.
+ ///
+ ShowMaximized = 3,
+
+ ///
+ /// Displays a window in its most recent size and position. This value
+ /// is similar to , except
+ /// the window is not activated.
+ ///
+ ShowNoActivate = 4,
+
+ ///
+ /// Activates the window and displays it in its current size and position.
+ ///
+ Show = 5,
+
+ ///
+ /// Minimizes the specified window and activates the next top-level
+ /// window in the Z order.
+ ///
+ Minimize = 6,
+
+ ///
+ /// Displays the window as a minimized window. This value is similar to
+ /// , except the
+ /// window is not activated.
+ ///
+ ShowMinNoActive = 7,
+
+ ///
+ /// Displays the window in its current size and position. This value is
+ /// similar to , except the
+ /// window is not activated.
+ ///
+ ShowNA = 8,
+
+ ///
+ /// Activates and displays the window. If the window is minimized or
+ /// maximized, the system restores it to its original size and position.
+ /// An application should specify this flag when restoring a minimized window.
+ ///
+ Restore = 9,
+
+ ///
+ /// Sets the show state based on the SW_* value specified in the
+ /// STARTUPINFO structure passed to the CreateProcess function by the
+ /// program that started the application.
+ ///
+ ShowDefault = 10,
+
+ ///
+ /// Windows 2000/XP: Minimizes a window, even if the thread
+ /// that owns the window is not responding. This flag should only be
+ /// used when minimizing windows from a different thread.
+ ///
+ ForceMinimize = 11,
+ }
+
+ ///
+ /// The rendering policy to use for set window attribute
+ ///
+ [Flags]
+ public enum DwmNCRenderingPolicy
+ {
+ UseWindowStyle,
+ Disabled,
+ Enabled,
+ Last,
+ }
+
+ ///
+ /// Window attribute
+ ///
+ [Flags]
+ public enum DwmWindowAttribute
+ {
+ NCRenderingEnabled = 1,
+ NCRenderingPolicy,
+ TransitionsForceDisabled,
+ AllowNCPaint,
+ CaptionButtonBounds,
+ NonClientRtlLayout,
+ ForceIconicRepresentation,
+ Flip3DPolicy,
+ ExtendedFrameBounds,
+ HasIconicBitmap,
+ DisallowPeek,
+ ExcludedFromPeek,
+ Last,
+ }
+
+ ///
+ /// Flags for accessing the process in trying to get icon for the process
+ ///
+ [Flags]
+ public enum ProcessAccessFlags
+ {
+ ///
+ /// Required to create a thread.
+ ///
+ CreateThread = 0x0002,
+
+ ///
+ ///
+ ///
+ SetSessionId = 0x0004,
+
+ ///
+ /// Required to perform an operation on the address space of a process
+ ///
+ VmOperation = 0x0008,
+
+ ///
+ /// Required to read memory in a process using ReadProcessMemory.
+ ///
+ VmRead = 0x0010,
+
+ ///
+ /// Required to write to memory in a process using WriteProcessMemory.
+ ///
+ VmWrite = 0x0020,
+
+ ///
+ /// Required to duplicate a handle using DuplicateHandle.
+ ///
+ DupHandle = 0x0040,
+
+ ///
+ /// Required to create a process.
+ ///
+ CreateProcess = 0x0080,
+
+ ///
+ /// Required to set memory limits using SetProcessWorkingSetSize.
+ ///
+ SetQuota = 0x0100,
+
+ ///
+ /// Required to set certain information about a process, such as its priority class (see SetPriorityClass).
+ ///
+ SetInformation = 0x0200,
+
+ ///
+ /// Required to retrieve certain information about a process, such as its token, exit code, and priority class (see OpenProcessToken).
+ ///
+ QueryInformation = 0x0400,
+
+ ///
+ /// Required to suspend or resume a process.
+ ///
+ SuspendResume = 0x0800,
+
+ ///
+ /// Required to retrieve certain information about a process (see GetExitCodeProcess, GetPriorityClass, IsProcessInJob, QueryFullProcessImageName).
+ /// A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted PROCESS_QUERY_LIMITED_INFORMATION.
+ ///
+ QueryLimitedInformation = 0x1000,
+
+ ///
+ /// Required to wait for the process to terminate using the wait functions.
+ ///
+ Synchronize = 0x100000,
+
+ ///
+ /// Required to delete the object.
+ ///
+ Delete = 0x00010000,
+
+ ///
+ /// Required to read information in the security descriptor for the object, not including the information in the SACL.
+ /// To read or write the SACL, you must request the ACCESS_SYSTEM_SECURITY access right. For more information, see SACL Access Right.
+ ///
+ ReadControl = 0x00020000,
+
+ ///
+ /// Required to modify the DACL in the security descriptor for the object.
+ ///
+ WriteDac = 0x00040000,
+
+ ///
+ /// Required to change the owner in the security descriptor for the object.
+ ///
+ WriteOwner = 0x00080000,
+
+ StandardRightsRequired = 0x000F0000,
+
+ ///
+ /// All possible access rights for a process object.
+ ///
+ AllAccess = StandardRightsRequired | Synchronize | 0xFFFF,
+ }
+
+ ///
+ /// Contains information about the placement of a window on the screen.
+ ///
+ [Serializable]
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct WINDOWPLACEMENT
+ {
+ ///
+ /// The length of the structure, in bytes. Before calling the GetWindowPlacement or SetWindowPlacement functions, set this member to sizeof(WINDOWPLACEMENT).
+ ///
+ /// GetWindowPlacement and SetWindowPlacement fail if this member is not set correctly.
+ ///
+ ///
+ public int Length;
+
+ ///
+ /// Specifies flags that control the position of the minimized window and the method by which the window is restored.
+ ///
+ public int Flags;
+
+ ///
+ /// The current show state of the window.
+ ///
+ public ShowWindowCommands ShowCmd;
+
+ ///
+ /// The coordinates of the window's upper-left corner when the window is minimized.
+ ///
+ public POINT MinPosition;
+
+ ///
+ /// The coordinates of the window's upper-left corner when the window is maximized.
+ ///
+ public POINT MaxPosition;
+
+ ///
+ /// The window's coordinates when the window is in the restored position.
+ ///
+ public RECT NormalPosition;
+
+ ///
+ /// Gets the default (empty) value.
+ ///
+ public static WINDOWPLACEMENT Default
+ {
+ get
+ {
+ WINDOWPLACEMENT result = default;
+ result.Length = Marshal.SizeOf(result);
+ return result;
+ }
+ }
+ }
+
+ ///
+ /// Required pointless variables that we don't use in making a windows show
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public struct RECT
+ {
+ public int Left;
+ public int Top;
+ public int Right;
+ public int Bottom;
+
+ public RECT(int left, int top, int right, int bottom)
+ {
+ Left = left;
+ Top = top;
+ Right = right;
+ Bottom = bottom;
+ }
+
+ public RECT(System.Drawing.Rectangle r)
+ : this(r.Left, r.Top, r.Right, r.Bottom)
+ {
+ }
+
+ public int X
+ {
+ get
+ {
+ return Left;
+ }
+
+ set
+ {
+ Right -= Left - value;
+ Left = value;
+ }
+ }
+
+ public int Y
+ {
+ get
+ {
+ return Top;
+ }
+
+ set
+ {
+ Bottom -= Top - value;
+ Top = value;
+ }
+ }
+
+ public int Height
+ {
+ get { return Bottom - Top; }
+ set { Bottom = value + Top; }
+ }
+
+ public int Width
+ {
+ get { return Right - Left; }
+ set { Right = value + Left; }
+ }
+
+ public System.Drawing.Point Location
+ {
+ get
+ {
+ return new System.Drawing.Point(Left, Top);
+ }
+
+ set
+ {
+ X = value.X;
+ Y = value.Y;
+ }
+ }
+
+ public System.Drawing.Size Size
+ {
+ get
+ {
+ return new System.Drawing.Size(Width, Height);
+ }
+
+ set
+ {
+ Width = value.Width;
+ Height = value.Height;
+ }
+ }
+
+ public static implicit operator System.Drawing.Rectangle(RECT r)
+ {
+ return new System.Drawing.Rectangle(r.Left, r.Top, r.Width, r.Height);
+ }
+
+ public static implicit operator RECT(System.Drawing.Rectangle r)
+ {
+ return new RECT(r);
+ }
+
+ public static bool operator ==(RECT r1, RECT r2)
+ {
+ return r1.Equals(r2);
+ }
+
+ public static bool operator !=(RECT r1, RECT r2)
+ {
+ return !r1.Equals(r2);
+ }
+
+ public bool Equals(RECT r)
+ {
+ return r.Left == Left && r.Top == Top && r.Right == Right && r.Bottom == Bottom;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is RECT)
+ {
+ return Equals((RECT)obj);
+ }
+ else if (obj is System.Drawing.Rectangle)
+ {
+ return Equals(new RECT((System.Drawing.Rectangle)obj));
+ }
+
+ return false;
+ }
+
+ public override int GetHashCode()
+ {
+ return ((System.Drawing.Rectangle)this).GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return string.Format(System.Globalization.CultureInfo.CurrentCulture, "{{Left={0},Top={1},Right={2},Bottom={3}}}", Left, Top, Right, Bottom);
+ }
+ }
+
+ ///
+ /// Same as the RECT struct above
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public struct POINT
+ {
+ public int X;
+ public int Y;
+
+ public POINT(int x, int y)
+ {
+ X = x;
+ Y = y;
+ }
+
+ public POINT(System.Drawing.Point pt)
+ : this(pt.X, pt.Y)
+ {
+ }
+
+ public static implicit operator System.Drawing.Point(POINT p)
+ {
+ return new System.Drawing.Point(p.X, p.Y);
+ }
+
+ public static implicit operator POINT(System.Drawing.Point p)
+ {
+ return new POINT(p.X, p.Y);
+ }
+ }
+
+ ///
+ /// GetWindow relationship between the specified window and the window whose handle is to be retrieved.
+ ///
+ public enum GetWindowCmd : uint
+ {
+ ///
+ /// The retrieved handle identifies the window of the same type that is highest in the Z order.
+ ///
+ GW_HWNDFIRST = 0,
+ ///
+ /// The retrieved handle identifies the window of the same type that is lowest in the Z order.
+ ///
+ GW_HWNDLAST = 1,
+ ///
+ /// The retrieved handle identifies the window below the specified window in the Z order.
+ ///
+ GW_HWNDNEXT = 2,
+ ///
+ /// The retrieved handle identifies the window above the specified window in the Z order.
+ ///
+ GW_HWNDPREV = 3,
+ ///
+ /// The retrieved handle identifies the specified window's owner window, if any.
+ ///
+ GW_OWNER = 4,
+ ///
+ /// The retrieved handle identifies the child window at the top of the Z order, if the specified window
+ /// is a parent window.
+ ///
+ GW_CHILD = 5,
+ ///
+ /// The retrieved handle identifies the enabled popup window owned by the specified window.
+ ///
+ GW_ENABLEDPOPUP = 6
+ }
+
+ ///
+ /// GetWindowLong index to retrieves the extended window styles.
+ ///
+ public const int GWL_EXSTYLE = -20;
+
+ ///
+ /// The following are the extended window styles
+ ///
+ [Flags]
+ public enum ExtendedWindowStyles : UInt32
+ {
+ ///
+ /// The window has a double border; the window can, optionally, be created with a title bar by specifying
+ /// the WS_CAPTION style in the dwStyle parameter.
+ ///
+ WS_EX_DLGMODALFRAME = 0X0001,
+ ///
+ /// The child window created with this style does not send the WM_PARENTNOTIFY message to its parent window
+ /// when it is created or destroyed.
+ ///
+ WS_EX_NOPARENTNOTIFY = 0X0004,
+ ///
+ /// The window should be placed above all non-topmost windows and should stay above all non-topmost windows
+ /// and should stay above them, even when the window is deactivated.
+ ///
+ WS_EX_TOPMOST = 0X0008,
+ ///
+ /// The window accepts drag-drop files.
+ ///
+ WS_EX_ACCEPTFILES = 0x0010,
+ ///
+ /// The window should not be painted until siblings beneath the window (that were created by the same thread)
+ /// have been painted.
+ ///
+ WS_EX_TRANSPARENT = 0x0020,
+ ///
+ /// The window is a MDI child window.
+ ///
+ WS_EX_MDICHILD = 0x0040,
+ ///
+ /// The window is intended to be used as a floating toolbar. A tool window has a title bar that is shorter
+ /// than a normal title bar, and the window title is drawn using a smaller font. A tool window does not
+ /// appear in the taskbar or in the dialog that appears when the user presses ALT+TAB.
+ ///
+ WS_EX_TOOLWINDOW = 0x0080,
+ ///
+ /// The window has a border with a raised edge.
+ ///
+ WS_EX_WINDOWEDGE = 0x0100,
+ ///
+ /// The window has a border with a sunken edge.
+ ///
+ WS_EX_CLIENTEDGE = 0x0200,
+ ///
+ /// The title bar of the window includes a question mark.
+ ///
+ WS_EX_CONTEXTHELP = 0x0400,
+ ///
+ /// The window has generic "right-aligned" properties. This depends on the window class. This style has
+ /// an effect only if the shell language supports reading-order alignment, otherwise is ignored.
+ ///
+ WS_EX_RIGHT = 0x1000,
+ ///
+ /// The window has generic left-aligned properties. This is the default.
+ ///
+ WS_EX_LEFT = 0x0,
+ ///
+ /// If the shell language supports reading-order alignment, the window text is displayed using right-to-left
+ /// reading-order properties. For other languages, the styles is ignored.
+ ///
+ WS_EX_RTLREADING = 0x2000,
+ ///
+ /// The window text is displayed using left-to-right reading-order properties. This is the default.
+ ///
+ WS_EX_LTRREADING = 0x0,
+ ///
+ /// If the shell language supports reading order alignment, the vertical scroll bar (if present) is to
+ /// the left of the client area. For other languages, the style is ignored.
+ ///
+ WS_EX_LEFTSCROLLBAR = 0x4000,
+ ///
+ /// The vertical scroll bar (if present) is to the right of the client area. This is the default.
+ ///
+ WS_EX_RIGHTSCROLLBAR = 0x0,
+ ///
+ /// The window itself contains child windows that should take part in dialog box, navigation. If this
+ /// style is specified, the dialog manager recurses into children of this window when performing
+ /// navigation operations such as handling tha TAB key, an arrow key, or a keyboard mnemonic.
+ ///
+ WS_EX_CONTROLPARENT = 0x10000,
+ ///
+ /// The window has a three-dimensional border style intended to be used for items that do not accept
+ /// user input.
+ ///
+ WS_EX_STATICEDGE = 0x20000,
+ ///
+ /// Forces a top-level window onto the taskbar when the window is visible.
+ ///
+ WS_EX_APPWINDOW = 0x40000,
+ ///
+ /// The window is an overlapped window.
+ ///
+ WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE,
+ ///
+ /// The window is palette window, which is a modeless dialog box that presents an array of commands.
+ ///
+ WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
+ ///
+ /// The window is a layered window. This style cannot be used if the window has a class style of either
+ /// CS_OWNDC or CS_CLASSDC. Only for top level window before Windows 8, and child windows from Windows 8.
+ ///
+ WS_EX_LAYERED = 0x80000,
+ ///
+ /// The window does not pass its window layout to its child windows.
+ ///
+ WS_EX_NOINHERITLAYOUT = 0x100000,
+ ///
+ /// If the shell language supports reading order alignment, the horizontal origin of the window is on the
+ /// right edge. Increasing horizontal values advance to the left.
+ ///
+ WS_EX_LAYOUTRTL = 0x400000,
+ ///
+ /// Paints all descendants of a window in bottom-to-top painting order using double-buffering.
+ /// Bottom-to-top painting order allows a descendent window to have translucency (alpha) and
+ /// transparency (color-key) effects, but only if the descendent window also has the WS_EX_TRANSPARENT
+ /// bit set. Double-buffering allows the window and its descendents to be painted without flicker.
+ ///
+ WS_EX_COMPOSITED = 0x2000000,
+ ///
+ /// A top-level window created with this style does not become the foreground window when the user
+ /// clicks it. The system does not bring this window to the foreground when the user minimizes or closes
+ /// the foreground window.
+ ///
+ WS_EX_NOACTIVATE = 0x8000000
+ }
+
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)]
+ public static extern int EnumWindows(CallBackPtr callPtr, int lPar);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern IntPtr GetWindow(IntPtr hWnd, GetWindowCmd uCmd);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
+
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)]
+ public static extern int EnumChildWindows(IntPtr hWnd, CallBackPtr callPtr, int lPar);
+
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)]
+ public static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
+
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)]
+ public static extern int GetWindowTextLength(IntPtr hWnd);
+
+ [DllImport("user32.dll")]
+ public static extern bool IsWindowVisible(IntPtr hWnd);
+
+ [DllImport("user32.dll")]
+ public static extern bool IsWindow(IntPtr hWnd);
+
+ [DllImport("user32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, SetWindowPosFlags uFlags);
+
+ [DllImport("user32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool SetForegroundWindow(IntPtr hWnd);
+
+ [DllImport("user32.dll")]
+ public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);
+
+ [DllImport("user32.dll")]
+ public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ [DllImport("user32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);
+
+ [DllImport("user32.dll")]
+ public static extern bool FlashWindow(IntPtr hwnd, bool bInvert);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
+
+ [DllImport("psapi.dll")]
+ public static extern uint GetProcessImageFileName(IntPtr hProcess, [Out] StringBuilder lpImageFileName, [In] [MarshalAs(UnmanagedType.U4)] int nSize);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern IntPtr GetProp(IntPtr hWnd, string lpString);
+
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId);
+
+ [DllImport("dwmapi.dll", EntryPoint = "#113", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DwmpActivateLivePreview([MarshalAs(UnmanagedType.Bool)]bool fActivate, IntPtr hWndExclude, IntPtr hWndInsertBefore, LivePreviewTrigger lpt, IntPtr prcFinalRect);
+
+ [DllImport("dwmapi.dll", PreserveSig = false)]
+ public static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
+
+ [DllImport("dwmapi.dll", PreserveSig = false)]
+ public static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out int pvAttribute, int cbAttribute);
+
+ [DllImport("user32.dll")]
+ public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl);
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/LivePreview.cs b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/LivePreview.cs
new file mode 100644
index 0000000000..1adf836736
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/LivePreview.cs
@@ -0,0 +1,57 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/
+
+using System;
+
+namespace Wox.Plugin.WindowWalker.Components
+{
+ ///
+ /// Class containg methods to control the live preview
+ ///
+ internal class LivePreview
+ {
+ ///
+ /// Makes sure that a window is excluded from the live preview
+ ///
+ /// handle to the window to exclude
+ public static void SetWindowExlusionFromLivePreview(IntPtr hwnd)
+ {
+ int renderPolicy = (int)InteropAndHelpers.DwmNCRenderingPolicy.Enabled;
+
+ InteropAndHelpers.DwmSetWindowAttribute(
+ hwnd,
+ 12,
+ ref renderPolicy,
+ sizeof(int));
+ }
+
+ ///
+ /// Activates the live preview
+ ///
+ /// the window to show by making all other windows transparent
+ /// the window which should not be transparent but is not the target window
+ public static void ActivateLivePreview(IntPtr targetWindow, IntPtr windowToSpare)
+ {
+ InteropAndHelpers.DwmpActivateLivePreview(
+ true,
+ targetWindow,
+ windowToSpare,
+ InteropAndHelpers.LivePreviewTrigger.Superbar,
+ IntPtr.Zero);
+ }
+
+ ///
+ /// Deactivates the live preview
+ ///
+ public static void DeactivateLivePreview()
+ {
+ InteropAndHelpers.DwmpActivateLivePreview(
+ false,
+ IntPtr.Zero,
+ IntPtr.Zero,
+ InteropAndHelpers.LivePreviewTrigger.AltTab,
+ IntPtr.Zero);
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/OpenWindows.cs b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/OpenWindows.cs
new file mode 100644
index 0000000000..0d36f4a8b9
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/OpenWindows.cs
@@ -0,0 +1,103 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Wox.Plugin.WindowWalker.Components
+{
+ ///
+ /// Class that represents the state of the desktops windows
+ ///
+ internal class OpenWindows
+ {
+ ///
+ /// Delegate handler for open windows updates
+ ///
+ public delegate void OpenWindowsUpdateHandler(object sender, SearchController.SearchResultUpdateEventArgs e);
+
+ ///
+ /// Event raised when there is an update to the list of open windows
+ ///
+ public event OpenWindowsUpdateHandler OnOpenWindowsUpdate;
+
+ ///
+ /// List of all the open windows
+ ///
+ private readonly List windows = new List();
+
+ ///
+ /// An instance of the class OpenWindows
+ ///
+ private static OpenWindows instance;
+
+ ///
+ /// Gets the list of all open windows
+ ///
+ public List Windows
+ {
+ get { return new List(windows); }
+ }
+
+ ///
+ /// Gets an instance property of this class that makes sure that
+ /// the first instance gets created and that all the requests
+ /// end up at that one instance
+ ///
+ public static OpenWindows Instance
+ {
+ get
+ {
+ if (instance == null)
+ {
+ instance = new OpenWindows();
+ }
+
+ return instance;
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Private constructor to make sure there is never
+ /// more than one instance of this class
+ ///
+ private OpenWindows()
+ {
+ }
+
+ ///
+ /// Updates the list of open windows
+ ///
+ public void UpdateOpenWindowsList()
+ {
+ windows.Clear();
+ InteropAndHelpers.CallBackPtr callbackptr = new InteropAndHelpers.CallBackPtr(WindowEnumerationCallBack);
+ InteropAndHelpers.EnumWindows(callbackptr, 0);
+ }
+
+ ///
+ /// Call back method for window enumeration
+ ///
+ /// The handle to the current window being enumerated
+ /// Value being passed from the caller (we don't use this but might come in handy
+ /// in the future
+ /// true to make sure to contiue enumeration
+ public bool WindowEnumerationCallBack(IntPtr hwnd, IntPtr lParam)
+ {
+ Window newWindow = new Window(hwnd);
+
+ if (newWindow.IsWindow && newWindow.Visible && newWindow.IsOwner &&
+ (!newWindow.IsToolWindow || newWindow.IsAppWindow ) && !newWindow.TaskListDeleted &&
+ newWindow.ClassName != "Windows.UI.Core.CoreWindow")
+ {
+ windows.Add(newWindow);
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/SearchController.cs b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/SearchController.cs
new file mode 100644
index 0000000000..0edc3777e9
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/SearchController.cs
@@ -0,0 +1,185 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Wox.Plugin.WindowWalker.Components
+{
+ ///
+ /// Responsible for searching and finding matches for the strings provided.
+ /// Essentially the UI independent model of the application
+ ///
+ internal class SearchController
+ {
+ ///
+ /// the current search text
+ ///
+ private string searchText;
+
+ ///
+ /// Open window search results
+ /// searchMatches;
+
+ ///
+ /// Singleton pattern
+ ///
+ private static SearchController instance;
+
+ ///
+ /// Delegate handler for open windows updates
+ ///
+ public delegate void SearchResultUpdateHandler(object sender, SearchResultUpdateEventArgs e);
+
+ ///
+ /// Event raised when there is an update to the list of open windows
+ ///
+ public event SearchResultUpdateHandler OnSearchResultUpdate;
+
+ ///
+ /// Gets or sets the current search text
+ ///
+ public string SearchText
+ {
+ get
+ {
+ return searchText;
+ }
+
+ set
+ {
+ searchText = value.ToLower().Trim();
+ }
+ }
+
+ ///
+ /// Gets the open window search results
+ ///
+ public List SearchMatches
+ {
+ get { return new List(searchMatches).OrderByDescending(x => x.Score).ToList(); }
+ }
+
+ ///
+ /// Gets singleton Pattern
+ ///
+ public static SearchController Instance
+ {
+ get
+ {
+ if (instance == null)
+ {
+ instance = new SearchController();
+ }
+
+ return instance;
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Initializes the search controller object
+ ///
+ private SearchController()
+ {
+ searchText = string.Empty;
+ OpenWindows.Instance.OnOpenWindowsUpdate += OpenWindowsUpdateHandler;
+ }
+
+ ///
+ /// Event handler for when the search text has been updated
+ ///
+ public async Task UpdateSearchText(string searchText)
+ {
+ this.SearchText = searchText;
+ await SyncOpenWindowsWithModelAsync();
+ }
+
+ ///
+ /// Event handler called when the OpenWindows list changes
+ ///
+ ///
+ ///
+ public async void OpenWindowsUpdateHandler(object sender, SearchResultUpdateEventArgs e)
+ {
+ await SyncOpenWindowsWithModelAsync();
+ }
+
+ ///
+ /// Syncs the open windows with the OpenWindows Model
+ ///
+ public async Task SyncOpenWindowsWithModelAsync()
+ {
+ System.Diagnostics.Debug.Print("Syncing WindowSearch result with OpenWindows Model");
+
+ List snapshotOfOpenWindows = OpenWindows.Instance.Windows;
+
+ if (SearchText == string.Empty)
+ {
+ searchMatches = new List();
+ }
+ else
+ {
+ searchMatches = await FuzzySearchOpenWindowsAsync(snapshotOfOpenWindows);
+ }
+
+ OnSearchResultUpdate?.Invoke(this, new SearchResultUpdateEventArgs());
+ }
+
+ ///
+ /// Redirecting method for Fuzzy searching
+ ///
+ ///
+ /// Returns search results
+ private Task> FuzzySearchOpenWindowsAsync(List openWindows)
+ {
+ return Task.Run(
+ () =>
+ FuzzySearchOpenWindows(openWindows));
+ }
+
+ ///
+ /// Search method that matches the title of windows with the user search text
+ ///
+ ///
+ /// Returns search results
+ private List FuzzySearchOpenWindows(List openWindows)
+ {
+ List result = new List();
+ List searchStrings = new List();
+
+ searchStrings.Add(new SearchString(searchText, SearchResult.SearchType.Fuzzy));
+
+ foreach (var searchString in searchStrings)
+ {
+ foreach (var window in openWindows)
+ {
+ var titleMatch = FuzzyMatching.FindBestFuzzyMatch(window.Title, searchString.SearchText);
+ var processMatch = FuzzyMatching.FindBestFuzzyMatch(window.ProcessName, searchString.SearchText);
+
+ if ((titleMatch.Count != 0 || processMatch.Count != 0) &&
+ window.Title.Length != 0)
+ {
+ var temp = new SearchResult(window, titleMatch, processMatch, searchString.SearchType);
+ result.Add(temp);
+ }
+ }
+ }
+
+ System.Diagnostics.Debug.Print("Found " + result.Count + " windows that match the search text");
+
+ return result;
+ }
+
+ ///
+ /// Event args for a window list update event
+ ///
+ public class SearchResultUpdateEventArgs : EventArgs
+ {
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/SearchResult.cs b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/SearchResult.cs
new file mode 100644
index 0000000000..f27ec80205
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/SearchResult.cs
@@ -0,0 +1,135 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/
+
+using System.Collections.Generic;
+
+namespace Wox.Plugin.WindowWalker.Components
+{
+ ///
+ /// Contains search result windows with each window including the reason why the result was included
+ ///
+ public class SearchResult
+ {
+ ///
+ /// Gets the actual window reference for the search result
+ ///
+ public Window Result
+ {
+ get;
+ private set;
+ }
+
+ ///
+ /// Gets the list of indexes of the matching characters for the search in the title window
+ ///
+ public List SearchMatchesInTitle
+ {
+ get;
+ private set;
+ }
+
+ ///
+ /// Gets the list of indexes of the matching characters for the search in the
+ /// name of the process
+ ///
+ public List SearchMatchesInProcessName
+ {
+ get;
+ private set;
+ }
+
+ ///
+ /// Gets the type of match (shortcut, fuzzy or nothing)
+ ///
+ public SearchType SearchResultMatchType
+ {
+ get;
+ private set;
+ }
+
+ ///
+ /// Gets a score indicating how well this matches what we are looking for
+ ///
+ public int Score
+ {
+ get;
+ private set;
+ }
+
+ ///
+ /// Gets the source of where the best score was found
+ ///
+ public TextType BestScoreSource
+ {
+ get;
+ private set;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Constructor
+ ///
+ public SearchResult(Window window, List matchesInTitle, List matchesInProcessName, SearchType matchType)
+ {
+ Result = window;
+ SearchMatchesInTitle = matchesInTitle;
+ SearchMatchesInProcessName = matchesInProcessName;
+ SearchResultMatchType = matchType;
+ CalculateScore();
+ }
+
+ ///
+ /// Calculates the score for how closely this window matches the search string
+ ///
+ ///
+ /// Higher Score is better
+ ///
+ private void CalculateScore()
+ {
+ if (FuzzyMatching.CalculateScoreForMatches(SearchMatchesInProcessName) >
+ FuzzyMatching.CalculateScoreForMatches(SearchMatchesInTitle))
+ {
+ Score = FuzzyMatching.CalculateScoreForMatches(SearchMatchesInProcessName);
+ BestScoreSource = TextType.ProcessName;
+ }
+ else
+ {
+ Score = FuzzyMatching.CalculateScoreForMatches(SearchMatchesInTitle);
+ BestScoreSource = TextType.WindowTitle;
+ }
+ }
+
+ ///
+ /// The type of text that a string represents
+ ///
+ public enum TextType
+ {
+ ProcessName,
+ WindowTitle,
+ }
+
+ ///
+ /// The type of search
+ ///
+ public enum SearchType
+ {
+ ///
+ /// the search string is empty, which means all open windows are
+ /// going to be returned
+ ///
+ Empty,
+
+ ///
+ /// Regular fuzzy match search
+ ///
+ Fuzzy,
+
+ ///
+ /// The user has entered text that has been matched to a shortcut
+ /// and the shortcut is now being searched
+ ///
+ Shortcut,
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/SearchString.cs b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/SearchString.cs
new file mode 100644
index 0000000000..8e08f8dea7
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/SearchString.cs
@@ -0,0 +1,45 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/
+
+namespace Wox.Plugin.WindowWalker.Components
+{
+ ///
+ /// A class to represent a search string
+ ///
+ /// Class was added inorder to be able to attach various context data to
+ /// a search string
+ internal class SearchString
+ {
+ ///
+ /// Gets where is the search string coming from (is it a shortcut
+ /// or direct string, etc...)
+ ///
+ public SearchResult.SearchType SearchType
+ {
+ get;
+ private set;
+ }
+
+ ///
+ /// Gets the actual text we are searching for
+ ///
+ public string SearchText
+ {
+ get;
+ private set;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Constructor
+ ///
+ ///
+ ///
+ public SearchString(string searchText, SearchResult.SearchType searchType)
+ {
+ SearchText = searchText;
+ SearchType = searchType;
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/Window.cs b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/Window.cs
new file mode 100644
index 0000000000..8916026b97
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/Window.cs
@@ -0,0 +1,401 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Interop;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace Wox.Plugin.WindowWalker.Components
+{
+ ///
+ /// Represents a specific open window
+ ///
+ public class Window
+ {
+ ///
+ /// Maximum size of a file name
+ ///
+ private const int MaximumFileNameLength = 1000;
+
+ ///
+ /// The list of owners of a window so that we don't have to
+ /// constantly query for the process owning a specific window
+ ///
+ private static readonly Dictionary _handlesToProcessCache = new Dictionary();
+
+ ///
+ /// The list of icons from process so that we don't have to keep
+ /// loading them from disk
+ ///
+ private static readonly Dictionary _processIdsToIconsCache = new Dictionary();
+
+ ///
+ /// The handle to the window
+ ///
+ private readonly IntPtr hwnd;
+
+ ///
+ /// Gets the title of the window (the string displayed at the top of the window)
+ ///
+ public string Title
+ {
+ get
+ {
+ int sizeOfTitle = InteropAndHelpers.GetWindowTextLength(hwnd);
+ if (sizeOfTitle++ > 0)
+ {
+ StringBuilder titleBuffer = new StringBuilder(sizeOfTitle);
+ InteropAndHelpers.GetWindowText(hwnd, titleBuffer, sizeOfTitle);
+ return titleBuffer.ToString();
+ }
+ else
+ {
+ return string.Empty;
+ }
+ }
+ }
+
+ ///
+ /// Gets the handle to the window
+ ///
+ public IntPtr Hwnd
+ {
+ get { return hwnd; }
+ }
+
+ public uint ProcessID { get; set; }
+
+ ///
+ /// Gets returns the name of the process
+ ///
+ public string ProcessName
+ {
+ get
+ {
+ lock (_handlesToProcessCache)
+ {
+ if (_handlesToProcessCache.Count > 7000)
+ {
+ Debug.Print("Clearing Process Cache because it's size is " + _handlesToProcessCache.Count);
+ _handlesToProcessCache.Clear();
+ }
+
+ if (!_handlesToProcessCache.ContainsKey(Hwnd))
+ {
+ var processName = GetProcessNameFromWindowHandle(Hwnd);
+
+ if (processName.Length != 0)
+ {
+ _handlesToProcessCache.Add(
+ Hwnd,
+ processName.ToString().Split('\\').Reverse().ToArray()[0]);
+ }
+ else
+ {
+ _handlesToProcessCache.Add(Hwnd, string.Empty);
+ }
+ }
+
+ if (_handlesToProcessCache[hwnd].ToLower() == "applicationframehost.exe")
+ {
+ new Task(() =>
+ {
+ InteropAndHelpers.CallBackPtr callbackptr = new InteropAndHelpers.CallBackPtr((IntPtr hwnd, IntPtr lParam) =>
+ {
+ var childProcessId = GetProcessIDFromWindowHandle(hwnd);
+ if (childProcessId != this.ProcessID)
+ {
+ _handlesToProcessCache[Hwnd] = GetProcessNameFromWindowHandle(hwnd);
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ });
+ InteropAndHelpers.EnumChildWindows(Hwnd, callbackptr, 0);
+ }).Start();
+ }
+
+ return _handlesToProcessCache[hwnd];
+ }
+ }
+ }
+
+ ///
+ /// Gets returns the name of the class for the window represented
+ ///
+ public string ClassName
+ {
+ get
+ {
+ StringBuilder windowClassName = new StringBuilder(300);
+ InteropAndHelpers.GetClassName(Hwnd, windowClassName, windowClassName.MaxCapacity);
+
+ return windowClassName.ToString();
+ }
+ }
+
+ ///
+ /// Gets represents the Window Icon for the specified window
+ ///
+ public ImageSource WindowIcon
+ {
+ get
+ {
+ lock (_processIdsToIconsCache)
+ {
+ InteropAndHelpers.GetWindowThreadProcessId(Hwnd, out uint processId);
+
+ if (!_processIdsToIconsCache.ContainsKey(processId))
+ {
+ try
+ {
+ Process process = Process.GetProcessById((int)processId);
+ Icon tempIcon = Icon.ExtractAssociatedIcon(process.Modules[0].FileName);
+ _processIdsToIconsCache.Add(processId, Imaging.CreateBitmapSourceFromHIcon(
+ tempIcon.Handle,
+ Int32Rect.Empty,
+ BitmapSizeOptions.FromEmptyOptions()));
+ }
+ catch
+ {
+ BitmapImage failedImage = new BitmapImage(new Uri(@"Images\failedIcon.jpg", UriKind.Relative));
+ _processIdsToIconsCache.Add(processId, failedImage);
+ }
+ }
+
+ return _processIdsToIconsCache[processId];
+ }
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether is the window visible (might return false if it is a hidden IE tab)
+ ///
+ public bool Visible
+ {
+ get
+ {
+ return InteropAndHelpers.IsWindowVisible(Hwnd);
+ }
+ }
+
+ ///
+ /// Determines whether the specified window handle identifies an existing window.
+ ///
+ public bool IsWindow
+ {
+ get
+ {
+ return InteropAndHelpers.IsWindow(Hwnd);
+ }
+ }
+
+ ///
+ /// Get a value indicating whether is the window GWL_EX_STYLE is a toolwindow
+ ///
+ public bool IsToolWindow
+ {
+ get
+ {
+ return (InteropAndHelpers.GetWindowLong(Hwnd, InteropAndHelpers.GWL_EXSTYLE) &
+ (uint)InteropAndHelpers.ExtendedWindowStyles.WS_EX_TOOLWINDOW) ==
+ (uint)InteropAndHelpers.ExtendedWindowStyles.WS_EX_TOOLWINDOW;
+ }
+ }
+
+ ///
+ /// Get a value indicating whether the window GWL_EX_STYLE is an appwindow
+ ///
+ public bool IsAppWindow
+ {
+ get
+ {
+ return (InteropAndHelpers.GetWindowLong(Hwnd, InteropAndHelpers.GWL_EXSTYLE) &
+ (uint)InteropAndHelpers.ExtendedWindowStyles.WS_EX_APPWINDOW) ==
+ (uint)InteropAndHelpers.ExtendedWindowStyles.WS_EX_APPWINDOW;
+ }
+ }
+
+ ///
+ /// Get a value indicating whether the window has ITaskList_Deleted property
+ ///
+ public bool TaskListDeleted
+ {
+ get
+ {
+ return InteropAndHelpers.GetProp(Hwnd, "ITaskList_Deleted") != IntPtr.Zero;
+ }
+ }
+
+ ///
+ /// Get a value indicating whether the app is a cloaked UWP app
+ ///
+ public bool IsUWPCloaked
+ {
+ get
+ {
+ return (this.IsWindowCloaked() && this.ClassName == "ApplicationFrameWindow");
+ }
+ }
+
+ ///
+ /// Determines whether the specified windows is the owner
+ ///
+ public bool IsOwner
+ {
+ get
+ {
+ return InteropAndHelpers.GetWindow(Hwnd, InteropAndHelpers.GetWindowCmd.GW_OWNER) != null;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether is the window cloaked. To detect UWP apps in background or win32 apps running in another virtual desktop
+ ///
+ public bool IsWindowCloaked()
+ {
+ int isCloaked = 0;
+ const int DWMWA_CLOAKED = 14;
+ InteropAndHelpers.DwmGetWindowAttribute(this.hwnd, DWMWA_CLOAKED, out isCloaked, sizeof(int));
+ return isCloaked != 0;
+ }
+
+ ///
+ /// Gets a value indicating whether returns true if the window is minimized
+ ///
+ public bool Minimized
+ {
+ get
+ {
+ return GetWindowSizeState() == WindowSizeState.Minimized;
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Initializes a new Window representation
+ ///
+ /// the handle to the window we are representing
+ public Window(IntPtr hwnd)
+ {
+ // TODO: Add verification as to whether the window handle is valid
+ this.hwnd = hwnd;
+ }
+
+ ///
+ /// Highlights a window to help the user identify the window that has been selected
+ ///
+ public void HighlightWindow()
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Switches dekstop focus to the window
+ ///
+ public void SwitchToWindow()
+ {
+ // The following block is necessary because
+ // 1) There is a weird flashing behaviour when trying
+ // to use ShowWindow for switching tabs in IE
+ // 2) SetForegroundWindow fails on minimized windows
+ if (ProcessName.ToLower().Equals("iexplore.exe") || !Minimized)
+ {
+ InteropAndHelpers.SetForegroundWindow(Hwnd);
+ }
+ else
+ {
+ InteropAndHelpers.ShowWindow(Hwnd, InteropAndHelpers.ShowWindowCommands.Restore);
+ }
+
+ InteropAndHelpers.FlashWindow(Hwnd, true);
+ }
+
+ ///
+ /// Converts the window name to string along with the process name
+ ///
+ /// The title of the window
+ public override string ToString()
+ {
+ return Title + " (" + ProcessName.ToUpper() + ")";
+ }
+
+ ///
+ /// Returns what the window size is
+ ///
+ /// The state (minimized, maximized, etc..) of the window
+ public WindowSizeState GetWindowSizeState()
+ {
+ InteropAndHelpers.GetWindowPlacement(Hwnd, out InteropAndHelpers.WINDOWPLACEMENT placement);
+
+ switch (placement.ShowCmd)
+ {
+ case InteropAndHelpers.ShowWindowCommands.Normal:
+ return WindowSizeState.Normal;
+ case InteropAndHelpers.ShowWindowCommands.Minimize:
+ case InteropAndHelpers.ShowWindowCommands.ShowMinimized:
+ return WindowSizeState.Minimized;
+ case InteropAndHelpers.ShowWindowCommands.Maximize: // No need for ShowMaximized here since its also of value 3
+ return WindowSizeState.Maximized;
+ default:
+ // throw new Exception("Don't know how to handle window state = " + placement.ShowCmd);
+ return WindowSizeState.Unknown;
+ }
+ }
+
+ ///
+ /// Enum to simplify the state of the window
+ ///
+ public enum WindowSizeState
+ {
+ Normal,
+ Minimized,
+ Maximized,
+ Unknown,
+ }
+
+ ///
+ /// Gets the name of the process using the window handle
+ ///
+ /// The handle to the window
+ /// A string representing the process name or an empty string if the function fails
+ private string GetProcessNameFromWindowHandle(IntPtr hwnd)
+ {
+ uint processId = GetProcessIDFromWindowHandle(hwnd);
+ ProcessID = processId;
+ IntPtr processHandle = InteropAndHelpers.OpenProcess(InteropAndHelpers.ProcessAccessFlags.AllAccess, true, (int)processId);
+ StringBuilder processName = new StringBuilder(MaximumFileNameLength);
+
+ if (InteropAndHelpers.GetProcessImageFileName(processHandle, processName, MaximumFileNameLength) != 0)
+ {
+ return processName.ToString().Split('\\').Reverse().ToArray()[0];
+ }
+ else
+ {
+ return string.Empty;
+ }
+ }
+
+ ///
+ /// Gets the process ID for the Window handle
+ ///
+ /// The handle to the window
+ /// The process ID
+ private uint GetProcessIDFromWindowHandle(IntPtr hwnd)
+ {
+ InteropAndHelpers.GetWindowThreadProcessId(hwnd, out uint processId);
+ return processId;
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/WindowResult.cs b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/WindowResult.cs
new file mode 100644
index 0000000000..c7f790b596
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Components/WindowResult.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Wox.Plugin.WindowWalker.Components
+{
+ class WindowResult:Window
+ {
+ ///
+ /// Number of letters in between constant for when
+ /// the result hasn't been set yet
+ ///
+ public const int NoResult = -1;
+
+ ///
+ /// Properties that signify how many characters (including spaces)
+ /// were found when matching the results
+ ///
+ public int LettersInBetweenScore
+ {
+ get;
+ set;
+ }
+
+ ///
+ /// Constructor for WindowResult
+ ///
+ public WindowResult(Window window):base(window.Hwnd)
+ {
+ LettersInBetweenScore = WindowResult.NoResult;
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Images/windowwalker.png b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Images/windowwalker.png
new file mode 100644
index 0000000000..3bf9ae17d0
Binary files /dev/null and b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Images/windowwalker.png differ
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Languages/en.xaml b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Languages/en.xaml
new file mode 100644
index 0000000000..48fb7976e9
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Languages/en.xaml
@@ -0,0 +1,7 @@
+
+
+ Window Walker
+ Alt-Tab alternative enabling searching through your windows.
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Main.cs b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Main.cs
new file mode 100644
index 0000000000..7c5da70c94
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Main.cs
@@ -0,0 +1,57 @@
+using System.Collections.Generic;
+using System.Linq;
+using Wox.Plugin.WindowWalker.Components;
+
+namespace Wox.Plugin.WindowWalker
+{
+ public class Main : IPlugin, IPluginI18n
+ {
+ private static List _results = new List();
+ private static string IcoPath = "Images/windowwalker.png";
+ private PluginInitContext Context { get; set; }
+
+ static Main()
+ {
+ SearchController.Instance.OnSearchResultUpdate += SearchResultUpdated;
+ OpenWindows.Instance.UpdateOpenWindowsList();
+ }
+
+ public List Query(Query query)
+ {
+ SearchController.Instance.UpdateSearchText(query.RawQuery).Wait();
+ OpenWindows.Instance.UpdateOpenWindowsList();
+ return _results.Select(x => new Result()
+ {
+ Title = x.Result.Title,
+ IcoPath = IcoPath,
+ SubTitle = x.Result.ProcessName,
+ Action = c =>
+ {
+ x.Result.SwitchToWindow();
+ return true;
+ }
+ }
+ ).ToList();
+ }
+
+ public void Init(PluginInitContext context)
+ {
+ Context = context;
+ }
+
+ public string GetTranslatedPluginTitle()
+ {
+ return Context.API.GetTranslation("wox_plugin_windowwalker_plugin_name");
+ }
+
+ public string GetTranslatedPluginDescription()
+ {
+ return Context.API.GetTranslation("wox_plugin_windowwalker_plugin_description");
+ }
+
+ private static void SearchResultUpdated(object sender, SearchController.SearchResultUpdateEventArgs e)
+ {
+ _results = SearchController.Instance.SearchMatches;
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Wox.Plugin.WindowWalker.csproj b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Wox.Plugin.WindowWalker.csproj
new file mode 100644
index 0000000000..0e0b414355
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/Wox.Plugin.WindowWalker.csproj
@@ -0,0 +1,71 @@
+
+
+
+ netcoreapp3.1
+ {74F1B9ED-F59C-4FE7-B473-7B453E30837E}
+ Properties
+ Wox.Plugin.WindowWalker
+ Wox.Plugin.WindowWalker
+ true
+ false
+ false
+ x64
+
+
+
+ true
+ ..\..\..\..\..\x64\Debug\modules\launcher\Plugins\Wox.Plugin.WindowWalker\
+ DEBUG;TRACE
+ full
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+ false
+
+
+
+ ..\..\..\..\..\x64\Release\modules\launcher\Plugins\Wox.Plugin.WindowWalker\
+ TRACE
+ true
+ pdbonly
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/plugin.json b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/plugin.json
new file mode 100644
index 0000000000..6289cc70e6
--- /dev/null
+++ b/src/modules/launcher/Plugins/Wox.Plugin.WindowWalker/plugin.json
@@ -0,0 +1,12 @@
+{
+ "ID":"CEA0FDFC6D3B4085823D60DC76F28855",
+ "ActionKeyword":"*",
+ "Name":"Window Walker",
+ "Description":"Alt-Tab alternative enabling searching through your windows.",
+ "Author":"betadele",
+ "Version":"1.0.0",
+ "Language":"csharp",
+ "Website":"http://www.windowwalker.com/",
+ "ExecuteFileName":"Wox.Plugin.WindowWalker.dll",
+ "IcoPath":"Images\\windowwalker.png"
+}
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher.UI/App.xaml b/src/modules/launcher/PowerLauncher.UI/App.xaml
new file mode 100644
index 0000000000..d40a829ba0
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher.UI/App.xaml
@@ -0,0 +1,306 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #FF979797
+ 8
+
+
+
+
+
+
+
+
+ 8
+ #FF979797
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher.UI/App.xaml.cs b/src/modules/launcher/PowerLauncher.UI/App.xaml.cs
new file mode 100644
index 0000000000..3e96413d1d
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher.UI/App.xaml.cs
@@ -0,0 +1,10 @@
+namespace PowerLauncher.UI
+{
+ public sealed partial class App : Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication
+ {
+ public App()
+ {
+ this.Initialize();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/LargeTile.scale-100.png b/src/modules/launcher/PowerLauncher.UI/Assets/LargeTile.scale-100.png
new file mode 100644
index 0000000000..93b935fa0e
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/LargeTile.scale-100.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/LargeTile.scale-125.png b/src/modules/launcher/PowerLauncher.UI/Assets/LargeTile.scale-125.png
new file mode 100644
index 0000000000..7678b5a6f3
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/LargeTile.scale-125.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/LargeTile.scale-150.png b/src/modules/launcher/PowerLauncher.UI/Assets/LargeTile.scale-150.png
new file mode 100644
index 0000000000..32bf775619
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/LargeTile.scale-150.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/LargeTile.scale-200.png b/src/modules/launcher/PowerLauncher.UI/Assets/LargeTile.scale-200.png
new file mode 100644
index 0000000000..62befcfda5
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/LargeTile.scale-200.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/LargeTile.scale-400.png b/src/modules/launcher/PowerLauncher.UI/Assets/LargeTile.scale-400.png
new file mode 100644
index 0000000000..ca9290097a
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/LargeTile.scale-400.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/LockScreenLogo.scale-200.png b/src/modules/launcher/PowerLauncher.UI/Assets/LockScreenLogo.scale-200.png
new file mode 100644
index 0000000000..735f57adb5
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/LockScreenLogo.scale-200.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/SmallTile.scale-100.png b/src/modules/launcher/PowerLauncher.UI/Assets/SmallTile.scale-100.png
new file mode 100644
index 0000000000..128ec44703
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/SmallTile.scale-100.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/SmallTile.scale-125.png b/src/modules/launcher/PowerLauncher.UI/Assets/SmallTile.scale-125.png
new file mode 100644
index 0000000000..adab046be3
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/SmallTile.scale-125.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/SmallTile.scale-150.png b/src/modules/launcher/PowerLauncher.UI/Assets/SmallTile.scale-150.png
new file mode 100644
index 0000000000..54ea9de7bb
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/SmallTile.scale-150.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/SmallTile.scale-200.png b/src/modules/launcher/PowerLauncher.UI/Assets/SmallTile.scale-200.png
new file mode 100644
index 0000000000..231345d621
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/SmallTile.scale-200.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/SmallTile.scale-400.png b/src/modules/launcher/PowerLauncher.UI/Assets/SmallTile.scale-400.png
new file mode 100644
index 0000000000..441b7ddf5c
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/SmallTile.scale-400.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/SplashScreen.scale-100.png b/src/modules/launcher/PowerLauncher.UI/Assets/SplashScreen.scale-100.png
new file mode 100644
index 0000000000..6751461aa2
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/SplashScreen.scale-100.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/SplashScreen.scale-125.png b/src/modules/launcher/PowerLauncher.UI/Assets/SplashScreen.scale-125.png
new file mode 100644
index 0000000000..06b8072329
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/SplashScreen.scale-125.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/SplashScreen.scale-150.png b/src/modules/launcher/PowerLauncher.UI/Assets/SplashScreen.scale-150.png
new file mode 100644
index 0000000000..1c71d0bf48
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/SplashScreen.scale-150.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/SplashScreen.scale-200.png b/src/modules/launcher/PowerLauncher.UI/Assets/SplashScreen.scale-200.png
new file mode 100644
index 0000000000..f9078aa78b
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/SplashScreen.scale-200.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/SplashScreen.scale-400.png b/src/modules/launcher/PowerLauncher.UI/Assets/SplashScreen.scale-400.png
new file mode 100644
index 0000000000..b811db999e
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/SplashScreen.scale-400.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square150x150Logo.scale-100.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square150x150Logo.scale-100.png
new file mode 100644
index 0000000000..ca5e703c82
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square150x150Logo.scale-100.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square150x150Logo.scale-125.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square150x150Logo.scale-125.png
new file mode 100644
index 0000000000..39b786d1a4
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square150x150Logo.scale-125.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square150x150Logo.scale-150.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square150x150Logo.scale-150.png
new file mode 100644
index 0000000000..6e955d168b
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square150x150Logo.scale-150.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square150x150Logo.scale-200.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square150x150Logo.scale-200.png
new file mode 100644
index 0000000000..c3011b37e8
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square150x150Logo.scale-200.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square150x150Logo.scale-400.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square150x150Logo.scale-400.png
new file mode 100644
index 0000000000..334b93f280
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square150x150Logo.scale-400.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png
new file mode 100644
index 0000000000..e38a73b6af
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png
new file mode 100644
index 0000000000..2d9ed35fdb
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png
new file mode 100644
index 0000000000..5be53047fe
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png
new file mode 100644
index 0000000000..0569d4c084
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png
new file mode 100644
index 0000000000..ae5b7466bc
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-unplated_targetsize-16.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-unplated_targetsize-16.png
new file mode 100644
index 0000000000..e38a73b6af
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-unplated_targetsize-16.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-unplated_targetsize-256.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-unplated_targetsize-256.png
new file mode 100644
index 0000000000..5be53047fe
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-unplated_targetsize-256.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-unplated_targetsize-32.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-unplated_targetsize-32.png
new file mode 100644
index 0000000000..0569d4c084
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-unplated_targetsize-32.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-unplated_targetsize-48.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-unplated_targetsize-48.png
new file mode 100644
index 0000000000..ae5b7466bc
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.altform-unplated_targetsize-48.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.scale-100.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.scale-100.png
new file mode 100644
index 0000000000..af65038a1f
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.scale-100.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.scale-125.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.scale-125.png
new file mode 100644
index 0000000000..352b81495d
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.scale-125.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.scale-150.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.scale-150.png
new file mode 100644
index 0000000000..1097fe0a7f
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.scale-150.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.scale-200.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.scale-200.png
new file mode 100644
index 0000000000..3ac5789869
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.scale-200.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.scale-400.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.scale-400.png
new file mode 100644
index 0000000000..680f6c25d1
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.scale-400.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.targetsize-16.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.targetsize-16.png
new file mode 100644
index 0000000000..93e7d2363b
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.targetsize-16.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.targetsize-24.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.targetsize-24.png
new file mode 100644
index 0000000000..c1bdf83a6d
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.targetsize-24.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.targetsize-24_altform-unplated.png
new file mode 100644
index 0000000000..5b89ff687f
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.targetsize-256.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.targetsize-256.png
new file mode 100644
index 0000000000..d751f6e2d9
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.targetsize-256.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.targetsize-32.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.targetsize-32.png
new file mode 100644
index 0000000000..fbe8df3743
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.targetsize-32.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.targetsize-48.png b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.targetsize-48.png
new file mode 100644
index 0000000000..9e421fe380
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Square44x44Logo.targetsize-48.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.backup.png b/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.backup.png
new file mode 100644
index 0000000000..7385b56c0e
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.backup.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.png b/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.png
new file mode 100644
index 0000000000..7385b56c0e
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.scale-100.png b/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.scale-100.png
new file mode 100644
index 0000000000..871d8b277b
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.scale-100.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.scale-125.png b/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.scale-125.png
new file mode 100644
index 0000000000..1dad0ab75d
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.scale-125.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.scale-150.png b/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.scale-150.png
new file mode 100644
index 0000000000..59e7a826f8
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.scale-150.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.scale-200.png b/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.scale-200.png
new file mode 100644
index 0000000000..0f615be581
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.scale-200.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.scale-400.png b/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.scale-400.png
new file mode 100644
index 0000000000..dfaf92c7dc
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/StoreLogo.scale-400.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Wide310x150Logo.scale-100.png b/src/modules/launcher/PowerLauncher.UI/Assets/Wide310x150Logo.scale-100.png
new file mode 100644
index 0000000000..1b49790eca
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Wide310x150Logo.scale-100.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Wide310x150Logo.scale-125.png b/src/modules/launcher/PowerLauncher.UI/Assets/Wide310x150Logo.scale-125.png
new file mode 100644
index 0000000000..eb56ac9e45
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Wide310x150Logo.scale-125.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Wide310x150Logo.scale-150.png b/src/modules/launcher/PowerLauncher.UI/Assets/Wide310x150Logo.scale-150.png
new file mode 100644
index 0000000000..c30052f1ea
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Wide310x150Logo.scale-150.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Wide310x150Logo.scale-200.png b/src/modules/launcher/PowerLauncher.UI/Assets/Wide310x150Logo.scale-200.png
new file mode 100644
index 0000000000..6751461aa2
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Wide310x150Logo.scale-200.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Assets/Wide310x150Logo.scale-400.png b/src/modules/launcher/PowerLauncher.UI/Assets/Wide310x150Logo.scale-400.png
new file mode 100644
index 0000000000..f9078aa78b
Binary files /dev/null and b/src/modules/launcher/PowerLauncher.UI/Assets/Wide310x150Logo.scale-400.png differ
diff --git a/src/modules/launcher/PowerLauncher.UI/Behaviors/CloseFlyoutAction.cs b/src/modules/launcher/PowerLauncher.UI/Behaviors/CloseFlyoutAction.cs
new file mode 100644
index 0000000000..419c67e1ec
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher.UI/Behaviors/CloseFlyoutAction.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Xaml.Interactivity;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Media;
+
+namespace PowerToysUX.Behaviors
+{
+ public class CloseFlyoutAction : DependencyObject, IAction
+ {
+ public object Execute(object sender, object parameter)
+ {
+ var parent = TargetObject ?? sender as DependencyObject;
+ while (parent != null)
+ {
+ if (parent is FlyoutPresenter)
+ {
+ ((parent as FlyoutPresenter).Parent as Popup).IsOpen = false;
+ break;
+ }
+ else
+ {
+ parent = VisualTreeHelper.GetParent(parent);
+ }
+ }
+ return null;
+ }
+
+ public Control TargetObject
+ {
+ get { return (Control)GetValue(TargetObjectProperty); }
+ set { SetValue(TargetObjectProperty, value); }
+ }
+ public static readonly DependencyProperty TargetObjectProperty =
+ DependencyProperty.Register(nameof(TargetObject), typeof(Control), typeof(CloseFlyoutAction), new PropertyMetadata(null));
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher.UI/Behaviors/OpenFlyoutAction.cs b/src/modules/launcher/PowerLauncher.UI/Behaviors/OpenFlyoutAction.cs
new file mode 100644
index 0000000000..b5f9db824a
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher.UI/Behaviors/OpenFlyoutAction.cs
@@ -0,0 +1,24 @@
+using Microsoft.Xaml.Interactivity;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+
+namespace PowerToysUX.Behaviours
+{
+ public class OpenFlyoutAction : DependencyObject, IAction
+ {
+ public object Execute(object sender, object parameter)
+ {
+ FlyoutBase.ShowAttachedFlyout(TargetObject ?? (FrameworkElement)sender);
+ return null;
+ }
+
+ public Control TargetObject
+ {
+ get { return (Control)GetValue(TargetObjectProperty); }
+ set { SetValue(TargetObjectProperty, value); }
+ }
+ public static readonly DependencyProperty TargetObjectProperty =
+ DependencyProperty.Register(nameof(TargetObject), typeof(Control), typeof(OpenFlyoutAction), new PropertyMetadata(null));
+ }
+}
diff --git a/src/modules/launcher/PowerLauncher.UI/LauncherControl.xaml b/src/modules/launcher/PowerLauncher.UI/LauncherControl.xaml
new file mode 100644
index 0000000000..6db4be5db7
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher.UI/LauncherControl.xaml
@@ -0,0 +1,386 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Visible
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher.UI/LauncherControl.xaml.cs b/src/modules/launcher/PowerLauncher.UI/LauncherControl.xaml.cs
new file mode 100644
index 0000000000..1713974da9
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher.UI/LauncherControl.xaml.cs
@@ -0,0 +1,50 @@
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Media;
+
+namespace PowerLauncher.UI
+{
+ public sealed partial class LauncherControl : UserControl, INotifyPropertyChanged
+ {
+ private Brush _borderBrush;
+
+ public LauncherControl()
+ {
+ InitializeComponent();
+ }
+
+ public Brush SolidBorderBrush
+ {
+ get { return _borderBrush; }
+ set { Set(ref _borderBrush, value); }
+ }
+
+ private void Set(ref T storage, T value, [CallerMemberName]string propertyName = null)
+ {
+ if (Equals(storage, value))
+ {
+ return;
+ }
+
+ storage = value;
+ OnPropertyChanged(propertyName);
+ }
+
+ private void UserControl_ActualThemeChanged(FrameworkElement sender, object args)
+ {
+ SolidBorderBrush = Application.Current.Resources["SystemChromeLow"] as SolidColorBrush;
+ }
+
+ private void UserControl_Loaded(object sender, RoutedEventArgs e)
+ {
+ SolidBorderBrush = Application.Current.Resources["SystemChromeLow"] as SolidColorBrush;
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ private void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher.UI/MainPage.xaml b/src/modules/launcher/PowerLauncher.UI/MainPage.xaml
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/modules/launcher/PowerLauncher.UI/Package.appxmanifest b/src/modules/launcher/PowerLauncher.UI/Package.appxmanifest
new file mode 100644
index 0000000000..89ff629cd3
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher.UI/Package.appxmanifest
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+ PowerLauncher.UI
+ bakudies
+ Assets\StoreLogo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher.UI/PowerLauncher.UI.csproj b/src/modules/launcher/PowerLauncher.UI/PowerLauncher.UI.csproj
new file mode 100644
index 0000000000..1cebeb6ee4
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher.UI/PowerLauncher.UI.csproj
@@ -0,0 +1,124 @@
+
+
+
+
+ Debug
+ x64
+ {4A3DE70C-684C-410D-B851-C23B6DAEDF16}
+ AppContainerExe
+ Properties
+ PowerLauncher.UI
+ PowerLauncher.UI
+ en-US
+ UAP
+ 10.0.18362.0
+ 10.0.18362.0
+ 14
+ 512
+ {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ true
+ false
+
+
+ true
+ bin\x64\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ x64
+ false
+ prompt
+ true
+
+
+ bin\x64\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ x64
+ false
+ prompt
+ true
+ true
+
+
+ PackageReference
+
+
+
+ App.xaml
+
+
+ LauncherControl.xaml
+
+
+
+ ResultList.xaml
+
+
+
+
+ Designer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+ MSBuild:Compile
+ Designer
+
+
+ Designer
+ MSBuild:Compile
+
+
+
+
+ 6.2.10
+
+
+ 6.0.0
+
+
+ 6.0.0
+
+
+ 6.0.0
+
+
+ 6.0.1
+
+
+ 2.4.0-prerelease.200322001
+
+
+
+ 14.0
+
+
+
+
+ false
+ false
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher.UI/Properties/AssemblyInfo.cs b/src/modules/launcher/PowerLauncher.UI/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..7386690a20
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher.UI/Properties/AssemblyInfo.cs
@@ -0,0 +1,29 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("PowerLauncher.UI")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("PowerLauncher.UI")]
+[assembly: AssemblyCopyright("Copyright © 2020")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: ComVisible(false)]
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher.UI/Properties/Default.rd.xml b/src/modules/launcher/PowerLauncher.UI/Properties/Default.rd.xml
new file mode 100644
index 0000000000..af00722cdf
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher.UI/Properties/Default.rd.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher.UI/ResultList.xaml b/src/modules/launcher/PowerLauncher.UI/ResultList.xaml
new file mode 100644
index 0000000000..fc912c4989
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher.UI/ResultList.xaml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher.UI/ResultList.xaml.cs b/src/modules/launcher/PowerLauncher.UI/ResultList.xaml.cs
new file mode 100644
index 0000000000..72a87caa4c
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher.UI/ResultList.xaml.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Foundation;
+using Windows.UI.Xaml.Controls;
+
+namespace PowerLauncher.UI
+{
+ public sealed partial class ResultList : UserControl
+ {
+ public ResultList()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/src/modules/launcher/PowerLauncher/ActionKeywords.xaml b/src/modules/launcher/PowerLauncher/ActionKeywords.xaml
new file mode 100644
index 0000000000..ffe1b5400b
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/ActionKeywords.xaml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Old ActionKeywords:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/ActionKeywords.xaml.cs b/src/modules/launcher/PowerLauncher/ActionKeywords.xaml.cs
new file mode 100644
index 0000000000..68024b1270
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/ActionKeywords.xaml.cs
@@ -0,0 +1,58 @@
+using System.Windows;
+using Wox.Core.Plugin;
+using Wox.Core.Resource;
+using Wox.Infrastructure.Exception;
+using Wox.Infrastructure.UserSettings;
+using Wox.Plugin;
+
+namespace Wox
+{
+ public partial class ActionKeywords : Window
+ {
+ private PluginPair _plugin;
+ private Settings _settings;
+ private readonly Internationalization _translater = InternationalizationManager.Instance;
+
+ public ActionKeywords(string pluginId, Settings settings)
+ {
+ InitializeComponent();
+ _plugin = PluginManager.GetPluginForId(pluginId);
+ _settings = settings;
+ if (_plugin == null)
+ {
+ MessageBox.Show(_translater.GetTranslation("cannotFindSpecifiedPlugin"));
+ Close();
+ }
+ }
+
+ private void ActionKeyword_OnLoaded(object sender, RoutedEventArgs e)
+ {
+ tbOldActionKeyword.Text = string.Join(Query.ActionKeywordSeperater, _plugin.Metadata.ActionKeywords.ToArray());
+ tbAction.Focus();
+ }
+
+ private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
+ {
+ Close();
+ }
+
+ private void btnDone_OnClick(object sender, RoutedEventArgs _)
+ {
+ var oldActionKeyword = _plugin.Metadata.ActionKeywords[0];
+ var newActionKeyword = tbAction.Text.Trim();
+ newActionKeyword = newActionKeyword.Length > 0 ? newActionKeyword : "*";
+ if (!PluginManager.ActionKeywordRegistered(newActionKeyword))
+ {
+ var id = _plugin.Metadata.ID;
+ PluginManager.ReplaceActionKeyword(id, oldActionKeyword, newActionKeyword);
+ MessageBox.Show(_translater.GetTranslation("success"));
+ Close();
+ }
+ else
+ {
+ string msg = _translater.GetTranslation("newActionKeywordsHasBeenAssigned");
+ MessageBox.Show(msg);
+ }
+ }
+ }
+}
diff --git a/src/modules/launcher/PowerLauncher/App.xaml b/src/modules/launcher/PowerLauncher/App.xaml
new file mode 100644
index 0000000000..ec92b631a4
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/App.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/App.xaml.cs b/src/modules/launcher/PowerLauncher/App.xaml.cs
new file mode 100644
index 0000000000..1e8e5c1d86
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/App.xaml.cs
@@ -0,0 +1,147 @@
+using System;
+using System.Diagnostics;
+using System.Threading.Tasks;
+using System.Timers;
+using System.Windows;
+using Wox;
+using Wox.Core;
+using Wox.Core.Plugin;
+using Wox.Core.Resource;
+using Wox.Helper;
+using Wox.Infrastructure;
+using Wox.Infrastructure.Http;
+using Wox.Infrastructure.Image;
+using Wox.Infrastructure.Logger;
+using Wox.Infrastructure.UserSettings;
+using Wox.ViewModel;
+using Stopwatch = Wox.Infrastructure.Stopwatch;
+
+namespace PowerLauncher
+{
+
+
+ public partial class App : IDisposable, ISingleInstanceApp
+ {
+ public static PublicAPIInstance API { get; private set; }
+ private const string Unique = "PowerLauncher_Unique_Application_Mutex";
+ private static bool _disposed;
+ private Settings _settings;
+ private MainViewModel _mainVM;
+ private SettingWindowViewModel _settingsVM;
+ private readonly Alphabet _alphabet = new Alphabet();
+ private StringMatcher _stringMatcher;
+ private SettingsWatcher _settingsWatcher;
+
+ [STAThread]
+ public static void Main()
+ {
+ if (SingleInstance.InitializeAsFirstInstance(Unique))
+ {
+ using (new UI.App())
+ {
+ using (var application = new App())
+ {
+ application.InitializeComponent();
+ application.Run();
+ }
+ }
+ }
+ }
+
+ private void OnStartup(object sender, StartupEventArgs e)
+ {
+ Stopwatch.Normal("|App.OnStartup|Startup cost", () =>
+ {
+ Log.Info("|App.OnStartup|Begin Wox startup ----------------------------------------------------");
+ Log.Info($"|App.OnStartup|Runtime info:{ErrorReporting.RuntimeInfo()}");
+ RegisterAppDomainExceptions();
+ RegisterDispatcherUnhandledException();
+
+ ImageLoader.Initialize();
+
+ _settingsVM = new SettingWindowViewModel();
+ _settings = _settingsVM.Settings;
+
+ _alphabet.Initialize(_settings);
+ _stringMatcher = new StringMatcher(_alphabet);
+ StringMatcher.Instance = _stringMatcher;
+ _stringMatcher.UserSettingSearchPrecision = _settings.QuerySearchPrecision;
+
+ PluginManager.LoadPlugins(_settings.PluginSettings);
+ _mainVM = new MainViewModel(_settings);
+ var window = new MainWindow(_settings, _mainVM);
+ API = new PublicAPIInstance(_settingsVM, _mainVM, _alphabet);
+ PluginManager.InitializePlugins(API);
+
+ Current.MainWindow = window;
+ Current.MainWindow.Title = Constant.ExeFileName;
+
+ // happlebao todo temp fix for instance code logic
+ // load plugin before change language, because plugin language also needs be changed
+ InternationalizationManager.Instance.Settings = _settings;
+ InternationalizationManager.Instance.ChangeLanguage(_settings.Language);
+ // main windows needs initialized before theme change because of blur settigns
+ ThemeManager.Instance.Settings = _settings;
+ ThemeManager.Instance.ChangeTheme(_settings.Theme);
+
+ Http.Proxy = _settings.Proxy;
+
+ RegisterExitEvents();
+
+ _settingsWatcher = new SettingsWatcher(_settings);
+
+ _mainVM.MainWindowVisibility = Visibility.Visible;
+ Log.Info("|App.OnStartup|End Wox startup ---------------------------------------------------- ");
+
+
+
+ //[Conditional("RELEASE")]
+ // check udpate every 5 hours
+
+ // check updates on startup
+ });
+ }
+
+ private void RegisterExitEvents()
+ {
+ AppDomain.CurrentDomain.ProcessExit += (s, e) => Dispose();
+ Current.Exit += (s, e) => Dispose();
+ Current.SessionEnding += (s, e) => Dispose();
+ }
+
+ ///
+ /// let exception throw as normal is better for Debug
+ ///
+ [Conditional("RELEASE")]
+ private void RegisterDispatcherUnhandledException()
+ {
+ DispatcherUnhandledException += ErrorReporting.DispatcherUnhandledException;
+ }
+
+
+ ///
+ /// let exception throw as normal is better for Debug
+ ///
+ [Conditional("RELEASE")]
+ private static void RegisterAppDomainExceptions()
+ {
+ AppDomain.CurrentDomain.UnhandledException += ErrorReporting.UnhandledExceptionHandle;
+ }
+
+ public void Dispose()
+ {
+ // if sessionending is called, exit proverbially be called when log off / shutdown
+ // but if sessionending is not called, exit won't be called when log off / shutdown
+ if (!_disposed)
+ {
+ API.SaveAppAllSettings();
+ _disposed = true;
+ }
+ }
+
+ public void OnSecondAppStarted()
+ {
+ Current.MainWindow.Visibility = Visibility.Visible;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/FodyWeavers.xml b/src/modules/launcher/PowerLauncher/FodyWeavers.xml
new file mode 100644
index 0000000000..4e68ed1a8b
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/FodyWeavers.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/FodyWeavers.xsd b/src/modules/launcher/PowerLauncher/FodyWeavers.xsd
new file mode 100644
index 0000000000..666da5c56f
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/FodyWeavers.xsd
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+ Used to control if the On_PropertyName_Changed feature is enabled.
+
+
+
+
+ Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form.
+
+
+
+
+ Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project.
+
+
+
+
+ Used to control if equality checks should use the Equals method resolved from the base class.
+
+
+
+
+ Used to control if equality checks should use the static Equals method resolved from the base class.
+
+
+
+
+ Used to turn off build warnings from this weaver.
+
+
+
+
+ Used to turn off build warnings about mismatched On_PropertyName_Changed methods.
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/HotkeyControl.xaml b/src/modules/launcher/PowerLauncher/HotkeyControl.xaml
new file mode 100644
index 0000000000..affe185567
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/HotkeyControl.xaml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/HotkeyControl.xaml.cs b/src/modules/launcher/PowerLauncher/HotkeyControl.xaml.cs
new file mode 100644
index 0000000000..f2ba82a06c
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/HotkeyControl.xaml.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using NHotkey.Wpf;
+using Wox.Core.Resource;
+using Wox.Infrastructure.Hotkey;
+using Wox.Plugin;
+
+namespace Wox
+{
+ public partial class HotkeyControl : UserControl
+ {
+ public HotkeyModel CurrentHotkey { get; private set; }
+ public bool CurrentHotkeyAvailable { get; private set; }
+
+ public event EventHandler HotkeyChanged;
+
+ protected virtual void OnHotkeyChanged()
+ {
+ EventHandler handler = HotkeyChanged;
+ if (handler != null) handler(this, EventArgs.Empty);
+ }
+
+ public HotkeyControl()
+ {
+ InitializeComponent();
+ }
+
+ void TbHotkey_OnPreviewKeyDown(object sender, KeyEventArgs e)
+ {
+ e.Handled = true;
+ tbMsg.Visibility = Visibility.Hidden;
+
+ //when alt is pressed, the real key should be e.SystemKey
+ Key key = (e.Key == Key.System ? e.SystemKey : e.Key);
+
+ SpecialKeyState specialKeyState = GlobalHotkey.Instance.CheckModifiers();
+
+ var hotkeyModel = new HotkeyModel(
+ specialKeyState.AltPressed,
+ specialKeyState.ShiftPressed,
+ specialKeyState.WinPressed,
+ specialKeyState.CtrlPressed,
+ key);
+
+ var hotkeyString = hotkeyModel.ToString();
+
+ if (hotkeyString == tbHotkey.Text)
+ {
+ return;
+ }
+
+ Dispatcher.InvokeAsync(async () =>
+ {
+ await Task.Delay(500);
+ SetHotkey(hotkeyModel);
+ });
+ }
+
+ public void SetHotkey(HotkeyModel keyModel, bool triggerValidate = true)
+ {
+ CurrentHotkey = keyModel;
+
+ tbHotkey.Text = CurrentHotkey.ToString();
+ tbHotkey.Select(tbHotkey.Text.Length, 0);
+
+ if (triggerValidate)
+ {
+ CurrentHotkeyAvailable = CheckHotkeyAvailability();
+ if (!CurrentHotkeyAvailable)
+ {
+ tbMsg.Foreground = new SolidColorBrush(Colors.Red);
+ tbMsg.Text = InternationalizationManager.Instance.GetTranslation("hotkeyUnavailable");
+ }
+ else
+ {
+ tbMsg.Foreground = new SolidColorBrush(Colors.Green);
+ tbMsg.Text = InternationalizationManager.Instance.GetTranslation("success");
+ }
+ tbMsg.Visibility = Visibility.Visible;
+ OnHotkeyChanged();
+ }
+ }
+
+ public void SetHotkey(string keyStr, bool triggerValidate = true)
+ {
+ SetHotkey(new HotkeyModel(keyStr), triggerValidate);
+ }
+
+ private bool CheckHotkeyAvailability()
+ {
+ try
+ {
+ HotkeyManager.Current.AddOrReplace("HotkeyAvailabilityTest", CurrentHotkey.CharKey, CurrentHotkey.ModifierKeys, (sender, e) => { });
+
+ return true;
+ }
+ catch
+ {
+ }
+ finally
+ {
+ HotkeyManager.Current.Remove("HotkeyAvailabilityTest");
+ }
+
+ return false;
+ }
+
+ public new bool IsFocused
+ {
+ get { return tbHotkey.IsFocused; }
+ }
+ }
+}
diff --git a/src/modules/launcher/PowerLauncher/Images/Browser.png b/src/modules/launcher/PowerLauncher/Images/Browser.png
new file mode 100644
index 0000000000..619d1ad6ab
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/Browser.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/EXE.png b/src/modules/launcher/PowerLauncher/Images/EXE.png
new file mode 100644
index 0000000000..e4c7896898
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/EXE.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/Link.png b/src/modules/launcher/PowerLauncher/Images/Link.png
new file mode 100644
index 0000000000..c0c3607bd1
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/Link.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/New Message.png b/src/modules/launcher/PowerLauncher/Images/New Message.png
new file mode 100644
index 0000000000..2dd2c45f2e
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/New Message.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/app.png b/src/modules/launcher/PowerLauncher/Images/app.png
new file mode 100644
index 0000000000..8311fd5d41
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/app.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/app_error.png b/src/modules/launcher/PowerLauncher/Images/app_error.png
new file mode 100644
index 0000000000..ca781a9c40
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/app_error.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/calculator.png b/src/modules/launcher/PowerLauncher/Images/calculator.png
new file mode 100644
index 0000000000..102a86bde5
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/calculator.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/cancel.png b/src/modules/launcher/PowerLauncher/Images/cancel.png
new file mode 100644
index 0000000000..022fbc197a
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/cancel.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/close.png b/src/modules/launcher/PowerLauncher/Images/close.png
new file mode 100644
index 0000000000..17c4363ad3
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/close.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/cmd.png b/src/modules/launcher/PowerLauncher/Images/cmd.png
new file mode 100644
index 0000000000..686583653f
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/cmd.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/color.png b/src/modules/launcher/PowerLauncher/Images/color.png
new file mode 100644
index 0000000000..da28583b1c
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/color.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/copy.png b/src/modules/launcher/PowerLauncher/Images/copy.png
new file mode 100644
index 0000000000..8f1fca752f
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/copy.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/down.png b/src/modules/launcher/PowerLauncher/Images/down.png
new file mode 100644
index 0000000000..2349d89175
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/down.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/file.png b/src/modules/launcher/PowerLauncher/Images/file.png
new file mode 100644
index 0000000000..36156767a6
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/file.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/find.png b/src/modules/launcher/PowerLauncher/Images/find.png
new file mode 100644
index 0000000000..a3f0be1f5b
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/find.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/folder.png b/src/modules/launcher/PowerLauncher/Images/folder.png
new file mode 100644
index 0000000000..569fa70491
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/folder.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/history.png b/src/modules/launcher/PowerLauncher/Images/history.png
new file mode 100644
index 0000000000..2a3b72dc46
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/history.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/image.png b/src/modules/launcher/PowerLauncher/Images/image.png
new file mode 100644
index 0000000000..7fc14c38e9
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/image.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/lock.png b/src/modules/launcher/PowerLauncher/Images/lock.png
new file mode 100644
index 0000000000..4aef7007ba
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/lock.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/logoff.png b/src/modules/launcher/PowerLauncher/Images/logoff.png
new file mode 100644
index 0000000000..0d1378830e
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/logoff.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/ok.png b/src/modules/launcher/PowerLauncher/Images/ok.png
new file mode 100644
index 0000000000..f2dde98efb
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/ok.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/open.png b/src/modules/launcher/PowerLauncher/Images/open.png
new file mode 100644
index 0000000000..b5c7a0e19c
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/open.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/plugin.png b/src/modules/launcher/PowerLauncher/Images/plugin.png
new file mode 100644
index 0000000000..6ff9b8b157
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/plugin.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/recyclebin.png b/src/modules/launcher/PowerLauncher/Images/recyclebin.png
new file mode 100644
index 0000000000..2cc3b0116c
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/recyclebin.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/restart.png b/src/modules/launcher/PowerLauncher/Images/restart.png
new file mode 100644
index 0000000000..aaa2ee7116
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/restart.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/search.png b/src/modules/launcher/PowerLauncher/Images/search.png
new file mode 100644
index 0000000000..a3f0be1f5b
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/search.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/settings.png b/src/modules/launcher/PowerLauncher/Images/settings.png
new file mode 100644
index 0000000000..c61729fe02
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/settings.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/shutdown.png b/src/modules/launcher/PowerLauncher/Images/shutdown.png
new file mode 100644
index 0000000000..7da7a528d6
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/shutdown.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/sleep.png b/src/modules/launcher/PowerLauncher/Images/sleep.png
new file mode 100644
index 0000000000..42426286dd
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/sleep.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/up.png b/src/modules/launcher/PowerLauncher/Images/up.png
new file mode 100644
index 0000000000..e68def0b5e
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/up.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/update.png b/src/modules/launcher/PowerLauncher/Images/update.png
new file mode 100644
index 0000000000..6fe5790917
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/update.png differ
diff --git a/src/modules/launcher/PowerLauncher/Images/warning.png b/src/modules/launcher/PowerLauncher/Images/warning.png
new file mode 100644
index 0000000000..8d29625ee7
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/warning.png differ
diff --git a/src/modules/launcher/PowerLauncher/Languages/da.xaml b/src/modules/launcher/PowerLauncher/Languages/da.xaml
new file mode 100644
index 0000000000..c68024fbef
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Languages/da.xaml
@@ -0,0 +1,130 @@
+
+
+ Kunne ikke registrere genvejstast: {0}
+ Kunne ikke starte {0}
+ Ugyldigt Wox plugin filformat
+ Sæt øverst i denne søgning
+ Annuller øverst i denne søgning
+ Udfør søgning: {0}
+ Seneste afviklingstid: {0}
+ Åben
+ Indstillinger
+ Om
+ Afslut
+
+
+ Wox indstillinger
+ Generelt
+ Start Wox ved system start
+ Skjul Wox ved mistet fokus
+ Vis ikke notifikationer om nye versioner
+ Husk seneste position
+ Sprog
+ Maksimum antal resultater vist
+ Ignorer genvejstaster i fuldskærmsmode
+ Autoopdatering
+ Skjul Wox ved opstart
+
+
+ Plugin
+ Find flere plugins
+ Deaktiver
+ Nøgleord
+ Plugin bibliotek
+ Forfatter
+ Initaliseringstid: {0}ms
+ Søgetid: {0}ms
+
+
+ Tema
+ Søg efter flere temaer
+ Hej Wox
+ Søgefelt skrifttype
+ Resultat skrifttype
+ Vindue mode
+ Gennemsigtighed
+
+
+ Genvejstast
+ Wox genvejstast
+ Tilpasset søgegenvejstast
+ Slet
+ Rediger
+ Tilføj
+ Vælg venligst
+ Er du sikker på du vil slette {0} plugin genvejstast?
+
+
+ HTTP Proxy
+ Aktiver HTTP Proxy
+ HTTP Server
+ Port
+ Brugernavn
+ Adgangskode
+ Test Proxy
+ Gem
+ Server felt må ikke være tomt
+ Port felt må ikke være tomt
+ Ugyldigt port format
+ Proxy konfiguration gemt
+ Proxy konfiguret korrekt
+ Proxy forbindelse fejlet
+
+
+ Om
+ Website
+ Version
+ Du har aktiveret Wox {0} gange
+ Tjek for opdateringer
+ Ny version {0} er tilgængelig, genstart venligst Wox
+ Release Notes:
+
+
+ Gammelt nøgleord
+ Nyt nøgleord
+ Annuller
+ Færdig
+ Kan ikke finde det valgte plugin
+ Nyt nøgleord må ikke være tomt
+ Nyt nøgleord er tilknyttet et andet plugin, tilknyt venligst et andet nyt nøgeleord
+ Fortsæt
+ Brug * hvis du ikke vil angive et nøgleord
+
+
+ Vis
+ Genvejstast er utilgængelig, vælg venligst en ny genvejstast
+ Ugyldig plugin genvejstast
+ Opdater
+
+
+ Genvejstast utilgængelig
+
+
+ Version
+ Tid
+ Beskriv venligst hvordan Wox crashede, så vi kan rette det.
+ Send rapport
+ Annuller
+ Generelt
+ Exceptions
+ Exception Type
+ Kilde
+ Stack Trace
+ Sender
+ Rapport sendt korrekt
+ Kunne ikke sende rapport
+ Wox fik en fejl
+
+
+ Ny Wox udgivelse {0} er nu tilgængelig
+ Der skete en fejl ifm. opdatering af Wox
+ Opdater
+ Annuler
+ Denne opdatering vil genstarte Wox
+ Følgende filer bliver opdateret
+ Opdatereringsfiler
+ Opdateringsbeskrivelse
+
+
diff --git a/src/modules/launcher/PowerLauncher/Languages/de.xaml b/src/modules/launcher/PowerLauncher/Languages/de.xaml
new file mode 100644
index 0000000000..a11cbd8013
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Languages/de.xaml
@@ -0,0 +1,130 @@
+
+
+ Tastenkombinationregistrierung: {0} fehlgeschlagen
+ Kann {0} nicht starten
+ Fehlerhaftes Wox-Plugin Dateiformat
+ In dieser Abfrage als oberstes setzen
+ In dieser Abfrage oberstes abbrechen
+ Abfrage ausführen:{0}
+ Letzte Ausführungszeit:{0}
+ Öffnen
+ Einstellungen
+ Über
+ Schließen
+
+
+ Wox Einstellungen
+ Allgemein
+ Starte Wox bei Systemstart
+ Verstecke Wox wenn der Fokus verloren geht
+ Zeige keine Nachricht wenn eine neue Version vorhanden ist
+ Merke letzte Ausführungsposition
+ Sprache
+ Maximale Anzahl Ergebnissen
+ Ignoriere Tastenkombination wenn Fenster im Vollbildmodus ist
+ Automatische Aktualisierung
+ Verstecke Wox bei Systemstart
+
+
+ Plugin
+ Suche nach weiteren Plugins
+ Deaktivieren
+ Aktionsschlüsselwörter
+ Pluginordner
+ Autor
+ Initialisierungszeit: {0}ms
+ Abfragezeit: {0}ms
+
+
+ Theme
+ Suche nach weiteren Themes
+ Hallo Wox
+ Abfragebox Schriftart
+ Ergebnis Schriftart
+ Fenstermodus
+ Transparenz
+
+
+ Tastenkombination
+ Wox Tastenkombination
+ Benutzerdefinierte Abfrage Tastenkombination
+ Löschen
+ Bearbeiten
+ Hinzufügen
+ Bitte einen Eintrag auswählen
+ Wollen Sie die {0} Plugin Tastenkombination wirklich löschen?
+
+
+ HTTP Proxy
+ Aktiviere HTTP Proxy
+ HTTP Server
+ Port
+ Benutzername
+ Passwort
+ Teste Proxy
+ Speichern
+ Server darf nicht leer sein
+ Server Port darf nicht leer sein
+ Falsches Port Format
+ Proxy wurde erfolgreich gespeichert
+ Proxy ist korrekt
+ Verbindung zum Proxy fehlgeschlagen
+
+
+ Über
+ Webseite
+ Version
+ Sie haben Wox {0} mal aktiviert
+ Nach Aktuallisierungen Suchen
+ Eine neue Version ({0}) ist vorhanden. Bitte starten Sie Wox neu.
+ Versionshinweise:
+
+
+ Altes Aktionsschlüsselwort
+ Neues Aktionsschlüsselwort
+ Abbrechen
+ Fertig
+ Kann das angegebene Plugin nicht finden
+ Neues Aktionsschlüsselwort darf nicht leer sein
+ Aktionsschlüsselwort ist schon bei einem anderen Plugin in verwendung. Bitte stellen Sie ein anderes Aktionsschlüsselwort ein.
+ Erfolgreich
+ Benutzen Sie * wenn Sie ein Aktionsschlüsselwort definieren wollen.
+
+
+ Vorschau
+ Tastenkombination ist nicht verfügbar, bitte wähle eine andere Tastenkombination
+ Ungültige Plugin Tastenkombination
+ Aktualisieren
+
+
+ Tastenkombination nicht verfügbar
+
+
+ Version
+ Zeit
+ Bitte teilen Sie uns mit, wie die Anwendung abgestürzt ist, damit wir den Fehler beheben können.
+ Sende Report
+ Abbrechen
+ Allgemein
+ Fehler
+ Fehlertypen
+ Quelle
+ Stack Trace
+ Sende
+ Report erfolgreich
+ Report fehlgeschlagen
+ Wox hat einen Fehler
+
+
+ V{0} von Wox ist verfügbar
+ Es ist ein Fehler während der Installation der Aktualisierung aufgetreten.
+ Aktualisieren
+ Abbrechen
+ Diese Aktualisierung wird Wox neu starten
+ Folgende Dateien werden aktualisiert
+ Aktualisiere Dateien
+ Aktualisierungbeschreibung
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Languages/en.xaml b/src/modules/launcher/PowerLauncher/Languages/en.xaml
new file mode 100644
index 0000000000..cf177cab52
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Languages/en.xaml
@@ -0,0 +1,144 @@
+
+
+ Failed to register hotkey: {0}
+ Could not start {0}
+ Invalid Wox plugin file format
+ Set as topmost in this query
+ Cancel topmost in this query
+ Execute query: {0}
+ Last execution time: {0}
+ Open
+ Settings
+ About
+ Exit
+
+
+ Wox Settings
+ General
+ Start Wox on system startup
+ Hide Wox when focus is lost
+ Do not show new version notifications
+ Remember last launch location
+ Language
+ Last Query Style
+ Preserve Last Query
+ Select last Query
+ Empty last Query
+ Maximum results shown
+ Ignore hotkeys in fullscreen mode
+ Auto Update
+ Hide Wox on startup
+ Hide tray icon
+ Query Search Precision
+ Should Use Pinyin
+
+
+ Plugin
+ Find more plugins
+ Disable
+ Action keywords
+ Plugin Directory
+ Author
+ Init time: {0}ms
+ Query time: {0}ms
+
+
+ Theme
+ Browse for more themes
+ Hello Wox
+ Query Box Font
+ Result Item Font
+ Window Mode
+ Opacity
+ Theme {0} not exists, fallback to default theme
+ Fail to load theme {0}, fallback to default theme
+
+
+ Hotkey
+ Wox Hotkey
+ Custom Query Hotkey
+ Delete
+ Edit
+ Add
+ Please select an item
+ Are you sure you want to delete {0} plugin hotkey?
+
+
+ HTTP Proxy
+ Enable HTTP Proxy
+ HTTP Server
+ Port
+ User Name
+ Password
+ Test Proxy
+ Save
+ Server field can't be empty
+ Port field can't be empty
+ Invalid port format
+ Proxy configuration saved successfully
+ Proxy configured correctly
+ Proxy connection failed
+
+
+ About
+ Website
+ Version
+ You have activated Wox {0} times
+ Check for Updates
+ New version {0} is available, please restart Wox.
+ Check updates failed, please check your connection and proxy settings to api.github.com.
+
+ Download updates failed, please check your connection and proxy settings to github-cloud.s3.amazonaws.com,
+ or go to https://github.com/Wox-launcher/Wox/releases to download updates manually.
+
+ Release Notes:
+
+
+ Old Action Keyword
+ New Action Keyword
+ Cancel
+ Done
+ Can't find specified plugin
+ New Action Keyword can't be empty
+ New Action Keywords have been assigned to another plugin, please assign other new action keyword
+ Success
+ Use * if you don't want to specify an action keyword
+
+
+ Preview
+ Hotkey is unavailable, please select a new hotkey
+ Invalid plugin hotkey
+ Update
+
+
+ Hotkey unavailable
+
+
+ Version
+ Time
+ Please tell us how application crashed so we can fix it
+ Send Report
+ Cancel
+ General
+ Exceptions
+ Exception Type
+ Source
+ Stack Trace
+ Sending
+ Report sent successfully
+ Failed to send report
+ Wox got an error
+
+
+ New Wox release {0} is now available
+ An error occurred while trying to install software updates
+ Update
+ Cancel
+ This upgrade will restart Wox
+ Following files will be updated
+ Update files
+ Update description
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Languages/fr.xaml b/src/modules/launcher/PowerLauncher/Languages/fr.xaml
new file mode 100644
index 0000000000..ecbcda3ae9
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Languages/fr.xaml
@@ -0,0 +1,136 @@
+
+
+ Échec lors de l'enregistrement du raccourci : {0}
+ Impossible de lancer {0}
+ Le format de fichier n'est pas un plugin Wox valide
+ Définir en tant que favori pour cette requête
+ Annuler le favori
+ Lancer la requête : {0}
+ Dernière exécution : {0}
+ Ouvrir
+ Paramètres
+ À propos
+ Quitter
+
+
+ Paramètres - Wox
+ Général
+ Lancer Wox au démarrage du système
+ Cacher Wox lors de la perte de focus
+ Ne pas afficher le message de mise à jour pour les nouvelles versions
+ Se souvenir du dernier emplacement de la fenêtre
+ Langue
+ Affichage de la dernière recherche
+ Conserver la dernière recherche
+ Sélectionner la dernière recherche
+ Ne pas afficher la dernière recherche
+ Résultats maximums à afficher
+ Ignore les raccourcis lorsqu'une application est en plein écran
+ Mettre à jour automatiquement
+ Cacher Wox au démarrage
+
+
+ Modules
+ Trouver plus de modules
+ Désactivé
+ Mot-clé d'action :
+ Répertoire
+ Auteur
+ Chargement : {0}ms
+ Utilisation : {0}ms
+
+
+ Thèmes
+ Trouver plus de thèmes
+ Hello Wox
+ Police (barre de recherche)
+ Police (liste des résultats)
+ Mode fenêtré
+ Opacité
+
+
+ Raccourcis
+ Ouvrir Wox
+ Requêtes personnalisées
+ Supprimer
+ Modifier
+ Ajouter
+ Veuillez sélectionner un élément
+ Voulez-vous vraiment supprimer {0} raccourci(s) ?
+
+
+ Proxy HTTP
+ Activer le HTTP proxy
+ Serveur HTTP
+ Port
+ Utilisateur
+ Mot de passe
+ Tester
+ Sauvegarder
+ Un serveur doit être indiqué
+ Un port doit être indiqué
+ Format du port invalide
+ Proxy sauvegardé avec succès
+ Le proxy est valide
+ Connexion au proxy échouée
+
+
+ À propos
+ Site web
+ Version
+ Vous avez utilisé Wox {0} fois
+ Vérifier les mises à jour
+ Nouvelle version {0} disponible, veuillez redémarrer Wox
+ Échec de la vérification de la mise à jour, vérifiez votre connexion et vos paramètres de configuration proxy pour pouvoir acceder à api.github.com.
+ Échec du téléchargement de la mise à jour, vérifiez votre connexion et vos paramètres de configuration proxy pour pouvoir acceder à github-cloud.s3.amazonaws.com, ou téléchargez manuelement la mise à jour sur https://github.com/Wox-launcher/Wox/releases.
+ Notes de changement :
+
+
+ Ancien mot-clé d'action
+ Nouveau mot-clé d'action
+ Annuler
+ Terminé
+ Impossible de trouver le module spécifié
+ Le nouveau mot-clé d'action doit être spécifié
+ Le nouveau mot-clé d'action a été assigné à un autre module, veuillez en choisir un autre
+ Ajouté
+ Saisissez * si vous ne souhaitez pas utiliser de mot-clé spécifique
+
+
+ Prévisualiser
+ Raccourci indisponible. Veuillez en choisir un autre.
+ Raccourci invalide
+ Actualiser
+
+
+ Raccourci indisponible
+
+
+ Version
+ Heure
+ Veuillez nous indiquer comment l'application a planté afin que nous puissions le corriger
+ Envoyer le rapport
+ Annuler
+ Général
+ Exceptions
+ Type d'exception
+ Source
+ Trace d'appel
+ Envoi en cours
+ Signalement envoyé
+ Échec de l'envoi du signalement
+ Wox a rencontré une erreur
+
+
+ Version v{0} de Wox disponible
+ Une erreur s'est produite lors de l'installation de la mise à jour
+ Mettre à jour
+ Annuler
+ Wox doit redémarrer pour installer cette mise à jour
+ Les fichiers suivants seront mis à jour
+ Fichiers mis à jour
+ Description de la mise à jour
+
+
diff --git a/src/modules/launcher/PowerLauncher/Languages/it.xaml b/src/modules/launcher/PowerLauncher/Languages/it.xaml
new file mode 100644
index 0000000000..a3a826d6a5
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Languages/it.xaml
@@ -0,0 +1,139 @@
+
+
+ Impossibile salvare il tasto di scelta rapida: {0}
+ Avvio fallito {0}
+ Formato file plugin non valido
+ Risultato prioritario con questa query
+ Rimuovi risultato prioritario con questa query
+ Query d'esecuzione: {0}
+ Ultima esecuzione: {0}
+ Apri
+ Impostazioni
+ About
+ Esci
+
+
+ Impostaizoni Wox
+ Generale
+ Avvia Wow all'avvio di Windows
+ Nascondi Wox quando perde il focus
+ Non mostrare le notifiche per una nuova versione
+ Ricorda l'ultima posizione di avvio del launcher
+ Lingua
+ Comportamento ultima ricerca
+ Conserva ultima ricerca
+ Seleziona ultima ricerca
+ Cancella ultima ricerca
+ Numero massimo di risultati mostrati
+ Ignora i tasti di scelta rapida in applicazione a schermo pieno
+ Aggiornamento automatico
+ Nascondi Wox all'avvio
+
+
+ Plugin
+ Cerca altri plugins
+ Disabilita
+ Parole chiave
+ Cartella Plugin
+ Autore
+ Tempo di avvio: {0}ms
+ Tempo ricerca: {0}ms
+
+
+ Tema
+ Sfoglia per altri temi
+ Hello Wox
+ Font campo di ricerca
+ Font campo risultati
+ Modalità finestra
+ Opacità
+
+
+ Tasti scelta rapida
+ Tasto scelta rapida Wox
+ Tasti scelta rapida per ricerche personalizzate
+ Cancella
+ Modifica
+ Aggiungi
+ Selezionare un oggetto
+ Volete cancellare il tasto di scelta rapida per il plugin {0}?
+
+
+ Proxy HTTP
+ Abilita Proxy HTTP
+ Server HTTP
+ Porta
+ User Name
+ Password
+ Proxy Test
+ Salva
+ Il campo Server non può essere vuoto
+ Il campo Porta non può essere vuoto
+ Formato Porta non valido
+ Configurazione Proxy salvata correttamente
+ Proxy Configurato correttamente
+ Connessione Proxy fallita
+
+
+ About
+ Sito web
+ Versione
+ Hai usato Wox {0} volte
+ Cerca aggiornamenti
+ Una nuova versione {0} è disponibile, riavvia Wox per favore.
+ Ricerca aggiornamenti fallita, per favore controlla la tua connessione e le eventuali impostazioni proxy per api.github.com.
+
+ Download degli aggiornamenti fallito, per favore controlla la tua connessione ed eventuali impostazioni proxy per github-cloud.s3.amazonaws.com,
+ oppure vai su https://github.com/Wox-launcher/Wox/releases per scaricare gli aggiornamenti manualmente.
+
+ Note di rilascio:
+
+
+ Vecchia parola chiave d'azione
+ Nuova parola chiave d'azione
+ Annulla
+ Conferma
+ Impossibile trovare il plugin specificato
+ La nuova parola chiave d'azione non può essere vuota
+ La nuova parola chiave d'azione è stata assegnata ad un altro plugin, per favore sceglierne una differente
+ Successo
+ Usa * se non vuoi specificare una parola chiave d'azione
+
+
+ Anteprima
+ Tasto di scelta rapida non disponibile, per favore scegli un nuovo tasto di scelta rapida
+ Tasto di scelta rapida plugin non valido
+ Aggiorna
+
+
+ Tasto di scelta rapida non disponibile
+
+
+ Versione
+ Tempo
+ Per favore raccontaci come l'applicazione si è chiusa inaspettatamente così che possimo risolvere il problema
+ Invia rapporto
+ Annulla
+ Generale
+ Eccezioni
+ Tipo di eccezione
+ Risorsa
+ Traccia dello stack
+ Invio in corso
+ Rapporto inviato correttamente
+ Invio rapporto fallito
+ Wox ha riportato un errore
+
+
+ E' disponibile la nuova release {0} di Wox
+ Errore durante l'installazione degli aggiornamenti software
+ Aggiorna
+ Annulla
+ Questo aggiornamento riavvierà Wox
+ I seguenti file saranno aggiornati
+ File aggiornati
+ Descrizione aggiornamento
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Languages/ja.xaml b/src/modules/launcher/PowerLauncher/Languages/ja.xaml
new file mode 100644
index 0000000000..598a162bdb
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Languages/ja.xaml
@@ -0,0 +1,142 @@
+
+
+ ホットキー「{0}」の登録に失敗しました
+ {0}の起動に失敗しました
+ Woxプラグインの形式が正しくありません
+ このクエリを最上位にセットする
+ このクエリを最上位にセットをキャンセル
+ 次のコマンドを実行します:{0}
+ 最終実行時間:{0}
+ 開く
+ 設定
+ Woxについて
+ 終了
+
+
+ Wox設定
+ 一般
+ スタートアップ時にWoxを起動する
+ フォーカスを失った時にWoxを隠す
+ 最新版が入手可能であっても、アップグレードメッセージを表示しない
+ 前回のランチャーの位置を記憶
+ 言語
+ 前回のクエリの扱い
+ 前回のクエリを保存
+ 前回のクエリを選択
+ 前回のクエリを消去
+ 結果の最大表示件数
+ ウィンドウがフルスクリーン時にホットキーを無効にする
+ 自動更新
+ 起動時にWoxを隠す
+ トレイアイコンを隠す
+
+
+ プラグイン
+ プラグインを探す
+ 無効
+ キーワード
+ プラグイン・ディレクトリ
+ 作者
+ 初期化時間: {0}ms
+ クエリ時間: {0}ms
+
+
+ テーマ
+ テーマを探す
+ こんにちは Wox
+ 検索ボックスのフォント
+ 検索結果一覧のフォント
+ ウィンドウモード
+ 透過度
+ テーマ {0} が存在しません、デフォルトのテーマに戻します。
+ テーマ {0} を読み込めません、デフォルトのテーマに戻します。
+
+
+ ホットキー
+ Wox ホットキー
+ カスタムクエリ ホットキー
+ 削除
+ 編集
+ 追加
+ 項目選択してください
+ {0} プラグインのホットキーを本当に削除しますか?
+
+
+ HTTP プロキシ
+ HTTP プロキシを有効化
+ HTTP サーバ
+ ポート
+ ユーザ名
+ パスワード
+ プロキシをテストする
+ 保存
+ サーバーは空白にできません
+ ポートは空白にできません
+ ポートの形式が正しくありません
+ プロキシの保存に成功しました
+ プロキシは正しいです
+ プロキシ接続に失敗しました
+
+
+ Woxについて
+ ウェブサイト
+ バージョン
+ あなたはWoxを {0} 回利用しました
+ アップデートを確認する
+ 新しいバージョン {0} が利用可能です。Woxを再起動してください。
+ アップデートの確認に失敗しました、api.github.com への接続とプロキシ設定を確認してください。
+
+ 更新のダウンロードに失敗しました、github-cloud.s3.amazonaws.com への接続とプロキシ設定を確認するか、
+ https://github.com/Wox-launcher/Wox/releases から手動でアップデートをダウンロードしてください。
+
+ リリースノート:
+
+
+ 古いアクションキーボード
+ 新しいアクションキーボード
+ キャンセル
+ 完了
+ プラグインが見つかりません
+ 新しいアクションキーボードを空にすることはできません
+ 新しいアクションキーボードは他のプラグインに割り当てられています。他のアクションキーボードを指定してください
+ 成功しました
+ アクションキーボードを指定しない場合、* を使用してください
+
+
+ プレビュー
+ ホットキーは使用できません。新しいホットキーを選択してください
+ プラグインホットキーは無効です
+ 更新
+
+
+ ホットキーは使用できません
+
+
+ バージョン
+ 時間
+ アプリケーションが突然終了した手順を私たちに教えてくださると、バグ修正ができます
+ クラッシュレポートを送信
+ キャンセル
+ 一般
+ 例外
+ 例外の種類
+ ソース
+ スタックトレース
+ 送信中
+ クラッシュレポートの送信に成功しました
+ クラッシュレポートの送信に失敗しました
+ Woxにエラーが発生しました
+
+
+ Wox の最新バージョン V{0} が入手可能です
+ Woxのアップデート中にエラーが発生しました
+ アップデート
+ キャンセル
+ このアップデートでは、Woxの再起動が必要です
+ 次のファイルがアップデートされます
+ 更新ファイル一覧
+ アップデートの詳細
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Languages/ko.xaml b/src/modules/launcher/PowerLauncher/Languages/ko.xaml
new file mode 100644
index 0000000000..28277da284
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Languages/ko.xaml
@@ -0,0 +1,134 @@
+
+
+ 핫키 등록 실패: {0}
+ {0}을 실행할 수 없습니다.
+ Wox 플러그인 파일 형식이 유효하지 않습니다.
+ 이 쿼리의 최상위로 설정
+ 이 쿼리의 최상위 설정 취소
+ 쿼리 실행: {0}
+ 마지막 실행 시간: {0}
+ 열기
+ 설정
+ 정보
+ 종료
+
+
+ Wox 설정
+ 일반
+ 시스템 시작 시 Wox 실행
+ 포커스 잃으면 Wox 숨김
+ 새 버전 알림 끄기
+ 마지막 실행 위치 기억
+ 언어
+ 마지막 쿼리 스타일
+ 직전 쿼리에 계속 입력
+ 직전 쿼리 내용 선택
+ 직전 쿼리 지우기
+ 표시할 결과 수
+ 전체화면 모드에서는 핫키 무시
+ 자동 업데이트
+ 시작 시 Wox 숨김
+
+
+ 플러그인
+ 플러그인 더 찾아보기
+ 비활성화
+ 액션 키워드
+ 플러그인 디렉토리
+ 저자
+ 초기화 시간: {0}ms
+ 쿼리 시간: {0}ms
+
+
+ 테마
+ 테마 더 찾아보기
+ Hello Wox
+ 쿼리 상자 글꼴
+ 결과 항목 글꼴
+ 윈도우 모드
+ 투명도
+
+
+ 핫키
+ Wox 핫키
+ 사용자지정 쿼리 핫키
+ 삭제
+ 편집
+ 추가
+ 항목을 선택하세요.
+ {0} 플러그인 핫키를 삭제하시겠습니까?
+
+
+ HTTP 프록시
+ HTTP 프록시 켜기
+ HTTP 서버
+ 포트
+ 사용자명
+ 패스워드
+ 프록시 테스트
+ 저장
+ 서버를 입력하세요.
+ 포트를 입력하세요.
+ 유효하지 않은 포트 형식
+ 프록시 설정이 저장되었습니다.
+ 프록시 설정 정상
+ 프록시 연결 실패
+
+
+ 정보
+ 웹사이트
+ 버전
+ Wox를 {0}번 실행했습니다.
+ 업데이트 확인
+ 새 버전({0})이 있습니다. Wox를 재시작하세요.
+ 릴리즈 노트:
+
+
+ 예전 액션 키워드
+ 새 액션 키워드
+ 취소
+ 완료
+ 플러그인을 찾을 수 없습니다.
+ 새 액션 키워드를 입력하세요.
+ 새 액션 키워드가 할당된 플러그인이 이미 있습니다. 다른 액션 키워드를 입력하세요.
+ 성공
+ 액션 키워드를 지정하지 않으려면 *를 사용하세요.
+
+
+ 미리보기
+ 핫키를 사용할 수 없습니다. 다른 핫키를 입력하세요.
+ 플러그인 핫키가 유효하지 않습니다.
+ 업데이트
+
+
+ 핫키를 사용할 수 없습니다.
+
+
+ 버전
+ 시간
+ 수정을 위해 애플리케이션이 어떻게 충돌했는지 알려주세요.
+ 보고서 보내기
+ 취소
+ 일반
+ 예외
+ 예외 유형
+ 소스
+ 스택 추적
+ 보내는 중
+ 보고서를 정상적으로 보냈습니다.
+ 보고서를 보내지 못했습니다.
+ Wox에 문제가 발생했습니다.
+
+
+ 새 Wox 버전({0})을 사용할 수 있습니다.
+ 소프트웨어 업데이트를 설치하는 중에 오류가 발생했습니다.
+ 업데이트
+ 취소
+ 업데이트를 위해 Wox를 재시작합니다.
+ 아래 파일들이 업데이트됩니다.
+ 업데이트 파일
+ 업데이트 설명
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Languages/nb-NO.xaml b/src/modules/launcher/PowerLauncher/Languages/nb-NO.xaml
new file mode 100644
index 0000000000..b5dcbdd910
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Languages/nb-NO.xaml
@@ -0,0 +1,139 @@
+
+
+ Kunne ikke registrere hurtigtast: {0}
+ Kunne ikke starte {0}
+ Ugyldig filformat for Wox-utvidelse
+ Sett til øverste for denne spørringen
+ Avbryt øverste for denne spørringen
+ Utfør spørring: {0}
+ Tid for siste gjennomføring: {0}
+ Åpne
+ Innstillinger
+ Om
+ Avslutt
+
+
+ Wox-innstillinger
+ Generelt
+ Start Wox ved systemstart
+ Skjul Wox ved mistet fokus
+ Ikke vis varsel om ny versjon
+ Husk siste plassering
+ Språk
+ Stil for siste spørring
+ Bevar siste spørring
+ Velg siste spørring
+ Tøm siste spørring
+ Maks antall resultater vist
+ Ignorer hurtigtaster i fullskjermsmodus
+ Oppdater automatisk
+ Skjul Wox ved oppstart
+
+
+ Utvidelse
+ Finn flere utvidelser
+ Deaktiver
+ Handlingsnøkkelord
+ Utvidelseskatalog
+ Forfatter
+ Oppstartstid: {0}ms
+ Spørringstid: {0}ms
+
+
+ Tema
+ Finn flere temaer
+ Hallo Wox
+ Font for spørringsboks
+ Font for resultat
+ Vindusmodus
+ Gjennomsiktighet
+
+
+ Hurtigtast
+ Wox-hurtigtast
+ Egendefinerd spørringshurtigtast
+ Slett
+ Rediger
+ Legg til
+ Vennligst velg et element
+ Er du sikker på at du vil slette utvidelserhurtigtasten for {0}?
+
+
+ HTTP-proxy
+ Aktiver HTTP-proxy
+ HTTP-server
+ Port
+ Brukernavn
+ Passord
+ Test proxy
+ Lagre
+ Serverfeltet kan ikke være tomt
+ Portfelter kan ikke være tomt
+ Ugyldig portformat
+ Proxy-konfigurasjon lagret med suksess
+ Proxy konfigurert riktig
+ Proxy-tilkobling feilet
+
+
+ Om
+ Netside
+ Versjon
+ Du har aktivert Wox {0} ganger
+ Sjekk for oppdatering
+ Ny versjon {0} er tilgjengelig, vennligst start Wox på nytt.
+ Oppdateringssjekk feilet, vennligst sjekk tilkoblingen og proxy-innstillene for api.github.com.
+
+ Nedlastning av oppdateringer feilet, vennligst sjekk tilkoblingen og proxy-innstillene for github-cloud.s3.amazonaws.com,
+ eller gå til https://github.com/Wox-launcher/Wox/releases for å laste ned oppdateringer manuelt.
+
+ Versjonsmerknader:
+
+
+ Gammelt handlingsnøkkelord
+ Nytt handlingsnøkkelord
+ Avbryt
+ Ferdig
+ Kan ikke finne den angitte utvidelsen
+ Nytt handlingsnøkkelord kan ikke være tomt
+ De nye handlingsnøkkelordene er tildelt en annen utvidelse, vennligst velg et annet nøkkelord
+ Vellykket
+ Bruk * hvis du ikke ønsker å angi et handlingsnøkkelord
+
+
+ Forhåndsvis
+ Hurtigtasten er ikke tilgjengelig, vennligst velg en ny hurtigtast
+ Ugyldig hurtigtast for utvidelse
+ Oppdater
+
+
+ Hurtigtast utilgjengelig
+
+
+ Versjon
+ Tid
+ Fortell oss hvordan programmet krasjet, så vi kan fikse det
+ Send rapport
+ Avbryt
+ Generelt
+ Unntak
+ Unntakstype
+ Kilde
+ Stack Trace
+ Sender
+ Rapport sendt med suksess
+ Kunne ikke sende rapport
+ Wox møtte på en feil
+
+
+ Versjon {0} av Wox er nå tilgjengelig
+ En feil oppstod under installasjon av programvareoppdateringer
+ Oppdater
+ Avbryt
+ Denne opgraderingen vil starte Wox på nytt
+ Følgende filer vil bli oppdatert
+ Oppdateringsfiler
+ Oppdateringsbeskrivelse
+
+
diff --git a/src/modules/launcher/PowerLauncher/Languages/nl.xaml b/src/modules/launcher/PowerLauncher/Languages/nl.xaml
new file mode 100644
index 0000000000..73aa621093
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Languages/nl.xaml
@@ -0,0 +1,130 @@
+
+
+ Sneltoets registratie: {0} mislukt
+ Kan {0} niet starten
+ Ongeldige Wox plugin bestandsextensie
+ Stel in als hoogste in deze query
+ Annuleer hoogste in deze query
+ Executeer query: {0}
+ Laatste executie tijd: {0}
+ Open
+ Instellingen
+ About
+ Afsluiten
+
+
+ Wox Instellingen
+ Algemeen
+ Start Wox als systeem opstart
+ Verberg Wox als focus verloren is
+ Laat geen nieuwe versie notificaties zien
+ Herinner laatste opstart locatie
+ Taal
+ Laat maximale resultaten zien
+ Negeer sneltoetsen in fullscreen mode
+ Automatische Update
+ Verberg Wox als systeem opstart
+
+
+ Plugin
+ Zoek meer plugins
+ Disable
+ Action terfwoorden
+ Plugin map
+ Auteur
+ Init tijd: {0}ms
+ Query tijd: {0}ms
+
+
+ Thema
+ Zoek meer thema´s
+ Hallo Wox
+ Query Box lettertype
+ Resultaat Item lettertype
+ Window Mode
+ Ondoorzichtigheid
+
+
+ Sneltoets
+ Wox Sneltoets
+ Custom Query Sneltoets
+ Verwijder
+ Bewerken
+ Toevoegen
+ Selecteer een item
+ Weet u zeker dat je {0} plugin sneltoets wilt verwijderen?
+
+
+ HTTP Proxy
+ Enable HTTP Proxy
+ HTTP Server
+ Poort
+ Gebruikersnaam
+ Wachtwoord
+ Test Proxy
+ Opslaan
+ Server moet ingevuld worden
+ Poort moet ingevuld worden
+ Ongeldige poort formaat
+ Proxy succesvol opgeslagen
+ Proxy correct geconfigureerd
+ Proxy connectie mislukt
+
+
+ Over
+ Website
+ Versie
+ U heeft Wox {0} keer opgestart
+ Zoek naar Updates
+ Nieuwe versie {0} beschikbaar, start Wox opnieuw op
+ Release Notes:
+
+
+ Oude actie sneltoets
+ Nieuwe actie sneltoets
+ Annuleer
+ Klaar
+ Kan plugin niet vinden
+ Nieuwe actie sneltoets moet ingevuld worden
+ Nieuwe actie sneltoets is toegewezen aan een andere plugin, wijs een nieuwe actie sneltoets aan
+ Succesvol
+ Gebruik * wanneer je geen nieuwe actie sneltoets wilt specificeren
+
+
+ Voorbeeld
+ Sneltoets is niet beschikbaar, selecteer een nieuwe sneltoets
+ Ongeldige plugin sneltoets
+ Update
+
+
+ Sneltoets niet beschikbaar
+
+
+ Versie
+ Tijd
+ Vertel ons hoe de applicatie is gecrashed, zodat wij de applicatie kunnen verbeteren
+ Verstuur Rapport
+ Annuleer
+ Algemeen
+ Uitzonderingen
+ Uitzondering Type
+ Bron
+ Stack Opzoeken
+ Versturen
+ Rapport succesvol
+ Rapport mislukt
+ Wox heeft een error
+
+
+ Nieuwe Wox release {0} nu beschikbaar
+ Een error is voorgekomen tijdens het installeren van de update
+ Update
+ Annuleer
+ Deze upgrade zal Wox opnieuw opstarten
+ Volgende bestanden zullen worden geüpdatet
+ Update bestanden
+ Update beschrijving
+
+
diff --git a/src/modules/launcher/PowerLauncher/Languages/pl.xaml b/src/modules/launcher/PowerLauncher/Languages/pl.xaml
new file mode 100644
index 0000000000..b7aa238b95
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Languages/pl.xaml
@@ -0,0 +1,130 @@
+
+
+ Nie udało się ustawić skrótu klawiszowego: {0}
+ Nie udało się uruchomić: {0}
+ Niepoprawny format pliku wtyczki
+ Ustaw jako najwyższy wynik dla tego zapytania
+ Usuń ten najwyższy wynik dla tego zapytania
+ Wyszukaj: {0}
+ Ostatni czas wykonywania: {0}
+ Otwórz
+ Ustawienia
+ O programie
+ Wyjdź
+
+
+ Ustawienia Wox
+ Ogólne
+ Uruchamiaj Wox przy starcie systemu
+ Ukryj okno Wox kiedy przestanie ono być aktywne
+ Nie pokazuj powiadomienia o nowej wersji
+ Zapamiętaj ostatnią pozycję okna
+ Język
+ Maksymalna liczba wyników
+ Ignoruj skróty klawiszowe w trybie pełnego ekranu
+ Automatyczne aktualizacje
+ Uruchamiaj Wox zminimalizowany
+
+
+ Wtyczki
+ Znajdź więcej wtyczek
+ Wyłącz
+ Wyzwalacze
+ Folder wtyczki
+ Autor
+ Czas ładowania: {0}ms
+ Czas zapytania: {0}ms
+
+
+ Skórka
+ Znajdź więcej skórek
+ Witaj Wox
+ Czcionka okna zapytania
+ Czcionka okna wyników
+ Tryb w oknie
+ Przeźroczystość
+
+
+ Skrót klawiszowy
+ Skrót klawiszowy Wox
+ Skrót klawiszowy niestandardowych zapytań
+ Usuń
+ Edytuj
+ Dodaj
+ Musisz coś wybrać
+ Czy jesteś pewien że chcesz usunąć skrót klawiszowy {0} wtyczki?
+
+
+ Serwer proxy HTTP
+ Używaj HTTP proxy
+ HTTP Serwer
+ Port
+ Nazwa użytkownika
+ Hasło
+ Sprawdź proxy
+ Zapisz
+ Nazwa serwera nie może być pusta
+ Numer portu nie może być pusty
+ Nieprawidłowy format numeru portu
+ Ustawienia proxy zostały zapisane
+ Proxy zostało skonfigurowane poprawnie
+ Nie udało się połączyć z serwerem proxy
+
+
+ O programie
+ Strona internetowa
+ Wersja
+ Uaktywniłeś Wox {0} razy
+ Szukaj aktualizacji
+ Nowa wersja {0} jest dostępna, uruchom ponownie Wox
+ Zmiany:
+
+
+ Stary wyzwalacz
+ Nowy wyzwalacz
+ Anuluj
+ Zapisz
+ Nie można odnaleźć podanej wtyczki
+ Nowy wyzwalacz nie może być pusty
+ Ten wyzwalacz został już przypisany do innej wtyczki, musisz podać inny wyzwalacz.
+ Sukces
+ Użyj * jeżeli nie chcesz podawać wyzwalacza
+
+
+ Podgląd
+ Skrót klawiszowy jest niedostępny, musisz podać inny skrót klawiszowy
+ Niepoprawny skrót klawiszowy
+ Aktualizuj
+
+
+ Niepoprawny skrót klawiszowy
+
+
+ Wersja
+ Czas
+ Proszę powiedz nam co się stało zanim wystąpił błąd dzięki czemu będziemy mogli go naprawić (tylko po angielsku)
+ Wyślij raport błędu
+ Anuluj
+ Ogólne
+ Wyjątki
+ Typ wyjątku
+ Źródło
+ Stos wywołań
+ Wysyłam raport...
+ Raport wysłany pomyślnie
+ Nie udało się wysłać raportu
+ W programie Wox wystąpił błąd
+
+
+ Nowa wersja Wox {0} jest dostępna
+ Wystąpił błąd podczas instalowania aktualizacji programu
+ Aktualizuj
+ Anuluj
+ Aby dokończyć proces aktualizacji Wox musi zostać zresetowany
+ Następujące pliki zostaną zaktualizowane
+ Aktualizuj pliki
+ Opis aktualizacji
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Languages/pt-br.xaml b/src/modules/launcher/PowerLauncher/Languages/pt-br.xaml
new file mode 100644
index 0000000000..6e0b5f2072
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Languages/pt-br.xaml
@@ -0,0 +1,139 @@
+
+
+ Falha ao registrar atalho: {0}
+ Não foi possível iniciar {0}
+ Formato de plugin Wox inválido
+ Tornar a principal nessa consulta
+ Cancelar a principal nessa consulta
+ Executar consulta: {0}
+ Última execução: {0}
+ Abrir
+ Configurações
+ Sobre
+ Sair
+
+
+ Configurações do Wox
+ Geral
+ Iniciar Wox com inicialização do sistema
+ Esconder Wox quando foco for perdido
+ Não mostrar notificações de novas versões
+ Lembrar última localização de lançamento
+ Idioma
+ Estilo da Última Consulta
+ Preservar Última Consulta
+ Selecionar última consulta
+ Limpar última consulta
+ Máximo de resultados mostrados
+ Ignorar atalhos em tela cheia
+ Atualizar Automaticamente
+ Esconder Wox na inicialização
+
+
+ Plugin
+ Encontrar mais plugins
+ Desabilitar
+ Palavras-chave de ação
+ Diretório de Plugins
+ Autor
+ Tempo de inicialização: {0}ms
+ Tempo de consulta: {0}ms
+
+
+ Tema
+ Ver mais temas
+ Olá Wox
+ Fonte da caixa de Consulta
+ Fonte do Resultado
+ Modo Janela
+ Opacidade
+
+
+ Atalho
+ Atalho do Wox
+ Atalho de Consulta Personalizada
+ Apagar
+ Editar
+ Adicionar
+ Por favor selecione um item
+ Tem cereza de que deseja deletar o atalho {0} do plugin?
+
+
+ Proxy HTTP
+ Habilitar Proxy HTTP
+ Servidor HTTP
+ Porta
+ Usuário
+ Senha
+ Testar Proxy
+ Salvar
+ O campo de servidor não pode ser vazio
+ O campo de porta não pode ser vazio
+ Formato de porta inválido
+ Configuração de proxy salva com sucesso
+ Proxy configurado corretamente
+ Conexão por proxy falhou
+
+
+ Sobre
+ Website
+ Versão
+ Você ativou o Wox {0} vezes
+ Procurar atualizações
+ A nova versão {0} está disponível, por favor reinicie o Wox.
+ Falha ao procurar atualizações, confira sua conexão e configuração de proxy para api.github.com.
+
+ Falha ao baixar atualizações, confira sua conexão e configuração de proxy para github-cloud.s3.amazonaws.com,
+ ou acesse https://github.com/Wox-launcher/Wox/releases para baixar manualmente.
+
+ Notas de Versão:
+
+
+ Antiga palavra-chave da ação
+ Nova palavra-chave da ação
+ Cancelar
+ Finalizado
+ Não foi possível encontrar o plugin especificado
+ A nova palavra-chave da ação não pode ser vazia
+ A nova palavra-chave da ação já foi atribuída a outro plugin, por favor tente outra
+ Sucesso
+ Use * se não quiser especificar uma palavra-chave de ação
+
+
+ Prévia
+ Atalho indisponível, escolha outro
+ Atalho de plugin inválido
+ Atualizar
+
+
+ Atalho indisponível
+
+
+ Versão
+ Horário
+ Por favor, conte como a aplicação parou de funcionar para que possamos consertar
+ Enviar Relatório
+ Cancelar
+ Geral
+ Exceções
+ Tipo de Exceção
+ Fonte
+ Rastreamento de pilha
+ Enviando
+ Relatório enviado com sucesso
+ Falha ao enviar relatório
+ Wox apresentou um erro
+
+
+ A nova versão {0} do Wox agora está disponível
+ Ocorreu um erro ao tentar instalar atualizações do progama
+ Atualizar
+ Cancelar
+ Essa atualização reiniciará o Wox
+ Os seguintes arquivos serão atualizados
+ Atualizar arquivos
+ Atualizar descrição
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Languages/ru.xaml b/src/modules/launcher/PowerLauncher/Languages/ru.xaml
new file mode 100644
index 0000000000..f80c3ed1fa
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Languages/ru.xaml
@@ -0,0 +1,130 @@
+
+
+ Регистрация хоткея {0} не удалась
+ Не удалось запустить {0}
+ Неверный формат файла wox плагина
+ Отображать это окно выше всех при этом запросе
+ Не отображать это окно выше всех при этом запросе
+ Выполнить запрос:{0}
+ Последний раз выполнен в:{0}
+ Открыть
+ Настройки
+ О Wox
+ Закрыть
+
+
+ Настройки Wox
+ Общие
+ Запускать Wox при запуске системы
+ Скрывать Wox если потерян фокус
+ Не отображать сообщение об обновлении когда доступна новая версия
+ Запомнить последнее место запуска
+ Язык
+ Максимальное количество результатов
+ Игнорировать горячие клавиши, если окно в полноэкранном режиме
+ Auto Update
+ Hide Wox on startup
+
+
+ Плагины
+ Найти больше плагинов
+ Отключить
+ Ключевое слово
+ Папка
+ Автор
+ Инициализация: {0}ms
+ Запрос: {0}ms
+
+
+ Темы
+ Найти больше тем
+ Привет Wox
+ Шрифт запросов
+ Шрифт результатов
+ Оконный режим
+ Прозрачность
+
+
+ Горячие клавиши
+ Горячая клавиша Wox
+ Задаваемые горячие клавиши для запросов
+ Удалить
+ Изменить
+ Добавить
+ Сначала выберите элемент
+ Вы уверены что хотите удалить горячую клавишу для плагина {0}?
+
+
+ HTTP Прокси
+ Включить HTTP прокси
+ HTTP Сервер
+ Порт
+ Логин
+ Пароль
+ Проверить
+ Сохранить
+ Необходимо задать сервер
+ Необходимо задать порт
+ Неверный формат порта
+ Прокси успешно сохранён
+ Прокси сервер задан правильно
+ Подключение к прокси серверу не удалось
+
+
+ О Wox
+ Сайт
+ Версия
+ Вы воспользовались Wox уже {0} раз
+ Check for Updates
+ New version {0} avaiable, please restart wox
+ Release Notes:
+
+
+ Текущая горячая клавиша
+ Новая горячая клавиша
+ Отменить
+ Подтвердить
+ Не удалось найти заданный плагин
+ Новая горячая клавиша не может быть пустой
+ Новая горячая клавиша уже используется другим плагином. Пожалуйста, задайте новую
+ Сохранено
+ Используйте * в случае, если вы не хотите задавать конкретную горячую клавишу
+
+
+ Проверить
+ Горячая клавиша недоступна. Пожалуйста, задайте новую
+ Недействительная горячая клавиша плагина
+ Изменить
+
+
+ Горячая клавиша недоступна
+
+
+ Версия
+ Время
+ Пожалуйста, сообщите что произошло когда произошёл сбой в приложении, чтобы мы могли его исправить
+ Отправить отчёт
+ Отмена
+ Общие
+ Исключения
+ Тип исключения
+ Источник
+ Трессировка стека
+ Отправляем
+ Отчёт успешно отправлен
+ Не удалось отправить отчёт
+ Произошёл сбой в Wox
+
+
+ Доступна новая версия Wox V{0}
+ Произошла ошибка при попытке установить обновление
+ Обновить
+ Отмена
+ Это обновление перезапустит Wox
+ Следующие файлы будут обновлены
+ Обновить файлы
+ Описание обновления
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Languages/sk.xaml b/src/modules/launcher/PowerLauncher/Languages/sk.xaml
new file mode 100644
index 0000000000..15ee75ad6d
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Languages/sk.xaml
@@ -0,0 +1,140 @@
+
+
+ Nepodarilo sa registrovať klávesovú skratku {0}
+ Nepodarilo sa spustiť {0}
+ Neplatný formát súboru pre plugin Wox
+ Pri tomto dopyte umiestniť navrchu
+ Zrušiť umiestnenie navrchu pri tomto dopyte
+ Spustiť dopyt: {0}
+ Posledný čas realizácie: {0}
+ Otvoriť
+ Nastavenia
+ O aplikácii
+ Ukončiť
+
+
+ Nastavenia Wox
+ Všeobecné
+ Spustiť Wox po štarte systému
+ Schovať Wox po strate fokusu
+ Nezobrazovať upozornenia na novú verziu
+ Zapamätať si posledné umiestnenie
+ Jazyk
+ Posledný dopyt
+ Ponechať
+ Označiť posledný dopyt
+ Prázdne
+ Max. výsledkov
+ Ignorovať klávesové skraty v režime na celú obrazovku
+ Automatická aktualizácia
+ Schovať Wox po spustení
+ Schovať ikonu v oblasti oznámení
+
+
+ Plugin
+ Nájsť ďalšie pluginy
+ Zakázať
+ Skratka akcie
+ Priečinok s pluginmy
+ Autor
+ Čas inic.: {0}ms
+ Čas dopytu: {0}ms
+
+
+ Motív
+ Prehliadať viac motívov
+ Ahoj Wox
+ Písmo poľa pre dopyt
+ Písmo výsledkov
+ Režim okno
+ Nepriehľadnosť
+
+
+ Klávesová skratka
+ Klávesová skratka pre Wox
+ Vlastná klávesová skratka pre dopyt
+ Odstrániť
+ Upraviť
+ Pridať
+ Vyberte položku, prosím
+ Ste si istý, že chcete odstrániť klávesovú skratku {0} pre plugin?
+
+
+ HTTP Proxy
+ Povoliť HTTP Proxy
+ HTTP Server
+ Port
+ Používateľské meno
+ Heslo
+ Test Proxy
+ Uložiť
+ Pole Server nemôže byť prázdne
+ Pole Port nemôže byť prázdne
+ Neplatný formát portu
+ Nastavenie proxy úspešne uložené
+ Nastavenie proxy je v poriadku
+ Pripojenie proxy zlyhalo
+
+
+ O aplikácii
+ Webstránka
+ Verzia
+ Wox bol aktivovaný {0}-krát
+ Skontrolovať aktualizácie
+ Je dostupná nová verzia {0}, prosím, reštartujte Wox.
+ Kontrola aktualizácií zlyhala, prosím, skontrolujte pripojenie na internet a nastavenie proxy k api.github.com.
+
+ Sťahovanie aktualizácií zlyhalo, skontrolujte pripojenie na internet a nastavenie proxy k github-cloud.s3.amazonaws.com,
+ alebo prejdite na https://github.com/Wox-launcher/Wox/releases pre manuálne stiahnutie aktualizácií.
+
+ Poznámky k vydaniu:
+
+
+ Stará skratka akcie
+ Nová skratka akcie
+ Zrušiť
+ Hotovo
+ Nepodarilo sa nájsť zadaný plugin
+ Nová skratka pre akciu nemôže byť prázdna
+ Nová skratka pre akciu bola priradená pre iný plugin, prosím, zvoľte inú skratku
+ Úspešné
+ Použite * ak nechcete určiť skratku pre akciu
+
+
+ Náhľad
+ Klávesová skratka je nedostupná, prosím, zadajte novú
+ Neplatná klávesová skratka pluginu
+ Aktualizovať
+
+
+ Klávesová skratka nedostupná
+
+
+ Verzia
+ Čas
+ Prosím, napíšte nám, ako došlo k pádu aplikácie, aby sme to mohli opraviť
+ Odoslať hlásenie
+ Zrušiť
+ Všeobecné
+ Výnimky
+ Typ výnimky
+ Zdroj
+ Stack Trace
+ Odosiela sa
+ Hlásenie bolo úspešne odoslané
+ Odoslanie hlásenia zlyhalo
+ Wox zaznamenal chybu
+
+
+ Je dostupné nové vydanie Wox {0}
+ Počas inštalácie aktualizácií došlo k chybe
+ Aktualizovať
+ Zrušiť
+ Tento upgrade reštartuje Wox
+ Nasledujúce súbory budú aktualizované
+ Aktualizovať súbory
+ Aktualizovať popis
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Languages/sr.xaml b/src/modules/launcher/PowerLauncher/Languages/sr.xaml
new file mode 100644
index 0000000000..91d6ec6155
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Languages/sr.xaml
@@ -0,0 +1,139 @@
+
+
+ Neuspešno registrovana prečica: {0}
+ Neuspešno pokretanje {0}
+ Nepravilni Wox plugin format datoteke
+ Postavi kao najviši u ovom upitu
+ Poništi najviši u ovom upitu
+ Izvrši upit: {0}
+ Vreme poslednjeg izvršenja: {0}
+ Otvori
+ Podešavanja
+ O Wox-u
+ Izlaz
+
+
+ Wox Podešavanja
+ Opšte
+ Pokreni Wox pri podizanju sistema
+ Sakri Wox kada se izgubi fokus
+ Ne prikazuj obaveštenje o novoj verziji
+ Zapamti lokaciju poslednjeg pokretanja
+ Jezik
+ Stil Poslednjeg upita
+ Sačuvaj poslednji Upit
+ Selektuj poslednji Upit
+ Isprazni poslednji Upit
+ Maksimum prikazanih rezultata
+ Ignoriši prečice u fullscreen režimu
+ Auto ažuriranje
+ Sakrij Wox pri podizanju sistema
+
+
+ Plugin
+ Nađi još plugin-a
+ Onemogući
+ Ključne reči
+ Plugin direktorijum
+ Autor
+ Vreme inicijalizacije: {0}ms
+ Vreme upita: {0}ms
+
+
+ Tema
+ Pretražite još tema
+ Zdravo Wox
+ Font upita
+ Font rezultata
+ Režim prozora
+ Neprozirnost
+
+
+ Prečica
+ Wox prečica
+ prečica za ručno dodat upit
+ Obriši
+ Izmeni
+ Dodaj
+ Molim Vas izaberite stavku
+ Da li ste sigurni da želite da obrišete prečicu za {0} plugin?
+
+
+ HTTP proksi
+ Uključi HTTP proksi
+ HTTP Server
+ Port
+ Korisničko ime
+ Šifra
+ Test proksi
+ Sačuvaj
+ Polje za server ne može da bude prazno
+ Polje za port ne može da bude prazno
+ Nepravilan format porta
+ Podešavanja proksija uspešno sačuvana
+ Proksi uspešno podešen
+ Veza sa proksijem neuspešna
+
+
+ O Wox-u
+ Veb sajt
+ Verzija
+ Aktivirali ste Wox {0} puta
+ Proveri ažuriranja
+ Nove verzija {0} je dostupna, molim Vas ponovo pokrenite Wox.
+ Neuspešna provera ažuriranja, molim Vas proverite vašu vezu i podešavanja za proksi prema api.github.com.
+
+ Neuspešno preuzimanje ažuriranja, molim Vas proverite vašu vezu i podešavanja za proksi prema github-cloud.s3.amazonaws.com,
+ ili posetite https://github.com/Wox-launcher/Wox/releases da preuzmete ažuriranja ručno.
+
+ U novoj verziji:
+
+
+ Prečica za staru radnju
+ Prečica za novu radnju
+ Otkaži
+ Gotovo
+ Navedeni plugin nije moguće pronaći
+ Prečica za novu radnju ne može da bude prazna
+ Prečica za novu radnju je dodeljena drugom plugin-u, molim Vas dodelite drugu prečicu
+ Uspešno
+ Koristite * ako ne želite da navedete prečicu za radnju
+
+
+ Pregled
+ Prečica je nedustupna, molim Vas izaberite drugu prečicu
+ Nepravlna prečica za plugin
+ Ažuriraj
+
+
+ Prečica nedostupna
+
+
+ Verzija
+ Vreme
+ Molimo Vas recite nam kako je aplikacija prestala sa radom, da bi smo je ispravili
+ Pošalji izveštaj
+ Otkaži
+ Opšte
+ Izuzetak
+ Tipovi Izuzetaka
+ Izvor
+ Stack Trace
+ Slanje
+ Izveštaj uspešno poslat
+ Izveštaj neuspešno poslat
+ Wox je dobio grešku
+
+
+ Nova verzija Wox-a {0} je dostupna
+ Došlo je do greške prilokom instalacije ažuriranja
+ Ažuriraj
+ Otkaži
+ Ova nadogradnja će ponovo pokrenuti Wox
+ Sledeće datoteke će biti ažurirane
+ Ažuriraj datoteke
+ Opis ažuriranja
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Languages/tr.xaml b/src/modules/launcher/PowerLauncher/Languages/tr.xaml
new file mode 100644
index 0000000000..2690c79f10
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Languages/tr.xaml
@@ -0,0 +1,143 @@
+
+
+ Kısayol tuşu ataması başarısız oldu: {0}
+ {0} başlatılamıyor
+ Geçersiz Wox eklenti dosyası formatı
+ Bu sorgu için başa sabitle
+ Sabitlemeyi kaldır
+ Sorguyu çalıştır: {0}
+ Son çalıştırma zamanı: {0}
+ Aç
+ Ayarlar
+ Hakkında
+ Çıkış
+
+
+ Wox Ayarları
+ Genel
+ Wox'u başlangıçta başlat
+ Odak pencereden ayrıldığında Wox'u gizle
+ Güncelleme bildirimlerini gösterme
+ Pencere konumunu hatırla
+ Dil
+ Pencere açıldığında
+ Son sorguyu sakla
+ Son sorguyu sakla ve tümünü seç
+ Sorgu kutusunu temizle
+ Maksimum sonuç sayısı
+ Tam ekran modunda kısayol tuşunu gözardı et
+ Otomatik Güncelle
+ Başlangıçta Wox'u gizle
+ Sistem çekmecesi simgesini gizle
+ Sorgu Arama Hassasiyeti
+
+
+ Eklentiler
+ Daha fazla eklenti bul
+ Devre Dışı
+ Anahtar Kelimeler
+ Eklenti Klasörü
+ Yapımcı
+ Açılış Süresi: {0}ms
+ Sorgu Süresi: {0}ms
+
+
+ Temalar
+ Daha fazla tema bul
+ Merhaba Wox
+ Pencere Yazı Tipi
+ Sonuç Yazı Tipi
+ Pencere Modu
+ Saydamlık
+ {0} isimli tema bulunamadı, varsayılan temaya dönülüyor.
+ {0} isimli tema yüklenirken hata oluştu, varsayılan temaya dönülüyor.
+
+
+ Kısayol Tuşu
+ Wox Kısayolu
+ Özel Sorgu Kısayolları
+ Sil
+ Düzenle
+ Ekle
+ Lütfen bir öğe seçin
+ {0} eklentisi için olan kısayolu silmek istediğinize emin misiniz?
+
+
+ Vekil Sunucu
+ HTTP vekil sunucuyu etkinleştir.
+ Sunucu Adresi
+ Port
+ Kullanıcı Adı
+ Parola
+ Ayarları Sına
+ Kaydet
+ Sunucu adresi boş olamaz
+ Port boş olamaz
+ Port biçimi geçersiz
+ Vekil sunucu ayarları başarıyla kaydedildi
+ Vekil sunucu doğru olarak ayarlandı
+ Vekil sunucuya bağlanılırken hata oluştu
+
+
+ Hakkında
+ Web Sitesi
+ Sürüm
+ Şu ana kadar Wox'u {0} kez aktifleştirdiniz.
+ Güncellemeleri Kontrol Et
+ Uygulamanın yeni sürümü ({0}) mevcut, Lütfen Wox'u yeniden başlatın.
+ Güncelleme kontrolü başarısız oldu. Lütfen bağlantınız ve vekil sunucu ayarlarınızın api.github.com adresine ulaşabilir olduğunu kontrol edin.
+
+ Güncellemenin yüklenmesi başarısız oldu. Lütfen bağlantınız ve vekil sunucu ayarlarınızın github-cloud.s3.amazonaws.com
+ adresine ulaşabilir olduğunu kontrol edin ya da https://github.com/Wox-launcher/Wox/releases adresinden güncellemeyi elle indirin.
+
+ Sürüm Notları:
+
+
+ Eski Anahtar Kelime
+ Yeni Anahtar Kelime
+ İptal
+ Tamam
+ Belirtilen eklenti bulunamadı
+ Yeni anahtar kelime boş olamaz
+ Yeni anahtar kelime başka bir eklentiye atanmış durumda. Lütfen başka bir anahtar kelime seçin
+ Başarılı
+ Anahtar kelime belirlemek istemiyorsanız * kullanın
+
+
+ Önizleme
+ Kısayol tuşu kullanılabilir değil, lütfen başka bir kısayol tuşu seçin
+ Geçersiz eklenti kısayol tuşu
+ Güncelle
+
+
+ Kısayol tuşu kullanılabilir değil
+
+
+ Sürüm
+ Tarih
+ Sorunu çözebilmemiz için lütfen uygulamanın ne yaparken çöktüğünü belirtin.
+ Raporu Gönder
+ İptal
+ Genel
+ Özel Durumlar
+ Özel Durum Tipi
+ Kaynak
+ Yığın İzleme
+ Gönderiliyor
+ Hata raporu başarıyla gönderildi
+ Hata raporu gönderimi başarısız oldu
+ Wox'ta bir hata oluştu
+
+
+ Wox'un yeni bir sürümü ({0}) mevcut
+ Güncellemelerin kurulması sırasında bir hata oluştu
+ Güncelle
+ İptal
+ Bu güncelleme Wox'u yeniden başlatacaktır
+ Aşağıdaki dosyalar güncelleştirilecektir
+ Güncellenecek dosyalar
+ Güncelleme açıklaması
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Languages/uk-UA.xaml b/src/modules/launcher/PowerLauncher/Languages/uk-UA.xaml
new file mode 100644
index 0000000000..ccebeae7ee
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Languages/uk-UA.xaml
@@ -0,0 +1,130 @@
+
+
+ Реєстрація хоткея {0} не вдалася
+ Не вдалося запустити {0}
+ Невірний формат файлу плагіна Wox
+ Відображати першим при такому ж запиті
+ Відмінити відображення першим при такому ж запиті
+ Виконати запит: {0}
+ Час останнього використання: {0}
+ Відкрити
+ Налаштування
+ Про Wox
+ Закрити
+
+
+ Налаштування Wox
+ Основні
+ Запускати Wox при запуску системи
+ Сховати Wox якщо втрачено фокус
+ Не повідомляти про доступні нові версії
+ Запам'ятати останнє місце запуску
+ Мова
+ Максимальна кількість результатів
+ Ігнорувати гарячі клавіші в повноекранному режимі
+ Автоматичне оновлення
+ Сховати Wox при запуску системи
+
+
+ Плагіни
+ Знайти більше плагінів
+ Відключити
+ Ключове слово
+ Директорія плагіну
+ Автор
+ Ініціалізація: {0}ms
+ Запит: {0}ms
+
+
+ Теми
+ Знайти більше тем
+ Привіт Wox
+ Шрифт запитів
+ Шрифт результатів
+ Віконний режим
+ Прозорість
+
+
+ Гарячі клавіші
+ Гаряча клавіша Wox
+ Задані гарячі клавіші для запитів
+ Видалити
+ Змінити
+ Додати
+ Спочатку виберіть елемент
+ Ви впевнені, що хочете видалити гарячу клавішу ({0}) плагіну?
+
+
+ HTTP Proxy
+ Включити HTTP Proxy
+ HTTP Сервер
+ Порт
+ Логін
+ Пароль
+ Перевірити Proxy
+ Зберегти
+ Необхідно вказати "HTTP Сервер"
+ Необхідно вказати "Порт"
+ Невірний формат порту
+ Налаштування Proxy успішно збережено
+ Proxy успішно налаштований
+ Невдале підключення Proxy
+
+
+ Про Wox
+ Сайт
+ Версия
+ Ви скористалися Wox вже {0} разів
+ Перевірити наявність оновлень
+ Доступна нова версія {0}, будь ласка, перезавантажте Wox
+ Примітки до поточного релізу:
+
+
+ Поточна гаряча клавіша
+ Нова гаряча клавіша
+ Скасувати
+ Готово
+ Не вдалося знайти вказаний плагін
+ Нова гаряча клавіша не може бути порожньою
+ Нова гаряча клавіша вже використовується іншим плагіном. Будь ласка, вкажіть нову
+ Збережено
+ Використовуйте * у разі, якщо ви не хочете ставити конкретну гарячу клавішу
+
+
+ Перевірити
+ Гаряча клавіша недоступна. Будь ласка, вкажіть нову
+ Недійсна гаряча клавіша плагіна
+ Оновити
+
+
+ Гаряча клавіша недоступна
+
+
+ Версія
+ Час
+ Будь ласка, розкажіть нам, як додаток вийшов із ладу, щоб ми могли це виправити
+ Надіслати звіт
+ Скасувати
+ Основне
+ Винятки
+ Тип винятку
+ Джерело
+ Траса стеку
+ Відправити
+ Звіт успішно відправлено
+ Не вдалося відправити звіт
+ Стався збій в додатоку Wox
+
+
+ Доступна нова версія Wox V{0}
+ Сталася помилка під час спроби встановити оновлення
+ Оновити
+ Скасувати
+ Це оновлення перезавантажить Wox
+ Ці файли будуть оновлені
+ Оновити файли
+ Опис оновлення
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Languages/zh-cn.xaml b/src/modules/launcher/PowerLauncher/Languages/zh-cn.xaml
new file mode 100644
index 0000000000..48f5bc5aca
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Languages/zh-cn.xaml
@@ -0,0 +1,137 @@
+
+
+ 注册热键:{0} 失败
+ 启动命令 {0} 失败
+ 不是合法的Wox插件格式
+ 在当前查询中置顶
+ 取消置顶
+ 执行查询:{0}
+ 上次执行时间:{0}
+ 打开
+ 设置
+ 关于
+ 退出
+
+
+ Wox设置
+ 通用
+ 开机启动
+ 失去焦点时自动隐藏Wox
+ 不显示新版本提示
+ 记住上次启动位置
+ 语言
+ 上次搜索关键字模式
+ 保留上次搜索关键字
+ 全选上次搜索关键字
+ 清空上次搜索关键字
+ 最大结果显示个数
+ 全屏模式下忽略热键
+ 自动更新
+ 启动时不显示主窗口
+ 隐藏任务栏图标
+
+
+ 插件
+ 浏览更多插件
+ 禁用
+ 触发关键字
+ 插件目录
+ 作者
+ 加载耗时 {0}ms
+ 查询耗时 {0}ms
+
+
+ 主题
+ 浏览更多主题
+ 你好,Wox
+ 查询框字体
+ 结果项字体
+ 窗口模式
+ 透明度
+ 无法找到主题 {0} ,切换为默认主题
+ 无法加载主题 {0} ,切换为默认主题
+
+
+ 热键
+ Wox激活热键
+ 自定义查询热键
+ 删除
+ 编辑
+ 增加
+ 请选择一项
+ 你确定要删除插件 {0} 的热键吗?
+
+
+ HTTP 代理
+ 启用 HTTP 代理
+ HTTP 服务器
+ 端口
+ 用户名
+ 密码
+ 测试代理
+ 保存
+ 服务器不能为空
+ 端口不能为空
+ 非法的端口格式
+ 保存代理设置成功
+ 代理设置正确
+ 代理连接失败
+
+
+ 关于
+ 网站
+ 版本
+ 你已经激活了Wox {0} 次
+ 检查更新
+ 发现新版本 {0} , 请重启 wox。
+ 更新说明:
+
+
+ 旧触发关键字
+ 新触发关键字
+ 取消
+ 确定
+ 找不到指定的插件
+ 新触发关键字不能为空
+ 新触发关键字已经被指派给其他插件了,请重新选择一个关键字
+ 成功
+ 如果你不想设置触发关键字,可以使用*代替
+
+
+ 预览
+ 热键不可用,请选择一个新的热键
+ 插件热键不合法
+ 更新
+
+
+ 热键不可用
+
+
+ 版本
+ 时间
+ 请告诉我们如何重现此问题,以便我们进行修复
+ 发送报告
+ 取消
+ 基本信息
+ 异常信息
+ 异常类型
+ 异常源
+ 堆栈信息
+ 发送中
+ 发送成功
+ 发送失败
+ Wox出错啦
+
+
+ 发现Wox新版本 V{0}
+ 更新Wox出错
+ 更新
+ 取消
+ 此更新需要重启Wox
+ 下列文件会被更新
+ 更新文件
+ 更新日志
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Languages/zh-tw.xaml b/src/modules/launcher/PowerLauncher/Languages/zh-tw.xaml
new file mode 100644
index 0000000000..ef0270aa00
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Languages/zh-tw.xaml
@@ -0,0 +1,130 @@
+
+
+ 登錄快速鍵:{0} 失敗
+ 啟動命令 {0} 失敗
+ 無效的 Wox 外掛格式
+ 在目前查詢中置頂
+ 取消置頂
+ 執行查詢:{0}
+ 上次執行時間:{0}
+ 開啟
+ 設定
+ 關於
+ 結束
+
+
+ Wox 設定
+ 一般
+ 開機時啟動
+ 失去焦點時自動隱藏 Wox
+ 不顯示新版本提示
+ 記住上次啟動位置
+ 語言
+ 最大結果顯示個數
+ 全螢幕模式下忽略熱鍵
+ 自動更新
+ 啟動時不顯示主視窗
+
+
+ 外掛
+ 瀏覽更多外掛
+ 停用
+ 觸發關鍵字
+ 外掛資料夾
+ 作者
+ 載入耗時:{0}ms
+ 查詢耗時:{0}ms
+
+
+ 主題
+ 瀏覽更多主題
+ 你好,Wox
+ 查詢框字體
+ 結果項字體
+ 視窗模式
+ 透明度
+
+
+ 熱鍵
+ Wox 執行熱鍵
+ 自定義熱鍵查詢
+ 刪除
+ 編輯
+ 新增
+ 請選擇一項
+ 確定要刪除外掛 {0} 的熱鍵嗎?
+
+
+ HTTP 代理
+ 啟用 HTTP 代理
+ HTTP 伺服器
+ Port
+ 使用者
+ 密碼
+ 測試代理
+ 儲存
+ 伺服器不能為空
+ Port 不能為空
+ 不正確的 Port 格式
+ 儲存代理設定成功
+ 代理設定完成
+ 代理連線失敗
+
+
+ 關於
+ 網站
+ 版本
+ 您已經啟動了 Wox {0} 次
+ 檢查更新
+ 發現有新版本 {0}, 請重新啟動 Wox。
+ 更新說明:
+
+
+ 舊觸發關鍵字
+ 新觸發關鍵字
+ 取消
+ 確定
+ 找不到指定的外掛
+ 新觸發關鍵字不能為空白
+ 新觸發關鍵字已經被指派給另一外掛,請設定其他關鍵字。
+ 成功
+ 如果不想設定觸發關鍵字,可以使用*代替
+
+
+ 預覽
+ 熱鍵不存在,請設定一個新的熱鍵
+ 外掛熱鍵無法使用
+ 更新
+
+
+ 熱鍵無法使用
+
+
+ 版本
+ 時間
+ 請告訴我們如何重現此問題,以便我們進行修復
+ 發送報告
+ 取消
+ 基本訊息
+ 例外訊息
+ 例外類型
+ 例外來源
+ 堆疊資訊
+ 傳送中
+ 傳送成功
+ 傳送失敗
+ Wox 出錯啦
+
+
+ 發現 Wox 新版本 V{0}
+ 更新 Wox 出錯
+ 更新
+ 取消
+ 此更新需要重新啟動 Wox
+ 下列檔案會被更新
+ 更新檔案
+ 更新日誌
+
+
diff --git a/src/modules/launcher/PowerLauncher/MainWindow.xaml b/src/modules/launcher/PowerLauncher/MainWindow.xaml
new file mode 100644
index 0000000000..257ef5f2b6
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/MainWindow.xaml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/launcher/PowerLauncher/MainWindow.xaml.cs b/src/modules/launcher/PowerLauncher/MainWindow.xaml.cs
new file mode 100644
index 0000000000..4e2b94d670
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/MainWindow.xaml.cs
@@ -0,0 +1,447 @@
+using System;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Media.Animation;
+using Wox.Core.Plugin;
+using Wox.Core.Resource;
+using Wox.Helper;
+using Wox.Infrastructure.UserSettings;
+using Wox.ViewModel;
+
+using Screen = System.Windows.Forms.Screen;
+using DataFormats = System.Windows.DataFormats;
+using DragEventArgs = System.Windows.DragEventArgs;
+using KeyEventArgs = System.Windows.Input.KeyEventArgs;
+using MessageBox = System.Windows.MessageBox;
+using Microsoft.Toolkit.Wpf.UI.XamlHost;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Input;
+using Windows.System;
+using System.Threading.Tasks;
+using Windows.UI.Xaml;
+using Windows.UI.Core;
+using System.Windows.Media;
+using Windows.UI.Xaml.Data;
+
+namespace PowerLauncher
+{
+ public partial class MainWindow
+ {
+
+ #region Private Fields
+
+ private readonly Storyboard _progressBarStoryboard = new Storyboard();
+ private Settings _settings;
+ private MainViewModel _viewModel;
+ private bool _isTextSetProgramatically;
+ const int ROW_HEIGHT = 75;
+ #endregion
+
+ public MainWindow(Settings settings, MainViewModel mainVM)
+ {
+ DataContext = mainVM;
+ _viewModel = mainVM;
+ _settings = settings;
+ InitializeComponent();
+
+ }
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+
+ private void OnClosing(object sender, CancelEventArgs e)
+ {
+ _viewModel.Save();
+ }
+
+ private void OnInitialized(object sender, EventArgs e)
+ {
+
+ }
+
+ private void OnLoaded(object sender, System.Windows.RoutedEventArgs _)
+ {
+ InitializePosition();
+ }
+
+ private void InitializePosition()
+ {
+ //Top = WindowTop();
+ Left = WindowLeft();
+ //_settings.WindowTop = Top;
+ _settings.WindowLeft = Left;
+ }
+
+ private void OnMouseDown(object sender, MouseButtonEventArgs e)
+ {
+ if (e.ChangedButton == MouseButton.Left) DragMove();
+ }
+
+ private void OnDrop(object sender, DragEventArgs e)
+ {
+ if (e.Data.GetDataPresent(DataFormats.FileDrop))
+ {
+ // Note that you can have more than one file.
+ string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
+ if (files[0].ToLower().EndsWith(".wox"))
+ {
+ PluginManager.InstallPlugin(files[0]);
+ }
+ else
+ {
+ MessageBox.Show(InternationalizationManager.Instance.GetTranslation("invalidWoxPluginFileFormat"));
+ }
+ }
+ e.Handled = false;
+ }
+
+ private void OnPreviewDragOver(object sender, DragEventArgs e)
+ {
+ e.Handled = true;
+ }
+
+ private void OnDeactivated(object sender, EventArgs e)
+ {
+ if (_settings.HideWhenDeactive)
+ {
+ Hide();
+ }
+ }
+
+ private void UpdatePosition()
+ {
+ if (_settings.RememberLastLaunchLocation)
+ {
+ Left = _settings.WindowLeft;
+ Top = _settings.WindowTop;
+ }
+ else
+ {
+ Left = WindowLeft();
+ //Top = WindowTop();
+ }
+ }
+
+ private void OnLocationChanged(object sender, EventArgs e)
+ {
+ if (_settings.RememberLastLaunchLocation)
+ {
+ _settings.WindowLeft = Left;
+ _settings.WindowTop = Top;
+ }
+ }
+
+ private double WindowLeft()
+ {
+ var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
+ var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
+ var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
+ var left = (dip2.X - ActualWidth) / 2 + dip1.X;
+ return left;
+ }
+
+
+ private PowerLauncher.UI.LauncherControl _launcher = null;
+ private void WindowsXamlHostTextBox_ChildChanged(object sender, EventArgs ev)
+ {
+ if (sender == null) return;
+
+ var host = (WindowsXamlHost)sender;
+ _launcher = (PowerLauncher.UI.LauncherControl)host.Child;
+ _launcher.DataContext = _viewModel;
+ _launcher.KeyDown += _launcher_KeyDown;
+ _launcher.TextBox.TextChanging += QueryTextBox_TextChanging;
+ _launcher.TextBox.Loaded += TextBox_Loaded;
+ _launcher.PropertyChanged += UserControl_PropertyChanged;
+ _viewModel.PropertyChanged += (o, e) =>
+ {
+ if (e.PropertyName == nameof(MainViewModel.MainWindowVisibility))
+ {
+ if (Visibility == System.Windows.Visibility.Visible)
+ {
+ Activate();
+ UpdatePosition();
+ _settings.ActivateTimes++;
+ if (!_viewModel.LastQuerySelected)
+ {
+ _viewModel.LastQuerySelected = true;
+ }
+
+ // to select the text so that the user can continue to type
+ if(!String.IsNullOrEmpty(_launcher.TextBox.Text))
+ {
+ _launcher.TextBox.SelectAll();
+ }
+ }
+ }
+ else if(e.PropertyName == nameof(MainViewModel.SystemQueryText))
+ {
+ this._isTextSetProgramatically = true;
+ _launcher.TextBox.Text = _viewModel.SystemQueryText;
+ }
+ };
+ }
+
+
+
+ private void UserControl_PropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "SolidBorderBrush")
+ {
+ if (_launcher != null)
+ {
+ Windows.UI.Xaml.Media.SolidColorBrush uwpBrush = _launcher.SolidBorderBrush as Windows.UI.Xaml.Media.SolidColorBrush;
+ System.Windows.Media.Color borderColor = System.Windows.Media.Color.FromArgb(uwpBrush.Color.A, uwpBrush.Color.R, uwpBrush.Color.G, uwpBrush.Color.B);
+ this.SearchBoxBorder.BorderBrush = new SolidColorBrush(borderColor);
+ this.ListBoxBorder.BorderBrush = new SolidColorBrush(borderColor);
+ }
+ }
+ }
+
+ private void TextBox_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
+ {
+ TextBox tb = (TextBox)sender;
+ tb.Focus(FocusState.Programmatic);
+ _viewModel.MainWindowVisibility = System.Windows.Visibility.Collapsed;
+ }
+
+ private UI.ResultList _resultList = null;
+ private void WindowsXamlHostListView_ChildChanged(object sender, EventArgs ev)
+ {
+ if (sender == null) return;
+
+ var host = (WindowsXamlHost)sender;
+ _resultList = (UI.ResultList)host.Child;
+ _resultList.DataContext = _viewModel;
+ _resultList.Tapped += SuggestionsList_Tapped;
+ _resultList.SuggestionsList.Loaded += SuggestionsList_Loaded;
+ _resultList.SuggestionsList.SelectionChanged += SuggestionsList_SelectionChanged;
+ _resultList.SuggestionsList.ContainerContentChanging += SuggestionList_UpdateListSize;
+ }
+
+ private void SuggestionsList_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
+ {
+ _viewModel.ColdStartFix();
+ }
+
+ private bool IsKeyDown(VirtualKey key)
+ {
+ var keyState = CoreWindow.GetForCurrentThread().GetKeyState(key);
+ return (keyState & CoreVirtualKeyStates.Down) == CoreVirtualKeyStates.Down;
+ }
+
+ private void _launcher_KeyDown(object sender, KeyRoutedEventArgs e)
+ {
+ if (e.Key == VirtualKey.Tab && IsKeyDown(VirtualKey.Shift))
+ {
+ _viewModel.SelectPrevTabItemCommand.Execute(null);
+ UpdateTextBoxToSelectedItem();
+ e.Handled = true;
+ }
+ else if (e.Key == VirtualKey.Tab)
+ {
+ _viewModel.SelectNextTabItemCommand.Execute(null);
+ UpdateTextBoxToSelectedItem();
+ e.Handled = true;
+ }
+ else if (e.Key == VirtualKey.Down)
+ {
+ _viewModel.SelectNextItemCommand.Execute(null);
+ UpdateTextBoxToSelectedItem();
+ e.Handled = true;
+ }
+ else if (e.Key == VirtualKey.Up)
+ {
+ _viewModel.SelectPrevItemCommand.Execute(null);
+ UpdateTextBoxToSelectedItem();
+ e.Handled = true;
+ }
+ else if (e.Key == VirtualKey.PageDown)
+ {
+ _viewModel.SelectNextPageCommand.Execute(null);
+ e.Handled = true;
+ }
+ else if (e.Key == VirtualKey.PageUp)
+ {
+ _viewModel.SelectPrevPageCommand.Execute(null);
+ e.Handled = true;
+ }
+ }
+
+ private void UpdateTextBoxToSelectedItem()
+ {
+ var itemText = _viewModel?.Results?.SelectedItem?.ToString() ?? null;
+ if (!String.IsNullOrEmpty(itemText))
+ {
+ _viewModel.ChangeQueryText(itemText);
+ }
+ }
+
+ private void SuggestionsList_Tapped(object sender, TappedRoutedEventArgs e)
+ {
+ var result = ((Windows.UI.Xaml.FrameworkElement)e.OriginalSource).DataContext;
+ if (result != null)
+ {
+ var resultVM = result as ResultViewModel;
+
+ //This may be null if the tapped item was one of the context buttons (run as admin etc).
+ if (resultVM != null)
+ {
+ _viewModel.Results.SelectedItem = resultVM;
+ _viewModel.OpenResultCommand.Execute(null);
+ }
+ }
+ }
+
+ /* Note: This function has been added because a white-background was observed when the list resized,
+ * when the number of elements were lesser than the maximum capacity of the list (ie. 4).
+ * Binding Height/MaxHeight Properties did not solve this issue.
+ */
+ private void SuggestionList_UpdateListSize(object sender, ContainerContentChangingEventArgs e)
+ {
+ int count = _viewModel?.Results?.Results.Count ?? 0;
+ int displayCount = Math.Min(count, _settings.MaxResultsToShow);
+ _resultList.Height = displayCount * ROW_HEIGHT;
+ }
+
+ private void SuggestionsList_SelectionChanged(object sender, Windows.UI.Xaml.Controls.SelectionChangedEventArgs e)
+ {
+ Windows.UI.Xaml.Controls.ListView listview = (Windows.UI.Xaml.Controls.ListView)sender;
+ _viewModel.Results.SelectedItem = (ResultViewModel) listview.SelectedItem;
+ if (e.AddedItems.Count > 0 && e.AddedItems[0] != null)
+ {
+ listview.ScrollIntoView(e.AddedItems[0]);
+ }
+
+ // To populate the AutoCompleteTextBox as soon as the selection is changed or set.
+ // Setting it here instead of when the text is changed as there is a delay in executing the query and populating the result
+ _launcher.AutoCompleteTextBox.PlaceholderText = ListView_FirstItem(_viewModel.QueryText);
+
+ }
+
+ private void ResultsList_ItemClick(object sender, ItemClickEventArgs e)
+ {
+ ResultViewModel result = e?.ClickedItem as ResultViewModel;
+ if(result != null)
+ {
+ _viewModel.Results.SelectedItem = result;
+ _viewModel.OpenResultCommand.Execute(null);
+ }
+ }
+
+ private void AutoSuggestBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
+ {
+ if (args != null && args.ChosenSuggestion != null)
+ {
+ ResultViewModel result = (ResultViewModel)args.ChosenSuggestion;
+ if (result != null)
+ {
+ _viewModel.Results.SelectedItem = result;
+ _viewModel.OpenResultCommand.Execute(null);
+ }
+ }
+ }
+
+ private const int millisecondsToWait = 200;
+ private static DateTime s_lastTimeOfTyping;
+
+ private string ListView_FirstItem(String input)
+ {
+ if (!String.IsNullOrEmpty(input))
+ {
+ String selectedItem = _viewModel.Results?.SelectedItem?.ToString();
+ int selectedIndex = _viewModel.Results.SelectedIndex;
+ if (selectedItem != null && selectedIndex == 0)
+ {
+ if (selectedItem.IndexOf(input) == 0)
+ {
+ return selectedItem;
+ }
+ }
+ }
+
+ return String.Empty;
+ }
+
+ private void QueryTextBox_TextChanging(TextBox sender, TextBoxTextChangingEventArgs args)
+ {
+ ClearAllQueryTextChangedHanlders();
+
+ if(this._isTextSetProgramatically)
+ {
+ this._launcher.TextBox.TextChanged += QueryTextBox_TextChangedProgramatically;
+ }
+ else
+ {
+ this._launcher.TextBox.TextChanged += QueryTextBox_TextChangedByUserInput;
+ }
+ }
+
+ private void ClearAllQueryTextChangedHanlders()
+ {
+ this._launcher.TextBox.TextChanged -= QueryTextBox_TextChangedProgramatically;
+ this._launcher.TextBox.TextChanged -= QueryTextBox_TextChangedByUserInput;
+ }
+
+ private void QueryTextBox_TextChangedProgramatically(object sender, Windows.UI.Xaml.Controls.TextChangedEventArgs e)
+ {
+ var textBox = ((Windows.UI.Xaml.Controls.TextBox)sender);
+ textBox.SelectionStart = textBox.Text.Length;
+
+ this._isTextSetProgramatically = false;
+ }
+
+
+ private void QueryTextBox_TextChangedByUserInput(object sender, Windows.UI.Xaml.Controls.TextChangedEventArgs e)
+ {
+ var text = ((Windows.UI.Xaml.Controls.TextBox)sender).Text;
+ //To clear the auto-suggest immediately instead of waiting for selection changed
+ if (text == String.Empty)
+ {
+ _launcher.AutoCompleteTextBox.PlaceholderText = String.Empty;
+ }
+
+ _viewModel.QueryText = text;
+ var latestTimeOfTyping = DateTime.Now;
+
+ Task.Run(() => DelayedCheck(latestTimeOfTyping, text));
+ s_lastTimeOfTyping = latestTimeOfTyping;
+ }
+
+ private async Task DelayedCheck(DateTime latestTimeOfTyping, string text)
+ {
+ await Task.Delay(millisecondsToWait);
+ if (latestTimeOfTyping.Equals(s_lastTimeOfTyping))
+ {
+ await System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
+ {
+ _viewModel.Query();
+ }));
+ }
+ }
+
+private void WindowsXamlHost_PreviewMouseDown(object sender, MouseButtonEventArgs e)
+ {
+ // if (sender != null && e.OriginalSource != null)
+ // {
+ // //var r = (ResultListBox)sender;
+ // //var d = (DependencyObject)e.OriginalSource;
+ // //var item = ItemsControl.ContainerFromElement(r, d) as ListBoxItem;
+ // //var result = (ResultViewModel)item?.DataContext;
+ // //if (result != null)
+ // //{
+ // // if (e.ChangedButton == MouseButton.Left)
+ // // {
+ // // _viewModel.OpenResultCommand.Execute(null);
+ // // }
+ // // else if (e.ChangedButton == MouseButton.Right)
+ // // {
+ // // _viewModel.LoadContextMenuCommand.Execute(null);
+ // // }
+ // //}
+ // }
+ }
+ }
+ }
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/PowerLauncher.csproj b/src/modules/launcher/PowerLauncher/PowerLauncher.csproj
new file mode 100644
index 0000000000..f80d2a7e79
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/PowerLauncher.csproj
@@ -0,0 +1,197 @@
+
+
+
+ WinExe
+ netcoreapp3.1
+ true
+ true
+ PowerLauncher.App
+ Resources\placeholderLauncher.ico
+ app.manifest
+ false
+ false
+ x64
+ uap10.0.18362
+
+
+
+
+ ..\..\..\..\x64\Debug\modules\launcher\
+ DEBUG;TRACE
+ full
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+ false
+
+
+
+
+ ..\..\..\..\x64\Release\modules\launcher\
+ TRACE;RELEASE
+ true
+ pdbonly
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Properties/AssemblyInfo.cs b/src/modules/launcher/PowerLauncher/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..332bfac3fc
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Properties/AssemblyInfo.cs
@@ -0,0 +1,7 @@
+using System.Reflection;
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None,
+ ResourceDictionaryLocation.SourceAssembly
+)]
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Properties/Resources.Designer.cs b/src/modules/launcher/PowerLauncher/Properties/Resources.Designer.cs
new file mode 100644
index 0000000000..26a9ddff90
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Properties/Resources.Designer.cs
@@ -0,0 +1,73 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Wox.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Wox.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
+ ///
+ internal static System.Drawing.Icon app {
+ get {
+ object obj = ResourceManager.GetObject("app", resourceCulture);
+ return ((System.Drawing.Icon)(obj));
+ }
+ }
+ }
+}
diff --git a/src/modules/launcher/PowerLauncher/Properties/Resources.resx b/src/modules/launcher/PowerLauncher/Properties/Resources.resx
new file mode 100644
index 0000000000..ac84216765
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Properties/Resources.resx
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ ..\Resources\placeholderLauncher.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Properties/Settings.Designer.cs b/src/modules/launcher/PowerLauncher/Properties/Settings.Designer.cs
new file mode 100644
index 0000000000..78a699cb3e
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Properties/Settings.Designer.cs
@@ -0,0 +1,35 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace PowerLauncher.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.5.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("https://github.com/Wox-launcher/Wox")]
+ public string GithubRepo {
+ get {
+ return ((string)(this["GithubRepo"]));
+ }
+ }
+ }
+}
diff --git a/src/modules/launcher/PowerLauncher/Properties/Settings.settings b/src/modules/launcher/PowerLauncher/Properties/Settings.settings
new file mode 100644
index 0000000000..1fc52390be
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Properties/Settings.settings
@@ -0,0 +1,9 @@
+
+
+
+
+
+ https://github.com/Wox-launcher/Wox
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/ReportWindow.xaml b/src/modules/launcher/PowerLauncher/ReportWindow.xaml
new file mode 100644
index 0000000000..ea9d52fdce
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/ReportWindow.xaml
@@ -0,0 +1,23 @@
+
+
+
+
diff --git a/src/modules/launcher/PowerLauncher/ReportWindow.xaml.cs b/src/modules/launcher/PowerLauncher/ReportWindow.xaml.cs
new file mode 100644
index 0000000000..0e25009cbe
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/ReportWindow.xaml.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Text;
+using System.Linq;
+using System.Windows;
+using System.Windows.Documents;
+using Wox.Helper;
+using Wox.Infrastructure;
+using Wox.Infrastructure.Logger;
+
+namespace PowerLauncher
+{
+ internal partial class ReportWindow
+ {
+ public ReportWindow(Exception exception)
+ {
+ InitializeComponent();
+ ErrorTextbox.Document.Blocks.FirstBlock.Margin = new Thickness(0);
+ SetException(exception);
+ }
+
+ private void SetException(Exception exception)
+ {
+ string path = Log.CurrentLogDirectory;
+ var directory = new DirectoryInfo(path);
+ var log = directory.GetFiles().OrderByDescending(f => f.LastWriteTime).First();
+
+ var paragraph = Hyperlink("Please open new issue in: ", Constant.Issue);
+ paragraph.Inlines.Add($"1. upload log file: {log.FullName}\n");
+ paragraph.Inlines.Add($"2. copy below exception message");
+ ErrorTextbox.Document.Blocks.Add(paragraph);
+
+ StringBuilder content = new StringBuilder();
+ content.AppendLine(ErrorReporting.RuntimeInfo());
+ content.AppendLine($"Date: {DateTime.Now.ToString(CultureInfo.InvariantCulture)}");
+ content.AppendLine("Exception:");
+ content.AppendLine(exception.ToString());
+ paragraph = new Paragraph();
+ paragraph.Inlines.Add(content.ToString());
+ ErrorTextbox.Document.Blocks.Add(paragraph);
+ }
+
+ private Paragraph Hyperlink(string textBeforeUrl, string url)
+ {
+ var paragraph = new Paragraph();
+ paragraph.Margin = new Thickness(0);
+
+ var link = new Hyperlink { IsEnabled = true };
+ link.Inlines.Add(url);
+ link.NavigateUri = new Uri(url);
+ link.RequestNavigate += (s, e) => Process.Start(e.Uri.ToString());
+ link.Click += (s, e) => Process.Start(url);
+
+ paragraph.Inlines.Add(textBeforeUrl);
+ paragraph.Inlines.Add(link);
+ paragraph.Inlines.Add("\n");
+
+ return paragraph;
+ }
+ }
+}
diff --git a/src/modules/launcher/PowerLauncher/Resources/placeholderLauncher.ico b/src/modules/launcher/PowerLauncher/Resources/placeholderLauncher.ico
new file mode 100644
index 0000000000..8aa98e515d
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Resources/placeholderLauncher.ico differ
diff --git a/src/modules/launcher/PowerLauncher/ResultListBox.xaml b/src/modules/launcher/PowerLauncher/ResultListBox.xaml
new file mode 100644
index 0000000000..31562e3dfc
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/ResultListBox.xaml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/launcher/PowerLauncher/ResultListBox.xaml.cs b/src/modules/launcher/PowerLauncher/ResultListBox.xaml.cs
new file mode 100644
index 0000000000..a63e95f3fb
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/ResultListBox.xaml.cs
@@ -0,0 +1,58 @@
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+
+namespace PowerLauncher
+{
+ public partial class ResultListBox
+ {
+ protected object _lock = new object();
+ private Point _lastpos;
+ private ListBoxItem curItem = null;
+ public ResultListBox()
+ {
+ InitializeComponent();
+ }
+
+ private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (e.AddedItems.Count > 0 && e.AddedItems[0] != null)
+ {
+ ScrollIntoView(e.AddedItems[0]);
+ }
+ }
+
+ private void OnMouseEnter(object sender, MouseEventArgs e)
+ {
+ lock(_lock)
+ {
+ curItem = (ListBoxItem)sender;
+ var p = e.GetPosition((IInputElement)sender);
+ _lastpos = p;
+ }
+ }
+
+ private void OnMouseMove(object sender, MouseEventArgs e)
+ {
+ lock(_lock)
+ {
+ var p = e.GetPosition((IInputElement)sender);
+ if (_lastpos != p)
+ {
+ ((ListBoxItem)sender).IsSelected = true;
+ }
+ }
+ }
+
+ private void ListBox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
+ {
+ lock(_lock)
+ {
+ if (curItem != null)
+ {
+ curItem.IsSelected = true;
+ }
+ }
+ }
+ }
+}
diff --git a/src/modules/launcher/PowerLauncher/SettingsWatcher.cs b/src/modules/launcher/PowerLauncher/SettingsWatcher.cs
new file mode 100644
index 0000000000..11f1c8a5c5
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/SettingsWatcher.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Wox.Plugin;
+
+using Microsoft.PowerToys.Settings.UI.Lib;
+using Wox.Infrastructure.UserSettings;
+using Microsoft.PowerToys.Settings.UI.Lib.Utilities;
+using System.Diagnostics;
+using System.Threading;
+using Wox.Infrastructure.Hotkey;
+using System.Windows.Input;
+using Wox.Core.Plugin;
+using System.IO;
+
+namespace PowerLauncher
+{
+ // Watch for /Local/Microsoft/PowerToys/Launcher/Settings.json changes
+ public class SettingsWatcher : BaseModel
+ {
+ private static int MAX_RETRIES = 10;
+ private FileSystemWatcher _watcher;
+ private Settings _settings;
+ public SettingsWatcher(Settings settings)
+ {
+ _settings = settings;
+ // Set up watcher
+ try
+ {
+ _watcher = Helper.GetFileWatcher("Launcher", "settings.json", OverloadSettings);
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e.Message);
+ }
+
+ // Load initial settings file
+ OverloadSettings();
+ }
+
+ public void OverloadSettings()
+ {
+ Monitor.Enter(_watcher);
+ var retry = true;
+ for (int i = 0; retry && i < MAX_RETRIES; i++)
+ {
+ retry = false;
+ try
+ {
+ var overloadSettings = SettingsUtils.GetSettings("Launcher");
+
+ var openPowerlauncher = ConvertHotkey(overloadSettings.properties.open_powerlauncher);
+ if (_settings.Hotkey != openPowerlauncher)
+ {
+ _settings.Hotkey = ConvertHotkey(overloadSettings.properties.open_powerlauncher);
+ }
+
+ var shell = PluginManager.AllPlugins.Find(pp => pp.Metadata.Name == "Shell");
+ if (shell != null)
+ {
+ var shellSettings = shell.Plugin as ISettingProvider;
+ shellSettings.UpdateSettings(overloadSettings);
+ }
+
+ if (_settings.MaxResultsToShow != overloadSettings.properties.maximum_number_of_results)
+ {
+ _settings.MaxResultsToShow = overloadSettings.properties.maximum_number_of_results;
+ }
+ }
+ catch (Exception e)
+ {
+ retry = true;
+ Thread.Sleep(1000);
+ Debug.WriteLine(e.Message);
+ }
+ }
+ Monitor.Exit(_watcher);
+ }
+
+ private string ConvertHotkey(HotkeySettings hotkey)
+ {
+ Key key = KeyInterop.KeyFromVirtualKey(hotkey.Code);
+ HotkeyModel model = new HotkeyModel(hotkey.Alt, hotkey.Shift, hotkey.Win, hotkey.Ctrl, key);
+ return model.ToString();
+ }
+
+ }
+}
diff --git a/src/modules/launcher/PowerLauncher/Themes/Base.xaml b/src/modules/launcher/PowerLauncher/Themes/Base.xaml
new file mode 100644
index 0000000000..ae195feaa4
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Themes/Base.xaml
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/launcher/PowerLauncher/Themes/BlackAndWhite.xaml b/src/modules/launcher/PowerLauncher/Themes/BlackAndWhite.xaml
new file mode 100644
index 0000000000..8c3f0cbd45
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Themes/BlackAndWhite.xaml
@@ -0,0 +1 @@
+ #4F6180
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Themes/BlurBlack.xaml b/src/modules/launcher/PowerLauncher/Themes/BlurBlack.xaml
new file mode 100644
index 0000000000..488048cf44
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Themes/BlurBlack.xaml
@@ -0,0 +1,58 @@
+
+
+
+
+
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #356ef3
+
+
+
+
+
+
diff --git a/src/modules/launcher/PowerLauncher/Themes/BlurWhite.xaml b/src/modules/launcher/PowerLauncher/Themes/BlurWhite.xaml
new file mode 100644
index 0000000000..2fe9e3185f
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Themes/BlurWhite.xaml
@@ -0,0 +1,58 @@
+
+
+
+
+
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #356ef3
+
+
+
+
+
+
diff --git a/src/modules/launcher/PowerLauncher/Themes/Dark.xaml b/src/modules/launcher/PowerLauncher/Themes/Dark.xaml
new file mode 100644
index 0000000000..5bd54b0eda
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Themes/Dark.xaml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #4F6180
+
+
+
+
+
+
diff --git a/src/modules/launcher/PowerLauncher/Themes/Gray.xaml b/src/modules/launcher/PowerLauncher/Themes/Gray.xaml
new file mode 100644
index 0000000000..e52d4201be
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Themes/Gray.xaml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #00AAF6
+
+
+
+
diff --git a/src/modules/launcher/PowerLauncher/Themes/Light.xaml b/src/modules/launcher/PowerLauncher/Themes/Light.xaml
new file mode 100644
index 0000000000..6c373ef602
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Themes/Light.xaml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #3875D7
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Themes/Metro Server.xaml b/src/modules/launcher/PowerLauncher/Themes/Metro Server.xaml
new file mode 100644
index 0000000000..9648203946
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Themes/Metro Server.xaml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ #006ac1
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Themes/Pink.xaml b/src/modules/launcher/PowerLauncher/Themes/Pink.xaml
new file mode 100644
index 0000000000..93f2dee6e3
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Themes/Pink.xaml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ #cc1081
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/PowerLauncher/Themes/ThemeBuilder/Template.xaml b/src/modules/launcher/PowerLauncher/Themes/ThemeBuilder/Template.xaml
new file mode 100644
index 0000000000..8d1c4845a4
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Themes/ThemeBuilder/Template.xaml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {%selectedResultBackgroundColor%}
+
+
+
+
+
+
diff --git a/src/modules/launcher/PowerLauncher/Themes/ThemeBuilder/ThemeConvertor.py b/src/modules/launcher/PowerLauncher/Themes/ThemeBuilder/ThemeConvertor.py
new file mode 100644
index 0000000000..674728ec3d
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Themes/ThemeBuilder/ThemeConvertor.py
@@ -0,0 +1,26 @@
+import os,plistlib
+
+def convert(path,templatePath):
+ pl = plistlib.readPlist(path)
+ with open(templatePath, 'r') as content_file:
+ template = content_file.read()
+ for key in pl:
+ if "rgba" in pl[key]:
+ template = template.replace("{%"+key+"%}",tohex(pl[key].replace("rgba","rgb")))
+ f = open(path.replace(".alfredtheme",".xaml"),'w')
+ f.write(template)
+ f.close()
+
+
+def tohex(string):
+ string = string[4:]
+ split = string.split(",")
+ split[2] = ''.join(split[2].split(")")[0])
+ r = int(split[0])
+ g = int(split[1])
+ b = int(split[2])
+ tu = (r, g, b)
+ return '#%02x%02x%02x' % tu
+
+#print tohex("rgb(255,255,255,0.50)")
+print convert("Night.alfredtheme","Light.xaml")
diff --git a/src/modules/launcher/PowerLauncher/app.manifest b/src/modules/launcher/PowerLauncher/app.manifest
new file mode 100644
index 0000000000..52d1c39327
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/app.manifest
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/launcher/PowerLauncher/app.png b/src/modules/launcher/PowerLauncher/app.png
new file mode 100644
index 0000000000..ae307b7c77
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/app.png differ
diff --git a/src/modules/launcher/README.md b/src/modules/launcher/README.md
new file mode 100644
index 0000000000..46b21d59f7
--- /dev/null
+++ b/src/modules/launcher/README.md
@@ -0,0 +1 @@
+TO DO UPDATE
diff --git a/src/modules/launcher/Wox.Core/FodyWeavers.xml b/src/modules/launcher/Wox.Core/FodyWeavers.xml
new file mode 100644
index 0000000000..4e68ed1a8b
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/FodyWeavers.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Core/FodyWeavers.xsd b/src/modules/launcher/Wox.Core/FodyWeavers.xsd
new file mode 100644
index 0000000000..221aeb8a54
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/FodyWeavers.xsd
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+ Used to control if the On_PropertyName_Changed feature is enabled.
+
+
+
+
+ Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form.
+
+
+
+
+ Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project.
+
+
+
+
+ Used to control if equality checks should use the Equals method resolved from the base class.
+
+
+
+
+ Used to control if equality checks should use the static Equals method resolved from the base class.
+
+
+
+
+ Used to turn off build warnings from this weaver.
+
+
+
+
+ Used to turn off build warnings about mismatched On_PropertyName_Changed methods.
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Core/Plugin/ExecutablePlugin.cs b/src/modules/launcher/Wox.Core/Plugin/ExecutablePlugin.cs
new file mode 100644
index 0000000000..2753b00b3a
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/Plugin/ExecutablePlugin.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Diagnostics;
+using Wox.Plugin;
+
+namespace Wox.Core.Plugin
+{
+ internal class ExecutablePlugin : JsonRPCPlugin
+ {
+ private readonly ProcessStartInfo _startInfo;
+ public override string SupportedLanguage { get; set; } = AllowedLanguage.Executable;
+
+ public ExecutablePlugin(string filename)
+ {
+ _startInfo = new ProcessStartInfo
+ {
+ FileName = filename,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true
+ };
+ }
+
+ protected override string ExecuteQuery(Query query)
+ {
+ JsonRPCServerRequestModel request = new JsonRPCServerRequestModel
+ {
+ Method = "query",
+ Parameters = new object[] { query.Search },
+ };
+
+ _startInfo.Arguments = $"\"{request}\"";
+
+ return Execute(_startInfo);
+ }
+
+ protected override string ExecuteCallback(JsonRPCRequestModel rpcRequest)
+ {
+ _startInfo.Arguments = $"\"{rpcRequest}\"";
+ return Execute(_startInfo);
+ }
+
+ protected override string ExecuteContextMenu(Result selectedResult) {
+ JsonRPCServerRequestModel request = new JsonRPCServerRequestModel {
+ Method = "contextmenu",
+ Parameters = new object[] { selectedResult.ContextData },
+ };
+
+ _startInfo.Arguments = $"\"{request}\"";
+
+ return Execute(_startInfo);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Core/Plugin/JsonPRCModel.cs b/src/modules/launcher/Wox.Core/Plugin/JsonPRCModel.cs
new file mode 100644
index 0000000000..3a0253e559
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/Plugin/JsonPRCModel.cs
@@ -0,0 +1,135 @@
+
+/* We basically follow the Json-RPC 2.0 spec (http://www.jsonrpc.org/specification) to invoke methods between Wox and other plugins,
+ * like python or other self-execute program. But, we added addtional infos (proxy and so on) into rpc request. Also, we didn't use the
+ * "id" and "jsonrpc" in the request, since it's not so useful in our request model.
+ *
+ * When execute a query:
+ * Wox -------JsonRPCServerRequestModel--------> client
+ * Wox <------JsonRPCQueryResponseModel--------- client
+ *
+ * When execute a action (which mean user select an item in reulst item):
+ * Wox -------JsonRPCServerRequestModel--------> client
+ * Wox <------JsonRPCResponseModel-------------- client
+ *
+ */
+
+using System.Collections.Generic;
+using System.Linq;
+using Wox.Plugin;
+
+namespace Wox.Core.Plugin
+{
+ public class JsonRPCErrorModel
+ {
+ public int Code { get; set; }
+
+ public string Message { get; set; }
+
+ public string Data { get; set; }
+ }
+
+ public class JsonRPCModelBase
+ {
+ public int Id { get; set; }
+ }
+
+ public class JsonRPCResponseModel : JsonRPCModelBase
+ {
+ public string Result { get; set; }
+
+ public JsonRPCErrorModel Error { get; set; }
+ }
+
+ public class JsonRPCQueryResponseModel : JsonRPCResponseModel
+ {
+ public new List Result { get; set; }
+ }
+
+ public class JsonRPCRequestModel : JsonRPCModelBase
+ {
+ public string Method { get; set; }
+
+ public object[] Parameters { get; set; }
+
+ public override string ToString()
+ {
+ string rpc = string.Empty;
+ if (Parameters != null && Parameters.Length > 0)
+ {
+ string parameters = Parameters.Aggregate("[", (current, o) => current + (GetParameterByType(o) + ","));
+ parameters = parameters.Substring(0, parameters.Length - 1) + "]";
+ rpc = string.Format(@"{{\""method\"":\""{0}\"",\""parameters\"":{1}", Method, parameters);
+ }
+ else
+ {
+ rpc = string.Format(@"{{\""method\"":\""{0}\"",\""parameters\"":[]", Method);
+ }
+
+ return rpc;
+
+ }
+
+ private string GetParameterByType(object parameter)
+ {
+ if (parameter == null) {
+ return "null";
+ }
+ if (parameter is string)
+ {
+ return string.Format(@"\""{0}\""", ReplaceEscapes(parameter.ToString()));
+ }
+ if (parameter is int || parameter is float || parameter is double)
+ {
+ return string.Format(@"{0}", parameter);
+ }
+ if (parameter is bool)
+ {
+ return string.Format(@"{0}", parameter.ToString().ToLower());
+ }
+ return parameter.ToString();
+ }
+
+ private string ReplaceEscapes(string str)
+ {
+ return str.Replace(@"\", @"\\") //Escapes in ProcessStartInfo
+ .Replace(@"\", @"\\") //Escapes itself when passed to client
+ .Replace(@"""", @"\\""""");
+ }
+ }
+
+ ///
+ /// Json RPC Request that Wox sent to client
+ ///
+ public class JsonRPCServerRequestModel : JsonRPCRequestModel
+ {
+ public override string ToString()
+ {
+ string rpc = base.ToString();
+ return rpc + "}";
+ }
+ }
+
+ ///
+ /// Json RPC Request(in query response) that client sent to Wox
+ ///
+ public class JsonRPCClientRequestModel : JsonRPCRequestModel
+ {
+ public bool DontHideAfterAction { get; set; }
+
+ public override string ToString()
+ {
+ string rpc = base.ToString();
+ return rpc + "}";
+ }
+ }
+
+ ///
+ /// Represent the json-rpc result item that client send to Wox
+ /// Typically, we will send back this request model to client after user select the result item
+ /// But if the request method starts with "Wox.", we will invoke the public APIs we expose.
+ ///
+ public class JsonRPCResult : Result
+ {
+ public JsonRPCClientRequestModel JsonRPCAction { get; set; }
+ }
+}
diff --git a/src/modules/launcher/Wox.Core/Plugin/JsonRPCPlugin.cs b/src/modules/launcher/Wox.Core/Plugin/JsonRPCPlugin.cs
new file mode 100644
index 0000000000..6b65dd09cb
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/Plugin/JsonRPCPlugin.cs
@@ -0,0 +1,208 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using Newtonsoft.Json;
+using Wox.Infrastructure.Exception;
+using Wox.Infrastructure.Logger;
+using Wox.Plugin;
+
+namespace Wox.Core.Plugin
+{
+ ///
+ /// Represent the plugin that using JsonPRC
+ /// every JsonRPC plugin should has its own plugin instance
+ ///
+ internal abstract class JsonRPCPlugin : IPlugin, IContextMenu
+ {
+ protected PluginInitContext context;
+ public const string JsonRPC = "JsonRPC";
+
+ ///
+ /// The language this JsonRPCPlugin support
+ ///
+ public abstract string SupportedLanguage { get; set; }
+
+ protected abstract string ExecuteQuery(Query query);
+ protected abstract string ExecuteCallback(JsonRPCRequestModel rpcRequest);
+ protected abstract string ExecuteContextMenu(Result selectedResult);
+
+ public List Query(Query query)
+ {
+ string output = ExecuteQuery(query);
+ try
+ {
+ return DeserializedResult(output);
+ }
+ catch (Exception e)
+ {
+ Log.Exception($"|JsonRPCPlugin.Query|Exception when query <{query}>", e);
+ return null;
+ }
+ }
+
+ public List LoadContextMenus(Result selectedResult)
+ {
+ string output = ExecuteContextMenu(selectedResult);
+ try
+ {
+ //This should not hit. If it does it's because Wox shares the same interface for querying context menu items as well as search results. In this case please file a bug.
+ //To my knowledge we aren't supporting this JSonRPC commands in Launcher, and am not able to repro this, but I will leave this here for the time being in case I'm proven wrong.
+ //We should remove this, or identify and test officially supported use cases and Deserialize this properly.
+ //return DeserializedResult(output);
+ throw new NotImplementedException();
+ }
+ catch (Exception e)
+ {
+ Log.Exception($"|JsonRPCPlugin.LoadContextMenus| THIS IS A BUG - Exception on result <{selectedResult}>", e);
+ return null;
+ }
+ }
+
+ private List DeserializedResult(string output)
+ {
+ if (!String.IsNullOrEmpty(output))
+ {
+ List results = new List();
+
+ JsonRPCQueryResponseModel queryResponseModel = JsonConvert.DeserializeObject(output);
+ if (queryResponseModel.Result == null) return null;
+
+ foreach (JsonRPCResult result in queryResponseModel.Result)
+ {
+ JsonRPCResult result1 = result;
+ result.Action = c =>
+ {
+ if (result1.JsonRPCAction == null) return false;
+
+ if (!String.IsNullOrEmpty(result1.JsonRPCAction.Method))
+ {
+ if (result1.JsonRPCAction.Method.StartsWith("Wox."))
+ {
+ ExecuteWoxAPI(result1.JsonRPCAction.Method.Substring(4), result1.JsonRPCAction.Parameters);
+ }
+ else
+ {
+ string actionReponse = ExecuteCallback(result1.JsonRPCAction);
+ JsonRPCRequestModel jsonRpcRequestModel = JsonConvert.DeserializeObject(actionReponse);
+ if (jsonRpcRequestModel != null
+ && !String.IsNullOrEmpty(jsonRpcRequestModel.Method)
+ && jsonRpcRequestModel.Method.StartsWith("Wox."))
+ {
+ ExecuteWoxAPI(jsonRpcRequestModel.Method.Substring(4), jsonRpcRequestModel.Parameters);
+ }
+ }
+ }
+ return !result1.JsonRPCAction.DontHideAfterAction;
+ };
+ results.Add(result);
+ }
+ return results;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ private void ExecuteWoxAPI(string method, object[] parameters)
+ {
+ MethodInfo methodInfo = PluginManager.API.GetType().GetMethod(method);
+ if (methodInfo != null)
+ {
+ try
+ {
+ methodInfo.Invoke(PluginManager.API, parameters);
+ }
+ catch (Exception)
+ {
+#if (DEBUG)
+ {
+ throw;
+ }
+#endif
+ }
+ }
+ }
+
+ ///
+ /// Execute external program and return the output
+ ///
+ ///
+ ///
+ ///
+ protected string Execute(string fileName, string arguments)
+ {
+ ProcessStartInfo start = new ProcessStartInfo();
+ start.FileName = fileName;
+ start.Arguments = arguments;
+ start.UseShellExecute = false;
+ start.CreateNoWindow = true;
+ start.RedirectStandardOutput = true;
+ start.RedirectStandardError = true;
+ return Execute(start);
+ }
+
+ protected string Execute(ProcessStartInfo startInfo)
+ {
+ try
+ {
+ using (var process = Process.Start(startInfo))
+ {
+ if (process != null)
+ {
+ using (var standardOutput = process.StandardOutput)
+ {
+ var result = standardOutput.ReadToEnd();
+ if (string.IsNullOrEmpty(result))
+ {
+ using (var standardError = process.StandardError)
+ {
+ var error = standardError.ReadToEnd();
+ if (!string.IsNullOrEmpty(error))
+ {
+ Log.Error($"|JsonRPCPlugin.Execute|{error}");
+ return string.Empty;
+ }
+ else
+ {
+ Log.Error("|JsonRPCPlugin.Execute|Empty standard output and standard error.");
+ return string.Empty;
+ }
+ }
+ }
+ else if (result.StartsWith("DEBUG:"))
+ {
+ MessageBox.Show(new Form { TopMost = true }, result.Substring(6));
+ return string.Empty;
+ }
+ else
+ {
+ return result;
+ }
+ }
+ }
+ else
+ {
+ Log.Error("|JsonRPCPlugin.Execute|Can't start new process");
+ return string.Empty;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Log.Exception($"|JsonRPCPlugin.Execute|Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>", e);
+ return string.Empty;
+ }
+ }
+
+ public void Init(PluginInitContext ctx)
+ {
+ context = ctx;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Core/Plugin/PluginConfig.cs b/src/modules/launcher/Wox.Core/Plugin/PluginConfig.cs
new file mode 100644
index 0000000000..8721a85a1c
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/Plugin/PluginConfig.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.IO;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using Wox.Infrastructure.Exception;
+using Wox.Infrastructure.Logger;
+using Wox.Plugin;
+
+namespace Wox.Core.Plugin
+{
+
+ internal abstract class PluginConfig
+ {
+ private const string PluginConfigName = "plugin.json";
+ private static readonly List PluginMetadatas = new List();
+
+ ///
+ /// Parse plugin metadata in giving directories
+ ///
+ ///
+ ///
+ public static List Parse(string[] pluginDirectories)
+ {
+ PluginMetadatas.Clear();
+ var directories = pluginDirectories.SelectMany(Directory.GetDirectories);
+ ParsePluginConfigs(directories);
+ return PluginMetadatas;
+ }
+
+ private static void ParsePluginConfigs(IEnumerable directories)
+ {
+ // todo use linq when diable plugin is implmented since parallel.foreach + list is not thread saft
+ foreach (var directory in directories)
+ {
+ if (File.Exists(Path.Combine(directory, "NeedDelete.txt")))
+ {
+ try
+ {
+ Directory.Delete(directory, true);
+ }
+ catch (Exception e)
+ {
+ Log.Exception($"|PluginConfig.ParsePLuginConfigs|Can't delete <{directory}>", e);
+ }
+ }
+ else
+ {
+ PluginMetadata metadata = GetPluginMetadata(directory);
+ if (metadata != null)
+ {
+ PluginMetadatas.Add(metadata);
+ }
+ }
+ }
+ }
+
+ private static PluginMetadata GetPluginMetadata(string pluginDirectory)
+ {
+ string configPath = Path.Combine(pluginDirectory, PluginConfigName);
+ if (!File.Exists(configPath))
+ {
+ Log.Error($"|PluginConfig.GetPluginMetadata|Didn't find config file <{configPath}>");
+ return null;
+ }
+
+ PluginMetadata metadata;
+ try
+ {
+ metadata = JsonConvert.DeserializeObject(File.ReadAllText(configPath));
+ metadata.PluginDirectory = pluginDirectory;
+ // for plugins which doesn't has ActionKeywords key
+ metadata.ActionKeywords = metadata.ActionKeywords ?? new List { metadata.ActionKeyword };
+ // for plugin still use old ActionKeyword
+ metadata.ActionKeyword = metadata.ActionKeywords?[0];
+ }
+ catch (Exception e)
+ {
+ Log.Exception($"|PluginConfig.GetPluginMetadata|invalid json for config <{configPath}>", e);
+ return null;
+ }
+
+
+ if (!AllowedLanguage.IsAllowed(metadata.Language))
+ {
+ Log.Error($"|PluginConfig.GetPluginMetadata|Invalid language <{metadata.Language}> for config <{configPath}>");
+ return null;
+ }
+
+ if (!File.Exists(metadata.ExecuteFilePath))
+ {
+ Log.Error($"|PluginConfig.GetPluginMetadata|execute file path didn't exist <{metadata.ExecuteFilePath}> for conifg <{configPath}");
+ return null;
+ }
+
+ return metadata;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Core/Plugin/PluginInstaller.cs b/src/modules/launcher/Wox.Core/Plugin/PluginInstaller.cs
new file mode 100644
index 0000000000..ffdb0aa758
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/Plugin/PluginInstaller.cs
@@ -0,0 +1,202 @@
+using System;
+using System.IO;
+using System.Windows;
+using ICSharpCode.SharpZipLib.Zip;
+using Newtonsoft.Json;
+using Wox.Plugin;
+
+namespace Wox.Core.Plugin
+{
+ internal class PluginInstaller
+ {
+ internal static void Install(string path)
+ {
+ if (File.Exists(path))
+ {
+ string tempFoler = Path.Combine(Path.GetTempPath(), "wox\\plugins");
+ if (Directory.Exists(tempFoler))
+ {
+ Directory.Delete(tempFoler, true);
+ }
+ UnZip(path, tempFoler, true);
+
+ string iniPath = Path.Combine(tempFoler, "plugin.json");
+ if (!File.Exists(iniPath))
+ {
+ MessageBox.Show("Install failed: plugin config is missing");
+ return;
+ }
+
+ PluginMetadata plugin = GetMetadataFromJson(tempFoler);
+ if (plugin == null || plugin.Name == null)
+ {
+ MessageBox.Show("Install failed: plugin config is invalid");
+ return;
+ }
+
+ string pluginFolerPath = Infrastructure.Constant.PluginsDirectory;
+
+ string newPluginName = plugin.Name
+ .Replace("/", "_")
+ .Replace("\\", "_")
+ .Replace(":", "_")
+ .Replace("<", "_")
+ .Replace(">", "_")
+ .Replace("?", "_")
+ .Replace("*", "_")
+ .Replace("|", "_")
+ + "-" + Guid.NewGuid();
+ string newPluginPath = Path.Combine(pluginFolerPath, newPluginName);
+ string content = $"Do you want to install following plugin?{Environment.NewLine}{Environment.NewLine}" +
+ $"Name: {plugin.Name}{Environment.NewLine}" +
+ $"Version: {plugin.Version}{Environment.NewLine}" +
+ $"Author: {plugin.Author}";
+ PluginPair existingPlugin = PluginManager.GetPluginForId(plugin.ID);
+
+ if (existingPlugin != null)
+ {
+ content = $"Do you want to update following plugin?{Environment.NewLine}{Environment.NewLine}" +
+ $"Name: {plugin.Name}{Environment.NewLine}" +
+ $"Old Version: {existingPlugin.Metadata.Version}" +
+ $"{Environment.NewLine}New Version: {plugin.Version}" +
+ $"{Environment.NewLine}Author: {plugin.Author}";
+ }
+
+ var result = MessageBox.Show(content, "Install plugin", MessageBoxButton.YesNo, MessageBoxImage.Question);
+ if (result == MessageBoxResult.Yes)
+ {
+ if (existingPlugin != null && Directory.Exists(existingPlugin.Metadata.PluginDirectory))
+ {
+ //when plugin is in use, we can't delete them. That's why we need to make plugin folder a random name
+ File.Create(Path.Combine(existingPlugin.Metadata.PluginDirectory, "NeedDelete.txt")).Close();
+ }
+
+ UnZip(path, newPluginPath, true);
+ Directory.Delete(tempFoler, true);
+
+ //exsiting plugins may be has loaded by application,
+ //if we try to delelte those kind of plugins, we will get a error that indicate the
+ //file is been used now.
+ //current solution is to restart wox. Ugly.
+ //if (MainWindow.Initialized)
+ //{
+ // Plugins.Initialize();
+ //}
+ if (MessageBox.Show($"You have installed plugin {plugin.Name} successfully.{Environment.NewLine}" +
+ "Restart Wox to take effect?",
+ "Install plugin", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
+ {
+ PluginManager.API.RestarApp();
+ }
+ }
+ }
+ }
+
+ private static PluginMetadata GetMetadataFromJson(string pluginDirectory)
+ {
+ string configPath = Path.Combine(pluginDirectory, "plugin.json");
+ PluginMetadata metadata;
+
+ if (!File.Exists(configPath))
+ {
+ return null;
+ }
+
+ try
+ {
+ metadata = JsonConvert.DeserializeObject(File.ReadAllText(configPath));
+ metadata.PluginDirectory = pluginDirectory;
+ }
+ catch (Exception)
+ {
+ string error = $"Parse plugin config {configPath} failed: json format is not valid";
+#if (DEBUG)
+ {
+ throw new Exception(error);
+ }
+#endif
+ return null;
+ }
+
+
+ if (!AllowedLanguage.IsAllowed(metadata.Language))
+ {
+ string error = $"Parse plugin config {configPath} failed: invalid language {metadata.Language}";
+#if (DEBUG)
+ {
+ throw new Exception(error);
+ }
+#endif
+ return null;
+ }
+ if (!File.Exists(metadata.ExecuteFilePath))
+ {
+ string error = $"Parse plugin config {configPath} failed: ExecuteFile {metadata.ExecuteFilePath} didn't exist";
+#if (DEBUG)
+ {
+ throw new Exception(error);
+ }
+#endif
+ return null;
+ }
+
+ return metadata;
+ }
+
+ ///
+ /// unzip
+ ///
+ /// The ziped file.
+ /// The STR directory.
+ /// overwirte
+ private static void UnZip(string zipedFile, string strDirectory, bool overWrite)
+ {
+ if (strDirectory == "")
+ strDirectory = Directory.GetCurrentDirectory();
+ if (!strDirectory.EndsWith("\\"))
+ strDirectory = strDirectory + "\\";
+
+ using (ZipInputStream s = new ZipInputStream(File.OpenRead(zipedFile)))
+ {
+ ZipEntry theEntry;
+
+ while ((theEntry = s.GetNextEntry()) != null)
+ {
+ string directoryName = "";
+ string pathToZip = "";
+ pathToZip = theEntry.Name;
+
+ if (pathToZip != "")
+ directoryName = Path.GetDirectoryName(pathToZip) + "\\";
+
+ string fileName = Path.GetFileName(pathToZip);
+
+ Directory.CreateDirectory(strDirectory + directoryName);
+
+ if (fileName != "")
+ {
+ if ((File.Exists(strDirectory + directoryName + fileName) && overWrite) || (!File.Exists(strDirectory + directoryName + fileName)))
+ {
+ using (FileStream streamWriter = File.Create(strDirectory + directoryName + fileName))
+ {
+ byte[] data = new byte[2048];
+ while (true)
+ {
+ int size = s.Read(data, 0, data.Length);
+
+ if (size > 0)
+ streamWriter.Write(data, 0, size);
+ else
+ break;
+ }
+ streamWriter.Close();
+ }
+ }
+ }
+ }
+
+ s.Close();
+ }
+ }
+ }
+}
diff --git a/src/modules/launcher/Wox.Core/Plugin/PluginManager.cs b/src/modules/launcher/Wox.Core/Plugin/PluginManager.cs
new file mode 100644
index 0000000000..0d007715eb
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/Plugin/PluginManager.cs
@@ -0,0 +1,291 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Wox.Infrastructure;
+using Wox.Infrastructure.Logger;
+using Wox.Infrastructure.Storage;
+using Wox.Infrastructure.UserSettings;
+using Wox.Plugin;
+
+namespace Wox.Core.Plugin
+{
+ ///
+ /// The entry for managing Wox plugins
+ ///
+ public static class PluginManager
+ {
+ private static IEnumerable _contextMenuPlugins;
+
+ ///
+ /// Directories that will hold Wox plugin directory
+ ///
+
+ public static List AllPlugins { get; private set; }
+ public static readonly List GlobalPlugins = new List();
+ public static readonly Dictionary NonGlobalPlugins = new Dictionary();
+
+ public static IPublicAPI API { private set; get; }
+
+ // todo happlebao, this should not be public, the indicator function should be embeded
+ public static PluginsSettings Settings;
+ private static List _metadatas;
+ private static readonly string[] Directories = { Constant.PreinstalledDirectory, Constant.PluginsDirectory };
+
+ private static void ValidateUserDirectory()
+ {
+ if (!Directory.Exists(Constant.PluginsDirectory))
+ {
+ Directory.CreateDirectory(Constant.PluginsDirectory);
+ }
+ }
+
+ public static void Save()
+ {
+ foreach (var plugin in AllPlugins)
+ {
+ var savable = plugin.Plugin as ISavable;
+ savable?.Save();
+ }
+ }
+
+ public static void ReloadData()
+ {
+ foreach(var plugin in AllPlugins)
+ {
+ var reloadablePlugin = plugin.Plugin as IReloadable;
+ reloadablePlugin?.ReloadData();
+ }
+ }
+
+ static PluginManager()
+ {
+ ValidateUserDirectory();
+ }
+
+ ///
+ /// because InitializePlugins needs API, so LoadPlugins needs to be called first
+ /// todo happlebao The API should be removed
+ ///
+ ///
+ public static void LoadPlugins(PluginsSettings settings)
+ {
+ _metadatas = PluginConfig.Parse(Directories);
+ Settings = settings;
+ Settings.UpdatePluginSettings(_metadatas);
+ AllPlugins = PluginsLoader.Plugins(_metadatas, Settings);
+ }
+
+ ///
+ /// Call initialize for all plugins
+ ///
+ /// return the list of failed to init plugins or null for none
+ public static void InitializePlugins(IPublicAPI api)
+ {
+ API = api;
+ var failedPlugins = new ConcurrentQueue();
+ Parallel.ForEach(AllPlugins, pair =>
+ {
+ try
+ {
+ var milliseconds = Stopwatch.Debug($"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>", () =>
+ {
+ pair.Plugin.Init(new PluginInitContext
+ {
+ CurrentPluginMetadata = pair.Metadata,
+ API = API
+ });
+ });
+ pair.Metadata.InitTime += milliseconds;
+ Log.Info($"|PluginManager.InitializePlugins|Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>");
+ }
+ catch (Exception e)
+ {
+ Log.Exception(nameof(PluginManager), $"Fail to Init plugin: {pair.Metadata.Name}", e);
+ pair.Metadata.Disabled = true;
+ failedPlugins.Enqueue(pair);
+ }
+ });
+
+ _contextMenuPlugins = GetPluginsForInterface();
+ foreach (var plugin in AllPlugins)
+ {
+ if (IsGlobalPlugin(plugin.Metadata))
+ GlobalPlugins.Add(plugin);
+
+ // Plugins may have multiple ActionKeywords, eg. WebSearch
+ plugin.Metadata.ActionKeywords.Where(x => x != Query.GlobalPluginWildcardSign)
+ .ToList()
+ .ForEach(x => NonGlobalPlugins[x] = plugin);
+ }
+
+ if (failedPlugins.Any())
+ {
+ var failed = string.Join(",", failedPlugins.Select(x => x.Metadata.Name));
+ API.ShowMsg($"Fail to Init Plugins", $"Plugins: {failed} - fail to load and would be disabled, please contact plugin creator for help", "", false);
+ }
+ }
+
+ public static void InstallPlugin(string path)
+ {
+ PluginInstaller.Install(path);
+ }
+
+ public static List ValidPluginsForQuery(Query query)
+ {
+ if (NonGlobalPlugins.ContainsKey(query.ActionKeyword))
+ {
+ var plugin = NonGlobalPlugins[query.ActionKeyword];
+ return new List { plugin };
+ }
+ else
+ {
+ return GlobalPlugins;
+ }
+ }
+
+ public static List QueryForPlugin(PluginPair pair, Query query)
+ {
+ try
+ {
+ List results = null;
+ var metadata = pair.Metadata;
+ var milliseconds = Stopwatch.Debug($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}", () =>
+ {
+ results = pair.Plugin.Query(query) ?? new List();
+ UpdatePluginMetadata(results, metadata, query);
+ });
+ metadata.QueryCount += 1;
+ metadata.AvgQueryTime = metadata.QueryCount == 1 ? milliseconds : (metadata.AvgQueryTime + milliseconds) / 2;
+ return results;
+ }
+ catch (Exception e)
+ {
+ Log.Exception($"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>", e);
+ return new List();
+ }
+ }
+
+ public static void UpdatePluginMetadata(List results, PluginMetadata metadata, Query query)
+ {
+ foreach (var r in results)
+ {
+ r.PluginDirectory = metadata.PluginDirectory;
+ r.PluginID = metadata.ID;
+ r.OriginQuery = query;
+ }
+ }
+
+ private static bool IsGlobalPlugin(PluginMetadata metadata)
+ {
+ return metadata.ActionKeywords.Contains(Query.GlobalPluginWildcardSign);
+ }
+
+ ///
+ /// get specified plugin, return null if not found
+ ///
+ ///
+ ///
+ public static PluginPair GetPluginForId(string id)
+ {
+ return AllPlugins.FirstOrDefault(o => o.Metadata.ID == id);
+ }
+
+ public static IEnumerable GetPluginsForInterface() where T : IFeatures
+ {
+ return AllPlugins.Where(p => p.Plugin is T);
+ }
+
+ public static List GetContextMenusForPlugin(Result result)
+ {
+ var pluginPair = _contextMenuPlugins.FirstOrDefault(o => o.Metadata.ID == result.PluginID);
+ if (pluginPair != null)
+ {
+ var metadata = pluginPair.Metadata;
+ var plugin = (IContextMenu)pluginPair.Plugin;
+
+ try
+ {
+ var results = plugin.LoadContextMenus(result);
+ return results;
+ }
+ catch (Exception e)
+ {
+ Log.Exception($"|PluginManager.GetContextMenusForPlugin|Can't load context menus for plugin <{metadata.Name}>", e);
+ return new List();
+ }
+ }
+ else
+ {
+ return new List();
+ }
+
+ }
+
+ public static bool ActionKeywordRegistered(string actionKeyword)
+ {
+ if (actionKeyword != Query.GlobalPluginWildcardSign &&
+ NonGlobalPlugins.ContainsKey(actionKeyword))
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// used to add action keyword for multiple action keyword plugin
+ /// e.g. web search
+ ///
+ public static void AddActionKeyword(string id, string newActionKeyword)
+ {
+ var plugin = GetPluginForId(id);
+ if (newActionKeyword == Query.GlobalPluginWildcardSign)
+ {
+ GlobalPlugins.Add(plugin);
+ }
+ else
+ {
+ NonGlobalPlugins[newActionKeyword] = plugin;
+ }
+ plugin.Metadata.ActionKeywords.Add(newActionKeyword);
+ }
+
+ ///
+ /// used to add action keyword for multiple action keyword plugin
+ /// e.g. web search
+ ///
+ public static void RemoveActionKeyword(string id, string oldActionkeyword)
+ {
+ var plugin = GetPluginForId(id);
+ if (oldActionkeyword == Query.GlobalPluginWildcardSign
+ && // Plugins may have multiple ActionKeywords that are global, eg. WebSearch
+ plugin.Metadata.ActionKeywords
+ .Where(x => x == Query.GlobalPluginWildcardSign)
+ .ToList()
+ .Count == 1)
+ {
+ GlobalPlugins.Remove(plugin);
+ }
+
+ if(oldActionkeyword != Query.GlobalPluginWildcardSign)
+ NonGlobalPlugins.Remove(oldActionkeyword);
+
+
+ plugin.Metadata.ActionKeywords.Remove(oldActionkeyword);
+ }
+
+ public static void ReplaceActionKeyword(string id, string oldActionKeyword, string newActionKeyword)
+ {
+ if (oldActionKeyword != newActionKeyword)
+ {
+ AddActionKeyword(id, newActionKeyword);
+ RemoveActionKeyword(id, oldActionKeyword);
+ }
+ }
+ }
+}
diff --git a/src/modules/launcher/Wox.Core/Plugin/PluginsLoader.cs b/src/modules/launcher/Wox.Core/Plugin/PluginsLoader.cs
new file mode 100644
index 0000000000..fcfdf5b1e7
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/Plugin/PluginsLoader.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.Loader;
+using Wox.Infrastructure;
+using Wox.Infrastructure.Exception;
+using Wox.Infrastructure.Logger;
+using Wox.Infrastructure.UserSettings;
+using Wox.Plugin;
+
+namespace Wox.Core.Plugin
+{
+ public static class PluginsLoader
+ {
+ public const string PATH = "PATH";
+
+ public static List Plugins(List metadatas, PluginsSettings settings)
+ {
+ var csharpPlugins = CSharpPlugins(metadatas).ToList();
+ var executablePlugins = ExecutablePlugins(metadatas);
+ var plugins = csharpPlugins.Concat(executablePlugins).ToList();
+ return plugins;
+ }
+
+ public static IEnumerable CSharpPlugins(List source)
+ {
+ var plugins = new List();
+ var metadatas = source.Where(o => o.Language.ToUpper() == AllowedLanguage.CSharp);
+
+ foreach (var metadata in metadatas)
+ {
+ var milliseconds = Stopwatch.Debug($"|PluginsLoader.CSharpPlugins|Constructor init cost for {metadata.Name}", () =>
+ {
+
+#if DEBUG
+ var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(metadata.ExecuteFilePath);
+ var types = assembly.GetTypes();
+ var type = types.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin)));
+ var plugin = (IPlugin)Activator.CreateInstance(type);
+#else
+ Assembly assembly;
+ try
+ {
+ assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(metadata.ExecuteFilePath);
+ }
+ catch (Exception e)
+ {
+ Log.Exception($"|PluginsLoader.CSharpPlugins|Couldn't load assembly for {metadata.Name}", e);
+ return;
+ }
+ var types = assembly.GetTypes();
+ Type type;
+ try
+ {
+ type = types.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin)));
+ }
+ catch (InvalidOperationException e)
+ {
+ Log.Exception($"|PluginsLoader.CSharpPlugins|Can't find class implement IPlugin for <{metadata.Name}>", e);
+ return;
+ }
+ IPlugin plugin;
+ try
+ {
+ plugin = (IPlugin)Activator.CreateInstance(type);
+ }
+ catch (Exception e)
+ {
+ Log.Exception($"|PluginsLoader.CSharpPlugins|Can't create instance for <{metadata.Name}>", e);
+ return;
+ }
+#endif
+ PluginPair pair = new PluginPair
+ {
+ Plugin = plugin,
+ Metadata = metadata
+ };
+ plugins.Add(pair);
+ });
+ metadata.InitTime += milliseconds;
+
+ }
+ return plugins;
+ }
+
+ public static IEnumerable ExecutablePlugins(IEnumerable source)
+ {
+ var metadatas = source.Where(o => o.Language.ToUpper() == AllowedLanguage.Executable);
+
+ var plugins = metadatas.Select(metadata => new PluginPair
+ {
+ Plugin = new ExecutablePlugin(metadata.ExecuteFilePath),
+ Metadata = metadata
+ });
+ return plugins;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Core/Plugin/QueryBuilder.cs b/src/modules/launcher/Wox.Core/Plugin/QueryBuilder.cs
new file mode 100644
index 0000000000..b01a93291c
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/Plugin/QueryBuilder.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Wox.Plugin;
+
+namespace Wox.Core.Plugin
+{
+ public static class QueryBuilder
+ {
+ public static Query Build(string text, Dictionary nonGlobalPlugins)
+ {
+ // replace multiple white spaces with one white space
+ var terms = text.Split(new[] { Query.TermSeperater }, StringSplitOptions.RemoveEmptyEntries);
+ if (terms.Length == 0)
+ { // nothing was typed
+ return null;
+ }
+
+ var rawQuery = string.Join(Query.TermSeperater, terms);
+ string actionKeyword, search;
+ string possibleActionKeyword = terms[0];
+ List actionParameters;
+ if (nonGlobalPlugins.TryGetValue(possibleActionKeyword, out var pluginPair) && !pluginPair.Metadata.Disabled)
+ { // use non global plugin for query
+ actionKeyword = possibleActionKeyword;
+ actionParameters = terms.Skip(1).ToList();
+ search = actionParameters.Count > 0 ? rawQuery.Substring(actionKeyword.Length + 1) : string.Empty;
+ }
+ else
+ { // non action keyword
+ actionKeyword = string.Empty;
+ actionParameters = terms.ToList();
+ search = rawQuery;
+ }
+
+ var query = new Query
+ {
+ Terms = terms,
+ RawQuery = rawQuery,
+ ActionKeyword = actionKeyword,
+ Search = search,
+ // Obsolete value initialisation
+ ActionName = actionKeyword,
+ ActionParameters = actionParameters
+ };
+
+ return query;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Core/Plugin/README.md b/src/modules/launcher/Wox.Core/Plugin/README.md
new file mode 100644
index 0000000000..c464179c54
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/Plugin/README.md
@@ -0,0 +1,5 @@
+There are two kinds of plugins:
+1. System plugin
+ Those plugins that action keyword is "*", those plugin doesn't need action keyword
+2. User Plugin
+ Those plugins that contains customized action keyword
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Core/README.md b/src/modules/launcher/Wox.Core/README.md
new file mode 100644
index 0000000000..b2e9b83de3
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/README.md
@@ -0,0 +1,9 @@
+What does Wox.Core do?
+=====
+
+* Handle Query
+* Define Wox exceptions
+* Manage Plugins (including system plugin and user plugin)
+* Manage Themes
+* Manage i18n
+* Manage Update and version
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Core/Resource/AvailableLanguages.cs b/src/modules/launcher/Wox.Core/Resource/AvailableLanguages.cs
new file mode 100644
index 0000000000..e6c1e07f26
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/Resource/AvailableLanguages.cs
@@ -0,0 +1,52 @@
+using System.Collections.Generic;
+
+namespace Wox.Core.Resource
+{
+ internal static class AvailableLanguages
+ {
+ public static Language English = new Language("en", "English");
+ public static Language Chinese = new Language("zh-cn", "中文");
+ public static Language Chinese_TW = new Language("zh-tw", "中文(繁体)");
+ public static Language Ukrainian = new Language("uk-UA", "Українська");
+ public static Language Russian = new Language("ru", "Русский");
+ public static Language French = new Language("fr", "Français");
+ public static Language Japanese = new Language("ja", "日本語");
+ public static Language Dutch = new Language("nl", "Dutch");
+ public static Language Polish = new Language("pl", "Polski");
+ public static Language Danish = new Language("da", "Dansk");
+ public static Language German = new Language("de", "Deutsch");
+ public static Language Korean = new Language("ko", "한국어");
+ public static Language Serbian = new Language("sr", "Srpski");
+ public static Language Portuguese_BR = new Language("pt-br", "Português (Brasil)");
+ public static Language Italian = new Language("it", "Italiano");
+ public static Language Norwegian_Bokmal = new Language("nb-NO", "Norsk Bokmål");
+ public static Language Slovak = new Language("sk", "Slovenský");
+ public static Language Turkish = new Language("tr", "Türkçe");
+
+ public static List GetAvailableLanguages()
+ {
+ List languages = new List
+ {
+ English,
+ Chinese,
+ Chinese_TW,
+ Ukrainian,
+ Russian,
+ French,
+ Japanese,
+ Dutch,
+ Polish,
+ Danish,
+ German,
+ Korean,
+ Serbian,
+ Portuguese_BR,
+ Italian,
+ Norwegian_Bokmal,
+ Slovak,
+ Turkish
+ };
+ return languages;
+ }
+ }
+}
diff --git a/src/modules/launcher/Wox.Core/Resource/FontHelper.cs b/src/modules/launcher/Wox.Core/Resource/FontHelper.cs
new file mode 100644
index 0000000000..c7cc54bf67
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/Resource/FontHelper.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Linq;
+using System.Windows;
+using System.Windows.Media;
+
+namespace Wox.Core.Resource
+{
+ public static class FontHelper
+ {
+ static FontWeightConverter fontWeightConverter = new FontWeightConverter();
+ public static FontWeight GetFontWeightFromInvariantStringOrNormal(string value)
+ {
+ if (value == null) return FontWeights.Normal;
+
+ try
+ {
+ return (FontWeight) fontWeightConverter.ConvertFromInvariantString(value);
+ }
+ catch {
+ return FontWeights.Normal;
+ }
+ }
+
+ static FontStyleConverter fontStyleConverter = new FontStyleConverter();
+ public static FontStyle GetFontStyleFromInvariantStringOrNormal(string value)
+ {
+ if (value == null) return FontStyles.Normal;
+
+ try
+ {
+ return (FontStyle)fontStyleConverter.ConvertFromInvariantString(value);
+ }
+ catch
+ {
+ return FontStyles.Normal;
+ }
+ }
+
+ static FontStretchConverter fontStretchConverter = new FontStretchConverter();
+ public static FontStretch GetFontStretchFromInvariantStringOrNormal(string value)
+ {
+ if (value == null) return FontStretches.Normal;
+ try
+ {
+ return (FontStretch)fontStretchConverter.ConvertFromInvariantString(value);
+ }
+ catch
+ {
+ return FontStretches.Normal;
+ }
+ }
+
+ public static FamilyTypeface ChooseRegularFamilyTypeface(this FontFamily family)
+ {
+ return family.FamilyTypefaces.OrderBy(o =>
+ {
+ return Math.Abs(o.Stretch.ToOpenTypeStretch() - FontStretches.Normal.ToOpenTypeStretch()) * 100 +
+ Math.Abs(o.Weight.ToOpenTypeWeight() - FontWeights.Normal.ToOpenTypeWeight()) +
+ (o.Style == FontStyles.Normal ? 0 : o.Style == FontStyles.Oblique ? 1 : 2) * 1000;
+ }).FirstOrDefault() ?? family.FamilyTypefaces.FirstOrDefault();
+ }
+
+ public static FamilyTypeface ConvertFromInvariantStringsOrNormal(this FontFamily family, string style, string weight, string stretch)
+ {
+ var styleObj = GetFontStyleFromInvariantStringOrNormal(style);
+ var weightObj = GetFontWeightFromInvariantStringOrNormal(weight);
+ var stretchObj = GetFontStretchFromInvariantStringOrNormal(stretch);
+ return family.FamilyTypefaces.FirstOrDefault(o => o.Style == styleObj && o.Weight == weightObj && o.Stretch == stretchObj)
+ ?? family.ChooseRegularFamilyTypeface();
+ }
+
+ }
+}
diff --git a/src/modules/launcher/Wox.Core/Resource/Internationalization.cs b/src/modules/launcher/Wox.Core/Resource/Internationalization.cs
new file mode 100644
index 0000000000..53cce9174c
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/Resource/Internationalization.cs
@@ -0,0 +1,217 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Windows;
+using Wox.Core.Plugin;
+using Wox.Infrastructure;
+using Wox.Infrastructure.Logger;
+using Wox.Infrastructure.UserSettings;
+using Wox.Plugin;
+
+namespace Wox.Core.Resource
+{
+ public class Internationalization
+ {
+ public Settings Settings { get; set; }
+ private const string Folder = "Languages";
+ private const string DefaultFile = "en.xaml";
+ private const string Extension = ".xaml";
+ private readonly List _languageDirectories = new List();
+ private readonly List _oldResources = new List();
+
+ public Internationalization()
+ {
+ AddPluginLanguageDirectories();
+ LoadDefaultLanguage();
+ // we don't want to load /Languages/en.xaml twice
+ // so add wox language directory after load plugin language files
+ AddWoxLanguageDirectory();
+ }
+
+
+ private void AddWoxLanguageDirectory()
+ {
+ var directory = Path.Combine(Constant.ProgramDirectory, Folder);
+ _languageDirectories.Add(directory);
+ }
+
+
+ private void AddPluginLanguageDirectories()
+ {
+ foreach (var plugin in PluginManager.GetPluginsForInterface())
+ {
+ var location = Assembly.GetAssembly(plugin.Plugin.GetType()).Location;
+ var dir = Path.GetDirectoryName(location);
+ if (dir != null)
+ {
+ var pluginThemeDirectory = Path.Combine(dir, Folder);
+ _languageDirectories.Add(pluginThemeDirectory);
+ }
+ else
+ {
+ Log.Error($"|Internationalization.AddPluginLanguageDirectories|Can't find plugin path <{location}> for <{plugin.Metadata.Name}>");
+ }
+ }
+ }
+
+ private void LoadDefaultLanguage()
+ {
+ LoadLanguage(AvailableLanguages.English);
+ _oldResources.Clear();
+ }
+
+ public void ChangeLanguage(string languageCode)
+ {
+ languageCode = languageCode.NonNull();
+ Language language = GetLanguageByLanguageCode(languageCode);
+ ChangeLanguage(language);
+ }
+
+ private Language GetLanguageByLanguageCode(string languageCode)
+ {
+ var lowercase = languageCode.ToLower();
+ var language = AvailableLanguages.GetAvailableLanguages().FirstOrDefault(o => o.LanguageCode.ToLower() == lowercase);
+ if (language == null)
+ {
+ Log.Error($"|Internationalization.GetLanguageByLanguageCode|Language code can't be found <{languageCode}>");
+ return AvailableLanguages.English;
+ }
+ else
+ {
+ return language;
+ }
+ }
+
+ public void ChangeLanguage(Language language)
+ {
+ language = language.NonNull();
+
+ Settings.Language = language.LanguageCode;
+
+ RemoveOldLanguageFiles();
+ if (language != AvailableLanguages.English)
+ {
+ LoadLanguage(language);
+ }
+ UpdatePluginMetadataTranslations();
+
+ }
+
+ public bool PromptShouldUsePinyin(string languageCodeToSet)
+ {
+ var languageToSet = GetLanguageByLanguageCode(languageCodeToSet);
+
+ if (Settings.ShouldUsePinyin)
+ return false;
+
+ if (languageToSet != AvailableLanguages.Chinese && languageToSet != AvailableLanguages.Chinese_TW)
+ return false;
+
+ if (MessageBox.Show("Do you want to turn on search with Pinyin?", string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
+ return false;
+
+ return true;
+ }
+
+ private void RemoveOldLanguageFiles()
+ {
+ var dicts = Application.Current.Resources.MergedDictionaries;
+ foreach (var r in _oldResources)
+ {
+ dicts.Remove(r);
+ }
+ }
+
+ private void LoadLanguage(Language language)
+ {
+ var dicts = Application.Current.Resources.MergedDictionaries;
+ var filename = $"{language.LanguageCode}{Extension}";
+ var files = _languageDirectories
+ .Select(d => LanguageFile(d, filename))
+ .Where(f => !string.IsNullOrEmpty(f))
+ .ToArray();
+
+ if (files.Length > 0)
+ {
+ foreach (var f in files)
+ {
+ var r = new ResourceDictionary
+ {
+ Source = new Uri(f, UriKind.Absolute)
+ };
+ dicts.Add(r);
+ _oldResources.Add(r);
+ }
+ }
+ }
+
+ public List LoadAvailableLanguages()
+ {
+ return AvailableLanguages.GetAvailableLanguages();
+ }
+
+ public string GetTranslation(string key)
+ {
+ var translation = Application.Current.TryFindResource(key);
+ if (translation is string)
+ {
+ return translation.ToString();
+ }
+ else
+ {
+ Log.Error($"|Internationalization.GetTranslation|No Translation for key {key}");
+ return $"No Translation for key {key}";
+ }
+ }
+
+ private void UpdatePluginMetadataTranslations()
+ {
+ foreach (var p in PluginManager.GetPluginsForInterface())
+ {
+ var pluginI18N = p.Plugin as IPluginI18n;
+ if (pluginI18N == null) return;
+ try
+ {
+ p.Metadata.Name = pluginI18N.GetTranslatedPluginTitle();
+ p.Metadata.Description = pluginI18N.GetTranslatedPluginDescription();
+ }
+ catch (Exception e)
+ {
+ Log.Exception($"|Internationalization.UpdatePluginMetadataTranslations|Failed for <{p.Metadata.Name}>", e);
+ }
+ }
+ }
+
+ public string LanguageFile(string folder, string language)
+ {
+ if (Directory.Exists(folder))
+ {
+ string path = Path.Combine(folder, language);
+ if (File.Exists(path))
+ {
+ return path;
+ }
+ else
+ {
+ Log.Error($"|Internationalization.LanguageFile|Language path can't be found <{path}>");
+ string english = Path.Combine(folder, DefaultFile);
+ if (File.Exists(english))
+ {
+ return english;
+ }
+ else
+ {
+ Log.Error($"|Internationalization.LanguageFile|Default English Language path can't be found <{path}>");
+ return string.Empty;
+ }
+ }
+ }
+ else
+ {
+ return string.Empty;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Core/Resource/InternationalizationManager.cs b/src/modules/launcher/Wox.Core/Resource/InternationalizationManager.cs
new file mode 100644
index 0000000000..ffc2c2ecfe
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/Resource/InternationalizationManager.cs
@@ -0,0 +1,26 @@
+namespace Wox.Core.Resource
+{
+ public static class InternationalizationManager
+ {
+ private static Internationalization instance;
+ private static object syncObject = new object();
+
+ public static Internationalization Instance
+ {
+ get
+ {
+ if (instance == null)
+ {
+ lock (syncObject)
+ {
+ if (instance == null)
+ {
+ instance = new Internationalization();
+ }
+ }
+ }
+ return instance;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Core/Resource/Language.cs b/src/modules/launcher/Wox.Core/Resource/Language.cs
new file mode 100644
index 0000000000..c0bdc1ccb4
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/Resource/Language.cs
@@ -0,0 +1,18 @@
+namespace Wox.Core.Resource
+{
+ public class Language
+ {
+ public Language(string code, string display)
+ {
+ LanguageCode = code;
+ Display = display;
+ }
+
+ ///
+ /// E.g. En or Zh-CN
+ ///
+ public string LanguageCode { get; set; }
+
+ public string Display { get; set; }
+ }
+}
diff --git a/src/modules/launcher/Wox.Core/Resource/Theme.cs b/src/modules/launcher/Wox.Core/Resource/Theme.cs
new file mode 100644
index 0000000000..4b8c37ccfd
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/Resource/Theme.cs
@@ -0,0 +1,263 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Interop;
+using System.Windows.Markup;
+using System.Windows.Media;
+using Wox.Infrastructure;
+using Wox.Infrastructure.Logger;
+using Wox.Infrastructure.UserSettings;
+
+namespace Wox.Core.Resource
+{
+ public class Theme
+ {
+ private readonly List _themeDirectories = new List();
+ private ResourceDictionary _oldResource;
+ private string _oldTheme;
+ public Settings Settings { get; set; }
+ private const string Folder = "Themes";
+ private const string Extension = ".xaml";
+ private string DirectoryPath => Path.Combine(Constant.ProgramDirectory, Folder);
+ private string UserDirectoryPath => Path.Combine(Constant.DataDirectory, Folder);
+
+ public Theme()
+ {
+ _themeDirectories.Add(DirectoryPath);
+ _themeDirectories.Add(UserDirectoryPath);
+ MakesureThemeDirectoriesExist();
+
+ var dicts = Application.Current.Resources.MergedDictionaries;
+ _oldResource = dicts.First(d =>
+ {
+ var p = d.Source.AbsolutePath;
+ var dir = Path.GetDirectoryName(p).NonNull();
+ var info = new DirectoryInfo(dir);
+ var f = info.Name;
+ var e = Path.GetExtension(p);
+ var found = f == Folder && e == Extension;
+ return found;
+ });
+ _oldTheme = Path.GetFileNameWithoutExtension(_oldResource.Source.AbsolutePath);
+ }
+
+ private void MakesureThemeDirectoriesExist()
+ {
+ foreach (string dir in _themeDirectories)
+ {
+ if (!Directory.Exists(dir))
+ {
+ try
+ {
+ Directory.CreateDirectory(dir);
+ }
+ catch (Exception e)
+ {
+ Log.Exception($"|Theme.MakesureThemeDirectoriesExist|Exception when create directory <{dir}>", e);
+ }
+ }
+ }
+ }
+
+ public bool ChangeTheme(string theme)
+ {
+ const string defaultTheme = "Dark";
+
+ string path = GetThemePath(theme);
+ try
+ {
+ if (string.IsNullOrEmpty(path))
+ throw new DirectoryNotFoundException("Theme path can't be found <{path}>");
+
+ Settings.Theme = theme;
+
+ var dicts = Application.Current.Resources.MergedDictionaries;
+ //always allow re-loading default theme, in case of failure of switching to a new theme from default theme
+ if (_oldTheme != theme || theme == defaultTheme)
+ {
+ dicts.Remove(_oldResource);
+ var newResource = GetResourceDictionary();
+ dicts.Add(newResource);
+ _oldResource = newResource;
+ _oldTheme = Path.GetFileNameWithoutExtension(_oldResource.Source.AbsolutePath);
+ }
+ }
+ catch (DirectoryNotFoundException e)
+ {
+ Log.Error($"|Theme.ChangeTheme|Theme <{theme}> path can't be found");
+ if (theme != defaultTheme)
+ {
+ MessageBox.Show(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_path_not_exists"), theme));
+ ChangeTheme(defaultTheme);
+ }
+ return false;
+ }
+ catch (XamlParseException e)
+ {
+ Log.Error($"|Theme.ChangeTheme|Theme <{theme}> fail to parse");
+ if (theme != defaultTheme)
+ {
+ MessageBox.Show(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_parse_error"), theme));
+ ChangeTheme(defaultTheme);
+ }
+ return false;
+ }
+ return true;
+ }
+
+ public ResourceDictionary GetResourceDictionary()
+ {
+ var uri = GetThemePath(Settings.Theme);
+ var dict = new ResourceDictionary
+ {
+ Source = new Uri(uri, UriKind.Absolute)
+ };
+
+ Style queryBoxStyle = dict["QueryBoxStyle"] as Style;
+ if (queryBoxStyle != null)
+ {
+ queryBoxStyle.Setters.Add(new Setter(TextBox.FontFamilyProperty, new FontFamily(Settings.QueryBoxFont)));
+ queryBoxStyle.Setters.Add(new Setter(TextBox.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(Settings.QueryBoxFontStyle)));
+ queryBoxStyle.Setters.Add(new Setter(TextBox.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(Settings.QueryBoxFontWeight)));
+ queryBoxStyle.Setters.Add(new Setter(TextBox.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(Settings.QueryBoxFontStretch)));
+ }
+
+ Style resultItemStyle = dict["ItemTitleStyle"] as Style;
+ Style resultSubItemStyle = dict["ItemSubTitleStyle"] as Style;
+ Style resultItemSelectedStyle = dict["ItemTitleSelectedStyle"] as Style;
+ Style resultSubItemSelectedStyle = dict["ItemSubTitleSelectedStyle"] as Style;
+ if (resultItemStyle != null && resultSubItemStyle != null && resultSubItemSelectedStyle != null && resultItemSelectedStyle != null)
+ {
+ Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(Settings.ResultFont));
+ Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(Settings.ResultFontStyle));
+ Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(Settings.ResultFontWeight));
+ Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(Settings.ResultFontStretch));
+
+ Setter[] setters = { fontFamily, fontStyle, fontWeight, fontStretch };
+ Array.ForEach(new[] { resultItemStyle, resultSubItemStyle, resultItemSelectedStyle, resultSubItemSelectedStyle }, o => Array.ForEach(setters, p => o.Setters.Add(p)));
+ }
+ return dict;
+ }
+
+ public List LoadAvailableThemes()
+ {
+ List themes = new List();
+ foreach (var themeDirectory in _themeDirectories)
+ {
+ themes.AddRange(
+ Directory.GetFiles(themeDirectory)
+ .Where(filePath => filePath.EndsWith(Extension) && !filePath.EndsWith("Base.xaml"))
+ .ToList());
+ }
+ return themes.OrderBy(o => o).ToList();
+ }
+
+ private string GetThemePath(string themeName)
+ {
+ foreach (string themeDirectory in _themeDirectories)
+ {
+ string path = Path.Combine(themeDirectory, themeName + Extension);
+ if (File.Exists(path))
+ {
+ return path;
+ }
+ }
+
+ return string.Empty;
+ }
+
+ #region Blur Handling
+ /*
+ Found on https://github.com/riverar/sample-win10-aeroglass
+ */
+ private enum AccentState
+ {
+ ACCENT_DISABLED = 0,
+ ACCENT_ENABLE_GRADIENT = 1,
+ ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
+ ACCENT_ENABLE_BLURBEHIND = 3,
+ ACCENT_INVALID_STATE = 4
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct AccentPolicy
+ {
+ public AccentState AccentState;
+ public int AccentFlags;
+ public int GradientColor;
+ public int AnimationId;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct WindowCompositionAttributeData
+ {
+ public WindowCompositionAttribute Attribute;
+ public IntPtr Data;
+ public int SizeOfData;
+ }
+
+ private enum WindowCompositionAttribute
+ {
+ WCA_ACCENT_POLICY = 19
+ }
+ [DllImport("user32.dll")]
+ private static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data);
+
+ ///
+ /// Sets the blur for a window via SetWindowCompositionAttribute
+ ///
+ public void SetBlurForWindow()
+ {
+
+ // Exception of FindResource can't be cathed if global exception handle is set
+ if (Environment.OSVersion.Version >= new Version(6, 2))
+ {
+ var resource = Application.Current.TryFindResource("ThemeBlurEnabled");
+ bool blur;
+ if (resource is bool)
+ {
+ blur = (bool)resource;
+ }
+ else
+ {
+ blur = false;
+ }
+
+ if (blur)
+ {
+ SetWindowAccent(Application.Current.MainWindow, AccentState.ACCENT_ENABLE_BLURBEHIND);
+ }
+ else
+ {
+ SetWindowAccent(Application.Current.MainWindow, AccentState.ACCENT_DISABLED);
+ }
+ }
+ }
+
+ private void SetWindowAccent(Window w, AccentState state)
+ {
+ var windowHelper = new WindowInteropHelper(w);
+ var accent = new AccentPolicy { AccentState = state };
+ var accentStructSize = Marshal.SizeOf(accent);
+
+ var accentPtr = Marshal.AllocHGlobal(accentStructSize);
+ Marshal.StructureToPtr(accent, accentPtr, false);
+
+ var data = new WindowCompositionAttributeData
+ {
+ Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY,
+ SizeOfData = accentStructSize,
+ Data = accentPtr
+ };
+
+ SetWindowCompositionAttribute(windowHelper.Handle, ref data);
+
+ Marshal.FreeHGlobal(accentPtr);
+ }
+ #endregion
+ }
+}
diff --git a/src/modules/launcher/Wox.Core/Resource/ThemeManager.cs b/src/modules/launcher/Wox.Core/Resource/ThemeManager.cs
new file mode 100644
index 0000000000..193b0bd534
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/Resource/ThemeManager.cs
@@ -0,0 +1,26 @@
+namespace Wox.Core.Resource
+{
+ public class ThemeManager
+ {
+ private static Theme instance;
+ private static object syncObject = new object();
+
+ public static Theme Instance
+ {
+ get
+ {
+ if (instance == null)
+ {
+ lock (syncObject)
+ {
+ if (instance == null)
+ {
+ instance = new Theme();
+ }
+ }
+ }
+ return instance;
+ }
+ }
+ }
+}
diff --git a/src/modules/launcher/Wox.Core/Wox.Core.csproj b/src/modules/launcher/Wox.Core/Wox.Core.csproj
new file mode 100644
index 0000000000..ed43594791
--- /dev/null
+++ b/src/modules/launcher/Wox.Core/Wox.Core.csproj
@@ -0,0 +1,71 @@
+
+
+
+ netcoreapp3.1
+ true
+ true
+ Library
+ Properties
+ Wox.Core
+ Wox.Core
+ false
+ false
+ x64
+
+
+
+ true
+ ..\..\..\..\x64\Debug\modules\launcher\
+ DEBUG;TRACE
+ full
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+ false
+
+
+
+ ..\..\..\..\x64\Release\modules\launcher\
+ TRACE
+ true
+ pdbonly
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.CrashReporter/Images/app_error.png b/src/modules/launcher/Wox.CrashReporter/Images/app_error.png
new file mode 100644
index 0000000000..5106d6e8a9
Binary files /dev/null and b/src/modules/launcher/Wox.CrashReporter/Images/app_error.png differ
diff --git a/src/modules/launcher/Wox.CrashReporter/Images/crash_go.png b/src/modules/launcher/Wox.CrashReporter/Images/crash_go.png
new file mode 100644
index 0000000000..2dd2c45f2e
Binary files /dev/null and b/src/modules/launcher/Wox.CrashReporter/Images/crash_go.png differ
diff --git a/src/modules/launcher/Wox.CrashReporter/Images/crash_stop.png b/src/modules/launcher/Wox.CrashReporter/Images/crash_stop.png
new file mode 100644
index 0000000000..022fbc197a
Binary files /dev/null and b/src/modules/launcher/Wox.CrashReporter/Images/crash_stop.png differ
diff --git a/src/modules/launcher/Wox.CrashReporter/Images/crash_warning.png b/src/modules/launcher/Wox.CrashReporter/Images/crash_warning.png
new file mode 100644
index 0000000000..8d29625ee7
Binary files /dev/null and b/src/modules/launcher/Wox.CrashReporter/Images/crash_warning.png differ
diff --git a/src/modules/launcher/Wox.CrashReporter/Properties/AssemblyInfo.cs b/src/modules/launcher/Wox.CrashReporter/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..87313ef761
--- /dev/null
+++ b/src/modules/launcher/Wox.CrashReporter/Properties/AssemblyInfo.cs
@@ -0,0 +1,5 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("Wox.CrashReporter")]
+[assembly: Guid("0ea3743c-2c0d-4b13-b9ce-e5e1f85aea23")]
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.CrashReporter/Wox.CrashReporter.csproj b/src/modules/launcher/Wox.CrashReporter/Wox.CrashReporter.csproj
new file mode 100644
index 0000000000..dd42697968
--- /dev/null
+++ b/src/modules/launcher/Wox.CrashReporter/Wox.CrashReporter.csproj
@@ -0,0 +1,115 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {2FEB2298-7653-4009-B1EA-FFFB1A768BCC}
+ Library
+ Properties
+ Wox.CrashReporter
+ Wox.CrashReporter
+ v4.5.2
+ 512
+ ..\
+
+
+
+ true
+ full
+ false
+ ..\Output\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ true
+ ..\Output\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+ ..\packages\Exceptionless.1.5.2121\lib\net45\Exceptionless.dll
+ True
+
+
+ ..\packages\Exceptionless.1.5.2121\lib\net45\Exceptionless.Models.dll
+ True
+
+
+ ..\packages\JetBrains.Annotations.10.1.4\lib\net20\JetBrains.Annotations.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Properties\SolutionAssemblyInfo.cs
+
+
+
+
+ ReportWindow.xaml
+
+
+
+
+ Designer
+ MSBuild:Compile
+
+
+
+
+ {B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}
+ Wox.Core
+
+
+ {4fd29318-a8ab-4d8f-aa47-60bc241b8da3}
+ Wox.Infrastructure
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
+ PreserveNewest
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.CrashReporter/packages.config b/src/modules/launcher/Wox.CrashReporter/packages.config
new file mode 100644
index 0000000000..ddad0d3b3e
--- /dev/null
+++ b/src/modules/launcher/Wox.CrashReporter/packages.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Infrastructure/Alphabet.cs b/src/modules/launcher/Wox.Infrastructure/Alphabet.cs
new file mode 100644
index 0000000000..4754394cbc
--- /dev/null
+++ b/src/modules/launcher/Wox.Infrastructure/Alphabet.cs
@@ -0,0 +1,189 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using hyjiacan.util.p4n;
+using hyjiacan.util.p4n.format;
+using JetBrains.Annotations;
+using Wox.Infrastructure.Logger;
+using Wox.Infrastructure.Storage;
+using Wox.Infrastructure.UserSettings;
+
+namespace Wox.Infrastructure
+{
+ public interface IAlphabet
+ {
+ string Translate(string stringToTranslate);
+ }
+
+ public class Alphabet : IAlphabet
+ {
+ private readonly HanyuPinyinOutputFormat Format = new HanyuPinyinOutputFormat();
+ private ConcurrentDictionary PinyinCache;
+ private BinaryStorage> _pinyinStorage;
+ private Settings _settings;
+
+ public void Initialize([NotNull] Settings settings)
+ {
+ _settings = settings ?? throw new ArgumentNullException(nameof(settings));
+ InitializePinyinHelpers();
+ }
+
+ private void InitializePinyinHelpers()
+ {
+ Format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
+
+ Stopwatch.Normal("|Wox.Infrastructure.Alphabet.Initialize|Preload pinyin cache", () =>
+ {
+ _pinyinStorage = new BinaryStorage>("Pinyin");
+ PinyinCache = _pinyinStorage.TryLoad(new ConcurrentDictionary());
+
+ // force pinyin library static constructor initialize
+ PinyinHelper.toHanyuPinyinStringArray('T', Format);
+ });
+ Log.Info($"|Wox.Infrastructure.Alphabet.Initialize|Number of preload pinyin combination<{PinyinCache.Count}>");
+ }
+
+ public string Translate(string str)
+ {
+ return ConvertChineseCharactersToPinyin(str);
+ }
+
+ public string ConvertChineseCharactersToPinyin(string source)
+ {
+ if (!_settings.ShouldUsePinyin)
+ return source;
+
+ if (string.IsNullOrEmpty(source))
+ return source;
+
+ if (!ContainsChinese(source))
+ return source;
+
+ var combination = PinyinCombination(source);
+
+ var pinyinArray=combination.Select(x => string.Join("", x));
+ var acronymArray = combination.Select(Acronym).Distinct();
+
+ var joinedSingleStringCombination = new StringBuilder();
+ var all = acronymArray.Concat(pinyinArray);
+ all.ToList().ForEach(x => joinedSingleStringCombination.Append(x));
+
+ return joinedSingleStringCombination.ToString();
+ }
+
+ public void Save()
+ {
+ if (!_settings.ShouldUsePinyin)
+ {
+ return;
+ }
+ _pinyinStorage.Save(PinyinCache);
+ }
+
+ private static string[] EmptyStringArray = new string[0];
+ private static string[][] Empty2DStringArray = new string[0][];
+
+ [Obsolete("Not accurate, eg 音乐 will not return yinyue but returns yinle ")]
+ ///
+ /// replace chinese character with pinyin, non chinese character won't be modified
+ /// should be word or sentence, instead of single character. e.g. 微软
+ ///
+ public string[] Pinyin(string word)
+ {
+ if (!_settings.ShouldUsePinyin)
+ {
+ return EmptyStringArray;
+ }
+
+ var pinyin = word.Select(c =>
+ {
+ var pinyins = PinyinHelper.toHanyuPinyinStringArray(c);
+ var result = pinyins == null ? c.ToString() : pinyins[0];
+ return result;
+ }).ToArray();
+ return pinyin;
+ }
+
+ ///
+ /// replace chinese character with pinyin, non chinese character won't be modified
+ /// Because we don't have words dictionary, so we can only return all possiblie pinyin combination
+ /// e.g. 音乐 will return yinyue and yinle
+ /// should be word or sentence, instead of single character. e.g. 微软
+ ///
+ public string[][] PinyinCombination(string characters)
+ {
+ if (!_settings.ShouldUsePinyin || string.IsNullOrEmpty(characters))
+ {
+ return Empty2DStringArray;
+ }
+
+ if (!PinyinCache.ContainsKey(characters))
+ {
+ var allPinyins = new List();
+ foreach (var c in characters)
+ {
+ var pinyins = PinyinHelper.toHanyuPinyinStringArray(c, Format);
+ if (pinyins != null)
+ {
+ var r = pinyins.Distinct().ToArray();
+ allPinyins.Add(r);
+ }
+ else
+ {
+ var r = new[] { c.ToString() };
+ allPinyins.Add(r);
+ }
+ }
+
+ var combination = allPinyins.Aggregate(Combination).Select(c => c.Split(';')).ToArray();
+ PinyinCache[characters] = combination;
+ return combination;
+ }
+ else
+ {
+ return PinyinCache[characters];
+ }
+ }
+
+ public string Acronym(string[] pinyin)
+ {
+ var acronym = string.Join("", pinyin.Select(p => p[0]));
+ return acronym;
+ }
+
+ public bool ContainsChinese(string word)
+ {
+ if (!_settings.ShouldUsePinyin)
+ {
+ return false;
+ }
+
+ if (word.Length > 40)
+ {
+ //Skip strings that are too long string for Pinyin conversion.
+ return false;
+ }
+
+ var chinese = word.Select(PinyinHelper.toHanyuPinyinStringArray)
+ .Any(p => p != null);
+ return chinese;
+ }
+
+ private string[] Combination(string[] array1, string[] array2)
+ {
+ if (!_settings.ShouldUsePinyin)
+ {
+ return EmptyStringArray;
+ }
+
+ var combination = (
+ from a1 in array1
+ from a2 in array2
+ select $"{a1};{a2}"
+ ).ToArray();
+ return combination;
+ }
+ }
+}
diff --git a/src/modules/launcher/Wox.Infrastructure/Exception/ExceptionFormatter.cs b/src/modules/launcher/Wox.Infrastructure/Exception/ExceptionFormatter.cs
new file mode 100644
index 0000000000..80c3e95d18
--- /dev/null
+++ b/src/modules/launcher/Wox.Infrastructure/Exception/ExceptionFormatter.cs
@@ -0,0 +1,175 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Xml;
+using Microsoft.Win32;
+
+namespace Wox.Infrastructure.Exception
+{
+ public class ExceptionFormatter
+ {
+ public static string FormatExcpetion(System.Exception exception)
+ {
+ return CreateExceptionReport(exception);
+ }
+
+ //todo log /display line by line
+ private static string CreateExceptionReport(System.Exception ex)
+ {
+ var sb = new StringBuilder();
+ sb.AppendLine();
+ sb.AppendLine("## Exception");
+ sb.AppendLine();
+ sb.AppendLine("```");
+
+ var exlist = new List();
+
+ while (ex != null)
+ {
+ var exsb = new StringBuilder();
+ exsb.Append(ex.GetType().FullName);
+ exsb.Append(": ");
+ exsb.AppendLine(ex.Message);
+ if (ex.Source != null)
+ {
+ exsb.Append(" Source: ");
+ exsb.AppendLine(ex.Source);
+ }
+ if (ex.TargetSite != null)
+ {
+ exsb.Append(" TargetAssembly: ");
+ exsb.AppendLine(ex.TargetSite.Module.Assembly.ToString());
+ exsb.Append(" TargetModule: ");
+ exsb.AppendLine(ex.TargetSite.Module.ToString());
+ exsb.Append(" TargetSite: ");
+ exsb.AppendLine(ex.TargetSite.ToString());
+ }
+ exsb.AppendLine(ex.StackTrace);
+ exlist.Add(exsb);
+
+ ex = ex.InnerException;
+ }
+
+ foreach (var result in exlist.Select(o => o.ToString()).Reverse())
+ {
+ sb.AppendLine(result);
+ }
+ sb.AppendLine("```");
+ sb.AppendLine();
+
+ sb.AppendLine("## Environment");
+ sb.AppendLine($"* Command Line: {Environment.CommandLine}");
+ sb.AppendLine($"* Timestamp: {DateTime.Now.ToString(CultureInfo.InvariantCulture)}");
+ sb.AppendLine($"* Wox version: {Constant.Version}");
+ sb.AppendLine($"* OS Version: {Environment.OSVersion.VersionString}");
+ sb.AppendLine($"* IntPtr Length: {IntPtr.Size}");
+ sb.AppendLine($"* x64: {Environment.Is64BitOperatingSystem}");
+ sb.AppendLine($"* CLR Version: {Environment.Version}");
+ sb.AppendLine($"* Installed .NET Framework: ");
+ foreach (var result in GetFrameworkVersionFromRegistry())
+ {
+ sb.Append(" * ");
+ sb.AppendLine(result);
+ }
+
+ sb.AppendLine();
+ sb.AppendLine("## Assemblies - " + AppDomain.CurrentDomain.FriendlyName);
+ sb.AppendLine();
+ foreach (var ass in AppDomain.CurrentDomain.GetAssemblies().OrderBy(o => o.GlobalAssemblyCache ? 50 : 0))
+ {
+ sb.Append("* ");
+ sb.Append(ass.FullName);
+ sb.Append(" (");
+
+ if (ass.IsDynamic)
+ {
+ sb.Append("dynamic assembly doesn't has location");
+ }
+ else if (string.IsNullOrEmpty(ass.Location))
+ {
+ sb.Append("location is null or empty");
+
+ }
+ else
+ {
+ sb.Append(ass.Location);
+
+ }
+ sb.AppendLine(")");
+ }
+
+ return sb.ToString();
+ }
+
+ // http://msdn.microsoft.com/en-us/library/hh925568%28v=vs.110%29.aspx
+ private static List GetFrameworkVersionFromRegistry()
+ {
+ try
+ {
+ var result = new List();
+ using (RegistryKey ndpKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\NET Framework Setup\NDP\"))
+ {
+ foreach (string versionKeyName in ndpKey.GetSubKeyNames())
+ {
+ if (versionKeyName.StartsWith("v"))
+ {
+ RegistryKey versionKey = ndpKey.OpenSubKey(versionKeyName);
+ string name = (string)versionKey.GetValue("Version", "");
+ string sp = versionKey.GetValue("SP", "").ToString();
+ string install = versionKey.GetValue("Install", "").ToString();
+ if (install != "")
+ if (sp != "" && install == "1")
+ result.Add(string.Format("{0} {1} SP{2}", versionKeyName, name, sp));
+ else
+ result.Add(string.Format("{0} {1}", versionKeyName, name));
+
+ if (name != "")
+ {
+ continue;
+ }
+ foreach (string subKeyName in versionKey.GetSubKeyNames())
+ {
+ RegistryKey subKey = versionKey.OpenSubKey(subKeyName);
+ name = (string)subKey.GetValue("Version", "");
+ if (name != "")
+ sp = subKey.GetValue("SP", "").ToString();
+ install = subKey.GetValue("Install", "").ToString();
+ if (install != "")
+ {
+ if (sp != "" && install == "1")
+ result.Add(string.Format("{0} {1} {2} SP{3}", versionKeyName, subKeyName, name, sp));
+ else if (install == "1")
+ result.Add(string.Format("{0} {1} {2}", versionKeyName, subKeyName, name));
+ }
+
+ }
+
+ }
+ }
+ }
+ using (RegistryKey ndpKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\"))
+ {
+ int releaseKey = (int)ndpKey.GetValue("Release");
+ {
+ if (releaseKey == 378389)
+ result.Add("v4.5");
+
+ if (releaseKey == 378675)
+ result.Add("v4.5.1 installed with Windows 8.1");
+
+ if (releaseKey == 378758)
+ result.Add("4.5.1 installed on Windows 8, Windows 7 SP1, or Windows Vista SP2");
+ }
+ }
+ return result;
+ }
+ catch (System.Exception e)
+ {
+ return new List();
+ }
+
+ }
+ }
+}
diff --git a/src/modules/launcher/Wox.Infrastructure/FuzzyMatcher.cs b/src/modules/launcher/Wox.Infrastructure/FuzzyMatcher.cs
new file mode 100644
index 0000000000..49520e19d2
--- /dev/null
+++ b/src/modules/launcher/Wox.Infrastructure/FuzzyMatcher.cs
@@ -0,0 +1,32 @@
+using System;
+
+namespace Wox.Infrastructure
+{
+ [Obsolete("This class is obsolete and should not be used. Please use the static function StringMatcher.FuzzySearch")]
+ public class FuzzyMatcher
+ {
+ private string query;
+ private MatchOption opt;
+
+ private FuzzyMatcher(string query, MatchOption opt)
+ {
+ this.query = query.Trim();
+ this.opt = opt;
+ }
+
+ public static FuzzyMatcher Create(string query)
+ {
+ return new FuzzyMatcher(query, new MatchOption());
+ }
+
+ public static FuzzyMatcher Create(string query, MatchOption opt)
+ {
+ return new FuzzyMatcher(query, opt);
+ }
+
+ public MatchResult Evaluate(string str)
+ {
+ return StringMatcher.Instance.FuzzyMatch(query, str, opt);
+ }
+ }
+}
diff --git a/src/modules/launcher/Wox.Infrastructure/Helper.cs b/src/modules/launcher/Wox.Infrastructure/Helper.cs
new file mode 100644
index 0000000000..dc31bd70d7
--- /dev/null
+++ b/src/modules/launcher/Wox.Infrastructure/Helper.cs
@@ -0,0 +1,78 @@
+using System;
+using System.IO;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+
+namespace Wox.Infrastructure
+{
+ public static class Helper
+ {
+ ///
+ /// http://www.yinwang.org/blog-cn/2015/11/21/programming-philosophy
+ ///
+ public static T NonNull(this T obj)
+ {
+ if (obj == null)
+ {
+ throw new NullReferenceException();
+ }
+ else
+ {
+ return obj;
+ }
+ }
+
+ public static void RequireNonNull(this T obj)
+ {
+ if (obj == null)
+ {
+ throw new NullReferenceException();
+ }
+ }
+
+ public static void ValidateDataDirectory(string bundledDataDirectory, string dataDirectory)
+ {
+ if (!Directory.Exists(dataDirectory))
+ {
+ Directory.CreateDirectory(dataDirectory);
+ }
+
+ foreach (var bundledDataPath in Directory.GetFiles(bundledDataDirectory))
+ {
+ var data = Path.GetFileName(bundledDataPath);
+ var dataPath = Path.Combine(dataDirectory, data.NonNull());
+ if (!File.Exists(dataPath))
+ {
+ File.Copy(bundledDataPath, dataPath);
+ }
+ else
+ {
+ var time1 = new FileInfo(bundledDataPath).LastWriteTimeUtc;
+ var time2 = new FileInfo(dataPath).LastWriteTimeUtc;
+ if (time1 != time2)
+ {
+ File.Copy(bundledDataPath, dataPath, true);
+ }
+ }
+ }
+ }
+
+ public static void ValidateDirectory(string path)
+ {
+ if (!Directory.Exists(path))
+ {
+ Directory.CreateDirectory(path);
+ }
+ }
+
+ public static string Formatted(this T t)
+ {
+ var formatted = JsonConvert.SerializeObject(
+ t,
+ Formatting.Indented,
+ new StringEnumConverter()
+ );
+ return formatted;
+ }
+ }
+}
diff --git a/src/modules/launcher/Wox.Infrastructure/Hotkey/GlobalHotkey.cs b/src/modules/launcher/Wox.Infrastructure/Hotkey/GlobalHotkey.cs
new file mode 100644
index 0000000000..d999a1e6ac
--- /dev/null
+++ b/src/modules/launcher/Wox.Infrastructure/Hotkey/GlobalHotkey.cs
@@ -0,0 +1,107 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Wox.Plugin;
+
+namespace Wox.Infrastructure.Hotkey
+{
+ ///
+ /// Listens keyboard globally.
+ /// Uses WH_KEYBOARD_LL.
+ ///
+ public class GlobalHotkey : IDisposable
+ {
+ private static GlobalHotkey instance;
+ private InterceptKeys.LowLevelKeyboardProc hookedLowLevelKeyboardProc;
+ private IntPtr hookId = IntPtr.Zero;
+ public delegate bool KeyboardCallback(KeyEvent keyEvent, int vkCode, SpecialKeyState state);
+ public event KeyboardCallback hookedKeyboardCallback;
+
+ //Modifier key constants
+ private const int VK_SHIFT = 0x10;
+ private const int VK_CONTROL = 0x11;
+ private const int VK_ALT = 0x12;
+ private const int VK_WIN = 91;
+
+ public static GlobalHotkey Instance
+ {
+ get
+ {
+ if (instance == null)
+ {
+ instance = new GlobalHotkey();
+ }
+ return instance;
+ }
+ }
+
+ private GlobalHotkey()
+ {
+ // We have to store the LowLevelKeyboardProc, so that it is not garbage collected runtime
+ hookedLowLevelKeyboardProc = LowLevelKeyboardProc;
+ // Set the hook
+ hookId = InterceptKeys.SetHook(hookedLowLevelKeyboardProc);
+ }
+
+ public SpecialKeyState CheckModifiers()
+ {
+ SpecialKeyState state = new SpecialKeyState();
+ if ((InterceptKeys.GetKeyState(VK_SHIFT) & 0x8000) != 0)
+ {
+ //SHIFT is pressed
+ state.ShiftPressed = true;
+ }
+ if ((InterceptKeys.GetKeyState(VK_CONTROL) & 0x8000) != 0)
+ {
+ //CONTROL is pressed
+ state.CtrlPressed = true;
+ }
+ if ((InterceptKeys.GetKeyState(VK_ALT) & 0x8000) != 0)
+ {
+ //ALT is pressed
+ state.AltPressed = true;
+ }
+ if ((InterceptKeys.GetKeyState(VK_WIN) & 0x8000) != 0)
+ {
+ //WIN is pressed
+ state.WinPressed = true;
+ }
+
+ return state;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam)
+ {
+ bool continues = true;
+
+ if (nCode >= 0)
+ {
+ if (wParam.ToUInt32() == (int)KeyEvent.WM_KEYDOWN ||
+ wParam.ToUInt32() == (int)KeyEvent.WM_KEYUP ||
+ wParam.ToUInt32() == (int)KeyEvent.WM_SYSKEYDOWN ||
+ wParam.ToUInt32() == (int)KeyEvent.WM_SYSKEYUP)
+ {
+ if (hookedKeyboardCallback != null)
+ continues = hookedKeyboardCallback((KeyEvent)wParam.ToUInt32(), Marshal.ReadInt32(lParam), CheckModifiers());
+ }
+ }
+
+ if (continues)
+ {
+ return InterceptKeys.CallNextHookEx(hookId, nCode, wParam, lParam);
+ }
+ return (IntPtr)1;
+ }
+
+ ~GlobalHotkey()
+ {
+ Dispose();
+ }
+
+ public void Dispose()
+ {
+ InterceptKeys.UnhookWindowsHookEx(hookId);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Infrastructure/Hotkey/HotkeyModel.cs b/src/modules/launcher/Wox.Infrastructure/Hotkey/HotkeyModel.cs
new file mode 100644
index 0000000000..624473c086
--- /dev/null
+++ b/src/modules/launcher/Wox.Infrastructure/Hotkey/HotkeyModel.cs
@@ -0,0 +1,145 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Input;
+
+namespace Wox.Infrastructure.Hotkey
+{
+ public class HotkeyModel
+ {
+ public bool Alt { get; set; }
+ public bool Shift { get; set; }
+ public bool Win { get; set; }
+ public bool Ctrl { get; set; }
+ public Key CharKey { get; set; }
+
+
+ Dictionary specialSymbolDictionary = new Dictionary
+ {
+ {Key.Space, "Space"},
+ {Key.Oem3, "~"}
+ };
+
+ public ModifierKeys ModifierKeys
+ {
+ get
+ {
+ ModifierKeys modifierKeys = ModifierKeys.None;
+ if (Alt)
+ {
+ modifierKeys = ModifierKeys.Alt;
+ }
+ if (Shift)
+ {
+ modifierKeys = modifierKeys | ModifierKeys.Shift;
+ }
+ if (Win)
+ {
+ modifierKeys = modifierKeys | ModifierKeys.Windows;
+ }
+ if (Ctrl)
+ {
+ modifierKeys = modifierKeys | ModifierKeys.Control;
+ }
+ return modifierKeys;
+ }
+ }
+
+ public HotkeyModel(string hotkeyString)
+ {
+ Parse(hotkeyString);
+ }
+
+ public HotkeyModel(bool alt, bool shift, bool win, bool ctrl, Key key)
+ {
+ Alt = alt;
+ Shift = shift;
+ Win = win;
+ Ctrl = ctrl;
+ CharKey = key;
+ }
+
+ private void Parse(string hotkeyString)
+ {
+ if (string.IsNullOrEmpty(hotkeyString))
+ {
+ return;
+ }
+ List keys = hotkeyString.Replace(" ", "").Split('+').ToList();
+ if (keys.Contains("Alt"))
+ {
+ Alt = true;
+ keys.Remove("Alt");
+ }
+ if (keys.Contains("Shift"))
+ {
+ Shift = true;
+ keys.Remove("Shift");
+ }
+ if (keys.Contains("Win"))
+ {
+ Win = true;
+ keys.Remove("Win");
+ }
+ if (keys.Contains("Ctrl"))
+ {
+ Ctrl = true;
+ keys.Remove("Ctrl");
+ }
+ if (keys.Count > 0)
+ {
+ string charKey = keys[0];
+ KeyValuePair? specialSymbolPair = specialSymbolDictionary.FirstOrDefault(pair => pair.Value == charKey);
+ if (specialSymbolPair.Value.Value != null)
+ {
+ CharKey = specialSymbolPair.Value.Key;
+ }
+ else
+ {
+ try
+ {
+ CharKey = (Key) Enum.Parse(typeof (Key), charKey);
+ }
+ catch (ArgumentException)
+ {
+
+ }
+ }
+ }
+ }
+
+ public override string ToString()
+ {
+ string text = string.Empty;
+ if (Ctrl)
+ {
+ text += "Ctrl + ";
+ }
+ if (Alt)
+ {
+ text += "Alt + ";
+ }
+ if (Shift)
+ {
+ text += "Shift + ";
+ }
+ if (Win)
+ {
+ text += "Win + ";
+ }
+
+ if (CharKey != Key.None)
+ {
+ text += specialSymbolDictionary.ContainsKey(CharKey)
+ ? specialSymbolDictionary[CharKey]
+ : CharKey.ToString();
+ }
+ else if (!string.IsNullOrEmpty(text))
+ {
+ text = text.Remove(text.Length - 3);
+ }
+
+ return text;
+ }
+ }
+}
diff --git a/src/modules/launcher/Wox.Infrastructure/Hotkey/InterceptKeys.cs b/src/modules/launcher/Wox.Infrastructure/Hotkey/InterceptKeys.cs
new file mode 100644
index 0000000000..ef8dd576df
--- /dev/null
+++ b/src/modules/launcher/Wox.Infrastructure/Hotkey/InterceptKeys.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace Wox.Infrastructure.Hotkey
+{
+ internal static class InterceptKeys
+ {
+ public delegate IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam);
+
+ private const int WH_KEYBOARD_LL = 13;
+
+ public static IntPtr SetHook(LowLevelKeyboardProc proc)
+ {
+ using (Process curProcess = Process.GetCurrentProcess())
+ using (ProcessModule curModule = curProcess.MainModule)
+ {
+ return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
+ }
+ }
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool UnhookWindowsHookEx(IntPtr hhk);
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, UIntPtr wParam, IntPtr lParam);
+
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern IntPtr GetModuleHandle(string lpModuleName);
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
+ public static extern short GetKeyState(int keyCode);
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Infrastructure/Hotkey/KeyEvent.cs b/src/modules/launcher/Wox.Infrastructure/Hotkey/KeyEvent.cs
new file mode 100644
index 0000000000..7c601660ec
--- /dev/null
+++ b/src/modules/launcher/Wox.Infrastructure/Hotkey/KeyEvent.cs
@@ -0,0 +1,25 @@
+namespace Wox.Infrastructure.Hotkey
+{
+ public enum KeyEvent
+ {
+ ///
+ /// Key down
+ ///
+ WM_KEYDOWN = 256,
+
+ ///
+ /// Key up
+ ///
+ WM_KEYUP = 257,
+
+ ///
+ /// System key up
+ ///
+ WM_SYSKEYUP = 261,
+
+ ///
+ /// System key down
+ ///
+ WM_SYSKEYDOWN = 260
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Infrastructure/Http/Http.cs b/src/modules/launcher/Wox.Infrastructure/Http/Http.cs
new file mode 100644
index 0000000000..f8767e6e3e
--- /dev/null
+++ b/src/modules/launcher/Wox.Infrastructure/Http/Http.cs
@@ -0,0 +1,83 @@
+using System.IO;
+using System.Net;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+using Wox.Infrastructure.Logger;
+using Wox.Infrastructure.UserSettings;
+
+namespace Wox.Infrastructure.Http
+{
+ public static class Http
+ {
+ private const string UserAgent = @"Mozilla/5.0 (Trident/7.0; rv:11.0) like Gecko";
+
+ static Http()
+ {
+ // need to be added so it would work on a win10 machine
+ ServicePointManager.Expect100Continue = true;
+ ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls
+ | SecurityProtocolType.Tls11
+ | SecurityProtocolType.Tls12;
+ }
+
+ public static HttpProxy Proxy { private get; set; }
+ public static IWebProxy WebProxy()
+ {
+ if (Proxy != null && Proxy.Enabled && !string.IsNullOrEmpty(Proxy.Server))
+ {
+ if (string.IsNullOrEmpty(Proxy.UserName) || string.IsNullOrEmpty(Proxy.Password))
+ {
+ var webProxy = new WebProxy(Proxy.Server, Proxy.Port);
+ return webProxy;
+ }
+ else
+ {
+ var webProxy = new WebProxy(Proxy.Server, Proxy.Port)
+ {
+ Credentials = new NetworkCredential(Proxy.UserName, Proxy.Password)
+ };
+ return webProxy;
+ }
+ }
+ else
+ {
+ return WebRequest.GetSystemWebProxy();
+ }
+ }
+
+ public static void Download([NotNull] string url, [NotNull] string filePath)
+ {
+ var client = new WebClient { Proxy = WebProxy() };
+ client.Headers.Add("user-agent", UserAgent);
+ client.DownloadFile(url, filePath);
+ }
+
+ public static async Task Get([NotNull] string url, string encoding = "UTF-8")
+ {
+ Log.Debug($"|Http.Get|Url <{url}>");
+ var request = WebRequest.CreateHttp(url);
+ request.Method = "GET";
+ request.Timeout = 1000;
+ request.Proxy = WebProxy();
+ request.UserAgent = UserAgent;
+ var response = await request.GetResponseAsync() as HttpWebResponse;
+ response = response.NonNull();
+ var stream = response.GetResponseStream().NonNull();
+
+ using (var reader = new StreamReader(stream, Encoding.GetEncoding(encoding)))
+ {
+ var content = await reader.ReadToEndAsync();
+ if (response.StatusCode == HttpStatusCode.OK)
+ {
+ return content;
+ }
+ else
+ {
+ throw new HttpRequestException($"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>");
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Infrastructure/Image/ImageCache.cs b/src/modules/launcher/Wox.Infrastructure/Image/ImageCache.cs
new file mode 100644
index 0000000000..a47f4d255f
--- /dev/null
+++ b/src/modules/launcher/Wox.Infrastructure/Image/ImageCache.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Concurrent;
+using System.Linq;
+using Windows.UI.Xaml.Media;
+
+namespace Wox.Infrastructure.Image
+{
+ [Serializable]
+ public class ImageCache
+ {
+ private const int MaxCached = 5000;
+ public ConcurrentDictionary Usage = new ConcurrentDictionary();
+ private readonly ConcurrentDictionary _data = new ConcurrentDictionary();
+
+
+ public ImageSource this[string path]
+ {
+ get
+ {
+ Usage.AddOrUpdate(path, 1, (k, v) => v + 1);
+ var i = _data[path];
+ return i;
+ }
+ set { _data[path] = value; }
+ }
+
+ public void Cleanup()
+ {
+ var images = Usage
+ .OrderByDescending(o => o.Value)
+ .Take(MaxCached)
+ .ToDictionary(i => i.Key, i => i.Value);
+ Usage = new ConcurrentDictionary(images);
+ }
+
+ public bool ContainsKey(string key)
+ {
+ var contains = _data.ContainsKey(key);
+ return contains;
+ }
+
+ public int CacheSize()
+ {
+ return _data.Count;
+ }
+
+ ///
+ /// return the number of unique images in the cache (by reference not by checking images content)
+ ///
+ public int UniqueImagesInCache()
+ {
+ return _data.Values.Distinct().Count();
+ }
+ }
+
+}
diff --git a/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs b/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs
new file mode 100644
index 0000000000..4c148065eb
--- /dev/null
+++ b/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs
@@ -0,0 +1,183 @@
+using System;
+using System.Collections.Concurrent;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Wox.Infrastructure.Logger;
+using Wox.Infrastructure.Storage;
+using Windows.UI.Xaml.Media.Imaging;
+using Windows.UI.Xaml.Media;
+
+namespace Wox.Infrastructure.Image
+{
+ public static class ImageLoader
+ {
+ private static readonly ImageCache ImageCache = new ImageCache();
+ private static BinaryStorage> _storage;
+
+ private static readonly string[] ImageExtensions =
+ {
+ ".png",
+ ".jpg",
+ ".jpeg",
+ ".gif",
+ ".bmp",
+ ".tiff",
+ ".ico"
+ };
+
+
+ public static void Initialize()
+ {
+ _storage = new BinaryStorage>("Image");
+ ImageCache.Usage = _storage.TryLoad(new ConcurrentDictionary());
+
+ foreach (var icon in new[] { Constant.DefaultIcon, Constant.ErrorIcon })
+ {
+ ImageSource img = new BitmapImage(new Uri(icon));
+ ImageCache[icon] = img;
+ }
+ Task.Run(() =>
+ {
+ Stopwatch.Normal("|ImageLoader.Initialize|Preload images cost", () =>
+ {
+ ImageCache.Usage.AsParallel().ForAll(x =>
+ {
+ Load(x.Key);
+ });
+ });
+ Log.Info($"|ImageLoader.Initialize|Number of preload images is <{ImageCache.Usage.Count}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}");
+ });
+ }
+
+ public static void Save()
+ {
+ ImageCache.Cleanup();
+ _storage.Save(ImageCache.Usage);
+ }
+
+ private class ImageResult
+ {
+ public ImageResult(ImageSource imageSource, ImageType imageType)
+ {
+ ImageSource = imageSource;
+ ImageType = imageType;
+ }
+
+ public ImageType ImageType { get; }
+ public ImageSource ImageSource { get; }
+ }
+
+ private enum ImageType
+ {
+ File,
+ Folder,
+ Data,
+ ImageFile,
+ Error,
+ Cache
+ }
+
+ private static ImageResult LoadInternal(string path, bool loadFullImage = false)
+ {
+ ImageSource image;
+ ImageType type = ImageType.Error;
+ try
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ return new ImageResult(ImageCache[Constant.ErrorIcon], ImageType.Error);
+ }
+ if (ImageCache.ContainsKey(path))
+ {
+ return new ImageResult(ImageCache[path], ImageType.Cache);
+ }
+
+ if (path.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
+ {
+ var imageSource = new BitmapImage(new Uri(path));
+ return new ImageResult(imageSource, ImageType.Data);
+ }
+
+ if (!Path.IsPathRooted(path))
+ {
+ path = Path.Combine(Constant.ProgramDirectory, "Images", Path.GetFileName(path));
+ }
+
+ if (Directory.Exists(path))
+ {
+ /* Directories can also have thumbnails instead of shell icons.
+ * Generating thumbnails for a bunch of folders while scrolling through
+ * results from Everything makes a big impact on performance and
+ * Wox responsibility.
+ * - Solution: just load the icon
+ */
+ type = ImageType.Folder;
+ image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize,
+ Constant.ThumbnailSize, ThumbnailOptions.IconOnly);
+
+ }
+ else if (File.Exists(path))
+ {
+ var extension = Path.GetExtension(path).ToLower();
+ if (ImageExtensions.Contains(extension))
+ {
+ type = ImageType.ImageFile;
+ if (loadFullImage)
+ {
+ image = LoadFullImage(path);
+ }
+ else
+ {
+ /* Although the documentation for GetImage on MSDN indicates that
+ * if a thumbnail is available it will return one, this has proved to not
+ * be the case in many situations while testing.
+ * - Solution: explicitly pass the ThumbnailOnly flag
+ */
+ image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize,
+ Constant.ThumbnailSize, ThumbnailOptions.ThumbnailOnly);
+ }
+ }
+ else
+ {
+ type = ImageType.File;
+ image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize,
+ Constant.ThumbnailSize, ThumbnailOptions.None);
+ }
+ }
+ else
+ {
+ image = ImageCache[Constant.ErrorIcon];
+ path = Constant.ErrorIcon;
+ }
+ }
+ catch (System.Exception e)
+ {
+ Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path}", e);
+ type = ImageType.Error;
+ image = ImageCache[Constant.ErrorIcon];
+ ImageCache[path] = image;
+ }
+ return new ImageResult(image, type);
+ }
+
+ public static ImageSource Load(string path, bool loadFullImage = false)
+ {
+ var imageResult = LoadInternal(path, loadFullImage);
+
+ var img = imageResult.ImageSource;
+ if (imageResult.ImageType != ImageType.Error && imageResult.ImageType != ImageType.Cache)
+ {
+ // update cache
+ ImageCache[path] = img;
+ }
+ return img;
+ }
+
+ private static BitmapImage LoadFullImage(string path)
+ {
+ BitmapImage image = new BitmapImage(new Uri(path));
+ return image;
+ }
+ }
+}
diff --git a/src/modules/launcher/Wox.Infrastructure/Image/ThumbnailReader.cs b/src/modules/launcher/Wox.Infrastructure/Image/ThumbnailReader.cs
new file mode 100644
index 0000000000..88d6d464c4
--- /dev/null
+++ b/src/modules/launcher/Wox.Infrastructure/Image/ThumbnailReader.cs
@@ -0,0 +1,178 @@
+using System;
+using System.Runtime.InteropServices;
+using System.IO;
+using System.Windows.Interop;
+using System.Windows.Media.Imaging;
+using System.Windows;
+using Windows.Storage.Streams;
+using BitmapSourceWPF = System.Windows.Media.Imaging.BitmapSource;
+using BitmapImageUWP = Windows.UI.Xaml.Media.Imaging.BitmapImage;
+
+namespace Wox.Infrastructure.Image
+{
+ [Flags]
+ public enum ThumbnailOptions
+ {
+ None = 0x00,
+ BiggerSizeOk = 0x01,
+ InMemoryOnly = 0x02,
+ IconOnly = 0x04,
+ ThumbnailOnly = 0x08,
+ InCacheOnly = 0x10,
+ }
+
+ public class WindowsThumbnailProvider
+ {
+ // Based on https://stackoverflow.com/questions/21751747/extract-thumbnail-for-any-file-in-windows
+
+ private const string IShellItem2Guid = "7E9FB0D3-919F-4307-AB2E-9B1860310C93";
+
+ [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ internal static extern int SHCreateItemFromParsingName(
+ [MarshalAs(UnmanagedType.LPWStr)] string path,
+ IntPtr pbc,
+ ref Guid riid,
+ [MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem);
+
+ [DllImport("gdi32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal static extern bool DeleteObject(IntPtr hObject);
+
+ [ComImport]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ [Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
+ internal interface IShellItem
+ {
+ void BindToHandler(IntPtr pbc,
+ [MarshalAs(UnmanagedType.LPStruct)]Guid bhid,
+ [MarshalAs(UnmanagedType.LPStruct)]Guid riid,
+ out IntPtr ppv);
+
+ void GetParent(out IShellItem ppsi);
+ void GetDisplayName(SIGDN sigdnName, out IntPtr ppszName);
+ void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
+ void Compare(IShellItem psi, uint hint, out int piOrder);
+ };
+
+ internal enum SIGDN : uint
+ {
+ NORMALDISPLAY = 0,
+ PARENTRELATIVEPARSING = 0x80018001,
+ PARENTRELATIVEFORADDRESSBAR = 0x8001c001,
+ DESKTOPABSOLUTEPARSING = 0x80028000,
+ PARENTRELATIVEEDITING = 0x80031001,
+ DESKTOPABSOLUTEEDITING = 0x8004c000,
+ FILESYSPATH = 0x80058000,
+ URL = 0x80068000
+ }
+
+ internal enum HResult
+ {
+ Ok = 0x0000,
+ False = 0x0001,
+ InvalidArguments = unchecked((int)0x80070057),
+ OutOfMemory = unchecked((int)0x8007000E),
+ NoInterface = unchecked((int)0x80004002),
+ Fail = unchecked((int)0x80004005),
+ ExtractionFailed = unchecked((int)0x8004B200),
+ ElementNotFound = unchecked((int)0x80070490),
+ TypeElementNotFound = unchecked((int)0x8002802B),
+ NoObject = unchecked((int)0x800401E5),
+ Win32ErrorCanceled = 1223,
+ Canceled = unchecked((int)0x800704C7),
+ ResourceInUse = unchecked((int)0x800700AA),
+ AccessDenied = unchecked((int)0x80030005)
+ }
+
+ [ComImportAttribute()]
+ [GuidAttribute("bcc18b79-ba16-442f-80c4-8a59c30c463b")]
+ [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
+ internal interface IShellItemImageFactory
+ {
+ [PreserveSig]
+ HResult GetImage(
+ [In, MarshalAs(UnmanagedType.Struct)] NativeSize size,
+ [In] ThumbnailOptions flags,
+ [Out] out IntPtr phbm);
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct NativeSize
+ {
+ private int width;
+ private int height;
+
+ public int Width { set { width = value; } }
+ public int Height { set { height = value; } }
+ };
+
+ public static BitmapImageUWP ByteToImage(byte[] imageData)
+ {
+ using (InMemoryRandomAccessStream ms = new InMemoryRandomAccessStream())
+ {
+ using (DataWriter writer = new DataWriter(ms.GetOutputStreamAt(0)))
+ {
+ writer.WriteBytes(imageData);
+ writer.StoreAsync().GetResults();
+ }
+ BitmapImageUWP image = new BitmapImageUWP();
+ image.SetSource(ms);
+ return image;
+ }
+ }
+
+ public static BitmapImageUWP GetThumbnail(string fileName, int width, int height, ThumbnailOptions options)
+ {
+ IntPtr hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options);
+ try
+ {
+ byte[] data;
+ BitmapSourceWPF bitmapSourceWPF = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
+ PngBitmapEncoder encoder = new PngBitmapEncoder();
+ encoder.Frames.Add(BitmapFrame.Create(bitmapSourceWPF));
+ using (MemoryStream ms = new MemoryStream())
+ {
+ encoder.Save(ms);
+ data = ms.ToArray();
+ }
+ return ByteToImage(data);
+ }
+ finally
+ {
+ // delete HBitmap to avoid memory leaks
+ DeleteObject(hBitmap);
+ }
+ }
+
+ private static IntPtr GetHBitmap(string fileName, int width, int height, ThumbnailOptions options)
+ {
+ IShellItem nativeShellItem;
+ Guid shellItem2Guid = new Guid(IShellItem2Guid);
+ int retCode = SHCreateItemFromParsingName(fileName, IntPtr.Zero, ref shellItem2Guid, out nativeShellItem);
+
+ if (retCode != 0)
+ throw Marshal.GetExceptionForHR(retCode);
+
+ NativeSize nativeSize = new NativeSize
+ {
+ Width = width,
+ Height = height
+ };
+
+ IntPtr hBitmap;
+ HResult hr = ((IShellItemImageFactory)nativeShellItem).GetImage(nativeSize, options, out hBitmap);
+
+ // if extracting image thumbnail and failed, extract shell icon
+ if (options == ThumbnailOptions.ThumbnailOnly && hr == HResult.ExtractionFailed)
+ {
+ hr = ((IShellItemImageFactory) nativeShellItem).GetImage(nativeSize, ThumbnailOptions.IconOnly, out hBitmap);
+ }
+
+ Marshal.ReleaseComObject(nativeShellItem);
+
+ if (hr == HResult.Ok) return hBitmap;
+
+ throw new COMException($"Error while extracting thumbnail for {fileName}", Marshal.GetExceptionForHR((int)hr));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Infrastructure/Logger/Log.cs b/src/modules/launcher/Wox.Infrastructure/Logger/Log.cs
new file mode 100644
index 0000000000..611265baa4
--- /dev/null
+++ b/src/modules/launcher/Wox.Infrastructure/Logger/Log.cs
@@ -0,0 +1,196 @@
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.CompilerServices;
+using NLog;
+using NLog.Config;
+using NLog.Targets;
+
+namespace Wox.Infrastructure.Logger
+{
+ public static class Log
+ {
+ public const string DirectoryName = "Logs";
+
+ public static string CurrentLogDirectory { get; }
+
+ static Log()
+ {
+ CurrentLogDirectory = Path.Combine(Constant.DataDirectory, DirectoryName, Constant.Version);
+ if (!Directory.Exists(CurrentLogDirectory))
+ {
+ Directory.CreateDirectory(CurrentLogDirectory);
+ }
+
+ var configuration = new LoggingConfiguration();
+ var target = new FileTarget();
+ configuration.AddTarget("file", target);
+ target.FileName = CurrentLogDirectory.Replace(@"\", "/") + "/${shortdate}.txt";
+#if DEBUG
+ var rule = new LoggingRule("*", LogLevel.Debug, target);
+#else
+ var rule = new LoggingRule("*", LogLevel.Info, target);
+#endif
+ configuration.LoggingRules.Add(rule);
+ LogManager.Configuration = configuration;
+ }
+
+ private static void LogFaultyFormat(string message)
+ {
+ var logger = LogManager.GetLogger("FaultyLogger");
+ message = $"Wrong logger message format <{message}>";
+ System.Diagnostics.Debug.WriteLine($"FATAL|{message}");
+ logger.Fatal(message);
+ }
+
+ private static bool FormatValid(string message)
+ {
+ var parts = message.Split('|');
+ var valid = parts.Length == 3 && !string.IsNullOrWhiteSpace(parts[1]) && !string.IsNullOrWhiteSpace(parts[2]);
+ return valid;
+ }
+
+
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public static void Exception(string className, string message, System.Exception exception, [CallerMemberName] string methodName = "")
+ {
+ var classNameWithMethod = CheckClassAndMessageAndReturnFullClassWithMethod(className, message, methodName);
+
+ ExceptionInternal(classNameWithMethod, message, exception);
+ }
+
+ private static string CheckClassAndMessageAndReturnFullClassWithMethod(string className, string message,
+ string methodName)
+ {
+ if (string.IsNullOrWhiteSpace(className))
+ {
+ LogFaultyFormat($"Fail to specify a class name during logging of message: {message ?? "no message entered"}");
+ }
+
+ if (string.IsNullOrWhiteSpace(message))
+ {
+ // todo: not sure we really need that
+ LogFaultyFormat($"Fail to specify a message during logging");
+ }
+
+ if (!string.IsNullOrWhiteSpace(methodName))
+ {
+ return className + "." + methodName;
+ }
+
+ return className;
+ }
+
+ private static void ExceptionInternal(string classAndMethod, string message, System.Exception e)
+ {
+ var logger = LogManager.GetLogger(classAndMethod);
+
+ System.Diagnostics.Debug.WriteLine($"ERROR|{message}");
+
+ logger.Error("-------------------------- Begin exception --------------------------");
+ logger.Error(message);
+
+ do
+ {
+ logger.Error($"Exception full name:\n <{e.GetType().FullName}>");
+ logger.Error($"Exception message:\n <{e.Message}>");
+ logger.Error($"Exception stack trace:\n <{e.StackTrace}>");
+ logger.Error($"Exception source:\n <{e.Source}>");
+ logger.Error($"Exception target site:\n <{e.TargetSite}>");
+ logger.Error($"Exception HResult:\n <{e.HResult}>");
+ e = e.InnerException;
+ } while (e != null);
+
+ logger.Error("-------------------------- End exception --------------------------");
+ }
+
+ private static void LogInternal(string message, LogLevel level)
+ {
+ if (FormatValid(message))
+ {
+ var parts = message.Split('|');
+ var prefix = parts[1];
+ var unprefixed = parts[2];
+ var logger = LogManager.GetLogger(prefix);
+
+ System.Diagnostics.Debug.WriteLine($"{level.Name}|{message}");
+ logger.Log(level, unprefixed);
+ }
+ else
+ {
+ LogFaultyFormat(message);
+ }
+ }
+
+ /// example: "|prefix|unprefixed"
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public static void Exception(string message, System.Exception e)
+ {
+ if (FormatValid(message))
+ {
+ var parts = message.Split('|');
+ var prefix = parts[1];
+ var unprefixed = parts[2];
+ ExceptionInternal(prefix, unprefixed, e);
+ }
+ else
+ {
+ LogFaultyFormat(message);
+ }
+ }
+
+ /// example: "|prefix|unprefixed"
+ public static void Error(string message)
+ {
+ LogInternal(message, LogLevel.Error);
+ }
+
+ public static void Error(string className, string message, [CallerMemberName] string methodName = "")
+ {
+ LogInternal(LogLevel.Error, className, message, methodName);
+ }
+
+ private static void LogInternal(LogLevel level, string className, string message, [CallerMemberName] string methodName = "")
+ {
+ var classNameWithMethod = CheckClassAndMessageAndReturnFullClassWithMethod(className, message, methodName);
+
+ var logger = LogManager.GetLogger(classNameWithMethod);
+
+ System.Diagnostics.Debug.WriteLine($"{level.Name}|{message}");
+ logger.Log(level, message);
+ }
+
+ public static void Debug(string className, string message, [CallerMemberName] string methodName = "")
+ {
+ LogInternal(LogLevel.Debug, className, message, methodName);
+ }
+
+ /// example: "|prefix|unprefixed"
+ public static void Debug(string message)
+ {
+ LogInternal(message, LogLevel.Debug);
+ }
+
+ public static void Info(string className, string message, [CallerMemberName] string methodName = "")
+ {
+ LogInternal(LogLevel.Info, className, message, methodName);
+ }
+
+ /// example: "|prefix|unprefixed"
+ public static void Info(string message)
+ {
+ LogInternal(message, LogLevel.Info);
+ }
+
+ public static void Warn(string className, string message, [CallerMemberName] string methodName = "")
+ {
+ LogInternal(LogLevel.Warn, className, message, methodName);
+ }
+
+ /// example: "|prefix|unprefixed"
+ public static void Warn(string message)
+ {
+ LogInternal(message, LogLevel.Warn);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Infrastructure/Properties/AssemblyInfo.cs b/src/modules/launcher/Wox.Infrastructure/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..92248ee964
--- /dev/null
+++ b/src/modules/launcher/Wox.Infrastructure/Properties/AssemblyInfo.cs
@@ -0,0 +1,6 @@
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("Wox")]
+[assembly: InternalsVisibleTo("PowerLauncher")]
+[assembly: InternalsVisibleTo("Wox.Core")]
+[assembly: InternalsVisibleTo("Wox.Test")]
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Infrastructure/Stopwatch.cs b/src/modules/launcher/Wox.Infrastructure/Stopwatch.cs
new file mode 100644
index 0000000000..6315654552
--- /dev/null
+++ b/src/modules/launcher/Wox.Infrastructure/Stopwatch.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using Wox.Infrastructure.Logger;
+
+namespace Wox.Infrastructure
+{
+ public static class Stopwatch
+ {
+ private static readonly Dictionary Count = new Dictionary