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

1415 lines
39 KiB

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