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.Diagnostics; 6 using System.Diagnostics.CodeAnalysis; 7 using System.Runtime.InteropServices; 8 9 using LazyCache; 10 using ManagedCommon; 11 using Microsoft.PowerToys.Run.Plugin.OneNote.Properties; 12 using ScipBe.Common.Office.OneNote; 13 using Windows.Win32; 14 using Windows.Win32.Foundation; 15 using Windows.Win32.UI.WindowsAndMessaging; 16 using Wox.Plugin; 17 18 namespace Microsoft.PowerToys.Run.Plugin.OneNote 19 { 20 /// <summary> 21 /// A power launcher plugin to search across time zones. 22 /// </summary> 23 public class Main : IPlugin, IDelayedExecutionPlugin, IPluginI18n 24 { 25 /// <summary> 26 /// A value indicating if the OneNote interop library was able to successfully initialize. 27 /// </summary> 28 private bool _oneNoteInstalled; 29 30 /// <summary> 31 /// LazyCache CachingService instance to speed up repeated queries. 32 /// </summary> 33 private CachingService? _cache; 34 35 /// <summary> 36 /// The initial context for this plugin (contains API and meta-data) 37 /// </summary> 38 private PluginInitContext? _context; 39 40 /// <summary> 41 /// The path to the icon for each result 42 /// </summary> 43 private string _iconPath; 44 45 /// <summary> 46 /// Initializes a new instance of the <see cref="Main"/> class. 47 /// </summary> 48 public Main() 49 { 50 UpdateIconPath(Theme.Light); 51 } 52 53 /// <summary> 54 /// Gets the localized name. 55 /// </summary> 56 public string Name => Resources.PluginTitle; 57 58 /// <summary> 59 /// Gets the localized description. 60 /// </summary> 61 public string Description => Resources.PluginDescription; 62 63 /// <summary> 64 /// Gets the plugin ID for validation 65 /// </summary> 66 public static string PluginID => "0778F0C264114FEC8A3DF59447CF0A74"; 67 68 /// <summary> 69 /// Initialize the plugin with the given <see cref="PluginInitContext"/> 70 /// </summary> 71 /// <param name="context">The <see cref="PluginInitContext"/> for this plugin</param> 72 public void Init(PluginInitContext context) 73 { 74 _context = context ?? throw new ArgumentNullException(nameof(context)); 75 76 try 77 { 78 _ = OneNoteProvider.PageItems.Any(); 79 _oneNoteInstalled = true; 80 81 _cache = new CachingService(); 82 _cache.DefaultCachePolicy.DefaultCacheDurationSeconds = (int)TimeSpan.FromDays(1).TotalSeconds; 83 } 84 catch (COMException) 85 { 86 // OneNote isn't installed, plugin won't do anything. 87 _oneNoteInstalled = false; 88 } 89 90 _context.API.ThemeChanged += OnThemeChanged; 91 UpdateIconPath(_context.API.GetCurrentTheme()); 92 } 93 94 /// <summary> 95 /// Return a filtered list, based on the given query 96 /// </summary> 97 /// <param name="query">The query to filter the list</param> 98 /// <returns>A filtered list, can be empty when nothing was found</returns> 99 public List<Result> Query(Query query) 100 { 101 if (!_oneNoteInstalled || query is null || string.IsNullOrWhiteSpace(query.Search) || _cache is null) 102 { 103 return new List<Result>(0); 104 } 105 106 // If there's cached results for this query, return immediately; otherwise, wait for delayedExecution. 107 var results = _cache.Get<List<Result>>(query.Search); 108 return results ?? Query(query, false); 109 } 110 111 /// <summary> 112 /// Return a filtered list, based on the given query 113 /// </summary> 114 /// <param name="query">The query to filter the list</param> 115 /// <param name="delayedExecution">False if this is the first pass through plugins, true otherwise. Slow plugins should run delayed.</param> 116 /// <returns>A filtered list, can be empty when nothing was found</returns> 117 public List<Result> Query(Query query, bool delayedExecution) 118 { 119 if (!delayedExecution || !_oneNoteInstalled || query is null || string.IsNullOrWhiteSpace(query.Search) || _cache is null) 120 { 121 return new List<Result>(0); 122 } 123 124 // Get results from cache if they already exist for this query; otherwise, query OneNote. Results will be cached for 1 day. 125 var results = _cache.GetOrAdd(query.Search, () => 126 { 127 var pages = OneNoteProvider.FindPages(query.Search); 128 129 return pages.Select(p => new Result 130 { 131 IcoPath = _iconPath, 132 Title = p.Name, 133 QueryTextDisplay = p.Name, 134 SubTitle = @$"{p.Notebook.Name}\{p.Section.Name}", 135 Action = (_) => OpenPageInOneNote(p), 136 ContextData = p, 137 ToolTipData = new ToolTipData(Name, @$"{p.Notebook.Name}\{p.Section.Name}\{p.Name}"), 138 }).ToList(); 139 }); 140 141 return results; 142 } 143 144 /// <summary> 145 /// Return the translated plugin title. 146 /// </summary> 147 /// <returns>A translated plugin title.</returns> 148 public string GetTranslatedPluginTitle() => Resources.PluginTitle; 149 150 /// <summary> 151 /// Return the translated plugin description. 152 /// </summary> 153 /// <returns>A translated plugin description.</returns> 154 public string GetTranslatedPluginDescription() => Resources.PluginDescription; 155 156 private void OnThemeChanged(Theme currentTheme, Theme newTheme) 157 { 158 UpdateIconPath(newTheme); 159 } 160 161 [MemberNotNull(nameof(_iconPath))] 162 private void UpdateIconPath(Theme theme) 163 { 164 _iconPath = theme == Theme.Light || theme == Theme.HighContrastWhite ? "Images/oneNote.light.png" : "Images/oneNote.dark.png"; 165 } 166 167 private bool OpenPageInOneNote(IOneNoteExtPage page) 168 { 169 try 170 { 171 page.OpenInOneNote(); 172 ShowOneNote(); 173 return true; 174 } 175 catch (COMException) 176 { 177 // The page, section or even notebook may no longer exist, ignore and do nothing. 178 return false; 179 } 180 } 181 182 /// <summary> 183 /// Brings OneNote to the foreground and restores it if minimized. 184 /// </summary> 185 private void ShowOneNote() 186 { 187 using var process = Process.GetProcessesByName("onenote").FirstOrDefault(); 188 if (process?.MainWindowHandle != null) 189 { 190 HWND handle = (HWND)process.MainWindowHandle; 191 if (PInvoke.IsIconic(handle)) 192 { 193 PInvoke.ShowWindow(handle, SHOW_WINDOW_CMD.SW_RESTORE); 194 } 195 196 PInvoke.SetForegroundWindow(handle); 197 } 198 } 199 } 200 }