From 82d30c6e745925b4f5eb98bedc34a3493dbc7627 Mon Sep 17 00:00:00 2001 From: qianlifeng Date: Sat, 7 Feb 2015 16:53:33 +0800 Subject: [PATCH] Allow searches in context menu --- .../ContextMenuStorage.cs | 24 +- .../Wox.Plugin.Everything/Languages/en.xaml | 11 + .../Languages/zh-cn.xaml | 11 + .../Languages/zh-tw.xaml | 11 + Plugins/Wox.Plugin.Everything/Main.cs | 59 +- .../Wox.Plugin.Everything.csproj | 267 +++--- Plugins/Wox.Plugin.Program/Languages/en.xaml | 3 + .../Wox.Plugin.Program/Languages/zh-cn.xaml | 49 +- .../Wox.Plugin.Program/Languages/zh-tw.xaml | 48 +- Plugins/Wox.Plugin.Program/Programs.cs | 8 +- Wox.Infrastructure/StringMatcher.cs | 33 + Wox.Infrastructure/Wox.Infrastructure.csproj | 1 + Wox.Plugin/IPublicAPI.cs | 8 +- Wox/Images/{topmost.png => down.png} | Bin 4304 -> 4187 bytes Wox/Images/up.png | Bin 0 -> 4132 bytes Wox/Languages/en.xaml | 2 + Wox/Languages/zh-cn.xaml | 2 + Wox/Languages/zh-tw.xaml | 3 +- Wox/MainWindow.xaml.cs | 72 +- Wox/Msg.xaml | 2 +- Wox/Msg.xaml.cs | 5 +- Wox/Wox.csproj | 5 +- Wox/ias03vpr.pm0 | 850 ++++++++++++++++++ Wox/su54u2mz.xrz | 850 ++++++++++++++++++ 24 files changed, 2103 insertions(+), 221 deletions(-) create mode 100644 Plugins/Wox.Plugin.Everything/Languages/en.xaml create mode 100644 Plugins/Wox.Plugin.Everything/Languages/zh-cn.xaml create mode 100644 Plugins/Wox.Plugin.Everything/Languages/zh-tw.xaml create mode 100644 Wox.Infrastructure/StringMatcher.cs rename Wox/Images/{topmost.png => down.png} (63%) create mode 100644 Wox/Images/up.png create mode 100644 Wox/ias03vpr.pm0 create mode 100644 Wox/su54u2mz.xrz diff --git a/Plugins/Wox.Plugin.Everything/ContextMenuStorage.cs b/Plugins/Wox.Plugin.Everything/ContextMenuStorage.cs index f778bd96ec..1970e39a7c 100644 --- a/Plugins/Wox.Plugin.Everything/ContextMenuStorage.cs +++ b/Plugins/Wox.Plugin.Everything/ContextMenuStorage.cs @@ -11,13 +11,13 @@ namespace Wox.Plugin.Everything { public class ContextMenuStorage : JsonStrorage { - [JsonProperty] - public List ContextMenus = new List(); - + [JsonProperty] public List ContextMenus = new List(); [JsonProperty] public int MaxSearchCount { get; set; } + public IPublicAPI API { get; set; } + protected override string ConfigName { get { return "EverythingContextMenu"; } @@ -28,20 +28,14 @@ namespace Wox.Plugin.Everything get { return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); } } + protected override void OnAfterLoad(ContextMenuStorage obj) + { + + } + protected override ContextMenuStorage LoadDefault() { - ContextMenus = new List() - { - new ContextMenu() - { - Name = "Open Containing Folder", - Command = "explorer.exe", - Argument = " /select,\"{path}\"", - ImagePath ="Images\\folder.png" - } - }; MaxSearchCount = 100; - Save(); return this; } } @@ -60,4 +54,4 @@ namespace Wox.Plugin.Everything [JsonProperty] public string ImagePath { get; set; } } -} +} \ No newline at end of file diff --git a/Plugins/Wox.Plugin.Everything/Languages/en.xaml b/Plugins/Wox.Plugin.Everything/Languages/en.xaml new file mode 100644 index 0000000000..18c583ae0e --- /dev/null +++ b/Plugins/Wox.Plugin.Everything/Languages/en.xaml @@ -0,0 +1,11 @@ + + + Everything is not runnin + Everything plugin has an error (enter to copy error message) + Copied + Can't start {0} + Open containing folder + + \ No newline at end of file diff --git a/Plugins/Wox.Plugin.Everything/Languages/zh-cn.xaml b/Plugins/Wox.Plugin.Everything/Languages/zh-cn.xaml new file mode 100644 index 0000000000..50d3f189c1 --- /dev/null +++ b/Plugins/Wox.Plugin.Everything/Languages/zh-cn.xaml @@ -0,0 +1,11 @@ + + + Everything没有运行,我们将自动为您启动内置Everything,请稍后进行查询 + Everything插件发生了一个错误(回车拷贝具体错误信息) + 拷贝成功 + 不能启动 {0} + 打开所属文件夹 + + \ No newline at end of file diff --git a/Plugins/Wox.Plugin.Everything/Languages/zh-tw.xaml b/Plugins/Wox.Plugin.Everything/Languages/zh-tw.xaml new file mode 100644 index 0000000000..c61e4ffec7 --- /dev/null +++ b/Plugins/Wox.Plugin.Everything/Languages/zh-tw.xaml @@ -0,0 +1,11 @@ + + + Everything沒有運行,我們將自動為您啟動內置Everything,請稍後進行查詢 + Everything插件發生了一個錯誤(回車拷貝具體錯誤信息) + 拷貝成功 + 不能啟動 {0} + 打開所屬文件夾 + + \ No newline at end of file diff --git a/Plugins/Wox.Plugin.Everything/Main.cs b/Plugins/Wox.Plugin.Everything/Main.cs index 3322f2541f..86862097c0 100644 --- a/Plugins/Wox.Plugin.Everything/Main.cs +++ b/Plugins/Wox.Plugin.Everything/Main.cs @@ -9,7 +9,7 @@ using Wox.Plugin.Everything.Everything; namespace Wox.Plugin.Everything { - public class Main : IPlugin + public class Main : IPlugin, IPluginI18n { PluginInitContext context; EverythingAPI api = new EverythingAPI(); @@ -59,7 +59,7 @@ namespace Wox.Plugin.Everything StartEverything(); results.Add(new Result() { - Title = "Everything is not running, we already run it for you now. Try search again", + Title = context.API.GetTranslation("wox_plugin_everything_is_not_running"), IcoPath = "Images\\warning.png" }); } @@ -67,12 +67,12 @@ namespace Wox.Plugin.Everything { results.Add(new Result() { - Title = "Everything plugin has an error (enter to copy error message)", + Title = context.API.GetTranslation("wox_plugin_everything_query_error"), SubTitle = e.Message, Action = _ => { System.Windows.Clipboard.SetText(e.Message + "\r\n" + e.StackTrace); - context.API.ShowMsg("Copied", "Error message has copied to your clipboard", string.Empty); + context.API.ShowMsg(context.API.GetTranslation("wox_plugin_everything_copied"), null, string.Empty); return false; }, IcoPath = "Images\\error.png" @@ -114,9 +114,13 @@ namespace Wox.Plugin.Everything { List contextMenus = new List(); + List availableContextMenus = new List(); + availableContextMenus.AddRange(GetDefaultContextMenu()); + availableContextMenus.AddRange(ContextMenuStorage.Instance.ContextMenus); + if (record.Type == ResultType.File) { - foreach (ContextMenu contextMenu in ContextMenuStorage.Instance.ContextMenus) + foreach (ContextMenu contextMenu in availableContextMenus) { contextMenus.Add(new Result() { @@ -126,11 +130,11 @@ namespace Wox.Plugin.Everything string argument = contextMenu.Argument.Replace("{path}", record.FullPath); try { - System.Diagnostics.Process.Start(contextMenu.Command, argument); + Process.Start(contextMenu.Command, argument); } catch { - context.API.ShowMsg("Can't start " + record.FullPath, string.Empty, string.Empty); + context.API.ShowMsg(string.Format(context.API.GetTranslation("wox_plugin_everything_canot_start"), record.FullPath), string.Empty, string.Empty); return false; } return true; @@ -143,9 +147,25 @@ namespace Wox.Plugin.Everything return contextMenus; } + private List GetDefaultContextMenu() + { + List defaultContextMenus = new List(); + ContextMenu openFolderContextMenu = new ContextMenu() + { + Name = context.API.GetTranslation("wox_plugin_everything_open_containing_folder"), + Command = "explorer.exe", + Argument = " /select,\"{path}\"", + ImagePath = "Images\\folder.png" + }; + + defaultContextMenus.Add(openFolderContextMenu); + return defaultContextMenus; + } + public void Init(PluginInitContext context) { this.context = context; + ContextMenuStorage.Instance.API = context.API; LoadLibrary(Path.Combine( Path.Combine(context.CurrentPluginMetadata.PluginDirectory, (IntPtr.Size == 4) ? "x86" : "x64"), @@ -159,12 +179,20 @@ namespace Wox.Plugin.Everything { if (!CheckEverythingIsRunning()) { - Process p = new Process(); - p.StartInfo.Verb = "runas"; - p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; - p.StartInfo.FileName = GetEverythingPath(); - p.StartInfo.UseShellExecute = true; - p.Start(); + try + { + Process p = new Process(); + p.StartInfo.Verb = "runas"; + p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; + p.StartInfo.FileName = GetEverythingPath(); + p.StartInfo.UseShellExecute = true; + p.Start(); + } + catch (Exception e) + { + context.API.ShowMsg("Start Everything failed"); + } + } } @@ -178,5 +206,10 @@ namespace Wox.Plugin.Everything string everythingFolder = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "PortableEverything"); return Path.Combine(everythingFolder, "Everything.exe"); } + + public string GetLanguagesFolder() + { + return Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Languages"); + } } } diff --git a/Plugins/Wox.Plugin.Everything/Wox.Plugin.Everything.csproj b/Plugins/Wox.Plugin.Everything/Wox.Plugin.Everything.csproj index e25b8e2256..6eef11c31b 100644 --- a/Plugins/Wox.Plugin.Everything/Wox.Plugin.Everything.csproj +++ b/Plugins/Wox.Plugin.Everything/Wox.Plugin.Everything.csproj @@ -1,130 +1,151 @@ - - - - - Debug - AnyCPU - {230AE83F-E92E-4E69-8355-426B305DA9C0} - Library - Properties - Wox.Plugin.Everything - Wox.Plugin.Everything - v3.5 - 512 - ..\Wox\ - true - - - true - full - false - ..\..\Output\Debug\Plugins\Wox.Plugin.Everything\ - DEBUG;TRACE - prompt - 4 - AnyCPU - - - pdbonly - true - ..\..\Output\Release\Plugins\Wox.Plugin.Everything\ - TRACE - prompt - 4 - - - - - - - False - ..\..\packages\Newtonsoft.Json.6.0.8\lib\net35\Newtonsoft.Json.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - PreserveNewest - - - - - {4fd29318-a8ab-4d8f-aa47-60bc241b8da3} - Wox.Infrastructure - - - {8451ecdd-2ea4-4966-bb0a-7bbc40138e80} - Wox.Plugin - - - - - - - - - - - 这台计算机上缺少此项目引用的 NuGet 程序包。启用“NuGet 程序包还原”可下载这些程序包。有关详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 - - - + + + + + Debug + AnyCPU + {230AE83F-E92E-4E69-8355-426B305DA9C0} + Library + Properties + Wox.Plugin.Everything + Wox.Plugin.Everything + v3.5 + 512 + ..\Wox\ + true + + + true + full + false + ..\..\Output\Debug\Plugins\Wox.Plugin.Everything\ + DEBUG;TRACE + prompt + 4 + AnyCPU + + + pdbonly + true + ..\..\Output\Release\Plugins\Wox.Plugin.Everything\ + TRACE + prompt + 4 + + + + + + + False + ..\..\packages\Newtonsoft.Json.6.0.8\lib\net35\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + PreserveNewest + + + + + {4fd29318-a8ab-4d8f-aa47-60bc241b8da3} + Wox.Infrastructure + + + {8451ecdd-2ea4-4966-bb0a-7bbc40138e80} + Wox.Plugin + + + + + MSBuild:Compile + Designer + PreserveNewest + + + + + MSBuild:Compile + Designer + PreserveNewest + + + + + MSBuild:Compile + Designer + PreserveNewest + + + + + + + + + + + 这台计算机上缺少此项目引用的 NuGet 程序包。启用“NuGet 程序包还原”可下载这些程序包。有关详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 + + + + --> \ No newline at end of file diff --git a/Plugins/Wox.Plugin.Program/Languages/en.xaml b/Plugins/Wox.Plugin.Program/Languages/en.xaml index 3e9a4f1b62..c0b194f017 100644 --- a/Plugins/Wox.Plugin.Program/Languages/en.xaml +++ b/Plugins/Wox.Plugin.Program/Languages/en.xaml @@ -19,5 +19,8 @@ (Each suffix should split by ;) Sucessfully update file suffixes File suffixes can't be empty + + Run As Administrator + Open containing folder \ No newline at end of file diff --git a/Plugins/Wox.Plugin.Program/Languages/zh-cn.xaml b/Plugins/Wox.Plugin.Program/Languages/zh-cn.xaml index 3887344677..288e3ba13a 100644 --- a/Plugins/Wox.Plugin.Program/Languages/zh-cn.xaml +++ b/Plugins/Wox.Plugin.Program/Languages/zh-cn.xaml @@ -1,24 +1,27 @@ - - - - 删除 - 编辑 - 增加 - 位置 - 索引文件后缀 - 重新索引 - 索引中 - - - 请先选择一项 - 你确定要删除{0}吗? - - 更新 - Wox仅索引下列后缀的文件: - (每个后缀以英文状态下的分号分隔) - 成功更新索引文件后缀 - 文件后缀不能为空 - + + + + 删除 + 编辑 + 增加 + 位置 + 索引文件后缀 + 重新索引 + 索引中 + + + 请先选择一项 + 你确定要删除{0}吗? + + 更新 + Wox仅索引下列后缀的文件: + (每个后缀以英文状态下的分号分隔) + 成功更新索引文件后缀 + 文件后缀不能为空 + + 以管理员身份运行 + 打开所属文件夹 + \ No newline at end of file diff --git a/Plugins/Wox.Plugin.Program/Languages/zh-tw.xaml b/Plugins/Wox.Plugin.Program/Languages/zh-tw.xaml index d56fc2d4cd..36b4cb42fc 100644 --- a/Plugins/Wox.Plugin.Program/Languages/zh-tw.xaml +++ b/Plugins/Wox.Plugin.Program/Languages/zh-tw.xaml @@ -1,24 +1,26 @@ - - - - 刪除 - 編輯 - 增加 - 位置 - 索引文件後綴 - 重新索引 - 索引中 - - - 請先選擇一項 - 你確定要刪除{0}嗎? - - 更新 - Wox僅索引下列後綴的文件: - (每個後綴以英文狀態下的分號分隔) - 成功更新索引文件後綴 - 文件後綴不能為空 - + + + + 刪除 + 編輯 + 增加 + 位置 + 索引文件後綴 + 重新索引 + 索引中 + + + 請先選擇一項 + 你確定要刪除{0}嗎? + + 更新 + Wox僅索引下列後綴的文件: + (每個後綴以英文狀態下的分號分隔) + 成功更新索引文件後綴 + 文件後綴不能為空 + + 以管理員身份運行 + 打開所屬文件夾 \ No newline at end of file diff --git a/Plugins/Wox.Plugin.Program/Programs.cs b/Plugins/Wox.Plugin.Program/Programs.cs index 4d888bd5b6..63280d12a2 100644 --- a/Plugins/Wox.Plugin.Program/Programs.cs +++ b/Plugins/Wox.Plugin.Program/Programs.cs @@ -48,7 +48,7 @@ namespace Wox.Plugin.Program { new Result() { - Title = "Run As Administrator", + Title = context.API.GetTranslation("wox_plugin_program_run_as_administrator"), Action = _ => { context.API.HideApp(); @@ -59,7 +59,7 @@ namespace Wox.Plugin.Program }, new Result() { - Title = "Open Containing Folder", + Title = context.API.GetTranslation("wox_plugin_program_open_containing_folder"), Action = _ => { context.API.HideApp(); @@ -85,8 +85,8 @@ namespace Wox.Plugin.Program static string ResolveShortcut(string filePath) { // IWshRuntimeLibrary is in the COM library "Windows Script Host Object Model" - IWshRuntimeLibrary.WshShell shell = new IWshRuntimeLibrary.WshShell(); - IWshRuntimeLibrary.IWshShortcut shortcut = (IWshRuntimeLibrary.IWshShortcut)shell.CreateShortcut(filePath); + WshShell shell = new WshShell(); + IWshShortcut shortcut = (IWshShortcut)shell.CreateShortcut(filePath); return shortcut.TargetPath; } diff --git a/Wox.Infrastructure/StringMatcher.cs b/Wox.Infrastructure/StringMatcher.cs new file mode 100644 index 0000000000..4d3d9dd5ae --- /dev/null +++ b/Wox.Infrastructure/StringMatcher.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Wox.Infrastructure +{ + public class StringMatcher + { + /// + /// Check if a candidate is match with the source + /// + /// + /// + /// Match score + public static int Match(string source, string candidate) + { + if (string.IsNullOrEmpty(source) || string.IsNullOrEmpty(candidate)) return 0; + + FuzzyMatcher matcher = FuzzyMatcher.Create(candidate); + int score = matcher.Evaluate(source).Score; + if (score > 0) return score; + + score = matcher.Evaluate(source.Unidecode()).Score; + return score; + } + + public static bool IsMatch(string source, string candidate) + { + return Match(source, candidate) > 0; + } + } +} diff --git a/Wox.Infrastructure/Wox.Infrastructure.csproj b/Wox.Infrastructure/Wox.Infrastructure.csproj index 75a45afeea..80a96bc322 100644 --- a/Wox.Infrastructure/Wox.Infrastructure.csproj +++ b/Wox.Infrastructure/Wox.Infrastructure.csproj @@ -66,6 +66,7 @@ + diff --git a/Wox.Plugin/IPublicAPI.cs b/Wox.Plugin/IPublicAPI.cs index b564480db8..3b2691177e 100644 --- a/Wox.Plugin/IPublicAPI.cs +++ b/Wox.Plugin/IPublicAPI.cs @@ -42,6 +42,12 @@ namespace Wox.Plugin /// void ChangeQuery(string query, bool requery = false); + /// + /// Just change the query text, this won't raise search + /// + /// + void ChangeQueryText(string query); + /// /// Close Wox /// @@ -63,7 +69,7 @@ namespace Wox.Plugin /// Message title /// Message subtitle /// Message icon path (relative path to your plugin folder) - void ShowMsg(string title, string subTitle, string iconPath); + void ShowMsg(string title, string subTitle = "", string iconPath = ""); /// /// Open setting dialog diff --git a/Wox/Images/topmost.png b/Wox/Images/down.png similarity index 63% rename from Wox/Images/topmost.png rename to Wox/Images/down.png index 50b68e6e2076c2b9f85fa562ff5340b9901c34a5..b993805091e42d10c759965cd952da744a6c4992 100644 GIT binary patch delta 1454 zcmV;f1yTCYA=@CZzzTncNkl=0 z)45LWbUKrnWO6^w*?akL&b^b?7-q&M8#e2l!`W;7_kaD@+FO6jjMa`^`;Hs}_N?O9 zUjR?7#;2?nz^;8q$_3{x|LDv2veZogL4aajk)AVzyc*B^?)YMTY;@><`1!zlj@1vR z0`-ea96d8lzEr}wi0WV-MKA*u;j_iHxLg|m04{Yrs)DMbPEZt8QUHL6+*|;nf{NfW zn^Qpqf`Ci&aC3hFs0gYc!ip6^ERC%p#r6A01 zav5e6fCwQN7QjmyINS;by52yQ5CRsAVCnA7z?+eTwNZcYUx(4IeMjyGo(Db!)Oz1< z%Q?Pq=T;U9Mdp?}sB?fYT#nLCnB`|{-qCEn&+G3rKOE}dph53u_^BmAMk%ZtN_TS50(MR0EILqo)p}G!b95z zIX*SZv2!!8)W=4DxT-3skBu$@j~t)6_+C{K1~m{ug7^L!03n?g2;Np$zzE(@V9(Y; z&d*-r(8Prkz|(6g!|}bl&jXK-Pd2)n29zj?@o_@%A=`PndmoI1jB;;%ao)F~MBM4{ zo0ET&4dBuG*l26bePDd=?xTo2vv9e^w(AVQV4J&RUT_oM`|&T^}1g z@lkZbZ@w`z|0gxiNHIdXEqoFaeE5I>2XlXp2S+L#I@92d>A7d?W23KpB&~RS?`{t~ zeyZ`_=~Cp_T#T@G3!fxuHJCA=!k2bbIX2bg`1ytmzeTjdq++9tCyuLEKcc$XXfgdFhj_Cd}z=Q;e=^aoX`%i^5V?3;S;HDgFbM9fS8H8Z{IJ1;*xuzCB>@4NGMw`2G=wiR={SbuZw z@>{1KoZR<=Nx;%2W+GywUE0$AWFdcq5SEvh4brQ;mdj-)B6%~5%xnM{FtdV)L}pe9 zAr!s$CGUO3dtbhIbpKrS(|2Fluw~@_&y5W6mg*oqi+063UIn%Qd z5SdvHa3Z4le4av~V6I#)iyUd)VaikXqi z<*s)RHW49?`CTZ6xcy?@b@aO%7 z9@KPi&J4^9Bl|T_GtwkQ%`h``_nqB)ZM_4~Xf$ay>MsJn`S0((yFWn79g;hH=2U-o zZy(KO3i2Uit?yL=Lta_z- z4NMg!15iur)dqj68k#AoN~U=mMD|_1Ry*1S8QqoZ|GN22q-E{4Suk*61Jel7pyACw zZ(RX z{nK|SUH=Nc){Ap#D-a%VLkECQ@@TV*iVm_A%&{4afPm8kNt$%!x?XyGasH{zy0TMi zA|f~us`Z4@PRi-IlV1&^=p`4GTfu6M&HsFXrN4i^KxO?j8Yo^mEg|ixIEWg6p%Q=y z#WPnJoc%MkdWduKgC`&V)CF=qtZh~aoSJ_m&@_jpU64f343X5~EWxS&_G6Epz7Td7SkHt%QgQV{~5T1dTEB8wdq3up7}lfwGu;ViE4kUr>3U*e?2ojU;Lm{!;E7A0I0%7xyi`r z@R)Dhxl}3dd^sOCK3Knhj-7YU9^!&r6N&2(?3@^zh+bV-n4VqRPEfT&O~0QNyeXBs zXJm46KF)W)nwaagK#$0T7NY6~qBwW?(X(?;hzse7V6YG|)Ekq}g&fwu7?6uXP85G~ z^yMSsKnTN-(eaTlN$g%xANC%a2~X5l=%V^qVp{&&wHv=`l~T;7K0f)0v9FI!O+?jt zdMH5{IyTo!X{ogFTj$)4nK=G&b%qbpCme06j{+WW6KcqCA!5H(9-Y37>yd*?jj8 zATCqldk3ZBI-1n?gacGpZm@FgBBuT!`h#Ks7qPSa7RztFj8Ce2uGFN4CiUa>1G{Y$ zV2$^d#p&BY?J$MAn}YL+Yx6!XnE1CVSTm;Lt{=aDYxSXs>#oCt9cWGWaQ zBML)QmAy@3g5e5?+cmv>l%7!A{H2){aDl6=EWQ8B(%N!2=AjkoLfa`xvR=!?f!CU- z-)__PKo@j$L=T)=q%9sqf;crZC)xA8;wT-Zz(7nu)hA(=mi&W3*dZMA|NeCRzX1Sb W?J+Q8F^5h70000KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000F~Nkld18ciqC9rT^(ElNdZRgg*#NBd%*@X1 z0uKJ_C)fIqPcHD%v9Ae*0>8fXx)I>zdZRhAZUAOx=f;5-@8309T8_KC+v=jlB5!=N z%;->+2X304&X_1&b^tT8bA!Okzt}l3K3ppB&lAhgw}B`skQf|3vBb`?8hdw6>;Vp5 zRsb`za~?Q!_xAA{uCA4M^{Y14S0aic#DRCvQRDE*C2qTVj61H`vM;TW%g4Z;LwA+r;4W%5&T-P7Y4h|&cEs5hE}zzh4YpBO2X`+4JJ2Xh{9m>H^IYN#5f z5ERT(Q&Qgz#^1kO8` zgT*+|jssm)MACVYsNozdLHnFgiyQ;q5oK~XVE#ZN08on@$B%!_-#$J5-E)rtM}g9BsY!@SQ{JvJZHH5E2v!npT8bmJbS% z^mbm7kv>4vnZODIX){`P2J((aJ`eI*G9mE) z$N>z=00c8!mVkLb=xHfE*ZRHM&dve8Z)ODTU4pe5k!;$uNvhSV>+kRPwOY-0=0Ec( zfNU_WvXJcsn$KjQR;&4Px$LXes;kv%PO@h$38Ye~i1*&5#f0m0Iu3B&dr6W++DG5+ zAKJPt-Go*J3uF=iCXyzlnS>Dfz4s;}7DCX0fdK>by+`+}s_N3xlH~wgrBZPs5~*qg zL?YrddBn_|sz$93{xx8C{gUb75>B?P++b6+h|9J^FT$1O0)T`N21O(QA#YP=7Jx)V zl5)A6h)9Skl?rAi+0Df{CuWw@CE37|)_>mVoLM^k=An1rzvXSjF<`~#CL*ER z?P_0NpS4;o`)*PQz|hc;#BofqSdRegister hotkey: {0} failed Could not start {0} Invalid wox plugin file format + Set as topmost in this query + Cancel topmost in this query diff --git a/Wox/Languages/zh-cn.xaml b/Wox/Languages/zh-cn.xaml index 131e333bee..977c44799d 100644 --- a/Wox/Languages/zh-cn.xaml +++ b/Wox/Languages/zh-cn.xaml @@ -5,6 +5,8 @@ 注册热键:{0} 失败 启动命令 {0} 失败 不是合法的Wox插件格式 + 在当前查询中置顶 + 取消置顶 diff --git a/Wox/Languages/zh-tw.xaml b/Wox/Languages/zh-tw.xaml index e788226e77..79cc35b51c 100644 --- a/Wox/Languages/zh-tw.xaml +++ b/Wox/Languages/zh-tw.xaml @@ -5,7 +5,8 @@ 註冊熱鍵:{0} 失敗 啟動命令 {0} 失敗 不是合法的Wox插件格式 - + 在當前查詢中置頂 + 取消置頂 Wox設置 diff --git a/Wox/MainWindow.xaml.cs b/Wox/MainWindow.xaml.cs index 3c710ccfd5..482604f4a2 100644 --- a/Wox/MainWindow.xaml.cs +++ b/Wox/MainWindow.xaml.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; +using System.IO; using System.Linq; using System.Net; using System.Reflection; @@ -11,7 +12,6 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Forms; using System.Windows.Input; -using System.Windows.Interop; using System.Windows.Media.Animation; using NHotkey; using NHotkey.Wpf; @@ -25,18 +25,14 @@ using Wox.Infrastructure; using Wox.Infrastructure.Hotkey; using Wox.Plugin; using Wox.Storage; -using Brushes = System.Windows.Media.Brushes; -using Color = System.Windows.Media.Color; using ContextMenu = System.Windows.Forms.ContextMenu; using DataFormats = System.Windows.DataFormats; using DragEventArgs = System.Windows.DragEventArgs; +using IDataObject = System.Windows.IDataObject; using KeyEventArgs = System.Windows.Input.KeyEventArgs; using MenuItem = System.Windows.Forms.MenuItem; using MessageBox = System.Windows.MessageBox; using ToolTip = System.Windows.Controls.ToolTip; -using Wox.Infrastructure.Logger; -using IDataObject = System.Windows.IDataObject; -using System.IO; namespace Wox { @@ -52,6 +48,8 @@ namespace Wox private ToolTip toolTip = new ToolTip(); private bool ignoreTextChange = false; + private List CurrentContextMenus = new List(); + private string textBeforeEnterContextMenuMode; #endregion @@ -70,6 +68,16 @@ namespace Wox })); } + public void ChangeQueryText(string query) + { + Dispatcher.Invoke(new Action(() => + { + ignoreTextChange = true; + tbQuery.Text = query; + tbQuery.CaretIndex = tbQuery.Text.Length; + })); + } + public void CloseApp() { Dispatcher.Invoke(new Action(() => @@ -380,13 +388,43 @@ namespace Wox notifyIcon.ContextMenu = new ContextMenu(childen); } + private void QueryContextMenu() + { + pnlContextMenu.Clear(); + var query = tbQuery.Text.ToLower(); + if (string.IsNullOrEmpty(query)) + { + pnlContextMenu.AddResults(CurrentContextMenus); + } + else + { + List filterResults = new List(); + foreach (Result contextMenu in CurrentContextMenus) + { + if (StringMatcher.IsMatch(contextMenu.Title, query) + || StringMatcher.IsMatch(contextMenu.SubTitle, query)) + { + filterResults.Add(contextMenu); + } + } + pnlContextMenu.AddResults(filterResults); + } + } + private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e) { if (ignoreTextChange) { ignoreTextChange = false; return; } - lastQuery = tbQuery.Text; toolTip.IsOpen = false; pnlResult.Dirty = true; + + if (IsInContextMenuMode) + { + QueryContextMenu(); + return; + } + + lastQuery = tbQuery.Text; int searchDelay = GetSearchDelay(lastQuery); Dispatcher.DelayInvoke("UpdateSearch", @@ -464,11 +502,11 @@ namespace Wox { PluginManager.Query(q); StopProgress(); - BackToResultMode(); } private void BackToResultMode() { + ChangeQueryText(textBeforeEnterContextMenuMode); pnlResult.Visibility = Visibility.Visible; pnlContextMenu.Visibility = Visibility.Collapsed; } @@ -490,6 +528,7 @@ namespace Wox private void HideWox() { + BackToResultMode(); Hide(); } @@ -553,7 +592,7 @@ namespace Wox case Key.N: case Key.J: - if (GlobalHotkey.Instance.CheckModifiers().AltPressed) + if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) { SelectNextItem(); } @@ -561,14 +600,14 @@ namespace Wox case Key.P: case Key.K: - if (GlobalHotkey.Instance.CheckModifiers().AltPressed) + if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) { SelectPrevItem(); } break; case Key.O: - if (GlobalHotkey.Instance.CheckModifiers().AltPressed) + if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) { if (IsInContextMenuMode) { @@ -592,7 +631,7 @@ namespace Wox break; case Key.D: - if (GlobalHotkey.Instance.CheckModifiers().AltPressed) + if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) { pnlResult.SelectNextPage(); } @@ -604,7 +643,7 @@ namespace Wox break; case Key.U: - if (GlobalHotkey.Instance.CheckModifiers().AltPressed) + if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) { pnlResult.SelectPrevPage(); } @@ -774,7 +813,7 @@ namespace Wox { if (TopMostRecordStorage.Instance.IsTopMost(result)) { - result.ContextMenu.Add(new Result("Remove top most in this query", "Images\\topmost.png") + result.ContextMenu.Add(new Result(GetTranslation("cancelTopMostInThisQuery"), "Images\\down.png") { PluginDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Action = _ => @@ -787,7 +826,7 @@ namespace Wox } else { - result.ContextMenu.Add(new Result("Set as top most in this query", "Images\\topmost.png") + result.ContextMenu.Add(new Result(GetTranslation("setAsTopMostInThisQuery"), "Images\\up.png") { PluginDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Action = _ => @@ -804,8 +843,11 @@ namespace Wox { if (result.ContextMenu != null && result.ContextMenu.Count > 0) { + textBeforeEnterContextMenuMode = tbQuery.Text; + ChangeQueryText(""); pnlContextMenu.Clear(); pnlContextMenu.AddResults(result.ContextMenu); + CurrentContextMenus = result.ContextMenu; pnlContextMenu.Visibility = Visibility.Visible; pnlResult.Visibility = Visibility.Collapsed; } diff --git a/Wox/Msg.xaml b/Wox/Msg.xaml index f99c9d0bfc..aa24747048 100644 --- a/Wox/Msg.xaml +++ b/Wox/Msg.xaml @@ -27,7 +27,7 @@ - + Title diff --git a/Wox/Msg.xaml.cs b/Wox/Msg.xaml.cs index bf61c06ab1..9f9801cf30 100644 --- a/Wox/Msg.xaml.cs +++ b/Wox/Msg.xaml.cs @@ -57,8 +57,11 @@ namespace Wox { public void Show(string title, string subTitle, string icopath) { tbTitle.Text = title; tbSubTitle.Text = subTitle; + if (string.IsNullOrEmpty(subTitle)) + { + tbSubTitle.Visibility = Visibility.Collapsed; + } if (!File.Exists(icopath)) { - //icopath = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath), "Images\\app.png"); imgIco.Source = new BitmapImage(new Uri("Images\\app.png", UriKind.Relative)); } else { diff --git a/Wox/Wox.csproj b/Wox/Wox.csproj index c290bdc4a2..4badbf06b5 100644 --- a/Wox/Wox.csproj +++ b/Wox/Wox.csproj @@ -180,7 +180,10 @@ PreserveNewest - + + PreserveNewest + + PreserveNewest diff --git a/Wox/ias03vpr.pm0 b/Wox/ias03vpr.pm0 new file mode 100644 index 0000000000..2bf2868f8c --- /dev/null +++ b/Wox/ias03vpr.pm0 @@ -0,0 +1,850 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Threading; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Forms; +using System.Windows.Input; +using System.Windows.Media.Animation; +using NHotkey; +using NHotkey.Wpf; +using Wox.Core.i18n; +using Wox.Core.Plugin; +using Wox.Core.Theme; +using Wox.Core.Updater; +using Wox.Core.UserSettings; +using Wox.Helper; +using Wox.Infrastructure; +using Wox.Infrastructure.Hotkey; +using Wox.Plugin; +using Wox.Storage; +using ContextMenu = System.Windows.Forms.ContextMenu; +using DataFormats = System.Windows.DataFormats; +using DragEventArgs = System.Windows.DragEventArgs; +using IDataObject = System.Windows.IDataObject; +using KeyEventArgs = System.Windows.Input.KeyEventArgs; +using MenuItem = System.Windows.Forms.MenuItem; +using MessageBox = System.Windows.MessageBox; +using ToolTip = System.Windows.Controls.ToolTip; + +namespace Wox +{ + public partial class MainWindow : IPublicAPI + { + + #region Properties + + private readonly Storyboard progressBarStoryboard = new Storyboard(); + private NotifyIcon notifyIcon; + private bool queryHasReturn; + private string lastQuery; + private ToolTip toolTip = new ToolTip(); + + private bool ignoreTextChange = false; + + #endregion + + #region Public API + + public void ChangeQuery(string query, bool requery = false) + { + Dispatcher.Invoke(new Action(() => + { + tbQuery.Text = query; + tbQuery.CaretIndex = tbQuery.Text.Length; + if (requery) + { + TextBoxBase_OnTextChanged(null, null); + } + })); + } + + public void CloseApp() + { + Dispatcher.Invoke(new Action(() => + { + notifyIcon.Visible = false; + Close(); + Environment.Exit(0); + })); + } + + public void HideApp() + { + Dispatcher.Invoke(new Action(HideWox)); + } + + public void ShowApp() + { + Dispatcher.Invoke(new Action(() => ShowWox())); + } + + public void ShowMsg(string title, string subTitle, string iconPath) + { + Dispatcher.Invoke(new Action(() => + { + var m = new Msg { Owner = GetWindow(this) }; + m.Show(title, subTitle, iconPath); + })); + } + + public void OpenSettingDialog() + { + Dispatcher.Invoke(new Action(() => WindowOpener.Open(this))); + } + + public void StartLoadingBar() + { + Dispatcher.Invoke(new Action(StartProgress)); + } + + public void StopLoadingBar() + { + Dispatcher.Invoke(new Action(StopProgress)); + } + + public void InstallPlugin(string path) + { + Dispatcher.Invoke(new Action(() => PluginManager.InstallPlugin(path))); + } + + public void ReloadPlugins() + { + Dispatcher.Invoke(new Action(() => PluginManager.Init(this))); + } + + public string GetTranslation(string key) + { + return InternationalizationManager.Instance.GetTranslation(key); + } + + public List GetAllPlugins() + { + return PluginManager.AllPlugins; + } + + public event WoxKeyDownEventHandler BackKeyDownEvent; + public event WoxGlobalKeyboardEventHandler GlobalKeyboardEvent; + public event AfterWoxQueryEventHandler AfterWoxQueryEvent; + public event AfterWoxQueryEventHandler BeforeWoxQueryEvent; + public event ResultItemDropEventHandler ResultItemDropEvent; + + public void PushResults(Query query, PluginMetadata plugin, List results) + { + results.ForEach(o => + { + o.PluginDirectory = plugin.PluginDirectory; + o.PluginID = plugin.ID; + o.OriginQuery = query; + if (o.ContextMenu != null) + { + o.ContextMenu.ForEach(t => + { + t.PluginDirectory = plugin.PluginDirectory; + t.PluginID = plugin.ID; + }); + } + }); + UpdateResultView(results); + } + + public void ShowContextMenu(PluginMetadata plugin, List results) + { + if (results != null && results.Count > 0) + { + results.ForEach(o => + { + o.PluginDirectory = plugin.PluginDirectory; + o.PluginID = plugin.ID; + o.ContextMenu = null; + }); + pnlContextMenu.Clear(); + pnlContextMenu.AddResults(results); + pnlContextMenu.Visibility = Visibility.Visible; + pnlResult.Visibility = Visibility.Collapsed; + } + } + + #endregion + + public MainWindow() + { + InitializeComponent(); + ThreadPool.SetMaxThreads(30, 10); + ThreadPool.SetMinThreads(10, 5); + + WebRequest.RegisterPrefix("data", new DataWebRequestFactory()); + GlobalHotkey.Instance.hookedKeyboardCallback += KListener_hookedKeyboardCallback; + progressBar.ToolTip = toolTip; + InitialTray(); + pnlResult.LeftMouseClickEvent += SelectResult; + pnlResult.ItemDropEvent += pnlResult_ItemDropEvent; + pnlContextMenu.LeftMouseClickEvent += SelectResult; + pnlResult.RightMouseClickEvent += pnlResult_RightMouseClickEvent; + + ThemeManager.Theme.ChangeTheme(UserSettingStorage.Instance.Theme); + InternationalizationManager.Instance.ChangeLanguage(UserSettingStorage.Instance.Language); + + SetHotkey(UserSettingStorage.Instance.Hotkey, OnHotkey); + SetCustomPluginHotkey(); + + Closing += MainWindow_Closing; + //since MainWIndow implement IPublicAPI, so we need to finish ctor MainWindow object before + //PublicAPI invoke in plugin init methods. E.g FolderPlugin + ThreadPool.QueueUserWorkItem(o => + { + Thread.Sleep(50); + PluginManager.Init(this); + }); + ThreadPool.QueueUserWorkItem(o => + { + Thread.Sleep(50); + PreLoadImages(); + }); + } + + void pnlResult_ItemDropEvent(Result result, IDataObject dropDataObject, DragEventArgs args) + { + PluginPair pluginPair = PluginManager.AllPlugins.FirstOrDefault(o => o.Metadata.ID == result.PluginID); + if (ResultItemDropEvent != null && pluginPair != null) + { + foreach (var delegateHandler in ResultItemDropEvent.GetInvocationList()) + { + if (delegateHandler.Target == pluginPair.Plugin) + { + delegateHandler.DynamicInvoke(result, dropDataObject, args); + } + } + } + } + + private bool KListener_hookedKeyboardCallback(KeyEvent keyevent, int vkcode, SpecialKeyState state) + { + if (GlobalKeyboardEvent != null) + { + return GlobalKeyboardEvent((int)keyevent, vkcode, state); + } + return true; + } + + private void PreLoadImages() + { + ImageLoader.ImageLoader.PreloadImages(); + } + + void pnlResult_RightMouseClickEvent(Result result) + { + ShowContextMenuFromResult(result); + } + + void MainWindow_Closing(object sender, CancelEventArgs e) + { + UserSettingStorage.Instance.WindowLeft = Left; + UserSettingStorage.Instance.WindowTop = Top; + UserSettingStorage.Instance.Save(); + this.HideWox(); + e.Cancel = true; + } + + private void MainWindow_OnLoaded(object sender, RoutedEventArgs e) + { + if (UserSettingStorage.Instance.WindowLeft == 0 + && UserSettingStorage.Instance.WindowTop == 0) + { + Left = UserSettingStorage.Instance.WindowLeft + = (SystemParameters.PrimaryScreenWidth - ActualWidth) / 2; + Top = UserSettingStorage.Instance.WindowTop + = (SystemParameters.PrimaryScreenHeight - ActualHeight) / 5; + } + else + { + Left = UserSettingStorage.Instance.WindowLeft; + Top = UserSettingStorage.Instance.WindowTop; + } + + InitProgressbarAnimation(); + WindowIntelopHelper.DisableControlBox(this); + CheckUpdate(); + } + + private void CheckUpdate() + { + UpdaterManager.Instance.PrepareUpdateReady += OnPrepareUpdateReady; + UpdaterManager.Instance.UpdateError += OnUpdateError; + UpdaterManager.Instance.CheckUpdate(); + } + + void OnUpdateError(object sender, EventArgs e) + { + string updateError = InternationalizationManager.Instance.GetTranslation("update_wox_update_error"); + MessageBox.Show(updateError); + } + + private void OnPrepareUpdateReady(object sender, EventArgs e) + { + Dispatcher.Invoke(new Action(() => + { + new WoxUpdate().ShowDialog(); + })); + } + + public void SetHotkey(string hotkeyStr, EventHandler action) + { + var hotkey = new HotkeyModel(hotkeyStr); + try + { + HotkeyManager.Current.AddOrReplace(hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys, action); + } + catch (Exception) + { + string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr); + MessageBox.Show(errorMsg); + } + } + + public void RemoveHotkey(string hotkeyStr) + { + if (!string.IsNullOrEmpty(hotkeyStr)) + { + HotkeyManager.Current.Remove(hotkeyStr); + } + } + + private void SetCustomPluginHotkey() + { + if (UserSettingStorage.Instance.CustomPluginHotkeys == null) return; + foreach (CustomPluginHotkey hotkey in UserSettingStorage.Instance.CustomPluginHotkeys) + { + CustomPluginHotkey hotkey1 = hotkey; + SetHotkey(hotkey.Hotkey, delegate + { + ShowApp(); + ChangeQuery(hotkey1.ActionKeyword, true); + }); + } + } + + private void OnHotkey(object sender, HotkeyEventArgs e) + { + ToggleWox(); + e.Handled = true; + } + + public void ToggleWox() + { + if (!IsVisible) + { + ShowWox(); + } + else + { + HideWox(); + } + } + + private void InitProgressbarAnimation() + { + var da = new DoubleAnimation(progressBar.X2, ActualWidth + 100, new Duration(new TimeSpan(0, 0, 0, 0, 1600))); + var da1 = new DoubleAnimation(progressBar.X1, ActualWidth, new Duration(new TimeSpan(0, 0, 0, 0, 1600))); + Storyboard.SetTargetProperty(da, new PropertyPath("(Line.X2)")); + Storyboard.SetTargetProperty(da1, new PropertyPath("(Line.X1)")); + progressBarStoryboard.Children.Add(da); + progressBarStoryboard.Children.Add(da1); + progressBarStoryboard.RepeatBehavior = RepeatBehavior.Forever; + progressBar.Visibility = Visibility.Hidden; + progressBar.BeginStoryboard(progressBarStoryboard); + } + + private void InitialTray() + { + notifyIcon = new NotifyIcon { Text = "Wox", Icon = Properties.Resources.app, Visible = true }; + notifyIcon.Click += (o, e) => ShowWox(); + var open = new MenuItem("Open"); + open.Click += (o, e) => ShowWox(); + var setting = new MenuItem("Settings"); + setting.Click += (o, e) => OpenSettingDialog(); + var exit = new MenuItem("Exit"); + exit.Click += (o, e) => CloseApp(); + MenuItem[] childen = { open, setting, exit }; + notifyIcon.ContextMenu = new ContextMenu(childen); + } + + private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e) + { + if (ignoreTextChange) { ignoreTextChange = false; return; } + + lastQuery = tbQuery.Text; + toolTip.IsOpen = false; + pnlResult.Dirty = true; + int searchDelay = GetSearchDelay(lastQuery); + + Dispatcher.DelayInvoke("UpdateSearch", + o => + { + Dispatcher.DelayInvoke("ClearResults", i => + { + // first try to use clear method inside pnlResult, which is more closer to the add new results + // and this will not bring splash issues.After waiting 100ms, if there still no results added, we + // must clear the result. otherwise, it will be confused why the query changed, but the results + // didn't. + if (pnlResult.Dirty) pnlResult.Clear(); + }, TimeSpan.FromMilliseconds(100), null); + queryHasReturn = false; + Query query = new Query(lastQuery); + query.IsIntantQuery = searchDelay == 0; + FireBeforeWoxQueryEvent(query); + Query(query); + Dispatcher.DelayInvoke("ShowProgressbar", originQuery => + { + if (!queryHasReturn && originQuery == tbQuery.Text && !string.IsNullOrEmpty(lastQuery)) + { + StartProgress(); + } + }, TimeSpan.FromMilliseconds(150), tbQuery.Text); + FireAfterWoxQueryEvent(query); + }, TimeSpan.FromMilliseconds(searchDelay)); + } + + private int GetSearchDelay(string query) + { + if (!string.IsNullOrEmpty(query) && PluginManager.IsInstantQuery(query)) + { + DebugHelper.WriteLine("execute query without delay"); + return 0; + } + + DebugHelper.WriteLine("execute query with 200ms delay"); + return 200; + } + + private void FireAfterWoxQueryEvent(Query q) + { + if (AfterWoxQueryEvent != null) + { + //We shouldn't let those events slow down real query + //so I put it in the new thread + ThreadPool.QueueUserWorkItem(o => + { + AfterWoxQueryEvent(new WoxQueryEventArgs() + { + Query = q + }); + }); + } + } + + private void FireBeforeWoxQueryEvent(Query q) + { + if (BeforeWoxQueryEvent != null) + { + //We shouldn't let those events slow down real query + //so I put it in the new thread + ThreadPool.QueueUserWorkItem(o => + { + BeforeWoxQueryEvent(new WoxQueryEventArgs() + { + Query = q + }); + }); + } + } + + private void Query(Query q) + { + PluginManager.Query(q); + StopProgress(); + BackToResultMode(); + } + + private void BackToResultMode() + { + pnlResult.Visibility = Visibility.Visible; + pnlContextMenu.Visibility = Visibility.Collapsed; + } + + private void Border_OnMouseDown(object sender, MouseButtonEventArgs e) + { + if (e.ChangedButton == MouseButton.Left) DragMove(); + } + + private void StartProgress() + { + progressBar.Visibility = Visibility.Visible; + } + + private void StopProgress() + { + progressBar.Visibility = Visibility.Hidden; + } + + private void HideWox() + { + Hide(); + } + + private void ShowWox(bool selectAll = true) + { + UserSettingStorage.Instance.IncreaseActivateTimes(); + if (!double.IsNaN(Left) && !double.IsNaN(Top)) + { + var origScreen = Screen.FromRectangle(new Rectangle((int)Left, (int)Top, (int)ActualWidth, (int)ActualHeight)); + var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); + var coordX = (Left - origScreen.WorkingArea.Left) / (origScreen.WorkingArea.Width - ActualWidth); + var coordY = (Top - origScreen.WorkingArea.Top) / (origScreen.WorkingArea.Height - ActualHeight); + Left = (screen.WorkingArea.Width - ActualWidth) * coordX + screen.WorkingArea.Left; + Top = (screen.WorkingArea.Height - ActualHeight) * coordY + screen.WorkingArea.Top; + } + + Show(); + Activate(); + Focus(); + tbQuery.Focus(); + if (selectAll) tbQuery.SelectAll(); + } + + private void MainWindow_OnDeactivated(object sender, EventArgs e) + { + if (UserSettingStorage.Instance.HideWhenDeactive) + { + HideWox(); + } + } + + private void TbQuery_OnPreviewKeyDown(object sender, KeyEventArgs e) + { + //when alt is pressed, the real key should be e.SystemKey + Key key = (e.Key == Key.System ? e.SystemKey : e.Key); + switch (key) + { + case Key.Escape: + if (IsInContextMenuMode) + { + BackToResultMode(); + } + else + { + HideWox(); + } + e.Handled = true; + break; + + case Key.Tab: + if (GlobalHotkey.Instance.CheckModifiers().ShiftPressed) + { + SelectPrevItem(); + } + else + { + SelectNextItem(); + } + e.Handled = true; + break; + + case Key.N: + case Key.J: + if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) + { + SelectNextItem(); + } + break; + + case Key.P: + case Key.K: + if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) + { + SelectPrevItem(); + } + break; + + case Key.O: + if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) + { + if (IsInContextMenuMode) + { + BackToResultMode(); + } + else + { + ShowContextMenuFromResult(GetActiveResult()); + } + } + break; + + case Key.Down: + SelectNextItem(); + e.Handled = true; + break; + + case Key.Up: + SelectPrevItem(); + e.Handled = true; + break; + + case Key.D: + if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) + { + pnlResult.SelectNextPage(); + } + break; + + case Key.PageDown: + pnlResult.SelectNextPage(); + e.Handled = true; + break; + + case Key.U: + if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) + { + pnlResult.SelectPrevPage(); + } + break; + + case Key.PageUp: + pnlResult.SelectPrevPage(); + e.Handled = true; + break; + + case Key.Back: + if (BackKeyDownEvent != null) + { + BackKeyDownEvent(new WoxKeyDownEventArgs() + { + Query = tbQuery.Text, + keyEventArgs = e + }); + } + break; + + case Key.F1: + Process.Start("http://doc.getwox.com"); + break; + + case Key.Enter: + Result activeResult = GetActiveResult(); + if (GlobalHotkey.Instance.CheckModifiers().ShiftPressed) + { + ShowContextMenuFromResult(activeResult); + } + else + { + SelectResult(activeResult); + } + e.Handled = true; + break; + + case Key.D1: + SelectItem(1); + break; + + case Key.D2: + SelectItem(2); + break; + + case Key.D3: + SelectItem(3); + break; + + case Key.D4: + SelectItem(4); + break; + + case Key.D5: + SelectItem(5); + break; + case Key.D6: + SelectItem(6); + break; + + } + } + + private void SelectItem(int index) + { + int zeroBasedIndex = index - 1; + SpecialKeyState keyState = GlobalHotkey.Instance.CheckModifiers(); + if (keyState.AltPressed || keyState.CtrlPressed) + { + List visibleResults = pnlResult.GetVisibleResults(); + if (zeroBasedIndex < visibleResults.Count) + { + SelectResult(visibleResults[zeroBasedIndex]); + } + } + } + + private bool IsInContextMenuMode + { + get { return pnlContextMenu.Visibility == Visibility.Visible; } + } + + private Result GetActiveResult() + { + if (IsInContextMenuMode) + { + return pnlContextMenu.GetActiveResult(); + } + else + { + return pnlResult.GetActiveResult(); + } + } + + private void SelectPrevItem() + { + if (IsInContextMenuMode) + { + pnlContextMenu.SelectPrev(); + } + else + { + pnlResult.SelectPrev(); + } + toolTip.IsOpen = false; + } + + private void SelectNextItem() + { + if (IsInContextMenuMode) + { + pnlContextMenu.SelectNext(); + } + else + { + pnlResult.SelectNext(); + } + toolTip.IsOpen = false; + } + + private void SelectResult(Result result) + { + if (result != null) + { + if (result.Action != null) + { + bool hideWindow = result.Action(new ActionContext() + { + SpecialKeyState = GlobalHotkey.Instance.CheckModifiers() + }); + if (hideWindow) + { + HideWox(); + } + UserSelectedRecordStorage.Instance.Add(result); + } + } + } + + private void UpdateResultView(List list) + { + queryHasReturn = true; + progressBar.Dispatcher.Invoke(new Action(StopProgress)); + if (list == null || list.Count == 0) return; + + if (list.Count > 0) + { + list.ForEach(o => + { + o.Score += UserSelectedRecordStorage.Instance.GetSelectedCount(o) * 5; + if (o.ContextMenu == null) + { + o.ContextMenu = new List(); + } + HanleTopMost(o); + }); + List l = list.Where(o => o.OriginQuery != null && o.OriginQuery.RawQuery == lastQuery).ToList(); + Dispatcher.Invoke(new Action(() => + { + pnlResult.AddResults(l); + })); + } + } + + private void HanleTopMost(Result result) + { + if (TopMostRecordStorage.Instance.IsTopMost(result)) + { + result.ContextMenu.Add(new Result("Remove top most in this query", "Images\\down.png") + { + PluginDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), + Action = _ => + { + TopMostRecordStorage.Instance.Remove(result); + ShowMsg("Succeed", "", ""); + return false; + } + }); + } + else + { + result.ContextMenu.Add(new Result("Set as top most in this query", "Images\\up.png") + { + PluginDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), + Action = _ => + { + TopMostRecordStorage.Instance.Add(result); + ShowMsg("Succeed", "", ""); + return false; + } + }); + } + } + + private void ShowContextMenuFromResult(Result result) + { + if (result.ContextMenu != null && result.ContextMenu.Count > 0) + { + pnlContextMenu.Clear(); + pnlContextMenu.AddResults(result.ContextMenu); + pnlContextMenu.Visibility = Visibility.Visible; + pnlResult.Visibility = Visibility.Collapsed; + } + } + + public bool ShellRun(string cmd, bool runAsAdministrator = false) + { + try + { + if (string.IsNullOrEmpty(cmd)) + throw new ArgumentNullException(); + + WindowsShellRun.Start(cmd, runAsAdministrator); + return true; + } + catch (Exception ex) + { + string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("couldnotStartCmd"), cmd); + ShowMsg(errorMsg, ex.Message, null); + } + return false; + } + + private void MainWindow_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")); + } + } + } + + private void TbQuery_OnPreviewDragOver(object sender, DragEventArgs e) + { + e.Handled = true; + } + } +} \ No newline at end of file diff --git a/Wox/su54u2mz.xrz b/Wox/su54u2mz.xrz new file mode 100644 index 0000000000..b83197e8fe --- /dev/null +++ b/Wox/su54u2mz.xrz @@ -0,0 +1,850 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Threading; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Forms; +using System.Windows.Input; +using System.Windows.Media.Animation; +using NHotkey; +using NHotkey.Wpf; +using Wox.Core.i18n; +using Wox.Core.Plugin; +using Wox.Core.Theme; +using Wox.Core.Updater; +using Wox.Core.UserSettings; +using Wox.Helper; +using Wox.Infrastructure; +using Wox.Infrastructure.Hotkey; +using Wox.Plugin; +using Wox.Storage; +using ContextMenu = System.Windows.Forms.ContextMenu; +using DataFormats = System.Windows.DataFormats; +using DragEventArgs = System.Windows.DragEventArgs; +using IDataObject = System.Windows.IDataObject; +using KeyEventArgs = System.Windows.Input.KeyEventArgs; +using MenuItem = System.Windows.Forms.MenuItem; +using MessageBox = System.Windows.MessageBox; +using ToolTip = System.Windows.Controls.ToolTip; + +namespace Wox +{ + public partial class MainWindow : IPublicAPI + { + + #region Properties + + private readonly Storyboard progressBarStoryboard = new Storyboard(); + private NotifyIcon notifyIcon; + private bool queryHasReturn; + private string lastQuery; + private ToolTip toolTip = new ToolTip(); + + private bool ignoreTextChange = false; + + #endregion + + #region Public API + + public void ChangeQuery(string query, bool requery = false) + { + Dispatcher.Invoke(new Action(() => + { + tbQuery.Text = query; + tbQuery.CaretIndex = tbQuery.Text.Length; + if (requery) + { + TextBoxBase_OnTextChanged(null, null); + } + })); + } + + public void CloseApp() + { + Dispatcher.Invoke(new Action(() => + { + notifyIcon.Visible = false; + Close(); + Environment.Exit(0); + })); + } + + public void HideApp() + { + Dispatcher.Invoke(new Action(HideWox)); + } + + public void ShowApp() + { + Dispatcher.Invoke(new Action(() => ShowWox())); + } + + public void ShowMsg(string title, string subTitle, string iconPath) + { + Dispatcher.Invoke(new Action(() => + { + var m = new Msg { Owner = GetWindow(this) }; + m.Show(title, subTitle, iconPath); + })); + } + + public void OpenSettingDialog() + { + Dispatcher.Invoke(new Action(() => WindowOpener.Open(this))); + } + + public void StartLoadingBar() + { + Dispatcher.Invoke(new Action(StartProgress)); + } + + public void StopLoadingBar() + { + Dispatcher.Invoke(new Action(StopProgress)); + } + + public void InstallPlugin(string path) + { + Dispatcher.Invoke(new Action(() => PluginManager.InstallPlugin(path))); + } + + public void ReloadPlugins() + { + Dispatcher.Invoke(new Action(() => PluginManager.Init(this))); + } + + public string GetTranslation(string key) + { + return InternationalizationManager.Instance.GetTranslation(key); + } + + public List GetAllPlugins() + { + return PluginManager.AllPlugins; + } + + public event WoxKeyDownEventHandler BackKeyDownEvent; + public event WoxGlobalKeyboardEventHandler GlobalKeyboardEvent; + public event AfterWoxQueryEventHandler AfterWoxQueryEvent; + public event AfterWoxQueryEventHandler BeforeWoxQueryEvent; + public event ResultItemDropEventHandler ResultItemDropEvent; + + public void PushResults(Query query, PluginMetadata plugin, List results) + { + results.ForEach(o => + { + o.PluginDirectory = plugin.PluginDirectory; + o.PluginID = plugin.ID; + o.OriginQuery = query; + if (o.ContextMenu != null) + { + o.ContextMenu.ForEach(t => + { + t.PluginDirectory = plugin.PluginDirectory; + t.PluginID = plugin.ID; + }); + } + }); + UpdateResultView(results); + } + + public void ShowContextMenu(PluginMetadata plugin, List results) + { + if (results != null && results.Count > 0) + { + results.ForEach(o => + { + o.PluginDirectory = plugin.PluginDirectory; + o.PluginID = plugin.ID; + o.ContextMenu = null; + }); + pnlContextMenu.Clear(); + pnlContextMenu.AddResults(results); + pnlContextMenu.Visibility = Visibility.Visible; + pnlResult.Visibility = Visibility.Collapsed; + } + } + + #endregion + + public MainWindow() + { + InitializeComponent(); + ThreadPool.SetMaxThreads(30, 10); + ThreadPool.SetMinThreads(10, 5); + + WebRequest.RegisterPrefix("data", new DataWebRequestFactory()); + GlobalHotkey.Instance.hookedKeyboardCallback += KListener_hookedKeyboardCallback; + progressBar.ToolTip = toolTip; + InitialTray(); + pnlResult.LeftMouseClickEvent += SelectResult; + pnlResult.ItemDropEvent += pnlResult_ItemDropEvent; + pnlContextMenu.LeftMouseClickEvent += SelectResult; + pnlResult.RightMouseClickEvent += pnlResult_RightMouseClickEvent; + + ThemeManager.Theme.ChangeTheme(UserSettingStorage.Instance.Theme); + InternationalizationManager.Instance.ChangeLanguage(UserSettingStorage.Instance.Language); + + SetHotkey(UserSettingStorage.Instance.Hotkey, OnHotkey); + SetCustomPluginHotkey(); + + Closing += MainWindow_Closing; + //since MainWIndow implement IPublicAPI, so we need to finish ctor MainWindow object before + //PublicAPI invoke in plugin init methods. E.g FolderPlugin + ThreadPool.QueueUserWorkItem(o => + { + Thread.Sleep(50); + PluginManager.Init(this); + }); + ThreadPool.QueueUserWorkItem(o => + { + Thread.Sleep(50); + PreLoadImages(); + }); + } + + void pnlResult_ItemDropEvent(Result result, IDataObject dropDataObject, DragEventArgs args) + { + PluginPair pluginPair = PluginManager.AllPlugins.FirstOrDefault(o => o.Metadata.ID == result.PluginID); + if (ResultItemDropEvent != null && pluginPair != null) + { + foreach (var delegateHandler in ResultItemDropEvent.GetInvocationList()) + { + if (delegateHandler.Target == pluginPair.Plugin) + { + delegateHandler.DynamicInvoke(result, dropDataObject, args); + } + } + } + } + + private bool KListener_hookedKeyboardCallback(KeyEvent keyevent, int vkcode, SpecialKeyState state) + { + if (GlobalKeyboardEvent != null) + { + return GlobalKeyboardEvent((int)keyevent, vkcode, state); + } + return true; + } + + private void PreLoadImages() + { + ImageLoader.ImageLoader.PreloadImages(); + } + + void pnlResult_RightMouseClickEvent(Result result) + { + ShowContextMenuFromResult(result); + } + + void MainWindow_Closing(object sender, CancelEventArgs e) + { + UserSettingStorage.Instance.WindowLeft = Left; + UserSettingStorage.Instance.WindowTop = Top; + UserSettingStorage.Instance.Save(); + this.HideWox(); + e.Cancel = true; + } + + private void MainWindow_OnLoaded(object sender, RoutedEventArgs e) + { + if (UserSettingStorage.Instance.WindowLeft == 0 + && UserSettingStorage.Instance.WindowTop == 0) + { + Left = UserSettingStorage.Instance.WindowLeft + = (SystemParameters.PrimaryScreenWidth - ActualWidth) / 2; + Top = UserSettingStorage.Instance.WindowTop + = (SystemParameters.PrimaryScreenHeight - ActualHeight) / 5; + } + else + { + Left = UserSettingStorage.Instance.WindowLeft; + Top = UserSettingStorage.Instance.WindowTop; + } + + InitProgressbarAnimation(); + WindowIntelopHelper.DisableControlBox(this); + CheckUpdate(); + } + + private void CheckUpdate() + { + UpdaterManager.Instance.PrepareUpdateReady += OnPrepareUpdateReady; + UpdaterManager.Instance.UpdateError += OnUpdateError; + UpdaterManager.Instance.CheckUpdate(); + } + + void OnUpdateError(object sender, EventArgs e) + { + string updateError = InternationalizationManager.Instance.GetTranslation("update_wox_update_error"); + MessageBox.Show(updateError); + } + + private void OnPrepareUpdateReady(object sender, EventArgs e) + { + Dispatcher.Invoke(new Action(() => + { + new WoxUpdate().ShowDialog(); + })); + } + + public void SetHotkey(string hotkeyStr, EventHandler action) + { + var hotkey = new HotkeyModel(hotkeyStr); + try + { + HotkeyManager.Current.AddOrReplace(hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys, action); + } + catch (Exception) + { + string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr); + MessageBox.Show(errorMsg); + } + } + + public void RemoveHotkey(string hotkeyStr) + { + if (!string.IsNullOrEmpty(hotkeyStr)) + { + HotkeyManager.Current.Remove(hotkeyStr); + } + } + + private void SetCustomPluginHotkey() + { + if (UserSettingStorage.Instance.CustomPluginHotkeys == null) return; + foreach (CustomPluginHotkey hotkey in UserSettingStorage.Instance.CustomPluginHotkeys) + { + CustomPluginHotkey hotkey1 = hotkey; + SetHotkey(hotkey.Hotkey, delegate + { + ShowApp(); + ChangeQuery(hotkey1.ActionKeyword, true); + }); + } + } + + private void OnHotkey(object sender, HotkeyEventArgs e) + { + ToggleWox(); + e.Handled = true; + } + + public void ToggleWox() + { + if (!IsVisible) + { + ShowWox(); + } + else + { + HideWox(); + } + } + + private void InitProgressbarAnimation() + { + var da = new DoubleAnimation(progressBar.X2, ActualWidth + 100, new Duration(new TimeSpan(0, 0, 0, 0, 1600))); + var da1 = new DoubleAnimation(progressBar.X1, ActualWidth, new Duration(new TimeSpan(0, 0, 0, 0, 1600))); + Storyboard.SetTargetProperty(da, new PropertyPath("(Line.X2)")); + Storyboard.SetTargetProperty(da1, new PropertyPath("(Line.X1)")); + progressBarStoryboard.Children.Add(da); + progressBarStoryboard.Children.Add(da1); + progressBarStoryboard.RepeatBehavior = RepeatBehavior.Forever; + progressBar.Visibility = Visibility.Hidden; + progressBar.BeginStoryboard(progressBarStoryboard); + } + + private void InitialTray() + { + notifyIcon = new NotifyIcon { Text = "Wox", Icon = Properties.Resources.app, Visible = true }; + notifyIcon.Click += (o, e) => ShowWox(); + var open = new MenuItem("Open"); + open.Click += (o, e) => ShowWox(); + var setting = new MenuItem("Settings"); + setting.Click += (o, e) => OpenSettingDialog(); + var exit = new MenuItem("Exit"); + exit.Click += (o, e) => CloseApp(); + MenuItem[] childen = { open, setting, exit }; + notifyIcon.ContextMenu = new ContextMenu(childen); + } + + private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e) + { + if (ignoreTextChange) { ignoreTextChange = false; return; } + + lastQuery = tbQuery.Text; + toolTip.IsOpen = false; + pnlResult.Dirty = true; + int searchDelay = GetSearchDelay(lastQuery); + + Dispatcher.DelayInvoke("UpdateSearch", + o => + { + Dispatcher.DelayInvoke("ClearResults", i => + { + // first try to use clear method inside pnlResult, which is more closer to the add new results + // and this will not bring splash issues.After waiting 100ms, if there still no results added, we + // must clear the result. otherwise, it will be confused why the query changed, but the results + // didn't. + if (pnlResult.Dirty) pnlResult.Clear(); + }, TimeSpan.FromMilliseconds(100), null); + queryHasReturn = false; + Query query = new Query(lastQuery); + query.IsIntantQuery = searchDelay == 0; + FireBeforeWoxQueryEvent(query); + Query(query); + Dispatcher.DelayInvoke("ShowProgressbar", originQuery => + { + if (!queryHasReturn && originQuery == tbQuery.Text && !string.IsNullOrEmpty(lastQuery)) + { + StartProgress(); + } + }, TimeSpan.FromMilliseconds(150), tbQuery.Text); + FireAfterWoxQueryEvent(query); + }, TimeSpan.FromMilliseconds(searchDelay)); + } + + private int GetSearchDelay(string query) + { + if (!string.IsNullOrEmpty(query) && PluginManager.IsInstantQuery(query)) + { + DebugHelper.WriteLine("execute query without delay"); + return 0; + } + + DebugHelper.WriteLine("execute query with 200ms delay"); + return 200; + } + + private void FireAfterWoxQueryEvent(Query q) + { + if (AfterWoxQueryEvent != null) + { + //We shouldn't let those events slow down real query + //so I put it in the new thread + ThreadPool.QueueUserWorkItem(o => + { + AfterWoxQueryEvent(new WoxQueryEventArgs() + { + Query = q + }); + }); + } + } + + private void FireBeforeWoxQueryEvent(Query q) + { + if (BeforeWoxQueryEvent != null) + { + //We shouldn't let those events slow down real query + //so I put it in the new thread + ThreadPool.QueueUserWorkItem(o => + { + BeforeWoxQueryEvent(new WoxQueryEventArgs() + { + Query = q + }); + }); + } + } + + private void Query(Query q) + { + PluginManager.Query(q); + StopProgress(); + BackToResultMode(); + } + + private void BackToResultMode() + { + pnlResult.Visibility = Visibility.Visible; + pnlContextMenu.Visibility = Visibility.Collapsed; + } + + private void Border_OnMouseDown(object sender, MouseButtonEventArgs e) + { + if (e.ChangedButton == MouseButton.Left) DragMove(); + } + + private void StartProgress() + { + progressBar.Visibility = Visibility.Visible; + } + + private void StopProgress() + { + progressBar.Visibility = Visibility.Hidden; + } + + private void HideWox() + { + Hide(); + } + + private void ShowWox(bool selectAll = true) + { + UserSettingStorage.Instance.IncreaseActivateTimes(); + if (!double.IsNaN(Left) && !double.IsNaN(Top)) + { + var origScreen = Screen.FromRectangle(new Rectangle((int)Left, (int)Top, (int)ActualWidth, (int)ActualHeight)); + var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); + var coordX = (Left - origScreen.WorkingArea.Left) / (origScreen.WorkingArea.Width - ActualWidth); + var coordY = (Top - origScreen.WorkingArea.Top) / (origScreen.WorkingArea.Height - ActualHeight); + Left = (screen.WorkingArea.Width - ActualWidth) * coordX + screen.WorkingArea.Left; + Top = (screen.WorkingArea.Height - ActualHeight) * coordY + screen.WorkingArea.Top; + } + + Show(); + Activate(); + Focus(); + tbQuery.Focus(); + if (selectAll) tbQuery.SelectAll(); + } + + private void MainWindow_OnDeactivated(object sender, EventArgs e) + { + if (UserSettingStorage.Instance.HideWhenDeactive) + { + HideWox(); + } + } + + private void TbQuery_OnPreviewKeyDown(object sender, KeyEventArgs e) + { + //when alt is pressed, the real key should be e.SystemKey + Key key = (e.Key == Key.System ? e.SystemKey : e.Key); + switch (key) + { + case Key.Escape: + if (IsInContextMenuMode) + { + BackToResultMode(); + } + else + { + HideWox(); + } + e.Handled = true; + break; + + case Key.Tab: + if (GlobalHotkey.Instance.CheckModifiers().ShiftPressed) + { + SelectPrevItem(); + } + else + { + SelectNextItem(); + } + e.Handled = true; + break; + + case Key.N: + case Key.J: + if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) + { + SelectNextItem(); + } + break; + + case Key.P: + case Key.K: + if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) + { + SelectPrevItem(); + } + break; + + case Key.O: + if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) + { + if (IsInContextMenuMode) + { + BackToResultMode(); + } + else + { + ShowContextMenuFromResult(GetActiveResult()); + } + } + break; + + case Key.Down: + SelectNextItem(); + e.Handled = true; + break; + + case Key.Up: + SelectPrevItem(); + e.Handled = true; + break; + + case Key.D: + if (GlobalHotkey.Instance.CheckModifiers().AltPressed) + { + pnlResult.SelectNextPage(); + } + break; + + case Key.PageDown: + pnlResult.SelectNextPage(); + e.Handled = true; + break; + + case Key.U: + if (GlobalHotkey.Instance.CheckModifiers().AltPressed) + { + pnlResult.SelectPrevPage(); + } + break; + + case Key.PageUp: + pnlResult.SelectPrevPage(); + e.Handled = true; + break; + + case Key.Back: + if (BackKeyDownEvent != null) + { + BackKeyDownEvent(new WoxKeyDownEventArgs() + { + Query = tbQuery.Text, + keyEventArgs = e + }); + } + break; + + case Key.F1: + Process.Start("http://doc.getwox.com"); + break; + + case Key.Enter: + Result activeResult = GetActiveResult(); + if (GlobalHotkey.Instance.CheckModifiers().ShiftPressed) + { + ShowContextMenuFromResult(activeResult); + } + else + { + SelectResult(activeResult); + } + e.Handled = true; + break; + + case Key.D1: + SelectItem(1); + break; + + case Key.D2: + SelectItem(2); + break; + + case Key.D3: + SelectItem(3); + break; + + case Key.D4: + SelectItem(4); + break; + + case Key.D5: + SelectItem(5); + break; + case Key.D6: + SelectItem(6); + break; + + } + } + + private void SelectItem(int index) + { + int zeroBasedIndex = index - 1; + SpecialKeyState keyState = GlobalHotkey.Instance.CheckModifiers(); + if (keyState.AltPressed || keyState.CtrlPressed) + { + List visibleResults = pnlResult.GetVisibleResults(); + if (zeroBasedIndex < visibleResults.Count) + { + SelectResult(visibleResults[zeroBasedIndex]); + } + } + } + + private bool IsInContextMenuMode + { + get { return pnlContextMenu.Visibility == Visibility.Visible; } + } + + private Result GetActiveResult() + { + if (IsInContextMenuMode) + { + return pnlContextMenu.GetActiveResult(); + } + else + { + return pnlResult.GetActiveResult(); + } + } + + private void SelectPrevItem() + { + if (IsInContextMenuMode) + { + pnlContextMenu.SelectPrev(); + } + else + { + pnlResult.SelectPrev(); + } + toolTip.IsOpen = false; + } + + private void SelectNextItem() + { + if (IsInContextMenuMode) + { + pnlContextMenu.SelectNext(); + } + else + { + pnlResult.SelectNext(); + } + toolTip.IsOpen = false; + } + + private void SelectResult(Result result) + { + if (result != null) + { + if (result.Action != null) + { + bool hideWindow = result.Action(new ActionContext() + { + SpecialKeyState = GlobalHotkey.Instance.CheckModifiers() + }); + if (hideWindow) + { + HideWox(); + } + UserSelectedRecordStorage.Instance.Add(result); + } + } + } + + private void UpdateResultView(List list) + { + queryHasReturn = true; + progressBar.Dispatcher.Invoke(new Action(StopProgress)); + if (list == null || list.Count == 0) return; + + if (list.Count > 0) + { + list.ForEach(o => + { + o.Score += UserSelectedRecordStorage.Instance.GetSelectedCount(o) * 5; + if (o.ContextMenu == null) + { + o.ContextMenu = new List(); + } + HanleTopMost(o); + }); + List l = list.Where(o => o.OriginQuery != null && o.OriginQuery.RawQuery == lastQuery).ToList(); + Dispatcher.Invoke(new Action(() => + { + pnlResult.AddResults(l); + })); + } + } + + private void HanleTopMost(Result result) + { + if (TopMostRecordStorage.Instance.IsTopMost(result)) + { + result.ContextMenu.Add(new Result("Remove top most in this query", "Images\\down.png") + { + PluginDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), + Action = _ => + { + TopMostRecordStorage.Instance.Remove(result); + ShowMsg("Succeed", "", ""); + return false; + } + }); + } + else + { + result.ContextMenu.Add(new Result("Set as top most in this query", "Images\\up.png") + { + PluginDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), + Action = _ => + { + TopMostRecordStorage.Instance.Add(result); + ShowMsg("Succeed", "", ""); + return false; + } + }); + } + } + + private void ShowContextMenuFromResult(Result result) + { + if (result.ContextMenu != null && result.ContextMenu.Count > 0) + { + pnlContextMenu.Clear(); + pnlContextMenu.AddResults(result.ContextMenu); + pnlContextMenu.Visibility = Visibility.Visible; + pnlResult.Visibility = Visibility.Collapsed; + } + } + + public bool ShellRun(string cmd, bool runAsAdministrator = false) + { + try + { + if (string.IsNullOrEmpty(cmd)) + throw new ArgumentNullException(); + + WindowsShellRun.Start(cmd, runAsAdministrator); + return true; + } + catch (Exception ex) + { + string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("couldnotStartCmd"), cmd); + ShowMsg(errorMsg, ex.Message, null); + } + return false; + } + + private void MainWindow_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")); + } + } + } + + private void TbQuery_OnPreviewDragOver(object sender, DragEventArgs e) + { + e.Handled = true; + } + } +} \ No newline at end of file