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.

632 lines
19 KiB

  1. // BrowseDlg.cpp
  2. // Dialog box to enable user to select a directory and/or files.
  3. // Author: t-michkr (June 22, 2000)
  4. #include <windows.h>
  5. #include "stdafx.h"
  6. // We make use of some Win2K specific controls
  7. #include <shellapi.h>
  8. #include <shlwapi.h>
  9. #include <tchar.h>
  10. #include <assert.h>
  11. #include "filebrowser.h"
  12. #include "resource.h"
  13. #include "commctrl.h"
  14. // Display browse dialog box, and return dir string.
  15. PSTR BrowseForFolder(HWND hwnd, PSTR szInitialPath, UINT uiFlags);
  16. // Expand a tree item to include sub items.
  17. void AddTreeSubItems(HWND hwTree, HTREEITEM hParent);
  18. // Remove a tree item's subitems
  19. void RemoveTreeSubItems(HWND hwTree, HTREEITEM hParent);
  20. void CheckTreeSubItems(HWND hwTree, HTREEITEM hChild);
  21. // Given a path, select the appropriate item in the tree.
  22. // If path is invalid, it will expand as much as possible
  23. // (until invalid element appears)
  24. void SelectItemFromFullPath(HWND hwTree, PTSTR szPath);
  25. // Get full item path. Assumes szPath is a buffer of MAX_PATH size,
  26. // initialized with '\0'.
  27. void GetItemPath(HWND hwTree, HTREEITEM hItem, PTSTR szPath);
  28. // Browse dialog proc
  29. BOOL CALLBACK BrowseDialogProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam);
  30. // Browse dialog box message handlers.
  31. BOOL HandleInitBrowse(HWND hwnd);
  32. void HandleBrowseCommand(HWND hwnd, UINT uiCtrlID, UINT uiNotify, HWND hwChild);
  33. void HandleBrowseNotify(HWND hwnd, void* pvArg);
  34. // Buffer to hold returned path
  35. static TCHAR s_szPathBuffer[MAX_PATH];
  36. static PTSTR s_szInitialPath = 0;
  37. static HIMAGELIST s_himlSystem = 0;
  38. // Create browse dialog box, and return a path string, or
  39. // NULL if cancel was selected.
  40. PTSTR BrowseForFolder(HWND hwnd, PTSTR szInitialPath)
  41. {
  42. CoInitialize(0);
  43. s_szInitialPath = szInitialPath;
  44. PTSTR szRet = reinterpret_cast<TCHAR*>(DialogBox(_Module.GetModuleInstance(),
  45. MAKEINTRESOURCE(IDD_BROWSE), hwnd, BrowseDialogProc));
  46. CoUninitialize();
  47. return szRet;
  48. }
  49. // Browse dialog box proc.
  50. BOOL CALLBACK BrowseDialogProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
  51. {
  52. switch(uiMsg)
  53. {
  54. case WM_INITDIALOG:
  55. return HandleInitBrowse(hwnd);
  56. break;
  57. case WM_COMMAND:
  58. HandleBrowseCommand(hwnd, LOWORD(wParam), HIWORD(wParam),
  59. reinterpret_cast<HWND>(lParam));
  60. break;
  61. case WM_NOTIFY:
  62. HandleBrowseNotify(hwnd, reinterpret_cast<void*>(lParam));
  63. break;
  64. default:
  65. return FALSE;
  66. }
  67. return TRUE;
  68. }
  69. // Dialog box initialization, init tree and root tree items.
  70. BOOL HandleInitBrowse(HWND hwnd)
  71. {
  72. // Get the treeview control
  73. HWND hwTree = GetDlgItem(hwnd, IDC_DIRTREE);
  74. if(!hwTree)
  75. return FALSE;
  76. SHFILEINFO sfi;
  77. TreeView_SetImageList(hwTree, reinterpret_cast<HIMAGELIST>(SHGetFileInfo(TEXT("C:\\"),
  78. 0,&sfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON)),
  79. TVSIL_NORMAL);
  80. // Get all user drives
  81. DWORD dwLength = GetLogicalDriveStrings(0,0);
  82. if(dwLength == 0)
  83. return FALSE;
  84. TCHAR* szDrives = new TCHAR[dwLength+1];
  85. if(!szDrives)
  86. return FALSE;
  87. GetLogicalDriveStrings(dwLength, szDrives);
  88. TCHAR* szCurrDrive = szDrives;
  89. // Go through each drive
  90. while(*szCurrDrive)
  91. {
  92. // Only pay attention to fixed drives (non-network, non-CD, non-floppy)
  93. if(GetDriveType(szCurrDrive) == DRIVE_FIXED)
  94. {
  95. SHGetFileInfo(szCurrDrive, 0, &sfi, sizeof(sfi),
  96. SHGFI_SYSICONINDEX);
  97. // Get rid of the terminating '\'
  98. szCurrDrive[lstrlen(szCurrDrive)-1] = TEXT('\0');
  99. // Insert a disk drive item into the tree root.
  100. TVINSERTSTRUCT tvis;
  101. tvis.hParent = TVI_ROOT;
  102. tvis.hInsertAfter = TVI_LAST;
  103. tvis.itemex.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE| TVIF_TEXT;
  104. tvis.itemex.iImage = sfi.iIcon;
  105. tvis.itemex.iSelectedImage = sfi.iIcon;
  106. tvis.itemex.pszText = szCurrDrive;
  107. tvis.itemex.cchTextMax = lstrlen(szCurrDrive);
  108. HTREEITEM hTreeItem = TreeView_InsertItem(hwTree, &tvis);
  109. assert(hTreeItem);
  110. // Add subitems to the item
  111. AddTreeSubItems(hwTree, hTreeItem);
  112. // Move to next drive
  113. szCurrDrive += lstrlen(szCurrDrive) + 2;
  114. }
  115. else
  116. // Move to next drive.
  117. szCurrDrive += lstrlen(szCurrDrive) + 1;
  118. }
  119. delete szDrives;
  120. // Select the first element.
  121. HTREEITEM hItem = TreeView_GetChild(hwTree, TVI_ROOT);
  122. TreeView_SelectItem(hwTree, hItem);
  123. // Force tree to update, and restore original focus
  124. SetFocus(hwTree);
  125. SetFocus(GetDlgItem(hwnd, IDOK));
  126. return TRUE;
  127. }
  128. // Catch notification messages, so we can control expansion/collapsing.
  129. void HandleBrowseNotify(HWND hwnd, void* pvArg)
  130. {
  131. // Get tree control
  132. HWND hwTree = GetDlgItem(hwnd, IDC_DIRTREE);
  133. HWND hwFileList = GetDlgItem(hwnd, IDC_FILELISTCOMBO);
  134. if(!hwTree || !hwFileList)
  135. {
  136. DestroyWindow(GetParent(hwnd));
  137. return;
  138. }
  139. HTREEITEM hItem;
  140. TCHAR szPath[MAX_PATH] = TEXT("\0");
  141. // Get notification headers
  142. NMHDR* pHdr = reinterpret_cast<NMHDR*>(pvArg);
  143. LPNMTREEVIEW pnmTreeView = reinterpret_cast<LPNMTREEVIEW>(pvArg);
  144. switch(pHdr->code)
  145. {
  146. // Expanding or collapsing, called for each child.
  147. case TVN_ITEMEXPANDED:
  148. // If we're expanding, get the sub items of all children
  149. if(pnmTreeView->action & TVE_EXPAND)
  150. {
  151. // Switch our parent to an open folder icon.
  152. if(TreeView_GetParent(hwTree, pnmTreeView->itemNew.hItem))
  153. {
  154. szPath[0] = TEXT('\0');
  155. GetItemPath(hwTree, pnmTreeView->itemNew.hItem, szPath);
  156. SHFILEINFO sfi;
  157. SHGetFileInfo(szPath, 0, &sfi, sizeof(sfi),
  158. SHGFI_SYSICONINDEX | SHGFI_OPENICON);
  159. TVITEMEX tvitemex;
  160. tvitemex.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_HANDLE;
  161. tvitemex.hItem = pnmTreeView->itemNew.hItem;
  162. tvitemex.iImage = sfi.iIcon;
  163. tvitemex.iSelectedImage = sfi.iIcon;
  164. TreeView_SetItem(hwTree, &tvitemex);
  165. }
  166. // Add all sub-items to this item.
  167. AddTreeSubItems(hwTree, pnmTreeView->itemNew.hItem);
  168. // Go through each child, and and check if expansion should be allowed
  169. HTREEITEM hChild = TreeView_GetChild(hwTree, pnmTreeView->itemNew.hItem);
  170. while(hChild != NULL)
  171. {
  172. CheckTreeSubItems(hwTree, hChild);
  173. hChild = TreeView_GetNextSibling(hwTree, hChild);
  174. }
  175. }
  176. else if(pnmTreeView->action & TVE_COLLAPSE)
  177. {
  178. // Switch parent to a closed icon.
  179. if(TreeView_GetParent(hwTree, pnmTreeView->itemNew.hItem))
  180. {
  181. szPath[0] = TEXT('\0');
  182. GetItemPath(hwTree, pnmTreeView->itemNew.hItem, szPath);
  183. SHFILEINFO sfi;
  184. SHGetFileInfo(szPath, 0, &sfi, sizeof(sfi),
  185. SHGFI_SYSICONINDEX | SHGFI_OPENICON);
  186. TVITEMEX tvitemex;
  187. tvitemex.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_HANDLE;
  188. tvitemex.hItem = pnmTreeView->itemNew.hItem;
  189. tvitemex.iImage = sfi.iIcon;
  190. tvitemex.iSelectedImage = sfi.iIcon;
  191. TreeView_SetItem(hwTree, &tvitemex);
  192. }
  193. // Remove all subitems for every child.
  194. RemoveTreeSubItems(hwTree, pnmTreeView->itemNew.hItem);
  195. CheckTreeSubItems(hwTree, pnmTreeView->itemNew.hItem);
  196. }
  197. break;
  198. case TVN_SELCHANGED:
  199. // Only bother updating edit box if the tree has the focus
  200. if(GetFocus() == hwTree)
  201. {
  202. GetItemPath(hwTree, pnmTreeView->itemNew.hItem, szPath);
  203. SetWindowText(hwFileList, szPath);
  204. }
  205. break;
  206. // When treeview gains focus, make sure file list and tree view
  207. // selection are in sync.
  208. case NM_SETFOCUS:
  209. hItem = TreeView_GetSelection(hwTree);
  210. GetItemPath(hwTree, hItem, szPath);
  211. SetWindowText(hwFileList, szPath);
  212. break;
  213. }
  214. }
  215. // Handle a command message.
  216. void HandleBrowseCommand(HWND hwnd, UINT uiCtrlID, UINT uiNotify, HWND hwCtrl)
  217. {
  218. HWND hwTree = GetDlgItem(hwnd, IDC_DIRTREE);
  219. HTREEITEM hSelected;
  220. TVITEMEX tvItem;
  221. TCHAR szPath[MAX_PATH];
  222. switch(uiCtrlID)
  223. {
  224. // Get path of item, and return it.
  225. case IDOK:
  226. // Retrieve item from tree view.
  227. hSelected = TreeView_GetSelection(hwTree);
  228. if(!hSelected)
  229. {
  230. MessageBeep(0);
  231. break;
  232. }
  233. s_szPathBuffer[0] = TEXT('\0');
  234. GetItemPath(hwTree, hSelected, s_szPathBuffer);
  235. if(s_szPathBuffer[lstrlen(s_szPathBuffer)-1]== TEXT('\\'))
  236. s_szPathBuffer[lstrlen(s_szPathBuffer)-1] = TEXT('\0');
  237. // Validate the path
  238. if(GetFileAttributes(s_szPathBuffer)==static_cast<DWORD>(-1))
  239. ::MessageBox(0, TEXT("Invalid Path"), TEXT("ERROR"),
  240. MB_OK | MB_ICONINFORMATION);
  241. else
  242. EndDialog(hwnd, reinterpret_cast<INT_PTR>(s_szPathBuffer));
  243. break;
  244. case IDCANCEL:
  245. // User selected cancel, just return null.
  246. EndDialog(hwnd, 0);
  247. break;
  248. case IDC_FILELISTCOMBO:
  249. switch(uiNotify)
  250. {
  251. case CBN_EDITCHANGE:
  252. SendMessage(hwCtrl, WM_GETTEXT, MAX_PATH,
  253. reinterpret_cast<LPARAM>(szPath));
  254. SelectItemFromFullPath(hwTree, szPath);
  255. break;
  256. case CBN_DROPDOWN:
  257. // clear the combo box.
  258. SendMessage(hwCtrl, CB_RESETCONTENT, 0, 0);
  259. // Fill the combo box with all the lowest level items under
  260. // treeview selection
  261. hSelected = TreeView_GetSelection(hwTree);
  262. tvItem.mask = TVIF_STATE | TVIF_HANDLE;
  263. tvItem.hItem = hSelected;
  264. TreeView_GetItem(hwTree, &tvItem);
  265. if(tvItem.state & TVIS_EXPANDED)
  266. {
  267. szPath[0] = TEXT('\0');
  268. GetItemPath(hwTree, hSelected, szPath);
  269. SendMessage(hwCtrl, CB_ADDSTRING, 0,
  270. reinterpret_cast<LPARAM>(szPath));
  271. HTREEITEM hItem = TreeView_GetChild(hwTree, tvItem.hItem);
  272. while(hItem)
  273. {
  274. szPath[0] = TEXT('\0');
  275. GetItemPath(hwTree, hItem, szPath);
  276. SendMessage(hwCtrl, CB_ADDSTRING, 0,
  277. reinterpret_cast<LPARAM>(szPath));
  278. hItem = TreeView_GetNextSibling(hwTree, hItem);
  279. }
  280. }
  281. else
  282. {
  283. HTREEITEM hItem;
  284. hItem = TreeView_GetParent(hwTree, tvItem.hItem);
  285. hItem = TreeView_GetChild(hwTree, hItem);
  286. while(hItem)
  287. {
  288. szPath[0] = TEXT('\0');
  289. GetItemPath(hwTree, hItem, szPath);
  290. SendMessage(hwCtrl, CB_ADDSTRING, 0,
  291. reinterpret_cast<LPARAM>(szPath));
  292. hItem = TreeView_GetNextSibling(hwTree, hItem);
  293. }
  294. }
  295. break;
  296. }
  297. break;
  298. };
  299. }
  300. // Expand an item to get its full path.
  301. void GetItemPath(HWND hwTree, HTREEITEM hItem, PTSTR szPath)
  302. {
  303. assert(hwTree);
  304. assert(hItem);
  305. assert(szPath);
  306. assert(szPath[0] == TEXT('\0'));
  307. // Recurse to get parent's path.
  308. HTREEITEM hParent = TreeView_GetParent(hwTree, hItem);
  309. if(hParent)
  310. {
  311. GetItemPath(hwTree, hParent, szPath);
  312. lstrcat(szPath, TEXT("\\"));
  313. }
  314. // Get item text, concatenate on current path..
  315. TVITEMEX tvItem;
  316. tvItem.mask = TVIF_TEXT | TVIF_HANDLE;
  317. tvItem.hItem = hItem;
  318. tvItem.pszText = szPath + lstrlen(szPath);
  319. tvItem.cchTextMax = MAX_PATH - lstrlen(szPath);
  320. TreeView_GetItem(hwTree, &tvItem);
  321. }
  322. // Remove all subitems below an element.
  323. void RemoveTreeSubItems(HWND hwTree, HTREEITEM hParent)
  324. {
  325. assert(hwTree);
  326. // Go through each child and delete.
  327. HTREEITEM hChild = TreeView_GetChild(hwTree, hParent);
  328. while(hChild != NULL)
  329. {
  330. HTREEITEM hSibling = TreeView_GetNextSibling(hwTree, hChild);
  331. // Recursively delete all subitems in this child.
  332. RemoveTreeSubItems(hwTree, hChild);
  333. // Remove this item.
  334. TreeView_DeleteItem(hwTree, hChild);
  335. // Move to next.
  336. hChild = hSibling;
  337. }
  338. }
  339. // Add items below an element.
  340. void AddTreeSubItems(HWND hwTree, HTREEITEM hParent)
  341. {
  342. assert(hwTree);
  343. // Clear-out (to ensure we don't add items twice)
  344. RemoveTreeSubItems(hwTree, hParent);
  345. // Do an early out if the item has already been expanded
  346. TVITEMEX tvitem;
  347. tvitem.mask = TVIF_CHILDREN | TVIF_HANDLE;
  348. tvitem.hItem = hParent;
  349. TreeView_GetItem(hwTree, &tvitem);
  350. if(tvitem.cChildren)
  351. return;
  352. // Do a search on all directories
  353. TCHAR szPath[MAX_PATH] = TEXT("");
  354. GetItemPath(hwTree, hParent, szPath);
  355. WIN32_FIND_DATA findData;
  356. lstrcat(szPath, TEXT("\\*.*"));
  357. HANDLE hSearch = FindFirstFile(szPath, &findData);
  358. if(hSearch == INVALID_HANDLE_VALUE)
  359. return;
  360. do
  361. {
  362. // Ignore if a relative directory (. or ..)
  363. // or if no select files were selected and it is not a directory
  364. // otherwise
  365. if(findData.cFileName[0] != TEXT('.'))
  366. {
  367. if(StrStrI(findData.cFileName, ".exe") || (
  368. findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  369. {
  370. SHFILEINFO sfi;
  371. szPath[0] = TEXT('\0');
  372. GetItemPath(hwTree, hParent, szPath);
  373. lstrcat(szPath, TEXT("\\"));
  374. lstrcat(szPath, findData.cFileName);
  375. SHGetFileInfo(szPath, 0, &sfi, sizeof(sfi),
  376. SHGFI_SYSICONINDEX);
  377. // Insert an item representing this directory.
  378. TVINSERTSTRUCT tvis;
  379. tvis.hParent = hParent;
  380. tvis.hInsertAfter = TVI_SORT;
  381. tvis.itemex.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT;
  382. tvis.itemex.iImage = sfi.iIcon;
  383. tvis.itemex.iSelectedImage = sfi.iIcon;
  384. tvis.itemex.pszText = findData.cFileName;
  385. tvis.itemex.cchTextMax = lstrlen(findData.cFileName);
  386. TreeView_InsertItem(hwTree, &tvis);
  387. }
  388. }
  389. // Move to next file.
  390. } while(FindNextFile(hSearch, &findData));
  391. FindClose(hSearch);
  392. }
  393. void CheckTreeSubItems(HWND hwTree, HTREEITEM hParent)
  394. {
  395. assert(hwTree);
  396. // Do a search on all directories
  397. TCHAR szPath[MAX_PATH] = TEXT("");
  398. GetItemPath(hwTree, hParent, szPath);
  399. WIN32_FIND_DATA findData;
  400. lstrcat(szPath, TEXT("\\*.*"));
  401. HANDLE hSearch = FindFirstFile(szPath, &findData);
  402. if(hSearch == INVALID_HANDLE_VALUE)
  403. return;
  404. do
  405. {
  406. // Ignore if a relative directory (. or ..)
  407. // or if no select files were selected and it is not a directory
  408. // otherwise
  409. if((findData.cFileName[0] != TEXT('.')))
  410. {
  411. if(StrStrI(findData.cFileName, ".exe") || (
  412. findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  413. {
  414. SHFILEINFO sfi;
  415. szPath[0] = TEXT('\0');
  416. GetItemPath(hwTree, hParent, szPath);
  417. lstrcat(szPath, TEXT("\\"));
  418. lstrcat(szPath, findData.cFileName);
  419. SHGetFileInfo(szPath, 0, &sfi, sizeof(sfi),
  420. SHGFI_SYSICONINDEX);
  421. // Insert an item representing this directory.
  422. TVINSERTSTRUCT tvis;
  423. tvis.hParent = hParent;
  424. tvis.hInsertAfter = TVI_SORT;
  425. tvis.itemex.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT;
  426. tvis.itemex.iImage = sfi.iIcon;
  427. tvis.itemex.iSelectedImage = sfi.iIcon;
  428. tvis.itemex.pszText = findData.cFileName;
  429. tvis.itemex.cchTextMax = lstrlen(findData.cFileName);
  430. TreeView_InsertItem(hwTree, &tvis);
  431. FindClose(hSearch);
  432. return;
  433. }
  434. }
  435. // Move to next file.
  436. } while(FindNextFile(hSearch, &findData));
  437. FindClose(hSearch);
  438. }
  439. // Given a relative path and a tree item, select a subitem from the relative path.
  440. // Returns true if item successfully selected, false otherwise.
  441. bool SelectSubitemFromPartialPath(HWND hwTree, HTREEITEM hItem, PTSTR szPath)
  442. {
  443. bool fExpandIt = false;
  444. TCHAR* szPathDelim = _tcschr(szPath, TEXT('\\'));
  445. if(szPathDelim)
  446. {
  447. if(szPathDelim == szPath)
  448. return false;
  449. *szPathDelim = TEXT('\0');
  450. if(szPathDelim[1] == TEXT('\0'))
  451. {
  452. szPathDelim = 0;
  453. fExpandIt = true;
  454. }
  455. }
  456. // Find this path.
  457. HTREEITEM hClosestChild = 0;
  458. HTREEITEM hChild = TreeView_GetChild(hwTree, hItem);
  459. while(hChild)
  460. {
  461. TCHAR szItemPath[MAX_PATH];
  462. TVITEMEX tvitem;
  463. tvitem.mask = TVIF_HANDLE | TVIF_TEXT;
  464. tvitem.hItem = hChild;
  465. tvitem.pszText = szItemPath;
  466. tvitem.cchTextMax = MAX_PATH;
  467. TreeView_GetItem(hwTree, &tvitem);
  468. if(lstrcmpi(szPath,tvitem.pszText) == 0)
  469. break;
  470. else if((StrStrI(tvitem.pszText, szPath) == tvitem.pszText) && !fExpandIt)
  471. {
  472. hClosestChild = hChild;
  473. break;
  474. }
  475. hChild = TreeView_GetNextSibling(hwTree, hChild);
  476. }
  477. if(!hChild)
  478. {
  479. if(!hClosestChild)
  480. return false;
  481. else
  482. {
  483. hChild = hClosestChild;
  484. szPathDelim = 0;
  485. }
  486. }
  487. // If nothing more on the path, select this item,
  488. // or expand and continue
  489. if(szPathDelim == 0)
  490. {
  491. if(fExpandIt)
  492. TreeView_Expand(hwTree, hChild, TVE_EXPAND);
  493. TreeView_SelectItem(hwTree, hChild);
  494. }
  495. else
  496. {
  497. if(fExpandIt)
  498. TreeView_Expand(hwTree, hChild, TVE_EXPAND);
  499. if(!SelectSubitemFromPartialPath(hwTree, hChild, szPathDelim+1))
  500. return false;
  501. }
  502. return true;
  503. }
  504. // Given a path, select the appropriate item in the tree.
  505. // If path is invalid, it will expand as much as possible
  506. // (until invalid element appears)
  507. // szPath is trashed.
  508. void SelectItemFromFullPath(HWND hwTree, PTSTR szPath)
  509. {
  510. if(!SelectSubitemFromPartialPath(hwTree, 0, szPath))
  511. TreeView_SelectItem(hwTree, 0);
  512. }