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
Binary files /dev/null and b/src/modules/powerrename/PowerRenameUIHost/PowerRenameUIHost.rc differ
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 0000000000..4f111feaac
Binary files /dev/null and b/src/modules/powerrename/PowerRenameUIHost/small.ico differ
diff --git a/src/modules/powerrename/ui/targetver.h b/src/modules/powerrename/PowerRenameUIHost/targetver.h
similarity index 100%
rename from src/modules/powerrename/ui/targetver.h
rename to src/modules/powerrename/PowerRenameUIHost/targetver.h
diff --git a/src/modules/powerrename/PowerRenameUILib/App.cpp b/src/modules/powerrename/PowerRenameUILib/App.cpp
new file mode 100644
index 0000000000..f1840cbe19
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/App.cpp
@@ -0,0 +1,18 @@
+#include "pch.h"
+#include "App.h"
+#include "App.g.cpp"
+using namespace winrt;
+using namespace Windows::UI::Xaml;
+namespace winrt::PowerRenameUILib::implementation
+{
+ App::App()
+ {
+ Initialize();
+ AddRef();
+ m_inner.as<::IUnknown>()->Release();
+ }
+ 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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/powerrename/PowerRenameUILib/Assets/LockScreenLogo.scale-200.png b/src/modules/powerrename/PowerRenameUILib/Assets/LockScreenLogo.scale-200.png
new file mode 100644
index 0000000000..735f57adb5
Binary files /dev/null and b/src/modules/powerrename/PowerRenameUILib/Assets/LockScreenLogo.scale-200.png differ
diff --git a/src/modules/powerrename/PowerRenameUILib/Assets/SplashScreen.scale-200.png b/src/modules/powerrename/PowerRenameUILib/Assets/SplashScreen.scale-200.png
new file mode 100644
index 0000000000..023e7f1fed
Binary files /dev/null and b/src/modules/powerrename/PowerRenameUILib/Assets/SplashScreen.scale-200.png differ
diff --git a/src/modules/powerrename/PowerRenameUILib/Assets/Square150x150Logo.scale-200.png b/src/modules/powerrename/PowerRenameUILib/Assets/Square150x150Logo.scale-200.png
new file mode 100644
index 0000000000..af49fec1a5
Binary files /dev/null and b/src/modules/powerrename/PowerRenameUILib/Assets/Square150x150Logo.scale-200.png differ
diff --git a/src/modules/powerrename/PowerRenameUILib/Assets/Square44x44Logo.scale-200.png b/src/modules/powerrename/PowerRenameUILib/Assets/Square44x44Logo.scale-200.png
new file mode 100644
index 0000000000..ce342a2ec8
Binary files /dev/null and b/src/modules/powerrename/PowerRenameUILib/Assets/Square44x44Logo.scale-200.png differ
diff --git a/src/modules/powerrename/PowerRenameUILib/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/src/modules/powerrename/PowerRenameUILib/Assets/Square44x44Logo.targetsize-24_altform-unplated.png
new file mode 100644
index 0000000000..f6c02ce97e
Binary files /dev/null and b/src/modules/powerrename/PowerRenameUILib/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ
diff --git a/src/modules/powerrename/PowerRenameUILib/Assets/StoreLogo.png b/src/modules/powerrename/PowerRenameUILib/Assets/StoreLogo.png
new file mode 100644
index 0000000000..7385b56c0e
Binary files /dev/null and b/src/modules/powerrename/PowerRenameUILib/Assets/StoreLogo.png differ
diff --git a/src/modules/powerrename/PowerRenameUILib/Assets/Wide310x150Logo.scale-200.png b/src/modules/powerrename/PowerRenameUILib/Assets/Wide310x150Logo.scale-200.png
new file mode 100644
index 0000000000..288995b397
Binary files /dev/null and b/src/modules/powerrename/PowerRenameUILib/Assets/Wide310x150Logo.scale-200.png differ
diff --git a/src/modules/powerrename/PowerRenameUILib/Assets/file.png b/src/modules/powerrename/PowerRenameUILib/Assets/file.png
new file mode 100644
index 0000000000..1f9c65c0af
Binary files /dev/null and b/src/modules/powerrename/PowerRenameUILib/Assets/file.png differ
diff --git a/src/modules/powerrename/PowerRenameUILib/Assets/folder.png b/src/modules/powerrename/PowerRenameUILib/Assets/folder.png
new file mode 100644
index 0000000000..f969029e04
Binary files /dev/null and b/src/modules/powerrename/PowerRenameUILib/Assets/folder.png differ
diff --git a/src/modules/powerrename/PowerRenameUILib/ExplorerItem.cpp b/src/modules/powerrename/PowerRenameUILib/ExplorerItem.cpp
new file mode 100644
index 0000000000..8897dc9039
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/ExplorerItem.cpp
@@ -0,0 +1,105 @@
+#include "pch.h"
+#include "ExplorerItem.h"
+#include "ExplorerItem.g.cpp"
+
+namespace winrt::PowerRenameUILib::implementation
+{
+ ExplorerItem::ExplorerItem(int32_t id, hstring const& original, hstring const& renamed, int32_t type, bool checked) :
+ m_id{ id }, m_idStr{ std::to_wstring(id) }, m_original{ original }, m_renamed{ renamed }, m_type{ type }, m_checked{ checked }
+ {
+ if (m_type == static_cast(ExplorerItemType::Folder))
+ {
+ m_children = winrt::single_threaded_observable_vector();
+ }
+ }
+
+ int32_t ExplorerItem::Id()
+ {
+ return m_id;
+ }
+
+ hstring ExplorerItem::IdStr()
+ {
+ return m_idStr;
+ }
+
+ hstring ExplorerItem::Original()
+ {
+ return m_original;
+ }
+
+ void ExplorerItem::Original(hstring const& value)
+ {
+ if (m_original != value)
+ {
+ m_original = value;
+ m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"Original" });
+ }
+ }
+
+ hstring ExplorerItem::Renamed()
+ {
+ return m_renamed;
+ }
+
+ void ExplorerItem::Renamed(hstring const& value)
+ {
+ if (m_renamed != value)
+ {
+ m_renamed = value;
+ m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"Renamed" });
+ }
+ }
+
+ int32_t ExplorerItem::Type()
+ {
+ return m_type;
+ }
+
+ void ExplorerItem::Type(int32_t value)
+ {
+ if (m_type != value)
+ {
+ m_type = value;
+ m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"Type" });
+ }
+ }
+
+ bool ExplorerItem::Checked()
+ {
+ return m_checked;
+ }
+
+ void ExplorerItem::Checked(bool value)
+ {
+ if (m_checked != value)
+ {
+ m_checked = value;
+ m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"Checked" });
+ }
+ }
+
+ winrt::Windows::Foundation::Collections::IObservableVector ExplorerItem::Children()
+ {
+ return m_children;
+ }
+
+ void ExplorerItem::Children(Windows::Foundation::Collections::IObservableVector const& value)
+ {
+ if (m_children != value)
+ {
+ m_children = value;
+ m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"Children" });
+ }
+ }
+
+ winrt::event_token ExplorerItem::PropertyChanged(winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler)
+ {
+ return m_propertyChanged.add(handler);
+ }
+
+ void ExplorerItem::PropertyChanged(winrt::event_token const& token) noexcept
+ {
+ m_propertyChanged.remove(token);
+ }
+}
diff --git a/src/modules/powerrename/PowerRenameUILib/ExplorerItem.h b/src/modules/powerrename/PowerRenameUILib/ExplorerItem.h
new file mode 100644
index 0000000000..f1d97d80ed
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/ExplorerItem.h
@@ -0,0 +1,48 @@
+#pragma once
+#include "ExplorerItem.g.h"
+
+namespace winrt::PowerRenameUILib::implementation
+{
+ struct ExplorerItem : ExplorerItemT
+ {
+ enum class ExplorerItemType
+ {
+ Folder = 0,
+ File = 1
+ };
+
+ ExplorerItem() = delete;
+
+ ExplorerItem(int32_t id, hstring const& original, hstring const& renamed, int32_t type, bool checked);
+ int32_t Id();
+ hstring IdStr();
+ hstring Original();
+ void Original(hstring const& value);
+ hstring Renamed();
+ void Renamed(hstring const& value);
+ int32_t Type();
+ void Type(int32_t value);
+ bool Checked();
+ void Checked(bool value);
+ Windows::Foundation::Collections::IObservableVector Children();
+ void Children(Windows::Foundation::Collections::IObservableVector const& value);
+ winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler);
+ void PropertyChanged(winrt::event_token const& token) noexcept;
+
+ private:
+ int32_t m_id;
+ hstring m_idStr;
+ winrt::hstring m_original;
+ winrt::hstring m_renamed;
+ winrt::Windows::Foundation::Collections::IObservableVector m_children;
+ int32_t m_type;
+ bool m_checked;
+ winrt::event m_propertyChanged;
+ };
+}
+namespace winrt::PowerRenameUILib::factory_implementation
+{
+ struct ExplorerItem : ExplorerItemT
+ {
+ };
+}
diff --git a/src/modules/powerrename/PowerRenameUILib/ExplorerItem.idl b/src/modules/powerrename/PowerRenameUILib/ExplorerItem.idl
new file mode 100644
index 0000000000..c628b2af6c
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/ExplorerItem.idl
@@ -0,0 +1,14 @@
+namespace PowerRenameUILib
+{
+ runtimeclass ExplorerItem : Windows.UI.Xaml.Data.INotifyPropertyChanged
+ {
+ ExplorerItem(Int32 id, String original, String renamed, Int32 type, Boolean checked);
+ Int32 Id { get; };
+ String IdStr { get; };
+ String Original;
+ String Renamed;
+ Int32 Type;
+ Boolean Checked;
+ Windows.Foundation.Collections.IObservableVector Children;
+ }
+}
diff --git a/src/modules/powerrename/PowerRenameUILib/ExplorerItemTemplateSelector.cpp b/src/modules/powerrename/PowerRenameUILib/ExplorerItemTemplateSelector.cpp
new file mode 100644
index 0000000000..500c7eb3df
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/ExplorerItemTemplateSelector.cpp
@@ -0,0 +1,37 @@
+#include "pch.h"
+#include "ExplorerItemTemplateSelector.h"
+#include "ExplorerItemTemplateSelector.g.cpp"
+
+namespace winrt::PowerRenameUILib::implementation
+{
+ Windows::UI::Xaml::DataTemplate ExplorerItemTemplateSelector::SelectTemplateCore(IInspectable const& item)
+ {
+ ExplorerItem explorerItem = (ExplorerItem&)item;
+ return explorerItem.Type() == 0 ? m_folderTemplate : m_fileTemplate;
+ }
+
+ Windows::UI::Xaml::DataTemplate ExplorerItemTemplateSelector::SelectTemplateCore(IInspectable const&, Windows::UI::Xaml::DependencyObject const&)
+ {
+ return Windows::UI::Xaml::DataTemplate();
+ }
+
+ winrt::Windows::UI::Xaml::DataTemplate ExplorerItemTemplateSelector::FolderTemplate()
+ {
+ return m_folderTemplate;
+ }
+
+ void ExplorerItemTemplateSelector::FolderTemplate(winrt::Windows::UI::Xaml::DataTemplate const& value)
+ {
+ m_folderTemplate = value;
+ }
+
+ winrt::Windows::UI::Xaml::DataTemplate ExplorerItemTemplateSelector::FileTemplate()
+ {
+ return m_fileTemplate;
+ }
+
+ void ExplorerItemTemplateSelector::FileTemplate(winrt::Windows::UI::Xaml::DataTemplate const& value)
+ {
+ m_fileTemplate = value;
+ }
+}
diff --git a/src/modules/powerrename/PowerRenameUILib/ExplorerItemTemplateSelector.h b/src/modules/powerrename/PowerRenameUILib/ExplorerItemTemplateSelector.h
new file mode 100644
index 0000000000..35c46638a2
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/ExplorerItemTemplateSelector.h
@@ -0,0 +1,28 @@
+#pragma once
+#include "ExplorerItemTemplateSelector.g.h"
+
+namespace winrt::PowerRenameUILib::implementation
+{
+ struct ExplorerItemTemplateSelector : ExplorerItemTemplateSelectorT
+ {
+ ExplorerItemTemplateSelector() = default;
+
+ Windows::UI::Xaml::DataTemplate SelectTemplateCore(IInspectable const&);
+ Windows::UI::Xaml::DataTemplate SelectTemplateCore(IInspectable const&, Windows::UI::Xaml::DependencyObject const&);
+
+ winrt::Windows::UI::Xaml::DataTemplate FolderTemplate();
+ void FolderTemplate(winrt::Windows::UI::Xaml::DataTemplate const& value);
+ winrt::Windows::UI::Xaml::DataTemplate FileTemplate();
+ void FileTemplate(winrt::Windows::UI::Xaml::DataTemplate const& value);
+
+ private:
+ Windows::UI::Xaml::DataTemplate m_folderTemplate{ nullptr };
+ Windows::UI::Xaml::DataTemplate m_fileTemplate{ nullptr };
+ };
+}
+namespace winrt::PowerRenameUILib::factory_implementation
+{
+ struct ExplorerItemTemplateSelector : ExplorerItemTemplateSelectorT
+ {
+ };
+}
diff --git a/src/modules/powerrename/PowerRenameUILib/ExplorerItemTemplateSelector.idl b/src/modules/powerrename/PowerRenameUILib/ExplorerItemTemplateSelector.idl
new file mode 100644
index 0000000000..f31cf94294
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/ExplorerItemTemplateSelector.idl
@@ -0,0 +1,11 @@
+namespace PowerRenameUILib
+{
+ [bindable]
+ [default_interface] runtimeclass ExplorerItemTemplateSelector : Windows.UI.Xaml.Controls.DataTemplateSelector
+ {
+ ExplorerItemTemplateSelector();
+
+ Windows.UI.Xaml.DataTemplate FolderTemplate;
+ Windows.UI.Xaml.DataTemplate FileTemplate;
+ }
+}
diff --git a/src/modules/powerrename/ui/LocProject.json b/src/modules/powerrename/PowerRenameUILib/LocProject.json
similarity index 51%
rename from src/modules/powerrename/ui/LocProject.json
rename to src/modules/powerrename/PowerRenameUILib/LocProject.json
index 1b7bce6bfd..06cb5a5bac 100644
--- a/src/modules/powerrename/ui/LocProject.json
+++ b/src/modules/powerrename/PowerRenameUILib/LocProject.json
@@ -4,11 +4,11 @@
"LanguageSet": "Azure_Languages",
"LocItems": [
{
- "SourceFile": "src\\modules\\powerrename\\ui\\Resources.resx",
+ "SourceFile": "src\\modules\\powerrename\\PowerRenameUILib\\Strings\\en-us\\Resources.resw",
"CopyOption": "LangIDOnName",
- "OutputPath": "src\\modules\\powerrename\\ui"
+ "OutputPath": "src\\modules\\powerrename\\PowerRenameUILib\\Strings"
}
]
}
]
-}
+}
\ No newline at end of file
diff --git a/src/modules/powerrename/PowerRenameUILib/MainWindow.cpp b/src/modules/powerrename/PowerRenameUILib/MainWindow.cpp
new file mode 100644
index 0000000000..d6c82b9fd2
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/MainWindow.cpp
@@ -0,0 +1,326 @@
+#include "pch.h"
+#include "MainWindow.h"
+#if __has_include("MainWindow.g.cpp")
+#include "MainWindow.g.cpp"
+#endif
+
+using namespace winrt;
+using namespace Windows::UI::Xaml;
+
+namespace winrt::PowerRenameUILib::implementation
+{
+ MainWindow::MainWindow() :
+ m_allSelected{ true }
+ {
+ m_searchMRU = winrt::single_threaded_observable_vector();
+ m_replaceMRU = winrt::single_threaded_observable_vector();
+
+ m_explorerItems = winrt::single_threaded_observable_vector();
+
+ m_searchRegExShortcuts = winrt::single_threaded_observable_vector();
+ auto resourceLoader{ Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView() };
+
+ m_searchRegExShortcuts.Append(winrt::make(L"\\.", resourceLoader.GetString(L"RegExCheatSheet_MatchAny")));
+ m_searchRegExShortcuts.Append(winrt::make(L"\\d", resourceLoader.GetString(L"RegExCheatSheet_MatchDigit")));
+ m_searchRegExShortcuts.Append(winrt::make(L"\\D", resourceLoader.GetString(L"RegExCheatSheet_MatchNonDigit")));
+ m_searchRegExShortcuts.Append(winrt::make(L"\\w", resourceLoader.GetString(L"RegExCheatSheet_MatchNonWS")));
+ m_searchRegExShortcuts.Append(winrt::make(L"\\S", resourceLoader.GetString(L"RegExCheatSheet_MatchWordChar")));
+ m_searchRegExShortcuts.Append(winrt::make(L"\\S+", resourceLoader.GetString(L"RegExCheatSheet_MatchSeveralWS")));
+ m_searchRegExShortcuts.Append(winrt::make(L"\\b", resourceLoader.GetString(L"RegExCheatSheet_MatchWordBoundary")));
+
+ m_dateTimeShortcuts = winrt::single_threaded_observable_vector();
+ m_dateTimeShortcuts.Append(winrt::make(L"$YYYY", resourceLoader.GetString(L"DateTimeCheatSheet_FullYear")));
+ m_dateTimeShortcuts.Append(winrt::make(L"$YY", resourceLoader.GetString(L"DateTimeCheatSheet_YearLastTwoDigits")));
+ m_dateTimeShortcuts.Append(winrt::make(L"$Y", resourceLoader.GetString(L"DateTimeCheatSheet_YearLastDigit")));
+ m_dateTimeShortcuts.Append(winrt::make(L"$MMMM", resourceLoader.GetString(L"DateTimeCheatSheet_MonthName")));
+ m_dateTimeShortcuts.Append(winrt::make(L"$MMM", resourceLoader.GetString(L"DateTimeCheatSheet_MonthNameAbbr")));
+ m_dateTimeShortcuts.Append(winrt::make(L"$MM", resourceLoader.GetString(L"DateTimeCheatSheet_MonthDigitLZero")));
+ m_dateTimeShortcuts.Append(winrt::make(L"$M", resourceLoader.GetString(L"DateTimeCheatSheet_MonthDigit")));
+ m_dateTimeShortcuts.Append(winrt::make(L"$DDDD", resourceLoader.GetString(L"DateTimeCheatSheet_DayName")));
+ m_dateTimeShortcuts.Append(winrt::make(L"$DDD", resourceLoader.GetString(L"DateTimeCheatSheet_DayNameAbbr")));
+ m_dateTimeShortcuts.Append(winrt::make(L"$DD", resourceLoader.GetString(L"DateTimeCheatSheet_DayDigitLZero")));
+ m_dateTimeShortcuts.Append(winrt::make(L"$D", resourceLoader.GetString(L"DateTimeCheatSheet_DayDigit")));
+ m_dateTimeShortcuts.Append(winrt::make(L"$hh", resourceLoader.GetString(L"DateTimeCheatSheet_HoursLZero")));
+ m_dateTimeShortcuts.Append(winrt::make(L"$h", resourceLoader.GetString(L"DateTimeCheatSheet_Hours")));
+ m_dateTimeShortcuts.Append(winrt::make(L"$mm", resourceLoader.GetString(L"DateTimeCheatSheet_MinutesLZero")));
+ m_dateTimeShortcuts.Append(winrt::make(L"$m", resourceLoader.GetString(L"DateTimeCheatSheet_Minutes")));
+ m_dateTimeShortcuts.Append(winrt::make(L"$ss", resourceLoader.GetString(L"DateTimeCheatSheet_SecondsLZero")));
+ m_dateTimeShortcuts.Append(winrt::make(L"$s", resourceLoader.GetString(L"DateTimeCheatSheet_Seconds")));
+ m_dateTimeShortcuts.Append(winrt::make(L"$fff", resourceLoader.GetString(L"DateTimeCheatSheet_MilliSeconds3D")));
+ m_dateTimeShortcuts.Append(winrt::make(L"$ff", resourceLoader.GetString(L"DateTimeCheatSheet_MilliSeconds2D")));
+ m_dateTimeShortcuts.Append(winrt::make(L"$f", resourceLoader.GetString(L"DateTimeCheatSheet_MilliSeconds1D")));
+
+ InitializeComponent();
+ }
+
+ Windows::Foundation::Collections::IObservableVector MainWindow::SearchMRU()
+ {
+ return m_searchMRU;
+ }
+
+ Windows::Foundation::Collections::IObservableVector MainWindow::ReplaceMRU()
+ {
+ return m_replaceMRU;
+ }
+
+ winrt::Windows::Foundation::Collections::IObservableVector MainWindow::ExplorerItems()
+ {
+ return m_explorerItems;
+ }
+
+ winrt::Windows::Foundation::Collections::IObservableVector MainWindow::SearchRegExShortcuts()
+ {
+ return m_searchRegExShortcuts;
+ }
+
+ winrt::Windows::Foundation::Collections::IObservableVector MainWindow::DateTimeShortcuts()
+ {
+ return m_dateTimeShortcuts;
+ }
+
+ Windows::UI::Xaml::Controls::AutoSuggestBox MainWindow::AutoSuggestBoxSearch()
+ {
+ return textBox_search();
+ }
+
+ Windows::UI::Xaml::Controls::AutoSuggestBox MainWindow::AutoSuggestBoxReplace()
+ {
+ return textBox_replace();
+ }
+
+ Windows::UI::Xaml::Controls::CheckBox MainWindow::CheckBoxRegex()
+ {
+ return checkBox_regex();
+ }
+
+ Windows::UI::Xaml::Controls::CheckBox MainWindow::CheckBoxCaseSensitive()
+ {
+ return checkBox_case();
+ }
+
+ Windows::UI::Xaml::Controls::CheckBox MainWindow::CheckBoxMatchAll()
+ {
+ return checkBox_matchAll();
+ }
+
+ Windows::UI::Xaml::Controls::ComboBox MainWindow::ComboBoxRenameParts()
+ {
+ return comboBox_renameParts();
+ }
+
+ Windows::UI::Xaml::Controls::Primitives::ToggleButton MainWindow::ToggleButtonIncludeFiles()
+ {
+ return toggleButton_includeFiles();
+ }
+
+ Windows::UI::Xaml::Controls::Primitives::ToggleButton MainWindow::ToggleButtonIncludeFolders()
+ {
+ return toggleButton_includeFolders();
+ }
+
+ Windows::UI::Xaml::Controls::Primitives::ToggleButton MainWindow::ToggleButtonIncludeSubfolders()
+ {
+ return toggleButton_includeSubfolders();
+ }
+
+ Windows::UI::Xaml::Controls::Primitives::ToggleButton MainWindow::ToggleButtonEnumerateItems()
+ {
+ return toggleButton_enumItems();
+ }
+
+ Windows::UI::Xaml::Controls::Primitives::ToggleButton MainWindow::ToggleButtonUpperCase()
+ {
+ return toggleButton_upperCase();
+ }
+
+ Windows::UI::Xaml::Controls::Primitives::ToggleButton MainWindow::ToggleButtonLowerCase()
+ {
+ return toggleButton_lowerCase();
+ }
+
+ Windows::UI::Xaml::Controls::Primitives::ToggleButton MainWindow::ToggleButtonTitleCase()
+ {
+ return toggleButton_titleCase();
+ }
+
+ Windows::UI::Xaml::Controls::Primitives::ToggleButton MainWindow::ToggleButtonCapitalize()
+ {
+ return toggleButton_capitalize();
+ }
+
+ Windows::UI::Xaml::Controls::Button MainWindow::ButtonSettings()
+ {
+ return button_settings();
+ }
+
+ Windows::UI::Xaml::Controls::CheckBox MainWindow::CheckBoxSelectAll()
+ {
+ return checkBox_selectAll();
+ }
+
+ PowerRenameUILib::UIUpdates MainWindow::UIUpdatesItem()
+ {
+ return m_uiUpdatesItem;
+ }
+
+ void MainWindow::AddExplorerItem(int32_t id, hstring const& original, hstring const& renamed, int32_t type, int32_t parentId, bool checked)
+ {
+ auto newItem = winrt::make(id, original, renamed, type, checked);
+ if (parentId == 0)
+ {
+ m_explorerItems.Append(newItem);
+ }
+ else
+ {
+ auto parent = FindById(parentId);
+ parent.Children().Append(newItem);
+ }
+ }
+
+ void MainWindow::UpdateExplorerItem(int32_t id, hstring const& newName)
+ {
+ auto itemToUpdate = FindById(id);
+ if (itemToUpdate != NULL)
+ {
+ itemToUpdate.Renamed(newName);
+ }
+ }
+
+ void MainWindow::UpdateRenamedExplorerItem(int32_t id, hstring const& newOriginalName)
+ {
+ auto itemToUpdate = FindById(id);
+ if (itemToUpdate != NULL)
+ {
+ itemToUpdate.Original(newOriginalName);
+ itemToUpdate.Renamed(L"");
+ }
+ }
+
+ void MainWindow::AppendSearchMRU(hstring const& value)
+ {
+ m_searchMRU.Append(value);
+ }
+
+ void MainWindow::AppendReplaceMRU(hstring const& value)
+ {
+ m_replaceMRU.Append(value);
+ }
+
+ PowerRenameUILib::ExplorerItem MainWindow::FindById(int32_t id)
+ {
+ auto fakeRoot = winrt::make(0, L"Fake", L"", 0, false);
+ fakeRoot.Children(m_explorerItems);
+ return FindById(fakeRoot, id);
+ }
+
+ PowerRenameUILib::ExplorerItem MainWindow::FindById(PowerRenameUILib::ExplorerItem& root, int32_t id)
+ {
+ if (root.Id() == id)
+ return root;
+
+ if (root.Type() == static_cast(ExplorerItem::ExplorerItemType::Folder))
+ {
+ for (auto c : root.Children())
+ {
+ auto result = FindById(c, id);
+ if (result != NULL)
+ return result;
+ }
+ }
+
+ return NULL;
+ }
+
+ void MainWindow::ToggleAll(PowerRenameUILib::ExplorerItem node, bool checked)
+ {
+ if (node == NULL)
+ return;
+
+ node.Checked(checked);
+
+ if (node.Type() == static_cast(ExplorerItem::ExplorerItemType::Folder))
+ {
+ for (auto c : node.Children())
+ {
+ ToggleAll(c, checked);
+ }
+ }
+ }
+
+ void MainWindow::Checked_ids(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const&)
+ {
+ auto checkbox = sender.as();
+ auto id = std::stoi(std::wstring{ checkbox.Name() });
+ auto item = FindById(id);
+ if (checkbox.IsChecked().GetBoolean() != item.Checked())
+ {
+ m_uiUpdatesItem.Checked(checkbox.IsChecked().GetBoolean());
+ m_uiUpdatesItem.ChangedExplorerItemId(id);
+ }
+ }
+
+ void MainWindow::SelectAll(winrt::Windows::Foundation::IInspectable const&, winrt::Windows::UI::Xaml::RoutedEventArgs const&)
+ {
+ if (checkBox_selectAll().IsChecked().GetBoolean() != m_allSelected)
+ {
+ auto fakeRoot = winrt::make(0, L"Fake", L"", 0, false);
+ fakeRoot.Children(m_explorerItems);
+ ToggleAll(fakeRoot, checkBox_selectAll().IsChecked().GetBoolean());
+ m_uiUpdatesItem.ToggleAll();
+ m_allSelected = !m_allSelected;
+ }
+ }
+
+ void MainWindow::ShowAll(winrt::Windows::Foundation::IInspectable const&, winrt::Windows::UI::Xaml::RoutedEventArgs const&)
+ {
+ button_showAll().IsChecked(true);
+ button_showRenamed().IsChecked(false);
+ if (!m_uiUpdatesItem.ShowAll())
+ {
+ m_explorerItems.Clear();
+ m_uiUpdatesItem.ShowAll(true);
+ }
+ }
+
+ void MainWindow::ShowRenamed(winrt::Windows::Foundation::IInspectable const&, winrt::Windows::UI::Xaml::RoutedEventArgs const&)
+ {
+ button_showRenamed().IsChecked(true);
+ button_showAll().IsChecked(false);
+ if (m_uiUpdatesItem.ShowAll())
+ {
+ m_explorerItems.Clear();
+ m_uiUpdatesItem.ShowAll(false);
+ }
+ }
+
+ void MainWindow::RegExItemClick(winrt::Windows::Foundation::IInspectable const&, winrt::Windows::UI::Xaml::Controls::ItemClickEventArgs const& e)
+ {
+ auto s = e.ClickedItem().try_as();
+ RegExFlyout().Hide();
+ textBox_search().Text(textBox_search().Text() + s->Code());
+ }
+
+ void MainWindow::DateTimeItemClick(winrt::Windows::Foundation::IInspectable const&, winrt::Windows::UI::Xaml::Controls::ItemClickEventArgs const& e)
+ {
+ auto s = e.ClickedItem().try_as();
+ DateTimeFlyout().Hide();
+ textBox_replace().Text(textBox_replace().Text() + s->Code());
+ }
+
+ void MainWindow::button_rename_Click(winrt::Microsoft::UI::Xaml::Controls::SplitButton const&, winrt::Microsoft::UI::Xaml::Controls::SplitButtonClickEventArgs const&)
+ {
+ m_uiUpdatesItem.CloseUIWindow(false);
+ m_uiUpdatesItem.Rename();
+ }
+
+ void MainWindow::MenuFlyoutItem_Click(winrt::Windows::Foundation::IInspectable const&, winrt::Windows::UI::Xaml::RoutedEventArgs const&)
+ {
+ m_uiUpdatesItem.CloseUIWindow(true);
+ m_uiUpdatesItem.Rename();
+ }
+
+ void MainWindow::OpenDocs(winrt::Windows::Foundation::IInspectable const&, winrt::Windows::UI::Xaml::RoutedEventArgs const&)
+ {
+ Windows::System::Launcher::LaunchUriAsync(winrt::Windows::Foundation::Uri{ L"https://aka.ms/PowerToysOverview_PowerRename" });
+ }
+}
diff --git a/src/modules/powerrename/PowerRenameUILib/MainWindow.h b/src/modules/powerrename/PowerRenameUILib/MainWindow.h
new file mode 100644
index 0000000000..b017ca3ac6
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/MainWindow.h
@@ -0,0 +1,88 @@
+#pragma once
+
+#include "winrt/Windows.UI.Xaml.h"
+#include "winrt/Windows.UI.Xaml.Markup.h"
+#include "winrt/Windows.UI.Xaml.Interop.h"
+#include "winrt/Windows.UI.Xaml.Controls.Primitives.h"
+#include "MainWindow.g.h"
+#include "PatternSnippet.h"
+#include "ExplorerItem.h"
+#include "ExplorerItemTemplateSelector.h"
+
+namespace winrt::PowerRenameUILib::implementation
+{
+ struct MainWindow : MainWindowT
+ {
+ MainWindow();
+
+ Windows::Foundation::Collections::IObservableVector SearchMRU();
+ Windows::Foundation::Collections::IObservableVector ReplaceMRU();
+ winrt::Windows::Foundation::Collections::IObservableVector ExplorerItems();
+ winrt::Windows::Foundation::Collections::IObservableVector SearchRegExShortcuts();
+ winrt::Windows::Foundation::Collections::IObservableVector DateTimeShortcuts();
+
+ Windows::UI::Xaml::Controls::AutoSuggestBox AutoSuggestBoxSearch();
+ Windows::UI::Xaml::Controls::AutoSuggestBox AutoSuggestBoxReplace();
+
+ Windows::UI::Xaml::Controls::CheckBox CheckBoxRegex();
+ Windows::UI::Xaml::Controls::CheckBox CheckBoxCaseSensitive();
+ Windows::UI::Xaml::Controls::CheckBox CheckBoxMatchAll();
+
+ Windows::UI::Xaml::Controls::ComboBox ComboBoxRenameParts();
+
+ Windows::UI::Xaml::Controls::Primitives::ToggleButton ToggleButtonIncludeFiles();
+ Windows::UI::Xaml::Controls::Primitives::ToggleButton ToggleButtonIncludeFolders();
+ Windows::UI::Xaml::Controls::Primitives::ToggleButton ToggleButtonIncludeSubfolders();
+
+ Windows::UI::Xaml::Controls::Primitives::ToggleButton ToggleButtonUpperCase();
+ Windows::UI::Xaml::Controls::Primitives::ToggleButton ToggleButtonLowerCase();
+ Windows::UI::Xaml::Controls::Primitives::ToggleButton ToggleButtonTitleCase();
+ Windows::UI::Xaml::Controls::Primitives::ToggleButton ToggleButtonCapitalize();
+
+ Windows::UI::Xaml::Controls::Primitives::ToggleButton ToggleButtonEnumerateItems();
+
+ Windows::UI::Xaml::Controls::Button ButtonSettings();
+
+ Windows::UI::Xaml::Controls::CheckBox CheckBoxSelectAll();
+
+ PowerRenameUILib::UIUpdates UIUpdatesItem();
+
+ void AddExplorerItem(int32_t id, hstring const& original, hstring const& renamed, int32_t type, int32_t parentId, bool checked);
+ void UpdateExplorerItem(int32_t id, hstring const& newName);
+ void UpdateRenamedExplorerItem(int32_t id, hstring const& newOriginalName);
+ void AppendSearchMRU(hstring const& value);
+ void AppendReplaceMRU(hstring const& value);
+
+ void Checked_ids(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
+ void SelectAll(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
+ void ShowAll(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
+ void ShowRenamed(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
+
+ private:
+ bool m_allSelected;
+ PowerRenameUILib::UIUpdates m_uiUpdatesItem;
+ PowerRenameUILib::ExplorerItem FindById(int32_t id);
+ PowerRenameUILib::ExplorerItem FindById(PowerRenameUILib::ExplorerItem& root, int32_t id);
+ void ToggleAll(PowerRenameUILib::ExplorerItem node, bool checked);
+
+ winrt::Windows::Foundation::Collections::IObservableVector m_searchMRU;
+ winrt::Windows::Foundation::Collections::IObservableVector m_replaceMRU;
+ winrt::Windows::Foundation::Collections::IObservableVector m_explorerItems;
+ winrt::Windows::Foundation::Collections::IObservableVector m_searchRegExShortcuts;
+ winrt::Windows::Foundation::Collections::IObservableVector m_dateTimeShortcuts;
+
+ public:
+ void RegExItemClick(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Controls::ItemClickEventArgs const& e);
+ void DateTimeItemClick(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Controls::ItemClickEventArgs const& e);
+ void button_rename_Click(winrt::Microsoft::UI::Xaml::Controls::SplitButton const& sender, winrt::Microsoft::UI::Xaml::Controls::SplitButtonClickEventArgs const& args);
+ void MenuFlyoutItem_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
+ void OpenDocs(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
+ };
+}
+
+namespace winrt::PowerRenameUILib::factory_implementation
+{
+ struct MainWindow : MainWindowT
+ {
+ };
+}
diff --git a/src/modules/powerrename/PowerRenameUILib/MainWindow.idl b/src/modules/powerrename/PowerRenameUILib/MainWindow.idl
new file mode 100644
index 0000000000..344ccf8ebc
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/MainWindow.idl
@@ -0,0 +1,61 @@
+import "ExplorerItem.idl";
+import "PatternSnippet.idl";
+
+namespace PowerRenameUILib
+{
+ runtimeclass UIUpdates : Windows.UI.Xaml.Data.INotifyPropertyChanged
+ {
+ UIUpdates();
+ Boolean ShowAll;
+ Int32 ChangedExplorerItemId;
+ Boolean Checked;
+ void ToggleAll();
+ Boolean CloseUIWindow();
+ void CloseUIWindow(Boolean closeUIWindow);
+ Boolean ButtonRenameEnabled;
+ void Rename();
+ }
+
+ [default_interface] runtimeclass MainWindow : Windows.UI.Xaml.Controls.UserControl
+ {
+ MainWindow();
+
+ Windows.Foundation.Collections.IObservableVector SearchMRU { get; };
+ Windows.Foundation.Collections.IObservableVector ReplaceMRU { get; };
+
+ Windows.Foundation.Collections.IObservableVector ExplorerItems { get; };
+ Windows.Foundation.Collections.IObservableVector SearchRegExShortcuts { get; };
+ Windows.Foundation.Collections.IObservableVector DateTimeShortcuts { get; };
+
+ Windows.UI.Xaml.Controls.AutoSuggestBox AutoSuggestBoxSearch { get; };
+ Windows.UI.Xaml.Controls.AutoSuggestBox AutoSuggestBoxReplace { get; };
+
+ Windows.UI.Xaml.Controls.CheckBox CheckBoxRegex { get; };
+ Windows.UI.Xaml.Controls.CheckBox CheckBoxCaseSensitive { get; };
+ Windows.UI.Xaml.Controls.CheckBox CheckBoxMatchAll { get; };
+ Windows.UI.Xaml.Controls.Primitives.ToggleButton ToggleButtonEnumerateItems { get; };
+
+ Windows.UI.Xaml.Controls.ComboBox ComboBoxRenameParts { get; };
+
+ Windows.UI.Xaml.Controls.Primitives.ToggleButton ToggleButtonIncludeFiles { get; };
+ Windows.UI.Xaml.Controls.Primitives.ToggleButton ToggleButtonIncludeFolders { get; };
+ Windows.UI.Xaml.Controls.Primitives.ToggleButton ToggleButtonIncludeSubfolders { get; };
+
+ Windows.UI.Xaml.Controls.Button ButtonSettings { get; };
+
+ Windows.UI.Xaml.Controls.CheckBox CheckBoxSelectAll { get; };
+
+ Windows.UI.Xaml.Controls.Primitives.ToggleButton ToggleButtonUpperCase { get; };
+ Windows.UI.Xaml.Controls.Primitives.ToggleButton ToggleButtonLowerCase { get; };
+ Windows.UI.Xaml.Controls.Primitives.ToggleButton ToggleButtonTitleCase { get; };
+ Windows.UI.Xaml.Controls.Primitives.ToggleButton ToggleButtonCapitalize { get; };
+
+ void AddExplorerItem(Int32 id, String original, String renamed, Int32 type, Int32 parentId, Boolean checked);
+ void UpdateExplorerItem(Int32 id, String newName);
+ void UpdateRenamedExplorerItem(Int32 id, String newOriginalName);
+ void AppendSearchMRU(String value);
+ void AppendReplaceMRU(String value);
+
+ UIUpdates UIUpdatesItem { get; };
+ }
+}
diff --git a/src/modules/powerrename/PowerRenameUILib/MainWindow.xaml b/src/modules/powerrename/PowerRenameUILib/MainWindow.xaml
new file mode 100644
index 0000000000..a7c8866b6e
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/MainWindow.xaml
@@ -0,0 +1,326 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Filename + extension
+ Filename only
+ Extension only
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/powerrename/PowerRenameUILib/Package.appxmanifest b/src/modules/powerrename/PowerRenameUILib/Package.appxmanifest
new file mode 100644
index 0000000000..ea55f633fe
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/Package.appxmanifest
@@ -0,0 +1,27 @@
+
+
+
+
+
+ PowerRenameUILib
+ Microsoft Corporation
+ Assets\StoreLogo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/powerrename/PowerRenameUILib/PatternSnippet.cpp b/src/modules/powerrename/PowerRenameUILib/PatternSnippet.cpp
new file mode 100644
index 0000000000..3dbc54a45b
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/PatternSnippet.cpp
@@ -0,0 +1,49 @@
+#include "pch.h"
+#include "PatternSnippet.h"
+#include "PatternSnippet.g.cpp"
+
+namespace winrt::PowerRenameUILib::implementation
+{
+ PatternSnippet::PatternSnippet(hstring const& code, hstring const& description) :
+ m_code{ code }, m_description{ description }
+ {
+ }
+
+ hstring PatternSnippet::Code()
+ {
+ return m_code;
+ }
+
+ void PatternSnippet::Code(hstring const& value)
+ {
+ if (m_code != value)
+ {
+ m_code = value;
+ m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"Code" });
+ }
+ }
+
+ hstring PatternSnippet::Description()
+ {
+ return m_description;
+ }
+
+ void PatternSnippet::Description(hstring const& value)
+ {
+ if (m_description != value)
+ {
+ m_description = value;
+ m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"Description" });
+ }
+ }
+
+ winrt::event_token PatternSnippet::PropertyChanged(winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler)
+ {
+ return m_propertyChanged.add(handler);
+ }
+
+ void PatternSnippet::PropertyChanged(winrt::event_token const& token) noexcept
+ {
+ m_propertyChanged.remove(token);
+ }
+}
diff --git a/src/modules/powerrename/PowerRenameUILib/PatternSnippet.h b/src/modules/powerrename/PowerRenameUILib/PatternSnippet.h
new file mode 100644
index 0000000000..812f9fc2a4
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/PatternSnippet.h
@@ -0,0 +1,29 @@
+#pragma once
+#include "PatternSnippet.g.h"
+
+namespace winrt::PowerRenameUILib::implementation
+{
+ struct PatternSnippet : PatternSnippetT
+ {
+ PatternSnippet() = delete;
+
+ PatternSnippet(hstring const& code, hstring const& description);
+ hstring Code();
+ void Code(hstring const& value);
+ hstring Description();
+ void Description(hstring const& value);
+ winrt::event_token PropertyChanged(winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler);
+ void PropertyChanged(winrt::event_token const& token) noexcept;
+
+ private:
+ winrt::hstring m_code;
+ winrt::hstring m_description;
+ winrt::event m_propertyChanged;
+ };
+}
+namespace winrt::PowerRenameUILib::factory_implementation
+{
+ struct PatternSnippet : PatternSnippetT
+ {
+ };
+}
diff --git a/src/modules/powerrename/PowerRenameUILib/PatternSnippet.idl b/src/modules/powerrename/PowerRenameUILib/PatternSnippet.idl
new file mode 100644
index 0000000000..86aff46bd1
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/PatternSnippet.idl
@@ -0,0 +1,9 @@
+namespace PowerRenameUILib
+{
+ runtimeclass PatternSnippet : Windows.UI.Xaml.Data.INotifyPropertyChanged
+ {
+ PatternSnippet(String code, String description);
+ String Code;
+ String Description;
+ }
+}
diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameUILib.filters b/src/modules/powerrename/PowerRenameUILib/PowerRenameUILib.filters
new file mode 100644
index 0000000000..60d0472aaa
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameUILib.filters
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Assets
+
+
+ Assets
+
+
+ Assets
+
+
+ Assets
+
+
+ Assets
+
+
+ Assets
+
+
+ Assets
+
+
+ Assets
+
+
+ Assets
+
+
+
+
+
+
+
+ {e48dc53e-40b1-40cb-970a-f89935452892}
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameUILib.vcxproj b/src/modules/powerrename/PowerRenameUILib/PowerRenameUILib.vcxproj
new file mode 100644
index 0000000000..5886229b5f
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameUILib.vcxproj
@@ -0,0 +1,210 @@
+
+
+
+
+
+ true
+ true
+ true
+ true
+ {4642d596-723f-4bfc-894c-46811219ac4a}
+ PowerRenameUILib
+ PowerRenameUILib
+ en-US
+ 15.0
+ true
+ Windows Store
+ 10.0
+ 10.0.18362.0
+ 10.0.18362.0
+ normal
+
+
+ true
+ true
+ App
+ true
+
+
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ DynamicLibrary
+ v143
+ v142
+ v141
+ v140
+ Unicode
+
+
+ true
+ true
+
+
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(SolutionDir)$(Platform)\$(Configuration)\modules\PowerRename\
+
+
+ $(SolutionDir)$(Platform)\$(Configuration)\modules\PowerRename\
+
+
+
+ Use
+ pch.h
+ $(IntDir)pch.pch
+ Level4
+ %(AdditionalOptions) /bigobj
+
+ /DWINRT_NO_MAKE_DETECTION %(AdditionalOptions)
+ WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions)
+
+
+ false
+
+
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ MultiThreadedDebugDLL
+
+
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+
+
+ true
+ true
+
+
+
+
+
+
+
+ MainWindow.xaml
+ Code
+
+
+
+ App.xaml
+
+
+
+
+
+
+ Designer
+
+
+ Designer
+
+
+
+
+ Designer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MainWindow.xaml
+ Code
+
+
+ Create
+
+
+ App.xaml
+
+
+
+
+
+
+
+ App.xaml
+
+
+
+
+ MainWindow.xaml
+ Code
+
+
+
+
+ <_WildCardPRIResource Include="Strings\*\Resources.resw" />
+
+
+
+
+ true
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+ 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}.
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/powerrename/PowerRenameUILib/PropertySheet.props b/src/modules/powerrename/PowerRenameUILib/PropertySheet.props
new file mode 100644
index 0000000000..e34141b019
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/PropertySheet.props
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/powerrename/PowerRenameUILib/Strings/en-us/Resources.resw b/src/modules/powerrename/PowerRenameUILib/Strings/en-us/Resources.resw
new file mode 100644
index 0000000000..be4d6f6163
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/Strings/en-us/Resources.resw
@@ -0,0 +1,335 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Search for
+
+
+ RegEx help
+
+
+ Matches any character
+
+
+ Any digit, short for [0-9]
+
+
+ A non-digit, short for [^0-9]
+
+
+ A non-whitespace character, short for [^\\s]
+
+
+ A word character, short for [a-zA-Z_0-9]
+
+
+ Several non-whitespace characters
+
+
+ Matches a word boundary where a word character is [a-zA-Z0-9_].
+
+
+ Learn more about RegEx
+
+
+ Replace with
+
+
+ Replace using file creation date and time
+
+
+ Year represented by a full four or five digits, depending on the calendar used.
+
+
+ Year represented only by the last two digits. A leading zero is added for single-digit years.
+
+
+ Year represented only by the last digit.
+
+
+ Name of the month.
+
+
+ Abbreviated name of the month.
+
+
+ Month as digits with leading zeros for single-digit months.
+
+
+ Month as digits without leading zeros for single-digit months.
+
+
+ Name of the day of the week.
+
+
+ Abbreviated name of the day of the week.
+
+
+ Day of the month as digits with leading zeros for single-digit days.
+
+
+ Day of the month as digits without leading zeros for single-digit days.
+
+
+ Hours with leading zeros for single-digit hours.
+
+
+ Hours without leading zeros for single-digit hours.
+
+
+ Minutes with leading zeros for single-digit minutes.
+
+
+ Minutes without leading zeros for single-digit minutes.
+
+
+ Seconds with leading zeros for single-digit seconds.
+
+
+ Seconds without leading zeros for single-digit seconds.
+
+
+ Milliseconds represented by full three digits.
+
+
+ Milliseconds represented only by the first two digit.
+
+
+ Milliseconds represented only by the first digit.
+
+
+ Use regular expressions
+
+
+ Match all occurences
+
+
+ Case sensitive
+
+
+ Apply to
+
+
+ Documentation
+
+
+ Documentation
+
+
+ Include files
+
+
+ Include files
+
+
+ Include folders
+
+
+ Include folders
+
+
+ Include subfolders
+
+
+ Include subfolders
+
+
+ Text formatting
+
+
+ lowercase
+
+
+ Lowercase
+
+
+ UPPERCASE
+
+
+ Uppercase
+
+
+ Title case
+
+
+ Title case
+
+
+ Capitalize Each Word
+
+
+ Capitalize each word
+
+
+ Enumerate items
+
+
+ Enumerate items
+
+
+ Select or deselect all
+
+
+ Filter
+
+
+ Filter
+
+
+ Show all files
+
+
+ Only show files that will be renamed
+
+
+ Original
+
+
+ Renamed
+
+
+ Settings
+
+
+ Settings
+
+
+ Apply
+
+
+ Apply
+
+
+ Apply and close
+
+
+ RegEx help
+
+
+ RegEx help
+
+
+ File creation date and time help
+
+
+ File creation date and time help
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/powerrename/PowerRenameUILib/UIUpdates.cpp b/src/modules/powerrename/PowerRenameUILib/UIUpdates.cpp
new file mode 100644
index 0000000000..eef989ec0c
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/UIUpdates.cpp
@@ -0,0 +1,90 @@
+#include "pch.h"
+#include "UIUpdates.h"
+#include "UIUpdates.g.cpp"
+
+namespace winrt::PowerRenameUILib::implementation
+{
+ UIUpdates::UIUpdates() :
+ m_showAll{ true }, m_changedItemId{ -1 }, m_checked{ true }, m_closeUIWindow{ false }, m_buttonRenameEnabled{ false }
+ {
+ }
+
+ bool UIUpdates::ShowAll()
+ {
+ return m_showAll;
+ }
+
+ void UIUpdates::ShowAll(bool value)
+ {
+ if (m_showAll != value)
+ {
+ m_showAll = value;
+ m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"ShowAll" });
+ }
+ }
+
+ int32_t UIUpdates::ChangedExplorerItemId()
+ {
+ return m_changedItemId;
+ }
+
+ void UIUpdates::ChangedExplorerItemId(int32_t value)
+ {
+ m_changedItemId = value;
+ m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"ChangedItemId" });
+ }
+
+ bool UIUpdates::Checked()
+ {
+ return m_checked;
+ }
+
+ void UIUpdates::Checked(bool value)
+ {
+ m_checked = value;
+ }
+
+ winrt::event_token UIUpdates::PropertyChanged(winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler)
+ {
+ return m_propertyChanged.add(handler);
+ }
+
+ void UIUpdates::PropertyChanged(winrt::event_token const& token) noexcept
+ {
+ m_propertyChanged.remove(token);
+ }
+
+ void UIUpdates::ToggleAll()
+ {
+ m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"ToggleAll" });
+ }
+
+ void UIUpdates::Rename()
+ {
+ m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"Rename" });
+ }
+
+ bool UIUpdates::CloseUIWindow()
+ {
+ return m_closeUIWindow;
+ }
+
+ void UIUpdates::CloseUIWindow(bool closeUIWindow)
+ {
+ m_closeUIWindow = closeUIWindow;
+ }
+
+ bool UIUpdates::ButtonRenameEnabled()
+ {
+ return m_buttonRenameEnabled;
+ }
+
+ void UIUpdates::ButtonRenameEnabled(bool value)
+ {
+ if (m_buttonRenameEnabled != value)
+ {
+ m_buttonRenameEnabled = value;
+ m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"ButtonRenameEnabled" });
+ }
+ }
+}
diff --git a/src/modules/powerrename/PowerRenameUILib/UIUpdates.h b/src/modules/powerrename/PowerRenameUILib/UIUpdates.h
new file mode 100644
index 0000000000..b9ca2f38c3
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/UIUpdates.h
@@ -0,0 +1,39 @@
+#pragma once
+#include "UIUpdates.g.h"
+
+namespace winrt::PowerRenameUILib::implementation
+{
+ struct UIUpdates : UIUpdatesT
+ {
+ UIUpdates();
+
+ bool ShowAll();
+ void ShowAll(bool value);
+ int32_t ChangedExplorerItemId();
+ void ChangedExplorerItemId(int32_t value);
+ bool Checked();
+ void Checked(bool value);
+ winrt::event_token PropertyChanged(winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler);
+ void PropertyChanged(winrt::event_token const& token) noexcept;
+ void ToggleAll();
+ bool CloseUIWindow();
+ void CloseUIWindow(bool closeUIWindow);
+ bool ButtonRenameEnabled();
+ void ButtonRenameEnabled(bool value);
+ void Rename();
+
+ private:
+ bool m_showAll;
+ int32_t m_changedItemId;
+ bool m_checked;
+ bool m_closeUIWindow;
+ bool m_buttonRenameEnabled;
+ winrt::event m_propertyChanged;
+ };
+}
+namespace winrt::PowerRenameUILib::factory_implementation
+{
+ struct UIUpdates : UIUpdatesT
+ {
+ };
+}
diff --git a/src/modules/powerrename/PowerRenameUILib/app.base.h b/src/modules/powerrename/PowerRenameUILib/app.base.h
new file mode 100644
index 0000000000..1fe87fb61c
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/app.base.h
@@ -0,0 +1,35 @@
+#pragma once
+namespace winrt::PowerRenameUILib::implementation
+{
+ template
+ struct App_baseWithProvider : public App_base
+ {
+ using IXamlType = ::winrt::Windows::UI::Xaml::Markup::IXamlType;
+ IXamlType GetXamlType(::winrt::Windows::UI::Xaml::Interop::TypeName const& type)
+ {
+ return AppProvider()->GetXamlType(type);
+ }
+ IXamlType GetXamlType(::winrt::hstring const& fullName)
+ {
+ return AppProvider()->GetXamlType(fullName);
+ }
+ ::winrt::com_array<::winrt::Windows::UI::Xaml::Markup::XmlnsDefinition> GetXmlnsDefinitions()
+ {
+ return AppProvider()->GetXmlnsDefinitions();
+ }
+
+ private:
+ bool _contentLoaded{ false };
+ winrt::com_ptr _appProvider;
+ winrt::com_ptr AppProvider()
+ {
+ if (!_appProvider)
+ {
+ _appProvider = winrt::make_self();
+ }
+ return _appProvider;
+ }
+ };
+ template
+ using AppT2 = App_baseWithProvider;
+}
diff --git a/src/modules/powerrename/PowerRenameUILib/packages.config b/src/modules/powerrename/PowerRenameUILib/packages.config
new file mode 100644
index 0000000000..eaa2143ee5
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/packages.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/powerrename/PowerRenameUILib/pch.cpp b/src/modules/powerrename/PowerRenameUILib/pch.cpp
new file mode 100644
index 0000000000..bcb5590be1
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/pch.cpp
@@ -0,0 +1 @@
+#include "pch.h"
diff --git a/src/modules/powerrename/PowerRenameUILib/pch.h b/src/modules/powerrename/PowerRenameUILib/pch.h
new file mode 100644
index 0000000000..76b2cfbd5a
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/pch.h
@@ -0,0 +1,27 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "winrt/Microsoft.UI.Xaml.Automation.Peers.h"
+#include "winrt/Microsoft.UI.Xaml.Controls.Primitives.h"
+#include "winrt/Microsoft.UI.Xaml.Controls.AnimatedVisuals.h"
+#include "winrt/Microsoft.UI.Xaml.Media.h"
+#include "winrt/Microsoft.UI.Xaml.XamlTypeInfo.h"
diff --git a/src/modules/powerrename/PowerRenameUILib/placeholder.exe b/src/modules/powerrename/PowerRenameUILib/placeholder.exe
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/modules/powerrename/PowerRenameUILib/readme.txt b/src/modules/powerrename/PowerRenameUILib/readme.txt
new file mode 100644
index 0000000000..89043935e1
--- /dev/null
+++ b/src/modules/powerrename/PowerRenameUILib/readme.txt
@@ -0,0 +1,23 @@
+========================================================================
+ C++/WinRT PowerRenameUILib Project Overview
+========================================================================
+
+This project demonstrates how to get started writing XAML apps directly
+with standard C++, using the C++/WinRT SDK component and XAML compiler
+support to generate implementation headers from interface (IDL) files.
+These headers can then be used to implement the local Windows Runtime
+classes referenced in the app's XAML pages.
+
+Steps:
+1. Create an interface (IDL) file to define any local Windows Runtime
+ classes referenced in the app's XAML pages.
+2. Build the project once to generate implementation templates under
+ the "Generated Files" folder, as well as skeleton class definitions
+ under "Generated Files\sources".
+3. Use the skeleton class definitions for reference to implement your
+ Windows Runtime classes.
+
+========================================================================
+Learn more about C++/WinRT here:
+http://aka.ms/cppwinrt/
+========================================================================
diff --git a/src/modules/powerrename/UWPui/PowerRenameUWPUI.rc b/src/modules/powerrename/UWPui/PowerRenameUWPUI.rc
index 5fdc21b863..cf8c5b565e 100644
--- a/src/modules/powerrename/UWPui/PowerRenameUWPUI.rc
+++ b/src/modules/powerrename/UWPui/PowerRenameUWPUI.rc
@@ -5,7 +5,6 @@
// We need both DLL and UI resource files for UWP UI.
#include "../dll/Generated Files/PowerRenameExt.rc"
#undef IDC_STATIC
-#include "../ui/Generated Files/PowerRenameUI.rc"
2 VERSIONINFO
FILEVERSION FILE_VERSION
diff --git a/src/modules/powerrename/UWPui/PowerRenameUWPUI.vcxproj b/src/modules/powerrename/UWPui/PowerRenameUWPUI.vcxproj
index 55d2ebbb2e..aed1a9ea97 100644
--- a/src/modules/powerrename/UWPui/PowerRenameUWPUI.vcxproj
+++ b/src/modules/powerrename/UWPui/PowerRenameUWPUI.vcxproj
@@ -63,9 +63,6 @@
{51920f1f-c28c-4adf-8660-4238766796c2}
-
- {0e072714-d127-460b-afad-b4c40b412798}
-
diff --git a/src/modules/powerrename/dll/PowerRenameExt.base.rc b/src/modules/powerrename/dll/PowerRenameExt.base.rc
index 3165ed5d93..5f99345c17 100644
Binary files a/src/modules/powerrename/dll/PowerRenameExt.base.rc and b/src/modules/powerrename/dll/PowerRenameExt.base.rc differ
diff --git a/src/modules/powerrename/dll/PowerRenameExt.cpp b/src/modules/powerrename/dll/PowerRenameExt.cpp
index 1b611bdb2f..099b6a3615 100644
--- a/src/modules/powerrename/dll/PowerRenameExt.cpp
+++ b/src/modules/powerrename/dll/PowerRenameExt.cpp
@@ -1,8 +1,5 @@
#include "pch.h"
#include "PowerRenameExt.h"
-#include
-#include
-#include
#include
#include
#include
@@ -11,6 +8,7 @@
#include
#include
+#include
extern HINSTANCE g_hInst;
@@ -116,6 +114,11 @@ HRESULT CPowerRenameMenu::QueryContextMenu(HMENU hMenu, UINT index, UINT uIDFirs
}
HRESULT CPowerRenameMenu::InvokeCommand(_In_ LPCMINVOKECOMMANDINFO pici)
+{
+ return RunPowerRename(pici, nullptr);
+}
+
+HRESULT CPowerRenameMenu::RunPowerRename(CMINVOKECOMMANDINFO* pici, IShellItemArray* psiItemArray)
{
HRESULT hr = E_FAIL;
@@ -124,84 +127,119 @@ HRESULT CPowerRenameMenu::InvokeCommand(_In_ LPCMINVOKECOMMANDINFO pici)
(LOWORD(pici->lpVerb) == 0))
{
Trace::Invoked();
- InvokeStruct* pInvokeData = new (std::nothrow) InvokeStruct;
- hr = E_OUTOFMEMORY;
- if (pInvokeData)
+ // Set the application path based on the location of the dll
+ std::wstring path = get_module_folderpath(g_hInst);
+ path = path + L"\\PowerRename.exe";
+ LPTSTR lpApplicationName = (LPTSTR)path.c_str();
+ // Create an anonymous pipe to stream filenames
+ SECURITY_ATTRIBUTES sa;
+ HANDLE hReadPipe;
+ HANDLE hWritePipe;
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+ if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0))
{
- pInvokeData->hwndParent = pici->hwnd;
- hr = CoMarshalInterThreadInterfaceInStream(__uuidof(m_spdo), m_spdo, &(pInvokeData->pstrm));
- if (SUCCEEDED(hr))
- {
- hr = SHCreateThread(s_PowerRenameUIThreadProc, pInvokeData, CTF_COINIT | CTF_PROCESS_REF, nullptr) ? S_OK : E_FAIL;
- if (FAILED(hr))
- {
- pInvokeData->pstrm->Release(); // if we failed to create the thread, then we must release the stream
- }
- }
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ return hr;
+ }
+ if (!SetHandleInformation(hWritePipe, HANDLE_FLAG_INHERIT, 0))
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ return hr;
+ }
+ CAtlFile writePipe(hWritePipe);
- if (FAILED(hr))
+ CString commandLine;
+ commandLine.Format(_T("\"%s\""), lpApplicationName);
+
+ 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.hStdInput = hReadPipe;
+ startupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
+ if (pici)
+ {
+ startupInfo.wShowWindow = pici->nShow;
+ }
+ else
+ {
+ 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))
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ return hr;
+ }
+ if (!CloseHandle(processInformation.hThread))
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ return hr;
+ }
+
+ // psiItemArray is NULL if called from InvokeCommand. This part is used for the MSI installer. It is not NULL if it is called from Invoke (MSIX).
+ if (!psiItemArray)
+ {
+ // Stream the input files
+ HDropIterator i(m_spdo);
+ for (i.First(); !i.IsDone(); i.Next())
{
- delete pInvokeData;
+ CString fileName(i.CurrentItem());
+ // File name can't contain '?'
+ fileName.Append(_T("?"));
+
+ writePipe.Write(fileName, fileName.GetLength() * sizeof(TCHAR));
}
}
- Trace::InvokedRet(hr);
+ else
+ {
+ //m_pdtobj will be NULL when invoked from the MSIX build as Initialize is never called (IShellExtInit functions aren't called in case of MSIX).
+ DWORD fileCount = 0;
+ // Gets the list of files currently selected using the IShellItemArray
+ psiItemArray->GetCount(&fileCount);
+ // Iterate over the list of files
+ for (DWORD i = 0; i < fileCount; i++)
+ {
+ IShellItem* shellItem;
+ psiItemArray->GetItemAt(i, &shellItem);
+ LPWSTR itemName;
+ // Retrieves the entire file system path of the file from its shell item
+ shellItem->GetDisplayName(SIGDN_FILESYSPATH, &itemName);
+ CString fileName(itemName);
+ // File name can't contain '?'
+ fileName.Append(_T("?"));
+ // Write the file path into the input stream for image resizer
+ writePipe.Write(fileName, fileName.GetLength() * sizeof(TCHAR));
+ }
+ }
+
+ writePipe.Close();
}
+ Trace::InvokedRet(hr);
return hr;
}
-DWORD WINAPI CPowerRenameMenu::s_PowerRenameUIThreadProc(_In_ void* pData)
-{
- InvokeStruct* pInvokeData = static_cast(pData);
- CComPtr dataSource;
- HRESULT hr = CoGetInterfaceAndReleaseStream(pInvokeData->pstrm, IID_PPV_ARGS(&dataSource));
- if (SUCCEEDED(hr))
- {
- // Create the rename manager
- CComPtr spsrm;
- hr = CPowerRenameManager::s_CreateInstance(&spsrm);
- if (SUCCEEDED(hr))
- {
- // Create the factory for our items
- CComPtr spsrif;
- hr = CPowerRenameItem::s_CreateInstance(nullptr, IID_PPV_ARGS(&spsrif));
- if (SUCCEEDED(hr))
- {
- // Pass the factory to the manager
- hr = spsrm->PutRenameItemFactory(spsrif);
- if (SUCCEEDED(hr))
- {
- // Create the rename UI instance and pass the rename manager
- CComPtr spsrui;
- hr = CPowerRenameUI::s_CreateInstance(spsrm, dataSource, false, &spsrui);
-
- if (SUCCEEDED(hr))
- {
- IDataObject* dummy;
- // If we're running on a local COM server, we need to decrement module refcount, which was previously incremented in CPowerRenameMenu::Invoke.
- if (SUCCEEDED(dataSource->QueryInterface(IID_IShellItemArray, reinterpret_cast(&dummy))))
- {
- ModuleRelease();
- }
- // Call blocks until we are done
- spsrui->Show(pInvokeData->hwndParent);
- spsrui->Close();
- }
- }
- }
-
- // Need to call shutdown to break circular dependencies
- spsrm->Shutdown();
- }
- }
-
- delete pInvokeData;
-
- Trace::UIShownRet(hr);
-
- return 0;
-}
-
HRESULT __stdcall CPowerRenameMenu::GetTitle(IShellItemArray* /*psiItemArray*/, LPWSTR* ppszName)
{
return SHStrDup(app_name.c_str(), ppszName);
@@ -261,7 +299,7 @@ HRESULT __stdcall CPowerRenameMenu::Invoke(IShellItemArray* psiItemArray, IBindC
}
// Prevent Shutting down before PowerRenameUI is created
ModuleAddRef();
- hr = SHCreateThread(s_PowerRenameUIThreadProc, pInvokeData, CTF_COINIT | CTF_PROCESS_REF, nullptr) ? S_OK : E_FAIL;
+ hr = RunPowerRename(nullptr, psiItemArray);
}
Trace::InvokedRet(hr);
return S_OK;
diff --git a/src/modules/powerrename/dll/PowerRenameExt.h b/src/modules/powerrename/dll/PowerRenameExt.h
index ab575ab1d4..c67c6d5610 100644
--- a/src/modules/powerrename/dll/PowerRenameExt.h
+++ b/src/modules/powerrename/dll/PowerRenameExt.h
@@ -49,6 +49,8 @@ public:
return E_NOTIMPL;
}
+ HRESULT RunPowerRename(CMINVOKECOMMANDINFO* pici, IShellItemArray* psiItemArray);
+
// Inherited via IExplorerCommand
virtual HRESULT __stdcall GetTitle(IShellItemArray* psiItemArray, LPWSTR* ppszName) override;
virtual HRESULT __stdcall GetIcon(IShellItemArray* psiItemArray, LPWSTR* ppszIcon) override;
@@ -60,7 +62,6 @@ public:
virtual HRESULT __stdcall EnumSubCommands(IEnumExplorerCommand** ppEnum) override;
static HRESULT s_CreateInstance(_In_opt_ IUnknown* punkOuter, _In_ REFIID riid, _Outptr_ void** ppv);
- static DWORD WINAPI s_PowerRenameUIThreadProc(_In_ void* pData);
static bool SetEnabled(_In_ bool enabled);
static bool IsEnabled();
diff --git a/src/modules/powerrename/dll/PowerRenameExt.vcxproj b/src/modules/powerrename/dll/PowerRenameExt.vcxproj
index acd801ab0f..9005dbeee9 100644
--- a/src/modules/powerrename/dll/PowerRenameExt.vcxproj
+++ b/src/modules/powerrename/dll/PowerRenameExt.vcxproj
@@ -21,7 +21,7 @@
..\lib\;..\ui\;..\;..\..\..\;..\..\..\common\telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories)
- Pathcch.lib;comctl32.lib;$(SolutionDir)$(Platform)\$(Configuration)\obj\PowerRenameUI\PowerRenameUI.res;shcore.lib;%(AdditionalDependencies)
+ Pathcch.lib;comctl32.lib;shcore.lib;%(AdditionalDependencies)
PowerRenameExt.def
gdi32.dll;shell32.dll;ole32.dll;shlwapi.dll;oleaut32.dll;%(DelayLoadDLLs)
@@ -60,9 +60,6 @@
{51920f1f-c28c-4adf-8660-4238766796c2}
-
- {0e072714-d127-460b-afad-b4c40b412798}
-
diff --git a/src/modules/powerrename/dll/pch.h b/src/modules/powerrename/dll/pch.h
index f3036a15a2..27ab94b42f 100644
--- a/src/modules/powerrename/dll/pch.h
+++ b/src/modules/powerrename/dll/pch.h
@@ -10,6 +10,9 @@
#include
#include
#include
+#include
+#include
+#include
#include
#include
#include "CLSID.h"
diff --git a/src/modules/powerrename/lib/Helpers.cpp b/src/modules/powerrename/lib/Helpers.cpp
index 47dafe0d82..090bc583e7 100644
--- a/src/modules/powerrename/lib/Helpers.cpp
+++ b/src/modules/powerrename/lib/Helpers.cpp
@@ -7,6 +7,13 @@
namespace fs = std::filesystem;
+namespace
+{
+ const int MAX_INPUT_STRING_LEN = 1024;
+
+ const wchar_t c_rootRegPath[] = L"Software\\Microsoft\\PowerRename";
+}
+
HRESULT GetTrimmedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source)
{
HRESULT hr = E_INVALIDARG;
@@ -545,3 +552,56 @@ HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p
return hwnd;
}
+
+std::wstring GetRegString(const std::wstring& valueName, const std::wstring& subPath)
+{
+ wchar_t value[MAX_INPUT_STRING_LEN];
+ value[0] = L'\0';
+ DWORD type = REG_SZ;
+ DWORD size = MAX_INPUT_STRING_LEN * sizeof(wchar_t);
+ std::wstring completePath = std::wstring(c_rootRegPath) + subPath;
+ if (SHGetValue(HKEY_CURRENT_USER, completePath.c_str(), valueName.c_str(), &type, value, &size) == ERROR_SUCCESS)
+ {
+ return std::wstring(value);
+ }
+ return std::wstring{};
+}
+
+unsigned int GetRegNumber(const std::wstring& valueName, unsigned int defaultValue)
+{
+ DWORD type = REG_DWORD;
+ DWORD data = 0;
+ DWORD size = sizeof(DWORD);
+ if (SHGetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName.c_str(), &type, &data, &size) == ERROR_SUCCESS)
+ {
+ return data;
+ }
+ return defaultValue;
+}
+
+void SetRegNumber(const std::wstring& valueName, unsigned int value)
+{
+ SHSetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName.c_str(), REG_DWORD, &value, sizeof(value));
+}
+
+bool GetRegBoolean(const std::wstring& valueName, bool defaultValue)
+{
+ DWORD value = GetRegNumber(valueName.c_str(), defaultValue ? 1 : 0);
+ return (value == 0) ? false : true;
+}
+
+void SetRegBoolean(const std::wstring& valueName, bool value)
+{
+ SetRegNumber(valueName, value ? 1 : 0);
+}
+
+bool LastModifiedTime(const std::wstring& filePath, FILETIME* lpFileTime)
+{
+ WIN32_FILE_ATTRIBUTE_DATA attr{};
+ if (GetFileAttributesExW(filePath.c_str(), GetFileExInfoStandard, &attr))
+ {
+ *lpFileTime = attr.ftLastWriteTime;
+ return true;
+ }
+ return false;
+}
diff --git a/src/modules/powerrename/lib/Helpers.h b/src/modules/powerrename/lib/Helpers.h
index a803ba15f5..18705a45c1 100644
--- a/src/modules/powerrename/lib/Helpers.h
+++ b/src/modules/powerrename/lib/Helpers.h
@@ -1,6 +1,8 @@
#pragma once
-#include
+#include "PowerRenameInterfaces.h"
+
+#include
HRESULT GetTrimmedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source);
HRESULT GetTransformedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, DWORD flags);
@@ -16,3 +18,10 @@ BOOL GetEnumeratedFileName(
unsigned long ulMinLong,
__inout unsigned long* pulNumUsed);
HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p);
+
+std::wstring GetRegString(const std::wstring& valueName, const std::wstring& subPath);
+unsigned int GetRegNumber(const std::wstring& valueName, unsigned int defaultValue);
+void SetRegNumber(const std::wstring& valueName, unsigned int value);
+bool GetRegBoolean(const std::wstring& valueName, bool defaultValue);
+void SetRegBoolean(const std::wstring& valueName, bool value);
+bool LastModifiedTime(const std::wstring& filePath, FILETIME* lpFileTime);
diff --git a/src/modules/powerrename/lib/MRUListHandler.cpp b/src/modules/powerrename/lib/MRUListHandler.cpp
new file mode 100644
index 0000000000..0465013a6e
--- /dev/null
+++ b/src/modules/powerrename/lib/MRUListHandler.cpp
@@ -0,0 +1,181 @@
+#include "pch.h"
+#include "MRUListHandler.h"
+#include "Helpers.h"
+
+#include
+#include
+
+namespace
+{
+ const wchar_t c_mruList[] = L"MRUList";
+ const wchar_t c_insertionIdx[] = L"InsertionIdx";
+ const wchar_t c_maxMRUSize[] = L"MaxMRUSize";
+}
+
+MRUListHandler::MRUListHandler(unsigned int size, const std::wstring& filePath, const std::wstring& regPath) :
+ pushIdx(0),
+ nextIdx(1),
+ size(size),
+ jsonFilePath(PTSettingsHelper::get_module_save_folder_location(PowerRenameConstants::ModuleKey) + filePath),
+ registryFilePath(regPath)
+{
+ items.resize(size);
+ Load();
+}
+
+void MRUListHandler::Push(const std::wstring& data)
+{
+ if (Exists(data))
+ {
+ // TODO: Already existing item should be put on top of MRU list.
+ return;
+ }
+ items[pushIdx] = data;
+ pushIdx = (pushIdx + 1) % size;
+ Save();
+}
+
+bool MRUListHandler::Next(std::wstring& data)
+{
+ if (nextIdx == size + 1)
+ {
+ Reset();
+ return false;
+ }
+ // Go backwards to consume latest items first.
+ unsigned int idx = (pushIdx + size - nextIdx) % size;
+ if (items[idx].empty())
+ {
+ Reset();
+ return false;
+ }
+ data = items[idx];
+ ++nextIdx;
+ return true;
+}
+
+void MRUListHandler::Reset()
+{
+ nextIdx = 1;
+}
+
+const std::vector& MRUListHandler::GetItems()
+{
+ return items;
+}
+
+void MRUListHandler::Load()
+{
+ if (!std::filesystem::exists(jsonFilePath))
+ {
+ MigrateFromRegistry();
+
+ Save();
+ }
+ else
+ {
+ ParseJson();
+ }
+}
+
+void MRUListHandler::Save()
+{
+ json::JsonObject jsonData;
+
+ jsonData.SetNamedValue(c_maxMRUSize, json::value(size));
+ jsonData.SetNamedValue(c_insertionIdx, json::value(pushIdx));
+ jsonData.SetNamedValue(c_mruList, Serialize());
+
+ json::to_file(jsonFilePath, jsonData);
+}
+
+json::JsonArray MRUListHandler::Serialize()
+{
+ json::JsonArray searchMRU{};
+
+ std::wstring data{};
+ for (const std::wstring& item : items)
+ {
+ searchMRU.Append(json::value(item));
+ }
+
+ return searchMRU;
+}
+
+void MRUListHandler::MigrateFromRegistry()
+{
+ std::wstring searchListKeys = GetRegString(c_mruList, registryFilePath);
+ std::sort(std::begin(searchListKeys), std::end(searchListKeys));
+ for (const wchar_t& key : searchListKeys)
+ {
+ Push(GetRegString(std::wstring(1, key), registryFilePath));
+ }
+}
+
+void MRUListHandler::ParseJson()
+{
+ auto json = json::from_file(jsonFilePath);
+ if (json)
+ {
+ const json::JsonObject& jsonObject = json.value();
+ try
+ {
+ unsigned int oldSize{ size };
+ if (json::has(jsonObject, c_maxMRUSize, json::JsonValueType::Number))
+ {
+ oldSize = (unsigned int)jsonObject.GetNamedNumber(c_maxMRUSize);
+ }
+ unsigned int oldPushIdx{ 0 };
+ if (json::has(jsonObject, c_insertionIdx, json::JsonValueType::Number))
+ {
+ oldPushIdx = (unsigned int)jsonObject.GetNamedNumber(c_insertionIdx);
+ if (oldPushIdx < 0 || oldPushIdx >= oldSize)
+ {
+ oldPushIdx = 0;
+ }
+ }
+ if (json::has(jsonObject, c_mruList, json::JsonValueType::Array))
+ {
+ auto jsonArray = jsonObject.GetNamedArray(c_mruList);
+ if (oldSize == size)
+ {
+ for (uint32_t i = 0; i < jsonArray.Size(); ++i)
+ {
+ items[i] = std::wstring(jsonArray.GetStringAt(i));
+ }
+ pushIdx = oldPushIdx;
+ }
+ else
+ {
+ std::vector temp;
+ for (unsigned int i = 0; i < min(jsonArray.Size(), size); ++i)
+ {
+ int idx = (oldPushIdx + oldSize - (i + 1)) % oldSize;
+ temp.push_back(std::wstring(jsonArray.GetStringAt(idx)));
+ }
+ if (size > oldSize)
+ {
+ std::reverse(std::begin(temp), std::end(temp));
+ pushIdx = (unsigned int)temp.size();
+ temp.resize(size);
+ }
+ else
+ {
+ temp.resize(size);
+ std::reverse(std::begin(temp), std::end(temp));
+ }
+ items = std::move(temp);
+ Save();
+ }
+ }
+ }
+ catch (const winrt::hresult_error&)
+ {
+ }
+ }
+}
+
+bool MRUListHandler::Exists(const std::wstring& data)
+{
+ return std::find(std::begin(items), std::end(items), data) != std::end(items);
+}
diff --git a/src/modules/powerrename/lib/MRUListHandler.h b/src/modules/powerrename/lib/MRUListHandler.h
new file mode 100644
index 0000000000..62446172a0
--- /dev/null
+++ b/src/modules/powerrename/lib/MRUListHandler.h
@@ -0,0 +1,35 @@
+#pragma once
+#include "pch.h"
+
+#include
+#include
+
+#include
+
+class MRUListHandler
+{
+public:
+ MRUListHandler(unsigned int size, const std::wstring& filePath, const std::wstring& regPath);
+
+ void Push(const std::wstring& data);
+ bool Next(std::wstring& data);
+
+ void Reset();
+
+ const std::vector& GetItems();
+private:
+ void Load();
+ void Save();
+ void MigrateFromRegistry();
+ json::JsonArray Serialize();
+ void ParseJson();
+
+ bool Exists(const std::wstring& data);
+
+ std::vector items;
+ unsigned int pushIdx;
+ unsigned int nextIdx;
+ unsigned int size;
+ const std::wstring jsonFilePath;
+ const std::wstring registryFilePath;
+};
diff --git a/src/modules/powerrename/lib/PowerRenameEnum.cpp b/src/modules/powerrename/lib/PowerRenameEnum.cpp
index 94b575226a..02edebdca2 100644
--- a/src/modules/powerrename/lib/PowerRenameEnum.cpp
+++ b/src/modules/powerrename/lib/PowerRenameEnum.cpp
@@ -28,20 +28,10 @@ IFACEMETHODIMP CPowerRenameEnum::QueryInterface(_In_ REFIID riid, _Outptr_ void*
return QISearch(this, qit, riid, ppv);
}
-IFACEMETHODIMP CPowerRenameEnum::Start()
+IFACEMETHODIMP CPowerRenameEnum::Start(_In_ IEnumShellItems* enumShellItems)
{
m_canceled = false;
- CComPtr spsia;
- HRESULT hr = GetShellItemArrayFromDataObject(m_spdo, &spsia);
- if (SUCCEEDED(hr))
- {
- CComPtr spesi;
- hr = spsia->EnumItems(&spesi);
- if (SUCCEEDED(hr))
- {
- hr = _ParseEnumItems(spesi);
- }
- }
+ HRESULT hr = _ParseEnumItems(enumShellItems);
return hr;
}
diff --git a/src/modules/powerrename/lib/PowerRenameEnum.h b/src/modules/powerrename/lib/PowerRenameEnum.h
index c2923bc587..75e22d4cf4 100644
--- a/src/modules/powerrename/lib/PowerRenameEnum.h
+++ b/src/modules/powerrename/lib/PowerRenameEnum.h
@@ -14,7 +14,7 @@ public:
IFACEMETHODIMP_(ULONG) Release();
// ISmartRenameEnum
- IFACEMETHODIMP Start();
+ IFACEMETHODIMP Start(_In_ IEnumShellItems* enumShellItems);
IFACEMETHODIMP Cancel();
public:
diff --git a/src/modules/powerrename/lib/PowerRenameInterfaces.h b/src/modules/powerrename/lib/PowerRenameInterfaces.h
index c9a03b23c6..107be6bc86 100644
--- a/src/modules/powerrename/lib/PowerRenameInterfaces.h
+++ b/src/modules/powerrename/lib/PowerRenameInterfaces.h
@@ -1,5 +1,7 @@
#pragma once
#include "pch.h"
+#include
+#include
enum PowerRenameFlags
{
@@ -41,9 +43,9 @@ public:
IFACEMETHOD(Advise)(_In_ IPowerRenameRegExEvents* regExEvents, _Out_ DWORD* cookie) = 0;
IFACEMETHOD(UnAdvise)(_In_ DWORD cookie) = 0;
IFACEMETHOD(GetSearchTerm)(_Outptr_ PWSTR* searchTerm) = 0;
- IFACEMETHOD(PutSearchTerm)(_In_ PCWSTR searchTerm) = 0;
+ IFACEMETHOD(PutSearchTerm)(_In_ PCWSTR searchTerm, bool forceRenaming = false) = 0;
IFACEMETHOD(GetReplaceTerm)(_Outptr_ PWSTR* replaceTerm) = 0;
- IFACEMETHOD(PutReplaceTerm)(_In_ PCWSTR replaceTerm) = 0;
+ IFACEMETHOD(PutReplaceTerm)(_In_ PCWSTR replaceTerm, bool forceRenaming = false) = 0;
IFACEMETHOD(GetFlags)(_Out_ DWORD* flags) = 0;
IFACEMETHOD(PutFlags)(_In_ DWORD flags) = 0;
IFACEMETHOD(PutFileTime)(_In_ SYSTEMTIME fileTime) = 0;
@@ -54,11 +56,13 @@ public:
interface __declspec(uuid("C7F59201-4DE1-4855-A3A2-26FC3279C8A5")) IPowerRenameItem : public IUnknown
{
public:
- IFACEMETHOD(GetPath)(_Outptr_ PWSTR* path) = 0;
+ IFACEMETHOD(PutPath)(_In_opt_ PCWSTR newPath) = 0;
+ IFACEMETHOD(GetPath)(_Outptr_ PWSTR * path) = 0;
IFACEMETHOD(GetTime)(_Outptr_ SYSTEMTIME* time) = 0;
IFACEMETHOD(GetShellItem)(_Outptr_ IShellItem** ppsi) = 0;
- IFACEMETHOD(GetOriginalName)(_Outptr_ PWSTR* originalName) = 0;
- IFACEMETHOD(GetNewName)(_Outptr_ PWSTR* newName) = 0;
+ IFACEMETHOD(GetOriginalName)(_Outptr_ PWSTR * originalName) = 0;
+ IFACEMETHOD(PutOriginalName)(_In_opt_ PCWSTR originalName) = 0;
+ IFACEMETHOD(GetNewName)(_Outptr_ PWSTR * newName) = 0;
IFACEMETHOD(PutNewName)(_In_opt_ PCWSTR newName) = 0;
IFACEMETHOD(GetIsFolder)(_Out_ bool* isFolder) = 0;
IFACEMETHOD(GetIsSubFolderContent)(_Out_ bool* isSubFolderContent) = 0;
@@ -83,13 +87,14 @@ interface __declspec(uuid("87FC43F9-7634-43D9-99A5-20876AFCE4AD")) IPowerRenameM
{
public:
IFACEMETHOD(OnItemAdded)(_In_ IPowerRenameItem* renameItem) = 0;
- IFACEMETHOD(OnUpdate)(_In_ IPowerRenameItem* renameItem) = 0;
- IFACEMETHOD(OnError)(_In_ IPowerRenameItem* renameItem) = 0;
+ IFACEMETHOD(OnUpdate)(_In_ IPowerRenameItem * renameItem) = 0;
+ IFACEMETHOD(OnRename)(_In_ IPowerRenameItem * renameItem) = 0;
+ IFACEMETHOD(OnError)(_In_ IPowerRenameItem * renameItem) = 0;
IFACEMETHOD(OnRegExStarted)(_In_ DWORD threadId) = 0;
IFACEMETHOD(OnRegExCanceled)(_In_ DWORD threadId) = 0;
IFACEMETHOD(OnRegExCompleted)(_In_ DWORD threadId) = 0;
IFACEMETHOD(OnRenameStarted)() = 0;
- IFACEMETHOD(OnRenameCompleted)() = 0;
+ IFACEMETHOD(OnRenameCompleted)(_In_ bool closeUIWindowAfterRenaming) = 0;
};
interface __declspec(uuid("001BBD88-53D2-4FA6-95D2-F9A9FA4F9F70")) IPowerRenameManager : public IUnknown
@@ -101,8 +106,10 @@ public:
IFACEMETHOD(Stop)() = 0;
IFACEMETHOD(Reset)() = 0;
IFACEMETHOD(Shutdown)() = 0;
- IFACEMETHOD(Rename)(_In_ HWND hwndParent) = 0;
- IFACEMETHOD(AddItem)(_In_ IPowerRenameItem* pItem) = 0;
+ IFACEMETHOD(Rename)(_In_ HWND hwndParent, _In_ bool closeWindow) = 0;
+ IFACEMETHOD(UpdateChildrenPath)(_In_ int parentId, _In_ size_t oldParentPathSize) = 0;
+ IFACEMETHOD(GetCloseUIWindowAfterRenaming)(_Out_ bool* closeUIWindowAfterRenaming) = 0;
+ IFACEMETHOD(AddItem)(_In_ IPowerRenameItem * pItem) = 0;
IFACEMETHOD(GetItemByIndex)(_In_ UINT index, _COM_Outptr_ IPowerRenameItem** ppItem) = 0;
IFACEMETHOD(GetVisibleItemByIndex)(_In_ UINT index, _COM_Outptr_ IPowerRenameItem ** ppItem) = 0;
IFACEMETHOD(SetVisible)() = 0;
@@ -113,7 +120,7 @@ public:
IFACEMETHOD(GetRenameItemCount)(_Out_ UINT* count) = 0;
IFACEMETHOD(GetFlags)(_Out_ DWORD* flags) = 0;
IFACEMETHOD(PutFlags)(_In_ DWORD flags) = 0;
- IFACEMETHOD(GetFilter)(_Out_ DWORD * filter) = 0;
+ IFACEMETHOD(GetFilter)(_Out_ DWORD* filter) = 0;
IFACEMETHOD(SwitchFilter)(_In_ int columnNumber) = 0;
IFACEMETHOD(GetRenameRegEx)(_COM_Outptr_ IPowerRenameRegEx** ppRegEx) = 0;
IFACEMETHOD(PutRenameRegEx)(_In_ IPowerRenameRegEx* pRegEx) = 0;
@@ -121,23 +128,17 @@ public:
IFACEMETHOD(PutRenameItemFactory)(_In_ IPowerRenameItemFactory* pItemFactory) = 0;
};
-interface __declspec(uuid("E6679DEB-460D-42C1-A7A8-E25897061C99")) IPowerRenameUI : public IUnknown
-{
-public:
- IFACEMETHOD(Show)(_In_opt_ HWND hwndParent) = 0;
- IFACEMETHOD(Close)() = 0;
- IFACEMETHOD(Update)() = 0;
-};
-
interface __declspec(uuid("04AAFABE-B76E-4E13-993A-B5941F52B139")) IPowerRenameMRU : public IUnknown
{
public:
IFACEMETHOD(AddMRUString)(_In_ PCWSTR entry) = 0;
+ IFACEMETHOD_(const std::vector&, GetMRUStrings)() = 0;
};
interface __declspec(uuid("CE8C8616-C1A8-457A-9601-10570F5B9F1F")) IPowerRenameEnum : public IUnknown
{
public:
- IFACEMETHOD(Start)() = 0;
+ IFACEMETHOD(Start)
+ (_In_ IEnumShellItems * enumShellItems) = 0;
IFACEMETHOD(Cancel)() = 0;
};
diff --git a/src/modules/powerrename/lib/PowerRenameItem.cpp b/src/modules/powerrename/lib/PowerRenameItem.cpp
index 454a73b7d6..8106cab873 100644
--- a/src/modules/powerrename/lib/PowerRenameItem.cpp
+++ b/src/modules/powerrename/lib/PowerRenameItem.cpp
@@ -32,6 +32,19 @@ IFACEMETHODIMP CPowerRenameItem::QueryInterface(_In_ REFIID riid, _Outptr_ void*
return QISearch(this, qit, riid, ppv);
}
+IFACEMETHODIMP CPowerRenameItem::PutPath(_In_opt_ PCWSTR newPath)
+{
+ CSRWSharedAutoLock lock(&m_lock);
+ CoTaskMemFree(m_path);
+ m_path = nullptr;
+ HRESULT hr = S_OK;
+ if (newPath != nullptr)
+ {
+ hr = SHStrDup(newPath, &m_path);
+ }
+ return hr;
+}
+
IFACEMETHODIMP CPowerRenameItem::GetPath(_Outptr_ PWSTR* path)
{
*path = nullptr;
@@ -47,7 +60,7 @@ IFACEMETHODIMP CPowerRenameItem::GetPath(_Outptr_ PWSTR* path)
IFACEMETHODIMP CPowerRenameItem::GetTime(_Outptr_ SYSTEMTIME* time)
{
CSRWSharedAutoLock lock(&m_lock);
- HRESULT hr = E_FAIL ;
+ HRESULT hr = E_FAIL;
if (m_isTimeParsed)
{
@@ -84,6 +97,19 @@ IFACEMETHODIMP CPowerRenameItem::GetShellItem(_Outptr_ IShellItem** ppsi)
return SHCreateItemFromParsingName(m_path, nullptr, IID_PPV_ARGS(ppsi));
}
+IFACEMETHODIMP CPowerRenameItem::PutOriginalName(_In_opt_ PCWSTR originalName)
+{
+ CSRWSharedAutoLock lock(&m_lock);
+ CoTaskMemFree(m_originalName);
+ m_originalName = nullptr;
+ HRESULT hr = S_OK;
+ if (originalName != nullptr)
+ {
+ hr = SHStrDup(originalName, &m_originalName);
+ }
+ return hr;
+}
+
IFACEMETHODIMP CPowerRenameItem::GetOriginalName(_Outptr_ PWSTR* originalName)
{
CSRWSharedAutoLock lock(&m_lock);
@@ -223,11 +249,11 @@ HRESULT CPowerRenameItem::s_CreateInstance(_In_opt_ IShellItem* psi, _In_ REFIID
{
*resultInterface = nullptr;
- CPowerRenameItem *newRenameItem = new CPowerRenameItem();
+ CPowerRenameItem* newRenameItem = new CPowerRenameItem();
HRESULT hr = E_OUTOFMEMORY;
if (newRenameItem)
{
- hr = S_OK ;
+ hr = S_OK;
if (psi != nullptr)
{
hr = newRenameItem->_Init(psi);
diff --git a/src/modules/powerrename/lib/PowerRenameItem.h b/src/modules/powerrename/lib/PowerRenameItem.h
index cb5ad8cc37..10610306ed 100644
--- a/src/modules/powerrename/lib/PowerRenameItem.h
+++ b/src/modules/powerrename/lib/PowerRenameItem.h
@@ -14,9 +14,11 @@ public:
IFACEMETHODIMP_(ULONG) Release();
// IPowerRenameItem
+ IFACEMETHODIMP PutPath(_In_opt_ PCWSTR newPath);
IFACEMETHODIMP GetPath(_Outptr_ PWSTR* path);
IFACEMETHODIMP GetTime(_Outptr_ SYSTEMTIME* time);
IFACEMETHODIMP GetShellItem(_Outptr_ IShellItem** ppsi);
+ IFACEMETHODIMP PutOriginalName(_In_opt_ PCWSTR originalName);
IFACEMETHODIMP GetOriginalName(_Outptr_ PWSTR* originalName);
IFACEMETHODIMP PutNewName(_In_opt_ PCWSTR newName);
IFACEMETHODIMP GetNewName(_Outptr_ PWSTR* newName);
diff --git a/src/modules/powerrename/lib/PowerRenameLib.vcxproj b/src/modules/powerrename/lib/PowerRenameLib.vcxproj
index 1372c57ba1..ea51a2d18c 100644
--- a/src/modules/powerrename/lib/PowerRenameLib.vcxproj
+++ b/src/modules/powerrename/lib/PowerRenameLib.vcxproj
@@ -40,10 +40,12 @@
+
+
@@ -53,9 +55,11 @@
+
+
@@ -70,6 +74,9 @@
{6955446d-23f7-4023-9bb3-8657f904af99}
+
+ {98537082-0fdb-40de-abd8-0dc5a4269bab}
+
diff --git a/src/modules/powerrename/lib/PowerRenameMRU.cpp b/src/modules/powerrename/lib/PowerRenameMRU.cpp
new file mode 100644
index 0000000000..1c29d19959
--- /dev/null
+++ b/src/modules/powerrename/lib/PowerRenameMRU.cpp
@@ -0,0 +1,87 @@
+#include "pch.h"
+#include "PowerRenameMRU.h"
+
+#include "Settings.h"
+
+namespace
+{
+ const wchar_t c_searchMRUListFilePath[] = L"\\search-mru.json";
+ const wchar_t c_replaceMRUListFilePath[] = L"\\replace-mru.json";
+ const wchar_t c_mruSearchRegPath[] = L"\\SearchMRU";
+ const wchar_t c_mruReplaceRegPath[] = L"\\ReplaceMRU";
+}
+
+CPowerRenameMRU::CPowerRenameMRU(int size, const std::wstring& filePath, const std::wstring& regPath) :
+ refCount(1)
+{
+ mruList = std::make_unique(size, filePath, regPath);
+}
+
+HRESULT CPowerRenameMRU::CreateInstance(_In_ const std::wstring& filePath, _In_ const std::wstring& regPath, _In_ REFIID iid, _Outptr_ void** resultInterface)
+{
+ *resultInterface = nullptr;
+ unsigned int maxMRUSize = CSettingsInstance().GetMaxMRUSize();
+ HRESULT hr = E_FAIL;
+ if (maxMRUSize > 0)
+ {
+ CPowerRenameMRU* renameMRU = new CPowerRenameMRU(maxMRUSize, filePath, regPath);
+ hr = E_OUTOFMEMORY;
+ if (renameMRU)
+ {
+ renameMRU->QueryInterface(iid, resultInterface);
+ renameMRU->Release();
+ hr = S_OK;
+ }
+ }
+
+ return hr;
+}
+
+IFACEMETHODIMP_(ULONG)
+CPowerRenameMRU::AddRef()
+{
+ return InterlockedIncrement(&refCount);
+}
+
+IFACEMETHODIMP_(ULONG)
+CPowerRenameMRU::Release()
+{
+ unsigned int cnt = InterlockedDecrement(&refCount);
+
+ if (cnt == 0)
+ {
+ delete this;
+ }
+ return cnt;
+}
+
+IFACEMETHODIMP CPowerRenameMRU::QueryInterface(_In_ REFIID riid, _Outptr_ void** ppv)
+{
+ static const QITAB qit[] = {
+ QITABENT(CPowerRenameMRU, IEnumString),
+ QITABENT(CPowerRenameMRU, IPowerRenameMRU),
+ { 0 }
+ };
+ return QISearch(this, qit, riid, ppv);
+}
+
+IFACEMETHODIMP_(const std::vector&) CPowerRenameMRU::GetMRUStrings()
+{
+ return mruList->GetItems();
+}
+
+IFACEMETHODIMP CPowerRenameMRU::AddMRUString(_In_ PCWSTR entry)
+{
+ mruList->Push(entry);
+ return S_OK;
+}
+
+HRESULT CPowerRenameMRU::CPowerRenameMRUSearch_CreateInstance(_Outptr_ IPowerRenameMRU** ppUnk)
+{
+ return CPowerRenameMRU::CreateInstance(c_searchMRUListFilePath, c_mruSearchRegPath, IID_PPV_ARGS(ppUnk));
+}
+
+HRESULT CPowerRenameMRU::CPowerRenameMRUReplace_CreateInstance(_Outptr_ IPowerRenameMRU** ppUnk)
+{
+ return CPowerRenameMRU::CreateInstance(c_replaceMRUListFilePath, c_mruReplaceRegPath, IID_PPV_ARGS(ppUnk));
+}
diff --git a/src/modules/powerrename/lib/PowerRenameMRU.h b/src/modules/powerrename/lib/PowerRenameMRU.h
new file mode 100644
index 0000000000..dd4b150d1b
--- /dev/null
+++ b/src/modules/powerrename/lib/PowerRenameMRU.h
@@ -0,0 +1,34 @@
+#pragma once
+#include "pch.h"
+#include "PowerRenameInterfaces.h"
+#include "MRUListHandler.h"
+
+#include
+#include
+#include
+
+class CPowerRenameMRU :
+ public IPowerRenameMRU
+{
+public:
+ // IUnknown
+ IFACEMETHODIMP_(ULONG)
+ AddRef();
+ IFACEMETHODIMP_(ULONG)
+ Release();
+ IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _Outptr_ void** ppv);
+
+ // IPowerRenameMRU
+ IFACEMETHODIMP AddMRUString(_In_ PCWSTR entry);
+ IFACEMETHODIMP_(const std::vector&) GetMRUStrings();
+
+ static HRESULT CPowerRenameMRUSearch_CreateInstance(_Outptr_ IPowerRenameMRU** ppUnk);
+ static HRESULT CPowerRenameMRUReplace_CreateInstance(_Outptr_ IPowerRenameMRU** ppUnk);
+
+private:
+ static HRESULT CreateInstance(_In_ const std::wstring& filePath, _In_ const std::wstring& regPath, _In_ REFIID iid, _Outptr_ void** resultInterface);
+ CPowerRenameMRU(int size, const std::wstring& filePath, const std::wstring& regPath);
+
+ std::unique_ptr mruList;
+ unsigned int refCount = 0;
+};
diff --git a/src/modules/powerrename/lib/PowerRenameManager.cpp b/src/modules/powerrename/lib/PowerRenameManager.cpp
index c48f70c62d..1ec6de2c4a 100644
--- a/src/modules/powerrename/lib/PowerRenameManager.cpp
+++ b/src/modules/powerrename/lib/PowerRenameManager.cpp
@@ -11,7 +11,7 @@
namespace fs = std::filesystem;
-extern HINSTANCE g_hInst;
+extern HINSTANCE g_hostHInst;
// The default FOF flags to use in the rename operations
#define FOF_DEFAULTFLAGS (FOF_ALLOWUNDO | FOFX_ADDUNDORECORD | FOFX_SHOWELEVATIONPROMPT | FOF_RENAMEONCOLLISION)
@@ -92,12 +92,56 @@ IFACEMETHODIMP CPowerRenameManager::Stop()
return E_NOTIMPL;
}
-IFACEMETHODIMP CPowerRenameManager::Rename(_In_ HWND hwndParent)
+IFACEMETHODIMP CPowerRenameManager::Rename(_In_ HWND hwndParent, bool closeWindow)
{
m_hwndParent = hwndParent;
+ m_closeUIWindowAfterRenaming = closeWindow;
return _PerformFileOperation();
}
+IFACEMETHODIMP CPowerRenameManager::UpdateChildrenPath(_In_ int parentId, _In_ size_t oldParentPathSize)
+{
+ auto parentIt = m_renameItems.find(parentId);
+ if (parentIt != m_renameItems.end())
+ {
+ UINT depth = 0;
+ winrt::check_hresult(parentIt->second->GetDepth(&depth));
+
+ PWSTR renamedPath = nullptr;
+ winrt::check_hresult(parentIt->second->GetPath(&renamedPath));
+ std::wstring renamedPathStr{ renamedPath };
+
+ for (auto it = ++parentIt; it != m_renameItems.end(); ++it)
+ {
+ UINT nextDepth = 0;
+ winrt::check_hresult(it->second->GetDepth(&nextDepth));
+
+ if (nextDepth > depth)
+ {
+ // This is child, update path
+ PWSTR path = nullptr;
+ winrt::check_hresult(it->second->GetPath(&path));
+ std::wstring pathStr{ path };
+
+ std::wstring newPath = pathStr.replace(0, oldParentPathSize, renamedPath);
+ it->second->PutPath(newPath.c_str());
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ return S_OK;
+}
+
+IFACEMETHODIMP CPowerRenameManager::GetCloseUIWindowAfterRenaming(_Out_ bool* closeUIWindowAfterRenaming)
+{
+ *closeUIWindowAfterRenaming = m_closeUIWindowAfterRenaming;
+ return S_OK;
+}
+
IFACEMETHODIMP CPowerRenameManager::Reset()
{
// Stop all threads and wait
@@ -338,21 +382,15 @@ IFACEMETHODIMP CPowerRenameManager::GetFilter(_Out_ DWORD* filter)
return S_OK;
}
-IFACEMETHODIMP CPowerRenameManager::SwitchFilter(_In_ int columnNumber)
+IFACEMETHODIMP CPowerRenameManager::SwitchFilter(_In_ int)
{
switch (m_filter)
{
case PowerRenameFilters::None:
- m_filter = (columnNumber == 0) ? PowerRenameFilters::Selected : PowerRenameFilters::ShouldRename;
- break;
- case PowerRenameFilters::Selected:
- m_filter = (columnNumber == 0) ? PowerRenameFilters::FlagsApplicable : PowerRenameFilters::ShouldRename;
- break;
- case PowerRenameFilters::FlagsApplicable:
- m_filter = (columnNumber == 0) ? PowerRenameFilters::None : PowerRenameFilters::ShouldRename;
+ m_filter = PowerRenameFilters::ShouldRename;
break;
case PowerRenameFilters::ShouldRename:
- m_filter = (columnNumber == 0) ? PowerRenameFilters::Selected : PowerRenameFilters::None;
+ m_filter = PowerRenameFilters::None;
break;
}
@@ -458,7 +496,7 @@ HRESULT CPowerRenameManager::_Init()
m_startRegExWorkerEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
m_cancelRegExWorkerEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
- m_hwndMessage = CreateMsgWindow(g_hInst, s_msgWndProc, this);
+ m_hwndMessage = CreateMsgWindow(g_hostHInst, s_msgWndProc, this);
return S_OK;
}
@@ -467,6 +505,7 @@ HRESULT CPowerRenameManager::_Init()
enum
{
SRM_REGEX_ITEM_UPDATED = (WM_APP + 1), // Single rename item processed by regex worker thread
+ SRM_REGEX_ITEM_RENAMED_KEEP_UI, // Single rename item processed by rename worker thread in case UI remains opened
SRM_REGEX_STARTED, // RegEx operation was started
SRM_REGEX_CANCELED, // Regex operation was canceled
SRM_REGEX_COMPLETE, // Regex worker thread completed
@@ -523,6 +562,16 @@ LRESULT CPowerRenameManager::_WndProc(_In_ HWND hwnd, _In_ UINT msg, _In_ WPARAM
}
break;
}
+ case SRM_REGEX_ITEM_RENAMED_KEEP_UI:
+ {
+ int id = static_cast(lParam);
+ CComPtr spItem;
+ if (SUCCEEDED(GetItemById(id, &spItem)))
+ {
+ _OnRename(spItem);
+ }
+ break;
+ }
case SRM_REGEX_STARTED:
_OnRegExStarted(static_cast(wParam));
break;
@@ -681,6 +730,9 @@ DWORD WINAPI CPowerRenameManager::s_fileOpWorkerThread(_In_ void* pv)
WorkerThreadData* pwtd = reinterpret_cast(pv);
if (pwtd)
{
+ bool closeUIWindowAfterRenaming = true;
+ pwtd->spsrm->GetCloseUIWindowAfterRenaming(&closeUIWindowAfterRenaming);
+
// Wait to be told we can begin
if (WaitForSingleObject(pwtd->startEvent, INFINITE) == WAIT_OBJECT_0)
{
@@ -732,6 +784,38 @@ DWORD WINAPI CPowerRenameManager::s_fileOpWorkerThread(_In_ void* pv)
if (SUCCEEDED(spItem->GetShellItem(&spShellItem)))
{
spFileOp->RenameItem(spShellItem, newName, nullptr);
+ if (!closeUIWindowAfterRenaming)
+ {
+ // Update item data
+ PWSTR originalName = nullptr;
+ winrt::check_hresult(spItem->GetOriginalName(&originalName));
+ std::wstring originalNameStr{ originalName };
+
+ PWSTR path = nullptr;
+ winrt::check_hresult(spItem->GetPath(&path));
+ std::wstring pathStr{ path };
+ size_t oldPathSize = pathStr.size();
+
+ auto fileNamePos = pathStr.find_last_of(L"\\");
+ pathStr.replace(fileNamePos + 1, originalNameStr.length(), std::wstring{ newName });
+ spItem->PutPath(pathStr.c_str());
+ spItem->PutOriginalName(newName);
+ spItem->PutNewName(nullptr);
+
+ // if folder, update children path
+ bool isFolder = false;
+ winrt::check_hresult(spItem->GetIsFolder(&isFolder));
+ if (isFolder)
+ {
+ int id = -1;
+ winrt::check_hresult(spItem->GetId(&id));
+ pwtd->spsrm->UpdateChildrenPath(id, oldPathSize);
+ }
+
+ int id = -1;
+ winrt::check_hresult(spItem->GetId(&id));
+ PostMessage(pwtd->hwndManager, SRM_REGEX_ITEM_RENAMED_KEEP_UI, GetCurrentThreadId(), id);
+ }
}
CoTaskMemFree(newName);
}
@@ -1137,6 +1221,19 @@ void CPowerRenameManager::_OnUpdate(_In_ IPowerRenameItem* renameItem)
}
}
+void CPowerRenameManager::_OnRename(_In_ IPowerRenameItem* renameItem)
+{
+ CSRWSharedAutoLock lock(&m_lockEvents);
+
+ for (auto it : m_powerRenameManagerEvents)
+ {
+ if (it.pEvents)
+ {
+ it.pEvents->OnRename(renameItem);
+ }
+ }
+}
+
void CPowerRenameManager::_OnError(_In_ IPowerRenameItem* renameItem)
{
CSRWSharedAutoLock lock(&m_lockEvents);
@@ -1210,7 +1307,7 @@ void CPowerRenameManager::_OnRenameCompleted()
{
if (it.pEvents)
{
- it.pEvents->OnRenameCompleted();
+ it.pEvents->OnRenameCompleted(m_closeUIWindowAfterRenaming);
}
}
}
diff --git a/src/modules/powerrename/lib/PowerRenameManager.h b/src/modules/powerrename/lib/PowerRenameManager.h
index c14f563432..e42eeffa6e 100644
--- a/src/modules/powerrename/lib/PowerRenameManager.h
+++ b/src/modules/powerrename/lib/PowerRenameManager.h
@@ -3,8 +3,7 @@
#include