[PowerRename] Fluent UX (#13678)

* PowerRename new UI

* Add scrollviewer

* Don't deploy PowerRenameUI_new

* Visual updates

* Visual updates

* Updates

* Update Resources.resw

* Added docs button

* Update MainWindow.xaml

* Wire Docs button

* RegEx -> regular expressions

* Update Show only renamed list on search/replace text changed

* Update Show only renamed list on search/replace text changed - proper fix
Set searchTerm to NULL when cleared - fix Show only renamed files on clear searchTerm

* Files/folders input error handling

* Fix renaming with keeping UI window opened

After renaming folder, all of it's children need path update.
Without path update, further renaming of children items would
fail.

* Update only children, not all items with greater depth

* Fix dictionary false positives

* Remove .NET dep

* Rename PowerRenameUI_new to PowerRenameUILib
Rename executable PowerRenameUIHost to PowerRename

Co-authored-by: Laute <Niels.Laute@philips.com>
This commit is contained in:
Stefan Markovic
2021-10-25 15:40:19 +02:00
committed by GitHub
parent ce942b0585
commit 5cfbd72fa8
128 changed files with 5759 additions and 8196 deletions

View File

@@ -1,8 +1,5 @@
#include "pch.h"
#include "PowerRenameExt.h"
#include <PowerRenameUI.h>
#include <PowerRenameItem.h>
#include <PowerRenameManager.h>
#include <trace.h>
#include <Helpers.h>
#include <common/themes/icon_helpers.h>
@@ -11,6 +8,7 @@
#include <common/utils/resources.h>
#include <common/utils/process_path.h>
#include <common/utils/HDropIterator.h>
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<InvokeStruct*>(pData);
CComPtr<IUnknown> dataSource;
HRESULT hr = CoGetInterfaceAndReleaseStream(pInvokeData->pstrm, IID_PPV_ARGS(&dataSource));
if (SUCCEEDED(hr))
{
// Create the rename manager
CComPtr<IPowerRenameManager> spsrm;
hr = CPowerRenameManager::s_CreateInstance(&spsrm);
if (SUCCEEDED(hr))
{
// Create the factory for our items
CComPtr<IPowerRenameItemFactory> 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<IPowerRenameUI> 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<void**>(&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;