[PowerRename] Add Filtering Feature (#6017)

* Implement basic functionality

* Change approach.
move filter controls to manager
edit redrawing to always work with new GetVisibleItemCount() and GetVisibleItemByIndex() calls

* Fix performance issues. Some refactoring.

* Handle toggleAll correctly

* Handle dangling elements when filter is on

Make an item visible if it has at least one visible subitem

* Support filtering for selected and shouldRename

* Refactor for readability, remove useless member from PowerRenameUI

* Change variable names in PowerRenameUI for clarity

Use wrapper function RedrawItems() and SetItemCount() for consistency

* Handle result value properly in getVisibleItemByIndex()

* Add FlagsApplicable filter

* Add visual indication of filters

* Improve performance

Check if no filter is selected
Call SetItemCount() only when necessary

* Refactor for readability

* Get lock in setVisible()

* Change function names to camel case

* Change function names to start with uppercase

* Change filter behaviour when search area is empty

Show all elements when search area is empty and ShouldRename filter is selected
Avoid warnings

* Resolve conflicts
This commit is contained in:
Mehmet Murat Akburak
2020-08-25 08:22:05 +03:00
committed by GitHub
parent 3ede1a0b53
commit d3b80b26e3
14 changed files with 460 additions and 199 deletions

View File

@@ -150,13 +150,13 @@ IFACEMETHODIMP CPowerRenameUI::Update()
return S_OK;
}
IFACEMETHODIMP CPowerRenameUI::get_hwnd(_Out_ HWND* hwnd)
IFACEMETHODIMP CPowerRenameUI::GetHwnd(_Out_ HWND* hwnd)
{
*hwnd = m_hwnd;
return S_OK;
}
IFACEMETHODIMP CPowerRenameUI::get_showUI(_Out_ bool* showUI)
IFACEMETHODIMP CPowerRenameUI::GetShowUI(_Out_ bool* showUI)
{
// Let callers know that it is OK to show UI (ex: progress dialog, error dialog and conflict dialog UI)
*showUI = true;
@@ -171,12 +171,13 @@ IFACEMETHODIMP CPowerRenameUI::OnItemAdded(_In_ IPowerRenameItem*)
IFACEMETHODIMP CPowerRenameUI::OnUpdate(_In_ IPowerRenameItem*)
{
UINT itemCount = 0;
UINT visibleItemCount = 0;
if (m_spsrm)
{
m_spsrm->GetItemCount(&itemCount);
m_spsrm->GetVisibleItemCount(&visibleItemCount);
}
m_listview.RedrawItems(0, itemCount);
m_listview.SetItemCount(visibleItemCount);
m_listview.RedrawItems(0, visibleItemCount);
_UpdateCounts();
return S_OK;
}
@@ -381,7 +382,7 @@ void CPowerRenameUI::_EnumerateItems(_In_ IUnknown* pdtobj)
m_disableCountUpdate = false;
UINT itemCount = 0;
m_spsrm->GetItemCount(&itemCount);
m_spsrm->GetVisibleItemCount(&itemCount);
m_listview.SetItemCount(itemCount);
_UpdateCounts();
@@ -396,14 +397,14 @@ HRESULT CPowerRenameUI::_ReadSettings()
if (CSettingsInstance().GetPersistState())
{
flags = CSettingsInstance().GetFlags();
m_spsrm->put_flags(flags);
m_spsrm->PutFlags(flags);
SetDlgItemText(m_hwnd, IDC_EDIT_SEARCHFOR, CSettingsInstance().GetSearchText().c_str());
SetDlgItemText(m_hwnd, IDC_EDIT_REPLACEWITH, CSettingsInstance().GetReplaceText().c_str());
}
else
{
m_spsrm->get_flags(&flags);
m_spsrm->GetFlags(&flags);
}
_SetCheckboxesFromFlags(flags);
@@ -417,7 +418,7 @@ HRESULT CPowerRenameUI::_WriteSettings()
if (CSettingsInstance().GetPersistState())
{
DWORD flags = 0;
m_spsrm->get_flags(&flags);
m_spsrm->GetFlags(&flags);
CSettingsInstance().SetFlags(flags);
wchar_t buffer[CSettings::MAX_INPUT_STRING_LEN];
@@ -736,12 +737,18 @@ BOOL CPowerRenameUI::_OnNotify(_In_ WPARAM wParam, _In_ LPARAM lParam)
LPNMHDR pnmdr = (LPNMHDR)lParam;
LPNMLISTVIEW pnmlv = (LPNMLISTVIEW)pnmdr;
NMLVEMPTYMARKUP* pnmMarkup = NULL;
if (pnmdr)
{
BOOL checked = FALSE;
switch (pnmdr->code)
{
case LVN_COLUMNCLICK:
if (m_spsrm)
{
m_listview.OnColumnClick(m_spsrm, ((LPNMLISTVIEW)lParam)->iSubItem);
}
break;
case HDN_ITEMSTATEICONCLICK:
if (m_spsrm)
{
@@ -886,16 +893,16 @@ 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)))
if (m_spsrm && SUCCEEDED(m_spsrm->GetRenameRegEx(&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);
spRegEx->PutSearchTerm(buffer);
buffer[0] = L'\0';
GetDlgItemText(m_hwnd, IDC_EDIT_REPLACEWITH, buffer, ARRAYSIZE(buffer));
spRegEx->put_replaceTerm(buffer);
spRegEx->PutReplaceTerm(buffer);
}
}
@@ -913,7 +920,7 @@ DWORD CPowerRenameUI::_GetFlagsFromCheckboxes()
// Ensure we update flags
if (m_spsrm)
{
m_spsrm->put_flags(flags);
m_spsrm->PutFlags(flags);
}
return flags;
@@ -1084,31 +1091,37 @@ void CPowerRenameListView::ToggleAll(_In_ IPowerRenameManager* psrm, _In_ bool s
{
if (m_hwndLV)
{
UINT itemCount = 0;
UINT visibleItemCount = 0, 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);
spItem->PutSelected(selected);
}
}
RedrawItems(0, itemCount);
psrm->GetVisibleItemCount(&visibleItemCount);
SetItemCount(visibleItemCount);
RedrawItems(0, visibleItemCount);
}
}
void CPowerRenameListView::ToggleItem(_In_ IPowerRenameManager* psrm, _In_ int item)
{
CComPtr<IPowerRenameItem> spItem;
if (SUCCEEDED(psrm->GetItemByIndex(item, &spItem)))
if (SUCCEEDED(psrm->GetVisibleItemByIndex(item, &spItem)))
{
bool selected = false;
spItem->get_selected(&selected);
spItem->put_selected(!selected);
spItem->GetSelected(&selected);
spItem->PutSelected(!selected);
RedrawItems(item, item);
UINT visibleItemCount = 0;
psrm->GetVisibleItemCount(&visibleItemCount);
SetItemCount(visibleItemCount);
RedrawItems(0, visibleItemCount);
}
}
@@ -1146,18 +1159,19 @@ void CPowerRenameListView::UpdateItemCheckState(_In_ IPowerRenameManager* psrm,
if (psrm && m_hwndLV && (iItem > -1))
{
CComPtr<IPowerRenameItem> spItem;
if (SUCCEEDED(psrm->GetItemByIndex(iItem, &spItem)))
if (SUCCEEDED(psrm->GetVisibleItemByIndex(iItem, &spItem)))
{
bool checked = ListView_GetCheckState(m_hwndLV, iItem);
spItem->put_selected(checked);
spItem->PutSelected(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);
UINT visibleItemCount = 0;
psrm->GetVisibleItemCount(&visibleItemCount);
SetItemCount(visibleItemCount);
RedrawItems(0, visibleItemCount);
}
// Get the total number of list items and compare it to what is selected
@@ -1174,7 +1188,7 @@ void CPowerRenameListView::UpdateItemCheckState(_In_ IPowerRenameManager* psrm,
void CPowerRenameListView::GetDisplayInfo(_In_ IPowerRenameManager* psrm, _Inout_ LV_DISPINFO* plvdi)
{
UINT count = 0;
psrm->GetItemCount(&count);
psrm->GetVisibleItemCount(&count);
if (plvdi->item.iItem < 0 || plvdi->item.iItem > static_cast<int>(count))
{
// Invalid index
@@ -1182,11 +1196,11 @@ void CPowerRenameListView::GetDisplayInfo(_In_ IPowerRenameManager* psrm, _Inout
}
CComPtr<IPowerRenameItem> renameItem;
if (SUCCEEDED(psrm->GetItemByIndex((int)plvdi->item.iItem, &renameItem)))
if (SUCCEEDED(psrm->GetVisibleItemByIndex((int)plvdi->item.iItem, &renameItem)))
{
if (plvdi->item.mask & LVIF_IMAGE)
{
renameItem->get_iconIndex(&plvdi->item.iImage);
renameItem->GetIconIndex(&plvdi->item.iImage);
}
if (plvdi->item.mask & LVIF_STATE)
@@ -1194,7 +1208,7 @@ void CPowerRenameListView::GetDisplayInfo(_In_ IPowerRenameManager* psrm, _Inout
plvdi->item.stateMask = LVIS_STATEIMAGEMASK;
bool isSelected = false;
renameItem->get_selected(&isSelected);
renameItem->GetSelected(&isSelected);
if (isSelected)
{
// Turn check box on
@@ -1210,14 +1224,14 @@ void CPowerRenameListView::GetDisplayInfo(_In_ IPowerRenameManager* psrm, _Inout
if (plvdi->item.mask & LVIF_PARAM)
{
int id = 0;
renameItem->get_id(&id);
renameItem->GetId(&id);
plvdi->item.lParam = static_cast<LPARAM>(id);
}
if (plvdi->item.mask & LVIF_INDENT)
{
UINT depth = 0;
renameItem->get_depth(&depth);
renameItem->GetDepth(&depth);
plvdi->item.iIndent = static_cast<int>(depth);
}
@@ -1226,16 +1240,16 @@ void CPowerRenameListView::GetDisplayInfo(_In_ IPowerRenameManager* psrm, _Inout
PWSTR subItemText = nullptr;
if (plvdi->item.iSubItem == COL_ORIGINAL_NAME)
{
renameItem->get_originalName(&subItemText);
renameItem->GetOriginalName(&subItemText);
}
else if (plvdi->item.iSubItem == COL_NEW_NAME)
{
DWORD flags = 0;
psrm->get_flags(&flags);
psrm->GetFlags(&flags);
bool shouldRename = false;
if (SUCCEEDED(renameItem->ShouldRenameItem(flags, &shouldRename)) && shouldRename)
{
renameItem->get_newName(&subItemText);
renameItem->GetNewName(&subItemText);
}
}
@@ -1261,7 +1275,11 @@ void CPowerRenameListView::RedrawItems(_In_ int first, _In_ int last)
void CPowerRenameListView::SetItemCount(_In_ UINT itemCount)
{
ListView_SetItemCount(m_hwndLV, itemCount);
if (m_itemCount != itemCount)
{
m_itemCount = itemCount;
ListView_SetItemCount(m_hwndLV, itemCount);
}
}
void CPowerRenameListView::_UpdateColumns()
@@ -1344,3 +1362,72 @@ void CPowerRenameListView::_UpdateHeaderCheckState(_In_ bool check)
Header_SetItem(hwndHeader, 0, &hdi);
}
}
void CPowerRenameListView::_UpdateHeaderFilterState(_In_ DWORD filter)
{
// Get a handle to the header of the columns
HWND hwndHeader = ListView_GetHeader(m_hwndLV);
if (hwndHeader)
{
wchar_t bufferOriginal[MAX_PATH] = { 0 };
bufferOriginal[0] = L'\0';
// Retrieve the existing header first so we
// don't trash the text already there
HDITEM hdiOriginal = { 0 };
hdiOriginal.mask = HDI_FORMAT | HDI_TEXT;
hdiOriginal.pszText = bufferOriginal;
hdiOriginal.cchTextMax = ARRAYSIZE(bufferOriginal);
Header_GetItem(hwndHeader, 0, &hdiOriginal);
if (filter == PowerRenameFilters::Selected || filter == PowerRenameFilters::FlagsApplicable)
{
hdiOriginal.fmt |= HDF_SORTDOWN;
}
else
{
hdiOriginal.fmt &= ~HDF_SORTDOWN;
}
Header_SetItem(hwndHeader, 0, &hdiOriginal);
wchar_t bufferRename[MAX_PATH] = { 0 };
bufferRename[0] = L'\0';
// Retrieve the existing header first so we
// don't trash the text already there
HDITEM hdiRename = { 0 };
hdiRename.mask = HDI_FORMAT | HDI_TEXT;
hdiRename.pszText = bufferRename;
hdiRename.cchTextMax = ARRAYSIZE(bufferRename);
Header_GetItem(hwndHeader, 1, &hdiRename);
if (filter == PowerRenameFilters::ShouldRename)
{
hdiRename.fmt |= HDF_SORTDOWN;
}
else
{
hdiRename.fmt &= ~HDF_SORTDOWN;
}
Header_SetItem(hwndHeader, 1, &hdiRename);
}
}
void CPowerRenameListView::OnColumnClick(_In_ IPowerRenameManager* psrm, _In_ int columnNumber)
{
DWORD filter = PowerRenameFilters::None;
psrm->SwitchFilter(columnNumber);
UINT visibleItemCount = 0;
psrm->GetVisibleItemCount(&visibleItemCount);
SetItemCount(visibleItemCount);
RedrawItems(0, visibleItemCount);
psrm->GetFilter(&filter);
_UpdateHeaderFilterState(filter);
}