This is a bit of a rewrite of the SHBrowseForFolder code, which I started to allow for a non-desktop root. I've added support for a few simple flags and cleaned up the enumeration of folders. Also, I added support so that the enumeration looks one level ahead, so that the "+" signs aren't displayed if that folder or item has no sub-folders. ChangeLog: - Support a few more flags - Rewrite the enumeration loop - Support non-desktop root - Silence harmless and implemented debug messages Rob
Index: wine/dlls/shell32/brsfolder.c =================================================================== RCS file: /home/wine/wine/dlls/shell32/brsfolder.c,v retrieving revision 1.44 diff -u -r1.44 brsfolder.c --- wine/dlls/shell32/brsfolder.c 7 Jan 2003 20:36:24 -0000 1.44 +++ wine/dlls/shell32/brsfolder.c 19 May 2003 14:24:24 -0000 @@ -16,9 +16,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * FIXME: - * - view with root unequal desktop * - many memory leaks - * - show only filesystem objects + * - many flags unimplemented */ #include <stdlib.h> @@ -40,13 +39,29 @@ static LPBROWSEINFOW lpBrowseInfo; static LPITEMIDLIST pidlRet; -static void FillTreeView(LPSHELLFOLDER lpsf, LPITEMIDLIST lpifq, HTREEITEM hParent); +static void FillTreeView(LPSHELLFOLDER lpsf, LPITEMIDLIST lpifq, HTREEITEM hParent, IEnumIDList* lpe); +static HTREEITEM InsertTreeViewItem(IShellFolder * lpsf, LPITEMIDLIST pidl, LPITEMIDLIST pidlParent, IEnumIDList* pEnumIL, HTREEITEM hParent); + +#define SUPPORTEDFLAGS (BIF_STATUSTEXT | \ + BIF_BROWSEFORCOMPUTER | \ + BIF_RETURNFSANCESTORS | \ + BIF_RETURNONLYFSDIRS | \ + BIF_BROWSEINCLUDEFILES) + +static inline DWORD BrowseFlagsToSHCONTF(UINT ulFlags) +{ + return SHCONTF_FOLDERS | (ulFlags & BIF_BROWSEINCLUDEFILES ? SHCONTF_NONFOLDERS : 0); +} static void InitializeTreeView(HWND hwndParent, LPCITEMIDLIST root) { HIMAGELIST hImageList; IShellFolder * lpsf; HRESULT hr; + IEnumIDList * pEnumIL = NULL; + LPITEMIDLIST parentofroot; + parentofroot = ILClone(root); + ILRemoveLastID(parentofroot); hwndTreeView = GetDlgItem (hwndParent, IDD_TREEVIEW); Shell_GetImageList(NULL, &hImageList); @@ -54,11 +69,8 @@ TRACE("dlg=%p tree=%p\n", hwndParent, hwndTreeView ); if (hImageList && hwndTreeView) - { TreeView_SetImageList(hwndTreeView, hImageList, 0); - } + TreeView_SetImageList(hwndTreeView, hImageList, 0); - /* so far, this method doesn't work (still missing the upper level), keep the old way */ -#if 0 if (_ILIsDesktop (root)) { hr = SHGetDesktopFolder(&lpsf); } else { @@ -66,37 +78,70 @@ hr = SHGetDesktopFolder(&lpsfdesktop); if (SUCCEEDED(hr)) { - hr = IShellFolder_BindToObject(lpsfdesktop, root, 0,(REFIID)&IID_IShellFolder,(LPVOID *)&lpsf); + hr = IShellFolder_BindToObject(lpsfdesktop, parentofroot, 0,(REFIID)&IID_IShellFolder,(LPVOID *)&lpsf); IShellFolder_Release(lpsfdesktop); } } -#else - hr = SHGetDesktopFolder(&lpsf); -#endif + if (SUCCEEDED(hr)) + { + IShellFolder * pSFRoot; + if (_ILIsPidlSimple(root)) + { + pSFRoot = lpsf; + IShellFolder_AddRef(pSFRoot); + } + else + hr = IShellFolder_BindToObject(lpsf,ILFindLastID(root),0,&IID_IShellFolder,(LPVOID *)&pSFRoot); + if (SUCCEEDED(hr)) + { + hr = IShellFolder_EnumObjects( + pSFRoot, + hwndParent, + BrowseFlagsToSHCONTF(lpBrowseInfo->ulFlags), + &pEnumIL); + IShellFolder_Release(pSFRoot); + } + } if (SUCCEEDED(hr) && hwndTreeView) - { TreeView_DeleteAllItems(hwndTreeView); - FillTreeView(lpsf, NULL, TVI_ROOT); + { + TreeView_DeleteAllItems(hwndTreeView); + TreeView_Expand(hwndTreeView, + InsertTreeViewItem(lpsf, _ILIsPidlSimple(root) ? root : ILFindLastID(root), parentofroot, pEnumIL, TVI_ROOT), + TVE_EXPAND); } if (SUCCEEDED(hr)) - { IShellFolder_Release(lpsf); - } + IShellFolder_Release(lpsf); + TRACE("done\n"); } static int GetIcon(LPITEMIDLIST lpi, UINT uFlags) -{ SHFILEINFOW sfi; +{ + SHFILEINFOW sfi; SHGetFileInfoW((LPCWSTR)lpi, 0 ,&sfi, sizeof(SHFILEINFOW), uFlags); return sfi.iIcon; } static void GetNormalAndSelectedIcons(LPITEMIDLIST lpifq, LPTVITEMW lpTV_ITEM) -{ TRACE("%p %p\n",lpifq, lpTV_ITEM); +{ + LPITEMIDLIST pidlDesktop = NULL; + + TRACE("%p %p\n",lpifq, lpTV_ITEM); + + if (!lpifq) + { + pidlDesktop = _ILCreateDesktop(); + lpifq = pidlDesktop; + } lpTV_ITEM->iImage = GetIcon(lpifq, SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON); lpTV_ITEM->iSelectedImage = GetIcon(lpifq, SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_OPENICON); + if (pidlDesktop) + ILFree(pidlDesktop); + return; } @@ -105,6 +150,7 @@ LPSHELLFOLDER lpsfParent; LPITEMIDLIST lpi; LPITEMIDLIST lpifq; + IEnumIDList* pEnumIL; } TV_ITEMDATA, *LPTV_ITEMDATA; static BOOL GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST lpi, DWORD dwFlags, LPWSTR lpFriendlyName) @@ -127,77 +173,123 @@ return bSuccess; } -static void FillTreeView(IShellFolder * lpsf, LPITEMIDLIST pidl, HTREEITEM hParent) +static HTREEITEM InsertTreeViewItem(IShellFolder * lpsf, LPITEMIDLIST pidl, LPITEMIDLIST pidlParent, IEnumIDList* pEnumIL, HTREEITEM hParent) { TVITEMW tvi; TVINSERTSTRUCTW tvins; + WCHAR szBuff[MAX_PATH]; + LPTV_ITEMDATA lptvid=0; + + tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; + + tvi.cChildren= pEnumIL ? 1 : 0; + tvi.mask |= TVIF_CHILDREN; + + if (!(lptvid = (LPTV_ITEMDATA)SHAlloc(sizeof(TV_ITEMDATA)))) + return NULL; + + if (!GetName(lpsf, pidl, SHGDN_NORMAL, szBuff)) + return NULL; + + tvi.pszText = szBuff; + tvi.cchTextMax = MAX_PATH; + tvi.lParam = (LPARAM)lptvid; + + IShellFolder_AddRef(lpsf); + lptvid->lpsfParent = lpsf; + lptvid->lpi = ILClone(pidl); + lptvid->lpifq = pidlParent ? ILCombine(pidlParent, pidl) : ILClone(pidl); + lptvid->pEnumIL = pEnumIL; + GetNormalAndSelectedIcons(lptvid->lpifq, &tvi); + + tvins.DUMMYUNIONNAME.item = tvi; + tvins.hInsertAfter = NULL; + tvins.hParent = hParent; + + return (HTREEITEM)TreeView_InsertItemW(hwndTreeView, &tvins); +} + +static void FillTreeView(IShellFolder * lpsf, LPITEMIDLIST pidl, HTREEITEM hParent, IEnumIDList* lpe) +{ HTREEITEM hPrev = 0; - LPENUMIDLIST lpe=0; LPITEMIDLIST pidlTemp=0; - LPTV_ITEMDATA lptvid=0; ULONG ulFetched; HRESULT hr; - WCHAR szBuff[MAX_PATH]; HWND hwnd=GetParent(hwndTreeView); TRACE("%p %p %x\n",lpsf, pidl, (INT)hParent); SetCapture(GetParent(hwndTreeView)); SetCursor(LoadCursorA(0, IDC_WAITA)); - hr=IShellFolder_EnumObjects(lpsf, hwnd, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &lpe); - - if (SUCCEEDED(hr)) - { while (NOERROR == IEnumIDList_Next(lpe,1,&pidlTemp,&ulFetched)) - { ULONG ulAttrs = SFGAO_HASSUBFOLDER | SFGAO_FOLDER; + while (NOERROR == IEnumIDList_Next(lpe,1,&pidlTemp,&ulFetched)) + { + ULONG ulAttrs = SFGAO_HASSUBFOLDER | SFGAO_FOLDER; + IEnumIDList* pEnumIL = NULL; + IShellFolder* pSFChild = NULL; IShellFolder_GetAttributesOf(lpsf, 1, &pidlTemp, &ulAttrs); - if (ulAttrs & (SFGAO_HASSUBFOLDER | SFGAO_FOLDER)) - { if (ulAttrs & SFGAO_FOLDER) - { tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; - - if (ulAttrs & SFGAO_HASSUBFOLDER) - { tvi.cChildren=1; - tvi.mask |= TVIF_CHILDREN; + if (ulAttrs & SFGAO_FOLDER) + { + hr = IShellFolder_BindToObject(lpsf,pidlTemp,NULL,&IID_IShellFolder,(LPVOID*)&pSFChild); + if (SUCCEEDED(hr)) + hr = IShellFolder_EnumObjects(pSFChild, hwnd, BrowseFlagsToSHCONTF(lpBrowseInfo->ulFlags), &pEnumIL); + if (SUCCEEDED(hr)) + { + if ((IEnumIDList_Skip(pEnumIL, 1) != S_OK) || FAILED(IEnumIDList_Reset(pEnumIL))) + { + IEnumIDList_Release(pEnumIL); + pEnumIL = NULL; + } } - - if (!(lptvid = (LPTV_ITEMDATA)SHAlloc(sizeof(TV_ITEMDATA)))) - goto Done; - - if (!GetName(lpsf, pidlTemp, SHGDN_NORMAL, szBuff)) - goto Done; - - tvi.pszText = szBuff; - tvi.cchTextMax = MAX_PATH; - tvi.lParam = (LPARAM)lptvid; - - IShellFolder_AddRef(lpsf); - lptvid->lpsfParent = lpsf; - lptvid->lpi = ILClone(pidlTemp); - lptvid->lpifq = ILCombine(pidl, pidlTemp); - GetNormalAndSelectedIcons(lptvid->lpifq, &tvi); - - tvins.DUMMYUNIONNAME.item = tvi; - tvins.hInsertAfter = hPrev; - tvins.hParent = hParent; - - hPrev = (HTREEITEM)TreeView_InsertItemW(hwndTreeView, &tvins); - - } + IShellFolder_Release(pSFChild); } + + if (!(hPrev = InsertTreeViewItem(lpsf, pidlTemp, pidl, pEnumIL, hParent))) + goto Done; SHFree(pidlTemp); /* Finally, free the pidl that the shell gave us... */ - pidlTemp=0; - } + pidlTemp=NULL; } Done: ReleaseCapture(); SetCursor(LoadCursorW(0, IDC_ARROWW)); - if (lpe) - IEnumIDList_Release(lpe); - if (pidlTemp ) + if (pidlTemp) SHFree(pidlTemp); } +static inline BOOL PIDLIsType(LPCITEMIDLIST pidl, PIDLTYPE type) +{ + LPPIDLDATA data = _ILGetDataPointer(pidl); + if (!data) + return FALSE; + return (data->type == type); +} + +static void BrsFolder_CheckValidSelection(HWND hWndTree, LPTV_ITEMDATA lptvid) +{ + LPCITEMIDLIST pidl = lptvid->lpi; + BOOL bEnabled = TRUE; + DWORD dwAttributes; + if ((lpBrowseInfo->ulFlags & BIF_BROWSEFORCOMPUTER) && + !PIDLIsType(pidl, PT_COMP)) + bEnabled = FALSE; + if (lpBrowseInfo->ulFlags & BIF_RETURNFSANCESTORS) + { + dwAttributes = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM; + if (FAILED(IShellFolder_GetAttributesOf(lptvid->lpsfParent, 1, &lptvid->lpi, &dwAttributes)) || + !dwAttributes) + bEnabled = FALSE; + } + if (lpBrowseInfo->ulFlags & BIF_RETURNONLYFSDIRS) + { + dwAttributes = SFGAO_FOLDER | SFGAO_FILESYSTEM; + if (FAILED(IShellFolder_GetAttributesOf(lptvid->lpsfParent, 1, &lptvid->lpi, &dwAttributes)) || + (dwAttributes != (SFGAO_FOLDER | SFGAO_FILESYSTEM))) + bEnabled = FALSE; + } + SendMessageW(hWndTree, BFFM_ENABLEOK, 0, (LPARAM)bEnabled); +} + static LRESULT MsgNotify(HWND hWnd, UINT CtlID, LPNMHDR lpnmh) { NMTREEVIEWW *pnmtv = (NMTREEVIEWW *)lpnmh; @@ -210,26 +302,29 @@ switch (pnmtv->hdr.idFrom) { case IDD_TREEVIEW: switch (pnmtv->hdr.code) - { case TVN_DELETEITEMA: + { + case TVN_DELETEITEMA: case TVN_DELETEITEMW: - { FIXME("TVN_DELETEITEMA/W\n"); - lptvid=(LPTV_ITEMDATA)pnmtv->itemOld.lParam; - IShellFolder_Release(lptvid->lpsfParent); - SHFree(lptvid->lpi); - SHFree(lptvid->lpifq); - SHFree(lptvid); - } + TRACE("TVN_DELETEITEMA/W\n"); + lptvid=(LPTV_ITEMDATA)pnmtv->itemOld.lParam; + IShellFolder_Release(lptvid->lpsfParent); + if (lptvid->pEnumIL) + IEnumIDList_Release(lptvid->pEnumIL); + SHFree(lptvid->lpi); + SHFree(lptvid->lpifq); + SHFree(lptvid); break; case TVN_ITEMEXPANDINGA: case TVN_ITEMEXPANDINGW: - { FIXME("TVN_ITEMEXPANDINGA/W\n"); + { + TRACE("TVN_ITEMEXPANDINGA/W\n"); if ((pnmtv->itemNew.state & TVIS_EXPANDEDONCE)) break; lptvid=(LPTV_ITEMDATA)pnmtv->itemNew.lParam; if (SUCCEEDED(IShellFolder_BindToObject(lptvid->lpsfParent, lptvid->lpi,0,(REFIID)&IID_IShellFolder,(LPVOID *)&lpsf2))) - { FillTreeView( lpsf2, lptvid->lpifq, pnmtv->itemNew.hItem ); + { FillTreeView( lpsf2, lptvid->lpifq, pnmtv->itemNew.hItem, lptvid->pEnumIL); } TreeView_SortChildren(hwndTreeView, pnmtv->itemNew.hItem, FALSE); } @@ -240,10 +335,11 @@ pidlRet = lptvid->lpifq; if (lpBrowseInfo->lpfn) (lpBrowseInfo->lpfn)(hWnd, BFFM_SELCHANGED, (LPARAM)pidlRet, lpBrowseInfo->lParam); + BrsFolder_CheckValidSelection(hWnd, lptvid); break; default: - FIXME("unhandled (%d)\n", pnmtv->hdr.code); + WARN("unhandled (%d)\n", pnmtv->hdr.code); break; } break; @@ -268,8 +364,8 @@ { case WM_INITDIALOG: pidlRet = NULL; lpBrowseInfo = (LPBROWSEINFOW) lParam; - if (lpBrowseInfo->ulFlags & ~(BIF_STATUSTEXT)) - FIXME("flags %x not implemented\n", lpBrowseInfo->ulFlags & ~(BIF_STATUSTEXT)); + if (lpBrowseInfo->ulFlags & ~SUPPORTEDFLAGS) + FIXME("flags %x not implemented\n", lpBrowseInfo->ulFlags & ~SUPPORTEDFLAGS); if (lpBrowseInfo->lpszTitle) { SetWindowTextW(GetDlgItem(hWnd, IDD_TITLE), lpBrowseInfo->lpszTitle); } else { @@ -278,15 +374,10 @@ if (!(lpBrowseInfo->ulFlags & BIF_STATUSTEXT)) ShowWindow(GetDlgItem(hWnd, IDD_STATUS), SW_HIDE); - if (lpBrowseInfo->pidlRoot ) - FIXME("root is desktop\n"); - InitializeTreeView(hWnd, lpBrowseInfo->pidlRoot); - if (lpBrowseInfo->lpfn) { + if (lpBrowseInfo->lpfn) (lpBrowseInfo->lpfn)(hWnd, BFFM_INITIALIZED, 0, lpBrowseInfo->lParam); - (lpBrowseInfo->lpfn)(hWnd, BFFM_SELCHANGED, 0/*FIXME*/, lpBrowseInfo->lParam); - } return TRUE; @@ -319,17 +410,27 @@ TRACE("Enable %ld\n", lParam); EnableWindow(GetDlgItem(hWnd, 1), (lParam)?TRUE:FALSE); break; + case BFFM_SETOKTEXT: /* unicode only */ + TRACE("Set OK text %s\n", debugstr_w((LPWSTR)wParam)); + SetWindowTextW(GetDlgItem(hWnd, 1), (LPWSTR)wParam); + break; case BFFM_SETSELECTIONA: if (wParam) - TRACE("Set selection %s\n", debugstr_a((LPSTR)lParam)); + FIXME("Set selection %s\n", debugstr_a((LPSTR)lParam)); else - TRACE("Set selection %p\n", (void*)lParam); + FIXME("Set selection %p\n", (void*)lParam); break; case BFFM_SETSELECTIONW: if (wParam) - TRACE("Set selection %s\n", debugstr_w((LPWSTR)lParam)); + FIXME("Set selection %s\n", debugstr_w((LPWSTR)lParam)); + else + FIXME("Set selection %p\n", (void*)lParam); + break; + case BFFM_SETEXPANDED: /* unicode only */ + if (wParam) + FIXME("Set expanded %s\n", debugstr_w((LPWSTR)lParam)); else - TRACE("Set selection %p\n", (void*)lParam); + FIXME("Set expanded %p\n", (void*)lParam); break; } return FALSE; Index: wine/include/shlobj.h =================================================================== RCS file: /home/wine/wine/include/shlobj.h,v retrieving revision 1.65 diff -u -r1.65 shlobj.h --- wine/include/shlobj.h 13 Apr 2003 01:05:31 -0000 1.65 +++ wine/include/shlobj.h 19 May 2003 14:24:26 -0000 @@ -292,6 +292,8 @@ #define BFFM_SETSELECTIONA (WM_USER+102) #define BFFM_SETSELECTIONW (WM_USER+103) #define BFFM_SETSTATUSTEXTW (WM_USER+104) +#define BFFM_SETOKTEXT (WM_USER+105) +#define BFFM_SETEXPANDED (WM_USER+106) LPITEMIDLIST WINAPI SHBrowseForFolderA(LPBROWSEINFOA lpbi); LPITEMIDLIST WINAPI SHBrowseForFolderW(LPBROWSEINFOW lpbi);