[SHELL32] brfolder.cpp: Refresh items (#6965)

Refresh the items in the tree-view
control of SHBrowseForFolderA/W
functions against change notifications.
JIRA issue: CORE-17340
- Add BrFolder_IsTreeItemInEnum and
  BrFolder_TreeItemHasThisChild helper
  functions.
- Add BrFolder_Refresh,
  BrFolder_RefreshRecurse, and
  BrFolder_UpdateItem functions to
  refresh items.
- Call BrFolder_Refresh in
  BrFolder_OnChangeEx function and on
  keyboard F5 and Ctrl+R actions.
This commit is contained in:
Katayama Hirofumi MZ 2024-05-31 22:40:56 +09:00 committed by GitHub
parent c3ec1b9ac6
commit b50ab0dc68
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -254,6 +254,73 @@ BrFolder_GetName(
return bSuccess;
}
static BOOL
BrFolder_IsTreeItemInEnum(
_Inout_ BrFolder *info,
_In_ HTREEITEM hItem,
_Inout_ IEnumIDList *pEnum)
{
BrItemData *pItemData = BrFolder_GetItemData(info, hItem);
if (!pItemData)
return FALSE;
pEnum->Reset();
CComHeapPtr<ITEMIDLIST_RELATIVE> pidlTemp;
while (pEnum->Next(1, &pidlTemp, NULL) == S_OK)
{
if (ILIsEqual(pidlTemp, pItemData->pidlChild))
return TRUE;
pidlTemp.Free();
}
return FALSE;
}
static BOOL
BrFolder_TreeItemHasThisChild(
_In_ BrFolder *info,
_In_ HTREEITEM hItem,
_Outptr_opt_ PITEMID_CHILD pidlChild)
{
for (hItem = TreeView_GetChild(info->hwndTreeView, hItem); hItem;
hItem = TreeView_GetNextSibling(info->hwndTreeView, hItem))
{
BrItemData *pItemData = BrFolder_GetItemData(info, hItem);
if (ILIsEqual(pItemData->pidlChild, pidlChild))
return TRUE;
}
return FALSE;
}
static void
BrFolder_UpdateItem(
_In_ BrFolder *info,
_In_ HTREEITEM hItem)
{
BrItemData *pItemData = BrFolder_GetItemData(info, hItem);
if (!pItemData)
return;
TVITEMW item = { TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN };
item.hItem = hItem;
BrFolder_GetIconPair(pItemData->pidlFull, &item);
item.cChildren = 0;
CComPtr<IEnumIDList> pEnum;
if (BrFolder_GetChildrenEnum(info, pItemData, &pEnum) == S_OK)
{
CComHeapPtr<ITEMIDLIST_RELATIVE> pidlTemp;
if (pEnum->Next(1, &pidlTemp, NULL) == S_OK)
++item.cChildren;
}
TreeView_SetItem(info->hwndTreeView, &item);
}
/******************************************************************************
* BrFolder_InsertItem [Internal]
*
@ -335,7 +402,7 @@ BrFolder_Expand(
SetCapture(info->hWnd);
SetCursor(LoadCursorW(NULL, (LPWSTR)IDC_WAIT));
CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidlTemp;
CComHeapPtr<ITEMIDLIST_RELATIVE> pidlTemp;
ULONG ulFetched;
while (S_OK == pEnum->Next(1, &pidlTemp, &ulFetched))
{
@ -474,7 +541,7 @@ BrFolder_Treeview_Rename(BrFolder *info, NMTVDISPINFOW *pnmtv)
return FALSE;
LPITEMIDLIST newFull;
if (SUCCEEDED(hr = SHILClone((LPITEMIDLIST)data->pidlFull, &newFull)))
if (SUCCEEDED(hr = SHILClone(data->pidlFull, &newFull)))
{
ILRemoveLastID(newFull);
if (SUCCEEDED(hr = SHILAppend(newChild, &newFull)))
@ -525,6 +592,9 @@ BrFolder_Delete(BrFolder *info, HTREEITEM hItem)
SHFileOperationW(&fileop);
}
static void
BrFolder_Refresh(_Inout_ BrFolder *info);
static LRESULT
BrFolder_Treeview_Keydown(BrFolder *info, LPNMTVKEYDOWN keydown)
{
@ -542,6 +612,17 @@ BrFolder_Treeview_Keydown(BrFolder *info, LPNMTVKEYDOWN keydown)
case VK_DELETE:
BrFolder_Delete(info, hItem);
break;
case 'R':
{
if (GetKeyState(VK_CONTROL) < 0) // Ctrl+R
BrFolder_Refresh(info);
break;
}
case VK_F5:
{
BrFolder_Refresh(info);
break;
}
}
return 0;
}
@ -1024,24 +1105,63 @@ BrFolder_OnDestroy(BrFolder *info)
SHChangeNotifyDeregister(info->hChangeNotify);
}
// Find a treeview node by recursively walking the treeview
static HTREEITEM
BrFolder_FindItemByPidl(BrFolder *info, PCIDLIST_ABSOLUTE pidlFull, HTREEITEM hItem)
static void
BrFolder_RefreshRecurse(
_Inout_ BrFolder *info,
_In_ HTREEITEM hTarget)
{
BrItemData *item_data = BrFolder_GetItemData(info, hItem);
// Get enum
CComPtr<IEnumIDList> pEnum;
BrItemData *pItemData = BrFolder_GetItemData(info, hTarget);
HRESULT hrEnum = BrFolder_GetChildrenEnum(info, pItemData, &pEnum);
if (ILIsEqual(item_data->pidlFull, pidlFull))
return hItem;
for (hItem = TreeView_GetChild(info->hwndTreeView, hItem); hItem;
hItem = TreeView_GetNextSibling(info->hwndTreeView, hItem))
// Insert new items
if (SUCCEEDED(hrEnum))
{
HTREEITEM newItem = BrFolder_FindItemByPidl(info, pidlFull, hItem);
if (newItem)
return newItem;
CComHeapPtr<ITEMIDLIST_RELATIVE> pidlTemp;
while (S_OK == pEnum->Next(1, &pidlTemp, NULL))
{
if (!BrFolder_TreeItemHasThisChild(info, hTarget, pidlTemp))
{
BrFolder_InsertItem(info, pItemData->lpsfParent, pidlTemp, pItemData->pidlFull,
hTarget);
}
pidlTemp.Free();
}
}
return NULL;
// Delete zombie items and update items
HTREEITEM hItem, hNextItem;
for (hItem = TreeView_GetChild(info->hwndTreeView, hTarget); hItem; hItem = hNextItem)
{
hNextItem = TreeView_GetNextSibling(info->hwndTreeView, hItem);
if (FAILED(hrEnum) || !BrFolder_IsTreeItemInEnum(info, hItem, pEnum))
{
TreeView_DeleteItem(info->hwndTreeView, hItem);
hNextItem = TreeView_GetChild(info->hwndTreeView, hTarget);
continue;
}
BrFolder_UpdateItem(info, hItem);
BrFolder_RefreshRecurse(info, hItem);
}
if (SUCCEEDED(hrEnum))
TreeView_SortChildren(info->hwndTreeView, hTarget, FALSE);
}
static void
BrFolder_Refresh(_Inout_ BrFolder *info)
{
HTREEITEM hRoot = TreeView_GetRoot(info->hwndTreeView);
SendMessageW(info->hwndTreeView, WM_SETREDRAW, FALSE, 0);
BrFolder_RefreshRecurse(info, hRoot);
SendMessageW(info->hwndTreeView, WM_SETREDRAW, TRUE, 0);
InvalidateRect(info->hwndTreeView, NULL, TRUE);
}
static void
@ -1058,30 +1178,16 @@ BrFolder_OnChangeEx(
case SHCNE_DRIVEADD:
case SHCNE_MKDIR:
case SHCNE_CREATE:
{
// FIXME
break;
}
case SHCNE_DRIVEREMOVED:
case SHCNE_RMDIR:
case SHCNE_DELETE:
{
HTREEITEM hRoot = TreeView_GetRoot(info->hwndTreeView);
for (;;)
{
HTREEITEM hItem = BrFolder_FindItemByPidl(info, pidl0, hRoot);
if (!hItem)
break;
TreeView_DeleteItem(info->hwndTreeView, hItem);
}
break;
}
case SHCNE_RENAMEFOLDER:
case SHCNE_RENAMEITEM:
case SHCNE_UPDATEDIR:
case SHCNE_UPDATEITEM:
{
// FIXME
// FIXME: Avoid full refresh and optimize for speed. Use pidl0 and pidl1
BrFolder_Refresh(info);
break;
}
}