Leaked source code of windows server 2003
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.

2217 lines
70 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. OemToCharBuff(lfndta.fd.cFileName, lfndta.fd.cFileName, sizeof(lfndta.fd.cFileName)/sizeof(lfndta.fd.cFileName[0]));
  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. iNode = InsertDirectory(hwndTC, NULL, 0, szTemp, &pNode);
  647. if (pNode) {
  648. dwAttribs = ATTR_DIR | (GetWindowLong(GetParent(hwndTC), GWL_ATTRIBS) & ATTR_HS);
  649. cNodes = 0;
  650. bCancelTree = FALSE;
  651. if (szDefaultDir) {
  652. lstrcpy(szExpand, szDefaultDir+3); // skip "X:\"
  653. p = szExpand;
  654. while (*p) { // null out all slashes
  655. while (*p && *p != '\\')
  656. p = AnsiNext(p);
  657. if (*p)
  658. *p++ = 0L;
  659. }
  660. p++;
  661. *p = 0L; // double null terminated
  662. } else
  663. *szExpand = 0;
  664. if (!ReadDirLevel(hwndTC, pNode, szTemp, 1, 0, dwAttribs, bFullyExpand, szExpand)) {
  665. lFreeSpace = -2L;
  666. }
  667. }
  668. }
  669. SendMessage(hwndLB, LB_SETHORIZONTALEXTENT, xTreeMax, 0L);
  670. if (szDefaultDir) {
  671. FindItemFromPath(hwndLB, szDefaultDir, FALSE, &pNode);
  672. }
  673. SendMessage(hwndLB, LB_SELECTSTRING, -1, (LPARAM)pNode);
  674. UpdateStatus(GetParent(hwndTC)); // Redraw the Status Bar
  675. SendMessage(hwndLB, WM_SETREDRAW, TRUE, 0L);
  676. InvalidateRect(hwndLB, NULL, TRUE);
  677. UpdateWindow(hwndLB); // make this look a bit better
  678. LEAVE("FillTreeListbox");
  679. }
  680. //
  681. // FindItemFromPath()
  682. //
  683. // find the PDNODE and LBIndex for a given path
  684. //
  685. // in:
  686. // hwndLB listbox of tree
  687. // lpszPath path to search for (ANSI)
  688. // bReturnParent TRUE if you want the parent, not the node
  689. //
  690. //
  691. // returns:
  692. // listbox index (0xFFFF if not found)
  693. // *ppNode is filled with pNode of node, or pNode of parent if bReturnParent is TRUE
  694. //
  695. WORD
  696. FindItemFromPath(
  697. HWND hwndLB,
  698. LPSTR lpszPath,
  699. BOOL bReturnParent,
  700. PDNODE *ppNode
  701. )
  702. {
  703. register WORD i;
  704. register LPSTR p;
  705. PDNODE pNode;
  706. PDNODE pPreviousNode;
  707. CHAR szElement[1+MAXFILENAMELEN+1];
  708. ENTER("FindItemFromPath");
  709. if (lstrlen(lpszPath) < 3) {
  710. LEAVE("FindItemFromPath");
  711. return -1;
  712. }
  713. if (IsDBCSLeadByte( lpszPath[0] ) || lpszPath[1] != ':') {
  714. LEAVE("FindItemFromPath");
  715. return -1;
  716. }
  717. i = 0;
  718. pPreviousNode = NULL;
  719. while (*lpszPath) {
  720. /* NULL out szElement[1] so the backslash hack isn't repeated with
  721. * a first level directory of length 1.
  722. */
  723. szElement[1] = 0L;
  724. /* Copy the next section of the path into 'szElement' */
  725. p = szElement;
  726. while (*lpszPath && *lpszPath != '\\') {
  727. *p++ = *lpszPath;
  728. if (IsDBCSLeadByte( *lpszPath ))
  729. *p++ = lpszPath[1]; // copy 2nd byte of DBCS char.
  730. lpszPath = AnsiNext( lpszPath );
  731. }
  732. /* Add a backslash for the Root directory. */
  733. if ( !IsDBCSLeadByte( szElement[0] ) && szElement[1] == ':' )
  734. *p++ = '\\';
  735. /* NULL terminate 'szElement' */
  736. *p = 0L;
  737. /* Skip over the path's next Backslash. */
  738. if (*lpszPath)
  739. lpszPath = AnsiNext(lpszPath);
  740. else if (bReturnParent) {
  741. /* We're at the end of a path which includes a filename. Return
  742. * the previously found parent.
  743. */
  744. if (ppNode) {
  745. *ppNode = pPreviousNode;
  746. }
  747. LEAVE("FindItemFromPath");
  748. return i;
  749. }
  750. while (TRUE) {
  751. /* Out of LB items? Not found. */
  752. if (SendMessage(hwndLB, LB_GETTEXT, i, (LPARAM)&pNode) == LB_ERR)
  753. return -1;
  754. if (pNode->pParent == pPreviousNode) {
  755. if (!lstrcmpi(szElement, pNode->szName)) {
  756. /* We've found the element... */
  757. pPreviousNode = pNode;
  758. break;
  759. }
  760. }
  761. i++;
  762. }
  763. }
  764. if (ppNode) {
  765. *ppNode = pPreviousNode;
  766. }
  767. LEAVE("FindItemFromPath");
  768. return i;
  769. }
  770. /*--------------------------------------------------------------------------*/
  771. /* */
  772. /* RectTreeItem() - */
  773. /* */
  774. /*--------------------------------------------------------------------------*/
  775. VOID
  776. RectTreeItem(
  777. HWND hwndLB,
  778. INT iItem,
  779. BOOL bFocusOn
  780. )
  781. {
  782. INT dx;
  783. INT len;
  784. HDC hdc;
  785. RECT rc;
  786. RECT rcClip;
  787. BOOL bSel;
  788. WORD wColor;
  789. PDNODE pNode;
  790. HBRUSH hBrush;
  791. HFONT hOld;
  792. CHAR szPath[MAXPATHLEN];
  793. ENTER("RectTreeItem");
  794. if (iItem == -1) {
  795. LEAVE("RectTreeItem");
  796. return;
  797. }
  798. /* Are we over ourselves? (i.e. a selected item in the source listbox) */
  799. bSel = (BOOL)SendMessage(hwndLB, LB_GETSEL, iItem, 0L);
  800. if (bSel && (hwndDragging == hwndLB)) {
  801. LEAVE("RectTreeItem");
  802. return;
  803. }
  804. SendMessage(hwndLB, LB_GETTEXT, iItem, (LPARAM)&pNode);
  805. SendMessage(hwndLB, LB_GETITEMRECT, iItem, (LPARAM)&rc);
  806. hdc = GetDC(hwndLB);
  807. len = lstrlen(pNode->szName);
  808. lstrcpy(szPath, pNode->szName);
  809. if ((wTextAttribs & TA_LOWERCASE) && !(pNode->wFlags & TF_LFN))
  810. AnsiLower(szPath);
  811. hOld = SelectObject(hdc, hFont);
  812. MGetTextExtent(hdc, szPath, len, &dx, NULL);
  813. dx += dyBorder;
  814. if (hOld)
  815. SelectObject(hdc, hOld);
  816. rc.left = pNode->nLevels * dxText * 2;
  817. rc.right = rc.left + dxFolder + dx + 4 * dyBorderx2;
  818. GetClientRect(hwndLB, &rcClip);
  819. IntersectRect(&rc, &rc, &rcClip);
  820. if (bFocusOn) {
  821. if (bSel) {
  822. wColor = COLOR_WINDOW;
  823. InflateRect(&rc, -dyBorder, -dyBorder);
  824. } else
  825. wColor = COLOR_WINDOWFRAME;
  826. if (hBrush = CreateSolidBrush(GetSysColor(wColor))) {
  827. FrameRect(hdc, &rc, hBrush);
  828. DeleteObject(hBrush);
  829. }
  830. } else {
  831. InvalidateRect(hwndLB, &rc, TRUE);
  832. UpdateWindow(hwndLB);
  833. }
  834. ReleaseDC(hwndLB, hdc);
  835. LEAVE("RectTreeItem");
  836. }
  837. // return the drive of the first window to respond to the FS_GETDRIVE
  838. // message. this usually starts from the source or dest of a drop
  839. // and travels up until we find a drive or hit the MDI client
  840. INT
  841. APIENTRY
  842. GetDrive(
  843. HWND hwnd,
  844. POINT pt
  845. )
  846. {
  847. CHAR chDrive;
  848. chDrive = 0L;
  849. while (hwnd && (hwnd != hwndMDIClient)) {
  850. chDrive = (CHAR)SendMessage(hwnd, FS_GETDRIVE, 0, MAKELONG((WORD)pt.x, (WORD)pt.y));
  851. if (chDrive)
  852. return chDrive;
  853. hwnd = GetParent(hwnd); // try the next higher up
  854. }
  855. return 0;
  856. }
  857. BOOL
  858. IsNetPath(
  859. PDNODE pNode
  860. )
  861. {
  862. CHAR szPath[MAXPATHLEN];
  863. INT i;
  864. if (pNode->iNetType == -1) {
  865. GetTreePath(pNode, szPath);
  866. if (WNetGetDirectoryType((LPSTR)szPath, (LPDWORD)&i, TRUE) == WN_SUCCESS)
  867. pNode->iNetType = i;
  868. else
  869. pNode->iNetType = 0;
  870. }
  871. return pNode->iNetType;
  872. }
  873. VOID
  874. TCWP_DrawItem(
  875. LPDRAWITEMSTRUCT lpLBItem,
  876. HWND hwndLB,
  877. HWND hWnd
  878. )
  879. {
  880. INT x, y, dx, dy;
  881. INT nLevel;
  882. HDC hdc;
  883. WORD len;
  884. RECT rc;
  885. BOOL bHasFocus, bDrawSelected;
  886. PDNODE pNode, pNTemp;
  887. DWORD rgbText;
  888. DWORD rgbBackground;
  889. HBRUSH hBrush, hOld;
  890. INT iBitmap;
  891. WORD view;
  892. CHAR szPath[MAXPATHLEN];
  893. ENTER("TCWP_DrawItem");
  894. if (lpLBItem->itemID == (DWORD)-1) {
  895. return;
  896. }
  897. hdc = lpLBItem->hDC;
  898. pNode = (PDNODE)lpLBItem->itemData;
  899. lstrcpy(szPath, pNode->szName);
  900. if ((wTextAttribs & TA_LOWERCASE) && !(pNode->wFlags & TF_LFN))
  901. AnsiLower(szPath);
  902. len = (WORD)lstrlen(szPath);
  903. MGetTextExtent(hdc, szPath, len, &dx, NULL);
  904. dx += dyBorder;
  905. rc = lpLBItem->rcItem;
  906. rc.left = pNode->nLevels * dxText * 2;
  907. rc.right = rc.left + dxFolder + dx + 4 * dyBorderx2;
  908. if (lpLBItem->itemAction & (ODA_DRAWENTIRE | ODA_SELECT)) {
  909. // draw the branches of the tree first
  910. nLevel = pNode->nLevels;
  911. x = (nLevel * dxText * 2) - dxText + dyBorderx2;
  912. dy = lpLBItem->rcItem.bottom - lpLBItem->rcItem.top;
  913. y = lpLBItem->rcItem.top + (dy/2);
  914. if (hBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOWTEXT))) {
  915. hOld = SelectObject(hdc, hBrush);
  916. if (pNode->pParent) {
  917. /* Draw the horizontal line over to the (possible) folder. */
  918. PatBlt(hdc, x, y, dyText, dyBorder, PATCOPY);
  919. /* Draw the top part of the vertical line. */
  920. PatBlt(hdc, x, lpLBItem->rcItem.top, dyBorder, dy/2, PATCOPY);
  921. /* If not the end of a node, draw the bottom part... */
  922. if (!(pNode->wFlags & TF_LASTLEVELENTRY))
  923. PatBlt(hdc, x, y+dyBorder, dyBorder, dy/2, PATCOPY);
  924. /* Draw the verticals on the left connecting other nodes. */
  925. pNTemp = pNode->pParent;
  926. while (pNTemp) {
  927. nLevel--;
  928. if (!(pNTemp->wFlags & TF_LASTLEVELENTRY))
  929. PatBlt(hdc, (nLevel * dxText * 2) - dxText + dyBorderx2,
  930. lpLBItem->rcItem.top, dyBorder,dy, PATCOPY);
  931. pNTemp = pNTemp->pParent;
  932. }
  933. }
  934. if (hOld)
  935. SelectObject(hdc, hOld);
  936. DeleteObject(hBrush);
  937. }
  938. bDrawSelected = (lpLBItem->itemState & ODS_SELECTED);
  939. bHasFocus = (GetFocus() == lpLBItem->hwndItem);
  940. // draw text with the proper background or rect
  941. if (bHasFocus && bDrawSelected) {
  942. rgbText = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
  943. rgbBackground = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
  944. }
  945. ExtTextOut(hdc, x + dxText + dxFolder + 2 * dyBorderx2,
  946. y-(dyText/2), ETO_OPAQUE, &rc,
  947. szPath, len, NULL);
  948. // draw the bitmaps as needed
  949. // HACK: Don't draw the bitmap when moving
  950. if (fShowSourceBitmaps || (hwndDragging != hwndLB) || !bDrawSelected) {
  951. // Blt the proper folder bitmap
  952. view = (WORD)GetWindowLong(GetParent(hWnd), GWL_VIEW);
  953. if (bNetAdmin && IsNetPath(pNode)) {
  954. // we need this bitmap from lisa
  955. if (bDrawSelected)
  956. iBitmap = BM_IND_OPENDFS;
  957. else
  958. iBitmap = BM_IND_CLOSEDFS;
  959. } else if (!(view & VIEW_PLUSES) || !(pNode->wFlags & TF_HASCHILDREN)) {
  960. if (bDrawSelected)
  961. iBitmap = BM_IND_OPEN;
  962. else
  963. iBitmap = BM_IND_CLOSE;
  964. } else {
  965. if (pNode->wFlags & TF_EXPANDED) {
  966. if (bDrawSelected)
  967. iBitmap = BM_IND_OPENMINUS;
  968. else
  969. iBitmap = BM_IND_CLOSEMINUS;
  970. } else {
  971. if (bDrawSelected)
  972. iBitmap = BM_IND_OPENPLUS;
  973. else
  974. iBitmap = BM_IND_CLOSEPLUS;
  975. }
  976. }
  977. BitBlt(hdc, x + dxText + dyBorder, y-(dyFolder/2), dxFolder, dyFolder,
  978. hdcMem, iBitmap * dxFolder, (bHasFocus && bDrawSelected) ? dyFolder : 0, SRCCOPY);
  979. }
  980. // restore text stuff and draw rect as required
  981. if (bDrawSelected) {
  982. if (bHasFocus) {
  983. SetTextColor(hdc, rgbText);
  984. SetBkColor(hdc, rgbBackground);
  985. } else {
  986. HBRUSH hbr;
  987. if (hbr = CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT))) {
  988. FrameRect(hdc, &rc, hbr);
  989. DeleteObject(hbr);
  990. }
  991. }
  992. }
  993. }
  994. if (lpLBItem->itemAction == ODA_FOCUS)
  995. DrawFocusRect(hdc, &rc);
  996. }
  997. /* A helper for both ExpandLevel and TreeCtlWndProc.TC_COLLAPSELEVEL.
  998. * Code moved from TreeCtlWndProc to be shared. EDH 13 Oct 91
  999. */
  1000. VOID
  1001. CollapseLevel(
  1002. HWND hwndLB,
  1003. PDNODE pNode,
  1004. INT nIndex
  1005. )
  1006. {
  1007. DWORD_PTR dwTemp;
  1008. PDNODE pParentNode = pNode;
  1009. INT nIndexT = nIndex;
  1010. /* Disable redrawing early. */
  1011. SendMessage(hwndLB, WM_SETREDRAW, FALSE, 0L);
  1012. nIndexT++;
  1013. /* Remove all subdirectories. */
  1014. while (TRUE) {
  1015. /* Make sure we don't run off the end of the listbox. */
  1016. if (SendMessage(hwndLB, LB_GETTEXT, nIndexT, (LPARAM)&dwTemp) == LB_ERR)
  1017. break;
  1018. pNode = (PDNODE)dwTemp;
  1019. if (pNode->nLevels <= pParentNode->nLevels)
  1020. break;
  1021. LocalFree((HANDLE)pNode);
  1022. SendMessage(hwndLB, LB_DELETESTRING, nIndexT, 0L);
  1023. }
  1024. pParentNode->wFlags &= ~TF_EXPANDED;
  1025. SendMessage(hwndLB, WM_SETREDRAW, TRUE, 0L);
  1026. InvalidateRect(hwndLB, NULL, TRUE);
  1027. }
  1028. VOID
  1029. ExpandLevel(
  1030. HWND hWnd,
  1031. WORD wParam,
  1032. INT nIndex,
  1033. PSTR szPath
  1034. )
  1035. {
  1036. HWND hwndLB;
  1037. DWORD_PTR dwTemp;
  1038. PDNODE pNode;
  1039. INT iNumExpanded;
  1040. INT iBottomIndex;
  1041. INT iTopIndex;
  1042. INT iNewTopIndex;
  1043. INT iExpandInView;
  1044. INT iCurrentIndex;
  1045. RECT rc;
  1046. if (GetWindowLong(hWnd, GWL_READLEVEL))
  1047. return;
  1048. hwndLB = GetDlgItem(hWnd, IDCW_TREELISTBOX);
  1049. if (nIndex == -1)
  1050. if ((nIndex = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L)) == LB_ERR)
  1051. return;
  1052. SendMessage(hwndLB, LB_GETTEXT, nIndex, (LPARAM)&dwTemp);
  1053. pNode = (PDNODE)dwTemp;
  1054. // collapse the current contents so we avoid doubling existing "plus" dirs
  1055. if (pNode->wFlags & TF_EXPANDED) {
  1056. if (wParam)
  1057. CollapseLevel(hwndLB, pNode, nIndex);
  1058. else
  1059. return;
  1060. }
  1061. GetTreePath(pNode, szPath);
  1062. StripBackslash(szPath); // remove the slash
  1063. cNodes = 0;
  1064. bCancelTree = FALSE;
  1065. SendMessage(hwndLB, WM_SETREDRAW, FALSE, 0L); // Disable redrawing.
  1066. iCurrentIndex = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
  1067. iNumExpanded = (INT)SendMessage(hwndLB, LB_GETCOUNT, 0, 0L);
  1068. iTopIndex = (INT)SendMessage(hwndLB, LB_GETTOPINDEX, 0, 0L);
  1069. GetClientRect(hwndLB, &rc);
  1070. iBottomIndex = iTopIndex + (rc.bottom+1) / dyFileName;
  1071. if (IsTheDiskReallyThere(hWnd, szPath, FUNC_EXPAND))
  1072. ReadDirLevel(hWnd, pNode, szPath, (WORD)(pNode->nLevels + 1), nIndex,
  1073. (DWORD)(ATTR_DIR | (GetWindowLong(GetParent(hWnd), GWL_ATTRIBS) & ATTR_HS)),
  1074. (BOOL)wParam, szNULL);
  1075. // this is how many will be in view
  1076. iExpandInView = (iBottomIndex - (INT)iCurrentIndex);
  1077. iNumExpanded = (INT)SendMessage(hwndLB, LB_GETCOUNT, 0, 0L) - iNumExpanded;
  1078. if (iNumExpanded >= iExpandInView) {
  1079. iNewTopIndex = min((INT)iCurrentIndex, iTopIndex + iNumExpanded - iExpandInView + 1);
  1080. SendMessage(hwndLB, LB_SETTOPINDEX, (WORD)iNewTopIndex, 0L);
  1081. }
  1082. SendMessage(hwndLB, LB_SETHORIZONTALEXTENT, xTreeMax, 0L);
  1083. SendMessage(hwndLB, WM_SETREDRAW, TRUE, 0L);
  1084. if (iNumExpanded)
  1085. InvalidateRect(hwndLB, NULL, TRUE);
  1086. // Redraw the Status Bar
  1087. UpdateStatus(GetParent(hWnd));
  1088. }
  1089. /*--------------------------------------------------------------------------*/
  1090. /* */
  1091. /* TreeControlWndProc() - */
  1092. /* */
  1093. /*--------------------------------------------------------------------------*/
  1094. /* WndProc for the directory tree control. */
  1095. INT_PTR
  1096. APIENTRY
  1097. TreeControlWndProc(
  1098. register HWND hWnd,
  1099. UINT wMsg,
  1100. WPARAM wParam,
  1101. LPARAM lParam
  1102. )
  1103. {
  1104. WORD iSel;
  1105. INT i, j;
  1106. WPARAM nIndex;
  1107. DWORD dwTemp;
  1108. PDNODE pNode, pNodeNext;
  1109. HWND hwndLB;
  1110. CHAR szPath[MAXPATHLEN];
  1111. STKCHK();
  1112. hwndLB = GetDlgItem(hWnd, IDCW_TREELISTBOX);
  1113. switch (wMsg) {
  1114. case FS_GETDRIVE:
  1115. MSG("TreeControlWndProc", "FS_GETDRIVE");
  1116. return (GetWindowLong(GetParent(hWnd), GWL_TYPE) + 'A');
  1117. case TC_COLLAPSELEVEL:
  1118. MSG("TreeControlWndProc", "TC_COLLAPSELEVEL");
  1119. {
  1120. PDNODE pParentNode;
  1121. if (wParam)
  1122. nIndex = wParam;
  1123. else {
  1124. nIndex = SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
  1125. if (nIndex == LB_ERR)
  1126. break;
  1127. }
  1128. SendMessage(hwndLB, LB_GETTEXT, nIndex, (LPARAM)&pParentNode);
  1129. // short circuit if we are already in this state
  1130. if (!(pParentNode->wFlags & TF_EXPANDED))
  1131. break;
  1132. CollapseLevel(hwndLB, pParentNode, (int)nIndex);
  1133. break;
  1134. }
  1135. case TC_EXPANDLEVEL:
  1136. MSG("TreeControlWndProc", "TC_EXPANDLEVEL");
  1137. ExpandLevel(hWnd, (WORD)wParam, (INT)-1, szPath);
  1138. break;
  1139. case TC_TOGGLELEVEL:
  1140. MSG("TreeControlWndProc", "TC_TOGGLELEVEL");
  1141. // don't do anything while the tree is being built
  1142. if (GetWindowLong(hWnd, GWL_READLEVEL))
  1143. return 1;
  1144. SendMessage(hwndLB, LB_GETTEXT, (WPARAM)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L), (LPARAM)&pNode);
  1145. if (pNode->wFlags & TF_EXPANDED)
  1146. wMsg = TC_COLLAPSELEVEL;
  1147. else
  1148. wMsg = TC_EXPANDLEVEL;
  1149. SendMessage(hWnd, wMsg, FALSE, 0L);
  1150. break;
  1151. case TC_GETDIR:
  1152. // get a full path for a particular dir
  1153. // wParam is the listbox index of path to get
  1154. // lParam LOWORD is PSTR to buffer to fill in
  1155. MSG("TreeControlWndProc", "TC_GETDIR");
  1156. SendMessage(hwndLB, LB_GETTEXT, wParam, (LPARAM)&pNode);
  1157. GetTreePath(pNode, (LPSTR)lParam);
  1158. break;
  1159. case TC_SETDIRECTORY:
  1160. MSG("TreeControlWndProc", "TC_SETDIRECTORY");
  1161. // set the selection in the tree to that for a given path
  1162. {
  1163. i = (INT)FindItemFromPath(hwndLB, (LPSTR)lParam, wParam ? TRUE : FALSE, NULL);
  1164. if (i != -1)
  1165. SendMessage(hwndLB, LB_SETCURSEL, i, 0L);
  1166. break;
  1167. }
  1168. case TC_SETDRIVE:
  1169. #define fFullyExpand LOBYTE(wParam)
  1170. #define fDontSteal HIBYTE(wParam)
  1171. #define szDir (LPSTR)lParam // NULL -> default == window text.
  1172. MSG("TreeControlWndProc", "TC_SETDRIVE");
  1173. {
  1174. RECT rc;
  1175. if (GetWindowLong(hWnd, GWL_READLEVEL))
  1176. break;
  1177. // is the drive/dir specified?
  1178. if (szDir) {
  1179. lstrcpy(szPath, szDir); // yes, use it
  1180. } else {
  1181. SendMessage(GetParent(hWnd), FS_GETDIRECTORY, sizeof(szPath), (LPARAM)szPath); // no, use current
  1182. StripBackslash(szPath);
  1183. }
  1184. AnsiUpperBuff(szPath, 1); // make sure
  1185. SetWindowLong(GetParent(hWnd), GWL_TYPE, 2);
  1186. // resize for new vol label
  1187. GetClientRect(GetParent(hWnd), &rc);
  1188. SendMessage(GetParent(hWnd), WM_SIZE, SIZENOMDICRAP, MAKELONG(rc.right, rc.bottom));
  1189. // ensure the disk is available if the whole dir structure is
  1190. // to be expanded
  1191. if (!fFullyExpand || IsTheDiskReallyThere(hWnd, szPath, FUNC_EXPAND))
  1192. FillTreeListbox(hWnd, szPath, fFullyExpand, fDontSteal);
  1193. // and force the dir half to update with a fake SELCHANGE message
  1194. SendMessage(hWnd, WM_COMMAND, GET_WM_COMMAND_MPS(IDCW_TREELISTBOX, hWnd, LBN_SELCHANGE));
  1195. break;
  1196. #undef fFullyExpand
  1197. #undef fDontSteal
  1198. #undef szDir
  1199. }
  1200. case WM_CHARTOITEM:
  1201. MSG("TreeControlWndProc", "WM_CHARTOITEM");
  1202. {
  1203. WORD w;
  1204. CHAR szB[2];
  1205. INT cItems;
  1206. CHAR ch;
  1207. if (GET_WM_CHARTOITEM_CHAR(wParam, lParam) == '\\') // backslash means the root
  1208. return 0L;
  1209. cItems = (INT)SendMessage(hwndLB, LB_GETCOUNT, 0, 0L);
  1210. i = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
  1211. ch = GET_WM_CHARTOITEM_CHAR(wParam, lParam);
  1212. if (i < 0 || ch <= ' ') // filter all other control chars
  1213. return -2L;
  1214. szB[1] = 0L;
  1215. ch &= 255;
  1216. for (j=1; j < cItems; j++) {
  1217. SendMessage(hwndLB, LB_GETTEXT, (i+j) % cItems, (LPARAM)&pNode);
  1218. szB[0] = pNode->szName[0];
  1219. /* Do it this way to be case insensitive. */
  1220. w = ch;
  1221. if (!lstrcmpi((LPSTR)&w, szB))
  1222. break;
  1223. }
  1224. if (j == cItems)
  1225. return -2L;
  1226. SendMessage(hwndLB, LB_SETTOPINDEX, (i+j) % cItems, 0L);
  1227. return((i+j) % cItems);
  1228. }
  1229. case WM_DESTROY:
  1230. MSG("TreeControlWndProc", "WM_DESTROY");
  1231. if (hwndLB == GetFocus()) {
  1232. HWND hwnd;
  1233. if (hwnd = HasDirWindow(GetParent(hWnd)))
  1234. SetFocus(hwnd);
  1235. else
  1236. SetFocus(HasDrivesWindow(GetParent(hWnd)));
  1237. }
  1238. FreeAllTreeData(hwndLB);
  1239. break;
  1240. case WM_CREATE:
  1241. TRACE(BF_WM_CREATE, "TreeControlWndProc - WM_CREATE");
  1242. // create the owner draw list box for the tree
  1243. {
  1244. HWND hwnd;
  1245. hwnd = CreateWindowEx(0L, szListbox, NULL, WS_TREESTYLE | WS_BORDER,
  1246. 0, 0, 0, 0, hWnd, (HMENU)IDCW_TREELISTBOX, hAppInstance, NULL);
  1247. if (!hwnd)
  1248. return -1L;
  1249. SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont, 0L);
  1250. SetWindowLong(hWnd, GWL_READLEVEL, 0);
  1251. break;
  1252. }
  1253. case WM_DRAWITEM:
  1254. MSG("TreeControlWndProc", "WM_DRAWITEM");
  1255. TCWP_DrawItem((LPDRAWITEMSTRUCT)lParam, hwndLB, hWnd);
  1256. break;
  1257. case WM_FILESYSCHANGE:
  1258. MSG("TreeControlWndProc", "WM_FILESYSCHANGE");
  1259. {
  1260. HWND hwndParent;
  1261. PDNODE pNodePrev;
  1262. PDNODE pNodeT;
  1263. if (!lParam || wParam == FSC_REFRESH)
  1264. break;
  1265. nIndex = FindItemFromPath(hwndLB, (LPSTR)lParam, wParam == FSC_MKDIR, &pNode);
  1266. if (nIndex == 0xFFFF) /* Did we find it? */
  1267. break;
  1268. lstrcpy(szPath, (LPSTR)lParam);
  1269. StripPath(szPath);
  1270. switch (wParam) {
  1271. case FSC_MKDIR:
  1272. // auto expand the branch so they can see the new
  1273. // directory just created
  1274. if (!(pNode->wFlags & TF_EXPANDED) &&
  1275. (nIndex == (WPARAM)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L)))
  1276. SendMessage(hWnd, TC_EXPANDLEVEL, FALSE, 0L);
  1277. // make sure this node isn't already here
  1278. if (FindItemFromPath(hwndLB, (LPSTR)lParam, FALSE, NULL) != 0xFFFF)
  1279. break;
  1280. // Insert it into the tree listbox
  1281. dwTemp = InsertDirectory(hWnd, pNode, (WORD)nIndex, szPath, &pNodeT);
  1282. // Add a plus if necessary
  1283. hwndParent = GetParent(hWnd);
  1284. if (GetWindowLong(hwndParent, GWL_VIEW) & VIEW_PLUSES) {
  1285. lstrcpy(szPath, (LPSTR)lParam);
  1286. ScanDirLevel((PDNODE)pNodeT, szPath, ATTR_DIR |
  1287. (GetWindowLong(hwndParent, GWL_ATTRIBS) & ATTR_HS));
  1288. // Invalidate the window so the plus gets drawn if needed
  1289. if (((PDNODE)pNodeT)->wFlags & TF_HASCHILDREN)
  1290. InvalidateRect(hWnd, NULL, FALSE);
  1291. }
  1292. // if we are inserting before or at the current selection
  1293. // push the current selection down
  1294. nIndex = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
  1295. if ((INT)LOWORD(dwTemp) <= nIndex) {
  1296. SendMessage(hwndLB, LB_SETCURSEL, nIndex + 1, 0L);
  1297. }
  1298. break;
  1299. case FSC_RMDIR:
  1300. if (nIndex == 0) /* NEVER delete the Root Dir! */
  1301. break;
  1302. if (pNode->wFlags & TF_LASTLEVELENTRY) {
  1303. // We are deleting the last subdirectory.
  1304. // If there are previous sibling directories, mark one
  1305. // as the last, else mark the parent as empty and unexpanded.
  1306. // It is necessary to do these checks if this bit
  1307. // is set, since if it isn't, there is another sibling
  1308. // with TF_LASTLEVELENTRY set, and so the parent is nonempty.
  1309. //
  1310. // Find the previous entry which has a level not deeper than
  1311. // the level of that being deleted.
  1312. i = (int)nIndex;
  1313. do {
  1314. SendMessage(hwndLB, LB_GETTEXT, --i, (LPARAM)&pNodePrev);
  1315. } while (pNodePrev->nLevels > pNode->nLevels);
  1316. if (pNodePrev->nLevels == pNode->nLevels) {
  1317. // The previous directory is a sibling... it becomes
  1318. // the new last level entry.
  1319. pNodePrev->wFlags |= TF_LASTLEVELENTRY;
  1320. } else {
  1321. // In order to find this entry, the parent must have
  1322. // been expanded, so if the parent of the deleted dir
  1323. // has no listbox entries under it, it may be assumed that
  1324. // the directory has no children.
  1325. pNodePrev->wFlags &= ~(TF_HASCHILDREN | TF_EXPANDED);
  1326. }
  1327. }
  1328. // Are we deleting the current selection?
  1329. // if so we move the selection to the item above the current.
  1330. // this should work in all cases because you can't delete
  1331. // the root.
  1332. if ((WPARAM)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L) == nIndex) {
  1333. SendMessage(hwndLB, LB_SETCURSEL, nIndex - 1, 0L);
  1334. SendMessage(hWnd, WM_COMMAND, GET_WM_COMMAND_MPS(0, 0, LBN_SELCHANGE));
  1335. }
  1336. SendMessage(hWnd, TC_COLLAPSELEVEL, nIndex, 0L);
  1337. SendMessage(hwndLB, LB_DELETESTRING, nIndex, 0L);
  1338. LocalFree((HANDLE)pNode);
  1339. break;
  1340. }
  1341. break;
  1342. }
  1343. case WM_COMMAND:
  1344. {
  1345. WORD id;
  1346. id = GET_WM_COMMAND_ID(wParam, lParam);
  1347. switch (GET_WM_COMMAND_CMD(wParam, lParam)) {
  1348. case LBN_SELCHANGE:
  1349. MSG("TreeControlWndProc", "LBN_SELCHANGE");
  1350. {
  1351. HWND hwndParent;
  1352. HWND hwndDir;
  1353. INT CurSel;
  1354. hwndParent = GetParent(hWnd);
  1355. CurSel = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
  1356. SendMessage(hWnd, TC_GETDIR, CurSel,(LPARAM)szPath);
  1357. AddBackslash(szPath);
  1358. SendMessage(hwndParent, FS_GETFILESPEC, sizeof(szPath) - lstrlen(szPath), (LPARAM)szPath+lstrlen(szPath));
  1359. if (hwndDir = HasDirWindow(hwndParent)) {
  1360. // update the dir window
  1361. id = CD_PATH;
  1362. // don't allow abort on first or last directories
  1363. if (CurSel > 0 &&
  1364. CurSel != ((INT)SendMessage(hwndLB, LB_GETCOUNT, 0, 0L) - 1)) {
  1365. id = CD_PATH | CD_ALLOWABORT;
  1366. }
  1367. SendMessage(hwndDir, FS_CHANGEDISPLAY, id, (LPARAM)szPath);
  1368. } else {
  1369. SetMDIWindowText(hwndParent, szPath);
  1370. }
  1371. UpdateStatus(hwndParent);
  1372. break;
  1373. }
  1374. case LBN_DBLCLK:
  1375. MSG("TreeControlWndProc", "LBN_DBLCLK");
  1376. SendMessage(hwndFrame, WM_COMMAND, GET_WM_COMMAND_MPS(IDM_OPEN, 0, 0));
  1377. break;
  1378. case LBN_SETFOCUS:
  1379. MSG("TreeControlWndProc", "LBN_SETFOCUS");
  1380. SetWindowLongPtr(GetParent(hWnd), GWLP_LASTFOCUS, (LPARAM)GET_WM_COMMAND_HWND(wParam, lParam));
  1381. UpdateSelection(GET_WM_COMMAND_HWND(wParam, lParam));
  1382. UpdateStatus(GetParent(hWnd)); // update the status bar
  1383. break;
  1384. case LBN_KILLFOCUS:
  1385. MSG("TreeControlWndProc", "LBN_KILLFOCUS");
  1386. SetWindowLongPtr(GetParent(hWnd), GWLP_LASTFOCUS, 0);
  1387. UpdateSelection(GET_WM_COMMAND_HWND(wParam, lParam));
  1388. SetWindowLongPtr(GetParent(hWnd), GWLP_LASTFOCUS, (LPARAM)GET_WM_COMMAND_HWND(wParam, lParam));
  1389. break;
  1390. }
  1391. }
  1392. break;
  1393. case WM_LBTRACKPOINT:
  1394. MSG("TreeControlWndProc", "WM_LBTRACKPOINT");
  1395. // wParam is the listbox index that we are over
  1396. // lParam is the mouse point
  1397. /* Return 0 to do nothing, 1 to abort everything, or 2 to abort just dblclicks. */
  1398. {
  1399. HDC hdc;
  1400. INT dx;
  1401. INT xNode;
  1402. MSG msg;
  1403. RECT rc;
  1404. HFONT hOld;
  1405. POINT pt;
  1406. DRAGOBJECTDATA dodata;
  1407. /* Someone clicked somewhere in the listbox. */
  1408. // don't do anything while the tree is being built
  1409. if (GetWindowLong(hWnd, GWL_READLEVEL))
  1410. return 1;
  1411. /* Get the node they clicked on. */
  1412. SendMessage(hwndLB, LB_GETTEXT, wParam, (LPARAM)&pNode);
  1413. lstrcpy(szPath, pNode->szName);
  1414. if ((wTextAttribs & TA_LOWERCASE) && !(pNode->wFlags & TF_LFN))
  1415. AnsiLower(szPath);
  1416. // if (pNode->wFlags | TF_DISABLED)
  1417. // return 2L;
  1418. // too FAR to the left?
  1419. i = LOWORD(lParam);
  1420. xNode = pNode->nLevels * dxText * 2;
  1421. if (i < xNode)
  1422. return 2; // yes, get out now
  1423. // too FAR to the right?
  1424. hdc = GetDC(hwndLB);
  1425. hOld = SelectObject(hdc, hFont);
  1426. MGetTextExtent(hdc, szPath, lstrlen(szPath), &dx, NULL);
  1427. dx += (dyBorderx2*2);
  1428. if (hOld)
  1429. SelectObject(hdc, hOld);
  1430. ReleaseDC(hwndLB, hdc);
  1431. if (i > xNode + dxFolder + dx + 4 * dyBorderx2)
  1432. return 2; // yes
  1433. // Emulate a SELCHANGE notification and notify our parent
  1434. SendMessage(hwndLB, LB_SETCURSEL, wParam, 0L);
  1435. SendMessage(hWnd, WM_COMMAND, GET_WM_COMMAND_MPS(0, hwndLB, LBN_SELCHANGE));
  1436. // make sure mouse still down
  1437. if (!(GetKeyState(VK_LBUTTON) & 0x8000))
  1438. return 1;
  1439. MPOINT2POINT(MAKEMPOINT(lParam), pt);
  1440. ClientToScreen(hwndLB, (LPPOINT)&pt);
  1441. ScreenToClient(hWnd, (LPPOINT)&pt);
  1442. SetRect(&rc, pt.x - dxClickRect, pt.y - dyClickRect,
  1443. pt.x + dxClickRect, pt.y + dyClickRect);
  1444. SetCapture(hWnd);
  1445. while (GetMessage(&msg, NULL, 0, 0)) {
  1446. DispatchMessage(&msg);
  1447. if (msg.message == WM_LBUTTONUP)
  1448. break;
  1449. MPOINT2POINT(MAKEMPOINT(msg.lParam), pt);
  1450. if (GetCapture() != hWnd) {
  1451. msg.message = WM_LBUTTONUP;
  1452. break;
  1453. }
  1454. if ((msg.message == WM_MOUSEMOVE) && !(PtInRect(&rc, pt)))
  1455. break;
  1456. }
  1457. ReleaseCapture();
  1458. /* Did the guy NOT drag anything? */
  1459. if (msg.message == WM_LBUTTONUP)
  1460. return 1;
  1461. /* Enter Danger Mouse's BatCave. */
  1462. SendMessage(GetParent(hWnd), FS_GETDIRECTORY, sizeof(szPath), (LPARAM)szPath);
  1463. StripBackslash(szPath);
  1464. hwndDragging = hwndLB;
  1465. iCurDrag = SINGLECOPYCURSOR;
  1466. dodata.pch = szPath;
  1467. dodata.hMemGlobal = 0;
  1468. DragObject(hwndMDIClient, hWnd, (UINT)DOF_DIRECTORY, (DWORD)(ULONG_PTR)&dodata, LoadCursor(hAppInstance, MAKEINTRESOURCE(iCurDrag)));
  1469. hwndDragging = NULL;
  1470. fShowSourceBitmaps = TRUE;
  1471. InvalidateRect(hwndLB, NULL, FALSE);
  1472. return 2;
  1473. }
  1474. case WM_DRAGSELECT:
  1475. MSG("TreeControlWndProc", "WM_DRAGSELECT");
  1476. /* WM_DRAGSELECT is sent whenever a new window returns TRUE to a
  1477. * QUERYDROPOBJECT.
  1478. */
  1479. iSelHilite = LOWORD(((LPDROPSTRUCT)lParam)->dwControlData);
  1480. RectTreeItem(hwndLB, iSelHilite, (BOOL)wParam);
  1481. break;
  1482. case WM_DRAGMOVE:
  1483. MSG("TreeControlWndProc", "WM_DRAGMOVE");
  1484. /* WM_DRAGMOVE is sent when two consequetive TRUE QUERYDROPOBJECT
  1485. * messages come from the same window.
  1486. */
  1487. /* Get the subitem we are over. */
  1488. iSel = LOWORD(((LPDROPSTRUCT)lParam)->dwControlData);
  1489. /* Is it a new one? */
  1490. if (iSel == (WORD)iSelHilite)
  1491. break;
  1492. /* Yup, un-select the old item. */
  1493. RectTreeItem(hwndLB, iSelHilite, FALSE);
  1494. /* Select the new one. */
  1495. iSelHilite = iSel;
  1496. RectTreeItem(hwndLB, iSel, TRUE);
  1497. break;
  1498. case WM_DRAGLOOP:
  1499. MSG("TreeControlWndProc", "WM_DRAGLOOP");
  1500. // wParam TRUE on dropable target
  1501. // FALSE not dropable target
  1502. // lParam lpds
  1503. {
  1504. BOOL bCopy;
  1505. #define lpds ((LPDROPSTRUCT)lParam)
  1506. /* Are we over a drop-able sink? */
  1507. if (wParam) {
  1508. if (GetKeyState(VK_CONTROL) < 0) // CTRL
  1509. bCopy = TRUE;
  1510. else if (GetKeyState(VK_MENU)<0 || GetKeyState(VK_SHIFT)<0) // ALT || SHIFT
  1511. bCopy = FALSE;
  1512. else
  1513. bCopy = (GetDrive(lpds->hwndSink, lpds->ptDrop) != GetDrive(lpds->hwndSource, lpds->ptDrop));
  1514. } else {
  1515. bCopy = TRUE;
  1516. }
  1517. if (bCopy != fShowSourceBitmaps) {
  1518. RECT rc;
  1519. fShowSourceBitmaps = bCopy;
  1520. iSel = (WORD)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
  1521. if (!(BOOL)SendMessage(hwndLB, LB_GETITEMRECT, iSel, (LPARAM)&rc))
  1522. break;
  1523. InvalidateRect(hwndLB, &rc, FALSE);
  1524. UpdateWindow(hwndLB);
  1525. // hack, set the cursor to match the move/copy state
  1526. if (wParam)
  1527. SetCursor(GetMoveCopyCursor());
  1528. }
  1529. break;
  1530. }
  1531. case WM_QUERYDROPOBJECT:
  1532. MSG("TreeControlWndProc", "WM_QUERYDROPOBJECT");
  1533. // wParam TRUE on NC area
  1534. // FALSE on client area
  1535. // lParam lpds
  1536. // Do nothing
  1537. return(FALSE);
  1538. #define lpds ((LPDROPSTRUCT)lParam)
  1539. /* Check for valid format. */
  1540. switch (lpds->wFmt) {
  1541. case DOF_EXECUTABLE:
  1542. case DOF_DOCUMENT:
  1543. case DOF_DIRECTORY:
  1544. case DOF_MULTIPLE:
  1545. if (fShowSourceBitmaps)
  1546. i = iCurDrag | 1; // copy
  1547. else
  1548. i = iCurDrag & 0xFFFE;
  1549. break;
  1550. default:
  1551. return FALSE;
  1552. }
  1553. /* Must be dropping on the listbox client area. */
  1554. if (lpds->hwndSink != hwndLB)
  1555. return FALSE;
  1556. if (LOWORD(lpds->dwControlData) == 0xFFFF)
  1557. return FALSE;
  1558. return (INT_PTR)GetMoveCopyCursor();
  1559. case WM_DROPOBJECT: // tree being dropped on do your thing
  1560. #define lpds ((LPDROPSTRUCT)lParam) // BUG: WM_DROPOBJECT structure packing!
  1561. // Do nothing
  1562. return(TRUE);
  1563. MSG("TreeControlWndProc", "WM_DROPOBJECT");
  1564. // dir (search) drop on tree:
  1565. // HIWORD(dwData) 0
  1566. // LOWORD(dwData) LPSTR to files being dragged
  1567. //
  1568. // tree drop on tree:
  1569. // HIWORD(dwData) index of source drag
  1570. // LOWORD(dwData) LPSTR to path
  1571. {
  1572. LPSTR pFrom;
  1573. nIndex = LOWORD(lpds->dwControlData);
  1574. pFrom = (LPSTR)(((LPDRAGOBJECTDATA)(lpds->dwData))->pch);
  1575. // Get the destination
  1576. SendMessage(hWnd, TC_GETDIR, nIndex, (LPARAM)szPath);
  1577. CheckEscapes(szPath);
  1578. // if source and dest are the same make this a NOP
  1579. if (!lstrcmpi(szPath, pFrom))
  1580. return TRUE;
  1581. AddBackslash(szPath);
  1582. lstrcat(szPath, szStarDotStar);
  1583. DMMoveCopyHelper(pFrom, szPath, fShowSourceBitmaps);
  1584. RectTreeItem(hwndLB, (int)nIndex, FALSE);
  1585. }
  1586. return TRUE;
  1587. #undef lpds
  1588. case WM_MEASUREITEM:
  1589. MSG("TreeControlWndProc", "WM_MEASUREITEM");
  1590. #define pLBMItem ((LPMEASUREITEMSTRUCT)lParam)
  1591. pLBMItem->itemHeight = (WORD)dyFileName;
  1592. break;
  1593. case WM_VKEYTOITEM:
  1594. MSG("TreeControlWndProc", "WM_VKEYTOITEM");
  1595. if (wParam == VK_ESCAPE) {
  1596. bCancelTree = TRUE;
  1597. return -2L;
  1598. }
  1599. i = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
  1600. if (i < 0)
  1601. return -2L;
  1602. j = 1;
  1603. SendMessage(hwndLB, LB_GETTEXT, i, (LPARAM)&pNode);
  1604. switch (GET_WM_VKEYTOITEM_ITEM(wParam, lParam)) {
  1605. case VK_LEFT:
  1606. while (SendMessage(hwndLB, LB_GETTEXT, --i, (LPARAM)&pNodeNext) != LB_ERR) {
  1607. if (pNode == pNode->pParent)
  1608. return(i);
  1609. }
  1610. goto SameSelection;
  1611. case VK_RIGHT:
  1612. if ((SendMessage(hwndLB, LB_GETTEXT, i+1, (LPARAM)&pNodeNext) == LB_ERR)
  1613. || (pNodeNext->pParent != pNode)) {
  1614. goto SameSelection;
  1615. }
  1616. return(i+1);
  1617. case VK_UP:
  1618. j = -1;
  1619. /** FALL THRU ***/
  1620. case VK_DOWN:
  1621. /* If the control key is not down, use default behavior. */
  1622. if (GetKeyState(VK_CONTROL) >= 0)
  1623. return(-1L);
  1624. while (SendMessage(hwndLB, LB_GETTEXT, i += j, (LPARAM)&pNodeNext) != LB_ERR) {
  1625. if (pNodeNext->pParent == pNode->pParent)
  1626. return(i);
  1627. }
  1628. SameSelection:
  1629. MessageBeep(0);
  1630. return(-2L);
  1631. case VK_F6: // like excel
  1632. case VK_TAB:
  1633. {
  1634. HWND hwndDir, hwndDrives;
  1635. BOOL bDir;
  1636. GetTreeWindows(GetParent(hWnd), NULL, &hwndDir, &hwndDrives);
  1637. // Check to see if we can change to the directory window
  1638. if (hwndDir) {
  1639. HWND L_hwndLB; /* Local scope ONLY */
  1640. L_hwndLB = GetDlgItem (hwndDir,IDCW_LISTBOX);
  1641. if (L_hwndLB) {
  1642. SendMessage (L_hwndLB,LB_GETTEXT,0, (LPARAM) &pNode);
  1643. bDir = pNode ? TRUE : FALSE;
  1644. }
  1645. }
  1646. if (GetKeyState(VK_SHIFT) < 0)
  1647. SetFocus(hwndDrives);
  1648. else
  1649. if (bDir)
  1650. SetFocus (hwndDir);
  1651. else
  1652. SetFocus (hwndDrives);
  1653. return -2L; // I dealt with this!
  1654. }
  1655. case VK_BACK:
  1656. {
  1657. BYTE nStartLevel;
  1658. if (i <= 0)
  1659. return -2L; // root case
  1660. nStartLevel = pNode->nLevels;
  1661. do {
  1662. SendMessage(hwndLB, LB_GETTEXT, --i, (LPARAM)&pNodeNext);
  1663. } while (i > 0 && pNodeNext->nLevels >= nStartLevel);
  1664. return i;
  1665. }
  1666. default:
  1667. if (GetKeyState(VK_CONTROL) < 0)
  1668. return SendMessage(GetDlgItem(GetParent(hWnd), IDCW_DRIVES), wMsg, wParam, lParam);
  1669. return -1L;
  1670. }
  1671. break;
  1672. case WM_SETFOCUS:
  1673. case WM_LBUTTONDOWN:
  1674. MSG("TreeControlWndProc", "WM_LBUTTONDOWN");
  1675. SetFocus(hwndLB);
  1676. break;
  1677. case WM_SIZE:
  1678. MSG("TreeControlWndProc", "WM_SIZE");
  1679. if (!IsIconic(GetParent(hWnd))) {
  1680. INT iMax;
  1681. MoveWindow(hwndLB, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
  1682. iMax = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
  1683. if (iMax >= 0) {
  1684. RECT rc;
  1685. INT top, bottom;
  1686. GetClientRect(hwndLB, &rc);
  1687. top = (INT)SendMessage(hwndLB, LB_GETTOPINDEX, 0, 0L);
  1688. bottom = top + rc.bottom / dyFileName;
  1689. if (iMax < top || iMax > bottom)
  1690. SendMessage(hwndLB, LB_SETTOPINDEX, iMax - ((bottom - top) / 2), 0L);
  1691. }
  1692. }
  1693. break;
  1694. default:
  1695. DEFMSG("TreeControlWndProc", (WORD)wMsg);
  1696. return DefWindowProc(hWnd, wMsg, wParam, lParam);
  1697. }
  1698. return 0L;
  1699. }