Main.cs
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Collections.Generic; 7 using System.Diagnostics; 8 using System.Linq; 9 using System.Threading.Tasks; 10 11 using ManagedCommon; 12 using Microsoft.Plugin.Program.ProgramArgumentParser; 13 using Microsoft.Plugin.Program.Programs; 14 using Microsoft.Plugin.Program.Storage; 15 using Wox.Infrastructure.Storage; 16 using Wox.Plugin; 17 using Wox.Plugin.Common; 18 19 using Stopwatch = Wox.Infrastructure.Stopwatch; 20 21 namespace Microsoft.Plugin.Program 22 { 23 public class Main : IPlugin, IPluginI18n, IContextMenu, ISavable, IDisposable 24 { 25 // The order of this array is important! The Parsers will be checked in order (index 0 to index Length-1) and the first parser which is able to parse the Query will be used 26 // NoArgumentsArgumentParser does always succeed and therefore should always be last/fallback 27 private static readonly IProgramArgumentParser[] _programArgumentParsers = new IProgramArgumentParser[] 28 { 29 new DoubleDashProgramArgumentParser(), 30 new InferredProgramArgumentParser(), 31 new NoArgumentsArgumentParser(), 32 }; 33 34 internal static ProgramPluginSettings Settings { get; set; } 35 36 internal static readonly ShellLocalization ShellLocalizationHelper = new(); 37 38 public string Name => Properties.Resources.wox_plugin_program_plugin_name; 39 40 public string Description => Properties.Resources.wox_plugin_program_plugin_description; 41 42 public static string PluginID => "791FC278BA414111B8D1886DFE447410"; 43 44 private static PluginInitContext _context; 45 private readonly PluginJsonStorage<ProgramPluginSettings> _settingsStorage; 46 private bool _disposed; 47 private PackageRepository _packageRepository; 48 private static Win32ProgramFileSystemWatchers _win32ProgramRepositoryHelper; 49 private static Win32ProgramRepository _win32ProgramRepository; 50 51 public Main() 52 { 53 _settingsStorage = new PluginJsonStorage<ProgramPluginSettings>(); 54 Settings = _settingsStorage.Load(); 55 56 // This helper class initializes the file system watchers based on the locations to watch 57 _win32ProgramRepositoryHelper = new Win32ProgramFileSystemWatchers(); 58 59 // Initialize the Win32ProgramRepository with the settings object 60 _win32ProgramRepository = new Win32ProgramRepository(_win32ProgramRepositoryHelper.FileSystemWatchers.Cast<IFileSystemWatcherWrapper>().ToList(), Settings, _win32ProgramRepositoryHelper.PathsToWatch); 61 } 62 63 public void Save() 64 { 65 _settingsStorage.Save(); 66 } 67 68 public List<Result> Query(Query query) 69 { 70 var sources = _programArgumentParsers 71 .Where(programArgumentParser => programArgumentParser.Enabled); 72 73 foreach (var programArgumentParser in sources) 74 { 75 if (!programArgumentParser.TryParse(query, out var program, out var programArguments)) 76 { 77 continue; 78 } 79 80 return Query(program, programArguments).ToList(); 81 } 82 83 return new List<Result>(0); 84 } 85 86 private IEnumerable<Result> Query(string program, string programArguments) 87 { 88 var result = _win32ProgramRepository 89 .Concat<IProgram>(_packageRepository) 90 .AsParallel() 91 .Where(p => p.Enabled) 92 .Select(p => p.Result(program, programArguments, _context.API)) 93 .Where(r => r?.Score > 0) 94 .ToArray(); 95 96 if (result.Length != 0) 97 { 98 var maxScore = result.Max(x => x.Score); 99 return result 100 .Where(x => x.Score > Settings.MinScoreThreshold * maxScore); 101 } 102 103 return Enumerable.Empty<Result>(); 104 } 105 106 public void Init(PluginInitContext context) 107 { 108 _context = context ?? throw new ArgumentNullException(nameof(context)); 109 _context.API.ThemeChanged += OnThemeChanged; 110 _packageRepository = new PackageRepository(new PackageCatalogWrapper(), _context); 111 112 var a = Task.Run(() => 113 { 114 Stopwatch.Normal("Microsoft.Plugin.Program.Main - Win32Program index cost", _win32ProgramRepository.IndexPrograms); 115 }); 116 117 var b = Task.Run(() => 118 { 119 Stopwatch.Normal("Microsoft.Plugin.Program.Main - Package index cost", _packageRepository.IndexPrograms); 120 UpdateUWPIconPath(_context.API.GetCurrentTheme()); 121 }); 122 123 Task.WaitAll(a, b); 124 125 Settings.LastIndexTime = DateTime.Today; 126 } 127 128 public void OnThemeChanged(Theme currentTheme, Theme newTheme) 129 { 130 UpdateUWPIconPath(newTheme); 131 } 132 133 public void UpdateUWPIconPath(Theme theme) 134 { 135 if (_packageRepository != null) 136 { 137 foreach (UWPApplication app in _packageRepository) 138 { 139 app.UpdateLogoPath(theme); 140 } 141 } 142 } 143 144 public void IndexPrograms() 145 { 146 var t1 = Task.Run(() => _win32ProgramRepository.IndexPrograms()); 147 var t2 = Task.Run(() => _packageRepository.IndexPrograms()); 148 149 Task.WaitAll(t1, t2); 150 151 Settings.LastIndexTime = DateTime.Today; 152 } 153 154 public string GetTranslatedPluginTitle() 155 { 156 return Properties.Resources.wox_plugin_program_plugin_name; 157 } 158 159 public string GetTranslatedPluginDescription() 160 { 161 return Properties.Resources.wox_plugin_program_plugin_description; 162 } 163 164 public List<ContextMenuResult> LoadContextMenus(Result selectedResult) 165 { 166 ArgumentNullException.ThrowIfNull(selectedResult); 167 168 var menuOptions = new List<ContextMenuResult>(); 169 if (selectedResult.ContextData is IProgram program) 170 { 171 menuOptions = program.ContextMenus(selectedResult.ProgramArguments, _context.API); 172 } 173 174 return menuOptions; 175 } 176 177 public static void StartProcess(Func<ProcessStartInfo, Process> runProcess, ProcessStartInfo info) 178 { 179 try 180 { 181 ArgumentNullException.ThrowIfNull(runProcess); 182 183 ArgumentNullException.ThrowIfNull(info); 184 185 runProcess(info); 186 } 187 catch (Exception ex) 188 { 189 Logger.ProgramLogger.Exception($"Unable to start ", ex, System.Reflection.MethodBase.GetCurrentMethod().DeclaringType, info?.FileName); 190 var name = "Plugin: " + Properties.Resources.wox_plugin_program_plugin_name; 191 var message = $"{Properties.Resources.powertoys_run_plugin_program_start_failed}: {info?.FileName}"; 192 _context.API.ShowMsg(name, message, string.Empty); 193 } 194 } 195 196 public void Dispose() 197 { 198 Dispose(disposing: true); 199 GC.SuppressFinalize(this); 200 } 201 202 protected virtual void Dispose(bool disposing) 203 { 204 if (!_disposed) 205 { 206 if (disposing) 207 { 208 if (_context != null && _context.API != null) 209 { 210 _context.API.ThemeChanged -= OnThemeChanged; 211 } 212 213 _win32ProgramRepositoryHelper?.Dispose(); 214 _disposed = true; 215 } 216 } 217 } 218 } 219 }