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.

3289 lines
80 KiB

  1. //============================================================================
  2. // Copyright (c) 1995, Microsoft Corporation
  3. //
  4. // File: treelist.c
  5. //
  6. // History:
  7. // Abolade Gbadegesin Nov-20-1995 Created.
  8. //
  9. // Implementation routines for TreeList control.
  10. //
  11. // The TreeList control is implemented as a custom control,
  12. // which creates and manages an owner-draw listview.
  13. //============================================================================
  14. #include <windows.h>
  15. #include <windowsx.h>
  16. #include <commctrl.h>
  17. #include <debug.h>
  18. #include <nouiutil.h>
  19. #include <uiutil.h>
  20. #include <list.h>
  21. #include "treelist.h"
  22. #include "tldef.h"
  23. #if 0
  24. #define TLTRACE (0x80000002)
  25. #else
  26. #define TLTRACE (0x00000002)
  27. #endif
  28. //----------------------------------------------------------------------------
  29. // Function: TL_Init
  30. //
  31. // Registers the TreeList window class.
  32. //----------------------------------------------------------------------------
  33. BOOL
  34. TL_Init(
  35. HINSTANCE hInstance
  36. ) {
  37. INT i;
  38. HICON hicon;
  39. WNDCLASS wc;
  40. //
  41. // do nothing if the class is already registered
  42. //
  43. if (GetClassInfo(hInstance, WC_TREELIST, &wc)) {
  44. return TRUE;
  45. }
  46. //
  47. // setup the wndclass structure, and register
  48. //
  49. wc.lpfnWndProc = TL_WndProc;
  50. wc.hCursor = LoadCursor(hInstance, IDC_ARROW);
  51. wc.hIcon = NULL;
  52. wc.lpszMenuName = NULL;
  53. wc.hInstance = hInstance;
  54. wc.lpszClassName = WC_TREELIST;
  55. wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  56. wc.style = CS_DBLCLKS;
  57. wc.cbWndExtra = sizeof(TL *);
  58. wc.cbClsExtra = 0;
  59. return RegisterClass(&wc);
  60. }
  61. //----------------------------------------------------------------------------
  62. // Function: TL_WndProc
  63. //
  64. // This function handles messages for TreeList windows
  65. //----------------------------------------------------------------------------
  66. LRESULT
  67. CALLBACK
  68. TL_WndProc(
  69. HWND hwnd,
  70. UINT uiMsg,
  71. WPARAM wParam,
  72. LPARAM lParam
  73. ) {
  74. TL *ptl;
  75. if (NULL == hwnd)
  76. {
  77. return (LRESULT)FALSE;
  78. }
  79. //
  80. // attempt to retrieve the data pointer for the window.
  81. // on WM_NCCREATE, this fails, so we allocate the data.
  82. //
  83. ptl = TL_GetPtr(hwnd);
  84. if (ptl == NULL) {
  85. if (uiMsg != WM_NCCREATE) {
  86. return DefWindowProc(hwnd, uiMsg, wParam, lParam);
  87. }
  88. //
  89. // allocate a block of memory
  90. //
  91. ptl = (TL *)Malloc(sizeof(TL));
  92. if (ptl == NULL) { return (LRESULT)FALSE; }
  93. //
  94. // save the pointer in the window's private bytes
  95. //
  96. ptl->hwnd = hwnd;
  97. //
  98. //Reset Error code, TL_SetPtr won't reset error code when
  99. //it succeeds
  100. //
  101. SetLastError(0);
  102. if ((0 == TL_SetPtr(hwnd, ptl)) && (0 != GetLastError()))
  103. {
  104. Free(ptl);
  105. return (LRESULT)FALSE;
  106. }
  107. return DefWindowProc(hwnd, uiMsg, wParam, lParam);
  108. }
  109. //
  110. // if the window is being destroyed, free the block allocated
  111. // and set the private bytes pointer to NULL
  112. //
  113. if (uiMsg == WM_NCDESTROY) {
  114. Free(ptl);
  115. TL_SetPtr(hwnd, NULL);
  116. return (LRESULT)0;
  117. }
  118. switch (uiMsg) {
  119. HANDLE_MSG(ptl, WM_CREATE, TL_OnCreate);
  120. HANDLE_MSG(ptl, WM_DESTROY, TL_OnDestroy);
  121. HANDLE_MSG(ptl, WM_DRAWITEM, TL_OnDrawItem);
  122. HANDLE_MSG(ptl, WM_MEASUREITEM, TL_OnMeasureItem);
  123. HANDLE_MSG(ptl, WM_NOTIFY, TL_OnNotify);
  124. case WM_ERASEBKGND: {
  125. TL_OnEraseBackground(ptl, (HDC)wParam);
  126. return (LRESULT)TRUE;
  127. }
  128. case WM_HELP: {
  129. //
  130. // change the control-id and HWND for the help to our values
  131. // and pass the message on to our parent
  132. //
  133. HELPINFO *phi = (HELPINFO *)lParam;
  134. phi->iCtrlId = ptl->iCtrlId;
  135. phi->hItemHandle = ptl->hwnd;
  136. return SendMessage(ptl->hwndParent, WM_HELP, 0L, lParam);
  137. }
  138. case WM_SYSCOLORCHANGE: {
  139. //
  140. // notify the listview window that a color has changed
  141. //
  142. TL_CreateTreeImages(ptl);
  143. FORWARD_WM_SYSCOLORCHANGE(ptl->hwndList, SendMessage);
  144. // ListView_SetBkColor(ptl->hwndList, GetSysColor(COLOR_WINDOW));
  145. return (LRESULT)0;
  146. }
  147. case WM_SETFOCUS: {
  148. //
  149. // if we receive the focus, give it to the listview instead
  150. //
  151. SetFocus(ptl->hwndList);
  152. return (LRESULT)0;
  153. }
  154. case WM_WINDOWPOSCHANGED: {
  155. TL_OnWindowPosChanged(ptl, (WINDOWPOS *)lParam);
  156. return 0;
  157. }
  158. //
  159. // the following cases handle TreeList-defined messages
  160. //
  161. case TLM_INSERTITEM: {
  162. return (LRESULT)TL_OnInsertItem(ptl, (TL_INSERTSTRUCT *)lParam);
  163. }
  164. case TLM_DELETEITEM: {
  165. return (LRESULT)TL_OnDeleteItem(ptl, (HTLITEM)lParam);
  166. }
  167. case TLM_DELETEALLITEMS: {
  168. return (LRESULT)TL_OnDeleteAllItems(ptl);
  169. }
  170. case TLM_GETITEM: {
  171. return (LRESULT)TL_OnGetItem(ptl, (LV_ITEM *)lParam);
  172. }
  173. case TLM_SETITEM: {
  174. return (LRESULT)TL_OnSetItem(ptl, (LV_ITEM *)lParam);
  175. }
  176. case TLM_GETITEMCOUNT: {
  177. return (LRESULT)TL_OnGetItemCount(ptl);
  178. }
  179. case TLM_GETNEXTITEM: {
  180. return (LRESULT)TL_OnGetNextItem(ptl, (UINT)wParam,(HTLITEM)lParam);
  181. }
  182. case TLM_EXPAND: {
  183. return (LRESULT)TL_OnExpand(ptl, (UINT)wParam, (HTLITEM)lParam);
  184. }
  185. case TLM_SETIMAGELIST: {
  186. return (LRESULT)ListView_SetImageList(
  187. ptl->hwndList, (HIMAGELIST)lParam, LVSIL_SMALL
  188. );
  189. }
  190. case TLM_GETIMAGELIST: {
  191. return (LRESULT)ListView_GetImageList(ptl->hwndList, LVSIL_SMALL);
  192. }
  193. case TLM_INSERTCOLUMN: {
  194. return (LRESULT)TL_OnInsertColumn(
  195. ptl, (INT)wParam, (LV_COLUMN *)lParam
  196. );
  197. }
  198. case TLM_DELETECOLUMN: {
  199. return (LRESULT)TL_OnDeleteColumn(ptl, (INT)wParam);
  200. }
  201. case TLM_SETSELECTION: {
  202. return (LRESULT)TL_OnSetSelection(ptl, (HTLITEM)lParam);
  203. }
  204. case TLM_REDRAW: {
  205. return (LRESULT)TL_OnRedraw(ptl);
  206. }
  207. case TLM_ISITEMEXPANDED: {
  208. return (LRESULT)TL_IsExpanded((TLITEM *)lParam);
  209. }
  210. case TLM_GETCOLUMNWIDTH: {
  211. return (LRESULT)SendMessage(
  212. ptl->hwndList, LVM_GETCOLUMNWIDTH, wParam, lParam
  213. );
  214. }
  215. case TLM_SETCOLUMNWIDTH: {
  216. return (LRESULT)SendMessage(
  217. ptl->hwndList, LVM_SETCOLUMNWIDTH, wParam, lParam
  218. );
  219. }
  220. }
  221. //
  222. // let the default processing be done
  223. //
  224. return DefWindowProc(hwnd, uiMsg, wParam, lParam);
  225. }
  226. //----------------------------------------------------------------------------
  227. // Function: TL_OnCreate
  228. //
  229. // This is called after WM_CREATE, and it initializes the window structure,
  230. // as well as creating the listview which will contain the items added
  231. //----------------------------------------------------------------------------
  232. BOOL
  233. TL_OnCreate(
  234. TL *ptl,
  235. CREATESTRUCT *pcs
  236. ) {
  237. RECT rc;
  238. HD_ITEM hdi;
  239. HWND hwndList;
  240. TLITEM *pRoot;
  241. DWORD dwStyle, dwExStyle;
  242. //
  243. // initialize the window structure
  244. //
  245. ptl->hbrBk = NULL;
  246. ptl->hbmp = NULL;
  247. ptl->hbmpStart = NULL;
  248. ptl->hbmpMem = NULL;
  249. ptl->hdcImages = NULL;
  250. ptl->hwndList = NULL;
  251. ptl->iCtrlId = PtrToUlong(pcs->hMenu);
  252. ptl->hwndParent = pcs->hwndParent;
  253. ptl->nColumns = 0;
  254. //
  255. // initialize the invisible root item
  256. //
  257. pRoot = &ptl->root;
  258. pRoot->pParent = NULL;
  259. pRoot->iLevel = -1;
  260. pRoot->iIndex = -1;
  261. pRoot->nChildren = 0;
  262. pRoot->uiFlag = TLI_EXPANDED;
  263. pRoot->pszText = TEXT("ROOT");
  264. InitializeListHead(&pRoot->lhChildren);
  265. InitializeListHead(&pRoot->lhSubitems);
  266. //
  267. // we pass on some of our window style bits to the listview
  268. // when we create it as our child; we also remove certain styles
  269. // which are never appropriate for the contained listview
  270. //
  271. dwStyle = pcs->style & ~(LVS_TYPESTYLEMASK | LVS_SORTASCENDING |
  272. LVS_SORTDESCENDING);
  273. dwStyle |= WS_CHILD | LVS_REPORT | LVS_SINGLESEL | LVS_OWNERDRAWFIXED;
  274. dwExStyle = pcs->dwExStyle & ~(WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE |
  275. WS_EX_STATICEDGE);
  276. //
  277. // create the listview window
  278. //
  279. GetClientRect(ptl->hwnd, &rc);
  280. hwndList = CreateWindowEx(
  281. dwExStyle, WC_LISTVIEW, NULL, dwStyle,
  282. 0, 0, rc.right, rc.bottom,
  283. ptl->hwnd, NULL, pcs->hInstance, NULL
  284. );
  285. if (hwndList == NULL) { return FALSE; }
  286. // ListView_SetBkColor(hwndList, GetSysColor(COLOR_WINDOW));
  287. //
  288. // We to set the background color to "NONE" to prevent the listview
  289. // from erasing its background itself. Removing the background color
  290. // causes the listview to forward its WM_ERASEBKGND messages to its parent,
  291. // which is our tree-list. We handle the WM_ERASEBKGND messages
  292. // efficiently by only erasing the background when absolutely necessary,
  293. // and this eliminates the flicker normally seen when windows are updated
  294. // frequently.
  295. //
  296. ListView_SetBkColor(hwndList, CLR_NONE);
  297. ptl->hwndList = hwndList;
  298. return TRUE;
  299. }
  300. //----------------------------------------------------------------------------
  301. // Function: TL_OnDestroy
  302. //
  303. // Delete all the items in the tree, and free the image bitmap
  304. // used for drawing the tree structure
  305. //----------------------------------------------------------------------------
  306. VOID
  307. TL_OnDestroy(
  308. TL *ptl
  309. ) {
  310. TL_OnDeleteAllItems(ptl);
  311. if (ptl->hdcImages != NULL) {
  312. if (ptl->hbmp) {
  313. SelectObject(ptl->hdcImages, ptl->hbmpStart);
  314. DeleteObject(ptl->hbmp);
  315. }
  316. DeleteDC(ptl->hdcImages);
  317. }
  318. if (ptl->hbmpMem) { DeleteObject(ptl->hbmpMem); }
  319. }
  320. //----------------------------------------------------------------------------
  321. // Function: TL_OnWindowPosChanged
  322. //
  323. // When the window width changes, we destroy our off-screen bitmap.
  324. //----------------------------------------------------------------------------
  325. VOID
  326. TL_OnWindowPosChanged(
  327. TL *ptl,
  328. WINDOWPOS *pwp
  329. ) {
  330. RECT rc;
  331. GetClientRect(ptl->hwnd, &rc);
  332. SetWindowPos(
  333. ptl->hwndList, ptl->hwnd, 0, 0, rc.right, rc.bottom, pwp->flags
  334. );
  335. }
  336. //----------------------------------------------------------------------------
  337. // Function: TL_OnEraseBackground
  338. //
  339. // When we are asked to erase the background, first test to see if
  340. // the update region is completely in the item-area for the listbox. If so,
  341. // we know we'll be called to update each item, so we can ignore this
  342. // request to erase our background.
  343. //----------------------------------------------------------------------------
  344. VOID
  345. TL_OnEraseBackground(
  346. TL *ptl,
  347. HDC hdc
  348. ) {
  349. RECT rc;
  350. INT count;
  351. HBRUSH hbrOld;
  352. LV_HITTESTINFO lvhi;
  353. //
  354. // Retrieve the rectangle to be erased
  355. //
  356. GetClipBox(hdc, &rc);
  357. TRACEX4(
  358. TLTRACE, "WM_ERASEBKGND: ClipBox: (%d, %d) (%d %d)",
  359. rc.left, rc.top, rc.right, rc.bottom
  360. );
  361. //
  362. // Retrieve the count of listview items.
  363. // This is necessary because the smooth-scrolling code triggers
  364. // a repaint inside the ListView_DeleteItem() processing,
  365. // at which point our indices may be out of sync (i.e. we have more items
  366. // than the listview).
  367. // The count retrieved is used to do a sanity-check
  368. // on the treelist-item indices below.
  369. //
  370. count = ListView_GetItemCount(ptl->hwndList);
  371. TRACEX1(TLTRACE, "WM_ERASEBKGND: Count: %d", count);
  372. //
  373. // If there are no treelist items, we always have to erase.
  374. // If there are treelist items, we only have to erase
  375. // if part of the erase-region lies below our lowest item.
  376. //
  377. while (!IsListEmpty(&ptl->root.lhChildren)) { // one-time loop
  378. RECT rctop;
  379. INT iTopIndex;
  380. INT cyUpdate;
  381. TLITEM *pItem;
  382. LIST_ENTRY *phead;
  383. //
  384. // We need to factor in the height of the header-control, if any;
  385. // to this end, we get the bounding rectangle of the topmost item
  386. // visible in the listview, and then we use the top of that item
  387. // as the basis for our computations below
  388. //
  389. iTopIndex = ListView_GetTopIndex(ptl->hwndList);
  390. TRACEX1(TLTRACE, "WM_ERASEBKGND: TopIndex: %d", iTopIndex);
  391. ListView_GetItemRect(ptl->hwndList, iTopIndex, &rctop, LVIR_BOUNDS);
  392. TRACEX1(TLTRACE, "WM_ERASEBKGND: rctop.top: %d", rctop.top);
  393. rc.top = rctop.top;
  394. //
  395. // If the area to be erased extends further right in the window
  396. // than our items do, we'll have to erase
  397. //
  398. if (rctop.right < rc.right) {
  399. TRACEX2(
  400. TLTRACE, "WM_ERASEBKGND: rctop.right < rc.right (%d < %d)",
  401. rctop.right, rc.right
  402. );
  403. break;
  404. }
  405. //
  406. // Get the total height of the area to be updated;
  407. // this excludes the area occupied by the header-control.
  408. //
  409. cyUpdate = rc.bottom - rctop.top;
  410. TRACEX1(TLTRACE, "WM_ERASEBKGND: CyUpdate: %d", cyUpdate);
  411. //
  412. // Get the lowest item; it is the one at the tail of the item-list
  413. //
  414. phead = ptl->root.lhChildren.Blink;
  415. pItem = CONTAINING_RECORD(phead, TLITEM, leSiblings);
  416. TRACEX1(TLTRACE, "WM_ERASEBKGND: CyItem: %d", ptl->cyItem);
  417. //
  418. // If the lowest item or one of its visible descendants is lower
  419. // than the bottom of the update region, we don't have to erase;
  420. // therefore, we walk down the list of the lowest item's descendants,
  421. // checking each time whether the descendant is lower than the region
  422. // we've been asked to erase.
  423. //
  424. do {
  425. TRACEX1(
  426. TLTRACE, "WM_ERASEBKGND: pItem->iIndex: %d", pItem->iIndex
  427. );
  428. //
  429. // force the erasure if the item's index is higher
  430. // than the number of listview items
  431. //
  432. if (pItem->iIndex >= count) { break; }
  433. //
  434. // defer the erasure if the item is lower than the bottom
  435. // of the update-rect
  436. //
  437. if ((pItem->iIndex - iTopIndex + 1) * (INT)ptl->cyItem > cyUpdate) {
  438. TRACEX(TLTRACE, "WM_ERASEBKGND: DEFERRING");
  439. return;
  440. }
  441. //
  442. // move on to the item's lowest child;
  443. // if it has none, it means the erase-region's lowest edge
  444. // is lower than our lowest item, and that means
  445. // that we'll have to erase it now instead of just letting it
  446. // get updated when we handle the WM_DRAWITEM
  447. //
  448. if (IsListEmpty(&pItem->lhChildren)) { pItem = NULL; }
  449. else {
  450. phead = pItem->lhChildren.Blink;
  451. pItem = CONTAINING_RECORD(phead, TLITEM, leSiblings);
  452. }
  453. } while (pItem && TL_IsVisible(pItem));
  454. break;
  455. }
  456. //
  457. // One of the points was not on an item, so erase
  458. //
  459. TRACEX(TLTRACE, "WM_ERASEBKGND: ERASING");
  460. hbrOld = SelectObject(hdc, ptl->hbrBk);
  461. PatBlt(
  462. hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY
  463. );
  464. SelectObject(hdc, hbrOld);
  465. }
  466. //----------------------------------------------------------------------------
  467. // Function: TL_OnDrawItem
  468. //
  469. // This is called by the listview when an item needs to be drawn.
  470. //----------------------------------------------------------------------------
  471. BOOL
  472. TL_OnDrawItem(
  473. TL *ptl,
  474. CONST DRAWITEMSTRUCT *pdis
  475. ) {
  476. //
  477. // make sure this is from our listview
  478. //
  479. if (pdis->CtlType != ODT_LISTVIEW) { return FALSE; }
  480. switch (pdis->itemAction) {
  481. //
  482. // currently listviews always send ODA_DRAWENTIRE,
  483. // but handle all cases anyway
  484. //
  485. case ODA_DRAWENTIRE:
  486. case ODA_FOCUS:
  487. case ODA_SELECT:
  488. return TL_DrawItem(ptl, pdis);
  489. }
  490. return TRUE;
  491. }
  492. //----------------------------------------------------------------------------
  493. // Function: TL_DrawItem
  494. //
  495. // This function does the actual drawing for a treelist item
  496. //----------------------------------------------------------------------------
  497. BOOL
  498. TL_DrawItem(
  499. TL *ptl,
  500. CONST DRAWITEMSTRUCT *pdis
  501. ) {
  502. HDC hdcMem;
  503. TCHAR *psz;
  504. RECT rc, rcItem;
  505. HBITMAP hbmpOld;
  506. HIMAGELIST himl;
  507. TLSUBITEM *pSubitem;
  508. HFONT hfont, hfontOld;
  509. TLITEM *pItem, *pParent;
  510. LIST_ENTRY *ple, *phead;
  511. INT cxIndent, cxImage, cyImage, i, tx, x, y, xcol;
  512. //
  513. // the itemData contains the lParam passed in ListView_InsertItem;
  514. // this lParam is the TLITEM pointer for the tree-item, so we retrieve it
  515. // and use the information it contains to draw the item
  516. //
  517. cxIndent = ptl->cxIndent;
  518. pItem = (TLITEM *)pdis->itemData;
  519. rcItem.left = 0; rcItem.top = 0;
  520. rcItem.right = pdis->rcItem.right - pdis->rcItem.left;
  521. rcItem.bottom = pdis->rcItem.bottom - pdis->rcItem.top;
  522. //
  523. // create a compatible DC
  524. //
  525. hdcMem = CreateCompatibleDC(pdis->hDC);
  526. if(NULL == hdcMem)
  527. {
  528. return FALSE;
  529. }
  530. if (ptl->hbmpMem) {
  531. if (rcItem.right > (INT)ptl->cxBmp || rcItem.bottom > (INT)ptl->cyBmp) {
  532. DeleteObject(ptl->hbmpMem); ptl->hbmpMem = NULL;
  533. }
  534. }
  535. if (!ptl->hbmpMem) {
  536. ptl->hbmpMem = CreateCompatibleBitmap(
  537. pdis->hDC, rcItem.right, rcItem.bottom
  538. );
  539. ptl->cxBmp = rcItem.right;
  540. ptl->cyBmp = rcItem.bottom;
  541. }
  542. hbmpOld = SelectObject(hdcMem, ptl->hbmpMem);
  543. hfontOld = SelectObject(hdcMem, GetWindowFont(pdis->hwndItem));
  544. //
  545. // erase the background
  546. //
  547. #if 0
  548. ptl->hbrBk = FORWARD_WM_CTLCOLOREDIT(
  549. ptl->hwndParent, hdcMem, ptl->hwnd, SendMessage
  550. );
  551. #endif
  552. FillRect(hdcMem, &rcItem, ptl->hbrBk);
  553. //
  554. // set the text background color based on whether or not
  555. // the item is selected
  556. //
  557. if (pdis->itemState & ODS_SELECTED) {
  558. SetTextColor(hdcMem, GetSysColor(COLOR_HIGHLIGHTTEXT));
  559. SetBkColor(hdcMem, GetSysColor(COLOR_HIGHLIGHT));
  560. }
  561. else {
  562. SetTextColor(hdcMem, GetSysColor(COLOR_WINDOWTEXT));
  563. SetBkColor(hdcMem, GetSysColor(COLOR_WINDOW));
  564. }
  565. //
  566. // compute the starting position as a multiple of
  567. // the item's level and the indentation per level
  568. //
  569. x = rcItem.left + pItem->iLevel * cxIndent;
  570. y = rcItem.top;
  571. xcol = rcItem.left + ListView_GetColumnWidth(pdis->hwndItem, 0);
  572. tx = x;
  573. x += cxIndent;
  574. //
  575. // now draw the item's tree image;
  576. // only draw as much as will fit in the first column
  577. //
  578. if (tx < xcol) {
  579. BitBlt(
  580. hdcMem, tx, y, min(cxIndent, xcol - tx), ptl->cyItem,
  581. ptl->hdcImages, pItem->iImage * cxIndent, 0, SRCCOPY
  582. );
  583. }
  584. //
  585. // draw the lines going down from the item's ancestors
  586. // to the item's ancestors' corresponding siblings;
  587. // in other words, for each ancestor which is not its parent's last child,
  588. // there should be a line going down from that ancestor to its next sibling
  589. // and the line will pass through the rows for all of that item's expanded
  590. // descendants.
  591. // note that we do not draw lines at the root-level
  592. //
  593. pParent = pItem->pParent;
  594. for (i = pItem->iLevel - 1, tx -= cxIndent; i > 0; i--, tx -= cxIndent) {
  595. if (tx < xcol &&
  596. pParent->leSiblings.Flink != &pParent->pParent->lhChildren) {
  597. BitBlt(
  598. hdcMem, tx, y, min(cxIndent, xcol - tx), ptl->cyItem,
  599. ptl->hdcImages, TL_VerticalLine * cxIndent, 0, SRCCOPY
  600. );
  601. }
  602. pParent = pParent->pParent;
  603. }
  604. //
  605. // draw the state image, if there is one,
  606. // and increment the left position by the width of the image
  607. //
  608. himl = ListView_GetImageList(pdis->hwndItem, LVSIL_STATE);
  609. if (himl != NULL && TL_StateImageValue(pItem)) {
  610. ImageList_GetIconSize(himl, &cxImage, &cyImage);
  611. ImageList_Draw(
  612. himl, TL_StateImageIndex(pItem), hdcMem,
  613. x, y + (ptl->cyItem - cyImage), ILD_NORMAL
  614. );
  615. x += cxImage;
  616. }
  617. //
  618. // draw the image, if there is an image list,
  619. // and increment the left position by the width of the image
  620. //
  621. himl = ListView_GetImageList(pdis->hwndItem, LVSIL_SMALL);
  622. if (himl != NULL && (pItem->lvi.mask & LVIF_IMAGE)) {
  623. ImageList_GetIconSize(himl, &cxImage, &cyImage);
  624. ImageList_Draw(
  625. himl, pItem->lvi.iImage, hdcMem,
  626. x, y + (ptl->cyItem - cyImage) / 2, ILD_NORMAL
  627. );
  628. x += cxImage;
  629. }
  630. //
  631. // compute the rectangle in the first column
  632. // which will be the clipping boundary for text
  633. //
  634. rc.left = x;
  635. rc.right = xcol;
  636. rc.top = rcItem.top;
  637. rc.bottom = rcItem.bottom;
  638. //
  639. // draw the first column's text
  640. //
  641. if (pItem->lvi.mask & LVIF_TEXT) {
  642. //
  643. // center the text vertically in the item-rectangle
  644. //
  645. psz = Ellipsisize(hdcMem, pItem->pszText, rc.right - rc.left, 0);
  646. ExtTextOut(
  647. hdcMem, rc.left + 2, rc.top + (ptl->cyItem - ptl->cyText) / 2,
  648. ETO_CLIPPED | ETO_OPAQUE, &rc, psz ? psz : pItem->pszText,
  649. lstrlen(psz ? psz : pItem->pszText), NULL
  650. );
  651. Free0(psz);
  652. }
  653. //
  654. // draw the subitems' texts
  655. //
  656. i = 1;
  657. phead = &pItem->lhSubitems;
  658. for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
  659. pSubitem = (TLSUBITEM *)CONTAINING_RECORD(ple, TLSUBITEM, leItems);
  660. //
  661. // we need to draw blank texts for subitems which have not been set;
  662. // this enables us to save memory on items which don't have
  663. // certain subitems set
  664. //
  665. for ( ; i < pSubitem->iSubItem; i++) {
  666. rc.left = rc.right;
  667. rc.right = rc.left + ListView_GetColumnWidth(pdis->hwndItem, i);
  668. ExtTextOut(
  669. hdcMem, rc.left + 2, rc.top + (ptl->cyItem - ptl->cyText) / 2,
  670. ETO_CLIPPED | ETO_OPAQUE, &rc, TEXT(""), 0, NULL
  671. );
  672. }
  673. //
  674. // now draw the text for the current item
  675. //
  676. rc.left = rc.right;
  677. rc.right = rc.left + ListView_GetColumnWidth(
  678. pdis->hwndItem, pSubitem->iSubItem
  679. );
  680. psz = Ellipsisize(hdcMem, pSubitem->pszText, rc.right - rc.left, 0);
  681. ExtTextOut(
  682. hdcMem, rc.left + 2, rc.top + (ptl->cyItem - ptl->cyText) / 2,
  683. ETO_CLIPPED | ETO_OPAQUE, &rc, psz ? psz : pSubitem->pszText,
  684. lstrlen(psz ? psz : pSubitem->pszText), NULL
  685. );
  686. Free0(psz);
  687. ++i;
  688. }
  689. //
  690. // we need to draw blank texts for subitems which have not been set
  691. //
  692. for ( ; i < (INT)ptl->nColumns; i++) {
  693. rc.left = rc.right;
  694. rc.right = rc.left + ListView_GetColumnWidth(pdis->hwndItem, i);
  695. ExtTextOut(
  696. hdcMem, rc.left + 2, rc.top + (ptl->cyItem - ptl->cyText) / 2,
  697. ETO_CLIPPED | ETO_OPAQUE, &rc, TEXT(""), 0, NULL
  698. );
  699. }
  700. //
  701. // restore the original background and text color
  702. //
  703. #if 0
  704. if (pdis->itemState & ODS_SELECTED) {
  705. SetTextColor(pdis->hDC, GetSysColor(COLOR_WINDOWTEXT));
  706. SetBkColor(pdis->hDC, GetSysColor(COLOR_WINDOW));
  707. }
  708. #endif
  709. //
  710. // draw the focus rectangle if necessary
  711. //
  712. if (pdis->itemState & ODS_FOCUS) {
  713. rc = rcItem;
  714. rc.left = min(x, xcol);
  715. DrawFocusRect(hdcMem, &rc);
  716. }
  717. //
  718. // Blt the changes to the screen DC
  719. //
  720. BitBlt(
  721. pdis->hDC, pdis->rcItem.left, pdis->rcItem.top, rcItem.right,
  722. rcItem.bottom, hdcMem, rcItem.left, rcItem.top, SRCCOPY
  723. );
  724. SelectObject(hdcMem, hbmpOld);
  725. SelectObject(hdcMem, hfontOld);
  726. DeleteDC(hdcMem);
  727. return TRUE;
  728. }
  729. //----------------------------------------------------------------------------
  730. // Function: TL_OnMeasureItem
  731. //
  732. // This is called by the listview when it needs to know
  733. // the height of each item; we use this opportunity to rebuild
  734. // the bitmap which holds images used for drawing tree lines.
  735. //
  736. // TODO: the listview currently seems to ignore the value we set,
  737. // and instead uses the height of a small icon (SM_CYSMICON).
  738. //----------------------------------------------------------------------------
  739. VOID
  740. TL_OnMeasureItem(
  741. TL *ptl,
  742. MEASUREITEMSTRUCT *pmis
  743. ) {
  744. HDC hdc;
  745. INT cyIcon;
  746. HFONT hfont;
  747. TEXTMETRIC tm;
  748. HWND hwndList;
  749. if (pmis->CtlType != ODT_LISTVIEW) { return; }
  750. //
  751. // retrieve the listview, its font, and its device context
  752. //
  753. hwndList = GetDlgItem(ptl->hwnd, pmis->CtlID);
  754. hfont = GetWindowFont(hwndList);
  755. hdc = GetDC(hwndList);
  756. if(NULL == hdc)
  757. {
  758. return;
  759. }
  760. SelectObject(hdc, hfont);
  761. //
  762. // get the height of the listview's font
  763. //
  764. if (!GetTextMetrics(hdc, &tm))
  765. {
  766. ReleaseDC(hwndList, hdc);
  767. return;
  768. }
  769. ptl->cyText = tm.tmHeight;
  770. pmis->itemHeight = ptl->cyText;
  771. //
  772. // make sure the item height is at least as high as a small icon
  773. //
  774. cyIcon = GetSystemMetrics(SM_CYSMICON);
  775. if (pmis->itemHeight < (UINT)cyIcon) {
  776. pmis->itemHeight = cyIcon;
  777. }
  778. pmis->itemHeight += GetSystemMetrics(SM_CYBORDER);
  779. #if 0
  780. pmis->itemHeight = (pmis->itemHeight + 1) & ~1;
  781. #endif
  782. ptl->cyItem = pmis->itemHeight;
  783. ptl->cxIndent = GetSystemMetrics(SM_CXSMICON);
  784. ReleaseDC(hwndList, hdc);
  785. //
  786. // rebuild the images used in drawing tree lines
  787. //
  788. TL_CreateTreeImages(ptl);
  789. }
  790. //----------------------------------------------------------------------------
  791. // Function: TL_CreateColorBitmap
  792. //
  793. // Utility function fro creating a color bitmap
  794. //----------------------------------------------------------------------------
  795. HBITMAP
  796. TL_CreateColorBitmap(
  797. INT cx,
  798. INT cy
  799. ) {
  800. HDC hdc;
  801. HBITMAP hbmp;
  802. hdc = GetDC(NULL);
  803. if(NULL == hdc)
  804. {
  805. return NULL;
  806. }
  807. hbmp = CreateCompatibleBitmap(hdc, cx, cy);
  808. ReleaseDC(NULL, hdc);
  809. return hbmp;
  810. }
  811. //----------------------------------------------------------------------------
  812. // Function: TL_CreateTreeImages
  813. //
  814. // This function builds a list of images which are scaled to
  815. // the height of each item in the tree. The appearance of the images
  816. // is shown in ASCII text in the code below
  817. //----------------------------------------------------------------------------
  818. VOID
  819. TL_CreateTreeImages(
  820. TL *ptl
  821. ) {
  822. HDC hdc;
  823. RECT rc;
  824. HBITMAP hbmpOld;
  825. INT cxIndent, x, c, xmid, ymid;
  826. HBRUSH hbrOld, hbrGrayText, hbrWinText;
  827. //
  828. // invalidate the listview's client area, to force a redraw
  829. //
  830. if (ptl->hwndList != NULL) { InvalidateRect(ptl->hwndList, NULL, TRUE); }
  831. //
  832. // create a device context if necessary
  833. //
  834. if (ptl->hdcImages == NULL) {
  835. ptl->hdcImages = CreateCompatibleDC(NULL);
  836. if(NULL == ptl->hdcImages)
  837. {
  838. return;
  839. }
  840. }
  841. hdc = ptl->hdcImages;
  842. cxIndent = ptl->cxIndent;
  843. ptl->hbrBk = FORWARD_WM_CTLCOLOREDIT(
  844. ptl->hwndParent, hdc, ptl->hwnd, SendMessage
  845. );
  846. //
  847. // create the bitmap to be used
  848. //
  849. hbmpOld = ptl->hbmp;
  850. ptl->hbmp = TL_CreateColorBitmap(TL_ImageCount * cxIndent, ptl->cyItem);
  851. if (hbmpOld == NULL) {
  852. ptl->hbmpStart = SelectObject(hdc, ptl->hbmp);
  853. }
  854. else {
  855. SelectObject(hdc, ptl->hbmp);
  856. DeleteObject(hbmpOld);
  857. }
  858. //
  859. // retreive system color brushes for drawing the tree images
  860. //
  861. hbrWinText = GetSysColorBrush(COLOR_WINDOWTEXT);
  862. hbrGrayText = GetSysColorBrush(COLOR_GRAYTEXT);
  863. hbrOld = SelectObject(hdc, hbrGrayText);
  864. rc.top = 0; rc.bottom = ptl->cyItem;
  865. rc.left = 0; rc.right = TL_ImageCount * cxIndent;
  866. //
  867. // fill the image with the background color
  868. //
  869. FillRect(hdc, &rc, ptl->hbrBk);
  870. xmid = cxIndent / 2;
  871. ymid = ((ptl->cyItem / 2) + 1) & ~1;
  872. c = min(xmid, ymid) / 2;
  873. // |
  874. // |
  875. x = TL_VerticalLine * cxIndent;
  876. TL_DottedLine(hdc, x + xmid, 0, ptl->cyItem, TRUE);
  877. //
  878. // ---
  879. //
  880. x = TL_RootChildless * cxIndent;
  881. TL_DottedLine(hdc, x + xmid, ymid, cxIndent - xmid, FALSE);
  882. //
  883. // +-+
  884. // |+|--
  885. // +-+
  886. //
  887. x = TL_RootParentCollapsed * cxIndent;
  888. TL_DottedLine(hdc, x + xmid, ymid, cxIndent - xmid, FALSE);
  889. TL_DrawButton(
  890. hdc, x + xmid, ymid, c, hbrWinText, hbrGrayText, ptl->hbrBk, TRUE
  891. );
  892. //
  893. // +-+
  894. // |-|--
  895. // +-+
  896. //
  897. x = TL_RootParentExpanded * cxIndent;
  898. SelectObject(hdc, hbrGrayText);
  899. TL_DottedLine(hdc, x + xmid, ymid, cxIndent - xmid, FALSE);
  900. TL_DrawButton(
  901. hdc, x + xmid, ymid, c, hbrWinText, hbrGrayText, ptl->hbrBk, FALSE
  902. );
  903. //
  904. // |
  905. // +--
  906. // |
  907. //
  908. x = TL_MidChildless * cxIndent;
  909. SelectObject(hdc, hbrGrayText);
  910. TL_DottedLine(hdc, x + xmid, 0, ptl->cyItem, TRUE);
  911. TL_DottedLine(hdc, x + xmid, ymid, cxIndent - xmid, FALSE);
  912. //
  913. // |
  914. // +-+
  915. // |+|--
  916. // +-+
  917. // |
  918. //
  919. x = TL_MidParentCollapsed * cxIndent;
  920. TL_DottedLine(hdc, x + xmid, 0, ptl->cyItem, TRUE);
  921. TL_DottedLine(hdc, x + xmid, ymid, cxIndent - xmid, FALSE);
  922. TL_DrawButton(
  923. hdc, x + xmid, ymid, c, hbrWinText, hbrGrayText, ptl->hbrBk, TRUE
  924. );
  925. //
  926. // |
  927. // +-+
  928. // |-|--
  929. // +-+
  930. // |
  931. //
  932. x = TL_MidParentExpanded * cxIndent;
  933. SelectObject(hdc, hbrGrayText);
  934. TL_DottedLine(hdc, x + xmid, 0, ptl->cyItem, TRUE);
  935. TL_DottedLine(hdc, x + xmid, ymid, cxIndent - xmid, FALSE);
  936. TL_DrawButton(
  937. hdc, x + xmid, ymid, c, hbrWinText, hbrGrayText, ptl->hbrBk, FALSE
  938. );
  939. //
  940. // |
  941. // +--
  942. //
  943. x = TL_EndChildless * cxIndent;
  944. SelectObject(hdc, hbrGrayText);
  945. TL_DottedLine(hdc, x + xmid, 0, ymid, TRUE);
  946. TL_DottedLine(hdc, x + xmid, ymid, cxIndent - xmid, FALSE);
  947. //
  948. // |
  949. // +-+
  950. // |+|--
  951. // +-+
  952. //
  953. x = TL_EndParentCollapsed * cxIndent;
  954. TL_DottedLine(hdc, x + xmid, 0, ymid, TRUE);
  955. TL_DottedLine(hdc, x + xmid, ymid, cxIndent - xmid, FALSE);
  956. TL_DrawButton(
  957. hdc, x + xmid, ymid, c, hbrWinText, hbrGrayText, ptl->hbrBk, TRUE
  958. );
  959. //
  960. // |
  961. // +-+
  962. // |-|--
  963. // +-+
  964. //
  965. x = TL_EndParentExpanded * cxIndent;
  966. SelectObject(hdc, hbrGrayText);
  967. TL_DottedLine(hdc, x + xmid, 0, ymid, TRUE);
  968. TL_DottedLine(hdc, x + xmid, ymid, cxIndent - xmid, FALSE);
  969. TL_DrawButton(
  970. hdc, x + xmid, ymid, c, hbrWinText, hbrGrayText, ptl->hbrBk, FALSE
  971. );
  972. if (hbrOld != NULL) {
  973. SelectObject(hdc, hbrOld);
  974. }
  975. DeleteObject(hbrGrayText);
  976. DeleteObject(hbrWinText);
  977. }
  978. //----------------------------------------------------------------------------
  979. // Function: TL_DottedLine
  980. //
  981. // Draws a dotted line eiter vertically or horizontally,
  982. // with the specified dimension as its length.
  983. //----------------------------------------------------------------------------
  984. VOID
  985. TL_DottedLine(
  986. HDC hdc,
  987. INT x,
  988. INT y,
  989. INT dim,
  990. BOOL fVertical
  991. ) {
  992. for ( ; dim > 0; dim -= 2) {
  993. PatBlt(hdc, x, y, 1, 1, PATCOPY);
  994. if (fVertical) {
  995. y += 2;
  996. }
  997. else {
  998. x += 2;
  999. }
  1000. }
  1001. }
  1002. //----------------------------------------------------------------------------
  1003. // Function: TL_DrawButton
  1004. //
  1005. // Draws a button with a plus or a minus, centered at the given location
  1006. //----------------------------------------------------------------------------
  1007. VOID
  1008. TL_DrawButton(
  1009. HDC hdc,
  1010. INT x,
  1011. INT y,
  1012. INT dim,
  1013. HBRUSH hbrSign,
  1014. HBRUSH hbrBox,
  1015. HBRUSH hbrBk,
  1016. BOOL bCollapsed
  1017. ) {
  1018. int n;
  1019. int p = (dim * 7) / 10;
  1020. n = p * 2 + 1;
  1021. //
  1022. // first fill with the background color
  1023. //
  1024. SelectObject(hdc, hbrBk);
  1025. PatBlt(hdc, x - dim, y - dim, dim * 2, dim * 2, PATCOPY);
  1026. //
  1027. // draw the sign
  1028. //
  1029. SelectObject(hdc, hbrSign);
  1030. if (p >= 5) {
  1031. PatBlt(hdc, x - p, y - 1, n, 3, PATCOPY);
  1032. if (bCollapsed) {
  1033. PatBlt(hdc, x - 1, y - p, 3, n, PATCOPY);
  1034. }
  1035. SelectObject(hdc, hbrBk);
  1036. p--;
  1037. n -= 2;
  1038. }
  1039. PatBlt(hdc, x - p, y, n, 1, PATCOPY);
  1040. if (bCollapsed) {
  1041. PatBlt(hdc, x, y - p, 1, n, PATCOPY);
  1042. }
  1043. n = dim * 2 + 1;
  1044. //
  1045. // draw the box around the sign
  1046. //
  1047. SelectObject(hdc, hbrBox);
  1048. PatBlt(hdc, x - dim, y - dim, n, 1, PATCOPY);
  1049. PatBlt(hdc, x - dim, y - dim, 1, n, PATCOPY);
  1050. PatBlt(hdc, x - dim, y + dim, n, 1, PATCOPY);
  1051. PatBlt(hdc, x + dim, y - dim, 1, n, PATCOPY);
  1052. }
  1053. //----------------------------------------------------------------------------
  1054. // Function: TL_UpdateListIndices
  1055. //
  1056. // This function updates the indices for all items in the tree
  1057. // which are visually below the specified item pStart, assuming that
  1058. // the list index for pStart is correct. Consider the case
  1059. // in the diagram below:
  1060. //
  1061. // -- child 1
  1062. // | |- child 1,1
  1063. // | -- child 1,2
  1064. // |- child 2
  1065. // -- child 3
  1066. // | |- child 3,1
  1067. // | | |- child 3,1,1
  1068. // | | -- child 3,1,2
  1069. // | |- child 3,2
  1070. // | -- child 3,3
  1071. // -- child 4
  1072. //
  1073. // Suppose that pStart points to "child 3,1". To set the indices,
  1074. // we first update the indices for all descendants of pStart,
  1075. // and then we update the indices for the siblings of pStart's ancestors
  1076. // which are after pStart's ancestors in the tree.
  1077. //----------------------------------------------------------------------------
  1078. VOID
  1079. TL_UpdateListIndices(
  1080. TL *ptl,
  1081. TLITEM *pStart
  1082. ) {
  1083. INT iIndex;
  1084. iIndex = pStart->iIndex;
  1085. if (pStart->nChildren > 0) {
  1086. //
  1087. // if the item is visible, set its index;
  1088. // otherwise pass in NULL to set its index
  1089. // and that of its descendants to -1
  1090. //
  1091. if (TL_IsExpanded(pStart) &&
  1092. (pStart == &ptl->root || TL_IsVisible(pStart))) {
  1093. TL_UpdateDescendantIndices(ptl, pStart, &iIndex);
  1094. }
  1095. else {
  1096. TL_UpdateDescendantIndices(ptl, pStart, NULL);
  1097. }
  1098. }
  1099. if (pStart->pParent != NULL) {
  1100. TL_UpdateAncestorIndices(ptl, pStart, &iIndex);
  1101. }
  1102. ptl->root.iIndex = -1;
  1103. }
  1104. //----------------------------------------------------------------------------
  1105. // Function: TL_UpdateDescendantIndices
  1106. //
  1107. // This function updates the indices of the descendants
  1108. // of the item specified. An item is not considered to be
  1109. // a descendant of itself.
  1110. //----------------------------------------------------------------------------
  1111. VOID
  1112. TL_UpdateDescendantIndices(
  1113. TL *ptl,
  1114. TLITEM *pStart,
  1115. INT *piIndex
  1116. ) {
  1117. //
  1118. // go through list of children setting indices
  1119. //
  1120. TLITEM *pItem;
  1121. LIST_ENTRY *ple;
  1122. for (ple = pStart->lhChildren.Flink;
  1123. ple != &pStart->lhChildren; ple = ple->Flink) {
  1124. pItem = (TLITEM *)CONTAINING_RECORD(ple, TLITEM, leSiblings);
  1125. //
  1126. // set the index of the child
  1127. //
  1128. pItem->iIndex = (piIndex ? ++(*piIndex) : -1);
  1129. //
  1130. // set the indices of the child's descendants
  1131. //
  1132. if (pItem->nChildren > 0) {
  1133. //
  1134. // if the item is visible, set its index;
  1135. // otherwise pass in NULL to set its index
  1136. // and that of its descendants to -1
  1137. //
  1138. if (TL_IsExpanded(pItem) && TL_IsVisible(pItem)) {
  1139. TL_UpdateDescendantIndices(ptl, pItem, piIndex);
  1140. }
  1141. else {
  1142. TL_UpdateDescendantIndices(ptl, pItem, NULL);
  1143. }
  1144. }
  1145. }
  1146. }
  1147. //----------------------------------------------------------------------------
  1148. // Function: TL_UpdateAncestorIndices
  1149. //
  1150. // This function updates the indices of the items which are
  1151. // visually below the specified item in the listview.
  1152. //----------------------------------------------------------------------------
  1153. VOID
  1154. TL_UpdateAncestorIndices(
  1155. TL *ptl,
  1156. TLITEM *pStart,
  1157. INT *piIndex
  1158. ) {
  1159. TLITEM *pItem;
  1160. LIST_ENTRY *ple;
  1161. //
  1162. // first set inidices for the siblings beneath this item;
  1163. // note that we begin walking the siblings AFTER the item passed in,
  1164. //
  1165. for (ple = pStart->leSiblings.Flink;
  1166. ple != &pStart->pParent->lhChildren;
  1167. ple = ple->Flink) {
  1168. pItem = (TLITEM *)CONTAINING_RECORD(ple, TLITEM, leSiblings);
  1169. //
  1170. // set the index for the sibling
  1171. //
  1172. pItem->iIndex = (piIndex ? ++(*piIndex) : -1);
  1173. if (pItem->nChildren > 0) {
  1174. //
  1175. // if the item is visible, set its index;
  1176. // otherwise pass in NULL to set its index
  1177. // and that of its descendants to -1
  1178. //
  1179. if (TL_IsExpanded(pItem) && TL_IsVisible(pItem)) {
  1180. TL_UpdateDescendantIndices(ptl, pItem, piIndex);
  1181. }
  1182. else {
  1183. TL_UpdateDescendantIndices(ptl, pItem, NULL);
  1184. }
  1185. }
  1186. }
  1187. //
  1188. // now set indices for the parent siblings which are beneath the parent
  1189. //
  1190. // TODO - OPTIMIZATION: this is post-recursion and therefore, it can
  1191. // be replaced by a loop, which at the very least would save stack
  1192. // space
  1193. //
  1194. if (pStart->pParent->pParent != NULL) {
  1195. TL_UpdateAncestorIndices(ptl, pStart->pParent, piIndex);
  1196. }
  1197. }
  1198. //----------------------------------------------------------------------------
  1199. // Function: TL_NotifyParent
  1200. //
  1201. // Forwards a notification to the treelist's parent
  1202. //----------------------------------------------------------------------------
  1203. BOOL
  1204. TL_NotifyParent(
  1205. TL *ptl,
  1206. NMHDR *pnmh
  1207. ) {
  1208. return (BOOL)SendMessage(
  1209. ptl->hwndParent, WM_NOTIFY, (WPARAM)ptl->hwnd, (LPARAM)pnmh
  1210. );
  1211. }
  1212. //----------------------------------------------------------------------------
  1213. // Function: TL_OnNotify
  1214. //
  1215. // Handles notifications from the listview window and its header control
  1216. //----------------------------------------------------------------------------
  1217. LRESULT
  1218. TL_OnNotify(
  1219. TL *ptl,
  1220. INT iCtrlId,
  1221. NMHDR *pnmh
  1222. ) {
  1223. NMHDR nmh;
  1224. TLITEM *pItem;
  1225. //
  1226. // notify parent of the message
  1227. //
  1228. if (TL_NotifyParent(ptl, pnmh)) { return FALSE; }
  1229. switch (pnmh->code) {
  1230. case HDN_ENDTRACK: {
  1231. //
  1232. // we need to redraw ourselves, AFTER the header resets;
  1233. // hence the use of PostMessage instead of SendMessage
  1234. //
  1235. PostMessage(ptl->hwnd, TLM_REDRAW, (WPARAM)0, (LPARAM)0);
  1236. return FALSE;
  1237. }
  1238. case NM_CLICK:
  1239. case NM_DBLCLK: {
  1240. //
  1241. // do a hit-test;
  1242. //
  1243. POINT pt;
  1244. INT iLeft;
  1245. LV_ITEM lvi;
  1246. LV_HITTESTINFO lvhi;
  1247. if (!GetCursorPos(&lvhi.pt)) { return FALSE; }
  1248. ScreenToClient(ptl->hwndList, &lvhi.pt);
  1249. if (ListView_HitTest(ptl->hwndList, &lvhi) == -1) { return FALSE; }
  1250. if (!(lvhi.flags & LVHT_ONITEM)) { return FALSE; }
  1251. //
  1252. // see which part of the item was clicked
  1253. //
  1254. if (!ListView_GetItemPosition(ptl->hwndList, lvhi.iItem, &pt)) {
  1255. return FALSE;
  1256. }
  1257. //
  1258. // retrieve the item clicked
  1259. //
  1260. lvi.iItem = lvhi.iItem;
  1261. lvi.iSubItem = 0;
  1262. lvi.mask = LVIF_PARAM;
  1263. if (!ListView_GetItem(ptl->hwndList, &lvi)) { return FALSE; }
  1264. pItem = (TLITEM *)lvi.lParam;
  1265. //
  1266. // compute the position of the item's tree image
  1267. //
  1268. iLeft = pItem->iLevel * ptl->cxIndent;
  1269. if (lvhi.pt.x > (pt.x + iLeft)) {
  1270. //
  1271. // the hit was to the right of the item's tree image
  1272. //
  1273. if (lvhi.pt.x < (pt.x + iLeft + (INT)ptl->cxIndent)) {
  1274. //
  1275. // the hit was on the item's tree image
  1276. //
  1277. if (pItem->nChildren > 0) {
  1278. //
  1279. // the +/- button was clicked, toggle expansion
  1280. //
  1281. return TL_OnExpand(ptl, TLE_TOGGLE, (HTLITEM)pItem);
  1282. }
  1283. }
  1284. else {
  1285. //
  1286. // the hit was on the item's state icon, image, or text
  1287. //
  1288. //
  1289. // see if the parent wants to handle it
  1290. //
  1291. nmh.code = pnmh->code;
  1292. nmh.idFrom = 0;
  1293. nmh.hwndFrom = ptl->hwnd;
  1294. TL_NotifyParent(ptl, &nmh);
  1295. if (nmh.idFrom != 0) { return TRUE; }
  1296. if (pnmh->code == NM_DBLCLK && pItem->nChildren > 0) {
  1297. //
  1298. // the item was double-clicked, toggle expansion
  1299. //
  1300. return TL_OnExpand(
  1301. ptl, TLE_TOGGLE, (HTLITEM)pItem
  1302. );
  1303. }
  1304. }
  1305. }
  1306. return FALSE;
  1307. }
  1308. case NM_RETURN: {
  1309. //
  1310. // get current selection;
  1311. // if a parent item, toggle expand-state
  1312. //
  1313. LV_ITEM lvi;
  1314. lvi.iItem = ListView_GetNextItem(ptl->hwndList, -1, LVNI_SELECTED);
  1315. if (lvi.iItem == -1) { return FALSE; }
  1316. lvi.iSubItem = 0;
  1317. lvi.mask = LVIF_PARAM;
  1318. if (!ListView_GetItem(ptl->hwndList, &lvi)) { return FALSE; }
  1319. pItem = (TLITEM *)lvi.lParam;
  1320. if (pItem->nChildren > 0) {
  1321. //
  1322. // the item has children, toggle expand state
  1323. //
  1324. return TL_OnExpand(ptl, TLE_TOGGLE, (HTLITEM)pItem);
  1325. }
  1326. return FALSE;
  1327. }
  1328. case LVN_KEYDOWN: {
  1329. //
  1330. // get key pressed and current selection;
  1331. // if a parent item and key is '+', expand;
  1332. // if key is '-' or left key, collapse
  1333. // if key is VK_RIGHT, expand and move to first child;
  1334. // if key is VK_LEFT, collapse parent and move to parent
  1335. //
  1336. LV_ITEM lvi;
  1337. LV_KEYDOWN *plvk;
  1338. plvk = (LV_KEYDOWN *)pnmh;
  1339. switch (plvk->wVKey) {
  1340. case VK_RIGHT:
  1341. case VK_ADD: {
  1342. //
  1343. // retrieve the item
  1344. //
  1345. lvi.iItem = ListView_GetNextItem(
  1346. ptl->hwndList, -1, LVNI_SELECTED
  1347. );
  1348. if (lvi.iItem == -1) { return FALSE; }
  1349. lvi.iSubItem = 0;
  1350. lvi.mask = LVIF_PARAM;
  1351. if (!ListView_GetItem(ptl->hwndList, &lvi)) {
  1352. return FALSE;
  1353. }
  1354. //
  1355. // expand the item if it is collapsed
  1356. //
  1357. pItem = (TLITEM *)lvi.lParam;
  1358. if (pItem->nChildren <= 0) { return FALSE; }
  1359. if (!TL_IsExpanded(pItem)) {
  1360. return TL_OnExpand(ptl, TLE_EXPAND, (HTLITEM)pItem);
  1361. }
  1362. else
  1363. if (plvk->wVKey == VK_RIGHT) {
  1364. //
  1365. // the key was VK_RIGHT,
  1366. // so we select the item's child
  1367. //
  1368. pItem = (TLITEM *)CONTAINING_RECORD(
  1369. pItem->lhChildren.Flink, TLITEM, leSiblings
  1370. );
  1371. if (TL_OnSetSelection(ptl, (HTLITEM)pItem)) {
  1372. return ListView_EnsureVisible(
  1373. ptl->hwndList, pItem->iIndex, FALSE
  1374. );
  1375. }
  1376. }
  1377. break;
  1378. }
  1379. case VK_LEFT:
  1380. case VK_SUBTRACT: {
  1381. //
  1382. // retrieve the current selection
  1383. //
  1384. lvi.iItem = ListView_GetNextItem(
  1385. ptl->hwndList, -1, LVNI_SELECTED
  1386. );
  1387. if (lvi.iItem == -1) { return FALSE; }
  1388. lvi.iSubItem = 0;
  1389. lvi.mask = LVIF_PARAM;
  1390. if (!ListView_GetItem(ptl->hwndList, &lvi)) {
  1391. return FALSE;
  1392. }
  1393. //
  1394. // collapse the item if it is expanded;
  1395. // otherwise, if the key is VK_LEFT,
  1396. // select the item's parent
  1397. //
  1398. pItem = (TLITEM *)lvi.lParam;
  1399. if (pItem->nChildren > 0) {
  1400. return TL_OnExpand(ptl, TLE_COLLAPSE, (HTLITEM)pItem);
  1401. }
  1402. else
  1403. if (plvk->wVKey == VK_LEFT &&
  1404. pItem->pParent != &ptl->root) {
  1405. if (TL_OnSetSelection(ptl, (HTLITEM)pItem->pParent)) {
  1406. return ListView_EnsureVisible(
  1407. ptl->hwndList, pItem->pParent->iIndex,
  1408. FALSE
  1409. );
  1410. }
  1411. }
  1412. break;
  1413. }
  1414. }
  1415. return FALSE;
  1416. }
  1417. case LVN_ITEMCHANGED: {
  1418. NMTREELIST nmtl;
  1419. NM_LISTVIEW *pnmlv;
  1420. pnmlv = (NM_LISTVIEW *)pnmh;
  1421. if ((pnmlv->uChanged & LVIF_STATE)) {
  1422. if (pnmlv->uNewState & LVIS_SELECTED) {
  1423. //
  1424. // the new state is selected;
  1425. // notify the parent that the selection has changed
  1426. //
  1427. nmtl.hdr.hwndFrom = ptl->hwnd;
  1428. nmtl.hdr.code = TLN_SELCHANGED;
  1429. nmtl.hItem = (HTLITEM)pnmlv->lParam;
  1430. nmtl.lParam = ((TLITEM *)nmtl.hItem)->lParam;
  1431. return TL_NotifyParent(ptl, (NMHDR *)&nmtl);
  1432. }
  1433. }
  1434. return FALSE;
  1435. }
  1436. case LVN_DELETEITEM: {
  1437. INT iItem;
  1438. LV_ITEM lvi;
  1439. TLITEM *pNext;
  1440. NM_LISTVIEW *pnmlv;
  1441. //
  1442. // get the item which is selected
  1443. //
  1444. pnmlv = (NM_LISTVIEW *)pnmh;
  1445. iItem = ListView_GetNextItem(ptl->hwndList, -1, LVNI_SELECTED);
  1446. if (iItem != -1) { return FALSE; }
  1447. //
  1448. // the deleted item was selected,
  1449. // so select another item
  1450. //
  1451. lvi.mask = LVIF_PARAM;
  1452. lvi.iItem = pnmlv->iItem;
  1453. lvi.iSubItem = 0;
  1454. if (!ListView_GetItem(ptl->hwndList, &lvi)) { return FALSE; }
  1455. pItem = (TLITEM *)lvi.lParam;
  1456. //
  1457. // choose sibling item before this one
  1458. //
  1459. pNext = (TLITEM *)TL_OnGetNextItem(
  1460. ptl, TLGN_PREVSIBLING, (HTLITEM)pItem
  1461. );
  1462. if (pNext == NULL) {
  1463. //
  1464. // that failed, so choose the sibling after this one
  1465. //
  1466. pNext = (TLITEM *)TL_OnGetNextItem(
  1467. ptl, TLGN_NEXTSIBLING, (HTLITEM)pItem
  1468. );
  1469. if (pNext == NULL) {
  1470. //
  1471. // that failed too, so choose the parent
  1472. // so long as the parent isn't the root
  1473. //
  1474. pNext = pItem->pParent;
  1475. if (pNext == &ptl->root) { return FALSE; }
  1476. }
  1477. }
  1478. return TL_OnSetSelection(ptl, (HTLITEM)pNext);
  1479. }
  1480. }
  1481. return FALSE;
  1482. }
  1483. //----------------------------------------------------------------------------
  1484. // Function: TL_UpdateImage
  1485. //
  1486. // This function updates the tree image for an item
  1487. // when the item's state changes; this is called when an item
  1488. // is inserted or deleted, or expanded or collapsed.
  1489. // Insertion or deletions can have side-effects, as follows:
  1490. //
  1491. // (1) an item is inserted as the child of a previously childless item;
  1492. // the parent's image changes to show a collapsed button
  1493. //
  1494. // (2) an item is inserted as the last child of a parent which
  1495. // had children; the image of the item which used to be
  1496. // the parent's last child changes:
  1497. // parent parent
  1498. // -- old ---> |- old
  1499. // -- new
  1500. //
  1501. // (3) the reverse of case 1, i.e. an item is removed which was
  1502. // the only child of a parent item; the parent's image changes
  1503. // to show that it is childless
  1504. //
  1505. // (4) the reverse of case 2, i.e. an item is removed which was
  1506. // the last child of a parent which has other children;
  1507. // the image of the item which will now be the last child changes
  1508. //
  1509. // In all of these cases, the item to which the side-effect occurs
  1510. // is written into ppChanged; so the caller can update the image
  1511. // for that item as well
  1512. //----------------------------------------------------------------------------
  1513. VOID
  1514. TL_UpdateImage(
  1515. TL *ptl,
  1516. TLITEM *pItem,
  1517. TLITEM **ppChanged
  1518. ) {
  1519. INT iImage;
  1520. TLITEM *pChanged;
  1521. if (ppChanged == NULL) { ppChanged = &pChanged; }
  1522. *ppChanged = NULL;
  1523. //
  1524. // special case for root-level items
  1525. //
  1526. if (pItem->pParent == &ptl->root) {
  1527. if (pItem->nChildren == 0) {
  1528. pItem->iImage = TL_RootChildless;
  1529. }
  1530. else {
  1531. pItem->iImage = TL_IsExpanded(pItem) ? TL_RootParentExpanded
  1532. : TL_RootParentCollapsed;
  1533. }
  1534. }
  1535. else
  1536. if (pItem->leSiblings.Flink == &pItem->pParent->lhChildren) {
  1537. //
  1538. // item is last of its parent's children
  1539. //
  1540. if (pItem->nChildren == 0) {
  1541. pItem->iImage = TL_EndChildless;
  1542. }
  1543. else {
  1544. pItem->iImage = TL_IsExpanded(pItem) ? TL_EndParentExpanded
  1545. : TL_EndParentCollapsed;
  1546. }
  1547. //
  1548. // if this is the only child, the parent was childless and
  1549. // its image should change; otherwise, the child before this one
  1550. // used to be the last child and its image should change
  1551. //
  1552. if (pItem->leSiblings.Blink == &pItem->pParent->lhChildren) {
  1553. *ppChanged = pItem->pParent;
  1554. }
  1555. else {
  1556. *ppChanged = (TLITEM *)CONTAINING_RECORD(
  1557. pItem->leSiblings.Blink, TLITEM, leSiblings
  1558. );
  1559. }
  1560. }
  1561. else {
  1562. //
  1563. // item is not last of its parent's children
  1564. //
  1565. if (pItem->nChildren == 0) {
  1566. pItem->iImage = TL_MidChildless;
  1567. }
  1568. else {
  1569. pItem->iImage = TL_IsExpanded(pItem) ? TL_MidParentExpanded
  1570. : TL_MidParentCollapsed;
  1571. }
  1572. }
  1573. return;
  1574. }
  1575. //----------------------------------------------------------------------------
  1576. // Function: TL_OnInsertItem
  1577. //
  1578. // Inserts an item with the properties specified in the given LV_ITEM,
  1579. // and returns a handle to the item inserted
  1580. //----------------------------------------------------------------------------
  1581. HTLITEM
  1582. TL_OnInsertItem(
  1583. TL *ptl,
  1584. TL_INSERTSTRUCT *ptlis
  1585. ) {
  1586. LV_ITEM *plvi;
  1587. LIST_ENTRY *ple, *phead;
  1588. BOOL bParentVisible;
  1589. TLITEM *pItem, *pChanged, *pTemp;
  1590. if (ptlis == NULL) { return NULL; }
  1591. //
  1592. // set up the new item
  1593. //
  1594. pItem = (TLITEM *)Malloc(sizeof(TLITEM));
  1595. if (pItem == NULL) { return NULL; }
  1596. ZeroMemory(pItem, sizeof(TLITEM));
  1597. if (ptlis->plvi->mask & LVIF_TEXT) {
  1598. pItem->pszText = StrDup(ptlis->plvi->pszText);
  1599. if (pItem->pszText == NULL) {
  1600. Free(pItem); return NULL;
  1601. }
  1602. }
  1603. //
  1604. // set up the private members
  1605. //
  1606. pItem->uiFlag = 0;
  1607. pItem->nChildren = 0;
  1608. InitializeListHead(&pItem->lhSubitems);
  1609. InitializeListHead(&pItem->lhChildren);
  1610. pItem->pParent = (TLITEM *)ptlis->hParent;
  1611. if (pItem->pParent == NULL) {
  1612. pItem->pParent = &ptl->root;
  1613. }
  1614. ++pItem->pParent->nChildren;
  1615. pItem->iLevel = pItem->pParent->iLevel + 1;
  1616. //
  1617. // set up the listview item
  1618. //
  1619. plvi = ptlis->plvi;
  1620. pItem->lvi = *plvi;
  1621. pItem->lParam = plvi->lParam;
  1622. pItem->lvi.lParam = (LPARAM)pItem;
  1623. pItem->lvi.pszText = pItem->pszText;
  1624. pItem->lvi.mask |= LVIF_PARAM;
  1625. //
  1626. // insert this item amongst its siblings
  1627. //
  1628. // switch (PtrToUlong(ptlis->hInsertAfter)) {
  1629. if(ptlis->hInsertAfter == TLI_FIRST)
  1630. {
  1631. InsertHeadList(&pItem->pParent->lhChildren, &pItem->leSiblings);
  1632. }
  1633. else if (ptlis->hInsertAfter == TLI_LAST)
  1634. {
  1635. InsertTailList(&pItem->pParent->lhChildren, &pItem->leSiblings);
  1636. }
  1637. else if (ptlis->hInsertAfter == TLI_SORT)
  1638. {
  1639. phead = &pItem->pParent->lhChildren;
  1640. for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
  1641. pTemp = (TLITEM *)CONTAINING_RECORD(ple, TLITEM, leSiblings);
  1642. if (lstrcmp(pItem->pszText, pTemp->pszText) < 0) {
  1643. break;
  1644. }
  1645. }
  1646. InsertTailList(ple, &pItem->leSiblings);
  1647. }
  1648. else
  1649. {
  1650. TLITEM *pPrev;
  1651. pPrev = (TLITEM *)ptlis->hInsertAfter;
  1652. InsertHeadList(&pPrev->leSiblings, &pItem->leSiblings);
  1653. }
  1654. //}
  1655. //
  1656. // set the item's image. if this was inserted
  1657. // as the last child, we need to change the image
  1658. // for the original last child, if any; otherwise,
  1659. // if this is the first child of its parent, the image
  1660. // for the parent must be changed
  1661. //
  1662. TL_UpdateImage(ptl, pItem, &pChanged);
  1663. if (pChanged != NULL) { TL_UpdateImage(ptl, pChanged, NULL); }
  1664. if (pItem->pParent != &ptl->root && !TL_IsVisible(pItem->pParent)) {
  1665. pItem->iIndex = -1;
  1666. }
  1667. else {
  1668. //
  1669. // the item's parent is visible;
  1670. // update the indices after the item's parent
  1671. //
  1672. TL_UpdateListIndices(ptl, pItem->pParent);
  1673. //
  1674. // insert the item in the list if its parent is expanded
  1675. //
  1676. if (TL_IsExpanded(pItem->pParent)) {
  1677. INT iItem, iCol;
  1678. //
  1679. // In owner-draw mode, there is a bug in the listview code
  1680. // where if an item has the focus but is not selected,
  1681. // and then a new item is inserted above it and selected,
  1682. // the focus rectangle remains on the item which had the focus
  1683. //
  1684. // To work around this, clear the focus if it is on the item
  1685. // below the item just inserted
  1686. //
  1687. iItem = ListView_GetNextItem(ptl->hwndList, -1, LVNI_FOCUSED);
  1688. pItem->lvi.iItem = pItem->iIndex;
  1689. pItem->lvi.iSubItem = 0;
  1690. ListView_InsertItem(ptl->hwndList, &pItem->lvi);
  1691. //
  1692. // if the item below this had the focus, clear the focus
  1693. //
  1694. if (iItem != -1 && iItem >= pItem->iIndex) {
  1695. ListView_SetItemState(
  1696. ptl->hwndList, -1, 0, LVNI_FOCUSED
  1697. );
  1698. }
  1699. //
  1700. // There is a bug in the listview code which shows up
  1701. // when an item is inserted with no subitem,
  1702. // and than an item is inserted above it with a subitem.
  1703. // When a third item is inserted at the bottom of the list,
  1704. // the insertion fails because there are now three items but
  1705. // the last subitem belongs to item 1.
  1706. // (See cairoshl\commctrl\listview.c, ListView_OnInsertItem())
  1707. //
  1708. // We get around this by setting blank text for each column
  1709. //
  1710. for (iCol = 1; iCol < (INT)ptl->nColumns; iCol++) {
  1711. ListView_SetItemText(
  1712. ptl->hwndList, pItem->iIndex, iCol, TEXT("")
  1713. );
  1714. }
  1715. //
  1716. // redraw the changed item as well
  1717. //
  1718. if (pChanged != NULL) {
  1719. pChanged->lvi.iItem = pChanged->iIndex;
  1720. ListView_RedrawItems(
  1721. ptl->hwndList, pChanged->lvi.iItem, pChanged->lvi.iItem
  1722. );
  1723. }
  1724. }
  1725. else
  1726. if (pChanged != NULL && pChanged == pItem->pParent) {
  1727. //
  1728. // the parent is visible, and it has changed, so redraw it
  1729. //
  1730. ListView_RedrawItems(
  1731. ptl->hwndList, pChanged->iIndex, pChanged->iIndex
  1732. );
  1733. }
  1734. }
  1735. return (HTLITEM)pItem;
  1736. }
  1737. //----------------------------------------------------------------------------
  1738. // Function: TL_OnDeleteItem
  1739. //
  1740. // Removes the item with the specified handle from the treelist.
  1741. //----------------------------------------------------------------------------
  1742. BOOL
  1743. TL_OnDeleteItem(
  1744. TL *ptl,
  1745. HTLITEM hItem
  1746. ) {
  1747. TLITEM *pItem, *pChanged, *pParent;
  1748. pItem = (TLITEM *)hItem;
  1749. pParent = pItem->pParent;
  1750. //
  1751. // if the item is visible and expanded,
  1752. // collapse it to simplify the deletion
  1753. //
  1754. if (TL_IsVisible(pItem) && TL_IsExpanded(pItem)) {
  1755. TL_OnExpand(ptl, TLE_COLLAPSE, hItem);
  1756. }
  1757. //
  1758. // see if there is a sibling after this item.
  1759. // if there is, nothing changes when the item is deleted
  1760. //
  1761. pChanged = TL_OnGetNextItem(ptl, TLGN_NEXTSIBLING, (HTLITEM)pItem);
  1762. if (pChanged != NULL) { pChanged = NULL; }
  1763. else {
  1764. //
  1765. // this item is the last of its parent's children, so the change
  1766. // is to the item's previous sibling, if any
  1767. //
  1768. pChanged = TL_OnGetNextItem(ptl, TLGN_PREVSIBLING, (HTLITEM)pItem);
  1769. if (pChanged == NULL) {
  1770. //
  1771. // this item is its parent's only child, so the change
  1772. // is to the item's parent
  1773. //
  1774. if (pParent != &ptl->root) { pChanged = pParent; }
  1775. }
  1776. }
  1777. //
  1778. // delete the item and its descendants
  1779. //
  1780. TL_DeleteAndNotify(ptl, pItem);
  1781. //
  1782. // if there was a side-effect, update the affected item
  1783. //
  1784. if (pChanged != NULL) { TL_UpdateImage(ptl, pChanged, NULL); }
  1785. //
  1786. // update the indices of the items below the deleted item
  1787. //
  1788. TL_UpdateListIndices(ptl, pParent);
  1789. return TRUE;
  1790. }
  1791. //----------------------------------------------------------------------------
  1792. // Function: TL_DeleteAndNotify
  1793. //
  1794. // This function performs a recursive deletion on a subtree,
  1795. // notifying the treelist's parent as each item is removed
  1796. //----------------------------------------------------------------------------
  1797. VOID
  1798. TL_DeleteAndNotify(
  1799. TL *ptl,
  1800. TLITEM *pItem
  1801. ) {
  1802. NMTREELIST nmtl;
  1803. TLSUBITEM *pSubitem;
  1804. LIST_ENTRY *ple, *phead;
  1805. TLITEM *pChild, *pChanged;
  1806. //
  1807. // do deletions on all descendants first
  1808. // note that the entry will be removed inside the recursive call,
  1809. // hence we walk the last be always picking off its head
  1810. //
  1811. phead = &pItem->lhChildren;
  1812. for (ple = phead->Flink; ple != phead; ple = phead->Flink) {
  1813. pChild = (TLITEM *)CONTAINING_RECORD(ple, TLITEM, leSiblings);
  1814. TL_DeleteAndNotify(ptl, pChild);
  1815. }
  1816. //
  1817. // notify the owner before completing the deletion
  1818. //
  1819. nmtl.hdr.hwndFrom = ptl->hwnd;
  1820. nmtl.hdr.code = TLN_DELETEITEM;
  1821. nmtl.hItem = (HTLITEM)pItem;
  1822. nmtl.lParam = pItem->lParam;
  1823. TL_NotifyParent(ptl, (NMHDR *)&nmtl);
  1824. //
  1825. // remove the entry from the listview if it is visible
  1826. //
  1827. if (TL_IsVisible(pItem)) {
  1828. ListView_DeleteItem(ptl->hwndList, pItem->iIndex);
  1829. }
  1830. //
  1831. // remove the entry from the list of its siblings
  1832. //
  1833. RemoveEntryList(&pItem->leSiblings);
  1834. --pItem->pParent->nChildren;
  1835. if (pItem->pParent->nChildren == 0 && pItem->pParent != &ptl->root) {
  1836. pItem->pParent->uiFlag &= ~TLI_EXPANDED;
  1837. }
  1838. //
  1839. // free the memory used by all its subitems, and free this item itself
  1840. //
  1841. while (!IsListEmpty(&pItem->lhSubitems)) {
  1842. ple = RemoveHeadList(&pItem->lhSubitems);
  1843. pSubitem = (TLSUBITEM *)CONTAINING_RECORD(ple, TLSUBITEM, leItems);
  1844. Free0(pSubitem->pszText);
  1845. Free(pSubitem);
  1846. }
  1847. Free0(pItem->pszText);
  1848. Free(pItem);
  1849. }
  1850. //----------------------------------------------------------------------------
  1851. // Function: TL_OnDeleteAllItems
  1852. //
  1853. // This function handles the case of deleting all items in the tree.
  1854. //----------------------------------------------------------------------------
  1855. BOOL
  1856. TL_OnDeleteAllItems(
  1857. TL *ptl
  1858. ) {
  1859. LIST_ENTRY *ple, *phead;
  1860. TLITEM *pItem, *pParent;
  1861. ListView_DeleteAllItems(ptl->hwndList);
  1862. phead = &ptl->root.lhChildren;
  1863. for (ple = phead->Flink; ple != phead; ple = phead->Flink) {
  1864. pItem = (TLITEM *)CONTAINING_RECORD(ple, TLITEM, leSiblings);
  1865. TL_DeleteAndNotify(ptl, pItem);
  1866. }
  1867. return TRUE;
  1868. }
  1869. //----------------------------------------------------------------------------
  1870. // Function: TL_OnGetItem
  1871. //
  1872. // This function is called to retrieve a specific item from the treelist
  1873. //----------------------------------------------------------------------------
  1874. BOOL
  1875. TL_OnGetItem(
  1876. TL *ptl,
  1877. LV_ITEM *plvi
  1878. ) {
  1879. PTSTR psz;
  1880. TLITEM *pItem;
  1881. TLSUBITEM *pSubitem;
  1882. LIST_ENTRY *ple, *phead;
  1883. psz = NULL;
  1884. pItem = (TLITEM *)UlongToPtr(plvi->iItem);
  1885. //
  1886. // get a pointer to the text for the item (or subitem)
  1887. //
  1888. if (plvi->iSubItem == 0) {
  1889. psz = pItem->pszText;
  1890. }
  1891. else
  1892. if (plvi->mask & LVIF_TEXT) {
  1893. phead = &pItem->lhSubitems;
  1894. for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
  1895. pSubitem = (TLSUBITEM *)CONTAINING_RECORD(ple, TLSUBITEM, leItems);
  1896. if (pSubitem->iSubItem == plvi->iSubItem) {
  1897. psz = pSubitem->pszText; break;
  1898. }
  1899. }
  1900. if (psz == NULL) { return FALSE; }
  1901. }
  1902. //
  1903. // retrieve the fields requested
  1904. //
  1905. if (plvi->mask & LVIF_TEXT) {
  1906. lstrcpyn(plvi->pszText, psz, plvi->cchTextMax);
  1907. }
  1908. if (plvi->mask & LVIF_IMAGE) {
  1909. plvi->iImage = pItem->lvi.iImage;
  1910. }
  1911. if (plvi->mask & LVIF_PARAM) {
  1912. plvi->lParam = pItem->lParam;
  1913. }
  1914. if (plvi->mask & LVIF_STATE) {
  1915. plvi->state = pItem->lvi.state;
  1916. }
  1917. return TRUE;
  1918. }
  1919. //----------------------------------------------------------------------------
  1920. // Function: TL_OnSetItem
  1921. //
  1922. // This function changes a specific item (or subitem).
  1923. //----------------------------------------------------------------------------
  1924. BOOL
  1925. TL_OnSetItem(
  1926. TL *ptl,
  1927. LV_ITEM *plvi
  1928. ) {
  1929. PTSTR *psz;
  1930. UINT uiMask;
  1931. BOOL bSuccess;
  1932. TLITEM *pItem;
  1933. TLSUBITEM *pSubitem;
  1934. LIST_ENTRY *ple, *phead;
  1935. psz = NULL;
  1936. uiMask = 0;
  1937. pItem = (TLITEM *)UlongToPtr(plvi->iItem);
  1938. //
  1939. // retrieve the text pointer for the item (or subitem)
  1940. //
  1941. if (plvi->iSubItem == 0) {
  1942. psz = &pItem->pszText;
  1943. }
  1944. else
  1945. if (plvi->mask & LVIF_TEXT) {
  1946. //
  1947. // search for the specified subitem
  1948. //
  1949. phead = &pItem->lhSubitems;
  1950. for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
  1951. pSubitem = (TLSUBITEM *)CONTAINING_RECORD(ple, TLSUBITEM, leItems);
  1952. if (pSubitem->iSubItem > plvi->iSubItem) {
  1953. break;
  1954. }
  1955. else
  1956. if (pSubitem->iSubItem == plvi->iSubItem) {
  1957. psz = &pSubitem->pszText; break;
  1958. }
  1959. }
  1960. if (psz == NULL) {
  1961. //
  1962. // create a new subitem
  1963. //
  1964. pSubitem = (TLSUBITEM *)Malloc(sizeof(TLSUBITEM));
  1965. if (pSubitem == NULL) { return FALSE; }
  1966. InsertTailList(ple, &pSubitem->leItems);
  1967. pSubitem->iSubItem = plvi->iSubItem;
  1968. pSubitem->pszText = NULL;
  1969. psz = &pSubitem->pszText;
  1970. }
  1971. }
  1972. //
  1973. // update the fields to be changed
  1974. //
  1975. if (plvi->mask & LVIF_TEXT) {
  1976. PTSTR pszTemp;
  1977. uiMask |= LVIF_TEXT;
  1978. pszTemp = StrDup(plvi->pszText);
  1979. if (!pszTemp) { return FALSE; }
  1980. Free0(*psz); *psz = pszTemp;
  1981. }
  1982. if (plvi->mask & LVIF_IMAGE) {
  1983. uiMask |= LVIF_IMAGE;
  1984. pItem->lvi.iImage = plvi->iImage;
  1985. }
  1986. if (plvi->mask & LVIF_PARAM) {
  1987. pItem->lParam = plvi->lParam;
  1988. }
  1989. if (plvi->mask & LVIF_STATE) {
  1990. uiMask |= LVIF_STATE;
  1991. pItem->lvi.stateMask = plvi->stateMask;
  1992. pItem->lvi.state = plvi->state;
  1993. }
  1994. bSuccess = TRUE;
  1995. pItem->lvi.mask |= uiMask;
  1996. //
  1997. // update the item's appearance if it is visible
  1998. //
  1999. if (TL_IsVisible(pItem)) {
  2000. UINT uiOldMask = pItem->lvi.mask;
  2001. pItem->lvi.mask = uiMask;
  2002. if(NULL != psz)
  2003. {
  2004. pItem->lvi.pszText = *psz;
  2005. }
  2006. pItem->lvi.iSubItem = plvi->iSubItem;
  2007. bSuccess = ListView_SetItem(ptl->hwndList, &pItem->lvi);
  2008. if (bSuccess) {
  2009. ListView_RedrawItems(ptl->hwndList, pItem->iIndex, pItem->iIndex);
  2010. }
  2011. pItem->lvi.mask = uiOldMask;
  2012. pItem->lvi.pszText = pItem->pszText;
  2013. pItem->lvi.iSubItem = 0;
  2014. }
  2015. return bSuccess;
  2016. }
  2017. //----------------------------------------------------------------------------
  2018. // Function: TL_GetItemCount
  2019. //
  2020. // This function retrieves a count of the items in the treelist
  2021. //----------------------------------------------------------------------------
  2022. UINT
  2023. TL_OnGetItemCount(
  2024. TL *ptl
  2025. ) {
  2026. INT iCount = 0;
  2027. //
  2028. // count the items in the subtree rooted at the invisible root,
  2029. // and decrement by one to exclude the root itself
  2030. //
  2031. TL_CountItems(&ptl->root, &iCount);
  2032. return (UINT)(iCount - 1);
  2033. }
  2034. //----------------------------------------------------------------------------
  2035. // Function: TL_CountItems
  2036. //
  2037. // This function recursively counts the items in the specified subtree
  2038. //----------------------------------------------------------------------------
  2039. VOID
  2040. TL_CountItems(
  2041. TLITEM *pParent,
  2042. INT *piCount
  2043. ) {
  2044. TLITEM *pItem;
  2045. LIST_ENTRY *ple, *phead;
  2046. ++(*piCount);
  2047. phead = &pParent->lhChildren;
  2048. for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
  2049. pItem = (TLITEM *)CONTAINING_RECORD(ple, TLITEM, leSiblings);
  2050. TL_CountItems(pItem, piCount);
  2051. }
  2052. return;
  2053. }
  2054. //----------------------------------------------------------------------------
  2055. // Function: TL_OnGetNextItem
  2056. //
  2057. // This function retrieves an item with a given property,
  2058. // or relative to a specified item
  2059. //----------------------------------------------------------------------------
  2060. HTLITEM
  2061. TL_OnGetNextItem(
  2062. TL *ptl,
  2063. UINT uiFlag,
  2064. HTLITEM hItem
  2065. ) {
  2066. TLITEM *pItem;
  2067. LIST_ENTRY *ple, *phead;
  2068. pItem = (TLITEM *)hItem;
  2069. switch (uiFlag) {
  2070. case TLGN_FIRST: {
  2071. if (IsListEmpty(&ptl->root.lhChildren)) {
  2072. return NULL;
  2073. }
  2074. ple = ptl->root.lhChildren.Flink;
  2075. return (HTLITEM)CONTAINING_RECORD(ple, TLITEM, leSiblings);
  2076. }
  2077. case TLGN_PARENT: {
  2078. if (pItem->pParent == &ptl->root) {
  2079. return NULL;
  2080. }
  2081. return (HTLITEM)pItem->pParent;
  2082. }
  2083. case TLGN_CHILD: {
  2084. if (IsListEmpty(&pItem->lhChildren)) {
  2085. return NULL;
  2086. }
  2087. ple = pItem->lhChildren.Flink;
  2088. return (HTLITEM)CONTAINING_RECORD(ple, TLITEM, leSiblings);
  2089. }
  2090. case TLGN_NEXTSIBLING: {
  2091. phead = &pItem->pParent->lhChildren;
  2092. ple = pItem->leSiblings.Flink;
  2093. if (ple == phead) { return NULL; }
  2094. return (HTLITEM)CONTAINING_RECORD(ple, TLITEM, leSiblings);
  2095. }
  2096. case TLGN_PREVSIBLING: {
  2097. phead = &pItem->pParent->lhChildren;
  2098. ple = pItem->leSiblings.Blink;
  2099. if (ple == phead) { return NULL; }
  2100. return (HTLITEM)CONTAINING_RECORD(ple, TLITEM, leSiblings);
  2101. }
  2102. case TLGN_ENUMERATE: {
  2103. TLITEM *pNext;
  2104. if (pItem == NULL) {
  2105. return TL_OnGetNextItem(ptl, TLGN_FIRST, NULL);
  2106. }
  2107. pNext = (TLITEM *)TL_OnGetNextItem(ptl, TLGN_CHILD, hItem);
  2108. if (pNext == NULL) {
  2109. pNext = TL_OnGetNextItem(ptl, TLGN_NEXTSIBLING, hItem);
  2110. if (pNext == NULL) {
  2111. for (pItem = pItem->pParent;
  2112. pItem != &ptl->root; pItem = pItem->pParent) {
  2113. pNext = TL_OnGetNextItem(
  2114. ptl, TLGN_NEXTSIBLING, (HTLITEM)pItem
  2115. );
  2116. if (pNext != NULL) { break; }
  2117. }
  2118. }
  2119. }
  2120. return pNext;
  2121. }
  2122. case TLGN_SELECTION: {
  2123. INT iItem;
  2124. LV_ITEM lvi;
  2125. iItem = ListView_GetNextItem(ptl->hwndList, -1, LVNI_SELECTED);
  2126. if (iItem == -1) {
  2127. iItem = ListView_GetNextItem(ptl->hwndList, -1, LVNI_FOCUSED);
  2128. if (iItem == -1) { return NULL; }
  2129. }
  2130. lvi.iItem = iItem;
  2131. lvi.iSubItem = 0;
  2132. lvi.mask = LVIF_PARAM;
  2133. if (!ListView_GetItem(ptl->hwndList, &lvi)) { return NULL; }
  2134. return (HTLITEM)lvi.lParam;
  2135. }
  2136. }
  2137. return NULL;
  2138. }
  2139. //----------------------------------------------------------------------------
  2140. // Function: TL_OnExpand
  2141. //
  2142. // This is called to expand or collapse an item,
  2143. // or to toggle the expand-state of an item
  2144. //----------------------------------------------------------------------------
  2145. BOOL
  2146. TL_OnExpand(
  2147. TL *ptl,
  2148. UINT uiFlag,
  2149. HTLITEM hItem
  2150. ) {
  2151. TLITEM *pItem;
  2152. BOOL bSuccess;
  2153. pItem = (TLITEM *)hItem;
  2154. if (pItem->uiFlag & TLI_EXPANDED) {
  2155. // item is expanded already, do nothing
  2156. if (uiFlag == TLE_EXPAND) {
  2157. return TRUE;
  2158. }
  2159. bSuccess = TL_ItemCollapse(ptl, pItem);
  2160. }
  2161. else {
  2162. // item is collapsed already, do nothing
  2163. if (uiFlag == TLE_COLLAPSE) {
  2164. return TRUE;
  2165. }
  2166. bSuccess = TL_ItemExpand(ptl, pItem);
  2167. }
  2168. //
  2169. // update the list indices and redraw the item expanded/collapsed
  2170. //
  2171. if (bSuccess) {
  2172. ListView_RedrawItems(ptl->hwndList, pItem->iIndex, pItem->iIndex);
  2173. }
  2174. return bSuccess;
  2175. }
  2176. //----------------------------------------------------------------------------
  2177. // Function: TL_ItemCollapse
  2178. //
  2179. // Collapses an item
  2180. //----------------------------------------------------------------------------
  2181. BOOL
  2182. TL_ItemCollapse(
  2183. TL *ptl,
  2184. TLITEM *pItem
  2185. ) {
  2186. INT i, iItem;
  2187. TLITEM *pChild;
  2188. LIST_ENTRY *ple, *phead;
  2189. if (pItem->nChildren == 0 || !TL_IsExpanded(pItem)) { return FALSE; }
  2190. //
  2191. // first collapse all descendants;
  2192. // note that this is done in reverse order,
  2193. // so that the indices of the higher items remain valid
  2194. // while the lower ones are being removed
  2195. //
  2196. phead = &pItem->lhChildren;
  2197. for (ple = phead->Blink; ple != phead; ple = ple->Blink) {
  2198. pChild = (TLITEM *)CONTAINING_RECORD(ple, TLITEM, leSiblings);
  2199. TL_ItemCollapse(ptl, pChild);
  2200. }
  2201. //
  2202. // delete all this item's children (they are now collapsed);
  2203. // since the listview shifts items up when an item is deleted,
  2204. // we delete items n through m by deleting item n (m-n)+1 times
  2205. //
  2206. iItem = pItem->iIndex;
  2207. for (i = 0; i < (INT)pItem->nChildren; i++) {
  2208. ListView_DeleteItem(ptl->hwndList, iItem + 1);
  2209. }
  2210. pItem->uiFlag &= ~TLI_EXPANDED;
  2211. TL_UpdateImage(ptl, pItem, NULL);
  2212. TL_UpdateListIndices(ptl, pItem);
  2213. return TRUE;
  2214. }
  2215. //----------------------------------------------------------------------------
  2216. // Function: TL_ItemExpand
  2217. //
  2218. // Expands an item
  2219. //----------------------------------------------------------------------------
  2220. BOOL
  2221. TL_ItemExpand(
  2222. TL *ptl,
  2223. TLITEM *pItem
  2224. ) {
  2225. INT i;
  2226. TLITEM *pChild;
  2227. TLSUBITEM *pSubitem;
  2228. LIST_ENTRY *ple, *phead, *ples, *psubhead;
  2229. if (pItem->nChildren == 0 || TL_IsExpanded(pItem)) { return FALSE; }
  2230. //
  2231. // update the expand-state and image for the item,
  2232. // and then recompute the indices of its children
  2233. //
  2234. pItem->uiFlag |= TLI_EXPANDED;
  2235. TL_UpdateImage(ptl, pItem, NULL);
  2236. TL_UpdateListIndices(ptl, pItem);
  2237. //
  2238. // insert items below this one;
  2239. // we also need to set the sub-item text for each inserted item
  2240. //
  2241. phead = &pItem->lhChildren;
  2242. for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
  2243. pChild = (TLITEM *)CONTAINING_RECORD(ple, TLITEM, leSiblings);
  2244. pChild->lvi.iItem = pChild->iIndex;
  2245. pChild->lvi.iSubItem = 0;
  2246. TL_UpdateImage(ptl, pChild, NULL);
  2247. ListView_InsertItem(ptl->hwndList, &pChild->lvi);
  2248. psubhead = &pChild->lhSubitems;
  2249. i = 1;
  2250. for (ples = psubhead->Flink; ples != psubhead; ples = ples->Flink) {
  2251. pSubitem = (TLSUBITEM *)CONTAINING_RECORD(ples, TLSUBITEM, leItems);
  2252. for ( ; i < pSubitem->iSubItem; i++) {
  2253. ListView_SetItemText(
  2254. ptl->hwndList, pChild->iIndex, i, TEXT("")
  2255. );
  2256. }
  2257. ListView_SetItemText(
  2258. ptl->hwndList, pChild->iIndex, pSubitem->iSubItem,
  2259. pSubitem->pszText
  2260. );
  2261. ++i;
  2262. }
  2263. for ( ; i < (INT)ptl->nColumns; i++) {
  2264. ListView_SetItemText(
  2265. ptl->hwndList, pChild->iIndex, i, TEXT("")
  2266. );
  2267. }
  2268. }
  2269. return TRUE;
  2270. }
  2271. //----------------------------------------------------------------------------
  2272. // Function: TL_OnInsertColumn
  2273. //
  2274. // Inserts a column. Memory for subitem's is not allocated until
  2275. // the subitems' texts aer actually set in TL_OnSetItem
  2276. //----------------------------------------------------------------------------
  2277. INT
  2278. TL_OnInsertColumn(
  2279. TL *ptl,
  2280. INT iCol,
  2281. LV_COLUMN *pCol
  2282. ) {
  2283. if ((iCol = ListView_InsertColumn(ptl->hwndList, iCol, pCol)) != -1) {
  2284. ++ptl->nColumns;
  2285. }
  2286. return iCol;
  2287. }
  2288. //----------------------------------------------------------------------------
  2289. // Function: TL_OnDeleteColumn
  2290. //
  2291. // Deletes a column, and removes all subitems corresponding to the column
  2292. //----------------------------------------------------------------------------
  2293. BOOL
  2294. TL_OnDeleteColumn(
  2295. TL *ptl,
  2296. INT iCol
  2297. ) {
  2298. TLITEM *pItem;
  2299. TLSUBITEM *pSubitem;
  2300. LIST_ENTRY *ple, *phead;
  2301. if (!ListView_DeleteColumn(ptl->hwndList, iCol)) {
  2302. return FALSE;
  2303. }
  2304. --ptl->nColumns;
  2305. //
  2306. // delete the subitems which correspond to this column
  2307. //
  2308. pItem = NULL;
  2309. while (pItem = TL_Enumerate(ptl, pItem)) {
  2310. phead = &pItem->lhSubitems;
  2311. for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
  2312. pSubitem = (TLSUBITEM *)CONTAINING_RECORD(ple, TLSUBITEM, leItems);
  2313. if (pSubitem->iSubItem > iCol) {
  2314. //
  2315. // the column was never set, so do nothing
  2316. //
  2317. break;
  2318. }
  2319. else
  2320. if (pSubitem->iSubItem == iCol) {
  2321. RemoveEntryList(&pSubitem->leItems);
  2322. Free0(pSubitem->pszText);
  2323. Free(pSubitem);
  2324. break;
  2325. }
  2326. }
  2327. }
  2328. return TRUE;
  2329. }
  2330. //----------------------------------------------------------------------------
  2331. // Function: TL_OnSetSelection
  2332. //
  2333. // Changes the currently selected treelist item
  2334. //----------------------------------------------------------------------------
  2335. BOOL
  2336. TL_OnSetSelection(
  2337. TL *ptl,
  2338. HTLITEM hItem
  2339. ) {
  2340. TLITEM *pItem;
  2341. pItem = (TLITEM *)hItem;
  2342. if (!TL_IsVisible(pItem)) { return FALSE; }
  2343. ListView_SetItemState(
  2344. ptl->hwndList, pItem->iIndex,
  2345. LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED
  2346. );
  2347. return TRUE;
  2348. }
  2349. //----------------------------------------------------------------------------
  2350. // Function: TL_OnRedraw
  2351. //
  2352. // Forces a redraw of the treelist by invalidating its entire client area
  2353. //----------------------------------------------------------------------------
  2354. BOOL
  2355. TL_OnRedraw(
  2356. TL *ptl
  2357. ) {
  2358. InvalidateRect(ptl->hwndList, NULL, TRUE);
  2359. return TRUE;
  2360. }