diff --git a/Plugins/Wox.Plugin.Folder/Main.cs b/Plugins/Wox.Plugin.Folder/Main.cs index f36029cb8..2c8cf2703 100644 --- a/Plugins/Wox.Plugin.Folder/Main.cs +++ b/Plugins/Wox.Plugin.Folder/Main.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -44,7 +44,7 @@ namespace Wox.Plugin.Folder var results = GetUserFolderResults(query); string search = query.Search.ToLower(); - if (!IsDriveOrSharedFolder(search)) + if (_driverNames != null && !_driverNames.Any(search.StartsWith)) return results; results.AddRange(QueryInternal_Directory_Exists(query)); @@ -58,27 +58,7 @@ namespace Wox.Plugin.Folder return results; } - private static bool IsDriveOrSharedFolder(string search) - { - if (search.StartsWith(@"\\")) - { - return true; - } - - if (_driverNames != null && _driverNames.Any(search.StartsWith)) - { - return true; - } - - if (_driverNames == null && search.Length > 2 && char.IsLetter(search[0]) && search[1] == ':') - { - return true; // we don't know so let's give it the possibility - } - - return false; - } - - private Result CreateFolderResult(string title, string path) + private Result CreateFolderResult(string title, string path, string queryActionKeyword) { return new Result { @@ -102,7 +82,7 @@ namespace Wox.Plugin.Folder } string changeTo = path.EndsWith("\\") ? path : path + "\\"; - _context.API.ChangeQuery(changeTo); + _context.API.ChangeQuery(string.IsNullOrEmpty(queryActionKeyword)? changeTo : queryActionKeyword + " " + changeTo); return false; } }; @@ -113,7 +93,7 @@ namespace Wox.Plugin.Folder 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)) + var results = userFolderLinks.Select(item => CreateFolderResult(item.Nickname, item.Path, query.ActionKeyword)) .ToList(); return results; } @@ -138,7 +118,7 @@ namespace Wox.Plugin.Folder private List QueryInternal_Directory_Exists(Query query) { - var search = query.Search.ToLower(); + var search = query.Search; var results = new List(); var hasSpecial = search.IndexOfAny(_specialSearchChars) >= 0; string incompleteName = ""; @@ -177,18 +157,32 @@ namespace Wox.Plugin.Folder incompleteName = incompleteName.Substring(1); } - // search folder and add results - var fileSystemInfos = directoryInfo.GetFileSystemInfos(incompleteName, searchOption); - - foreach (var fileSystemInfo in fileSystemInfos) + try { - if ((fileSystemInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) continue; + // search folder and add results + var fileSystemInfos = directoryInfo.GetFileSystemInfos(incompleteName, searchOption); - var result = - fileSystemInfo is DirectoryInfo - ? CreateFolderResult(fileSystemInfo.Name, fileSystemInfo.FullName) - : CreateFileResult(fileSystemInfo.FullName); - results.Add(result); + foreach (var fileSystemInfo in fileSystemInfos) + { + if ((fileSystemInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) continue; + + var result = + fileSystemInfo is DirectoryInfo + ? CreateFolderResult(fileSystemInfo.Name, fileSystemInfo.FullName, query.ActionKeyword) + : CreateFileResult(fileSystemInfo.FullName); + results.Add(result); + } + } + catch(Exception e) + { + if (e is UnauthorizedAccessException || e is ArgumentException) + { + results.Add(new Result { Title = e.Message, Score = 501 }); + + return results; + } + + throw; } return results; @@ -219,14 +213,19 @@ namespace Wox.Plugin.Folder private static Result CreateOpenCurrentFolderResult(string incompleteName, string search) { - string firstResult = "Open current directory"; + var firstResult = "Open current directory"; if (incompleteName.Length > 0) firstResult = "Open " + search; + + var folderName = search.TrimEnd('\\').Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None).Last(); + return new Result { Title = firstResult, + SubTitle = $"Use > to search files and subfolders within {folderName}, " + + $"* to search for file extensions in {folderName} or both >* to combine the search", IcoPath = search, - Score = 10000, + Score = 500, Action = c => { Process.Start(search); diff --git a/Plugins/Wox.Plugin.Shell/Languages/en.xaml b/Plugins/Wox.Plugin.Shell/Languages/en.xaml index 7a953986c..6f56e83f5 100644 --- a/Plugins/Wox.Plugin.Shell/Languages/en.xaml +++ b/Plugins/Wox.Plugin.Shell/Languages/en.xaml @@ -4,6 +4,7 @@ Replace Win+R Do not close Command Prompt after command execution + Always run as administrator Shell Allows to execute system commands from Wox. Commands should start with > this command has been executed {0} times diff --git a/Plugins/Wox.Plugin.Shell/Main.cs b/Plugins/Wox.Plugin.Shell/Main.cs index 1e60ef8c9..323cfb5cd 100644 --- a/Plugins/Wox.Plugin.Shell/Main.cs +++ b/Plugins/Wox.Plugin.Shell/Main.cs @@ -9,6 +9,7 @@ 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; @@ -164,16 +165,15 @@ namespace Wox.Plugin.Shell { 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 = new ProcessStartInfo - { - FileName = "cmd.exe", - Arguments = arguments, - }; + + info = ShellCommand.SetProcessStartInfo("cmd.exe", workingDirectory, arguments, runAsAdministratorArg); } else if (_settings.Shell == Shell.Powershell) { @@ -186,11 +186,8 @@ namespace Wox.Plugin.Shell { arguments = $"\"{command} ; Read-Host -Prompt \\\"Press Enter to continue\\\"\""; } - info = new ProcessStartInfo - { - FileName = "powershell.exe", - Arguments = arguments - }; + + info = ShellCommand.SetProcessStartInfo("powershell.exe", workingDirectory, arguments, runAsAdministratorArg); } else if (_settings.Shell == Shell.RunCommand) { @@ -200,21 +197,17 @@ namespace Wox.Plugin.Shell var filename = parts[0]; if (ExistInPath(filename)) { - var arguemtns = parts[1]; - info = new ProcessStartInfo - { - FileName = filename, - Arguments = arguemtns - }; + var arguments = parts[1]; + info = ShellCommand.SetProcessStartInfo(filename, workingDirectory, arguments, runAsAdministratorArg); } else { - info = new ProcessStartInfo(command); + info = ShellCommand.SetProcessStartInfo(command, verb: runAsAdministratorArg); } } else { - info = new ProcessStartInfo(command); + info = ShellCommand.SetProcessStartInfo(command, verb: runAsAdministratorArg); } } else @@ -222,10 +215,7 @@ namespace Wox.Plugin.Shell return; } - info.UseShellExecute = true; - info.WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - info.Verb = runAsAdministrator ? "runas" : ""; try { diff --git a/Plugins/Wox.Plugin.Shell/Settings.cs b/Plugins/Wox.Plugin.Shell/Settings.cs index 62eb31f4e..af149e829 100644 --- a/Plugins/Wox.Plugin.Shell/Settings.cs +++ b/Plugins/Wox.Plugin.Shell/Settings.cs @@ -7,6 +7,8 @@ namespace Wox.Plugin.Shell public Shell Shell { get; set; } = Shell.Cmd; public bool ReplaceWinR { get; set; } = true; public bool LeaveShellOpen { get; set; } + public bool RunAsAdministrator { get; set; } = true; + public Dictionary Count = new Dictionary(); public void AddCmdHistory(string cmdName) diff --git a/Plugins/Wox.Plugin.Shell/ShellSetting.xaml b/Plugins/Wox.Plugin.Shell/ShellSetting.xaml index f631f6e22..dc6de53ba 100644 --- a/Plugins/Wox.Plugin.Shell/ShellSetting.xaml +++ b/Plugins/Wox.Plugin.Shell/ShellSetting.xaml @@ -12,10 +12,12 @@ + - + + CMD PowerShell RunCommand diff --git a/Plugins/Wox.Plugin.Shell/ShellSetting.xaml.cs b/Plugins/Wox.Plugin.Shell/ShellSetting.xaml.cs index 639c5c3c9..ffa3b5856 100644 --- a/Plugins/Wox.Plugin.Shell/ShellSetting.xaml.cs +++ b/Plugins/Wox.Plugin.Shell/ShellSetting.xaml.cs @@ -17,6 +17,7 @@ namespace Wox.Plugin.Shell { ReplaceWinR.IsChecked = _settings.ReplaceWinR; LeaveShellOpen.IsChecked = _settings.LeaveShellOpen; + AlwaysRunAsAdministrator.IsChecked = _settings.RunAsAdministrator; LeaveShellOpen.IsEnabled = _settings.Shell != Shell.RunCommand; LeaveShellOpen.Checked += (o, e) => @@ -29,6 +30,16 @@ namespace Wox.Plugin.Shell _settings.LeaveShellOpen = false; }; + AlwaysRunAsAdministrator.Checked += (o, e) => + { + _settings.RunAsAdministrator = true; + }; + + AlwaysRunAsAdministrator.Unchecked += (o, e) => + { + _settings.RunAsAdministrator = false; + }; + ReplaceWinR.Checked += (o, e) => { _settings.ReplaceWinR = true; diff --git a/Wox.Infrastructure/Logger/Log.cs b/Wox.Infrastructure/Logger/Log.cs index dfa05c715..ff72dff1c 100644 --- a/Wox.Infrastructure/Logger/Log.cs +++ b/Wox.Infrastructure/Logger/Log.cs @@ -87,7 +87,7 @@ namespace Wox.Infrastructure.Logger do { - logger.Error($"Exception fulle name:\n <{e.GetType().FullName}>"); + 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}>"); diff --git a/Wox.Infrastructure/Storage/WoxJsonStorage.cs b/Wox.Infrastructure/Storage/WoxJsonStorage.cs index f117aee22..da0dbd073 100644 --- a/Wox.Infrastructure/Storage/WoxJsonStorage.cs +++ b/Wox.Infrastructure/Storage/WoxJsonStorage.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace Wox.Infrastructure.Storage { - class WoxJsonStorage : JsonStrorage where T : new() + public class WoxJsonStorage : JsonStrorage where T : new() { public WoxJsonStorage() { @@ -18,4 +18,4 @@ namespace Wox.Infrastructure.Storage FilePath = Path.Combine(directoryPath, $"{filename}{FileSuffix}"); } } -} +} \ No newline at end of file diff --git a/Wox.Plugin/Result.cs b/Wox.Plugin/Result.cs index 2f7459170..592bcac5c 100644 --- a/Wox.Plugin/Result.cs +++ b/Wox.Plugin/Result.cs @@ -108,7 +108,7 @@ namespace Wox.Plugin public object ContextData { get; set; } /// - /// Plugin ID that generate this result + /// Plugin ID that generated this result /// public string PluginID { get; internal set; } } diff --git a/Wox.Plugin/SharedCommands/ShellCommand.cs b/Wox.Plugin/SharedCommands/ShellCommand.cs new file mode 100644 index 000000000..d7071e735 --- /dev/null +++ b/Wox.Plugin/SharedCommands/ShellCommand.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wox.Plugin.SharedCommands +{ + public static class ShellCommand + { + public static ProcessStartInfo SetProcessStartInfo(this string fileName, string workingDirectory="", string arguments = "", string verb = "") + { + var info = new ProcessStartInfo + { + FileName = fileName, + WorkingDirectory = workingDirectory, + Arguments = arguments, + Verb = verb + }; + + return info; + } + } +} diff --git a/Wox.Plugin/Wox.Plugin.csproj b/Wox.Plugin/Wox.Plugin.csproj index 843cc7d8b..3a486fb42 100644 --- a/Wox.Plugin/Wox.Plugin.csproj +++ b/Wox.Plugin/Wox.Plugin.csproj @@ -78,6 +78,7 @@ + diff --git a/Wox/MainWindow.xaml b/Wox/MainWindow.xaml index d1e610dfc..7dfe8cd1e 100644 --- a/Wox/MainWindow.xaml +++ b/Wox/MainWindow.xaml @@ -36,6 +36,7 @@ + diff --git a/Wox/Storage/TopMostRecord.cs b/Wox/Storage/TopMostRecord.cs index 4d33f91eb..5f940f218 100644 --- a/Wox/Storage/TopMostRecord.cs +++ b/Wox/Storage/TopMostRecord.cs @@ -1,47 +1,50 @@ using System.Collections.Generic; using System.Linq; -using Wox.Infrastructure.Storage; +using Newtonsoft.Json; using Wox.Plugin; namespace Wox.Storage { + // todo this class is not thread safe.... but used from multiple threads. public class TopMostRecord { - public Dictionary records = new Dictionary(); + [JsonProperty] + private Dictionary records = new Dictionary(); internal bool IsTopMost(Result result) { + if (records.Count == 0) + { + return false; + } + + // since this dictionary should be very small (or empty) going over it should be pretty fast. return records.Any(o => o.Value.Title == result.Title - && o.Value.SubTitle == result.SubTitle - && o.Value.PluginID == result.PluginID - && o.Key == result.OriginQuery.RawQuery); + && o.Value.SubTitle == result.SubTitle + && o.Value.PluginID == result.PluginID + && o.Key == result.OriginQuery.RawQuery); } internal void Remove(Result result) { - if (records.ContainsKey(result.OriginQuery.RawQuery)) - { - records.Remove(result.OriginQuery.RawQuery); - } + records.Remove(result.OriginQuery.RawQuery); } internal void AddOrUpdate(Result result) { - if (records.ContainsKey(result.OriginQuery.RawQuery)) + var record = new Record { - records[result.OriginQuery.RawQuery].Title = result.Title; - records[result.OriginQuery.RawQuery].SubTitle = result.SubTitle; - records[result.OriginQuery.RawQuery].PluginID = result.PluginID; - } - else - { - records.Add(result.OriginQuery.RawQuery, new Record - { - PluginID = result.PluginID, - Title = result.Title, - SubTitle = result.SubTitle - }); - } + PluginID = result.PluginID, + Title = result.Title, + SubTitle = result.SubTitle + }; + records[result.OriginQuery.RawQuery] = record; + + } + + public void Load(Dictionary dictionary) + { + records = dictionary; } } diff --git a/Wox/Storage/UserSelectedRecord.cs b/Wox/Storage/UserSelectedRecord.cs index 900559041..ef8bf7f8e 100644 --- a/Wox/Storage/UserSelectedRecord.cs +++ b/Wox/Storage/UserSelectedRecord.cs @@ -12,21 +12,23 @@ namespace Wox.Storage public void Add(Result result) { - if (records.ContainsKey(result.ToString())) + var key = result.ToString(); + if (records.TryGetValue(key, out int value)) { - records[result.ToString()] += 1; + records[key] = value + 1; } else { - records.Add(result.ToString(), 1); + records.Add(key, 1); + } } public int GetSelectedCount(Result result) { - if (records.ContainsKey(result.ToString())) + if (records.TryGetValue(result.ToString(), out int value)) { - return records[result.ToString()]; + return value; } return 0; } diff --git a/Wox/ViewModel/MainViewModel.cs b/Wox/ViewModel/MainViewModel.cs index 1a12d6c8c..a7e17d9a7 100644 --- a/Wox/ViewModel/MainViewModel.cs +++ b/Wox/ViewModel/MainViewModel.cs @@ -126,6 +126,8 @@ namespace Wox.ViewModel SelectedResults.SelectPrevPage(); }); + SelectFirstResultCommand = new RelayCommand(_ => SelectedResults.SelectFirstResult()); + StartHelpCommand = new RelayCommand(_ => { Process.Start("http://doc.wox.one/"); @@ -268,6 +270,7 @@ namespace Wox.ViewModel public ICommand SelectPrevItemCommand { get; set; } public ICommand SelectNextPageCommand { get; set; } public ICommand SelectPrevPageCommand { get; set; } + public ICommand SelectFirstResultCommand { get; set; } public ICommand StartHelpCommand { get; set; } public ICommand LoadContextMenuCommand { get; set; } public ICommand LoadHistoryCommand { get; set; } @@ -419,6 +422,11 @@ namespace Wox.ViewModel UpdateResultView(results, plugin.Metadata, query); } }); + + // this should happen once after all queries are done so progress bar should continue + // until the end of all querying + _queryHasReturn = true; + ProgressBarVisibility = Visibility.Hidden; }, _updateToken); } } @@ -627,9 +635,6 @@ namespace Wox.ViewModel /// public void UpdateResultView(List list, PluginMetadata metadata, Query originQuery) { - _queryHasReturn = true; - ProgressBarVisibility = Visibility.Hidden; - foreach (var result in list) { if (_topMostRecord.IsTopMost(result)) diff --git a/Wox/ViewModel/ResultsViewModel.cs b/Wox/ViewModel/ResultsViewModel.cs index 674923228..f8197f78e 100644 --- a/Wox/ViewModel/ResultsViewModel.cs +++ b/Wox/ViewModel/ResultsViewModel.cs @@ -107,6 +107,11 @@ namespace Wox.ViewModel SelectedIndex = NewIndex(SelectedIndex - MaxResults); } + public void SelectFirstResult() + { + SelectedIndex = NewIndex(0); + } + public void Clear() { Results.Clear(); @@ -155,7 +160,6 @@ namespace Wox.ViewModel // Find the same results in A (old results) and B (new newResults) var sameResults = oldResults .Where(t1 => newResults.Any(x => x.Result.Equals(t1.Result))) - .Select(t1 => t1) .ToList(); // remove result of relative complement of B in A