Something went wrong. Try again.
Reactos
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489/* * PROJECT: ReactOS shell32 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) * PURPOSE: SHBrowseForFolderA/W functions * COPYRIGHT: Copyright 1999 Juergen Schmied * Copyright 2024 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> */
// FIXME: Many flags unimplemented
#include "precomp.h"
#include <ui/layout.h> // Resizable window#include <compat_undoc.h> // RosGetProcessEffectiveVersion
WINE_DEFAULT_DEBUG_CHANNEL(shell);
INT WINAPI Shell_GetCachedImageIndexW(LPCWSTR szPath, INT nIndex, UINT Flags);
#define SHV_CHANGE_NOTIFY (WM_USER + 0x1111)enum { SHGDN_TREE = SHGDN_NORMAL | SHGDN_INFOLDER };
static LPITEMIDLISTILCloneToDepth(LPCITEMIDLIST pidlSrc, UINT depth){ SIZE_T cb = 0; for (LPCITEMIDLIST pidl = pidlSrc; pidl && depth--; pidl = ILGetNext(pidl)) cb += pidl->mkid.cb;
LPITEMIDLIST pidlOut = (LPITEMIDLIST)SHAlloc(cb + sizeof(WORD)); if (pidlOut) { CopyMemory(pidlOut, pidlSrc, cb); ZeroMemory(((BYTE*)pidlOut) + cb, sizeof(WORD)); } return pidlOut;}
struct BrFolder{ LPBROWSEINFOW lpBrowseInfo; HWND hWnd; HWND hwndTreeView; PIDLIST_ABSOLUTE pidlRet; LAYOUT_DATA* layout; // Filled by LayoutInit, used by LayoutUpdate SIZE szMin; ULONG hChangeNotify; // Change notification handle IContextMenu* pContextMenu; // Active context menu};
struct BrItemData{ CComPtr<IShellFolder> lpsfParent; // IShellFolder of the parent PCIDLIST_RELATIVE pidlChild; // PIDL relative to parent CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidlFull; // Fully qualified PIDL};
static const LAYOUT_INFO g_layout_info[] ={ { IDC_BROWSE_FOR_FOLDER_TITLE, BF_TOP | BF_LEFT | BF_RIGHT }, { IDC_BROWSE_FOR_FOLDER_STATUS, BF_TOP | BF_LEFT | BF_RIGHT }, { IDC_BROWSE_FOR_FOLDER_TREEVIEW, BF_TOP | BF_BOTTOM | BF_LEFT | BF_RIGHT }, { IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT, BF_TOP | BF_LEFT | BF_RIGHT }, { IDC_BROWSE_FOR_FOLDER_NEW_FOLDER, BF_BOTTOM | BF_LEFT }, { IDOK, BF_BOTTOM | BF_RIGHT }, { IDCANCEL, BF_BOTTOM | BF_RIGHT },};
#define SUPPORTED_FLAGS (BIF_STATUSTEXT | BIF_BROWSEFORCOMPUTER | BIF_RETURNFSANCESTORS | \ BIF_RETURNONLYFSDIRS | BIF_NONEWFOLDERBUTTON | BIF_NEWDIALOGSTYLE | \ BIF_BROWSEINCLUDEFILES)
static HTREEITEMBrFolder_InsertItem( _Inout_ BrFolder *info, _Inout_ IShellFolder *lpsf, _In_ PCUITEMID_CHILD pidlChild, _In_ PCIDLIST_ABSOLUTE pidlParent, _In_ HTREEITEM hParent);
static inline DWORDBrowseFlagsToSHCONTF(UINT ulFlags){ return SHCONTF_FOLDERS | ((ulFlags & BIF_BROWSEINCLUDEFILES) ? SHCONTF_NONFOLDERS : 0);}
static voidBrFolder_Callback(LPBROWSEINFOW lpBrowseInfo, HWND hWnd, UINT uMsg, LPARAM lParam){ if (!lpBrowseInfo->lpfn) return; lpBrowseInfo->lpfn(hWnd, uMsg, lParam, lpBrowseInfo->lParam);}
static BrItemData *BrFolder_GetItemData(BrFolder *info, HTREEITEM hItem){ TVITEMW item = { TVIF_HANDLE | TVIF_PARAM }; item.hItem = hItem; if (!TreeView_GetItem(info->hwndTreeView, &item)) { ERR("TreeView_GetItem failed\n"); return NULL; } return (BrItemData *)item.lParam;}
static SFGAOFBrFolder_GetItemAttributes(BrFolder *info, HTREEITEM hItem, SFGAOF Query){ BrItemData *item = BrFolder_GetItemData(info, hItem); SFGAOF Att = Query; HRESULT hr = item ? item->lpsfParent->GetAttributesOf(1, &item->pidlChild, &Att) : E_FAIL; return SUCCEEDED(hr) ? (Query & Att) : 0;}
static HRESULTBrFolder_GetChildrenEnum( _In_ BrFolder *info, _In_ BrItemData *pItemData, _Outptr_opt_ IEnumIDList **ppEnum){ if (!pItemData) return E_FAIL;
CComPtr<IEnumIDList> pEnumIL; PCUITEMID_CHILD pidlRef = pItemData->pidlChild; ULONG attrs = SFGAO_HASSUBFOLDER | SFGAO_FOLDER; IShellFolder *lpsf = pItemData->lpsfParent; HRESULT hr = lpsf->GetAttributesOf(1, &pidlRef, &attrs); if (FAILED_UNEXPECTEDLY(hr) || !(attrs & SFGAO_FOLDER)) return E_FAIL;
CComPtr<IShellFolder> psfChild; if (_ILIsDesktop(pItemData->pidlFull)) hr = SHGetDesktopFolder(&psfChild); else hr = lpsf->BindToObject(pidlRef, NULL, IID_PPV_ARG(IShellFolder, &psfChild)); if (FAILED_UNEXPECTEDLY(hr)) return E_FAIL;
DWORD flags = BrowseFlagsToSHCONTF(info->lpBrowseInfo->ulFlags); hr = psfChild->EnumObjects(info->hWnd, flags, &pEnumIL); if (hr == S_OK) { if ((pEnumIL->Skip(1) != S_OK) || FAILED(pEnumIL->Reset())) pEnumIL = NULL; }
if (!pEnumIL || !(attrs & SFGAO_HASSUBFOLDER)) return E_FAIL; // No children
if (ppEnum) *ppEnum = pEnumIL.Detach();
return S_OK; // There are children}
/****************************************************************************** * BrFolder_InitTreeView [Internal] * * Called from WM_INITDIALOG handler. */static voidBrFolder_InitTreeView(BrFolder *info){ HIMAGELIST hImageList; HRESULT hr; HTREEITEM hItem;
Shell_GetImageLists(NULL, &hImageList); if (hImageList) TreeView_SetImageList(info->hwndTreeView, hImageList, 0);
/* We want to call BrFolder_InsertItem down the code, in order to insert * the root item of the treeview. Due to BrFolder_InsertItem's signature, * we need the following to do this: * * + An ITEMIDLIST corresponding to _the parent_ of root. * + An ITEMIDLIST, which is a relative path from root's parent to root * (containing a single SHITEMID). * + An IShellFolder interface pointer of root's parent folder. * * If root is 'Desktop', then root's parent is also 'Desktop'. */
PCIDLIST_ABSOLUTE pidlRoot = info->lpBrowseInfo->pidlRoot; CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidlParent(ILClone(pidlRoot)); ILRemoveLastID(pidlParent); PCIDLIST_RELATIVE pidlChild = ILFindLastID(pidlRoot);
CComPtr<IShellFolder> lpsfParent; hr = SHBindToObject(NULL, pidlParent, /*NULL, */ IID_PPV_ARG(IShellFolder, &lpsfParent)); if (FAILED_UNEXPECTEDLY(hr)) return;
TreeView_DeleteItem(info->hwndTreeView, TVI_ROOT); hItem = BrFolder_InsertItem(info, lpsfParent, pidlChild, pidlParent, TVI_ROOT); TreeView_Expand(info->hwndTreeView, hItem, TVE_EXPAND);}
static HRESULTBrFolder_GetIconPair(BrItemData &item, TVITEMW &tvi){ INT open = -1; INT normal = SHMapPIDLToSystemImageListIndex(item.lpsfParent, item.pidlChild, &open); if (normal >= 0) { tvi.iImage = normal; tvi.iSelectedImage = (open >= 0) ? open : normal; return S_OK; } tvi.iImage = tvi.iSelectedImage = Shell_GetCachedImageIndexW(swShell32Name, IDI_SHELL_FOLDER - 1, 0); return S_FALSE;}
/****************************************************************************** * BrFolder_GetName [Internal] * * Query a shell folder for the display name of one of its children * * PARAMS * lpsf [I] IShellFolder interface of the folder to be queried. * pidlChild [I] ITEMIDLIST of the child, relative to parent * dwFlags [I] as in IShellFolder::GetDisplayNameOf * lpFriendlyName [O] The desired display name in unicode * * RETURNS * Success: TRUE * Failure: FALSE */static BOOLBrFolder_GetName( _In_ IShellFolder *lpsf, _In_ PCIDLIST_RELATIVE pidlChild, _In_ DWORD dwFlags, _Out_ PWSTR lpFriendlyName){ BOOL bSuccess = FALSE; STRRET str;
TRACE("%p %p %x %p\n", lpsf, pidlChild, dwFlags, lpFriendlyName); if (!FAILED_UNEXPECTEDLY(lpsf->GetDisplayNameOf(pidlChild, dwFlags, &str))) bSuccess = StrRetToStrNW(lpFriendlyName, MAX_PATH, &str, pidlChild);
TRACE("-- %s\n", debugstr_w(lpFriendlyName)); return bSuccess;}
static BOOLBrFolder_GetName( _In_ BrFolder *info, _In_ HTREEITEM hItem, _In_ UINT Flags, _Out_ PWSTR Buffer){ if (BrItemData *item = BrFolder_GetItemData(info, hItem)) return BrFolder_GetName(item->lpsfParent, item->pidlChild, Flags, Buffer); return FALSE;}
static BOOLBrFolder_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 BOOLBrFolder_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 HTREEITEMBrFolder_FindTreeItemOfAbsoluteItem( _In_ BrFolder &info, _In_ PCIDLIST_ABSOLUTE pidl, _In_opt_ HTREEITEM hItem = NULL){ if (!hItem) hItem = TreeView_GetRoot(info.hwndTreeView);
for (; pidl && hItem; hItem = TreeView_GetNextSibling(info.hwndTreeView, hItem)) { BrItemData *pItemData = BrFolder_GetItemData(&info, hItem); if (ILIsEqual(pItemData->pidlFull, pidl)) return hItem; HTREEITEM hChild = TreeView_GetChild(info.hwndTreeView, hItem); if (hChild && (hChild = BrFolder_FindTreeItemOfAbsoluteItem(info, pidl, hChild)) != NULL) return hChild; } return NULL;}
static BOOLBrFolder_UpdateItemEx( _In_ BrFolder &info, _In_ HTREEITEM hItem, _In_opt_ PCIDLIST_ABSOLUTE pidlFull, _In_ UINT Flags = TVIF_TEXT | TVIF_IMAGE | TVIF_CHILDREN){ ASSERT(hItem); TVITEMW item = { (TVIF_HANDLE | Flags), hItem }; BrItemData *data = BrFolder_GetItemData(&info, hItem); if (!data) return FALSE;
if (pidlFull) { if ((pidlFull = ILClone(pidlFull)) == NULL) return FALSE; data->pidlFull.Free(); data->pidlFull.Attach(const_cast<PIDLIST_ABSOLUTE>(pidlFull)); data->pidlChild = ILFindLastID(data->pidlFull); // Note: We assume lpsfParent does not change }
WCHAR Name[MAX_PATH]; if (Flags & TVIF_TEXT) { item.pszText = Name; if (!BrFolder_GetName(data->lpsfParent, data->pidlChild, SHGDN_TREE, Name)) return FALSE; }
if (Flags & TVIF_IMAGE) { item.mask |= TVIF_SELECTEDIMAGE; BrFolder_GetIconPair(*data, item); }
item.cChildren = 0; CComPtr<IEnumIDList> pEnum; if ((Flags & TVIF_CHILDREN) && BrFolder_GetChildrenEnum(&info, data, &pEnum) == S_OK) { CComHeapPtr<ITEMIDLIST_RELATIVE> pidlTemp; if (pEnum->Next(1, &pidlTemp, NULL) == S_OK) ++item.cChildren; }
return TreeView_SetItem(info.hwndTreeView, &item);}
static voidBrFolder_UpdateItem( _In_ BrFolder *info, _In_ HTREEITEM hItem){ BrFolder_UpdateItemEx(*info, hItem, NULL, TVIF_IMAGE | TVIF_CHILDREN);}
/****************************************************************************** * BrFolder_InsertItem [Internal] * * PARAMS * info [I] data for the dialog * lpsf [I] IShellFolder interface of the item's parent shell folder * pidlChild [I] ITEMIDLIST of the child to insert, relative to parent * pidlParent [I] ITEMIDLIST of the parent shell folder * hParent [I] The treeview-item that represents the parent shell folder * * RETURNS * Success: Handle to the created and inserted treeview-item * Failure: NULL */static HTREEITEMBrFolder_InsertItem( _Inout_ BrFolder *info, _Inout_ IShellFolder *lpsf, _In_ PCUITEMID_CHILD pidlChild, _In_ PCIDLIST_ABSOLUTE pidlParent, _In_ HTREEITEM hParent){ if (!(BrowseFlagsToSHCONTF(info->lpBrowseInfo->ulFlags) & SHCONTF_NONFOLDERS)) {#ifdef BIF_BROWSEFILEJUNCTIONS if (!(info->lpBrowseInfo->ulFlags & BIF_BROWSEFILEJUNCTIONS))#endif if (SHGetAttributes(lpsf, pidlChild, SFGAO_STREAM) & SFGAO_STREAM) return NULL; // .zip files have both FOLDER and STREAM attributes set }
WCHAR szName[MAX_PATH]; if (!BrFolder_GetName(lpsf, pidlChild, SHGDN_TREE, szName)) return NULL;
BrItemData *pItemData = new BrItemData();
TVITEMW item = { TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_CHILDREN }; item.pszText = szName; item.cchTextMax = _countof(szName); item.lParam = (LPARAM)pItemData;
PIDLIST_ABSOLUTE pidlFull = (pidlParent ? ILCombine(pidlParent, pidlChild) : ILClone(pidlChild));
pItemData->lpsfParent = lpsf; pItemData->pidlFull.Attach(pidlFull); pItemData->pidlChild = ILFindLastID(pItemData->pidlFull); BrFolder_GetIconPair(*pItemData, item);
if (BrFolder_GetChildrenEnum(info, pItemData, NULL) == S_OK) item.cChildren = 1;
TVINSERTSTRUCTW tvins = { hParent }; tvins.item = item; return TreeView_InsertItem(info->hwndTreeView, &tvins);}
/****************************************************************************** * BrFolder_Expand [Internal] * * For each child (given by pEnum) of the parent shell folder, which is given by * lpsf and whose PIDL is pidl, insert a treeview-item right under hParent * * PARAMS * info [I] data for the dialog * lpsf [I] IShellFolder interface of the parent shell folder * pidl [I] ITEMIDLIST of the parent shell folder * hParent [I] The treeview item that represents the parent shell folder * pEnum [I] An iterator for the children of the parent shell folder */static voidBrFolder_Expand( _In_ BrFolder *info, _In_ IShellFolder *lpsf, _In_ PCIDLIST_ABSOLUTE pidlFull, _In_ HTREEITEM hParent){ TRACE("%p %p %p\n", lpsf, pidlFull, hParent);
// No IEnumIDList -> No children BrItemData *pItemData = BrFolder_GetItemData(info, hParent); CComPtr<IEnumIDList> pEnum; HRESULT hr = BrFolder_GetChildrenEnum(info, pItemData, &pEnum); if (FAILED(hr)) return;
SetCapture(info->hWnd); SetCursor(LoadCursorW(NULL, (LPWSTR)IDC_WAIT));
CComHeapPtr<ITEMIDLIST_RELATIVE> pidlTemp; ULONG ulFetched; while (S_OK == pEnum->Next(1, &pidlTemp, &ulFetched)) { /* Ignore return value of BrFolder_InsertItem to avoid incomplete folder listing */ BrFolder_InsertItem(info, lpsf, pidlTemp, pidlFull, hParent); pidlTemp.Free(); // Finally, free the pidl that the shell gave us... }
ReleaseCapture(); SetCursor(LoadCursorW(NULL, (LPWSTR)IDC_ARROW));}
static inline BOOLPIDLIsType(LPCITEMIDLIST pidl, PIDLTYPE type){ LPPIDLDATA data = _ILGetDataPointer(pidl); if (!data) return FALSE; return (data->type == type);}
static voidBrFolder_CheckValidSelection(BrFolder *info, BrItemData *pItemData){ LPBROWSEINFOW lpBrowseInfo = info->lpBrowseInfo; PCIDLIST_RELATIVE pidlChild = pItemData->pidlChild; DWORD dwAttributes; HRESULT hr;
BOOL bEnabled = TRUE; if ((lpBrowseInfo->ulFlags & BIF_BROWSEFORCOMPUTER) && !PIDLIsType(pidlChild, PT_COMP)) bEnabled = FALSE;
if (lpBrowseInfo->ulFlags & BIF_RETURNFSANCESTORS) { dwAttributes = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM; hr = pItemData->lpsfParent->GetAttributesOf(1, &pidlChild, &dwAttributes); if (FAILED(hr) || !(dwAttributes & (SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM))) bEnabled = FALSE; }
dwAttributes = SFGAO_FOLDER | SFGAO_FILESYSTEM; hr = pItemData->lpsfParent->GetAttributesOf(1, &pidlChild, &dwAttributes); if (FAILED_UNEXPECTEDLY(hr) || ((dwAttributes & (SFGAO_FOLDER | SFGAO_FILESYSTEM)) != (SFGAO_FOLDER | SFGAO_FILESYSTEM))) { if (lpBrowseInfo->ulFlags & BIF_RETURNONLYFSDIRS) bEnabled = FALSE; EnableWindow(GetDlgItem(info->hWnd, IDC_BROWSE_FOR_FOLDER_NEW_FOLDER), FALSE); } else { EnableWindow(GetDlgItem(info->hWnd, IDC_BROWSE_FOR_FOLDER_NEW_FOLDER), TRUE); }
SendMessageW(info->hWnd, BFFM_ENABLEOK, 0, bEnabled);}
static LRESULTBrFolder_Treeview_Delete(BrFolder *info, NMTREEVIEWW *pnmtv){ BrItemData *pItemData = (BrItemData *)pnmtv->itemOld.lParam;
TRACE("TVN_DELETEITEMA/W %p\n", pItemData);
delete pItemData; return 0;}
static LRESULTBrFolder_Treeview_Expand(BrFolder *info, NMTREEVIEWW *pnmtv){ BrItemData *pItemData = (BrItemData *)pnmtv->itemNew.lParam;
TRACE("TVN_ITEMEXPANDINGA/W\n");
if ((pnmtv->itemNew.state & TVIS_EXPANDEDONCE)) return 0;
HRESULT hr = S_OK; CComPtr<IShellFolder> lpsf2; if (!_ILIsEmpty(pItemData->pidlChild)) { hr = pItemData->lpsfParent->BindToObject(pItemData->pidlChild, NULL, IID_PPV_ARG(IShellFolder, &lpsf2)); } else { lpsf2 = pItemData->lpsfParent; }
HTREEITEM hItem = pnmtv->itemNew.hItem; if (!FAILED_UNEXPECTEDLY(hr)) BrFolder_Expand(info, lpsf2, pItemData->pidlFull, hItem);
// My Computer is already sorted and trying to do a simple text // sort will only mess things up if (!_ILIsMyComputer(pItemData->pidlChild)) TreeView_SortChildren(info->hwndTreeView, hItem, FALSE);
return 0;}
static HRESULTBrFolder_Treeview_Changed(BrFolder *info, NMTREEVIEWW *pnmtv){ BrItemData *pItemData = (BrItemData *)pnmtv->itemNew.lParam;
ILFree(info->pidlRet); info->pidlRet = ILClone(pItemData->pidlFull);
WCHAR szName[MAX_PATH]; if (BrFolder_GetName(pItemData->lpsfParent, pItemData->pidlChild, SHGDN_NORMAL | SHGDN_INFOLDER, szName)) SetDlgItemTextW(info->hWnd, IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT, szName);
BrFolder_Callback(info->lpBrowseInfo, info->hWnd, BFFM_SELCHANGED, (LPARAM)info->pidlRet); BrFolder_CheckValidSelection(info, pItemData); return S_OK;}
static LRESULTBrFolder_Treeview_Rename(BrFolder *info, NMTVDISPINFOW *pnmtv){ if (!pnmtv->item.pszText) return FALSE;
HTREEITEM hItem = TreeView_GetSelection(info->hwndTreeView); BrItemData *data = BrFolder_GetItemData(info, hItem); ASSERT(data); ASSERT(BrFolder_GetItemAttributes(info, hItem, SFGAO_CANRENAME) & SFGAO_CANRENAME);
PITEMID_CHILD newChild; HRESULT hr = data->lpsfParent->SetNameOf(info->hWnd, data->pidlChild, pnmtv->item.pszText, SHGDN_NORMAL | SHGDN_INFOLDER, &newChild); if (FAILED(hr)) return FALSE;
LPITEMIDLIST newFull; if (SUCCEEDED(hr = SHILClone(data->pidlFull, &newFull))) { ILRemoveLastID(newFull); if (SUCCEEDED(hr = SHILAppend(newChild, &newFull))) { data->pidlFull.Free(); data->pidlFull.Attach(newFull); data->pidlChild = ILFindLastID(data->pidlFull); } newChild = NULL; // SHILAppend is nuts and frees this } ILFree(newChild);
BOOL AllowTreeTextChange = !BrFolder_UpdateItemEx(*info, hItem, NULL, TVIF_TEXT);
NMTREEVIEWW nmtv; nmtv.itemNew.hItem = hItem; nmtv.itemNew.mask = TVIF_PARAM; nmtv.itemNew.lParam = (LPARAM)data; BrFolder_Treeview_Changed(info, &nmtv); return AllowTreeTextChange;}
static HRESULTBrFolder_Rename(BrFolder *info, HTREEITEM hItem){ TreeView_SelectItem(info->hwndTreeView, hItem); TreeView_EditLabel(info->hwndTreeView, hItem); return S_OK;}
static voidBrFolder_Delete(BrFolder *info, HTREEITEM hItem){ BrItemData *data = BrFolder_GetItemData(info, hItem); if (!data || !BrFolder_GetItemAttributes(info, hItem, SFGAO_CANDELETE)) return; CComPtr<IContextMenu> pCM; if (FAILED(data->lpsfParent->GetUIObjectOf(info->hWnd, 1, &data->pidlChild, IID_NULL_PPV_ARG(IContextMenu, &pCM)))) return; UINT fCMIC = 0; if (GetKeyState(VK_SHIFT) < 0) fCMIC |= CMIC_MASK_SHIFT_DOWN; if (GetKeyState(VK_CONTROL) < 0) fCMIC |= CMIC_MASK_CONTROL_DOWN; SHInvokeCommandOnContextMenu(info->hWnd, NULL, pCM, fCMIC, "delete");}
static voidBrFolder_Refresh(_Inout_ BrFolder *info);
static LRESULTBrFolder_Treeview_Keydown(BrFolder *info, LPNMTVKEYDOWN keydown){ // Old dialog doesn't support those advanced features if (!(info->lpBrowseInfo->ulFlags & BIF_USENEWUI)) return 0;
HTREEITEM hItem = TreeView_GetSelection(info->hwndTreeView);
switch (keydown->wVKey) { case VK_F2: BrFolder_Rename(info, hItem); break; 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;}
static LRESULTBrFolder_OnNotify(BrFolder *info, UINT CtlID, LPNMHDR lpnmh){ NMTREEVIEWW *pnmtv = (NMTREEVIEWW *)lpnmh;
TRACE("%p %x %p msg=%x\n", info, CtlID, lpnmh, pnmtv->hdr.code);
if (pnmtv->hdr.idFrom != IDC_BROWSE_FOR_FOLDER_TREEVIEW) return 0;
switch (pnmtv->hdr.code) { case TVN_DELETEITEMA: case TVN_DELETEITEMW: return BrFolder_Treeview_Delete(info, pnmtv);
case TVN_ITEMEXPANDINGA: case TVN_ITEMEXPANDINGW: return BrFolder_Treeview_Expand(info, pnmtv);
case TVN_SELCHANGEDA: case TVN_SELCHANGEDW: return BrFolder_Treeview_Changed(info, pnmtv);
case TVN_BEGINLABELEDITA: case TVN_BEGINLABELEDITW: { HWND hWndEdit = TreeView_GetEditControl(lpnmh->hwndFrom); NMTVDISPINFO &tvdi = *(NMTVDISPINFO*)lpnmh; UINT att = BrFolder_GetItemAttributes(info, tvdi.item.hItem, SFGAO_CANRENAME); if (!(att & SFGAO_CANRENAME)) { MessageBeep(0xffffffff); return S_FALSE; } WCHAR Name[MAX_PATH]; if (BrFolder_GetName(info, tvdi.item.hItem, SHGDN_INFOLDER | SHGDN_FOREDITING, Name)) SetWindowTextW(hWndEdit, Name); return S_OK; }
case TVN_ENDLABELEDITW: return BrFolder_Treeview_Rename(info, (LPNMTVDISPINFOW)pnmtv);
case TVN_KEYDOWN: return BrFolder_Treeview_Keydown(info, (LPNMTVKEYDOWN)pnmtv);
default: WARN("unhandled (%d)\n", pnmtv->hdr.code); break; }
return 0;}
static BOOLBrFolder_OnInitDialog(HWND hWnd, BrFolder *info){ CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidlDesktop; SHChangeNotifyEntry ntreg; LPBROWSEINFOW lpBrowseInfo = info->lpBrowseInfo;
info->hWnd = hWnd; SetWindowLongPtrW(hWnd, DWLP_USER, (LONG_PTR)info);
if (lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE) FIXME("flags BIF_NEWDIALOGSTYLE partially implemented\n");
if (lpBrowseInfo->ulFlags & ~SUPPORTED_FLAGS) FIXME("flags %x not implemented\n", (lpBrowseInfo->ulFlags & ~SUPPORTED_FLAGS));
info->layout = NULL; if (lpBrowseInfo->ulFlags & BIF_USENEWUI) { RECT rcWnd;
// Resize the treeview if there's not editbox if ((lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE) && !(lpBrowseInfo->ulFlags & BIF_EDITBOX)) { RECT rcEdit, rcTreeView; GetWindowRect(GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT), &rcEdit); GetWindowRect(GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_TREEVIEW), &rcTreeView); LONG cy = rcTreeView.top - rcEdit.top; MapWindowPoints(NULL, hWnd, (LPPOINT)&rcTreeView, sizeof(RECT) / sizeof(POINT)); rcTreeView.top -= cy; MoveWindow(GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_TREEVIEW), rcTreeView.left, rcTreeView.top, rcTreeView.right - rcTreeView.left, rcTreeView.bottom - rcTreeView.top, TRUE); }
if (lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE) info->layout = LayoutInit(hWnd, g_layout_info, _countof(g_layout_info));
// TODO: Windows allows shrinking the windows a bit GetWindowRect(hWnd, &rcWnd); info->szMin.cx = rcWnd.right - rcWnd.left; info->szMin.cy = rcWnd.bottom - rcWnd.top; }
if (lpBrowseInfo->lpszTitle) SetDlgItemTextW(hWnd, IDC_BROWSE_FOR_FOLDER_TITLE, lpBrowseInfo->lpszTitle); else ShowWindow(GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_TITLE), SW_HIDE);
if (!(lpBrowseInfo->ulFlags & BIF_STATUSTEXT) || (lpBrowseInfo->ulFlags & BIF_USENEWUI)) ShowWindow(GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_STATUS), SW_HIDE);
// Hide "Make New Folder" Button? if ((lpBrowseInfo->ulFlags & BIF_NONEWFOLDERBUTTON) || !(lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE)) { ShowWindow(GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_NEW_FOLDER), SW_HIDE); }
// Hide the editbox? if (!(lpBrowseInfo->ulFlags & BIF_EDITBOX)) { ShowWindow(GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_FOLDER), SW_HIDE); ShowWindow(GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT), SW_HIDE); }
info->hwndTreeView = GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_TREEVIEW); if (info->hwndTreeView) BrFolder_InitTreeView(info); else ERR("treeview control missing!\n");
// Register for change notifications SHGetFolderLocation(NULL, CSIDL_DESKTOP, NULL, 0, &pidlDesktop);
ntreg.pidl = pidlDesktop; ntreg.fRecursive = TRUE; info->hChangeNotify = SHChangeNotifyRegister(hWnd, SHCNRF_ShellLevel | SHCNRF_NewDelivery, SHCNE_ALLEVENTS, SHV_CHANGE_NOTIFY, 1, &ntreg);
if (!lpBrowseInfo->pidlRoot) { UINT csidl = (lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE) ? CSIDL_PERSONAL : CSIDL_DRIVES; LPITEMIDLIST pidl = SHCloneSpecialIDList(NULL, csidl, TRUE); if (pidl) { SendMessageW(info->hWnd, BFFM_SETSELECTION, FALSE, (LPARAM)pidl); if (csidl == CSIDL_DRIVES) SendMessageW(info->hWnd, BFFM_SETEXPANDED, FALSE, (LPARAM)pidl); ILFree(pidl); } }
BrFolder_Callback(info->lpBrowseInfo, hWnd, BFFM_INITIALIZED, 0);
SHAutoComplete(GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT), (SHACF_FILESYS_ONLY | SHACF_URLHISTORY | SHACF_FILESYSTEM)); return TRUE;}
static HRESULTBrFolder_NewFolder(BrFolder *info){ CComPtr<IShellFolder> desktop, cur; WCHAR wszNewFolder[25], path[MAX_PATH], name[MAX_PATH];
HRESULT hr = SHGetDesktopFolder(&desktop); if (FAILED_UNEXPECTEDLY(hr)) return hr;
if (info->pidlRet) { hr = desktop->BindToObject(info->pidlRet, NULL, IID_PPV_ARG(IShellFolder, &cur)); if (FAILED_UNEXPECTEDLY(hr)) return hr;
hr = SHGetPathFromIDListW(info->pidlRet, path); } else { cur = desktop; hr = SHGetFolderPathW(NULL, CSIDL_DESKTOPDIRECTORY, NULL, SHGFP_TYPE_CURRENT, path); }
if (FAILED_UNEXPECTEDLY(hr)) return hr;
hr = E_FAIL; if (!LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, _countof(wszNewFolder))) return hr;
if (!PathYetAnotherMakeUniqueName(name, path, NULL, wszNewFolder)) return hr;
INT len = lstrlenW(path); if (len < MAX_PATH && name[len] == L'\\') len++;
if (!CreateDirectoryW(name, NULL)) return hr;
// Update parent of newly created directory HTREEITEM hParent = TreeView_GetSelection(info->hwndTreeView); if (!hParent) return hr;
TreeView_Expand(info->hwndTreeView, hParent, TVE_EXPAND);
TVITEMW item = { TVIF_PARAM | TVIF_STATE }; item.hItem = hParent; TreeView_GetItem(info->hwndTreeView, &item); BrItemData *item_data = (BrItemData *)item.lParam; if (!item_data) return hr;
// Update treeview if (!(item.state & TVIS_EXPANDEDONCE)) { item.mask = TVIF_STATE; item.state = item.stateMask = TVIS_EXPANDEDONCE; TreeView_SetItem(info->hwndTreeView, &item); }
CComHeapPtr<ITEMIDLIST_RELATIVE> pidlNew; hr = cur->ParseDisplayName(NULL, NULL, name + len, NULL, &pidlNew, NULL); if (FAILED_UNEXPECTEDLY(hr)) return hr;
HTREEITEM hAdded = BrFolder_InsertItem(info, cur, pidlNew, item_data->pidlFull, hParent); TreeView_SortChildren(info->hwndTreeView, hParent, FALSE); return BrFolder_Rename(info, hAdded);}
static voidBrFolder_OnOK(BrFolder *info){ // Get the text WCHAR szPath[MAX_PATH]; GetDlgItemTextW(info->hWnd, IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT, szPath, _countof(szPath)); StrTrimW(szPath, L" \t");
// The original pidl is owned by the treeview and will be free'd. if (!PathIsRelativeW(szPath) && PathIsDirectoryW(szPath)) info->pidlRet = ILCreateFromPathW(szPath); // It's valid path else info->pidlRet = ILClone(info->pidlRet);
if (!info->pidlRet) // A null pidl would mean a cancel info->pidlRet = _ILCreateDesktop();
pdump(info->pidlRet);
LPBROWSEINFOW lpBrowseInfo = info->lpBrowseInfo; if (!lpBrowseInfo->pszDisplayName) return;
SHFILEINFOW fileInfo = { NULL }; lpBrowseInfo->pszDisplayName[0] = UNICODE_NULL; if (SHGetFileInfoW((LPCWSTR)info->pidlRet, 0, &fileInfo, sizeof(fileInfo), SHGFI_PIDL | SHGFI_DISPLAYNAME)) { lstrcpynW(lpBrowseInfo->pszDisplayName, fileInfo.szDisplayName, MAX_PATH); }}
static voidBrFolder_OnCommand(BrFolder *info, UINT id){ switch (id) { case IDOK: { BrFolder_OnOK(info); EndDialog(info->hWnd, IDOK); break; } case IDCANCEL: { EndDialog(info->hWnd, IDCANCEL); break; } case IDC_BROWSE_FOR_FOLDER_NEW_FOLDER: { BrFolder_NewFolder(info); break; } }}
static voidGetTreeViewItemContextMenuPos(HWND hWnd, HTREEITEM hItem, POINT *ppt){ RECT rc; if (TreeView_GetItemRect(hWnd, hItem, &rc, TRUE)) { ppt->x = (rc.left + rc.right) / 2; ppt->y = (rc.top + rc.bottom) / 2; } ClientToScreen(hWnd, ppt);}
static voidBrFolder_OnContextMenu(BrFolder &info, LPARAM lParam){ enum { IDC_TOGGLE = 1, ID_FIRSTCMD, ID_LASTCMD = 0xffff }; HTREEITEM hSelected = TreeView_GetSelection(info.hwndTreeView); CMINVOKECOMMANDINFOEX ici = { sizeof(ici), CMIC_MASK_PTINVOKE, info.hWnd }; ici.nShow = SW_SHOW; ici.ptInvoke.x = GET_X_LPARAM(lParam); ici.ptInvoke.y = GET_Y_LPARAM(lParam); if ((int)lParam == -1) // Keyboard { GetTreeViewItemContextMenuPos(info.hwndTreeView, hSelected, &ici.ptInvoke); } else // Get correct item for right-click on not current item { TVHITTESTINFO hti = { ici.ptInvoke }; ScreenToClient(info.hwndTreeView, &hti.pt); hSelected = TreeView_HitTest(info.hwndTreeView, &hti); } BrItemData *item = BrFolder_GetItemData(&info, hSelected); if (!item) return; // Not on an item
TV_ITEM tvi; tvi.mask = TVIF_STATE | TVIF_CHILDREN; tvi.stateMask = TVIS_EXPANDED; tvi.hItem = hSelected; TreeView_GetItem(info.hwndTreeView, &tvi);
CComPtr<IContextMenu> pcm; HRESULT hr = item->lpsfParent->GetUIObjectOf(info.hWnd, 1, &item->pidlChild, IID_IContextMenu, NULL, (void**)&pcm); if (FAILED(hr)) return;
HMENU hMenu = CreatePopupMenu(); if (!hMenu) return; info.pContextMenu = pcm; UINT cmf = ((GetKeyState(VK_SHIFT) < 0) ? CMF_EXTENDEDVERBS : 0) | CMF_CANRENAME; hr = pcm->QueryContextMenu(hMenu, 0, ID_FIRSTCMD, ID_LASTCMD, CMF_EXPLORE | cmf); if (hr > 0) _InsertMenuItemW(hMenu, 0, TRUE, 0, MFT_SEPARATOR, NULL, 0); _InsertMenuItemW(hMenu, 0, TRUE, IDC_TOGGLE, MFT_STRING, MAKEINTRESOURCEW((tvi.state & TVIS_EXPANDED) ? IDS_COLLAPSE : IDS_EXPAND), MFS_DEFAULT | (tvi.cChildren ? 0 : MFS_GRAYED));
UINT cmd = TrackPopupMenuEx(hMenu, TPM_RETURNCMD, ici.ptInvoke.x, ici.ptInvoke.y, info.hWnd, NULL); ici.lpVerb = MAKEINTRESOURCEA(cmd - ID_FIRSTCMD); if (cmd == IDC_TOGGLE) { TreeView_SelectItem(info.hwndTreeView, hSelected); TreeView_Expand(info.hwndTreeView, hSelected, TVE_TOGGLE); } else if (cmd != 0 && GetDfmCmd(pcm, ici.lpVerb) == DFM_CMD_RENAME) { BrFolder_Rename(&info, hSelected); } else if (cmd != 0) { if (GetKeyState(VK_SHIFT) < 0) ici.fMask |= CMIC_MASK_SHIFT_DOWN; if (GetKeyState(VK_CONTROL) < 0) ici.fMask |= CMIC_MASK_CONTROL_DOWN; pcm->InvokeCommand((CMINVOKECOMMANDINFO*)&ici); } info.pContextMenu = NULL; DestroyMenu(hMenu);}
static BOOLBrFolder_ExpandToPidl(BrFolder *info, LPITEMIDLIST pidlSelection, HTREEITEM *phItem){ LPITEMIDLIST pidlCurrent = pidlSelection; if (_ILIsDesktop(pidlSelection)) { if (phItem) *phItem = TVI_ROOT; return TRUE; }
// Initialize item to point to the first child of the root folder. TVITEMEXW item = { TVIF_PARAM }; item.hItem = TreeView_GetRoot(info->hwndTreeView); if (item.hItem) item.hItem = TreeView_GetChild(info->hwndTreeView, item.hItem);
// Walk the tree along the nodes corresponding to the remaining ITEMIDLIST UINT depth = _ILGetDepth(info->lpBrowseInfo->pidlRoot); while (item.hItem && pidlCurrent) { LPITEMIDLIST pidlNeedle = ILCloneToDepth(pidlSelection, ++depth); if (_ILIsEmpty(pidlNeedle)) { ILFree(pidlNeedle); item.hItem = NULL; // Failure break; }next: TreeView_GetItem(info->hwndTreeView, &item); const BrItemData *pItemData = (BrItemData *)item.lParam; if (ILIsEqual(pItemData->pidlFull, pidlNeedle)) { BOOL done = _ILGetDepth(pidlSelection) == _ILGetDepth(pidlNeedle); if (done) { pidlCurrent = NULL; // Success } else { TreeView_Expand(info->hwndTreeView, item.hItem, TVE_EXPAND); item.hItem = TreeView_GetChild(info->hwndTreeView, item.hItem); } } else { item.hItem = TreeView_GetNextSibling(info->hwndTreeView, item.hItem); if (item.hItem) goto next; } ILFree(pidlNeedle); }
if (phItem) *phItem = item.hItem;
return (_ILIsEmpty(pidlCurrent) && item.hItem);}
static BOOLBrFolder_ExpandToString(BrFolder *info, LPWSTR pszString, HTREEITEM *phItem){ CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidlSelection; HRESULT hr = SHParseDisplayName(pszString, NULL, &pidlSelection, 0, NULL); return SUCCEEDED(hr) && BrFolder_ExpandToPidl(info, pidlSelection, phItem);}
static BOOLBrFolder_OnSetExpanded(BrFolder *info, LPITEMIDLIST pidlSelection, LPWSTR pszString){ HTREEITEM hItem; BOOL ret; if (pszString) ret = BrFolder_ExpandToString(info, pszString, &hItem); else ret = BrFolder_ExpandToPidl(info, pidlSelection, &hItem);
if (ret) TreeView_Expand(info->hwndTreeView, hItem, TVE_EXPAND); return ret;}
static BOOLBrFolder_OnSetSelectionPidl(BrFolder *info, LPITEMIDLIST pidlSelection){ if (!pidlSelection) return FALSE;
HTREEITEM hItem; BOOL ret = BrFolder_ExpandToPidl(info, pidlSelection, &hItem); if (ret) TreeView_SelectItem(info->hwndTreeView, hItem); return ret;}
static BOOLBrFolder_OnSetSelectionW(BrFolder *info, LPWSTR pszSelection){ if (!pszSelection) return FALSE;
HTREEITEM hItem; BOOL ret = BrFolder_ExpandToString(info, pszSelection, &hItem); if (ret) TreeView_SelectItem(info->hwndTreeView, hItem); return ret;}
static BOOLBrFolder_OnSetSelectionA(BrFolder *info, LPSTR pszSelectionA){ if (!pszSelectionA) return FALSE;
CComHeapPtr<WCHAR> pszSelectionW; __SHCloneStrAtoW(&pszSelectionW, pszSelectionA); if (!pszSelectionW) return FALSE;
return BrFolder_OnSetSelectionW(info, pszSelectionW);}
static voidBrFolder_OnDestroy(BrFolder *info){ if (info->layout) { LayoutDestroy(info->layout); info->layout = NULL; }
SHChangeNotifyDeregister(info->hChangeNotify);}
static voidBrFolder_RefreshRecurse( _Inout_ BrFolder *info, _In_ HTREEITEM hTarget){ // Get enum CComPtr<IEnumIDList> pEnum; BrItemData *pItemData = BrFolder_GetItemData(info, hTarget); HRESULT hrEnum = BrFolder_GetChildrenEnum(info, pItemData, &pEnum);
// Insert new items if (SUCCEEDED(hrEnum)) { 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(); } }
// 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 voidBrFolder_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 voidBrFolder_OnChangeEx( _Inout_ BrFolder *info, _In_ PCIDLIST_ABSOLUTE pidl1, _In_ PCIDLIST_ABSOLUTE pidl2, _In_ LONG event){ TRACE("(%p)->(%p, %p, 0x%lX)\n", info, pidl1, pidl2, event);
switch (event) { case SHCNE_RENAMEFOLDER: case SHCNE_RENAMEITEM: case SHCNE_UPDATEITEM: { UINT UpdateFlags = (event == SHCNE_UPDATEITEM) ? (TVIF_IMAGE | TVIF_CHILDREN) : (TVIF_TEXT); if (HTREEITEM hTI = BrFolder_FindTreeItemOfAbsoluteItem(*info, pidl1)) { if (BrFolder_UpdateItemEx(*info, hTI, pidl2, UpdateFlags)) { if ((hTI = TreeView_GetParent(info->hwndTreeView, hTI)) != NULL) TreeView_SortChildren(info->hwndTreeView, hTI, FALSE); return; } } break; } }
switch (event) { case SHCNE_DRIVEADD: case SHCNE_MKDIR: case SHCNE_CREATE: case SHCNE_DRIVEREMOVED: case SHCNE_RMDIR: case SHCNE_DELETE: case SHCNE_RENAMEFOLDER: case SHCNE_RENAMEITEM: case SHCNE_UPDATEDIR: case SHCNE_UPDATEITEM: { // FIXME: Avoid full refresh and optimize for speed. Use pidl0 and pidl1 BrFolder_Refresh(info); break; } }}
// SHV_CHANGE_NOTIFYstatic voidBrFolder_OnChange(BrFolder *info, WPARAM wParam, LPARAM lParam){ // We use SHCNRF_NewDelivery method HANDLE hChange = (HANDLE)wParam; DWORD dwProcID = (DWORD)lParam;
PIDLIST_ABSOLUTE *ppidl = NULL; LONG event; HANDLE hLock = SHChangeNotification_Lock(hChange, dwProcID, &ppidl, &event); if (hLock == NULL) { ERR("hLock == NULL\n"); return; }
BrFolder_OnChangeEx(info, ppidl[0], ppidl[1], (event & ~SHCNE_INTERRUPT));
SHChangeNotification_Unlock(hLock);}
/************************************************************************* * BrFolderDlgProc32 (not an exported API function) */static INT_PTR CALLBACKBrFolderDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ if (uMsg == WM_INITDIALOG) return BrFolder_OnInitDialog(hWnd, (BrFolder *)lParam);
BrFolder *info = (BrFolder *)GetWindowLongPtrW(hWnd, DWLP_USER); if (!info) return 0;
if (info->pContextMenu && SHELL_IsContextMenuMsg(uMsg)) { LRESULT result; if (SHForwardContextMenuMsg(info->pContextMenu, uMsg, wParam, lParam, &result, TRUE) == S_OK) { SetWindowLongPtr(hWnd, DWLP_MSGRESULT, result); return TRUE; } }
switch (uMsg) { case WM_NOTIFY: SetWindowLongPtr(hWnd, DWLP_MSGRESULT, BrFolder_OnNotify(info, (UINT)wParam, (LPNMHDR)lParam)); return TRUE;
case WM_COMMAND: BrFolder_OnCommand(info, wParam); break;
case WM_CONTEXTMENU: if (info->hwndTreeView == (HWND)wParam) BrFolder_OnContextMenu(*info, lParam); break;
case WM_GETMINMAXINFO: ((LPMINMAXINFO)lParam)->ptMinTrackSize.x = info->szMin.cx; ((LPMINMAXINFO)lParam)->ptMinTrackSize.y = info->szMin.cy; break;
case WM_SIZE: if (info->layout) // New style dialogs LayoutUpdate(hWnd, info->layout, g_layout_info, _countof(g_layout_info)); break;
case BFFM_SETSTATUSTEXTA: SetDlgItemTextA(hWnd, IDC_BROWSE_FOR_FOLDER_STATUS, (LPSTR)lParam); break;
case BFFM_SETSTATUSTEXTW: SetDlgItemTextW(hWnd, IDC_BROWSE_FOR_FOLDER_STATUS, (LPWSTR)lParam); break;
case BFFM_ENABLEOK: EnableWindow(GetDlgItem(hWnd, IDOK), lParam != 0); break;
case BFFM_SETOKTEXT: // Unicode only SetDlgItemTextW(hWnd, IDOK, (LPWSTR)lParam); break;
case BFFM_SETSELECTIONA: if (wParam) // String return BrFolder_OnSetSelectionA(info, (LPSTR)lParam); else // PIDL return BrFolder_OnSetSelectionPidl(info, (LPITEMIDLIST)lParam);
case BFFM_SETSELECTIONW: if (wParam) // String return BrFolder_OnSetSelectionW(info, (LPWSTR)lParam); else // PIDL return BrFolder_OnSetSelectionPidl(info, (LPITEMIDLIST)lParam);
case BFFM_SETEXPANDED: // Unicode only if (wParam) // String return BrFolder_OnSetExpanded(info, NULL, (LPWSTR)lParam); else // PIDL return BrFolder_OnSetExpanded(info, (LPITEMIDLIST)lParam, NULL);
case SHV_CHANGE_NOTIFY: BrFolder_OnChange(info, wParam, lParam); break;
case WM_DESTROY: BrFolder_OnDestroy(info); break; }
return 0;}
/************************************************************************* * SHBrowseForFolderA [SHELL32.@] * SHBrowseForFolder [SHELL32.@] */EXTERN_CLPITEMIDLIST WINAPISHBrowseForFolderA(LPBROWSEINFOA lpbi){ BROWSEINFOW bi; bi.hwndOwner = lpbi->hwndOwner; bi.pidlRoot = lpbi->pidlRoot;
WCHAR szName[MAX_PATH]; bi.pszDisplayName = (lpbi->pszDisplayName ? szName : NULL);
CComHeapPtr<WCHAR> pszTitle; if (lpbi->lpszTitle) __SHCloneStrAtoW(&pszTitle, lpbi->lpszTitle); bi.lpszTitle = pszTitle;
bi.ulFlags = lpbi->ulFlags; bi.lpfn = lpbi->lpfn; bi.lParam = lpbi->lParam; bi.iImage = lpbi->iImage; PIDLIST_ABSOLUTE pidl = SHBrowseForFolderW(&bi);
if (bi.pszDisplayName) SHUnicodeToAnsi(bi.pszDisplayName, lpbi->pszDisplayName, MAX_PATH);
lpbi->iImage = bi.iImage; return pidl;}
/************************************************************************* * SHBrowseForFolderW [SHELL32.@] */EXTERN_CLPITEMIDLIST WINAPISHBrowseForFolderW(LPBROWSEINFOW lpbi){ TRACE("%p\n", lpbi);
// MSDN says the caller must initialize COM. We do it anyway in case the caller forgot. COleInit OleInit; BrFolder info = { lpbi };
INT id = ((lpbi->ulFlags & BIF_USENEWUI) ? IDD_BROWSE_FOR_FOLDER_NEW : IDD_BROWSE_FOR_FOLDER); INT_PTR ret = DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(id), lpbi->hwndOwner, BrFolderDlgProc, (LPARAM)&info); if (ret == IDOK && !(lpbi->ulFlags & BIF_NOTRANSLATETARGETS) && RosGetProcessEffectiveVersion() >= _WIN32_WINNT_WINXP) { PIDLIST_ABSOLUTE pidlTarget; if (SHELL_GetIDListTarget(info.pidlRet, &pidlTarget) == S_OK) { ILFree(info.pidlRet); info.pidlRet = pidlTarget; } } if (ret != IDOK) { ILFree(info.pidlRet); return NULL; }
return info.pidlRet;}