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.

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