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.

838 lines
25 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 NEAR TV_DeleteItemRecurse(PTREE pTree, TREEITEM FAR * hItem, UINT flags)
  11. {
  12. TREEITEM FAR *hKid;
  13. TREEITEM FAR *hNext;
  14. TREEITEM FAR *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. MyNotifyWinEvent(EVENT_OBJECT_DESTROY, pTree->ci.hwnd, OBJID_CLIENT,
  23. (LONG_PTR)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)) // BUGBUG: this is not set by anyone
  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 : TVSIF_NOTIFY, 0);
  112. ASSERT(pTree->hCaret != hItem);
  113. }
  114. // BUGBUG: might want to really do this
  115. ASSERT(pTree->hItemPainting != hItem);
  116. ControlFree(pTree->hheap, hItem);
  117. }
  118. // ----------------------------------------------------------------------------
  119. //
  120. // Removes the given item and all children from the tree.
  121. // Special case: if the given item is the hidden root, all children are
  122. // removed, but the hidden root is NOT removed.
  123. //
  124. // sets cItems
  125. //
  126. // ----------------------------------------------------------------------------
  127. BOOL NEAR TV_DeleteItem(PTREE pTree, TREEITEM FAR * hItem, UINT flags)
  128. {
  129. if (hItem == TVI_ROOT || !hItem)
  130. hItem = pTree->hRoot;
  131. // BUGUBG: send TVN_DELETEALLITEMS and TVDI_NONOTIFY if they respond
  132. // if (hItem == pTree->hRoot)
  133. // etc.
  134. if (!ValidateTreeItem(hItem, 0))
  135. return FALSE;
  136. // Collapse first to speed things up (not as much scroll bar recalcs) and
  137. // to set the top index correctly after the remove.
  138. if (hItem != pTree->hRoot)
  139. TV_Expand(pTree, TVE_COLLAPSE, hItem, FALSE);
  140. else
  141. {
  142. // TV_Expand punts on the root item, so manually iterate through it's kids
  143. TREEITEM *hKid = hItem->hKids;
  144. while (hKid)
  145. {
  146. TV_Expand(pTree, TVE_COLLAPSE, hKid, FALSE);
  147. if (!ValidateTreeItem(hKid, 0)) break; // callback during collapse could delete
  148. hKid = hKid->hNext;
  149. }
  150. }
  151. // Invalidate everything below this item; must be done AFTER setting the
  152. // selection
  153. if (hItem->hParent == pTree->hRoot || hItem == pTree->hRoot || ITEM_VISIBLE(hItem->hParent)) {
  154. if (pTree->fRedraw) {
  155. InvalidateRect(pTree->ci.hwnd, NULL, TRUE);
  156. }
  157. } else {
  158. TV_ScrollBelow(pTree, hItem->hParent, FALSE, FALSE);
  159. }
  160. // We can pass in the root to clear all items
  161. if (hItem == pTree->hRoot)
  162. flags |= TVDI_CHILDRENONLY;
  163. TV_DeleteItemRecurse(pTree, hItem, flags);
  164. ASSERT(pTree->hRoot); // didn't go too far, did we?
  165. // maybe everything's gone...
  166. // check out our cleanup job
  167. if (!pTree->hRoot->hKids) {
  168. // the tree itself
  169. ASSERT(pTree->cItems == 0);
  170. pTree->cItems = 0; // just removed it all, didn't we?
  171. // BUGBUG: this fails because we don't touch hTop if redraw is off
  172. // in TV_DeleteItemRecurse()
  173. // AssertMsg(pTree->hTop == NULL, TEXT("hTop not NULL, but empty tree"));
  174. pTree->hTop = NULL;
  175. AssertMsg(pTree->hCaret == NULL, TEXT("hCaret not NULL, but empty tree"));
  176. pTree->hCaret = NULL;
  177. pTree->fNameEditPending = FALSE;
  178. pTree->cxMax = 0;
  179. pTree->xPos = 0;
  180. // the invisible root
  181. ASSERT(pTree->hRoot->hNext == NULL);
  182. pTree->hRoot->hNext = NULL;
  183. ASSERT(pTree->hRoot->hParent == NULL);
  184. pTree->hRoot->hParent = NULL;
  185. ASSERT(pTree->hRoot->hKids == NULL);
  186. pTree->hRoot->hKids = NULL;
  187. ASSERT(pTree->hRoot->state & TVIS_EXPANDED);
  188. pTree->hRoot->state |= (TVIS_EXPANDED | TVIS_EXPANDEDONCE);
  189. ASSERT(pTree->hRoot->iLevel == (BYTE)-1);
  190. pTree->hRoot->iLevel = (BYTE) -1;
  191. ASSERT(pTree->hRoot->iShownIndex == (WORD)-1);
  192. pTree->hRoot->iShownIndex = (WORD) -1;
  193. }
  194. return TRUE;
  195. }
  196. // ----------------------------------------------------------------------------
  197. //
  198. // Creates the hidden root node for the tree -- all items will trace up to
  199. // this root, and the first child of the root is the first item in the tree.
  200. //
  201. // sets hRoot
  202. //
  203. // ----------------------------------------------------------------------------
  204. BOOL NEAR PASCAL TV_CreateRoot(PTREE pTree)
  205. {
  206. TREEITEM FAR * hRoot = ControlAlloc(pTree->hheap, sizeof(TREEITEM));
  207. if (!hRoot)
  208. return FALSE;
  209. // hRoot->hNext = NULL;
  210. // hRoot->hKids = NULL;
  211. // hRoot->hParent = NULL;
  212. hRoot->iLevel = (BYTE) -1;
  213. hRoot->state = (TVIS_EXPANDED | TVIS_EXPANDEDONCE);
  214. hRoot->iShownIndex = (WORD)-1;
  215. hRoot->wSignature = TV_SIG;
  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 NEAR DumpItem(TREEITEM FAR *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. TREEITEM FAR * NEAR TV_InsertItemA(PTREE pTree, LPTV_INSERTSTRUCTA lpis)
  247. {
  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. TREEITEM FAR * NEAR TV_InsertItem(PTREE pTree, LPTV_INSERTSTRUCT lpis)
  269. {
  270. TREEITEM FAR *hNewItem, FAR *hItem;
  271. TREEITEM FAR *hParent;
  272. TREEITEM FAR *hInsertAfter;
  273. UINT mask;
  274. if (!lpis)
  275. return NULL; //BUGBUG: Validate LPTV_INSERTSTRUCT
  276. // initialize _after_ the check for NULL!
  277. hParent = lpis->hParent;
  278. hInsertAfter = lpis->hInsertAfter;
  279. mask = lpis->DUMMYUNION_MEMBER(item).mask;
  280. // don't allow undefined bits
  281. AssertMsg((lpis->DUMMYUNION_MEMBER(item).mask & ~TVIF_ALL) == 0, TEXT("Invalid TVIF mask specified"));
  282. if (mask & ~TVIF_ALL) {
  283. // if they used bogus bits,
  284. // restrict to win95 bits only
  285. // I'd like to fail completely, but for win95 compat, we can't
  286. //
  287. // this fixes QuaterDesk's CleanSweep which has bogus garbage on the stack for a mask
  288. mask = (TVIF_WIN95 & mask);
  289. }
  290. TV_DismissEdit(pTree, FALSE);
  291. //
  292. // Zillions of apps pass garbage for hInsertAfter, so don't fail if
  293. // it's invalid. Fortunately, we never dereference hInsertAfter, so
  294. // garbage is okay.
  295. if (!ValidateTreeItem(hParent, VTI_NULLOK)) // NULL means TVI_ROOT
  296. return NULL;
  297. DBG_ValidateTreeItem(hInsertAfter, 0);
  298. hNewItem = ControlAlloc(pTree->hheap, sizeof(TREEITEM));
  299. if (!hNewItem)
  300. {
  301. TraceMsg(TF_ERROR, "TreeView: Out of memory");
  302. return NULL;
  303. }
  304. hNewItem->wSignature = TV_SIG;
  305. if (mask & TVIF_TEXT)
  306. {
  307. //
  308. // We will setup the text string next, before we link our self in
  309. // as to handle the case where we run out of memory and need to
  310. // destroy ourself without having to unlink.
  311. //
  312. if (!lpis->DUMMYUNION_MEMBER(item).pszText)
  313. {
  314. hNewItem->lpstr = LPSTR_TEXTCALLBACK;
  315. }
  316. else
  317. {
  318. if (!Str_Set(&hNewItem->lpstr, lpis->DUMMYUNION_MEMBER(item).pszText))
  319. {
  320. // Memory allocation failure...
  321. TraceMsg(TF_ERROR, "TreeView: Out of memory");
  322. TV_MarkAsDead(hNewItem);
  323. ControlFree(pTree->hheap, hNewItem);
  324. return NULL;
  325. }
  326. }
  327. } else
  328. Str_Set(&hNewItem->lpstr, c_szNULL);
  329. AssertMsg(hNewItem->lpstr != NULL, TEXT("Item added with NULL text"));
  330. if ((hParent == NULL) || (hParent == TVI_ROOT))
  331. {
  332. hParent = pTree->hRoot;
  333. if (!pTree->hTop)
  334. pTree->hTop = hNewItem;
  335. }
  336. else if (!pTree->hRoot->hKids)
  337. {
  338. TV_MarkAsDead(hNewItem);
  339. ControlFree(pTree->hheap, hNewItem);
  340. return NULL;
  341. }
  342. // We will do the sort later, so we can handle TEXTCALLBACK things
  343. if ((hInsertAfter == TVI_FIRST || hInsertAfter == TVI_SORT) || !hParent->hKids)
  344. {
  345. hNewItem->hNext = hParent->hKids;
  346. hParent->hKids = hNewItem;
  347. }
  348. else
  349. {
  350. // BUGBUG: we should cache the last insert after pointer to try to
  351. // catch the case of consecutive adds to the end of a node
  352. if (hInsertAfter == TVI_LAST)
  353. for (hItem = hParent->hKids; hItem->hNext; hItem = hItem->hNext)
  354. ;
  355. else
  356. {
  357. for (hItem = hParent->hKids; hItem->hNext; hItem = hItem->hNext)
  358. if (hItem == hInsertAfter)
  359. break;
  360. }
  361. hNewItem->hNext = hItem->hNext;
  362. hItem->hNext = hNewItem;
  363. }
  364. // hNewItem->hKids = NULL;
  365. hNewItem->hParent = hParent;
  366. hNewItem->iLevel = hParent->iLevel + 1;
  367. // hNewItem->iWidth = 0;
  368. // hNewItem->state = 0;
  369. if ((mask & TVIF_INTEGRAL) &&
  370. LOWORD(lpis->DUMMYUNION_MEMBER(itemex).iIntegral) > 0) {
  371. hNewItem->iIntegral = LOWORD(lpis->DUMMYUNION_MEMBER(itemex).iIntegral);
  372. } else {
  373. #ifdef CHEEDEBUG
  374. // just to get some variety
  375. hNewItem->iIntegral = ((((int)hNewItem) /100) % 2) + 1;
  376. #else
  377. hNewItem->iIntegral = 1;
  378. #endif
  379. }
  380. if (pTree->hTop == hNewItem)
  381. hNewItem->iShownIndex = 0; // calc me please!
  382. else
  383. hNewItem->iShownIndex = (WORD)-1; // calc me please!
  384. if (mask & TVIF_IMAGE)
  385. hNewItem->iImage = (WORD) lpis->DUMMYUNION_MEMBER(item).iImage;
  386. if (mask & TVIF_SELECTEDIMAGE)
  387. hNewItem->iSelectedImage = (WORD) lpis->DUMMYUNION_MEMBER(item).iSelectedImage;
  388. if (mask & TVIF_PARAM)
  389. hNewItem->lParam = lpis->DUMMYUNION_MEMBER(item).lParam;
  390. if (mask & TVIF_STATE)
  391. hNewItem->state = lpis->DUMMYUNION_MEMBER(item).state & lpis->DUMMYUNION_MEMBER(item).stateMask;
  392. // if we're in check box mode, inforce that it has a check box
  393. if (pTree->ci.style & TVS_CHECKBOXES) {
  394. if ((hNewItem->state & TVIS_STATEIMAGEMASK) == 0) {
  395. hNewItem->state |= INDEXTOSTATEIMAGEMASK(1);
  396. }
  397. }
  398. if ((hNewItem->state & TVIS_BOLD) && !pTree->hFontBold) //$BOLD
  399. TV_CreateBoldFont(pTree); //$BOLD
  400. // TraceMsg(TF_TRACE, "Tree: Inserting i = %d state = %d", TV_StateIndex(&lpis->item), lpis->item.state);
  401. if (mask & TVIF_CHILDREN) {
  402. switch (lpis->DUMMYUNION_MEMBER(item).cChildren) {
  403. case I_CHILDRENCALLBACK:
  404. hNewItem->fKids = KIDS_CALLBACK;
  405. break;
  406. case 0:
  407. hNewItem->fKids = KIDS_FORCE_NO;
  408. break;
  409. default:
  410. hNewItem->fKids = KIDS_FORCE_YES;
  411. break;
  412. }
  413. }
  414. // accept state bits on create?
  415. // mask & TVIF_STATE
  416. pTree->cItems++;
  417. // I don't want to do any callbacks until the item is completed
  418. // so sorting waits until the end
  419. // special case an only child for speed
  420. // (hKids && hKids->hNext means more than one child)
  421. if ((hInsertAfter == TVI_SORT) && hParent->hKids && hParent->hKids->hNext)
  422. {
  423. TVITEMEX sThisItem, sNextItem;
  424. TCHAR szThis[64], szNext[64]; // BUGBUG: these are too small
  425. sThisItem.pszText = szThis;
  426. sThisItem.cchTextMax = ARRAYSIZE(szThis);
  427. TV_GetItem(pTree, hNewItem, TVIF_TEXT, &sThisItem);
  428. // We know that the first kid of hParent is hNewItem
  429. for (hItem = hNewItem->hNext; hItem; hItem = hItem->hNext)
  430. {
  431. sNextItem.pszText = szNext;
  432. sNextItem.cchTextMax = ARRAYSIZE(szNext);
  433. TV_GetItem(pTree, hItem, TVIF_TEXT, &sNextItem);
  434. if (lstrcmpi(sThisItem.pszText, sNextItem.pszText) < 0)
  435. break;
  436. hInsertAfter = hItem;
  437. }
  438. // Check if this is still the first item
  439. if (hInsertAfter != TVI_SORT)
  440. {
  441. // Move this item from the beginning to where it
  442. // should be
  443. hParent->hKids = hNewItem->hNext;
  444. hNewItem->hNext = hInsertAfter->hNext;
  445. hInsertAfter->hNext = hNewItem;
  446. }
  447. }
  448. if ((hNewItem->hNext == pTree->hTop) && !pTree->fVert) {
  449. // there's no scrollbars and we got added before the top
  450. // item. we're now the top.
  451. hNewItem->iShownIndex = 0;
  452. pTree->hTop = hNewItem;
  453. }
  454. if (pTree->fRedraw)
  455. {
  456. BOOL fVert = pTree->fVert;
  457. RECT rc;
  458. RECT rc2;
  459. if (TV_ScrollBarsAfterAdd(pTree, hNewItem)) {
  460. // scroll everything down one
  461. if (ITEM_VISIBLE(hNewItem)) {
  462. int iTop = hNewItem->iShownIndex - pTree->hTop->iShownIndex;
  463. // if there wasn't a scrollbar and we're the 0th item,
  464. // TV_ScrollBarsAfterAdd already scrolled us
  465. if (iTop > 0 || !fVert)
  466. TV_ScrollItems(pTree, hNewItem->iIntegral, iTop + hNewItem->iIntegral - 1, TRUE);
  467. }
  468. }
  469. // connect the lines, add the buttons, etc. on the item above
  470. // TV_GetPrevVisItem only works after TV_Scroll* stuff is done
  471. if (TV_GetItemRect(pTree, hNewItem, &rc, FALSE)) {
  472. // find the previous sibling or the parent if no prev sib.
  473. if (hParent->hKids == hNewItem) {
  474. hItem = hParent;
  475. } else {
  476. hItem = hParent->hKids;
  477. while ( hItem->hNext != hNewItem ) {
  478. ASSERT(hItem->hNext);
  479. hItem = hItem->hNext;
  480. }
  481. }
  482. // invalidate from there to the new one
  483. if (TV_GetItemRect(pTree, hItem, &rc2, FALSE)) {
  484. rc.top = rc2.top;
  485. }
  486. RedrawWindow(pTree->ci.hwnd, &rc, NULL, RDW_INVALIDATE | RDW_ERASE);
  487. }
  488. }
  489. // DumpItem(hNewItem);
  490. MyNotifyWinEvent(EVENT_OBJECT_CREATE, pTree->ci.hwnd, OBJID_CLIENT, (LONG_PTR)hNewItem);
  491. if (pTree->hToolTip)
  492. {
  493. TV_PopBubble(pTree);
  494. }
  495. return hNewItem;
  496. }
  497. void TV_DeleteHotFonts(PTREE pTree)
  498. {
  499. if (pTree->hFontHot)
  500. DeleteObject(pTree->hFontHot);
  501. if (pTree->hFontBoldHot)
  502. DeleteObject(pTree->hFontBoldHot);
  503. pTree->hFontHot = pTree->hFontBoldHot = NULL;
  504. }
  505. // ----------------------------------------------------------------------------
  506. //
  507. // Frees all allocated memory and objects associated with the tree.
  508. //
  509. // ----------------------------------------------------------------------------
  510. void NEAR TV_DestroyTree(PTREE pTree)
  511. {
  512. HWND hwnd = pTree->ci.hwnd;
  513. ASSERT(pTree->hRoot);
  514. pTree->fRedraw = FALSE;
  515. TV_OnSetBkColor(pTree, (COLORREF)-1);
  516. if (pTree->hCurHot)
  517. DestroyCursor(pTree->hCurHot);
  518. if (IsWindow(pTree->hwndToolTips)) {
  519. DestroyWindow(pTree->hwndToolTips);
  520. }
  521. pTree->hwndToolTips = NULL;
  522. if (IsWindow(pTree->hwndEdit)) {
  523. DestroyWindow(pTree->hwndEdit);
  524. }
  525. pTree->hwndEdit = NULL;
  526. // BUGUBG: send TVN_DELETEALLITEMS and TVDI_NONOTIFY if they respond
  527. TV_DeleteItem(pTree, pTree->hRoot, TVDI_CHILDRENONLY | TVDI_NOSELCHANGE);
  528. if (pTree->hRoot)
  529. {
  530. Str_Set(&pTree->hRoot->lpstr, NULL);
  531. // No point in marking dead since the entire control is going away
  532. ControlFree(pTree->hheap, pTree->hRoot);
  533. }
  534. if (pTree->hdcBits)
  535. {
  536. if (pTree->hBmp)
  537. {
  538. SelectObject(pTree->hdcBits, pTree->hStartBmp);
  539. DeleteObject(pTree->hBmp);
  540. }
  541. DeleteDC(pTree->hdcBits);
  542. }
  543. if (pTree->fCreatedFont && pTree->hFont)
  544. DeleteObject(pTree->hFont);
  545. if (pTree->hFontBold) //$BOLD
  546. DeleteObject(pTree->hFontBold); //$BOLD
  547. Str_Set(&pTree->pszTip, NULL);
  548. if (pTree->pszTipA)
  549. LocalFree(pTree->pszTipA);
  550. TV_DeleteHotFonts(pTree);
  551. if (pTree->hdpaWatch)
  552. DPA_Destroy(pTree->hdpaWatch);
  553. IncrementSearchFree(&pTree->is);
  554. NearFree(pTree);
  555. // Don't try to use this var when window is destroyed...
  556. SetWindowInt(hwnd, 0, 0);
  557. }
  558. void TV_CreateToolTips(PTREE pTree);
  559. // ----------------------------------------------------------------------------
  560. //
  561. // Allocates space for the tree and initializes the tree's data
  562. //
  563. // ----------------------------------------------------------------------------
  564. LRESULT NEAR TV_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreate)
  565. {
  566. PTREE pTree = NearAlloc(sizeof(TREE));
  567. if (!pTree)
  568. return -1; // fail the create window
  569. pTree->hheap = GetProcessHeap();
  570. if (!TV_CreateRoot(pTree)) {
  571. NearFree((HLOCAL)pTree);
  572. return -1; // fail the create window
  573. }
  574. pTree->hdpaWatch = DPA_Create(8);
  575. if (!pTree->hdpaWatch) {
  576. // No point in marking dead since the entire control is going away
  577. ControlFree(pTree->hheap, pTree->hRoot);
  578. NearFree((HLOCAL)pTree);
  579. return -1; // fail the create window
  580. }
  581. SetWindowPtr(hwnd, 0, pTree);
  582. CIInitialize(&pTree->ci, hwnd, lpCreate);
  583. if (lpCreate->dwExStyle & WS_EX_RTLREADING)
  584. {
  585. pTree->ci.style |= TVS_RTLREADING;
  586. }
  587. #ifdef DEBUG
  588. if (GetAsyncKeyState(VK_SHIFT) < 0 &&
  589. GetAsyncKeyState(VK_CONTROL) < 0) {
  590. pTree->ci.style |= TVS_SHOWSELALWAYS; // | TVS_CHECKBOXES;
  591. SetWindowLong(pTree->ci.hwnd, GWL_STYLE, pTree->ci.style);
  592. }
  593. #endif
  594. pTree->fRedraw = TRUE;
  595. pTree->clrBk = (COLORREF)-1;
  596. pTree->clrText = (COLORREF)-1;
  597. pTree->clrim = CLR_DEFAULT;
  598. pTree->clrLine = CLR_DEFAULT;
  599. // pTree->fHorz = FALSE;
  600. // pTree->fVert = FALSE;
  601. // pTree->fFocus = FALSE;
  602. // pTree->fNameEditPending = FALSE;
  603. // pTree->cxMax = 0;
  604. // pTree->cxWnd = 0;
  605. // pTree->cyWnd = 0;
  606. // pTree->hTop = NULL;
  607. // pTree->hCaret = NULL;
  608. // pTree->hDropTarget = NULL;
  609. // pTree->hOldDrop = NULL;
  610. // pTree->cItems = 0;
  611. // pTree->cShowing = 0;
  612. pTree->cFullVisible = 1;
  613. // pTree->hdcBits = NULL;
  614. // pTree->hBmp = NULL;
  615. // pTree->hbrBk = NULL;
  616. // pTree->xPos = 0;
  617. // pTree->cxIndent = 0; // init this for real in TV_OnSetFont()
  618. // pTree->dwCDDepth = 0;
  619. pTree->uMaxScrollTime = SSI_DEFAULT;
  620. TV_OnSetFont(pTree, NULL, TRUE);
  621. // You cannot combine TVS_HASLINES and TVS_FULLROWSELECT
  622. // because it doesn't work
  623. if (pTree->ci.style & TVS_HASLINES) {
  624. if (pTree->ci.style & TVS_FULLROWSELECT) {
  625. DebugMsg(DM_ERROR, TEXT("Cannot combine TVS_HASLINES and TVS_FULLROWSELECT"));
  626. }
  627. pTree->ci.style &= ~TVS_FULLROWSELECT;
  628. }
  629. if (!(pTree->ci.style & TVS_NOTOOLTIPS)) {
  630. TV_CreateToolTips(pTree);
  631. }
  632. SetScrollRange(hwnd, SB_HORZ, 0, 0, TRUE);
  633. SetScrollRange(hwnd, SB_VERT, 0, 0, TRUE);
  634. return 0; // success
  635. }
  636. void TV_CreateToolTips(PTREE pTree)
  637. {
  638. DWORD exStyle = 0;
  639. if (pTree->ci.style & TVS_RTLREADING)
  640. {
  641. exStyle |= WS_EX_RTLREADING;
  642. }
  643. pTree->hwndToolTips = CreateWindowEx(exStyle, c_szSToolTipsClass, NULL,
  644. WS_POPUP | TTS_NOPREFIX,
  645. CW_USEDEFAULT, CW_USEDEFAULT,
  646. CW_USEDEFAULT, CW_USEDEFAULT,
  647. pTree->ci.hwnd, NULL, HINST_THISDLL,
  648. NULL);
  649. if (pTree->hwndToolTips)
  650. {
  651. TOOLINFO ti;
  652. ti.cbSize = sizeof(ti);
  653. ti.uFlags = TTF_IDISHWND | TTF_TRANSPARENT;
  654. ti.hwnd = pTree->ci.hwnd;
  655. ti.uId = (UINT_PTR)pTree->ci.hwnd;
  656. ti.lpszText = LPSTR_TEXTCALLBACK;
  657. ti.lParam = 0;
  658. SendMessage(pTree->hwndToolTips, TTM_ADDTOOL, 0,
  659. (LPARAM)(LPTOOLINFO)&ti);
  660. SendMessage(pTree->hwndToolTips, WM_SETFONT, (WPARAM)pTree->hFont, (LPARAM)TRUE);
  661. SendMessage(pTree->hwndToolTips, TTM_SETDELAYTIME, TTDT_INITIAL, (LPARAM)500);
  662. }
  663. else
  664. {
  665. pTree->ci.style |= (TVS_NOTOOLTIPS);
  666. }
  667. }