diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 1e7abbb5b3..3fe8f316ab 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -347,6 +347,7 @@ cxfksword CXSMICON CXVIRTUALSCREEN cxxopts +cyberrex CYSMICON CYVIRTUALSCREEN czf @@ -589,6 +590,7 @@ Filterx finalizer findfast findstr +Firefox FIXEDFILEINFO FLASHZONES FLASHZONESONQUICKSWITCH @@ -1796,6 +1798,7 @@ somil Soref SOURCECLIENTAREAONLY SOURCEHEADER +sourceid sourcesdirectory spamming spdisp diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json index 8370f8e07a..6a2fdfee12 100644 --- a/.pipelines/ESRPSigning_core.json +++ b/.pipelines/ESRPSigning_core.json @@ -88,6 +88,7 @@ "modules\\launcher\\Plugins\\VSCodeWorkspaces\\Community.PowerToys.Run.Plugin.VSCodeWorkspaces.dll", "modules\\launcher\\Plugins\\Service\\Microsoft.PowerToys.Run.Plugin.Service.dll", "modules\\launcher\\Plugins\\System\\Microsoft.PowerToys.Run.Plugin.System.dll", + "modules\\launcher\\Plugins\\WebSearch\\Community.PowerToys.Run.Plugin.WebSearch.dll", "modules\\launcher\\Plugins\\WindowsTerminal\\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.dll", "modules\\MouseUtils\\PowerToys.FindMyMouse.dll", diff --git a/.pipelines/pipeline.user.windows.yml b/.pipelines/pipeline.user.windows.yml index a9e570abb0..683d859203 100644 --- a/.pipelines/pipeline.user.windows.yml +++ b/.pipelines/pipeline.user.windows.yml @@ -161,6 +161,7 @@ build: - 'modules\launcher\Plugins\WindowWalker\PowerToys.ManagedTelemetry.dll' - 'modules\launcher\Plugins\UnitConverter\Community.PowerToys.Run.Plugin.UnitConverter.dll' - 'modules\launcher\Plugins\VSCodeWorkspaces\Community.PowerToys.Run.Plugin.VSCodeWorkspaces.dll' + - 'modules\launcher\Plugins\WebSearch\Community.PowerToys.Run.Plugin.WebSearch.dll' - 'modules\launcher\Plugins\Service\Microsoft.PowerToys.Run.Plugin.Service.dll' - 'modules\launcher\Plugins\System\Microsoft.PowerToys.Run.Plugin.System.dll' - 'modules\launcher\Plugins\WindowsTerminal\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.dll' diff --git a/PowerToys.sln b/PowerToys.sln index 29d7382265..209b6e4af4 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -129,6 +129,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Launcher", "src\m EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerLauncher", "src\modules\launcher\PowerLauncher\PowerLauncher.csproj", "{F97E5003-F263-4D4A-A964-0F1F3C82DEF2}" ProjectSection(ProjectDependencies) = postProject + {9F94B303-5E21-4364-9362-64426F8DB932} = {9F94B303-5E21-4364-9362-64426F8DB932} {FD8EB419-FF9C-4D88-BB6F-BF6CED37747B} = {FD8EB419-FF9C-4D88-BB6F-BF6CED37747B} {03276A39-D4E9-417C-8FFD-200B0EE5E871} = {03276A39-D4E9-417C-8FFD-200B0EE5E871} {4D971245-7A70-41D5-BAA0-DDB5684CAF51} = {4D971245-7A70-41D5-BAA0-DDB5684CAF51} @@ -382,6 +383,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GcodePreviewHandler", "src\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests-GcodePreviewHandler", "src\modules\previewpane\UnitTests-GcodePreviewHandler\UnitTests-GcodePreviewHandler.csproj", "{FCF3E52D-B80A-4FC3-98FD-6391354F0EE3}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Community.PowerToys.Run.Plugin.WebSearch", "src\modules\launcher\Plugins\Community.PowerToys.Run.Plugin.WebSearch\Community.PowerToys.Run.Plugin.WebSearch.csproj", "{9F94B303-5E21-4364-9362-64426F8DB932}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -1027,6 +1030,12 @@ Global {FCF3E52D-B80A-4FC3-98FD-6391354F0EE3}.Release|x64.ActiveCfg = Release|x64 {FCF3E52D-B80A-4FC3-98FD-6391354F0EE3}.Release|x64.Build.0 = Release|x64 {FCF3E52D-B80A-4FC3-98FD-6391354F0EE3}.Release|x86.ActiveCfg = Release|x64 + {9F94B303-5E21-4364-9362-64426F8DB932}.Debug|x64.ActiveCfg = Debug|x64 + {9F94B303-5E21-4364-9362-64426F8DB932}.Debug|x64.Build.0 = Debug|x64 + {9F94B303-5E21-4364-9362-64426F8DB932}.Debug|x86.ActiveCfg = Debug|x64 + {9F94B303-5E21-4364-9362-64426F8DB932}.Release|x64.ActiveCfg = Release|x64 + {9F94B303-5E21-4364-9362-64426F8DB932}.Release|x64.Build.0 = Release|x64 + {9F94B303-5E21-4364-9362-64426F8DB932}.Release|x86.ActiveCfg = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1151,6 +1160,7 @@ Global {133281D8-1BCE-4D07-B31E-796612A9609E} = {2F305555-C296-497E-AC20-5FA1B237996A} {805306FF-A562-4415-8DEF-E493BDC45918} = {2F305555-C296-497E-AC20-5FA1B237996A} {FCF3E52D-B80A-4FC3-98FD-6391354F0EE3} = {2F305555-C296-497E-AC20-5FA1B237996A} + {9F94B303-5E21-4364-9362-64426F8DB932} = {4AFC9975-2456-4C70-94A4-84073C1CED93} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0} diff --git a/doc/devdocs/modules/launcher/plugins/websearch.md b/doc/devdocs/modules/launcher/plugins/websearch.md new file mode 100644 index 0000000000..a011bdfb71 --- /dev/null +++ b/doc/devdocs/modules/launcher/plugins/websearch.md @@ -0,0 +1,11 @@ +## Web Search Plugin +The Web Search Plugin, as the name suggests, is used to perform a web search - in the default search engine in the default browser - on the query that has been entered by the user. + +![Image of Web Search plugin](/doc/images/launcher/plugins/WebSearch.png) + +## Default Browser Icon +- The icon for each web search result is that of the default browser set by the user. +- It, and the browser path, are obtained from the user registry and updated each time the theme of PT Run is changed. + +## Score +- The web search result always has a score of 0 which indicates that it would show up after each of the other plugins, other than the indexer plugin and possibly the uri plugin which both have a score of 0. \ No newline at end of file diff --git a/doc/devdocs/modules/launcher/readme.md b/doc/devdocs/modules/launcher/readme.md index 69af0edc46..9ca90366c3 100644 --- a/doc/devdocs/modules/launcher/readme.md +++ b/doc/devdocs/modules/launcher/readme.md @@ -14,3 +14,4 @@ - [Sys](/doc/devdocs/modules/launcher/plugins/sys.md) - [Uri](/doc/devdocs/modules/launcher/plugins/uri.md) - [Window Walker](/doc/devdocs/modules/launcher/plugins/windowwalker.md) + - [Web Search](/doc/devdocs/modules/launcher/plugins/WebSearch.md) diff --git a/doc/images/launcher/plugins/WebSearch.png b/doc/images/launcher/plugins/WebSearch.png new file mode 100644 index 0000000000..da94fd71ce Binary files /dev/null and b/doc/images/launcher/plugins/WebSearch.png differ diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 28d5777612..8df7b554a8 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -312,6 +312,10 @@ + + + + @@ -1008,7 +1012,7 @@ - + @@ -1278,6 +1282,17 @@ + + + + + + + + + + + diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Community.PowerToys.Run.Plugin.WebSearch.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Community.PowerToys.Run.Plugin.WebSearch.csproj new file mode 100644 index 0000000000..94f6e67606 --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Community.PowerToys.Run.Plugin.WebSearch.csproj @@ -0,0 +1,106 @@ + + + + + netcoreapp3.1 + {9F94B303-5E21-4364-9362-64426F8DB932} + Properties + Community.PowerToys.Run.Plugin.WebSearch + Community.PowerToys.Run.Plugin.WebSearch + $(Version).0 + true + false + false + x64 + en-US + + + + true + ..\..\..\..\..\x64\Debug\modules\launcher\Plugins\WebSearch\ + DEBUG;TRACE + full + x64 + 7.3 + prompt + MinimumRecommendedRules.ruleset + 4 + false + true + + + + ..\..\..\..\..\x64\Release\modules\launcher\Plugins\WebSearch\ + TRACE + true + pdbonly + x64 + 7.3 + prompt + MinimumRecommendedRules.ruleset + 4 + true + + + + + GlobalSuppressions.cs + + + StyleCop.json + + + + + + false + + + false + + + + + + PreserveNewest + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + True + True + Resources.resx + + + + + + PublicResXFileCodeGenerator + Resources.Designer.cs + + + + + + PreserveNewest + + + PreserveNewest + + + + diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Images/WebSearch.dark.png b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Images/WebSearch.dark.png new file mode 100644 index 0000000000..9f8f033814 Binary files /dev/null and b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Images/WebSearch.dark.png differ diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Images/WebSearch.light.png b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Images/WebSearch.light.png new file mode 100644 index 0000000000..d376742c89 Binary files /dev/null and b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Images/WebSearch.light.png differ diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/LocProject.json b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/LocProject.json new file mode 100644 index 0000000000..2d308675e9 --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/LocProject.json @@ -0,0 +1,14 @@ +{ + "Projects": [ + { + "LanguageSet": "Azure_Languages", + "LocItems": [ + { + "SourceFile": "src\\modules\\launcher\\Plugins\\Community.PowerToys.Run.Plugin.WebSearch\\Properties\\Resources.resx", + "CopyOption": "LangIDOnName", + "OutputPath": "src\\modules\\launcher\\Plugins\\Community.PowerToys.Run.Plugin.WebSearch\\Properties" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Main.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Main.cs new file mode 100644 index 0000000000..e99a3ba4e6 --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Main.cs @@ -0,0 +1,430 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.IO.Abstractions; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using ManagedCommon; +using Microsoft.PowerToys.Settings.UI.Library; +using Wox.Infrastructure; +using Wox.Plugin; +using Wox.Plugin.Logger; + +namespace Community.PowerToys.Run.Plugin.WebSearch +{ + public class Main : IPlugin, IPluginI18n, IContextMenu, ISettingProvider, IDisposable + { + private static readonly IFileSystem FileSystem = new FileSystem(); + private static readonly IPath Path = FileSystem.Path; + private static readonly IFile File = FileSystem.File; + + private const string NotGlobalIfUri = nameof(NotGlobalIfUri); + + /// If true, dont show global result on queries that are URIs + private bool _notGlobalIfUri; + + private PluginInitContext _context; + + private string _searchEngineUrl; + + private string _browserName = Properties.Resources.plugin_browser; + private string _browserIconPath; + private string _browserPath; + private string _defaultIconPath; + + private bool _disposed; + + public string Name => Properties.Resources.plugin_name; + + public string Description => Properties.Resources.plugin_description; + + public IEnumerable AdditionalOptions => new List() + { + new PluginAdditionalOption() + { + Key = NotGlobalIfUri, + DisplayLabel = Properties.Resources.plugin_global_if_uri, + Value = false, + }, + }; + + public List LoadContextMenus(Result selectedResult) + { + return new List(0); + } + + public List Query(Query query) + { + if (query is null) + { + throw new ArgumentNullException(nameof(query)); + } + + var results = new List(); + + if (!AreResultsGlobal() + && query.ActionKeyword == query.RawQuery + && IsDefaultBrowserSet()) + { + string arguments = "\"? \""; + results.Add(new Result + { + Title = Properties.Resources.plugin_description.Remove(Description.Length - 1, 1), + SubTitle = Properties.Resources.plugin_in_browser, + QueryTextDisplay = string.Empty, + IcoPath = _defaultIconPath, + ProgramArguments = arguments, + Action = action => + { + if (!Helper.OpenInShell(_browserPath, arguments)) + { + _context.API.ShowMsg( + $"Plugin: {Properties.Resources.plugin_name}", + $"{Properties.Resources.plugin_search_failed}: "); + return false; + } + + return true; + }, + }); + return results; + } + + if (!string.IsNullOrEmpty(query.Search)) + { + string searchTerm = query.Search; + + // Don't include in global results if the query is a URI (and if the option NotGlobalIfUri is enabled) + if (_notGlobalIfUri + && AreResultsGlobal() + && IsURI(searchTerm)) + { + return results; + } + + var result = new Result + { + Title = searchTerm, + SubTitle = string.Format(System.Globalization.CultureInfo.CurrentCulture, Properties.Resources.plugin_open, _browserName), + QueryTextDisplay = searchTerm, + IcoPath = _defaultIconPath, + }; + + if (_searchEngineUrl is null) + { + string arguments = $"\"? {searchTerm}\""; + + result.ProgramArguments = arguments; + result.Action = action => + { + if (!Helper.OpenInShell(_browserPath, arguments)) + { + _context.API.ShowMsg( + $"Plugin: {Properties.Resources.plugin_name}", + $"{Properties.Resources.plugin_search_failed}: "); + return false; + } + + return true; + }; + } + else + { + string url = string.Format(System.Globalization.CultureInfo.InvariantCulture, _searchEngineUrl, searchTerm); + + result.Action = action => + { + if (!Helper.OpenInShell(url)) + { + _context.API.ShowMsg( + $"Plugin: {Properties.Resources.plugin_name}", + $"{Properties.Resources.plugin_search_failed}: "); + return false; + } + + return true; + }; + } + + results.Add(result); + } + + return results; + + bool AreResultsGlobal() + { + return string.IsNullOrEmpty(query.ActionKeyword); + } + + // Checks if input is a URI the same way Microsoft.Plugin.Uri.UriHelper.ExtendedUriParser does + bool IsURI(string input) + { + if (input.EndsWith(":", StringComparison.OrdinalIgnoreCase) + && !input.StartsWith("http", StringComparison.OrdinalIgnoreCase) + && !input.Contains("/", StringComparison.OrdinalIgnoreCase) + && !input.All(char.IsDigit)) + { + return true; + } + + if (input.EndsWith(":", StringComparison.CurrentCulture) + || input.EndsWith(".", StringComparison.CurrentCulture) + || input.EndsWith(":/", StringComparison.CurrentCulture) + || input.EndsWith("://", StringComparison.CurrentCulture) + || input.All(char.IsDigit)) + { + return false; + } + + try + { + _ = new UriBuilder(input); + } + catch (UriFormatException) + { + return false; + } + + return true; + } + } + + private bool IsDefaultBrowserSet() + { + return !string.IsNullOrEmpty(_browserPath); + } + + public void Init(PluginInitContext context) + { + _context = context ?? throw new ArgumentNullException(nameof(context)); + _context.API.ThemeChanged += OnThemeChanged; + UpdateIconPath(_context.API.GetCurrentTheme()); + UpdateBrowserIconPath(_context.API.GetCurrentTheme()); + } + + public string GetTranslatedPluginTitle() + { + return Properties.Resources.plugin_name; + } + + public string GetTranslatedPluginDescription() + { + return Properties.Resources.plugin_description; + } + + private void OnThemeChanged(Theme oldtheme, Theme newTheme) + { + UpdateIconPath(newTheme); + UpdateBrowserIconPath(newTheme); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Design", + "CA1031:Do not catch general exception types", + Justification = "We want to keep the process alive but will log the exception")] + private void UpdateBrowserIconPath(Theme newTheme) + { + try + { + string progId = GetRegistryValue( + "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice", + "ProgId"); + + // The `?` argument doesn't work on opera, so we get the user's default search engine: + if (progId.StartsWith("Opera", StringComparison.OrdinalIgnoreCase)) + { + // Opera user preferences file: + string prefFile; + + if (progId.Contains("GX", StringComparison.OrdinalIgnoreCase)) + { + _browserName = "Opera GX"; + prefFile = System.IO.File.ReadAllText($"{Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)}\\Opera Software\\Opera GX Stable\\Preferences"); + } + else + { + _browserName = "Opera"; + prefFile = System.IO.File.ReadAllText($"{Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)}\\Opera Software\\Opera Stable\\Preferences"); + } + + // "default_search_provider_data" doesn't exist if the user hasn't searched for the first time, + // therefore we set `url` to opera's default search engine: + string url = "https://www.google.com/search?client=opera&q={0}&sourceid=opera"; + + using (System.Text.Json.JsonDocument doc = System.Text.Json.JsonDocument.Parse(prefFile)) + { + if (doc.RootElement.TryGetProperty("default_search_provider_data", out var element)) + { + if (element.TryGetProperty("template_url_data", out element)) + { + if (element.TryGetProperty("url", out element)) + { + url = element.GetString(); + } + } + } + } + + url = url + .Replace("{searchTerms}", "{0}", StringComparison.Ordinal) + .Replace("{inputEncoding}", "UTF-8", StringComparison.Ordinal) + .Replace("{outputEncoding}", "UTF-8", StringComparison.Ordinal); + + int startIndex = url.IndexOf('}', StringComparison.Ordinal) + 1; + + // In case there are other url parameters (e.g. `&foo={bar}`), remove them: + for (int i = url.IndexOf("}", startIndex, StringComparison.Ordinal); + i != -1; + i = url.IndexOf("}", startIndex, StringComparison.Ordinal)) + { + for (int j = i - 1; j > 0; --j) + { + if (url[j] == '&') + { + url = url.Remove(j, i - j + 1); + break; + } + } + } + + _searchEngineUrl = url; + } + else + { + string appName = GetRegistryValue($"HKEY_CLASSES_ROOT\\{progId}\\Application", "ApplicationName") + ?? GetRegistryValue($"HKEY_CLASSES_ROOT\\{progId}", "FriendlyTypeName"); + + if (appName is null) + { + appName = Properties.Resources.plugin_browser; + } + else + { + // Handle indirect strings: + if (appName.StartsWith("@", StringComparison.Ordinal)) + { + appName = GetIndirectString(appName); + } + + appName = appName + .Replace("URL", null, StringComparison.OrdinalIgnoreCase) + .Replace("HTML", null, StringComparison.OrdinalIgnoreCase) + .Replace("Document", null, StringComparison.OrdinalIgnoreCase) + .TrimEnd(); + } + + _browserName = appName; + + _searchEngineUrl = null; + } + + var programLocation = + + // Resolve App Icon (UWP) + GetRegistryValue( + "HKEY_CLASSES_ROOT\\" + progId + "\\Application", + "ApplicationIcon") + + // Resolves default file association icon (UWP + Normal) + ?? GetRegistryValue("HKEY_CLASSES_ROOT\\" + progId + "\\DefaultIcon", null); + + // "Handles 'Indirect Strings' (UWP programs)" + // Using Ordinal since this is internal and used with a symbol + if (programLocation.StartsWith("@", StringComparison.Ordinal)) + { + // Check if there's a postfix with contract-white/contrast-black icon is available and use that instead + string directProgramLocation = GetIndirectString(programLocation); + var themeIcon = newTheme == Theme.Light || newTheme == Theme.HighContrastWhite + ? "contrast-white" + : "contrast-black"; + var extension = Path.GetExtension(directProgramLocation); + var themedProgLocation = + $"{directProgramLocation.Substring(0, directProgramLocation.Length - extension.Length)}_{themeIcon}{extension}"; + _browserIconPath = File.Exists(themedProgLocation) + ? themedProgLocation + : directProgramLocation; + } + else + { + // Using Ordinal since this is internal and used with a symbol + var indexOfComma = programLocation.IndexOf(',', StringComparison.Ordinal); + _browserIconPath = indexOfComma > 0 + ? programLocation.Substring(0, indexOfComma) + : programLocation; + _browserPath = _browserIconPath; + } + } + catch (Exception e) + { + _browserIconPath = _defaultIconPath; + Log.Exception("Exception when retrieving icon", e, GetType()); + } + + string GetRegistryValue(string registryLocation, string valueName) + { + return Microsoft.Win32.Registry.GetValue(registryLocation, valueName, null) as string; + } + + string GetIndirectString(string str) + { + var stringBuilder = new StringBuilder(128); + if (NativeMethods.SHLoadIndirectString( + str, + stringBuilder, + (uint)stringBuilder.Capacity, + IntPtr.Zero) + == NativeMethods.Hresult.Ok) + { + return stringBuilder.ToString(); + } + + throw new Exception("Could not load indirect string."); + } + } + + private void UpdateIconPath(Theme theme) + { + if (theme == Theme.Light || theme == Theme.HighContrastWhite) + { + _defaultIconPath = "Images/WebSearch.light.png"; + } + else + { + _defaultIconPath = "Images/WebSearch.dark.png"; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed && disposing) + { + if (_context != null && _context.API != null) + { + _context.API.ThemeChanged -= OnThemeChanged; + } + + _disposed = true; + } + } + + public Control CreateSettingPanel() + { + throw new NotImplementedException(); + } + + public void UpdateSettings(PowerLauncherPluginSettings settings) + { + _notGlobalIfUri = settings?.AdditionalOptions?.FirstOrDefault(x => x.Key == NotGlobalIfUri)?.Value ?? false; + } + } +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/NativeMethods.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/NativeMethods.cs new file mode 100644 index 0000000000..2faa79df9d --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/NativeMethods.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace Community.PowerToys.Run.Plugin.WebSearch +{ + internal static class NativeMethods + { + internal enum Hresult : uint + { + Ok = 0x0000, + } + + [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] + internal static extern Hresult SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf, IntPtr ppvReserved); + } +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Properties/Resources.Designer.cs new file mode 100644 index 0000000000..6e48115836 --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Properties/Resources.Designer.cs @@ -0,0 +1,126 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Community.PowerToys.Run.Plugin.WebSearch.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Community.PowerToys.Run.Plugin.WebSearch.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to the default browser. + /// + public static string plugin_browser { + get { + return ResourceManager.GetString("plugin_browser", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Search the web.. + /// + public static string plugin_description { + get { + return ResourceManager.GetString("plugin_description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Don't include in global results on queries that are URIs. + /// + public static string plugin_global_if_uri { + get { + return ResourceManager.GetString("plugin_global_if_uri", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to In the default browser. + /// + public static string plugin_in_browser { + get { + return ResourceManager.GetString("plugin_in_browser", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Web Search. + /// + public static string plugin_name { + get { + return ResourceManager.GetString("plugin_name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Search the web in {0}. + /// + public static string plugin_open { + get { + return ResourceManager.GetString("plugin_open", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to open the default browser. + /// + public static string plugin_search_failed { + get { + return ResourceManager.GetString("plugin_search_failed", resourceCulture); + } + } + } +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Properties/Resources.resx b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Properties/Resources.resx new file mode 100644 index 0000000000..5899780264 --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Properties/Resources.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + the default browser + + + Search the web. + + + Don't include in global results on queries that are URIs + + + In the default browser + + + Web Search + + + Search the web in {0} + + + Failed to open the default browser + + \ No newline at end of file diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/plugin.json b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/plugin.json new file mode 100644 index 0000000000..6a751c6071 --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/plugin.json @@ -0,0 +1,13 @@ +{ + "ID": "9F1B49201C3F4BF781CAAD5CD88EA4DC", + "ActionKeyword": "??", + "IsGlobal": true, + "Name": "Web Search", + "Author": "cyberrex5", + "Version": "1.0.0", + "Language": "csharp", + "Website": "https://aka.ms/powertoys", + "ExecuteFileName": "Community.PowerToys.Run.Plugin.WebSearch.dll", + "IcoPathDark": "Images\\WebSearch.dark.png", + "IcoPathLight": "Images\\WebSearch.light.png" +} \ No newline at end of file