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.

907 lines
25 KiB

  1. #include "ctlspriv.h"
  2. #include "treeview.h"
  3. #if defined(MAINWIN)
  4. #include <mainwin.h>
  5. #endif
  6. void TV_ScrollItems(PTREE pTree, int nItems, int iTopShownIndex, BOOL fDown);
  7. // in:
  8. // hItem item to delete
  9. // flags controls how/what to delete
  10. // TVDI_NORMAL delete this node and all children
  11. // TVDI_NONOTIFY don't send notify messages
  12. // TVDI_CHILDRENONLY just delete the kids (not the item)
  13. void TV_DeleteItemRecurse(PTREE pTree, TREEITEM * hItem, UINT flags)
  14. {
  15. TREEITEM *hKid;
  16. TREEITEM *hNext;
  17. TREEITEM *hParent;
  18. int i;
  19. DBG_ValidateTreeItem(hItem, 0);
  20. //
  21. // We do this from DeleteItemRecurse(), kind of like how USER sends
  22. // Destroy notifications from its FreeWindow() code, so that we get
  23. // deletes for parent and children both.
  24. //
  25. NotifyWinEvent(EVENT_OBJECT_DESTROY, pTree->ci.hwnd, OBJID_CLIENT,
  26. TV_GetAccId(hItem));
  27. //
  28. // While the item is still valid, clean up if it's the insertion point.
  29. // The item needs to be valid because we're going to call other
  30. // functions that validate their parameters...
  31. //
  32. if (hItem == pTree->htiInsert)
  33. {
  34. TV_SetInsertMark(pTree, NULL, FALSE);
  35. ASSERT(pTree->htiInsert == NULL);
  36. }
  37. // remove all kids (and their kids)
  38. for (hKid = hItem->hKids; hKid; hKid = hNext) {
  39. hNext = hKid->hNext;
  40. // recurse on each child
  41. TV_DeleteItemRecurse(pTree, hKid, flags & ~TVDI_CHILDRENONLY);
  42. }
  43. if ((flags & TVDI_CHILDRENONLY) || !hItem->hParent)
  44. return;
  45. if (!(flags & TVDI_NONOTIFY))
  46. {
  47. NM_TREEVIEW nm;
  48. // Let the app clean up after itself
  49. nm.itemOld.hItem = hItem;
  50. nm.itemOld.lParam = hItem->lParam;
  51. nm.itemNew.mask = 0;
  52. nm.itemOld.mask = (TVIF_HANDLE | TVIF_PARAM);
  53. CCSendNotify(&pTree->ci, TVN_DELETEITEM, &nm.hdr);
  54. }
  55. //
  56. // If anybody has a watch on our item, let him know that it's gone.
  57. //
  58. i = DPA_GetPtrCount(pTree->hdpaWatch);
  59. while (--i >= 0)
  60. {
  61. PTVWATCHEDITEM pwi = DPA_FastGetPtr(pTree->hdpaWatch, i);
  62. ASSERT(pwi);
  63. if (pwi->hti == hItem) {
  64. pwi->hti = hItem->hNext;
  65. pwi->fStale = TRUE;
  66. }
  67. }
  68. hParent = hItem->hParent;
  69. ASSERT(hParent);
  70. // unlink ourselves from the parent child chain
  71. if (hParent->hKids == hItem) {
  72. hParent->hKids = hItem->hNext;
  73. hKid = NULL;
  74. } else {
  75. // not the first child, find our previous item (linear search!)
  76. hKid = TV_GetNextItem(pTree, hItem, TVGN_PREVIOUS);
  77. ASSERT(hKid);
  78. hKid->hNext = hItem->hNext;
  79. }
  80. pTree->cItems--;
  81. TV_ScrollBarsAfterRemove(pTree, hItem);
  82. // reset tooltip after unlink from the parent child chain
  83. if (pTree->hToolTip == hItem)
  84. TV_SetToolTipTarget(pTree, NULL);
  85. Str_Set(&hItem->lpstr, NULL);
  86. TV_MarkAsDead(hItem);
  87. // be careful from here down. hItem is unlinked but
  88. // still has some valid fields
  89. // Check to see if the user has deleted one of the
  90. // special items that is stored in the main tree structure.
  91. if (hItem == pTree->htiEdit)
  92. pTree->htiEdit = NULL;
  93. if (hItem == pTree->hDropTarget)
  94. pTree->hDropTarget = NULL;
  95. if (hItem == pTree->hOldDrop)
  96. pTree->hOldDrop = NULL;
  97. if (hItem == pTree->hHot )
  98. pTree->hHot = NULL;
  99. if (hItem == pTree->htiSearch )
  100. pTree->htiSearch = NULL;
  101. // if the caret escaped the collapsed area and landed on us, push it away
  102. if (pTree->hCaret == hItem) {
  103. HTREEITEM hTemp;
  104. if (hItem->hNext)
  105. hTemp = hItem->hNext;
  106. else {
  107. hTemp = VISIBLE_PARENT(hItem);
  108. if (!hTemp)
  109. hTemp = hKid; // set above when we unlinked from the previous item
  110. }
  111. // Reset the caret to NULL as to not try to reference our
  112. // invalidated item.
  113. pTree->hCaret = NULL;
  114. TV_SelectItem(pTree, TVGN_CARET, hTemp, (flags & TVDI_NOSELCHANGE) ? 0 : TVSIFI_NOTIFY, 0);
  115. ASSERT(pTree->hCaret != hItem);
  116. }
  117. ASSERT(pTree->hItemPainting != hItem);
  118. ControlFree(pTree->hheap, hItem);
  119. }
  120. // ----------------------------------------------------------------------------
  121. //
  122. // Removes the given item and all children from the tree.
  123. // Special case: if the given item is the hidden root, all children are
  124. // removed, but the hidden root is NOT removed.
  125. //
  126. // sets cItems
  127. //
  128. // ----------------------------------------------------------------------------
  129. BOOL TV_DeleteItem(PTREE pTree, TREEITEM * hItem, UINT flags)
  130. {
  131. if (hItem == TVI_ROOT || !hItem)
  132. hItem = pTree->hRoot;
  133. // BUGUBG: send TVN_DELETEALLITEMS and TVDI_NONOTIFY if they respond
  134. // if (hItem == pTree->hRoot)
  135. // etc.
  136. if (!ValidateTreeItem(hItem, 0))
  137. return FALSE;
  138. // Collapse first to speed things up (not as much scroll bar recalcs) and
  139. // to set the top index correctly after the remove.
  140. if (hItem != pTree->hRoot)
  141. TV_Expand(pTree, TVE_COLLAPSE, hItem, FALSE);
  142. else
  143. {
  144. // TV_Expand punts on the root item, so manually iterate through it's kids
  145. TREEITEM *hKid = hItem->hKids;
  146. while (hKid)
  147. {
  148. TV_Expand(pTree, TVE_COLLAPSE, hKid, FALSE);
  149. if (!ValidateTreeItem(hKid, 0)) break; // callback during collapse could delete
  150. hKid = hKid->hNext;
  151. }
  152. }
  153. // Invalidate everything below this item; must be done AFTER setting the
  154. // selection
  155. if (hItem->hParent == pTree->hRoot || hItem == pTree->hRoot || ITEM_VISIBLE(hItem->hParent)) {
  156. if (pTree->fRedraw) {
  157. InvalidateRect(pTree->ci.hwnd, NULL, TRUE);
  158. }
  159. } else {
  160. TV_ScrollBelow(pTree, hItem->hParent, FALSE, FALSE);
  161. }
  162. // We can pass in the root to clear all items
  163. if (hItem == pTree->hRoot)
  164. flags |= TVDI_CHILDRENONLY;
  165. TV_DeleteItemRecurse(pTree, hItem, flags);
  166. ASSERT(pTree->hRoot); // didn't go too far, did we?
  167. // maybe everything's gone...
  168. // check out our cleanup job
  169. if (!pTree->hRoot->hKids) {
  170. // the tree itself
  171. ASSERT(pTree->cItems == 0);
  172. pTree->cItems = 0; // just removed it all, didn't we?
  173. pTree->hTop = NULL;
  174. AssertMsg(pTree->hCaret == NULL, TEXT("hCaret not NULL, but empty tree"));
  175. pTree->hCaret = NULL;
  176. pTree->fNameEditPending = FALSE;
  177. pTree->cxMax = 0;
  178. pTree->xPos = 0;
  179. // the invisible root
  180. ASSERT(pTree->hRoot->hNext == NULL);
  181. pTree->hRoot->hNext = NULL;
  182. ASSERT(pTree->hRoot->hParent == NULL);
  183. pTree->hRoot->hParent = NULL;
  184. ASSERT(pTree->hRoot->hKids == NULL);
  185. pTree->hRoot->hKids = NULL;
  186. ASSERT(pTree->hRoot->state & TVIS_EXPANDED);
  187. pTree->hRoot->state |= (TVIS_EXPANDED | TVIS_EXPANDEDONCE);
  188. ASSERT(pTree->hRoot->iLevel == (BYTE)-1);
  189. pTree->hRoot->iLevel = (BYTE) -1;
  190. ASSERT(pTree->hRoot->iShownIndex == (WORD)-1);
  191. pTree->hRoot->iShownIndex = (WORD) -1;
  192. }
  193. return TRUE;
  194. }
  195. // ----------------------------------------------------------------------------
  196. //
  197. // Creates the hidden root node for the tree -- all items will trace up to
  198. // this root, and the first child of the root is the first item in the tree.
  199. //
  200. // sets hRoot
  201. //
  202. // ----------------------------------------------------------------------------
  203. BOOL TV_CreateRoot(PTREE pTree)
  204. {
  205. TREEITEM * hRoot = ControlAlloc(pTree->hheap, sizeof(TREEITEM));
  206. if (!hRoot)
  207. return FALSE;
  208. // hRoot->hNext = NULL;
  209. // hRoot->hKids = NULL;
  210. // hRoot->hParent = NULL;
  211. hRoot->iLevel = (BYTE) -1;
  212. hRoot->state = (TVIS_EXPANDED | TVIS_EXPANDEDONCE);
  213. hRoot->iShownIndex = (WORD)-1;
  214. hRoot->wSignature = TV_SIG;
  215. hRoot->dwAccId = pTree->dwLastAccId++;
  216. pTree->hRoot = hRoot;
  217. // OLEACC asks for the text of the root item (d'oh!)
  218. Str_Set(&hRoot->lpstr, c_szNULL);
  219. return TRUE;
  220. }
  221. #ifdef DEBUG
  222. void DumpItem(TREEITEM *hItem)
  223. {
  224. LPTSTR p;
  225. if (hItem->lpstr == LPSTR_TEXTCALLBACK)
  226. p = TEXT("(callback)");
  227. else if (hItem->lpstr == NULL)
  228. p = TEXT("(null)");
  229. else
  230. p = hItem->lpstr;
  231. TraceMsg(TF_TREEVIEW, "%s", p);
  232. TraceMsg(TF_TREEVIEW, "\tstate:%4.4x show index:%3d level:%2d kids:%ld lparam:%4.4x",
  233. hItem->state, hItem->iShownIndex,
  234. hItem->iLevel, hItem->fKids, hItem->lParam);
  235. }
  236. #else
  237. #define DumpItem(hItem)
  238. #endif
  239. // ----------------------------------------------------------------------------
  240. //
  241. // Adds the item described by the given arguments to the tree.
  242. //
  243. // sets hTop, cItems
  244. //
  245. // ----------------------------------------------------------------------------
  246. #ifdef UNICODE
  247. TREEITEM * TV_InsertItemA(PTREE pTree, LPTV_INSERTSTRUCTA lpis) {
  248. LPSTR pszA = NULL;
  249. TREEITEM *ptvi;
  250. //HACK Alert! This code assumes that TV_INSERTSTRUCTA is exactly the same
  251. // as TV_INSERTSTRUCTW except for the text pointer in the TVITEM
  252. COMPILETIME_ASSERT(sizeof(TV_INSERTSTRUCTA) == sizeof(TV_INSERTSTRUCTW));
  253. if (!IsFlagPtr(lpis) && (lpis->DUMMYUNION_MEMBER(item).mask & TVIF_TEXT) && !IsFlagPtr(lpis->DUMMYUNION_MEMBER(item).pszText)) {
  254. pszA = lpis->DUMMYUNION_MEMBER(item).pszText;
  255. lpis->DUMMYUNION_MEMBER(item).pszText = (LPSTR)ProduceWFromA(pTree->ci.uiCodePage, lpis->DUMMYUNION_MEMBER(item).pszText);
  256. if (lpis->DUMMYUNION_MEMBER(item).pszText == NULL) {
  257. lpis->DUMMYUNION_MEMBER(item).pszText = pszA;
  258. return NULL;
  259. }
  260. }
  261. ptvi = TV_InsertItem( pTree, (LPTV_INSERTSTRUCTW)lpis );
  262. if (pszA) {
  263. FreeProducedString(lpis->DUMMYUNION_MEMBER(item).pszText);
  264. lpis->DUMMYUNION_MEMBER(item).pszText = pszA;
  265. }
  266. return ptvi;
  267. }
  268. #endif
  269. TREEITEM * TV_InsertItem(PTREE pTree, LPTV_INSERTSTRUCT lpis)
  270. {
  271. TREEITEM *hNewItem, *hItem;
  272. TREEITEM *hParent;
  273. TREEITEM *hInsertAfter;
  274. UINT mask;
  275. if (!lpis)
  276. return NULL; //Bug#94345: Validate LPTV_INSERTSTRUCT
  277. // initialize _after_ the check for NULL!
  278. hParent = lpis->hParent;
  279. hInsertAfter = lpis->hInsertAfter;
  280. mask = lpis->DUMMYUNION_MEMBER(item).mask;
  281. // don't allow undefined bits
  282. AssertMsg((lpis->DUMMYUNION_MEMBER(item).mask & ~TVIF_ALL) == 0, TEXT("Invalid TVIF mask specified"));
  283. if (mask & ~TVIF_ALL) {
  284. // if they used bogus bits,
  285. // restrict to win95 bits only
  286. // I'd like to fail completely, but for win95 compat, we can't
  287. //
  288. // this fixes QuaterDesk's CleanSweep which has bogus garbage on the stack for a mask
  289. mask = (TVIF_WIN95 & mask);
  290. }
  291. TV_DismissEdit(pTree, FALSE);
  292. //
  293. // Zillions of apps pass garbage for hInsertAfter, so don't fail if
  294. // it's invalid. Fortunately, we never dereference hInsertAfter, so
  295. // garbage is okay.
  296. if (!ValidateTreeItem(hParent, VTI_NULLOK)) // NULL means TVI_ROOT
  297. return NULL;
  298. DBG_ValidateTreeItem(hInsertAfter, 0);
  299. hNewItem = ControlAlloc(pTree->hheap, sizeof(TREEITEM));
  300. if (!hNewItem)
  301. {
  302. TraceMsg(TF_ERROR, "TreeView: Out of memory");
  303. return NULL;
  304. }
  305. hNewItem->wSignature = TV_SIG;
  306. if (mask & TVIF_TEXT)
  307. {
  308. //
  309. // We will setup the text string next, before we link our self in
  310. // as to handle the case where we run out of memory and need to
  311. // destroy ourself without having to unlink.
  312. //
  313. if (!lpis->DUMMYUNION_MEMBER(item).pszText)
  314. {
  315. hNewItem->lpstr = LPSTR_TEXTCALLBACK;
  316. }
  317. else
  318. {
  319. if (!Str_Set(&hNewItem->lpstr, lpis->DUMMYUNION_MEMBER(item).pszText))
  320. {
  321. // Memory allocation failure...
  322. TraceMsg(TF_ERROR, "TreeView: Out of memory");
  323. TV_MarkAsDead(hNewItem);
  324. ControlFree(pTree->hheap, hNewItem);
  325. return NULL;
  326. }
  327. }
  328. }
  329. else
  330. {
  331. Str_Set(&hNewItem->lpstr, c_szNULL);
  332. }
  333. AssertMsg(hNewItem->lpstr != NULL, TEXT("Item added with NULL text"));
  334. if ((hParent == NULL) || (hParent == TVI_ROOT))
  335. {
  336. hParent = pTree->hRoot;
  337. if (!pTree->hTop)
  338. pTree->hTop = hNewItem;
  339. }
  340. else if (!pTree->hRoot->hKids)
  341. {
  342. TV_MarkAsDead(hNewItem);
  343. ControlFree(pTree->hheap, hNewItem);
  344. return NULL;
  345. }
  346. // We will do the sort later, so we can handle TEXTCALLBACK things
  347. if ((hInsertAfter == TVI_FIRST || hInsertAfter == TVI_SORT) || !hParent->hKids)
  348. {
  349. hNewItem->hNext = hParent->hKids;
  350. hParent->hKids = hNewItem;
  351. }
  352. else
  353. {
  354. // Bug#94348: we should cache the last insert after pointer to try to
  355. // catch the case of consecutive adds to the end of a node
  356. if (hInsertAfter == TVI_LAST)
  357. for (hItem = hParent->hKids; hItem->hNext; hItem = hItem->hNext)
  358. ;
  359. else
  360. {
  361. for (hItem = hParent->hKids; hItem->hNext; hItem = hItem->hNext)
  362. if (hItem == hInsertAfter)
  363. break;
  364. }
  365. hNewItem->hNext = hItem->hNext;
  366. hItem->hNext = hNewItem;
  367. }
  368. // hNewItem->hKids = NULL;
  369. hNewItem->hParent = hParent;
  370. hNewItem->iLevel = hParent->iLevel + 1;
  371. // hNewItem->iWidth = 0;
  372. // hNewItem->state = 0;
  373. if ((mask & TVIF_INTEGRAL) &&
  374. LOWORD(lpis->DUMMYUNION_MEMBER(itemex).iIntegral) > 0)
  375. {
  376. hNewItem->iIntegral = LOWORD(lpis->DUMMYUNION_MEMBER(itemex).iIntegral);
  377. }
  378. else
  379. {
  380. hNewItem->iIntegral = 1;
  381. }
  382. if (pTree->hTop == hNewItem)
  383. hNewItem->iShownIndex = 0; // calc me please!
  384. else
  385. hNewItem->iShownIndex = (WORD)-1; // calc me please!
  386. if (mask & TVIF_IMAGE)
  387. hNewItem->iImage = (WORD) lpis->DUMMYUNION_MEMBER(item).iImage;
  388. if (mask & TVIF_SELECTEDIMAGE)
  389. hNewItem->iSelectedImage = (WORD) lpis->DUMMYUNION_MEMBER(item).iSelectedImage;
  390. if (mask & TVIF_PARAM)
  391. hNewItem->lParam = lpis->DUMMYUNION_MEMBER(item).lParam;
  392. if (mask & TVIF_STATE)
  393. hNewItem->state = lpis->DUMMYUNION_MEMBER(item).state & lpis->DUMMYUNION_MEMBER(item).stateMask;
  394. // if we're in check box mode, inforce that it has a check box
  395. if (pTree->ci.style & TVS_CHECKBOXES)
  396. {
  397. if ((hNewItem->state & TVIS_STATEIMAGEMASK) == 0)
  398. {
  399. hNewItem->state |= INDEXTOSTATEIMAGEMASK(1);
  400. }
  401. }
  402. if ((hNewItem->state & TVIS_BOLD) && !pTree->hFontBold) //$BOLD
  403. TV_CreateBoldFont(pTree); //$BOLD
  404. // TraceMsg(TF_TRACE, "Tree: Inserting i = %d state = %d", TV_StateIndex(&lpis->item), lpis->item.state);
  405. if (mask & TVIF_CHILDREN)
  406. {
  407. switch (lpis->DUMMYUNION_MEMBER(item).cChildren)
  408. {
  409. case I_CHILDRENCALLBACK:
  410. hNewItem->fKids = KIDS_CALLBACK;
  411. break;
  412. case I_CHILDRENAUTO:
  413. hNewItem->fKids = KIDS_COMPUTE;
  414. break;
  415. case 0:
  416. hNewItem->fKids = KIDS_FORCE_NO;
  417. break;
  418. default:
  419. hNewItem->fKids = KIDS_FORCE_YES;
  420. break;
  421. }
  422. }
  423. hNewItem->dwAccId = pTree->dwLastAccId++;
  424. // accept state bits on create?
  425. // mask & TVIF_STATE
  426. pTree->cItems++;
  427. // I don't want to do any callbacks until the item is completed
  428. // so sorting waits until the end
  429. // special case an only child for speed
  430. // (hKids && hKids->hNext means more than one child)
  431. if ((hInsertAfter == TVI_SORT) && hParent->hKids && hParent->hKids->hNext)
  432. {
  433. TVITEMEX sThisItem, sNextItem;
  434. TCHAR szThis[64], szNext[64];
  435. sThisItem.pszText = szThis;
  436. sThisItem.cchTextMax = ARRAYSIZE(szThis);
  437. TV_GetItem(pTree, hNewItem, TVIF_TEXT, &sThisItem);
  438. // We know that the first kid of hParent is hNewItem
  439. for (hItem = hNewItem->hNext; hItem; hItem = hItem->hNext)
  440. {
  441. sNextItem.pszText = szNext;
  442. sNextItem.cchTextMax = ARRAYSIZE(szNext);
  443. TV_GetItem(pTree, hItem, TVIF_TEXT, &sNextItem);
  444. if (lstrcmpi(sThisItem.pszText, sNextItem.pszText) < 0)
  445. break;
  446. hInsertAfter = hItem;
  447. }
  448. // Check if this is still the first item
  449. if (hInsertAfter != TVI_SORT)
  450. {
  451. // Move this item from the beginning to where it
  452. // should be
  453. hParent->hKids = hNewItem->hNext;
  454. hNewItem->hNext = hInsertAfter->hNext;
  455. hInsertAfter->hNext = hNewItem;
  456. }
  457. }
  458. if ((hNewItem->hNext == pTree->hTop) && !pTree->fVert)
  459. {
  460. // there's no scrollbars and we got added before the top
  461. // item. we're now the top.
  462. hNewItem->iShownIndex = 0;
  463. pTree->hTop = hNewItem;
  464. }
  465. if (pTree->fRedraw)
  466. {
  467. BOOL fVert = pTree->fVert;
  468. RECT rc;
  469. RECT rc2;
  470. if (TV_ScrollBarsAfterAdd(pTree, hNewItem))
  471. {
  472. // scroll everything down one
  473. if (ITEM_VISIBLE(hNewItem))
  474. {
  475. int iTop = hNewItem->iShownIndex - pTree->hTop->iShownIndex;
  476. // if there wasn't a scrollbar and we're the 0th item,
  477. // TV_ScrollBarsAfterAdd already scrolled us
  478. if (iTop > 0 || !fVert)
  479. TV_ScrollItems(pTree, hNewItem->iIntegral, iTop + hNewItem->iIntegral - 1, TRUE);
  480. }
  481. }
  482. // connect the lines, add the buttons, etc. on the item above
  483. // TV_GetPrevVisItem only works after TV_Scroll* stuff is done
  484. if (TV_GetItemRect(pTree, hNewItem, &rc, FALSE))
  485. {
  486. // find the previous sibling or the parent if no prev sib.
  487. if (hParent->hKids == hNewItem)
  488. {
  489. hItem = hParent;
  490. }
  491. else
  492. {
  493. hItem = hParent->hKids;
  494. while ( hItem->hNext != hNewItem )
  495. {
  496. ASSERT(hItem->hNext);
  497. hItem = hItem->hNext;
  498. }
  499. }
  500. // invalidate from there to the new one
  501. if (TV_GetItemRect(pTree, hItem, &rc2, FALSE))
  502. {
  503. rc.top = rc2.top;
  504. }
  505. RedrawWindow(pTree->ci.hwnd, &rc, NULL, RDW_INVALIDATE | RDW_ERASE);
  506. }
  507. }
  508. // DumpItem(hNewItem);
  509. NotifyWinEvent(EVENT_OBJECT_CREATE, pTree->ci.hwnd, OBJID_CLIENT, TV_GetAccId(hNewItem));
  510. if (pTree->hToolTip)
  511. {
  512. TV_PopBubble(pTree);
  513. }
  514. return hNewItem;
  515. }
  516. void TV_DeleteHotFonts(PTREE pTree)
  517. {
  518. if (pTree->hFontHot)
  519. DeleteObject(pTree->hFontHot);
  520. if (pTree->hFontBoldHot)
  521. DeleteObject(pTree->hFontBoldHot);
  522. pTree->hFontHot = pTree->hFontBoldHot = NULL;
  523. }
  524. // ----------------------------------------------------------------------------
  525. //
  526. // Frees all allocated memory and objects associated with the tree.
  527. //
  528. // ----------------------------------------------------------------------------
  529. void TV_DestroyTree(PTREE pTree)
  530. {
  531. HWND hwnd = pTree->ci.hwnd;
  532. ASSERT(pTree->hRoot);
  533. pTree->fRedraw = FALSE;
  534. TV_OnSetBkColor(pTree, (COLORREF)-1);
  535. if (pTree->hbrLine)
  536. {
  537. DeleteObject(pTree->hbrLine);
  538. }
  539. if (pTree->hbrText)
  540. {
  541. DeleteObject(pTree->hbrText);
  542. }
  543. if (pTree->hCurHot)
  544. {
  545. DestroyCursor(pTree->hCurHot);
  546. }
  547. if (IsWindow(pTree->hwndToolTips))
  548. {
  549. DestroyWindow(pTree->hwndToolTips);
  550. }
  551. pTree->hwndToolTips = NULL;
  552. if (IsWindow(pTree->hwndEdit))
  553. {
  554. DestroyWindow(pTree->hwndEdit);
  555. }
  556. pTree->hwndEdit = NULL;
  557. TV_DeleteItem(pTree, pTree->hRoot, TVDI_CHILDRENONLY | TVDI_NOSELCHANGE);
  558. if (pTree->hRoot)
  559. {
  560. Str_Set(&pTree->hRoot->lpstr, NULL);
  561. // No point in marking dead since the entire control is going away
  562. ControlFree(pTree->hheap, pTree->hRoot);
  563. }
  564. if (pTree->hdcBits)
  565. {
  566. if (pTree->hBmp)
  567. {
  568. SelectObject(pTree->hdcBits, pTree->hStartBmp);
  569. DeleteObject(pTree->hBmp);
  570. }
  571. DeleteDC(pTree->hdcBits);
  572. }
  573. if (pTree->fCreatedFont && pTree->hFont)
  574. {
  575. DeleteObject(pTree->hFont);
  576. }
  577. if (pTree->hFontBold)
  578. {
  579. DeleteObject(pTree->hFontBold);
  580. }
  581. Str_Set(&pTree->pszTip, NULL);
  582. if (pTree->pszTipA)
  583. {
  584. LocalFree(pTree->pszTipA);
  585. }
  586. TV_DeleteHotFonts(pTree);
  587. if (pTree->hdpaWatch)
  588. {
  589. DPA_Destroy(pTree->hdpaWatch);
  590. }
  591. IncrementSearchFree(&pTree->is);
  592. if (pTree->hTheme)
  593. {
  594. CloseThemeData(pTree->hTheme);
  595. }
  596. NearFree(pTree);
  597. // Don't try to use this var when window is destroyed...
  598. SetWindowInt(hwnd, 0, 0);
  599. }
  600. void TV_CreateToolTips(PTREE pTree);
  601. void TV_InitThemeMetrics(PTREE pTree, HTHEME hTheme)
  602. {
  603. COLORREF cr;
  604. HRESULT hr = GetThemeColor(hTheme, 0, 0, TMT_COLOR, &cr);
  605. if (SUCCEEDED(hr))
  606. SendMessage(pTree->ci.hwnd, TVM_SETBKCOLOR, 0, cr);
  607. // Line color
  608. hr = GetThemeColor(hTheme, TVP_BRANCH, 0, TMT_COLOR, &cr);
  609. if (SUCCEEDED(hr))
  610. SendMessage(pTree->ci.hwnd, TVM_SETLINECOLOR, 0, cr);
  611. }
  612. // ----------------------------------------------------------------------------
  613. //
  614. // Allocates space for the tree and initializes the tree's data
  615. //
  616. // ----------------------------------------------------------------------------
  617. LRESULT TV_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreate)
  618. {
  619. HTHEME hTheme;
  620. HRESULT hr = E_FAIL;
  621. PTREE pTree = NearAlloc(sizeof(TREE));
  622. if (!pTree)
  623. return -1; // fail the create window
  624. pTree->hheap = GetProcessHeap();
  625. if (!TV_CreateRoot(pTree))
  626. {
  627. NearFree((HLOCAL)pTree);
  628. return -1; // fail the create window
  629. }
  630. pTree->hdpaWatch = DPA_Create(8);
  631. if (!pTree->hdpaWatch)
  632. {
  633. // No point in marking dead since the entire control is going away
  634. ControlFree(pTree->hheap, pTree->hRoot);
  635. NearFree((HLOCAL)pTree);
  636. return -1; // fail the create window
  637. }
  638. SetWindowPtr(hwnd, 0, pTree);
  639. CIInitialize(&pTree->ci, hwnd, lpCreate);
  640. if (lpCreate->dwExStyle & WS_EX_RTLREADING)
  641. pTree->ci.style |= TVS_RTLREADING;
  642. pTree->fRedraw = TRUE;
  643. pTree->clrim = CLR_DEFAULT;
  644. pTree->clrText = (COLORREF)-1;
  645. pTree->clrBkNonTheme = pTree->clrBk = (COLORREF)-1;
  646. pTree->clrLineNonTheme = pTree->clrLine = CLR_DEFAULT;
  647. pTree->hbrLine = g_hbrGrayText;
  648. pTree->cxBorder = 3;
  649. hTheme = OpenThemeData(pTree->ci.hwnd, L"TreeView");
  650. if (hTheme)
  651. {
  652. TV_InitThemeMetrics(pTree, hTheme);
  653. }
  654. pTree->hTheme = hTheme;
  655. pTree->hbrText = g_hbrWindowText;
  656. // pTree->fHorz = FALSE;
  657. // pTree->fVert = FALSE;
  658. // pTree->fFocus = FALSE;
  659. // pTree->fNameEditPending = FALSE;
  660. // pTree->cxMax = 0;
  661. // pTree->cxWnd = 0;
  662. // pTree->cyWnd = 0;
  663. // pTree->hTop = NULL;
  664. // pTree->hCaret = NULL;
  665. // pTree->hDropTarget = NULL;
  666. // pTree->hOldDrop = NULL;
  667. // pTree->cItems = 0;
  668. // pTree->cShowing = 0;
  669. pTree->cFullVisible = 1;
  670. // pTree->hdcBits = NULL;
  671. // pTree->hBmp = NULL;
  672. // pTree->hbrBk = NULL;
  673. // pTree->xPos = 0;
  674. // pTree->cxIndent = 0; // init this for real in TV_OnSetFont()
  675. // pTree->dwCDDepth = 0;
  676. pTree->uMaxScrollTime = SSI_DEFAULT;
  677. pTree->dwLastAccId = 1; // Start at 1 because 0 means self
  678. // pTree->dwExStyle = 0;
  679. // pTree->fInTextCallback = FALSE;
  680. TV_OnSetFont(pTree, NULL, TRUE);
  681. // You cannot combine TVS_HASLINES and TVS_FULLROWSELECT
  682. // because it doesn't work
  683. if (pTree->ci.style & TVS_HASLINES) {
  684. if (pTree->ci.style & TVS_FULLROWSELECT) {
  685. DebugMsg(DM_ERROR, TEXT("Cannot combine TVS_HASLINES and TVS_FULLROWSELECT"));
  686. }
  687. pTree->ci.style &= ~TVS_FULLROWSELECT;
  688. }
  689. if (!(pTree->ci.style & TVS_NOTOOLTIPS)) {
  690. TV_CreateToolTips(pTree);
  691. }
  692. SetScrollRange(hwnd, SB_HORZ, 0, 0, TRUE);
  693. SetScrollRange(hwnd, SB_VERT, 0, 0, TRUE);
  694. return 0; // success
  695. }
  696. void TV_CreateToolTips(PTREE pTree)
  697. {
  698. DWORD exStyle = 0;
  699. if(pTree->ci.style & TVS_RTLREADING)
  700. {
  701. exStyle |= WS_EX_RTLREADING;
  702. }
  703. pTree->hwndToolTips = CreateWindowEx(WS_EX_TRANSPARENT | exStyle, c_szSToolTipsClass, NULL,
  704. WS_POPUP | TTS_NOPREFIX,
  705. CW_USEDEFAULT, CW_USEDEFAULT,
  706. CW_USEDEFAULT, CW_USEDEFAULT,
  707. pTree->ci.hwnd, NULL, HINST_THISDLL,
  708. NULL);
  709. if (pTree->hwndToolTips)
  710. {
  711. TOOLINFO ti;
  712. ti.cbSize = sizeof(ti);
  713. ti.uFlags = TTF_IDISHWND | TTF_TRANSPARENT;
  714. ti.hwnd = pTree->ci.hwnd;
  715. ti.uId = (UINT_PTR)pTree->ci.hwnd;
  716. ti.lpszText = LPSTR_TEXTCALLBACK;
  717. ti.lParam = 0;
  718. SendMessage(pTree->hwndToolTips, TTM_ADDTOOL, 0,
  719. (LPARAM)(LPTOOLINFO)&ti);
  720. SendMessage(pTree->hwndToolTips, WM_SETFONT, (WPARAM)pTree->hFont, (LPARAM)TRUE);
  721. SendMessage(pTree->hwndToolTips, TTM_SETDELAYTIME, TTDT_INITIAL, (LPARAM)500);
  722. }
  723. else
  724. pTree->ci.style |= (TVS_NOTOOLTIPS);
  725. }