Resend: SHBrowseForFolder Improvements

Robert Shearman R.J.Shearman at warwick.ac.uk
Wed Jun 4 18:05:47 CDT 2003


Any reason this wasn't applied first time, or was it just lost?

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
-------------- next part --------------
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);


More information about the wine-patches mailing list