Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2715 lines
86 KiB

  1. #include "shellprv.h"
  2. #include "ids.h"
  3. #include "findhlp.h"
  4. #include "shitemid.h"
  5. #include "findfilter.h"
  6. #include <inetreg.h>
  7. #include <help.h>
  8. class CBrowseForFolder;
  9. // Structure to pass information to browse for folder dialog
  10. typedef struct
  11. {
  12. HWND hwndOwner;
  13. LPCITEMIDLIST pidlRoot; // Root of search. Typically desktop or my net
  14. LPTSTR pszDisplayName;// Return display name of item selected.
  15. int *piImage; // where to return the Image index.
  16. LPCTSTR lpszTitle; // resource (or text to go in the banner over the tree.
  17. UINT ulFlags; // Flags that control the return stuff
  18. BFFCALLBACK lpfn;
  19. LPARAM lParam;
  20. HWND hwndDlg; // The window handle to the dialog
  21. HWND hwndTree; // The tree control.
  22. HWND hwndEdit;
  23. HTREEITEM htiCurParent; // tree item associated with Current shell folder
  24. IShellFolder *psfParent; // Cache of the last IShell folder I needed...
  25. LPITEMIDLIST pidlCurrent; // IDlist of current folder to select
  26. BOOL fShowAllObjects; // Should we Show all ?
  27. BOOL fUnicode; // 1:unicode entry pt 0:ansi
  28. } BFSF;
  29. LPITEMIDLIST SHBrowseForFolder2(BFSF * pbfsf);
  30. BOOL_PTR CALLBACK _BrowseDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
  31. LPITEMIDLIST _BFSFUpdateISHCache(BFSF *pbfsf, HTREEITEM hti, LPITEMIDLIST pidlItem);
  32. // We want to use SHBrowseForFolder2 if either:
  33. // 1. pbfsf->lpfn == NULL since the caller wont' customize the dialog, or
  34. // 2. pbfsf->ulFlags
  35. BOOL ShouldUseBrowseForFolder2(BFSF *pbfsf)
  36. {
  37. // FEATURE: Enable the following code after we have it working with all the backward compat cases.
  38. // return (!pbfsf->lpfn || (BIF_NEWDIALOGSTYLE == pbfsf->ulFlags));
  39. return (BIF_NEWDIALOGSTYLE & pbfsf->ulFlags);
  40. }
  41. STDAPI_(LPITEMIDLIST) SHBrowseForFolder(BROWSEINFO *pbi)
  42. {
  43. HRESULT hrOle = SHCoInitialize(); // Init OLE for AutoComplete
  44. // NB: The ANSI Thunk (see below) does not call through this routine,
  45. // but rather called DialogBoxParam on its own. If you change this
  46. // routine, change the A version as well!!
  47. BFSF bfsf = {
  48. pbi->hwndOwner,
  49. pbi->pidlRoot,
  50. pbi->pszDisplayName,
  51. &pbi->iImage,
  52. pbi->lpszTitle,
  53. pbi->ulFlags,
  54. pbi->lpfn,
  55. pbi->lParam,
  56. };
  57. HCURSOR hcOld = SetCursor(LoadCursor(NULL,IDC_WAIT));
  58. SHELLSTATE ss;
  59. SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE);
  60. bfsf.fShowAllObjects = BOOLIFY(ss.fShowAllObjects);
  61. bfsf.fUnicode = 1;
  62. LPITEMIDLIST pidlRet = NULL;
  63. if (ShouldUseBrowseForFolder2(&bfsf))
  64. {
  65. pidlRet = SHBrowseForFolder2(&bfsf); // Continue even if OLE wasn't initialized.
  66. }
  67. else if (DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_BROWSEFORFOLDER),
  68. pbi->hwndOwner, _BrowseDlgProc, (LPARAM)&bfsf))
  69. {
  70. pidlRet = bfsf.pidlCurrent;
  71. }
  72. if (pidlRet && !(pbi->ulFlags & BIF_NOTRANSLATETARGETS))
  73. {
  74. LPITEMIDLIST pidlTarget;
  75. if (SUCCEEDED(SHGetTargetFolderIDList(pidlRet, &pidlTarget)))
  76. {
  77. ILFree(pidlRet);
  78. pidlRet = pidlTarget;
  79. }
  80. }
  81. if (hcOld)
  82. SetCursor(hcOld);
  83. SHCoUninitialize(hrOle);
  84. return pidlRet;
  85. }
  86. STDAPI_(LPITEMIDLIST) SHBrowseForFolderA(BROWSEINFOA *pbi)
  87. {
  88. LPITEMIDLIST pidlRet = NULL;
  89. HRESULT hrOle = SHCoInitialize(); // Init OLE for AutoComplete
  90. ThunkText *pThunkText = ConvertStrings(1, pbi->lpszTitle);
  91. if (pThunkText)
  92. {
  93. WCHAR wszReturn[MAX_PATH];
  94. BFSF bfsf =
  95. {
  96. pbi->hwndOwner,
  97. pbi->pidlRoot,
  98. wszReturn,
  99. &pbi->iImage,
  100. pThunkText->m_pStr[0], // UNICODE copy of pbi->lpszTitle
  101. pbi->ulFlags,
  102. pbi->lpfn,
  103. pbi->lParam,
  104. };
  105. HCURSOR hcOld = SetCursor(LoadCursor(NULL,IDC_WAIT));
  106. SHELLSTATE ss;
  107. SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE);
  108. bfsf.fShowAllObjects = BOOLIFY(ss.fShowAllObjects);
  109. bfsf.fUnicode = 0;
  110. // Now Create the dialog that will be doing the browsing.
  111. if (ShouldUseBrowseForFolder2(&bfsf))
  112. {
  113. pidlRet = SHBrowseForFolder2(&bfsf);
  114. }
  115. else if (DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_BROWSEFORFOLDER),
  116. pbi->hwndOwner, _BrowseDlgProc, (LPARAM)&bfsf))
  117. {
  118. pidlRet = bfsf.pidlCurrent;
  119. }
  120. LocalFree(pThunkText);
  121. if (hcOld)
  122. SetCursor(hcOld);
  123. if (pidlRet)
  124. {
  125. if (pbi->pszDisplayName)
  126. {
  127. SHUnicodeToAnsi(wszReturn, pbi->pszDisplayName, MAX_PATH);
  128. }
  129. if (!(pbi->ulFlags & BIF_NOTRANSLATETARGETS))
  130. {
  131. LPITEMIDLIST pidlTarget;
  132. if (SUCCEEDED(SHGetTargetFolderIDList(pidlRet, &pidlTarget)))
  133. {
  134. ILFree(pidlRet);
  135. pidlRet = pidlTarget;
  136. }
  137. }
  138. }
  139. }
  140. SHCoUninitialize(hrOle);
  141. return pidlRet;
  142. }
  143. int BFSFCallback(BFSF *pbfsf, UINT uMsg, LPARAM lParam)
  144. {
  145. return pbfsf->lpfn ? pbfsf->lpfn(pbfsf->hwndDlg, uMsg, lParam, pbfsf->lParam) : 0;
  146. }
  147. HTREEITEM _BFSFAddItemToTree(HWND hwndTree, HTREEITEM htiParent, LPITEMIDLIST pidl, int cChildren)
  148. {
  149. TV_INSERTSTRUCT tii;
  150. // Initialize item to add with callback for everything
  151. tii.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE |
  152. TVIF_PARAM | TVIF_CHILDREN;
  153. tii.hParent = htiParent;
  154. tii.hInsertAfter = TVI_FIRST;
  155. tii.item.iImage = I_IMAGECALLBACK;
  156. tii.item.iSelectedImage = I_IMAGECALLBACK;
  157. tii.item.pszText = LPSTR_TEXTCALLBACK; //
  158. tii.item.cChildren = cChildren; // Assume it has children
  159. tii.item.lParam = (LPARAM)pidl;
  160. return TreeView_InsertItem(hwndTree, &tii);
  161. }
  162. LPITEMIDLIST _BFSFGetIDListFromTreeItem(HWND hwndTree, HTREEITEM hti)
  163. {
  164. LPITEMIDLIST pidl;
  165. LPITEMIDLIST pidlT;
  166. TV_ITEM tvi;
  167. // If no hti passed in, get the selected on.
  168. if (hti == NULL)
  169. {
  170. hti = TreeView_GetSelection(hwndTree);
  171. if (hti == NULL)
  172. return(NULL);
  173. }
  174. // now lets get the information about the item
  175. tvi.mask = TVIF_PARAM | TVIF_HANDLE;
  176. tvi.hItem = hti;
  177. if (!TreeView_GetItem(hwndTree, &tvi))
  178. return(NULL); // Failed again
  179. pidl = ILClone((LPITEMIDLIST)tvi.lParam);
  180. // Now walk up parents.
  181. while ((NULL != (tvi.hItem = TreeView_GetParent(hwndTree, tvi.hItem))) && pidl)
  182. {
  183. if (!TreeView_GetItem(hwndTree, &tvi))
  184. return(pidl); // will assume I messed up...
  185. pidlT = ILCombine((LPITEMIDLIST)tvi.lParam, pidl);
  186. ILFree(pidl);
  187. pidl = pidlT;
  188. }
  189. return pidl;
  190. }
  191. int CALLBACK _BFSFTreeCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  192. {
  193. IShellFolder *psfParent = (IShellFolder *)lParamSort;
  194. HRESULT hr = psfParent->CompareIDs(0, (LPITEMIDLIST)lParam1, (LPITEMIDLIST)lParam2);
  195. if (FAILED(hr))
  196. return 0;
  197. return (short)SCODE_CODE(GetScode(hr));
  198. }
  199. void _BFSFSort(BFSF *pbfsf, HTREEITEM hti, IShellFolder *psf)
  200. {
  201. TV_SORTCB sSortCB;
  202. sSortCB.hParent = hti;
  203. sSortCB.lpfnCompare = _BFSFTreeCompare;
  204. psf->AddRef();
  205. sSortCB.lParam = (LPARAM)psf;
  206. TreeView_SortChildrenCB(pbfsf->hwndTree, &sSortCB, FALSE);
  207. psf->Release();
  208. }
  209. BOOL _BFSFHandleItemExpanding(BFSF *pbfsf, LPNM_TREEVIEW lpnmtv)
  210. {
  211. LPITEMIDLIST pidlToExpand;
  212. LPITEMIDLIST pidl;
  213. IShellFolder *psf;
  214. BYTE bType;
  215. DWORD grfFlags;
  216. BOOL fPrinterTest = FALSE;
  217. int cAdded = 0;
  218. TV_ITEM tvi;
  219. if (lpnmtv->action != TVE_EXPAND)
  220. return FALSE;
  221. if ((lpnmtv->itemNew.state & TVIS_EXPANDEDONCE))
  222. return FALSE;
  223. // set this bit now because we might be reentered via the wnet apis
  224. tvi.mask = TVIF_STATE;
  225. tvi.hItem = lpnmtv->itemNew.hItem;
  226. tvi.state = TVIS_EXPANDEDONCE;
  227. tvi.stateMask = TVIS_EXPANDEDONCE;
  228. TreeView_SetItem(pbfsf->hwndTree, &tvi);
  229. if (lpnmtv->itemNew.hItem == NULL)
  230. {
  231. lpnmtv->itemNew.hItem = TreeView_GetSelection(pbfsf->hwndTree);
  232. if (lpnmtv->itemNew.hItem == NULL)
  233. return FALSE;
  234. }
  235. pidlToExpand = _BFSFGetIDListFromTreeItem(pbfsf->hwndTree, lpnmtv->itemNew.hItem);
  236. if (pidlToExpand == NULL)
  237. return FALSE;
  238. // Now lets get the IShellFolder and iterator for this object
  239. // special case to handle if the Pidl is the desktop
  240. // This is rather gross, but the desktop appears to be simply a pidl
  241. // of length 0 and ILIsEqual will not work...
  242. if (FAILED(SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidlToExpand, &psf))))
  243. {
  244. ILFree(pidlToExpand);
  245. return FALSE; // Could not get IShellFolder.
  246. }
  247. // Need to do a couple of special cases here to allow us to
  248. // browse for a network printer. In this case if we are at server
  249. // level we then need to change what we search for non folders when
  250. // we are the level of a server.
  251. if (pbfsf->ulFlags & BIF_BROWSEFORPRINTER)
  252. {
  253. grfFlags = SHCONTF_FOLDERS | SHCONTF_NETPRINTERSRCH | SHCONTF_NONFOLDERS;
  254. pidl = ILFindLastID(pidlToExpand);
  255. bType = SIL_GetType(pidl);
  256. fPrinterTest = ((bType & (SHID_NET | SHID_INGROUPMASK)) == SHID_NET_SERVER);
  257. }
  258. else if (pbfsf->ulFlags & BIF_BROWSEINCLUDEFILES)
  259. grfFlags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;
  260. else
  261. grfFlags = SHCONTF_FOLDERS;
  262. if (pbfsf->fShowAllObjects)
  263. grfFlags |= SHCONTF_INCLUDEHIDDEN;
  264. IEnumIDList *penum;
  265. if (S_OK != psf->EnumObjects(pbfsf->hwndDlg, grfFlags, &penum))
  266. {
  267. psf->Release();
  268. ILFree(pidlToExpand);
  269. return FALSE;
  270. }
  271. // psf->AddRef();
  272. while (S_OK == penum->Next(1, &pidl, NULL))
  273. {
  274. int cChildren = I_CHILDRENCALLBACK; // Do call back for children
  275. //
  276. // We need to special case here in the netcase where we onlyu
  277. // browse down to workgroups...
  278. //
  279. //
  280. // Here is where I also need to special case to not go below
  281. // workgroups when the appropriate option is set.
  282. //
  283. bType = SIL_GetType(pidl);
  284. if ((pbfsf->ulFlags & BIF_DONTGOBELOWDOMAIN) && (bType & SHID_NET))
  285. {
  286. switch (bType & (SHID_NET | SHID_INGROUPMASK))
  287. {
  288. case SHID_NET_SERVER:
  289. ILFree(pidl); // Dont want to add this one
  290. continue; // Try the next one
  291. case SHID_NET_DOMAIN:
  292. cChildren = 0; // Force to not have children;
  293. }
  294. }
  295. else if ((pbfsf->ulFlags & BIF_BROWSEFORCOMPUTER) && (bType & SHID_NET))
  296. {
  297. if ((bType & (SHID_NET | SHID_INGROUPMASK)) == SHID_NET_SERVER)
  298. cChildren = 0; // Don't expand below it...
  299. }
  300. else if (pbfsf->ulFlags & BIF_BROWSEFORPRINTER)
  301. {
  302. // Special case when we are only allowing printers.
  303. // for now I will simply key on the fact that it is non-FS.
  304. ULONG ulAttr = SFGAO_FILESYSANCESTOR;
  305. psf->GetAttributesOf(1, (LPCITEMIDLIST *) &pidl, &ulAttr);
  306. if ((ulAttr & SFGAO_FILESYSANCESTOR) == 0)
  307. {
  308. cChildren = 0; // Force to not have children;
  309. }
  310. else if (fPrinterTest)
  311. {
  312. ILFree(pidl); // We are down to server level so don't add other things here
  313. continue; // Try the next one
  314. }
  315. }
  316. else if (pbfsf->ulFlags & BIF_BROWSEINCLUDEFILES)
  317. {
  318. // Lets not use the callback to see if this item has children or not
  319. // as some or files (no children) and it is not worth writing our own
  320. // enumerator as we don't want the + to depend on if there are sub-folders
  321. // but instead it should be if it has files...
  322. ULONG ulAttr = SFGAO_FOLDER;
  323. psf->GetAttributesOf(1, (LPCITEMIDLIST *) &pidl, &ulAttr);
  324. if ((ulAttr & SFGAO_FOLDER)== 0)
  325. cChildren = 0; // Force to not have children;
  326. else
  327. cChildren = 1;
  328. }
  329. if (pbfsf->ulFlags & (BIF_RETURNONLYFSDIRS | BIF_RETURNFSANCESTORS))
  330. {
  331. // If we are only looking for FS level things only add items
  332. // that are in the name space that are file system objects or
  333. // ancestors of file system objects
  334. ULONG ulAttr = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM;
  335. psf->GetAttributesOf(1, (LPCITEMIDLIST *) &pidl, &ulAttr);
  336. if ((ulAttr & (SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM))== 0)
  337. {
  338. ILFree(pidl); // We are down to server level so don't add other things here
  339. continue; // Try the next one
  340. }
  341. }
  342. _BFSFAddItemToTree(pbfsf->hwndTree, lpnmtv->itemNew.hItem,
  343. pidl, cChildren);
  344. cAdded++;
  345. }
  346. // Now Cleanup after ourself
  347. penum->Release();
  348. _BFSFSort(pbfsf, lpnmtv->itemNew.hItem, psf);
  349. psf->Release();
  350. ILFree(pidlToExpand);
  351. // If we did not add anything we should update this item to let
  352. // the user know something happened.
  353. //
  354. if (cAdded == 0)
  355. {
  356. TV_ITEM tvi;
  357. tvi.mask = TVIF_CHILDREN | TVIF_HANDLE; // only change the number of children
  358. tvi.hItem = lpnmtv->itemNew.hItem;
  359. tvi.cChildren = 0;
  360. TreeView_SetItem(pbfsf->hwndTree, &tvi);
  361. }
  362. return TRUE;
  363. }
  364. void _BFSFHandleDeleteItem(BFSF *pbfsf, LPNM_TREEVIEW lpnmtv)
  365. {
  366. // We need to free the IDLists that we allocated previously
  367. if (lpnmtv->itemOld.lParam != 0)
  368. ILFree((LPITEMIDLIST)lpnmtv->itemOld.lParam);
  369. }
  370. LPITEMIDLIST _BFSFUpdateISHCache(BFSF *pbfsf, HTREEITEM hti, LPITEMIDLIST pidlItem)
  371. {
  372. HTREEITEM htiParent;
  373. if ((pidlItem == NULL) || (pbfsf == NULL))
  374. return NULL;
  375. // Need to handle the root case here!
  376. htiParent = TreeView_GetParent(pbfsf->hwndTree, hti);
  377. if ((htiParent != pbfsf->htiCurParent) || (pbfsf->psfParent == NULL))
  378. {
  379. LPITEMIDLIST pidl;
  380. IShellFolder *psfDesktop;
  381. if (SUCCEEDED(SHGetDesktopFolder(&psfDesktop)))
  382. {
  383. if (pbfsf->psfParent)
  384. {
  385. if (pbfsf->psfParent != psfDesktop)
  386. pbfsf->psfParent->Release();
  387. pbfsf->psfParent = NULL;
  388. }
  389. if (htiParent)
  390. {
  391. pidl = _BFSFGetIDListFromTreeItem(pbfsf->hwndTree, htiParent);
  392. }
  393. else
  394. {
  395. //
  396. // If No Parent then the item here is one of our roots which
  397. // should be fully qualified. So try to get the parent by
  398. // decomposing the ID.
  399. //
  400. LPITEMIDLIST pidlT = (LPITEMIDLIST)ILFindLastID(pidlItem);
  401. if (pidlT != pidlItem)
  402. {
  403. pidl = ILClone(pidlItem);
  404. ILRemoveLastID(pidl);
  405. pidlItem = pidlT;
  406. }
  407. else
  408. pidl = NULL;
  409. }
  410. pbfsf->htiCurParent = htiParent;
  411. // If still NULL then we use root of evil...
  412. SHBindToObject(psfDesktop, IID_X_PPV_ARG(IShellFolder, pidl, &pbfsf->psfParent));
  413. ILFree(pidl);
  414. if (pbfsf->psfParent == NULL)
  415. return NULL;
  416. psfDesktop->Release();
  417. }
  418. }
  419. return ILFindLastID(pidlItem);
  420. }
  421. void _BFSFGetDisplayInfo(BFSF *pbfsf, TV_DISPINFO *lpnm)
  422. {
  423. LPITEMIDLIST pidlItem = (LPITEMIDLIST)lpnm->item.lParam;
  424. if ((lpnm->item.mask & (TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_CHILDREN)) == 0)
  425. return; // nothing for us to do here.
  426. pidlItem = _BFSFUpdateISHCache(pbfsf, lpnm->item.hItem, pidlItem);
  427. if (pidlItem && pbfsf->psfParent)
  428. {
  429. TV_ITEM ti;
  430. ti.mask = 0;
  431. ti.hItem = (HTREEITEM)lpnm->item.hItem;
  432. // They are asking for IconIndex. See if we can find it now.
  433. // Once found update their list, such that they wont call us back for
  434. // it again.
  435. if (lpnm->item.mask & (TVIF_IMAGE | TVIF_SELECTEDIMAGE))
  436. {
  437. // We now need to map the item into the right image index.
  438. ti.iImage = lpnm->item.iImage = SHMapPIDLToSystemImageListIndex(
  439. pbfsf->psfParent, pidlItem, &ti.iSelectedImage);
  440. // we should save it back away to
  441. lpnm->item.iSelectedImage = ti.iSelectedImage;
  442. ti.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
  443. }
  444. // Also see if this guy has any child folders
  445. if (lpnm->item.mask & TVIF_CHILDREN)
  446. {
  447. ULONG ulAttrs = SFGAO_HASSUBFOLDER;
  448. pbfsf->psfParent->GetAttributesOf(1, (LPCITEMIDLIST *) &pidlItem, &ulAttrs);
  449. ti.cChildren = lpnm->item.cChildren =
  450. (ulAttrs & SFGAO_HASSUBFOLDER)? 1 : 0;
  451. ti.mask |= TVIF_CHILDREN;
  452. }
  453. if (lpnm->item.mask & TVIF_TEXT)
  454. {
  455. if (SUCCEEDED(DisplayNameOf(pbfsf->psfParent, pidlItem, SHGDN_INFOLDER, lpnm->item.pszText, lpnm->item.cchTextMax)))
  456. {
  457. ti.mask |= TVIF_TEXT;
  458. ti.pszText = lpnm->item.pszText;
  459. }
  460. else
  461. {
  462. AssertMsg(0, TEXT("The folder %08x that owns pidl %08x rejected it!"),
  463. pbfsf, pidlItem);
  464. // Oh well - display a blank name and hope for the best.
  465. }
  466. }
  467. // Update the item now
  468. ti.mask |= TVIF_DI_SETITEM;
  469. }
  470. }
  471. void DlgEnableOk(HWND hwndDlg, LPARAM lParam)
  472. {
  473. EnableWindow(GetDlgItem(hwndDlg, IDOK), BOOLFROMPTR(lParam));
  474. return;
  475. }
  476. void _BFSFHandleSelChanged(BFSF *pbfsf, LPNM_TREEVIEW lpnmtv)
  477. {
  478. LPITEMIDLIST pidl;
  479. // We only need to do anything if we only want to return File system
  480. // level objects.
  481. if ((pbfsf->ulFlags & (BIF_RETURNONLYFSDIRS | BIF_RETURNFSANCESTORS | BIF_BROWSEFORPRINTER | BIF_BROWSEFORCOMPUTER)) == 0)
  482. goto NotifySelChange;
  483. // We need to get the attributes of this object...
  484. pidl = _BFSFUpdateISHCache(pbfsf, lpnmtv->itemNew.hItem,
  485. (LPITEMIDLIST)lpnmtv->itemNew.lParam);
  486. if (pidl && pbfsf->psfParent)
  487. {
  488. BOOL fEnable = TRUE;
  489. BYTE bType = SIL_GetType(pidl);
  490. if ((pbfsf->ulFlags & (BIF_RETURNFSANCESTORS|BIF_RETURNONLYFSDIRS)) != 0)
  491. {
  492. int i;
  493. // if this is the root pidl, then do a get attribs on 0
  494. // so that we'll get the attributes on the root, rather than
  495. // random returned values returned by FSFolder
  496. if (ILIsEmpty(pidl))
  497. i = 0;
  498. else
  499. i = 1;
  500. ULONG ulAttrs = SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR;
  501. pbfsf->psfParent->GetAttributesOf(i, (LPCITEMIDLIST *) &pidl, &ulAttrs);
  502. fEnable = (((ulAttrs & SFGAO_FILESYSTEM) && (pbfsf->ulFlags & BIF_RETURNONLYFSDIRS)) ||
  503. ((ulAttrs & SFGAO_FILESYSANCESTOR) && (pbfsf->ulFlags & BIF_RETURNFSANCESTORS))) ||
  504. ((bType & (SHID_NET | SHID_INGROUPMASK)) == SHID_NET_SERVER);
  505. }
  506. else if ((pbfsf->ulFlags & BIF_BROWSEFORCOMPUTER) != 0)
  507. fEnable = ((bType & (SHID_NET | SHID_INGROUPMASK)) == SHID_NET_SERVER);
  508. else if ((pbfsf->ulFlags & BIF_BROWSEFORPRINTER) != 0)
  509. {
  510. // Printers are of type Share and usage Print...
  511. fEnable = ((bType & (SHID_NET | SHID_INGROUPMASK)) == SHID_NET_SHARE);
  512. }
  513. DlgEnableOk(pbfsf->hwndDlg, fEnable);
  514. }
  515. NotifySelChange:
  516. if (pbfsf->ulFlags & BIF_EDITBOX)
  517. {
  518. TCHAR szText[MAX_PATH]; // update the edit box
  519. TVITEM tvi;
  520. szText[0] = 0;
  521. tvi.mask = TVIF_TEXT;
  522. tvi.hItem = lpnmtv->itemNew.hItem;
  523. tvi.pszText = szText;
  524. tvi.cchTextMax = ARRAYSIZE(szText);
  525. TreeView_GetItem(pbfsf->hwndTree, &tvi);
  526. SetWindowText(pbfsf->hwndEdit, szText);
  527. }
  528. if (pbfsf->lpfn)
  529. {
  530. pidl = _BFSFGetIDListFromTreeItem(pbfsf->hwndTree, lpnmtv->itemNew.hItem);
  531. if (pidl)
  532. {
  533. BFSFCallback(pbfsf, BFFM_SELCHANGED, (LPARAM)pidl);
  534. ILFree(pidl);
  535. }
  536. }
  537. }
  538. BOOL BrowseSelectPidl(BFSF *pbfsf, LPCITEMIDLIST pidl)
  539. {
  540. HTREEITEM htiParent;
  541. LPITEMIDLIST pidlTemp;
  542. LPITEMIDLIST pidlNext = NULL;
  543. LPITEMIDLIST pidlParent = NULL;
  544. BOOL fRet = FALSE;
  545. htiParent = TreeView_GetChild(pbfsf->hwndTree, NULL);
  546. if (htiParent)
  547. {
  548. // step through each item of the pidl
  549. for (;;)
  550. {
  551. TreeView_Expand(pbfsf->hwndTree, htiParent, TVE_EXPAND);
  552. pidlParent = _BFSFGetIDListFromTreeItem(pbfsf->hwndTree, htiParent);
  553. if (!pidlParent)
  554. break;
  555. pidlNext = ILClone(pidl);
  556. if (!pidlNext)
  557. break;
  558. pidlTemp = ILFindChild(pidlParent, pidlNext);
  559. if (!pidlTemp)
  560. break;
  561. if (ILIsEmpty(pidlTemp))
  562. {
  563. // found it!
  564. TreeView_SelectItem(pbfsf->hwndTree, htiParent);
  565. fRet = TRUE;
  566. break;
  567. }
  568. else
  569. {
  570. // loop to find the next item
  571. HTREEITEM htiChild;
  572. pidlTemp = ILGetNext(pidlTemp);
  573. if (!pidlTemp)
  574. break;
  575. else
  576. pidlTemp->mkid.cb = 0;
  577. htiChild = TreeView_GetChild(pbfsf->hwndTree, htiParent);
  578. while (htiChild)
  579. {
  580. BOOL fEqual;
  581. pidlTemp = _BFSFGetIDListFromTreeItem(pbfsf->hwndTree, htiChild);
  582. if (!pidlTemp)
  583. {
  584. htiChild = NULL;
  585. break;
  586. }
  587. fEqual = ILIsEqual(pidlTemp, pidlNext);
  588. ILFree(pidlTemp);
  589. if (fEqual)
  590. {
  591. break;
  592. }
  593. else
  594. {
  595. htiChild = TreeView_GetNextSibling(pbfsf->hwndTree, htiChild);
  596. }
  597. }
  598. if (!htiChild)
  599. {
  600. // we didn't find the next one... bail
  601. break;
  602. }
  603. else
  604. {
  605. // the found child becomes the next parent
  606. htiParent = htiChild;
  607. ILFree(pidlParent);
  608. ILFree(pidlNext);
  609. }
  610. }
  611. }
  612. }
  613. if (pidlParent) ILFree(pidlParent);
  614. if (pidlNext) ILFree(pidlNext);
  615. return fRet;
  616. }
  617. //===========================================================================
  618. // _BrowseForFolderOnBFSFInitDlg - Process the init dialog
  619. //===========================================================================
  620. BOOL _BrowseForFolderOnBFSFInitDlg(HWND hwnd, HWND hwndFocus, LPARAM lParam)
  621. {
  622. HTREEITEM hti;
  623. BFSF *pbfsf = (BFSF *)lParam;
  624. HIMAGELIST himl;
  625. LPTSTR lpsz;
  626. TCHAR szTitle[80]; // no title should be bigger than this!
  627. HWND hwndTree;
  628. lpsz = ResourceCStrToStr(HINST_THISDLL, pbfsf->lpszTitle);
  629. SetDlgItemText(hwnd, IDD_BROWSETITLE, lpsz);
  630. if (lpsz != pbfsf->lpszTitle)
  631. {
  632. LocalFree(lpsz);
  633. lpsz = NULL;
  634. }
  635. if(!(IS_WINDOW_RTL_MIRRORED(pbfsf->hwndOwner)))
  636. {
  637. SHSetWindowBits(hwnd, GWL_EXSTYLE, RTL_MIRRORED_WINDOW, 0);
  638. }
  639. SetWindowLongPtr(hwnd, DWLP_USER, lParam);
  640. pbfsf->hwndDlg = hwnd;
  641. hwndTree = pbfsf->hwndTree = GetDlgItem(hwnd, IDD_FOLDERLIST);
  642. if (hwndTree)
  643. {
  644. UINT swpFlags = SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER
  645. | SWP_NOACTIVATE;
  646. RECT rc;
  647. POINT pt = {0,0};
  648. GetClientRect(hwndTree, &rc);
  649. MapWindowPoints(hwndTree, hwnd, (POINT*)&rc, 2);
  650. pbfsf->hwndEdit = GetDlgItem(hwnd, IDD_BROWSEEDIT);
  651. if (!(pbfsf->ulFlags & BIF_STATUSTEXT))
  652. {
  653. HWND hwndStatus = GetDlgItem(hwnd, IDD_BROWSESTATUS);
  654. // nuke the status window
  655. ShowWindow(hwndStatus, SW_HIDE);
  656. MapWindowPoints(hwndStatus, hwnd, &pt, 1);
  657. rc.top = pt.y;
  658. swpFlags = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE;
  659. }
  660. if (pbfsf->ulFlags & BIF_EDITBOX)
  661. {
  662. RECT rcT;
  663. GetClientRect(pbfsf->hwndEdit, &rcT);
  664. SetWindowPos(pbfsf->hwndEdit, NULL, rc.left, rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  665. rc.top += (rcT.bottom - rcT.top) + GetSystemMetrics(SM_CYEDGE) * 4;
  666. swpFlags = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE;
  667. SHAutoComplete(GetDlgItem(hwnd, IDD_BROWSEEDIT), (SHACF_FILESYSTEM | SHACF_URLALL | SHACF_FILESYS_ONLY));
  668. }
  669. else
  670. {
  671. DestroyWindow(pbfsf->hwndEdit);
  672. pbfsf->hwndEdit = NULL;
  673. }
  674. Shell_GetImageLists(NULL, &himl);
  675. TreeView_SetImageList(hwndTree, himl, TVSIL_NORMAL);
  676. SetWindowLongPtr(hwndTree, GWL_EXSTYLE,
  677. GetWindowLongPtr(hwndTree, GWL_EXSTYLE) | WS_EX_CLIENTEDGE);
  678. // Now try to get this window to know to recalc
  679. SetWindowPos(hwndTree, NULL, rc.left, rc.top,
  680. rc.right - rc.left, rc.bottom - rc.top, swpFlags);
  681. }
  682. // If they passed in a root, add it, else add the contents of the
  683. // Root of evil... to the list as ROOT objects.
  684. if (pbfsf->pidlRoot)
  685. {
  686. LPITEMIDLIST pidl;
  687. if (IS_INTRESOURCE(pbfsf->pidlRoot))
  688. {
  689. pidl = SHCloneSpecialIDList(NULL, PtrToUlong((void *)pbfsf->pidlRoot), TRUE);
  690. }
  691. else
  692. {
  693. pidl = ILClone(pbfsf->pidlRoot);
  694. }
  695. // Now lets insert the Root object
  696. hti = _BFSFAddItemToTree(hwndTree, TVI_ROOT, pidl, 1);
  697. // Still need to expand below this point. to the starting location
  698. // That was passed in. But for now expand the first level.
  699. TreeView_Expand(hwndTree, hti, TVE_EXPAND);
  700. }
  701. else
  702. {
  703. LPITEMIDLIST pidlDesktop = SHCloneSpecialIDList(NULL, CSIDL_DESKTOP, FALSE);
  704. HTREEITEM htiRoot = _BFSFAddItemToTree(hwndTree, TVI_ROOT, pidlDesktop, 1);
  705. BOOL bFoundDrives = FALSE;
  706. // Expand the first level under the desktop
  707. TreeView_Expand(hwndTree, htiRoot, TVE_EXPAND);
  708. // Lets Preexpand the Drives portion....
  709. hti = TreeView_GetChild(hwndTree, htiRoot);
  710. while (hti && !bFoundDrives)
  711. {
  712. LPITEMIDLIST pidl = _BFSFGetIDListFromTreeItem(hwndTree, hti);
  713. if (pidl)
  714. {
  715. LPITEMIDLIST pidlDrives = SHCloneSpecialIDList(NULL, CSIDL_DRIVES, FALSE);
  716. if (pidlDrives)
  717. {
  718. bFoundDrives = ILIsEqual(pidl, pidlDrives);
  719. if (bFoundDrives)
  720. {
  721. TreeView_Expand(hwndTree, hti, TVE_EXPAND);
  722. TreeView_SelectItem(hwndTree, hti);
  723. }
  724. ILFree(pidlDrives);
  725. }
  726. ILFree(pidl);
  727. }
  728. hti = TreeView_GetNextSibling(hwndTree, hti);
  729. }
  730. }
  731. // go to our internal selection changed code to do any window enabling needed
  732. {
  733. NM_TREEVIEW nmtv;
  734. hti = TreeView_GetSelection(hwndTree);
  735. if (hti)
  736. {
  737. TV_ITEM ti;
  738. ti.mask = TVIF_PARAM;
  739. ti.hItem = hti;
  740. TreeView_GetItem(hwndTree, &ti);
  741. nmtv.itemNew.hItem = hti;
  742. nmtv.itemNew.lParam = ti.lParam;
  743. _BFSFHandleSelChanged(pbfsf, &nmtv);
  744. }
  745. }
  746. if ((pbfsf->ulFlags & BIF_BROWSEFORCOMPUTER) != 0)
  747. {
  748. LoadString(HINST_THISDLL, IDS_FINDSEARCH_COMPUTER, szTitle, ARRAYSIZE(szTitle));
  749. SetWindowText(hwnd, szTitle);
  750. }
  751. else if ((pbfsf->ulFlags & BIF_BROWSEFORPRINTER) != 0)
  752. {
  753. LoadString(HINST_THISDLL, IDS_FINDSEARCH_PRINTER, szTitle, ARRAYSIZE(szTitle));
  754. SetWindowText(hwnd, szTitle);
  755. }
  756. BFSFCallback(pbfsf, BFFM_INITIALIZED, 0);
  757. return TRUE;
  758. }
  759. //
  760. // Called when a ANSI app sends BFFM_SETSTATUSTEXT message.
  761. //
  762. void _BFSFSetStatusTextA(BFSF *pbfsf, LPCSTR lpszText)
  763. {
  764. CHAR szText[100];
  765. if (IS_INTRESOURCE(lpszText))
  766. {
  767. LoadStringA(HINST_THISDLL, LOWORD((DWORD_PTR)lpszText), szText, ARRAYSIZE(szText));
  768. lpszText = szText;
  769. }
  770. SetDlgItemTextA(pbfsf->hwndDlg, IDD_BROWSESTATUS, lpszText);
  771. }
  772. // UNICODE BFFM_SETSTATUSTEXT message.
  773. void _BFSFSetStatusTextW(BFSF *pbfsf, LPCWSTR lpszText)
  774. {
  775. WCHAR szText[100];
  776. if (IS_INTRESOURCE(lpszText))
  777. {
  778. LoadStringW(HINST_THISDLL, LOWORD((DWORD_PTR)lpszText), szText, ARRAYSIZE(szText));
  779. lpszText = szText;
  780. }
  781. SetDlgItemTextW(pbfsf->hwndDlg, IDD_BROWSESTATUS, lpszText);
  782. }
  783. // ANSI BFFM_SETSELECTION message.
  784. BOOL _BFSFSetSelectionA(BFSF *pbfsf, BOOL blParamIsPath, LPARAM lParam)
  785. {
  786. if (blParamIsPath)
  787. {
  788. TCHAR szPath[MAX_PATH];
  789. SHAnsiToTChar((LPCSTR)lParam, szPath, ARRAYSIZE(szPath));
  790. lParam = (LPARAM)SHSimpleIDListFromPath(szPath);
  791. if (!lParam)
  792. return FALSE; // Failed pidl creation.
  793. }
  794. BOOL fRet = BrowseSelectPidl(pbfsf, (LPITEMIDLIST)lParam);
  795. if (blParamIsPath)
  796. ILFree((LPITEMIDLIST)lParam);
  797. return fRet;
  798. }
  799. // UNICODE BFFM_SETSELECTION message.
  800. BOOL _BFSFSetSelectionW(BFSF *pbfsf, BOOL blParamIsPath, LPARAM lParam)
  801. {
  802. if (blParamIsPath)
  803. {
  804. lParam = (LPARAM)SHSimpleIDListFromPath((LPCTSTR)lParam);
  805. if (!lParam)
  806. return FALSE; // Failed pidl creation.
  807. }
  808. BOOL fRet = BrowseSelectPidl(pbfsf, (LPITEMIDLIST)lParam);
  809. if (blParamIsPath)
  810. ILFree((LPITEMIDLIST)lParam);
  811. return fRet;
  812. }
  813. // Called when an app sends BFFM_SETOKTEXT message.
  814. void _BFSFSetOkText(BFSF *pbfsf, LPCTSTR pszText)
  815. {
  816. LPTSTR psz = ResourceCStrToStr(HINST_THISDLL, pszText);
  817. SetDlgItemText(pbfsf->hwndDlg, IDOK, psz);
  818. if (psz != pszText)
  819. LocalFree(psz);
  820. }
  821. // Process the WM_COMMAND message
  822. void _BrowseOnCommand(BFSF *pbfsf, int id, HWND hwndCtl, UINT codeNotify)
  823. {
  824. HTREEITEM hti;
  825. switch (id)
  826. {
  827. case IDD_BROWSEEDIT:
  828. if (codeNotify == EN_CHANGE)
  829. {
  830. TCHAR szBuf[4]; // (arb. size, anything > 2)
  831. szBuf[0] = 1; // if Get fails ('impossible'), enable OK
  832. GetDlgItemText(pbfsf->hwndDlg, IDD_BROWSEEDIT, szBuf,
  833. ARRAYSIZE(szBuf));
  834. DlgEnableOk(pbfsf->hwndDlg, (WPARAM)(BOOL)szBuf[0]);
  835. }
  836. break;
  837. case IDOK:
  838. {
  839. TV_ITEM tvi;
  840. TCHAR szText[MAX_PATH];
  841. BOOL fDone = TRUE;
  842. // We can now update the structure with the idlist of the item selected
  843. hti = TreeView_GetSelection(pbfsf->hwndTree);
  844. pbfsf->pidlCurrent = _BFSFGetIDListFromTreeItem(pbfsf->hwndTree,
  845. hti);
  846. tvi.mask = TVIF_TEXT | TVIF_IMAGE;
  847. tvi.hItem = hti;
  848. tvi.pszText = pbfsf->pszDisplayName;
  849. if (!tvi.pszText)
  850. tvi.pszText = szText;
  851. tvi.cchTextMax = MAX_PATH;
  852. TreeView_GetItem(pbfsf->hwndTree, &tvi);
  853. if (pbfsf->ulFlags & BIF_EDITBOX)
  854. {
  855. TCHAR szEditTextRaw[MAX_PATH];
  856. TCHAR szEditText[MAX_PATH];
  857. GetWindowText(pbfsf->hwndEdit, szEditTextRaw, ARRAYSIZE(szEditTextRaw));
  858. SHExpandEnvironmentStrings(szEditTextRaw, szEditText, ARRAYSIZE(szEditText));
  859. if (lstrcmpi(szEditText, tvi.pszText))
  860. {
  861. // the two are different, we need to get the user typed one
  862. LPITEMIDLIST pidl;
  863. if (SUCCEEDED(SHParseDisplayName(szEditText, NULL, &pidl, 0, NULL)))
  864. {
  865. ILFree(pbfsf->pidlCurrent);
  866. pbfsf->pidlCurrent = pidl;
  867. lstrcpy(tvi.pszText, szEditText);
  868. tvi.iImage = -1;
  869. }
  870. else if (pbfsf->ulFlags & BIF_VALIDATE)
  871. {
  872. LPARAM lParam;
  873. char szAnsi[MAX_PATH];
  874. ASSERTMSG(pbfsf->lpfn != NULL, "No BrowseCallbackProc supplied with BIF_VALIDATE flag");
  875. // n.b. we free everything up, not callback (fewer bugs...)
  876. ILFree(pbfsf->pidlCurrent);
  877. pbfsf->pidlCurrent = NULL;
  878. tvi.pszText[0] = 0;
  879. tvi.iImage = -1;
  880. lParam = (LPARAM)szEditText;
  881. if (!pbfsf->fUnicode)
  882. {
  883. SHUnicodeToAnsi(szEditText, szAnsi, ARRAYSIZE(szAnsi));
  884. lParam = (LPARAM)szAnsi;
  885. }
  886. // 0:EndDialog, 1:continue
  887. fDone = BFSFCallback(pbfsf, pbfsf->fUnicode ? BFFM_VALIDATEFAILEDW : BFFM_VALIDATEFAILEDA, lParam) == 0;
  888. }
  889. // else old behavior: hand back last-clicked pidl (even
  890. // though it doesn't match editbox text!)
  891. }
  892. }
  893. if (pbfsf->piImage)
  894. *pbfsf->piImage = tvi.iImage;
  895. if (fDone)
  896. EndDialog(pbfsf->hwndDlg, TRUE); // To return TRUE.
  897. break;
  898. }
  899. case IDCANCEL:
  900. EndDialog(pbfsf->hwndDlg, 0); // to return FALSE from this.
  901. break;
  902. }
  903. }
  904. const static DWORD aBrowseHelpIDs[] = { // Context Help IDs
  905. IDD_BROWSETITLE, NO_HELP,
  906. IDD_BROWSESTATUS, NO_HELP,
  907. IDD_FOLDERLABLE, NO_HELP,
  908. IDD_BROWSEEDIT, IDH_DISPLAY_FOLDER,
  909. IDD_BFF_RESIZE_TAB, NO_HELP,
  910. IDD_NEWFOLDER_BUTTON, IDH_CREATE_NEW_FOLDER,
  911. IDD_FOLDERLIST, IDH_BROWSELIST,
  912. 0, 0
  913. };
  914. BOOL_PTR CALLBACK _BrowseDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
  915. {
  916. BFSF *pbfsf = (BFSF *)GetWindowLongPtr(hwndDlg, DWLP_USER);
  917. switch (msg)
  918. {
  919. HANDLE_MSG(pbfsf, WM_COMMAND, _BrowseOnCommand);
  920. case WM_INITDIALOG:
  921. return (BOOL)HANDLE_WM_INITDIALOG(hwndDlg, wParam, lParam, _BrowseForFolderOnBFSFInitDlg);
  922. case WM_DESTROY:
  923. if (pbfsf && pbfsf->psfParent)
  924. {
  925. IShellFolder *psfDesktop;
  926. SHGetDesktopFolder(&psfDesktop);
  927. if (pbfsf->psfParent != psfDesktop)
  928. {
  929. pbfsf->psfParent->Release();
  930. pbfsf->psfParent = NULL;
  931. }
  932. }
  933. break;
  934. case BFFM_SETSTATUSTEXTA:
  935. _BFSFSetStatusTextA(pbfsf, (LPCSTR)lParam);
  936. break;
  937. case BFFM_SETSTATUSTEXTW:
  938. _BFSFSetStatusTextW(pbfsf, (LPCWSTR)lParam);
  939. break;
  940. case BFFM_SETSELECTIONW:
  941. return _BFSFSetSelectionW(pbfsf, (BOOL)wParam, lParam);
  942. case BFFM_SETSELECTIONA:
  943. return _BFSFSetSelectionA(pbfsf, (BOOL)wParam, lParam);
  944. case BFFM_ENABLEOK:
  945. DlgEnableOk(hwndDlg, lParam);
  946. break;
  947. case BFFM_SETOKTEXT:
  948. _BFSFSetOkText(pbfsf, (LPCTSTR)lParam);
  949. break;
  950. case WM_NOTIFY:
  951. switch (((NMHDR *)lParam)->code)
  952. {
  953. case TVN_GETDISPINFOA:
  954. case TVN_GETDISPINFOW:
  955. _BFSFGetDisplayInfo(pbfsf, (TV_DISPINFO *)lParam);
  956. break;
  957. case TVN_ITEMEXPANDINGA:
  958. case TVN_ITEMEXPANDINGW:
  959. SetCursor(LoadCursor(NULL, IDC_WAIT));
  960. _BFSFHandleItemExpanding(pbfsf, (LPNM_TREEVIEW)lParam);
  961. break;
  962. case TVN_ITEMEXPANDEDA:
  963. case TVN_ITEMEXPANDEDW:
  964. SetCursor(LoadCursor(NULL, IDC_ARROW));
  965. break;
  966. case TVN_DELETEITEMA:
  967. case TVN_DELETEITEMW:
  968. _BFSFHandleDeleteItem(pbfsf, (LPNM_TREEVIEW)lParam);
  969. break;
  970. case TVN_SELCHANGEDA:
  971. case TVN_SELCHANGEDW:
  972. _BFSFHandleSelChanged(pbfsf, (LPNM_TREEVIEW)lParam);
  973. break;
  974. }
  975. break;
  976. case WM_HELP:
  977. WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, NULL,
  978. HELP_WM_HELP, (ULONG_PTR)(LPTSTR) aBrowseHelpIDs);
  979. break;
  980. case WM_CONTEXTMENU:
  981. WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(void *) aBrowseHelpIDs);
  982. break;
  983. default:
  984. return FALSE;
  985. }
  986. return TRUE;
  987. }
  988. // Flags for _OnPidlNavigation()
  989. #define SHBFFN_NONE 0x00000000 //
  990. #define SHBFFN_FIRE_SEL_CHANGE 0x00000001 //
  991. #define SHBFFN_UPDATE_TREE 0x00000002 //
  992. #define SHBFFN_STRICT_PARSING 0x00000004 //
  993. #define SHBFFN_DISPLAY_ERRORS 0x00000008 // If the parse fails, display an error dialog to inform the user.
  994. /***********************************************************************\
  995. DESCRIPTION:
  996. The API SHBrowseForFolder will now be able to act differently
  997. if a callback function isn't provided or the caller specified a flag
  998. to use the new UI. We can't rev the old UI because so many 3rd parties
  999. hack on it that we would break them. Therefore, we leave the code
  1000. above this point alone and use the code below if and only if we know
  1001. we won't break a 3rd party hacking on our dialog.
  1002. NOTES:
  1003. _pidlSelected/_fEditboxDirty: This is used to keep track of what
  1004. is most up to date, the editbox or the TreeView.
  1005. \***********************************************************************/
  1006. #define WNDPROP_CBrowseForFolder TEXT("WNDPROP_CBrowseForFolder_THIS")
  1007. class CBrowseForFolder : public IFolderFilter
  1008. , public IFolderFilterSite
  1009. {
  1010. public:
  1011. LPITEMIDLIST DisplayDialog(BFSF * pbfsf);
  1012. // *** IUnknown ***
  1013. STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
  1014. STDMETHODIMP_(ULONG) AddRef(void);
  1015. STDMETHODIMP_(ULONG) Release(void);
  1016. // *** IFolderFilter methods ***
  1017. STDMETHODIMP ShouldShow(IShellFolder* psf, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem) {return _ShouldShow(psf, pidlFolder, pidlItem, FALSE);};
  1018. STDMETHODIMP GetEnumFlags(IShellFolder* psf, LPCITEMIDLIST pidlFolder, HWND *phwnd, DWORD *pgrfFlags);
  1019. // *** IFolderFilterSite methods ***
  1020. STDMETHODIMP SetFilter(IUnknown* punk);
  1021. CBrowseForFolder(void);
  1022. ~CBrowseForFolder(void);
  1023. private:
  1024. // Private Methods
  1025. BOOL_PTR _DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  1026. LRESULT _NameSpaceWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  1027. BOOL _CreateNewFolder(HWND hDlg);
  1028. BOOL _OnCreateNameSpace(HWND hwnd);
  1029. BOOL _OnOK(void);
  1030. void _OnNotify(LPNMHDR pnm);
  1031. BOOL_PTR _OnCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify);
  1032. HRESULT _InitAutoComplete(HWND hwndEdit);
  1033. HRESULT _OnInitDialog(HWND hwnd);
  1034. HRESULT _OnInitSize(HWND hwnd);
  1035. HRESULT _OnLoadSize(HWND hwnd);
  1036. HRESULT _OnSaveSize(HWND hwnd);
  1037. HRESULT _OnSizeDialog(HWND hwnd, DWORD dwWidth, DWORD dwHeight);
  1038. HDWP _SizeControls(HWND hwnd, HDWP hdwp, RECT rcTree, int dx, int dy);
  1039. HRESULT _SetDialogSize(HWND hwnd, DWORD dwWidth, DWORD dwHeight);
  1040. BOOL_PTR _OnGetMinMaxInfo(MINMAXINFO * pMinMaxInfo);
  1041. HRESULT _ProcessEditChangeOnOK(BOOL fUpdateTree);
  1042. HRESULT _OnTreeSelectChange(DWORD dwFlags);
  1043. HRESULT _OnSetSelectPathA(LPCSTR pszPath);
  1044. HRESULT _OnSetSelectPathW(LPCWSTR pwzPath);
  1045. HRESULT _OnSetSelectPidl(LPCITEMIDLIST pidl);
  1046. HRESULT _OnSetExpandedPath(LPCTSTR pszPath);
  1047. HRESULT _OnSetExpandedPidl(LPCITEMIDLIST pidl);
  1048. HRESULT _OnPidlNavigation(LPCITEMIDLIST pidl, DWORD dwFlags);
  1049. HRESULT _OfferToPrepPath(OUT LPTSTR szPath, IN DWORD cchSize);
  1050. HRESULT _InitFilter(void);
  1051. HRESULT _DoesMatchFilter(IShellFolder* psf, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlChild, BOOL fStrict);
  1052. HRESULT _FilterThisFolder(LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlChild);
  1053. BOOL _DoesFilterAllow(LPCITEMIDLIST pidl, BOOL fStrictParsing);
  1054. HRESULT _ShouldShow(IShellFolder* psf, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem, BOOL fStrict);
  1055. // Private Member Variables
  1056. LONG _cRef;
  1057. INSCTree * _pns;
  1058. IWinEventHandler * _pweh;
  1059. IPersistFolder * _ppf; // AutoComplete's interface to set the current working directory for AC.
  1060. LPITEMIDLIST _pidlSelected;
  1061. BOOL _fEditboxDirty; // Is the editbox the last thing the user modified (over the selection tree).
  1062. HWND _hwndTv; // This is the NSC tree.
  1063. HWND _hwndBFF; // This is our NSC host hwnd.
  1064. HWND _hDlg;
  1065. BFSF * _pbfsf;
  1066. BOOL _fPrinterFilter;
  1067. LPITEMIDLIST _pidlChildFilter; // If non-NULL, we want to filter all children in this filder. (Including grandchildren)
  1068. IFolderFilter * _pClientFilter; // A client provided filter.
  1069. // Resize Info
  1070. POINT _ptLastSize; // Sizes in Window Coords
  1071. DWORD _dwMinWidth; // Sizes in Client Coords
  1072. DWORD _dwMinHeight; // Sizes in Client Coords
  1073. int _cxGrip;
  1074. int _cyGrip;
  1075. static BOOL_PTR CALLBACK BrowseForDirDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  1076. static LRESULT CALLBACK NameSpaceWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  1077. };
  1078. LPITEMIDLIST SHBrowseForFolder2(BFSF * pbfsf)
  1079. {
  1080. LPITEMIDLIST pidl = NULL;
  1081. HRESULT hrOle = SHOleInitialize(0); // The caller may not have inited OLE and we need to CoCreate _pns.
  1082. CBrowseForFolder * pcshbff = new CBrowseForFolder();
  1083. if (pcshbff)
  1084. {
  1085. pidl = pcshbff->DisplayDialog(pbfsf);
  1086. delete pcshbff;
  1087. }
  1088. SHOleUninitialize(hrOle);
  1089. return pidl;
  1090. }
  1091. CBrowseForFolder::CBrowseForFolder() : _cRef(1)
  1092. {
  1093. DllAddRef();
  1094. ASSERT(!_pns);
  1095. ASSERT(!_ppf);
  1096. ASSERT(!_pClientFilter);
  1097. }
  1098. CBrowseForFolder::~CBrowseForFolder()
  1099. {
  1100. ATOMICRELEASE(_pns);
  1101. ATOMICRELEASE(_ppf);
  1102. ATOMICRELEASE(_pweh);
  1103. ATOMICRELEASE(_pClientFilter);
  1104. Pidl_Set(&_pidlSelected, NULL);
  1105. AssertMsg((1 == _cRef), TEXT("CBrowseForFolder isn't a real COM object, but let's make sure people RefCount us like one."));
  1106. _FilterThisFolder(NULL, NULL);
  1107. DllRelease();
  1108. }
  1109. LPITEMIDLIST CBrowseForFolder::DisplayDialog(BFSF * pbfsf)
  1110. {
  1111. _pbfsf = pbfsf;
  1112. HRESULT hr = (HRESULT) DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_BROWSEFORFOLDER2), pbfsf->hwndOwner, BrowseForDirDlgProc, (LPARAM)this);
  1113. return (((S_OK == hr) && _pidlSelected) ? ILClone(_pidlSelected) : NULL);
  1114. }
  1115. //This WndProc will get the this pointer and call _NameSpaceWndProc() to do all the real work.
  1116. // This window proc is for the parent window of the tree control, not the dialog.
  1117. LRESULT CALLBACK CBrowseForFolder::NameSpaceWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1118. { // GWL_USERDATA
  1119. LRESULT lResult = 0;
  1120. CBrowseForFolder * pThis = (CBrowseForFolder *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
  1121. switch (uMsg)
  1122. {
  1123. case WM_CREATE:
  1124. {
  1125. CREATESTRUCT * pcs = (CREATESTRUCT *) lParam;
  1126. pThis = (CBrowseForFolder *)pcs->lpCreateParams;
  1127. SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)(void*)(CBrowseForFolder*)pThis);
  1128. }
  1129. break;
  1130. }
  1131. // we get a few messages before we get the WM_INITDIALOG (such as WM_SETFONT)
  1132. // and until we get the WM_INITDIALOG we dont have our pmbci pointer, we just
  1133. // return false
  1134. if (pThis)
  1135. lResult = (LRESULT) pThis->_NameSpaceWndProc(hwnd, uMsg, wParam, lParam);
  1136. else
  1137. lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
  1138. return lResult;
  1139. }
  1140. // Now that NameSpaceWndProc() gave us our this pointer, let's continue.
  1141. // This window proc is for the parent window of the tree control, not the dialog.
  1142. LRESULT CBrowseForFolder::_NameSpaceWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1143. {
  1144. LRESULT lResult = 0; // 0 means we didn't do anything
  1145. switch (uMsg)
  1146. {
  1147. case WM_CREATE:
  1148. _OnCreateNameSpace(hwnd);
  1149. break;
  1150. case WM_DESTROY:
  1151. IUnknown_SetSite(_pns, NULL);
  1152. break;
  1153. case WM_SETFOCUS:
  1154. SetFocus(_hwndTv);
  1155. break;
  1156. case WM_NOTIFY:
  1157. _OnNotify((LPNMHDR)lParam);
  1158. // Fall Thru...
  1159. case WM_SYSCOLORCHANGE:
  1160. case WM_WININICHANGE:
  1161. case WM_PALETTECHANGED:
  1162. if (_pweh)
  1163. _pweh->OnWinEvent(hwnd, uMsg, wParam, lParam, &lResult);
  1164. break;
  1165. default:
  1166. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  1167. }
  1168. return lResult;
  1169. }
  1170. BOOL_PTR CBrowseForFolder::_OnCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify)
  1171. {
  1172. switch (id)
  1173. {
  1174. case IDOK:
  1175. if (_OnOK())
  1176. {
  1177. EVAL(SUCCEEDED(_OnSaveSize(hDlg)));
  1178. EndDialog(hDlg, (int) S_OK);
  1179. return TRUE;
  1180. }
  1181. break;
  1182. case IDCANCEL:
  1183. EVAL(SUCCEEDED(_OnSaveSize(hDlg)));
  1184. EndDialog(hDlg, (int) S_FALSE);
  1185. return TRUE;
  1186. break;
  1187. case IDD_BROWSEEDIT:
  1188. if (codeNotify == EN_CHANGE)
  1189. _fEditboxDirty = TRUE;
  1190. break;
  1191. case IDD_NEWFOLDER_BUTTON:
  1192. _CreateNewFolder(hDlg);
  1193. return TRUE;
  1194. break;
  1195. default:
  1196. break;
  1197. }
  1198. return FALSE;
  1199. }
  1200. // This DlgProc will get the this pointer and call _DlgProc() to do all the real work.
  1201. // This window proc is for the dialog.
  1202. BOOL_PTR CALLBACK CBrowseForFolder::BrowseForDirDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1203. {
  1204. BOOL_PTR pfResult = FALSE;
  1205. CBrowseForFolder * pThis = (CBrowseForFolder *)GetWindowLongPtr(hDlg, GWLP_USERDATA);
  1206. switch (uMsg)
  1207. {
  1208. case WM_INITDIALOG:
  1209. pThis = (CBrowseForFolder *)lParam;
  1210. SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)(void*)(CBrowseForFolder*)pThis);
  1211. pfResult = TRUE;
  1212. break;
  1213. }
  1214. // we get a few messages before we get the WM_INITDIALOG (such as WM_SETFONT)
  1215. // and until we get the WM_INITDIALOG we dont have our pmbci pointer, we just
  1216. // return false
  1217. if (pThis)
  1218. pfResult = pThis->_DlgProc(hDlg, uMsg, wParam, lParam);
  1219. return pfResult;
  1220. }
  1221. #define WINDOWSTYLES_EX_BFF (WS_EX_LEFT | WS_EX_LTRREADING)
  1222. #define WINDOWSTYLES_BFF (WS_CHILD | WS_VISIBLE | WS_TABSTOP)
  1223. // Now that BrowseForDirDlgProc() gave us our this pointer, let's continue.
  1224. // This window proc is for the dialog.
  1225. BOOL_PTR CBrowseForFolder::_DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1226. {
  1227. BOOL_PTR pfResult = FALSE;
  1228. switch (uMsg)
  1229. {
  1230. case WM_INITDIALOG:
  1231. EVAL(SUCCEEDED(_OnInitDialog(hDlg)));
  1232. // Give initial focus to the ok button
  1233. pfResult = SetFocus(GetDlgItem(hDlg, IDOK)) == 0;
  1234. // Return FALSE if the ok button got focus.
  1235. break;
  1236. case WM_SIZE:
  1237. EVAL(SUCCEEDED(_OnSizeDialog(hDlg, LOWORD(lParam), HIWORD(lParam))));
  1238. pfResult = FALSE;
  1239. break;
  1240. case WM_GETMINMAXINFO:
  1241. pfResult = _OnGetMinMaxInfo((MINMAXINFO *) lParam);
  1242. break;
  1243. case WM_CLOSE:
  1244. BFSFCallback(_pbfsf, BFFM_IUNKNOWN, (LPARAM)NULL);
  1245. wParam = IDCANCEL;
  1246. // fall through
  1247. case WM_COMMAND:
  1248. pfResult = _OnCommand(hDlg, (int) LOWORD(wParam), (HWND) lParam, (UINT)HIWORD(wParam));
  1249. break;
  1250. // These BFFM_* messages are sent by the callback proc (_pbfsf->lpfn)
  1251. case BFFM_SETSTATUSTEXTA:
  1252. _BFSFSetStatusTextA(_pbfsf, (LPCSTR)lParam);
  1253. break;
  1254. case BFFM_SETSTATUSTEXTW:
  1255. _BFSFSetStatusTextW(_pbfsf, (LPCWSTR)lParam);
  1256. break;
  1257. case BFFM_SETSELECTIONW:
  1258. if ((BOOL)wParam)
  1259. {
  1260. // Is it a path?
  1261. pfResult = SUCCEEDED(_OnSetSelectPathW((LPCWSTR)lParam));
  1262. break;
  1263. }
  1264. // Fall Thru for pidl case
  1265. case BFFM_SETSELECTIONA:
  1266. if ((BOOL)wParam)
  1267. {
  1268. // Is it a path?
  1269. pfResult = SUCCEEDED(_OnSetSelectPathA((LPCSTR)lParam));
  1270. break;
  1271. }
  1272. // We hit the pidl case.
  1273. pfResult = SUCCEEDED(_OnSetSelectPidl((LPCITEMIDLIST)lParam));
  1274. break;
  1275. case BFFM_SETEXPANDED:
  1276. if ((BOOL)wParam)
  1277. {
  1278. // Is it a path?
  1279. pfResult = SUCCEEDED(_OnSetExpandedPath((LPCTSTR)lParam));
  1280. break;
  1281. }
  1282. // We hit the pidl case.
  1283. pfResult = SUCCEEDED(_OnSetExpandedPidl((LPCITEMIDLIST)lParam));
  1284. break;
  1285. case BFFM_ENABLEOK:
  1286. DlgEnableOk(_hDlg, lParam);
  1287. break;
  1288. case BFFM_SETOKTEXT:
  1289. _BFSFSetOkText(_pbfsf, (LPCTSTR)lParam);
  1290. break;
  1291. case WM_NOTIFY:
  1292. switch (((NMHDR *)lParam)->code)
  1293. {
  1294. case TVN_ITEMEXPANDINGA:
  1295. case TVN_ITEMEXPANDINGW:
  1296. SetCursor(LoadCursor(NULL, IDC_WAIT));
  1297. break;
  1298. case TVN_ITEMEXPANDEDA:
  1299. case TVN_ITEMEXPANDEDW:
  1300. SetCursor(LoadCursor(NULL, IDC_ARROW));
  1301. break;
  1302. case TVN_SELCHANGEDA:
  1303. case TVN_SELCHANGEDW:
  1304. _OnTreeSelectChange(SHBFFN_DISPLAY_ERRORS);
  1305. break;
  1306. }
  1307. break;
  1308. case WM_HELP:
  1309. WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR)(LPTSTR) aBrowseHelpIDs);
  1310. break;
  1311. case WM_CONTEXTMENU:
  1312. WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(LPTSTR) aBrowseHelpIDs);
  1313. break;
  1314. }
  1315. return pfResult;
  1316. }
  1317. HRESULT CBrowseForFolder::_OnInitDialog(HWND hwnd)
  1318. {
  1319. RECT rcDlg;
  1320. RECT rcTree;
  1321. WNDCLASS wc = {0};
  1322. LONG lTreeBottom = 0;
  1323. wc.style = CS_PARENTDC;
  1324. wc.lpfnWndProc = NameSpaceWndProc;
  1325. wc.hInstance = HINST_THISDLL;
  1326. wc.lpszClassName = CLASS_NSC;
  1327. SHRegisterClass(&wc);
  1328. _pbfsf->hwndDlg = hwnd;
  1329. _hDlg = hwnd;
  1330. GetWindowRect(GetDlgItem(hwnd, IDD_FOLDERLIST), &rcTree);
  1331. // cannot have both at the same time...
  1332. if ((_pbfsf->ulFlags & (BIF_UAHINT | BIF_EDITBOX)) == (BIF_UAHINT | BIF_EDITBOX))
  1333. _pbfsf->ulFlags &= ~BIF_UAHINT;
  1334. if (_pbfsf->ulFlags & BIF_NONEWFOLDERBUTTON)
  1335. {
  1336. EnableWindow(GetDlgItem(hwnd, IDD_NEWFOLDER_BUTTON), FALSE);
  1337. ShowWindow(GetDlgItem(hwnd, IDD_NEWFOLDER_BUTTON), SW_HIDE);
  1338. }
  1339. // Hide the edit box (or hide the UA hint if the edit box is enabled)
  1340. if (!(_pbfsf->ulFlags & BIF_EDITBOX))
  1341. {
  1342. // Hide the edit box
  1343. HWND hwndBrowseEdit = GetDlgItem(hwnd, IDD_BROWSEEDIT);
  1344. HWND hwndBrowseEditLabel = GetDlgItem(hwnd, IDD_FOLDERLABLE);
  1345. EnableWindow(hwndBrowseEdit, FALSE);
  1346. EnableWindow(hwndBrowseEditLabel, FALSE);
  1347. ShowWindow(hwndBrowseEdit, SW_HIDE);
  1348. ShowWindow(hwndBrowseEditLabel, SW_HIDE);
  1349. // Bottom of tree
  1350. RECT rcEdit;
  1351. GetWindowRect(hwndBrowseEdit, &rcEdit);
  1352. lTreeBottom = rcEdit.bottom;
  1353. }
  1354. if (!(_pbfsf->ulFlags & BIF_UAHINT))
  1355. {
  1356. // Hide the UA hint
  1357. HWND hwndBrowseInstruction = GetDlgItem(hwnd, IDD_BROWSEINSTRUCTION);
  1358. EnableWindow(hwndBrowseInstruction, FALSE);
  1359. ShowWindow(hwndBrowseInstruction, SW_HIDE);
  1360. }
  1361. if (!(_pbfsf->ulFlags & (BIF_EDITBOX | BIF_UAHINT)))
  1362. {
  1363. // Neither a UA hint nor an edit box.
  1364. // Increase the size of the tree
  1365. rcTree.bottom = lTreeBottom;
  1366. }
  1367. EnableWindow(GetDlgItem(hwnd, IDD_FOLDERLIST), FALSE);
  1368. ShowWindow(GetDlgItem(hwnd, IDD_FOLDERLIST), SW_HIDE);
  1369. EVAL(MapWindowPoints(HWND_DESKTOP, hwnd, (LPPOINT)&rcTree, 2));
  1370. _hwndBFF = CreateWindowEx(WINDOWSTYLES_EX_BFF, CLASS_NSC, NULL, WINDOWSTYLES_BFF, rcTree.left, rcTree.top, RECTWIDTH(rcTree), RECTHEIGHT(rcTree), hwnd, NULL, HINST_THISDLL, (void *)this);
  1371. ASSERT(_hwndBFF);
  1372. // Make sure the NSCTree is the first focusable control after the title static control,
  1373. // so that an accelerator in the title will give focus to the NSCTree.
  1374. SetWindowPos(_hwndBFF, GetDlgItem(hwnd, IDD_FOLDERLIST), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
  1375. LPTSTR psz = ResourceCStrToStr(HINST_THISDLL, _pbfsf->lpszTitle);
  1376. if (psz)
  1377. {
  1378. SetWindowText(GetDlgItem(hwnd, IDD_BROWSETITLE), psz);
  1379. if (psz != _pbfsf->lpszTitle)
  1380. LocalFree(psz);
  1381. }
  1382. _InitAutoComplete(GetDlgItem(hwnd, IDD_BROWSEEDIT));
  1383. BFSFCallback(_pbfsf, BFFM_INITIALIZED, 0);
  1384. BFSFCallback(_pbfsf, BFFM_IUNKNOWN, (LPARAM)SAFECAST(this, IFolderFilter *));
  1385. GetClientRect(hwnd, &rcDlg);
  1386. // Get the size of the gripper and position it.
  1387. _cxGrip = GetSystemMetrics(SM_CXVSCROLL);
  1388. _cyGrip = GetSystemMetrics(SM_CYHSCROLL);
  1389. _dwMinWidth = RECTWIDTH(rcDlg); // Sizes in Client Coords
  1390. _dwMinHeight = RECTHEIGHT(rcDlg);
  1391. GetWindowRect(hwnd, &rcDlg); // _ptLastSize sizes in Window Coords
  1392. _ptLastSize.x = RECTWIDTH(rcDlg); // This will force a resize for the first time.
  1393. _ptLastSize.y = RECTHEIGHT(rcDlg); // This will force a resize for the first time.
  1394. if ((_pbfsf->ulFlags & BIF_BROWSEFORPRINTER) != 0)
  1395. {
  1396. TCHAR szTitle[80];
  1397. LoadString(HINST_THISDLL, IDS_FINDSEARCH_PRINTER, szTitle, ARRAYSIZE(szTitle));
  1398. SetWindowText(hwnd, szTitle);
  1399. }
  1400. if (S_OK != _OnLoadSize(hwnd)) // Set the dialog size.
  1401. _OnInitSize(hwnd);
  1402. return S_OK;
  1403. }
  1404. HRESULT TranslateCloneOrDefault(LPCITEMIDLIST pidl, UINT csidl, LPITEMIDLIST *ppidl)
  1405. {
  1406. HRESULT hr;
  1407. if (pidl)
  1408. {
  1409. // map into friendly part of the name space
  1410. hr = SHILAliasTranslate(pidl, ppidl, XLATEALIAS_ALL);
  1411. if (FAILED(hr))
  1412. hr = SHILClone(pidl, ppidl);
  1413. }
  1414. else
  1415. {
  1416. hr = SHGetFolderLocation(NULL, csidl, NULL, 0, ppidl);
  1417. }
  1418. return hr;
  1419. }
  1420. BOOL CBrowseForFolder::_OnCreateNameSpace(HWND hwnd)
  1421. {
  1422. HRESULT hr = CoCreateInstance(CLSID_NSCTree, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(INSCTree, &_pns));
  1423. if (SUCCEEDED(hr))
  1424. {
  1425. RECT rc;
  1426. DWORD shcontf = SHCONTF_FOLDERS;
  1427. IFolderFilterSite *psffs;
  1428. hr = _pns->QueryInterface(IID_PPV_ARG(IFolderFilterSite, &psffs));
  1429. if (SUCCEEDED(hr))
  1430. {
  1431. hr = psffs->SetFilter(SAFECAST(this, IFolderFilter *));
  1432. psffs->Release();
  1433. }
  1434. _pns->SetNscMode(0); // 0 == Tree
  1435. _hwndTv = NULL;
  1436. DWORD dwStyle = WS_HSCROLL, dwExStyle = 0;
  1437. // ifdef'd out the following section of code until we can resolve the following problem:
  1438. // A TVS_SINGLEEXPAND tree expands selections even if they are done programatically. This
  1439. // results in My Documents, and any other selections by the client, expanding those nodes.
  1440. if (SHRegGetBoolUSValue(TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"),
  1441. TEXT("FriendlyTree"), FALSE, TRUE))
  1442. {
  1443. dwStyle |= TVS_HASBUTTONS | TVS_SINGLEEXPAND | TVS_TRACKSELECT;
  1444. dwExStyle |= TVS_EX_NOSINGLECOLLAPSE;
  1445. }
  1446. else
  1447. {
  1448. dwStyle |= TVS_HASBUTTONS | TVS_HASLINES;
  1449. }
  1450. INSCTree2 *pns2;
  1451. if (dwExStyle && SUCCEEDED(_pns->QueryInterface(IID_PPV_ARG(INSCTree2, &pns2))))
  1452. {
  1453. pns2->CreateTree2(hwnd, dwStyle, dwExStyle, &_hwndTv);
  1454. pns2->Release();
  1455. }
  1456. else
  1457. {
  1458. _pns->CreateTree(hwnd, dwStyle, &_hwndTv);
  1459. }
  1460. _pns->QueryInterface(IID_PPV_ARG(IWinEventHandler, &_pweh));
  1461. LPTSTR psz = ResourceCStrToStr(HINST_THISDLL, _pbfsf->lpszTitle);
  1462. if (psz)
  1463. {
  1464. SetWindowText(_hwndTv, psz);
  1465. if (psz != _pbfsf->lpszTitle)
  1466. LocalFree(psz);
  1467. }
  1468. // Turn on the ClientEdge
  1469. SetWindowBits(_hwndTv, GWL_EXSTYLE, WS_EX_CLIENTEDGE, WS_EX_CLIENTEDGE);
  1470. #define SIZE_ZOOM 1
  1471. // Show the ClientEdge
  1472. GetWindowRect(_hwndTv, &rc);
  1473. MapWindowRect(NULL, GetParent(_hwndTv), &rc);
  1474. InflateRect(&rc, (- SIZE_ZOOM * GetSystemMetrics(SM_CXEDGE)), (- SIZE_ZOOM * GetSystemMetrics(SM_CYEDGE)));
  1475. SetWindowPos(_hwndTv, NULL, rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc), SWP_NOZORDER);
  1476. _InitFilter();
  1477. if (_pbfsf->ulFlags & BIF_BROWSEFORPRINTER)
  1478. {
  1479. shcontf |= SHCONTF_NONFOLDERS;
  1480. }
  1481. if (_pbfsf->ulFlags & BIF_BROWSEINCLUDEFILES)
  1482. {
  1483. shcontf |= SHCONTF_NONFOLDERS;
  1484. }
  1485. if (_pbfsf->fShowAllObjects)
  1486. {
  1487. shcontf |= SHCONTF_INCLUDEHIDDEN;
  1488. }
  1489. LPITEMIDLIST pidlRoot;
  1490. TranslateCloneOrDefault(_pbfsf->pidlRoot, CSIDL_DESKTOP, &pidlRoot);
  1491. _pns->Initialize(pidlRoot, shcontf, NSS_DROPTARGET);
  1492. if (!_pbfsf->pidlRoot)
  1493. {
  1494. LPITEMIDLIST pidl;
  1495. if (SUCCEEDED(SHGetFolderLocation(NULL, CSIDL_PERSONAL, NULL, 0, &pidl)))
  1496. {
  1497. _pns->SetSelectedItem(pidl, TRUE, FALSE, 0);
  1498. ILFree(pidl);
  1499. }
  1500. }
  1501. ILFree(pidlRoot);
  1502. _pns->ShowWindow(TRUE);
  1503. _OnTreeSelectChange(SHBFFN_UPDATE_TREE | SHBFFN_NONE);
  1504. }
  1505. return TRUE;
  1506. }
  1507. // returns:
  1508. // TRUE - close the dialog
  1509. // FALSE - keep it up
  1510. BOOL CBrowseForFolder::_OnOK(void)
  1511. {
  1512. HRESULT hr = S_OK;
  1513. if (_pns)
  1514. {
  1515. // We get the <ENTER> event even if it was pressed while in editbox in a rename in
  1516. // the tree. Is that the case now?
  1517. if (_pns->InLabelEdit())
  1518. return FALSE; // Yes, so just bail.
  1519. // Was IDD_BROWSEEDIT modified more recently than the selection in the tree?
  1520. if (_fEditboxDirty)
  1521. {
  1522. // No, so _ProcessEditChangeOnOK() will update _pidlSelected with what's in the editbox.
  1523. // SUCCEEDED(hr)->EndDialog, FAILED(hr)->continue
  1524. // NOTE: FALSE means don't update the tree (since the dialog is closing)
  1525. hr = _ProcessEditChangeOnOK(FALSE);
  1526. }
  1527. else
  1528. {
  1529. // The user may have just finished editing the name of a newly created folder
  1530. // and NSC didn't tell use the pidl changes because of the rename. NT #377453.
  1531. // Therefore, we just update the pidl before we leave.
  1532. hr = _OnTreeSelectChange(SHBFFN_NONE);
  1533. }
  1534. if (SUCCEEDED(hr))
  1535. {
  1536. if (_pbfsf->pszDisplayName && _pidlSelected)
  1537. {
  1538. // the browse struct doesnt contain a buffer
  1539. // size so we assume MAX_PATH here....
  1540. SHGetNameAndFlags(_pidlSelected, SHGDN_NORMAL, _pbfsf->pszDisplayName, MAX_PATH, NULL);
  1541. }
  1542. }
  1543. }
  1544. return hr == S_OK;
  1545. }
  1546. #define SIZE_MAX_HEIGHT 1600
  1547. #define SIZE_MAX_WIDTH 1200
  1548. HRESULT CBrowseForFolder::_OnInitSize(HWND hwnd)
  1549. {
  1550. // The user hasn't yet sized the dialog, so we need to generate a good
  1551. // default size. The goal will be to base the window size on the monitor
  1552. // size with scaling properties.
  1553. //
  1554. // Size Algorithm:
  1555. // a) 1/3 hight of screen - Defined in the resource.
  1556. // b) Never larger than max - Based on a 1600x1200 screen
  1557. // c) Never smaller than min - Defined in the resource.
  1558. DWORD dwWidth;
  1559. DWORD dwHeight;
  1560. HMONITOR hmon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
  1561. MONITORINFO monInfo = {sizeof(monInfo), 0};
  1562. EVAL(GetMonitorInfo(hmon, &monInfo));
  1563. // a) 1/3 height of screen - Defined in the resource.
  1564. dwHeight = RECTHEIGHT(monInfo.rcWork) / 3;
  1565. dwWidth = (dwHeight * _dwMinHeight) / _dwMinWidth; // Scale up the width. Make it have the same ratio as _dwMinWidth/_dwMinHeight
  1566. // b) Never larger than max - Based on a 1600x1200 screen
  1567. if (dwWidth > SIZE_MAX_WIDTH)
  1568. dwWidth = SIZE_MAX_WIDTH;
  1569. if (dwHeight > SIZE_MAX_HEIGHT)
  1570. dwHeight = SIZE_MAX_HEIGHT;
  1571. // c) Never smaller than min - Defined in the resource.
  1572. // Set them to the min sizes if they are too small.
  1573. if (dwWidth < _dwMinWidth)
  1574. dwWidth = _dwMinWidth;
  1575. if (dwHeight < _dwMinHeight)
  1576. dwHeight = _dwMinHeight;
  1577. return _SetDialogSize(hwnd, dwWidth, dwHeight);
  1578. }
  1579. BOOL_PTR CBrowseForFolder::_OnGetMinMaxInfo(MINMAXINFO * pMinMaxInfo)
  1580. {
  1581. BOOL_PTR pfResult = 1;
  1582. if (pMinMaxInfo)
  1583. {
  1584. pMinMaxInfo->ptMinTrackSize.x = _dwMinWidth;
  1585. pMinMaxInfo->ptMinTrackSize.y = _dwMinHeight;
  1586. pfResult = 0; // Indicate it's handled.
  1587. }
  1588. return pfResult;
  1589. }
  1590. HRESULT CBrowseForFolder::_OnLoadSize(HWND hwnd)
  1591. {
  1592. HRESULT hr = S_FALSE;
  1593. DWORD dwWidth;
  1594. DWORD dwHeight;
  1595. DWORD cbSize1 = sizeof(dwWidth);
  1596. DWORD cbSize2 = sizeof(dwHeight);
  1597. if ((ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"), TEXT("Browse For Folder Width"), NULL, (void *)&dwWidth, &cbSize1)) &&
  1598. (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"), TEXT("Browse For Folder Height"), NULL, (void *)&dwHeight, &cbSize2)))
  1599. {
  1600. HMONITOR hmon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
  1601. MONITORINFO monInfo = {sizeof(monInfo), 0};
  1602. EVAL(GetMonitorInfo(hmon, &monInfo));
  1603. // Is the saved size within this monitor size?
  1604. if ((dwWidth < (DWORD)RECTWIDTH(monInfo.rcWork)) &&
  1605. (dwHeight < (DWORD)RECTHEIGHT(monInfo.rcWork)))
  1606. {
  1607. // Set them to the min sizes if they are too small.
  1608. if (dwWidth < _dwMinWidth)
  1609. dwWidth = _dwMinWidth;
  1610. if (dwHeight < _dwMinHeight)
  1611. dwHeight = _dwMinHeight;
  1612. hr = _SetDialogSize(hwnd, dwWidth, dwHeight);
  1613. }
  1614. }
  1615. return hr;
  1616. }
  1617. HRESULT CBrowseForFolder::_OnSaveSize(HWND hwnd)
  1618. {
  1619. RECT rc;
  1620. GetClientRect(hwnd, &rc);
  1621. DWORD dwWidth = (rc.right - rc.left);
  1622. DWORD dwHeight = (rc.bottom - rc.top);
  1623. SHSetValue(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"), TEXT("Browse For Folder Width"), REG_DWORD, (void *)&dwWidth, sizeof(dwWidth));
  1624. SHSetValue(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"), TEXT("Browse For Folder Height"), REG_DWORD, (void *)&dwHeight, sizeof(dwHeight));
  1625. return S_OK;
  1626. }
  1627. HDWP CBrowseForFolder::_SizeControls(HWND hwnd, HDWP hdwp, RECT rcTree, int dx, int dy)
  1628. {
  1629. // Move the controls.
  1630. HWND hwndControl = ::GetWindow(hwnd, GW_CHILD);
  1631. while (hwndControl && hdwp)
  1632. {
  1633. RECT rcControl;
  1634. GetWindowRect(hwndControl, &rcControl);
  1635. MapWindowRect(HWND_DESKTOP, hwnd, &rcControl);
  1636. switch (GetDlgCtrlID(hwndControl))
  1637. {
  1638. case IDD_BROWSETITLE:
  1639. // Increase the width of these controls
  1640. hdwp = DeferWindowPos(hdwp, hwndControl, NULL, rcControl.left, rcControl.top, (RECTWIDTH(rcControl) + dx), RECTHEIGHT(rcControl), (SWP_NOZORDER | SWP_NOACTIVATE));
  1641. InvalidateRect(hwndControl, NULL, TRUE);
  1642. break;
  1643. case IDD_FOLDERLABLE:
  1644. // Move these controls down if needed
  1645. hdwp = DeferWindowPos(hdwp, hwndControl, NULL, rcControl.left, (rcControl.top + dy), 0, 0, (SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE));
  1646. break;
  1647. case IDD_BROWSEEDIT:
  1648. // Increase the width move down if needed
  1649. hdwp = DeferWindowPos(hdwp, hwndControl, NULL, rcControl.left, (rcControl.top + dy), (RECTWIDTH(rcControl) + dx), RECTHEIGHT(rcControl), SWP_NOZORDER | SWP_NOACTIVATE);
  1650. break;
  1651. case IDD_BROWSEINSTRUCTION:
  1652. // Increase the width, move down if needed
  1653. hdwp = DeferWindowPos(hdwp, hwndControl, NULL, rcControl.left, (rcControl.top + dy), (RECTWIDTH(rcControl) + dx), RECTHEIGHT(rcControl), SWP_NOZORDER | SWP_NOACTIVATE);
  1654. break;
  1655. case IDD_NEWFOLDER_BUTTON:
  1656. // Keep this guy on the left, and move it down if needed.
  1657. hdwp = DeferWindowPos(hdwp, hwndControl, NULL, rcControl.left, (rcControl.top + dy), 0, 0, (SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE));
  1658. break;
  1659. case IDOK:
  1660. case IDCANCEL:
  1661. // Move these controls to the right, down if needed.
  1662. hdwp = DeferWindowPos(hdwp, hwndControl, NULL, (rcControl.left + dx), (rcControl.top + dy), 0, 0, (SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE));
  1663. break;
  1664. }
  1665. hwndControl = ::GetWindow(hwndControl, GW_HWNDNEXT);
  1666. }
  1667. return hdwp;
  1668. }
  1669. HRESULT CBrowseForFolder::_SetDialogSize(HWND hwnd, DWORD dwWidth, DWORD dwHeight)
  1670. {
  1671. HRESULT hr = S_OK;
  1672. RECT rcDlg = {0, 0, dwWidth, dwHeight};
  1673. // Set the sizing grip to the correct location.
  1674. SetWindowPos(GetDlgItem(hwnd, IDD_BFF_RESIZE_TAB), NULL, (dwWidth - _cxGrip), (dwHeight - _cyGrip), 0, 0, (SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE));
  1675. EVAL(AdjustWindowRect(&rcDlg, (DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_CONTEXTHELP | WS_EX_CLIENTEDGE | WS_SIZEBOX), NULL));
  1676. rcDlg.right -= rcDlg.left; // Adjust for other side.
  1677. rcDlg.bottom -= rcDlg.top; //
  1678. SetWindowPos(hwnd, NULL, 0, 0, rcDlg.right, rcDlg.bottom, (SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE));
  1679. // We don't need to call _OnSizeDialog() because SetWindowPos() will end up calling WS_SIZE so it will automatically get called.
  1680. return hr;
  1681. }
  1682. HRESULT CBrowseForFolder::_OnSizeDialog(HWND hwnd, DWORD dwWidth, DWORD dwHeight)
  1683. {
  1684. RECT rcNew; // Sizes in window Coords
  1685. RECT rcTree; // Sizes in window Coords
  1686. DWORD dwFullWidth;
  1687. DWORD dwFullHeight;
  1688. // Calculate the deltas in the x and y positions that we need to move
  1689. // each of the child controls.
  1690. GetWindowRect(hwnd, &rcNew);
  1691. dwFullWidth = RECTWIDTH(rcNew);
  1692. dwFullHeight = RECTHEIGHT(rcNew);
  1693. // If it's smaller than the min, fix it for the rest of the dialog.
  1694. if (dwFullWidth < _dwMinWidth)
  1695. dwFullWidth = _dwMinWidth;
  1696. if (dwFullHeight < _dwMinHeight)
  1697. dwFullHeight = _dwMinHeight;
  1698. int dx = (dwFullWidth - _ptLastSize.x);
  1699. int dy = (dwFullHeight - _ptLastSize.y);
  1700. // Update the new size.
  1701. _ptLastSize.x = dwFullWidth;
  1702. _ptLastSize.y = dwFullHeight;
  1703. // Size the view.
  1704. GetWindowRect(_hwndBFF, &rcTree);
  1705. MapWindowRect(HWND_DESKTOP, hwnd, &rcTree);
  1706. // Don't do anything if the size remains the same
  1707. if ((dx != 0) || (dy != 0))
  1708. {
  1709. HDWP hdwp = BeginDeferWindowPos(15);
  1710. // Set the sizing grip to the correct location.
  1711. SetWindowPos(GetDlgItem(hwnd, IDD_BFF_RESIZE_TAB), NULL, (dwWidth - _cxGrip), (dwHeight - _cyGrip), 0, 0, (SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE));
  1712. if (EVAL(hdwp))
  1713. {
  1714. hdwp = DeferWindowPos(hdwp, _hwndBFF, NULL, 0, 0, (RECTWIDTH(rcTree) + dx), (RECTHEIGHT(rcTree) + dy), (SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE));
  1715. if (hdwp)
  1716. hdwp = _SizeControls(hwnd, hdwp, rcTree, dx, dy);
  1717. if (EVAL(hdwp))
  1718. EVAL(EndDeferWindowPos(hdwp));
  1719. }
  1720. SetWindowPos(_hwndTv, NULL, 0, 0, (RECTWIDTH(rcTree) + dx - (SIZE_ZOOM * GetSystemMetrics(SM_CXEDGE))), (RECTHEIGHT(rcTree) + dy - (SIZE_ZOOM * GetSystemMetrics(SM_CYEDGE))), (SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE));
  1721. }
  1722. return S_OK;
  1723. }
  1724. HRESULT CBrowseForFolder::_OnSetSelectPathA(LPCSTR pszPath)
  1725. {
  1726. TCHAR szPath[MAX_PATH];
  1727. SHAnsiToTChar(pszPath, szPath, ARRAYSIZE(szPath));
  1728. return _OnSetSelectPathW(szPath);
  1729. }
  1730. HRESULT CBrowseForFolder::_OnSetSelectPathW(LPCWSTR pwzPath)
  1731. {
  1732. LPITEMIDLIST pidl;
  1733. HRESULT hr = SHParseDisplayName(pwzPath, NULL, &pidl, 0, NULL);
  1734. if (SUCCEEDED(hr))
  1735. {
  1736. hr = _OnPidlNavigation(pidl, SHBFFN_UPDATE_TREE);
  1737. ILFree(pidl);
  1738. }
  1739. return hr;
  1740. }
  1741. HRESULT CBrowseForFolder::_OnSetSelectPidl(LPCITEMIDLIST pidl)
  1742. {
  1743. LPITEMIDLIST pidlToFree;
  1744. HRESULT hr = TranslateCloneOrDefault(pidl, CSIDL_PERSONAL, &pidlToFree);
  1745. if (SUCCEEDED(hr))
  1746. {
  1747. hr = _OnPidlNavigation(pidlToFree, SHBFFN_UPDATE_TREE);
  1748. ILFree(pidlToFree);
  1749. }
  1750. return hr;
  1751. }
  1752. HRESULT CBrowseForFolder::_OnSetExpandedPath(LPCTSTR pszPath)
  1753. {
  1754. LPITEMIDLIST pidl;
  1755. HRESULT hr = SHParseDisplayName(pszPath, NULL, &pidl, 0, NULL);
  1756. if (SUCCEEDED(hr))
  1757. {
  1758. hr = _OnSetExpandedPidl(pidl);
  1759. ILFree(pidl);
  1760. }
  1761. return hr;
  1762. }
  1763. HRESULT CBrowseForFolder::_OnSetExpandedPidl(LPCITEMIDLIST pidl)
  1764. {
  1765. HRESULT hr = E_FAIL;
  1766. if (pidl && _pns)
  1767. {
  1768. IShellNameSpace *psns;
  1769. hr = _pns->QueryInterface(IID_PPV_ARG(IShellNameSpace, &psns));
  1770. if (SUCCEEDED(hr))
  1771. {
  1772. VARIANT varPidl;
  1773. hr = InitVariantFromIDList(&varPidl, pidl);
  1774. if (SUCCEEDED(hr))
  1775. {
  1776. hr = psns->Expand(varPidl, 1); // To a depth of 1
  1777. VariantClear(&varPidl);
  1778. }
  1779. psns->Release();
  1780. }
  1781. }
  1782. return hr;
  1783. }
  1784. BOOL CBrowseForFolder::_DoesFilterAllow(LPCITEMIDLIST pidl, BOOL fStrictParsing)
  1785. {
  1786. IShellFolder *psfParent;
  1787. LPCITEMIDLIST pidlChild;
  1788. HRESULT hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psfParent), &pidlChild);
  1789. if (SUCCEEDED(hr))
  1790. {
  1791. hr = _ShouldShow(psfParent, NULL, pidlChild, fStrictParsing);
  1792. psfParent->Release();
  1793. }
  1794. return ((S_OK == hr) ? TRUE : FALSE);
  1795. }
  1796. HRESULT CBrowseForFolder::_OnPidlNavigation(LPCITEMIDLIST pidl, DWORD dwFlags)
  1797. {
  1798. HRESULT hr = S_OK;
  1799. if (_DoesFilterAllow(pidl, (SHBFFN_STRICT_PARSING & dwFlags)))
  1800. {
  1801. Pidl_Set(&_pidlSelected, pidl);
  1802. if (_pidlSelected)
  1803. {
  1804. // NOTE: for perf, fUpdateTree is FALSE when closing the dialog, so
  1805. // we don't bother to call INSCTree::SetSelectedItem()
  1806. if ((SHBFFN_UPDATE_TREE & dwFlags) && _pns)
  1807. {
  1808. hr = _pns->SetSelectedItem(_pidlSelected, TRUE, FALSE, 0);
  1809. }
  1810. TCHAR szDisplayName[MAX_URL_STRING];
  1811. hr = SHGetNameAndFlags(_pidlSelected, SHGDN_NORMAL, szDisplayName, SIZECHARS(szDisplayName), NULL);
  1812. if (SUCCEEDED(hr))
  1813. {
  1814. EVAL(SetWindowText(GetDlgItem(_hDlg, IDD_BROWSEEDIT), szDisplayName));
  1815. _fEditboxDirty = FALSE;
  1816. }
  1817. if (SHBFFN_FIRE_SEL_CHANGE & dwFlags)
  1818. {
  1819. // For back compat reasons, we need to re-enable the OK button
  1820. // because the callback may turn it off.
  1821. EnableWindow(GetDlgItem(_hDlg, IDOK), TRUE);
  1822. BFSFCallback(_pbfsf, BFFM_SELCHANGED, (LPARAM)_pidlSelected);
  1823. }
  1824. if (_ppf) // Tell AutoComplete we are in a new location.
  1825. EVAL(SUCCEEDED(_ppf->Initialize(_pidlSelected)));
  1826. }
  1827. }
  1828. else
  1829. {
  1830. if (SHBFFN_DISPLAY_ERRORS & dwFlags)
  1831. {
  1832. TCHAR szPath[MAX_URL_STRING];
  1833. SHGetNameAndFlags(pidl, SHGDN_FORPARSING | SHGDN_FORADDRESSBAR, szPath, SIZECHARS(szPath), NULL);
  1834. // Display Error UI.
  1835. ShellMessageBox(HINST_THISDLL, _hDlg, MAKEINTRESOURCE(IDS_FOLDER_NOT_ALLOWED),
  1836. MAKEINTRESOURCE(IDS_FOLDER_NOT_ALLOWED_TITLE), (MB_OK | MB_ICONHAND), szPath);
  1837. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  1838. }
  1839. else
  1840. hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
  1841. }
  1842. return hr;
  1843. }
  1844. BOOL CBrowseForFolder::_CreateNewFolder(HWND hDlg)
  1845. {
  1846. IShellFavoritesNameSpace * psfns;
  1847. if (_pns && SUCCEEDED(_pns->QueryInterface(IID_PPV_ARG(IShellFavoritesNameSpace, &psfns))))
  1848. {
  1849. HRESULT hr = psfns->NewFolder();
  1850. if (FAILED(hr) && (HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr))
  1851. {
  1852. // If it failed, then the user doesn't have permission to create a
  1853. // new folder here. We can't disable the "New Folder" button because
  1854. // it takes too long (perf) to see if it's supported. The only way
  1855. // is to determine if "New Folder" is in the ContextMenu.
  1856. ShellMessageBox(HINST_THISDLL, hDlg, MAKEINTRESOURCE(IDS_NEWFOLDER_NOT_HERE),
  1857. MAKEINTRESOURCE(IDS_NEWFOLDER_NOT_HERE_TITLE), (MB_OK | MB_ICONHAND));
  1858. }
  1859. else
  1860. {
  1861. if (SUCCEEDED(hr))
  1862. {
  1863. _fEditboxDirty = FALSE; // The newly selected node in the tree is the most up to date.
  1864. }
  1865. }
  1866. psfns->Release();
  1867. }
  1868. return TRUE;
  1869. }
  1870. HRESULT IUnknown_SetOptions(IUnknown * punk, DWORD dwACLOptions)
  1871. {
  1872. IACList2 * pal2;
  1873. HRESULT hr = punk->QueryInterface(IID_PPV_ARG(IACList2, &pal2));
  1874. if (SUCCEEDED(hr))
  1875. {
  1876. hr = pal2->SetOptions(dwACLOptions);
  1877. pal2->Release();
  1878. }
  1879. return hr;
  1880. }
  1881. HRESULT CBrowseForFolder::_InitAutoComplete(HWND hwndEdit)
  1882. {
  1883. HRESULT hr = CoCreateInstance(CLSID_ACListISF, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IPersistFolder, &_ppf));
  1884. if (SUCCEEDED(hr))
  1885. {
  1886. IAutoComplete2 * pac;
  1887. // Create the AutoComplete Object
  1888. hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IAutoComplete2, &pac));
  1889. if (SUCCEEDED(hr))
  1890. {
  1891. hr = pac->Init(hwndEdit, _ppf, NULL, NULL);
  1892. // Set the autocomplete options
  1893. DWORD dwACOptions = 0;
  1894. if (SHRegGetBoolUSValue(REGSTR_PATH_AUTOCOMPLETE, REGSTR_VAL_USEAUTOAPPEND, FALSE, /*default:*/FALSE))
  1895. {
  1896. dwACOptions |= ACO_AUTOAPPEND;
  1897. }
  1898. if (SHRegGetBoolUSValue(REGSTR_PATH_AUTOCOMPLETE, REGSTR_VAL_USEAUTOSUGGEST, FALSE, /*default:*/TRUE))
  1899. {
  1900. dwACOptions |= ACO_AUTOSUGGEST;
  1901. }
  1902. EVAL(SUCCEEDED(pac->SetOptions(dwACOptions)));
  1903. EVAL(SUCCEEDED(IUnknown_SetOptions(_ppf, ACLO_FILESYSONLY)));
  1904. _OnTreeSelectChange(SHBFFN_UPDATE_TREE | SHBFFN_NONE);
  1905. pac->Release();
  1906. }
  1907. }
  1908. return hr;
  1909. }
  1910. void CBrowseForFolder::_OnNotify(LPNMHDR pnm)
  1911. {
  1912. if (pnm)
  1913. {
  1914. switch (pnm->code)
  1915. {
  1916. case TVN_SELCHANGEDA:
  1917. case TVN_SELCHANGEDW:
  1918. _OnTreeSelectChange(SHBFFN_DISPLAY_ERRORS);
  1919. break;
  1920. }
  1921. }
  1922. }
  1923. /***********************************************************************\
  1924. DESCRIPTION:
  1925. If the string was formatted as a UNC or Drive path, offer to
  1926. create the directory path if it doesn't exist. If the media isn't
  1927. inserted or formated, offer to do that also.
  1928. PARAMETER:
  1929. szPath: The path the user entered into the editbox after it was
  1930. expanded.
  1931. RETURN: S_OK means it's not a file system path or it exists.
  1932. S_FALSE means it's was a file system path but didn't exist
  1933. or creating it didn't work but NO ERROR UI was displayed.
  1934. FAILURE(): Error UI was displayed, so caller should not
  1935. display error UI.
  1936. \***********************************************************************/
  1937. HRESULT CBrowseForFolder::_OfferToPrepPath(OUT LPTSTR szPath, IN DWORD cchSize)
  1938. {
  1939. HRESULT hr = S_OK;
  1940. TCHAR szDisplayName[MAX_URL_STRING];
  1941. BOOL fSkipValidation = FALSE; // Only skip validation if we display the err UI.
  1942. // TODO: Replace this with CShellUrl->ParseFromOutsideSource(), however, this will require
  1943. // making CShellUrl (browseui) into a COM object. This will allow us to parse relative
  1944. // paths.
  1945. GetDlgItemText(_hDlg, IDD_BROWSEEDIT, szDisplayName, ARRAYSIZE(szDisplayName));
  1946. // Callers
  1947. if (SHExpandEnvironmentStrings(szDisplayName, szPath, cchSize)
  1948. && (PathIsUNC(szPath) || (-1 != PathGetDriveNumber(szPath))))
  1949. {
  1950. hr = E_FAIL;
  1951. // I found a problem where UNC paths to printers
  1952. // will fail SHPathPrepareForWrite(). If the caller is
  1953. // looking for printers, we want to succeed in that case and
  1954. // not fail.
  1955. if ((_pbfsf->ulFlags & BIF_BROWSEFORPRINTER) && PathIsUNCServerShare(szPath))
  1956. {
  1957. LPITEMIDLIST pidlTest;
  1958. hr = SHParseDisplayName(szPath, NULL, &pidlTest, 0, NULL);
  1959. if (SUCCEEDED(hr))
  1960. ILFree(pidlTest);
  1961. }
  1962. if (FAILED(hr))
  1963. {
  1964. // yes, so make sure the drive is inserted (if ejectable)
  1965. // This will also offer to format unformatted drives.
  1966. hr = SHPathPrepareForWrite(_hDlg, NULL, szPath, SHPPFW_MEDIACHECKONLY);
  1967. if (FAILED_AND_NOT_CANCELED(hr))
  1968. {
  1969. hr = S_OK; // this function needs to succeed for us to be able to send VALIDATEFAILED
  1970. }
  1971. }
  1972. }
  1973. else
  1974. StrCpyN(szPath, szDisplayName, cchSize);
  1975. return hr;
  1976. }
  1977. HRESULT CBrowseForFolder::_ProcessEditChangeOnOK(BOOL fUpdateTree)
  1978. {
  1979. TCHAR szPath[MAX_URL_STRING];
  1980. HRESULT hr = _OfferToPrepPath(szPath, ARRAYSIZE(szPath));
  1981. // It will succeed if it was successful or DIDN'T display an error
  1982. // dialog and we didn't.
  1983. if (SUCCEEDED(hr))
  1984. {
  1985. LPITEMIDLIST pidl;
  1986. if (SUCCEEDED(SHParseDisplayName(szPath, NULL, &pidl, 0, NULL)))
  1987. {
  1988. DWORD dwFlags = (SHBFFN_FIRE_SEL_CHANGE | SHBFFN_STRICT_PARSING | SHBFFN_DISPLAY_ERRORS);
  1989. _fEditboxDirty = FALSE;
  1990. if (fUpdateTree)
  1991. dwFlags |= SHBFFN_UPDATE_TREE;
  1992. hr = _OnPidlNavigation(pidl, dwFlags);
  1993. if (SUCCEEDED(hr))
  1994. _fEditboxDirty = FALSE;
  1995. }
  1996. if ((_pbfsf->ulFlags & BIF_VALIDATE) && !pidl)
  1997. {
  1998. LPARAM lParam;
  1999. CHAR szAnsi[MAX_URL_STRING];
  2000. WCHAR wzUnicode[MAX_URL_STRING];
  2001. if (_pbfsf->fUnicode)
  2002. {
  2003. SHTCharToUnicode(szPath, wzUnicode, ARRAYSIZE(wzUnicode));
  2004. lParam = (LPARAM) wzUnicode;
  2005. }
  2006. else
  2007. {
  2008. SHTCharToAnsi(szPath, szAnsi, ARRAYSIZE(szAnsi));
  2009. lParam = (LPARAM) szAnsi;
  2010. }
  2011. ASSERTMSG(_pbfsf->lpfn != NULL, "No BrowseCallbackProc supplied with BIF_VALIDATE flag");
  2012. // 0:EndDialog, 1:continue
  2013. if (0 == BFSFCallback(_pbfsf, (_pbfsf->fUnicode? BFFM_VALIDATEFAILEDW : BFFM_VALIDATEFAILEDA), lParam))
  2014. hr = S_OK; // This is returned so the dialog can close in _OnOK
  2015. else
  2016. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  2017. }
  2018. ILFree(pidl);
  2019. }
  2020. return hr;
  2021. }
  2022. HRESULT CBrowseForFolder::_OnTreeSelectChange(DWORD dwFlags)
  2023. {
  2024. HRESULT hr = S_OK;
  2025. if (_pns)
  2026. {
  2027. LPITEMIDLIST pidl;
  2028. hr = _pns->GetSelectedItem(&pidl, 0);
  2029. if (S_OK == hr)
  2030. {
  2031. hr = _OnPidlNavigation(pidl, (SHBFFN_FIRE_SEL_CHANGE | dwFlags));
  2032. ILFree(pidl);
  2033. }
  2034. }
  2035. return hr;
  2036. }
  2037. HRESULT CBrowseForFolder::QueryInterface(REFIID riid, void **ppv)
  2038. {
  2039. static const QITAB qit[] = {
  2040. QITABENT(CBrowseForFolder, IFolderFilter), // IID_IFolderFilter
  2041. QITABENT(CBrowseForFolder, IFolderFilterSite), // IID_IFolderFilterSite
  2042. { 0 },
  2043. };
  2044. return QISearch(this, qit, riid, ppv);
  2045. }
  2046. ULONG CBrowseForFolder::AddRef()
  2047. {
  2048. return InterlockedIncrement(&_cRef);
  2049. }
  2050. ULONG CBrowseForFolder::Release()
  2051. {
  2052. if (InterlockedDecrement(&_cRef))
  2053. return _cRef;
  2054. // We aren't a real COM object yet.
  2055. // delete this;
  2056. return 0;
  2057. }
  2058. HRESULT CBrowseForFolder::_ShouldShow(IShellFolder* psf, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem, BOOL fStrict)
  2059. {
  2060. HRESULT hr = S_OK;
  2061. BOOL fFilterChildern = FALSE;
  2062. // Do we want to filter our all the children of a certain folder?
  2063. if (_pidlChildFilter)
  2064. {
  2065. // Yes, let's see if the tree walking caller is still
  2066. // in this folder?
  2067. if (pidlFolder && ILIsParent(_pidlChildFilter, pidlFolder, FALSE))
  2068. {
  2069. // Yes, so don't use it.
  2070. hr = S_FALSE;
  2071. }
  2072. else
  2073. {
  2074. // The calling tree walker has walked out side of
  2075. // this folder, so remove the filter.
  2076. _FilterThisFolder(NULL, NULL);
  2077. }
  2078. }
  2079. AssertMsg((ILIsEmpty(pidlItem) || ILIsEmpty(_ILNext(pidlItem))), TEXT("CBrowseForFolder::ShouldShow() pidlItem needs to be only one itemID long because we don't handle that case."));
  2080. if (S_OK == hr)
  2081. {
  2082. hr = _DoesMatchFilter(psf, pidlFolder, pidlItem, fStrict);
  2083. }
  2084. // If this pidl has still not been filtered out, give our client filter a chance.
  2085. if (_pClientFilter && (hr == S_OK))
  2086. {
  2087. hr = _pClientFilter->ShouldShow(psf, pidlFolder, pidlItem);
  2088. }
  2089. return hr;
  2090. }
  2091. HRESULT CBrowseForFolder::GetEnumFlags(IShellFolder* psf, LPCITEMIDLIST pidlFolder, HWND *phwnd, DWORD *pgrfFlags)
  2092. {
  2093. if (_pbfsf->ulFlags & BIF_SHAREABLE)
  2094. *pgrfFlags |= SHCONTF_SHAREABLE;
  2095. if (_pbfsf->ulFlags & BIF_BROWSEFORPRINTER)
  2096. *pgrfFlags |= SHCONTF_NETPRINTERSRCH;
  2097. // Also delegate to client filter.
  2098. if (_pClientFilter)
  2099. {
  2100. return _pClientFilter->GetEnumFlags(psf, pidlFolder, phwnd, pgrfFlags);
  2101. }
  2102. return S_OK;
  2103. }
  2104. // *** IFolderFilterSite method ***
  2105. HRESULT CBrowseForFolder::SetFilter(IUnknown* punk)
  2106. {
  2107. HRESULT hr = S_OK;
  2108. ATOMICRELEASE(_pClientFilter);
  2109. if (punk)
  2110. hr = punk->QueryInterface(IID_PPV_ARG(IFolderFilter, &_pClientFilter));
  2111. return hr;
  2112. }
  2113. HRESULT CBrowseForFolder::_FilterThisFolder(LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlChild)
  2114. {
  2115. if (_pidlChildFilter)
  2116. ILFree(_pidlChildFilter);
  2117. if (pidlChild)
  2118. _pidlChildFilter = ILCombine(pidlFolder, pidlChild);
  2119. else
  2120. {
  2121. if (pidlFolder)
  2122. _pidlChildFilter = ILClone(pidlFolder);
  2123. else
  2124. _pidlChildFilter = NULL;
  2125. }
  2126. return S_OK;
  2127. }
  2128. HRESULT CBrowseForFolder::_InitFilter(void)
  2129. {
  2130. HRESULT hr = S_OK;
  2131. // Need to do a couple of special cases here to allow us to
  2132. // browse for a network printer. In this case if we are at server
  2133. // level we then need to change what we search for non folders when
  2134. // we are the level of a server.
  2135. if ((_pbfsf->ulFlags & (BIF_BROWSEFORPRINTER | BIF_NEWDIALOGSTYLE)) == BIF_BROWSEFORPRINTER)
  2136. {
  2137. LPCITEMIDLIST pidl = ILFindLastID(_pbfsf->pidlRoot);
  2138. _fPrinterFilter = ((SIL_GetType(pidl) & (SHID_NET | SHID_INGROUPMASK)) == SHID_NET_SERVER);
  2139. }
  2140. return hr;
  2141. }
  2142. BOOL IsPidlUrl(IShellFolder *psf, LPCITEMIDLIST pidlChild)
  2143. {
  2144. BOOL fIsURL = FALSE;
  2145. WCHAR wzDisplayName[MAX_URL_STRING];
  2146. if (SUCCEEDED(DisplayNameOf(psf, pidlChild, SHGDN_FORPARSING, wzDisplayName, ARRAYSIZE(wzDisplayName))))
  2147. {
  2148. fIsURL = PathIsURLW(wzDisplayName);
  2149. }
  2150. return fIsURL;
  2151. }
  2152. HRESULT CBrowseForFolder::_DoesMatchFilter(IShellFolder *psf, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlChild, BOOL fStrict)
  2153. {
  2154. HRESULT hr = S_OK;
  2155. // We need to special case here in the netcase where we only
  2156. // browse down to workgroups...
  2157. //
  2158. //
  2159. // Here is where I also need to special case to not go below
  2160. // workgroups when the appropriate option is set.
  2161. BYTE bType = SIL_GetType(pidlChild);
  2162. if ((_pbfsf->ulFlags & BIF_DONTGOBELOWDOMAIN) && (bType & SHID_NET))
  2163. {
  2164. switch (bType & (SHID_NET | SHID_INGROUPMASK))
  2165. {
  2166. case SHID_NET_SERVER:
  2167. hr = S_FALSE; // don't add it
  2168. break;
  2169. case SHID_NET_DOMAIN:
  2170. _FilterThisFolder(pidlFolder, pidlChild); // Force to not have children;
  2171. break;
  2172. }
  2173. }
  2174. else if ((_pbfsf->ulFlags & BIF_BROWSEFORCOMPUTER) && (bType & SHID_NET))
  2175. {
  2176. if ((bType & (SHID_NET | SHID_INGROUPMASK)) == SHID_NET_SERVER)
  2177. _FilterThisFolder(pidlFolder, pidlChild); // Don't expand below it...
  2178. }
  2179. else if (_pbfsf->ulFlags & BIF_BROWSEFORPRINTER)
  2180. {
  2181. /* This code is a work in progress and will be refined post beta 1 */
  2182. IShellLink* pShortcut = NULL;
  2183. if (SUCCEEDED(psf->BindToObject(pidlChild, NULL, IID_PPV_ARG(IShellLink, &pShortcut))))
  2184. {
  2185. LPITEMIDLIST pShortcutTargetIDList = NULL;
  2186. if (SUCCEEDED(pShortcut->GetIDList(&pShortcutTargetIDList)))
  2187. {
  2188. IShellFolder* pTargetParentFolder;
  2189. LPITEMIDLIST pTargetRelativeIDList;
  2190. if (SUCCEEDED(SHBindToIDListParent(pShortcutTargetIDList, IID_PPV_ARG(IShellFolder, &pTargetParentFolder), (LPCITEMIDLIST*) &pTargetRelativeIDList)))
  2191. {
  2192. BYTE NetResourceArray[2048];
  2193. NETRESOURCE* pNetResource = (NETRESOURCE*) NetResourceArray;
  2194. SHGetDataFromIDList(pTargetParentFolder, pTargetRelativeIDList, SHGDFIL_NETRESOURCE, (void*) &NetResourceArray, sizeof(NetResourceArray));
  2195. if (RESOURCEDISPLAYTYPE_SHARE == pNetResource->dwDisplayType)
  2196. {
  2197. hr = S_FALSE;
  2198. }
  2199. pTargetParentFolder->Release();
  2200. }
  2201. ILFree(pShortcutTargetIDList);
  2202. }
  2203. pShortcut->Release();
  2204. }
  2205. if (S_OK == hr) // we don't want S_FALSE
  2206. {
  2207. // Special case when we are only allowing printers.
  2208. // for now I will simply key on the fact that it is non-FS.
  2209. ULONG ulAttr = SFGAO_FILESYSANCESTOR;
  2210. psf->GetAttributesOf(1, &pidlChild, &ulAttr);
  2211. if ((ulAttr & (SFGAO_FILESYSANCESTOR)) == 0)
  2212. {
  2213. _FilterThisFolder(pidlFolder, pidlChild); // Don't expand below it...
  2214. }
  2215. else
  2216. {
  2217. if (_fPrinterFilter)
  2218. hr = S_FALSE; // don't add it
  2219. }
  2220. }
  2221. }
  2222. else if (!(_pbfsf->ulFlags & BIF_BROWSEINCLUDEFILES))
  2223. {
  2224. // If the caller wants to include URLs and this is an URL,
  2225. // then we are done. Otherwise, we need to enter this if and
  2226. // filter out items that don't have the SFGAO_FOLDER attribute
  2227. // set.
  2228. if (!(_pbfsf->ulFlags & BIF_BROWSEINCLUDEURLS) || !IsPidlUrl(psf, pidlChild))
  2229. {
  2230. // Lets not use the callback to see if this item has children or not
  2231. // as some or files (no children) and it is not worth writing our own
  2232. // enumerator as we don't want the + to depend on if there are sub-folders
  2233. // but instead it should be if it has files...
  2234. ULONG ulAttr = SFGAO_FOLDER;
  2235. psf->GetAttributesOf(1, (LPCITEMIDLIST *) &pidlChild, &ulAttr);
  2236. if ((ulAttr & SFGAO_FOLDER)== 0)
  2237. hr = S_FALSE; // don't add it
  2238. }
  2239. }
  2240. if (_pbfsf->ulFlags & (BIF_RETURNONLYFSDIRS | BIF_RETURNFSANCESTORS))
  2241. {
  2242. // If we are only looking for FS level things only add items
  2243. // that are in the name space that are file system objects or
  2244. // ancestors of file system objects
  2245. ULONG ulAttr = 0;
  2246. if (fStrict)
  2247. {
  2248. if (_pbfsf->ulFlags & BIF_RETURNONLYFSDIRS)
  2249. ulAttr |= SFGAO_FILESYSTEM;
  2250. if (_pbfsf->ulFlags & BIF_RETURNFSANCESTORS)
  2251. ulAttr |= SFGAO_FILESYSANCESTOR;
  2252. }
  2253. else
  2254. {
  2255. ulAttr = (SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM);
  2256. }
  2257. psf->GetAttributesOf(1, (LPCITEMIDLIST *) &pidlChild, &ulAttr);
  2258. if ((ulAttr & (SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM))== 0)
  2259. {
  2260. hr = S_FALSE; // don't add it
  2261. }
  2262. }
  2263. return hr;
  2264. }