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 @@
+/ / M i c r o s o f t V i s u a l C + + g e n e r a t e d r e s o u r c e s c r i p t .
+ / /
+ # i n c l u d e " r e s o u r c e . h "
+
+ # d e f i n e A P S T U D I O _ R E A D O N L Y _ S Y M B O L S
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ / /
+ / / G e n e r a t e d f r o m t h e T E X T I N C L U D E r e s o u r c e .
+ / /
+ # i f n d e f A P S T U D I O _ I N V O K E D
+ # i n c l u d e " t a r g e t v e r . h "
+ # e n d i f
+ # d e f i n e A P S T U D I O _ H I D D E N _ S Y M B O L S
+ # i n c l u d e " w i n d o w s . h "
+ # u n d e f A P S T U D I O _ H I D D E N _ S Y M B O L S
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ # u n d e f A P S T U D I O _ R E A D O N L Y _ S Y M B O L S
+
+ # i f ! d e f i n e d ( A F X _ R E S O U R C E _ D L L ) | | d e f i n e d ( A F X _ T A R G _ E N U )
+ L A N G U A G E 9 , 1
+
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ / /
+ / / I c o n
+ / /
+
+ / / I c o n w i t h l o w e s t I D v a l u e p l a c e d f i r s t t o e n s u r e a p p l i c a t i o n i c o n
+ / / r e m a i n s c o n s i s t e n t o n a l l s y s t e m s .
+
+ I D I _ P O W E R R E N A M E U I H O S T I C O N " P o w e r R e n a m e U I H o s t . i c o "
+ I D I _ S M A L L I C O N " s m a l l . i c o "
+
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ / /
+ / / M e n u
+ / /
+
+ I D C _ P O W E R R E N A M E U I H O S T M E N U
+ B E G I N
+ P O P U P " &