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.

1367 lines
39 KiB

  1. #include "ctlspriv.h"
  2. #include "treeview.h"
  3. BOOL TV_EnsureVisible(PTREE pTree, TREEITEM * hItem);
  4. // ----------------------------------------------------------------------------
  5. //
  6. // Updates the iShownIndex for every item below (in list order) a given item
  7. //
  8. // ----------------------------------------------------------------------------
  9. #define TVITEM_HIDDEN -1
  10. int TV_UpdateShownIndexes(PTREE pTree, HTREEITEM hWalk)
  11. {
  12. WORD iShownIndex;
  13. if (hWalk == pTree->hRoot) {
  14. hWalk = pTree->hRoot->hKids;
  15. if (hWalk) {
  16. hWalk->iShownIndex = 0;
  17. } else {
  18. return TVITEM_HIDDEN;
  19. }
  20. }
  21. iShownIndex = hWalk->iShownIndex + hWalk->iIntegral;
  22. if (iShownIndex <= 0)
  23. {
  24. return(TVITEM_HIDDEN);
  25. }
  26. while ((hWalk = TV_GetNextVisItem(hWalk)) != NULL) {
  27. hWalk->iShownIndex = iShownIndex;
  28. iShownIndex += (WORD) hWalk->iIntegral;
  29. }
  30. //#ifdef DEBUG
  31. // TraceMsg(TF_TREEVIEW, "tv: updated show indexes (now %d items)", (int)iShownIndex);
  32. //#endif
  33. return (int)iShownIndex;
  34. }
  35. //
  36. // in:
  37. // hItem expanded node to count decendants of
  38. //
  39. // returns:
  40. // total number of expanded descendants below the given item.
  41. //
  42. UINT TV_CountVisibleDescendants(HTREEITEM hItem)
  43. {
  44. UINT cnt;
  45. for (cnt = 0, hItem = hItem->hKids; hItem; hItem = hItem->hNext)
  46. {
  47. cnt += hItem->iIntegral;
  48. if (hItem->hKids && (hItem->state & TVIS_EXPANDED))
  49. cnt += TV_CountVisibleDescendants(hItem);
  50. }
  51. return cnt;
  52. }
  53. // scrolls nItems in the direction of fDown starting from iTopShownIndex
  54. void TV_ScrollItems(PTREE pTree, int nItems, int iTopShownIndex, BOOL fDown)
  55. {
  56. RECT rc;
  57. SMOOTHSCROLLINFO si;
  58. rc.left = 0;
  59. rc.top = (iTopShownIndex+1) * pTree->cyItem;
  60. rc.right = pTree->cxWnd;
  61. rc.bottom = pTree->cyWnd;
  62. si.cbSize = sizeof(si);
  63. si.fMask = SSIF_MINSCROLL | SSIF_MAXSCROLLTIME;
  64. si.hwnd = pTree->ci.hwnd;
  65. si.dx = 0;
  66. si.dy = ((fDown)?1:-1) * nItems * pTree->cyItem;
  67. si.lprcSrc = &rc;
  68. si.lprcClip = &rc;
  69. si.hrgnUpdate = NULL;
  70. si.lprcUpdate = NULL;
  71. si.fuScroll = SW_ERASE|SW_INVALIDATE;
  72. si.uMaxScrollTime = pTree->uMaxScrollTime;
  73. si.cxMinScroll = 1;
  74. si.cyMinScroll = 1;
  75. si.pfnScrollProc = NULL;
  76. SmoothScrollWindow(&si);
  77. TV_UpdateToolTip(pTree);
  78. }
  79. //
  80. // If fRedrawParent is FALSE, then the return value is garbage.
  81. // If fRedrawParent is TRUE, then returns the number of children scrolled.
  82. //
  83. // Does not update iShownIndex for any items.
  84. //
  85. UINT TV_ScrollBelow(PTREE pTree, HTREEITEM hItem, BOOL fRedrawParent, BOOL fDown)
  86. {
  87. int iTop;
  88. UINT cnt;
  89. // Do nothing if the item is not visible
  90. if (!ITEM_VISIBLE(hItem))
  91. return 0;
  92. cnt = hItem->iIntegral; // default return val
  93. if (pTree->fRedraw) {
  94. UINT cVisDesc;
  95. BOOL fEffect;
  96. // iTop is the top edge (client coordinates) of the bottom integral
  97. // cell of the item that just got expanded/contracted.
  98. // (Confused yet? I sure am.)
  99. iTop = hItem->iShownIndex - pTree->hTop->iShownIndex + hItem->iIntegral - 1;
  100. cVisDesc = TV_CountVisibleDescendants(hItem);
  101. // See if the item being expanded/contracted has any effect on the
  102. // screen. If not, then don't TV_ScrollItems or we will end up
  103. // double-counting them when we do post-scroll adjustment.
  104. if (fDown)
  105. {
  106. // When scrolling down, we have an effect if the item that just
  107. // got expanded was below the top of the screen
  108. fEffect = iTop >= 0;
  109. }
  110. else
  111. {
  112. // When scrolling up, we have an effect if any of the items
  113. // that just got collapsed out were below the top of the screen
  114. fEffect = (int)(iTop + cVisDesc) >= 0;
  115. }
  116. if (fEffect)
  117. {
  118. TV_ScrollItems(pTree, cVisDesc, iTop, fDown);
  119. }
  120. TV_InvalidateItem(pTree, hItem, TRUE);
  121. if (fRedrawParent)
  122. cnt = cVisDesc;
  123. } else {
  124. if (fRedrawParent)
  125. cnt = TV_CountVisibleDescendants(hItem);
  126. }
  127. return(cnt);
  128. }
  129. // The FakeCustomDraw functions are used when you want the customdraw client
  130. // to set up a HDC so you can do stuff like GetTextExtent.
  131. //
  132. // Usage:
  133. //
  134. // TVFAKEDRAW tvfd;
  135. // TreeView_BeginFakeCustomDraw(pTree, &tvfd);
  136. // for each item you care about {
  137. // TreeView_BeginFakeItemDraw(&tvfd, hitem);
  138. // <party on the HDC in tvfd.nmcd.nmcd.hdc>
  139. // TreeView_EndFakeItemDraw(&tvfd);
  140. // }
  141. // TreeView_EndFakeCustomDraw(&tvfd);
  142. //
  143. void TreeView_BeginFakeCustomDraw(PTREE pTree, PTVFAKEDRAW ptvfd)
  144. {
  145. ptvfd->nmcd.nmcd.hdc = GetDC(pTree->ci.hwnd);
  146. ptvfd->nmcd.nmcd.uItemState = 0;
  147. ptvfd->nmcd.nmcd.dwItemSpec = 0;
  148. ptvfd->nmcd.nmcd.lItemlParam = 0;
  149. ptvfd->hfontPrev = (HFONT)GetCurrentObject(ptvfd->nmcd.nmcd.hdc, OBJ_FONT);
  150. //
  151. // Since we aren't actually painting anything, we pass an empty
  152. // paint rectangle. Gosh, I hope no app faults when it sees an
  153. // empty paint rectangle.
  154. //
  155. SetRectEmpty(&ptvfd->nmcd.nmcd.rc);
  156. ptvfd->pTree = pTree;
  157. ptvfd->dwCustomPrev = pTree->ci.dwCustom;
  158. pTree->ci.dwCustom = CIFakeCustomDrawNotify(&pTree->ci, CDDS_PREPAINT, &ptvfd->nmcd.nmcd);
  159. }
  160. DWORD TreeView_BeginFakeItemDraw(PTVFAKEDRAW ptvfd, HTREEITEM hitem)
  161. {
  162. PTREE pTree = ptvfd->pTree;
  163. // Note that if the client says CDRF_SKIPDEFAULT (i.e., is owner-draw)
  164. // we measure the item anyway, because that's what IE4 did.
  165. ptvfd->nmcd.nmcd.dwItemSpec = (DWORD_PTR)hitem;
  166. ptvfd->nmcd.nmcd.lItemlParam = hitem->lParam;
  167. if (hitem->state & TVIS_BOLD) {
  168. SelectFont(ptvfd->nmcd.nmcd.hdc, pTree->hFontBold);
  169. } else {
  170. SelectFont(ptvfd->nmcd.nmcd.hdc, pTree->hFont);
  171. }
  172. if (!(pTree->ci.dwCustom & CDRF_SKIPDEFAULT)) {
  173. // Font should not depend on colors or flags since those change
  174. // dynamically but we cache the width info forever. So we don't
  175. // need to set up uItemState.
  176. ptvfd->nmcd.clrText = pTree->clrText;
  177. ptvfd->nmcd.clrTextBk = pTree->clrBk;
  178. ptvfd->nmcd.iLevel = hitem->iLevel;
  179. ptvfd->dwCustomItem = CIFakeCustomDrawNotify(&pTree->ci, CDDS_ITEMPREPAINT, &ptvfd->nmcd.nmcd);
  180. } else {
  181. ptvfd->dwCustomItem = CDRF_DODEFAULT;
  182. }
  183. return ptvfd->dwCustomItem;
  184. }
  185. void TreeView_EndFakeItemDraw(PTVFAKEDRAW ptvfd)
  186. {
  187. PTREE pTree = ptvfd->pTree;
  188. if (!(ptvfd->dwCustomItem & CDRF_SKIPDEFAULT) &&
  189. (ptvfd->dwCustomItem & CDRF_NOTIFYPOSTPAINT)) {
  190. CIFakeCustomDrawNotify(&pTree->ci, CDDS_ITEMPOSTPAINT, &ptvfd->nmcd.nmcd);
  191. }
  192. }
  193. void TreeView_EndFakeCustomDraw(PTVFAKEDRAW ptvfd)
  194. {
  195. PTREE pTree = ptvfd->pTree;
  196. // notify parent afterwards if they want us to
  197. if (!(pTree->ci.dwCustom & CDRF_SKIPDEFAULT) &&
  198. pTree->ci.dwCustom & CDRF_NOTIFYPOSTPAINT) {
  199. CIFakeCustomDrawNotify(&pTree->ci, CDDS_POSTPAINT, &ptvfd->nmcd.nmcd);
  200. }
  201. // Restore previous state
  202. pTree->ci.dwCustom = ptvfd->dwCustomPrev;
  203. SelectObject(ptvfd->nmcd.nmcd.hdc, ptvfd->hfontPrev);
  204. ReleaseDC(pTree->ci.hwnd, ptvfd->nmcd.nmcd.hdc);
  205. }
  206. // ----------------------------------------------------------------------------
  207. //
  208. // Returns the width of the widest shown item in the tree
  209. //
  210. // ----------------------------------------------------------------------------
  211. UINT TV_RecomputeMaxWidth(PTREE pTree)
  212. {
  213. if (!(pTree->ci.style & TVS_NOSCROLL)) {
  214. HTREEITEM hItem;
  215. WORD wMax = 0;
  216. // REVIEW: this might not be the most efficient traversal of the tree
  217. for (hItem = pTree->hRoot->hKids; hItem; hItem = TV_GetNextVisItem(hItem))
  218. {
  219. if (wMax < FULL_WIDTH(pTree, hItem))
  220. wMax = FULL_WIDTH(pTree, hItem);
  221. }
  222. return((UINT)wMax);
  223. } else {
  224. return pTree->cxWnd;
  225. }
  226. }
  227. // ----------------------------------------------------------------------------
  228. //
  229. // Returns the horizontal text extent of the given item's text
  230. //
  231. // ----------------------------------------------------------------------------
  232. WORD TV_GetItemTextWidth(HDC hdc, PTREE pTree, HTREEITEM hItem)
  233. {
  234. TVITEMEX sItem;
  235. TCHAR szTemp[MAX_PATH];
  236. SIZE size = {0,0};
  237. sItem.pszText = szTemp;
  238. sItem.cchTextMax = ARRAYSIZE(szTemp);
  239. TV_GetItem(pTree, hItem, TVIF_TEXT, &sItem);
  240. GetTextExtentPoint(hdc, sItem.pszText, lstrlen(sItem.pszText), &size);
  241. return (WORD)(size.cx + (g_cxLabelMargin * 2) + pTree->cxBorder);
  242. }
  243. // ----------------------------------------------------------------------------
  244. //
  245. // Compute the text extent and the full width (indent, image, and text) of
  246. // the given item.
  247. //
  248. // If there is a HDC, then we assume that the HDC has been set up with
  249. // the proper attributes (specifically, the font). If there is no HDC,
  250. // then we will set one up, measure the text, then tear it down.
  251. // If you will be measuring more than one item, it is recommended that
  252. // the caller set up the HDC and keep re-using it, because creating,
  253. // initializing, then destroy the HDC is rather slow.
  254. //
  255. // ----------------------------------------------------------------------------
  256. void TV_ComputeItemWidth(PTREE pTree, HTREEITEM hItem, HDC hdc)
  257. {
  258. TVFAKEDRAW tvfd; // in case client uses customdraw
  259. int iOldWidth = hItem->iWidth;
  260. if (hdc == NULL) {
  261. TreeView_BeginFakeCustomDraw(pTree, &tvfd);
  262. TreeView_BeginFakeItemDraw(&tvfd, hItem);
  263. }
  264. else
  265. {
  266. tvfd.nmcd.nmcd.hdc = hdc;
  267. }
  268. hItem->iWidth = TV_GetItemTextWidth(tvfd.nmcd.nmcd.hdc, pTree, hItem);
  269. if (!(pTree->ci.style & TVS_NOSCROLL) && iOldWidth != hItem->iWidth)
  270. if (pTree->cxMax < FULL_WIDTH(pTree, hItem)) {
  271. PostMessage(pTree->ci.hwnd, TVMP_CALCSCROLLBARS, 0, 0);
  272. pTree->cxMax = FULL_WIDTH(pTree, hItem);
  273. }
  274. if (hdc == NULL)
  275. {
  276. TreeView_EndFakeItemDraw(&tvfd);
  277. TreeView_EndFakeCustomDraw(&tvfd);
  278. }
  279. }
  280. // ----------------------------------------------------------------------------
  281. //
  282. // Returns TRUE if the item is expanded, FALSE otherwise
  283. //
  284. // ----------------------------------------------------------------------------
  285. BOOL TV_IsShowing(HTREEITEM hItem)
  286. {
  287. for (hItem = hItem->hParent; hItem; hItem = hItem->hParent)
  288. if (!(hItem->state & TVIS_EXPANDED))
  289. return FALSE;
  290. return TRUE;
  291. }
  292. // ----------------------------------------------------------------------------
  293. //
  294. // If the added item is showing, update the shown (expanded) count, the max
  295. // item width -- then recompute the scroll bars.
  296. //
  297. // sets cxMax, cShowing
  298. //
  299. // ----------------------------------------------------------------------------
  300. BOOL TV_ScrollBarsAfterAdd(PTREE pTree, HTREEITEM hItem)
  301. {
  302. HTREEITEM hPrev;
  303. if (!TV_IsShowing(hItem))
  304. {
  305. // item isn't visible -- set index to NOTVISIBLE and return
  306. hItem->iShownIndex = (WORD)-1;
  307. return FALSE;
  308. }
  309. hPrev = TV_GetPrevVisItem(hItem);
  310. // increment every shown index after newly added item
  311. hItem->iShownIndex = (hPrev) ? hPrev->iShownIndex + hPrev->iIntegral : 0;
  312. TV_UpdateShownIndexes(pTree, hItem);
  313. pTree->cShowing += hItem->iIntegral;
  314. TV_ComputeItemWidth(pTree, hItem, NULL);
  315. TV_CalcScrollBars(pTree);
  316. return(TRUE);
  317. }
  318. // ----------------------------------------------------------------------------
  319. //
  320. // If the removed item was showing, update the shown (expanded) count, the max
  321. // item width -- then recompute the scroll bars.
  322. //
  323. // sets cxMax, cShowing
  324. //
  325. // ----------------------------------------------------------------------------
  326. BOOL TV_ScrollBarsAfterRemove(PTREE pTree, HTREEITEM hItem)
  327. {
  328. HTREEITEM hWalk;
  329. if (!ITEM_VISIBLE(hItem))
  330. return FALSE;
  331. // decrement every shown index after removed item
  332. hItem->iShownIndex = (WORD)-1;
  333. hWalk = TV_GetNextVisItem(hItem);
  334. if (hWalk)
  335. {
  336. hWalk->iShownIndex -= (WORD) hItem->iIntegral;
  337. TV_UpdateShownIndexes(pTree, hWalk);
  338. // If we delete the top item, the tree scrolls to the end, so ...
  339. if (pTree->hTop == hItem)
  340. {
  341. TV_SetTopItem(pTree, hWalk->iShownIndex);
  342. }
  343. }
  344. pTree->cShowing -= hItem->iIntegral;
  345. if (pTree->fRedraw)
  346. {
  347. if (!hItem->iWidth)
  348. TV_ComputeItemWidth(pTree, hItem, NULL);
  349. if (!(pTree->ci.style & TVS_NOSCROLL))
  350. if (pTree->cxMax == FULL_WIDTH(pTree, hItem))
  351. pTree->cxMax = (WORD) TV_RecomputeMaxWidth(pTree);
  352. TV_CalcScrollBars(pTree);
  353. }
  354. return TRUE;
  355. }
  356. // ----------------------------------------------------------------------------
  357. //
  358. // Common worker function for
  359. // TV_ScrollBarsAfterExpand and TV_ScrollBarsAfterCollapse, since they
  360. // are completely identical save for two lines of code.
  361. //
  362. // If the expanded items are / collapsed items were showing, update
  363. // the shown (expanded) count, the max item width -- then recompute
  364. // the scroll bars.
  365. //
  366. // ----------------------------------------------------------------------------
  367. #define SBAEC_COLLAPSE 0
  368. #define SBAEC_EXPAND 1
  369. BOOL TV_ScrollBarsAfterExpandCollapse(PTREE pTree, HTREEITEM hParent, UINT flags)
  370. {
  371. WORD cxMax = 0;
  372. HTREEITEM hWalk;
  373. TVFAKEDRAW tvfd;
  374. if (!ITEM_VISIBLE(hParent))
  375. return FALSE;
  376. //
  377. // We're going to be measuring a lot of items, so let's set up
  378. // our DC ahead of time.
  379. //
  380. TreeView_BeginFakeCustomDraw(pTree, &tvfd);
  381. for (hWalk = hParent->hKids;
  382. hWalk && (hWalk->iLevel > hParent->iLevel);
  383. hWalk = TV_GetNextVisItem(hWalk))
  384. {
  385. if (flags == SBAEC_COLLAPSE)
  386. hWalk->iShownIndex = (WORD)-1;
  387. if (!hWalk->iWidth)
  388. {
  389. TreeView_BeginFakeItemDraw(&tvfd, hWalk);
  390. TV_ComputeItemWidth(pTree, hWalk, tvfd.nmcd.nmcd.hdc);
  391. TreeView_EndFakeItemDraw(&tvfd);
  392. }
  393. if (cxMax < FULL_WIDTH(pTree, hWalk))
  394. cxMax = FULL_WIDTH(pTree, hWalk);
  395. }
  396. TreeView_EndFakeCustomDraw(&tvfd);
  397. // update every shown index after expanded parent
  398. pTree->cShowing = TV_UpdateShownIndexes(pTree, hParent);
  399. // Update the pTree->cxMax if it is affected by the items we
  400. // expanded/collapsed.
  401. if (!(pTree->ci.style & TVS_NOSCROLL))
  402. {
  403. if (flags == SBAEC_COLLAPSE)
  404. {
  405. // If one of our newly-hidden items was responsible for
  406. // the width being what it is, recompute the max width
  407. // since we hid those items.
  408. if (cxMax == pTree->cxMax)
  409. pTree->cxMax = (WORD) TV_RecomputeMaxWidth(pTree);
  410. }
  411. else
  412. {
  413. // If one of our newly-shown items was responsible is wider
  414. // then the previous max, then we have set a new max.
  415. if (cxMax > pTree->cxMax)
  416. pTree->cxMax = cxMax;
  417. }
  418. }
  419. TV_CalcScrollBars(pTree);
  420. return(TRUE);
  421. }
  422. // ----------------------------------------------------------------------------
  423. //
  424. // If the expanded items are showing, update the shown (expanded) count,
  425. // the max item width -- then recompute the scroll bars.
  426. //
  427. // sets cxMax, cShowing
  428. //
  429. // ----------------------------------------------------------------------------
  430. BOOL TV_ScrollBarsAfterExpand(PTREE pTree, HTREEITEM hParent)
  431. {
  432. return TV_ScrollBarsAfterExpandCollapse(pTree, hParent, SBAEC_EXPAND);
  433. }
  434. // ----------------------------------------------------------------------------
  435. //
  436. // If the collapsed items were showing, update the shown (expanded) count,
  437. // the max item width -- then recompute the scroll bars.
  438. //
  439. // sets cxMax, cShowing
  440. //
  441. // ----------------------------------------------------------------------------
  442. BOOL TV_ScrollBarsAfterCollapse(PTREE pTree, HTREEITEM hParent)
  443. {
  444. return TV_ScrollBarsAfterExpandCollapse(pTree, hParent, SBAEC_COLLAPSE);
  445. }
  446. // ----------------------------------------------------------------------------
  447. //
  448. // If the added item changed height, then scroll thing around,
  449. // update the shown (expanded) count, recompute the scroll bars.
  450. //
  451. // sets cShowing
  452. //
  453. // ----------------------------------------------------------------------------
  454. void TV_ScrollBarsAfterResize(PTREE pTree, HTREEITEM hItem, int iIntegralPrev, UINT uRDWFlags)
  455. {
  456. int iMaxIntegral = max(hItem->iIntegral, iIntegralPrev);
  457. ASSERT(hItem->iIntegral != iIntegralPrev);
  458. if (pTree->fRedraw)
  459. {
  460. int iTop = hItem->iShownIndex - pTree->hTop->iShownIndex +
  461. iMaxIntegral - 1;
  462. if (iTop >= 0)
  463. {
  464. int iGrowth = hItem->iIntegral - iIntegralPrev;
  465. TV_ScrollItems(pTree, abs(iGrowth), iTop, iGrowth > 0);
  466. }
  467. }
  468. // update every shown index after resized item
  469. pTree->cShowing = TV_UpdateShownIndexes(pTree, hItem);
  470. TV_CalcScrollBars(pTree);
  471. // Invalidate based on the worst-case height so we handle
  472. // both the grow and shrink cases.
  473. if (pTree->fRedraw)
  474. {
  475. RECT rc;
  476. if (TV_GetItemRect(pTree, hItem, &rc, FALSE))
  477. {
  478. rc.bottom = rc.top + pTree->cyItem * iMaxIntegral;
  479. RedrawWindow(pTree->ci.hwnd, &rc, NULL, uRDWFlags);
  480. }
  481. }
  482. }
  483. // ----------------------------------------------------------------------------
  484. //
  485. // Returns the item just below the given item in the tree.
  486. //
  487. // ----------------------------------------------------------------------------
  488. TREEITEM * TV_GetNext(TREEITEM * hItem)
  489. {
  490. DBG_ValidateTreeItem(hItem, FALSE);
  491. if (hItem->hKids)
  492. return hItem->hKids;
  493. checkNext:
  494. if (hItem->hNext)
  495. return hItem->hNext;
  496. hItem = hItem->hParent;
  497. if (hItem)
  498. goto checkNext;
  499. return NULL;
  500. }
  501. // ----------------------------------------------------------------------------
  502. //
  503. // Go through all the items in the tree, recomputing each item's text extent
  504. // and full width (indent, image, and text).
  505. //
  506. // ----------------------------------------------------------------------------
  507. void TV_RecomputeItemWidths(PTREE pTree)
  508. {
  509. HTREEITEM hItem;
  510. TVFAKEDRAW tvfd;
  511. TreeView_BeginFakeCustomDraw(pTree, &tvfd);
  512. hItem = pTree->hRoot->hKids;
  513. while (hItem)
  514. {
  515. TreeView_BeginFakeItemDraw(&tvfd, hItem);
  516. TV_ComputeItemWidth(pTree, hItem, tvfd.nmcd.nmcd.hdc);
  517. TreeView_EndFakeItemDraw(&tvfd);
  518. hItem = TV_GetNext(hItem);
  519. }
  520. TreeView_EndFakeCustomDraw(&tvfd);
  521. }
  522. // ----------------------------------------------------------------------------
  523. //
  524. // If a single item's width changed, alter the max width if needed.
  525. // If all widths changed, recompute widths and max width.
  526. // Then recompute the scroll bars.
  527. //
  528. // sets cxMax
  529. //
  530. // ----------------------------------------------------------------------------
  531. BOOL TV_ScrollBarsAfterSetWidth(PTREE pTree, HTREEITEM hItem)
  532. {
  533. if (hItem)
  534. {
  535. UINT iOldWidth = FULL_WIDTH(pTree, hItem);
  536. TV_ComputeItemWidth(pTree, hItem, NULL);
  537. if (!(pTree->ci.style & TVS_NOSCROLL)) {
  538. if (pTree->cxMax == iOldWidth)
  539. pTree->cxMax = (WORD) TV_RecomputeMaxWidth(pTree);
  540. else
  541. return(FALSE);
  542. }
  543. }
  544. else
  545. {
  546. TV_RecomputeItemWidths(pTree);
  547. pTree->cxMax = (WORD) TV_RecomputeMaxWidth(pTree);
  548. }
  549. TV_CalcScrollBars(pTree);
  550. return(TRUE);
  551. }
  552. // ----------------------------------------------------------------------------
  553. //
  554. // Scroll window vertically as needed to make given item fully visible
  555. // vertically
  556. //
  557. // ----------------------------------------------------------------------------
  558. BOOL TV_ScrollVertIntoView(PTREE pTree, HTREEITEM hItem)
  559. {
  560. // This function has crashed in stress before, so we need to assert the incoming parameters.
  561. ASSERT(hItem);
  562. ASSERT(pTree && pTree->hTop);
  563. // Do nothing if the parameters are invalid
  564. if (!hItem || !pTree || !(pTree->hTop))
  565. return FALSE;
  566. // Do nothing if this item is not visible
  567. if (!ITEM_VISIBLE(hItem))
  568. return FALSE;
  569. if (hItem->iShownIndex < pTree->hTop->iShownIndex)
  570. return(TV_SetTopItem(pTree, hItem->iShownIndex));
  571. if (hItem->iShownIndex >= (pTree->hTop->iShownIndex + pTree->cFullVisible))
  572. return(TV_SetTopItem(pTree, hItem->iShownIndex + 1 - pTree->cFullVisible));
  573. return FALSE;
  574. }
  575. // ----------------------------------------------------------------------------
  576. //
  577. // Scroll window vertically and horizontally as needed to make given item
  578. // fully visible vertically and horizontally
  579. //
  580. // ----------------------------------------------------------------------------
  581. BOOL TV_ScrollIntoView(PTREE pTree, HTREEITEM hItem)
  582. {
  583. UINT iWidth, iOffset;
  584. BOOL fChange;
  585. fChange = TV_ScrollVertIntoView(pTree, hItem);
  586. // ensure that item's text is fully visible horizontally
  587. iWidth = pTree->cxImage + pTree->cxState + hItem->iWidth;
  588. if (iWidth > (UINT)pTree->cxWnd)
  589. iWidth = pTree->cxWnd; //hItem->iWidth;
  590. iOffset = ITEM_OFFSET(pTree, hItem);
  591. if ((int) (iOffset) < pTree->xPos)
  592. fChange |= TV_SetLeft(pTree, iOffset);
  593. else if ((iOffset + iWidth) > (UINT)(pTree->xPos + pTree->cxWnd))
  594. fChange |= TV_SetLeft(pTree, iOffset + iWidth - pTree->cxWnd);
  595. return fChange;
  596. }
  597. // ----------------------------------------------------------------------------
  598. //
  599. // Sets position of horizontal scroll bar and scrolls window to match that
  600. // position
  601. //
  602. // sets xPos
  603. //
  604. // ----------------------------------------------------------------------------
  605. BOOL TV_SetLeft(PTREE pTree, int x)
  606. {
  607. if (!pTree->fHorz || pTree->ci.style & (TVS_NOSCROLL | TVS_NOHSCROLL))
  608. return(FALSE);
  609. if (x > (int) (pTree->cxMax - pTree->cxWnd))
  610. x = (pTree->cxMax - pTree->cxWnd);
  611. if (x < 0)
  612. x = 0;
  613. if (x == pTree->xPos)
  614. return(FALSE);
  615. if (pTree->fRedraw)
  616. {
  617. SMOOTHSCROLLINFO si;
  618. si.cbSize = sizeof(si);
  619. si.fMask = SSIF_MINSCROLL | SSIF_MAXSCROLLTIME;
  620. si.hwnd = pTree->ci.hwnd;
  621. si.dx = pTree->xPos - x;
  622. si.dy = 0;
  623. si.lprcSrc = NULL;
  624. si.lprcClip = NULL;
  625. si.hrgnUpdate = NULL;
  626. si.lprcUpdate = NULL;
  627. si.fuScroll = SW_INVALIDATE | SW_ERASE;
  628. si.uMaxScrollTime = pTree->uMaxScrollTime;
  629. si.cxMinScroll = 1;
  630. si.cyMinScroll = 1;
  631. si.pfnScrollProc = NULL;
  632. SmoothScrollWindow(&si);
  633. }
  634. pTree->xPos = (SHORT) x;
  635. SetScrollPos(pTree->ci.hwnd, SB_HORZ, x, TRUE);
  636. TV_UpdateToolTip(pTree);
  637. return(TRUE);
  638. }
  639. // ----------------------------------------------------------------------------
  640. //
  641. // Returns the tree's item that has the given shown index, NULL if no item
  642. // found with that index.
  643. //
  644. // ----------------------------------------------------------------------------
  645. HTREEITEM TV_GetShownIndexItem(HTREEITEM hItem, UINT wShownIndex)
  646. {
  647. HTREEITEM hWalk;
  648. if (hItem == NULL)
  649. return NULL;
  650. //ASSERT((int)wShownIndex >= 0);
  651. for (hWalk = hItem;
  652. hWalk && (hWalk->iShownIndex <= wShownIndex);
  653. hWalk = hWalk->hNext) {
  654. hItem = hWalk;
  655. if (hWalk->iShownIndex + (UINT)hWalk->iIntegral > wShownIndex)
  656. return hWalk;
  657. }
  658. return TV_GetShownIndexItem(hItem->hKids, wShownIndex);
  659. }
  660. // ----------------------------------------------------------------------------
  661. //
  662. // Sets position of vertical scroll bar and scrolls window to match that
  663. // position
  664. //
  665. // sets hTop
  666. //
  667. // ----------------------------------------------------------------------------
  668. BOOL TV_SmoothSetTopItem(PTREE pTree, UINT wNewTop, UINT uSmooth)
  669. {
  670. HTREEITEM hItem = pTree->hRoot->hKids;
  671. UINT wOldTop;
  672. if (!hItem)
  673. return FALSE;
  674. if ((pTree->ci.style & TVS_NOSCROLL) || (wNewTop == (UINT)-1) || (pTree->cShowing <= pTree->cFullVisible)) {
  675. // we've wrapped around (treat as a negative index) -- use min pos
  676. // or there aren't enough items to scroll
  677. wNewTop = 0;
  678. } else if (wNewTop > (UINT)(pTree->cShowing - pTree->cFullVisible)) {
  679. // we've gone too far down -- use max pos
  680. wNewTop = (pTree->cShowing - pTree->cFullVisible);
  681. }
  682. // if there's no room for anything to show. peg at the end
  683. if (wNewTop > 0 && wNewTop >= pTree->cShowing) {
  684. wNewTop = pTree->cShowing - 1;
  685. }
  686. hItem = TV_GetShownIndexItem(hItem, wNewTop);
  687. //ASSERT(hItem);
  688. if (NULL == hItem || pTree->hTop == hItem)
  689. return FALSE;
  690. // need to refetch because wNewTop couldhave pointed to the middle of this item,
  691. // which is not allowed
  692. wNewTop = hItem->iShownIndex;
  693. wOldTop = pTree->hTop->iShownIndex;
  694. pTree->hTop = hItem;
  695. if (pTree->fRedraw)
  696. {
  697. SMOOTHSCROLLINFO si;
  698. si.cbSize = sizeof(si);
  699. si.fMask = SSIF_MINSCROLL | SSIF_MAXSCROLLTIME;
  700. si.hwnd = pTree->ci.hwnd;
  701. si.dx = 0;
  702. si.dy = (int) (wOldTop - wNewTop) * (int) pTree->cyItem;
  703. si.lprcSrc = NULL;
  704. si.lprcClip = NULL;
  705. si.hrgnUpdate = NULL;
  706. si.lprcUpdate = NULL;
  707. si.fuScroll = SW_INVALIDATE | SW_ERASE | uSmooth;
  708. si.uMaxScrollTime = pTree->uMaxScrollTime;
  709. si.cxMinScroll = 1;
  710. si.cyMinScroll = 1;
  711. si.pfnScrollProc = NULL;
  712. SmoothScrollWindow(&si);
  713. }
  714. SetScrollPos(pTree->ci.hwnd, SB_VERT, wNewTop, TRUE);
  715. TV_UpdateToolTip(pTree);
  716. return(TRUE);
  717. }
  718. // ----------------------------------------------------------------------------
  719. //
  720. // Computes the horizontal and vertical scroll bar ranges, pages, and
  721. // positions, adding or removing the scroll bars as needed.
  722. //
  723. // sets fHorz, fVert
  724. //
  725. // ----------------------------------------------------------------------------
  726. BOOL TV_CalcScrollBars(PTREE pTree)
  727. {
  728. // UINT wMaxPos;
  729. BOOL fChange = FALSE;
  730. SCROLLINFO si;
  731. if (pTree->ci.style & TVS_NOSCROLL)
  732. return FALSE;
  733. si.cbSize = sizeof(SCROLLINFO);
  734. if (!(pTree->ci.style & TVS_NOHSCROLL))
  735. {
  736. if ((SHORT)pTree->cxMax > (SHORT)pTree->cxWnd)
  737. {
  738. if (!pTree->fHorz)
  739. {
  740. fChange = TRUE;
  741. pTree->fHorz = TRUE;
  742. }
  743. si.fMask = SIF_PAGE | SIF_RANGE;
  744. si.nMin = 0;
  745. si.nMax = pTree->cxMax - 1;
  746. si.nPage = pTree->cxWnd;
  747. TV_SetLeft(pTree, (UINT)SetScrollInfo(pTree->ci.hwnd, SB_HORZ, &si, TRUE));
  748. }
  749. else if (pTree->fHorz)
  750. {
  751. TV_SetLeft(pTree, 0);
  752. SetScrollRange(pTree->ci.hwnd, SB_HORZ, 0, 0, TRUE);
  753. pTree->fHorz = FALSE;
  754. fChange = TRUE;
  755. }
  756. }
  757. if (pTree->cShowing > pTree->cFullVisible)
  758. {
  759. if (!pTree->fVert)
  760. {
  761. pTree->fVert = TRUE;
  762. fChange = TRUE;
  763. }
  764. si.fMask = SIF_PAGE | SIF_RANGE;
  765. si.nMin = 0;
  766. si.nMax = pTree->cShowing - 1;
  767. si.nPage = pTree->cFullVisible;
  768. TV_SetTopItem(pTree, (UINT)SetScrollInfo(pTree->ci.hwnd, SB_VERT, &si, TRUE));
  769. }
  770. else if (pTree->fVert)
  771. {
  772. TV_SetTopItem(pTree, 0);
  773. SetScrollRange(pTree->ci.hwnd, SB_VERT, 0, 0, TRUE);
  774. pTree->fVert = FALSE;
  775. fChange = TRUE;
  776. }
  777. if (fChange)
  778. TV_SizeWnd(pTree, 0, 0);
  779. return(TRUE);
  780. }
  781. // ----------------------------------------------------------------------------
  782. //
  783. // Handles horizontal scrolling.
  784. //
  785. // ----------------------------------------------------------------------------
  786. BOOL TV_HorzScroll(PTREE pTree, UINT wCode, UINT wNewPos)
  787. {
  788. BOOL fChanged;
  789. TV_DismissEdit(pTree, FALSE);
  790. switch (wCode)
  791. {
  792. case SB_BOTTOM:
  793. wNewPos = pTree->cxMax - pTree->cxWnd;
  794. break;
  795. case SB_ENDSCROLL:
  796. wNewPos = pTree->xPos;
  797. break;
  798. case SB_LINEDOWN:
  799. wNewPos = pTree->xPos + MAGIC_HORZLINE;
  800. break;
  801. case SB_LINEUP:
  802. wNewPos = pTree->xPos - MAGIC_HORZLINE;
  803. break;
  804. case SB_PAGEDOWN:
  805. wNewPos = pTree->xPos + (pTree->cxWnd - MAGIC_HORZLINE);
  806. break;
  807. case SB_PAGEUP:
  808. wNewPos = pTree->xPos - (pTree->cxWnd - MAGIC_HORZLINE);
  809. break;
  810. case SB_THUMBPOSITION:
  811. case SB_THUMBTRACK:
  812. break;
  813. case SB_TOP:
  814. wNewPos = 0;
  815. break;
  816. }
  817. if (fChanged = TV_SetLeft(pTree, wNewPos))
  818. UpdateWindow(pTree->ci.hwnd);
  819. return(fChanged);
  820. }
  821. // ----------------------------------------------------------------------------
  822. //
  823. // Handles vertical scrolling.
  824. //
  825. // ----------------------------------------------------------------------------
  826. BOOL TV_VertScroll(PTREE pTree, UINT wCode, UINT wPos)
  827. {
  828. UINT wNewPos = 0;
  829. UINT wOldPos;
  830. BOOL fChanged;
  831. UINT uSmooth = 0;
  832. if (!pTree->hTop)
  833. return FALSE;
  834. wOldPos = pTree->hTop->iShownIndex;
  835. TV_DismissEdit(pTree, FALSE);
  836. switch (wCode)
  837. {
  838. case SB_BOTTOM:
  839. wNewPos = pTree->cShowing - pTree->cFullVisible;
  840. break;
  841. case SB_ENDSCROLL:
  842. wNewPos = wOldPos;
  843. break;
  844. case SB_LINEDOWN:
  845. wNewPos = wOldPos + pTree->hTop->iIntegral;
  846. break;
  847. case SB_LINEUP:
  848. wNewPos = wOldPos - 1;
  849. if (wNewPos > wOldPos)
  850. wNewPos = 0;
  851. break;
  852. case SB_PAGEDOWN:
  853. wNewPos = wOldPos + (pTree->cFullVisible - 1);
  854. break;
  855. case SB_PAGEUP:
  856. wNewPos = wOldPos - (pTree->cFullVisible - 1);
  857. if (wNewPos > wOldPos)
  858. wNewPos = 0;
  859. break;
  860. case SB_THUMBPOSITION:
  861. case SB_THUMBTRACK:
  862. uSmooth = SSW_EX_IMMEDIATE;
  863. wNewPos = wPos;
  864. break;
  865. case SB_TOP:
  866. wNewPos = 0;
  867. break;
  868. }
  869. if (fChanged = TV_SmoothSetTopItem(pTree, wNewPos, uSmooth))
  870. UpdateWindow(pTree->ci.hwnd);
  871. return(fChanged);
  872. }
  873. #ifdef DEBUG
  874. static int nCompares;
  875. #endif
  876. typedef struct {
  877. LPTSTR lpstr;
  878. BOOL bCallBack;
  879. HTREEITEM hItem;
  880. } TVCOMPARE, *LPTVCOMPARE;
  881. // Pointer comparision function for Sort and Search functions.
  882. // lParam is lParam passed to sort/search functions. Returns
  883. // -1 if p1 < p2, 0 if p1 == p2, and 1 if p1 > p2.
  884. //
  885. int CALLBACK TV_DefCompare(LPTVCOMPARE sCmp1, LPTVCOMPARE sCmp2, LPARAM lParam)
  886. {
  887. #ifdef DEBUG
  888. ++nCompares;
  889. #endif
  890. return lstrcmpi(sCmp1->lpstr, sCmp2->lpstr);
  891. }
  892. int CALLBACK TV_CompareItems(LPTVCOMPARE sCmp1, LPTVCOMPARE sCmp2, LPARAM lParam)
  893. {
  894. TV_SORTCB *pSortCB = (TV_SORTCB *)lParam;
  895. #ifdef DEBUG
  896. ++nCompares;
  897. #endif
  898. return(pSortCB->lpfnCompare(sCmp1->hItem->lParam, sCmp2->hItem->lParam,
  899. pSortCB->lParam));
  900. }
  901. UINT TV_CountKids(HTREEITEM hItem)
  902. {
  903. int cnt;
  904. for (cnt = 0, hItem = hItem->hKids; hItem; hItem = hItem->hNext)
  905. cnt++;
  906. return cnt;
  907. }
  908. BOOL TV_SortCB(PTREE pTree, TV_SORTCB *pSortCB, BOOL bRecurse,
  909. PFNDPACOMPARE lpfnDPACompare)
  910. {
  911. HDPA dpaSort;
  912. HDSA dsaCmp;
  913. HTREEITEM hItem, hNext, hFirstMoved;
  914. LPTVCOMPARE psCompare, *ppsCompare;
  915. int i, cKids;
  916. HTREEITEM hParent = pSortCB->hParent;
  917. #ifdef DEBUG
  918. DWORD dwTime = GetTickCount();
  919. nCompares = 0;
  920. #endif
  921. if (!hParent || hParent == TVI_ROOT)
  922. hParent = pTree->hRoot;
  923. if (!ValidateTreeItem(hParent, FALSE))
  924. return FALSE; // Invalid parameter
  925. // Code below assumes at least one kid
  926. cKids = TV_CountKids(hParent);
  927. if (!cKids)
  928. return FALSE;
  929. // Create a DSA for all the extra info we'll need
  930. dsaCmp = DSA_Create(sizeof(TVCOMPARE), cKids);
  931. if (!dsaCmp)
  932. goto Error1;
  933. // Create a DPA containing all the tree items
  934. dpaSort = DPA_Create(cKids);
  935. if (!dpaSort)
  936. goto Error2;
  937. for (hItem = hParent->hKids; hItem; hItem = hItem->hNext)
  938. {
  939. TVCOMPARE sCompare;
  940. int nItem;
  941. // If I can't sort all of them, I don't want to sort any of them
  942. // We want to cache the text callback for default processing
  943. if (!lpfnDPACompare && hItem->lpstr==LPSTR_TEXTCALLBACK)
  944. {
  945. TVITEMEX sItem;
  946. TCHAR szTemp[MAX_PATH];
  947. sItem.pszText = szTemp;
  948. sItem.cchTextMax = ARRAYSIZE(szTemp);
  949. TV_GetItem(pTree, hItem, TVIF_TEXT, &sItem);
  950. sCompare.lpstr = NULL;
  951. sCompare.bCallBack = TRUE;
  952. Str_Set(&sCompare.lpstr, sItem.pszText);
  953. if (!sCompare.lpstr)
  954. {
  955. goto Error3;
  956. }
  957. }
  958. else
  959. {
  960. sCompare.lpstr = hItem->lpstr;
  961. sCompare.bCallBack = FALSE;
  962. }
  963. // Create the pointer for this guy and add it to the DPA list
  964. sCompare.hItem = hItem;
  965. nItem = DSA_AppendItem(dsaCmp, &sCompare);
  966. if (nItem < 0)
  967. {
  968. if (sCompare.bCallBack)
  969. {
  970. Str_Set(&sCompare.lpstr, NULL);
  971. }
  972. goto Error3;
  973. }
  974. if (DPA_AppendPtr(dpaSort, DSA_GetItemPtr(dsaCmp, nItem)) < 0)
  975. {
  976. goto Error3;
  977. }
  978. }
  979. // Sort the DPA, then stick them back under the parent in the new order
  980. DPA_Sort(dpaSort, lpfnDPACompare ? (PFNDPACOMPARE)lpfnDPACompare :
  981. (PFNDPACOMPARE) TV_DefCompare, (LPARAM)pSortCB);
  982. // Look for the first moved item, so we can invalidate a smaller area
  983. ppsCompare = (LPTVCOMPARE *)DPA_GetPtrPtr(dpaSort);
  984. if (hParent->hKids != (*ppsCompare)->hItem)
  985. {
  986. hParent->hKids = (*ppsCompare)->hItem;
  987. hFirstMoved = hParent->hKids;
  988. }
  989. else
  990. {
  991. hFirstMoved = NULL;
  992. }
  993. // We do n-1 iterations here
  994. for (i = DPA_GetPtrCount(dpaSort) - 1; i > 0; --i, ++ppsCompare)
  995. {
  996. hNext = (*(ppsCompare+1))->hItem;
  997. if ((*ppsCompare)->hItem->hNext != hNext && !hFirstMoved)
  998. {
  999. hFirstMoved = hNext;
  1000. }
  1001. (*ppsCompare)->hItem->hNext = hNext;
  1002. }
  1003. (*ppsCompare)->hItem->hNext = NULL;
  1004. TV_UpdateShownIndexes(pTree, hParent);
  1005. if ((pSortCB->hParent == TVI_ROOT) || !hParent) {
  1006. if (pTree->cShowing < pTree->cFullVisible) {
  1007. pTree->hTop = pTree->hRoot->hKids;
  1008. }
  1009. }
  1010. if (hFirstMoved && (hParent->state & TVIS_EXPANDED))
  1011. {
  1012. RECT rcUpdate;
  1013. TV_GetItemRect(pTree, hFirstMoved, &rcUpdate, FALSE);
  1014. if (hParent->hNext)
  1015. {
  1016. RECT rcTemp;
  1017. TV_GetItemRect(pTree, hParent->hNext, &rcTemp, FALSE);
  1018. rcUpdate.bottom = rcTemp.bottom;
  1019. }
  1020. else
  1021. {
  1022. RECT rcClient;
  1023. GetClientRect(pTree->ci.hwnd, &rcClient);
  1024. // Set to maximal positive number, so the whole rest of
  1025. // the treeview gets invalidated
  1026. rcUpdate.bottom = rcClient.bottom;
  1027. }
  1028. if (pTree->fRedraw)
  1029. InvalidateRect(pTree->ci.hwnd, &rcUpdate, TRUE);
  1030. }
  1031. Error3:
  1032. DPA_Destroy(dpaSort);
  1033. Error2:
  1034. for (i = DSA_GetItemCount(dsaCmp) - 1; i >= 0; --i)
  1035. {
  1036. psCompare = DSA_GetItemPtr(dsaCmp, i);
  1037. if (psCompare->bCallBack)
  1038. {
  1039. Str_Set(&(psCompare->lpstr), NULL);
  1040. }
  1041. }
  1042. DSA_Destroy(dsaCmp);
  1043. Error1:
  1044. #ifdef DEBUG
  1045. TraceMsg(TF_TREEVIEW, "tv.sort: %ld ms; %d cmps", GetTickCount()-dwTime, nCompares);
  1046. #endif
  1047. {
  1048. int wNewPos;
  1049. // restore the scroll position
  1050. if (GetWindowStyle(pTree->ci.hwnd) & WS_VSCROLL) {
  1051. SCROLLINFO si;
  1052. si.cbSize = sizeof(SCROLLINFO);
  1053. si.fMask = SIF_POS;
  1054. wNewPos = 0;
  1055. if (GetScrollInfo(pTree->ci.hwnd, SB_VERT, &si)) {
  1056. wNewPos = si.nPos;
  1057. }
  1058. } else {
  1059. wNewPos = 0;
  1060. }
  1061. if (TV_SetTopItem(pTree, wNewPos))
  1062. UpdateWindow(pTree->ci.hwnd);
  1063. }
  1064. // if the caret is the child of the thing that was sorted, make sure it's
  1065. // visible (but if we're sorting something completely unrelated, don't bother
  1066. if (pTree->hCaret) {
  1067. hItem = pTree->hCaret;
  1068. do {
  1069. // do this first. if hParent is hCaret, we don't want to ensure visible...
  1070. // only if it's an eventual child
  1071. hItem = hItem->hParent;
  1072. if (hParent == hItem) {
  1073. TV_EnsureVisible(pTree, pTree->hCaret);
  1074. }
  1075. } while(hItem && hItem != pTree->hRoot);
  1076. }
  1077. // The items in the view may have moved around; let apps know
  1078. // Do this last because this call might yield
  1079. NotifyWinEvent(EVENT_OBJECT_REORDER, pTree->ci.hwnd, OBJID_CLIENT, 0);
  1080. return TRUE;
  1081. }
  1082. BOOL TV_SortChildrenCB(PTREE pTree, LPTV_SORTCB pSortCB, BOOL bRecurse)
  1083. {
  1084. if (pSortCB == NULL)
  1085. {
  1086. RIPMSG(0, "TVM_SORTCHILDRENCB: Invalid parameter (NULL for TVSORTCB)");
  1087. return FALSE;
  1088. }
  1089. return(TV_SortCB(pTree, pSortCB, bRecurse, (PFNDPACOMPARE)TV_CompareItems));
  1090. }
  1091. BOOL TV_SortChildren(PTREE pTree, HTREEITEM hParent, BOOL bRecurse)
  1092. {
  1093. TV_SORTCB sSortCB;
  1094. sSortCB.hParent = hParent;
  1095. return(TV_SortCB(pTree, &sSortCB, bRecurse, NULL));
  1096. }