From 5cfbd72fa81a489d266558f99fbf16f20defea0a Mon Sep 17 00:00:00 2001 From: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com> Date: Mon, 25 Oct 2021 15:40:19 +0200 Subject: [PATCH] [PowerRename] Fluent UX (#13678) * PowerRename new UI * Add scrollviewer * Don't deploy PowerRenameUI_new * Visual updates * Visual updates * Updates * Update Resources.resw * Added docs button * Update MainWindow.xaml * Wire Docs button * RegEx -> regular expressions * Update Show only renamed list on search/replace text changed * Update Show only renamed list on search/replace text changed - proper fix Set searchTerm to NULL when cleared - fix Show only renamed files on clear searchTerm * Files/folders input error handling * Fix renaming with keeping UI window opened After renaming folder, all of it's children need path update. Without path update, further renaming of children items would fail. * Update only children, not all items with greater depth * Fix dictionary false positives * Remove .NET dep * Rename PowerRenameUI_new to PowerRenameUILib Rename executable PowerRenameUIHost to PowerRename Co-authored-by: Laute --- .github/actions/spell-check/expect.txt | 7 + .pipelines/pipeline.user.windows.yml | 2 + Cpp.Build.props | 3 +- PowerToys.sln | 42 +- Solution.props | 6 + doc/devdocs/modules/powerrename.md | 3 - installer/PowerToysSetup/Product.wxs | 21 +- src/common/interop/pch.h | 1 + src/common/utils/HDropIterator.h | 58 + .../imageresizer/dll/ContextMenuHandler.cpp | 2 +- .../imageresizer/dll/HDropIterator.cpp | 49 - src/modules/imageresizer/dll/HDropIterator.h | 17 - .../imageresizer/dll/ImageResizerExt.vcxproj | 2 - .../dll/ImageResizerExt.vcxproj.filters | 6 - .../PowerRenameUIHost/PowerRenameUIHost.cpp | 917 +++++++++ .../PowerRenameUIHost/PowerRenameUIHost.h | 153 ++ .../PowerRenameUIHost.ico} | Bin .../PowerRenameUIHost/PowerRenameUIHost.rc | 150 ++ .../PowerRenameUIHost.vcxproj | 165 ++ .../PowerRenameUIHost.vcxproj.filters | 67 + .../powerrename/PowerRenameUIHost/Resource.h | 30 + .../PowerRenameUIHost/XamlBridge.cpp | 249 +++ .../PowerRenameUIHost/XamlBridge.h | 108 ++ .../PowerRenameUIHost/app.manifest | 8 + .../powerrename/PowerRenameUIHost/framework.h | 15 + .../PowerRenameUIHost/packages.config | 10 + .../{ui => PowerRenameUIHost}/pch.cpp | 0 .../powerrename/PowerRenameUIHost/pch.h | 5 + .../powerrename/PowerRenameUIHost/small.ico | Bin 0 -> 118203 bytes .../{ui => PowerRenameUIHost}/targetver.h | 0 .../powerrename/PowerRenameUILib/App.cpp | 18 + .../powerrename/PowerRenameUILib/App.h | 18 + .../powerrename/PowerRenameUILib/App.idl | 7 + .../powerrename/PowerRenameUILib/App.xaml | 1066 +++++++++++ .../Assets/LockScreenLogo.scale-200.png | Bin 0 -> 1430 bytes .../Assets/SplashScreen.scale-200.png | Bin 0 -> 7700 bytes .../Assets/Square150x150Logo.scale-200.png | Bin 0 -> 2937 bytes .../Assets/Square44x44Logo.scale-200.png | Bin 0 -> 1647 bytes ...x44Logo.targetsize-24_altform-unplated.png | Bin 0 -> 1255 bytes .../PowerRenameUILib/Assets/StoreLogo.png | Bin 0 -> 1451 bytes .../Assets/Wide310x150Logo.scale-200.png | Bin 0 -> 3204 bytes .../PowerRenameUILib/Assets/file.png | Bin 0 -> 44217 bytes .../PowerRenameUILib/Assets/folder.png | Bin 0 -> 65705 bytes .../PowerRenameUILib/ExplorerItem.cpp | 105 ++ .../PowerRenameUILib/ExplorerItem.h | 48 + .../PowerRenameUILib/ExplorerItem.idl | 14 + .../ExplorerItemTemplateSelector.cpp | 37 + .../ExplorerItemTemplateSelector.h | 28 + .../ExplorerItemTemplateSelector.idl | 11 + .../{ui => PowerRenameUILib}/LocProject.json | 6 +- .../PowerRenameUILib/MainWindow.cpp | 326 ++++ .../powerrename/PowerRenameUILib/MainWindow.h | 88 + .../PowerRenameUILib/MainWindow.idl | 61 + .../PowerRenameUILib/MainWindow.xaml | 326 ++++ .../PowerRenameUILib/Package.appxmanifest | 27 + .../PowerRenameUILib/PatternSnippet.cpp | 49 + .../PowerRenameUILib/PatternSnippet.h | 29 + .../PowerRenameUILib/PatternSnippet.idl | 9 + .../PowerRenameUILib/PowerRenameUILib.filters | 77 + .../PowerRenameUILib/PowerRenameUILib.vcxproj | 210 +++ .../PowerRenameUILib/PropertySheet.props | 16 + .../Strings/en-us/Resources.resw | 335 ++++ .../PowerRenameUILib/UIUpdates.cpp | 90 + .../powerrename/PowerRenameUILib/UIUpdates.h | 39 + .../powerrename/PowerRenameUILib/app.base.h | 35 + .../PowerRenameUILib/packages.config | 6 + .../powerrename/PowerRenameUILib/pch.cpp | 1 + .../powerrename/PowerRenameUILib/pch.h | 27 + .../PowerRenameUILib/placeholder.exe | 0 .../powerrename/PowerRenameUILib/readme.txt | 23 + .../powerrename/UWPui/PowerRenameUWPUI.rc | 1 - .../UWPui/PowerRenameUWPUI.vcxproj | 3 - .../powerrename/dll/PowerRenameExt.base.rc | 4 + .../powerrename/dll/PowerRenameExt.cpp | 182 +- src/modules/powerrename/dll/PowerRenameExt.h | 3 +- .../powerrename/dll/PowerRenameExt.vcxproj | 5 +- src/modules/powerrename/dll/pch.h | 3 + src/modules/powerrename/lib/Helpers.cpp | 60 + src/modules/powerrename/lib/Helpers.h | 11 +- .../powerrename/lib/MRUListHandler.cpp | 181 ++ src/modules/powerrename/lib/MRUListHandler.h | 35 + .../powerrename/lib/PowerRenameEnum.cpp | 14 +- src/modules/powerrename/lib/PowerRenameEnum.h | 2 +- .../powerrename/lib/PowerRenameInterfaces.h | 41 +- .../powerrename/lib/PowerRenameItem.cpp | 32 +- src/modules/powerrename/lib/PowerRenameItem.h | 2 + .../powerrename/lib/PowerRenameLib.vcxproj | 7 + .../powerrename/lib/PowerRenameMRU.cpp | 87 + src/modules/powerrename/lib/PowerRenameMRU.h | 34 + .../powerrename/lib/PowerRenameManager.cpp | 123 +- .../powerrename/lib/PowerRenameManager.h | 9 +- .../powerrename/lib/PowerRenameRegEx.cpp | 17 +- .../powerrename/lib/PowerRenameRegEx.h | 4 +- src/modules/powerrename/lib/Settings.cpp | 389 +--- src/modules/powerrename/lib/Settings.h | 5 - .../powerrename/testapp/PowerRenameTest.cpp | 73 +- .../testapp/PowerRenameTest.vcxproj | 2 +- .../powerrename/ui/PowerRenameUI.base.rc | 85 - src/modules/powerrename/ui/PowerRenameUI.cpp | 1674 ----------------- src/modules/powerrename/ui/PowerRenameUI.h | 215 --- .../powerrename/ui/PowerRenameUI.vcxproj | 93 - .../ui/PowerRenameUI.vcxproj.filters | 55 - src/modules/powerrename/ui/Resources.resx | 217 --- .../modules/powerrename/ui/Resources.resx.lcl | 306 --- .../modules/powerrename/ui/Resources.resx.lcl | 306 --- .../modules/powerrename/ui/Resources.resx.lcl | 306 --- .../modules/powerrename/ui/Resources.resx.lcl | 306 --- .../modules/powerrename/ui/Resources.resx.lcl | 306 --- .../modules/powerrename/ui/Resources.resx.lcl | 306 --- .../modules/powerrename/ui/Resources.resx.lcl | 306 --- .../modules/powerrename/ui/Resources.resx.lcl | 306 --- .../modules/powerrename/ui/Resources.resx.lcl | 306 --- .../modules/powerrename/ui/Resources.resx.lcl | 306 --- .../modules/powerrename/ui/Resources.resx.lcl | 306 --- .../modules/powerrename/ui/Resources.resx.lcl | 306 --- .../modules/powerrename/ui/Resources.resx.lcl | 306 --- .../modules/powerrename/ui/Resources.resx.lcl | 306 --- .../modules/powerrename/ui/Resources.resx.lcl | 306 --- .../modules/powerrename/ui/Resources.resx.lcl | 306 --- .../modules/powerrename/ui/Resources.resx.lcl | 306 --- src/modules/powerrename/ui/packages.config | 4 - src/modules/powerrename/ui/pch.h | 13 - src/modules/powerrename/ui/resource.base.h | 35 - .../MockPowerRenameManagerEvents.cpp | 23 +- .../unittests/MockPowerRenameManagerEvents.h | 7 +- .../unittests/PowerRenameLibUnitTests.vcxproj | 2 +- .../unittests/PowerRenameManagerTests.cpp | 4 +- tools/localization/move_uwp_resources.ps1 | 8 +- 128 files changed, 5913 insertions(+), 8281 deletions(-) create mode 100644 Solution.props create mode 100644 src/common/utils/HDropIterator.h delete mode 100644 src/modules/imageresizer/dll/HDropIterator.cpp delete mode 100644 src/modules/imageresizer/dll/HDropIterator.h create mode 100644 src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.cpp create mode 100644 src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.h rename src/modules/powerrename/{ui/PowerRename.ico => PowerRenameUIHost/PowerRenameUIHost.ico} (100%) create mode 100644 src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.rc create mode 100644 src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.vcxproj create mode 100644 src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.vcxproj.filters create mode 100644 src/modules/powerrename/PowerRenameUIHost/Resource.h create mode 100644 src/modules/powerrename/PowerRenameUIHost/XamlBridge.cpp create mode 100644 src/modules/powerrename/PowerRenameUIHost/XamlBridge.h create mode 100644 src/modules/powerrename/PowerRenameUIHost/app.manifest create mode 100644 src/modules/powerrename/PowerRenameUIHost/framework.h create mode 100644 src/modules/powerrename/PowerRenameUIHost/packages.config rename src/modules/powerrename/{ui => PowerRenameUIHost}/pch.cpp (100%) create mode 100644 src/modules/powerrename/PowerRenameUIHost/pch.h create mode 100644 src/modules/powerrename/PowerRenameUIHost/small.ico rename src/modules/powerrename/{ui => PowerRenameUIHost}/targetver.h (100%) create mode 100644 src/modules/powerrename/PowerRenameUILib/App.cpp create mode 100644 src/modules/powerrename/PowerRenameUILib/App.h create mode 100644 src/modules/powerrename/PowerRenameUILib/App.idl create mode 100644 src/modules/powerrename/PowerRenameUILib/App.xaml create mode 100644 src/modules/powerrename/PowerRenameUILib/Assets/LockScreenLogo.scale-200.png create mode 100644 src/modules/powerrename/PowerRenameUILib/Assets/SplashScreen.scale-200.png create mode 100644 src/modules/powerrename/PowerRenameUILib/Assets/Square150x150Logo.scale-200.png create mode 100644 src/modules/powerrename/PowerRenameUILib/Assets/Square44x44Logo.scale-200.png create mode 100644 src/modules/powerrename/PowerRenameUILib/Assets/Square44x44Logo.targetsize-24_altform-unplated.png create mode 100644 src/modules/powerrename/PowerRenameUILib/Assets/StoreLogo.png create mode 100644 src/modules/powerrename/PowerRenameUILib/Assets/Wide310x150Logo.scale-200.png create mode 100644 src/modules/powerrename/PowerRenameUILib/Assets/file.png create mode 100644 src/modules/powerrename/PowerRenameUILib/Assets/folder.png create mode 100644 src/modules/powerrename/PowerRenameUILib/ExplorerItem.cpp create mode 100644 src/modules/powerrename/PowerRenameUILib/ExplorerItem.h create mode 100644 src/modules/powerrename/PowerRenameUILib/ExplorerItem.idl create mode 100644 src/modules/powerrename/PowerRenameUILib/ExplorerItemTemplateSelector.cpp create mode 100644 src/modules/powerrename/PowerRenameUILib/ExplorerItemTemplateSelector.h create mode 100644 src/modules/powerrename/PowerRenameUILib/ExplorerItemTemplateSelector.idl rename src/modules/powerrename/{ui => PowerRenameUILib}/LocProject.json (51%) create mode 100644 src/modules/powerrename/PowerRenameUILib/MainWindow.cpp create mode 100644 src/modules/powerrename/PowerRenameUILib/MainWindow.h create mode 100644 src/modules/powerrename/PowerRenameUILib/MainWindow.idl create mode 100644 src/modules/powerrename/PowerRenameUILib/MainWindow.xaml create mode 100644 src/modules/powerrename/PowerRenameUILib/Package.appxmanifest create mode 100644 src/modules/powerrename/PowerRenameUILib/PatternSnippet.cpp create mode 100644 src/modules/powerrename/PowerRenameUILib/PatternSnippet.h create mode 100644 src/modules/powerrename/PowerRenameUILib/PatternSnippet.idl create mode 100644 src/modules/powerrename/PowerRenameUILib/PowerRenameUILib.filters create mode 100644 src/modules/powerrename/PowerRenameUILib/PowerRenameUILib.vcxproj create mode 100644 src/modules/powerrename/PowerRenameUILib/PropertySheet.props create mode 100644 src/modules/powerrename/PowerRenameUILib/Strings/en-us/Resources.resw create mode 100644 src/modules/powerrename/PowerRenameUILib/UIUpdates.cpp create mode 100644 src/modules/powerrename/PowerRenameUILib/UIUpdates.h create mode 100644 src/modules/powerrename/PowerRenameUILib/app.base.h create mode 100644 src/modules/powerrename/PowerRenameUILib/packages.config create mode 100644 src/modules/powerrename/PowerRenameUILib/pch.cpp create mode 100644 src/modules/powerrename/PowerRenameUILib/pch.h create mode 100644 src/modules/powerrename/PowerRenameUILib/placeholder.exe create mode 100644 src/modules/powerrename/PowerRenameUILib/readme.txt create mode 100644 src/modules/powerrename/lib/MRUListHandler.cpp create mode 100644 src/modules/powerrename/lib/MRUListHandler.h create mode 100644 src/modules/powerrename/lib/PowerRenameMRU.cpp create mode 100644 src/modules/powerrename/lib/PowerRenameMRU.h delete mode 100644 src/modules/powerrename/ui/PowerRenameUI.base.rc delete mode 100644 src/modules/powerrename/ui/PowerRenameUI.cpp delete mode 100644 src/modules/powerrename/ui/PowerRenameUI.h delete mode 100644 src/modules/powerrename/ui/PowerRenameUI.vcxproj delete mode 100644 src/modules/powerrename/ui/PowerRenameUI.vcxproj.filters delete mode 100644 src/modules/powerrename/ui/Resources.resx delete mode 100644 src/modules/powerrename/ui/loc/cs/src/modules/powerrename/ui/Resources.resx.lcl delete mode 100644 src/modules/powerrename/ui/loc/de/src/modules/powerrename/ui/Resources.resx.lcl delete mode 100644 src/modules/powerrename/ui/loc/es/src/modules/powerrename/ui/Resources.resx.lcl delete mode 100644 src/modules/powerrename/ui/loc/fr/src/modules/powerrename/ui/Resources.resx.lcl delete mode 100644 src/modules/powerrename/ui/loc/hu/src/modules/powerrename/ui/Resources.resx.lcl delete mode 100644 src/modules/powerrename/ui/loc/it/src/modules/powerrename/ui/Resources.resx.lcl delete mode 100644 src/modules/powerrename/ui/loc/ja/src/modules/powerrename/ui/Resources.resx.lcl delete mode 100644 src/modules/powerrename/ui/loc/ko/src/modules/powerrename/ui/Resources.resx.lcl delete mode 100644 src/modules/powerrename/ui/loc/nl/src/modules/powerrename/ui/Resources.resx.lcl delete mode 100644 src/modules/powerrename/ui/loc/pl/src/modules/powerrename/ui/Resources.resx.lcl delete mode 100644 src/modules/powerrename/ui/loc/pt-BR/src/modules/powerrename/ui/Resources.resx.lcl delete mode 100644 src/modules/powerrename/ui/loc/pt-PT/src/modules/powerrename/ui/Resources.resx.lcl delete mode 100644 src/modules/powerrename/ui/loc/ru/src/modules/powerrename/ui/Resources.resx.lcl delete mode 100644 src/modules/powerrename/ui/loc/sv/src/modules/powerrename/ui/Resources.resx.lcl delete mode 100644 src/modules/powerrename/ui/loc/tr/src/modules/powerrename/ui/Resources.resx.lcl delete mode 100644 src/modules/powerrename/ui/loc/zh-Hans/src/modules/powerrename/ui/Resources.resx.lcl delete mode 100644 src/modules/powerrename/ui/loc/zh-Hant/src/modules/powerrename/ui/Resources.resx.lcl delete mode 100644 src/modules/powerrename/ui/packages.config delete mode 100644 src/modules/powerrename/ui/pch.h delete mode 100644 src/modules/powerrename/ui/resource.base.h diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 562ce51cf4..5dbd6fafa0 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -7,6 +7,7 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ abgr abi ABlocked +ABOUTBOX Abug accctrl Acceleratorkeys @@ -1048,6 +1049,7 @@ IWeb IWIC IWindows IWork +IXaml IXml ixx IYUV @@ -1157,6 +1159,7 @@ lmcons LMEM LMENU lnk +LOADSTRING LOCALAPPDATA LOCALDISPLAY localhost @@ -1178,6 +1181,7 @@ lowlevel LOWORD lparam LPBYTE +LPCITEMIDLIST LPCMINVOKECOMMANDINFO LPCREATESTRUCT LPCTSTR @@ -1224,6 +1228,7 @@ LVS LVSIL LWA lwin +LZero lzw mailto MAINICON @@ -1579,6 +1584,7 @@ phwnd pici pid pidl +PIDLIST PINDIR pinfo pinvoke @@ -1611,6 +1617,7 @@ powerlauncher powerpreview powerrename POWERRENAMETEST +POWERRENAMEUIHOST powershell powertoy powertoysinterop diff --git a/.pipelines/pipeline.user.windows.yml b/.pipelines/pipeline.user.windows.yml index 8f4b0da2f4..22f6cf723a 100644 --- a/.pipelines/pipeline.user.windows.yml +++ b/.pipelines/pipeline.user.windows.yml @@ -172,6 +172,8 @@ build: - 'modules\launcher\Wox.Plugin.dll' - 'modules\MouseUtils\FindMyMouse.dll' - 'modules\PowerRename\PowerRenameExt.dll' + - 'modules\PowerRename\PowerRenameUILib.dll' + - 'modules\PowerRename\PowerRename.exe' - 'modules\ShortcutGuide\ShortcutGuide\PowerToys.ShortcutGuide.exe' - 'modules\ShortcutGuide\ShortcutGuideModuleInterface\ShortcutGuideModuleInterface.dll' - 'modules\VideoConference\VideoConferenceModule.dll' diff --git a/Cpp.Build.props b/Cpp.Build.props index b696fe7475..1f7d2a3931 100644 --- a/Cpp.Build.props +++ b/Cpp.Build.props @@ -76,7 +76,7 @@ - 10.0.17134.0 + 10.0.18362.0 @@ -84,7 +84,6 @@ v142 $(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\ Unicode - Spectre diff --git a/PowerToys.sln b/PowerToys.sln index 17e285653d..ad3f59a4e9 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -5,7 +5,6 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "runner", "src\runner\runner.vcxproj", "{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}" ProjectSection(ProjectDependencies) = postProject {217DF501-135C-4E38-BFC8-99D4821032EA} = {217DF501-135C-4E38-BFC8-99D4821032EA} - {0E072714-D127-460B-AFAD-B4C40B412798} = {0E072714-D127-460B-AFAD-B4C40B412798} {48804216-2A0E-4168-A6D8-9CD068D14227} = {48804216-2A0E-4168-A6D8-9CD068D14227} {51920F1F-C28C-4ADF-8660-4238766796C2} = {51920F1F-C28C-4ADF-8660-4238766796C2} {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB} = {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB} @@ -57,26 +56,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "powerrename", "powerrename" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRenameExt", "src\modules\powerrename\dll\PowerRenameExt.vcxproj", "{B25AC7A5-FB9F-4789-B392-D5C85E948670}" ProjectSection(ProjectDependencies) = postProject - {0E072714-D127-460B-AFAD-B4C40B412798} = {0E072714-D127-460B-AFAD-B4C40B412798} {51920F1F-C28C-4ADF-8660-4238766796C2} = {51920F1F-C28C-4ADF-8660-4238766796C2} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRenameLib", "src\modules\powerrename\lib\PowerRenameLib.vcxproj", "{51920F1F-C28C-4ADF-8660-4238766796C2}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRenameUI", "src\modules\powerrename\ui\PowerRenameUI.vcxproj", "{0E072714-D127-460B-AFAD-B4C40B412798}" - ProjectSection(ProjectDependencies) = postProject - {51920F1F-C28C-4ADF-8660-4238766796C2} = {51920F1F-C28C-4ADF-8660-4238766796C2} - EndProjectSection -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRenameTest", "src\modules\powerrename\testapp\PowerRenameTest.vcxproj", "{A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}" ProjectSection(ProjectDependencies) = postProject - {0E072714-D127-460B-AFAD-B4C40B412798} = {0E072714-D127-460B-AFAD-B4C40B412798} {51920F1F-C28C-4ADF-8660-4238766796C2} = {51920F1F-C28C-4ADF-8660-4238766796C2} 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} {B25AC7A5-FB9F-4789-B392-D5C85E948670} = {B25AC7A5-FB9F-4789-B392-D5C85E948670} EndProjectSection @@ -84,9 +75,6 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ModuleTemplateCompileTest", "tools\project_template\ModuleTemplate\ModuleTemplateCompileTest.vcxproj", "{64A80062-4D8B-4229-8A38-DFA1D7497749}" 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} - EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManager", "src\modules\keyboardmanager\dll\KeyboardManager.vcxproj", "{89F34AF7-1C34-4A72-AA6E-534BCF972BD9}" EndProject @@ -194,6 +182,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject src\.editorconfig = src\.editorconfig src\tests\win-app-driver\packages.config = src\tests\win-app-driver\packages.config + Solution.props = Solution.props EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Settings.UI.Library", "src\settings-ui\Microsoft.PowerToys.Settings.UI.Library\Microsoft.PowerToys.Settings.UI.Library.csproj", "{B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}" @@ -283,6 +272,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "utils", "utils", "{B39DC643 src\common\utils\EventLocker.h = src\common\utils\EventLocker.h src\common\utils\EventWaiter.h = src\common\utils\EventWaiter.h src\common\utils\exec.h = src\common\utils\exec.h + src\common\utils\HDropIterator.h = src\common\utils\HDropIterator.h src\common\utils\HttpClient.h = src\common\utils\HttpClient.h src\common\utils\json.h = src\common\utils\json.h src\common\utils\logger_helper.h = src\common\utils\logger_helper.h @@ -372,6 +362,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plu EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.WindowsTerminal.UnitTests", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.UnitTests\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.UnitTests.csproj", "{4ED320BC-BA04-4D42-8D15-CBE62151F08B}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRenameUIHost", "src\modules\powerrename\PowerRenameUIHost\PowerRenameUIHost.vcxproj", "{F7EC4E6C-19CA-4FBD-9918-B8AC5FEF4F63}" + ProjectSection(ProjectDependencies) = postProject + {4642D596-723F-4BFC-894C-46811219AC4A} = {4642D596-723F-4BFC-894C-46811219AC4A} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRenameUILib", "src\modules\powerrename\PowerRenameUILib\PowerRenameUILib.vcxproj", "{4642D596-723F-4BFC-894C-46811219AC4A}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MouseUtils", "MouseUtils", "{322566EF-20DC-43A6-B9F8-616AF942579A}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FindMyMouse", "src\modules\MouseUtils\FindMyMouse\FindMyMouse.vcxproj", "{E94FD11C-0591-456F-899F-EFC0CA548336}" @@ -426,12 +423,6 @@ Global {51920F1F-C28C-4ADF-8660-4238766796C2}.Release|x64.ActiveCfg = Release|x64 {51920F1F-C28C-4ADF-8660-4238766796C2}.Release|x64.Build.0 = Release|x64 {51920F1F-C28C-4ADF-8660-4238766796C2}.Release|x86.ActiveCfg = Release|x64 - {0E072714-D127-460B-AFAD-B4C40B412798}.Debug|x64.ActiveCfg = Debug|x64 - {0E072714-D127-460B-AFAD-B4C40B412798}.Debug|x64.Build.0 = Debug|x64 - {0E072714-D127-460B-AFAD-B4C40B412798}.Debug|x86.ActiveCfg = Debug|x64 - {0E072714-D127-460B-AFAD-B4C40B412798}.Release|x64.ActiveCfg = Release|x64 - {0E072714-D127-460B-AFAD-B4C40B412798}.Release|x64.Build.0 = Release|x64 - {0E072714-D127-460B-AFAD-B4C40B412798}.Release|x86.ActiveCfg = Release|x64 {A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Debug|x64.ActiveCfg = Debug|x64 {A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Debug|x64.Build.0 = Debug|x64 {A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Debug|x86.ActiveCfg = Debug|x64 @@ -985,6 +976,18 @@ Global {4ED320BC-BA04-4D42-8D15-CBE62151F08B}.Release|x64.Build.0 = Release|x64 {4ED320BC-BA04-4D42-8D15-CBE62151F08B}.Release|x86.ActiveCfg = Release|Any CPU {4ED320BC-BA04-4D42-8D15-CBE62151F08B}.Release|x86.Build.0 = Release|Any CPU + {F7EC4E6C-19CA-4FBD-9918-B8AC5FEF4F63}.Debug|x64.ActiveCfg = Debug|x64 + {F7EC4E6C-19CA-4FBD-9918-B8AC5FEF4F63}.Debug|x64.Build.0 = Debug|x64 + {F7EC4E6C-19CA-4FBD-9918-B8AC5FEF4F63}.Debug|x86.ActiveCfg = Debug|x64 + {F7EC4E6C-19CA-4FBD-9918-B8AC5FEF4F63}.Release|x64.ActiveCfg = Release|x64 + {F7EC4E6C-19CA-4FBD-9918-B8AC5FEF4F63}.Release|x64.Build.0 = Release|x64 + {F7EC4E6C-19CA-4FBD-9918-B8AC5FEF4F63}.Release|x86.ActiveCfg = Release|x64 + {4642D596-723F-4BFC-894C-46811219AC4A}.Debug|x64.ActiveCfg = Debug|x64 + {4642D596-723F-4BFC-894C-46811219AC4A}.Debug|x64.Build.0 = Debug|x64 + {4642D596-723F-4BFC-894C-46811219AC4A}.Debug|x86.ActiveCfg = Debug|x64 + {4642D596-723F-4BFC-894C-46811219AC4A}.Release|x64.ActiveCfg = Release|x64 + {4642D596-723F-4BFC-894C-46811219AC4A}.Release|x64.Build.0 = Release|x64 + {4642D596-723F-4BFC-894C-46811219AC4A}.Release|x86.ActiveCfg = Release|x64 {E94FD11C-0591-456F-899F-EFC0CA548336}.Debug|x64.ActiveCfg = Debug|x64 {E94FD11C-0591-456F-899F-EFC0CA548336}.Debug|x64.Build.0 = Debug|x64 {E94FD11C-0591-456F-899F-EFC0CA548336}.Debug|x86.ActiveCfg = Debug|x64 @@ -1005,7 +1008,6 @@ Global {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} {B25AC7A5-FB9F-4789-B392-D5C85E948670} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} {51920F1F-C28C-4ADF-8660-4238766796C2} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} - {0E072714-D127-460B-AFAD-B4C40B412798} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} {A3935CF4-46C5-4A88-84D3-6B12E16E6BA2} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} {2151F984-E006-4A9F-92EF-C6DDE3DC8413} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} {0485F45C-EA7A-4BB5-804B-3E8D14699387} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} @@ -1108,6 +1110,8 @@ Global {F40C3397-1834-4530-B2D9-8F8B8456BCDF} = {2F305555-C296-497E-AC20-5FA1B237996A} {A2D583F0-B70C-4462-B1F0-8E81AFB7BA85} = {4AFC9975-2456-4C70-94A4-84073C1CED93} {4ED320BC-BA04-4D42-8D15-CBE62151F08B} = {4AFC9975-2456-4C70-94A4-84073C1CED93} + {F7EC4E6C-19CA-4FBD-9918-B8AC5FEF4F63} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} + {4642D596-723F-4BFC-894C-46811219AC4A} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} {322566EF-20DC-43A6-B9F8-616AF942579A} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} {E94FD11C-0591-456F-899F-EFC0CA548336} = {322566EF-20DC-43A6-B9F8-616AF942579A} EndGlobalSection diff --git a/Solution.props b/Solution.props new file mode 100644 index 0000000000..1598df0553 --- /dev/null +++ b/Solution.props @@ -0,0 +1,6 @@ + + + + $(IntDir)Generated Files\ + + diff --git a/doc/devdocs/modules/powerrename.md b/doc/devdocs/modules/powerrename.md index b22984e6f1..79a72002f1 100644 --- a/doc/devdocs/modules/powerrename.md +++ b/doc/devdocs/modules/powerrename.md @@ -21,6 +21,3 @@ TODO #### [`trace.cpp`](/src/modules/powerrename/lib/trace.cpp) TODO - -#### [`PowerRenameUI.cpp`](/src/modules/powerrename/ui/PowerRenameUI.cpp) -TODO \ No newline at end of file diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 32df99d259..add2df8ec2 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -236,7 +236,9 @@ - + + + @@ -694,7 +696,15 @@ - + + + + + + + + + @@ -707,6 +717,12 @@ + + + + + + @@ -1011,6 +1027,7 @@ + diff --git a/src/common/interop/pch.h b/src/common/interop/pch.h index 409d905e38..2f415a620e 100644 --- a/src/common/interop/pch.h +++ b/src/common/interop/pch.h @@ -10,5 +10,6 @@ #define WIN32_LEAN_AND_MEAN // add headers that you want to pre-compile here #include +#include #endif //PCH_H diff --git a/src/common/utils/HDropIterator.h b/src/common/utils/HDropIterator.h new file mode 100644 index 0000000000..7fbc0d7173 --- /dev/null +++ b/src/common/utils/HDropIterator.h @@ -0,0 +1,58 @@ +#pragma once +#include + +class HDropIterator +{ +public: + HDropIterator(IDataObject* pDataObject) + { + _current = 0; + + FORMATETC formatetc = { + CF_HDROP, + NULL, + DVASPECT_CONTENT, + -1, + TYMED_HGLOBAL + }; + + pDataObject->GetData(&formatetc, &m_medium); + + _listCount = DragQueryFile((HDROP)m_medium.hGlobal, 0xFFFFFFFF, NULL, 0); + } + + ~HDropIterator() + { + ReleaseStgMedium(&m_medium); + } + + void First() + { + _current = 0; + } + + void Next() + { + _current++; + } + + bool IsDone() const + { + return _current >= _listCount; + } + + LPTSTR CurrentItem() const + { + UINT cch = DragQueryFile((HDROP)m_medium.hGlobal, _current, NULL, 0) + 1; + LPTSTR pszPath = (LPTSTR)malloc(sizeof(TCHAR) * cch); + + DragQueryFile((HDROP)m_medium.hGlobal, _current, pszPath, cch); + + return pszPath; + } + +private: + UINT _listCount; + STGMEDIUM m_medium; + UINT _current; +}; diff --git a/src/modules/imageresizer/dll/ContextMenuHandler.cpp b/src/modules/imageresizer/dll/ContextMenuHandler.cpp index 6674cffa57..681dabce7c 100644 --- a/src/modules/imageresizer/dll/ContextMenuHandler.cpp +++ b/src/modules/imageresizer/dll/ContextMenuHandler.cpp @@ -2,11 +2,11 @@ #include "pch.h" #include "ContextMenuHandler.h" -#include "HDropIterator.h" #include "Settings.h" #include #include #include +#include #include "trace.h" diff --git a/src/modules/imageresizer/dll/HDropIterator.cpp b/src/modules/imageresizer/dll/HDropIterator.cpp deleted file mode 100644 index bfc23b39ee..0000000000 --- a/src/modules/imageresizer/dll/HDropIterator.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "pch.h" -#include "HDropIterator.h" - -HDropIterator::HDropIterator(IDataObject* pdtobj) -{ - _current = 0; - - FORMATETC formatetc = { - CF_HDROP, - NULL, - DVASPECT_CONTENT, - -1, - TYMED_HGLOBAL - }; - - pdtobj->GetData(&formatetc, &m_medium); - - _listCount = DragQueryFile((HDROP)m_medium.hGlobal, 0xFFFFFFFF, NULL, 0); -} - -HDropIterator::~HDropIterator() -{ - ReleaseStgMedium(&m_medium); -} - -void HDropIterator::First() -{ - _current = 0; -} - -void HDropIterator::Next() -{ - _current++; -} - -bool HDropIterator::IsDone() const -{ - return _current >= _listCount; -} - -LPTSTR HDropIterator::CurrentItem() const -{ - UINT cch = DragQueryFile((HDROP)m_medium.hGlobal, _current, NULL, 0) + 1; - LPTSTR pszPath = (LPTSTR)malloc(sizeof(TCHAR) * cch); - - DragQueryFile((HDROP)m_medium.hGlobal, _current, pszPath, cch); - - return pszPath; -} diff --git a/src/modules/imageresizer/dll/HDropIterator.h b/src/modules/imageresizer/dll/HDropIterator.h deleted file mode 100644 index 8f1d26151d..0000000000 --- a/src/modules/imageresizer/dll/HDropIterator.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -class HDropIterator -{ -public: - HDropIterator(IDataObject *pDataObject); - ~HDropIterator(); - void First(); - void Next(); - bool IsDone() const; - LPTSTR CurrentItem() const; - -private: - UINT _listCount; - STGMEDIUM m_medium; - UINT _current; -}; diff --git a/src/modules/imageresizer/dll/ImageResizerExt.vcxproj b/src/modules/imageresizer/dll/ImageResizerExt.vcxproj index c5b18627e2..d97f61e4aa 100644 --- a/src/modules/imageresizer/dll/ImageResizerExt.vcxproj +++ b/src/modules/imageresizer/dll/ImageResizerExt.vcxproj @@ -77,7 +77,6 @@ - false @@ -97,7 +96,6 @@ - diff --git a/src/modules/imageresizer/dll/ImageResizerExt.vcxproj.filters b/src/modules/imageresizer/dll/ImageResizerExt.vcxproj.filters index bbd3a2437c..dd6a23aa9a 100644 --- a/src/modules/imageresizer/dll/ImageResizerExt.vcxproj.filters +++ b/src/modules/imageresizer/dll/ImageResizerExt.vcxproj.filters @@ -28,9 +28,6 @@ Source Files - - Source Files - Source Files @@ -57,9 +54,6 @@ Header Files - - Header Files - Header Files diff --git a/src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.cpp b/src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.cpp new file mode 100644 index 0000000000..28c85ab5a7 --- /dev/null +++ b/src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.cpp @@ -0,0 +1,917 @@ +// PowerRenameUIHost.cpp : Defines the entry point for the application. +// +#include "pch.h" + +#include "PowerRenameUIHost.h" +#include +#include +#include +#include +#include +#include + +#define MAX_LOADSTRING 100 + +const wchar_t c_WindowClass[] = L"PowerRename"; +HINSTANCE g_hostHInst; + +int AppWindow::Show(HINSTANCE hInstance, std::vector files) +{ + auto window = AppWindow(hInstance, files); + window.CreateAndShowWindow(); + return window.MessageLoop(window.m_accelerators.get()); +} + +LRESULT AppWindow::MessageHandler(UINT message, WPARAM wParam, LPARAM lParam) noexcept +{ + switch (message) + { + HANDLE_MSG(WindowHandle(), WM_CREATE, OnCreate); + HANDLE_MSG(WindowHandle(), WM_COMMAND, OnCommand); + HANDLE_MSG(WindowHandle(), WM_DESTROY, OnDestroy); + HANDLE_MSG(WindowHandle(), WM_SIZE, OnResize); + default: + return base_type::MessageHandler(message, wParam, lParam); + } + + return base_type::MessageHandler(message, wParam, lParam); +} + +AppWindow::AppWindow(HINSTANCE hInstance, std::vector files) noexcept : + m_instance{ hInstance }, m_managerEvents{ this } +{ + HRESULT hr = CPowerRenameManager::s_CreateInstance(&m_prManager); + // Create the factory for our items + CComPtr prItemFactory; + hr = CPowerRenameItem::s_CreateInstance(nullptr, IID_PPV_ARGS(&prItemFactory)); + hr = m_prManager->PutRenameItemFactory(prItemFactory); + hr = m_prManager->Advise(&m_managerEvents, &m_cookie); + + if (SUCCEEDED(hr)) + { + CComPtr shellItemArray; + // To test PowerRenameUIHost uncomment this line and update the path to + // your local (absolute or relative) path which you want to see in PowerRename + //files.push_back(L""); + + if (!files.empty()) + { + hr = CreateShellItemArrayFromPaths(files, &shellItemArray); + if (SUCCEEDED(hr)) + { + CComPtr enumShellItems; + hr = shellItemArray->EnumItems(&enumShellItems); + if (SUCCEEDED(hr)) + { + EnumerateShellItems(enumShellItems); + } + } + } + } +} + +void AppWindow::CreateAndShowWindow() +{ + m_accelerators.reset(LoadAcceleratorsW(m_instance, MAKEINTRESOURCE(IDC_POWERRENAMEUIHOST))); + + WNDCLASSEXW wcex = { sizeof(wcex) }; + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.hInstance = m_instance; + wcex.hIcon = LoadIconW(m_instance, MAKEINTRESOURCE(IDC_POWERRENAMEUIHOST)); + wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW); + wcex.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1); + wcex.lpszClassName = c_WindowClass; + wcex.hIconSm = LoadIconW(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); + RegisterClassExW(&wcex); // don't test result, handle error at CreateWindow + + wchar_t title[64]; + LoadStringW(m_instance, IDS_APP_TITLE, title, ARRAYSIZE(title)); + + m_window = CreateWindowW(c_WindowClass, title, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, m_instance, this); + THROW_LAST_ERROR_IF(!m_window); + + ShowWindow(m_window, SW_SHOWNORMAL); + UpdateWindow(m_window); + SetFocus(m_window); +} + +bool AppWindow::OnCreate(HWND, LPCREATESTRUCT) noexcept +{ + m_mainUserControl = winrt::PowerRenameUILib::MainWindow(); + m_xamlIsland = CreateDesktopWindowsXamlSource(WS_TABSTOP, m_mainUserControl); + + PopulateExplorerItems(); + SetHandlers(); + ReadSettings(); + + m_mainUserControl.UIUpdatesItem().ButtonRenameEnabled(false); + InitAutoComplete(); + SearchReplaceChanged(); + return true; +} + +void AppWindow::OnCommand(HWND, int id, HWND hwndControl, UINT codeNotify) noexcept +{ + switch (id) + { + case IDM_ABOUT: + DialogBoxW(m_instance, MAKEINTRESOURCE(IDD_ABOUTBOX), WindowHandle(), [](HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) -> INT_PTR { + switch (message) + { + case WM_INITDIALOG: + return TRUE; + + case WM_COMMAND: + if ((LOWORD(wParam) == IDOK) || (LOWORD(wParam) == IDCANCEL)) + { + EndDialog(hDlg, LOWORD(wParam)); + return TRUE; + } + break; + } + return FALSE; + }); + break; + + case IDM_EXIT: + PostQuitMessage(0); + break; + } +} + +void AppWindow::OnDestroy(HWND hwnd) noexcept +{ + base_type::OnDestroy(hwnd); +} + +void AppWindow::OnResize(HWND, UINT state, int cx, int cy) noexcept +{ + SetWindowPos(m_xamlIsland, NULL, 0, 0, cx, cy, SWP_SHOWWINDOW); +} + +HRESULT AppWindow::CreateShellItemArrayFromPaths( + std::vector files, + IShellItemArray** shellItemArray) +{ + *shellItemArray = nullptr; + PIDLIST_ABSOLUTE* itemList = nullptr; + itemList = new (std::nothrow) PIDLIST_ABSOLUTE[files.size()]; + HRESULT hr = itemList ? S_OK : E_OUTOFMEMORY; + UINT itemsCnt = 0; + for (const auto& file : files) + { + const DWORD BUFSIZE = 4096; + TCHAR buffer[BUFSIZE] = TEXT(""); + auto retval = GetFullPathName(file.c_str(), BUFSIZE, buffer, NULL); + if (retval != 0 && PathFileExists(buffer)) + { + hr = SHParseDisplayName(buffer, nullptr, &itemList[itemsCnt], 0, nullptr); + ++itemsCnt; + } + } + if (SUCCEEDED(hr) && itemsCnt > 0) + { + hr = SHCreateShellItemArrayFromIDLists(itemsCnt, const_cast(itemList), shellItemArray); + + for (UINT i = 0; i < itemsCnt; i++) + { + CoTaskMemFree(itemList[i]); + } + } + else + { + hr = E_FAIL; + } + + delete[] itemList; + return hr; +} + +void AppWindow::PopulateExplorerItems() +{ + UINT count = 0; + m_prManager->GetVisibleItemCount(&count); + + UINT currDepth = 0; + std::stack parents{}; + UINT prevId = 0; + parents.push(0); + + for (UINT i = 0; i < count; ++i) + { + CComPtr renameItem; + if (SUCCEEDED(m_prManager->GetVisibleItemByIndex(i, &renameItem))) + { + int id = 0; + renameItem->GetId(&id); + + PWSTR originalName = nullptr; + renameItem->GetOriginalName(&originalName); + PWSTR newName = nullptr; + renameItem->GetNewName(&newName); + + bool selected; + renameItem->GetSelected(&selected); + + UINT depth = 0; + renameItem->GetDepth(&depth); + + bool isFolder = false; + bool isSubFolderContent = false; + winrt::check_hresult(renameItem->GetIsFolder(&isFolder)); + + if (depth > currDepth) + { + parents.push(prevId); + currDepth = depth; + } + else + { + while (currDepth > depth) + { + parents.pop(); + currDepth--; + } + currDepth = depth; + } + m_mainUserControl.AddExplorerItem( + id, originalName, newName == nullptr ? hstring{} : hstring{ newName }, isFolder ? 0 : 1, parents.top(), selected); + prevId = id; + } + } +} + +HRESULT AppWindow::InitAutoComplete() +{ + HRESULT hr = S_OK; + if (CSettingsInstance().GetMRUEnabled()) + { + hr = CPowerRenameMRU::CPowerRenameMRUSearch_CreateInstance(&m_searchMRU); + if (SUCCEEDED(hr)) + { + for (const auto& item : m_searchMRU->GetMRUStrings()) + { + if (!item.empty()) + { + m_mainUserControl.AppendSearchMRU(item); + } + } + } + + if (SUCCEEDED(hr)) + { + hr = CPowerRenameMRU::CPowerRenameMRUReplace_CreateInstance(&m_replaceMRU); + if (SUCCEEDED(hr)) + { + for (const auto& item : m_replaceMRU->GetMRUStrings()) + { + if (!item.empty()) + { + m_mainUserControl.AppendReplaceMRU(item); + } + } + } + } + } + + return hr; +} + +HRESULT AppWindow::EnumerateShellItems(_In_ IEnumShellItems* enumShellItems) +{ + HRESULT hr = S_OK; + // Enumerate the data object and populate the manager + if (m_prManager) + { + m_disableCountUpdate = true; + + // Ensure we re-create the enumerator + m_prEnum = nullptr; + hr = CPowerRenameEnum::s_CreateInstance(nullptr, m_prManager, IID_PPV_ARGS(&m_prEnum)); + if (SUCCEEDED(hr)) + { + hr = m_prEnum->Start(enumShellItems); + } + + m_disableCountUpdate = false; + } + + return hr; +} + +void AppWindow::SearchReplaceChanged(bool forceRenaming) +{ + // Pass updated search and replace terms to the IPowerRenameRegEx handler + CComPtr prRegEx; + if (m_prManager && SUCCEEDED(m_prManager->GetRenameRegEx(&prRegEx))) + { + winrt::hstring searchTerm = m_mainUserControl.AutoSuggestBoxSearch().Text(); + prRegEx->PutSearchTerm(searchTerm.c_str(), forceRenaming); + + winrt::hstring replaceTerm = m_mainUserControl.AutoSuggestBoxReplace().Text(); + prRegEx->PutReplaceTerm(replaceTerm.c_str(), forceRenaming); + } +} + +void AppWindow::ValidateFlags(PowerRenameFlags flag) +{ + if (flag == Uppercase) + { + if (m_mainUserControl.ToggleButtonUpperCase().IsChecked()) + { + m_mainUserControl.ToggleButtonLowerCase().IsChecked(false); + m_mainUserControl.ToggleButtonTitleCase().IsChecked(false); + m_mainUserControl.ToggleButtonCapitalize().IsChecked(false); + } + } + else if (flag == Lowercase) + { + if (m_mainUserControl.ToggleButtonLowerCase().IsChecked()) + { + m_mainUserControl.ToggleButtonUpperCase().IsChecked(false); + m_mainUserControl.ToggleButtonTitleCase().IsChecked(false); + m_mainUserControl.ToggleButtonCapitalize().IsChecked(false); + } + } + else if (flag == Titlecase) + { + if (m_mainUserControl.ToggleButtonTitleCase().IsChecked()) + { + m_mainUserControl.ToggleButtonUpperCase().IsChecked(false); + m_mainUserControl.ToggleButtonLowerCase().IsChecked(false); + m_mainUserControl.ToggleButtonCapitalize().IsChecked(false); + } + } + else if (flag == Capitalized) + { + if (m_mainUserControl.ToggleButtonCapitalize().IsChecked()) + { + m_mainUserControl.ToggleButtonUpperCase().IsChecked(false); + m_mainUserControl.ToggleButtonLowerCase().IsChecked(false); + m_mainUserControl.ToggleButtonTitleCase().IsChecked(false); + } + } + + m_flagValidationInProgress = true; +} + +void AppWindow::UpdateFlag(PowerRenameFlags flag, UpdateFlagCommand command) +{ + DWORD flags{}; + m_prManager->GetFlags(&flags); + + if (command == UpdateFlagCommand::Set) + { + flags |= flag; + } + else if (command == UpdateFlagCommand::Reset) + { + flags &= ~flag; + } + + // Ensure we update flags + if (m_prManager) + { + m_prManager->PutFlags(flags); + } +} + +void AppWindow::SetHandlers() +{ + m_mainUserControl.UIUpdatesItem().PropertyChanged([&](winrt::Windows::Foundation::IInspectable const& sender, Data::PropertyChangedEventArgs const& e) { + std::wstring property{ e.PropertyName() }; + if (property == L"ShowAll") + { + SwitchView(); + } + else if (property == L"ChangedItemId") + { + ToggleItem(m_mainUserControl.UIUpdatesItem().ChangedExplorerItemId(), m_mainUserControl.UIUpdatesItem().Checked()); + } + else if (property == L"ToggleAll") + { + ToggleAll(); + } + else if (property == L"Rename") + { + Rename(m_mainUserControl.UIUpdatesItem().CloseUIWindow()); + } + }); + + // AutoSuggestBox Search + m_mainUserControl.AutoSuggestBoxSearch().TextChanged([&](winrt::Windows::Foundation::IInspectable const& sender, AutoSuggestBoxTextChangedEventArgs const&) { + SearchReplaceChanged(); + }); + + // AutoSuggestBox Replace + m_mainUserControl.AutoSuggestBoxReplace().TextChanged([&](winrt::Windows::Foundation::IInspectable const& sender, AutoSuggestBoxTextChangedEventArgs const&) { + SearchReplaceChanged(); + }); + + // ToggleButton UpperCase + m_mainUserControl.ToggleButtonUpperCase().Checked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + ValidateFlags(Uppercase); + UpdateFlag(Uppercase, UpdateFlagCommand::Set); + }); + m_mainUserControl.ToggleButtonUpperCase().Unchecked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + UpdateFlag(Uppercase, UpdateFlagCommand::Reset); + }); + + // ToggleButton LowerCase + m_mainUserControl.ToggleButtonLowerCase().Checked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + ValidateFlags(Lowercase); + UpdateFlag(Lowercase, UpdateFlagCommand::Set); + }); + m_mainUserControl.ToggleButtonLowerCase().Unchecked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + UpdateFlag(Lowercase, UpdateFlagCommand::Reset); + }); + + // ToggleButton TitleCase + m_mainUserControl.ToggleButtonTitleCase().Checked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + ValidateFlags(Titlecase); + UpdateFlag(Titlecase, UpdateFlagCommand::Set); + }); + m_mainUserControl.ToggleButtonTitleCase().Unchecked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + UpdateFlag(Titlecase, UpdateFlagCommand::Reset); + }); + + // ToggleButton Capitalize + m_mainUserControl.ToggleButtonCapitalize().Checked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + ValidateFlags(Capitalized); + UpdateFlag(Capitalized, UpdateFlagCommand::Set); + }); + m_mainUserControl.ToggleButtonCapitalize().Unchecked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + UpdateFlag(Capitalized, UpdateFlagCommand::Reset); + }); + + // CheckBox Regex + m_mainUserControl.CheckBoxRegex().Checked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + ValidateFlags(UseRegularExpressions); + UpdateFlag(UseRegularExpressions, UpdateFlagCommand::Set); + }); + m_mainUserControl.CheckBoxRegex().Unchecked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + UpdateFlag(UseRegularExpressions, UpdateFlagCommand::Reset); + }); + + // CheckBox CaseSensitive + m_mainUserControl.CheckBoxCaseSensitive().Checked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + ValidateFlags(CaseSensitive); + UpdateFlag(CaseSensitive, UpdateFlagCommand::Set); + }); + m_mainUserControl.CheckBoxCaseSensitive().Unchecked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + UpdateFlag(CaseSensitive, UpdateFlagCommand::Reset); + }); + + // ComboBox RenameParts + m_mainUserControl.ComboBoxRenameParts().SelectionChanged([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + if (m_mainUserControl.ComboBoxRenameParts().SelectedIndex() == 0) + { // Filename + extension + UpdateFlag(NameOnly, UpdateFlagCommand::Reset); + UpdateFlag(ExtensionOnly, UpdateFlagCommand::Reset); + } + else if (m_mainUserControl.ComboBoxRenameParts().SelectedIndex() == 1) // Filename Only + { + ValidateFlags(NameOnly); + UpdateFlag(ExtensionOnly, UpdateFlagCommand::Reset); + UpdateFlag(NameOnly, UpdateFlagCommand::Set); + } + else if (m_mainUserControl.ComboBoxRenameParts().SelectedIndex() == 2) // Extension Only + { + ValidateFlags(ExtensionOnly); + UpdateFlag(NameOnly, UpdateFlagCommand::Reset); + UpdateFlag(ExtensionOnly, UpdateFlagCommand::Set); + } + }); + + // CheckBox MatchAllOccurences + m_mainUserControl.CheckBoxMatchAll().Checked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + ValidateFlags(MatchAllOccurences); + UpdateFlag(MatchAllOccurences, UpdateFlagCommand::Set); + }); + m_mainUserControl.CheckBoxMatchAll().Unchecked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + UpdateFlag(MatchAllOccurences, UpdateFlagCommand::Reset); + }); + + // ToggleButton IncludeFiles + m_mainUserControl.ToggleButtonIncludeFiles().Checked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + ValidateFlags(ExcludeFiles); + UpdateFlag(ExcludeFiles, UpdateFlagCommand::Reset); + }); + m_mainUserControl.ToggleButtonIncludeFiles().Unchecked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + UpdateFlag(ExcludeFiles, UpdateFlagCommand::Set); + }); + + // ToggleButton IncludeFolders + m_mainUserControl.ToggleButtonIncludeFolders().Checked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + ValidateFlags(ExcludeFolders); + UpdateFlag(ExcludeFolders, UpdateFlagCommand::Reset); + }); + m_mainUserControl.ToggleButtonIncludeFolders().Unchecked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + UpdateFlag(ExcludeFolders, UpdateFlagCommand::Set); + }); + + // ToggleButton IncludeSubfolders + m_mainUserControl.ToggleButtonIncludeSubfolders().Checked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + ValidateFlags(ExcludeSubfolders); + UpdateFlag(ExcludeSubfolders, UpdateFlagCommand::Reset); + }); + m_mainUserControl.ToggleButtonIncludeSubfolders().Unchecked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + UpdateFlag(ExcludeSubfolders, UpdateFlagCommand::Set); + }); + + // CheckBox EnumerateItems + m_mainUserControl.ToggleButtonEnumerateItems().Checked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + ValidateFlags(EnumerateItems); + UpdateFlag(EnumerateItems, UpdateFlagCommand::Set); + }); + m_mainUserControl.ToggleButtonEnumerateItems().Unchecked([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + UpdateFlag(EnumerateItems, UpdateFlagCommand::Reset); + }); + + // ButtonSettings + m_mainUserControl.ButtonSettings().Click([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { + OpenSettingsApp(); + }); +} + +void AppWindow::ToggleItem(int32_t id, bool checked) +{ + CComPtr spItem; + m_prManager->GetItemById(id, &spItem); + spItem->PutSelected(checked); + UpdateCounts(); +} + +void AppWindow::ToggleAll() +{ + UINT itemCount = 0; + m_prManager->GetItemCount(&itemCount); + bool selected = m_mainUserControl.CheckBoxSelectAll().IsChecked().GetBoolean(); + for (UINT i = 0; i < itemCount; i++) + { + CComPtr spItem; + if (SUCCEEDED(m_prManager->GetItemByIndex(i, &spItem))) + { + spItem->PutSelected(selected); + } + } + UpdateCounts(); +} + +void AppWindow::SwitchView() +{ + m_prManager->SwitchFilter(0); + PopulateExplorerItems(); +} + +void AppWindow::Rename(bool closeWindow) +{ + if (m_prManager) + { + m_prManager->Rename(m_window, closeWindow); + } + + // Persist the current settings. We only do this when + // a rename is actually performed. Not when the user + // closes/cancels the dialog. + WriteSettings(); +} + +HRESULT AppWindow::ReadSettings() +{ + // Check if we should read flags from settings + // or the defaults from the manager. + DWORD flags = 0; + if (CSettingsInstance().GetPersistState()) + { + flags = CSettingsInstance().GetFlags(); + + m_mainUserControl.AutoSuggestBoxSearch().Text(CSettingsInstance().GetSearchText().c_str()); + m_mainUserControl.AutoSuggestBoxReplace().Text(CSettingsInstance().GetReplaceText().c_str()); + } + else + { + m_prManager->GetFlags(&flags); + } + + m_prManager->PutFlags(flags); + SetCheckboxesFromFlags(flags); + + return S_OK; +} + +HRESULT AppWindow::WriteSettings() +{ + // Check if we should store our settings + if (CSettingsInstance().GetPersistState()) + { + DWORD flags = 0; + m_prManager->GetFlags(&flags); + CSettingsInstance().SetFlags(flags); + + winrt::hstring searchTerm = m_mainUserControl.AutoSuggestBoxSearch().Text(); + CSettingsInstance().SetSearchText(std::wstring{ searchTerm }); + + if (CSettingsInstance().GetMRUEnabled() && m_searchMRU) + { + CComPtr spSearchMRU; + if (SUCCEEDED(m_searchMRU->QueryInterface(IID_PPV_ARGS(&spSearchMRU)))) + { + spSearchMRU->AddMRUString(searchTerm.c_str()); + } + } + + winrt::hstring replaceTerm = m_mainUserControl.AutoSuggestBoxReplace().Text(); + CSettingsInstance().SetReplaceText(std::wstring{ replaceTerm }); + + if (CSettingsInstance().GetMRUEnabled() && m_replaceMRU) + { + CComPtr spReplaceMRU; + if (SUCCEEDED(m_replaceMRU->QueryInterface(IID_PPV_ARGS(&spReplaceMRU)))) + { + spReplaceMRU->AddMRUString(replaceTerm.c_str()); + } + } + + Trace::SettingsChanged(); + } + + return S_OK; +} + +HRESULT AppWindow::OpenSettingsApp() +{ + std::wstring path = get_module_folderpath(g_hostHInst); + path += L"\\..\\..\\PowerToys.exe"; + + std::wstring openSettings = L"--open-settings=PowerRename"; + + CString commandLine; + commandLine.Format(_T("\"%s\""), path.c_str()); + commandLine.AppendFormat(_T(" %s"), openSettings.c_str()); + + int nSize = commandLine.GetLength() + 1; + LPTSTR lpszCommandLine = new TCHAR[nSize]; + _tcscpy_s(lpszCommandLine, nSize, commandLine); + + STARTUPINFO startupInfo; + ZeroMemory(&startupInfo, sizeof(STARTUPINFO)); + startupInfo.cb = sizeof(STARTUPINFO); + startupInfo.wShowWindow = SW_SHOWNORMAL; + + PROCESS_INFORMATION processInformation; + + // Start the resizer + CreateProcess( + NULL, + lpszCommandLine, + NULL, + NULL, + TRUE, + 0, + NULL, + NULL, + &startupInfo, + &processInformation); + + delete[] lpszCommandLine; + + if (!CloseHandle(processInformation.hProcess)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + if (!CloseHandle(processInformation.hThread)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + return S_OK; +} + +void AppWindow::SetCheckboxesFromFlags(DWORD flags) +{ + if (flags & CaseSensitive) + { + m_mainUserControl.CheckBoxCaseSensitive().IsChecked(true); + } + if (flags & MatchAllOccurences) + { + m_mainUserControl.CheckBoxMatchAll().IsChecked(true); + } + if (flags & UseRegularExpressions) + { + m_mainUserControl.CheckBoxRegex().IsChecked(true); + } + if (flags & EnumerateItems) + { + m_mainUserControl.ToggleButtonEnumerateItems().IsChecked(true); + } + if (flags & ExcludeFiles) + { + m_mainUserControl.ToggleButtonIncludeFiles().IsChecked(false); + } + if (flags & ExcludeFolders) + { + m_mainUserControl.ToggleButtonIncludeFolders().IsChecked(false); + } + if (flags & ExcludeSubfolders) + { + m_mainUserControl.ToggleButtonIncludeSubfolders().IsChecked(false); + } + if (flags & NameOnly) + { + m_mainUserControl.ComboBoxRenameParts().SelectedIndex(1); + } + else if (flags & ExtensionOnly) + { + m_mainUserControl.ComboBoxRenameParts().SelectedIndex(2); + } + if (flags & Uppercase) + { + m_mainUserControl.ToggleButtonUpperCase().IsChecked(true); + } + else if (flags & Lowercase) + { + m_mainUserControl.ToggleButtonLowerCase().IsChecked(true); + } + else if (flags & Titlecase) + { + m_mainUserControl.ToggleButtonTitleCase().IsChecked(true); + } + else if (flags & Capitalized) + { + m_mainUserControl.ToggleButtonCapitalize().IsChecked(true); + } +} + +void AppWindow::UpdateCounts() +{ + // This method is CPU intensive. We disable it during certain operations + // for performance reasons. + if (m_disableCountUpdate) + { + return; + } + + UINT selectedCount = 0; + UINT renamingCount = 0; + if (m_prManager) + { + m_prManager->GetSelectedItemCount(&selectedCount); + m_prManager->GetRenameItemCount(&renamingCount); + } + + if (m_selectedCount != selectedCount || + m_renamingCount != renamingCount) + { + m_selectedCount = selectedCount; + m_renamingCount = renamingCount; + + // Update counts UI elements if/when added + + // Update Rename button state + m_mainUserControl.UIUpdatesItem().ButtonRenameEnabled(renamingCount > 0); + } +} + +HRESULT AppWindow::OnItemAdded(_In_ IPowerRenameItem* renameItem) +{ + return S_OK; +} + +HRESULT AppWindow::OnUpdate(_In_ IPowerRenameItem* renameItem) +{ + int id; + HRESULT hr = renameItem->GetId(&id); + if (SUCCEEDED(hr)) + { + PWSTR newName = nullptr; + hr = renameItem->GetNewName(&newName); + if (SUCCEEDED(hr)) + { + hstring newNameStr = newName == nullptr ? hstring{} : newName; + m_mainUserControl.UpdateExplorerItem(id, newNameStr); + } + } + + UpdateCounts(); + return S_OK; +} + +HRESULT AppWindow::OnRename(_In_ IPowerRenameItem* renameItem) +{ + int id; + HRESULT hr = renameItem->GetId(&id); + if (SUCCEEDED(hr)) + { + PWSTR newName = nullptr; + hr = renameItem->GetOriginalName(&newName); + if (SUCCEEDED(hr)) + { + hstring newNameStr = newName == nullptr ? hstring{} : newName; + m_mainUserControl.UpdateRenamedExplorerItem(id, newNameStr); + } + } + + UpdateCounts(); + return S_OK; +} + +HRESULT AppWindow::OnError(_In_ IPowerRenameItem* renameItem) +{ + return S_OK; +} + +HRESULT AppWindow::OnRegExStarted(_In_ DWORD threadId) +{ + return S_OK; +} + +HRESULT AppWindow::OnRegExCanceled(_In_ DWORD threadId) +{ + return S_OK; +} + +HRESULT AppWindow::OnRegExCompleted(_In_ DWORD threadId) +{ + if (m_flagValidationInProgress) + { + m_flagValidationInProgress = false; + } + else + { + DWORD filter = 0; + m_prManager->GetFilter(&filter); + if (filter == PowerRenameFilters::ShouldRename) + { + m_mainUserControl.ExplorerItems().Clear(); + PopulateExplorerItems(); + } + } + + return S_OK; +} + +HRESULT AppWindow::OnRenameStarted() +{ + return S_OK; +} + +HRESULT AppWindow::OnRenameCompleted(bool closeUIWindowAfterRenaming) +{ + if (closeUIWindowAfterRenaming) + { + // Close the window + PostMessage(m_window, WM_CLOSE, (WPARAM)0, (LPARAM)0); + } + else + { + // Force renaming work to start so newly renamed items are processed right away + SearchReplaceChanged(true); + } + return S_OK; +} + +int APIENTRY wWinMain(_In_ HINSTANCE hInstance, + _In_opt_ HINSTANCE hPrevInstance, + _In_ LPWSTR lpCmdLine, + _In_ int nCmdShow) +{ +#define BUFSIZE 4096 * 4 + + HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); + if (hStdin == INVALID_HANDLE_VALUE) + ExitProcess(1); + BOOL bSuccess; + WCHAR chBuf[BUFSIZE]; + DWORD dwRead; + std::vector files; + for (;;) + { + // Read from standard input and stop on error or no data. + bSuccess = ReadFile(hStdin, chBuf, BUFSIZE * sizeof(wchar_t), &dwRead, NULL); + + if (!bSuccess || dwRead == 0) + break; + + std::wstring inputBatch{ chBuf, dwRead / sizeof(wchar_t) }; + + std::wstringstream ss(inputBatch); + std::wstring item; + wchar_t delimiter = '?'; + while (std::getline(ss, item, delimiter)) + { + files.push_back(item); + } + + if (!bSuccess) + break; + } + + g_hostHInst = hInstance; + winrt::init_apartment(winrt::apartment_type::single_threaded); + + winrt::PowerRenameUILib::App app; + const auto result = AppWindow::Show(hInstance, files); + app.Close(); +} diff --git a/src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.h b/src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.h new file mode 100644 index 0000000000..80b16cf6b6 --- /dev/null +++ b/src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.h @@ -0,0 +1,153 @@ +#pragma once +#include "pch.h" + +#include "resource.h" +#include "XamlBridge.h" + +#include +#include +#include +#include +#include + +#include +#include +#pragma push_macro("GetCurrentTime") +#undef GetCurrentTime +#include +#pragma pop_macro("GetCurrentTime") +#include +#include +#include +#include +#include +#include +#include + +using namespace winrt; +using namespace Windows::UI; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Composition; +using namespace Windows::UI::Xaml::Hosting; +using namespace Windows::Foundation::Numerics; +using namespace Windows::UI::Xaml::Controls; + +class AppWindow : public DesktopWindowT +{ +public: + // Proxy class to Advise() PRManager, as AppWindow can't implement IPowerRenameManagerEvents + class UIHostPowerRenameManagerEvents : public IPowerRenameManagerEvents + { + public: + UIHostPowerRenameManagerEvents(AppWindow* app) : + m_refCount{ 1 }, m_app{ app } + { + } + + IFACEMETHODIMP_(ULONG) + AddRef() + { + return InterlockedIncrement(&m_refCount); + } + + IFACEMETHODIMP_(ULONG) + Release() + { + long refCount = InterlockedDecrement(&m_refCount); + + if (refCount == 0) + { + delete this; + } + return refCount; + } + + IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _Outptr_ void** ppv) + { + static const QITAB qit[] = { + QITABENT(UIHostPowerRenameManagerEvents, IPowerRenameManagerEvents), + { 0 } + }; + return QISearch(this, qit, riid, ppv); + } + + HRESULT OnItemAdded(_In_ IPowerRenameItem* renameItem) override { return m_app->OnItemAdded(renameItem); } + HRESULT OnUpdate(_In_ IPowerRenameItem* renameItem) override { return m_app->OnUpdate(renameItem); } + HRESULT OnRename(_In_ IPowerRenameItem* renameItem) override { return m_app->OnRename(renameItem); } + HRESULT OnError(_In_ IPowerRenameItem* renameItem) override { return m_app->OnError(renameItem); } + HRESULT OnRegExStarted(_In_ DWORD threadId) override { return m_app->OnRegExStarted(threadId); } + HRESULT OnRegExCanceled(_In_ DWORD threadId) override { return m_app->OnRegExCanceled(threadId); } + HRESULT OnRegExCompleted(_In_ DWORD threadId) override { return m_app->OnRegExCompleted(threadId); } + HRESULT OnRenameStarted() override { return m_app->OnRenameStarted(); } + HRESULT OnRenameCompleted(bool closeUIWindowAfterRenaming) override { return m_app->OnRenameCompleted(closeUIWindowAfterRenaming); } + + private: + long m_refCount; + + AppWindow* m_app; + }; + + static int Show(HINSTANCE hInstance, std::vector files); + LRESULT MessageHandler(UINT message, WPARAM wParam, LPARAM lParam) noexcept; + +private: + enum class UpdateFlagCommand + { + Set = 0, + Reset + }; + + AppWindow(HINSTANCE hInstance, std::vector files) noexcept; + void CreateAndShowWindow(); + bool OnCreate(HWND, LPCREATESTRUCT) noexcept; + void OnCommand(HWND, int id, HWND hwndControl, UINT codeNotify) noexcept; + void OnDestroy(HWND hwnd) noexcept; + void OnResize(HWND, UINT state, int cx, int cy) noexcept; + HRESULT CreateShellItemArrayFromPaths(std::vector files, IShellItemArray** shellItemArray); + + void PopulateExplorerItems(); + HRESULT InitAutoComplete(); + HRESULT EnumerateShellItems(_In_ IEnumShellItems* enumShellItems); + void SearchReplaceChanged(bool forceRenaming = false); + void ValidateFlags(PowerRenameFlags flag); + void UpdateFlag(PowerRenameFlags flag, UpdateFlagCommand command); + void SetHandlers(); + void ToggleItem(int32_t id, bool checked); + void ToggleAll(); + void SwitchView(); + void Rename(bool closeWindow); + HRESULT ReadSettings(); + HRESULT WriteSettings(); + HRESULT OpenSettingsApp(); + void SetCheckboxesFromFlags(DWORD flags); + void UpdateCounts(); + + HRESULT OnItemAdded(_In_ IPowerRenameItem* renameItem); + HRESULT OnUpdate(_In_ IPowerRenameItem* renameItem); + HRESULT OnRename(_In_ IPowerRenameItem* renameItem); + HRESULT OnError(_In_ IPowerRenameItem* renameItem); + HRESULT OnRegExStarted(_In_ DWORD threadId); + HRESULT OnRegExCanceled(_In_ DWORD threadId); + HRESULT OnRegExCompleted(_In_ DWORD threadId); + HRESULT OnRenameStarted(); + HRESULT OnRenameCompleted(bool closeUIWindowAfterRenaming); + + wil::unique_haccel m_accelerators; + const HINSTANCE m_instance; + HWND m_xamlIsland{}; + HWND m_window{}; + winrt::PowerRenameUILib::MainWindow m_mainUserControl{ nullptr }; + + bool m_disableCountUpdate = false; + CComPtr m_prManager; + CComPtr m_dataSource; + CComPtr m_prEnum; + UIHostPowerRenameManagerEvents m_managerEvents; + DWORD m_cookie = 0; + CComPtr m_searchMRU; + CComPtr m_replaceMRU; + UINT m_selectedCount = 0; + UINT m_renamingCount = 0; + + bool m_flagValidationInProgress = false; +}; diff --git a/src/modules/powerrename/ui/PowerRename.ico b/src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.ico similarity index 100% rename from src/modules/powerrename/ui/PowerRename.ico rename to src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.ico diff --git a/src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.rc b/src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.rc new file mode 100644 index 0000000000..c2ed5aca34 --- /dev/null +++ b/src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.rc @@ -0,0 +1,150 @@ +//Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE resource. +// +#ifndef APSTUDIO_INVOKED +#include "targetver.h" +#endif +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE 9, 1 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. + +IDI_POWERRENAMEUIHOST ICON "PowerRenameUIHost.ico" +IDI_SMALL ICON "small.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDC_POWERRENAMEUIHOST MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "E&xit", IDM_EXIT + END + POPUP "&Help" + BEGIN + MENUITEM "&About ...", IDM_ABOUT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDC_POWERRENAMEUIHOST ACCELERATORS +BEGIN + "?", IDM_ABOUT, ASCII, ALT + "/", IDM_ABOUT, ASCII, ALT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOGEX 0, 0, 170, 62 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About PowerRenameUIHost" +FONT 8, "MS Shell Dlg" +BEGIN + ICON IDR_MAINFRAME,IDC_STATIC,14,14,21,20 + LTEXT "PowerRenameUIHost, Version 1.0",IDC_STATIC,42,14,114,8,SS_NOPREFIX + LTEXT "Copyright (c) 2021",IDC_STATIC,42,26,114,8 + DEFPUSHBUTTON "OK",IDOK,113,41,50,14,WS_GROUP +END + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_ABOUTBOX, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 163 + TOPMARGIN, 7 + BOTTOMMARGIN, 55 + END +END +#endif // APSTUDIO_INVOKED + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#ifndef APSTUDIO_INVOKED\r\n" + "#include ""targetver.h""\r\n" + "#endif\r\n" + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDC_POWERRENAMEUIHOST "POWERRENAMEUIHOST" + IDS_APP_TITLE "PowerRename" +END + +#endif +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE resource. +// + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + \ No newline at end of file diff --git a/src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.vcxproj b/src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.vcxproj new file mode 100644 index 0000000000..483078fe18 --- /dev/null +++ b/src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.vcxproj @@ -0,0 +1,165 @@ + + + + + + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {f7ec4e6c-19ca-4fbd-9918-b8ac5fef4f63} + PowerRenameUIHost + 10.0.18362.0 + $(WindowsTargetPlatformVersion) + + + + Application + true + v142 + Unicode + false + + + Application + false + v142 + true + Unicode + false + + + + + + + + + + + + + + + true + $(SolutionDir)$(Platform)\$(Configuration)\modules\PowerRename\ + PowerRename + + + false + $(SolutionDir)$(Platform)\$(Configuration)\modules\PowerRename\ + PowerRename + + + + Level3 + true + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir)..\..\..\;$(ProjectDir)..\..\..\common\Telemetry;$(ProjectDir)..\lib;%(AdditionalIncludeDirectories) + + + Windows + true + + + PerMonitorHighDPIAware + + + + + Level3 + true + true + true + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + true + $(ProjectDir)..\..\..\;$(ProjectDir)..\..\..\common\Telemetry;$(ProjectDir)..\lib;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + + + PerMonitorHighDPIAware + + + + + + + + + + + + + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + + + + + + + PowerRenameUILib + + + $(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\;$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\Generated Files\; + + + + + {51920f1f-c28c-4adf-8660-4238766796c2} + + + \ No newline at end of file diff --git a/src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.vcxproj.filters b/src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.vcxproj.filters new file mode 100644 index 0000000000..8be01112e6 --- /dev/null +++ b/src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.vcxproj.filters @@ -0,0 +1,67 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;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 + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + + + Resource Files + + + Resource Files + + + + + + + + + \ No newline at end of file diff --git a/src/modules/powerrename/PowerRenameUIHost/Resource.h b/src/modules/powerrename/PowerRenameUIHost/Resource.h new file mode 100644 index 0000000000..3f2ed88ba1 --- /dev/null +++ b/src/modules/powerrename/PowerRenameUIHost/Resource.h @@ -0,0 +1,30 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by PowerRenameUIHost.rc + +#define IDS_APP_TITLE 103 + +#define IDR_MAINFRAME 128 +#define IDD_POWERRENAMEUIHOST_DIALOG 102 +#define IDD_ABOUTBOX 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define IDI_POWERRENAMEUIHOST 107 +#define IDI_SMALL 108 +#define IDC_POWERRENAMEUIHOST 109 +#define IDC_MYICON 2 +#ifndef IDC_STATIC +#define IDC_STATIC -1 +#endif +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS + +#define _APS_NO_MFC 130 +#define _APS_NEXT_RESOURCE_VALUE 129 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/src/modules/powerrename/PowerRenameUIHost/XamlBridge.cpp b/src/modules/powerrename/PowerRenameUIHost/XamlBridge.cpp new file mode 100644 index 0000000000..f5b551e5e2 --- /dev/null +++ b/src/modules/powerrename/PowerRenameUIHost/XamlBridge.cpp @@ -0,0 +1,249 @@ +#include "pch.h" + +#include "XamlBridge.h" +#include "Resource.h" +#include + +bool DesktopWindow::FilterMessage(const MSG* msg) +{ + // When multiple child windows are present it is needed to pre dispatch messages to all + // DesktopWindowXamlSource instances so keyboard accelerators and + // keyboard focus work correctly. + for (auto& xamlSource : m_xamlSources) + { + BOOL xamlSourceProcessedMessage = FALSE; + winrt::check_hresult(xamlSource.as()->PreTranslateMessage(msg, &xamlSourceProcessedMessage)); + if (xamlSourceProcessedMessage != FALSE) + { + return true; + } + } + + return false; +} + +const auto static invalidReason = static_cast(-1); + +winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason GetReasonFromKey(WPARAM key) +{ + auto reason = invalidReason; + if (key == VK_TAB) + { + byte keyboardState[256] = {}; + WINRT_VERIFY(::GetKeyboardState(keyboardState)); + reason = (keyboardState[VK_SHIFT] & 0x80) ? + winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Last : + winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::First; + } + else if (key == VK_LEFT) + { + reason = winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Left; + } + else if (key == VK_RIGHT) + { + reason = winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Right; + } + else if (key == VK_UP) + { + reason = winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Up; + } + else if (key == VK_DOWN) + { + reason = winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Down; + } + return reason; +} + +winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource DesktopWindow::GetNextFocusedIsland(const MSG* msg) +{ + if (msg->message == WM_KEYDOWN) + { + const auto key = msg->wParam; + auto reason = GetReasonFromKey(key); + if (reason != invalidReason) + { + const BOOL previous = + (reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::First || + reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Down || + reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Right) ? + false : + true; + + const auto currentFocusedWindow = ::GetFocus(); + const auto nextElement = ::GetNextDlgTabItem(m_window.get(), currentFocusedWindow, previous); + for (auto& xamlSource : m_xamlSources) + { + HWND islandWnd{}; + winrt::check_hresult(xamlSource.as()->get_WindowHandle(&islandWnd)); + if (nextElement == islandWnd) + { + return xamlSource; + } + } + } + } + + return nullptr; +} + +winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource DesktopWindow::GetFocusedIsland() +{ + for (auto& xamlSource : m_xamlSources) + { + if (xamlSource.HasFocus()) + { + return xamlSource; + } + } + return nullptr; +} + +bool DesktopWindow::NavigateFocus(MSG* msg) +{ + if (const auto nextFocusedIsland = GetNextFocusedIsland(msg)) + { + const auto previousFocusedWindow = ::GetFocus(); + RECT rect = {}; + WINRT_VERIFY(::GetWindowRect(previousFocusedWindow, &rect)); + const auto nativeIsland = nextFocusedIsland.as(); + HWND islandWnd = nullptr; + winrt::check_hresult(nativeIsland->get_WindowHandle(&islandWnd)); + POINT pt = { rect.left, rect.top }; + SIZE size = { rect.right - rect.left, rect.bottom - rect.top }; + ::ScreenToClient(islandWnd, &pt); + const auto hintRect = winrt::Windows::Foundation::Rect({ static_cast(pt.x), static_cast(pt.y), static_cast(size.cx), static_cast(size.cy) }); + const auto reason = GetReasonFromKey(msg->wParam); + const auto request = winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationRequest(reason, hintRect); + m_lastFocusRequestId = request.CorrelationId(); + const auto result = nextFocusedIsland.NavigateFocus(request); + return result.WasFocusMoved(); + } + else + { + const bool islandIsFocused = GetFocusedIsland() != nullptr; + byte keyboardState[256] = {}; + WINRT_VERIFY(::GetKeyboardState(keyboardState)); + const bool isMenuModifier = keyboardState[VK_MENU] & 0x80; + if (islandIsFocused && !isMenuModifier) + { + return false; + } + const bool isDialogMessage = !!IsDialogMessage(m_window.get(), msg); + return isDialogMessage; + } +} + +int DesktopWindow::MessageLoop(HACCEL accelerators) +{ + MSG msg = {}; + while (GetMessage(&msg, nullptr, 0, 0)) + { + const bool processedMessage = FilterMessage(&msg); + if (!processedMessage && !TranslateAcceleratorW(msg.hwnd, accelerators, &msg)) + { + if (!NavigateFocus(&msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } + return static_cast(msg.wParam); +} + +static const WPARAM invalidKey = (WPARAM)-1; + +WPARAM GetKeyFromReason(winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason reason) +{ + auto key = invalidKey; + if (reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Last || reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::First) + { + key = VK_TAB; + } + else if (reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Left) + { + key = VK_LEFT; + } + else if (reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Right) + { + key = VK_RIGHT; + } + else if (reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Up) + { + key = VK_UP; + } + else if (reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Down) + { + key = VK_DOWN; + } + return key; +} + +void DesktopWindow::OnTakeFocusRequested(winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource const& sender, winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSourceTakeFocusRequestedEventArgs const& args) +{ + if (args.Request().CorrelationId() != m_lastFocusRequestId) + { + const auto reason = args.Request().Reason(); + const BOOL previous = + (reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::First || + reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Down || + reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Right) ? + false : + true; + + const auto nativeXamlSource = sender.as(); + HWND senderHwnd = nullptr; + winrt::check_hresult(nativeXamlSource->get_WindowHandle(&senderHwnd)); + + MSG msg = {}; + msg.hwnd = senderHwnd; + msg.message = WM_KEYDOWN; + msg.wParam = GetKeyFromReason(reason); + if (!NavigateFocus(&msg)) + { + const auto nextElement = ::GetNextDlgTabItem(m_window.get(), senderHwnd, previous); + ::SetFocus(nextElement); + } + } + else + { + const auto request = winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationRequest(winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Restore); + m_lastFocusRequestId = request.CorrelationId(); + sender.NavigateFocus(request); + } +} + +HWND DesktopWindow::CreateDesktopWindowsXamlSource(DWORD extraWindowStyles, const winrt::Windows::UI::Xaml::UIElement& content) +{ + winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopSource; + + auto interop = desktopSource.as(); + // Parent the DesktopWindowXamlSource object to current window + winrt::check_hresult(interop->AttachToWindow(m_window.get())); + HWND xamlSourceWindow{}; // Lifetime controlled desktopSource + winrt::check_hresult(interop->get_WindowHandle(&xamlSourceWindow)); + const DWORD style = GetWindowLongW(xamlSourceWindow, GWL_STYLE) | extraWindowStyles; + SetWindowLongW(xamlSourceWindow, GWL_STYLE, style); + + desktopSource.Content(content); + + m_takeFocusEventRevokers.push_back(desktopSource.TakeFocusRequested(winrt::auto_revoke, { this, &DesktopWindow::OnTakeFocusRequested })); + m_xamlSources.push_back(desktopSource); + + return xamlSourceWindow; +} + +void DesktopWindow::ClearXamlIslands() +{ + for (auto& takeFocusRevoker : m_takeFocusEventRevokers) + { + takeFocusRevoker.revoke(); + } + m_takeFocusEventRevokers.clear(); + + for (auto& xamlSource : m_xamlSources) + { + xamlSource.Close(); + } + m_xamlSources.clear(); +} diff --git a/src/modules/powerrename/PowerRenameUIHost/XamlBridge.h b/src/modules/powerrename/PowerRenameUIHost/XamlBridge.h new file mode 100644 index 0000000000..24056ec28a --- /dev/null +++ b/src/modules/powerrename/PowerRenameUIHost/XamlBridge.h @@ -0,0 +1,108 @@ +#pragma once + +#include // To enable support for non-WinRT interfaces, unknwn.h must be included before any C++/WinRT headers. +#include +#include +#include +#pragma push_macro("GetCurrentTime") +#undef GetCurrentTime +#include +#pragma pop_macro("GetCurrentTime") +#include +#include +#include +#include + +class DesktopWindow +{ +protected: + int MessageLoop(HACCEL accelerators); + HWND CreateDesktopWindowsXamlSource(DWORD extraStyles, const winrt::Windows::UI::Xaml::UIElement& content); + void ClearXamlIslands(); + + HWND WindowHandle() const + { + return m_window.get(); + } + + static void OnNCCreate(HWND window, LPARAM lparam) noexcept + { + auto cs = reinterpret_cast(lparam); + auto that = static_cast(cs->lpCreateParams); + that->m_window.reset(window); // take ownership of the window + SetWindowLongPtrW(window, GWLP_USERDATA, reinterpret_cast(that)); + } + +private: + winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource GetFocusedIsland(); + bool FilterMessage(const MSG* msg); + void OnTakeFocusRequested(winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource const& sender, winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSourceTakeFocusRequestedEventArgs const& args); + winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource GetNextFocusedIsland(const MSG* msg); + bool NavigateFocus(MSG* msg); + + wil::unique_hwnd m_window; + winrt::guid m_lastFocusRequestId; + std::vector m_takeFocusEventRevokers; + std::vector m_xamlSources; +}; + +template +struct DesktopWindowT : public DesktopWindow +{ +protected: + using base_type = DesktopWindowT; + + static LRESULT __stdcall WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept + { + if (message == WM_NCCREATE) + { + OnNCCreate(window, lparam); + } + else if (message == WM_NCDESTROY) + { + SetWindowLongPtrW(window, GWLP_USERDATA, 0); + } + else if (auto that = reinterpret_cast(GetWindowLongPtrW(window, GWLP_USERDATA))) + { + return that->MessageHandler(message, wparam, lparam); + } + + return DefWindowProcW(window, message, wparam, lparam); + } + + LRESULT MessageHandler(UINT message, WPARAM wParam, LPARAM lParam) noexcept + { + switch (message) + { + HANDLE_MSG(WindowHandle(), WM_DESTROY, OnDestroy); + HANDLE_MSG(WindowHandle(), WM_ACTIVATE, OnActivate); + HANDLE_MSG(WindowHandle(), WM_SETFOCUS, OnSetFocus); + } + return DefWindowProcW(WindowHandle(), message, wParam, lParam); + } + + void OnDestroy(HWND) + { + ClearXamlIslands(); + PostQuitMessage(0); + } + +private: + void OnActivate(HWND, UINT state, HWND hwndActDeact, BOOL fMinimized) + { + if (state == WA_INACTIVE) + { + m_hwndLastFocus = GetFocus(); + } + } + + void OnSetFocus(HWND, HWND hwndOldFocus) + { + if (m_hwndLastFocus) + { + SetFocus(m_hwndLastFocus); + } + } + + HWND m_hwndLastFocus = nullptr; +}; diff --git a/src/modules/powerrename/PowerRenameUIHost/app.manifest b/src/modules/powerrename/PowerRenameUIHost/app.manifest new file mode 100644 index 0000000000..1b57546819 --- /dev/null +++ b/src/modules/powerrename/PowerRenameUIHost/app.manifest @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/modules/powerrename/PowerRenameUIHost/framework.h b/src/modules/powerrename/PowerRenameUIHost/framework.h new file mode 100644 index 0000000000..ce362d71f8 --- /dev/null +++ b/src/modules/powerrename/PowerRenameUIHost/framework.h @@ -0,0 +1,15 @@ +// header.h : include file for standard system include files, +// or project specific include files +// + +#pragma once + +#include "targetver.h" +// #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include +// C RunTime Header Files +#include +#include +#include +#include diff --git a/src/modules/powerrename/PowerRenameUIHost/packages.config b/src/modules/powerrename/PowerRenameUIHost/packages.config new file mode 100644 index 0000000000..cb4fc6898e --- /dev/null +++ b/src/modules/powerrename/PowerRenameUIHost/packages.config @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/powerrename/ui/pch.cpp b/src/modules/powerrename/PowerRenameUIHost/pch.cpp similarity index 100% rename from src/modules/powerrename/ui/pch.cpp rename to src/modules/powerrename/PowerRenameUIHost/pch.cpp diff --git a/src/modules/powerrename/PowerRenameUIHost/pch.h b/src/modules/powerrename/PowerRenameUIHost/pch.h new file mode 100644 index 0000000000..61ca50ecd8 --- /dev/null +++ b/src/modules/powerrename/PowerRenameUIHost/pch.h @@ -0,0 +1,5 @@ +#pragma once + +#include "framework.h" +#include +#include diff --git a/src/modules/powerrename/PowerRenameUIHost/small.ico b/src/modules/powerrename/PowerRenameUIHost/small.ico new file mode 100644 index 0000000000000000000000000000000000000000..4f111feaac04a0afa2cf9b52350541051f1654eb GIT binary patch literal 118203 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 + } + App::~App() + { + Close(); + } +} diff --git a/src/modules/powerrename/PowerRenameUILib/App.h b/src/modules/powerrename/PowerRenameUILib/App.h new file mode 100644 index 0000000000..2ba215af18 --- /dev/null +++ b/src/modules/powerrename/PowerRenameUILib/App.h @@ -0,0 +1,18 @@ +#pragma once +#include "App.g.h" +#include "App.base.h" +namespace winrt::PowerRenameUILib::implementation +{ + class App : public AppT2 + { + public: + App(); + ~App(); + }; +} +namespace winrt::PowerRenameUILib::factory_implementation +{ + class App : public AppT + { + }; +} diff --git a/src/modules/powerrename/PowerRenameUILib/App.idl b/src/modules/powerrename/PowerRenameUILib/App.idl new file mode 100644 index 0000000000..648b2544a4 --- /dev/null +++ b/src/modules/powerrename/PowerRenameUILib/App.idl @@ -0,0 +1,7 @@ +namespace PowerRenameUILib +{ + [default_interface] runtimeclass App : Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication + { + App(); + } +} \ No newline at end of file diff --git a/src/modules/powerrename/PowerRenameUILib/App.xaml b/src/modules/powerrename/PowerRenameUILib/App.xaml new file mode 100644 index 0000000000..7795f1b7b2 --- /dev/null +++ b/src/modules/powerrename/PowerRenameUILib/App.xaml @@ -0,0 +1,1066 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Visible + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Filename + extension + Filename only + Extension only + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +