[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; 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] * BrFolder_InsertItem [Internal]
* *
@ -335,7 +402,7 @@ BrFolder_Expand(
SetCapture(info->hWnd); SetCapture(info->hWnd);
SetCursor(LoadCursorW(NULL, (LPWSTR)IDC_WAIT)); SetCursor(LoadCursorW(NULL, (LPWSTR)IDC_WAIT));
CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidlTemp; CComHeapPtr<ITEMIDLIST_RELATIVE> pidlTemp;
ULONG ulFetched; ULONG ulFetched;
while (S_OK == pEnum->Next(1, &pidlTemp, &ulFetched)) while (S_OK == pEnum->Next(1, &pidlTemp, &ulFetched))
{ {
@ -474,7 +541,7 @@ BrFolder_Treeview_Rename(BrFolder *info, NMTVDISPINFOW *pnmtv)
return FALSE; return FALSE;
LPITEMIDLIST newFull; LPITEMIDLIST newFull;
if (SUCCEEDED(hr = SHILClone((LPITEMIDLIST)data->pidlFull, &newFull))) if (SUCCEEDED(hr = SHILClone(data->pidlFull, &newFull)))
{ {
ILRemoveLastID(newFull); ILRemoveLastID(newFull);
if (SUCCEEDED(hr = SHILAppend(newChild, &newFull))) if (SUCCEEDED(hr = SHILAppend(newChild, &newFull)))
@ -525,6 +592,9 @@ BrFolder_Delete(BrFolder *info, HTREEITEM hItem)
SHFileOperationW(&fileop); SHFileOperationW(&fileop);
} }
static void
BrFolder_Refresh(_Inout_ BrFolder *info);
static LRESULT static LRESULT
BrFolder_Treeview_Keydown(BrFolder *info, LPNMTVKEYDOWN keydown) BrFolder_Treeview_Keydown(BrFolder *info, LPNMTVKEYDOWN keydown)
{ {
@ -542,6 +612,17 @@ BrFolder_Treeview_Keydown(BrFolder *info, LPNMTVKEYDOWN keydown)
case VK_DELETE: case VK_DELETE:
BrFolder_Delete(info, hItem); BrFolder_Delete(info, hItem);
break; break;
case 'R':
{
if (GetKeyState(VK_CONTROL) < 0) // Ctrl+R
BrFolder_Refresh(info);
break;
}
case VK_F5:
{
BrFolder_Refresh(info);
break;
}
} }
return 0; return 0;
} }
@ -1024,24 +1105,63 @@ BrFolder_OnDestroy(BrFolder *info)
SHChangeNotifyDeregister(info->hChangeNotify); SHChangeNotifyDeregister(info->hChangeNotify);
} }
// Find a treeview node by recursively walking the treeview static void
static HTREEITEM BrFolder_RefreshRecurse(
BrFolder_FindItemByPidl(BrFolder *info, PCIDLIST_ABSOLUTE pidlFull, HTREEITEM hItem) _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)) // Insert new items
return hItem; if (SUCCEEDED(hrEnum))
for (hItem = TreeView_GetChild(info->hwndTreeView, hItem); hItem;
hItem = TreeView_GetNextSibling(info->hwndTreeView, hItem))
{ {
HTREEITEM newItem = BrFolder_FindItemByPidl(info, pidlFull, hItem); CComHeapPtr<ITEMIDLIST_RELATIVE> pidlTemp;
if (newItem) while (S_OK == pEnum->Next(1, &pidlTemp, NULL))
return newItem; {
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 static void
@ -1058,30 +1178,16 @@ BrFolder_OnChangeEx(
case SHCNE_DRIVEADD: case SHCNE_DRIVEADD:
case SHCNE_MKDIR: case SHCNE_MKDIR:
case SHCNE_CREATE: case SHCNE_CREATE:
{
// FIXME
break;
}
case SHCNE_DRIVEREMOVED: case SHCNE_DRIVEREMOVED:
case SHCNE_RMDIR: case SHCNE_RMDIR:
case SHCNE_DELETE: 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_RENAMEFOLDER:
case SHCNE_RENAMEITEM: case SHCNE_RENAMEITEM:
case SHCNE_UPDATEDIR: case SHCNE_UPDATEDIR:
case SHCNE_UPDATEITEM: case SHCNE_UPDATEITEM:
{ {
// FIXME // FIXME: Avoid full refresh and optimize for speed. Use pidl0 and pidl1
BrFolder_Refresh(info);
break; break;
} }
} }