From b50ab0dc68875458b7d520dfe60920293a7776f7 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Fri, 31 May 2024 22:40:56 +0900 Subject: [PATCH] [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. --- dll/win32/shell32/brfolder.cpp | 168 +++++++++++++++++++++++++++------ 1 file changed, 137 insertions(+), 31 deletions(-) diff --git a/dll/win32/shell32/brfolder.cpp b/dll/win32/shell32/brfolder.cpp index 0d3153de4fd..55b85ad90fe 100644 --- a/dll/win32/shell32/brfolder.cpp +++ b/dll/win32/shell32/brfolder.cpp @@ -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 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 pEnum; + if (BrFolder_GetChildrenEnum(info, pItemData, &pEnum) == S_OK) + { + CComHeapPtr 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 pidlTemp; + CComHeapPtr 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 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 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; } }