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  }