PowerToys/src/modules/powerrename/ui/PowerRenameUI.cpp

1284 lines
34 KiB
C++
Raw Normal View History

#include "stdafx.h"
#include "resource.h"
#include "PowerRenameUI.h"
#include "dpi_aware.h"
#include <commctrl.h>
#include <Shlobj.h>
#include <helpers.h>
#include <settings.h>
#include <windowsx.h>
#include <thread>
#include <trace.h>
extern HINSTANCE g_hInst;
2019-12-20 14:59:20 +01:00
int g_rgnMatchModeResIDs[] = {
IDS_ENTIREITEMNAME,
IDS_NAMEONLY,
IDS_EXTENSIONONLY
};
enum
{
MATCHMODE_FULLNAME = 0,
MATCHMODE_NAMEONLY,
MATCHMODE_EXTENIONONLY
};
struct FlagCheckboxMap
{
DWORD flag;
DWORD id;
};
2019-12-20 14:59:20 +01:00
FlagCheckboxMap g_flagCheckboxMap[] = {
{ UseRegularExpressions, IDC_CHECK_USEREGEX },
2019-12-20 14:59:20 +01:00
{ ExcludeSubfolders, IDC_CHECK_EXCLUDESUBFOLDERS },
{ EnumerateItems, IDC_CHECK_ENUMITEMS },
{ ExcludeFiles, IDC_CHECK_EXCLUDEFILES },
{ CaseSensitive, IDC_CHECK_CASESENSITIVE },
{ MatchAllOccurences, IDC_CHECK_MATCHALLOCCURENCES },
{ ExcludeFolders, IDC_CHECK_EXCLUDEFOLDERS },
{ NameOnly, IDC_CHECK_NAMEONLY },
{ ExtensionOnly, IDC_CHECK_EXTENSIONONLY }
};
2019-11-01 07:57:42 +01:00
struct RepositionMap
{
DWORD id;
DWORD flags;
};
enum
{
Reposition_None = 0,
Reposition_X = 0x1,
Reposition_Y = 0x2,
Reposition_Width = 0x4,
Reposition_Height = 0x8
};
2019-12-20 14:59:20 +01:00
RepositionMap g_repositionMap[] = {
{ IDC_SEARCHREPLACEGROUP, Reposition_Width },
{ IDC_OPTIONSGROUP, Reposition_Width },
{ IDC_PREVIEWGROUP, Reposition_Width | Reposition_Height },
{ IDC_EDIT_SEARCHFOR, Reposition_Width },
{ IDC_EDIT_REPLACEWITH, Reposition_Width },
{ IDC_LIST_PREVIEW, Reposition_Width | Reposition_Height },
{ IDC_STATUS_MESSAGE, Reposition_Y },
{ ID_RENAME, Reposition_X | Reposition_Y },
{ ID_ABOUT, Reposition_X | Reposition_Y },
{ IDCANCEL, Reposition_X | Reposition_Y }
2019-11-01 07:57:42 +01:00
};
2019-12-20 14:59:20 +01:00
inline int RECT_WIDTH(RECT& r)
{
return r.right - r.left;
}
inline int RECT_HEIGHT(RECT& r)
{
return r.bottom - r.top;
}
2019-11-01 07:57:42 +01:00
// IUnknown
IFACEMETHODIMP CPowerRenameUI::QueryInterface(__in REFIID riid, __deref_out void** ppv)
{
2019-12-20 14:59:20 +01:00
static const QITAB qit[] = {
QITABENT(CPowerRenameUI, IPowerRenameUI),
QITABENT(CPowerRenameUI, IPowerRenameManagerEvents),
QITABENT(CPowerRenameUI, IDropTarget),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
2019-12-20 14:59:20 +01:00
IFACEMETHODIMP_(ULONG)
CPowerRenameUI::AddRef()
{
return InterlockedIncrement(&m_refCount);
}
2019-12-20 14:59:20 +01:00
IFACEMETHODIMP_(ULONG)
CPowerRenameUI::Release()
{
long refCount = InterlockedDecrement(&m_refCount);
if (refCount == 0)
{
delete this;
}
return refCount;
}
HRESULT CPowerRenameUI::s_CreateInstance(_In_ IPowerRenameManager* psrm, _In_opt_ IUnknown* dataSource, _In_ bool enableDragDrop, _Outptr_ IPowerRenameUI** ppsrui)
{
*ppsrui = nullptr;
2019-12-20 14:59:20 +01:00
CPowerRenameUI* prui = new CPowerRenameUI();
HRESULT hr = prui ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
// Pass the IPowerRenameManager to the IPowerRenameUI so it can subscribe to events
hr = prui->_Initialize(psrm, dataSource, enableDragDrop);
if (SUCCEEDED(hr))
{
hr = prui->QueryInterface(IID_PPV_ARGS(ppsrui));
}
prui->Release();
}
return hr;
}
// IPowerRenameUI
IFACEMETHODIMP CPowerRenameUI::Show(_In_opt_ HWND hwndParent)
{
return _DoModeless(hwndParent);
}
IFACEMETHODIMP CPowerRenameUI::Close()
{
_OnCloseDlg();
return S_OK;
}
IFACEMETHODIMP CPowerRenameUI::Update()
{
return S_OK;
}
IFACEMETHODIMP CPowerRenameUI::get_hwnd(_Out_ HWND* hwnd)
{
*hwnd = m_hwnd;
return S_OK;
}
IFACEMETHODIMP CPowerRenameUI::get_showUI(_Out_ bool* showUI)
{
// Let callers know that it is OK to show UI (ex: progress dialog, error dialog and conflict dialog UI)
*showUI = true;
return S_OK;
}
// IPowerRenameManagerEvents
IFACEMETHODIMP CPowerRenameUI::OnItemAdded(_In_ IPowerRenameItem*)
{
return S_OK;
}
IFACEMETHODIMP CPowerRenameUI::OnUpdate(_In_ IPowerRenameItem*)
{
UINT itemCount = 0;
if (m_spsrm)
{
m_spsrm->GetItemCount(&itemCount);
}
m_listview.RedrawItems(0, itemCount);
_UpdateCounts();
return S_OK;
}
IFACEMETHODIMP CPowerRenameUI::OnError(_In_ IPowerRenameItem*)
{
return S_OK;
}
IFACEMETHODIMP CPowerRenameUI::OnRegExStarted(_In_ DWORD threadId)
{
m_disableCountUpdate = true;
m_currentRegExId = threadId;
_UpdateCounts();
return S_OK;
}
IFACEMETHODIMP CPowerRenameUI::OnRegExCanceled(_In_ DWORD threadId)
{
if (m_currentRegExId == threadId)
{
m_disableCountUpdate = false;
_UpdateCounts();
}
return S_OK;
}
IFACEMETHODIMP CPowerRenameUI::OnRegExCompleted(_In_ DWORD threadId)
{
// Enable list view
if (m_currentRegExId == threadId)
{
m_disableCountUpdate = false;
_UpdateCounts();
}
return S_OK;
}
IFACEMETHODIMP CPowerRenameUI::OnRenameStarted()
{
// Disable controls
EnableWindow(m_hwnd, FALSE);
return S_OK;
}
IFACEMETHODIMP CPowerRenameUI::OnRenameCompleted()
{
// Enable controls
EnableWindow(m_hwnd, TRUE);
// Close the window
2019-11-09 09:30:00 +01:00
PostMessage(m_hwnd, WM_CLOSE, (WPARAM)0, (LPARAM)0);
return S_OK;
}
// IDropTarget
IFACEMETHODIMP CPowerRenameUI::DragEnter(_In_ IDataObject* pdtobj, DWORD /* grfKeyState */, POINTL pt, _Inout_ DWORD* pdwEffect)
{
if (m_spdth)
{
POINT ptT = { pt.x, pt.y };
m_spdth->DragEnter(m_hwnd, pdtobj, &ptT, *pdwEffect);
}
return S_OK;
}
IFACEMETHODIMP CPowerRenameUI::DragOver(DWORD /* grfKeyState */, POINTL pt, _Inout_ DWORD* pdwEffect)
{
if (m_spdth)
{
POINT ptT = { pt.x, pt.y };
m_spdth->DragOver(&ptT, *pdwEffect);
}
return S_OK;
}
IFACEMETHODIMP CPowerRenameUI::DragLeave()
{
if (m_spdth)
{
m_spdth->DragLeave();
}
return S_OK;
}
IFACEMETHODIMP CPowerRenameUI::Drop(_In_ IDataObject* pdtobj, DWORD, POINTL pt, _Inout_ DWORD* pdwEffect)
{
if (m_spdth)
{
POINT ptT = { pt.x, pt.y };
m_spdth->Drop(pdtobj, &ptT, *pdwEffect);
}
EnableWindow(GetDlgItem(m_hwnd, ID_RENAME), TRUE);
EnableWindow(m_hwndLV, TRUE);
// Populate the manager from the data object
if (m_spsrm)
{
_EnumerateItems(pdtobj);
}
return S_OK;
}
HRESULT CPowerRenameUI::_Initialize(_In_ IPowerRenameManager* psrm, _In_opt_ IUnknown* dataSource, _In_ bool enableDragDrop)
{
// Cache the rename manager
m_spsrm = psrm;
// Cache the data source for enumeration later
m_dataSource = dataSource;
m_enableDragDrop = enableDragDrop;
HRESULT hr = CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&m_spdth));
if (SUCCEEDED(hr))
{
// Subscribe to rename manager events
hr = m_spsrm->Advise(this, &m_cookie);
}
if (FAILED(hr))
{
_Cleanup();
}
return hr;
}
HRESULT CPowerRenameUI::_InitAutoComplete()
{
HRESULT hr = S_OK;
if (CSettings::GetMRUEnabled())
{
hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&m_spSearchAC));
if (SUCCEEDED(hr))
{
hr = CRenameMRUSearch_CreateInstance(&m_spSearchACL);
if (SUCCEEDED(hr))
{
hr = m_spSearchAC->Init(GetDlgItem(m_hwnd, IDC_EDIT_SEARCHFOR), m_spSearchACL, nullptr, nullptr);
if (SUCCEEDED(hr))
{
hr = m_spSearchAC->SetOptions(ACO_AUTOSUGGEST | ACO_AUTOAPPEND | ACO_UPDOWNKEYDROPSLIST);
}
}
}
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&m_spReplaceAC));
if (SUCCEEDED(hr))
{
hr = CRenameMRUReplace_CreateInstance(&m_spReplaceACL);
if (SUCCEEDED(hr))
{
hr = m_spReplaceAC->Init(GetDlgItem(m_hwnd, IDC_EDIT_REPLACEWITH), m_spReplaceACL, nullptr, nullptr);
if (SUCCEEDED(hr))
{
hr = m_spReplaceAC->SetOptions(ACO_AUTOSUGGEST | ACO_AUTOAPPEND | ACO_UPDOWNKEYDROPSLIST);
}
}
}
}
}
return hr;
}
void CPowerRenameUI::_Cleanup()
{
if (m_spsrm && m_cookie != 0)
{
m_spsrm->UnAdvise(m_cookie);
m_cookie = 0;
m_spsrm = nullptr;
}
m_dataSource = nullptr;
m_spdth = nullptr;
if (m_enableDragDrop)
{
RevokeDragDrop(m_hwnd);
}
m_hwnd = NULL;
}
void CPowerRenameUI::_EnumerateItems(_In_ IUnknown* pdtobj)
{
// Enumerate the data object and popuplate the manager
if (m_spsrm)
{
m_disableCountUpdate = true;
EnumerateDataObject(pdtobj, m_spsrm);
m_disableCountUpdate = false;
UINT itemCount = 0;
m_spsrm->GetItemCount(&itemCount);
m_listview.SetItemCount(itemCount);
_UpdateCounts();
}
}
HRESULT CPowerRenameUI::_ReadSettings()
{
// Check if we should read flags from settings
// or the defaults from the manager.
DWORD flags = 0;
if (CSettings::GetPersistState())
{
flags = CSettings::GetFlags();
m_spsrm->put_flags(flags);
wchar_t buffer[CSettings::MAX_INPUT_STRING_LEN];
buffer[0] = L'\0';
CSettings::GetSearchText(buffer, ARRAYSIZE(buffer));
SetDlgItemText(m_hwnd, IDC_EDIT_SEARCHFOR, buffer);
buffer[0] = L'\0';
CSettings::GetReplaceText(buffer, ARRAYSIZE(buffer));
SetDlgItemText(m_hwnd, IDC_EDIT_REPLACEWITH, buffer);
}
else
{
m_spsrm->get_flags(&flags);
}
_SetCheckboxesFromFlags(flags);
return S_OK;
}
HRESULT CPowerRenameUI::_WriteSettings()
{
// Check if we should store our settings
if (CSettings::GetPersistState())
{
DWORD flags = 0;
m_spsrm->get_flags(&flags);
CSettings::SetFlags(flags);
wchar_t buffer[CSettings::MAX_INPUT_STRING_LEN];
buffer[0] = L'\0';
GetDlgItemText(m_hwnd, IDC_EDIT_SEARCHFOR, buffer, ARRAYSIZE(buffer));
CSettings::SetSearchText(buffer);
if (CSettings::GetMRUEnabled() && m_spSearchACL)
{
CComPtr<IPowerRenameMRU> spSearchMRU;
if (SUCCEEDED(m_spSearchACL->QueryInterface(IID_PPV_ARGS(&spSearchMRU))))
{
spSearchMRU->AddMRUString(buffer);
}
}
buffer[0] = L'\0';
GetDlgItemText(m_hwnd, IDC_EDIT_REPLACEWITH, buffer, ARRAYSIZE(buffer));
CSettings::SetReplaceText(buffer);
if (CSettings::GetMRUEnabled() && m_spReplaceACL)
{
CComPtr<IPowerRenameMRU> spReplaceMRU;
if (SUCCEEDED(m_spReplaceACL->QueryInterface(IID_PPV_ARGS(&spReplaceMRU))))
{
spReplaceMRU->AddMRUString(buffer);
}
}
Trace::SettingsChanged();
}
return S_OK;
}
void CPowerRenameUI::_OnCloseDlg()
{
if (m_hwnd != NULL)
{
if (m_modeless)
{
DestroyWindow(m_hwnd);
}
else
{
EndDialog(m_hwnd, 1);
}
}
}
void CPowerRenameUI::_OnDestroyDlg()
{
_Cleanup();
if (m_modeless)
{
PostQuitMessage(0);
}
}
void CPowerRenameUI::_OnRename()
{
if (m_spsrm)
{
m_spsrm->Rename(m_hwnd);
}
// Persist the current settings. We only do this when
// a rename is actually performed. Not when the user
// closes/cancels the dialog.
_WriteSettings();
}
void CPowerRenameUI::_OnAbout()
{
// Launch github page
2019-12-20 14:59:20 +01:00
SHELLEXECUTEINFO info = { 0 };
info.cbSize = sizeof(SHELLEXECUTEINFO);
info.lpVerb = L"open";
info.lpFile = L"https://github.com/microsoft/PowerToys/tree/master/src/modules/powerrename";
info.nShow = SW_SHOWDEFAULT;
ShellExecuteEx(&info);
}
HRESULT CPowerRenameUI::_DoModal(__in_opt HWND hwnd)
{
m_modeless = false;
HRESULT hr = S_OK;
INT_PTR ret = DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_MAIN), hwnd, s_DlgProc, (LPARAM)this);
if (ret < 0)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
return hr;
}
void CPowerRenameUI::BecomeForegroundWindow()
{
static INPUT i = { INPUT_MOUSE, {} };
SendInput(1, &i, sizeof(i));
SetForegroundWindow(m_hwnd);
}
HRESULT CPowerRenameUI::_DoModeless(__in_opt HWND hwnd)
{
m_modeless = true;
HRESULT hr = S_OK;
if (NULL != CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_MAIN), hwnd, s_DlgProc, (LPARAM)this))
{
ShowWindow(m_hwnd, SW_SHOWNORMAL);
BecomeForegroundWindow();
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
if (!IsDialogMessage(m_hwnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
DestroyWindow(m_hwnd);
m_hwnd = NULL;
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
return hr;
}
INT_PTR CPowerRenameUI::_DlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
2019-12-20 14:59:20 +01:00
INT_PTR bRet = TRUE; // default for all handled cases in switch below
switch (uMsg)
{
case WM_INITDIALOG:
_OnInitDlg();
break;
case WM_COMMAND:
_OnCommand(wParam, lParam);
break;
case WM_NOTIFY:
bRet = _OnNotify(wParam, lParam);
break;
case WM_THEMECHANGED:
_OnSize(SIZE_RESTORED);
break;
2019-11-01 07:57:42 +01:00
case WM_SIZE:
_OnSize(wParam);
break;
case WM_GETMINMAXINFO:
_OnGetMinMaxInfo(lParam);
break;
case WM_CLOSE:
_OnCloseDlg();
break;
case WM_DESTROY:
_OnDestroyDlg();
break;
default:
bRet = FALSE;
}
return bRet;
}
void CPowerRenameUI::_OnInitDlg()
{
m_hwndLV = GetDlgItem(m_hwnd, IDC_LIST_PREVIEW);
m_listview.Init(m_hwndLV);
if (m_dataSource)
{
// Populate the manager from the data object
_EnumerateItems(m_dataSource);
}
// Initialize from stored settings. Do this now in case we have
// restored a previous search or replace text that needs to be
// evaluated against the items we just enumerated.
_ReadSettings();
// Load the main icon
LoadIconWithScaleDown(g_hInst, MAKEINTRESOURCE(IDI_RENAME), 32, 32, &m_iconMain);
// Update the icon associated with our main app window
SendMessage(m_hwnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)m_iconMain);
SendMessage(m_hwnd, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)m_iconMain);
// TODO: put this behind a setting?
if (m_enableDragDrop)
{
RegisterDragDrop(m_hwnd, this);
}
2019-11-01 07:57:42 +01:00
RECT rc = { 0 };
GetWindowRect(m_hwnd, &rc);
m_initialWidth = RECT_WIDTH(rc);
m_initialHeight = RECT_HEIGHT(rc);
UINT dummy = 0;
DPIAware::GetScreenDPIForWindow(m_hwnd, m_initialDPI, dummy);
for (UINT u = 0; u < ARRAYSIZE(g_repositionMap); u++)
{
_CollectItemPosition(g_repositionMap[u].id);
}
2019-11-01 07:57:42 +01:00
_InitAutoComplete();
// Disable rename button by default. It will be enabled in _UpdateCounts if
// there are tiems to be renamed
EnableWindow(GetDlgItem(m_hwnd, ID_RENAME), FALSE);
// Update UI elements that depend on number of items selected or to be renamed
_UpdateCounts();
m_initialized = true;
}
void CPowerRenameUI::_OnCommand(_In_ WPARAM wParam, _In_ LPARAM lParam)
{
switch (LOWORD(wParam))
{
case IDOK:
case IDCANCEL:
_OnCloseDlg();
break;
case ID_RENAME:
_OnRename();
break;
case ID_ABOUT:
_OnAbout();
break;
case IDC_EDIT_REPLACEWITH:
case IDC_EDIT_SEARCHFOR:
if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
{
_OnSearchReplaceChanged();
}
break;
case IDC_CHECK_CASESENSITIVE:
case IDC_CHECK_ENUMITEMS:
case IDC_CHECK_EXCLUDEFILES:
case IDC_CHECK_EXCLUDEFOLDERS:
case IDC_CHECK_EXCLUDESUBFOLDERS:
2019-11-01 07:57:42 +01:00
case IDC_CHECK_MATCHALLOCCURENCES:
case IDC_CHECK_USEREGEX:
case IDC_CHECK_EXTENSIONONLY:
case IDC_CHECK_NAMEONLY:
if (BN_CLICKED == HIWORD(wParam))
{
_ValidateFlagCheckbox(LOWORD(wParam));
_GetFlagsFromCheckboxes();
}
break;
}
}
BOOL CPowerRenameUI::_OnNotify(_In_ WPARAM wParam, _In_ LPARAM lParam)
{
bool ret = FALSE;
2019-12-20 14:59:20 +01:00
LPNMHDR pnmdr = (LPNMHDR)lParam;
LPNMLISTVIEW pnmlv = (LPNMLISTVIEW)pnmdr;
NMLVEMPTYMARKUP* pnmMarkup = NULL;
if (pnmdr)
{
BOOL checked = FALSE;
switch (pnmdr->code)
{
case HDN_ITEMSTATEICONCLICK:
if (m_spsrm)
{
m_listview.ToggleAll(m_spsrm, (!(((LPNMHEADER)lParam)->pitem->fmt & HDF_CHECKED)));
_UpdateCounts();
}
break;
case LVN_GETEMPTYMARKUP:
pnmMarkup = (NMLVEMPTYMARKUP*)lParam;
pnmMarkup->dwFlags = EMF_CENTERED;
LoadString(g_hInst, IDS_LISTVIEW_EMPTY, pnmMarkup->szMarkup, ARRAYSIZE(pnmMarkup->szMarkup));
ret = TRUE;
break;
case LVN_BEGINLABELEDIT:
ret = TRUE;
break;
case LVN_KEYDOWN:
if (m_spsrm)
{
m_listview.OnKeyDown(m_spsrm, (LV_KEYDOWN*)pnmdr);
_UpdateCounts();
}
break;
case LVN_GETDISPINFO:
if (m_spsrm)
{
m_listview.GetDisplayInfo(m_spsrm, (LV_DISPINFO*)pnmlv);
}
break;
2019-12-20 14:59:20 +01:00
case NM_CLICK: {
if (m_spsrm)
{
2019-12-20 14:59:20 +01:00
m_listview.OnClickList(m_spsrm, (NM_LISTVIEW*)pnmdr);
_UpdateCounts();
}
2019-12-20 14:59:20 +01:00
break;
}
}
}
return ret;
}
2019-11-01 07:57:42 +01:00
void CPowerRenameUI::_OnGetMinMaxInfo(_In_ LPARAM lParam)
{
if (m_initialWidth)
{
// Prevent resizing the dialog less than the original size
MINMAXINFO* pMinMaxInfo = reinterpret_cast<MINMAXINFO*>(lParam);
pMinMaxInfo->ptMinTrackSize.x = m_initialWidth;
pMinMaxInfo->ptMinTrackSize.y = m_initialHeight;
}
}
void CPowerRenameUI::_OnSize(_In_ WPARAM wParam)
{
if ((wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED) && m_initialWidth)
{
for (UINT u = 0; u < ARRAYSIZE(g_repositionMap); u++)
{
_MoveControl(g_repositionMap[u].id, g_repositionMap[u].flags);
2019-11-01 07:57:42 +01:00
}
m_listview.OnSize();
2019-11-01 07:57:42 +01:00
}
}
void CPowerRenameUI::_MoveControl(_In_ DWORD id, _In_ DWORD repositionFlags)
2019-11-01 07:57:42 +01:00
{
HWND hwnd = GetDlgItem(m_hwnd, id);
UINT flags = SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOACTIVATE;
if (!((repositionFlags & Reposition_X) || (repositionFlags & Reposition_Y)))
{
flags |= SWP_NOMOVE;
}
if (!((repositionFlags & Reposition_Width) || (repositionFlags & Reposition_Height)))
{
flags |= SWP_NOSIZE;
}
RECT rc = { 0 };
GetWindowRect(m_hwnd, &rc);
int mainWindowWidth = rc.right - rc.left;
int mainWindowHeight = rc.bottom - rc.top;
2019-11-01 07:57:42 +01:00
RECT rcWindow = { 0 };
GetWindowRect(hwnd, &rcWindow);
MapWindowPoints(HWND_DESKTOP, GetParent(hwnd), (LPPOINT)&rcWindow, 2);
int x = rcWindow.left;
int y = rcWindow.top;
int width = rcWindow.right - rcWindow.left;
int height = rcWindow.bottom - rcWindow.top;
2019-11-01 07:57:42 +01:00
UINT currentDPI = 0, dummy;
DPIAware::GetScreenDPIForWindow(m_hwnd, currentDPI, dummy);
float scale = (float)currentDPI / m_initialDPI;
2019-11-01 07:57:42 +01:00
switch (id)
2019-11-01 07:57:42 +01:00
{
case IDC_EDIT_SEARCHFOR:
case IDC_EDIT_REPLACEWITH:
width = mainWindowWidth - static_cast<int>(m_itemsPositioning.searchReplaceWidthDiff * scale);
break;
case IDC_PREVIEWGROUP:
height = mainWindowHeight - static_cast<int>(m_itemsPositioning.previewGroupHeightDiff * scale);
case IDC_SEARCHREPLACEGROUP:
case IDC_OPTIONSGROUP:
width = mainWindowWidth - static_cast<int>(m_itemsPositioning.groupsWidthDiff * scale);
break;
case IDC_LIST_PREVIEW:
width = mainWindowWidth - static_cast<int>(m_itemsPositioning.listPreviewWidthDiff * scale);
height = mainWindowHeight - static_cast<int>(m_itemsPositioning.listPreviewHeightDiff * scale);
break;
case IDC_STATUS_MESSAGE:
y = mainWindowHeight - static_cast<int>(m_itemsPositioning.statusMessageYDiff * scale);
break;
case ID_RENAME:
x = mainWindowWidth - static_cast<int>(m_itemsPositioning.renameButtonXDiff * scale);
y = mainWindowHeight - static_cast<int>(m_itemsPositioning.renameButtonYDiff * scale);
break;
case ID_ABOUT:
x = mainWindowWidth - static_cast<int>(m_itemsPositioning.helpButtonXDiff * scale);
y = mainWindowHeight - static_cast<int>(m_itemsPositioning.helpButtonYDiff * scale);
break;
case IDCANCEL:
x = mainWindowWidth - static_cast<int>(m_itemsPositioning.cancelButtonXDiff * scale);
y = mainWindowHeight - static_cast<int>(m_itemsPositioning.cancelButtonYDiff * scale);
break;
2019-11-01 07:57:42 +01:00
}
SetWindowPos(hwnd, NULL, x, y, width, height, flags);
RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE);
2019-11-01 07:57:42 +01:00
}
void CPowerRenameUI::_OnSearchReplaceChanged()
{
// Pass updated search and replace terms to the IPowerRenameRegEx handler
CComPtr<IPowerRenameRegEx> spRegEx;
if (m_spsrm && SUCCEEDED(m_spsrm->get_renameRegEx(&spRegEx)))
{
wchar_t buffer[CSettings::MAX_INPUT_STRING_LEN];
buffer[0] = L'\0';
GetDlgItemText(m_hwnd, IDC_EDIT_SEARCHFOR, buffer, ARRAYSIZE(buffer));
spRegEx->put_searchTerm(buffer);
buffer[0] = L'\0';
GetDlgItemText(m_hwnd, IDC_EDIT_REPLACEWITH, buffer, ARRAYSIZE(buffer));
spRegEx->put_replaceTerm(buffer);
}
}
DWORD CPowerRenameUI::_GetFlagsFromCheckboxes()
{
DWORD flags = 0;
for (int i = 0; i < ARRAYSIZE(g_flagCheckboxMap); i++)
{
if (Button_GetCheck(GetDlgItem(m_hwnd, g_flagCheckboxMap[i].id)) == BST_CHECKED)
{
flags |= g_flagCheckboxMap[i].flag;
}
}
// Ensure we update flags
if (m_spsrm)
{
m_spsrm->put_flags(flags);
}
return flags;
}
void CPowerRenameUI::_SetCheckboxesFromFlags(_In_ DWORD flags)
{
for (int i = 0; i < ARRAYSIZE(g_flagCheckboxMap); i++)
{
Button_SetCheck(GetDlgItem(m_hwnd, g_flagCheckboxMap[i].id), flags & g_flagCheckboxMap[i].flag);
}
}
void CPowerRenameUI::_ValidateFlagCheckbox(_In_ DWORD checkBoxId)
{
if (checkBoxId == IDC_CHECK_NAMEONLY)
{
if (Button_GetCheck(GetDlgItem(m_hwnd, IDC_CHECK_NAMEONLY)) == BST_CHECKED)
{
Button_SetCheck(GetDlgItem(m_hwnd, IDC_CHECK_EXTENSIONONLY), FALSE);
}
}
else if (checkBoxId == IDC_CHECK_EXTENSIONONLY)
{
if (Button_GetCheck(GetDlgItem(m_hwnd, IDC_CHECK_EXTENSIONONLY)) == BST_CHECKED)
{
Button_SetCheck(GetDlgItem(m_hwnd, IDC_CHECK_NAMEONLY), FALSE);
}
}
}
void CPowerRenameUI::_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_spsrm)
{
m_spsrm->GetSelectedItemCount(&selectedCount);
m_spsrm->GetRenameItemCount(&renamingCount);
}
if (m_selectedCount != selectedCount ||
m_renamingCount != renamingCount)
{
m_selectedCount = selectedCount;
m_renamingCount = renamingCount;
// Update selected and rename count label
wchar_t countsLabelFormat[100] = { 0 };
LoadString(g_hInst, IDS_COUNTSLABELFMT, countsLabelFormat, ARRAYSIZE(countsLabelFormat));
wchar_t countsLabel[100] = { 0 };
StringCchPrintf(countsLabel, ARRAYSIZE(countsLabel), countsLabelFormat, selectedCount, renamingCount);
SetDlgItemText(m_hwnd, IDC_STATUS_MESSAGE, countsLabel);
// Update Rename button state
EnableWindow(GetDlgItem(m_hwnd, ID_RENAME), (renamingCount > 0));
}
}
void CPowerRenameUI::_CollectItemPosition(_In_ DWORD id)
{
HWND hwnd = GetDlgItem(m_hwnd, id);
RECT rcWindow = { 0 };
GetWindowRect(hwnd, &rcWindow);
MapWindowPoints(HWND_DESKTOP, GetParent(hwnd), (LPPOINT)&rcWindow, 2);
int itemWidth = rcWindow.right - rcWindow.left;
int itemHeight = rcWindow.bottom - rcWindow.top;
switch (id)
{
case IDC_EDIT_SEARCHFOR:
/* IDC_EDIT_REPLACEWITH uses same value*/
m_itemsPositioning.searchReplaceWidthDiff = m_initialWidth - itemWidth;
break;
case IDC_PREVIEWGROUP:
m_itemsPositioning.previewGroupHeightDiff = m_initialHeight - itemHeight;
break;
case IDC_SEARCHREPLACEGROUP:
/* IDC_OPTIONSGROUP uses same value */
m_itemsPositioning.groupsWidthDiff = m_initialWidth - itemWidth;
break;
case IDC_LIST_PREVIEW:
m_itemsPositioning.listPreviewWidthDiff = m_initialWidth - itemWidth;
m_itemsPositioning.listPreviewHeightDiff = m_initialHeight - itemHeight;
break;
case IDC_STATUS_MESSAGE:
m_itemsPositioning.statusMessageYDiff = m_initialHeight - rcWindow.top;
break;
case ID_RENAME:
m_itemsPositioning.renameButtonXDiff = m_initialWidth - rcWindow.left;
m_itemsPositioning.renameButtonYDiff = m_initialHeight - rcWindow.top;
break;
case ID_ABOUT:
m_itemsPositioning.helpButtonXDiff = m_initialWidth - rcWindow.left;
m_itemsPositioning.helpButtonYDiff = m_initialHeight - rcWindow.top;
break;
case IDCANCEL:
m_itemsPositioning.cancelButtonXDiff = m_initialWidth - rcWindow.left;
m_itemsPositioning.cancelButtonYDiff = m_initialHeight - rcWindow.top;
break;
}
}
void CPowerRenameListView::Init(_In_ HWND hwndLV)
{
if (hwndLV)
{
m_hwndLV = hwndLV;
EnableWindow(m_hwndLV, TRUE);
// Set the standard styles
DWORD dwLVStyle = (DWORD)GetWindowLongPtr(m_hwndLV, GWL_STYLE);
dwLVStyle |= LVS_ALIGNLEFT | LVS_REPORT | LVS_SHAREIMAGELISTS | LVS_SINGLESEL;
SetWindowLongPtr(m_hwndLV, GWL_STYLE, dwLVStyle);
// Set the extended view styles
ListView_SetExtendedListViewStyle(m_hwndLV, LVS_EX_CHECKBOXES | LVS_EX_DOUBLEBUFFER | LVS_EX_AUTOSIZECOLUMNS);
// Get the system image lists. Our list view is setup to not destroy
// these since the image list belongs to the entire explorer process
HIMAGELIST himlLarge;
HIMAGELIST himlSmall;
if (Shell_GetImageLists(&himlLarge, &himlSmall))
{
ListView_SetImageList(m_hwndLV, himlSmall, LVSIL_SMALL);
ListView_SetImageList(m_hwndLV, himlLarge, LVSIL_NORMAL);
}
_UpdateColumns();
}
}
void CPowerRenameListView::ToggleAll(_In_ IPowerRenameManager* psrm, _In_ bool selected)
{
if (m_hwndLV)
{
UINT itemCount = 0;
psrm->GetItemCount(&itemCount);
for (UINT i = 0; i < itemCount; i++)
{
CComPtr<IPowerRenameItem> spItem;
if (SUCCEEDED(psrm->GetItemByIndex(i, &spItem)))
{
spItem->put_selected(selected);
}
}
RedrawItems(0, itemCount);
}
}
void CPowerRenameListView::ToggleItem(_In_ IPowerRenameManager* psrm, _In_ int item)
{
CComPtr<IPowerRenameItem> spItem;
if (SUCCEEDED(psrm->GetItemByIndex(item, &spItem)))
{
bool selected = false;
spItem->get_selected(&selected);
spItem->put_selected(!selected);
RedrawItems(item, item);
}
}
void CPowerRenameListView::OnKeyDown(_In_ IPowerRenameManager* psrm, _In_ LV_KEYDOWN* lvKeyDown)
{
if (lvKeyDown->wVKey == VK_SPACE)
{
int selectionMark = ListView_GetSelectionMark(m_hwndLV);
if (selectionMark != -1)
{
ToggleItem(psrm, selectionMark);
}
}
}
void CPowerRenameListView::OnClickList(_In_ IPowerRenameManager* psrm, NM_LISTVIEW* pnmListView)
{
LVHITTESTINFO hitinfo;
//Copy click point
hitinfo.pt = pnmListView->ptAction;
//Make the hit test...
int item = ListView_HitTest(m_hwndLV, &hitinfo);
if (item != -1)
{
if ((hitinfo.flags & LVHT_ONITEM) != 0)
{
ToggleItem(psrm, item);
}
}
}
void CPowerRenameListView::UpdateItemCheckState(_In_ IPowerRenameManager* psrm, _In_ int iItem)
{
if (psrm && m_hwndLV && (iItem > -1))
{
CComPtr<IPowerRenameItem> spItem;
if (SUCCEEDED(psrm->GetItemByIndex(iItem, &spItem)))
{
bool checked = ListView_GetCheckState(m_hwndLV, iItem);
spItem->put_selected(checked);
UINT uSelected = (checked) ? LVIS_SELECTED : 0;
ListView_SetItemState(m_hwndLV, iItem, uSelected, LVIS_SELECTED);
// Update the rename column if necessary
int id = 0;
spItem->get_id(&id);
RedrawItems(id, id);
}
// Get the total number of list items and compare it to what is selected
// We need to update the column checkbox if all items are selected or if
// not all of the items are selected.
bool checkHeader = (ListView_GetSelectedCount(m_hwndLV) == ListView_GetItemCount(m_hwndLV));
_UpdateHeaderCheckState(checkHeader);
}
}
2019-12-20 14:59:20 +01:00
#define COL_ORIGINAL_NAME 0
#define COL_NEW_NAME 1
void CPowerRenameListView::GetDisplayInfo(_In_ IPowerRenameManager* psrm, _Inout_ LV_DISPINFO* plvdi)
{
UINT count = 0;
psrm->GetItemCount(&count);
if (plvdi->item.iItem < 0 || plvdi->item.iItem > static_cast<int>(count))
{
// Invalid index
return;
}
CComPtr<IPowerRenameItem> renameItem;
if (SUCCEEDED(psrm->GetItemByIndex((int)plvdi->item.iItem, &renameItem)))
{
if (plvdi->item.mask & LVIF_IMAGE)
{
renameItem->get_iconIndex(&plvdi->item.iImage);
}
if (plvdi->item.mask & LVIF_STATE)
{
plvdi->item.stateMask = LVIS_STATEIMAGEMASK;
bool isSelected = false;
renameItem->get_selected(&isSelected);
if (isSelected)
{
// Turn check box on
plvdi->item.state = INDEXTOSTATEIMAGEMASK(2);
}
else
{
// Turn check box off
plvdi->item.state = INDEXTOSTATEIMAGEMASK(1);
}
}
if (plvdi->item.mask & LVIF_PARAM)
{
int id = 0;
renameItem->get_id(&id);
plvdi->item.lParam = static_cast<LPARAM>(id);
}
if (plvdi->item.mask & LVIF_INDENT)
{
UINT depth = 0;
renameItem->get_depth(&depth);
plvdi->item.iIndent = static_cast<int>(depth);
}
if (plvdi->item.mask & LVIF_TEXT)
{
PWSTR subItemText = nullptr;
if (plvdi->item.iSubItem == COL_ORIGINAL_NAME)
{
renameItem->get_originalName(&subItemText);
}
else if (plvdi->item.iSubItem == COL_NEW_NAME)
{
DWORD flags = 0;
psrm->get_flags(&flags);
bool shouldRename = false;
if (SUCCEEDED(renameItem->ShouldRenameItem(flags, &shouldRename)) && shouldRename)
{
renameItem->get_newName(&subItemText);
}
}
StringCchCopy(plvdi->item.pszText, plvdi->item.cchTextMax, subItemText ? subItemText : L"");
CoTaskMemFree(subItemText);
subItemText = nullptr;
}
}
}
void CPowerRenameListView::OnSize()
{
RECT rc = { 0 };
GetClientRect(m_hwndLV, &rc);
ListView_SetColumnWidth(m_hwndLV, 0, RECT_WIDTH(rc) / 2);
ListView_SetColumnWidth(m_hwndLV, 1, RECT_WIDTH(rc) / 2);
}
void CPowerRenameListView::RedrawItems(_In_ int first, _In_ int last)
{
ListView_RedrawItems(m_hwndLV, first, last);
}
void CPowerRenameListView::SetItemCount(_In_ UINT itemCount)
{
ListView_SetItemCount(m_hwndLV, itemCount);
}
void CPowerRenameListView::_UpdateColumns()
{
if (m_hwndLV)
{
// And the list view columns
int iInsertPoint = 0;
LV_COLUMN lvc = { 0 };
lvc.mask = LVCF_FMT | LVCF_ORDER | LVCF_WIDTH | LVCF_TEXT;
lvc.fmt = LVCFMT_LEFT;
lvc.iOrder = iInsertPoint;
wchar_t buffer[64] = { 0 };
LoadString(g_hInst, IDS_ORIGINAL, buffer, ARRAYSIZE(buffer));
lvc.pszText = buffer;
ListView_InsertColumn(m_hwndLV, iInsertPoint, &lvc);
iInsertPoint++;
lvc.iOrder = iInsertPoint;
LoadString(g_hInst, IDS_RENAMED, buffer, ARRAYSIZE(buffer));
lvc.pszText = buffer;
ListView_InsertColumn(m_hwndLV, iInsertPoint, &lvc);
// Get a handle to the header of the columns
HWND hwndHeader = ListView_GetHeader(m_hwndLV);
if (hwndHeader)
{
// Update the header style to allow checkboxes
DWORD dwHeaderStyle = (DWORD)GetWindowLongPtr(hwndHeader, GWL_STYLE);
dwHeaderStyle |= HDS_CHECKBOXES;
SetWindowLongPtr(hwndHeader, GWL_STYLE, dwHeaderStyle);
_UpdateHeaderCheckState(TRUE);
}
_UpdateColumnSizes();
}
}
void CPowerRenameListView::_UpdateColumnSizes()
{
if (m_hwndLV)
{
RECT rc;
GetClientRect(m_hwndLV, &rc);
ListView_SetColumnWidth(m_hwndLV, 0, (rc.right - rc.left) / 2);
ListView_SetColumnWidth(m_hwndLV, 1, (rc.right - rc.left) / 2);
}
}
void CPowerRenameListView::_UpdateHeaderCheckState(_In_ bool check)
{
// Get a handle to the header of the columns
HWND hwndHeader = ListView_GetHeader(m_hwndLV);
if (hwndHeader)
{
wchar_t buffer[MAX_PATH] = { 0 };
buffer[0] = L'\0';
// Retrieve the existing header first so we
// don't trash the text already there
HDITEM hdi = { 0 };
hdi.mask = HDI_FORMAT | HDI_TEXT;
hdi.pszText = buffer;
hdi.cchTextMax = ARRAYSIZE(buffer);
Header_GetItem(hwndHeader, 0, &hdi);
// Set the first column to contain a checkbox
hdi.fmt |= HDF_CHECKBOX;
hdi.fmt |= (check) ? HDF_CHECKED : 0;
Header_SetItem(hwndHeader, 0, &hdi);
}
}