From 3da6732b1c4904daa8892c2fa18ba8838f0b6972 Mon Sep 17 00:00:00 2001 From: udit3333 Date: Wed, 19 Feb 2020 11:32:24 -0800 Subject: [PATCH] User/udit3333/mirror dev preview pane (#3) * Added project template for common library * Added reference to stylecop.json * Fixed xml documetation file path for common project * Added reference to stylecop.json * Added COM interface interpolations to C# * Changed namespace to Common * Added xml doc to com interfaces * Removed AnyCPU configuration from solution file * Added Preview Hander and form User Control Implementation * Fix stylecop warnings * Added test control and handler * Added Xaml description for preview handler * Added Xml documenatation * Updated the control to form * Added registration and unregistration logic for the handler * Moved the files in separate folder and fix PR comments * updated the name of previewhandler class to base class * Added the DoPreview to PreviewHandlerControl Interface * Modified the Dopreview and Unload as virtual method * Uncommented the DocumentText to help bug repro * HTML Parsing Extension for preview pane markdown renderer (#1108) * Added Extension for html post processing * Added unit test poroject for preview pane * Added pipline test and base test function * Added Tests for extension * Added tests for url slashes * Added tests for url and figure caption * Markdown preview pane (#1128) * Added Extension for html post processing * Added unit test poroject for preview pane * Added pipline test and base test function * Added Tests for extension * Added handler and control for markdown * Tests added * Locally working version for markdown * Working image relative url's in markdown * Added CSS to preview display * Updates CSS for code block * Removed html file write comment in markdown control * Updated assembly version and web browser control test * Add Svg preview handler (#1129) * Added a new project for Svg preview handler * Added initial implementation of Svg Preview Handler * Fixed output path * Added Unit Test Project * Added StreamWrapper and Update Svg Control * Updated Svg Handler Guid * Removed migration backup folder * Removed Fluent Assertions NuGet * Added Comments for StreamWrapper * Removed the manual GC collect * Added unit tests for Svg preview Handler * Updated the xml doc for stream wrapper * User/lamotile/add_powerpreviewsettings (#1075) * Added powersettings to PowerToys Settings UI * added settings library * updated settings-web * updated project oncfiguration * updated project onfiguration * updated project .sln file * removed .etl file and added it to git-ignore * separated the PowerPreviewModule into split classes .cpp and .h * moved PowerPreviewModule implemnetations to .cpp file * fixed StringTable formatter * fixed spacing in resource.h * added m_ to member varibales * initiliaze m_isPreviewEnabled in the base class * removed duplication of objects by using pass by refference and std::move * made the getters const * updated naming convention * Split test calsses * Add const string * Replaced move with const string * Made attributes private * Made attributes private * removed unused constructor * Update resource.h formatted resource.h * Adding unit tests for preview handler common (#1156) * Changed the name of the Common library to PreviewHandlerCommon * Added unit tests project for PreviewHandlerCommon * Updated ComInterop accessor type * Added unit tests for PreviewHandlerbase * Added tests for file and stream based handler * Added unit tests for StreamWrapper * Added form handler unit tests * Added Unit tests for FormHandlerControl * Added file header * Add Powerpreview project * Add spacing in sln file * swapped string refferences (#1199) * added registry methods and enable/disable preview handlers (#1230) * added registry methods and enable/disable preview handlers * formatted .rc file. * formatted resource file * formatted .rc file. * formatted settings.cpp * formatted settings.h * formatted SVGPreviewSettingsClassTest.cpp * Formatted MarkDownPreviewSettingsClassTest.cpp * using wide strings * formatted settings.h * FileExplorerPreviewSettingsTest.cpp formatting * fixed typo and formatting * closing Registry and fixing typos * formmarted code using ctrl+k+d * fixed naming * fixed typo * changed if/else reverse order * Markdown preview pane (#1220) * Added rich text bar for information display * Added infobar * Added tests for extension and markdown control * Added xml docstring for markdown preview handler control * Updated assembly file for markdown preview pane * Updated removal of script tag without modifying CSS * Added info bar text string to resource file. * Removed error with infobar display on first rendering * Updated assembly version * Remove script and image element from Svg (#1231) * Added implementation to remove script and image tag * Added Unit tests for SvgPreviewHandlerHelper * Updated Unit tests for SvgPreviewControl * Moved the hardcoded string to resource file * Changed the LocalMachine to CurrentUser for preview handler registration * Added unit tests for multiple blocked elements tags * User/lamotile/update settings objects (#1263) * added registry methods and enable/disable preview handlers * formatted .rc file. * formatted resource file * formatted .rc file. * formatted settings.cpp * formatted settings.h * formatted SVGPreviewSettingsClassTest.cpp * Formatted MarkDownPreviewSettingsClassTest.cpp * using wide strings * formatted settings.h * FileExplorerPreviewSettingsTest.cpp formatting * fixed typo and formatting * closing Registry and fixing typos * formmarted code using ctrl+k+d * fixed naming * fixed typo * changed if/else reverse order * updated setiings_objects.cpp * removed changes on files that are not part of this PR * removed const ref on primative types * updated pass by ref semantic and removed pas by reff on primative types * fixed spaces in the commas * fixed spaces in brackets * Preview pane telemetry (#1299) * Added telemetry base class and markdown telemetry class * Updated docstring for telemetry event. * Added telemetry to markdown for error * Added try catch for markdown preview handler and display error bar * Updated markdown telemetry to make event names global variable * Updated parameter name to camel casing and telemetry event name naming. * Corrected assembbly version for svg renderer * Markdown Image files display (#1303) * Added telemetry base class and markdown telemetry class * Updated docstring for telemetry event. * Added telemetry to markdown for error * Added try catch for markdown preview handler and display error bar * Updated markdown telemetry to make event names global variable * Updated Markdown preview to display without vertical scrollbar and removed xml doc to html agility pack. * Updated parameter name to camel casing and telemetry event name naming. * Corrected assembbly version for svg renderer * Removed duplicate function Co-authored-by: divyanara Co-authored-by: Divyansh Srivastava Co-authored-by: Lavius Motileng <58791731+laviusmotileng-ms@users.noreply.github.com> --- .gitignore | 3 + PowerToys.sln | 106 +- src/common/Telemetry/PowerToys.wprp | Bin 1533 -> 393216 bytes .../UnitTests-CommonLib/Settings.Tests.cpp | 17 + src/common/settings_objects.cpp | 82 +- src/common/settings_objects.h | 84 +- src/modules/example_powertoy/trace.cpp | 1 + .../HTMLParsingExtension.cs | 117 + .../MarkDownPreviewHandler.csproj | 105 + .../Properties/AssemblyInfo.cs | 40 + .../MarkdownPreviewHandler.cs | 36 + .../MarkdownPreviewHandler.snk | Bin 0 -> 596 bytes .../MarkdownPreviewHandlerControl.cs | 206 + .../MarkdownTelemetry.cs | 71 + .../Properties/Resources.Designer.cs | 81 + .../Properties/Resources.resx | 128 + .../HTMLParsingExtensionTest.cs | 128 + .../HelperFiles/MarkdownWithExternalImage.txt | 2 + .../HelperFiles/MarkdownWithscript.txt | 1 + .../MarkdownPreviewHandlerTest.cs | 108 + .../Properties/AssemblyInfo.cs | 20 + .../UnitTests-MarkdownPreviewHandler.csproj | 86 + .../Properties/AssemblyInfo.cs | 40 + .../SvgPreviewHandler/Resource.Designer.cs | 72 + .../SvgPreviewHandler/Resource.resx | 123 + .../SvgPreviewHandler/SvgPreviewControl.cs | 126 + .../SvgPreviewHandler/SvgPreviewHandler.cs | 36 + .../SvgPreviewHandler.csproj | 98 + .../SvgPreviewHandler/SvgPreviewHandler.snk | Bin 0 -> 596 bytes .../Utilities/SvgPreviewHandlerHelper.cs | 51 + .../FileBasedPreviewHandlerTests.cs | 44 + .../FormHandlerControlTests.cs | 181 + .../PreviewHandlerAttributeTests.cs | 84 + .../PreviewHandlerBaseTests.cs | 391 ++ .../Properties/AssemblyInfo.cs | 20 + .../StreamBasedPreviewHandlerTests.cs | 45 + .../StreamWrapperTests.cs | 332 ++ .../UnitTests-PreviewHandlerCommon.csproj | 76 + .../Properties/AssemblyInfo.cs | 20 + .../SvgPreviewControlTests.cs | 191 + .../SvgPreviewHandlerHelperTests.cs | 133 + .../UnitTests-SvgPreviewHandler.csproj | 78 + .../common/PowerToys - Shortcut.lnk | Bin 0 -> 1293 bytes .../common/PreviewHandlerCommon.csproj | 99 + .../common/PreviewHandlerCommon.snk | Bin 0 -> 596 bytes .../common/Properties/AssemblyInfo.cs | 40 + .../common/Telemetry/TelemetryBase.cs | 44 + .../common/Utilities/StreamWrapper.cs | 242 ++ .../previewpane/common/cominterop/COLORREF.cs | 35 + .../common/cominterop/IInitializeWithFile.cs | 25 + .../cominterop/IInitializeWithStream.cs | 26 + .../common/cominterop/IObjectWithSite.cs | 31 + .../common/cominterop/IOleWindow.cs | 31 + .../common/cominterop/IPreviewHandler.cs | 62 + .../common/cominterop/IPreviewHandlerFrame.cs | 33 + .../cominterop/IPreviewHandlerVisuals.cs | 36 + .../previewpane/common/cominterop/LOGFONT.cs | 88 + .../previewpane/common/cominterop/MSG.cs | 51 + .../previewpane/common/cominterop/RECT.cs | 45 + .../common/controls/FormHandlerControl.cs | 160 + .../common/controls/IPreviewHandlerControl.cs | 77 + .../examplehandler/CustomControlTest.cs | 35 + .../examplehandler/TestCustomHandler.cs | 34 + .../handlers/FileBasedPreviewHandler.cs | 27 + .../handlers/PreviewHandlerAttribute.cs | 43 + .../common/handlers/PreviewHandlerBase.cs | 253 ++ .../handlers/StreamBasedPreviewHandler.cs | 27 + .../previewpane/powerpreview/dllmain.cpp | 28 + src/modules/previewpane/powerpreview/pch.cpp | 3 + src/modules/previewpane/powerpreview/pch.h | 5 + .../previewpane/powerpreview/powerpreview.cpp | 125 + .../previewpane/powerpreview/powerpreview.h | 43 + .../previewpane/powerpreview/powerpreview.rc | 78 + .../powerpreview/powerpreview.vcxproj | 127 + .../powerpreview/powerpreview.vcxproj.filters | 20 + .../previewpane/powerpreview/resource.h | 32 + .../previewpane/powerpreview/settings.cpp | 274 ++ .../previewpane/powerpreview/settings.h | 62 + .../previewpane/powerpreview/trace.cpp | 155 + src/modules/previewpane/powerpreview/trace.h | 20 + .../BaseSettingsClassTest.cpp | 18 + .../powerpreviewTest/BaseSettingsClassTest.h | 15 + .../FileExplorerPreviewSettingsTest.cpp | 78 + .../MarkDownPreviewSettingsClassTest.cpp | 47 + .../SVGPreviewSettingsClassTest.cpp | 46 + .../previewpane/powerpreviewTest/pch.cpp | 3 + .../previewpane/powerpreviewTest/pch.h | 15 + .../powerpreviewTest/powerpreviewTest.vcxproj | 186 + .../powerpreviewTest.vcxproj.filters | 42 + src/runner/main.cpp | 3 +- src/settings-web/package-lock.json | 200 +- src/settings-web/package.json | 3 +- .../src/components/ModuleSettings.tsx | 4 + src/settings-web/src/css/layout.css | 3 + .../src/icons/config/fabric-icons.json | 4 + .../src/icons/css/fabric-icons-inline.css | 3 +- .../src/icons/css/fabric-icons.css | 1 + src/settings-web/src/icons/fabric-icons.html | 13 + .../src/icons/fonts/fabric-icons.woff | Bin 3004 -> 3204 bytes .../src/icons/scss/fabric-icons-inline.scss | 4 +- .../src/icons/scss/fabric-icons.scss | 2 + src/settings-web/src/icons/src/IconNames.ts | 3607 +++++++++-------- .../src/icons/src/data/AllIconNames.json | 117 +- .../src/icons/src/fabric-icons.ts | 6 +- src/settings-web/src/icons/src/iconAliases.ts | 4 + src/settings-web/src/setup_icons.tsx | 2 + src/settings/settings-html/dist/bundle.js | 10 +- src/settings/settings-html/index-dark.html | 4 +- src/settings/settings-html/index.html | 4 +- 109 files changed, 8565 insertions(+), 1958 deletions(-) create mode 100644 src/modules/previewpane/MarkDownPreviewHandler/HTMLParsingExtension.cs create mode 100644 src/modules/previewpane/MarkDownPreviewHandler/MarkDownPreviewHandler.csproj create mode 100644 src/modules/previewpane/MarkDownPreviewHandler/Properties/AssemblyInfo.cs create mode 100644 src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.cs create mode 100644 src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.snk create mode 100644 src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandlerControl.cs create mode 100644 src/modules/previewpane/MarkdownPreviewHandler/MarkdownTelemetry.cs create mode 100644 src/modules/previewpane/MarkdownPreviewHandler/Properties/Resources.Designer.cs create mode 100644 src/modules/previewpane/MarkdownPreviewHandler/Properties/Resources.resx create mode 100644 src/modules/previewpane/PreviewPaneUnitTests/HTMLParsingExtensionTest.cs create mode 100644 src/modules/previewpane/PreviewPaneUnitTests/HelperFiles/MarkdownWithExternalImage.txt create mode 100644 src/modules/previewpane/PreviewPaneUnitTests/HelperFiles/MarkdownWithscript.txt create mode 100644 src/modules/previewpane/PreviewPaneUnitTests/MarkdownPreviewHandlerTest.cs create mode 100644 src/modules/previewpane/PreviewPaneUnitTests/Properties/AssemblyInfo.cs create mode 100644 src/modules/previewpane/PreviewPaneUnitTests/UnitTests-MarkdownPreviewHandler.csproj create mode 100644 src/modules/previewpane/SvgPreviewHandler/Properties/AssemblyInfo.cs create mode 100644 src/modules/previewpane/SvgPreviewHandler/Resource.Designer.cs create mode 100644 src/modules/previewpane/SvgPreviewHandler/Resource.resx create mode 100644 src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs create mode 100644 src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.cs create mode 100644 src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj create mode 100644 src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.snk create mode 100644 src/modules/previewpane/SvgPreviewHandler/Utilities/SvgPreviewHandlerHelper.cs create mode 100644 src/modules/previewpane/UnitTests-PreviewHandlerCommon/FileBasedPreviewHandlerTests.cs create mode 100644 src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs create mode 100644 src/modules/previewpane/UnitTests-PreviewHandlerCommon/PreviewHandlerAttributeTests.cs create mode 100644 src/modules/previewpane/UnitTests-PreviewHandlerCommon/PreviewHandlerBaseTests.cs create mode 100644 src/modules/previewpane/UnitTests-PreviewHandlerCommon/Properties/AssemblyInfo.cs create mode 100644 src/modules/previewpane/UnitTests-PreviewHandlerCommon/StreamBasedPreviewHandlerTests.cs create mode 100644 src/modules/previewpane/UnitTests-PreviewHandlerCommon/StreamWrapperTests.cs create mode 100644 src/modules/previewpane/UnitTests-PreviewHandlerCommon/UnitTests-PreviewHandlerCommon.csproj create mode 100644 src/modules/previewpane/UnitTests-SvgPreviewHandler/Properties/AssemblyInfo.cs create mode 100644 src/modules/previewpane/UnitTests-SvgPreviewHandler/SvgPreviewControlTests.cs create mode 100644 src/modules/previewpane/UnitTests-SvgPreviewHandler/SvgPreviewHandlerHelperTests.cs create mode 100644 src/modules/previewpane/UnitTests-SvgPreviewHandler/UnitTests-SvgPreviewHandler.csproj create mode 100644 src/modules/previewpane/common/PowerToys - Shortcut.lnk create mode 100644 src/modules/previewpane/common/PreviewHandlerCommon.csproj create mode 100644 src/modules/previewpane/common/PreviewHandlerCommon.snk create mode 100644 src/modules/previewpane/common/Properties/AssemblyInfo.cs create mode 100644 src/modules/previewpane/common/Telemetry/TelemetryBase.cs create mode 100644 src/modules/previewpane/common/Utilities/StreamWrapper.cs create mode 100644 src/modules/previewpane/common/cominterop/COLORREF.cs create mode 100644 src/modules/previewpane/common/cominterop/IInitializeWithFile.cs create mode 100644 src/modules/previewpane/common/cominterop/IInitializeWithStream.cs create mode 100644 src/modules/previewpane/common/cominterop/IObjectWithSite.cs create mode 100644 src/modules/previewpane/common/cominterop/IOleWindow.cs create mode 100644 src/modules/previewpane/common/cominterop/IPreviewHandler.cs create mode 100644 src/modules/previewpane/common/cominterop/IPreviewHandlerFrame.cs create mode 100644 src/modules/previewpane/common/cominterop/IPreviewHandlerVisuals.cs create mode 100644 src/modules/previewpane/common/cominterop/LOGFONT.cs create mode 100644 src/modules/previewpane/common/cominterop/MSG.cs create mode 100644 src/modules/previewpane/common/cominterop/RECT.cs create mode 100644 src/modules/previewpane/common/controls/FormHandlerControl.cs create mode 100644 src/modules/previewpane/common/controls/IPreviewHandlerControl.cs create mode 100644 src/modules/previewpane/common/examplehandler/CustomControlTest.cs create mode 100644 src/modules/previewpane/common/examplehandler/TestCustomHandler.cs create mode 100644 src/modules/previewpane/common/handlers/FileBasedPreviewHandler.cs create mode 100644 src/modules/previewpane/common/handlers/PreviewHandlerAttribute.cs create mode 100644 src/modules/previewpane/common/handlers/PreviewHandlerBase.cs create mode 100644 src/modules/previewpane/common/handlers/StreamBasedPreviewHandler.cs create mode 100644 src/modules/previewpane/powerpreview/dllmain.cpp create mode 100644 src/modules/previewpane/powerpreview/pch.cpp create mode 100644 src/modules/previewpane/powerpreview/pch.h create mode 100644 src/modules/previewpane/powerpreview/powerpreview.cpp create mode 100644 src/modules/previewpane/powerpreview/powerpreview.h create mode 100644 src/modules/previewpane/powerpreview/powerpreview.rc create mode 100644 src/modules/previewpane/powerpreview/powerpreview.vcxproj create mode 100644 src/modules/previewpane/powerpreview/powerpreview.vcxproj.filters create mode 100644 src/modules/previewpane/powerpreview/resource.h create mode 100644 src/modules/previewpane/powerpreview/settings.cpp create mode 100644 src/modules/previewpane/powerpreview/settings.h create mode 100644 src/modules/previewpane/powerpreview/trace.cpp create mode 100644 src/modules/previewpane/powerpreview/trace.h create mode 100644 src/modules/previewpane/powerpreviewTest/BaseSettingsClassTest.cpp create mode 100644 src/modules/previewpane/powerpreviewTest/BaseSettingsClassTest.h create mode 100644 src/modules/previewpane/powerpreviewTest/FileExplorerPreviewSettingsTest.cpp create mode 100644 src/modules/previewpane/powerpreviewTest/MarkDownPreviewSettingsClassTest.cpp create mode 100644 src/modules/previewpane/powerpreviewTest/SVGPreviewSettingsClassTest.cpp create mode 100644 src/modules/previewpane/powerpreviewTest/pch.cpp create mode 100644 src/modules/previewpane/powerpreviewTest/pch.h create mode 100644 src/modules/previewpane/powerpreviewTest/powerpreviewTest.vcxproj create mode 100644 src/modules/previewpane/powerpreviewTest/powerpreviewTest.vcxproj.filters diff --git a/.gitignore b/.gitignore index e77200d68fa8..3e212e902691 100644 --- a/.gitignore +++ b/.gitignore @@ -332,3 +332,6 @@ ASALocalRun/ # Temp build files src/settings/settings-html/200.html src/settings/settings-html/404.html + +# Temp telemetry files. +src/common/Telemetry/*.etl \ No newline at end of file diff --git a/PowerToys.sln b/PowerToys.sln index dfe503a704d8..fb63169c72c6 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -6,9 +6,7 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "runner", "src\runner\runner.vcxproj", "{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}" ProjectSection(ProjectDependencies) = postProject {48804216-2A0E-4168-A6D8-9CD068D14227} = {48804216-2A0E-4168-A6D8-9CD068D14227} - {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6} = {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6} {74485049-C722-400F-ABE5-86AC52D929B3} = {74485049-C722-400F-ABE5-86AC52D929B3} - {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB} = {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB} {A46629C4-1A6C-40FA-A8B6-10E5102BB0BA} = {A46629C4-1A6C-40FA-A8B6-10E5102BB0BA} {07C389E3-6BC8-41CF-923E-307B1265FA2D} = {07C389E3-6BC8-41CF-923E-307B1265FA2D} EndProjectSection @@ -16,14 +14,8 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "src\common\common.vcxproj", "{74485049-C722-400F-ABE5-86AC52D929B3}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shortcut_guide", "src\modules\shortcut_guide\shortcut_guide.vcxproj", "{A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}" - ProjectSection(ProjectDependencies) = postProject - {74485049-C722-400F-ABE5-86AC52D929B3} = {74485049-C722-400F-ABE5-86AC52D929B3} - EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_powertoy", "src\modules\example_powertoy\example_powertoy.vcxproj", "{44CC9375-3E6E-4D99-8913-7FB748807EBD}" - ProjectSection(ProjectDependencies) = postProject - {74485049-C722-400F-ABE5-86AC52D929B3} = {74485049-C722-400F-ABE5-86AC52D929B3} - EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "modules", "modules", "{4574FDD0-F61D-4376-98BF-E5A1262C11EC}" EndProject @@ -43,22 +35,17 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FancyZonesLib", "src\module EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fancyzones", "src\modules\fancyzones\dll\FancyZonesModule.vcxproj", "{48804216-2A0E-4168-A6D8-9CD068D14227}" ProjectSection(ProjectDependencies) = postProject - {74485049-C722-400F-ABE5-86AC52D929B3} = {74485049-C722-400F-ABE5-86AC52D929B3} {5CCC8468-DEC8-4D36-99D4-5C891BEBD481} = {5CCC8468-DEC8-4D36-99D4-5C891BEBD481} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests-FancyZones", "src\modules\fancyzones\tests\UnitTests\UnitTests.vcxproj", "{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}" ProjectSection(ProjectDependencies) = postProject - {74485049-C722-400F-ABE5-86AC52D929B3} = {74485049-C722-400F-ABE5-86AC52D929B3} {F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99} = {F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99} EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "common", "common", "{1AFB6476-670D-4E80-A464-657E01DFF482}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests-CommonLib", "src\common\UnitTests-CommonLib\UnitTests-CommonLib.vcxproj", "{1A066C63-64B3-45F8-92FE-664E1CCE8077}" - ProjectSection(ProjectDependencies) = postProject - {74485049-C722-400F-ABE5-86AC52D929B3} = {74485049-C722-400F-ABE5-86AC52D929B3} - EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FancyZonesEditor", "src\modules\fancyzones\editor\FancyZonesEditor\FancyZonesEditor.csproj", "{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}" EndProject @@ -68,7 +55,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRenameExt", "src\modul ProjectSection(ProjectDependencies) = postProject {0E072714-D127-460B-AFAD-B4C40B412798} = {0E072714-D127-460B-AFAD-B4C40B412798} {51920F1F-C28C-4ADF-8660-4238766796C2} = {51920F1F-C28C-4ADF-8660-4238766796C2} - {74485049-C722-400F-ABE5-86AC52D929B3} = {74485049-C722-400F-ABE5-86AC52D929B3} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRenameLib", "src\modules\powerrename\lib\PowerRenameLib.vcxproj", "{51920F1F-C28C-4ADF-8660-4238766796C2}" @@ -82,45 +68,41 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRenameTest", "src\modu ProjectSection(ProjectDependencies) = postProject {0E072714-D127-460B-AFAD-B4C40B412798} = {0E072714-D127-460B-AFAD-B4C40B412798} {51920F1F-C28C-4ADF-8660-4238766796C2} = {51920F1F-C28C-4ADF-8660-4238766796C2} - {74485049-C722-400F-ABE5-86AC52D929B3} = {74485049-C722-400F-ABE5-86AC52D929B3} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRenameUnitTests", "src\modules\powerrename\unittests\PowerRenameLibUnitTests.vcxproj", "{2151F984-E006-4A9F-92EF-C6DDE3DC8413}" ProjectSection(ProjectDependencies) = postProject {0E072714-D127-460B-AFAD-B4C40B412798} = {0E072714-D127-460B-AFAD-B4C40B412798} {51920F1F-C28C-4ADF-8660-4238766796C2} = {51920F1F-C28C-4ADF-8660-4238766796C2} - {74485049-C722-400F-ABE5-86AC52D929B3} = {74485049-C722-400F-ABE5-86AC52D929B3} {B25AC7A5-FB9F-4789-B392-D5C85E948670} = {B25AC7A5-FB9F-4789-B392-D5C85E948670} EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{BEEAB7F2-FFF6-45AB-9CDB-B04CC0734B88}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ModuleTemplateCompileTest", "tools\project_template\ModuleTemplate\ModuleTemplateCompileTest.vcxproj", "{64A80062-4D8B-4229-8A38-DFA1D7497749}" - ProjectSection(ProjectDependencies) = postProject - {74485049-C722-400F-ABE5-86AC52D929B3} = {74485049-C722-400F-ABE5-86AC52D929B3} - EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRenameUWPUI", "src\modules\powerrename\UWPui\PowerRenameUWPUI.vcxproj", "{0485F45C-EA7A-4BB5-804B-3E8D14699387}" ProjectSection(ProjectDependencies) = postProject {0E072714-D127-460B-AFAD-B4C40B412798} = {0E072714-D127-460B-AFAD-B4C40B412798} - {74485049-C722-400F-ABE5-86AC52D929B3} = {74485049-C722-400F-ABE5-86AC52D929B3} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "notifications", "src\common\notifications_winrt\notifications.vcxproj", "{0B593A6C-4143-4337-860E-DB5710FB87DB}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "previewpane", "previewpane", "{2F305555-C296-497E-AC20-5FA1B237996A}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "notifications_dll", "src\common\notifications\notifications_dll.vcxproj", "{031AC72E-FA28-4AB7-B690-6F7B9C28AA73}" - ProjectSection(ProjectDependencies) = postProject - {0B593A6C-4143-4337-860E-DB5710FB87DB} = {0B593A6C-4143-4337-860E-DB5710FB87DB} - EndProjectSection +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PreviewHandlerCommon", "src\modules\previewpane\Common\PreviewHandlerCommon.csproj", "{AF2349B8-E5B6-4004-9502-687C1C7730B1}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "windowwalker", "windowwalker", "{8DC78AF7-DC3E-4C57-A8FB-7E347DE74A03}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MarkdownPreviewHandler", "src\modules\previewpane\MarkDownPreviewHandler\MarkdownPreviewHandler.csproj", "{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Window Walker", "src\modules\windowwalker\app\Window Walker\Window Walker.csproj", "{B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests-MarkdownPreviewHandler", "src\modules\previewpane\PreviewPaneUnitTests\UnitTests-MarkdownPreviewHandler.csproj", "{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowWalker", "src\modules\windowwalker\dll\WindowWalker.vcxproj", "{51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}" - ProjectSection(ProjectDependencies) = postProject - {74485049-C722-400F-ABE5-86AC52D929B3} = {74485049-C722-400F-ABE5-86AC52D929B3} - EndProjectSection +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SvgPreviewHandler", "src\modules\previewpane\SvgPreviewHandler\SvgPreviewHandler.csproj", "{DA425894-6E13-404F-8DCB-78584EC0557A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests-SvgPreviewHandler", "src\modules\previewpane\UnitTests-SvgPreviewHandler\UnitTests-SvgPreviewHandler.csproj", "{060D75DA-2D1C-48E6-A4A1-6F0718B64661}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests-PreviewHandlerCommon", "src\modules\previewpane\UnitTests-PreviewHandlerCommon\UnitTests-PreviewHandlerCommon.csproj", "{748417CA-F17E-487F-9411-CAFB6D3F4877}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "powerpreview", "src\modules\previewpane\powerpreview\powerpreview.vcxproj", "{217DF501-135C-4E38-BFC8-99D4821032EA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "powerpreviewTest", "src\modules\previewpane\powerpreviewTest\powerpreviewTest.vcxproj", "{47310AB4-9034-4BD1-8D8B-E88AD21A171B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -196,22 +178,38 @@ Global {0485F45C-EA7A-4BB5-804B-3E8D14699387}.Debug|x64.Build.0 = Debug|x64 {0485F45C-EA7A-4BB5-804B-3E8D14699387}.Release|x64.ActiveCfg = Release|x64 {0485F45C-EA7A-4BB5-804B-3E8D14699387}.Release|x64.Build.0 = Release|x64 - {0B593A6C-4143-4337-860E-DB5710FB87DB}.Debug|x64.ActiveCfg = Debug|x64 - {0B593A6C-4143-4337-860E-DB5710FB87DB}.Debug|x64.Build.0 = Debug|x64 - {0B593A6C-4143-4337-860E-DB5710FB87DB}.Release|x64.ActiveCfg = Release|x64 - {0B593A6C-4143-4337-860E-DB5710FB87DB}.Release|x64.Build.0 = Release|x64 - {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Debug|x64.ActiveCfg = Debug|x64 - {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Debug|x64.Build.0 = Debug|x64 - {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Release|x64.ActiveCfg = Release|x64 - {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Release|x64.Build.0 = Release|x64 - {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Debug|x64.ActiveCfg = Debug|x64 - {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Debug|x64.Build.0 = Debug|x64 - {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Release|x64.ActiveCfg = Release|x64 - {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Release|x64.Build.0 = Release|x64 - {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Debug|x64.ActiveCfg = Debug|x64 - {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Debug|x64.Build.0 = Debug|x64 - {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Release|x64.ActiveCfg = Release|x64 - {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Release|x64.Build.0 = Release|x64 + {AF2349B8-E5B6-4004-9502-687C1C7730B1}.Debug|x64.ActiveCfg = Debug|x64 + {AF2349B8-E5B6-4004-9502-687C1C7730B1}.Debug|x64.Build.0 = Debug|x64 + {AF2349B8-E5B6-4004-9502-687C1C7730B1}.Release|x64.ActiveCfg = Release|x64 + {AF2349B8-E5B6-4004-9502-687C1C7730B1}.Release|x64.Build.0 = Release|x64 + {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Debug|x64.ActiveCfg = Debug|x64 + {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Debug|x64.Build.0 = Debug|x64 + {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Release|x64.ActiveCfg = Release|x64 + {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Release|x64.Build.0 = Release|x64 + {A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Debug|x64.ActiveCfg = Debug|x64 + {A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Debug|x64.Build.0 = Debug|x64 + {A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Release|x64.ActiveCfg = Release|x64 + {A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Release|x64.Build.0 = Release|x64 + {DA425894-6E13-404F-8DCB-78584EC0557A}.Debug|x64.ActiveCfg = Debug|x64 + {DA425894-6E13-404F-8DCB-78584EC0557A}.Debug|x64.Build.0 = Debug|x64 + {DA425894-6E13-404F-8DCB-78584EC0557A}.Release|x64.ActiveCfg = Release|x64 + {DA425894-6E13-404F-8DCB-78584EC0557A}.Release|x64.Build.0 = Release|x64 + {060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Debug|x64.ActiveCfg = Debug|x64 + {060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Debug|x64.Build.0 = Debug|x64 + {060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Release|x64.ActiveCfg = Release|x64 + {060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Release|x64.Build.0 = Release|x64 + {748417CA-F17E-487F-9411-CAFB6D3F4877}.Debug|x64.ActiveCfg = Debug|x64 + {748417CA-F17E-487F-9411-CAFB6D3F4877}.Debug|x64.Build.0 = Debug|x64 + {748417CA-F17E-487F-9411-CAFB6D3F4877}.Release|x64.ActiveCfg = Release|x64 + {748417CA-F17E-487F-9411-CAFB6D3F4877}.Release|x64.Build.0 = Release|x64 + {217DF501-135C-4E38-BFC8-99D4821032EA}.Debug|x64.ActiveCfg = Debug|x64 + {217DF501-135C-4E38-BFC8-99D4821032EA}.Debug|x64.Build.0 = Debug|x64 + {217DF501-135C-4E38-BFC8-99D4821032EA}.Release|x64.ActiveCfg = Release|x64 + {217DF501-135C-4E38-BFC8-99D4821032EA}.Release|x64.Build.0 = Release|x64 + {47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Debug|x64.ActiveCfg = Debug|x64 + {47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Debug|x64.Build.0 = Debug|x64 + {47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Release|x64.ActiveCfg = Release|x64 + {47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -235,11 +233,15 @@ Global {2151F984-E006-4A9F-92EF-C6DDE3DC8413} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} {64A80062-4D8B-4229-8A38-DFA1D7497749} = {BEEAB7F2-FFF6-45AB-9CDB-B04CC0734B88} {0485F45C-EA7A-4BB5-804B-3E8D14699387} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} - {0B593A6C-4143-4337-860E-DB5710FB87DB} = {1AFB6476-670D-4E80-A464-657E01DFF482} - {031AC72E-FA28-4AB7-B690-6F7B9C28AA73} = {1AFB6476-670D-4E80-A464-657E01DFF482} - {8DC78AF7-DC3E-4C57-A8FB-7E347DE74A03} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} - {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB} = {8DC78AF7-DC3E-4C57-A8FB-7E347DE74A03} - {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6} = {8DC78AF7-DC3E-4C57-A8FB-7E347DE74A03} + {2F305555-C296-497E-AC20-5FA1B237996A} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} + {AF2349B8-E5B6-4004-9502-687C1C7730B1} = {2F305555-C296-497E-AC20-5FA1B237996A} + {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB} = {2F305555-C296-497E-AC20-5FA1B237996A} + {A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A} = {2F305555-C296-497E-AC20-5FA1B237996A} + {DA425894-6E13-404F-8DCB-78584EC0557A} = {2F305555-C296-497E-AC20-5FA1B237996A} + {060D75DA-2D1C-48E6-A4A1-6F0718B64661} = {2F305555-C296-497E-AC20-5FA1B237996A} + {748417CA-F17E-487F-9411-CAFB6D3F4877} = {2F305555-C296-497E-AC20-5FA1B237996A} + {217DF501-135C-4E38-BFC8-99D4821032EA} = {2F305555-C296-497E-AC20-5FA1B237996A} + {47310AB4-9034-4BD1-8D8B-E88AD21A171B} = {2F305555-C296-497E-AC20-5FA1B237996A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0} diff --git a/src/common/Telemetry/PowerToys.wprp b/src/common/Telemetry/PowerToys.wprp index 70ccf172aa159e305ad73bb810820d4e3de03cb2..6f49e9929c852917e29ed8ed70b8b6513894aa75 100644 GIT binary patch literal 393216 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 literal 1533 zcmb_c(Qbk;6n*zAn!W=X=fngwQ@6xK7qczyaY49X6X2Q_b?)m|g(5Q0W%IGU?LFt7 zb6RWg8HHpETriAn+R~epK_Ha*|^$l2&~+yQ^Xy`bdTiJ$;eD5ZU6=i$cpfxogZAPXnl$m92Slo+MD zP{mWtGR_$knM(Fsc2F)Ss&+2%Y@1-3W^bwWy6u+MY5N@wdd-#wrqgv>-A=pNEx>0A z9U&@)*QIGo6revz880b8#vokI)(7{hwIWjt0-2T>dQzUW-xv~yg;UrIWcjgAYOcCbKq=ck@f11BD35I!nMqkT zvQ@2R)5Ej5g<(TSROh~wifhIm3FWUrJ<3}CDsnT3knjH)xVxW4 Y|B3wK>D+FLq8E9vWH2qG{^#z017v;#*Z=?k diff --git a/src/common/UnitTests-CommonLib/Settings.Tests.cpp b/src/common/UnitTests-CommonLib/Settings.Tests.cpp index 2cffabdee6ac..a15ec91d25c8 100644 --- a/src/common/UnitTests-CommonLib/Settings.Tests.cpp +++ b/src/common/UnitTests-CommonLib/Settings.Tests.cpp @@ -431,6 +431,23 @@ namespace UnitTestsCommonLib compareJsons(expected, actual); } + TEST_METHOD(SettingsAddLargeHeader) + { + const auto value = L"large header sample text "; + + Settings settings(nullptr, m_moduleName); + settings.add_header_szLarge(m_defaultSettingsName, m_defaultSettingsDescription, value); + + auto expected = m_defaultSettingsJson; + auto expectedProperties = createSettingsProperties(L"header_large"); + expectedProperties.SetNamedValue(L"value", json::JsonValue::CreateStringValue(value)); + expected.GetNamedObject(L"properties").SetNamedValue(m_defaultSettingsName, expectedProperties); + + const auto actual = json::JsonObject::Parse(settings.serialize()); + + compareJsons(expected, actual); + } + TEST_METHOD(SettingsAddStringMultiline) { const auto value = L"Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit,\nsed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\nDuis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\nExcepteur sint occaecat cupidatat non proident,\nsunt in culpa qui officia deserunt mollit anim id est laborum."; diff --git a/src/common/settings_objects.cpp b/src/common/settings_objects.cpp index b2fe76a084e1..71593833ef9d 100644 --- a/src/common/settings_objects.cpp +++ b/src/common/settings_objects.cpp @@ -4,7 +4,7 @@ namespace PowerToysSettings { - Settings::Settings(const HINSTANCE hinstance, std::wstring_view powertoy_name) { + Settings::Settings(const HINSTANCE hinstance, const std::wstring_view& powertoy_name) { m_instance = hinstance; m_json.SetNamedValue(L"version", json::value(L"1.0")); m_json.SetNamedValue(L"name", json::value(powertoy_name)); @@ -15,28 +15,28 @@ namespace PowerToysSettings { m_json.SetNamedValue(L"description", json::value(get_resource(resource_id))); } - void Settings::set_description(std::wstring_view description) { + void Settings::set_description(const std::wstring_view& description) { m_json.SetNamedValue(L"description", json::value(description)); } - void Settings::set_icon_key(std::wstring_view icon_key) { + void Settings::set_icon_key(const std::wstring_view& icon_key) { m_json.SetNamedValue(L"icon_key", json::value(icon_key)); } - void Settings::set_overview_link(std::wstring_view overview_link) { + void Settings::set_overview_link(const std::wstring_view& overview_link) { m_json.SetNamedValue(L"overview_link", json::value(overview_link)); } - void Settings::set_video_link(std::wstring_view video_link) { + void Settings::set_video_link(const std::wstring_view& video_link) { m_json.SetNamedValue(L"video_link", json::value(video_link)); } // add_bool_toogle overloads. - void Settings::add_bool_toogle(std::wstring_view name, UINT description_resource_id, bool value) { + void Settings::add_bool_toogle(const std::wstring_view& name, UINT description_resource_id, const bool& value) { add_bool_toogle(name, get_resource(description_resource_id), value); } - void Settings::add_bool_toogle(std::wstring_view name, std::wstring_view description, bool value) { + void Settings::add_bool_toogle(const std::wstring_view& name, const std::wstring_view& description, const bool& value) { json::JsonObject toggle; toggle.SetNamedValue(L"display_name", json::value(description)); toggle.SetNamedValue(L"editor_type", json::value(L"bool_toggle")); @@ -47,11 +47,11 @@ namespace PowerToysSettings { } // add_int_spinner overloads. - void Settings::add_int_spinner(std::wstring_view name, UINT description_resource_id, int value, int min, int max, int step) { + void Settings::add_int_spinner(const const std::wstring_view& name, UINT description_resource_id, int value, int min, int max, int step) { add_int_spinner(name, get_resource(description_resource_id), value, min, max, step); } - void Settings::add_int_spinner(std::wstring_view name, std::wstring_view description, int value, int min, int max, int step) { + void Settings::add_int_spinner(const std::wstring_view& name, const std::wstring_view& description, int value, int min, int max, int step) { json::JsonObject spinner; spinner.SetNamedValue(L"display_name", json::value(description)); spinner.SetNamedValue(L"editor_type", json::value(L"int_spinner")); @@ -65,11 +65,11 @@ namespace PowerToysSettings { } // add_string overloads. - void Settings::add_string(std::wstring_view name, UINT description_resource_id, std::wstring_view value) { + void Settings::add_string(const std::wstring_view& name, UINT description_resource_id, const std::wstring_view& value) { add_string(name, get_resource(description_resource_id), value); } - void Settings::add_string(std::wstring_view name, std::wstring_view description, std::wstring_view value) { + void Settings::add_string(const std::wstring_view& name, const std::wstring_view& description, const std::wstring_view& value) { json::JsonObject string; string.SetNamedValue(L"display_name", json::value(description)); string.SetNamedValue(L"editor_type", json::value(L"string_text")); @@ -80,11 +80,11 @@ namespace PowerToysSettings { } // add_multiline_string overloads. - void Settings::add_multiline_string(std::wstring_view name, UINT description_resource_id, std::wstring_view value) { + void Settings::add_multiline_string(const std::wstring_view& name, UINT description_resource_id, const std::wstring_view& value) { add_multiline_string(name, get_resource(description_resource_id), value); } - void Settings::add_multiline_string(std::wstring_view name, std::wstring_view description, std::wstring_view value) { + void Settings::add_multiline_string(const std::wstring_view& name, const std::wstring_view& description, const std::wstring_view& value) { json::JsonObject ml_string; ml_string.SetNamedValue(L"display_name", json::value(description)); ml_string.SetNamedValue(L"editor_type", json::value(L"string_text")); @@ -95,12 +95,22 @@ namespace PowerToysSettings { m_json.GetNamedObject(L"properties").SetNamedValue(name, ml_string); } + void Settings::add_header_szLarge(const std::wstring_view& name, const std::wstring_view& description, const std::wstring_view& value){ + json::JsonObject string; + string.SetNamedValue(L"display_name", json::value(description)); + string.SetNamedValue(L"editor_type", json::value(L"header_large")); + string.SetNamedValue(L"value", json::value(value)); + string.SetNamedValue(L"order", json::value(++m_curr_priority)); + + m_json.GetNamedObject(L"properties").SetNamedValue(name, string); + } + // add_color_picker overloads. - void Settings::add_color_picker(std::wstring_view name, UINT description_resource_id, std::wstring_view value) { + void Settings::add_color_picker(const std::wstring_view& name, UINT description_resource_id, const std::wstring_view& value) { add_color_picker(name, get_resource(description_resource_id), value); } - void Settings::add_color_picker(std::wstring_view name, std::wstring_view description, std::wstring_view value) { + void Settings::add_color_picker(const std::wstring_view& name, const std::wstring_view& description, const std::wstring_view& value) { json::JsonObject picker; picker.SetNamedValue(L"display_name", json::value(description)); picker.SetNamedValue(L"editor_type", json::value(L"color_picker")); @@ -110,11 +120,11 @@ namespace PowerToysSettings { m_json.GetNamedObject(L"properties").SetNamedValue(name, picker); } - void Settings::add_hotkey(std::wstring_view name, UINT description_resource_id, const HotkeyObject& hotkey) { + void Settings::add_hotkey(const std::wstring_view& name, UINT description_resource_id, const HotkeyObject& hotkey) { add_hotkey(name, get_resource(description_resource_id), hotkey); } - void Settings::add_hotkey(std::wstring_view name, std::wstring_view description, const HotkeyObject& hotkey_obj) { + void Settings::add_hotkey(const std::wstring_view& name, const std::wstring_view& description, const HotkeyObject& hotkey_obj) { json::JsonObject hotkey; hotkey.SetNamedValue(L"display_name", json::value(description)); hotkey.SetNamedValue(L"editor_type", json::value(L"hotkey")); @@ -124,7 +134,7 @@ namespace PowerToysSettings { m_json.GetNamedObject(L"properties").SetNamedValue(name, hotkey); } - void Settings::add_choice_group(std::wstring_view name, UINT description_resource_id, std::wstring_view value, const std::vector>& keys_and_text_ids) { + void Settings::add_choice_group(const std::wstring_view& name, UINT description_resource_id, const std::wstring_view& value, const std::vector>& keys_and_text_ids) { std::vector> keys_and_texts; keys_and_texts.reserve(keys_and_text_ids.size()); for (const auto& kv : keys_and_text_ids) { @@ -133,12 +143,12 @@ namespace PowerToysSettings { add_choice_group(name, get_resource(description_resource_id), value, keys_and_texts); } - void Settings::add_choice_group(std::wstring_view name, std::wstring_view description, std::wstring_view value, const std::vector>& keys_and_texts) { + void Settings::add_choice_group(const std::wstring_view& name, const std::wstring_view& description, const std::wstring_view& value, const std::vector>& keys_and_texts) { json::JsonObject choice_group; choice_group.SetNamedValue(L"display_name", json::value(description)); choice_group.SetNamedValue(L"editor_type", json::value(L"choice_group")); json::JsonArray options; - for(const auto & [key, text] : keys_and_texts) { + for(const auto& [key, text] : keys_and_texts) { json::JsonObject entry; entry.SetNamedValue(L"key", json::value(key)); entry.SetNamedValue(L"text", json::value(text)); @@ -151,7 +161,7 @@ namespace PowerToysSettings { m_json.GetNamedObject(L"properties").SetNamedValue(name, choice_group); } - void Settings::add_dropdown(std::wstring_view name, UINT description_resource_id, std::wstring_view value, const std::vector>& keys_and_text_ids) { + void Settings::add_dropdown(const std::wstring_view& name, UINT description_resource_id, const std::wstring_view& value, const std::vector>& keys_and_text_ids) { std::vector> keys_and_texts; keys_and_texts.reserve(keys_and_text_ids.size()); for (const auto& kv : keys_and_text_ids) { @@ -160,12 +170,12 @@ namespace PowerToysSettings { add_dropdown(name, get_resource(description_resource_id), value, keys_and_texts); } - void Settings::add_dropdown(std::wstring_view name, std::wstring_view description, std::wstring_view value, const std::vector>& keys_and_texts) { + void Settings::add_dropdown(const std::wstring_view& name, const std::wstring_view& description, const std::wstring_view& value, const std::vector>& keys_and_texts) { json::JsonObject dropdown; dropdown.SetNamedValue(L"display_name", json::value(description)); dropdown.SetNamedValue(L"editor_type", json::value(L"dropdown")); json::JsonArray options; - for(const auto & [key, text] : keys_and_texts) { + for(const auto& [key, text] : keys_and_texts) { json::JsonObject entry; entry.SetNamedValue(L"key", json::value(key)); entry.SetNamedValue(L"text", json::value(text)); @@ -179,15 +189,15 @@ namespace PowerToysSettings { } // add_custom_action overloads. - void Settings::add_custom_action(std::wstring_view name, UINT description_resource_id, UINT button_text_resource_id, UINT ext_description_resource_id) { + void Settings::add_custom_action(const std::wstring_view& name, UINT description_resource_id, UINT button_text_resource_id, UINT ext_description_resource_id) { add_custom_action(name, get_resource(description_resource_id), get_resource(button_text_resource_id), get_resource(ext_description_resource_id)); } - void Settings::add_custom_action(std::wstring_view name, UINT description_resource_id, UINT button_text_resource_id, std::wstring_view value) { + void Settings::add_custom_action(const std::wstring_view& name, UINT description_resource_id, UINT button_text_resource_id, const std::wstring_view& value) { add_custom_action(name, get_resource(description_resource_id), get_resource(button_text_resource_id), value); } - void Settings::add_custom_action(std::wstring_view name, std::wstring_view description, std::wstring_view button_text, std::wstring_view value) { + void Settings::add_custom_action(const std::wstring_view& name, const std::wstring_view& description, const std::wstring_view& button_text, const std::wstring_view& value) { json::JsonObject custom_action; custom_action.SetNamedValue(L"display_name", json::value(description)); custom_action.SetNamedValue(L"button_text", json::value(button_text)); @@ -217,33 +227,33 @@ namespace PowerToysSettings { } // Resource helper. - std::wstring Settings::get_resource(UINT resource_id) { + std::wstring Settings::get_resource(UINT resource_id) const{ if (resource_id != 0) { wchar_t* res_ptr; - const size_t resource_length = LoadStringW(m_instance, resource_id, reinterpret_cast(&res_ptr), 0); + const size_t resource_length = LoadStringW(m_instance, resource_id, reinterpret_cast(& res_ptr), 0); if (resource_length != 0) { - return {*reinterpret_cast(&res_ptr), resource_length}; + return {*reinterpret_cast(& res_ptr), resource_length}; } } return L"RESOURCE ID NOT FOUND: " + std::to_wstring(resource_id); } - PowerToyValues::PowerToyValues(std::wstring_view powertoy_name) { + PowerToyValues::PowerToyValues(const std::wstring_view& powertoy_name) { _name = powertoy_name; set_version(); m_json.SetNamedValue(L"name", json::value(powertoy_name)); m_json.SetNamedValue(L"properties", json::JsonObject{}); } - PowerToyValues PowerToyValues::from_json_string(std::wstring_view json) { + PowerToyValues PowerToyValues::from_json_string(const std::wstring_view& json) { PowerToyValues result = PowerToyValues(); result.m_json = json::JsonValue::Parse(json).GetObjectW(); result._name = result.m_json.GetNamedString(L"name"); return result; } - PowerToyValues PowerToyValues::load_from_settings_file(std::wstring_view powertoy_name) { + PowerToyValues PowerToyValues::load_from_settings_file(const std::wstring_view& powertoy_name) { PowerToyValues result = PowerToyValues(); result.m_json = PTSettingsHelper::load_module_settings(powertoy_name); result._name = powertoy_name; @@ -255,28 +265,28 @@ namespace PowerToysSettings { return json::has(props, name) && json::has(props.GetNamedObject(name), L"value", type); } - std::optional PowerToyValues::get_bool_value(std::wstring_view property_name) { + std::optional PowerToyValues::get_bool_value(const std::wstring_view& property_name) const{ if (!has_property(m_json, property_name, json::JsonValueType::Boolean)) { return std::nullopt; } return m_json.GetNamedObject(L"properties").GetNamedObject(property_name).GetNamedBoolean(L"value"); } - std::optional PowerToyValues::get_int_value(std::wstring_view property_name) { + std::optional PowerToyValues::get_int_value(const std::wstring_view& property_name) const{ if (!has_property(m_json, property_name, json::JsonValueType::Number)) { return std::nullopt; } return static_cast(m_json.GetNamedObject(L"properties").GetNamedObject(property_name).GetNamedNumber(L"value")); } - std::optional PowerToyValues::get_string_value(std::wstring_view property_name) { + std::optional PowerToyValues::get_string_value(const std::wstring_view& property_name) const{ if (!has_property(m_json, property_name, json::JsonValueType::String)) { return std::nullopt; } return m_json.GetNamedObject(L"properties").GetNamedObject(property_name).GetNamedString(L"value").c_str(); } - std::optional PowerToyValues::get_json(std::wstring_view property_name) { + std::optional PowerToyValues::get_json(const std::wstring_view& property_name) const{ if (!has_property(m_json, property_name, json::JsonValueType::Object)) { return std::nullopt; } diff --git a/src/common/settings_objects.h b/src/common/settings_objects.h index a6b1e1779564..296017038d14 100644 --- a/src/common/settings_objects.h +++ b/src/common/settings_objects.h @@ -10,47 +10,47 @@ namespace PowerToysSettings { public: Settings( const HINSTANCE hinstance, // Module handle of the PowerToy DLL 'IMAGE_DOS_HEADER __ImageBase' - std::wstring_view powertoy_name + const std::wstring_view& powertoy_name ); // Add additional general information to the PowerToy settings. void set_description(UINT resource_id); - void set_description(std::wstring_view description); + void set_description(const std::wstring_view& description); - void set_icon_key(std::wstring_view icon_key); - void set_overview_link(std::wstring_view overview_link); - void set_video_link(std::wstring_view video_link); + void set_icon_key(const std::wstring_view& icon_key); + void set_overview_link(const std::wstring_view& overview_link); + void set_video_link(const std::wstring_view& video_link); // Add properties to the PowerToy settings. - void add_bool_toogle(std::wstring_view name, UINT description_resource_id, bool value); - void add_bool_toogle(std::wstring_view name, std::wstring_view description, bool value); + void add_bool_toogle(const std::wstring_view& name, UINT description_resource_id, const bool& value); + void add_bool_toogle(const std::wstring_view& name, const std::wstring_view& description, const bool& value); - void add_int_spinner(std::wstring_view name, UINT description_resource_id, int value, int min, int max, int step); - void add_int_spinner(std::wstring_view name, std::wstring_view description, int value, int min, int max, int step); + void add_int_spinner(const std::wstring_view& name, UINT description_resource_id, int value, int min, int max, int step); + void add_int_spinner(const std::wstring_view& name, const std::wstring_view& description, int value, int min, int max, int step); - void add_string(std::wstring_view name, UINT description_resource_id, std::wstring_view value); - void add_string(std::wstring_view name, std::wstring_view description, std::wstring_view value); + void add_string(const std::wstring_view& name, UINT description_resource_id, const std::wstring_view& value); + void add_string(const std::wstring_view& name, const std::wstring_view& description, const std::wstring_view& value); - void add_multiline_string(std::wstring_view name, UINT description_resource_id, std::wstring_view value); - void add_multiline_string(std::wstring_view name, std::wstring_view description, std::wstring_view value); + void add_multiline_string(const std::wstring_view& name, UINT description_resource_id, const std::wstring_view& value); + void add_multiline_string(const std::wstring_view& name, const std::wstring_view& description, const std::wstring_view& value); - void add_color_picker(std::wstring_view name, UINT description_resource_id, std::wstring_view value); - void add_color_picker(std::wstring_view name, std::wstring_view description, std::wstring_view value); + void add_color_picker(const std::wstring_view& name, UINT description_resource_id, const std::wstring_view& value); + void add_color_picker(const std::wstring_view& name, const std::wstring_view& description, const std::wstring_view& value); - void add_hotkey(std::wstring_view name, UINT description_resource_id, const HotkeyObject& hotkey); - void add_hotkey(std::wstring_view name, std::wstring_view description, const HotkeyObject& hotkey); + void add_hotkey(const std::wstring_view& name, UINT description_resource_id, const HotkeyObject& hotkey); + void add_hotkey(const std::wstring_view& name, const std::wstring_view& description, const HotkeyObject& hotkey); - void add_choice_group(std::wstring_view name, UINT description_resource_id, std::wstring_view value, const std::vector>& keys_and_text_ids); - void add_choice_group(std::wstring_view name, std::wstring_view description, std::wstring_view value, const std::vector>& keys_and_texts); + void add_choice_group(const std::wstring_view& name, UINT description_resource_id, const std::wstring_view& value, const std::vector>& keys_and_text_ids); + void add_choice_group(const std::wstring_view& name, const std::wstring_view& description, const std::wstring_view& value, const std::vector>& keys_and_texts); - void add_dropdown(std::wstring_view name, UINT description_resource_id, std::wstring_view value, const std::vector>& keys_and_text_ids); - void add_dropdown(std::wstring_view name, std::wstring_view description, std::wstring_view value, const std::vector>& keys_and_texts); - - void add_custom_action(std::wstring_view name, UINT description_resource_id, UINT button_text_resource_id, UINT ext_description_resource_id); - void add_custom_action(std::wstring_view name, UINT description_resource_id, UINT button_text_resource_id, std::wstring_view value); - void add_custom_action(std::wstring_view name, std::wstring_view description, std::wstring_view button_text, std::wstring_view value); + void add_dropdown(const std::wstring_view& name, UINT description_resource_id, const std::wstring_view& value, const std::vector>& keys_and_text_ids); + void add_dropdown(const std::wstring_view& name, const std::wstring_view& description, const std::wstring_view& value, const std::vector>& keys_and_texts); + void add_custom_action(const std::wstring_view& name, UINT description_resource_id, UINT button_text_resource_id, UINT ext_description_resource_id); + void add_custom_action(const std::wstring_view& name, UINT description_resource_id, UINT button_text_resource_id, const std::wstring_view& value); + void add_custom_action(const std::wstring_view& name, const std::wstring_view& description, const std::wstring_view& button_text, const std::wstring_view& value); + void add_header_szLarge(const std::wstring_view& name, const std::wstring_view& description, const std::wstring_view& value); // Serialize the internal json to a string. std::wstring serialize(); // Serialize the internal json to the input buffer. @@ -61,27 +61,27 @@ namespace PowerToysSettings { int m_curr_priority = 0; // For keeping order when adding elements. HINSTANCE m_instance; - std::wstring get_resource(UINT resource_id); + std::wstring get_resource(UINT resource_id) const; }; class PowerToyValues { public: - PowerToyValues(std::wstring_view powertoy_name); - static PowerToyValues from_json_string(std::wstring_view json); - static PowerToyValues load_from_settings_file(std::wstring_view powertoy_name); + PowerToyValues(const std::wstring_view& powertoy_name); + static PowerToyValues from_json_string(const std::wstring_view& json); + static PowerToyValues load_from_settings_file(const std::wstring_view& powertoy_name); template - inline void add_property(std::wstring_view name, T value) + inline void add_property(const std::wstring_view& name, const T& value) { - json::JsonObject prop_value; - prop_value.SetNamedValue(L"value", json::value(value)); + json::JsonObject prop_value; + prop_value.SetNamedValue(L"value", json::value(value)); m_json.GetNamedObject(L"properties").SetNamedValue(name, prop_value); } - std::optional get_bool_value(std::wstring_view property_name); - std::optional get_int_value(std::wstring_view property_name); - std::optional get_string_value(std::wstring_view property_name); - std::optional get_json(std::wstring_view property_name); + std::optional get_bool_value(const std::wstring_view& property_name) const; + std::optional get_int_value(const std::wstring_view& property_name) const; + std::optional get_string_value(const std::wstring_view& property_name) const; + std::optional get_json(const std::wstring_view& property_name) const; std::wstring serialize(); void save_to_settings_file(); @@ -96,19 +96,19 @@ namespace PowerToysSettings { class CustomActionObject { public: - static CustomActionObject from_json_string(std::wstring_view json) { + static CustomActionObject from_json_string(const std::wstring_view& json) { return CustomActionObject(json::JsonValue::Parse(json).GetObjectW()); } - std::wstring get_name() { return m_json.GetNamedString(L"action_name").c_str(); } - std::wstring get_value() { return m_json.GetNamedString(L"value").c_str(); } + const std::wstring get_name() const { return m_json.GetNamedString(L"action_name").c_str(); } + const std::wstring get_value() const { return m_json.GetNamedString(L"value").c_str(); } protected: CustomActionObject(json::JsonObject action_json) : m_json(std::move(action_json)) {}; json::JsonObject m_json; }; - class HotkeyObject { + class HotkeyObject { public: static HotkeyObject from_json(json::JsonObject json) { return HotkeyObject(std::move(json)); @@ -150,8 +150,8 @@ namespace PowerToysSettings { // Determinate if vk is an extended key. Unfortunatly MAPVK_VK_TO_VSC_EX // does not return correct values. static std::vector extended_keys = { - VK_APPS, VK_CANCEL, VK_SNAPSHOT, VK_DIVIDE, VK_NUMLOCK, VK_LWIN, VK_RWIN, VK_RMENU, - VK_RCONTROL, VK_RSHIFT, VK_RETURN, VK_INSERT, VK_DELETE, VK_PRIOR, VK_NEXT, + VK_APPS, VK_CANCEL, VK_SNAPSHOT, VK_DIVIDE, VK_NUMLOCK, VK_LWIN, VK_RWIN, VK_RMENU, + VK_RCONTROL, VK_RSHIFT, VK_RETURN, VK_INSERT, VK_DELETE, VK_PRIOR, VK_NEXT, VK_HOME, VK_END, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT, }; if (find(begin(extended_keys), end(extended_keys), key_code) != end(extended_keys)) { diff --git a/src/modules/example_powertoy/trace.cpp b/src/modules/example_powertoy/trace.cpp index d8c81500f81b..17f7f78a8302 100644 --- a/src/modules/example_powertoy/trace.cpp +++ b/src/modules/example_powertoy/trace.cpp @@ -27,3 +27,4 @@ void Trace::MyEvent() TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); } + diff --git a/src/modules/previewpane/MarkDownPreviewHandler/HTMLParsingExtension.cs b/src/modules/previewpane/MarkDownPreviewHandler/HTMLParsingExtension.cs new file mode 100644 index 000000000000..986670353015 --- /dev/null +++ b/src/modules/previewpane/MarkDownPreviewHandler/HTMLParsingExtension.cs @@ -0,0 +1,117 @@ +// 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.IO; +using Markdig; +using Markdig.Extensions.Figures; +using Markdig.Extensions.Tables; +using Markdig.Renderers; +using Markdig.Renderers.Html; +using Markdig.Syntax; +using Markdig.Syntax.Inlines; + +namespace MarkdownPreviewHandler +{ + /// + /// Callback if extension blocks external images. + /// + public delegate void ImagesBlockedCallBack(); + + /// + /// Markdig Extension to process html nodes in markdown AST. + /// + public class HTMLParsingExtension : IMarkdownExtension + { + /// + /// Callback if extension blocks external images. + /// + private readonly ImagesBlockedCallBack imagesBlockedCallBack; + + /// + /// Initializes a new instance of the class. + /// + /// Callback function if image is blocked by extension. + /// Absolute path of markdown file. + public HTMLParsingExtension(ImagesBlockedCallBack imagesBlockedCallBack, string baseUrl = "") + { + this.imagesBlockedCallBack = imagesBlockedCallBack; + this.BaseUrl = baseUrl; + } + + /// + /// Gets or sets path to directory containing markdown file. + /// + public string BaseUrl { get; set; } + + /// + public void Setup(MarkdownPipelineBuilder pipeline) + { + // Make sure we don't have a delegate twice + pipeline.DocumentProcessed -= this.PipelineOnDocumentProcessed; + pipeline.DocumentProcessed += this.PipelineOnDocumentProcessed; + } + + /// + public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) + { + } + + /// + /// Process nodes in markdown AST. + /// + /// Markdown Document. + public void PipelineOnDocumentProcessed(MarkdownDocument document) + { + foreach (var node in document.Descendants()) + { + if (node is Block) + { + if (node is Table) + { + node.GetAttributes().AddClass("table table-striped table-bordered"); + } + else if (node is QuoteBlock) + { + node.GetAttributes().AddClass("blockquote"); + } + else if (node is Figure) + { + node.GetAttributes().AddClass("figure"); + } + else if (node is FigureCaption) + { + node.GetAttributes().AddClass("figure-caption"); + } + } + else if (node is Inline) + { + if (node is LinkInline link) + { + if (link.IsImage) + { + link.GetAttributes().AddClass("img-fluid"); + } + + if (!Uri.TryCreate(link.Url, UriKind.Absolute, out _)) + { + link.Url = link.Url.TrimStart('/', '\\'); + this.BaseUrl = this.BaseUrl.TrimEnd('/', '\\'); + Uri uriLink = new Uri(Path.Combine(this.BaseUrl, link.Url)); + link.Url = uriLink.ToString(); + } + else + { + if (link.IsImage) + { + link.Url = "#"; + this.imagesBlockedCallBack(); + } + } + } + } + } + } + } +} diff --git a/src/modules/previewpane/MarkDownPreviewHandler/MarkDownPreviewHandler.csproj b/src/modules/previewpane/MarkDownPreviewHandler/MarkDownPreviewHandler.csproj new file mode 100644 index 000000000000..79c3647aa6d3 --- /dev/null +++ b/src/modules/previewpane/MarkDownPreviewHandler/MarkDownPreviewHandler.csproj @@ -0,0 +1,105 @@ + + + + + Debug + AnyCPU + {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB} + Library + Properties + MarkdownPreviewHandler + MarkdownPreviewHandler + v4.8 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + bin/Debug/MarkdownPreviewPaneDocumentation.xml + x64 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + bin/Release/MarkdownPreviewPaneDocumentation.xml + x64 + + + true + + + MarkdownPreviewHandler.snk + + + + + + + + + + + + + + + + + + Form + + + + + True + True + Resources.resx + + + + + 1.8.10 + + + 0.18.0 + + + 1.1.118 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + StyleCop.json + + + + + {af2349b8-e5b6-4004-9502-687c1c7730b1} + PreviewHandlerCommon + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + \ No newline at end of file diff --git a/src/modules/previewpane/MarkDownPreviewHandler/Properties/AssemblyInfo.cs b/src/modules/previewpane/MarkDownPreviewHandler/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..9b60e01ac060 --- /dev/null +++ b/src/modules/previewpane/MarkDownPreviewHandler/Properties/AssemblyInfo.cs @@ -0,0 +1,40 @@ +// 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.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MarkdownPreviewHandler")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MarkdownPreviewHandler")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6a71162e-fc4c-4a2c-b90f-3cf94f59a9bb")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.cs b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.cs new file mode 100644 index 000000000000..783a9a2280ea --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.cs @@ -0,0 +1,36 @@ +// 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 Common; + +namespace MarkdownPreviewHandler +{ + /// + /// Implementation of preview handler for markdown files. + /// + [PreviewHandler("MarkdownPreviewPaneHandler", ".md", "{88235ab2-bfce-4be8-9ed0-0408cd8da792}")] + [ProgId("MarkdownPreviewPaneHandler")] + [Guid("45769bcc-e8fd-42d0-947e-02beef77a1f5")] + [ClassInterface(ClassInterfaceType.None)] + [ComVisible(true)] + public class MarkdownPreviewHandler : FileBasedPreviewHandler + { + private MarkdownPreviewHandlerControl markdownPreviewHandlerControl; + + /// + public override void DoPreview() + { + this.markdownPreviewHandlerControl.DoPreview(this.FilePath); + } + + /// + protected override IPreviewHandlerControl CreatePreviewHandlerControl() + { + this.markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl(); + return this.markdownPreviewHandlerControl; + } + } +} diff --git a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.snk b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.snk new file mode 100644 index 0000000000000000000000000000000000000000..ac2511080eb11d7fd40d244f47e19df5ee8757b6 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50096M36$S6@&4tg*#4tZ;c48~JA6O9bi4h=FV3Y8)lh*;IG2pv zmxW5@^u@h(-pIc>6v{xhlb0|eSha_Lp9N;4&1dnC8@y6=|EPzQiCp9P$Un&cu#wca z#3`}M5|FFU!3h}86zGFvxe1f{d_$9FJyiI0H`$QgN%ffNhwaa?FKTqI`H+X8GjUS@ zk&#g<<0|@E)*knhj2wSn`$V1UisP1zHZvJuXFy=PIP;@jDsA9zxZNn|99qVN{o^s^ z`9t?QTL`s0gXobsUa>4m+09X_2Q}PbZHXd9ZS?yHkk}1jtX@iGc4F86=dKoX^M{VW zN_kEa=pTnfv?~)~{m_SV9*GSW4w!srlw>(Icp|HN%k%;-k8#OTfanp2y=x*1#yrUe zli&ov3nJp5(47uir=H2xt$?_b5Plo;2Y*m{j2ljYdk7y)-dl+G=W}L71gMB3iVnWpPn3E>x^PQ&%(u^9-N#}R$18pS literal 0 HcmV?d00001 diff --git a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandlerControl.cs b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandlerControl.cs new file mode 100644 index 000000000000..1004164cc611 --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandlerControl.cs @@ -0,0 +1,206 @@ +// 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.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using Common; +using Markdig; +using MarkdownPreviewHandler.Properties; + +namespace MarkdownPreviewHandler +{ + /// + /// Win Form Implementation for Markdown Preview Handler. + /// + public class MarkdownPreviewHandlerControl : FormHandlerControl + { + /// + /// Extension to modify markdown AST. + /// + private readonly HTMLParsingExtension extension; + + /// + /// Markdig Pipeline builder. + /// + private readonly MarkdownPipelineBuilder pipelineBuilder; + + /// + /// Markdown HTML header. + /// + private readonly string htmlHeader = "
"; + + /// + /// Markdown HTML footer. + /// + private readonly string htmlFooter = "
"; + + /// + /// RichTextBox control to display if external images are blocked. + /// + private RichTextBox infoBar; + + /// + /// WebBrowser control to display markdown html. + /// + private WebBrowser browser; + + /// + /// True if external image is blocked, false otherwise. + /// + private bool infoBarDisplayed = false; + + /// + /// Initializes a new instance of the class. + /// + public MarkdownPreviewHandlerControl() + { + this.extension = new HTMLParsingExtension(this.ImagesBlockedCallBack); + this.pipelineBuilder = new MarkdownPipelineBuilder().UseAdvancedExtensions().UseEmojiAndSmiley(); + this.pipelineBuilder.Extensions.Add(this.extension); + } + + /// + /// Start the preview on the Control. + /// + /// Path to the file. + public override void DoPreview(T dataSource) + { + this.InvokeOnControlThread(() => + { + try + { + this.infoBarDisplayed = false; + + StringBuilder sb = new StringBuilder(); + string filePath = dataSource as string; + string fileText = File.ReadAllText(filePath); + this.extension.BaseUrl = Path.GetDirectoryName(filePath); + + MarkdownPipeline pipeline = this.pipelineBuilder.Build(); + string parsedMarkdown = Markdown.ToHtml(fileText, pipeline); + sb.AppendFormat("{0}{1}{2}", this.htmlHeader, parsedMarkdown, this.htmlFooter); + string markdownHTML = this.RemoveScriptFromHTML(sb.ToString()); + + this.browser = new WebBrowser + { + DocumentText = markdownHTML, + Dock = DockStyle.Fill, + IsWebBrowserContextMenuEnabled = false, + ScriptErrorsSuppressed = true, + ScrollBarsEnabled = true, + }; + this.browser.Navigating += this.WebBrowserNavigating; + this.Controls.Add(this.browser); + + if (this.infoBarDisplayed) + { + this.infoBar = this.GetTextBoxControl(Resources.BlockedImageInfoText); + this.Controls.Add(this.infoBar); + } + + this.Resize += this.FormResized; + base.DoPreview(dataSource); + MarkdownTelemetry.Log.MarkdownFilePreviewed(); + } + catch (Exception e) + { + MarkdownTelemetry.Log.MarkdownFilePreviewError(e.Message); + this.infoBarDisplayed = true; + this.infoBar = this.GetTextBoxControl(Resources.MarkdownNotPreviewedError); + this.Resize += this.FormResized; + this.Controls.Clear(); + this.Controls.Add(this.infoBar); + base.DoPreview(dataSource); + } + }); + } + + /// + /// Removes script tag from html string. + /// + /// html string. + /// HTML string without script tag. + public string RemoveScriptFromHTML(string html) + { + HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument(); + doc.LoadHtml(html); + + doc.DocumentNode.Descendants() + .Where(n => n.Name == "script") + .ToList() + .ForEach(n => n.Remove()); + return doc.DocumentNode.InnerHtml; + } + + /// + /// Gets a textbox control. + /// + /// Message to be displayed in textbox. + /// An object of type . + private RichTextBox GetTextBoxControl(string message) + { + RichTextBox richTextBox = new RichTextBox + { + Text = message, + BackColor = Color.LightYellow, + Multiline = true, + Dock = DockStyle.Top, + ReadOnly = true, + }; + richTextBox.ContentsResized += this.RTBContentsResized; + richTextBox.ScrollBars = RichTextBoxScrollBars.None; + richTextBox.BorderStyle = BorderStyle.None; + + return richTextBox; + } + + /// + /// Callback when RichTextBox is resized. + /// + /// Reference to resized control. + /// Provides data for the resize event. + private void RTBContentsResized(object sender, ContentsResizedEventArgs e) + { + RichTextBox richTextBox = (RichTextBox)sender; + richTextBox.Height = e.NewRectangle.Height + 5; + } + + /// + /// Callback when form is resized. + /// + /// Reference to resized control. + /// Provides data for the event. + private void FormResized(object sender, EventArgs e) + { + if (this.infoBarDisplayed) + { + this.infoBar.Width = this.Width; + } + } + + /// + /// Callback when image is blocked by extension. + /// + private void ImagesBlockedCallBack() + { + this.infoBarDisplayed = true; + } + + /// + /// Callback when link tag is clicked in html. + /// + /// Reference to resized control. + /// Provides data for the WebBrowserNavigatingEventArgs event. + private void WebBrowserNavigating(object sender, WebBrowserNavigatingEventArgs e) + { + e.Cancel = true; + Process.Start(e.Url.ToString()); + } + } +} diff --git a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownTelemetry.cs b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownTelemetry.cs new file mode 100644 index 000000000000..07bf17cd242a --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownTelemetry.cs @@ -0,0 +1,71 @@ +// 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.Diagnostics.Tracing; +using PreviewHandlerCommon.Telemetry; + +namespace MarkdownPreviewHandler +{ + /// + /// Telemetry helper class for markdown renderer. + /// + public class MarkdownTelemetry : TelemetryBase + { + /// + /// Name for ETW event. + /// + private const string EventSourceName = "Microsoft.PowerToys"; + + /// + /// ETW event name when markdown is previewed. + /// + private const string MarkdownFilePreviewedEventName = "PowerPreview_MDRenderer_Previewed"; + + /// + /// ETW event name when error is thrown during markdown preview. + /// + private const string MarkdownFilePreviewErrorEventName = "PowerPreview_MDRenderer_Error"; + + /// + /// Initializes a new instance of the class. + /// + public MarkdownTelemetry() + : base(EventSourceName) + { + return; + } + + /// + /// Gets an instance of the class. + /// + public static MarkdownTelemetry Log { get; } = new MarkdownTelemetry(); + + /// + /// Publishes ETW event when markdown is previewed successfully. + /// + public void MarkdownFilePreviewed() + { + this.Write(MarkdownFilePreviewedEventName, new EventSourceOptions() + { + Keywords = ProjectKeywordMeasure, + Tags = ProjectTelemetryTagProductAndServicePerformance, + }); + } + + /// + /// Publishes ETW event when markdown could not be previewed. + /// + public void MarkdownFilePreviewError(string message) + { + this.Write( + MarkdownFilePreviewErrorEventName, + new EventSourceOptions() + { + Keywords = ProjectKeywordMeasure, + Tags = ProjectTelemetryTagProductAndServicePerformance, + }, + new { Message = message, }); + } + } +} diff --git a/src/modules/previewpane/MarkdownPreviewHandler/Properties/Resources.Designer.cs b/src/modules/previewpane/MarkdownPreviewHandler/Properties/Resources.Designer.cs new file mode 100644 index 000000000000..ea7599efd60a --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandler/Properties/Resources.Designer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// 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 MarkdownPreviewHandler.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()] + internal 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)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MarkdownPreviewHandler.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)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Some pictures have been blocked to help prevent the sender from identifying this computer. Open this item to view pictures.. + /// + internal static string BlockedImageInfoText { + get { + return ResourceManager.GetString("BlockedImageInfoText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The markdown could not be preview due to an internal error.. + /// + internal static string MarkdownNotPreviewedError { + get { + return ResourceManager.GetString("MarkdownNotPreviewedError", resourceCulture); + } + } + } +} diff --git a/src/modules/previewpane/MarkdownPreviewHandler/Properties/Resources.resx b/src/modules/previewpane/MarkdownPreviewHandler/Properties/Resources.resx new file mode 100644 index 000000000000..9beb635bd91e --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandler/Properties/Resources.resx @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + Some pictures have been blocked to help prevent the sender from identifying this computer. Open this item to view pictures. + This text is displayed if image is blocked from being displayed. + + + The markdown could not be preview due to an internal error. + This text is displayed if markdown fails to preview + + \ No newline at end of file diff --git a/src/modules/previewpane/PreviewPaneUnitTests/HTMLParsingExtensionTest.cs b/src/modules/previewpane/PreviewPaneUnitTests/HTMLParsingExtensionTest.cs new file mode 100644 index 000000000000..5967b27539be --- /dev/null +++ b/src/modules/previewpane/PreviewPaneUnitTests/HTMLParsingExtensionTest.cs @@ -0,0 +1,128 @@ +using System; +using Markdig; +using MarkdownPreviewHandler; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace PreviewPaneUnitTests +{ + [TestClass] + public class HTMLParsingExtensionTest + { + private MarkdownPipeline BuidPipeline(IMarkdownExtension extension) + { + MarkdownPipelineBuilder pipelineBuilder = new MarkdownPipelineBuilder().UseAdvancedExtensions(); + pipelineBuilder.Extensions.Add(extension); + return pipelineBuilder.Build(); + } + + [TestMethod] + public void Extension_UpdatesTablesClass_WhenUsed() + { + // Arrange + String mdString = "| A | B |\n| -- | -- | "; + HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension(() => { }); + MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension); + + // Act + String html = Markdown.ToHtml(mdString, markdownPipeline); + + // Assert + Assert.AreEqual(html, "\n\n\n\n\n\n\n
AB
\n"); + } + + + [TestMethod] + public void Extension_UpdatesBlockQuotesClass_WhenUsed() + { + // Arrange + String mdString = "> Blockquotes."; + HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension(()=> { }); + MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension); + + // Act + String html = Markdown.ToHtml(mdString, markdownPipeline); + + // Assert + Assert.AreEqual(html, "
\n

Blockquotes.

\n
\n"); + } + + [TestMethod] + public void Extension_UpdatesFigureClassAndRelativeUrltoAbsolute_WhenUsed() + { + // arrange + String mdString = "![text](a.jpg \"Figure\")"; + HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension(() => { }, "C:\\Users\\"); + MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension); + + // Act + String html = Markdown.ToHtml(mdString, markdownPipeline); + + // Assert + Assert.AreEqual(html, "

\"text\"

\n"); + } + + [TestMethod] + public void Extension_CreatesCorrectAbsoluteLinkByTrimmingForwardSlash_WhenUsed() + { + // arrange + String mdString = "![text](\\document\\a.jpg \"Figure\")"; + HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension(() => { }, "C:\\Users\\"); + MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension); + + // Act + String html = Markdown.ToHtml(mdString, markdownPipeline); + + // Assert + Assert.AreEqual(html, "

\"text\"

\n"); + } + + [TestMethod] + public void Extension_CreatesCorrectAbsoluteLinkByTrimmingBackwardSlash_WhenUsed() + { + // arrange + String mdString = "![text](/document/a.jpg \"Figure\")"; + HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension(() => { }, "C:/Users/"); + MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension); + + // Act + String html = Markdown.ToHtml(mdString, markdownPipeline); + + // Assert + Assert.AreEqual(html, "

\"text\"

\n"); + } + + [TestMethod] + public void Extension_AddsClassToFigureCaption_WhenUsed() + { + // arrange + String mdString = "^^^ This is a caption"; + HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension(() => { }, "C:/Users/"); + MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension); + + // Act + String html = Markdown.ToHtml(mdString, markdownPipeline); + + // Assert + Assert.AreEqual(html, "
\n
This is a caption
\n
\n"); + } + + [TestMethod] + public void Extension_RemovesExternalImageUrlAndMakeCallback_WhenUsed() + { + // arrange + int count = 0; + String mdString = "![text](http://dev.nodeca.com \"Figure\")"; + HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension(() => { count++; }); + MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension); + + // Act + String html = Markdown.ToHtml(mdString, markdownPipeline); + + // Assert + Assert.AreEqual(count, 1); + Assert.AreEqual(html, "

\"text\"

\n"); + + } + + } +} diff --git a/src/modules/previewpane/PreviewPaneUnitTests/HelperFiles/MarkdownWithExternalImage.txt b/src/modules/previewpane/PreviewPaneUnitTests/HelperFiles/MarkdownWithExternalImage.txt new file mode 100644 index 000000000000..1439071e7a66 --- /dev/null +++ b/src/modules/previewpane/PreviewPaneUnitTests/HelperFiles/MarkdownWithExternalImage.txt @@ -0,0 +1,2 @@ +![Minion](https://octodex.github.com/images/minion.png) + \ No newline at end of file diff --git a/src/modules/previewpane/PreviewPaneUnitTests/HelperFiles/MarkdownWithscript.txt b/src/modules/previewpane/PreviewPaneUnitTests/HelperFiles/MarkdownWithscript.txt new file mode 100644 index 000000000000..d2311571cfd7 --- /dev/null +++ b/src/modules/previewpane/PreviewPaneUnitTests/HelperFiles/MarkdownWithscript.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/modules/previewpane/PreviewPaneUnitTests/MarkdownPreviewHandlerTest.cs b/src/modules/previewpane/PreviewPaneUnitTests/MarkdownPreviewHandlerTest.cs new file mode 100644 index 000000000000..6fc9a17322ea --- /dev/null +++ b/src/modules/previewpane/PreviewPaneUnitTests/MarkdownPreviewHandlerTest.cs @@ -0,0 +1,108 @@ +using System; +using System.Drawing; +using System.Linq; +using System.Text.RegularExpressions; +using System.Windows.Forms; +using System.Xml.Linq; +using Markdig; +using MarkdownPreviewHandler; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace PreviewPaneUnitTests +{ + [TestClass] + public class MarkdownPreviewHandlerTest + { + [TestMethod] + public void MarkdownPreviewHandlerControl__AddsBrowserToForm_WhenDoPreviewIsCalled() + { + // Arrange + MarkdownPreviewHandlerControl markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl(); + + // Act + markdownPreviewHandlerControl.DoPreview("HelperFiles/MarkdownWithExternalImage.txt"); + + // Assert + Assert.AreEqual(markdownPreviewHandlerControl.Controls.Count, 2); + Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[0], typeof(WebBrowser)); + } + + [TestMethod] + public void MarkdownPreviewHandlerControl__AddsInfoBarToFormIfExternalImageLinkPresent_WhenDoPreviewIsCalled() + { + // Arrange + MarkdownPreviewHandlerControl markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl(); + + // Act + markdownPreviewHandlerControl.DoPreview("HelperFiles/MarkdownWithExternalImage.txt"); + + // Assert + Assert.AreEqual(markdownPreviewHandlerControl.Controls.Count, 2); + Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[1], typeof(RichTextBox)); + } + + [TestMethod] + public void MarkdownPreviewHandlerControl__DoesNotAddInfoBarToFormIfExternalImageLinkNotPresent_WhenDoPreviewIsCalled() + { + // Arrange + MarkdownPreviewHandlerControl markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl(); + + // Act + markdownPreviewHandlerControl.DoPreview("HelperFiles/MarkdownWithScript.txt"); + + // Assert + Assert.AreEqual(markdownPreviewHandlerControl.Controls.Count, 1); + Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[0], typeof(WebBrowser)); + } + + [TestMethod] + public void MarkdownPreviewHandlerControl__UpdatesWebBrowserSettings_WhenDoPreviewIsCalled() + { + // Arrange + MarkdownPreviewHandlerControl markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl(); + + // Act + markdownPreviewHandlerControl.DoPreview("HelperFiles/MarkdownWithExternalImage.txt"); + + // Assert + Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[0], typeof(WebBrowser)); + Assert.IsNotNull(((WebBrowser)markdownPreviewHandlerControl.Controls[0]).DocumentText); + Assert.AreEqual(((WebBrowser)markdownPreviewHandlerControl.Controls[0]).Dock, DockStyle.Fill); + Assert.AreEqual(((WebBrowser)markdownPreviewHandlerControl.Controls[0]).IsWebBrowserContextMenuEnabled, false); + Assert.AreEqual(((WebBrowser)markdownPreviewHandlerControl.Controls[0]).ScriptErrorsSuppressed, true); + Assert.AreEqual(((WebBrowser)markdownPreviewHandlerControl.Controls[0]).ScrollBarsEnabled, true); + } + + [TestMethod] + public void MarkdownPreviewHandlerControl__UpdateInfobarSettings_WhenDoPreviewIsCalled() + { + // Arrange + MarkdownPreviewHandlerControl markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl(); + + // Act + markdownPreviewHandlerControl.DoPreview("HelperFiles/MarkdownWithExternalImage.txt"); + + // Assert + Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[1], typeof(RichTextBox)); + Assert.IsNotNull(((RichTextBox)markdownPreviewHandlerControl.Controls[1]).Text); + Assert.AreEqual(((RichTextBox)markdownPreviewHandlerControl.Controls[1]).Dock, DockStyle.Top); + Assert.AreEqual(((RichTextBox)markdownPreviewHandlerControl.Controls[1]).BorderStyle, BorderStyle.None); + Assert.AreEqual(((RichTextBox)markdownPreviewHandlerControl.Controls[1]).BackColor, Color.LightYellow); + Assert.AreEqual(((RichTextBox)markdownPreviewHandlerControl.Controls[1]).Multiline, true); + } + + [TestMethod] + public void MarkdownPreviewHandlerControl_RemovesScriptTags_RemoveScriptFromHTMLIsCalled() + { + // Arrange + MarkdownPreviewHandlerControl markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl(); + string html = ""; + + // Act + string parsedHTML = markdownPreviewHandlerControl.RemoveScriptFromHTML(html); + + // Assert + Assert.AreEqual(parsedHTML, "\r\n \r\n"); + } + } +} \ No newline at end of file diff --git a/src/modules/previewpane/PreviewPaneUnitTests/Properties/AssemblyInfo.cs b/src/modules/previewpane/PreviewPaneUnitTests/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..06aba0536872 --- /dev/null +++ b/src/modules/previewpane/PreviewPaneUnitTests/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("UnitTests-MarkdownPreviewHandler")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("UnitTests-MarkdownPreviewHandler")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("a2b51b8b-8f90-424e-bc97-f9ab7d76ca1a")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/modules/previewpane/PreviewPaneUnitTests/UnitTests-MarkdownPreviewHandler.csproj b/src/modules/previewpane/PreviewPaneUnitTests/UnitTests-MarkdownPreviewHandler.csproj new file mode 100644 index 000000000000..a6c8bed5a2b6 --- /dev/null +++ b/src/modules/previewpane/PreviewPaneUnitTests/UnitTests-MarkdownPreviewHandler.csproj @@ -0,0 +1,86 @@ + + + + + Debug + AnyCPU + {A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A} + Library + Properties + PreviewPaneUnitTests + PreviewPaneUnitTests + v4.8 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + x64 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + x64 + + + + + + + + + + + + + + + + 0.18.0 + + + 1.3.2 + + + 1.3.2 + + + + + {AF2349B8-E5B6-4004-9502-687C1C7730B1} + PreviewHandlerCommon + + + {6a71162e-fc4c-4a2c-b90f-3cf94f59a9bb} + MarkdownPreviewHandler + + + + + + Always + + + Always + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/SvgPreviewHandler/Properties/AssemblyInfo.cs b/src/modules/previewpane/SvgPreviewHandler/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..0bc87e35c8eb --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandler/Properties/AssemblyInfo.cs @@ -0,0 +1,40 @@ +// 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.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SvgPreviewHandler")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SvgPreviewHandler")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("da425894-6e13-404f-8dcb-78584ec0557a")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/modules/previewpane/SvgPreviewHandler/Resource.Designer.cs b/src/modules/previewpane/SvgPreviewHandler/Resource.Designer.cs new file mode 100644 index 000000000000..300036aa4c1e --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandler/Resource.Designer.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// 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 SvgPreviewHandler { + 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()] + internal class Resource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SvgPreviewHandler.Resource", typeof(Resource).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)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Some elements have been blocked to help prevent the sender from identifying your computer. Open this item to view all elements.. + /// + internal static string BlockedElementInfoText { + get { + return ResourceManager.GetString("BlockedElementInfoText", resourceCulture); + } + } + } +} diff --git a/src/modules/previewpane/SvgPreviewHandler/Resource.resx b/src/modules/previewpane/SvgPreviewHandler/Resource.resx new file mode 100644 index 000000000000..201ff83be4af --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandler/Resource.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + Some elements have been blocked to help prevent the sender from identifying your computer. Open this item to view all elements. + + \ No newline at end of file diff --git a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs new file mode 100644 index 000000000000..74c691492b8d --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs @@ -0,0 +1,126 @@ +// 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.Drawing; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.ComTypes; +using System.Windows.Forms; +using System.Xml; +using System.Xml.Linq; +using Common; +using Common.Utilities; +using SvgPreviewHandler.Utilities; + +namespace SvgPreviewHandler +{ + /// + /// Implementation of Control for Svg Preview Handler. + /// + public class SvgPreviewControl : FormHandlerControl + { + /// + /// Browser Control to display Svg. + /// + private WebBrowser browser; + + /// + /// Text box to display the information about blocked elements from Svg. + /// + private RichTextBox textBox; + + /// + /// Represent if any blocked element is found in the Svg. + /// + private bool foundFilteredElements = false; + + /// + /// Start the preview on the Control. + /// + /// Stream reference to access source file. + public override void DoPreview(T dataSource) + { + this.InvokeOnControlThread(() => + { + this.foundFilteredElements = false; + string svgData = null; + using (var stream = new StreamWrapper(dataSource as IStream)) + { + using (var reader = new StreamReader(stream)) + { + svgData = reader.ReadToEnd(); + } + } + + svgData = string.IsNullOrWhiteSpace(svgData) ? svgData : SvgPreviewHandlerHelper.RemoveElements(svgData, out this.foundFilteredElements); + if (this.foundFilteredElements) + { + this.AddTextBoxControl(); + } + + this.AddBrowserControl(svgData); + this.Resize += this.FormResized; + base.DoPreview(dataSource); + }); + } + + /// + /// Occurs when RichtextBox is resized. + /// + /// Reference to resized control. + /// Provides data for the ContentsResized event. + private void RTBContentsResized(object sender, ContentsResizedEventArgs e) + { + var richTextBox = sender as RichTextBox; + richTextBox.Height = e.NewRectangle.Height + 5; + } + + /// + /// Occurs when form is resized. + /// + /// Reference to resized control. + /// Provides data for the resize event. + private void FormResized(object sender, EventArgs e) + { + if (this.foundFilteredElements) + { + this.textBox.Width = this.Width; + } + } + + /// + /// Adds a Web Browser Control to Control Collection. + /// + /// Svg to display on Browser Control. + private void AddBrowserControl(string svgData) + { + this.browser = new WebBrowser(); + this.browser.DocumentText = svgData; + this.browser.Dock = DockStyle.Fill; + this.browser.IsWebBrowserContextMenuEnabled = false; + this.browser.ScriptErrorsSuppressed = true; + this.browser.ScrollBarsEnabled = true; + this.Controls.Add(this.browser); + } + + /// + /// Adds a Text Box in Controls for showing information about blocked elements. + /// + private void AddTextBoxControl() + { + this.textBox = new RichTextBox(); + this.textBox.Text = Resource.BlockedElementInfoText; + this.textBox.BackColor = Color.LightYellow; + this.textBox.Multiline = true; + this.textBox.Dock = DockStyle.Top; + this.textBox.ReadOnly = true; + this.textBox.ContentsResized += this.RTBContentsResized; + this.textBox.ScrollBars = RichTextBoxScrollBars.None; + this.textBox.BorderStyle = BorderStyle.None; + this.Controls.Add(this.textBox); + } + } +} diff --git a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.cs b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.cs new file mode 100644 index 000000000000..642cd13e83f0 --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.cs @@ -0,0 +1,36 @@ +// 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 Common; + +namespace SvgPreviewHandler +{ + /// + /// Extends for Svg Preview Handler. + /// + [PreviewHandler("SvgPreviewHandler", ".svg", "{88235ab2-bfce-4be8-9ed0-0408cd8da792}")] + [ProgId("SvgPreviewHandler")] + [Guid("ddee2b8a-6807-48a6-bb20-2338174ff779")] + [ClassInterface(ClassInterfaceType.None)] + [ComVisible(true)] + public class SvgPreviewHandler : StreamBasedPreviewHandler + { + private SvgPreviewControl svgPreviewControl; + + /// + public override void DoPreview() + { + this.svgPreviewControl.DoPreview(this.Stream); + } + + /// + protected override IPreviewHandlerControl CreatePreviewHandlerControl() + { + this.svgPreviewControl = new SvgPreviewControl(); + return this.svgPreviewControl; + } + } +} diff --git a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj new file mode 100644 index 000000000000..7456a52b3672 --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj @@ -0,0 +1,98 @@ + + + + + Debug + AnyCPU + {DA425894-6E13-404F-8DCB-78584EC0557A} + Library + Properties + SvgPreviewHandler + SvgPreviewHandler + v4.8 + 512 + true + + + true + + + SvgPreviewHandler.snk + + + true + bin\Debug\ + DEBUG;TRACE + bin\Debug\SvgPreviewHandler.xml + 2 + true + full + x64 + 7.3 + prompt + MinimumRecommendedRules.ruleset + + + bin\Release\ + TRACE + bin\Release\SvgPreviewHandler.xml + true + true + pdbonly + x64 + 7.3 + prompt + MinimumRecommendedRules.ruleset + + + + + + + + + + + + + + + + True + True + Resource.resx + + + Form + + + + + + + + {af2349b8-e5b6-4004-9502-687c1c7730b1} + PreviewHandlerCommon + + + + + StyleCop.json + + + + + + 1.1.118 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + ResXFileCodeGenerator + Resource.Designer.cs + + + + \ No newline at end of file diff --git a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.snk b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.snk new file mode 100644 index 0000000000000000000000000000000000000000..3375897afa1bf7275e1305344e104c29839d1a18 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50096Ay18EQ#E7hr6A49{)c7sb#Wc%soHgfg zLl?)ROEo}E*gqsDw3>`o6XUv6bKvqtPSL?8K0$N!WTZ4Q4^Mj`pIn8 zszE{v&ggSHEBv7I(A1)&q{HnT#o(BO7|M;3h`@e)EQBtsRgNB=_{(er|8ytqLeQTNv9S(AV| + /// Helper utilities for Svg Preview Handler. + /// + public class SvgPreviewHandlerHelper + { + /// + /// Dictionary of elements that are blocked from Svg for preview pane. + /// + private static Dictionary blockedElementsName = new Dictionary + { + { "script", true }, + { "image", true }, + }; + + /// + /// Remove blocked elements from the Input Svg. + /// + /// Input Svg to remove the blocked elements from. + /// Set true if any blocked element is present in the Svg otherwise false. + /// Svg with removed blocked elements if present. + public static string RemoveElements(string svgData, out bool foundBlockedElement) + { + foundBlockedElement = false; + var doc = XDocument.Parse(svgData); + var elements = doc.Descendants().ToList(); + foreach (XElement element in elements) + { + if (blockedElementsName.ContainsKey(element.Name.LocalName.ToLower())) + { + element.Remove(); + foundBlockedElement = true; + } + } + + return doc.ToString(); + } + } +} diff --git a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FileBasedPreviewHandlerTests.cs b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FileBasedPreviewHandlerTests.cs new file mode 100644 index 000000000000..4b3aaa88470c --- /dev/null +++ b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FileBasedPreviewHandlerTests.cs @@ -0,0 +1,44 @@ +// 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 Common; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace UnitTests_PreviewHandlerCommon +{ + [TestClass] + public class FileBasedPreviewHandlerTests + { + public class TestFileBasedPreviewHandler : FileBasedPreviewHandler + { + public override void DoPreview() + { + throw new NotImplementedException(); + } + + protected override IPreviewHandlerControl CreatePreviewHandlerControl() + { + return new Mock().Object; + } + } + + [DataTestMethod] + [DataRow((uint)0)] + [DataRow((uint)1)] + public void FileBasedPreviewHandler_ShouldSetFilePath_WhenInitializeCalled(uint grfMode) + { + // Arrange + var fileBasedPreviewHandler = new TestFileBasedPreviewHandler(); + var filePath = "C:\\valid-path"; + + // Act + fileBasedPreviewHandler.Initialize(filePath, grfMode); + + // Assert + Assert.AreEqual(fileBasedPreviewHandler.FilePath, filePath); + } + } +} diff --git a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs new file mode 100644 index 000000000000..c16c1e781dc6 --- /dev/null +++ b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs @@ -0,0 +1,181 @@ +// 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 Common; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace UnitTests_PreviewHandlerCommon +{ + [TestClass] + public class FormHandlerControlTests + { + private class TestFormControl : FormHandlerControl + { } + + [TestMethod] + public void FormHandlerControl_ShouldCreateHandle_OnIntialization() + { + // Arrange and act + var testFormHandlerControl = new TestFormControl(); + + // Assert + Assert.IsTrue(testFormHandlerControl.IsHandleCreated); + } + + [TestMethod] + public void FormHandlerControl_ShouldSetVisibleFalse_OnIntialization() + { + // Arrange and act + var testFormHandlerControl = new TestFormControl(); + + // Assert + Assert.IsFalse(testFormHandlerControl.Visible); + } + + [TestMethod] + public void FormHandlerControl_ShouldSetFormBorderStyle_OnIntialization() + { + // Arrange and act + var testFormHandlerControl = new TestFormControl(); + + // Assert + Assert.AreEqual(FormBorderStyle.None, testFormHandlerControl.FormBorderStyle); + } + + [TestMethod] + public void FormHandlerControl_ShouldReturnValidHandle_WhenGetHandleCalled() + { + // Arrange + var testFormHandlerControl = new TestFormControl(); + + // Act + var handle = testFormHandlerControl.GetHandle(); + + // Assert + Assert.AreEqual(testFormHandlerControl.Handle, handle); + } + + [TestMethod] + public void FormHandlerControl_ShouldSetBackgroundColor_WhenSetBackgroundColorCalled() + { + // Arrange + var testFormHandlerControl = new TestFormControl(); + var color = Color.Navy; + + // Act + testFormHandlerControl.SetBackgroundColor(color); + + // Assert + Assert.AreEqual(color, testFormHandlerControl.BackColor); + } + + [TestMethod] + public void FormHandlerControl_ShouldSetFont_WhenSetFontCalled() + { + // Arrange + var testFormHandlerControl = new TestFormControl(); + var font = new Font("Arial", 20); + + // Act + testFormHandlerControl.SetFont(font); + + // Assert + Assert.AreEqual(font, testFormHandlerControl.Font); + } + + [TestMethod] + public void FormHandlerControl_ShouldUpdateBounds_WhenSetRectCalled() + { + // Arrange + var testFormHandlerControl = new TestFormControl(); + var bounds = new Rectangle(2, 2, 4, 4); + + // Act + testFormHandlerControl.SetRect(bounds); + + // Assert + Assert.AreEqual(bounds, testFormHandlerControl.Bounds); + } + + [TestMethod] + public void FormHandlerControl_ShouldSetTextColor_WhenSetTextColorCalled() + { + // Arrange + var testFormHandlerControl = new TestFormControl(); + var color = Color.Navy; + + // Act + testFormHandlerControl.SetTextColor(color); + + // Assert + Assert.AreEqual(color, testFormHandlerControl.ForeColor); + } + + [TestMethod] + public void FormHandlerControl_ShouldClearAllControls_WhenUnloadCalled() + { + // Arrange + var testFormHandlerControl = new TestFormControl(); + testFormHandlerControl.Controls.Add(new TextBox()); + testFormHandlerControl.Controls.Add(new RichTextBox()); + + // Act + testFormHandlerControl.Unload(); + + // Assert + Assert.AreEqual(0, testFormHandlerControl.Controls.Count); + } + + [TestMethod] + public void FormHandlerControl_ShouldSetVisibleFalse_WhenUnloadCalled() + { + // Arrange + var testFormHandlerControl = new TestFormControl(); + + // Act + testFormHandlerControl.Unload(); + + // Assert + Assert.IsFalse(testFormHandlerControl.Visible); + } + + [TestMethod] + public void FormHandlerControl_ShouldSetVisibletrue_WhenDoPreviewCalled() + { + // Arrange + var testFormHandlerControl = new TestFormControl(); + + // Act + testFormHandlerControl.DoPreview("valid-path"); + + // Assert + Assert.IsTrue(testFormHandlerControl.Visible); + } + + [TestMethod] + public void FormHandlerControl_ShouldSetParentHandle_WhenSetWindowCalled() + { + // Arrange + var testFormHandlerControl = new TestFormControl(); + var parentFormWindow = new UserControl(); + var parentHwnd = parentFormWindow.Handle; + var rect = new Rectangle(2, 2, 4, 4); + + // Act + testFormHandlerControl.SetWindow(parentHwnd, rect); + var actualParentHwnd = GetAncestor(testFormHandlerControl.Handle, 1); // GA_PARENT 1 + + // Assert + Assert.AreEqual(parentHwnd, actualParentHwnd); + } + + // Gets the ancestor window: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getancestor + [DllImport("user32.dll")] + private static extern IntPtr GetAncestor(IntPtr hWnd, uint gaFlags); + } +} diff --git a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/PreviewHandlerAttributeTests.cs b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/PreviewHandlerAttributeTests.cs new file mode 100644 index 000000000000..1d037db67fa4 --- /dev/null +++ b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/PreviewHandlerAttributeTests.cs @@ -0,0 +1,84 @@ +// 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 Common; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UnitTests_PreviewHandlerCommon +{ + [TestClass] + public class PreviewHandlerAttributeTests + { + [PreviewHandler("valid-name", "valid-extension", "valid-appid")] + private class TestPreviewHandler + { + } + + + [DataTestMethod] + [DataRow("name")] + [DataRow("extension")] + [DataRow("appId")] + public void PreviewHandlerAttributes_ShouldThrow_IfInitializeWithNullArguments(string argName) + { + // Arrange + var name = (argName.Equals("name")) ? null : string.Empty; + var extension = (argName.Equals("extension")) ? null : string.Empty; + var appId = (argName.Equals("appId")) ? null : string.Empty; + ArgumentNullException exception = null; + + // Act + try + { + var previewHandlerAttribute = new PreviewHandlerAttribute(name, extension, appId); + } + catch (ArgumentNullException ex) + { + exception = ex; + } + + // Assert + Assert.IsNotNull(exception); + } + + [TestMethod] + public void PreviewHandlerAttributes_ShouldSetValidFields_IfInitializeWithValidArguments() + { + // Arrange + var name = "valid-name"; + var extension = "valid-extension"; + var appId = "valid-appid"; + + // Act + var previewHandlerAttribute = new PreviewHandlerAttribute(name, extension, appId); + + // Assert + Assert.AreEqual(name, previewHandlerAttribute.Name); + Assert.AreEqual(extension, previewHandlerAttribute.Extension); + Assert.AreEqual(appId, previewHandlerAttribute.AppId); + } + + [TestMethod] + public void PreviewHandlerAttributes_ShouldSetValidFields_WhenInitializeFromAttributes() + { + // Arrange + var testPreviewHandler = new TestPreviewHandler(); + + // Act + var attr = (object[])testPreviewHandler.GetType().GetCustomAttributes(typeof(PreviewHandlerAttribute)); + var previewHandlerAttributes = attr[0] as PreviewHandlerAttribute; + + // Assert + Assert.AreEqual("valid-name", previewHandlerAttributes.Name); + Assert.AreEqual("valid-extension", previewHandlerAttributes.Extension); + Assert.AreEqual("valid-appid", previewHandlerAttributes.AppId); + } + } +} diff --git a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/PreviewHandlerBaseTests.cs b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/PreviewHandlerBaseTests.cs new file mode 100644 index 000000000000..349949ac2d11 --- /dev/null +++ b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/PreviewHandlerBaseTests.cs @@ -0,0 +1,391 @@ +// 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.ComponentModel; +using System.Drawing; +using System.Drawing.Drawing2D; +using Common; +using Common.ComInterlop; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace UnitTests_PreviewHandlerCommon +{ + [TestClass] + public class PreviewHandlerBaseTests + { + private static IPreviewHandlerControl previewHandlerControl; + + public class TestPreviewHandler : PreviewHandlerBase + { + public override void DoPreview() + { + throw new NotImplementedException(); + } + + protected override IPreviewHandlerControl CreatePreviewHandlerControl() + { + return GetMockPreviewHandlerControl(); + } + } + + [TestMethod] + public void PreviewHandlerBase_ShouldCallPreviewControlSetWindow_WhenSetWindowCalled() + { + // Arrange + var mockPreviewControl = new Mock(); + var handle = new IntPtr(5); + var bounds = GetRectangle(2, 2, 4, 4); + + var actualHandle = IntPtr.Zero; + var actualBounds = Rectangle.Empty; + mockPreviewControl + .Setup(_ => _.SetWindow(It.IsAny(), It.IsAny())) + .Callback((hwnd, rect) => + { + actualHandle = hwnd; + actualBounds = rect; + }); + + previewHandlerControl = mockPreviewControl.Object; + var testPreviewHandler = new TestPreviewHandler(); + + // Act + testPreviewHandler.SetWindow(handle, ref bounds); + + // Assert + Assert.AreEqual(actualHandle, handle); + Assert.AreEqual(actualBounds, bounds.ToRectangle()); + mockPreviewControl.Verify(_ => _.SetWindow(It.IsAny(), It.IsAny()), Times.Once); + } + + [TestMethod] + public void PreviewHandlerBase_ShouldCallPreviewControlSetrect_WhenSetRectCalled() + { + // Arrange + var mockPreviewControl = new Mock(); + var bounds = GetRectangle(2, 2, 4, 4); + + var actualBounds = Rectangle.Empty; + mockPreviewControl + .Setup(_ => _.SetRect(It.IsAny())) + .Callback((rect) => + { + actualBounds = rect; + }); + + previewHandlerControl = mockPreviewControl.Object; + var testPreviewHandler = new TestPreviewHandler(); + + // Act + testPreviewHandler.SetRect(ref bounds); + + // Assert + Assert.AreEqual(actualBounds, bounds.ToRectangle()); + mockPreviewControl.Verify(_ => _.SetRect(It.IsAny()), Times.Once); + } + + [TestMethod] + public void PreviewHandlerBase_ShouldCallPreviewControlUnload_WhenUnloadCalled() + { + // Arrange + var mockPreviewControl = new Mock(); + + previewHandlerControl = mockPreviewControl.Object; + var testPreviewHandler = new TestPreviewHandler(); + + // Act + testPreviewHandler.Unload(); + + // Assert + mockPreviewControl.Verify(_ => _.Unload(), Times.Once); + } + + [TestMethod] + public void PreviewHandlerBase_ShouldCallPreviewControlSetBackgroundColor_WhenSetBackgroundColorCalled() + { + // Arrange + var mockPreviewControl = new Mock(); + + previewHandlerControl = mockPreviewControl.Object; + var testPreviewHandler = new TestPreviewHandler(); + var color = new COLORREF(); + + // Act + testPreviewHandler.SetBackgroundColor(color); + + // Assert + mockPreviewControl.Verify(_ => _.SetBackgroundColor(It.Is(c => (c == color.Color))), Times.Once); + } + + [TestMethod] + public void PreviewHandlerBase_ShouldCallPreviewControlSetTextColor_WhenSetTextColorCalled() + { + // Arrange + var mockPreviewControl = new Mock(); + + previewHandlerControl = mockPreviewControl.Object; + var testPreviewHandler = new TestPreviewHandler(); + var color = new COLORREF(); + + // Act + testPreviewHandler.SetTextColor(color); + + // Assert + mockPreviewControl.Verify(_ => _.SetTextColor(It.Is(c => (c == color.Color))), Times.Once); + } + + [TestMethod] + public void PreviewHandlerBase_ShouldCallPreviewControlSetFont_WhenSetFontCalled() + { + // Arrange + Font actualFont = null; + var mockPreviewControl = new Mock(); + mockPreviewControl + .Setup(x => x.SetFont(It.IsAny())) + .Callback((font) => + { + actualFont = font; + }); + previewHandlerControl = mockPreviewControl.Object; + var testPreviewHandler = new TestPreviewHandler(); + var logFont = GetLogFont(); + + // Act + testPreviewHandler.SetFont(ref logFont); + + // Assert + mockPreviewControl.Verify(_ => _.SetFont(It.IsAny()), Times.Once); + Assert.AreEqual(Font.FromLogFont(logFont), actualFont); + } + + [TestMethod] + public void PreviewHandlerBase_ShouldCallPreviewControlSetFocus_WhenSetFocusCalled() + { + // Arrange + var mockPreviewControl = new Mock(); + + previewHandlerControl = mockPreviewControl.Object; + var testPreviewHandler = new TestPreviewHandler(); + + // Act + testPreviewHandler.SetFocus(); + + // Assert + mockPreviewControl.Verify(_ => _.SetFocus(), Times.Once); + } + + [TestMethod] + public void PreviewHandlerBase_ShouldSetHandleOnQueryFocus_WhenPreviewControlsReturnValidHandle() + { + // Arrange + var hwnd = new IntPtr(5); + var mockPreviewControl = new Mock(); + mockPreviewControl.Setup(x => x.QueryFocus(out hwnd)); + var actualHwnd = IntPtr.Zero; + + previewHandlerControl = mockPreviewControl.Object; + var testPreviewHandler = new TestPreviewHandler(); + + // Act + testPreviewHandler.QueryFocus(out actualHwnd); + + // Assert + Assert.AreEqual(actualHwnd, hwnd); + mockPreviewControl.Verify(_ => _.QueryFocus(out hwnd), Times.Once); + } + + [TestMethod] + public void PreviewHandlerBase_ShouldThrowOnQueryFocus_WhenPreviewControlsReturnNotValidHandle() + { + // Arrange + var hwnd = IntPtr.Zero; + var mockPreviewControl = new Mock(); + mockPreviewControl.Setup(x => x.QueryFocus(out hwnd)); + var actualHwnd = IntPtr.Zero; + + previewHandlerControl = mockPreviewControl.Object; + var testPreviewHandler = new TestPreviewHandler(); + Win32Exception exception = null; + + // Act + try + { + testPreviewHandler.QueryFocus(out actualHwnd); + } + catch (Win32Exception ex) + { + exception = ex; + } + + // Assert + Assert.IsNotNull(exception); + mockPreviewControl.Verify(_ => _.QueryFocus(out hwnd), Times.Once); + } + + [TestMethod] + public void PreviewHandlerBase_ShouldDirectKeyStrokesToIPreviewHandlerFrame_IfIPreviewHandlerFrameIsSet() + { + // Arrange + var mockPreviewControl = new Mock(); + var mockPreviewHandlerFrame = new Mock(); + var msg = new MSG(); + + previewHandlerControl = mockPreviewControl.Object; + var testPreviewHandler = new TestPreviewHandler(); + testPreviewHandler.SetSite(mockPreviewHandlerFrame.Object); + + // Act + testPreviewHandler.TranslateAccelerator(ref msg); + + // Assert + mockPreviewHandlerFrame.Verify(_ => _.TranslateAccelerator(ref msg), Times.Once); + } + + [DataTestMethod] + [DataRow((uint)0)] + [DataRow((uint)1)] + public void PreviewHandlerBase_ShouldReturnIPreviewHandlerFrameResponse_IfIPreviewHandlerFrameIsSet(uint resultCode) + { + // Arrange + var mockPreviewControl = new Mock(); + var mockPreviewHandlerFrame = new Mock(); + var msg = new MSG(); + mockPreviewHandlerFrame + .Setup(x => x.TranslateAccelerator(ref msg)) + .Returns(resultCode); + + previewHandlerControl = mockPreviewControl.Object; + var testPreviewHandler = new TestPreviewHandler(); + testPreviewHandler.SetSite(mockPreviewHandlerFrame.Object); + + // Act + var actualResultCode = testPreviewHandler.TranslateAccelerator(ref msg); + + // Assert + Assert.AreEqual(resultCode, actualResultCode); + } + + [TestMethod] + public void PreviewHandlerBase_ShouldReturnS_FALSE_IfIPreviewHandlerFrameIsNotSet() + { + // Arrange + var mockPreviewControl = new Mock(); + var msg = new MSG(); + uint S_FALSE = 1; + + previewHandlerControl = mockPreviewControl.Object; + var testPreviewHandler = new TestPreviewHandler(); + + // Act + var result = testPreviewHandler.TranslateAccelerator(ref msg); + + // Assert + Assert.AreEqual(result, S_FALSE); + } + + [TestMethod] + public void PreviewHandlerBase_ShouldReturnPreviewControlHandle_IfGetWindowCalled() + { + // Arrange + var previewControlHandle = new IntPtr(5); + var mockPreviewControl = new Mock(); + mockPreviewControl.Setup(x => x.GetHandle()) + .Returns(previewControlHandle); + + previewHandlerControl = mockPreviewControl.Object; + var testPreviewHandler = new TestPreviewHandler(); + var hwndReceived = IntPtr.Zero; + + // Act + testPreviewHandler.GetWindow(out hwndReceived); + + // Assert + Assert.AreEqual(hwndReceived, previewControlHandle); + } + + [DataTestMethod] + [DataRow(true)] + [DataRow(false)] + public void PreviewHandlerBase_ShouldThrowNotImplementedException_IfContextSensitiveHelpCalled(bool fEnterMode) + { + // Arrange + var mockPreviewControl = new Mock(); + + previewHandlerControl = mockPreviewControl.Object; + var testPreviewHandler = new TestPreviewHandler(); + NotImplementedException exception = null; + + // Act + try + { + testPreviewHandler.ContextSensitiveHelp(fEnterMode); + } + catch (NotImplementedException ex) + { + exception = ex; + } + + // Assert + Assert.IsNotNull(exception); + } + + [TestMethod] + public void PreviewHandlerBase_ShouldReturnSite_WhenGetSiteCalled() + { + // Arrange + var mockPreviewControl = new Mock(); + + previewHandlerControl = mockPreviewControl.Object; + var testPreviewHandler = new TestPreviewHandler(); + var site = new Mock().Object; + testPreviewHandler.SetSite(site); + object actualSite = null; + var riid = Guid.Empty; + + // Act + testPreviewHandler.GetSite(ref riid, out actualSite); + + // Assert + Assert.AreEqual(actualSite, site); + } + + private LOGFONT GetLogFont() + { + var logFont = new LOGFONT(); + logFont.LfHeight = 12; + logFont.LfWidth = 0; + logFont.LfEscapement = 0; + logFont.LfWeight = 400; // FW_NORMAL + logFont.LfItalic = Convert.ToByte(false); + logFont.LfUnderline = Convert.ToByte(false); + logFont.LfStrikeOut = Convert.ToByte(0); + logFont.LfCharSet = Convert.ToByte(0); // ANSI_CHARSET + logFont.LfOutPrecision = Convert.ToByte(0); // OUT_DEFAULT_PRECIS + logFont.LfClipPrecision = Convert.ToByte(0); + logFont.LfQuality = Convert.ToByte(0); + logFont.LfPitchAndFamily = Convert.ToByte(0); + logFont.LfFaceName = "valid-font"; + + return logFont; + } + + private RECT GetRectangle(int left, int top, int right, int bottom) + { + var rect = new RECT(); + rect.Left = left; + rect.Top = top; + rect.Right = right; + rect.Bottom = bottom; + + return rect; + } + + private static IPreviewHandlerControl GetMockPreviewHandlerControl() + { + return previewHandlerControl; + } + } +} diff --git a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/Properties/AssemblyInfo.cs b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..52ef838110c3 --- /dev/null +++ b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("UnitTests-PreviewHandlerCommon")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("UnitTests-PreviewHandlerCommon")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("748417ca-f17e-487f-9411-cafb6d3f4877")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/StreamBasedPreviewHandlerTests.cs b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/StreamBasedPreviewHandlerTests.cs new file mode 100644 index 000000000000..fd62317879df --- /dev/null +++ b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/StreamBasedPreviewHandlerTests.cs @@ -0,0 +1,45 @@ +// 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.ComTypes; +using Common; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace UnitTests_PreviewHandlerCommon +{ + [TestClass] + public class StreamBasedPreviewHandlerTests + { + public class TestStreamBasedPreviewHandler : StreamBasedPreviewHandler + { + public override void DoPreview() + { + throw new NotImplementedException(); + } + + protected override IPreviewHandlerControl CreatePreviewHandlerControl() + { + return new Mock().Object; + } + } + + [DataTestMethod] + [DataRow((uint)0)] + [DataRow((uint)1)] + public void FileBasedPreviewHandler_ShouldSetFilePath_WhenInitializeCalled(uint grfMode) + { + // Arrange + var streamBasedPreviewHandler = new TestStreamBasedPreviewHandler(); + var stream = new Mock().Object; + + // Act + streamBasedPreviewHandler.Initialize(stream, grfMode); + + // Assert + Assert.AreEqual(streamBasedPreviewHandler.Stream, stream); + } + } +} diff --git a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/StreamWrapperTests.cs b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/StreamWrapperTests.cs new file mode 100644 index 000000000000..51ebec30277a --- /dev/null +++ b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/StreamWrapperTests.cs @@ -0,0 +1,332 @@ +// 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 Castle.Core.Logging; +using Common.Utilities; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.IO; +using System.Linq; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; + +namespace UnitTests_PreviewHandlerCommon +{ + [TestClass] + public class StreamWrapperTests + { + [TestMethod] + public void StreamWrapper_ShouldThrow_IfInitializeWithNullStream() + { + // Arrange + IStream stream = null; + ArgumentNullException exception = null; + + // Act + try + { + var streamWrapper = new StreamWrapper(stream); + } + catch (ArgumentNullException ex) + { + exception = ex; + } + + // Assert + Assert.IsNotNull(exception); + } + + [TestMethod] + public void StreamWrapper_ShouldReturnCanReadTrue() + { + // Arrange + var streamMock = new Mock(); + + // Act + var streamWrapper = new StreamWrapper(streamMock.Object); + + // Assert + Assert.AreEqual(streamWrapper.CanRead, true); + } + + [TestMethod] + public void StreamWrapper_ShouldReturnCanSeekTrue() + { + // Arrange + var streamMock = new Mock(); + + // Act + var streamWrapper = new StreamWrapper(streamMock.Object); + + // Assert + Assert.AreEqual(streamWrapper.CanSeek, true); + } + + [TestMethod] + public void StreamWrapper_ShouldReturnCanWriteFalse() + { + // Arrange + var streamMock = new Mock(); + + // Act + var streamWrapper = new StreamWrapper(streamMock.Object); + + // Assert + Assert.AreEqual(streamWrapper.CanWrite, false); + } + + [TestMethod] + public void StreamWrapper_ShouldReturnValidLength() + { + // Arrange + long streamLength = 5; + var stremMock = new Mock(); + var stat = new System.Runtime.InteropServices.ComTypes.STATSTG(); + stat.cbSize = streamLength; + + stremMock + .Setup(x => x.Stat(out stat, It.IsAny())); + var streamWrapper = new StreamWrapper(stremMock.Object); + + // Act + var actualLength = streamWrapper.Length; + + // Assert + Assert.AreEqual(actualLength, streamLength); + } + + [TestMethod] + public void StreamWrapper_ShouldReturnValidPosition() + { + // Arrange + int expectedDwOrigin = 1; // STREAM_SEEK_CUR + long expectedOffset = 0; + long currPosition = 5; + var stremMock = new Mock(); + + stremMock + .Setup(x => x.Seek(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((dlibMove, dwOrigin, plibNewPosition) => + { + Marshal.WriteInt64(plibNewPosition, currPosition); + }); + var streamWrapper = new StreamWrapper(stremMock.Object); + + // Act + var actualPosition = streamWrapper.Position; + + // Assert + Assert.AreEqual(actualPosition, currPosition); + stremMock.Verify(_ => _.Seek(It.Is(offset => offset == expectedOffset), It.Is(dworigin => dworigin == expectedDwOrigin), It.IsAny()), Times.Once); + } + + [TestMethod] + public void StreamWrapper_ShouldCallIStreamSeek_WhenSetPosition() + { + // Arrange + long positionToSet = 5; + int expectedDwOrigin = 0; // STREAM_SEEK_SET + var stremMock = new Mock(); + + var streamWrapper = new StreamWrapper(stremMock.Object); + + // Act + streamWrapper.Position = positionToSet; + + // Assert + stremMock.Verify(_ => _.Seek(It.Is(offset => offset == positionToSet), It.Is(dworigin => dworigin == expectedDwOrigin), It.IsAny()), Times.Once); + } + + [DataTestMethod] + [DataRow((long)0, SeekOrigin.Begin)] + [DataRow((long)5, SeekOrigin.Begin)] + [DataRow((long)0, SeekOrigin.Current)] + [DataRow((long)5, SeekOrigin.Current)] + [DataRow((long)0, SeekOrigin.End)] + [DataRow((long)5, SeekOrigin.End)] + public void StreamWrapper_ShouldCallIStreamSeekWithValidArguments_WhenSeekCalled(long offset, SeekOrigin origin) + { + // Arrange + int expectedDwOrigin = 0; + switch (origin) + { + case SeekOrigin.Begin: + expectedDwOrigin = 0; + break; + + case SeekOrigin.Current: + expectedDwOrigin = 1; + break; + + case SeekOrigin.End: + expectedDwOrigin = 2; + break; + } + + var stremMock = new Mock(); + var streamWrapper = new StreamWrapper(stremMock.Object); + + // Act + streamWrapper.Seek(offset, origin); + + // Assert + stremMock.Verify(_ => _.Seek(It.Is(actualOffset => actualOffset == offset), It.Is(actualDwOrigin => actualDwOrigin == expectedDwOrigin), It.IsAny()), Times.Once); + } + + [TestMethod] + public void StreamWrapper_ShouldReturnValidPosition_WhenSeekCalled() + { + // Arrange + long position = 5; + var stremMock = new Mock(); + + stremMock + .Setup(x => x.Seek(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((dlibMove, dwOrigin, plibNewPosition) => + { + Marshal.WriteInt64(plibNewPosition, position); + }); + + var streamWrapper = new StreamWrapper(stremMock.Object); + + // Act + var actualPosition = streamWrapper.Seek(0, SeekOrigin.Begin); + + // Assert + Assert.AreEqual(position, actualPosition); + } + + [DataTestMethod] + [DataRow(10, -1, 5)] + [DataRow(10, 0, -5)] + [DataRow(10, 0, 11)] + [DataRow(10, 5, 6)] + public void StreamWrapper_ShouldThrow_WhenReadCalledWithOutOfRangeArguments(int bufferLength, int offSet, int bytesToRead) + { + // Arrange + var buffer = new byte[bufferLength]; + var stremMock = new Mock(); + ArgumentOutOfRangeException exception = null; + + var streamWrapper = new StreamWrapper(stremMock.Object); + + // Act + try + { + streamWrapper.Read(buffer, offSet, bytesToRead); + } + catch (ArgumentOutOfRangeException ex) + { + exception = ex; + } + + // Assert + Assert.IsNotNull(exception); + } + + [DataTestMethod] + [DataRow(5, 0)] + [DataRow(5, 5)] + [DataRow(0, 5)] + public void StreamWrapper_ShouldSetValidBuffer_WhenReadCalled(int count, int offset) + { + // Arrange + var inputBuffer = new byte[1024]; + var streamBytes = new byte[count]; + for (int i = 0; i < count; i++) + { + streamBytes[i] = (byte)i; + } + + var stremMock = new Mock(); + + stremMock + .Setup(x => x.Read(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((buffer, countToRead , bytesReadPtr) => + { + Array.Copy(streamBytes, 0, buffer, 0, streamBytes.Length); + Marshal.WriteInt32(bytesReadPtr, count); + }); + + var streamWrapper = new StreamWrapper(stremMock.Object); + + // Act + var bytesRead = streamWrapper.Read(inputBuffer, offset, count); + + // Assert + CollectionAssert.AreEqual(streamBytes, inputBuffer.Skip(offset).Take(count).ToArray()); + Assert.AreEqual(count, bytesRead); + } + + [TestMethod] + public void StreamWrapper_ShouldThrowNotImplementedException_WhenFlushCalled() + { + // Arrange + var stremMock = new Mock(); + var streamWrapper = new StreamWrapper(stremMock.Object); + NotImplementedException exception = null; + + // Act + try + { + streamWrapper.Flush(); + } + catch (NotImplementedException ex) + { + exception = ex; + } + + // Assert + Assert.IsNotNull(exception); + } + + [TestMethod] + public void StreamWrapper_ShouldThrowNotImplementedException_WhenSetLengthCalled() + { + // Arrange + var stremMock = new Mock(); + var streamWrapper = new StreamWrapper(stremMock.Object); + NotImplementedException exception = null; + + // Act + try + { + streamWrapper.SetLength(5); + } + catch (NotImplementedException ex) + { + exception = ex; + } + + // Assert + Assert.IsNotNull(exception); + } + + [TestMethod] + public void StreamWrapper_ShouldThrowNotImplementedException_WhenWriteCalled() + { + // Arrange + var stremMock = new Mock(); + var streamWrapper = new StreamWrapper(stremMock.Object); + NotImplementedException exception = null; + + // Act + try + { + streamWrapper.Write(new byte[5], 0, 0); + } + catch (NotImplementedException ex) + { + exception = ex; + } + + // Assert + Assert.IsNotNull(exception); + } + + + } +} diff --git a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/UnitTests-PreviewHandlerCommon.csproj b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/UnitTests-PreviewHandlerCommon.csproj new file mode 100644 index 000000000000..999662d8a194 --- /dev/null +++ b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/UnitTests-PreviewHandlerCommon.csproj @@ -0,0 +1,76 @@ + + + + + Debug + AnyCPU + {748417CA-F17E-487F-9411-CAFB6D3F4877} + Library + Properties + UnitTests_PreviewHandlerCommon + UnitTests-PreviewHandlerCommon + v4.8 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + x64 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + x64 + + + + + + + + + + + + + + + + + + + 4.13.1 + + + 1.3.2 + + + 1.3.2 + + + + + {af2349b8-e5b6-4004-9502-687c1c7730b1} + PreviewHandlerCommon + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/UnitTests-SvgPreviewHandler/Properties/AssemblyInfo.cs b/src/modules/previewpane/UnitTests-SvgPreviewHandler/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..48cfa2bbd020 --- /dev/null +++ b/src/modules/previewpane/UnitTests-SvgPreviewHandler/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("UnitTests-SvgPreviewHandler")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("UnitTests-SvgPreviewHandler")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("060d75da-2d1c-48e6-a4a1-6f0718b64661")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/modules/previewpane/UnitTests-SvgPreviewHandler/SvgPreviewControlTests.cs b/src/modules/previewpane/UnitTests-SvgPreviewHandler/SvgPreviewControlTests.cs new file mode 100644 index 000000000000..c5ec5a838cb1 --- /dev/null +++ b/src/modules/previewpane/UnitTests-SvgPreviewHandler/SvgPreviewControlTests.cs @@ -0,0 +1,191 @@ +using System; +using System.Drawing; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using System.Windows.Forms; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using SvgPreviewHandler; + +namespace UnitTests_SvgPreviewHandler +{ + [TestClass] + public class SvgPreviewControlTests + { + [TestMethod] + public void SvgPreviewControl_ShouldAddBrowserControl_WhenDoPreviewCalled() + { + // Arrange + var svgPreviewControl = new SvgPreviewControl(); + + // Act + svgPreviewControl.DoPreview(GetMockStream("")); + + // Assert + Assert.AreEqual(svgPreviewControl.Controls.Count, 1); + Assert.IsInstanceOfType(svgPreviewControl.Controls[0], typeof(WebBrowser)); + } + + [TestMethod] + public void SvgPreviewControl_ShouldSetDocumentStream_WhenDoPreviewCalled() + { + // Arrange + var svgPreviewControl = new SvgPreviewControl(); + + // Act + svgPreviewControl.DoPreview(GetMockStream("")); + + // Assert + Assert.IsNotNull(((WebBrowser)svgPreviewControl.Controls[0]).DocumentStream); + } + + [TestMethod] + public void SvgPreviewControl_ShouldDisableWebBrowserContextMenu_WhenDoPreviewCalled() + { + // Arrange + var svgPreviewControl = new SvgPreviewControl(); + + // Act + svgPreviewControl.DoPreview(GetMockStream("")); + + // Assert + Assert.AreEqual(((WebBrowser)svgPreviewControl.Controls[0]).IsWebBrowserContextMenuEnabled, false); + } + + [TestMethod] + public void SvgPreviewControl_ShouldFillDockForWebBrowser_WhenDoPreviewCalled() + { + // Arrange + var svgPreviewControl = new SvgPreviewControl(); + + // Act + svgPreviewControl.DoPreview(GetMockStream("")); + + // Assert + Assert.AreEqual(((WebBrowser)svgPreviewControl.Controls[0]).Dock, DockStyle.Fill); + } + + [TestMethod] + public void SvgPreviewControl_ShouldSetScriptErrorsSuppressedProperty_WhenDoPreviewCalled() + { + // Arrange + var svgPreviewControl = new SvgPreviewControl(); + + // Act + svgPreviewControl.DoPreview(GetMockStream("")); + + // Assert + Assert.AreEqual(((WebBrowser)svgPreviewControl.Controls[0]).ScriptErrorsSuppressed, true); + } + + [TestMethod] + public void SvgPreviewControl_ShouldSetScrollBarsEnabledProperty_WhenDoPreviewCalled() + { + // Arrange + var svgPreviewControl = new SvgPreviewControl(); + + // Act + svgPreviewControl.DoPreview(GetMockStream("")); + + // Assert + Assert.AreEqual(((WebBrowser)svgPreviewControl.Controls[0]).ScrollBarsEnabled, true); + } + + [TestMethod] + public void SvgPreviewControl_ShouldAddTextBox_IfBlockedElementsArePresent() + { + // Arrange + var svgPreviewControl = new SvgPreviewControl(); + + // Act + svgPreviewControl.DoPreview(GetMockStream("\r\n \r\n ")); + + // Assert + Assert.IsInstanceOfType(svgPreviewControl.Controls[0], typeof(RichTextBox)); + Assert.IsInstanceOfType(svgPreviewControl.Controls[1], typeof(WebBrowser)); + Assert.AreEqual(svgPreviewControl.Controls.Count, 2); + } + + [TestMethod] + public void SvgPreviewControl_ShouldNotAddTextBox_IfNoBlockedElementsArePresent() + { + // Arrange + var svgPreviewControl = new SvgPreviewControl(); + + // Act + svgPreviewControl.DoPreview(GetMockStream("valid")); + + // Assert + Assert.IsNotInstanceOfType(svgPreviewControl.Controls[0], typeof(RichTextBox)); + Assert.AreEqual(svgPreviewControl.Controls.Count, 1); + } + + [TestMethod] + public void SvgPreviewControl_ShouldAddValidTextBox_IfBlockedElementsArePresent() + { + // Arrange + var svgPreviewControl = new SvgPreviewControl(); + + // Act + svgPreviewControl.DoPreview(GetMockStream("\r\n \r\n ")); + var textBox = svgPreviewControl.Controls[0] as RichTextBox; + + // Assert + Assert.IsFalse(string.IsNullOrWhiteSpace(textBox.Text)); + Assert.AreEqual(textBox.Dock, DockStyle.Top); + Assert.AreEqual(textBox.BackColor, Color.LightYellow); + Assert.IsTrue(textBox.Multiline); + Assert.IsTrue(textBox.ReadOnly); + Assert.AreEqual(textBox.ScrollBars, RichTextBoxScrollBars.None); + Assert.AreEqual(textBox.BorderStyle, BorderStyle.None); + } + + [TestMethod] + public void SvgPreviewControl_RichTextBoxWidthShouldAdjust_IfParentControlWidthChanges() + { + // Arrange + var svgPreviewControl = new SvgPreviewControl(); + svgPreviewControl.DoPreview(GetMockStream("\r\n \r\n ")); + var textBox = svgPreviewControl.Controls[0] as RichTextBox; + var incrementParentControlWidth = 5; + var intialParentWidth = svgPreviewControl.Width; + var intitialTextBoxWidth = textBox.Width; + var finalParentWidth = intialParentWidth + incrementParentControlWidth; + + // Act + svgPreviewControl.Width += incrementParentControlWidth; + + // Assert + Assert.AreEqual(intialParentWidth, intitialTextBoxWidth); + Assert.AreEqual(finalParentWidth, textBox.Width); + } + + private IStream GetMockStream(string streamData) + { + var mockStream = new Mock(); + var streamBytes = Encoding.UTF8.GetBytes(streamData); + + var streamMock = new Mock(); + var firstCall = true; + streamMock + .Setup(x => x.Read(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((buffer, countToRead, bytesReadPtr) => + { + if (firstCall) + { + Array.Copy(streamBytes, 0, buffer, 0, streamBytes.Length); + Marshal.WriteInt32(bytesReadPtr, streamBytes.Length); + firstCall = false; + } + else + { + Marshal.WriteInt32(bytesReadPtr, 0); + } + }); + + return streamMock.Object; + } + } +} diff --git a/src/modules/previewpane/UnitTests-SvgPreviewHandler/SvgPreviewHandlerHelperTests.cs b/src/modules/previewpane/UnitTests-SvgPreviewHandler/SvgPreviewHandlerHelperTests.cs new file mode 100644 index 000000000000..64a77f793297 --- /dev/null +++ b/src/modules/previewpane/UnitTests-SvgPreviewHandler/SvgPreviewHandlerHelperTests.cs @@ -0,0 +1,133 @@ +using Castle.Components.DictionaryAdapter.Xml; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SvgPreviewHandler.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace UnitTests_SvgPreviewHandler +{ + [TestClass] + public class SvgPreviewHandlerHelperTests + { + [DataTestMethod] + [DataRow("script")] + [DataRow("image")] + public void RemoveElements_ShoudSetfoundBlockedElementTrue_IfBlockedElementFound(string element) + { + // Arrange + var xmlTree = new XElement("svg", + new XElement(element, "valid") + ); + var svgData = xmlTree.ToString(); + bool foundFilteredElement; + + // Act + SvgPreviewHandlerHelper.RemoveElements(svgData, out foundFilteredElement); + + // Assert + Assert.IsTrue(foundFilteredElement); + } + + [TestMethod] + public void RemoveElements_ShoudSetfoundBlockedElementFalse_IfNoBlockedElementFound() + { + // Arrange + var xmlTree = new XElement("svg", + new XElement("validElement", "valid") + ); + var svgData = xmlTree.ToString(); + bool foundFilteredElement; + + // Act + SvgPreviewHandlerHelper.RemoveElements(svgData, out foundFilteredElement); + + // Assert + Assert.IsFalse(foundFilteredElement); + } + + [TestMethod] + public void RemoveElements_ShoudRemoveBlockedElements_IfPresent() + { + // Arrange + var xmlTree = new XElement("svg", + new XElement("script", "valid") + ); + var svgData = xmlTree.ToString(); + var expectedXmlTree = new XElement("svg"); + var expectedSvgData = expectedXmlTree.ToString(); + bool foundFilteredElement; + + // Act + var actualSvgData = SvgPreviewHandlerHelper.RemoveElements(svgData, out foundFilteredElement); + + // Assert + Assert.AreEqual(expectedSvgData, actualSvgData); + } + + [TestMethod] + public void RemoveElements_ShoudRemoveBlockedElements_IfPresentAtSubChildLevel() + { + // Arrange + var xmlTree = new XElement("svg", + new XElement("valid", new XElement("script", "valid-content")) + ); + var svgData = xmlTree.ToString(); + var expectedXmlTree = new XElement("svg", new XElement("valid")); + var expectedSvgData = expectedXmlTree.ToString(); + bool foundFilteredElement; + + // Act + var actualSvgData = SvgPreviewHandlerHelper.RemoveElements(svgData, out foundFilteredElement); + + // Assert + Assert.AreEqual(expectedSvgData, actualSvgData); + } + + [TestMethod] + public void RemoveElements_ShoudRemoveBlockedElementsAndChildElement_IfPresent() + { + // Arrange + var xmlTree = new XElement("svg", + new XElement("script", new XElement("validChild1", new XElement("validChild2", "valid"))) + ); + var svgData = xmlTree.ToString(); + var expectedXmlTree = new XElement("svg"); + var expectedSvgData = expectedXmlTree.ToString(); + bool foundFilteredElement; + + // Act + var actualSvgData = SvgPreviewHandlerHelper.RemoveElements(svgData, out foundFilteredElement); + + // Assert + Assert.AreEqual(expectedSvgData, actualSvgData); + } + + [TestMethod] + public void RemoveElements_ShoudRemoveAllBlockedElements_IfMultipleElementsArePresent() + { + // Arrange + var xmlTree = new XElement("svg", + new XElement("script", "valid-script-1"), + new XElement("script", "valid-script-2"), + new XElement("image", "valid-image"), + new XElement("valid-element", "valid") + ); + var svgData = xmlTree.ToString(); + var expectedXmlTree = new XElement("svg", + new XElement("valid-element", "valid") + ); + var expectedSvgData = expectedXmlTree.ToString(); + bool foundFilteredElement; + + // Act + var actualSvgData = SvgPreviewHandlerHelper.RemoveElements(svgData, out foundFilteredElement); + + // Assert + Assert.AreEqual(expectedSvgData, actualSvgData); + } + } +} diff --git a/src/modules/previewpane/UnitTests-SvgPreviewHandler/UnitTests-SvgPreviewHandler.csproj b/src/modules/previewpane/UnitTests-SvgPreviewHandler/UnitTests-SvgPreviewHandler.csproj new file mode 100644 index 000000000000..b9195a7fe167 --- /dev/null +++ b/src/modules/previewpane/UnitTests-SvgPreviewHandler/UnitTests-SvgPreviewHandler.csproj @@ -0,0 +1,78 @@ + + + + + Debug + AnyCPU + {060D75DA-2D1C-48E6-A4A1-6F0718B64661} + Library + Properties + UnitTests_SvgPreviewHandler + UnitTests-SvgPreviewHandler + v4.8 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + x64 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + x64 + + + + + + + + + + + + + + + + 4.13.1 + + + 1.3.2 + + + 1.3.2 + + + + + {AF2349B8-E5B6-4004-9502-687C1C7730B1} + PreviewHandlerCommon + + + {DA425894-6E13-404F-8DCB-78584EC0557A} + SvgPreviewHandler + False + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/common/PowerToys - Shortcut.lnk b/src/modules/previewpane/common/PowerToys - Shortcut.lnk new file mode 100644 index 0000000000000000000000000000000000000000..79a55646a5fce8c71b27aa0005496b7434184c2a GIT binary patch literal 1293 zcmb7DUr1A76hBuDQlk`?imbH2rNm@&nQo-18~bCXlR442gr#%;+@|-sOz4kK!OBq} zhEL^#=0htA@=s;ri}X@r)`ENyiV6x9@kL6ibMAFBNeIvQedm1Vp6_?gcfPYq0AM6B zxB^TGE9h^)Zb(A=mOa5_eG_KBN<*uM<~B8CiGhr$qN{_^K&H-+5!=9>9M%P?X36?tk4*C^E!!;hd=h8l@jj)82F8%L7R$A#QmjxGpmg0RmC-9_}!_v z<(u@a5rG8{)T1WxIK579hy45nwcY2!Stu_V?VoQqn1&v$T^Tv^$x&~wksFJ@C6BwF zxYy<3vHyLS;Z5U_--Fyri`lZMcrmYqfMIxTHf4}+Ib4JOW=fp7fz6l9f*jC-1}|vE z-qSnqX!|jo2Pb&Jj=R4nDVJyj3F1o0K3=caPgE9ZpRcF%%KAMs?dXrA2uh|vSU`@W zB8LJ*zF;X)AH#h}V?p9VP%(;? z?6GA-M$*tqOk>ZT@1bn;Cr|(tL&kRVMt6! + + + + Debug + AnyCPU + {AF2349B8-E5B6-4004-9502-687C1C7730B1} + Library + Properties + PreviewHandlerCommon + PreviewHandlerCommon + v4.8 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + bin/Debug/PreviewHandlerCommonDocumentation.xml + x64 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + bin/Release/PreviewHandlerCommonDocumentation.xml + x64 + + + true + + + PreviewHandlerCommon.snk + + + + + + + + + + + + + + + + + + + + + + + + + + + Form + + + + Form + + + + + + + + + + + + + StyleCop.json + + + + + + 1.1.118 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + \ No newline at end of file diff --git a/src/modules/previewpane/common/PreviewHandlerCommon.snk b/src/modules/previewpane/common/PreviewHandlerCommon.snk new file mode 100644 index 0000000000000000000000000000000000000000..ffa5460428d19fd4b8661042ba5b758e4673daa1 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50097f(#i0|EH5J%<93GRij&#o9sjzl?y1!p zu{ZU%X~E1``BeMDci9EHMafrS9X3@~Oh_VB%|5gvfuQI@eJDoDs*VXwQ_H`2DVdX&TK zlskhK#N0K57$`B;R&z9>(2}5ZV7+ID3ypggx#{JlNm_v^e2irfC(cS& zEPI6HAybeY>0IeIe9{w0a9*gUG;uCk=mp@k5<4E}tyc4uhn+4>(c) zZ&6Fv>K!#gz#AONf+(>o_=b7*O^w#6l8}EX>oXkFr)m{zf#ERsb{UEo8saNM8P#4F zemfI!=3x146P-2hgtv8Zfx^BaiKKq76Hw3JXL{^o?;N=viLZK>h|f3so};|Ppz8eSt literal 0 HcmV?d00001 diff --git a/src/modules/previewpane/common/Properties/AssemblyInfo.cs b/src/modules/previewpane/common/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..0afe1c322bbb --- /dev/null +++ b/src/modules/previewpane/common/Properties/AssemblyInfo.cs @@ -0,0 +1,40 @@ +// 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.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Common")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Common")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("af2349b8-e5b6-4004-9502-687c1c7730b1")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/modules/previewpane/common/Telemetry/TelemetryBase.cs b/src/modules/previewpane/common/Telemetry/TelemetryBase.cs new file mode 100644 index 000000000000..1440aec9d620 --- /dev/null +++ b/src/modules/previewpane/common/Telemetry/TelemetryBase.cs @@ -0,0 +1,44 @@ +// 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.Diagnostics.Eventing.Reader; +using System.Diagnostics.Tracing; + +namespace PreviewHandlerCommon.Telemetry +{ + /// + /// Base class for telemetry events. + /// + public class TelemetryBase : EventSource + { + /// + /// The event tag for this event source. + /// + public const EventTags ProjectTelemetryTagProductAndServicePerformance = (EventTags)0x0u; + + /// + /// The event keyword for this event source. + /// + public const EventKeywords ProjectKeywordMeasure = (EventKeywords)0x0; + + /// + /// Group ID for Powertoys project. + /// + private static readonly string[] PowerToysTelemetryTraits = { "ETW_GROUP", "{42749043-438c-46a2-82be-c6cbeb192ff2}" }; + + /// + /// Initializes a new instance of the class. + /// + /// . + public TelemetryBase( + string eventSourceName) + : base( + eventSourceName, + EventSourceSettings.EtwSelfDescribingEventFormat, + PowerToysTelemetryTraits) + { + return; + } + } +} diff --git a/src/modules/previewpane/common/Utilities/StreamWrapper.cs b/src/modules/previewpane/common/Utilities/StreamWrapper.cs new file mode 100644 index 000000000000..8d31b9eff7d4 --- /dev/null +++ b/src/modules/previewpane/common/Utilities/StreamWrapper.cs @@ -0,0 +1,242 @@ +// 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.IO; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; + +namespace Common.Utilities +{ + /// + /// Wraps interface into Class. + /// + /// + /// Implements only read from the stream functionality. + /// + public class StreamWrapper : Stream + { + private IStream stream; + + /// + /// Initializes a new instance of the class. + /// + /// A pointer to an interface that represents the stream source. + public StreamWrapper(IStream stream) + { + this.stream = stream ?? throw new ArgumentNullException(nameof(stream)); + } + + /// + /// Gets a value indicating whether the current stream supports reading. + /// + public override bool CanRead + { + get + { + return true; + } + } + + /// + /// Gets a value indicating whether the current stream supports seeking. + /// + public override bool CanSeek + { + get + { + return true; + } + } + + /// + /// Gets a value indicating whether the current stream supports writing. + /// + public override bool CanWrite + { + get + { + return false; + } + } + + /// + /// Gets the length in bytes of the stream. + /// + public override long Length + { + get + { + this.CheckDisposed(); + System.Runtime.InteropServices.ComTypes.STATSTG stat; + + // Stat called with STATFLAG_NONAME. The pwcsName is not required more details https://docs.microsoft.com/en-us/windows/win32/api/wtypes/ne-wtypes-statflag + this.stream.Stat(out stat, 1); // STATFLAG_NONAME + + return stat.cbSize; + } + } + + /// + /// Gets or Sets the position within the current. + /// + public override long Position + { + get + { + return this.Seek(0, SeekOrigin.Current); + } + + set + { + this.Seek(value, SeekOrigin.Begin); + } + } + + /// + /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. + /// + /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source. + /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream. + /// The maximum number of bytes to be read from the current stream. + /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero if the end of the stream has been reached. + public override int Read(byte[] buffer, int offset, int count) + { + this.CheckDisposed(); + + if (offset < 0 || count < 0 || offset + count > buffer.Length) + { + throw new ArgumentOutOfRangeException(); + } + + byte[] localBuffer = buffer; + + if (offset > 0) + { + localBuffer = new byte[count]; + } + + IntPtr bytesReadPtr = Marshal.AllocCoTaskMem(sizeof(int)); + + try + { + this.stream.Read(localBuffer, count, bytesReadPtr); + int bytesRead = Marshal.ReadInt32(bytesReadPtr); + + if (offset > 0) + { + Array.Copy(localBuffer, 0, buffer, offset, bytesRead); + } + + return bytesRead; + } + finally + { + Marshal.FreeCoTaskMem(bytesReadPtr); + } + } + + /// + /// Sets the position within the current stream. + /// + /// A byte offset relative to the origin parameter. + /// A value of type System.IO.SeekOrigin indicating the reference point used to obtain the new position. + /// The new position within the current stream. + public override long Seek(long offset, SeekOrigin origin) + { + this.CheckDisposed(); + int dwOrigin; + + // Maps the SeekOrigin with dworigin more details: https://docs.microsoft.com/en-us/windows/win32/api/objidl/ne-objidl-stream_seek + switch (origin) + { + case SeekOrigin.Begin: + dwOrigin = 0; // STREAM_SEEK_SET + break; + + case SeekOrigin.Current: + dwOrigin = 1; // STREAM_SEEK_CUR + break; + + case SeekOrigin.End: + dwOrigin = 2; // STREAM_SEEK_END + break; + + default: + throw new ArgumentOutOfRangeException(); + } + + IntPtr posPtr = Marshal.AllocCoTaskMem(sizeof(long)); + + try + { + this.stream.Seek(offset, dwOrigin, posPtr); + return Marshal.ReadInt64(posPtr); + } + finally + { + Marshal.FreeCoTaskMem(posPtr); + } + } + + /// + /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. + /// + /// + /// Not implemented current implementation supports only read. + /// + public override void Flush() + { + throw new NotImplementedException(); + } + + /// + /// Sets the length of the current stream. + /// + /// The desired length of the current stream in bytes. + /// /// + /// Not implemented current implementation supports only read. + /// + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + /// + /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. + /// + /// An array of bytes. This method copies count bytes from buffer to the current stream. + /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. + /// The number of bytes to be written to the current stream. + /// + /// Not implemented current implementation supports only read. + /// + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + + /// + protected override void Dispose(bool disposing) + { + if (this.stream != null) + { + if (Marshal.IsComObject(this.stream)) + { + Marshal.ReleaseComObject(this.stream); + } + + this.stream = null; + } + } + + private void CheckDisposed() + { + if (this.stream == null) + { + throw new ObjectDisposedException(nameof(StreamWrapper)); + } + } + } +} diff --git a/src/modules/previewpane/common/cominterop/COLORREF.cs b/src/modules/previewpane/common/cominterop/COLORREF.cs new file mode 100644 index 000000000000..ef9767516767 --- /dev/null +++ b/src/modules/previewpane/common/cominterop/COLORREF.cs @@ -0,0 +1,35 @@ +// 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.Drawing; +using System.Runtime.InteropServices; + +namespace Common.ComInterlop +{ + /// + /// The COLORREF value is used to specify an RGB color. + /// + [StructLayout(LayoutKind.Sequential)] + public struct COLORREF + { + /// + /// Stores an RGB color value in a 32 bit integer. + /// + public uint Dword; + + /// + /// Gets RGB value stored in in structure. + /// + public Color Color + { + get + { + return Color.FromArgb( + (int)(0x000000FFU & this.Dword), + (int)(0x0000FF00U & this.Dword) >> 8, + (int)(0x00FF0000U & this.Dword) >> 16); + } + } + } +} diff --git a/src/modules/previewpane/common/cominterop/IInitializeWithFile.cs b/src/modules/previewpane/common/cominterop/IInitializeWithFile.cs new file mode 100644 index 000000000000..55eddeec52d2 --- /dev/null +++ b/src/modules/previewpane/common/cominterop/IInitializeWithFile.cs @@ -0,0 +1,25 @@ +// 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; + +namespace Common.Cominterop +{ + /// + /// Exposes a method to initialize a handler, such as a property handler, thumbnail handler, or preview handler, with a file path. + /// + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("b7d14566-0509-4cce-a71f-0a554233bd9b")] + public interface IInitializeWithFile + { + /// + /// Initializes a handler with a file path. + /// + /// File Path. + /// Indicate the Access Mode either STGM_READ (Read Only Access) or STGM_READWRITE (Read and Write Access). + void Initialize([MarshalAs(UnmanagedType.LPWStr)] string pszFilePath, uint grfMode); + } +} diff --git a/src/modules/previewpane/common/cominterop/IInitializeWithStream.cs b/src/modules/previewpane/common/cominterop/IInitializeWithStream.cs new file mode 100644 index 000000000000..fc36cb3e8d63 --- /dev/null +++ b/src/modules/previewpane/common/cominterop/IInitializeWithStream.cs @@ -0,0 +1,26 @@ +// 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.Runtime.InteropServices.ComTypes; + +namespace Common.ComInterlop +{ + /// + /// Exposes a method that initializes a handler, such as a property handler, thumbnail handler, or preview handler, with a stream. + /// + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f")] + public interface IInitializeWithStream + { + /// + /// Initializes a handler with a stream. + /// + /// A pointer to an interface that represents the stream source. + /// One of the STGM values that indicates the access mode for . + void Initialize(IStream pstream, uint grfMode); + } +} diff --git a/src/modules/previewpane/common/cominterop/IObjectWithSite.cs b/src/modules/previewpane/common/cominterop/IObjectWithSite.cs new file mode 100644 index 000000000000..65aa60163691 --- /dev/null +++ b/src/modules/previewpane/common/cominterop/IObjectWithSite.cs @@ -0,0 +1,31 @@ +// 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; + +namespace Common.ComInterlop +{ + /// + /// Provides a simplified way to support communication between an object and its site in the container. + /// + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("fc4801a3-2ba9-11cf-a229-00aa003d7352")] + public interface IObjectWithSite + { + /// + /// Provides the site's pointer to the site object. + /// + /// Address of an interface pointer to the site managing this object. + void SetSite([In, MarshalAs(UnmanagedType.IUnknown)] object pUnkSite); + + /// + /// Retrieves the last site set using the method. In cases where there is no known site, the object returns an exception. + /// + /// Provides the IID of the interface pointer returned in the parameter. + /// The address of the caller's void variable in which the object stores the interface pointer of the site last seen in the . + void GetSite(ref Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppvSite); + } +} diff --git a/src/modules/previewpane/common/cominterop/IOleWindow.cs b/src/modules/previewpane/common/cominterop/IOleWindow.cs new file mode 100644 index 000000000000..0953a3cd9fd3 --- /dev/null +++ b/src/modules/previewpane/common/cominterop/IOleWindow.cs @@ -0,0 +1,31 @@ +// 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; + +namespace Common.ComInterlop +{ + /// + /// The IOleWindow interface provides methods that allow an application to obtain the handle to the various windows that participate + /// in in-place activation, and also to enter and exit context-sensitive help mode. + /// + [ComImport] + [Guid("00000114-0000-0000-C000-000000000046")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IOleWindow + { + /// + /// Retrieves a handle to one of the windows participating in in-place activation (frame, document, parent, or in-place object window). + /// + /// A pointer to a variable that receives the window handle. + void GetWindow(out IntPtr phwnd); + + /// + /// Determines whether context-sensitive help mode should be entered during an in-place activation session. + /// + /// TRUE if help mode should be entered; FALSE if it should be exited. + void ContextSensitiveHelp([MarshalAs(UnmanagedType.Bool)] bool fEnterMode); + } +} diff --git a/src/modules/previewpane/common/cominterop/IPreviewHandler.cs b/src/modules/previewpane/common/cominterop/IPreviewHandler.cs new file mode 100644 index 000000000000..4f9136449bb0 --- /dev/null +++ b/src/modules/previewpane/common/cominterop/IPreviewHandler.cs @@ -0,0 +1,62 @@ +// 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; + +namespace Common.ComInterlop +{ + /// + /// Exposes methods for the display of rich previews. + /// + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("8895b1c6-b41f-4c1c-a562-0d564250836f")] + public interface IPreviewHandler + { + /// + /// Sets the parent window of the previewer window, as well as the area within the parent to be used for the previewer window. + /// + /// A handle to the parent window. + /// A pointer to a defining the area for the previewer. + void SetWindow(IntPtr hwnd, ref RECT rect); + + /// + /// Directs the preview handler to change the area within the parent hwnd that it draws into. + /// + /// A pointer to a to be used for the preview. + void SetRect(ref RECT rect); + + /// + /// Directs the preview handler to load data from the source specified in an earlier Initialize method call, and to begin rendering to the previewer window. + /// + void DoPreview(); + + /// + /// Directs the preview handler to cease rendering a preview and to release all resources that have been allocated based on the item passed in during the initialization. + /// + void Unload(); + + /// + /// Directs the preview handler to set focus to itself. + /// + void SetFocus(); + + /// + /// Directs the preview handler to return the HWND from calling the GetFocus Function. + /// + /// When this method returns, contains a pointer to the HWND returned from calling the GetFocus Function from the preview handler's foreground thread. + void QueryFocus(out IntPtr phwnd); + + /// + /// Directs the preview handler to handle a keystroke passed up from the message pump of the process in which the preview handler is running. + /// + /// A pointer to a window message. + /// If the keystroke message can be processed by the preview handler, the handler will process it and return S_OK(0). If the preview handler cannot process the keystroke message, it + /// will offer it to the host using . If the host processes the message, this method will return S_OK(0). If the host does not process the message, this method will return S_FALSE(1). + /// + [PreserveSig] + uint TranslateAccelerator(ref MSG pmsg); + } +} diff --git a/src/modules/previewpane/common/cominterop/IPreviewHandlerFrame.cs b/src/modules/previewpane/common/cominterop/IPreviewHandlerFrame.cs new file mode 100644 index 000000000000..e639e3a6de7f --- /dev/null +++ b/src/modules/previewpane/common/cominterop/IPreviewHandlerFrame.cs @@ -0,0 +1,33 @@ +// 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; + +namespace Common.ComInterlop +{ + /// + /// Enables preview handlers to pass keyboard shortcuts to the host. This interface retrieves a list of keyboard shortcuts and directs the host to handle a keyboard shortcut. + /// + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("fec87aaf-35f9-447a-adb7-20234491401a")] + public interface IPreviewHandlerFrame + { + /// + /// Gets a list of the keyboard shortcuts for the preview host. + /// + /// A pointer to a PREVIEWHANDLERFRAMEINFO structure + /// that receives accelerator table information. + void GetWindowContext(IntPtr pinfo); + + /// + /// Directs the host to handle an keyboard shortcut passed from the preview handler. + /// + /// A reference to that corresponds to a keyboard shortcut. + /// If the keyboard shortcut is one that the host intends to handle, the host will process it and return S_OK(0); otherwise, it returns S_FALSE(1). + [PreserveSig] + uint TranslateAccelerator(ref MSG pmsg); + } +} diff --git a/src/modules/previewpane/common/cominterop/IPreviewHandlerVisuals.cs b/src/modules/previewpane/common/cominterop/IPreviewHandlerVisuals.cs new file mode 100644 index 000000000000..c1bc184dfc67 --- /dev/null +++ b/src/modules/previewpane/common/cominterop/IPreviewHandlerVisuals.cs @@ -0,0 +1,36 @@ +// 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; + +namespace Common.ComInterlop +{ + /// + /// Exposes methods for applying color and font information to preview handlers. + /// + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("8327b13c-b63f-4b24-9b8a-d010dcc3f599")] + public interface IPreviewHandlerVisuals + { + /// + /// Sets the background color of the preview handler. + /// + /// A value of type to use for the preview handler background. + void SetBackgroundColor(COLORREF color); + + /// + /// Sets the font attributes to be used for text within the preview handler. + /// + /// A pointer to a Structure containing the necessary attributes for the font to use. + void SetFont(ref LOGFONT plf); + + /// + /// Sets the color of the text within the preview handler. + /// + /// A value of type to use for the preview handler text color. + void SetTextColor(COLORREF color); + } +} diff --git a/src/modules/previewpane/common/cominterop/LOGFONT.cs b/src/modules/previewpane/common/cominterop/LOGFONT.cs new file mode 100644 index 000000000000..8f4e6add2c1e --- /dev/null +++ b/src/modules/previewpane/common/cominterop/LOGFONT.cs @@ -0,0 +1,88 @@ +// 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.Runtime.InteropServices; + +namespace Common.ComInterlop +{ + /// + /// Defines the attributes of a font. + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct LOGFONT + { + /// + /// Value of type INT that specifies the height, in logical units, of the font's character cell or character. + /// + public int LfHeight; + + /// + /// Value of type INT that specifies the width, in logical units, of characters in the font. + /// + public int LfWidth; + + /// + /// Value of type INT that contains the angle, in tenths of degrees, between the escapement vector and the x-axis of the device. The escapement + /// vector is parallel to the base line of a row of text. + /// + public int LfEscapement; + + /// + /// Value of type INT that specifies the angle, in tenths of degrees, between each character's base line and the x-axis of the device. + /// + public int LfOrientation; + + /// + /// Value of type INT that specifies the weight of the font in the range from 0 through 1000. + /// + public int LfWeight; + + /// + /// Value of type BYTE that specifies an italic font if set to TRUE. + /// + public byte LfItalic; + + /// + /// Value of type BYTE that specifies an underlined font if set to TRUE. + /// + public byte LfUnderline; + + /// + /// Value of type BYTE that specifies a strikeout font if set to TRUE. + /// + public byte LfStrikeOut; + + /// + /// Value of type BYTE that specifies the character set. + /// + public byte LfCharSet; + + /// + /// Value of type BYTE that specifies the output precision. The output precision defines how closely the output must match the requested + /// font's height, width, character orientation, escapement, pitch, and font type. + /// + public byte LfOutPrecision; + + /// + /// Value of type BYTE that specifies the clipping precision. The clipping precision defines how to clip characters that are partially outside the clipping region. + /// + public byte LfClipPrecision; + + /// + /// Value of type BYTE that specifies the output quality. The output quality defines how carefully the GDI must attempt to match the logical-font attributes to those of an actual physical font. + /// + public byte LfQuality; + + /// + /// Value of type BYTE that specifies the pitch and family of the font. + /// + public byte LfPitchAndFamily; + + /// + /// Array of wide characters that contains a null-terminated string that specifies the typeface name of the font. The length of the string must not exceed 32 characters, including the NULL terminator. + /// + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string LfFaceName; + } +} diff --git a/src/modules/previewpane/common/cominterop/MSG.cs b/src/modules/previewpane/common/cominterop/MSG.cs new file mode 100644 index 000000000000..ba2819ff6f63 --- /dev/null +++ b/src/modules/previewpane/common/cominterop/MSG.cs @@ -0,0 +1,51 @@ +// 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; + +namespace Common.ComInterlop +{ + /// + /// Contains message information from a thread's message queue. + /// + [StructLayout(LayoutKind.Sequential)] + public struct MSG + { + /// + /// A handle to the window whose window procedure receives the message. This member is NULL when the message is a thread message. + /// + public IntPtr Hwnd; + + /// + /// The message identifier. Applications can only use the low word; the high word is reserved by the system. + /// + public int Message; + + /// + /// Additional information about the message. The exact meaning depends on the value of the message member. + /// + public IntPtr WParam; + + /// + /// Additional information about the message. The exact meaning depends on the value of the message member. + /// + public IntPtr LParam; + + /// + /// The time at which the message was posted. + /// + public int Time; + + /// + /// The x coordinate of cursor position, in screen coordinates, when the message was posted. + /// + public int PtX; + + /// + /// The y coordinate of cursor position, in screen coordinates, when the message was posted. + /// + public int PtY; + } +} diff --git a/src/modules/previewpane/common/cominterop/RECT.cs b/src/modules/previewpane/common/cominterop/RECT.cs new file mode 100644 index 000000000000..932f80af0f05 --- /dev/null +++ b/src/modules/previewpane/common/cominterop/RECT.cs @@ -0,0 +1,45 @@ +// 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.Drawing; +using System.Runtime.InteropServices; + +namespace Common.ComInterlop +{ + /// + /// The RECT structure defines a rectangle by the coordinates of its upper-left and lower-right corners. + /// + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + /// + /// Specifies the x-coordinate of the upper-left corner of the rectangle. + /// + public int Left; + + /// + /// Specifies the y-coordinate of the upper-left corner of the rectangle. + /// + public int Top; + + /// + /// Specifies the x-coordinate of the lower-right corner of the rectangle. + /// + public int Right; + + /// + /// Specifies the y-coordinate of the lower-right corner of the rectangle. + /// + public int Bottom; + + /// + /// Creates a structure with the edge locations specified in the struct. + /// + /// Return a . + public Rectangle ToRectangle() + { + return Rectangle.FromLTRB(this.Left, this.Top, this.Right, this.Bottom); + } + } +} diff --git a/src/modules/previewpane/common/controls/FormHandlerControl.cs b/src/modules/previewpane/common/controls/FormHandlerControl.cs new file mode 100644 index 000000000000..642bc094fe8d --- /dev/null +++ b/src/modules/previewpane/common/controls/FormHandlerControl.cs @@ -0,0 +1,160 @@ +// 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.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace Common +{ + /// + /// Form based implementation of . + /// + public abstract class FormHandlerControl : Form, IPreviewHandlerControl + { + /// + /// Holds the parent window handle. + /// + private IntPtr parentHwnd; + + /// + /// Initializes a new instance of the class. + /// + public FormHandlerControl() + { + // Gets the handle of the control to create the control on the VI thread. Invoking the Control.Handle get accessor forces the creation of the underlying window for the control. + // This is important, because the thread that instantiates the preview handler component and calls its constructor is a single-threaded apartment (STA) thread, but the thread that calls into the interface members later on is a multithreaded apartment (MTA) thread. Windows Forms controls are meant to run on STA threads. + // More details: https://docs.microsoft.com/en-us/archive/msdn-magazine/2007/january/windows-vista-and-office-writing-your-own-preview-handlers. + var forceCreation = this.Handle; + this.FormBorderStyle = FormBorderStyle.None; + this.Visible = false; + } + + /// + public IntPtr GetHandle() + { + return this.Handle; + } + + /// + public void QueryFocus(out IntPtr result) + { + var getResult = IntPtr.Zero; + this.InvokeOnControlThread(() => + { + getResult = GetFocus(); + }); + result = getResult; + } + + /// + public void SetBackgroundColor(Color argbColor) + { + this.InvokeOnControlThread(() => + { + this.BackColor = argbColor; + }); + } + + /// + public void SetFocus() + { + this.InvokeOnControlThread(() => + { + this.Focus(); + }); + } + + /// + public void SetFont(Font font) + { + this.InvokeOnControlThread(() => + { + this.Font = font; + }); + } + + /// + public void SetRect(Rectangle windowBounds) + { + this.UpdateWindowBounds(windowBounds); + } + + /// + public void SetTextColor(Color color) + { + this.InvokeOnControlThread(() => + { + this.ForeColor = color; + }); + } + + /// + public void SetWindow(IntPtr hwnd, Rectangle rect) + { + this.parentHwnd = hwnd; + this.UpdateWindowBounds(rect); + } + + /// + public virtual void Unload() + { + this.InvokeOnControlThread(() => + { + this.Visible = false; + foreach (Control c in this.Controls) + { + c.Dispose(); + } + + this.Controls.Clear(); + }); + } + + /// + public virtual void DoPreview(T dataSource) + { + this.Visible = true; + } + + /// + /// Executes the specified delegate on the thread that owns the control's underlying window handle. + /// + /// Delegate to run. + public void InvokeOnControlThread(MethodInvoker func) + { + this.Invoke(func); + } + + /// + /// Changes the parent window of the specified child window. + /// + /// A handle to the child window. + /// A handle to the new parent window. + /// If the function succeeds, the return value is a handle to the previous parent window and NULL in case of failure. + [DllImport("user32.dll")] + private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); + + /// + /// Retrieves the handle to the window that has the keyboard focus, if the window is attached to the calling thread's message queue. + /// + /// The return value is the handle to the window with the keyboard focus. If the calling thread's message queue does not have an associated window with the keyboard focus, the return value is NULL. + [DllImport("user32.dll", CharSet = CharSet.Auto)] + private static extern IntPtr GetFocus(); + + /// + /// Update the Form Control window with the passed rectangle. + /// + /// An instance of rectangle. + private void UpdateWindowBounds(Rectangle windowBounds) + { + this.InvokeOnControlThread(() => + { + SetParent(this.Handle, this.parentHwnd); + this.Bounds = windowBounds; + }); + } + } +} diff --git a/src/modules/previewpane/common/controls/IPreviewHandlerControl.cs b/src/modules/previewpane/common/controls/IPreviewHandlerControl.cs new file mode 100644 index 000000000000..77308e191508 --- /dev/null +++ b/src/modules/previewpane/common/controls/IPreviewHandlerControl.cs @@ -0,0 +1,77 @@ +// 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.Drawing; + +namespace Common +{ + /// + /// Interface defining methods requirement by the control. + /// + public interface IPreviewHandlerControl + { + /// + /// Directs the preview handler to return the HWND from calling the GetFocus function. + /// Source: https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ipreviewhandler-queryfocus. + /// + /// Returns the handle of the window with focus. + void QueryFocus(out IntPtr result); + + /// + /// Sets focus to the control. + /// + void SetFocus(); + + /// + /// Sets the font according to the font set in Windows Settings. + /// More details: https://docs.microsoft.com/en-us/windows/win32/shell/building-preview-handlers#ipreviewhandlervisualssetfont. + /// + /// Instance of Font. + void SetFont(Font font); + + /// + /// Sets the Text color according to the Windows Settings. + /// + /// Instance of color. + void SetTextColor(Color color); + + /// + /// Sets the Background color. For instance to fill the window when the handler renders to area smaller provided by SetWindow and SetRect. + /// + /// Instance of color. + void SetBackgroundColor(Color argbColor); + + /// + /// Gets the HWND of the control window. + /// + /// Pointer to the window handle. + IntPtr GetHandle(); + + /// + /// Hide the preview and free any resource used for the preview. + /// + void Unload(); + + /// + /// Directs the control to change the area within the parent hwnd that it draws into. + /// + /// Instance of Rectangle defining the area. + void SetRect(Rectangle windowBounds); + + /// + /// Sets the parent window of the previewer window, as well as the area within the parent to be used for the previewer window.. + /// + /// Pointer to the parent window handle. + /// Instance of Rectangle defining the area. + void SetWindow(IntPtr hwnd, Rectangle rect); + + /// + /// Called by Preview Handler to start the preview. + /// + /// File Path or Stream reference for the file. + /// Represents the source of preview data. + void DoPreview(T dataSource); + } +} diff --git a/src/modules/previewpane/common/examplehandler/CustomControlTest.cs b/src/modules/previewpane/common/examplehandler/CustomControlTest.cs new file mode 100644 index 000000000000..c06f28809278 --- /dev/null +++ b/src/modules/previewpane/common/examplehandler/CustomControlTest.cs @@ -0,0 +1,35 @@ +// 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.IO; +using System.Windows.Forms; + +namespace Common +{ + /// + /// This is test custom control to test the implementation. + /// + public class CustomControlTest : FormHandlerControl + { + /// + /// Start the preview on the Control. + /// + /// Path to the file. + public override void DoPreview(T dataSource) + { + this.InvokeOnControlThread(() => + { + var filePath = dataSource as string; + WebBrowser browser = new WebBrowser(); + + browser.DocumentText = "Test"; + browser.Navigate(filePath); + browser.Dock = DockStyle.Fill; + browser.IsWebBrowserContextMenuEnabled = false; + this.Controls.Add(browser); + base.DoPreview(dataSource); + }); + } + } +} diff --git a/src/modules/previewpane/common/examplehandler/TestCustomHandler.cs b/src/modules/previewpane/common/examplehandler/TestCustomHandler.cs new file mode 100644 index 000000000000..daf06cde9e79 --- /dev/null +++ b/src/modules/previewpane/common/examplehandler/TestCustomHandler.cs @@ -0,0 +1,34 @@ +// 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.Runtime.InteropServices; + +namespace Common +{ + /// + /// This is a example custom handler to show how to extend the library. + /// + [PreviewHandler("SvgPreviewHandler", ".svg", "{88235ab2-bfce-4be8-9ed0-0408cd8da792}")] + [ProgId("SvgPreviewHandler")] + [Guid("22a1a8e8-e929-4732-90ce-91eaff38b614")] + [ClassInterface(ClassInterfaceType.None)] + [ComVisible(true)] + public class TestCustomHandler : FileBasedPreviewHandler + { + private CustomControlTest previewHandlerControl; + + /// + public override void DoPreview() + { + this.previewHandlerControl.DoPreview(this.FilePath); + } + + /// + protected override IPreviewHandlerControl CreatePreviewHandlerControl() + { + this.previewHandlerControl = new CustomControlTest(); + return this.previewHandlerControl; + } + } +} diff --git a/src/modules/previewpane/common/handlers/FileBasedPreviewHandler.cs b/src/modules/previewpane/common/handlers/FileBasedPreviewHandler.cs new file mode 100644 index 000000000000..c4ca04c9ef8a --- /dev/null +++ b/src/modules/previewpane/common/handlers/FileBasedPreviewHandler.cs @@ -0,0 +1,27 @@ +// 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.Runtime.InteropServices; +using Common.Cominterop; + +namespace Common +{ + /// + /// Extends the by implementing IInitializeWithFile. + /// + public abstract class FileBasedPreviewHandler : PreviewHandlerBase, IInitializeWithFile + { + /// + /// Gets the file path. + /// + public string FilePath { get; private set; } + + /// + public void Initialize([MarshalAs(UnmanagedType.LPWStr)] string pszFilePath, uint grfMode) + { + // Ignore the grfMode always use read mode to access the file. + this.FilePath = pszFilePath; + } + } +} diff --git a/src/modules/previewpane/common/handlers/PreviewHandlerAttribute.cs b/src/modules/previewpane/common/handlers/PreviewHandlerAttribute.cs new file mode 100644 index 000000000000..1fe38b50f5a9 --- /dev/null +++ b/src/modules/previewpane/common/handlers/PreviewHandlerAttribute.cs @@ -0,0 +1,43 @@ +// 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; + +namespace Common +{ + /// + /// Attribute class for Preview Handler used for registration. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public class PreviewHandlerAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the Preview Handler. + /// File extension type. + /// AppId Guid used for the process in which handler is created. + public PreviewHandlerAttribute(string name, string extension, string appId) + { + this.Name = name ?? throw new ArgumentNullException("name"); + this.Extension = extension ?? throw new ArgumentNullException("extension"); + this.AppId = appId ?? throw new ArgumentNullException("appId"); + } + + /// + /// Gets the Name of the handler. + /// + public string Name { get; private set; } + + /// + /// Gets the Extension type of the handler. + /// + public string Extension { get; private set; } + + /// + /// Gets the App Id for the Preview Handler. + /// + public string AppId { get; private set; } + } +} diff --git a/src/modules/previewpane/common/handlers/PreviewHandlerBase.cs b/src/modules/previewpane/common/handlers/PreviewHandlerBase.cs new file mode 100644 index 000000000000..0250b0978811 --- /dev/null +++ b/src/modules/previewpane/common/handlers/PreviewHandlerBase.cs @@ -0,0 +1,253 @@ +// 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.ComponentModel; +using System.Drawing; +using System.Runtime.InteropServices; +using Common.ComInterlop; +using Microsoft.Win32; + +namespace Common +{ + /// + /// Preview Handler base class implmenenting interfaces required by Preview Handler. + /// + public abstract class PreviewHandlerBase : IPreviewHandler, IOleWindow, IObjectWithSite, IPreviewHandlerVisuals + { + /// + /// An instance of Preview Control Used by the Handler. + /// + private IPreviewHandlerControl previewControl; + + /// + /// Hold reference for the window handle. + /// + private IntPtr parentHwnd; + + /// + /// Hold the bounds of the window. + /// + private Rectangle windowBounds; + + /// + /// Holds the site pointer. + /// + private object unkSite; + + /// + /// Holds reference for the IPreviewHandlerFrame. + /// + private IPreviewHandlerFrame frame; + + /// + /// Initializes a new instance of the class. + /// + public PreviewHandlerBase() + { + this.previewControl = this.CreatePreviewHandlerControl(); + } + + /// + /// Do the registeration of preview handler. + /// + /// Type of the class to register. + [ComRegisterFunction] + public static void Register(Type t) + { + Console.WriteLine("Adding keys to registry."); + if (t != null && t.IsSubclassOf(typeof(PreviewHandlerBase))) + { + object[] attrs = (object[])t.GetCustomAttributes(typeof(PreviewHandlerAttribute), true); + if (attrs != null && attrs.Length == 1) + { + PreviewHandlerAttribute attr = attrs[0] as PreviewHandlerAttribute; + RegisterPreviewHandler(attr.Name, attr.Extension, t.GUID.ToString("B"), attr.AppId); + } + } + } + + /// + /// Do the unregisteration of preview handler. + /// + /// Type of the class to register. + [ComUnregisterFunction] + public static void Unregister(Type t) + { + Console.WriteLine("Removing keys to registry."); + if (t != null && t.IsSubclassOf(typeof(PreviewHandlerBase))) + { + object[] attrs = (object[])t.GetCustomAttributes(typeof(PreviewHandlerAttribute), true); + if (attrs != null && attrs.Length == 1) + { + PreviewHandlerAttribute attr = attrs[0] as PreviewHandlerAttribute; + UnregisterPreviewHandler(attr.Extension, t.GUID.ToString("B"), attr.AppId); + } + } + } + + /// + public abstract void DoPreview(); + + /// + public void SetWindow(IntPtr hwnd, ref RECT rect) + { + this.parentHwnd = hwnd; + this.windowBounds = rect.ToRectangle(); + this.previewControl.SetWindow(hwnd, this.windowBounds); + } + + /// + public void SetRect(ref RECT rect) + { + this.windowBounds = rect.ToRectangle(); + this.previewControl.SetRect(this.windowBounds); + } + + /// + public void Unload() + { + this.previewControl.Unload(); + } + + /// + public void SetFocus() + { + this.previewControl.SetFocus(); + } + + /// + public void QueryFocus(out IntPtr phwnd) + { + this.previewControl.QueryFocus(out IntPtr result); + phwnd = result; + if (phwnd == IntPtr.Zero) + { + throw new Win32Exception(); + } + } + + /// + public uint TranslateAccelerator(ref MSG pmsg) + { + // Current implementation simply directs all Keystrokes to IPreviewHandlerFrame. This is the recommended approach to handle keystokes for all low-integrity preview handlers. + // Source: https://docs.microsoft.com/en-us/windows/win32/shell/building-preview-handlers#ipreviewhandlertranslateaccelerator + if (this.frame != null) + { + return this.frame.TranslateAccelerator(ref pmsg); + } + + const uint S_FALSE = 1; + return S_FALSE; + } + + /// + public void GetWindow(out IntPtr phwnd) + { + phwnd = this.previewControl.GetHandle(); + } + + /// + public void ContextSensitiveHelp(bool fEnterMode) + { + // Should always return NotImplementedException. Source: https://docs.microsoft.com/en-us/windows/win32/shell/building-preview-handlers#iolewindowcontextsensitivehelp + throw new NotImplementedException(); + } + + /// + public void SetSite(object pUnkSite) + { + // Implementation logic details: https://docs.microsoft.com/en-us/windows/win32/shell/building-preview-handlers#iobjectwithsitesetsite + this.unkSite = pUnkSite; + this.frame = this.unkSite as IPreviewHandlerFrame; + } + + /// + public void GetSite(ref Guid riid, out object ppvSite) + { + ppvSite = this.unkSite; + } + + /// + public void SetBackgroundColor(COLORREF color) + { + this.previewControl.SetBackgroundColor(color.Color); + } + + /// + public void SetFont(ref LOGFONT plf) + { + this.previewControl.SetFont(Font.FromLogFont(plf)); + } + + /// + public void SetTextColor(COLORREF color) + { + this.previewControl.SetTextColor(color.Color); + } + + /// + /// Provide instance of the implementation of . Should be overide by the implementation class with control object to be used. + /// + /// Instance of the . + protected abstract IPreviewHandlerControl CreatePreviewHandlerControl(); + + private static void RegisterPreviewHandler(string name, string extensions, string previewerGuid, string appId) + { + // Create a new prevhost AppID so that this always runs in its own isolated process + using (RegistryKey appIdsKey = Registry.ClassesRoot.OpenSubKey("AppID", true)) + using (RegistryKey appIdKey = appIdsKey.CreateSubKey(appId)) + { + appIdKey.SetValue("DllSurrogate", @"%SystemRoot%\system32\prevhost.exe", RegistryValueKind.ExpandString); + } + + // Add preview handler to preview handler list + using (RegistryKey handlersKey = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", true)) + { + handlersKey.SetValue(previewerGuid, name, RegistryValueKind.String); + } + + // Modify preview handler registration + using (RegistryKey clsidKey = Registry.ClassesRoot.OpenSubKey("CLSID")) + using (RegistryKey idKey = clsidKey.OpenSubKey(previewerGuid, true)) + { + idKey.SetValue("DisplayName", name, RegistryValueKind.String); + idKey.SetValue("AppID", appId, RegistryValueKind.String); + idKey.SetValue("DisableLowILProcessIsolation", 1, RegistryValueKind.DWord); // optional, depending on what preview handler needs to be able to do + } + + foreach (string extension in extensions.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + { + // Set preview handler for specific extension + using (RegistryKey extensionKey = Registry.ClassesRoot.CreateSubKey(extension)) + using (RegistryKey shellexKey = extensionKey.CreateSubKey("shellex")) + using (RegistryKey previewKey = shellexKey.CreateSubKey("{8895b1c6-b41f-4c1c-a562-0d564250836f}")) + { + previewKey.SetValue(null, previewerGuid, RegistryValueKind.String); + } + } + } + + private static void UnregisterPreviewHandler(string extensions, string previewerGuid, string appId) + { + foreach (string extension in extensions.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + { + using (RegistryKey shellexKey = Registry.ClassesRoot.OpenSubKey(extension + "\\shellex", true)) + { + shellexKey.DeleteSubKey("{8895b1c6-b41f-4c1c-a562-0d564250836f}"); + } + } + + using (RegistryKey appIdsKey = Registry.ClassesRoot.OpenSubKey("AppID", true)) + { + appIdsKey.DeleteSubKey(appId); + } + + using (RegistryKey classesKey = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", true)) + { + classesKey.DeleteValue(previewerGuid); + } + } + } +} diff --git a/src/modules/previewpane/common/handlers/StreamBasedPreviewHandler.cs b/src/modules/previewpane/common/handlers/StreamBasedPreviewHandler.cs new file mode 100644 index 000000000000..8412dd7b92a5 --- /dev/null +++ b/src/modules/previewpane/common/handlers/StreamBasedPreviewHandler.cs @@ -0,0 +1,27 @@ +// 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.Runtime.InteropServices.ComTypes; +using Common.ComInterlop; + +namespace Common +{ + /// + /// Extends the by implementing IInitializeWithStream. + /// + public abstract class StreamBasedPreviewHandler : PreviewHandlerBase, IInitializeWithStream + { + /// + /// Gets the stream object to access file. + /// + public IStream Stream { get; private set; } + + /// + public void Initialize(IStream pstream, uint grfMode) + { + // Ignore the grfMode always use read mode to access the file. + this.Stream = pstream; + } + } +} diff --git a/src/modules/previewpane/powerpreview/dllmain.cpp b/src/modules/previewpane/powerpreview/dllmain.cpp new file mode 100644 index 000000000000..ebf2c60988ff --- /dev/null +++ b/src/modules/previewpane/powerpreview/dllmain.cpp @@ -0,0 +1,28 @@ +#include "pch.h" +#include +#include "trace.h" +#include "powerpreview.h" + + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + Trace::RegisterProvider(); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + Trace::UnregisterProvider(); + break; + } + return TRUE; +} + + +extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() +{ + return new PowerPreviewModule(); +} diff --git a/src/modules/previewpane/powerpreview/pch.cpp b/src/modules/previewpane/powerpreview/pch.cpp new file mode 100644 index 000000000000..a3d1ff4225e9 --- /dev/null +++ b/src/modules/previewpane/powerpreview/pch.cpp @@ -0,0 +1,3 @@ +#include "pch.h" +#pragma comment(lib, "windowsapp") +#pragma comment(lib, "shlwapi.lib") \ No newline at end of file diff --git a/src/modules/previewpane/powerpreview/pch.h b/src/modules/previewpane/powerpreview/pch.h new file mode 100644 index 000000000000..f7efdb5cfe87 --- /dev/null +++ b/src/modules/previewpane/powerpreview/pch.h @@ -0,0 +1,5 @@ +#pragma once +#define WIN32_LEAN_AND_MEAN +#include +#include +#include \ No newline at end of file diff --git a/src/modules/previewpane/powerpreview/powerpreview.cpp b/src/modules/previewpane/powerpreview/powerpreview.cpp new file mode 100644 index 000000000000..3d20ff526511 --- /dev/null +++ b/src/modules/previewpane/powerpreview/powerpreview.cpp @@ -0,0 +1,125 @@ +#include "pch.h" +#include +#include +#include +#include +#include "powerpreview.h" +#include "trace.h" +#include "settings.h" +#include "resource.h" + +// Destroy the powertoy and free memory. +void PowerPreviewModule::destroy() +{ + Trace::Destroyed(); + delete this; +} + +// Return the display name of the powertoy, this will be cached. +const wchar_t* PowerPreviewModule::get_name() +{ + return m_moduleName.c_str(); +} + +const wchar_t** PowerPreviewModule::get_events() +{ + return nullptr; +} + +// Return JSON with the configuration options. +bool PowerPreviewModule::get_config(_Out_ wchar_t* buffer, _Out_ int* buffer_size) +{ + HINSTANCE hinstance = reinterpret_cast(&__ImageBase); + + // Create a Settings object. + PowerToysSettings::Settings settings(hinstance, get_name()); + + // General Settings. + settings.set_description(GET_RESOURCE_STRING(IDS_GENERAL_DESCRIPTION)); + settings.set_icon_key(GET_RESOURCE_STRING(IDS_ICON_KEY_NAME)); + + // Preview Pane: Settings Group Header. + settings.add_header_szLarge( + GET_RESOURCE_STRING(IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_HEADER_ID), + GET_RESOURCE_STRING(IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_DESC), + GET_RESOURCE_STRING(IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_TEXT)); + + // Preview Pane: SVG Settings. + settings.add_bool_toogle( + m_prevPaneSVGSettings.GetName(), + m_prevPaneSVGSettings.GetDescription(), + m_prevPaneSVGSettings.GetState()); + + // Preview Pane: Mark Down Settings. + settings.add_bool_toogle( + m_prevPaneMDSettings.GetName(), + m_prevPaneMDSettings.GetDescription(), + m_prevPaneMDSettings.GetState()); + + return settings.serialize_to_buffer(buffer, buffer_size); +} + +// Called by the runner to pass the updated settings values as a serialized JSON. +void PowerPreviewModule::set_config(const wchar_t* config) +{ + try + { + PowerToysSettings::PowerToyValues values = PowerToysSettings::PowerToyValues::from_json_string(config); + m_prevPaneSVGSettings.UpdateState(values); + m_prevPaneMDSettings.UpdateState(values); + values.save_to_settings_file(); + } + catch (std::exception const& e) + { + Trace::SetConfigInvalidJSON(e.what()); + } +} + +// Enable the powertoy +void PowerPreviewModule::enable() +{ + m_prevPaneSVGSettings.EnablePreview(); + m_prevPaneMDSettings.EnablePreview(); + Trace::FilePreviewerIsEnabled(); + this->m_enabled = true; +} + +// Disable the powertoy +void PowerPreviewModule::disable() +{ + m_prevPaneSVGSettings.DisablePreview(); + m_prevPaneMDSettings.DisablePreview(); + Trace::FilePreviewerIsDisabled(); + this->m_enabled = false; +} + +// Returns if the powertoys is enabled +bool PowerPreviewModule::is_enabled() +{ + return this->m_enabled; +} + +// Handle incoming event, data is event-specific +intptr_t PowerPreviewModule::signal_event(const wchar_t* name, intptr_t data) +{ + return 0; +} + +// Load the settings file. +void PowerPreviewModule::init_settings() +{ + try + { + // Load and parse the settings file for this PowerToy. + PowerToysSettings::PowerToyValues settings = + PowerToysSettings::PowerToyValues::load_from_settings_file(PowerPreviewModule::get_name()); + + // Load settings states. + m_prevPaneSVGSettings.LoadState(settings); + m_prevPaneMDSettings.LoadState(settings); + } + catch (std::exception const& e) + { + Trace::InitSetErrorLoadingFile(e.what()); + } +} \ No newline at end of file diff --git a/src/modules/previewpane/powerpreview/powerpreview.h b/src/modules/previewpane/powerpreview/powerpreview.h new file mode 100644 index 000000000000..20cc2ccb748a --- /dev/null +++ b/src/modules/previewpane/powerpreview/powerpreview.h @@ -0,0 +1,43 @@ +#include "pch.h" +#include +#include +#include "trace.h" +#include "settings.h" + +using namespace PowerPreviewSettings; + +extern "C" IMAGE_DOS_HEADER __ImageBase; + +// Implement the PowerToy Module Interface and all the required methods. +class PowerPreviewModule : public PowertoyModuleIface +{ +private: + // The PowerToy state. + bool m_enabled = false; + PrevPaneSVGRendrSettings m_prevPaneSVGSettings; + PrevPaneMDRendrSettings m_prevPaneMDSettings; + std::wstring m_moduleName; + +public: + PowerPreviewModule() + : + m_moduleName(GET_RESOURCE_STRING(IDS_MODULE_NAME)), + m_prevPaneSVGSettings(PrevPaneSVGRendrSettings()), + m_prevPaneMDSettings(PrevPaneMDRendrSettings()) + { + init_settings(); + }; + + virtual void destroy(); + virtual const wchar_t* get_name(); + virtual const wchar_t** get_events(); + virtual bool get_config(_Out_ wchar_t* buffer, _Out_ int* buffer_size); + virtual void set_config(const wchar_t* config); + virtual void enable(); + virtual void disable(); + virtual bool is_enabled(); + virtual void init_settings(); + virtual intptr_t signal_event(const wchar_t* name, intptr_t data); + virtual void register_system_menu_helper(PowertoySystemMenuIface* helper) override {} + virtual void signal_system_menu_action(const wchar_t* name) override {} +}; \ No newline at end of file diff --git a/src/modules/previewpane/powerpreview/powerpreview.rc b/src/modules/previewpane/powerpreview/powerpreview.rc new file mode 100644 index 000000000000..d912d6336de9 --- /dev/null +++ b/src/modules/previewpane/powerpreview/powerpreview.rc @@ -0,0 +1,78 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE 9, 1 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// +STRINGTABLE +BEGIN + IDS_GENERAL_DESCRIPTION L"These settings allow you to manage your Windows File Explorer Addons."; + IDS_MODULE_NAME L"File Explorer Preview"; + IDS_ICON_KEY_NAME L"pt-power-preview"; + IDS_EXPLR_ICONS_PREV_STTNGS_GROUP_HEADER_ID L"EXPLR_ICONS_PREV_STTNGS_GROUP_HEADER_ID"; + IDS_EXPLR_ICONS_PREV_STTNGS_GROUP_DESC L"Settings Group Header Text"; + IDS_EXPLR_ICONS_PREV_STTNGS_GROUP_TEXT L"Explorer Icons"; + IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_HEADER_ID L"PRVPANE_FILE_PREV_STTNGS_GROUP_HEADER_ID"; + IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_DESC L"Settings Group Header Text"; + IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_TEXT L"Preview Pane"; + IDS_PREVPANE_MD_BOOL_TOGGLE_CONTROLL L"PREVPANE_MD_BOOL_TOGGLE_CONTROLL_ID" + IDS_PREVPANE_MD_SETTINGS_DESCRIPTION L"Show Markdown" + IDS_PREVPANE_MD_SETTINGS_DISPLAYNAME L"Markdown Preview Handler" + IDS_PREVPANE_SVG_BOOL_TOGGLE_CONTROLL L"IDS_PREVPANE_SVG_BOOL_TOGGLE_CONTROLL" + IDS_PREVPANE_SVG_SETTINGS_DESCRIPTION L"Show SVG" + IDS_PREVPANE_SVG_SETTINGS_DISPLAYNAME L"SVG Preview Handler" + IDS_EXPLR_SVG_BOOL_TOGGLE_CONTROLL L"EXPLR_SVG_BOOL_TOGGLE_CONTROLL" + IDS_EXPLR_SVG_SETTINGS_DESCRIPTION L"Render SVG images" +END + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/src/modules/previewpane/powerpreview/powerpreview.vcxproj b/src/modules/previewpane/powerpreview/powerpreview.vcxproj new file mode 100644 index 000000000000..da0d967db095 --- /dev/null +++ b/src/modules/previewpane/powerpreview/powerpreview.vcxproj @@ -0,0 +1,127 @@ + + + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {217DF501-135C-4E38-BFC8-99D4821032EA} + Win32Proj + examplepowertoy + 10.0 + powerpreview + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + true + $(SolutionDir)$(Platform)\$(Configuration)\modules\ + + + false + $(SolutionDir)$(Platform)\$(Configuration)\modules\ + + + + Use + Level3 + Disabled + true + _DEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + ..\;..\..\..\common;..\..\..\common\telemetry;..\..\;..\..\..\;..\..\..\..\deps\cpprestsdk\include;%(AdditionalIncludeDirectories) + MultiThreadedDebug + stdcpplatest + + + Windows + true + $(OutDir)$(TargetName)$(TargetExt) + + + + + Use + Level3 + MaxSpeed + true + true + true + NDEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + ..\..\..\common\Telemetry;..\..\..\common;..\..\common\inc;..\common\Telemetry;..\;..\..\;..\..\..\deps\cpprestsdk\include;%(AdditionalIncludeDirectories) + MultiThreaded + stdcpplatest + + + Windows + true + true + true + $(OutDir)$(TargetName)$(TargetExt) + + + + + + + + + + + + Create + Create + pch.h + pch.h + + + + + + + + + + + + {74485049-c722-400f-abe5-86ac52d929b3} + + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/powerpreview/powerpreview.vcxproj.filters b/src/modules/previewpane/powerpreview/powerpreview.vcxproj.filters new file mode 100644 index 000000000000..1f14688df8a5 --- /dev/null +++ b/src/modules/previewpane/powerpreview/powerpreview.vcxproj.filters @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/powerpreview/resource.h b/src/modules/previewpane/powerpreview/resource.h new file mode 100644 index 000000000000..939a5addf66e --- /dev/null +++ b/src/modules/previewpane/powerpreview/resource.h @@ -0,0 +1,32 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by powerpreview.rc + +#define IDS_GENERAL_DESCRIPTION 200 +#define IDS_MODULE_NAME 201 +#define IDS_ICON_KEY_NAME 202 +#define IDS_EXPLR_ICONS_PREV_STTNGS_GROUP_HEADER_ID 203 +#define IDS_EXPLR_ICONS_PREV_STTNGS_GROUP_DESC 204 +#define IDS_EXPLR_ICONS_PREV_STTNGS_GROUP_TEXT 205 +#define IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_HEADER_ID 206 +#define IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_DESC 207 +#define IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_TEXT 208 +#define IDS_PREVPANE_MD_BOOL_TOGGLE_CONTROLL 209 +#define IDS_PREVPANE_MD_SETTINGS_DESCRIPTION 210 +#define IDS_PREVPANE_SVG_BOOL_TOGGLE_CONTROLL 211 +#define IDS_PREVPANE_SVG_SETTINGS_DESCRIPTION 212 +#define IDS_EXPLR_SVG_BOOL_TOGGLE_CONTROLL 213 +#define IDS_EXPLR_SVG_SETTINGS_DESCRIPTION 214 +#define IDS_PREVPANE_SVG_SETTINGS_DISPLAYNAME 215 +#define IDS_PREVPANE_MD_SETTINGS_DISPLAYNAME 216 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/modules/previewpane/powerpreview/settings.cpp b/src/modules/previewpane/powerpreview/settings.cpp new file mode 100644 index 000000000000..83915c620a96 --- /dev/null +++ b/src/modules/previewpane/powerpreview/settings.cpp @@ -0,0 +1,274 @@ +#include "pch.h" +#include +#include "settings.h" +#include "trace.h" +#include +#include + +using namespace std; + +namespace PowerPreviewSettings +{ + extern "C" IMAGE_DOS_HEADER __ImageBase; + + // Base Settinngs Class Implementation + FileExplorerPreviewSettings::FileExplorerPreviewSettings(bool state, const std::wstring name, const std::wstring description, LPCWSTR clsid, const std::wstring displayname) + : + m_isPreviewEnabled(state), + m_name(name), + m_description(description), + m_clsid(clsid), + m_displayName(displayname) {} + + FileExplorerPreviewSettings::FileExplorerPreviewSettings() + : + m_isPreviewEnabled(false), + m_name(L"_UNDEFINED_"), + m_description(L"_UNDEFINED_"), + m_clsid(L"_UNDEFINED_"), + m_displayName(L"_UNDEFINED_") {} + + bool FileExplorerPreviewSettings::GetState() const + { + return this->m_isPreviewEnabled; + } + + void FileExplorerPreviewSettings::SetState(bool state) + { + this->m_isPreviewEnabled = state; + } + + void FileExplorerPreviewSettings::LoadState(PowerToysSettings::PowerToyValues& settings) + { + auto toggle = settings.get_bool_value(this->GetName()); + if (toggle != std::nullopt) + { + this->m_isPreviewEnabled = toggle.value(); + } + } + + void FileExplorerPreviewSettings::UpdateState(PowerToysSettings::PowerToyValues& values) + { + auto toggle = values.get_bool_value(this->GetName()); + if (toggle != std::nullopt) + { + this->m_isPreviewEnabled = toggle.value(); + if (this->m_isPreviewEnabled) + { + this->EnablePreview(); + } + else + { + this->DisablePreview(); + } + } + else + { + Trace::PowerPreviewSettingsUpDateFailed(this->GetName().c_str()); + } + } + + LONG FileExplorerPreviewSettings::SetRegistryValue() const + { + HKEY hKey = HKEY_CURRENT_USER; + const REGSAM WRITE_PERMISSION = KEY_WRITE; + DWORD options = 0; + HKEY OpenResult; + + LONG err = RegOpenKeyEx(hKey, this->GetSubKey(), options, WRITE_PERMISSION, &OpenResult); + + if (err == ERROR_SUCCESS) + { + err = RegSetValueExW( + OpenResult, + this->GetCLSID(), + 0, + REG_SZ, + (LPBYTE)this->GetDisplayName().c_str(), + this->GetDisplayName().length() * sizeof(wchar_t)); + RegCloseKey(OpenResult); + if (err != ERROR_SUCCESS) + { + return err; + } + } + else + { + return err; + } + } + + LONG FileExplorerPreviewSettings::DeleteRegistryValue() const + { + HKEY hKey = HKEY_CURRENT_USER; + const REGSAM WRITE_PERMISSION = KEY_WRITE; + DWORD options = 0; + HKEY OpenResult; + + LONG err = RegOpenKeyEx(hKey, this->GetSubKey(), options, WRITE_PERMISSION, &OpenResult); + if (err == ERROR_SUCCESS) + { + err = RegDeleteKeyValueW( + OpenResult, + NULL, + this->GetCLSID()); + RegCloseKey(OpenResult); + + if (err != ERROR_SUCCESS) + { + return err; + } + } + else + { + return err; + } + } + + bool FileExplorerPreviewSettings::GetRegistryValue() const + { + HKEY OpenResult; + LONG err = RegOpenKeyEx( + HKEY_CURRENT_USER, + this->GetSubKey(), + 0, + KEY_READ, + &OpenResult); + + if (err == ERROR_SUCCESS) + { + DWORD dataType; + err = RegGetValueW( + OpenResult, + NULL, + this->GetCLSID(), + RRF_RT_ANY, + &dataType, + NULL, + 0); + RegCloseKey(OpenResult); + if (err != ERROR_SUCCESS) + { + return false; + } + } + else + { + return false; + } + return true; + } + + std::wstring FileExplorerPreviewSettings::GetName() const + { + return this->m_name; + } + + void FileExplorerPreviewSettings::SetName(const std::wstring& name) + { + this->m_name = name; + } + + std::wstring FileExplorerPreviewSettings::GetDescription() const + { + return this->m_description; + } + + void FileExplorerPreviewSettings::SetDescription(const std::wstring& description) + { + this->m_description = description; + } + + LPCWSTR FileExplorerPreviewSettings::GetSubKey() const + { + return this->m_subKey; + } + + LPCWSTR FileExplorerPreviewSettings::GetCLSID() const + { + return this->m_clsid; + } + + std::wstring FileExplorerPreviewSettings::GetDisplayName() const + { + return this->m_displayName; + } + + void FileExplorerPreviewSettings::SetDisplayName(const std::wstring& displayName) + { + this->m_displayName = displayName; + } + + // Preview Pane SVG Render Settings + PrevPaneSVGRendrSettings::PrevPaneSVGRendrSettings() + : + FileExplorerPreviewSettings( + false, + GET_RESOURCE_STRING(IDS_PREVPANE_SVG_BOOL_TOGGLE_CONTROLL), + GET_RESOURCE_STRING(IDS_PREVPANE_SVG_SETTINGS_DESCRIPTION), + L"{ddee2b8a-6807-48a6-bb20-2338174ff779}", + GET_RESOURCE_STRING(IDS_PREVPANE_SVG_SETTINGS_DISPLAYNAME)) {} + + void PrevPaneSVGRendrSettings::EnablePreview() + { + if (this->SetRegistryValue() == ERROR_SUCCESS) + { + Trace::ExplorerSVGRenderEnabled(); + this->SetState(true); + } + else + { + Trace::PowerPreviewSettingsUpDateFailed(this->GetName().c_str()); + } + } + + void PrevPaneSVGRendrSettings::DisablePreview() + { + if (this->DeleteRegistryValue() == ERROR_SUCCESS) + { + Trace::ExplorerSVGRenderDisabled(); + this->SetState(false); + } + else + { + Trace::PowerPreviewSettingsUpDateFailed(this->GetName().c_str()); + } + } + + // Preview Pane Mark Down Render Settings + PrevPaneMDRendrSettings::PrevPaneMDRendrSettings() + : + FileExplorerPreviewSettings( + false, + GET_RESOURCE_STRING(IDS_PREVPANE_MD_BOOL_TOGGLE_CONTROLL), + GET_RESOURCE_STRING(IDS_PREVPANE_MD_SETTINGS_DESCRIPTION), + L"{45769bcc-e8fd-42d0-947e-02beef77a1f5}", + GET_RESOURCE_STRING(IDS_PREVPANE_MD_SETTINGS_DISPLAYNAME)) {} + + void PrevPaneMDRendrSettings::EnablePreview() + { + if (this->SetRegistryValue() == ERROR_SUCCESS) + { + Trace::ExplorerSVGRenderEnabled(); + this->SetState(true); + } + else + { + Trace::PowerPreviewSettingsUpDateFailed(this->GetName().c_str()); + } + } + + void PrevPaneMDRendrSettings::DisablePreview() + { + if (this->DeleteRegistryValue() == ERROR_SUCCESS) + { + Trace::ExplorerSVGRenderDisabled(); + this->SetState(false); + } + else + { + Trace::PowerPreviewSettingsUpDateFailed(this->GetName().c_str()); + } + } + +} diff --git a/src/modules/previewpane/powerpreview/settings.h b/src/modules/previewpane/powerpreview/settings.h new file mode 100644 index 000000000000..e235c1f78c16 --- /dev/null +++ b/src/modules/previewpane/powerpreview/settings.h @@ -0,0 +1,62 @@ +#pragma once +#include +#include +#include "resource.h" +#include + +namespace PowerPreviewSettings +{ + // PowerToy Winodws Explore File Preview Settings. + class FileExplorerPreviewSettings + { + private: + bool m_isPreviewEnabled; + std::wstring m_name; + std::wstring m_description; + std::wstring m_displayName; + + LPCWSTR m_clsid; + LPCWSTR m_subKey = L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers"; + + public: + FileExplorerPreviewSettings(bool state, const std::wstring name, const std::wstring description, LPCWSTR clsid, const std::wstring displayname); + FileExplorerPreviewSettings(); + + virtual bool GetState() const; + virtual void SetState(bool state); + virtual void LoadState(PowerToysSettings::PowerToyValues& settings); + virtual void UpdateState(PowerToysSettings::PowerToyValues& values); + virtual std::wstring GetName() const; + virtual void SetName(const std::wstring& name); + virtual std::wstring GetDescription() const; + virtual void SetDescription(const std::wstring& description); + virtual void SetDisplayName(const std::wstring& displayName); + virtual LONG SetRegistryValue() const; + virtual LONG DeleteRegistryValue() const; + virtual bool GetRegistryValue() const; + virtual std::wstring GetDisplayName() const; + virtual LPCWSTR GetCLSID() const; + virtual LPCWSTR GetSubKey() const; + virtual void EnablePreview() = 0; + virtual void DisablePreview() = 0; + }; + + class PrevPaneSVGRendrSettings : public FileExplorerPreviewSettings + { + public: + PrevPaneSVGRendrSettings(); + + virtual void EnablePreview(); + virtual void DisablePreview(); + }; + + class PrevPaneMDRendrSettings : public FileExplorerPreviewSettings + { + public: + PrevPaneMDRendrSettings(); + + virtual void EnablePreview(); + virtual void DisablePreview(); + }; + +} diff --git a/src/modules/previewpane/powerpreview/trace.cpp b/src/modules/previewpane/powerpreview/trace.cpp new file mode 100644 index 000000000000..0a736ac2d32a --- /dev/null +++ b/src/modules/previewpane/powerpreview/trace.cpp @@ -0,0 +1,155 @@ +#include "pch.h" +#include "trace.h" +#include + +/* +* +* This file captures the telemetry for the File Explorer Custom Renders project. +* The following telemetry is to be captured for this library: +* (1.) Is the previewer enabled. +* (2.) File rendered per user in 24 hrs per file time (one for MD, one for SVG) +* (3.) Crashes. +* +*/ + +TRACELOGGING_DEFINE_PROVIDER( + g_hProvider, + "Microsoft.PowerToys", + // {38e8889b-9731-53f5-e901-e8a7c1753074} + (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), + TraceLoggingOptionProjectTelemetry()); + +void Trace::RegisterProvider() +{ + TraceLoggingRegister(g_hProvider); +} + +void Trace::UnregisterProvider() +{ + TraceLoggingUnregister(g_hProvider); +} + +void Trace::FilePreviewerIsEnabled() +{ + TraceLoggingWrite( + g_hProvider, + "PowerPreview_ExplorerFilePreview_PreviewIsEnabled", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} + +void Trace::FilePreviewerIsDisabled() +{ + TraceLoggingWrite( + g_hProvider, + "PowerPreview_ExplorerFilePreview_PreviewIsDisabled", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} + +void Trace::ExplorerSVGRenderEnabled() +{ + TraceLoggingWrite( + g_hProvider, + "PowerPreview_ExplorerFilePreview_SVGRenderEnabled", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} + +void Trace::ExplorerSVGRenderDisabled() +{ + TraceLoggingWrite( + g_hProvider, + "PowerPreview_ExplorerFilePreview_SVGRenderDisabled", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} + +void Trace::PowerPreviewSettingsUpDateFailed(LPCWSTR SettingsName) +{ + TraceLoggingWrite( + g_hProvider, + "PowerPreview_FilePreview_FailedUpdatingSettings", + TraceLoggingWideString(SettingsName, "ExceptionMessage"), + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} + +void Trace::PreviewPaneSVGRenderEnabled() +{ + TraceLoggingWrite( + g_hProvider, + "PowerPreview_PreviewPane_SVGRenderEnabled", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} + +void Trace::PreviewPaneSVGRenderDisabled() +{ + TraceLoggingWrite( + g_hProvider, + "PowerPreview_PreviewPane__SVGRenderDisabled", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} + + +void Trace::PreviewPaneMarkDownRenderDisabled() +{ + TraceLoggingWrite( + g_hProvider, + "PowerPreview_PreviewPane_MDRenderEnabled", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} + +void Trace::PreviewPaneMarkDownRenderEnabled() +{ + TraceLoggingWrite( + g_hProvider, + "PowerPreview_PreviewPane__MDRenderDisabled", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} + +void Trace::SetConfigInvalidJSON(const char* exceptionMessage) +{ + TraceLoggingWrite( + g_hProvider, + "PowerPreview_SetConfig__InvalidJSONGiven", + TraceLoggingString(exceptionMessage, "ExceptionMessage"), + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} + +void Trace::Destroyed() +{ + TraceLoggingWrite( + g_hProvider, + "PowerPreview__Destroyed", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} + +void Trace::InitSetErrorLoadingFile(const char* exceptionMessage) +{ + TraceLoggingWrite( + g_hProvider, + "PowerPreview_InitSet__ErrorLoadingFile", + TraceLoggingString(exceptionMessage, "ExceptionMessage"), + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} + diff --git a/src/modules/previewpane/powerpreview/trace.h b/src/modules/previewpane/powerpreview/trace.h new file mode 100644 index 000000000000..2af219bfda8f --- /dev/null +++ b/src/modules/previewpane/powerpreview/trace.h @@ -0,0 +1,20 @@ +#pragma once + +class Trace +{ +public: + static void RegisterProvider(); + static void UnregisterProvider(); + static void FilePreviewerIsEnabled(); + static void FilePreviewerIsDisabled(); + static void ExplorerSVGRenderEnabled(); + static void ExplorerSVGRenderDisabled(); + static void PreviewPaneSVGRenderEnabled(); + static void PreviewPaneSVGRenderDisabled(); + static void PreviewPaneMarkDownRenderDisabled(); + static void PreviewPaneMarkDownRenderEnabled(); + static void SetConfigInvalidJSON(const char* exceptionMessage); + static void InitSetErrorLoadingFile(const char* exceptionMessage); + static void PowerPreviewSettingsUpDateFailed(LPCWSTR SettingsName); + static void Destroyed(); +}; diff --git a/src/modules/previewpane/powerpreviewTest/BaseSettingsClassTest.cpp b/src/modules/previewpane/powerpreviewTest/BaseSettingsClassTest.cpp new file mode 100644 index 000000000000..cad755137215 --- /dev/null +++ b/src/modules/previewpane/powerpreviewTest/BaseSettingsClassTest.cpp @@ -0,0 +1,18 @@ +#include "pch.h" +#include "BaseSettingsClassTest.h" +#include +#include + +extern "C" IMAGE_DOS_HEADER __ImageBase; + +BaseSettingsClassTest::BaseSettingsClassTest() : + FileExplorerPreviewSettings( + false, + GET_RESOURCE_STRING(IDS_PREVPANE_MD_BOOL_TOGGLE_CONTROLL), + GET_RESOURCE_STRING(IDS_PREVPANE_MD_SETTINGS_DESCRIPTION), + L"{test-guid}", + TEXT("Test Handler\0")) {} + +void BaseSettingsClassTest::EnablePreview() {} + +void BaseSettingsClassTest::DisablePreview() {} diff --git a/src/modules/previewpane/powerpreviewTest/BaseSettingsClassTest.h b/src/modules/previewpane/powerpreviewTest/BaseSettingsClassTest.h new file mode 100644 index 000000000000..d05ddd01b60c --- /dev/null +++ b/src/modules/previewpane/powerpreviewTest/BaseSettingsClassTest.h @@ -0,0 +1,15 @@ +#include "pch.h" +#include +#include + +using namespace PowerPreviewSettings; + +class BaseSettingsClassTest : public FileExplorerPreviewSettings +{ +public: + BaseSettingsClassTest(); + + virtual void EnablePreview(); + + virtual void DisablePreview(); +}; diff --git a/src/modules/previewpane/powerpreviewTest/FileExplorerPreviewSettingsTest.cpp b/src/modules/previewpane/powerpreviewTest/FileExplorerPreviewSettingsTest.cpp new file mode 100644 index 000000000000..980964ea3384 --- /dev/null +++ b/src/modules/previewpane/powerpreviewTest/FileExplorerPreviewSettingsTest.cpp @@ -0,0 +1,78 @@ +#include "pch.h" +#include "CppUnitTest.h" +#include +#include + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; +using namespace PowerToysSettings; +using namespace PowerPreviewSettings; + +namespace PreviewHandlerSettingsTest +{ + TEST_CLASS(BaseSettingsTest) + { + public: + TEST_METHOD(LoadState_ShouldLoadNewState_WhenSucessfull) + { + // Arrange + BaseSettingsClassTest tempSettings = BaseSettingsClassTest(); + PowerToyValues values = PowerToyValues::from_json_string(GetJSONSettings(tempSettings.GetName(), L"true")); + tempSettings.SetState(false); + bool expectedState = true; + + // Act + tempSettings.LoadState(values); + bool actualState = tempSettings.GetState(); + + // Assert + Assert::AreEqual(actualState, expectedState); + } + + TEST_METHOD(UpdateState_ShouldChangeState_WhenSucessfull) + { + // Arrange + BaseSettingsClassTest tempSettings = BaseSettingsClassTest(); + PowerToyValues values = PowerToyValues::from_json_string(GetJSONSettings(tempSettings.GetName(), L"true")); + tempSettings.SetState(false); + bool expectedState = true; + + // Act + tempSettings.UpdateState(values); + bool actualState = tempSettings.GetState(); + + // Assert + Assert::AreEqual(actualState, expectedState); + } + + TEST_METHOD(SetRegistryValue_ShouldCreateAValueInRegistry_WhenSucessfull) + { + // Arrange + BaseSettingsClassTest tempSettings = BaseSettingsClassTest(); + + // Act + tempSettings.SetRegistryValue(); + bool results = tempSettings.GetRegistryValue(); + + // Assert + Assert::IsTrue(results); + } + + TEST_METHOD(RemoveRegistryValue_ShouldDeleteAValueInRegistry_WhenSucessfull) + { + // Arrange + BaseSettingsClassTest tempSettings = BaseSettingsClassTest(); + + // Act + tempSettings.SetRegistryValue(); + bool results = tempSettings.DeleteRegistryValue(); + + // Assert + Assert::IsFalse(results); + } + + std::wstring GetJSONSettings(const std::wstring &_settingsNameId, const std::wstring &_value) const + { + return L"{\"name\":\"Module Name\",\"properties\" : {\"" + _settingsNameId + L"\":{\"value\":" + _value + L"}},\"version\" : \"1.0\" }"; + } + }; +} diff --git a/src/modules/previewpane/powerpreviewTest/MarkDownPreviewSettingsClassTest.cpp b/src/modules/previewpane/powerpreviewTest/MarkDownPreviewSettingsClassTest.cpp new file mode 100644 index 000000000000..7c7a2ffdc844 --- /dev/null +++ b/src/modules/previewpane/powerpreviewTest/MarkDownPreviewSettingsClassTest.cpp @@ -0,0 +1,47 @@ +#include "pch.h" +#include "CppUnitTest.h" +#include +#include +#include + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; +using namespace PowerToysSettings; +using namespace PowerPreviewSettings; + +namespace PreviewHandlerSettingsTest +{ + TEST_CLASS(MarkDownPreviewSettingsClassTest) + { + public: + TEST_METHOD(EnableRender_ShouldUpdateStateToTrue_WhenSuccessful) + { + // Arrange + PrevPaneMDRendrSettings tempSettings = PrevPaneMDRendrSettings(); + tempSettings.SetState(false); //preview handler initially disabled + + // Act + tempSettings.EnablePreview(); + + // Assert + Assert::IsTrue(tempSettings.GetState()); + } + + TEST_METHOD(DisableRender_ShouldUpdateStateToFalse_WhenSuccessful) + { + // Arrange + PrevPaneMDRendrSettings tempSettings = PrevPaneMDRendrSettings(); + tempSettings.SetState(true); //preview handler initially enabled + + // Act + tempSettings.DisablePreview(); + + // Assert + Assert::IsFalse(tempSettings.GetState()); + } + + std::wstring GetJSONSettings(const std::wstring &_settingsNameId,const std::wstring &_value) const + { + return L"{\"name\":\"Module Name\",\"properties\" : {\"" + _settingsNameId + L"\":{\"value\":" + _value + L"}},\"version\" : \"1.0\" }"; + } + }; +} diff --git a/src/modules/previewpane/powerpreviewTest/SVGPreviewSettingsClassTest.cpp b/src/modules/previewpane/powerpreviewTest/SVGPreviewSettingsClassTest.cpp new file mode 100644 index 000000000000..ac50f3b4281a --- /dev/null +++ b/src/modules/previewpane/powerpreviewTest/SVGPreviewSettingsClassTest.cpp @@ -0,0 +1,46 @@ +#include "pch.h" +#include "CppUnitTest.h" +#include +#include + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; +using namespace PowerToysSettings; +using namespace PowerPreviewSettings; + +namespace PreviewHandlerSettingsTest +{ + TEST_CLASS(SVGPreviewSettingsClassTest) + { + public: + TEST_METHOD(EnableRender_ShouldUpdateStateToTrue_WhenSuccessful) + { + // Arrange + PrevPaneSVGRendrSettings tempSettings = PrevPaneSVGRendrSettings(); + tempSettings.SetState(false); //preview handler initially disabled + + // Act + tempSettings.EnablePreview(); + + // Assert + Assert::IsTrue(tempSettings.GetState()); + } + + TEST_METHOD(DisableRender_ShouldUpdateStateToFalse_WhenSuccessful) + { + // Arrange + PrevPaneSVGRendrSettings tempSettings = PrevPaneSVGRendrSettings(); + tempSettings.SetState(true); //preview handler initially enabled + + // Act + tempSettings.DisablePreview(); + + // Assert + Assert::IsFalse(tempSettings.GetState()); + } + + std::wstring GetJSONSettings(const std::wstring &_settingsNameId,const std::wstring &_value) const + { + return L"{\"name\":\"Module Name\",\"properties\" : {\"" + _settingsNameId + L"\":{\"value\":" + _value + L"}},\"version\" : \"1.0\" }"; + } + }; +} diff --git a/src/modules/previewpane/powerpreviewTest/pch.cpp b/src/modules/previewpane/powerpreviewTest/pch.cpp new file mode 100644 index 000000000000..a3d1ff4225e9 --- /dev/null +++ b/src/modules/previewpane/powerpreviewTest/pch.cpp @@ -0,0 +1,3 @@ +#include "pch.h" +#pragma comment(lib, "windowsapp") +#pragma comment(lib, "shlwapi.lib") \ No newline at end of file diff --git a/src/modules/previewpane/powerpreviewTest/pch.h b/src/modules/previewpane/powerpreviewTest/pch.h new file mode 100644 index 000000000000..85bdb7bd5cb3 --- /dev/null +++ b/src/modules/previewpane/powerpreviewTest/pch.h @@ -0,0 +1,15 @@ +// pch.h: This is a precompiled header file. +// Files listed below are compiled only once, improving build performance for future builds. +// This also affects IntelliSense performance, including code completion and many code browsing features. +// However, files listed here are ALL re-compiled if any one of them is updated between builds. +// Do not add files here that you will be updating frequently as this negates the performance advantage. + +#ifndef PCH_H +#define PCH_H + +// add headers that you want to pre-compile here +#include +#include +#include "CppUnitTest.h" + +#endif //PCH_H diff --git a/src/modules/previewpane/powerpreviewTest/powerpreviewTest.vcxproj b/src/modules/previewpane/powerpreviewTest/powerpreviewTest.vcxproj new file mode 100644 index 000000000000..73333aa674b2 --- /dev/null +++ b/src/modules/previewpane/powerpreviewTest/powerpreviewTest.vcxproj @@ -0,0 +1,186 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {47310AB4-9034-4BD1-8D8B-E88AD21A171B} + Win32Proj + powerpreviewTest + 10.0 + NativeUnitTestProject + + + + DynamicLibrary + true + v142 + Unicode + false + + + DynamicLibrary + false + v142 + true + Unicode + false + + + DynamicLibrary + true + v142 + Unicode + false + + + DynamicLibrary + false + v142 + true + Unicode + false + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + true + + + + Use + Level3 + true + ..\;..\..\..\common;..\..\..\common\telemetry;..\..\;..\..\..\;..\..\..\..\deps\cpprestsdk\include;..\..\..\..\;%(AdditionalIncludeDirectories) + _DEBUG;%(PreprocessorDefinitions) + true + pch.h + stdcpplatest + MultiThreadedDebug + + + Windows + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + Use + Level3 + true + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;%(PreprocessorDefinitions) + true + pch.h + + + Windows + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + Use + Level3 + true + true + true + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;%(PreprocessorDefinitions) + true + pch.h + + + Windows + true + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + Use + Level3 + true + true + true + ..\;..\..\..\common;..\..\..\common\telemetry;..\..\;..\..\..\;..\..\..\..\deps\cpprestsdk\include;..\..\..\..\;%(AdditionalIncludeDirectories) + NDEBUG;%(PreprocessorDefinitions) + true + pch.h + stdcpplatest + MultiThreaded + + + Windows + true + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + + + + Create + Create + Create + Create + + + + + + + + + + {74485049-c722-400f-abe5-86ac52d929b3} + + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/powerpreviewTest/powerpreviewTest.vcxproj.filters b/src/modules/previewpane/powerpreviewTest/powerpreviewTest.vcxproj.filters new file mode 100644 index 000000000000..2985fe0fc912 --- /dev/null +++ b/src/modules/previewpane/powerpreviewTest/powerpreviewTest.vcxproj.filters @@ -0,0 +1,42 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/src/runner/main.cpp b/src/runner/main.cpp index 20e48bcb2167..077d2ee99eda 100644 --- a/src/runner/main.cpp +++ b/src/runner/main.cpp @@ -59,7 +59,8 @@ int runner(bool isProcessElevated) std::unordered_set known_dlls = { L"shortcut_guide.dll", L"fancyzones.dll", - L"PowerRenameExt.dll" + L"PowerRenameExt.dll", + L"powerpreview.dll" }; for (auto& file : std::filesystem::directory_iterator(L"modules/")) { diff --git a/src/settings-web/package-lock.json b/src/settings-web/package-lock.json index 744b97e65e20..844f89f9fdb5 100644 --- a/src/settings-web/package-lock.json +++ b/src/settings-web/package-lock.json @@ -1139,9 +1139,9 @@ } }, "@microsoft/load-themed-styles": { - "version": "1.10.26", - "resolved": "https://registry.npmjs.org/@microsoft/load-themed-styles/-/load-themed-styles-1.10.26.tgz", - "integrity": "sha512-N//pFTBL/iCSrMuDoLvBLpgGjlk+MgKX2kyFI3bJVb+LRozeyWCOZVRcR8aMKiYHdqwy5isu2Frp8drvWx7RbA==" + "version": "1.10.33", + "resolved": "https://registry.npmjs.org/@microsoft/load-themed-styles/-/load-themed-styles-1.10.33.tgz", + "integrity": "sha512-6ke2PEMbjizIvqhSeb/K6iUStEJcfONndE1kfB46dstFYZ9jJmYgO6UHUZkA3VMufmjdI1z28bJKz3cMzmkZGw==" }, "@microsoft/package-deps-hash": { "version": "2.2.159", @@ -1422,25 +1422,54 @@ } }, "@uifabric/foundation": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@uifabric/foundation/-/foundation-7.5.0.tgz", - "integrity": "sha512-eymMyV3e+MFCkcfC1AFIAzVP/h6/QvDcYb1l6K3IaG1QG47ZwijJJXseEvNDjimfUiJhez9H7cSsRZPIIJ5MaQ==", - "requires": { - "@uifabric/merge-styles": "^7.8.0", - "@uifabric/set-version": "^7.0.2", - "@uifabric/styling": "^7.7.2", - "@uifabric/utilities": "^7.5.0", - "tslib": "^1.7.1" + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/@uifabric/foundation/-/foundation-7.5.2.tgz", + "integrity": "sha512-TNDVWjVDV/UbGOMbCsRblDY6yciTL9GPMOT1b4Ibuul6ytUVLaUCRchdnMf+8JIUNBwZIDMMJWwDVpdwURqpvQ==", + "requires": { + "@uifabric/merge-styles": "^7.8.2", + "@uifabric/set-version": "^7.0.3", + "@uifabric/styling": "^7.10.1", + "@uifabric/utilities": "^7.11.2", + "tslib": "^1.10.0" + }, + "dependencies": { + "@uifabric/merge-styles": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/@uifabric/merge-styles/-/merge-styles-7.8.2.tgz", + "integrity": "sha512-CiGZkOQegNdrXIaVvgd8pumeHLm3odSRE21rsrA7HiqdyF+fh6ArQ0RWsKTlJRIQklCUJjMkXaO54QIWNOlQyg==", + "requires": { + "@uifabric/set-version": "^7.0.3", + "tslib": "^1.10.0" + } + }, + "@uifabric/set-version": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@uifabric/set-version/-/set-version-7.0.3.tgz", + "integrity": "sha512-03A68Fyfx3y75dUW9rjQ2fZv/9zmGgMeovVLAQa0wc/oVjQ++eVDlAEK0AjfgnOaujYmhk79lXbYAuW3n+YUXw==", + "requires": { + "tslib": "^1.10.0" + } + } } }, "@uifabric/icons": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/@uifabric/icons/-/icons-7.3.0.tgz", - "integrity": "sha512-wbcR8fJce20sPjsK2bbTC/cAZfAOFuE4dd4LHw194+8H+/dqotsowrQVp5Lu8aaHGQk+fXoiZmUy30WA9cAG4Q==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@uifabric/icons/-/icons-7.3.1.tgz", + "integrity": "sha512-Jb2KjC6s+nY75H7U8/i23iKRwYFS3cavTXVYDz69guwLEhWh0aZ1QZqbAbUfF+YikVabJQmT8YT0Pup8/VbeWA==", "requires": { - "@uifabric/set-version": "^7.0.2", - "@uifabric/styling": "^7.7.1", - "tslib": "^1.7.1" + "@uifabric/set-version": "^7.0.3", + "@uifabric/styling": "^7.10.1", + "tslib": "^1.10.0" + }, + "dependencies": { + "@uifabric/set-version": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@uifabric/set-version/-/set-version-7.0.3.tgz", + "integrity": "sha512-03A68Fyfx3y75dUW9rjQ2fZv/9zmGgMeovVLAQa0wc/oVjQ++eVDlAEK0AjfgnOaujYmhk79lXbYAuW3n+YUXw==", + "requires": { + "tslib": "^1.10.0" + } + } } }, "@uifabric/merge-styles": { @@ -1453,13 +1482,23 @@ } }, "@uifabric/react-hooks": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@uifabric/react-hooks/-/react-hooks-7.0.1.tgz", - "integrity": "sha512-cIr/ToLvc48D7A+XJrH/rHmSa/YmonvyFGHykFqHKiFSYiGKvc50GIyRJ/gkOUDuaaYVThWRwBQNOIMr3iFCYA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@uifabric/react-hooks/-/react-hooks-7.0.2.tgz", + "integrity": "sha512-Ly2loVgrSJ3VYHvyOp6Q23aieOcX3w80Cf8t8+gXRZjLXgNh39omOhucD1nVnSlnUy+w88vDhr2aC1dCiw/o7w==", "requires": { - "@uifabric/set-version": "^7.0.2", - "@uifabric/utilities": "^7.0.10", - "tslib": "^1.7.1" + "@uifabric/set-version": "^7.0.3", + "@uifabric/utilities": "^7.11.2", + "tslib": "^1.10.0" + }, + "dependencies": { + "@uifabric/set-version": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@uifabric/set-version/-/set-version-7.0.3.tgz", + "integrity": "sha512-03A68Fyfx3y75dUW9rjQ2fZv/9zmGgMeovVLAQa0wc/oVjQ++eVDlAEK0AjfgnOaujYmhk79lXbYAuW3n+YUXw==", + "requires": { + "tslib": "^1.10.0" + } + } } }, "@uifabric/set-version": { @@ -1471,26 +1510,64 @@ } }, "@uifabric/styling": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@uifabric/styling/-/styling-7.8.0.tgz", - "integrity": "sha512-mRNQUvfasOWW0/RJARA5mPHmPMORJXrNDoOpjoTNt+J6uj1/sA8km0l/AQtZ6b36bqo6kkaQgB9+msRobmdpiQ==", - "requires": { - "@microsoft/load-themed-styles": "^1.7.13", - "@uifabric/merge-styles": "^7.8.1", - "@uifabric/set-version": "^7.0.2", - "@uifabric/utilities": "^7.8.0", - "tslib": "^1.7.1" + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@uifabric/styling/-/styling-7.10.1.tgz", + "integrity": "sha512-/G0BbIS1tI1SEiUD4mNxfNXq6dLyff6Ey5YqeMGx6v1AyQ2dWBolhGSYK8l7XGlBQCNZ+gR+AV4M6+8gWOkAaw==", + "requires": { + "@microsoft/load-themed-styles": "^1.10.26", + "@uifabric/merge-styles": "^7.8.2", + "@uifabric/set-version": "^7.0.3", + "@uifabric/utilities": "^7.11.2", + "tslib": "^1.10.0" + }, + "dependencies": { + "@uifabric/merge-styles": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/@uifabric/merge-styles/-/merge-styles-7.8.2.tgz", + "integrity": "sha512-CiGZkOQegNdrXIaVvgd8pumeHLm3odSRE21rsrA7HiqdyF+fh6ArQ0RWsKTlJRIQklCUJjMkXaO54QIWNOlQyg==", + "requires": { + "@uifabric/set-version": "^7.0.3", + "tslib": "^1.10.0" + } + }, + "@uifabric/set-version": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@uifabric/set-version/-/set-version-7.0.3.tgz", + "integrity": "sha512-03A68Fyfx3y75dUW9rjQ2fZv/9zmGgMeovVLAQa0wc/oVjQ++eVDlAEK0AjfgnOaujYmhk79lXbYAuW3n+YUXw==", + "requires": { + "tslib": "^1.10.0" + } + } } }, "@uifabric/utilities": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@uifabric/utilities/-/utilities-7.8.0.tgz", - "integrity": "sha512-ow3v9arBhe5C8GvklIuqD3PZMSNnY04HC7JoQVW301Q/gH+BGVECLTBzrFm/m66GCetMksDOMnkoBQSk0poPOQ==", + "version": "7.11.2", + "resolved": "https://registry.npmjs.org/@uifabric/utilities/-/utilities-7.11.2.tgz", + "integrity": "sha512-PZnBsV3fyS6FiqZlP7byBosRNvmiIg9B8TLRl0YVQYr4LDDpLE05HKYxLLrp7SepPX9sxOh5AXgwrgDPojAkpg==", "requires": { - "@uifabric/merge-styles": "^7.8.1", - "@uifabric/set-version": "^7.0.2", + "@uifabric/merge-styles": "^7.8.2", + "@uifabric/set-version": "^7.0.3", "prop-types": "^15.5.10", - "tslib": "^1.7.1" + "tslib": "^1.10.0" + }, + "dependencies": { + "@uifabric/merge-styles": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/@uifabric/merge-styles/-/merge-styles-7.8.2.tgz", + "integrity": "sha512-CiGZkOQegNdrXIaVvgd8pumeHLm3odSRE21rsrA7HiqdyF+fh6ArQ0RWsKTlJRIQklCUJjMkXaO54QIWNOlQyg==", + "requires": { + "@uifabric/set-version": "^7.0.3", + "tslib": "^1.10.0" + } + }, + "@uifabric/set-version": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@uifabric/set-version/-/set-version-7.0.3.tgz", + "integrity": "sha512-03A68Fyfx3y75dUW9rjQ2fZv/9zmGgMeovVLAQa0wc/oVjQ++eVDlAEK0AjfgnOaujYmhk79lXbYAuW3n+YUXw==", + "requires": { + "tslib": "^1.10.0" + } + } } }, "@webassemblyjs/ast": { @@ -8476,20 +8553,39 @@ "dev": true }, "office-ui-fabric-react": { - "version": "7.76.2", - "resolved": "https://registry.npmjs.org/office-ui-fabric-react/-/office-ui-fabric-react-7.76.2.tgz", - "integrity": "sha512-rWQrkv1o4jOdYhNk3GsLDxJw8mo1+6HspGNILiodRj7RfJx/OmSyVM4KT2gGJ5k2PZGNCdp7f7kp1JuICd31Mg==", - "requires": { - "@microsoft/load-themed-styles": "^1.7.13", - "@uifabric/foundation": "^7.5.0", - "@uifabric/icons": "^7.3.0", - "@uifabric/merge-styles": "^7.8.1", - "@uifabric/react-hooks": "^7.0.1", - "@uifabric/set-version": "^7.0.2", - "@uifabric/styling": "^7.8.0", - "@uifabric/utilities": "^7.8.0", + "version": "7.83.1", + "resolved": "https://registry.npmjs.org/office-ui-fabric-react/-/office-ui-fabric-react-7.83.1.tgz", + "integrity": "sha512-XDL+9baCqq8kDA1VHrAlRvfnWKEQRJVx9hlzsbFNxzMpZtXNH0ip4SHzahRRU0eqCXHBNkd5ZA4xUV532Uv8dQ==", + "requires": { + "@microsoft/load-themed-styles": "^1.10.26", + "@uifabric/foundation": "^7.5.2", + "@uifabric/icons": "^7.3.1", + "@uifabric/merge-styles": "^7.8.2", + "@uifabric/react-hooks": "^7.0.2", + "@uifabric/set-version": "^7.0.3", + "@uifabric/styling": "^7.10.1", + "@uifabric/utilities": "^7.11.2", "prop-types": "^15.5.10", - "tslib": "^1.7.1" + "tslib": "^1.10.0" + }, + "dependencies": { + "@uifabric/merge-styles": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/@uifabric/merge-styles/-/merge-styles-7.8.2.tgz", + "integrity": "sha512-CiGZkOQegNdrXIaVvgd8pumeHLm3odSRE21rsrA7HiqdyF+fh6ArQ0RWsKTlJRIQklCUJjMkXaO54QIWNOlQyg==", + "requires": { + "@uifabric/set-version": "^7.0.3", + "tslib": "^1.10.0" + } + }, + "@uifabric/set-version": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@uifabric/set-version/-/set-version-7.0.3.tgz", + "integrity": "sha512-03A68Fyfx3y75dUW9rjQ2fZv/9zmGgMeovVLAQa0wc/oVjQ++eVDlAEK0AjfgnOaujYmhk79lXbYAuW3n+YUXw==", + "requires": { + "tslib": "^1.10.0" + } + } } }, "on-finished": { diff --git a/src/settings-web/package.json b/src/settings-web/package.json index e9d09b659487..983ff89f97ef 100644 --- a/src/settings-web/package.json +++ b/src/settings-web/package.json @@ -30,7 +30,8 @@ "dependencies": { "@svgr/webpack": "^4.3.3", "@uifabric/azure-themes": "^7.0.10", - "office-ui-fabric-react": "^7.76.2", + "@uifabric/styling": "^7.10.1", + "office-ui-fabric-react": "^7.83.1", "react": "~16.8.0", "react-dom": "~16.8.0" }, diff --git a/src/settings-web/src/components/ModuleSettings.tsx b/src/settings-web/src/components/ModuleSettings.tsx index cc31d0780915..7069e0d3e691 100644 --- a/src/settings-web/src/components/ModuleSettings.tsx +++ b/src/settings-web/src/components/ModuleSettings.tsx @@ -163,6 +163,10 @@ export class ModuleSettings extends React.Component { on_change={this.parent_on_change} ref={(input) => {this.references[key]=input;}} />; + case 'header_large': + return {power_toys_properties[key].value}; default: return null; } diff --git a/src/settings-web/src/css/layout.css b/src/settings-web/src/css/layout.css index 3ccd4912f7d5..ffe03a130410 100644 --- a/src/settings-web/src/css/layout.css +++ b/src/settings-web/src/css/layout.css @@ -58,3 +58,6 @@ flex-direction: column; flex-grow: 1; } +.SubHeader{ + font-weight: bold; +} diff --git a/src/settings-web/src/icons/config/fabric-icons.json b/src/settings-web/src/icons/config/fabric-icons.json index 59e779e78357..3a91c42eb1b3 100644 --- a/src/settings-web/src/icons/config/fabric-icons.json +++ b/src/settings-web/src/icons/config/fabric-icons.json @@ -72,6 +72,10 @@ { "name": "CircleRing", "unicode": "EA3A" + }, + { + "name": "FabricReportLibrary", + "unicode": "F0A1" } ] } \ No newline at end of file diff --git a/src/settings-web/src/icons/css/fabric-icons-inline.css b/src/settings-web/src/icons/css/fabric-icons-inline.css index 86340e05531c..28c494dc7421 100644 --- a/src/settings-web/src/icons/css/fabric-icons-inline.css +++ b/src/settings-web/src/icons/css/fabric-icons-inline.css @@ -3,7 +3,7 @@ */ @font-face { font-family: 'FabricMDL2Icons'; - src: url('data:application/octet-stream;base64,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') format('truetype'); + src: url('data:application/octet-stream;base64,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') format('truetype'); } .ms-Icon { @@ -32,3 +32,4 @@ .ms-Icon--ChevronLeftMed:before { content: "\E973"; } .ms-Icon--ChevronRightMed:before { content: "\E974"; } .ms-Icon--CircleRing:before { content: "\EA3A"; } +.ms-Icon--FabricReportLibrary:before { content: "\F0A1"; } diff --git a/src/settings-web/src/icons/css/fabric-icons.css b/src/settings-web/src/icons/css/fabric-icons.css index a0e2e4d0b2e4..529cb871d216 100644 --- a/src/settings-web/src/icons/css/fabric-icons.css +++ b/src/settings-web/src/icons/css/fabric-icons.css @@ -32,3 +32,4 @@ .ms-Icon--ChevronLeftMed:before { content: "\E973"; } .ms-Icon--ChevronRightMed:before { content: "\E974"; } .ms-Icon--CircleRing:before { content: "\EA3A"; } +.ms-Icon--FabricReportLibrary:before { content: "\F0A1"; } diff --git a/src/settings-web/src/icons/fabric-icons.html b/src/settings-web/src/icons/fabric-icons.html index 1beaeb3ec18c..4dddb7cabd01 100644 --- a/src/settings-web/src/icons/fabric-icons.html +++ b/src/settings-web/src/icons/fabric-icons.html @@ -271,6 +271,19 @@

fabric-icons

+
+
+ +
+ +
+ FabricReportLibrary +
+ +
+ F0A1 +
+
\ No newline at end of file diff --git a/src/settings-web/src/icons/fonts/fabric-icons.woff b/src/settings-web/src/icons/fonts/fabric-icons.woff index 6b8d48adf44c2d19ee98cf9dd94e23667d0fc141..fd90f0ee2841b3a9f18e9e0333f847385f7eb6e0 100644 GIT binary patch delta 2649 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 delta 2452 zcmXX`c{r3`8-B;mWEvS;_DagWWH%@~F)?NkF=H8i7{(G3z4j#V^BdzcF7@1Ofoe-RD!;GY|-*M1lm56%Xb$d?M@yRkv4bW0FtZLEbCUXdUPnd$l?TO#!>zXG^=|${n6{x zgY3vWY#_evoMG#x5I4(tXIRf@t(aBPY{V&0${}uA9Fvp?8{L@Rm|Wdn#cpC5XVjBr z!53{t+tXLyb8R4jXkmga#}RpM5&g?_u2*^t&iHV?NWxA6Um5I;j?mY8!?TQ+)0+{b zOL4X@c-d^d*0H+uP*>)1-^* zgxBVf;v~Jf1_5LkI~EU83KTMBYXh z*SK|ic#@3LJpB~YihozUbkxj17=$8RgXinMJ9C~J>^ke{|KT{U+ZC7auzcaHIr#a( z=>5(z>}S(V8(!voWn*E-R&D{@b_0!A_BL!qJvT8swJ(LU)&|wA=rnrc7#`74cAZ%D zy_d4`iB*|hFJmxUQW;jb?tM;$950Q~&9!zijx@X+p!>&bjoAwq3pELG#Is&qx0~(} zjA=Wlyjx#HSeIkURw!J?KK+id2TQJ$D&cnYJ@Ait#)>;8adD}ra01zqe$C~B+~}a{ z*y^#{WB(tN$oXWysaktiibut58luaOz08jA#oP9Kcwq5gLP^b{ zf2wBx`G9JCG3+z`OzKK+73Mv#ig{eWBb=W&1ADa2JB4AJGcO9svKU8xfmx(p#m-KA z*zMx&r1dQ8^RmhMBe^Ws28QpSiU~ennfc6$e^rU@5SjL@(k5qEWqJ_#5WqcvGPu!uL=5*-ey=ZIuGg`mBLUMz?CDo417RjQ6 zb-x-Kl{Y-HP&wLf2zRg121(Z1br!J|CL&iKeitjwQ;$C`Ea;{k5hP!LKkkoORmLe` zpA4K)*78n&SIBmT0`Y~~*)Xb%Cw+5b&Ov{4OfaLs=yCKiJz=D^@lp7F*6Jc%!U(o& zn;Ww^fWEHLRM`Q-5N??;U~l{ge$a3`J}=eVU|1P5dR=*BLJ>U3;awJiGxPb6j|@JG zNR7lU1BoIt6ySGHAfdpAmjwR54(V6)@q(L*TA50)lY2hB|^ zZRGra#wUNQcAlX(H^$^I`&=_y8Xg&My8wdAy&Nyb-VbSj$$w*VFuB$g-Wc?^q;u?} zOwFD$MdOp(=Eqm{xjuGq%w-J#Auo@~xWGr!&`vo1(wF)6dIIa)X4 z(~X|21#}!{2gP4qK`i7l>|=9QS^OB}ZqbThi_u?I<}JBb;E^`?rG~;487W5|)8>ik zsf^UH+;I@|*DhApYl*(~BSrt&?nJr1_S5EewTf6DdZU+>@BC>R{<^-Nr9_JU(8(>m zv;O^4qqjV1=!8p09L)zbY4a7;cgKx~uXzT_4m9fbQ8AlX!-amYxsD)Wa^9i1>fg?r zLmg@sDt{`s1;Qk)Cr8yvYOf$hK2NbaU)H2 zwJxqB1!&wCtbwRG^NYohMfr*AwuY5;qd2Q@@p-jstoeZaD)#2nc3R_njHF3o;^CJ4 z`zPcpv^DZlc;T=zZi{Q^lT*L?29=zAke|Rj=9}X#Y%vP8fy2%i-ZseoT(!gFns&2) zAe4G>)j_j#vT!aW#r(8S@l3{QiS}SZ*ltGaj^2eZ{i6T { registerIconAlias('twelvepointstar', '12pointstar'); registerIconAlias('toggleon', 'toggleleft'); registerIconAlias('toggleoff', 'toggleright'); + registerIconAlias('edgelogo', 'edgeoldlogo'); + registerIconAlias('powerappscds', 'commondataservicecds'); + registerIconAlias('d365talenthrcore', 'd365corehr'); + registerIconAlias('crmcustomerinsightsapp', 'insights'); } export default registerIconAliases; diff --git a/src/settings-web/src/setup_icons.tsx b/src/settings-web/src/setup_icons.tsx index 8f25b7905a57..148bf8cac89c 100644 --- a/src/settings-web/src/setup_icons.tsx +++ b/src/settings-web/src/setup_icons.tsx @@ -1,5 +1,6 @@ import React from 'react'; import {registerIcons} from 'office-ui-fabric-react'; +import {Icon} from 'office-ui-fabric-react/lib/Icon'; import {initializeIcons} from './icons/src'; // Import SVG files for the icons here. @@ -15,6 +16,7 @@ export function setup_powertoys_icons(): void { 'pt-fancy-zones': ( ), 'pt-power-rename': ( ), 'pt-shortcut-guide': ( ), + 'pt-power-preview': ( ), } }); } diff --git a/src/settings/settings-html/dist/bundle.js b/src/settings/settings-html/dist/bundle.js index a3ab9a0c24de..8f591e3edbcd 100644 --- a/src/settings/settings-html/dist/bundle.js +++ b/src/settings/settings-html/dist/bundle.js @@ -1,9 +1,9 @@ -!function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=34)}([function(e,t,n){"use strict";e.exports=n(25)},,,,,,,function(e,t,n){"use strict";!function e(){if("undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE){0;try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(e){console.error(e)}}}(),e.exports=n(26)},,,,,function(e,t,n){"use strict";var o={},r=void 0;try{r=window}catch(e){}function i(e,t){if(void 0!==r){var n=r.__packages__=r.__packages__||{};if(!n[e]||!o[e])o[e]=t,(n[e]=n[e]||[]).push(t)}}n.d(t,"a",(function(){return i})),i("@uifabric/set-version","6.0.0")},,,,function(e,t,n){"use strict"; +!function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=35)}([function(e,t,n){"use strict";e.exports=n(26)},,,,,,,function(e,t,n){"use strict";!function e(){if("undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE){0;try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(e){console.error(e)}}}(),e.exports=n(27)},,,,,,,,,function(e,t,n){"use strict"; /* object-assign (c) Sindre Sorhus @license MIT -*/var o=Object.getOwnPropertySymbols,r=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable;function a(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var o={};return"abcdefghijklmnopqrst".split("").forEach((function(e){o[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},o)).join("")}catch(e){return!1}}()?Object.assign:function(e,t){for(var n,s,l=a(e),u=1;u0&&(!function(e){void 0===e&&(e=3);3!==e&&2!==e||(u(a.registeredStyles),a.registeredStyles=[]);3!==e&&1!==e||(u(a.registeredThemableStyles),a.registeredThemableStyles=[])}(1),s([].concat.apply([],e)))}}()}function u(e){e.forEach((function(e){var t=e&&e.styleElement;t&&t.parentElement&&t.parentElement.removeChild(t)}))}function c(e){var t=a.theme,n=!1;return{styleString:(e||[]).map((function(e){var o=e.theme;if(o){n=!0;var r=t?t[o]:void 0,i=e.defaultValue||"inherit";return!t||r||!console||o in t||"undefined"==typeof DEBUG||!DEBUG||console.warn('Theming value not provided for "'+o+'". Falling back to "'+i+'".'),r||i}return e.rawString})).join(""),themable:n}}}).call(this,n(17))},,,,,,,function(e,t,n){"use strict"; +*/var o=Object.getOwnPropertySymbols,r=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable;function a(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var o={};return"abcdefghijklmnopqrst".split("").forEach((function(e){o[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},o)).join("")}catch(e){return!1}}()?Object.assign:function(e,t){for(var n,s,l=a(e),u=1;u0&&(!function(e){void 0===e&&(e=3);3!==e&&2!==e||(u(a.registeredStyles),a.registeredStyles=[]);3!==e&&1!==e||(u(a.registeredThemableStyles),a.registeredThemableStyles=[])}(1),s([].concat.apply([],e)))}}()}function u(e){e.forEach((function(e){var t=e&&e.styleElement;t&&t.parentElement&&t.parentElement.removeChild(t)}))}function c(e){var t=a.theme,n=!1;return{styleString:(e||[]).map((function(e){var o=e.theme;if(o){n=!0;var r=t?t[o]:void 0,i=e.defaultValue||"inherit";return!t||r||!console||o in t||"undefined"==typeof DEBUG||!DEBUG||console.warn('Theming value not provided for "'+o+'". Falling back to "'+i+'".'),r||i}return e.rawString})).join(""),themable:n}}}).call(this,n(17))},,,,,,,,function(e,t,n){"use strict"; /** @license React v16.8.6 * react.production.min.js * @@ -11,7 +11,7 @@ object-assign * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var o=n(16),r="function"==typeof Symbol&&Symbol.for,i=r?Symbol.for("react.element"):60103,a=r?Symbol.for("react.portal"):60106,s=r?Symbol.for("react.fragment"):60107,l=r?Symbol.for("react.strict_mode"):60108,u=r?Symbol.for("react.profiler"):60114,c=r?Symbol.for("react.provider"):60109,d=r?Symbol.for("react.context"):60110,p=r?Symbol.for("react.concurrent_mode"):60111,f=r?Symbol.for("react.forward_ref"):60112,h=r?Symbol.for("react.suspense"):60113,m=r?Symbol.for("react.memo"):60115,g=r?Symbol.for("react.lazy"):60116,v="function"==typeof Symbol&&Symbol.iterator;function y(e){for(var t=arguments.length-1,n="https://reactjs.org/docs/error-decoder.html?invariant="+e,o=0;oN.length&&N.push(e)}function F(e,t,n){return null==e?0:function e(t,n,o,r){var s=typeof t;"undefined"!==s&&"boolean"!==s||(t=null);var l=!1;if(null===t)l=!0;else switch(s){case"string":case"number":l=!0;break;case"object":switch(t.$$typeof){case i:case a:l=!0}}if(l)return o(r,t,""===n?"."+O(t,0):n),1;if(l=0,n=""===n?".":n+":",Array.isArray(t))for(var u=0;uD.length&&D.push(e)}function F(e,t,n){return null==e?0:function e(t,n,o,r){var s=typeof t;"undefined"!==s&&"boolean"!==s||(t=null);var l=!1;if(null===t)l=!0;else switch(s){case"string":case"number":l=!0;break;case"object":switch(t.$$typeof){case i:case a:l=!0}}if(l)return o(r,t,""===n?"."+O(t,0):n),1;if(l=0,n=""===n?".":n+":",Array.isArray(t))for(var u=0;uthis.eventPool.length&&this.eventPool.push(e)}function pe(e){e.eventPool=[],e.getPooled=ce,e.release=de}r(ue.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=se)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=se)},persist:function(){this.isPersistent=se},isPersistent:le,destructor:function(){var e,t=this.constructor.Interface;for(e in t)this[e]=null;this.nativeEvent=this._targetInst=this.dispatchConfig=null,this.isPropagationStopped=this.isDefaultPrevented=le,this._dispatchInstances=this._dispatchListeners=null}}),ue.Interface={type:null,target:null,currentTarget:function(){return null},eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null},ue.extend=function(e){function t(){}function n(){return o.apply(this,arguments)}var o=this;t.prototype=o.prototype;var i=new t;return r(i,n.prototype),n.prototype=i,n.prototype.constructor=n,n.Interface=r({},o.Interface,e),n.extend=o.extend,pe(n),n},pe(ue);var fe=ue.extend({data:null}),he=ue.extend({data:null}),me=[9,13,27,32],ge=G&&"CompositionEvent"in window,ve=null;G&&"documentMode"in document&&(ve=document.documentMode);var ye=G&&"TextEvent"in window&&!ve,be=G&&(!ge||ve&&8=ve),_e=String.fromCharCode(32),ke={beforeInput:{phasedRegistrationNames:{bubbled:"onBeforeInput",captured:"onBeforeInputCapture"},dependencies:["compositionend","keypress","textInput","paste"]},compositionEnd:{phasedRegistrationNames:{bubbled:"onCompositionEnd",captured:"onCompositionEndCapture"},dependencies:"blur compositionend keydown keypress keyup mousedown".split(" ")},compositionStart:{phasedRegistrationNames:{bubbled:"onCompositionStart",captured:"onCompositionStartCapture"},dependencies:"blur compositionstart keydown keypress keyup mousedown".split(" ")},compositionUpdate:{phasedRegistrationNames:{bubbled:"onCompositionUpdate",captured:"onCompositionUpdateCapture"},dependencies:"blur compositionupdate keydown keypress keyup mousedown".split(" ")}},xe=!1;function Ce(e,t){switch(e){case"keyup":return-1!==me.indexOf(t.keyCode);case"keydown":return 229!==t.keyCode;case"keypress":case"mousedown":case"blur":return!0;default:return!1}}function we(e){return"object"==typeof(e=e.detail)&&"data"in e?e.data:null}var Se=!1;var Ee={eventTypes:ke,extractEvents:function(e,t,n,o){var r=void 0,i=void 0;if(ge)e:{switch(e){case"compositionstart":r=ke.compositionStart;break e;case"compositionend":r=ke.compositionEnd;break e;case"compositionupdate":r=ke.compositionUpdate;break e}r=void 0}else Se?Ce(e,n)&&(r=ke.compositionEnd):"keydown"===e&&229===n.keyCode&&(r=ke.compositionStart);return r?(be&&"ko"!==n.locale&&(Se||r!==ke.compositionStart?r===ke.compositionEnd&&Se&&(i=ae()):(re="value"in(oe=o)?oe.value:oe.textContent,Se=!0)),r=fe.getPooled(r,t,n,o),i?r.data=i:null!==(i=we(n))&&(r.data=i),V(r),i=r):i=null,(e=ye?function(e,t){switch(e){case"compositionend":return we(t);case"keypress":return 32!==t.which?null:(xe=!0,_e);case"textInput":return(e=t.data)===_e&&xe?null:e;default:return null}}(e,n):function(e,t){if(Se)return"compositionend"===e||!ge&&Ce(e,t)?(e=ae(),ie=re=oe=null,Se=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1