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.

2221 lines
68 KiB

  1. /****************************************************************************/
  2. /* */
  3. /* TREECTL.C - */
  4. /* */
  5. /* Windows Directory Tree Window Proc Routines */
  6. /* */
  7. /****************************************************************************/
  8. #define PUBLIC // avoid collision with shell.h
  9. #include "winfile.h"
  10. #include "treectl.h"
  11. #include "lfn.h"
  12. #include "winnet.h"
  13. #include "wfcopy.h"
  14. #define WS_TREESTYLE (WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL | WS_HSCROLL | LBS_OWNERDRAWFIXED | LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | LBS_DISABLENOSCROLL)
  15. WORD cNodes;
  16. // BOOL bCancelTree; .......... moved to winfile.c
  17. VOID RectTreeItem(HWND hwndLB, register INT iItem, BOOL bFocusOn);
  18. VOID GetTreePathIndirect(PDNODE pNode, register LPSTR szDest);
  19. VOID GetTreePath(PDNODE pNode, register LPSTR szDest);
  20. VOID ScanDirLevel(PDNODE pParentNode, LPSTR szPath, DWORD view);
  21. INT InsertDirectory(HWND hwndTreeCtl, PDNODE pParentNode, WORD iParentNode, LPSTR szName, PDNODE *ppNode);
  22. BOOL ReadDirLevel(HWND hwndTreeCtl, PDNODE pParentNode, LPSTR szPath,
  23. WORD nLevel, INT iParentNode, DWORD dwAttribs, BOOL bFullyExpand, LPSTR szAutoExpand);
  24. VOID FillTreeListbox(HWND hwndTreeCtl, LPSTR szDefaultDir, BOOL bFullyExpand, BOOL bDontSteal);
  25. WORD FindItemFromPath(HWND hwndLB, LPSTR lpszPath, BOOL bReturnParent, PDNODE *ppNode);
  26. VOID APIENTRY CheckEscapes(LPSTR);
  27. /*--------------------------------------------------------------------------*/
  28. /* */
  29. /* GetTreePathIndirect() - */
  30. /* */
  31. /* build a complete path for a given node in the tree by recursivly */
  32. /* traversing the tree structure */
  33. /* */
  34. /*--------------------------------------------------------------------------*/
  35. VOID
  36. GetTreePathIndirect(
  37. PDNODE pNode,
  38. register LPSTR szDest
  39. )
  40. {
  41. register PDNODE pParent;
  42. pParent = pNode->pParent;
  43. if (pParent)
  44. GetTreePathIndirect(pParent, szDest);
  45. lstrcat(szDest, pNode->szName);
  46. if (pParent)
  47. lstrcat(szDest, "\\");
  48. }
  49. /*--------------------------------------------------------------------------*/
  50. /* */
  51. /* GetTreePath() - */
  52. /* */
  53. /* build a complete path for a given node in the tree */
  54. /* */
  55. /*--------------------------------------------------------------------------*/
  56. VOID
  57. GetTreePath(
  58. PDNODE pNode,
  59. register LPSTR szDest
  60. )
  61. {
  62. szDest[0] = 0L;
  63. GetTreePathIndirect(pNode, szDest);
  64. /* Remove the last backslash (unless it is the root directory). */
  65. if (pNode->pParent)
  66. szDest[lstrlen(szDest)-1] = 0L;
  67. }
  68. /*--------------------------------------------------------------------------*/
  69. /* */
  70. /* ScanDirLevel() - */
  71. /* */
  72. /* look down to see if this node has any sub directories */
  73. /*
  74. /*--------------------------------------------------------------------------*/
  75. // szPath is ANSI
  76. VOID
  77. ScanDirLevel(
  78. PDNODE pParentNode,
  79. LPSTR szPath,
  80. DWORD view
  81. )
  82. {
  83. BOOL bFound;
  84. LFNDTA lfndta;
  85. ENTER("ScanDirLevel");
  86. /* Add '*.*' to the current path. */
  87. lstrcpy(szMessage, szPath);
  88. AddBackslash(szMessage);
  89. lstrcat(szMessage, szStarDotStar);
  90. /* Search for the first subdirectory on this level. */
  91. // FixAnsiPathForDos(szMessage);
  92. bFound = WFFindFirst(&lfndta, szMessage, ATTR_DIR | view);
  93. while (bFound) {
  94. /* Is this not a '.' or '..' directory? */
  95. if ((lfndta.fd.cFileName[0] != '.') && (lfndta.fd.dwFileAttributes & ATTR_DIR)) {
  96. pParentNode->wFlags |= TF_HASCHILDREN;
  97. bFound = FALSE;
  98. } else
  99. /* Search for the next subdirectory. */
  100. bFound = WFFindNext(&lfndta);
  101. }
  102. WFFindClose(&lfndta);
  103. LEAVE("ScanDirLevel");
  104. }
  105. // wizzy cool recursive path compare routine
  106. //
  107. // p1 and p2 must be on the same level (p1->nLevels == p2->nLevels)
  108. INT
  109. ComparePath(
  110. PDNODE p1,
  111. PDNODE p2
  112. )
  113. {
  114. INT ret;
  115. if (p1 == p2) {
  116. return 0; // equal (base case)
  117. } else {
  118. ret = ComparePath(p1->pParent, p2->pParent);
  119. if (ret == 0) {
  120. // parents are equal
  121. ret = lstrcmp(p1->szName, p2->szName);
  122. #if 0
  123. {
  124. CHAR buf[200];
  125. wsprintf(buf, "Compare(%s, %s) -> %d\r\n", (LPSTR)p1->szName, (LPSTR)p2->szName, ret);
  126. OutputDebugString(buf);
  127. }
  128. #endif
  129. }
  130. // not equal parents, propagate up the call tree
  131. return ret;
  132. }
  133. }
  134. INT
  135. CompareNodes(
  136. PDNODE p1,
  137. PDNODE p2
  138. )
  139. {
  140. PDNODE p1save, p2save;
  141. INT ret;
  142. ENTER("CompareNodes");
  143. ASSERT(p1 && p2);
  144. PRINT(BF_PARMTRACE, "IN: p1=%s", p1->szName);
  145. PRINT(BF_PARMTRACE, "IN: p2=%s", p2->szName);
  146. p1save = p1;
  147. p2save = p2;
  148. // get p1 and p2 to the same level
  149. while (p1->nLevels > p2->nLevels)
  150. p1 = p1->pParent;
  151. while (p2->nLevels > p1->nLevels)
  152. p2 = p2->pParent;
  153. // compare those paths
  154. ret = ComparePath(p1, p2);
  155. if (ret == 0)
  156. ret = (INT)p1save->nLevels - (INT)p2save->nLevels;
  157. LEAVE("CompareNodes");
  158. return ret;
  159. }
  160. //
  161. // InsertDirectory()
  162. //
  163. // wizzy quick n log n binary insert code!
  164. //
  165. // creates and inserts a new node in the tree, this also sets
  166. // the TF_LASTLEVELENTRY bits to mark a branch as being the last
  167. // for a given level as well as marking parents with
  168. // TF_HASCHILDREN | TF_EXPANDED to indicate they have been expanded
  169. // and have children.
  170. //
  171. // Returns iNode and fills ppNode with pNode.
  172. //
  173. INT
  174. InsertDirectory(
  175. HWND hwndTreeCtl,
  176. PDNODE pParentNode,
  177. WORD iParentNode,
  178. LPSTR szName,
  179. PDNODE *ppNode
  180. )
  181. {
  182. WORD len, x;
  183. PDNODE pNode, pMid;
  184. HWND hwndLB;
  185. INT iMin;
  186. INT iMax;
  187. INT iMid;
  188. ENTER("InsertDirectory");
  189. PRINT(BF_PARMTRACE, "IN: pParentNode=%lx", pParentNode);
  190. PRINT(BF_PARMTRACE, "IN: iParentNode=%d", iParentNode);
  191. PRINT(BF_PARMTRACE, "IN: szName=%s", szName);
  192. len = (WORD)lstrlen(szName);
  193. pNode = (PDNODE)LocalAlloc(LPTR, sizeof(DNODE)+len);
  194. if (!pNode) {
  195. if (ppNode) {
  196. *ppNode = NULL;
  197. }
  198. return 0;
  199. }
  200. pNode->pParent = pParentNode;
  201. pNode->nLevels = pParentNode ? (pParentNode->nLevels + (BYTE)1) : (BYTE)0;
  202. pNode->wFlags = (BYTE)NULL;
  203. pNode->iNetType = -1;
  204. if (IsLFN(szName)) {
  205. pNode->wFlags |= TF_LFN;
  206. }
  207. lstrcpy(pNode->szName, szName);
  208. if (pParentNode)
  209. pParentNode->wFlags |= TF_HASCHILDREN | TF_EXPANDED; // mark the parent
  210. hwndLB = GetDlgItem(hwndTreeCtl, IDCW_TREELISTBOX);
  211. // computing the real text extent is too slow so we aproximate
  212. // with the following (note, we don't keep this on a per tree
  213. // basis so it is kinda bogus anyway)
  214. x = (WORD)(len + 2 * pNode->nLevels) * (WORD)dxText;
  215. if (x > xTreeMax) {
  216. xTreeMax = x;
  217. }
  218. iMax = (INT)SendMessage(hwndLB, LB_GETCOUNT, 0, 0L);
  219. if (iMax > 0) {
  220. // do a binary insert
  221. iMin = iParentNode + 1;
  222. iMax--; // last index
  223. do {
  224. iMid = (iMax + iMin) / 2;
  225. SendMessage(hwndLB, LB_GETTEXT, iMid, (LPARAM)&pMid);
  226. if (CompareNodes(pNode, pMid) > 0)
  227. iMin = iMid + 1;
  228. else
  229. iMax = iMid - 1;
  230. } while (iMax > iMin);
  231. SendMessage(hwndLB, LB_GETTEXT, iMax, (LPARAM)&pMid);
  232. if (CompareNodes(pNode, pMid) > 0)
  233. iMax++; // insert after this one
  234. }
  235. // now reset the TF_LASTLEVEL flags as appropriate
  236. // look for the first guy on our level above us and turn off
  237. // his TF_LASTLEVELENTRY flag so he draws a line down to us
  238. iMid = iMax - 1;
  239. while (iMid >= 0) {
  240. SendMessage(hwndLB, LB_GETTEXT, iMid--, (LPARAM)&pMid);
  241. if (pMid->nLevels == pNode->nLevels) {
  242. pMid->wFlags &= ~TF_LASTLEVELENTRY;
  243. break;
  244. } else if (pMid->nLevels < pNode->nLevels)
  245. break;
  246. }
  247. // if no one below me or the level of the guy below is less, then
  248. // this is the last entry for this level
  249. if (((INT)SendMessage(hwndLB, LB_GETTEXT, iMax, (LPARAM)&pMid) == LB_ERR) ||
  250. (pMid->nLevels < pNode->nLevels))
  251. pNode->wFlags |= TF_LASTLEVELENTRY;
  252. SendMessage(hwndLB, LB_INSERTSTRING, iMax, (LPARAM)pNode);
  253. if (ppNode) {
  254. *ppNode = pNode;
  255. }
  256. LEAVE("InsertDirectory");
  257. return iMax;
  258. }
  259. // this yeilds control to other apps and allows us to process
  260. // messages and user input. to avoid overrunning the stack
  261. // from multiple tree reads being initiated at the same time
  262. // we check how much space we have on the stack before we yield
  263. extern WORD end; // C compiler end of static data symbol
  264. extern WORD pStackTop;
  265. WORD
  266. StackAvail(VOID)
  267. {
  268. #ifdef LATER
  269. _asm mov ax,sp
  270. _asm sub ax,pStackTop
  271. if (0) return 0; // get rid of warning, optimized out
  272. #endif
  273. return 0x7fff; // Hack. shouldn't really matter. StackAvail in NT is a NOP
  274. }
  275. VOID
  276. APIENTRY
  277. wfYield()
  278. {
  279. MSG msg;
  280. #ifdef LATER
  281. WORD free_stack;
  282. free_stack = StackAvail();
  283. #endif
  284. #if 0
  285. {
  286. CHAR buf[30];
  287. wsprintf(buf, "free stack: %d\r\n", free_stack);
  288. OutputDebugString(buf);
  289. }
  290. #endif
  291. #if LATER
  292. if (free_stack < 1024*4) {
  293. CHAR buf[40];
  294. wsprintf(buf, "not enough stack %d\r\n", free_stack);
  295. OutputDebugString(buf);
  296. return;
  297. }
  298. #endif
  299. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  300. if (!TranslateMDISysAccel(hwndMDIClient, &msg) &&
  301. (!hwndFrame || !TranslateAccelerator(hwndFrame, hAccel, &msg))) {
  302. TranslateMessage(&msg);
  303. DispatchMessage(&msg);
  304. }
  305. }
  306. }
  307. // INT iReadLevel = 0; ...............Moved to winfile.c
  308. //--------------------------------------------------------------------------
  309. //
  310. // ReadDirLevel() -
  311. //
  312. // this does a depth first search of the dir tree. note, a bredth
  313. // first implementation did not perform any better.
  314. //
  315. // szPath a directory path that MUST EXIST long enough
  316. // to hold the full path to the largest directory
  317. // that will be found (MAXPATHLEN). this is an
  318. // ANSI string. (ie C:\ and C:\FOO are valid)
  319. // nLevel level in the tree
  320. // iParentNode index of parent node
  321. // dwAttribs attributes to filter with
  322. // bFullyExpand TRUE means expand this node fully
  323. // szAutoExpand list of directories to autoexpand ANSI
  324. // (eg. for "C:\foo\bar\stuff"
  325. // "foo" NULL "bar" NULL "stuff" NULL NULL)
  326. //
  327. // returns:
  328. // TRUE tree read sucessful
  329. // FALSE user abort or bogus tree read
  330. //--------------------------------------------------------------------------
  331. BOOL
  332. ReadDirLevel(
  333. HWND hwndTreeCtl,
  334. PDNODE pParentNode,
  335. LPSTR szPath,
  336. WORD nLevel,
  337. INT iParentNode,
  338. DWORD dwAttribs,
  339. BOOL bFullyExpand,
  340. LPSTR szAutoExpand
  341. )
  342. {
  343. LPSTR szEndPath;
  344. LFNDTA lfndta;
  345. INT iNode;
  346. BOOL bFound;
  347. PDNODE pNode;
  348. BOOL bAutoExpand;
  349. BOOL bResult = TRUE;
  350. WORD view;
  351. HWND hwndParent;
  352. HWND hwndDir;
  353. HANDLE hDTA;
  354. LPMYDTA lpmydta;
  355. INT count;
  356. RECT rc;
  357. ENTER("ReadDirLevel");
  358. PRINT(BF_PARMTRACE, "IN: szPath=%s", szPath);
  359. PRINT(BF_PARMTRACE, "IN: nLevel=%d", (LPSTR)nLevel);
  360. PRINT(BF_PARMTRACE, "IN: bFullyExpand=%d", IntToPtr(bFullyExpand));
  361. PRINT(BF_PARMTRACE, "IN: szAutoExpand=%s", szAutoExpand);
  362. if (StackAvail() < 1024*2)
  363. return(TRUE);
  364. hwndParent = GetParent(hwndTreeCtl);
  365. view = (WORD)GetWindowLong(hwndParent, GWL_VIEW);
  366. // we optimize the tree read if we are not adding pluses and
  367. // we find a directory window that already has read all the
  368. // directories for the path we are about to search. in this
  369. // case we look through the DTA structure in the dir window
  370. // to get all the directories (instead of calling FindFirst/FindNext).
  371. // in this case we have to disable yielding since the user could
  372. // potentialy close the dir window that we are reading, or change
  373. // directory.
  374. hDTA = NULL;
  375. if (!(view & VIEW_PLUSES)) {
  376. if ((hwndDir = HasDirWindow(hwndParent)) &&
  377. (GetWindowLong(hwndParent, GWL_ATTRIBS) & ATTR_DIR)) {
  378. SendMessage(hwndDir, FS_GETDIRECTORY, sizeof(szMessage), (LPARAM)szMessage);
  379. StripBackslash(szMessage);
  380. if (!lstrcmpi(szMessage, szPath)) {
  381. SendMessage(hwndDir, FS_GETFILESPEC, sizeof(szMessage), (LPARAM)szMessage);
  382. if (!lstrcmp(szMessage, szStarDotStar)) {
  383. hDTA = (HANDLE)GetWindowLongPtr(hwndDir, GWLP_HDTA);
  384. lpmydta = (LPMYDTA)LocalLock(hDTA);
  385. count = (INT)lpmydta->my_nFileSizeLow; // holds number of entries, NOT size.
  386. }
  387. }
  388. }
  389. }
  390. SetWindowLong(hwndTreeCtl, GWL_READLEVEL, GetWindowLong(hwndTreeCtl, GWL_READLEVEL) + 1);
  391. iReadLevel++; // global for menu code
  392. szEndPath = (LPSTR)(szPath + lstrlen(szPath));
  393. /* Add '\*.*' to the current path. */
  394. AddBackslash(szPath);
  395. lstrcat(szPath, szStarDotStar);
  396. if (hDTA) {
  397. // steal the entry from the dir window
  398. lpmydta = GETDTAPTR(lpmydta, lpmydta->wSize);
  399. // search for any "real" directories
  400. while (count > 0 && (!(lpmydta->my_dwAttrs & ATTR_DIR) || (lpmydta->my_dwAttrs & ATTR_PARENT))) {
  401. lpmydta = GETDTAPTR(lpmydta, lpmydta->wSize);
  402. count--;
  403. }
  404. if (count > 0) {
  405. bFound = TRUE;
  406. memcpy(&(lfndta.fd.dwFileAttributes), &(lpmydta->my_dwAttrs), IMPORTANT_DTA_SIZE);
  407. lstrcpy(lfndta.fd.cFileName, lpmydta->my_cFileName);
  408. } else
  409. bFound = FALSE;
  410. } else {
  411. // get first file from DOS
  412. lstrcpy(szMessage, szPath);
  413. FixAnsiPathForDos(szMessage);
  414. bFound = WFFindFirst(&lfndta, szMessage, dwAttribs);
  415. }
  416. // for net drive case where we can't actually see what is in these
  417. // direcotries we will build the tree automatically
  418. if (!bFound && *szAutoExpand) {
  419. LPSTR p;
  420. p = szAutoExpand;
  421. szAutoExpand += lstrlen(szAutoExpand) + 1;
  422. iNode = InsertDirectory(hwndTreeCtl, pParentNode, (WORD)iParentNode, p, &pNode);
  423. pParentNode->wFlags |= TF_DISABLED;
  424. /* Construct the path to this new subdirectory. */
  425. *szEndPath = 0; // remove old stuff
  426. AddBackslash(szPath);
  427. lstrcat(szPath, p);
  428. if (pNode)
  429. ReadDirLevel(hwndTreeCtl, pNode, szPath, (WORD)(nLevel+1), iNode, dwAttribs, bFullyExpand, szAutoExpand);
  430. }
  431. while (bFound) {
  432. wfYield();
  433. if (bCancelTree) {
  434. bResult = FALSE;
  435. if (bCancelTree == 2)
  436. PostMessage(hwndFrame, WM_COMMAND, IDM_EXIT, 0L);
  437. goto DONE;
  438. }
  439. /* Is this not a '.' or '..' directory? */
  440. if ((lfndta.fd.cFileName[0] != '.') && (lfndta.fd.dwFileAttributes & ATTR_DIR)) {
  441. if (!hDTA)
  442. OemToAnsi(lfndta.fd.cFileName, lfndta.fd.cFileName);
  443. // we will try to auto expand this node if it matches
  444. if (*szAutoExpand && !lstrcmpi(szAutoExpand, lfndta.fd.cFileName)) {
  445. bAutoExpand = TRUE;
  446. szAutoExpand += lstrlen(szAutoExpand) + 1;
  447. } else {
  448. bAutoExpand = FALSE;
  449. }
  450. iNode = InsertDirectory(hwndTreeCtl, pParentNode, (WORD)iParentNode, lfndta.fd.cFileName, &pNode);
  451. if (bStatusBar && ((cNodes % 7) == 0)) {
  452. // make sure we are the active window before we
  453. // update the status bar
  454. if (hwndParent == (HWND)SendMessage(hwndMDIClient, WM_MDIGETACTIVE, 0, 0L)) {
  455. wsprintf(szStatusTree, szDirsRead, cNodes);
  456. // stomp over the status bar!
  457. GetClientRect(hwndFrame, &rc);
  458. rc.top = rc.bottom - dyStatus;
  459. InvalidateRect(hwndFrame, &rc, FALSE);
  460. // force the paint because we don't yield
  461. UpdateWindow(hwndFrame);
  462. }
  463. }
  464. cNodes++;
  465. /* Construct the path to this new subdirectory. */
  466. *szEndPath = 0L;
  467. AddBackslash(szPath);
  468. lstrcat(szPath, lfndta.fd.cFileName); // cFileName is ANSI now
  469. // either recurse or add pluses
  470. if (pNode) {
  471. if (bFullyExpand || bAutoExpand) {
  472. if (!ReadDirLevel(hwndTreeCtl, pNode, szPath, (WORD)(nLevel+1), iNode, dwAttribs, bFullyExpand, szAutoExpand)) {
  473. bResult = FALSE;
  474. goto DONE;
  475. }
  476. } else if (view & VIEW_PLUSES) {
  477. ScanDirLevel(pNode, szPath, dwAttribs & ATTR_HS);
  478. }
  479. }
  480. }
  481. if (hDTA) { // short cut, steal data from dir window
  482. count--;
  483. lpmydta = GETDTAPTR(lpmydta, lpmydta->wSize);
  484. while (count > 0 && (!(lpmydta->my_dwAttrs & ATTR_DIR) || (lpmydta->my_dwAttrs & ATTR_PARENT))) {
  485. lpmydta = GETDTAPTR(lpmydta, lpmydta->wSize);
  486. count--;
  487. }
  488. if (count > 0) {
  489. bFound = TRUE;
  490. memcpy(&(lfndta.fd.dwFileAttributes), &(lpmydta->my_dwAttrs), IMPORTANT_DTA_SIZE);
  491. lstrcpy(lfndta.fd.cFileName, lpmydta->my_cFileName);
  492. } else
  493. bFound = FALSE;
  494. } else {
  495. bFound = WFFindNext(&lfndta); // get it from dos
  496. }
  497. }
  498. *szEndPath = 0L; // clean off any stuff we left on the end of the path
  499. DONE:
  500. if (!hDTA) {
  501. WFFindClose(&lfndta);
  502. } else {
  503. LocalUnlock(hDTA);
  504. }
  505. SetWindowLong(hwndTreeCtl, GWL_READLEVEL, GetWindowLong(hwndTreeCtl, GWL_READLEVEL) - 1);
  506. iReadLevel--;
  507. LEAVE("ReadDirLevel");
  508. return bResult;
  509. }
  510. // this is used by StealTreeData() to avoid alias problems where
  511. // the nodes in one tree point to parents in the other tree.
  512. // basically, as we are duplicating the tree data structure we
  513. // have to find the parent node that coorisponds with the parent
  514. // of the tree we are copying from in the tree that we are building.
  515. // since the tree is build in order we run up the listbox, looking
  516. // for the parent (matched by it's level being one smaller than
  517. // the level of the node being inserted). when we find that we
  518. // return the pointer to that node.
  519. PDNODE
  520. FindParent(
  521. INT iLevelParent,
  522. INT iStartInd,
  523. HWND hwndLB
  524. )
  525. {
  526. PDNODE pNode;
  527. while (TRUE) {
  528. if (SendMessage(hwndLB, LB_GETTEXT, iStartInd, (LPARAM)&pNode) == LB_ERR)
  529. return NULL;
  530. if (pNode->nLevels == (BYTE)iLevelParent) {
  531. SendMessage(hwndLB, LB_GETTEXT, iStartInd, (LPARAM)&pNode);
  532. return pNode;
  533. }
  534. iStartInd--;
  535. }
  536. }
  537. BOOL
  538. StealTreeData(
  539. HWND hwndTC,
  540. HWND hwndLB,
  541. LPSTR szDir
  542. )
  543. {
  544. HWND hwndSrc, hwndT;
  545. CHAR szSrc[MAXPATHLEN];
  546. WORD wView;
  547. DWORD dwAttribs;
  548. ENTER("StealTreeData");
  549. // we need to match on these attributes as well as the name
  550. wView = (WORD)(GetWindowLong(GetParent(hwndTC), GWL_VIEW) & VIEW_PLUSES);
  551. dwAttribs = (DWORD)GetWindowLong(GetParent(hwndTC), GWL_ATTRIBS) & ATTR_HS;
  552. // get the dir of this new window for compare below
  553. for (hwndSrc = GetWindow(hwndMDIClient, GW_CHILD); hwndSrc;
  554. hwndSrc = GetWindow(hwndSrc, GW_HWNDNEXT)) {
  555. // avoid finding ourselves, make sure has a tree
  556. // and make sure the tree attributes match
  557. if ((hwndT = HasTreeWindow(hwndSrc)) &&
  558. (hwndT != hwndTC) &&
  559. !GetWindowLong(hwndT, GWL_READLEVEL) &&
  560. (wView == (WORD)(GetWindowLong(hwndSrc, GWL_VIEW) & VIEW_PLUSES)) &&
  561. (dwAttribs == (DWORD)(GetWindowLong(hwndSrc, GWL_ATTRIBS) & ATTR_HS))) {
  562. SendMessage(hwndSrc, FS_GETDIRECTORY, sizeof(szSrc), (LPARAM)szSrc);
  563. StripBackslash(szSrc);
  564. if (!lstrcmpi(szDir, szSrc)) // are they the same?
  565. break; // yes, do stuff below
  566. }
  567. }
  568. if (hwndSrc) {
  569. HWND hwndLBSrc;
  570. PDNODE pNode, pNewNode, pLastParent;
  571. INT i;
  572. hwndLBSrc = GetDlgItem(hwndT, IDCW_TREELISTBOX);
  573. // don't seal from a tree that hasn't been read yet!
  574. if ((INT)SendMessage(hwndLBSrc, LB_GETCOUNT, 0, 0L) == 0) {
  575. LEAVE("StealTreeData");
  576. return FALSE;
  577. }
  578. pLastParent = NULL;
  579. for (i = 0; SendMessage(hwndLBSrc, LB_GETTEXT, i, (LPARAM)&pNode) != LB_ERR; i++) {
  580. if (pNewNode = (PDNODE)LocalAlloc(LPTR, sizeof(DNODE)+lstrlen(pNode->szName))) {
  581. *pNewNode = *pNode; // dup the node
  582. lstrcpy(pNewNode->szName, pNode->szName); // and the name
  583. // accelerate the case where we are on the same level to avoid
  584. // slow linear search!
  585. if (pLastParent && pLastParent->nLevels == (pNode->nLevels - (BYTE)1)) {
  586. pNewNode->pParent = pLastParent;
  587. } else {
  588. pNewNode->pParent = pLastParent = FindParent(pNode->nLevels-1, i-1, hwndLB);
  589. }
  590. PRINT(BF_PARMTRACE, "(stolen)Inserting...0x%lx", pNewNode);
  591. PRINT(BF_PARMTRACE, " at %d", IntToPtr(i));
  592. SendMessage(hwndLB, LB_INSERTSTRING, i, (LPARAM)pNewNode);
  593. ASSERT((PDNODE)SendMessage(hwndLB, LB_GETITEMDATA, i, 0L) == pNewNode);
  594. }
  595. }
  596. LEAVE("StealTreeData");
  597. return TRUE; // successful steal
  598. }
  599. LEAVE("StealTreeData");
  600. return FALSE;
  601. }
  602. VOID
  603. FreeAllTreeData(
  604. HWND hwndLB
  605. )
  606. {
  607. INT nIndex;
  608. PDNODE pNode;
  609. ENTER("FreeAllTreeData");
  610. // Free up the old tree (if any)
  611. nIndex = (INT)SendMessage(hwndLB, LB_GETCOUNT, 0, 0L);
  612. while (--nIndex >= 0) {
  613. SendMessage(hwndLB, LB_GETTEXT, nIndex, (LPARAM)&pNode);
  614. LocalFree((HANDLE)pNode);
  615. }
  616. SendMessage(hwndLB, LB_RESETCONTENT, 0, 0L);
  617. LEAVE("FreeAllTreeData");
  618. }
  619. /*--------------------------------------------------------------------------*/
  620. /* */
  621. /* FillTreeListbox() - */
  622. /* */
  623. /*--------------------------------------------------------------------------*/
  624. // szDefaultDir is ANSI
  625. VOID
  626. FillTreeListbox(
  627. HWND hwndTC,
  628. LPSTR szDefaultDir,
  629. BOOL bFullyExpand,
  630. BOOL bDontSteal
  631. )
  632. {
  633. PDNODE pNode;
  634. INT iNode;
  635. DWORD dwAttribs;
  636. CHAR szTemp[MAXPATHLEN];
  637. CHAR szExpand[MAXPATHLEN];
  638. LPSTR p;
  639. HWND hwndLB;
  640. ENTER("FillTreeListbox");
  641. hwndLB = GetDlgItem(hwndTC, IDCW_TREELISTBOX);
  642. FreeAllTreeData(hwndLB);
  643. SendMessage(hwndLB, WM_SETREDRAW, FALSE, 0L);
  644. bDontSteal = TRUE; // Force recalc for now
  645. if (bDontSteal || bFullyExpand || !StealTreeData(hwndTC, hwndLB, szDefaultDir)) {
  646. wsprintf(szTemp, "\\", DRIVEID(szDefaultDir) + 'A');
  647. iNode = InsertDirectory(hwndTC, NULL, 0, szTemp, &pNode);
  648. if (pNode) {
  649. dwAttribs = ATTR_DIR | (GetWindowLong(GetParent(hwndTC), GWL_ATTRIBS) & ATTR_HS);
  650. cNodes = 0;
  651. bCancelTree = FALSE;
  652. if (szDefaultDir) {
  653. lstrcpy(szExpand, szDefaultDir+3); // skip "X:\"
  654. p = szExpand;
  655. while (*p) { // null out all slashes
  656. while (*p && *p != '\\')
  657. p = AnsiNext(p);
  658. if (*p)
  659. *p++ = 0L;
  660. }
  661. p++;
  662. *p = 0L; // double null terminated
  663. } else
  664. *szExpand = 0;
  665. if (!ReadDirLevel(hwndTC, pNode, szTemp, 1, 0, dwAttribs, bFullyExpand, szExpand)) {
  666. lFreeSpace = -2L;
  667. }
  668. }
  669. }
  670. SendMessage(hwndLB, LB_SETHORIZONTALEXTENT, xTreeMax, 0L);
  671. if (szDefaultDir) {
  672. FindItemFromPath(hwndLB, szDefaultDir, FALSE, &pNode);
  673. }
  674. SendMessage(hwndLB, LB_SELECTSTRING, -1, (LPARAM)pNode);
  675. UpdateStatus(GetParent(hwndTC)); // Redraw the Status Bar
  676. SendMessage(hwndLB, WM_SETREDRAW, TRUE, 0L);
  677. InvalidateRect(hwndLB, NULL, TRUE);
  678. UpdateWindow(hwndLB); // make this look a bit better
  679. LEAVE("FillTreeListbox");
  680. }
  681. //
  682. // FindItemFromPath()
  683. //
  684. // find the PDNODE and LBIndex for a given path
  685. //
  686. // in:
  687. // hwndLB listbox of tree
  688. // lpszPath path to search for (ANSI)
  689. // bReturnParent TRUE if you want the parent, not the node
  690. //
  691. //
  692. // returns:
  693. // listbox index (0xFFFF if not found)
  694. // *ppNode is filled with pNode of node, or pNode of parent if bReturnParent is TRUE
  695. //
  696. WORD
  697. FindItemFromPath(
  698. HWND hwndLB,
  699. LPSTR lpszPath,
  700. BOOL bReturnParent,
  701. PDNODE *ppNode
  702. )
  703. {
  704. register WORD i;
  705. register LPSTR p;
  706. PDNODE pNode;
  707. PDNODE pPreviousNode;
  708. CHAR szElement[1+MAXFILENAMELEN+1];
  709. ENTER("FindItemFromPath");
  710. if (lstrlen(lpszPath) < 3) {
  711. LEAVE("FindItemFromPath");
  712. return -1;
  713. }
  714. if (IsDBCSLeadByte( lpszPath[0] ) || lpszPath[1] != ':') {
  715. LEAVE("FindItemFromPath");
  716. return -1;
  717. }
  718. i = 0;
  719. pPreviousNode = NULL;
  720. while (*lpszPath) {
  721. /* NULL out szElement[1] so the backslash hack isn't repeated with
  722. * a first level directory of length 1.
  723. */
  724. szElement[1] = 0L;
  725. /* Copy the next section of the path into 'szElement' */
  726. p = szElement;
  727. while (*lpszPath && *lpszPath != '\\') {
  728. *p++ = *lpszPath;
  729. if (IsDBCSLeadByte( *lpszPath ))
  730. *p++ = lpszPath[1]; // copy 2nd byte of DBCS char.
  731. lpszPath = AnsiNext( lpszPath );
  732. }
  733. /* Add a backslash for the Root directory. */
  734. if ( !IsDBCSLeadByte( szElement[0] ) && szElement[1] == ':' )
  735. *p++ = '\\';
  736. /* NULL terminate 'szElement' */
  737. *p = 0L;
  738. /* Skip over the path's next Backslash. */
  739. if (*lpszPath)
  740. lpszPath = AnsiNext(lpszPath);
  741. else if (bReturnParent) {
  742. /* We're at the end of a path which includes a filename. Return
  743. * the previously found parent.
  744. */
  745. if (ppNode) {
  746. *ppNode = pPreviousNode;
  747. }
  748. LEAVE("FindItemFromPath");
  749. return i;
  750. }
  751. while (TRUE) {
  752. /* Out of LB items? Not found. */
  753. if (SendMessage(hwndLB, LB_GETTEXT, i, (LPARAM)&pNode) == LB_ERR)
  754. return -1;
  755. if (pNode->pParent == pPreviousNode) {
  756. if (!lstrcmpi(szElement, pNode->szName)) {
  757. /* We've found the element... */
  758. pPreviousNode = pNode;
  759. break;
  760. }
  761. }
  762. i++;
  763. }
  764. }
  765. if (ppNode) {
  766. *ppNode = pPreviousNode;
  767. }
  768. LEAVE("FindItemFromPath");
  769. return i;
  770. }
  771. /*--------------------------------------------------------------------------*/
  772. /* */
  773. /* RectTreeItem() - */
  774. /* */
  775. /*--------------------------------------------------------------------------*/
  776. VOID
  777. RectTreeItem(
  778. HWND hwndLB,
  779. INT iItem,
  780. BOOL bFocusOn
  781. )
  782. {
  783. INT dx;
  784. INT len;
  785. HDC hdc;
  786. RECT rc;
  787. RECT rcClip;
  788. BOOL bSel;
  789. WORD wColor;
  790. PDNODE pNode;
  791. HBRUSH hBrush;
  792. HFONT hOld;
  793. CHAR szPath[MAXPATHLEN];
  794. ENTER("RectTreeItem");
  795. if (iItem == -1) {
  796. LEAVE("RectTreeItem");
  797. return;
  798. }
  799. /* Are we over ourselves? (i.e. a selected item in the source listbox) */
  800. bSel = (BOOL)SendMessage(hwndLB, LB_GETSEL, iItem, 0L);
  801. if (bSel && (hwndDragging == hwndLB)) {
  802. LEAVE("RectTreeItem");
  803. return;
  804. }
  805. SendMessage(hwndLB, LB_GETTEXT, iItem, (LPARAM)&pNode);
  806. SendMessage(hwndLB, LB_GETITEMRECT, iItem, (LPARAM)&rc);
  807. hdc = GetDC(hwndLB);
  808. len = lstrlen(pNode->szName);
  809. lstrcpy(szPath, pNode->szName);
  810. if ((wTextAttribs & TA_LOWERCASE) && !(pNode->wFlags & TF_LFN))
  811. AnsiLower(szPath);
  812. hOld = SelectObject(hdc, hFont);
  813. MGetTextExtent(hdc, szPath, len, &dx, NULL);
  814. dx += dyBorder;
  815. if (hOld)
  816. SelectObject(hdc, hOld);
  817. rc.left = pNode->nLevels * dxText * 2;
  818. rc.right = rc.left + dxFolder + dx + 4 * dyBorderx2;
  819. GetClientRect(hwndLB, &rcClip);
  820. IntersectRect(&rc, &rc, &rcClip);
  821. if (bFocusOn) {
  822. if (bSel) {
  823. wColor = COLOR_WINDOW;
  824. InflateRect(&rc, -dyBorder, -dyBorder);
  825. } else
  826. wColor = COLOR_WINDOWFRAME;
  827. if (hBrush = CreateSolidBrush(GetSysColor(wColor))) {
  828. FrameRect(hdc, &rc, hBrush);
  829. DeleteObject(hBrush);
  830. }
  831. } else {
  832. InvalidateRect(hwndLB, &rc, TRUE);
  833. UpdateWindow(hwndLB);
  834. }
  835. ReleaseDC(hwndLB, hdc);
  836. LEAVE("RectTreeItem");
  837. }
  838. // return the drive of the first window to respond to the FS_GETDRIVE
  839. // message. this usually starts from the source or dest of a drop
  840. // and travels up until we find a drive or hit the MDI client
  841. INT
  842. APIENTRY
  843. GetDrive(
  844. HWND hwnd,
  845. POINT pt
  846. )
  847. {
  848. CHAR chDrive;
  849. chDrive = 0L;
  850. while (hwnd && (hwnd != hwndMDIClient)) {
  851. chDrive = (CHAR)SendMessage(hwnd, FS_GETDRIVE, 0, MAKELONG((WORD)pt.x, (WORD)pt.y));
  852. if (chDrive)
  853. return chDrive;
  854. hwnd = GetParent(hwnd); // try the next higher up
  855. }
  856. return 0;
  857. }
  858. BOOL
  859. IsNetPath(
  860. PDNODE pNode
  861. )
  862. {
  863. CHAR szPath[MAXPATHLEN];
  864. INT i;
  865. if (pNode->iNetType == -1) {
  866. GetTreePath(pNode, szPath);
  867. if (WNetGetDirectoryType((LPSTR)szPath, (LPDWORD)&i, TRUE) == WN_SUCCESS)
  868. pNode->iNetType = i;
  869. else
  870. pNode->iNetType = 0;
  871. }
  872. return pNode->iNetType;
  873. }
  874. VOID
  875. TCWP_DrawItem(
  876. LPDRAWITEMSTRUCT lpLBItem,
  877. HWND hwndLB,
  878. HWND hWnd
  879. )
  880. {
  881. INT x, y, dx, dy;
  882. INT nLevel;
  883. HDC hdc;
  884. WORD len;
  885. RECT rc;
  886. BOOL bHasFocus, bDrawSelected;
  887. PDNODE pNode, pNTemp;
  888. DWORD rgbText;
  889. DWORD rgbBackground;
  890. HBRUSH hBrush, hOld;
  891. INT iBitmap;
  892. WORD view;
  893. CHAR szPath[MAXPATHLEN];
  894. ENTER("TCWP_DrawItem");
  895. if (lpLBItem->itemID == (DWORD)-1) {
  896. return;
  897. }
  898. hdc = lpLBItem->hDC;
  899. pNode = (PDNODE)lpLBItem->itemData;
  900. lstrcpy(szPath, pNode->szName);
  901. if ((wTextAttribs & TA_LOWERCASE) && !(pNode->wFlags & TF_LFN))
  902. AnsiLower(szPath);
  903. len = (WORD)lstrlen(szPath);
  904. MGetTextExtent(hdc, szPath, len, &dx, NULL);
  905. dx += dyBorder;
  906. rc = lpLBItem->rcItem;
  907. rc.left = pNode->nLevels * dxText * 2;
  908. rc.right = rc.left + dxFolder + dx + 4 * dyBorderx2;
  909. if (lpLBItem->itemAction & (ODA_DRAWENTIRE | ODA_SELECT)) {
  910. // draw the branches of the tree first
  911. nLevel = pNode->nLevels;
  912. x = (nLevel * dxText * 2) - dxText + dyBorderx2;
  913. dy = lpLBItem->rcItem.bottom - lpLBItem->rcItem.top;
  914. y = lpLBItem->rcItem.top + (dy/2);
  915. if (hBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOWTEXT))) {
  916. hOld = SelectObject(hdc, hBrush);
  917. if (pNode->pParent) {
  918. /* Draw the horizontal line over to the (possible) folder. */
  919. PatBlt(hdc, x, y, dyText, dyBorder, PATCOPY);
  920. /* Draw the top part of the vertical line. */
  921. PatBlt(hdc, x, lpLBItem->rcItem.top, dyBorder, dy/2, PATCOPY);
  922. /* If not the end of a node, draw the bottom part... */
  923. if (!(pNode->wFlags & TF_LASTLEVELENTRY))
  924. PatBlt(hdc, x, y+dyBorder, dyBorder, dy/2, PATCOPY);
  925. /* Draw the verticals on the left connecting other nodes. */
  926. pNTemp = pNode->pParent;
  927. while (pNTemp) {
  928. nLevel--;
  929. if (!(pNTemp->wFlags & TF_LASTLEVELENTRY))
  930. PatBlt(hdc, (nLevel * dxText * 2) - dxText + dyBorderx2,
  931. lpLBItem->rcItem.top, dyBorder,dy, PATCOPY);
  932. pNTemp = pNTemp->pParent;
  933. }
  934. }
  935. if (hOld)
  936. SelectObject(hdc, hOld);
  937. DeleteObject(hBrush);
  938. }
  939. bDrawSelected = (lpLBItem->itemState & ODS_SELECTED);
  940. bHasFocus = (GetFocus() == lpLBItem->hwndItem);
  941. // draw text with the proper background or rect
  942. if (bHasFocus && bDrawSelected) {
  943. rgbText = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
  944. rgbBackground = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
  945. }
  946. ExtTextOut(hdc, x + dxText + dxFolder + 2 * dyBorderx2,
  947. y-(dyText/2), ETO_OPAQUE, &rc,
  948. szPath, len, NULL);
  949. // draw the bitmaps as needed
  950. // HACK: Don't draw the bitmap when moving
  951. if (fShowSourceBitmaps || (hwndDragging != hwndLB) || !bDrawSelected) {
  952. // Blt the proper folder bitmap
  953. view = (WORD)GetWindowLong(GetParent(hWnd), GWL_VIEW);
  954. if (bNetAdmin && IsNetPath(pNode)) {
  955. // we need this bitmap from lisa
  956. if (bDrawSelected)
  957. iBitmap = BM_IND_OPENDFS;
  958. else
  959. iBitmap = BM_IND_CLOSEDFS;
  960. } else if (!(view & VIEW_PLUSES) || !(pNode->wFlags & TF_HASCHILDREN)) {
  961. if (bDrawSelected)
  962. iBitmap = BM_IND_OPEN;
  963. else
  964. iBitmap = BM_IND_CLOSE;
  965. } else {
  966. if (pNode->wFlags & TF_EXPANDED) {
  967. if (bDrawSelected)
  968. iBitmap = BM_IND_OPENMINUS;
  969. else
  970. iBitmap = BM_IND_CLOSEMINUS;
  971. } else {
  972. if (bDrawSelected)
  973. iBitmap = BM_IND_OPENPLUS;
  974. else
  975. iBitmap = BM_IND_CLOSEPLUS;
  976. }
  977. }
  978. BitBlt(hdc, x + dxText + dyBorder, y-(dyFolder/2), dxFolder, dyFolder,
  979. hdcMem, iBitmap * dxFolder, (bHasFocus && bDrawSelected) ? dyFolder : 0, SRCCOPY);
  980. }
  981. // restore text stuff and draw rect as required
  982. if (bDrawSelected) {
  983. if (bHasFocus) {
  984. SetTextColor(hdc, rgbText);
  985. SetBkColor(hdc, rgbBackground);
  986. } else {
  987. HBRUSH hbr;
  988. if (hbr = CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT))) {
  989. FrameRect(hdc, &rc, hbr);
  990. DeleteObject(hbr);
  991. }
  992. }
  993. }
  994. }
  995. if (lpLBItem->itemAction == ODA_FOCUS)
  996. DrawFocusRect(hdc, &rc);
  997. }
  998. /* A helper for both ExpandLevel and TreeCtlWndProc.TC_COLLAPSELEVEL.
  999. * Code moved from TreeCtlWndProc to be shared. EDH 13 Oct 91
  1000. */
  1001. VOID
  1002. CollapseLevel(
  1003. HWND hwndLB,
  1004. PDNODE pNode,
  1005. INT nIndex
  1006. )
  1007. {
  1008. DWORD_PTR dwTemp;
  1009. PDNODE pParentNode = pNode;
  1010. INT nIndexT = nIndex;
  1011. /* Disable redrawing early. */
  1012. SendMessage(hwndLB, WM_SETREDRAW, FALSE, 0L);
  1013. nIndexT++;
  1014. /* Remove all subdirectories. */
  1015. while (TRUE) {
  1016. /* Make sure we don't run off the end of the listbox. */
  1017. if (SendMessage(hwndLB, LB_GETTEXT, nIndexT, (LPARAM)&dwTemp) == LB_ERR)
  1018. break;
  1019. pNode = (PDNODE)dwTemp;
  1020. if (pNode->nLevels <= pParentNode->nLevels)
  1021. break;
  1022. LocalFree((HANDLE)pNode);
  1023. SendMessage(hwndLB, LB_DELETESTRING, nIndexT, 0L);
  1024. }
  1025. pParentNode->wFlags &= ~TF_EXPANDED;
  1026. SendMessage(hwndLB, WM_SETREDRAW, TRUE, 0L);
  1027. InvalidateRect(hwndLB, NULL, TRUE);
  1028. }
  1029. VOID
  1030. ExpandLevel(
  1031. HWND hWnd,
  1032. WORD wParam,
  1033. INT nIndex,
  1034. PSTR szPath
  1035. )
  1036. {
  1037. HWND hwndLB;
  1038. DWORD_PTR dwTemp;
  1039. PDNODE pNode;
  1040. INT iNumExpanded;
  1041. INT iBottomIndex;
  1042. INT iTopIndex;
  1043. INT iNewTopIndex;
  1044. INT iExpandInView;
  1045. INT iCurrentIndex;
  1046. RECT rc;
  1047. if (GetWindowLong(hWnd, GWL_READLEVEL))
  1048. return;
  1049. hwndLB = GetDlgItem(hWnd, IDCW_TREELISTBOX);
  1050. if (nIndex == -1)
  1051. if ((nIndex = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L)) == LB_ERR)
  1052. return;
  1053. SendMessage(hwndLB, LB_GETTEXT, nIndex, (LPARAM)&dwTemp);
  1054. pNode = (PDNODE)dwTemp;
  1055. // collapse the current contents so we avoid doubling existing "plus" dirs
  1056. if (pNode->wFlags & TF_EXPANDED) {
  1057. if (wParam)
  1058. CollapseLevel(hwndLB, pNode, nIndex);
  1059. else
  1060. return;
  1061. }
  1062. GetTreePath(pNode, szPath);
  1063. StripBackslash(szPath); // remove the slash
  1064. cNodes = 0;
  1065. bCancelTree = FALSE;
  1066. SendMessage(hwndLB, WM_SETREDRAW, FALSE, 0L); // Disable redrawing.
  1067. iCurrentIndex = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
  1068. iNumExpanded = (INT)SendMessage(hwndLB, LB_GETCOUNT, 0, 0L);
  1069. iTopIndex = (INT)SendMessage(hwndLB, LB_GETTOPINDEX, 0, 0L);
  1070. GetClientRect(hwndLB, &rc);
  1071. iBottomIndex = iTopIndex + (rc.bottom+1) / dyFileName;
  1072. if (IsTheDiskReallyThere(hWnd, szPath, FUNC_EXPAND))
  1073. ReadDirLevel(hWnd, pNode, szPath, (WORD)(pNode->nLevels + 1), nIndex,
  1074. (DWORD)(ATTR_DIR | (GetWindowLong(GetParent(hWnd), GWL_ATTRIBS) & ATTR_HS)),
  1075. (BOOL)wParam, szNULL);
  1076. // this is how many will be in view
  1077. iExpandInView = (iBottomIndex - (INT)iCurrentIndex);
  1078. iNumExpanded = (INT)SendMessage(hwndLB, LB_GETCOUNT, 0, 0L) - iNumExpanded;
  1079. if (iNumExpanded >= iExpandInView) {
  1080. iNewTopIndex = min((INT)iCurrentIndex, iTopIndex + iNumExpanded - iExpandInView + 1);
  1081. SendMessage(hwndLB, LB_SETTOPINDEX, (WORD)iNewTopIndex, 0L);
  1082. }
  1083. SendMessage(hwndLB, LB_SETHORIZONTALEXTENT, xTreeMax, 0L);
  1084. SendMessage(hwndLB, WM_SETREDRAW, TRUE, 0L);
  1085. if (iNumExpanded)
  1086. InvalidateRect(hwndLB, NULL, TRUE);
  1087. // Redraw the Status Bar
  1088. UpdateStatus(GetParent(hWnd));
  1089. }
  1090. /*--------------------------------------------------------------------------*/
  1091. /* */
  1092. /* TreeControlWndProc() - */
  1093. /* */
  1094. /*--------------------------------------------------------------------------*/
  1095. /* WndProc for the directory tree control. */
  1096. INT_PTR
  1097. APIENTRY
  1098. TreeControlWndProc(
  1099. register HWND hWnd,
  1100. UINT wMsg,
  1101. WPARAM wParam,
  1102. LPARAM lParam
  1103. )
  1104. {
  1105. WORD iSel;
  1106. INT i, j;
  1107. WPARAM nIndex;
  1108. DWORD dwTemp;
  1109. PDNODE pNode, pNodeNext;
  1110. HWND hwndLB;
  1111. CHAR szPath[MAXPATHLEN];
  1112. STKCHK();
  1113. hwndLB = GetDlgItem(hWnd, IDCW_TREELISTBOX);
  1114. switch (wMsg) {
  1115. case FS_GETDRIVE:
  1116. MSG("TreeControlWndProc", "FS_GETDRIVE");
  1117. return (GetWindowLong(GetParent(hWnd), GWL_TYPE) + 'A');
  1118. case TC_COLLAPSELEVEL:
  1119. MSG("TreeControlWndProc", "TC_COLLAPSELEVEL");
  1120. {
  1121. PDNODE pParentNode;
  1122. if (wParam)
  1123. nIndex = wParam;
  1124. else {
  1125. nIndex = SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
  1126. if (nIndex == LB_ERR)
  1127. break;
  1128. }
  1129. SendMessage(hwndLB, LB_GETTEXT, nIndex, (LPARAM)&pParentNode);
  1130. // short circuit if we are already in this state
  1131. if (!(pParentNode->wFlags & TF_EXPANDED))
  1132. break;
  1133. CollapseLevel(hwndLB, pParentNode, (int)nIndex);
  1134. break;
  1135. }
  1136. case TC_EXPANDLEVEL:
  1137. MSG("TreeControlWndProc", "TC_EXPANDLEVEL");
  1138. ExpandLevel(hWnd, (WORD)wParam, (INT)-1, szPath);
  1139. break;
  1140. case TC_TOGGLELEVEL:
  1141. MSG("TreeControlWndProc", "TC_TOGGLELEVEL");
  1142. // don't do anything while the tree is being built
  1143. if (GetWindowLong(hWnd, GWL_READLEVEL))
  1144. return 1;
  1145. SendMessage(hwndLB, LB_GETTEXT, (WPARAM)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L), (LPARAM)&pNode);
  1146. if (pNode->wFlags & TF_EXPANDED)
  1147. wMsg = TC_COLLAPSELEVEL;
  1148. else
  1149. wMsg = TC_EXPANDLEVEL;
  1150. SendMessage(hWnd, wMsg, FALSE, 0L);
  1151. break;
  1152. case TC_GETDIR:
  1153. // get a full path for a particular dir
  1154. // wParam is the listbox index of path to get
  1155. // lParam LOWORD is PSTR to buffer to fill in
  1156. MSG("TreeControlWndProc", "TC_GETDIR");
  1157. SendMessage(hwndLB, LB_GETTEXT, wParam, (LPARAM)&pNode);
  1158. GetTreePath(pNode, (LPSTR)lParam);
  1159. break;
  1160. case TC_SETDIRECTORY:
  1161. MSG("TreeControlWndProc", "TC_SETDIRECTORY");
  1162. // set the selection in the tree to that for a given path
  1163. {
  1164. INT i;
  1165. i = (INT)FindItemFromPath(hwndLB, (LPSTR)lParam, wParam ? TRUE : FALSE, NULL);
  1166. if (i != -1)
  1167. SendMessage(hwndLB, LB_SETCURSEL, i, 0L);
  1168. break;
  1169. }
  1170. case TC_SETDRIVE:
  1171. #define fFullyExpand LOBYTE(wParam)
  1172. #define fDontSteal HIBYTE(wParam)
  1173. #define szDir (LPSTR)lParam // NULL -> default == window text.
  1174. MSG("TreeControlWndProc", "TC_SETDRIVE");
  1175. {
  1176. RECT rc;
  1177. if (GetWindowLong(hWnd, GWL_READLEVEL))
  1178. break;
  1179. // is the drive/dir specified?
  1180. if (szDir) {
  1181. lstrcpy(szPath, szDir); // yes, use it
  1182. } else {
  1183. SendMessage(GetParent(hWnd), FS_GETDIRECTORY, sizeof(szPath), (LPARAM)szPath); // no, use current
  1184. StripBackslash(szPath);
  1185. }
  1186. AnsiUpperBuff(szPath, 1); // make sure
  1187. SetWindowLong(GetParent(hWnd), GWL_TYPE, 2);
  1188. // resize for new vol label
  1189. GetClientRect(GetParent(hWnd), &rc);
  1190. SendMessage(GetParent(hWnd), WM_SIZE, SIZENOMDICRAP, MAKELONG(rc.right, rc.bottom));
  1191. // ensure the disk is available if the whole dir structure is
  1192. // to be expanded
  1193. if (!fFullyExpand || IsTheDiskReallyThere(hWnd, szPath, FUNC_EXPAND))
  1194. FillTreeListbox(hWnd, szPath, fFullyExpand, fDontSteal);
  1195. // and force the dir half to update with a fake SELCHANGE message
  1196. SendMessage(hWnd, WM_COMMAND, GET_WM_COMMAND_MPS(IDCW_TREELISTBOX, hWnd, LBN_SELCHANGE));
  1197. break;
  1198. #undef fFullyExpand
  1199. #undef fDontSteal
  1200. #undef szDir
  1201. }
  1202. case WM_CHARTOITEM:
  1203. MSG("TreeControlWndProc", "WM_CHARTOITEM");
  1204. {
  1205. WORD w;
  1206. CHAR szB[2];
  1207. INT cItems;
  1208. CHAR ch;
  1209. PDNODE pNode;
  1210. if (GET_WM_CHARTOITEM_CHAR(wParam, lParam) == '\\') // backslash means the root
  1211. return 0L;
  1212. cItems = (INT)SendMessage(hwndLB, LB_GETCOUNT, 0, 0L);
  1213. i = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
  1214. ch = GET_WM_CHARTOITEM_CHAR(wParam, lParam);
  1215. if (i < 0 || ch <= ' ') // filter all other control chars
  1216. return -2L;
  1217. szB[1] = 0L;
  1218. ch &= 255;
  1219. for (j=1; j < cItems; j++) {
  1220. SendMessage(hwndLB, LB_GETTEXT, (i+j) % cItems, (LPARAM)&pNode);
  1221. szB[0] = pNode->szName[0];
  1222. /* Do it this way to be case insensitive. */
  1223. w = ch;
  1224. if (!lstrcmpi((LPSTR)&w, szB))
  1225. break;
  1226. }
  1227. if (j == cItems)
  1228. return -2L;
  1229. SendMessage(hwndLB, LB_SETTOPINDEX, (i+j) % cItems, 0L);
  1230. return((i+j) % cItems);
  1231. }
  1232. case WM_DESTROY:
  1233. MSG("TreeControlWndProc", "WM_DESTROY");
  1234. if (hwndLB == GetFocus()) {
  1235. HWND hwnd;
  1236. if (hwnd = HasDirWindow(GetParent(hWnd)))
  1237. SetFocus(hwnd);
  1238. else
  1239. SetFocus(HasDrivesWindow(GetParent(hWnd)));
  1240. }
  1241. FreeAllTreeData(hwndLB);
  1242. break;
  1243. case WM_CREATE:
  1244. TRACE(BF_WM_CREATE, "TreeControlWndProc - WM_CREATE");
  1245. // create the owner draw list box for the tree
  1246. {
  1247. HWND hwnd;
  1248. hwnd = CreateWindowEx(0L, szListbox, NULL, WS_TREESTYLE | WS_BORDER,
  1249. 0, 0, 0, 0, hWnd, (HMENU)IDCW_TREELISTBOX, hAppInstance, NULL);
  1250. if (!hwnd)
  1251. return -1L;
  1252. SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont, 0L);
  1253. SetWindowLong(hWnd, GWL_READLEVEL, 0);
  1254. break;
  1255. }
  1256. case WM_DRAWITEM:
  1257. MSG("TreeControlWndProc", "WM_DRAWITEM");
  1258. TCWP_DrawItem((LPDRAWITEMSTRUCT)lParam, hwndLB, hWnd);
  1259. break;
  1260. case WM_FILESYSCHANGE:
  1261. MSG("TreeControlWndProc", "WM_FILESYSCHANGE");
  1262. {
  1263. HWND hwndParent;
  1264. PDNODE pNodePrev;
  1265. PDNODE pNodeT;
  1266. if (!lParam || wParam == FSC_REFRESH)
  1267. break;
  1268. nIndex = FindItemFromPath(hwndLB, (LPSTR)lParam, wParam == FSC_MKDIR, &pNode);
  1269. if (nIndex == 0xFFFF) /* Did we find it? */
  1270. break;
  1271. lstrcpy(szPath, (LPSTR)lParam);
  1272. StripPath(szPath);
  1273. switch (wParam) {
  1274. case FSC_MKDIR:
  1275. // auto expand the branch so they can see the new
  1276. // directory just created
  1277. if (!(pNode->wFlags & TF_EXPANDED) &&
  1278. (nIndex == (WPARAM)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L)))
  1279. SendMessage(hWnd, TC_EXPANDLEVEL, FALSE, 0L);
  1280. // make sure this node isn't already here
  1281. if (FindItemFromPath(hwndLB, (LPSTR)lParam, FALSE, NULL) != 0xFFFF)
  1282. break;
  1283. // Insert it into the tree listbox
  1284. dwTemp = InsertDirectory(hWnd, pNode, (WORD)nIndex, szPath, &pNodeT);
  1285. // Add a plus if necessary
  1286. hwndParent = GetParent(hWnd);
  1287. if (GetWindowLong(hwndParent, GWL_VIEW) & VIEW_PLUSES) {
  1288. lstrcpy(szPath, (LPSTR)lParam);
  1289. ScanDirLevel((PDNODE)pNodeT, szPath, ATTR_DIR |
  1290. (GetWindowLong(hwndParent, GWL_ATTRIBS) & ATTR_HS));
  1291. // Invalidate the window so the plus gets drawn if needed
  1292. if (((PDNODE)pNodeT)->wFlags & TF_HASCHILDREN)
  1293. InvalidateRect(hWnd, NULL, FALSE);
  1294. }
  1295. // if we are inserting before or at the current selection
  1296. // push the current selection down
  1297. nIndex = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
  1298. if ((INT)LOWORD(dwTemp) <= nIndex) {
  1299. SendMessage(hwndLB, LB_SETCURSEL, nIndex + 1, 0L);
  1300. }
  1301. break;
  1302. case FSC_RMDIR:
  1303. if (nIndex == 0) /* NEVER delete the Root Dir! */
  1304. break;
  1305. if (pNode->wFlags & TF_LASTLEVELENTRY) {
  1306. // We are deleting the last subdirectory.
  1307. // If there are previous sibling directories, mark one
  1308. // as the last, else mark the parent as empty and unexpanded.
  1309. // It is necessary to do these checks if this bit
  1310. // is set, since if it isn't, there is another sibling
  1311. // with TF_LASTLEVELENTRY set, and so the parent is nonempty.
  1312. //
  1313. // Find the previous entry which has a level not deeper than
  1314. // the level of that being deleted.
  1315. i = (int)nIndex;
  1316. do {
  1317. SendMessage(hwndLB, LB_GETTEXT, --i, (LPARAM)&pNodePrev);
  1318. } while (pNodePrev->nLevels > pNode->nLevels);
  1319. if (pNodePrev->nLevels == pNode->nLevels) {
  1320. // The previous directory is a sibling... it becomes
  1321. // the new last level entry.
  1322. pNodePrev->wFlags |= TF_LASTLEVELENTRY;
  1323. } else {
  1324. // In order to find this entry, the parent must have
  1325. // been expanded, so if the parent of the deleted dir
  1326. // has no listbox entries under it, it may be assumed that
  1327. // the directory has no children.
  1328. pNodePrev->wFlags &= ~(TF_HASCHILDREN | TF_EXPANDED);
  1329. }
  1330. }
  1331. // Are we deleting the current selection?
  1332. // if so we move the selection to the item above the current.
  1333. // this should work in all cases because you can't delete
  1334. // the root.
  1335. if ((WPARAM)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L) == nIndex) {
  1336. SendMessage(hwndLB, LB_SETCURSEL, nIndex - 1, 0L);
  1337. SendMessage(hWnd, WM_COMMAND, GET_WM_COMMAND_MPS(0, 0, LBN_SELCHANGE));
  1338. }
  1339. SendMessage(hWnd, TC_COLLAPSELEVEL, nIndex, 0L);
  1340. SendMessage(hwndLB, LB_DELETESTRING, nIndex, 0L);
  1341. LocalFree((HANDLE)pNode);
  1342. break;
  1343. }
  1344. break;
  1345. }
  1346. case WM_COMMAND:
  1347. {
  1348. WORD id;
  1349. id = GET_WM_COMMAND_ID(wParam, lParam);
  1350. switch (GET_WM_COMMAND_CMD(wParam, lParam)) {
  1351. case LBN_SELCHANGE:
  1352. MSG("TreeControlWndProc", "LBN_SELCHANGE");
  1353. {
  1354. HWND hwndParent;
  1355. HWND hwndDir;
  1356. INT CurSel;
  1357. hwndParent = GetParent(hWnd);
  1358. CurSel = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
  1359. SendMessage(hWnd, TC_GETDIR, CurSel,(LPARAM)szPath);
  1360. AddBackslash(szPath);
  1361. SendMessage(hwndParent, FS_GETFILESPEC, sizeof(szPath) - lstrlen(szPath), (LPARAM)szPath+lstrlen(szPath));
  1362. if (hwndDir = HasDirWindow(hwndParent)) {
  1363. // update the dir window
  1364. id = CD_PATH;
  1365. // don't allow abort on first or last directories
  1366. if (CurSel > 0 &&
  1367. CurSel != ((INT)SendMessage(hwndLB, LB_GETCOUNT, 0, 0L) - 1)) {
  1368. id = CD_PATH | CD_ALLOWABORT;
  1369. }
  1370. SendMessage(hwndDir, FS_CHANGEDISPLAY, id, (LPARAM)szPath);
  1371. } else {
  1372. SetMDIWindowText(hwndParent, szPath);
  1373. }
  1374. UpdateStatus(hwndParent);
  1375. break;
  1376. }
  1377. case LBN_DBLCLK:
  1378. MSG("TreeControlWndProc", "LBN_DBLCLK");
  1379. SendMessage(hwndFrame, WM_COMMAND, GET_WM_COMMAND_MPS(IDM_OPEN, 0, 0));
  1380. break;
  1381. case LBN_SETFOCUS:
  1382. MSG("TreeControlWndProc", "LBN_SETFOCUS");
  1383. SetWindowLongPtr(GetParent(hWnd), GWLP_LASTFOCUS, (LPARAM)GET_WM_COMMAND_HWND(wParam, lParam));
  1384. UpdateSelection(GET_WM_COMMAND_HWND(wParam, lParam));
  1385. UpdateStatus(GetParent(hWnd)); // update the status bar
  1386. break;
  1387. case LBN_KILLFOCUS:
  1388. MSG("TreeControlWndProc", "LBN_KILLFOCUS");
  1389. SetWindowLongPtr(GetParent(hWnd), GWLP_LASTFOCUS, 0);
  1390. UpdateSelection(GET_WM_COMMAND_HWND(wParam, lParam));
  1391. SetWindowLongPtr(GetParent(hWnd), GWLP_LASTFOCUS, (LPARAM)GET_WM_COMMAND_HWND(wParam, lParam));
  1392. break;
  1393. }
  1394. }
  1395. break;
  1396. case WM_LBTRACKPOINT:
  1397. MSG("TreeControlWndProc", "WM_LBTRACKPOINT");
  1398. // wParam is the listbox index that we are over
  1399. // lParam is the mouse point
  1400. /* Return 0 to do nothing, 1 to abort everything, or 2 to abort just dblclicks. */
  1401. {
  1402. HDC hdc;
  1403. INT dx;
  1404. INT xNode;
  1405. MSG msg;
  1406. RECT rc;
  1407. HFONT hOld;
  1408. POINT pt;
  1409. DRAGOBJECTDATA dodata;
  1410. /* Someone clicked somewhere in the listbox. */
  1411. // don't do anything while the tree is being built
  1412. if (GetWindowLong(hWnd, GWL_READLEVEL))
  1413. return 1;
  1414. /* Get the node they clicked on. */
  1415. SendMessage(hwndLB, LB_GETTEXT, wParam, (LPARAM)&pNode);
  1416. lstrcpy(szPath, pNode->szName);
  1417. if ((wTextAttribs & TA_LOWERCASE) && !(pNode->wFlags & TF_LFN))
  1418. AnsiLower(szPath);
  1419. // if (pNode->wFlags | TF_DISABLED)
  1420. // return 2L;
  1421. // too FAR to the left?
  1422. i = LOWORD(lParam);
  1423. xNode = pNode->nLevels * dxText * 2;
  1424. if (i < xNode)
  1425. return 2; // yes, get out now
  1426. // too FAR to the right?
  1427. hdc = GetDC(hwndLB);
  1428. hOld = SelectObject(hdc, hFont);
  1429. MGetTextExtent(hdc, szPath, lstrlen(szPath), &dx, NULL);
  1430. dx += (dyBorderx2*2);
  1431. if (hOld)
  1432. SelectObject(hdc, hOld);
  1433. ReleaseDC(hwndLB, hdc);
  1434. if (i > xNode + dxFolder + dx + 4 * dyBorderx2)
  1435. return 2; // yes
  1436. // Emulate a SELCHANGE notification and notify our parent
  1437. SendMessage(hwndLB, LB_SETCURSEL, wParam, 0L);
  1438. SendMessage(hWnd, WM_COMMAND, GET_WM_COMMAND_MPS(0, hwndLB, LBN_SELCHANGE));
  1439. // make sure mouse still down
  1440. if (!(GetKeyState(VK_LBUTTON) & 0x8000))
  1441. return 1;
  1442. MPOINT2POINT(MAKEMPOINT(lParam), pt);
  1443. ClientToScreen(hwndLB, (LPPOINT)&pt);
  1444. ScreenToClient(hWnd, (LPPOINT)&pt);
  1445. SetRect(&rc, pt.x - dxClickRect, pt.y - dyClickRect,
  1446. pt.x + dxClickRect, pt.y + dyClickRect);
  1447. SetCapture(hWnd);
  1448. while (GetMessage(&msg, NULL, 0, 0)) {
  1449. DispatchMessage(&msg);
  1450. if (msg.message == WM_LBUTTONUP)
  1451. break;
  1452. MPOINT2POINT(MAKEMPOINT(msg.lParam), pt);
  1453. if (GetCapture() != hWnd) {
  1454. msg.message = WM_LBUTTONUP;
  1455. break;
  1456. }
  1457. if ((msg.message == WM_MOUSEMOVE) && !(PtInRect(&rc, pt)))
  1458. break;
  1459. }
  1460. ReleaseCapture();
  1461. /* Did the guy NOT drag anything? */
  1462. if (msg.message == WM_LBUTTONUP)
  1463. return 1;
  1464. /* Enter Danger Mouse's BatCave. */
  1465. SendMessage(GetParent(hWnd), FS_GETDIRECTORY, sizeof(szPath), (LPARAM)szPath);
  1466. StripBackslash(szPath);
  1467. hwndDragging = hwndLB;
  1468. iCurDrag = SINGLECOPYCURSOR;
  1469. dodata.pch = szPath;
  1470. dodata.hMemGlobal = 0;
  1471. DragObject(hwndMDIClient, hWnd, (UINT)DOF_DIRECTORY, (DWORD)(ULONG_PTR)&dodata, LoadCursor(hAppInstance, MAKEINTRESOURCE(iCurDrag)));
  1472. hwndDragging = NULL;
  1473. fShowSourceBitmaps = TRUE;
  1474. InvalidateRect(hwndLB, NULL, FALSE);
  1475. return 2;
  1476. }
  1477. case WM_DRAGSELECT:
  1478. MSG("TreeControlWndProc", "WM_DRAGSELECT");
  1479. /* WM_DRAGSELECT is sent whenever a new window returns TRUE to a
  1480. * QUERYDROPOBJECT.
  1481. */
  1482. iSelHilite = LOWORD(((LPDROPSTRUCT)lParam)->dwControlData);
  1483. RectTreeItem(hwndLB, iSelHilite, (BOOL)wParam);
  1484. break;
  1485. case WM_DRAGMOVE:
  1486. MSG("TreeControlWndProc", "WM_DRAGMOVE");
  1487. /* WM_DRAGMOVE is sent when two consequetive TRUE QUERYDROPOBJECT
  1488. * messages come from the same window.
  1489. */
  1490. /* Get the subitem we are over. */
  1491. iSel = LOWORD(((LPDROPSTRUCT)lParam)->dwControlData);
  1492. /* Is it a new one? */
  1493. if (iSel == (WORD)iSelHilite)
  1494. break;
  1495. /* Yup, un-select the old item. */
  1496. RectTreeItem(hwndLB, iSelHilite, FALSE);
  1497. /* Select the new one. */
  1498. iSelHilite = iSel;
  1499. RectTreeItem(hwndLB, iSel, TRUE);
  1500. break;
  1501. case WM_DRAGLOOP:
  1502. MSG("TreeControlWndProc", "WM_DRAGLOOP");
  1503. // wParam TRUE on dropable target
  1504. // FALSE not dropable target
  1505. // lParam lpds
  1506. {
  1507. BOOL bCopy;
  1508. #define lpds ((LPDROPSTRUCT)lParam)
  1509. /* Are we over a drop-able sink? */
  1510. if (wParam) {
  1511. if (GetKeyState(VK_CONTROL) < 0) // CTRL
  1512. bCopy = TRUE;
  1513. else if (GetKeyState(VK_MENU)<0 || GetKeyState(VK_SHIFT)<0) // ALT || SHIFT
  1514. bCopy = FALSE;
  1515. else
  1516. bCopy = (GetDrive(lpds->hwndSink, lpds->ptDrop) != GetDrive(lpds->hwndSource, lpds->ptDrop));
  1517. } else {
  1518. bCopy = TRUE;
  1519. }
  1520. if (bCopy != fShowSourceBitmaps) {
  1521. RECT rc;
  1522. fShowSourceBitmaps = bCopy;
  1523. iSel = (WORD)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
  1524. if (!(BOOL)SendMessage(hwndLB, LB_GETITEMRECT, iSel, (LPARAM)&rc))
  1525. break;
  1526. InvalidateRect(hwndLB, &rc, FALSE);
  1527. UpdateWindow(hwndLB);
  1528. // hack, set the cursor to match the move/copy state
  1529. if (wParam)
  1530. SetCursor(GetMoveCopyCursor());
  1531. }
  1532. break;
  1533. }
  1534. case WM_QUERYDROPOBJECT:
  1535. MSG("TreeControlWndProc", "WM_QUERYDROPOBJECT");
  1536. // wParam TRUE on NC area
  1537. // FALSE on client area
  1538. // lParam lpds
  1539. // Do nothing
  1540. return(FALSE);
  1541. #define lpds ((LPDROPSTRUCT)lParam)
  1542. /* Check for valid format. */
  1543. switch (lpds->wFmt) {
  1544. case DOF_EXECUTABLE:
  1545. case DOF_DOCUMENT:
  1546. case DOF_DIRECTORY:
  1547. case DOF_MULTIPLE:
  1548. if (fShowSourceBitmaps)
  1549. i = iCurDrag | 1; // copy
  1550. else
  1551. i = iCurDrag & 0xFFFE;
  1552. break;
  1553. default:
  1554. return FALSE;
  1555. }
  1556. /* Must be dropping on the listbox client area. */
  1557. if (lpds->hwndSink != hwndLB)
  1558. return FALSE;
  1559. if (LOWORD(lpds->dwControlData) == 0xFFFF)
  1560. return FALSE;
  1561. return (INT_PTR)GetMoveCopyCursor();
  1562. case WM_DROPOBJECT: // tree being dropped on do your thing
  1563. #define lpds ((LPDROPSTRUCT)lParam) // BUG: WM_DROPOBJECT structure packing!
  1564. // Do nothing
  1565. return(TRUE);
  1566. MSG("TreeControlWndProc", "WM_DROPOBJECT");
  1567. // dir (search) drop on tree:
  1568. // HIWORD(dwData) 0
  1569. // LOWORD(dwData) LPSTR to files being dragged
  1570. //
  1571. // tree drop on tree:
  1572. // HIWORD(dwData) index of source drag
  1573. // LOWORD(dwData) LPSTR to path
  1574. {
  1575. LPSTR pFrom;
  1576. nIndex = LOWORD(lpds->dwControlData);
  1577. pFrom = (LPSTR)(((LPDRAGOBJECTDATA)(lpds->dwData))->pch);
  1578. // Get the destination
  1579. SendMessage(hWnd, TC_GETDIR, nIndex, (LPARAM)szPath);
  1580. CheckEscapes(szPath);
  1581. // if source and dest are the same make this a NOP
  1582. if (!lstrcmpi(szPath, pFrom))
  1583. return TRUE;
  1584. AddBackslash(szPath);
  1585. lstrcat(szPath, szStarDotStar);
  1586. DMMoveCopyHelper(pFrom, szPath, fShowSourceBitmaps);
  1587. RectTreeItem(hwndLB, (int)nIndex, FALSE);
  1588. }
  1589. return TRUE;
  1590. #undef lpds
  1591. case WM_MEASUREITEM:
  1592. MSG("TreeControlWndProc", "WM_MEASUREITEM");
  1593. #define pLBMItem ((LPMEASUREITEMSTRUCT)lParam)
  1594. pLBMItem->itemHeight = (WORD)dyFileName;
  1595. break;
  1596. case WM_VKEYTOITEM:
  1597. MSG("TreeControlWndProc", "WM_VKEYTOITEM");
  1598. if (wParam == VK_ESCAPE) {
  1599. bCancelTree = TRUE;
  1600. return -2L;
  1601. }
  1602. i = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
  1603. if (i < 0)
  1604. return -2L;
  1605. j = 1;
  1606. SendMessage(hwndLB, LB_GETTEXT, i, (LPARAM)&pNode);
  1607. switch (GET_WM_VKEYTOITEM_ITEM(wParam, lParam)) {
  1608. case VK_LEFT:
  1609. while (SendMessage(hwndLB, LB_GETTEXT, --i, (LPARAM)&pNodeNext) != LB_ERR) {
  1610. if (pNode == pNode->pParent)
  1611. return(i);
  1612. }
  1613. goto SameSelection;
  1614. case VK_RIGHT:
  1615. if ((SendMessage(hwndLB, LB_GETTEXT, i+1, (LPARAM)&pNodeNext) == LB_ERR)
  1616. || (pNodeNext->pParent != pNode)) {
  1617. goto SameSelection;
  1618. }
  1619. return(i+1);
  1620. case VK_UP:
  1621. j = -1;
  1622. /** FALL THRU ***/
  1623. case VK_DOWN:
  1624. /* If the control key is not down, use default behavior. */
  1625. if (GetKeyState(VK_CONTROL) >= 0)
  1626. return(-1L);
  1627. while (SendMessage(hwndLB, LB_GETTEXT, i += j, (LPARAM)&pNodeNext) != LB_ERR) {
  1628. if (pNodeNext->pParent == pNode->pParent)
  1629. return(i);
  1630. }
  1631. SameSelection:
  1632. MessageBeep(0);
  1633. return(-2L);
  1634. case VK_F6: // like excel
  1635. case VK_TAB:
  1636. {
  1637. HWND hwndDir, hwndDrives;
  1638. BOOL bDir;
  1639. GetTreeWindows(GetParent(hWnd), NULL, &hwndDir, &hwndDrives);
  1640. // Check to see if we can change to the directory window
  1641. if (hwndDir) {
  1642. HWND hwndLB; /* Local scope ONLY */
  1643. hwndLB = GetDlgItem (hwndDir,IDCW_LISTBOX);
  1644. if (hwndLB) {
  1645. SendMessage (hwndLB,LB_GETTEXT,0, (LPARAM) &pNode);
  1646. bDir = pNode ? TRUE : FALSE;
  1647. }
  1648. }
  1649. if (GetKeyState(VK_SHIFT) < 0)
  1650. SetFocus(hwndDrives);
  1651. else
  1652. if (bDir)
  1653. SetFocus (hwndDir);
  1654. else
  1655. SetFocus (hwndDrives);
  1656. return -2L; // I dealt with this!
  1657. }
  1658. case VK_BACK:
  1659. {
  1660. BYTE nStartLevel;
  1661. if (i <= 0)
  1662. return -2L; // root case
  1663. nStartLevel = pNode->nLevels;
  1664. do {
  1665. SendMessage(hwndLB, LB_GETTEXT, --i, (LPARAM)&pNodeNext);
  1666. } while (i > 0 && pNodeNext->nLevels >= nStartLevel);
  1667. return i;
  1668. }
  1669. default:
  1670. if (GetKeyState(VK_CONTROL) < 0)
  1671. return SendMessage(GetDlgItem(GetParent(hWnd), IDCW_DRIVES), wMsg, wParam, lParam);
  1672. return -1L;
  1673. }
  1674. break;
  1675. case WM_SETFOCUS:
  1676. case WM_LBUTTONDOWN:
  1677. MSG("TreeControlWndProc", "WM_LBUTTONDOWN");
  1678. SetFocus(hwndLB);
  1679. break;
  1680. case WM_SIZE:
  1681. MSG("TreeControlWndProc", "WM_SIZE");
  1682. if (!IsIconic(GetParent(hWnd))) {
  1683. INT iMax;
  1684. MoveWindow(hwndLB, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
  1685. iMax = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
  1686. if (iMax >= 0) {
  1687. RECT rc;
  1688. INT top, bottom;
  1689. GetClientRect(hwndLB, &rc);
  1690. top = (INT)SendMessage(hwndLB, LB_GETTOPINDEX, 0, 0L);
  1691. bottom = top + rc.bottom / dyFileName;
  1692. if (iMax < top || iMax > bottom)
  1693. SendMessage(hwndLB, LB_SETTOPINDEX, iMax - ((bottom - top) / 2), 0L);
  1694. }
  1695. }
  1696. break;
  1697. default:
  1698. DEFMSG("TreeControlWndProc", (WORD)wMsg);
  1699. return DefWindowProc(hWnd, wMsg, wParam, lParam);
  1700. }
  1701. return 0L;
  1702. }