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.

902 lines
26 KiB

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