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.

4081 lines
128 KiB

  1. #include "ctlspriv.h"
  2. #include "treeview.h"
  3. #include "listview.h"
  4. // BUGBUG -- penwin.h is messed up; define local stuff for now
  5. #define HN_BEGINDIALOG 40 // Lens/EditText/garbage detection dialog is about
  6. // to come up on this hedit/bedit
  7. #define HN_ENDDIALOG 41 // Lens/EditText/garbage detection dialog has
  8. // just been destroyed
  9. //---------------------------------------------------------
  10. #define IDT_SCROLLWAIT 43
  11. //-----------------------
  12. // ToolTip stuff...
  13. //
  14. #define REPEATTIME SendMessage(pTree->hwndToolTips,TTM_GETDELAYTIME,(WPARAM)TTDT_RESHOW, 0)
  15. #define CHECKFOCUSTIME (REPEATTIME)
  16. #define IDT_TOOLTIPWAIT 2
  17. #define IDT_FOCUSCHANGE 3
  18. // in tooltips.c
  19. BOOL ChildOfActiveWindow(HWND hwnd);
  20. void TV_HandleStateIconClick(PTREE pTree, HTREEITEM hItem);
  21. HWND TV_EditLabel(PTREE pTree, HTREEITEM hItem, LPTSTR pszInitial);
  22. void TV_CancelEditTimer(PTREE pTree);
  23. BOOL TV_SetItem(PTREE pTree, LPCTVITEMEX ptvi);
  24. void TV_DeleteHotFonts(PTREE pTree);
  25. BOOL TV_IsShowing(HTREEITEM hItem);
  26. LRESULT TV_OnScroll(PTREE ptv, LPNMHDR pnm);
  27. #define TVBD_FROMWHEEL 0x0001
  28. #define TVBD_WHEELFORWARD 0x0002
  29. #define TVBD_WHEELBACK 0x0004
  30. BOOL ValidateTreeItem(TREEITEM FAR * hItem, UINT flags)
  31. {
  32. BOOL fValid = TRUE;
  33. /*
  34. * Check the values to make sure the new Win64-compatible values
  35. * are consistent with the old Win32 values.
  36. */
  37. COMPILETIME_ASSERT(
  38. (DWORD)(ULONG_PTR)TVI_ROOT == 0xFFFF0000 &&
  39. (DWORD)(ULONG_PTR)TVI_FIRST == 0xFFFF0001 &&
  40. (DWORD)(ULONG_PTR)TVI_LAST == 0xFFFF0002 &&
  41. (DWORD)(ULONG_PTR)TVI_SORT == 0xFFFF0003);
  42. if (hItem) {
  43. if (HIWORD64(hItem) == HIWORD64(TVI_ROOT)) {
  44. switch (LOWORD(hItem)) {
  45. //#pragma warning(disable:4309)
  46. case LOWORD(TVI_ROOT):
  47. case LOWORD(TVI_FIRST):
  48. case LOWORD(TVI_LAST):
  49. case LOWORD(TVI_SORT):
  50. //#pragma warning(default:4309)
  51. break;
  52. default:
  53. AssertMsg(FALSE, TEXT("ValidateTreeItem() Invalid special item"));
  54. fValid = FALSE;
  55. break;
  56. }
  57. } else {
  58. __try {
  59. // Use "volatile" to force memory access at start of struct
  60. *(volatile LPVOID *)hItem;
  61. fValid = hItem->wSignature == TV_SIG;
  62. } __except(EXCEPTION_EXECUTE_HANDLER) {
  63. fValid = FALSE;
  64. } __endexcept
  65. }
  66. } else if (!flags) { // The only flag is VTI_NULLOK
  67. DebugMsg(DM_ERROR, TEXT("ValidateTreeItem(): NULL HTREEITEM"));
  68. fValid = FALSE;
  69. }
  70. return fValid;
  71. }
  72. // ----------------------------------------------------------------------------
  73. //
  74. // Initialize TreeView on library entry -- register SysTreeView class
  75. //
  76. // ----------------------------------------------------------------------------
  77. #pragma code_seg(CODESEG_INIT)
  78. BOOL FAR TV_Init(HINSTANCE hinst)
  79. {
  80. WNDCLASS wc;
  81. wc.lpfnWndProc = TV_WndProc;
  82. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  83. wc.hIcon = NULL;
  84. wc.lpszMenuName = NULL;
  85. wc.hInstance = hinst;
  86. wc.lpszClassName = c_szTreeViewClass;
  87. wc.hbrBackground = NULL;
  88. wc.style = CS_DBLCLKS | CS_GLOBALCLASS;
  89. wc.cbWndExtra = sizeof(PTREE);
  90. wc.cbClsExtra = 0;
  91. RegisterClass(&wc);
  92. return TRUE;
  93. }
  94. #pragma code_seg()
  95. // ----------------------------------------------------------------------------
  96. //
  97. // If the tooltip bubble is up, then pop it.
  98. //
  99. // ----------------------------------------------------------------------------
  100. void TV_PopBubble(PTREE pTree)
  101. {
  102. if (pTree->hwndToolTips && pTree->hToolTip)
  103. {
  104. pTree->hToolTip = NULL;
  105. SendMessage(pTree->hwndToolTips, TTM_POP, 0L, 0L);
  106. }
  107. }
  108. // ----------------------------------------------------------------------------
  109. //
  110. // Sends a TVN_BEGINDRAG or TVN_BEGINRDRAG notification with information in the ptDrag and
  111. // itemNew fields of an NM_TREEVIEW structure
  112. //
  113. // ----------------------------------------------------------------------------
  114. BOOL NEAR TV_SendBeginDrag(PTREE pTree, int code, TREEITEM FAR * hItem, int x, int y)
  115. {
  116. NM_TREEVIEW nm;
  117. TV_PopBubble(pTree); // dismiss the infotip if we start to drag
  118. nm.itemNew.hItem = hItem;
  119. nm.itemNew.state = hItem->state;
  120. nm.itemNew.lParam = hItem->lParam;
  121. nm.itemNew.mask = (TVIF_HANDLE | TVIF_STATE | TVIF_PARAM);
  122. nm.itemOld.mask = 0;
  123. nm.ptDrag.x = x;
  124. nm.ptDrag.y = y;
  125. return (BOOL)CCSendNotify(&pTree->ci, code, &nm.hdr);
  126. }
  127. // ----------------------------------------------------------------------------
  128. //
  129. // Sends a TVN_ITEMEXPANDING or TVN_ITEMEXPANDED notification with information
  130. // in the action and itemNew fields of an NM_TREEVIEW structure
  131. //
  132. // Returns FALSE to allow processing to continue, or TRUE to stop.
  133. //
  134. // If the hItem is destroyed by the callback, then we always return TRUE.
  135. //
  136. // Note that the application cannot stop a TVN_ITEMEXPANDED, so the only
  137. // way a TVN_ITEMEXPANDED can return "Stop" is if the item got destroyed.
  138. //
  139. // ----------------------------------------------------------------------------
  140. BOOL NEAR TV_SendItemExpand(PTREE pTree, int code, TREEITEM FAR * hItem, WPARAM action)
  141. {
  142. NM_TREEVIEW nm;
  143. TVWATCHEDITEM wi;
  144. BOOL fResult;
  145. BOOL fWatched;
  146. ASSERT(code == TVN_ITEMEXPANDING || code == TVN_ITEMEXPANDED);
  147. nm.itemNew.mask = 0;
  148. nm.itemNew.hItem = hItem;
  149. if (hItem == TVI_ROOT)
  150. hItem = pTree->hRoot;
  151. nm.itemNew.state = hItem->state;
  152. nm.itemNew.lParam = hItem->lParam;
  153. nm.itemNew.iImage = hItem->iImage;
  154. nm.itemNew.iSelectedImage = hItem->iSelectedImage;
  155. switch(hItem->fKids) {
  156. case KIDS_CALLBACK:
  157. case KIDS_FORCE_YES:
  158. nm.itemNew.cChildren = 1;
  159. nm.itemNew.mask = TVIF_CHILDREN;
  160. break;
  161. case KIDS_FORCE_NO:
  162. nm.itemNew.cChildren = 0;
  163. nm.itemNew.mask = TVIF_CHILDREN;
  164. break;
  165. }
  166. nm.itemNew.mask |= (TVIF_HANDLE | TVIF_STATE | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE);
  167. nm.itemOld.mask = 0;
  168. nm.action = (UINT)(action & TVE_ACTIONMASK);
  169. //
  170. // Some apps will delete the item while it is being expanded, since
  171. // during expansion, they will realize, "Hey, the thing represented
  172. // by this item no longer exists, I'd better delete it." (E.g,.
  173. // Explorer.) So keep an eye on the item so we don't fault when
  174. // this happens.
  175. //
  176. // If we can't start a watch, then tough, just send the notification
  177. // the unsafe way.
  178. fWatched = TV_StartWatch(pTree, &wi, hItem);
  179. fResult = (BOOL)CCSendNotify(&pTree->ci, code, &nm.hdr);
  180. // The app return code from TVN_ITEMEXPANDED is ignored.
  181. // You can't stop a TVN_ITEMEXPANDED; it's already happened.
  182. if (code == TVN_ITEMEXPANDED)
  183. fResult = FALSE; // Continue processing
  184. if (fWatched) {
  185. if (!TV_IsWatchValid(pTree, &wi))
  186. fResult = TRUE; // Oh no! Stop!
  187. TV_EndWatch(pTree, &wi);
  188. }
  189. return fResult;
  190. }
  191. // ----------------------------------------------------------------------------
  192. //
  193. // Sends a TVN_SELCHANGING or TVN_SELCHANGED notification with information in
  194. // the itemOld and itemNew fields of an NM_TREEVIEW structure
  195. //
  196. // ----------------------------------------------------------------------------
  197. BOOL NEAR TV_SendSelChange(PTREE pTree, int code, TREEITEM FAR * hOldItem, TREEITEM FAR * hNewItem, UINT action)
  198. {
  199. NM_TREEVIEW nm;
  200. nm.action = action;
  201. nm.itemNew.hItem = hNewItem;
  202. nm.itemNew.state = hNewItem ? hNewItem->state : 0;
  203. nm.itemNew.lParam = hNewItem ? hNewItem->lParam : 0;
  204. nm.itemNew.mask = (TVIF_HANDLE | TVIF_STATE | TVIF_PARAM);
  205. nm.itemOld.hItem = hOldItem;
  206. nm.itemOld.state = hOldItem ? hOldItem->state : 0;
  207. nm.itemOld.lParam = hOldItem ? hOldItem->lParam : 0;
  208. nm.itemOld.mask = (TVIF_HANDLE | TVIF_STATE | TVIF_PARAM);
  209. return (BOOL)CCSendNotify(&pTree->ci, code, &nm.hdr);
  210. }
  211. // ----------------------------------------------------------------------------
  212. //
  213. // Returns the first visible item above the given item in the tree.
  214. //
  215. // ----------------------------------------------------------------------------
  216. TREEITEM FAR * NEAR TV_GetPrevVisItem(TREEITEM FAR * hItem)
  217. {
  218. TREEITEM FAR * hParent = hItem->hParent;
  219. TREEITEM FAR * hWalk;
  220. DBG_ValidateTreeItem(hItem, 0);
  221. if (hParent->hKids == hItem)
  222. return VISIBLE_PARENT(hItem);
  223. for (hWalk = hParent->hKids; hWalk->hNext != hItem; hWalk = hWalk->hNext);
  224. checkKids:
  225. if (hWalk->hKids && (hWalk->state & TVIS_EXPANDED))
  226. {
  227. for (hWalk = hWalk->hKids; hWalk->hNext; hWalk = hWalk->hNext);
  228. goto checkKids;
  229. }
  230. return(hWalk);
  231. }
  232. // ----------------------------------------------------------------------------
  233. //
  234. // Returns the first visible item below the given item in the tree.
  235. //
  236. // ----------------------------------------------------------------------------
  237. TREEITEM FAR * NEAR TV_GetNextVisItem(TREEITEM FAR * hItem)
  238. {
  239. DBG_ValidateTreeItem(hItem, 0);
  240. if (hItem->hKids && (hItem->state & TVIS_EXPANDED))
  241. return hItem->hKids;
  242. checkNext:
  243. if (hItem->hNext)
  244. return(hItem->hNext);
  245. hItem = hItem->hParent;
  246. if (hItem)
  247. goto checkNext;
  248. return NULL;
  249. }
  250. // ----------------------------------------------------------------------------
  251. //
  252. // Determine what part of what item is at the given (x,y) location in the
  253. // tree's client area. If the location is outside the client area, NULL is
  254. // returned with the TVHT_TOLEFT, TVHT_TORIGHT, TVHT_ABOVE, and/or TVHT_BELOW
  255. // flags set in the wHitCode as appropriate. If the location is below the
  256. // last item, NULL is returned with wHitCode set to TVHT_NOWHERE. Otherwise,
  257. // the item is returned with wHitCode set to either TVHT_ONITEMINDENT,
  258. // TVHT_ONITEMBUTTON, TVHT_ONITEMICON, TVHT_ONITEMLABEL, or TVHT_ONITEMRIGHT
  259. //
  260. // ----------------------------------------------------------------------------
  261. TREEITEM FAR * NEAR TV_CheckHit(PTREE pTree, int x, int y, UINT FAR *wHitCode)
  262. {
  263. TREEITEM FAR * hItem = pTree->hTop;
  264. int cxState;
  265. TVITEMEX sItem;
  266. *wHitCode = 0;
  267. if (x < 0)
  268. *wHitCode |= TVHT_TOLEFT;
  269. else if (x > (int) pTree->cxWnd)
  270. *wHitCode |= TVHT_TORIGHT;
  271. if (y < 0)
  272. *wHitCode |= TVHT_ABOVE;
  273. else if (y > (int) pTree->cyWnd)
  274. *wHitCode |= TVHT_BELOW;
  275. if (*wHitCode)
  276. return NULL;
  277. {
  278. int index = y / pTree->cyItem;
  279. while (hItem && index >= hItem->iIntegral) {
  280. index -= hItem->iIntegral;
  281. hItem = TV_GetNextVisItem(hItem);
  282. }
  283. }
  284. if (!hItem)
  285. {
  286. *wHitCode = TVHT_NOWHERE;
  287. return NULL;
  288. }
  289. x -= (pTree->cxBorder + (hItem->iLevel * pTree->cxIndent));
  290. x += pTree->xPos;
  291. if ((pTree->ci.style & (TVS_HASLINES | TVS_HASBUTTONS)) &&
  292. (pTree->ci.style &TVS_LINESATROOT))
  293. {
  294. // Subtract some more to make up for the pluses at the root
  295. x -= pTree->cxIndent;
  296. }
  297. TV_GetItem(pTree, hItem, TVIF_CHILDREN, &sItem);
  298. cxState = TV_StateIndex(&sItem) ? pTree->cxState : 0;
  299. if (x <= (int) (hItem->iWidth + pTree->cxImage + cxState))
  300. {
  301. if (x >= 0) {
  302. if (pTree->himlState && (x < cxState)) {
  303. *wHitCode = TVHT_ONITEMSTATEICON;
  304. } else if (pTree->hImageList && (x < (int) pTree->cxImage + cxState)) {
  305. *wHitCode = TVHT_ONITEMICON;
  306. } else {
  307. *wHitCode = TVHT_ONITEMLABEL;
  308. }
  309. } else if ((x >= -pTree->cxIndent) && sItem.cChildren && (pTree->ci.style & TVS_HASBUTTONS))
  310. *wHitCode = TVHT_ONITEMBUTTON;
  311. else
  312. *wHitCode = TVHT_ONITEMINDENT;
  313. }
  314. else
  315. *wHitCode = TVHT_ONITEMRIGHT;
  316. return hItem;
  317. }
  318. // This is tricky because CheckForDragBegin yields and the app may have
  319. // destroyed the item we are thinking about dragging
  320. //
  321. // To give the app some feedback, we give the hItem the drop highlight
  322. // if it isn't already the caret. This also allows us to check if the
  323. // item got deleted behind our back - TV_DeleteItemRecurse makes sure
  324. // that deleted items are never the hCaret or hDropTarget.
  325. //
  326. // After TV_CheckForDragBegin, the caller must call TV_FinishCheckDrag
  327. // to clean up the UI changes that TV_CheckForDragBegin temporarily
  328. // performed.
  329. //
  330. BOOL TV_CheckForDragBegin(PTREE pTree, HTREEITEM hItem, int x, int y)
  331. {
  332. BOOL fDrag;
  333. //
  334. // If the item is not the caret, then make it the (temporary)
  335. // drop target so the user gets some feedback.
  336. //
  337. // BUGBUG raymondc - If hItem == pTree->hCaret, it still might not
  338. // be visible if the control doesn't yet have focus and the treeview
  339. // is not marked showselalways. Maybe we should just always set
  340. // hItem to DROPHILITE.
  341. //
  342. if (hItem == pTree->hCaret)
  343. {
  344. pTree->hOldDrop = NULL;
  345. pTree->fRestoreOldDrop = FALSE;
  346. }
  347. else
  348. {
  349. pTree->hOldDrop = pTree->hDropTarget;
  350. pTree->fRestoreOldDrop = TRUE;
  351. TV_SelectItem(pTree, TVGN_DROPHILITE, hItem, 0, TVC_BYMOUSE);
  352. ASSERT(hItem == pTree->hDropTarget);
  353. }
  354. //
  355. // We are dragging the hItem if CheckForDragBegin says okay,
  356. // and TV_DeleteItemRecurse didn't wipe us out.
  357. //
  358. fDrag = CheckForDragBegin(pTree->ci.hwnd, x, y) &&
  359. (hItem == pTree->hDropTarget || hItem == pTree->hCaret);
  360. return fDrag;
  361. }
  362. void TV_FinishCheckDrag(PTREE pTree)
  363. {
  364. //
  365. // Clean up our temporary UI changes that happened when we started
  366. // dragging.
  367. //
  368. if (pTree->fRestoreOldDrop)
  369. {
  370. HTREEITEM hOldDrop = pTree->hOldDrop;
  371. pTree->fRestoreOldDrop = FALSE;
  372. pTree->hOldDrop = NULL;
  373. TV_SelectItem(pTree, TVGN_DROPHILITE, hOldDrop, 0, TVC_BYMOUSE);
  374. }
  375. }
  376. void NEAR TV_SendRButtonDown(PTREE pTree, int x, int y)
  377. {
  378. BOOL fRet = FALSE;
  379. UINT wHitCode;
  380. TREEITEM FAR * hItem = TV_CheckHit(pTree, x, y, &wHitCode);
  381. HWND hwnd = pTree->ci.hwnd;
  382. if (!TV_DismissEdit(pTree, FALSE)) // end any previous editing (accept it)
  383. return; // Something happened such that we should not process button down
  384. //
  385. // Need to see if the user is going to start a drag operation
  386. //
  387. GetMessagePosClient(pTree->ci.hwnd, &pTree->ptCapture);
  388. if (TV_CheckForDragBegin(pTree, hItem, x, y))
  389. {
  390. // let them start dragging
  391. if (hItem)
  392. {
  393. pTree->htiDrag = hItem;
  394. TV_SendBeginDrag(pTree, TVN_BEGINRDRAG, hItem, x, y);
  395. }
  396. }
  397. else if (!IsWindow(hwnd))
  398. {
  399. return; // bail!
  400. }
  401. else
  402. {
  403. SetFocus(pTree->ci.hwnd); // Activate this window like listview...
  404. fRet = !CCSendNotify(&pTree->ci, NM_RCLICK, NULL);
  405. }
  406. // Don't finish the CheckForDragBegin until after the NM_RCLICK
  407. // because apps want to display the context menu while the
  408. // temporary drag UI is still active.
  409. TV_FinishCheckDrag(pTree);
  410. if (fRet)
  411. SendMessage(pTree->ci.hwndParent, WM_CONTEXTMENU, (WPARAM)pTree->ci.hwnd, GetMessagePos());
  412. }
  413. // ----------------------------------------------------------------------------
  414. //
  415. // If the given item is visible in the client area, the rectangle that
  416. // surrounds that item is invalidated
  417. //
  418. // ----------------------------------------------------------------------------
  419. void NEAR TV_InvalidateItem(PTREE pTree, TREEITEM FAR * hItem, UINT fRedraw)
  420. {
  421. RECT rc;
  422. if (hItem && pTree->fRedraw && TV_GetItemRect(pTree, hItem, &rc, FALSE))
  423. {
  424. RedrawWindow(pTree->ci.hwnd, &rc, NULL, fRedraw);
  425. }
  426. }
  427. //
  428. // Given an item, compute where the text of this item ends up being painted.
  429. // Basically, stare at TV_DrawItem and dutifully reproduce all the code that
  430. // messes with the x-coordinate.
  431. //
  432. int FAR PASCAL ITEM_OFFSET(PTREE pTree, HTREEITEM hItem)
  433. {
  434. int x = pTree->cxBorder + (hItem->iLevel * pTree->cxIndent);
  435. // state image
  436. // BUGBUG -- doesn't handle TVCDRF_NOIMAGES - whose idea was that?
  437. if (pTree->himlState && TV_StateIndex(hItem))
  438. x += pTree->cxState;
  439. // image
  440. if (pTree->hImageList) {
  441. // even if not drawing image, draw text in right place
  442. x += pTree->cxImage;
  443. }
  444. // "plus" at the front of the tree
  445. if ((pTree->ci.style & TVS_LINESATROOT) &&
  446. (pTree->ci.style & (TVS_HASLINES | TVS_HASBUTTONS)))
  447. x += pTree->cxIndent;
  448. return x;
  449. }
  450. // ----------------------------------------------------------------------------
  451. //
  452. // If the given item is visible in the client area, the rectangle that
  453. // surrounds that item is filled into lprc
  454. //
  455. // Returns TRUE if the item is shown, FALSE otherwise
  456. //
  457. // ----------------------------------------------------------------------------
  458. BOOL NEAR TV_GetItemRect(PTREE pTree, TREEITEM FAR * hItem, LPRECT lprc, BOOL bItemRect)
  459. {
  460. UINT iOffset;
  461. if (!hItem)
  462. return FALSE;
  463. DBG_ValidateTreeItem(hItem, 0);
  464. if (!ITEM_VISIBLE(hItem))
  465. return FALSE;
  466. iOffset = hItem->iShownIndex - pTree->hTop->iShownIndex;
  467. if (bItemRect) {
  468. // Calculate where X position should start...
  469. lprc->left = -pTree->xPos + ITEM_OFFSET(pTree, hItem);
  470. lprc->right = lprc->left + hItem->iWidth;
  471. } else {
  472. lprc->left = 0;
  473. lprc->right = pTree->cxWnd;
  474. }
  475. lprc->top = iOffset * pTree->cyItem;
  476. lprc->bottom = lprc->top + (pTree->cyItem * hItem->iIntegral) ;
  477. return TRUE;
  478. }
  479. void NEAR TV_OnSetRedraw(PTREE pTree, BOOL fRedraw)
  480. {
  481. pTree->fRedraw = TRUE && fRedraw;
  482. if (pTree->fRedraw)
  483. {
  484. // This use to only refresh the items from hTop down, this is bad as if items are inserted
  485. // before the visible point within the tree then we would fail!
  486. if ( pTree->hRoot )
  487. pTree->cShowing = TV_UpdateShownIndexes(pTree,pTree->hRoot);
  488. // Must force recalculation of all tree items to get the right cxMax.
  489. TV_ScrollBarsAfterSetWidth(pTree, NULL);
  490. InvalidateRect(pTree->ci.hwnd, NULL, TRUE); //REVIEW: could be smarter
  491. }
  492. }
  493. // Treeview item watching implementation
  494. //
  495. // You need to "watch" an item any time you hold onto its HTREEITEM
  496. // and then yield control to the application. If you didn't watch
  497. // the item, then if the app deletes the item, you end up with a
  498. // stale HTREEITEM pointer and fault.
  499. //
  500. // To begin watching an item, call TV_StartWatch with the item you
  501. // want to start watching. When finished watching, call TV_EndWatch.
  502. //
  503. // In between, you can call TV_IsWatchStale() which tells you if the
  504. // item has been deleted behind your back and you shouldn't use it.
  505. // Alternatively, use TV_IsWatchValid() which says if it's okay.
  506. //
  507. // Additional bonus behavior for enumeration: If the watched item
  508. // is deleted, we cache the hNext item so that you can step to the
  509. // item after the one that got deleted. Note that this works even
  510. // if the hNext item gets deleted before you get a chance to look,
  511. // because we just move the cached item to the hNext's hNext.
  512. //
  513. // Sample usage for watching:
  514. //
  515. // TVWATCHEDITEM wi;
  516. // if (TV_StartWatch(pTree, &wi, htiStartHere)) {
  517. // FunctionThatYields();
  518. // if (TV_IsWatchValid(pTree, &wi)) {
  519. // KeepUsing(htiStartHere);
  520. // } else {
  521. // // item was deleted while we yielded; stop using it
  522. // }
  523. // TV_EndWatch(pTree, &wi);
  524. // }
  525. //
  526. // Sample usage for enumerating:
  527. //
  528. // TVWATCHEDITEM wi;
  529. // if (TV_StartWatch(pTree, &wi, htiFirst)) {
  530. // while (TV_GetWatchItem(pTree, &wi)) {
  531. // FunctionThatYields(TV_GetWatchItem(pTree, &wi));
  532. // if (TV_IsWatchValid(pTree, &wi)) {
  533. // KeepUsing(htiStartHere);
  534. // } else {
  535. // // item was deleted while we yielded; stop using it
  536. // }
  537. // TV_NextWatchItem(pTree, &wi);
  538. // }
  539. // TV_EndWatch(pTree, &wi);
  540. // }
  541. //
  542. //
  543. //
  544. //
  545. // TV_StartWatch - Begin watching an item.
  546. //
  547. // Returns FALSE if out of memory.
  548. //
  549. BOOL TV_StartWatch(PTREE pTree, PTVWATCHEDITEM pwi, HTREEITEM htiStart)
  550. {
  551. pwi->hti = htiStart;
  552. pwi->fStale = FALSE;
  553. return DPA_AppendPtr(pTree->hdpaWatch, pwi) != -1;
  554. }
  555. //
  556. // TV_EndWatch - Remove the item from the watch list.
  557. //
  558. BOOL TV_EndWatch(PTREE pTree, PTVWATCHEDITEM pwi)
  559. {
  560. int i = DPA_GetPtrCount(pTree->hdpaWatch);
  561. while (--i >= 0)
  562. {
  563. PTVWATCHEDITEM pwiT = DPA_FastGetPtr(pTree->hdpaWatch, i);
  564. ASSERT(pwiT);
  565. if (pwi == pwiT)
  566. {
  567. DPA_DeletePtr(pTree->hdpaWatch, i);
  568. return TRUE;
  569. }
  570. }
  571. ASSERT(!"TV_EndWatch: Item not in list");
  572. return FALSE;
  573. }
  574. // End of treeview item watching implementation
  575. void NEAR TV_SetItemRecurse(PTREE pTree, TREEITEM FAR *hItem, LPTVITEMEX ptvi)
  576. {
  577. // Note: This code assumes nobody will try to delete an item
  578. // during a SetItem notification.
  579. while (hItem) {
  580. ptvi->hItem = hItem;
  581. TV_SetItem(pTree, ptvi);
  582. if (hItem->hKids) {
  583. TV_SetItemRecurse(pTree, hItem->hKids, ptvi);
  584. }
  585. hItem = hItem->hNext;
  586. }
  587. }
  588. BOOL NEAR TV_DoExpandRecurse(PTREE pTree, TREEITEM FAR *hItem, BOOL fNotify)
  589. {
  590. TVWATCHEDITEM wi;
  591. BOOL fRc = FALSE;
  592. if (TV_StartWatch(pTree, &wi, hItem))
  593. {
  594. while ((hItem = TV_GetWatchItem(pTree, &wi))) {
  595. // was the escape key pressed at any point since the last check?
  596. if (GetAsyncKeyState(VK_ESCAPE) & 0x1)
  597. goto failed;
  598. TV_Expand(pTree, TVE_EXPAND, hItem, fNotify); // yields
  599. if (TV_IsWatchValid(pTree, &wi)) {
  600. if (hItem->hKids) {
  601. if (!TV_DoExpandRecurse(pTree, hItem->hKids, fNotify))
  602. goto failed;
  603. }
  604. }
  605. TV_NextWatchItem(pTree, &wi);
  606. }
  607. fRc = TRUE;
  608. failed:
  609. TV_EndWatch(pTree, &wi);
  610. }
  611. return fRc;
  612. }
  613. void NEAR TV_ExpandRecurse(PTREE pTree, TREEITEM FAR *hItem, BOOL fNotify)
  614. {
  615. BOOL fRedraw = pTree->fRedraw;
  616. TV_OnSetRedraw(pTree, FALSE);
  617. // we're going to check this after each expand so clear it first
  618. GetAsyncKeyState(VK_ESCAPE);
  619. TV_Expand(pTree, TVE_EXPAND, hItem, fNotify);
  620. // BUGBUG hItem may have gone bad during that TV_Expand
  621. TV_DoExpandRecurse(pTree, hItem->hKids, fNotify);
  622. TV_OnSetRedraw(pTree, fRedraw);
  623. }
  624. void NEAR TV_ExpandParents(PTREE pTree, TREEITEM FAR *hItem, BOOL fNotify)
  625. {
  626. hItem = hItem->hParent;
  627. if (hItem) {
  628. TVWATCHEDITEM wi;
  629. if (TV_StartWatch(pTree, &wi, hItem)) {
  630. TV_ExpandParents(pTree, hItem, fNotify);
  631. // Item may have gone invalid during expansion
  632. if (TV_IsWatchValid(pTree, &wi) &&
  633. // make sure this item is not in a collapsed branch
  634. !(hItem->state & TVIS_EXPANDED)) {
  635. TV_Expand(pTree, TVE_EXPAND, hItem, fNotify);
  636. }
  637. TV_EndWatch(pTree, &wi);
  638. }
  639. }
  640. }
  641. // makes sure an item is expanded and scrolled into view
  642. BOOL NEAR TV_EnsureVisible(PTREE pTree, TREEITEM FAR * hItem)
  643. {
  644. TV_ExpandParents(pTree, hItem, TRUE);
  645. return TV_ScrollIntoView(pTree, hItem);
  646. }
  647. //
  648. // Walk up the tree towards the root until we find the item at level iLevel.
  649. // Note the cast to (char) because iLevel is a BYTE, so the root's level is
  650. // 0xFF. Casting to (char) turns 0xFF it into -1.
  651. //
  652. HTREEITEM TV_WalkToLevel(HTREEITEM hWalk, int iLevel)
  653. {
  654. int i;
  655. for (i = (char)hWalk->iLevel - iLevel; i > 0; i--)
  656. hWalk = hWalk->hParent;
  657. return hWalk;
  658. }
  659. // this is to handle single expand mode.
  660. // The new selection is toggled, and the old selection is collapsed
  661. // assume that parents of hNewSel are already fully expanded
  662. // to do this, we build a parent dpa for the old and new
  663. // then go through find the first parent node of the old selection that's not in
  664. // the new sel tree. and expand that.
  665. void TV_ExpandOnSelChange(PTREE pTree, TREEITEM *hNewSel, TREEITEM *hOldSel)
  666. {
  667. LRESULT dwAbort;
  668. NM_TREEVIEW nm;
  669. BOOL fCollapsing;
  670. TVWATCHEDITEM wiOld, wiNew;
  671. // Revalidate hNewSel and hOldSel since they may have been deleted
  672. // during all the notifications that occurred in the meantime.
  673. if (!ValidateTreeItem(hOldSel, VTI_NULLOK) ||
  674. !ValidateTreeItem(hNewSel, VTI_NULLOK))
  675. return;
  676. if (TV_StartWatch(pTree, &wiOld, hOldSel))
  677. {
  678. if (TV_StartWatch(pTree, &wiNew, hNewSel))
  679. {
  680. // Let the app clean up after itself
  681. nm.itemOld.hItem = hOldSel;
  682. if (hOldSel)
  683. nm.itemOld.lParam = hOldSel->lParam;
  684. nm.itemOld.mask = (TVIF_HANDLE | TVIF_PARAM);
  685. nm.itemNew.hItem = hNewSel;
  686. if (hNewSel)
  687. nm.itemNew.lParam = hNewSel->lParam;
  688. nm.itemNew.mask = (TVIF_HANDLE | TVIF_PARAM);
  689. dwAbort = CCSendNotify(&pTree->ci, TVN_SINGLEEXPAND, &nm.hdr);
  690. UpdateWindow(pTree->ci.hwnd);
  691. // Revalidate hNewSel and hOldSel since they may have been deleted
  692. // by that notification.
  693. if (!TV_IsWatchValid(pTree, &wiOld) ||
  694. !TV_IsWatchValid(pTree, &wiNew))
  695. goto cleanup;
  696. // Collapse if the NewSel currently expanded.
  697. fCollapsing = hNewSel && (hNewSel->state & TVIS_EXPANDED);
  698. // Note that Ctrl+select allows the user to suppress the collapse
  699. // of the old selection.
  700. if ((!(dwAbort & TVNRET_SKIPOLD)) && hOldSel && (GetKeyState(VK_CONTROL) >= 0)) {
  701. //
  702. // Collapse parents until we reach the common ancestor between
  703. // hOldSel and hNewSel. Note carefully that we don't cache
  704. // any HTREEITEMs to avoid revalidation problems.
  705. //
  706. //
  707. // Find the common ancestor, which might be the tree root.
  708. //
  709. int iLevelCommon;
  710. if (!hNewSel)
  711. iLevelCommon = -1; // common ancestor is root
  712. else
  713. {
  714. HTREEITEM hItemO, hItemN;
  715. iLevelCommon = min((char)hOldSel->iLevel, (char)hNewSel->iLevel);
  716. hItemO = TV_WalkToLevel(hOldSel, iLevelCommon);
  717. hItemN = TV_WalkToLevel(hNewSel, iLevelCommon);
  718. while (iLevelCommon >= 0 && hItemO != hItemN) {
  719. iLevelCommon--;
  720. hItemO = hItemO->hParent;
  721. hItemN = hItemN->hParent;
  722. }
  723. }
  724. //
  725. // Now walk up the tree from hOldSel, collapsing everything
  726. // until we reach the common ancestor. Do not collapse the
  727. // common ancestor.
  728. //
  729. while ((char)hOldSel->iLevel > iLevelCommon)
  730. {
  731. TV_Expand(pTree, TVE_COLLAPSE, hOldSel, TRUE);
  732. if (!TV_IsWatchValid(pTree, &wiOld))
  733. break;
  734. hOldSel = hOldSel->hParent;
  735. TV_RestartWatch(pTree, &wiOld, hOldSel);
  736. }
  737. }
  738. if ((!(dwAbort & TVNRET_SKIPNEW)) && hNewSel && TV_IsWatchValid(pTree, &wiNew)) {
  739. TV_Expand(pTree, TVE_TOGGLE, hNewSel, TRUE);
  740. UpdateWindow(pTree->ci.hwnd);
  741. }
  742. cleanup:
  743. TV_EndWatch(pTree, &wiNew);
  744. }
  745. TV_EndWatch(pTree, &wiOld);
  746. }
  747. }
  748. // ----------------------------------------------------------------------------
  749. //
  750. // Notify the parent that the selection is about to change. If the change is
  751. // accepted, de-select the current selected item and select the given item
  752. //
  753. // sets hCaret
  754. //
  755. // in:
  756. // hItem item to become selected
  757. // wType TVGN_ values (TVGN_CARET, TVGN_DROPHILIGHT are only valid values)
  758. // flags combination of flags
  759. // TVSIF_NOTIFY - send notify to parent window
  760. // TVSIF_UPDATENOW - do UpdateWindow() to force sync painting
  761. // TVSIF_NOSINGLEEXPAND- don't do single-expand stuff
  762. // action action code to send identifying how selection is being made
  763. //
  764. // NOTE: Multiple Selection still needs to be added -- this multiplesel code
  765. // is garbage
  766. //
  767. // ----------------------------------------------------------------------------
  768. BOOL NEAR TV_SelectItem(PTREE pTree, WPARAM wType, TREEITEM FAR * hItem, UINT flags, UINT action)
  769. {
  770. UINT uRDWFlags = RDW_INVALIDATE;
  771. if (pTree->hImageList && (ImageList_GetBkColor(pTree->hImageList) == (COLORREF)-1))
  772. uRDWFlags |= RDW_ERASE;
  773. if (!ValidateTreeItem(hItem, VTI_NULLOK))
  774. return FALSE; // Invalid parameter
  775. switch (wType) {
  776. case TVGN_FIRSTVISIBLE:
  777. if (!hItem)
  778. return FALSE;
  779. TV_EnsureVisible(pTree, hItem);
  780. if (pTree->fVert) TV_SetTopItem(pTree, hItem->iShownIndex);
  781. break;
  782. case TVGN_DROPHILITE:
  783. ASSERT(hItem == NULL || ITEM_VISIBLE(hItem));
  784. if (hItem != pTree->hDropTarget) {
  785. if (pTree->hDropTarget) {
  786. pTree->hDropTarget->state &= ~TVIS_DROPHILITED;
  787. TV_InvalidateItem(pTree, pTree->hDropTarget, uRDWFlags);
  788. }
  789. if (hItem) {
  790. hItem->state |= TVIS_DROPHILITED;
  791. TV_InvalidateItem(pTree, hItem, uRDWFlags);
  792. }
  793. pTree->hDropTarget = hItem;
  794. if (pTree->hCaret) {
  795. TV_InvalidateItem(pTree, pTree->hCaret, uRDWFlags);
  796. }
  797. if (flags & TVSIF_UPDATENOW)
  798. UpdateWindow(pTree->ci.hwnd);
  799. }
  800. break;
  801. case TVGN_CARET:
  802. // REVIEW: we may want to scroll into view in this case
  803. // it's already the selected item, just return
  804. if (pTree->hCaret != hItem) {
  805. TREEITEM FAR * hOldSel;
  806. if ((flags & TVSIF_NOTIFY) && TV_SendSelChange(pTree, TVN_SELCHANGING, pTree->hCaret, hItem, action))
  807. return FALSE;
  808. if (pTree->hCaret) {
  809. pTree->hCaret->state &= ~TVIS_SELECTED;
  810. TV_InvalidateItem(pTree, pTree->hCaret, uRDWFlags);
  811. }
  812. hOldSel = pTree->hCaret;
  813. pTree->hCaret = hItem;
  814. if (hItem) {
  815. hItem->state |= TVIS_SELECTED;
  816. // make sure this item is not in a collapsed branch
  817. TV_ExpandParents(pTree, hItem, (flags & TVSIF_NOTIFY));
  818. TV_InvalidateItem(pTree, hItem, uRDWFlags );
  819. if (action == TVC_BYMOUSE) {
  820. // if selected by mouse, let's wait a doubleclick sec before scrolling
  821. SetTimer(pTree->ci.hwnd, IDT_SCROLLWAIT, GetDoubleClickTime(), NULL);
  822. pTree->fScrollWait = TRUE;
  823. } else if (pTree->fRedraw)
  824. TV_ScrollVertIntoView(pTree, hItem);
  825. }
  826. if (pTree->hwndToolTips)
  827. TV_Timer(pTree, IDT_TOOLTIPWAIT);
  828. if (flags & TVSIF_NOTIFY)
  829. TV_SendSelChange(pTree, TVN_SELCHANGED, hOldSel, hItem, action);
  830. if ((pTree->ci.style & TVS_SINGLEEXPAND) &&
  831. !(flags & TVSIF_NOSINGLEEXPAND) &&
  832. action != TVC_BYKEYBOARD)
  833. {
  834. TV_ExpandOnSelChange(pTree, pTree->hCaret, hOldSel);
  835. }
  836. if (flags & TVSIF_UPDATENOW)
  837. UpdateWindow(pTree->ci.hwnd);
  838. MyNotifyWinEvent(EVENT_OBJECT_FOCUS, pTree->ci.hwnd, OBJID_CLIENT,
  839. (LONG_PTR)hItem);
  840. MyNotifyWinEvent(EVENT_OBJECT_SELECTION, pTree->ci.hwnd, OBJID_CLIENT,
  841. (LONG_PTR)hItem);
  842. }
  843. break;
  844. default:
  845. DebugMsg(DM_TRACE, TEXT("Invalid type passed to TV_SelectItem"));
  846. return FALSE;
  847. }
  848. return TRUE; // success
  849. }
  850. // remove all the children, but pretend they are still there
  851. BOOL NEAR TV_ResetItem(PTREE pTree, HTREEITEM hItem)
  852. {
  853. TV_DeleteItem(pTree, hItem, TVDI_CHILDRENONLY);
  854. hItem->state &= ~TVIS_EXPANDEDONCE;
  855. hItem->fKids = KIDS_FORCE_YES; // force children
  856. return TRUE;
  857. }
  858. // ----------------------------------------------------------------------------
  859. //
  860. // Expand or collapse an item's children
  861. // Returns TRUE if any change took place and FALSE if unchanged
  862. //
  863. // ----------------------------------------------------------------------------
  864. BOOL NEAR TV_Expand(PTREE pTree, WPARAM wCode, TREEITEM FAR * hItem, BOOL fNotify)
  865. {
  866. WORD fOldState;
  867. UINT cntVisDescendants;
  868. TVITEMEX sItem;
  869. TREEITEM FAR * hItemExpanding;
  870. // deal with the evil invisible root for multiple root trees.
  871. hItemExpanding = hItem;
  872. if ((hItem == NULL) || (hItem == TVI_ROOT))
  873. hItem = pTree->hRoot;
  874. DBG_ValidateTreeItem(hItem, 0);
  875. TV_GetItem(pTree, hItem, TVIF_CHILDREN, &sItem);
  876. if (!(wCode & TVE_ACTIONMASK) || sItem.cChildren == 0)
  877. return FALSE; // no children to expand or collapse
  878. if ((wCode & TVE_ACTIONMASK) == TVE_TOGGLE) {
  879. wCode = (wCode & ~TVE_ACTIONMASK);
  880. // if it's not expaned, or not fully expanded, expand now
  881. wCode |=
  882. (((!(hItem->state & TVIS_EXPANDED)) ||
  883. hItem->state & TVIS_EXPANDPARTIAL) ?
  884. TVE_EXPAND : TVE_COLLAPSE);
  885. }
  886. if (((wCode & TVE_ACTIONMASK) == TVE_EXPAND) && !(hItem->state & TVIS_EXPANDEDONCE))
  887. {
  888. // if its the first expand, ALWAYS notify the parent
  889. fNotify = TRUE;
  890. }
  891. // at this point the children may be added if they aren't already there (callback)
  892. if (fNotify && TV_SendItemExpand(pTree, TVN_ITEMEXPANDING, hItemExpanding, wCode))
  893. return FALSE;
  894. // if (!hItem->hKids && (hItem->fKids == KIDS_FORCE_NO)) // this may be right, but I don't
  895. // have proof now.
  896. if (!hItem->hKids)
  897. {
  898. // kids we removed, or never there
  899. TV_InvalidateItem(pTree, hItem, RDW_INVALIDATE);
  900. return FALSE;
  901. }
  902. fOldState = hItem->state;
  903. if (hItem->hParent) // never turn off TVIS_EXPANED for the invisible root
  904. {
  905. if ((wCode & TVE_ACTIONMASK) == TVE_EXPAND)
  906. hItem->state |= TVIS_EXPANDED;
  907. else
  908. hItem->state &= ~(TVIS_EXPANDED | TVIS_EXPANDPARTIAL);
  909. if (wCode & TVE_EXPANDPARTIAL) {
  910. hItem->state |= TVIS_EXPANDPARTIAL;
  911. } else {
  912. hItem->state &= ~(TVIS_EXPANDPARTIAL);
  913. }
  914. }
  915. // if we're not changing the expanded state
  916. // check to see if we're supposed to collapse reset
  917. if (!(fOldState & TVIS_EXPANDED) &&
  918. !(hItem->state & TVIS_EXPANDED))
  919. {
  920. if ((wCode & (TVE_ACTIONMASK | TVE_COLLAPSERESET)) == (TVE_COLLAPSE | TVE_COLLAPSERESET))
  921. {
  922. TV_ResetItem(pTree, hItem);
  923. }
  924. return FALSE;
  925. }
  926. // if we changed expaneded states, recalc the scrolling
  927. if ((fOldState ^ hItem->state) & TVIS_EXPANDED) {
  928. cntVisDescendants = TV_ScrollBelow(pTree, hItem, TRUE, hItem->state & TVIS_EXPANDED);
  929. if (hItem->state & TVIS_EXPANDED)
  930. {
  931. UINT wNewTop, wTopOffset, wLastKid;
  932. TV_ScrollBarsAfterExpand(pTree, hItem);
  933. wNewTop = pTree->hTop->iShownIndex;
  934. wTopOffset = hItem->iShownIndex - wNewTop;
  935. wLastKid = wTopOffset + cntVisDescendants + 1;
  936. if (wLastKid > pTree->cFullVisible)
  937. {
  938. wNewTop += min(wLastKid - pTree->cFullVisible, wTopOffset);
  939. TV_SetTopItem(pTree, wNewTop);
  940. }
  941. }
  942. else
  943. {
  944. TV_ScrollBarsAfterCollapse(pTree, hItem);
  945. TV_ScrollVertIntoView(pTree, hItem);
  946. // If we collapsed the subtree that contains the caret, then
  947. // pop the caret back to the last visible ancestor
  948. // Pass TVIS_NOSINGLEEXPAND so we won't expand an item right
  949. // after we collapsed it (d'oh!)
  950. if (pTree->hCaret)
  951. {
  952. TREEITEM FAR * hWalk = TV_WalkToLevel(pTree->hCaret, hItem->iLevel);
  953. if (hWalk == hItem)
  954. TV_SelectItem(pTree, TVGN_CARET, hItem, (fNotify ? TVSIF_NOTIFY : 0) | TVSIF_UPDATENOW | TVSIF_NOSINGLEEXPAND, TVC_UNKNOWN);
  955. }
  956. }
  957. } else if ((fOldState ^ hItem->state) & TVIS_EXPANDPARTIAL) {
  958. // we didn't change the expanded state, only the expand partial
  959. TV_InvalidateItem(pTree, hItem, RDW_INVALIDATE);
  960. }
  961. if (fNotify && TV_SendItemExpand(pTree, TVN_ITEMEXPANDED, hItem, wCode))
  962. return FALSE;
  963. hItem->state |= TVIS_EXPANDEDONCE;
  964. if ((wCode & (TVE_ACTIONMASK | TVE_COLLAPSERESET)) == (TVE_COLLAPSE | TVE_COLLAPSERESET))
  965. {
  966. TV_ResetItem(pTree, hItem);
  967. }
  968. // BUGBUG raymondc v6 we generate a notification even if nothing happened,
  969. // which cunfuses accessibility. E.g., app tried to expand something
  970. // that was already expanded. Explorer Band does this when you navigate.
  971. MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, pTree->ci.hwnd, OBJID_CLIENT,
  972. (LONG_PTR)hItem);
  973. return TRUE;
  974. }
  975. BOOL PASCAL BetweenItems(PTREE pTree, HTREEITEM hItem, HTREEITEM hItemStart, HTREEITEM hItemEnd)
  976. {
  977. if (hItemStart) {
  978. while ((hItemStart = TV_GetNextVisItem(hItemStart)) && (hItemEnd != hItemStart))
  979. {
  980. if (hItem == hItemStart)
  981. return TRUE;
  982. }
  983. }
  984. return FALSE;
  985. }
  986. #ifdef FE_IME
  987. // Now only Korean version is interested in incremental search with composition string.
  988. #define FREE_COMP_STRING(pszCompStr) LocalFree((HLOCAL)(pszCompStr))
  989. BOOL NEAR TV_OnImeComposition(PTREE pTree, WPARAM wParam, LPARAM lParam)
  990. {
  991. LPTSTR lpsz;
  992. int iCycle = 0;
  993. HTREEITEM hItem;
  994. TCHAR szTemp[MAXLABELTEXT];
  995. TVITEMEX ti;
  996. LPTSTR lpszAlt = NULL; // use only if SameChar
  997. int iLen;
  998. HIMC hImc;
  999. TCHAR *pszCompStr;
  1000. BOOL fRet = TRUE;
  1001. if (hImc = ImmGetContext(pTree->ci.hwnd))
  1002. {
  1003. if (lParam & GCS_RESULTSTR)
  1004. {
  1005. fRet = FALSE;
  1006. pszCompStr = GET_COMP_STRING(hImc, GCS_RESULTSTR);
  1007. if (pszCompStr)
  1008. {
  1009. IncrementSearchImeCompStr(&pTree->is, FALSE, pszCompStr, &lpsz);
  1010. FREE_COMP_STRING(pszCompStr);
  1011. }
  1012. }
  1013. if (lParam & GCS_COMPSTR)
  1014. {
  1015. fRet = TRUE;
  1016. pszCompStr = GET_COMP_STRING(hImc, GCS_COMPSTR);
  1017. if (pszCompStr)
  1018. {
  1019. if (IncrementSearchImeCompStr(&pTree->is, TRUE, pszCompStr, &lpsz)) {
  1020. if (pTree->hCaret) {
  1021. pTree->htiSearch = pTree->hCaret;
  1022. } else if (pTree->hRoot && pTree->hRoot->hKids) {
  1023. pTree->htiSearch = pTree->hRoot->hKids;
  1024. } else
  1025. return fRet;
  1026. }
  1027. if (!lpsz || !*lpsz || !pTree->hRoot || !pTree->hRoot->hKids)
  1028. return fRet;
  1029. hItem = pTree->htiSearch;
  1030. ti.cchTextMax = sizeof(szTemp);
  1031. iLen = lstrlen(lpsz);
  1032. if (iLen > 1 && SameChars(lpsz, lpsz[0]))
  1033. lpszAlt = lpsz + iLen - 1;
  1034. do {
  1035. ti.pszText = szTemp;
  1036. hItem = TV_GetNextVisItem(hItem);
  1037. if (!hItem) {
  1038. iCycle++;
  1039. hItem = pTree->hRoot->hKids;
  1040. }
  1041. TV_GetItem(pTree, hItem, TVIF_TEXT, &ti);
  1042. if ((ti.pszText != LPSTR_TEXTCALLBACK) &&
  1043. HIWORD64(ti.pszText)) {
  1044. // DebugMsg(DM_TRACE, "treesearch %d %s %s", (LPSTR)lpsz, (LPSTR)lpsz, (LPSTR)ti.pszText);
  1045. if (IntlStrEqNI(lpsz, ti.pszText, iLen) ||
  1046. (lpszAlt && IntlStrEqNI(lpszAlt, ti.pszText, 1) &&
  1047. BetweenItems(pTree, hItem, pTree->hCaret, pTree->htiSearch)))
  1048. {
  1049. DebugMsg(DM_TRACE, TEXT("Selecting"));
  1050. TV_SelectItem(pTree, TVGN_CARET, hItem, TVSIF_NOTIFY | TVSIF_UPDATENOW, TVC_BYKEYBOARD);
  1051. //notify of navigation key usage
  1052. CCNotifyNavigationKeyUsage(&(pTree->ci), UISF_HIDEFOCUS);
  1053. return fRet;
  1054. }
  1055. }
  1056. } while(iCycle < 2);
  1057. // if they hit the same key twice in a row at the beginning of
  1058. // the search, and there was no item found, they likely meant to
  1059. // retstart the search
  1060. if (lpszAlt) {
  1061. // first clear out the string so that we won't recurse again
  1062. IncrementSearchString(&pTree->is, 0, NULL);
  1063. TV_OnImeComposition(pTree, wParam, lParam);
  1064. } else {
  1065. IncrementSearchBeep(&pTree->is);
  1066. }
  1067. //notify of navigation key usage
  1068. CCNotifyNavigationKeyUsage(&(pTree->ci), UISF_HIDEFOCUS);
  1069. FREE_COMP_STRING(pszCompStr);
  1070. }
  1071. }
  1072. ImmReleaseContext(pTree->ci.hwnd, hImc);
  1073. }
  1074. return fRet;
  1075. }
  1076. #endif
  1077. void NEAR TV_OnChar(PTREE pTree, UINT ch, int cRepeat)
  1078. {
  1079. LPTSTR lpsz;
  1080. int iCycle = 0;
  1081. HTREEITEM hItem;
  1082. TCHAR szTemp[MAXLABELTEXT];
  1083. TVITEMEX ti;
  1084. LPTSTR lpszAlt = NULL; // use only if SameChar
  1085. int iLen;
  1086. if (IncrementSearchString(&pTree->is, ch, &lpsz) || !pTree->htiSearch) {
  1087. if (pTree->hCaret) {
  1088. pTree->htiSearch = pTree->hCaret;
  1089. } else if (pTree->hRoot && pTree->hRoot->hKids) {
  1090. pTree->htiSearch = pTree->hRoot->hKids;
  1091. } else
  1092. return;
  1093. }
  1094. if (!lpsz || !*lpsz || !pTree->hRoot || !pTree->hRoot->hKids)
  1095. return;
  1096. hItem = pTree->htiSearch;
  1097. ti.cchTextMax = ARRAYSIZE(szTemp);
  1098. iLen = lstrlen(lpsz);
  1099. if (iLen > 1 && SameChars(lpsz, lpsz[0]))
  1100. lpszAlt = lpsz + iLen - 1;
  1101. do {
  1102. ti.pszText = szTemp;
  1103. hItem = TV_GetNextVisItem(hItem);
  1104. if (!hItem) {
  1105. iCycle++;
  1106. hItem = pTree->hRoot->hKids;
  1107. }
  1108. TV_GetItem(pTree, hItem, TVIF_TEXT, &ti);
  1109. if ((ti.pszText != LPSTR_TEXTCALLBACK) &&
  1110. HIWORD64(ti.pszText)) {
  1111. // DebugMsg(DM_TRACE, TEXT("treesearch %d %s %s"), (LPTSTR)lpsz, (LPTSTR)lpsz, (LPTSTR)ti.pszText);
  1112. if (IntlStrEqNI(lpsz, ti.pszText, iLen) ||
  1113. (lpszAlt && IntlStrEqNI(lpszAlt, ti.pszText, 1) &&
  1114. BetweenItems(pTree, hItem, pTree->hCaret, pTree->htiSearch)))
  1115. {
  1116. DebugMsg(DM_TRACE, TEXT("Selecting"));
  1117. TV_SelectItem(pTree, TVGN_CARET, hItem, TVSIF_NOTIFY | TVSIF_UPDATENOW, TVC_BYKEYBOARD);
  1118. //notify of navigation key usage
  1119. CCNotifyNavigationKeyUsage(&(pTree->ci), UISF_HIDEFOCUS);
  1120. return;
  1121. }
  1122. }
  1123. } while(iCycle < 2);
  1124. // if they hit the same key twice in a row at the beginning of
  1125. // the search, and there was no item found, they likely meant to
  1126. // retstart the search
  1127. if (lpszAlt) {
  1128. // first clear out the string so that we won't recurse again
  1129. IncrementSearchString(&pTree->is, 0, NULL);
  1130. TV_OnChar(pTree, ch, cRepeat);
  1131. } else {
  1132. IncrementSearchBeep(&pTree->is);
  1133. }
  1134. //notify of navigation key usage
  1135. CCNotifyNavigationKeyUsage(&(pTree->ci), UISF_HIDEFOCUS);
  1136. }
  1137. // ----------------------------------------------------------------------------
  1138. //
  1139. // Handle WM_KEYDOWN messages
  1140. // If control key is down, treat keys as scroll codes; otherwise, treat keys
  1141. // as caret position changes.
  1142. //
  1143. // ----------------------------------------------------------------------------
  1144. BOOL NEAR TV_KeyDown(PTREE pTree, WPARAM wKey, LPARAM dwKeyData)
  1145. {
  1146. TREEITEM FAR * hItem;
  1147. UINT wShownIndex;
  1148. TV_KEYDOWN nm;
  1149. BOOL fPuntChar;
  1150. BOOL ret = TRUE;
  1151. // Notify
  1152. nm.wVKey = (WORD)wKey;
  1153. fPuntChar = (BOOL)CCSendNotify(&pTree->ci, TVN_KEYDOWN, &nm.hdr);
  1154. wKey = RTLSwapLeftRightArrows(&pTree->ci, wKey);
  1155. if (GetKeyState(VK_CONTROL) < 0)
  1156. {
  1157. // control key is down
  1158. UINT wScrollCode;
  1159. switch (wKey)
  1160. {
  1161. case VK_LEFT:
  1162. TV_HorzScroll(pTree, SB_LINEUP, 0);
  1163. break;
  1164. case VK_RIGHT:
  1165. TV_HorzScroll(pTree, SB_LINEDOWN, 0);
  1166. break;
  1167. case VK_PRIOR:
  1168. wScrollCode = SB_PAGEUP;
  1169. goto kdVertScroll;
  1170. case VK_HOME:
  1171. wScrollCode = SB_TOP;
  1172. goto kdVertScroll;
  1173. case VK_NEXT:
  1174. wScrollCode = SB_PAGEDOWN;
  1175. goto kdVertScroll;
  1176. case VK_END:
  1177. wScrollCode = SB_BOTTOM;
  1178. goto kdVertScroll;
  1179. case VK_UP:
  1180. wScrollCode = SB_LINEUP;
  1181. goto kdVertScroll;
  1182. case VK_DOWN:
  1183. wScrollCode = SB_LINEDOWN;
  1184. kdVertScroll:
  1185. TV_VertScroll(pTree, wScrollCode, 0);
  1186. break;
  1187. default:
  1188. ret = FALSE;
  1189. }
  1190. } else {
  1191. switch (wKey)
  1192. {
  1193. case VK_RETURN:
  1194. fPuntChar = (BOOL)CCSendNotify(&pTree->ci, NM_RETURN, NULL);
  1195. break;
  1196. case VK_PRIOR:
  1197. if (pTree->hCaret && (pTree->hCaret->iShownIndex > (pTree->cFullVisible - 1)))
  1198. {
  1199. wShownIndex = pTree->hCaret->iShownIndex - (pTree->cFullVisible - 1);
  1200. goto selectIndex;
  1201. }
  1202. // fall thru
  1203. case VK_HOME:
  1204. wShownIndex = 0;
  1205. goto selectIndex;
  1206. case VK_NEXT:
  1207. if (!pTree->hCaret)
  1208. {
  1209. wShownIndex = 0;
  1210. goto selectIndex;
  1211. }
  1212. wShownIndex = pTree->hCaret->iShownIndex + (pTree->cFullVisible - 1);
  1213. if (wShownIndex < pTree->cShowing)
  1214. goto selectIndex;
  1215. // fall thru
  1216. case VK_END:
  1217. wShownIndex = pTree->cShowing - 1;
  1218. selectIndex:
  1219. hItem = TV_GetShownIndexItem(pTree->hRoot->hKids, wShownIndex);
  1220. goto kdSetCaret;
  1221. break;
  1222. case VK_SUBTRACT:
  1223. if (pTree->hCaret) {
  1224. fPuntChar = TRUE;
  1225. TV_Expand(pTree, TVE_COLLAPSE, pTree->hCaret, TRUE);
  1226. }
  1227. break;
  1228. case VK_ADD:
  1229. if (pTree->hCaret) {
  1230. fPuntChar = TRUE;
  1231. TV_Expand(pTree, TVE_EXPAND, pTree->hCaret, TRUE);
  1232. }
  1233. break;
  1234. case VK_MULTIPLY:
  1235. if (pTree->hCaret) {
  1236. fPuntChar = TRUE;
  1237. TV_ExpandRecurse(pTree, pTree->hCaret, TRUE);
  1238. }
  1239. break;
  1240. case VK_LEFT:
  1241. if (pTree->hCaret && (pTree->hCaret->state & TVIS_EXPANDED)) {
  1242. TV_Expand(pTree, TVE_COLLAPSE, pTree->hCaret, TRUE);
  1243. break;
  1244. } else if (pTree->hCaret) {
  1245. hItem = VISIBLE_PARENT(pTree->hCaret);
  1246. goto kdSetCaret;
  1247. }
  1248. break;
  1249. case VK_BACK:
  1250. // get the parent, avoiding the root item
  1251. fPuntChar = TRUE;
  1252. if (pTree->hCaret) {
  1253. hItem = VISIBLE_PARENT(pTree->hCaret);
  1254. goto kdSetCaret;
  1255. }
  1256. break;
  1257. case VK_UP:
  1258. if (pTree->hCaret)
  1259. hItem = TV_GetPrevVisItem(pTree->hCaret);
  1260. else
  1261. hItem = pTree->hRoot->hKids;
  1262. goto kdSetCaret;
  1263. break;
  1264. case VK_RIGHT:
  1265. if (pTree->hCaret && !(pTree->hCaret->state & TVIS_EXPANDED)) {
  1266. TV_Expand(pTree, TVE_EXPAND, pTree->hCaret, TRUE);
  1267. break;
  1268. } // else fall through
  1269. case VK_DOWN:
  1270. if (pTree->hCaret)
  1271. hItem = TV_GetNextVisItem(pTree->hCaret);
  1272. else
  1273. hItem = pTree->hRoot->hKids;
  1274. kdSetCaret:
  1275. if (hItem)
  1276. TV_SelectItem(pTree, TVGN_CARET, hItem, TVSIF_NOTIFY | TVSIF_UPDATENOW, TVC_BYKEYBOARD);
  1277. break;
  1278. case VK_SPACE:
  1279. if ((pTree->ci.style & TVS_CHECKBOXES) && pTree->hCaret)
  1280. {
  1281. TV_HandleStateIconClick(pTree, pTree->hCaret);
  1282. fPuntChar = TRUE; // don't beep
  1283. }
  1284. break;
  1285. default:
  1286. ret = FALSE;
  1287. }
  1288. }
  1289. if (fPuntChar) {
  1290. pTree->iPuntChar++;
  1291. } else if (pTree->iPuntChar){
  1292. // this is tricky... if we want to punt the char, just increment the
  1293. // count. if we do NOT, then we must clear the queue of WM_CHAR's
  1294. // this is to preserve the iPuntChar to mean "punt the next n WM_CHAR messages
  1295. MSG msg;
  1296. while((pTree->iPuntChar > 0) && PeekMessage(&msg, pTree->ci.hwnd, WM_CHAR, WM_CHAR, PM_REMOVE)) {
  1297. pTree->iPuntChar--;
  1298. }
  1299. ASSERT(!pTree->iPuntChar);
  1300. }
  1301. if(VK_MENU!=wKey)
  1302. {
  1303. // notify of navigation key usage
  1304. CCNotifyNavigationKeyUsage(&(pTree->ci), UISF_HIDEFOCUS);
  1305. }
  1306. return ret;
  1307. }
  1308. // ----------------------------------------------------------------------------
  1309. //
  1310. // Sets the tree's indent width per hierarchy level and recompute widths.
  1311. //
  1312. // sets cxIndent
  1313. //
  1314. // ----------------------------------------------------------------------------
  1315. void NEAR TV_SetIndent(PTREE pTree, WPARAM cxIndent)
  1316. {
  1317. if (pTree->hImageList) {
  1318. if ((SHORT)cxIndent < pTree->cxImage)
  1319. cxIndent = pTree->cxImage;
  1320. }
  1321. if ((SHORT)cxIndent < pTree->cyText)
  1322. cxIndent = pTree->cyText;
  1323. if (cxIndent < MAGIC_MININDENT)
  1324. cxIndent = MAGIC_MININDENT;
  1325. pTree->cxIndent = (SHORT)cxIndent;
  1326. TV_CreateIndentBmps(pTree);
  1327. TV_ScrollBarsAfterSetWidth(pTree, NULL);
  1328. }
  1329. // ----------------------------------------------------------------------------
  1330. //
  1331. // Sets the tree's item height to be the maximum of the image height and text
  1332. // height. Then recompute the tree's full visible count.
  1333. //
  1334. // sets cyItem, cFullVisible
  1335. //
  1336. // ----------------------------------------------------------------------------
  1337. void NEAR TV_SetItemHeight(PTREE pTree)
  1338. {
  1339. // height MUST be even with TVS_HASLINES -- go ahead and make it always even
  1340. if (!pTree->fCyItemSet)
  1341. pTree->cyItem = (max(pTree->cyImage, pTree->cyText) + 1);
  1342. // height not always even not, only on haslines style.
  1343. if (pTree->cyItem <= 1) {
  1344. pTree->cyItem = 1; // Don't let it go zero or negative!
  1345. } else if (!(pTree->ci.style & TVS_NONEVENHEIGHT))
  1346. pTree->cyItem &= ~1;
  1347. pTree->cFullVisible = pTree->cyWnd / pTree->cyItem;
  1348. TV_CreateIndentBmps(pTree);
  1349. TV_CalcScrollBars(pTree);
  1350. }
  1351. // BUGBUG: does not deal with hfont == NULL
  1352. void NEAR TV_OnSetFont(PTREE pTree, HFONT hNewFont, BOOL fRedraw)
  1353. {
  1354. HDC hdc;
  1355. HFONT hfontSel;
  1356. TCHAR c = TEXT('J'); // for bog
  1357. SIZE size;
  1358. if (pTree->fCreatedFont && pTree->hFont) {
  1359. DeleteObject(pTree->hFont);
  1360. pTree->fCreatedFont = FALSE;
  1361. }
  1362. if (hNewFont == NULL) {
  1363. LOGFONT lf;
  1364. SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, FALSE);
  1365. // B#210235 - because NT4 initializes icontitle logfont with Ansi charset
  1366. // no matter what font is selected, following A/W conversion would fail
  1367. // on non US environment if we use this logfont to get codepage.
  1368. // The ACP is guaranteed to work with any Ansi apps because these apps
  1369. // assume ACP to be matching to their desired codepage.
  1370. if (staticIsOS(OS_NT4ORGREATER) && !staticIsOS(OS_WIN2000ORGREATER))
  1371. {
  1372. CHARSETINFO csi;
  1373. TranslateCharsetInfo(IntToPtr_(DWORD *, g_uiACP), &csi, TCI_SRCCODEPAGE);
  1374. lf.lfCharSet = (BYTE)csi.ciCharset;
  1375. }
  1376. hNewFont = CreateFontIndirect(&lf);
  1377. pTree->fCreatedFont = TRUE; // make sure we delete it
  1378. }
  1379. hdc = GetDC(pTree->ci.hwnd);
  1380. hfontSel = hNewFont ? SelectObject(hdc, hNewFont) : NULL;
  1381. // Office9 Setup had a bug where they installed a bogus font,
  1382. // which created okay but all APIs against it (e.g., GetTextExtentPoint)
  1383. // failed! Protect against failure by pre-setting the value to something
  1384. // non-garbage.
  1385. size.cy = 0;
  1386. GetTextExtentPoint(hdc, &c, 1, &size);
  1387. pTree->cyText = (SHORT)(size.cy + (g_cyBorder * 2));
  1388. if (hfontSel)
  1389. SelectObject(hdc, hfontSel);
  1390. ReleaseDC(pTree->ci.hwnd, hdc);
  1391. pTree->hFont = hNewFont;
  1392. if (pTree->hFontBold) {
  1393. TV_CreateBoldFont(pTree);
  1394. }
  1395. pTree->ci.uiCodePage = GetCodePageForFont(hNewFont);
  1396. TV_DeleteHotFonts(pTree);
  1397. if (pTree->cxIndent == 0) // first time init?
  1398. {
  1399. if (!pTree->cyItem) pTree->cyItem = pTree->cyText;
  1400. TV_SetIndent(pTree, 16 /*g_cxSmIcon*/ + MAGIC_INDENT);
  1401. }
  1402. TV_ScrollBarsAfterSetWidth(pTree, NULL);
  1403. TV_SetItemHeight(pTree);
  1404. if (pTree->hwndToolTips)
  1405. SendMessage(pTree->hwndToolTips, WM_SETFONT, (WPARAM)pTree->hFont, (LPARAM)TRUE);
  1406. // REVIEW: does this happen as a result of the above?
  1407. // if (fRedraw)
  1408. // RedrawWindow(pTree->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  1409. }
  1410. VOID NEAR PASCAL TV_CreateBoldFont(PTREE pTree)
  1411. {
  1412. LOGFONT lf;
  1413. if (pTree->hFontBold)
  1414. DeleteObject (pTree->hFontBold);
  1415. GetObject(pTree->hFont, sizeof (lf), &lf);
  1416. lf.lfWeight = FW_BOLD;
  1417. pTree->hFontBold = CreateFontIndirect(&lf);
  1418. }
  1419. HIMAGELIST NEAR TV_SetImageList(PTREE pTree, HIMAGELIST hImage, int iImageIndex)
  1420. {
  1421. int cx, cy;
  1422. HIMAGELIST hImageOld = NULL;
  1423. switch (iImageIndex) {
  1424. case TVSIL_STATE:
  1425. hImageOld = pTree->himlState;
  1426. pTree->himlState = hImage;
  1427. if (hImage) {
  1428. ImageList_GetIconSize(hImage, &pTree->cxState , &pTree->cyState);
  1429. } else {
  1430. pTree->cxState = 0;
  1431. }
  1432. break;
  1433. case TVSIL_NORMAL:
  1434. hImageOld = pTree->hImageList;
  1435. if (hImage && ImageList_GetIconSize(hImage, &cx, &cy))
  1436. {
  1437. pTree->cxImage = (cx + MAGIC_INDENT);
  1438. pTree->cyImage = (SHORT)cy;
  1439. if (pTree->cxIndent < pTree->cxImage)
  1440. TV_SetIndent(pTree, pTree->cxImage);
  1441. pTree->hImageList = hImage;
  1442. if (!hImageOld && pTree->ci.style & TVS_CHECKBOXES) {
  1443. TV_InitCheckBoxes(pTree);
  1444. }
  1445. }
  1446. else
  1447. {
  1448. pTree->cxImage = pTree->cyImage = 0;
  1449. pTree->hImageList = NULL;
  1450. }
  1451. break;
  1452. default:
  1453. DebugMsg(DM_TRACE, TEXT("sh TR - TVM_SETIMAGELIST: unrecognized iImageList"));
  1454. break;
  1455. }
  1456. TV_ScrollBarsAfterSetWidth(pTree, NULL);
  1457. TV_SetItemHeight(pTree);
  1458. return hImageOld;
  1459. }
  1460. // ----------------------------------------------------------------------------
  1461. //
  1462. // Gets the item with the described relationship to the given item, NULL if
  1463. // no item can be found with that relationship.
  1464. //
  1465. // ----------------------------------------------------------------------------
  1466. TREEITEM FAR * NEAR TV_GetNextItem(PTREE pTree, TREEITEM FAR * hItem, WPARAM wGetCode)
  1467. {
  1468. switch (wGetCode) {
  1469. case TVGN_ROOT:
  1470. return pTree->hRoot->hKids;
  1471. case TVGN_DROPHILITE:
  1472. return pTree->hDropTarget;
  1473. case TVGN_CARET:
  1474. return pTree->hCaret;
  1475. case TVGN_FIRSTVISIBLE:
  1476. return pTree->hTop;
  1477. case TVGN_LASTVISIBLE:
  1478. return TV_GetShownIndexItem(pTree->hRoot->hKids, pTree->cShowing-1);
  1479. case TVGN_CHILD:
  1480. if (!hItem || (hItem == TVI_ROOT))
  1481. return pTree->hRoot->hKids;
  1482. break;
  1483. }
  1484. // all of these require a valid hItem
  1485. if (!ValidateTreeItem(hItem, 0))
  1486. return NULL;
  1487. switch (wGetCode) {
  1488. case TVGN_NEXTVISIBLE:
  1489. return TV_GetNextVisItem(hItem);
  1490. case TVGN_PREVIOUSVISIBLE:
  1491. return TV_GetPrevVisItem(hItem);
  1492. case TVGN_NEXT:
  1493. return hItem->hNext;
  1494. case TVGN_PREVIOUS:
  1495. if (hItem->hParent->hKids == hItem)
  1496. return NULL;
  1497. else {
  1498. TREEITEM FAR * hWalk;
  1499. for (hWalk = hItem->hParent->hKids; hWalk->hNext != hItem; hWalk = hWalk->hNext);
  1500. return hWalk;
  1501. }
  1502. case TVGN_PARENT:
  1503. return VISIBLE_PARENT(hItem);
  1504. case TVGN_CHILD:
  1505. return hItem->hKids;
  1506. }
  1507. return NULL;
  1508. }
  1509. // ----------------------------------------------------------------------------
  1510. //
  1511. // Returns the number of items (including the partially visible item at the
  1512. // bottom based on the given flag) that fit in the tree's client window.
  1513. //
  1514. // ----------------------------------------------------------------------------
  1515. LRESULT NEAR TV_GetVisCount(PTREE pTree, BOOL fIncludePartial)
  1516. {
  1517. int i;
  1518. if (!fIncludePartial)
  1519. return(MAKELRESULTFROMUINT(pTree->cFullVisible));
  1520. i = pTree->cFullVisible;
  1521. if (pTree->cyWnd - (i * pTree->cyItem))
  1522. i++;
  1523. return i;
  1524. }
  1525. void TV_InvalidateInsertMarkRect(PTREE pTree, BOOL fErase)
  1526. {
  1527. RECT rc;
  1528. if (TV_GetInsertMarkRect(pTree, &rc))
  1529. InvalidateRect(pTree->ci.hwnd, &rc, fErase);
  1530. }
  1531. // ----------------------------------------------------------------------------
  1532. //
  1533. // recomputes tree's fields that rely on the tree's client window size
  1534. //
  1535. // sets cxWnd, cyWnd, cFullVisible
  1536. //
  1537. // ----------------------------------------------------------------------------
  1538. BOOL NEAR TV_SizeWnd(PTREE pTree, UINT cxWnd, UINT cyWnd)
  1539. {
  1540. RECT rc;
  1541. UINT cxOld = pTree->cxWnd;
  1542. if (!cxWnd || !cyWnd)
  1543. {
  1544. GetClientRect(pTree->ci.hwnd, &rc);
  1545. cxWnd = rc.right;
  1546. cyWnd = rc.bottom;
  1547. }
  1548. pTree->cxWnd = (SHORT)cxWnd;
  1549. pTree->cyWnd = (SHORT)cyWnd;
  1550. pTree->cFullVisible = cyWnd / pTree->cyItem;
  1551. if (pTree->ci.style & TVS_NOSCROLL)
  1552. pTree->cxMax = (WORD) cxWnd;
  1553. TV_CalcScrollBars(pTree);
  1554. if (pTree->cxBorder)
  1555. {
  1556. rc.top = 0;
  1557. rc.bottom = cyWnd;
  1558. rc.right = cxOld;
  1559. rc.left = cxOld - pTree->cxBorder;
  1560. if (rc.left < (int)cxWnd) {
  1561. // invalidate so clipping happens on right on size.
  1562. InvalidateRect(pTree->ci.hwnd, &rc, TRUE);
  1563. }
  1564. }
  1565. TV_InvalidateInsertMarkRect(pTree, TRUE);
  1566. return TRUE;
  1567. }
  1568. void TV_HandleStateIconClick(PTREE pTree, HTREEITEM hItem)
  1569. {
  1570. TVITEMEX tvi;
  1571. int iState;
  1572. tvi.stateMask = TVIS_STATEIMAGEMASK;
  1573. TV_GetItem(pTree, hItem, TVIF_STATE, &tvi);
  1574. iState = STATEIMAGEMASKTOINDEX(tvi.state & tvi.stateMask);
  1575. iState %= (ImageList_GetImageCount(pTree->himlState) - 1);
  1576. iState++;
  1577. tvi.mask = TVIF_STATE;
  1578. tvi.state = INDEXTOSTATEIMAGEMASK(iState);
  1579. tvi.hItem = hItem;
  1580. TV_SetItem(pTree, &tvi);
  1581. }
  1582. //
  1583. // Eudora is a piece of work.
  1584. //
  1585. // When they get a NM_DBLCLK notification from a treeview, they say,
  1586. // "Oh, I know that treeview allocates its NMHDR from the stack, and
  1587. // there's this local variable on Treeview's stack I'm really interested
  1588. // in, so I'm going to hard-code an offset from the pnmhdr and read the
  1589. // DWORD at that location so I can get at the local variable. I will then
  1590. // stop working if this value is zero."
  1591. //
  1592. // The conversion to UNICODE changed our stack layout enough that they
  1593. // end up always getting zero -- it's the NULL parameter which is the
  1594. // final argument to CCSendNotify. Since all this stack layout stuff is
  1595. // sensitive to how the compiler's optimizer feels today, we create a
  1596. // special notify structure Just For Eudora which mimics the stack layout
  1597. // they expected to see in Win95.
  1598. //
  1599. typedef struct NMEUDORA {
  1600. NMHDR nmhdr;
  1601. BYTE Padding[48];
  1602. DWORD MustBeNonzero; // Eudora fails to install if this is zero
  1603. } NMEUDORA;
  1604. // ----------------------------------------------------------------------------
  1605. //
  1606. // WM_LBUTTONDBLCLK message -- toggle expand/collapse state of item's children
  1607. // WM_LBUTTONDOWN message -- on item's button, do same as WM_LBUTTONDBLCLK,
  1608. // otherwise select item and ensure that item is fully visible
  1609. //
  1610. // ----------------------------------------------------------------------------
  1611. void NEAR TV_ButtonDown(PTREE pTree, UINT wMsg, UINT wFlags, int x, int y, UINT TVBD_flags)
  1612. {
  1613. UINT wHitCode;
  1614. TREEITEM FAR * hItem;
  1615. HWND hwndTree;
  1616. LRESULT lResult;
  1617. #ifdef _X86_
  1618. NMEUDORA nmeu;
  1619. nmeu.MustBeNonzero = 1;
  1620. COMPILETIME_ASSERT(FIELD_OFFSET(NMEUDORA, MustBeNonzero) == 0x3C);
  1621. #endif
  1622. GetMessagePosClient(pTree->ci.hwnd, &pTree->ptCapture);
  1623. if (!TV_DismissEdit(pTree, FALSE)) // end any previous editing (accept it)
  1624. return; // Something happened such that we should not process button down
  1625. hItem = TV_CheckHit(pTree, x, y, &wHitCode);
  1626. // Excel likes to destroy the entire tree when it gets a double-click
  1627. // so we need to watch the item in case it vanishes behind our back.
  1628. hwndTree = pTree->ci.hwnd;
  1629. if (wMsg == WM_LBUTTONDBLCLK)
  1630. {
  1631. //
  1632. // Cancel any name editing that might happen.
  1633. //
  1634. TV_CancelEditTimer(pTree);
  1635. if (wHitCode & (TVHT_ONITEM | TVHT_ONITEMBUTTON)) {
  1636. goto ExpandItem;
  1637. }
  1638. //
  1639. // Collapses node above the line double clicked on
  1640. //
  1641. else if ((pTree->ci.style & TVS_HASLINES) && (wHitCode & TVHT_ONITEMINDENT) &&
  1642. (abs(x % pTree->cxIndent - pTree->cxIndent/2) <= g_cxDoubleClk)) {
  1643. int i;
  1644. for (i = hItem->iLevel - x/pTree->cxIndent + ((pTree->ci.style & TVS_LINESATROOT)?1:0); i > 1; i--)
  1645. hItem = hItem->hParent;
  1646. ExpandItem:
  1647. #ifdef _X86_
  1648. lResult = CCSendNotify(&pTree->ci, wFlags & MK_RBUTTON ? NM_RDBLCLK : NM_DBLCLK, &nmeu.nmhdr);
  1649. #else
  1650. lResult = CCSendNotify(&pTree->ci, wFlags & MK_RBUTTON ? NM_RDBLCLK : NM_DBLCLK, NULL);
  1651. #endif
  1652. if (!IsWindow(hwndTree))
  1653. goto bail;
  1654. if (!lResult) {
  1655. // don't auto expand this if we're in single expand mode because the first click did it already
  1656. if (!(pTree->ci.style & TVS_SINGLEEXPAND))
  1657. TV_Expand(pTree, TVE_TOGGLE, hItem, TRUE);
  1658. }
  1659. }
  1660. pTree->fScrollWait = FALSE;
  1661. } else { // WM_LBUTTONDOWN
  1662. if (wHitCode == TVHT_ONITEMBUTTON)
  1663. {
  1664. if (!CCSendNotify(&pTree->ci, NM_CLICK, NULL)) {
  1665. if (TVBD_flags & TVBD_FROMWHEEL)
  1666. TV_Expand(pTree, (TVBD_flags & TVBD_WHEELFORWARD) ? TVE_EXPAND : TVE_COLLAPSE, hItem, TRUE);
  1667. else
  1668. TV_Expand(pTree, TVE_TOGGLE, hItem, TRUE);
  1669. }
  1670. }
  1671. else if (wHitCode & TVHT_ONITEM ||
  1672. ((pTree->ci.style & TVS_FULLROWSELECT) && (wHitCode & (TVHT_ONITEMRIGHT | TVHT_ONITEMINDENT))))
  1673. {
  1674. BOOL fSameItem, bDragging;
  1675. ASSERT(hItem);
  1676. fSameItem = (hItem == pTree->hCaret);
  1677. if (TVBD_flags & TVBD_FROMWHEEL)
  1678. bDragging = FALSE;
  1679. else if (pTree->ci.style & TVS_DISABLEDRAGDROP)
  1680. bDragging = FALSE;
  1681. else {
  1682. bDragging = TV_CheckForDragBegin(pTree, hItem, x, y);
  1683. TV_FinishCheckDrag(pTree);
  1684. }
  1685. if (bDragging)
  1686. {
  1687. pTree->htiDrag = hItem;
  1688. TV_SendBeginDrag(pTree, TVN_BEGINDRAG, hItem, x, y);
  1689. return;
  1690. }
  1691. if (!CCSendNotify(&pTree->ci, NM_CLICK, NULL)) {
  1692. if (wHitCode == TVHT_ONITEMSTATEICON &&
  1693. (pTree->ci.style & TVS_CHECKBOXES)) {
  1694. TV_HandleStateIconClick(pTree, hItem);
  1695. } else {
  1696. // Only set the caret (selection) if not dragging
  1697. TV_SelectItem(pTree, TVGN_CARET, hItem, TVSIF_NOTIFY | TVSIF_UPDATENOW, TVC_BYMOUSE);
  1698. if (fSameItem && (wHitCode & TVHT_ONITEMLABEL) && pTree->fFocus)
  1699. {
  1700. //
  1701. // The item and window are currently selected and user clicked
  1702. // on label. Try to enter into name editing mode.
  1703. //
  1704. SetTimer(pTree->ci.hwnd, IDT_NAMEEDIT, GetDoubleClickTime(), NULL);
  1705. pTree->fNameEditPending = TRUE;
  1706. }
  1707. if (fSameItem && pTree->ci.style & TVS_SINGLEEXPAND) {
  1708. // single click on the focus item toggles expand state
  1709. TV_Expand(pTree, TVE_TOGGLE, pTree->hCaret, TRUE);
  1710. }
  1711. }
  1712. }
  1713. } else {
  1714. CCSendNotify(&pTree->ci, NM_CLICK, NULL);
  1715. }
  1716. }
  1717. if (!pTree->fFocus)
  1718. SetFocus(pTree->ci.hwnd);
  1719. bail:;
  1720. }
  1721. // ----------------------------------------------------------------------------
  1722. //
  1723. // Gets the item's text, data, and/or image.
  1724. //
  1725. // ----------------------------------------------------------------------------
  1726. BOOL NEAR TV_OnGetItem(PTREE pTree, LPTVITEMEX ptvi)
  1727. {
  1728. if (!ptvi)
  1729. return FALSE;
  1730. if (!ValidateTreeItem(ptvi->hItem, 0))
  1731. return FALSE; // Invalid parameter
  1732. TV_GetItem(pTree, ptvi->hItem, ptvi->mask, ptvi);
  1733. return TRUE; // success
  1734. }
  1735. BOOL NEAR TV_OnGetItemA(PTREE pTree, LPTVITEMEXA ptvi)
  1736. {
  1737. BOOL bRet;
  1738. LPSTR pszA = NULL;
  1739. LPWSTR pszW = NULL;
  1740. //HACK Alert! This code assumes that TVITEMA is exactly the same
  1741. // as TVITEMW except for the text pointer in the TVITEM
  1742. ASSERT(sizeof(TVITEMA) == sizeof(TVITEMW));
  1743. if (!IsFlagPtr(ptvi) && (ptvi->mask & TVIF_TEXT) && !IsFlagPtr(ptvi->pszText)) {
  1744. pszA = ptvi->pszText;
  1745. pszW = LocalAlloc(LMEM_FIXED, ptvi->cchTextMax * sizeof(WCHAR));
  1746. if (pszW == NULL) {
  1747. return FALSE;
  1748. }
  1749. ptvi->pszText = (LPSTR)pszW;
  1750. }
  1751. bRet = TV_OnGetItem(pTree, (LPTVITEMEXW)ptvi);
  1752. if (pszA) {
  1753. if (bRet && ptvi->cchTextMax)
  1754. ConvertWToAN(pTree->ci.uiCodePage, pszA, ptvi->cchTextMax, (LPWSTR)(ptvi->pszText), -1);
  1755. LocalFree(pszW);
  1756. ptvi->pszText = pszA;
  1757. }
  1758. return bRet;
  1759. }
  1760. // ----------------------------------------------------------------------------
  1761. //
  1762. // Sets the item's text, data, and/or image.
  1763. //
  1764. // ----------------------------------------------------------------------------
  1765. BOOL NEAR TV_SetItemA(PTREE pTree, LPTVITEMEXA ptvi)
  1766. {
  1767. LPSTR pszA = NULL;
  1768. BOOL lRet;
  1769. //HACK Alert! This code assumes that TVITEMA is exactly the same
  1770. // as TVITEMW except for the text pointer in the TVITEM
  1771. ASSERT(sizeof(TVITEMA) == sizeof(TVITEMW));
  1772. if (!IsFlagPtr(ptvi) && (ptvi->mask & TVIF_TEXT) && !IsFlagPtr(ptvi->pszText)) {
  1773. pszA = ptvi->pszText;
  1774. ptvi->pszText = (LPSTR)ProduceWFromA(pTree->ci.uiCodePage, pszA);
  1775. if (ptvi->pszText == NULL) {
  1776. ptvi->pszText = pszA;
  1777. return -1;
  1778. }
  1779. }
  1780. lRet = TV_SetItem(pTree, (LPCTVITEMEX)ptvi);
  1781. if (pszA) {
  1782. FreeProducedString(ptvi->pszText);
  1783. ptvi->pszText = pszA;
  1784. }
  1785. return lRet;
  1786. }
  1787. BOOL NEAR TV_SetItem(PTREE pTree, LPCTVITEMEX ptvi)
  1788. {
  1789. UINT uRDWFlags = RDW_INVALIDATE;
  1790. BOOL fEraseIfTransparent = FALSE;
  1791. HTREEITEM hItem;
  1792. BOOL bActualChange = FALSE; // HACK: We want to keep track of which
  1793. // attributes were changed from CALLBACK to
  1794. // "real", and don't invalidate if those were
  1795. // the only changes
  1796. int iIntegralPrev;
  1797. BOOL fName = FALSE;
  1798. BOOL fFocusSel = FALSE;
  1799. BOOL fRecalcWidth = FALSE;
  1800. BOOL fStateImageChange = FALSE;
  1801. if (!ptvi)
  1802. return FALSE;
  1803. hItem = ptvi->hItem;
  1804. // deal with the evil invisible root for multiple root trees.
  1805. if (hItem == TVI_ROOT)
  1806. {
  1807. hItem = pTree->hRoot;
  1808. }
  1809. if (!ValidateTreeItem(hItem, 0))
  1810. return FALSE;
  1811. iIntegralPrev = hItem->iIntegral;
  1812. // BUGBUG: send ITEMCHANING and ITEMCHANGED msgs
  1813. if (ptvi->mask & TVIF_TEXT)
  1814. {
  1815. uRDWFlags = RDW_INVALIDATE |RDW_ERASE;
  1816. bActualChange = TRUE;
  1817. if (!ptvi->pszText)
  1818. {
  1819. Str_Set(&hItem->lpstr, LPSTR_TEXTCALLBACK);
  1820. }
  1821. else
  1822. {
  1823. if (!Str_Set(&hItem->lpstr, ptvi->pszText))
  1824. {
  1825. //
  1826. // Memory allocation failed - The best we can do now
  1827. // is to set the item back to callback, and hope that
  1828. // the top level program can handle it.
  1829. //
  1830. DebugMsg(DM_ERROR, TEXT("TreeView: Out of memory"));
  1831. hItem->lpstr = LPSTR_TEXTCALLBACK;
  1832. }
  1833. }
  1834. fRecalcWidth = TRUE;
  1835. fName = TRUE;
  1836. }
  1837. if (ptvi->mask & TVIF_PARAM)
  1838. {
  1839. bActualChange = TRUE;
  1840. hItem->lParam = ptvi->lParam;
  1841. }
  1842. if (ptvi->mask & TVIF_IMAGE)
  1843. {
  1844. if (hItem->iImage != (WORD)I_IMAGECALLBACK) {
  1845. bActualChange = TRUE;
  1846. fEraseIfTransparent = TRUE;
  1847. if (pTree->hImageList && (ImageList_GetBkColor(pTree->hImageList) == (COLORREF)-1))
  1848. uRDWFlags |= RDW_ERASE;
  1849. }
  1850. hItem->iImage = (SHORT)ptvi->iImage;
  1851. }
  1852. if (ptvi->mask & TVIF_SELECTEDIMAGE)
  1853. {
  1854. if (hItem->iSelectedImage != (WORD)I_IMAGECALLBACK)
  1855. bActualChange = TRUE;
  1856. hItem->iSelectedImage = (SHORT)ptvi->iSelectedImage;
  1857. }
  1858. if (ptvi->mask & TVIF_CHILDREN)
  1859. {
  1860. if (hItem->fKids != KIDS_CALLBACK)
  1861. bActualChange = TRUE;
  1862. if (ptvi->cChildren == I_CHILDRENCALLBACK) {
  1863. hItem->fKids = KIDS_CALLBACK;
  1864. } else {
  1865. if (ptvi->cChildren)
  1866. hItem->fKids = KIDS_FORCE_YES;
  1867. else
  1868. hItem->fKids = KIDS_FORCE_NO;
  1869. }
  1870. //
  1871. // If this item currently has no kid, reset the item.
  1872. //
  1873. if ((ptvi->cChildren == I_CHILDRENCALLBACK) && (hItem->hKids == NULL))
  1874. {
  1875. hItem->state &= ~TVIS_EXPANDEDONCE;
  1876. if (hItem->hParent)
  1877. hItem->state &= ~TVIS_EXPANDED;
  1878. }
  1879. }
  1880. if (ptvi->mask & TVIF_INTEGRAL)
  1881. {
  1882. if (LOWORD(ptvi->iIntegral) > 0)
  1883. hItem->iIntegral = LOWORD(ptvi->iIntegral);
  1884. }
  1885. if (ptvi->mask & TVIF_STATE)
  1886. {
  1887. // don't & ptvi->state with TVIS_ALL because win95 didn't
  1888. // and setting TVIS_FOCUS was retrievable even though we don't use it
  1889. UINT change = (hItem->state ^ ptvi->state) & ptvi->stateMask;
  1890. if (change)
  1891. {
  1892. // BUGBUG: (TVIS_SELECTED | TVIS_DROPHILITED) changes
  1893. // should effect tree state
  1894. hItem->state ^= change;
  1895. bActualChange = TRUE;
  1896. fEraseIfTransparent = TRUE;
  1897. if (hItem->state & TVIS_BOLD) {
  1898. if (!pTree->hFontBold)
  1899. TV_CreateBoldFont(pTree);
  1900. }
  1901. if (change & TVIS_BOLD){
  1902. // do this because changing the boldness
  1903. uRDWFlags |= RDW_ERASE;
  1904. fRecalcWidth = TRUE;
  1905. }
  1906. fStateImageChange = change & TVIS_STATEIMAGEMASK;
  1907. if (fStateImageChange) {
  1908. uRDWFlags |= RDW_ERASE;
  1909. // Adding/removing a state image changes the ITEM_OFFSET
  1910. // If old image was 0, then we are adding.
  1911. // If new image is 0, then we are removing.
  1912. // (If old=new, then we don't get into this code path, so we
  1913. // don't have to worry about that case.)
  1914. if (!(hItem->state & TVIS_STATEIMAGEMASK) || // new
  1915. !((hItem->state ^ change) & TVIS_STATEIMAGEMASK)) { // old
  1916. fRecalcWidth = TRUE;
  1917. }
  1918. }
  1919. fFocusSel = ((change & TVIS_SELECTED) != 0);
  1920. }
  1921. }
  1922. if (fRecalcWidth) {
  1923. hItem->iWidth = 0; // Invalidate old width
  1924. if (TV_IsShowing(hItem)) {
  1925. TV_ScrollBarsAfterSetWidth(pTree, hItem);
  1926. }
  1927. }
  1928. // force a redraw if something changed AND if we are not
  1929. // inside of a paint of this guy (callbacks will set the
  1930. // item on the paint callback to implement lazy data schemes)
  1931. if (bActualChange && (pTree->hItemPainting != hItem))
  1932. {
  1933. if (fEraseIfTransparent) {
  1934. if (pTree->hImageList) {
  1935. if (ImageList_GetBkColor(pTree->hImageList) == CLR_NONE) {
  1936. uRDWFlags |= RDW_ERASE;
  1937. }
  1938. }
  1939. }
  1940. // If item height changed, then we've got a lot of cleaning up
  1941. // to do.
  1942. if (hItem->iIntegral != iIntegralPrev)
  1943. {
  1944. TV_ScrollBarsAfterResize(pTree, hItem, iIntegralPrev, uRDWFlags);
  1945. }
  1946. else
  1947. {
  1948. TV_InvalidateItem(pTree, hItem, uRDWFlags);
  1949. }
  1950. // REVIEW: we might need to update the scroll bars if the
  1951. // text length changed!
  1952. }
  1953. if (bActualChange)
  1954. {
  1955. if (fName)
  1956. MyNotifyWinEvent(EVENT_OBJECT_NAMECHANGE, pTree->ci.hwnd, OBJID_CLIENT,
  1957. (LONG_PTR)hItem);
  1958. if (fFocusSel)
  1959. {
  1960. MyNotifyWinEvent(EVENT_OBJECT_FOCUS, pTree->ci.hwnd, OBJID_CLIENT,
  1961. (LONG_PTR)hItem);
  1962. MyNotifyWinEvent(((hItem->state & TVIS_SELECTED) ?
  1963. EVENT_OBJECT_SELECTIONADD : EVENT_OBJECT_SELECTIONREMOVE),
  1964. pTree->ci.hwnd, OBJID_CLIENT, (LONG_PTR)hItem);
  1965. }
  1966. if (fStateImageChange)
  1967. MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, pTree->ci.hwnd, OBJID_CLIENT,
  1968. (LONG_PTR)hItem);
  1969. }
  1970. return TRUE;
  1971. }
  1972. // ----------------------------------------------------------------------------
  1973. //
  1974. // Calls TV_CheckHit to get the hit test results and then package it in a
  1975. // structure back to the app.
  1976. //
  1977. // ----------------------------------------------------------------------------
  1978. HTREEITEM NEAR TV_OnHitTest(PTREE pTree, LPTV_HITTESTINFO lptvh)
  1979. {
  1980. if (!lptvh)
  1981. return 0; //BUGBUG: Validate LPTVHITTEST
  1982. lptvh->hItem = TV_CheckHit(pTree, lptvh->pt.x, lptvh->pt.y, &lptvh->flags);
  1983. return lptvh->hItem;
  1984. }
  1985. BOOL TV_IsItemTruncated(PTREE pTree, TREEITEM *hItem, LPRECT lprc)
  1986. {
  1987. if (TV_GetItemRect(pTree,hItem,lprc,TRUE)) {
  1988. lprc->left -= g_cxEdge;
  1989. lprc->top -= g_cyBorder;
  1990. if ((lprc->left + hItem->iWidth) > pTree->cxWnd) {
  1991. return TRUE;
  1992. }
  1993. }
  1994. return FALSE;
  1995. }
  1996. BOOL TV_HandleTTNShow(PTREE pTree, LPNMHDR lpnm)
  1997. {
  1998. if (pTree->hToolTip && pTree->fPlaceTooltip) {
  1999. LPNMTTSHOWINFO psi = (LPNMTTSHOWINFO)lpnm;
  2000. RECT rc;
  2001. TVITEMEX item;
  2002. // Now get the text associated with that item
  2003. item.stateMask = TVIS_BOLD;
  2004. TV_GetItem(pTree, pTree->hToolTip, TVIF_STATE, &item);
  2005. SendMessage(pTree->hwndToolTips, WM_SETFONT, (WPARAM)((item.state & TVIS_BOLD) ? pTree->hFontBold : pTree->hFont), 0);
  2006. TV_GetItemRect(pTree, pTree->hToolTip, &rc, TRUE);
  2007. MapWindowRect(pTree->ci.hwnd, HWND_DESKTOP, &rc);
  2008. // We draw the text with margins, so take those into account too.
  2009. // These values come from TV_DrawItem...
  2010. rc.top += g_cyBorder;
  2011. rc.left += g_cxLabelMargin;
  2012. //
  2013. // At this point, (rc.left, rc.top) are the coordinates we pass
  2014. // to DrawText. Ask the tooltip how we should position it so the
  2015. // tooltip text shows up in precisely the same location.
  2016. //
  2017. // BUGBUG raymondc v6: wrong coordinates if app has used TVM_SETITEMHEIGHT
  2018. SendMessage(pTree->hwndToolTips, TTM_ADJUSTRECT, TRUE, (LPARAM)&rc);
  2019. SetWindowPos(pTree->hwndToolTips, NULL, rc.left, rc.top,0,0,
  2020. SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
  2021. // This is an inplace tooltip, so disable animation.
  2022. psi->dwStyle |= TTS_NOANIMATE;
  2023. // handled!
  2024. return TRUE;
  2025. }
  2026. return FALSE;
  2027. }
  2028. //
  2029. // Copy the font from the treeview item into the tooltip so the tooltip
  2030. // shows up in the correct font.
  2031. //
  2032. BOOL TV_HandleTTCustomDraw(PTREE pTree, LPNMTTCUSTOMDRAW pnm)
  2033. {
  2034. if (pTree->hToolTip && pTree->fPlaceTooltip &&
  2035. (pnm->nmcd.dwDrawStage == CDDS_PREPAINT ||
  2036. pnm->nmcd.dwDrawStage == CDDS_ITEMPREPAINT))
  2037. {
  2038. //
  2039. // Set up the customdraw DC to match the font of the TV item.
  2040. //
  2041. TVFAKEDRAW tvfd;
  2042. DWORD dwCustom = 0;
  2043. TreeView_BeginFakeCustomDraw(pTree, &tvfd);
  2044. dwCustom = TreeView_BeginFakeItemDraw(&tvfd, pTree->hToolTip);
  2045. // If client changed the font, then transfer the font
  2046. // from our private hdc into the tooltip's HDC. We use
  2047. // a private HDC because we only want to let the app change
  2048. // the font, not the colors or anything else.
  2049. if (dwCustom & CDRF_NEWFONT)
  2050. {
  2051. SelectObject(pnm->nmcd.hdc, GetCurrentObject(tvfd.nmcd.nmcd.hdc, OBJ_FONT));
  2052. }
  2053. TreeView_EndFakeItemDraw(&tvfd);
  2054. TreeView_EndFakeCustomDraw(&tvfd);
  2055. // Don't return other wacky flags to TT, since all we
  2056. // did was change the font (if even that)
  2057. return dwCustom & CDRF_NEWFONT;
  2058. }
  2059. return CDRF_DODEFAULT;
  2060. }
  2061. BOOL TV_SetToolTipTarget(PTREE pTree, HTREEITEM hItem)
  2062. {
  2063. // update the item we're showing the bubble for...
  2064. if (pTree->hToolTip != hItem) {
  2065. // the hide will keep us from flashing
  2066. ShowWindow(pTree->hwndToolTips, SW_HIDE);
  2067. UpdateWindow(pTree->hwndToolTips);
  2068. pTree->hToolTip = hItem;
  2069. SendMessage(pTree->hwndToolTips, TTM_UPDATE, 0, 0);
  2070. return TRUE;
  2071. }
  2072. return FALSE;
  2073. }
  2074. TREEITEM* TV_ItemAtCursor(PTREE pTree, LPRECT prc)
  2075. {
  2076. RECT rc;
  2077. UINT wHitCode;
  2078. TREEITEM* hItem;
  2079. GetCursorPos((LPPOINT)&rc);
  2080. ScreenToClient(pTree->ci.hwnd, (LPPOINT)&rc);
  2081. hItem = TV_CheckHit(pTree,rc.left,rc.top,&wHitCode);
  2082. if (prc)
  2083. *prc = rc;
  2084. if (!(wHitCode & TVHT_ONITEM))
  2085. hItem = NULL;
  2086. return hItem;
  2087. }
  2088. BOOL TV_UpdateToolTipTarget(PTREE pTree)
  2089. {
  2090. RECT rc;
  2091. TREEITEM *hItem = TV_ItemAtCursor(pTree, &rc);
  2092. if (!(pTree->ci.style & TVS_NOTOOLTIPS)
  2093. && !TV_IsItemTruncated(pTree, hItem, &rc)
  2094. && !(pTree->ci.style & TVS_INFOTIP))
  2095. hItem = NULL;
  2096. // else if (!(pTree->ci.style & TVS_NOTOOLTIPS)
  2097. // || (pTree->ci.style & TVS_INFOTIP))
  2098. return TV_SetToolTipTarget(pTree, hItem);
  2099. }
  2100. BOOL TV_UpdateToolTip(PTREE pTree)
  2101. {
  2102. if (pTree->hwndToolTips && pTree->fRedraw)
  2103. return (TV_UpdateToolTipTarget(pTree));
  2104. return TRUE;
  2105. }
  2106. BOOL TV_SetInsertMark(PTREE pTree, HTREEITEM hItem, BOOL fAfter)
  2107. {
  2108. if (!ValidateTreeItem(hItem, VTI_NULLOK)) // NULL means remove insert mark
  2109. return FALSE;
  2110. TV_InvalidateInsertMarkRect(pTree, TRUE); // Make sure the old one gets erased
  2111. pTree->fInsertAfter = BOOLIFY(fAfter);
  2112. pTree->htiInsert = hItem;
  2113. TV_InvalidateInsertMarkRect(pTree, FALSE); // Make sure the new one gets drawn
  2114. return TRUE;
  2115. }
  2116. BOOL TV_GetInfoTip(PTREE pTree, LPTOOLTIPTEXT lpttt, HTREEITEM hti, LPTSTR szBuf, int cch)
  2117. {
  2118. NMTVGETINFOTIP git;
  2119. szBuf[0] = 0;
  2120. git.pszText = szBuf;
  2121. git.cchTextMax = cch;
  2122. git.hItem = hti;
  2123. git.lParam = hti->lParam;
  2124. // for folded items pszText is prepopulated with the
  2125. // item text, clients should append to this
  2126. CCSendNotify(&pTree->ci, TVN_GETINFOTIP, &git.hdr);
  2127. CCSetInfoTipWidth(pTree->ci.hwnd, pTree->hwndToolTips);
  2128. Str_Set(&pTree->pszTip, git.pszText);
  2129. lpttt->lpszText = pTree->pszTip;
  2130. if(pTree->ci.style & TVS_RTLREADING)
  2131. {
  2132. lpttt->uFlags |= TTF_RTLREADING;
  2133. }
  2134. return lpttt->lpszText && lpttt->lpszText[0];
  2135. }
  2136. void TV_HandleNeedText(PTREE pTree, LPTOOLTIPTEXT lpttt)
  2137. {
  2138. TVITEMEX tvItem;
  2139. TCHAR szBuf[INFOTIPSIZE];
  2140. RECT rc;
  2141. HTREEITEM hItem;
  2142. // No distracting tooltips while in-place editing, please
  2143. if (pTree->htiEdit)
  2144. {
  2145. return;
  2146. }
  2147. // If the cursor isn't over anything, then stop
  2148. hItem = TV_ItemAtCursor(pTree, &rc);
  2149. if (!hItem)
  2150. return;
  2151. // If the item has an infotip, then use it
  2152. if (pTree->ci.style & TVS_INFOTIP) {
  2153. if (hItem && TV_GetInfoTip(pTree, lpttt, hItem, szBuf, ARRAYSIZE(szBuf))) {
  2154. pTree->fPlaceTooltip = FALSE;
  2155. pTree->hToolTip = hItem;
  2156. return;
  2157. }
  2158. }
  2159. // Else it isn't an infotip
  2160. CCResetInfoTipWidth(pTree->ci.hwnd, pTree->hwndToolTips);
  2161. // If the item is not truncated, then no need for a tooltip
  2162. if (!TV_IsItemTruncated(pTree, hItem, &rc))
  2163. {
  2164. tvItem.hItem = NULL;
  2165. return;
  2166. }
  2167. // Display an in-place tooltip for the item
  2168. pTree->fPlaceTooltip = TRUE;
  2169. pTree->hToolTip = hItem;
  2170. tvItem.hItem = hItem;
  2171. tvItem.mask = TVIF_TEXT | TVIF_STATE;
  2172. tvItem.pszText = szBuf;
  2173. tvItem.stateMask = TVIS_DROPHILITED | TVIS_SELECTED;
  2174. COMPILETIME_ASSERT(MAXLABELTEXT <= ARRAYSIZE(szBuf));
  2175. tvItem.cchTextMax = MAXLABELTEXT;
  2176. TV_OnGetItem(pTree,&tvItem);
  2177. Str_Set(&pTree->pszTip, tvItem.pszText);
  2178. lpttt->lpszText = pTree->pszTip;
  2179. DebugMsg(DM_TRACE, TEXT("TV_HandleNeedText for %d returns %s"), tvItem.hItem, lpttt->szText);
  2180. }
  2181. //
  2182. // Visual Studio 5.0 Books Online (part of VB 5.0) subclasses
  2183. // us and responds NFR_ANSI, so we end up getting TTN_NEEDTEXTA
  2184. // instead of TTN_NEEDTEXTW. We can't risk forcing the tooltip
  2185. // to UNICODE because some other apps may have done this on purpose
  2186. // (because they intend to intercept TTN_NEEDTEXTA and do custom tooltips).
  2187. // So support the ANSI tooltip notification so VB stays happy.
  2188. // Note: This doesn't have to be efficient, as it's an error case anyway.
  2189. //
  2190. void TV_HandleNeedTextA(PTREE pTree, LPTOOLTIPTEXTA lptttA)
  2191. {
  2192. TOOLTIPTEXT ttt;
  2193. ttt.szText[0] = TEXT('\0');
  2194. ttt.hdr = lptttA->hdr;
  2195. ttt.lpszText = ttt.szText;
  2196. ttt.hinst = lptttA->hinst;
  2197. ttt.uFlags = lptttA->uFlags;
  2198. ttt.lParam = lptttA->lParam;
  2199. TV_HandleNeedText(pTree, &ttt);
  2200. if (pTree->pszTipA)
  2201. LocalFree(pTree->pszTipA);
  2202. pTree->pszTipA = ProduceAFromW(pTree->ci.uiCodePage, ttt.lpszText);
  2203. lptttA->lpszText = pTree->pszTipA;
  2204. lptttA->uFlags = ttt.uFlags;
  2205. }
  2206. // ----------------------------------------------------------------------------
  2207. //
  2208. // TV_Timer
  2209. //
  2210. // Checks to see if it is our name editing timer. If so it calls of to
  2211. // do name editing
  2212. //
  2213. // ----------------------------------------------------------------------------
  2214. LRESULT NEAR TV_Timer(PTREE pTree, UINT uTimerId)
  2215. {
  2216. switch (uTimerId)
  2217. {
  2218. case IDT_NAMEEDIT:
  2219. // Kill the timer as we wont need any more messages from it.
  2220. KillTimer(pTree->ci.hwnd, IDT_NAMEEDIT);
  2221. if (pTree->fNameEditPending)
  2222. {
  2223. // And start name editing mode.
  2224. if (!TV_EditLabel(pTree, pTree->hCaret, NULL))
  2225. {
  2226. TV_DismissEdit(pTree, FALSE);
  2227. }
  2228. // remove the flag...
  2229. pTree->fNameEditPending = FALSE;
  2230. }
  2231. break;
  2232. case IDT_SCROLLWAIT:
  2233. KillTimer(pTree->ci.hwnd, IDT_SCROLLWAIT);
  2234. if (pTree->fScrollWait)
  2235. {
  2236. if (pTree->hCaret) {
  2237. TV_ScrollVertIntoView(pTree, pTree->hCaret);
  2238. }
  2239. pTree->fScrollWait = FALSE;
  2240. }
  2241. break;
  2242. }
  2243. return 0;
  2244. }
  2245. // ----------------------------------------------------------------------------
  2246. //
  2247. // TV_Command
  2248. //
  2249. // Process the WM_COMMAND. See if it is an input from our edit windows.
  2250. // if so we may want to dismiss it, and or set it is being dirty...
  2251. //
  2252. // ----------------------------------------------------------------------------
  2253. void NEAR TV_Command(PTREE pTree, int id, HWND hwndCtl, UINT codeNotify)
  2254. {
  2255. if ((pTree != NULL) && (hwndCtl == pTree->hwndEdit))
  2256. {
  2257. switch (codeNotify)
  2258. {
  2259. case EN_UPDATE:
  2260. // We will use the ID of the window as a Dirty flag...
  2261. SetWindowID(pTree->hwndEdit, 1);
  2262. TV_SetEditSize(pTree);
  2263. break;
  2264. case EN_KILLFOCUS:
  2265. // We lost focus, so dismiss edit and save changes
  2266. // (Note that the owner might reject the change and restart
  2267. // edit mode, which traps the user. Owners need to give the
  2268. // user a way to get out.)
  2269. if (!TV_DismissEdit(pTree, FALSE))
  2270. return;
  2271. break;
  2272. case HN_BEGINDIALOG: // penwin is bringing up a dialog
  2273. ASSERT(GetSystemMetrics(SM_PENWINDOWS)); // only on a pen system
  2274. pTree->fNoDismissEdit = TRUE;
  2275. break;
  2276. case HN_ENDDIALOG: // penwin has destroyed dialog
  2277. ASSERT(GetSystemMetrics(SM_PENWINDOWS)); // only on a pen system
  2278. pTree->fNoDismissEdit = FALSE;
  2279. break;
  2280. }
  2281. // Forward edit control notifications up to parent
  2282. //
  2283. if (IsWindow(hwndCtl))
  2284. FORWARD_WM_COMMAND(pTree->ci.hwndParent, id, hwndCtl, codeNotify, SendMessage);
  2285. }
  2286. }
  2287. HIMAGELIST CreateCheckBoxImagelist(HIMAGELIST himl, BOOL fTree, BOOL fUseColorKey, BOOL fMirror);
  2288. void TV_CreateToolTips(PTREE pTree);
  2289. void TV_InitCheckBoxes(PTREE pTree)
  2290. {
  2291. HIMAGELIST himl;
  2292. TVITEMEX ti;
  2293. BOOL fNoColorKey = FALSE; // Backwards: If Cleartype is turned on, then we don't use colorkey.
  2294. if (g_bRunOnNT5)
  2295. {
  2296. #ifdef CLEARTYPE // Don't use SPI_CLEARTYPE because it's defined because of APIThk, but not in NT.
  2297. SystemParametersInfo(SPI_GETCLEARTYPE, 0, &fNoColorKey, 0);
  2298. #endif
  2299. }
  2300. himl = CreateCheckBoxImagelist(pTree->hImageList, TRUE, !fNoColorKey, IS_WINDOW_RTL_MIRRORED(pTree->ci.hwnd));
  2301. if (pTree->hImageList)
  2302. {
  2303. COLORREF cr = ImageList_GetBkColor(pTree->hImageList);
  2304. ImageList_SetBkColor(himl, fNoColorKey? (CLR_NONE) : (cr));
  2305. }
  2306. TV_SetImageList(pTree, himl, TVSIL_STATE);
  2307. ti.mask = TVIF_STATE;
  2308. ti.state = INDEXTOSTATEIMAGEMASK(1);
  2309. ti.stateMask = TVIS_STATEIMAGEMASK;
  2310. TV_SetItemRecurse(pTree, pTree->hRoot, &ti);
  2311. }
  2312. void NEAR TV_OnStyleChanged(PTREE pTree, WPARAM gwl, LPSTYLESTRUCT pinfo)
  2313. {
  2314. // Style changed: redraw everything...
  2315. //
  2316. // try to do this smartly, avoiding unnecessary redraws
  2317. if (gwl == GWL_STYLE)
  2318. {
  2319. DWORD changeFlags;
  2320. DWORD styleNew;
  2321. TV_DismissEdit(pTree, FALSE); // BUGBUG: FALSE == accept changes. Is this right?
  2322. // You cannot combine TVS_HASLINES and TVS_FULLROWSELECT
  2323. // because it doesn't work
  2324. styleNew = pinfo->styleNew;
  2325. if (styleNew & TVS_HASLINES) {
  2326. if (styleNew & TVS_FULLROWSELECT) {
  2327. DebugMsg(DM_ERROR, TEXT("Cannot combine TVS_HASLINES and TVS_FULLROWSELECT"));
  2328. }
  2329. styleNew &= ~TVS_FULLROWSELECT;
  2330. }
  2331. changeFlags = pTree->ci.style ^ styleNew; // those that changed
  2332. pTree->ci.style = styleNew; // change our version
  2333. pTree->ci.style &= ~TVS_RTLREADING;
  2334. pTree->ci.style |= (pinfo->styleNew & TVS_RTLREADING);
  2335. if (changeFlags & (TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT))
  2336. TV_CreateIndentBmps(pTree);
  2337. if (changeFlags & TVS_CHECKBOXES) {
  2338. if (pTree->ci.style & TVS_CHECKBOXES) {
  2339. TV_InitCheckBoxes(pTree);
  2340. }
  2341. }
  2342. if (changeFlags & TVS_NOTOOLTIPS) {
  2343. if (pTree->ci.style & TVS_NOTOOLTIPS) {
  2344. DestroyWindow(pTree->hwndToolTips);
  2345. pTree->hwndToolTips = NULL;
  2346. } else {
  2347. TV_CreateToolTips(pTree);
  2348. }
  2349. }
  2350. if (changeFlags & TVS_TRACKSELECT) {
  2351. if (!(pTree->ci.style & TVS_TRACKSELECT)) {
  2352. if (pTree->hHot) {
  2353. TV_InvalidateItem(pTree, pTree->hHot, RDW_INVALIDATE | RDW_ERASE);
  2354. pTree->hHot = NULL;
  2355. }
  2356. }
  2357. }
  2358. // Checkboxes and stuff may have changed width - go recompute
  2359. TV_ScrollBarsAfterSetWidth(pTree, NULL);
  2360. }
  2361. else if (gwl == GWL_EXSTYLE)
  2362. {
  2363. DWORD changeFlags;
  2364. changeFlags = (pinfo->styleNew & WS_EX_RTLREADING) ?TVS_RTLREADING :0;
  2365. if (changeFlags ^ (pTree->ci.style & TVS_RTLREADING))
  2366. {
  2367. pTree->ci.style ^= TVS_RTLREADING;
  2368. TV_DismissEdit(pTree, FALSE); // Cancels edits
  2369. DestroyWindow(pTree->hwndToolTips);
  2370. pTree->hwndToolTips = NULL;
  2371. TV_CreateToolTips(pTree);
  2372. }
  2373. }
  2374. }
  2375. void TV_OnMouseMove(PTREE pTree, DWORD dwPos, WPARAM wParam)
  2376. {
  2377. if (pTree->ci.style & TVS_TRACKSELECT) {
  2378. POINT pt;
  2379. HTREEITEM hHot;
  2380. UINT wHitCode;
  2381. pt.x = GET_X_LPARAM(dwPos);
  2382. pt.y = GET_Y_LPARAM(dwPos);
  2383. hHot = TV_CheckHit(pTree,pt.x,pt.y,&wHitCode);
  2384. if (!(pTree->ci.style & TVS_FULLROWSELECT) &&
  2385. !(wHitCode & TVHT_ONITEM)) {
  2386. hHot = NULL;
  2387. }
  2388. if (hHot != pTree->hHot) {
  2389. TV_InvalidateItem(pTree, pTree->hHot, RDW_INVALIDATE);
  2390. TV_InvalidateItem(pTree, hHot, RDW_INVALIDATE);
  2391. pTree->hHot = hHot;
  2392. // update now so that we won't have an invalid area
  2393. // under the tooltips
  2394. UpdateWindow(pTree->ci.hwnd);
  2395. }
  2396. }
  2397. if (pTree->hwndToolTips) {
  2398. if (!TV_UpdateToolTip(pTree)) {
  2399. RelayToToolTips(pTree->hwndToolTips, pTree->ci.hwnd, WM_MOUSEMOVE, wParam, dwPos);
  2400. }
  2401. }
  2402. }
  2403. void NEAR TV_OnWinIniChange(PTREE pTree, WPARAM wParam)
  2404. {
  2405. if (!wParam ||
  2406. (wParam == SPI_SETNONCLIENTMETRICS) ||
  2407. (wParam == SPI_SETICONTITLELOGFONT)) {
  2408. if (pTree->fCreatedFont)
  2409. TV_OnSetFont(pTree, NULL, TRUE);
  2410. if (!pTree->fIndentSet) {
  2411. // this will validate against the minimum
  2412. TV_SetIndent(pTree, 0);
  2413. }
  2414. }
  2415. }
  2416. void TV_OnSetBkColor(PTREE pTree, COLORREF clr)
  2417. {
  2418. if (pTree->clrBk != (COLORREF)-1) {
  2419. DeleteObject(pTree->hbrBk);
  2420. }
  2421. pTree->clrBk = clr;
  2422. if (clr != (COLORREF)-1) {
  2423. pTree->hbrBk = CreateSolidBrush(clr);
  2424. }
  2425. TV_CreateIndentBmps(pTree); // This also invalidates
  2426. }
  2427. BOOL TV_TranslateAccelerator(HWND hwnd, LPMSG lpmsg)
  2428. {
  2429. if (!lpmsg)
  2430. return FALSE;
  2431. if (GetFocus() != hwnd)
  2432. return FALSE;
  2433. switch (lpmsg->message) {
  2434. case WM_KEYUP:
  2435. case WM_KEYDOWN:
  2436. if (GetKeyState(VK_CONTROL) < 0) {
  2437. switch (lpmsg->wParam) {
  2438. case VK_LEFT:
  2439. case VK_RIGHT:
  2440. case VK_PRIOR:
  2441. case VK_HOME:
  2442. case VK_NEXT:
  2443. case VK_END:
  2444. case VK_UP:
  2445. case VK_DOWN:
  2446. TranslateMessage(lpmsg);
  2447. DispatchMessage(lpmsg);
  2448. return TRUE;
  2449. }
  2450. } else {
  2451. switch (lpmsg->wParam) {
  2452. case VK_RETURN:
  2453. case VK_PRIOR:
  2454. case VK_HOME:
  2455. case VK_NEXT:
  2456. case VK_END:
  2457. case VK_SUBTRACT:
  2458. case VK_ADD:
  2459. case VK_MULTIPLY:
  2460. case VK_LEFT:
  2461. case VK_BACK:
  2462. case VK_UP:
  2463. case VK_RIGHT:
  2464. case VK_DOWN:
  2465. case VK_SPACE:
  2466. TranslateMessage(lpmsg);
  2467. DispatchMessage(lpmsg);
  2468. return TRUE;
  2469. }
  2470. }
  2471. break;
  2472. }
  2473. return FALSE;
  2474. }
  2475. // ----------------------------------------------------------------------------
  2476. //
  2477. // TV_WndProc
  2478. //
  2479. // Take a guess.
  2480. //
  2481. // ----------------------------------------------------------------------------
  2482. LRESULT CALLBACK TV_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2483. {
  2484. PTREE pTree = (PTREE)GetWindowPtr(hwnd, 0);
  2485. if (pTree)
  2486. {
  2487. if ((uMsg >= WM_MOUSEFIRST) && (uMsg <= WM_MOUSELAST) &&
  2488. (pTree->ci.style & TVS_TRACKSELECT) && !pTree->fTrackSet)
  2489. {
  2490. TRACKMOUSEEVENT tme;
  2491. pTree->fTrackSet = TRUE;
  2492. tme.cbSize = sizeof(tme);
  2493. tme.hwndTrack = pTree->ci.hwnd;
  2494. tme.dwFlags = TME_LEAVE;
  2495. TrackMouseEvent(&tme);
  2496. }
  2497. else if (uMsg == g_uDragImages)
  2498. {
  2499. return TV_GenerateDragImage(pTree, (SHDRAGIMAGE*)lParam);
  2500. }
  2501. }
  2502. else
  2503. {
  2504. if (uMsg == WM_CREATE)
  2505. {
  2506. CCCreateWindow();
  2507. return TV_OnCreate(hwnd, (LPCREATESTRUCT)lParam);
  2508. }
  2509. goto DoDefault;
  2510. }
  2511. switch (uMsg)
  2512. {
  2513. case WM_MOUSELEAVE:
  2514. pTree->fTrackSet = FALSE;
  2515. TV_InvalidateItem(pTree, pTree->hHot, RDW_INVALIDATE);
  2516. pTree->hHot = NULL;
  2517. TV_PopBubble(pTree);
  2518. break;
  2519. case TVMP_CALCSCROLLBARS:
  2520. TV_CalcScrollBars(pTree);
  2521. break;
  2522. case TVM_GETITEMSTATE:
  2523. {
  2524. TVITEMEX tvi;
  2525. tvi.mask = TVIF_STATE;
  2526. tvi.stateMask = (UINT) lParam;
  2527. tvi.hItem = (HTREEITEM)wParam;
  2528. if (!TV_OnGetItem(pTree, &tvi))
  2529. return 0;
  2530. return tvi.state;
  2531. }
  2532. case TVM_SETBKCOLOR:
  2533. {
  2534. LRESULT lres = (LRESULT)pTree->clrBk;
  2535. TV_OnSetBkColor(pTree, (COLORREF)lParam);
  2536. return lres;
  2537. }
  2538. case TVM_SETTEXTCOLOR:
  2539. {
  2540. LRESULT lres = (LRESULT)pTree->clrText;
  2541. pTree->clrText = (COLORREF)lParam;
  2542. TV_CreateIndentBmps(pTree); // This also invalidates
  2543. return lres;
  2544. }
  2545. case TVM_GETBKCOLOR:
  2546. return (LRESULT)pTree->clrBk;
  2547. case TVM_GETTEXTCOLOR:
  2548. return (LRESULT)pTree->clrText;
  2549. case TVM_GETSCROLLTIME:
  2550. return (LRESULT)pTree->uMaxScrollTime;
  2551. case TVM_SETSCROLLTIME:
  2552. {
  2553. UINT u = pTree->uMaxScrollTime;
  2554. pTree->uMaxScrollTime = (UINT)wParam;
  2555. return (LRESULT)u;
  2556. }
  2557. case TVM_INSERTITEMA:
  2558. if (!lParam)
  2559. return 0;
  2560. return (LRESULT)TV_InsertItemA(pTree, (LPTV_INSERTSTRUCTA)lParam);
  2561. case TVM_GETITEMA:
  2562. if (!lParam)
  2563. return 0;
  2564. return (LRESULT)TV_OnGetItemA(pTree, (LPTVITEMEXA)lParam);
  2565. case TVM_SETITEMA:
  2566. if (!lParam)
  2567. return 0;
  2568. return (LRESULT)TV_SetItemA(pTree, (LPTVITEMEXA)lParam);
  2569. case TVM_INSERTITEM:
  2570. return (LRESULT)TV_InsertItem(pTree, (LPTV_INSERTSTRUCT)lParam);
  2571. case TVM_DELETEITEM:
  2572. // Assume if items are being deleted that name editing is invalid.
  2573. TV_DismissEdit(pTree, TRUE);
  2574. return TV_DeleteItem(pTree, (TREEITEM FAR *)lParam, TVDI_NORMAL);
  2575. case TVM_GETNEXTITEM:
  2576. return (LRESULT)TV_GetNextItem(pTree, (TREEITEM FAR *)lParam, wParam);
  2577. case TVM_GETITEMRECT:
  2578. // lParam points to hItem to get rect from on input
  2579. if (!lParam)
  2580. return 0;
  2581. if (!ValidateTreeItem(*(HTREEITEM FAR *)lParam, 0))
  2582. return 0; // Invalid parameter
  2583. return (LRESULT)TV_GetItemRect(pTree, *(HTREEITEM FAR *)lParam, (LPRECT)lParam, (BOOL)wParam);
  2584. case TVM_GETITEM:
  2585. return (LRESULT)TV_OnGetItem(pTree, (LPTVITEMEX)lParam);
  2586. case TVM_SETITEM:
  2587. return (LRESULT)TV_SetItem(pTree, (LPCTVITEMEX)lParam);
  2588. case TVM_ENSUREVISIBLE:
  2589. if (!ValidateTreeItem((HTREEITEM)lParam, 0))
  2590. return 0;
  2591. return TV_EnsureVisible(pTree, (TREEITEM FAR *)lParam);
  2592. case TVM_SETIMAGELIST:
  2593. return (LRESULT)(ULONG_PTR)TV_SetImageList(pTree, (HIMAGELIST)lParam, (int)wParam);
  2594. case TVM_EXPAND:
  2595. if (!ValidateTreeItem((HTREEITEM)lParam, 0))
  2596. return FALSE; // invalid parameter
  2597. return TV_Expand(pTree, wParam, (TREEITEM FAR *)lParam, FALSE);
  2598. case TVM_HITTEST:
  2599. return (LRESULT)TV_OnHitTest(pTree, (LPTV_HITTESTINFO)lParam);
  2600. case TVM_GETCOUNT:
  2601. return MAKELRESULTFROMUINT(pTree->cItems);
  2602. case TVM_GETIMAGELIST:
  2603. switch (wParam) {
  2604. case TVSIL_NORMAL:
  2605. return MAKELRESULTFROMUINT(pTree->hImageList);
  2606. case TVSIL_STATE:
  2607. return MAKELRESULTFROMUINT(pTree->himlState);
  2608. default:
  2609. return 0;
  2610. }
  2611. case TVM_GETISEARCHSTRINGA:
  2612. if (GetFocus() == pTree->ci.hwnd)
  2613. return (LRESULT)GetIncrementSearchStringA(&pTree->is, pTree->ci.uiCodePage, (LPSTR)lParam);
  2614. else
  2615. return 0;
  2616. case TVM_GETISEARCHSTRING:
  2617. if (GetFocus() == pTree->ci.hwnd)
  2618. return (LRESULT)GetIncrementSearchString(&pTree->is, (LPTSTR)lParam);
  2619. else
  2620. return 0;
  2621. case TVM_EDITLABELA:
  2622. {
  2623. LPWSTR lpEditString = NULL;
  2624. HWND hRet;
  2625. if (wParam) {
  2626. lpEditString = ProduceWFromA(pTree->ci.uiCodePage, (LPSTR)wParam);
  2627. }
  2628. hRet = TV_EditLabel(pTree, (HTREEITEM)lParam, lpEditString);
  2629. if (lpEditString) {
  2630. FreeProducedString(lpEditString);
  2631. }
  2632. return MAKELRESULTFROMUINT(hRet);
  2633. }
  2634. case TVM_EDITLABEL:
  2635. return MAKELRESULTFROMUINT(TV_EditLabel(pTree, (HTREEITEM)lParam,
  2636. (LPTSTR)wParam));
  2637. case TVM_GETVISIBLECOUNT:
  2638. return TV_GetVisCount(pTree, (BOOL) wParam);
  2639. case TVM_SETINDENT:
  2640. TV_SetIndent(pTree, wParam);
  2641. pTree->fIndentSet = TRUE;
  2642. break;
  2643. case TVM_GETINDENT:
  2644. return MAKELRESULTFROMUINT(pTree->cxIndent);
  2645. case TVM_CREATEDRAGIMAGE:
  2646. return MAKELRESULTFROMUINT(TV_CreateDragImage(pTree, (TREEITEM FAR *)lParam));
  2647. case TVM_GETEDITCONTROL:
  2648. return (LRESULT)(ULONG_PTR)pTree->hwndEdit;
  2649. case TVM_SORTCHILDREN:
  2650. return TV_SortChildren(pTree, (TREEITEM FAR *)lParam, (BOOL)wParam);
  2651. case TVM_SORTCHILDRENCB:
  2652. return TV_SortChildrenCB(pTree, (TV_SORTCB FAR *)lParam, (BOOL)wParam);
  2653. case TVM_SELECTITEM:
  2654. return TV_SelectItem(pTree, wParam, (TREEITEM FAR *)lParam, TVSIF_NOTIFY | TVSIF_UPDATENOW, TVC_UNKNOWN);
  2655. case TVM_ENDEDITLABELNOW:
  2656. return TV_DismissEdit(pTree, (BOOL)wParam);
  2657. case TVM_GETTOOLTIPS:
  2658. return (LRESULT)(ULONG_PTR)pTree->hwndToolTips;
  2659. case TVM_SETTOOLTIPS:{
  2660. HWND hwndOld = pTree->hwndToolTips;
  2661. pTree->hwndToolTips = (HWND)wParam;
  2662. return (LRESULT)(ULONG_PTR)hwndOld;
  2663. }
  2664. case TVM_GETITEMHEIGHT:
  2665. return pTree->cyItem;
  2666. case TVM_SETITEMHEIGHT:
  2667. {
  2668. int iOld = pTree->cyItem;
  2669. pTree->fCyItemSet = (wParam != (WPARAM)-1);
  2670. pTree->cyItem = (SHORT)wParam; // must be even
  2671. TV_SetItemHeight(pTree);
  2672. return iOld;
  2673. }
  2674. case TVM_SETBORDER:
  2675. {
  2676. int cyOld = pTree->cyBorder
  2677. , cxOld = pTree->cxBorder;
  2678. if (wParam & TVSBF_YBORDER)
  2679. pTree->cyBorder = HIWORD(lParam);
  2680. if (wParam & TVSBF_XBORDER)
  2681. pTree->cxBorder = LOWORD(lParam);
  2682. TV_CalcScrollBars(pTree);
  2683. return MAKELONG(cxOld, cyOld);
  2684. }
  2685. case TVM_GETBORDER:
  2686. return MAKELONG(pTree->cxBorder, pTree->cyBorder);
  2687. case TVM_SETINSERTMARK:
  2688. return TV_SetInsertMark(pTree, (TREEITEM FAR *)lParam, (BOOL) wParam);
  2689. case TVM_SETINSERTMARKCOLOR:
  2690. {
  2691. LRESULT lres = (LRESULT)pTree->clrim;
  2692. pTree->clrim = (COLORREF) lParam;
  2693. TV_InvalidateInsertMarkRect(pTree, FALSE); // Repaint in new color
  2694. return lres;
  2695. }
  2696. case TVM_GETINSERTMARKCOLOR:
  2697. return pTree->clrim;
  2698. case TVM_TRANSLATEACCELERATOR:
  2699. return TV_TranslateAccelerator(hwnd, (LPMSG)lParam);
  2700. case TVM_SETLINECOLOR:
  2701. {
  2702. LRESULT lres = (LRESULT)pTree->clrLine;
  2703. pTree->clrLine = (COLORREF)lParam;
  2704. TV_CreateIndentBmps(pTree); // This also invalidates
  2705. return lres;
  2706. }
  2707. case TVM_GETLINECOLOR:
  2708. return (LRESULT)pTree->clrLine;
  2709. #if defined(FE_IME)
  2710. case WM_IME_COMPOSITION:
  2711. // Now only Korean version is interested in incremental search with composition string.
  2712. if (g_fDBCSInputEnabled) {
  2713. if (((ULONG_PTR)GetKeyboardLayout(0L) & 0xF000FFFFL) == 0xE0000412L)
  2714. {
  2715. if (TV_OnImeComposition(pTree, wParam, lParam))
  2716. {
  2717. lParam &= ~GCS_RESULTSTR;
  2718. goto DoDefault;
  2719. }
  2720. else
  2721. break;
  2722. }
  2723. }
  2724. goto DoDefault;
  2725. #endif
  2726. case WM_CHAR:
  2727. if (pTree->iPuntChar) {
  2728. pTree->iPuntChar--;
  2729. return TRUE;
  2730. } else {
  2731. return HANDLE_WM_CHAR(pTree, wParam, lParam, TV_OnChar);
  2732. }
  2733. case WM_DESTROY:
  2734. CCDestroyWindow();
  2735. TV_DestroyTree(pTree);
  2736. break;
  2737. case WM_SETCURSOR:
  2738. {
  2739. NMMOUSE nm;
  2740. HTREEITEM hItem;
  2741. nm.dwHitInfo = lParam;
  2742. hItem = TV_ItemAtCursor(pTree, NULL);
  2743. if(hItem)
  2744. {
  2745. nm.dwItemSpec = (ULONG_PTR)hItem;
  2746. nm.dwItemData = (ULONG_PTR)(hItem->lParam);
  2747. }
  2748. else
  2749. {
  2750. nm.dwItemSpec = 0;
  2751. nm.dwItemData = 0;
  2752. }
  2753. if (CCSendNotify(&pTree->ci, NM_SETCURSOR, &nm.hdr))
  2754. {
  2755. return 0;
  2756. }
  2757. }
  2758. if (pTree->ci.style & TVS_TRACKSELECT) {
  2759. if (pTree->hHot) {
  2760. if (!pTree->hCurHot)
  2761. pTree->hCurHot = LoadHandCursor(0);
  2762. SetCursor(pTree->hCurHot);
  2763. return TRUE;
  2764. }
  2765. }
  2766. goto DoDefault;
  2767. break;
  2768. case WM_WININICHANGE:
  2769. TV_OnWinIniChange(pTree, wParam);
  2770. break;
  2771. case WM_STYLECHANGED:
  2772. TV_OnStyleChanged(pTree, wParam, (LPSTYLESTRUCT)lParam);
  2773. break;
  2774. case WM_SETREDRAW:
  2775. TV_OnSetRedraw(pTree, (BOOL)wParam);
  2776. break;
  2777. case WM_PRINTCLIENT:
  2778. case WM_PAINT:
  2779. TV_Paint(pTree, (HDC)wParam);
  2780. break;
  2781. case WM_ERASEBKGND:
  2782. {
  2783. RECT rc;
  2784. TV_GetBackgroundBrush(pTree, (HDC) wParam);
  2785. GetClipBox((HDC) wParam, &rc);
  2786. FillRect((HDC)wParam, &rc, pTree->hbrBk);
  2787. }
  2788. return TRUE;
  2789. case WM_GETDLGCODE:
  2790. return (LRESULT) (DLGC_WANTARROWS | DLGC_WANTCHARS);
  2791. case WM_HSCROLL:
  2792. TV_HorzScroll(pTree, GET_WM_HSCROLL_CODE(wParam, lParam), GET_WM_HSCROLL_POS(wParam, lParam));
  2793. break;
  2794. case WM_VSCROLL:
  2795. TV_VertScroll(pTree, GET_WM_VSCROLL_CODE(wParam, lParam), GET_WM_VSCROLL_POS(wParam, lParam));
  2796. break;
  2797. case WM_KEYDOWN:
  2798. if (TV_KeyDown(pTree, wParam, lParam))
  2799. IncrementSearchString(&pTree->is, 0, NULL);
  2800. goto DoDefault;
  2801. case WM_LBUTTONDBLCLK:
  2802. case WM_LBUTTONDOWN:
  2803. TV_ButtonDown(pTree, uMsg, (UINT) wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
  2804. break;
  2805. case WM_KILLFOCUS:
  2806. // Reset wheel scroll amount
  2807. gcWheelDelta = 0;
  2808. pTree->fFocus = FALSE;
  2809. if (pTree->hCaret)
  2810. {
  2811. TV_InvalidateItem(pTree, pTree->hCaret, RDW_INVALIDATE);
  2812. UpdateWindow(pTree->ci.hwnd);
  2813. }
  2814. CCSendNotify(&pTree->ci, NM_KILLFOCUS, NULL);
  2815. IncrementSearchString(&pTree->is, 0, NULL);
  2816. break;
  2817. case WM_SETFOCUS:
  2818. ASSERT(gcWheelDelta == 0);
  2819. pTree->fFocus = TRUE;
  2820. if (pTree->hCaret)
  2821. {
  2822. TV_InvalidateItem(pTree, pTree->hCaret, RDW_INVALIDATE);
  2823. MyNotifyWinEvent(EVENT_OBJECT_FOCUS, hwnd, OBJID_CLIENT, (LONG_PTR)pTree->hCaret);
  2824. }
  2825. else
  2826. TV_SelectItem(pTree, TVGN_CARET, pTree->hTop, TVSIF_NOTIFY | TVSIF_UPDATENOW, TVC_INTERNAL);
  2827. CCSendNotify(&pTree->ci, NM_SETFOCUS, NULL);
  2828. break;
  2829. case WM_GETFONT:
  2830. return MAKELRESULTFROMUINT(pTree->hFont);
  2831. case WM_SETFONT:
  2832. TV_OnSetFont(pTree, (HFONT) wParam, (BOOL) lParam);
  2833. break;
  2834. case WM_SIZE:
  2835. TV_SizeWnd(pTree, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  2836. break;
  2837. case WM_ENABLE:
  2838. // HACK: we don't get WM_STYLECHANGE on EnableWindow()
  2839. if (wParam)
  2840. pTree->ci.style &= ~WS_DISABLED; // enabled
  2841. else
  2842. pTree->ci.style |= WS_DISABLED; // disabled
  2843. TV_CreateIndentBmps(pTree); // This invalidates the whole window!
  2844. break;
  2845. case WM_SYSCOLORCHANGE:
  2846. InitGlobalColors();
  2847. TV_CreateIndentBmps(pTree); // This invalidates the whole window!
  2848. break;
  2849. case WM_RBUTTONDOWN:
  2850. TV_SendRButtonDown(pTree, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  2851. break;
  2852. case WM_TIMER:
  2853. TV_Timer(pTree, (UINT) wParam);
  2854. break;
  2855. case WM_MOUSEMOVE:
  2856. TV_OnMouseMove(pTree, (DWORD) lParam, wParam);
  2857. break;
  2858. case WM_COMMAND:
  2859. TV_Command(pTree, (int)GET_WM_COMMAND_ID(wParam, lParam), GET_WM_COMMAND_HWND(wParam, lParam),
  2860. (UINT)GET_WM_COMMAND_CMD(wParam, lParam));
  2861. break;
  2862. case WM_NOTIFY: {
  2863. LPNMHDR lpnm = (LPNMHDR)lParam;
  2864. if ((lpnm->code <= PGN_FIRST) && (PGN_LAST <= lpnm->code)) {
  2865. LRESULT TV_OnPagerControlNotify(PTREE pTree, LPNMHDR pnm);
  2866. return TV_OnPagerControlNotify(pTree, lpnm);
  2867. }
  2868. if (lpnm->hwndFrom == pTree->hwndToolTips) {
  2869. switch (lpnm->code) {
  2870. case TTN_NEEDTEXT:
  2871. TV_HandleNeedText(pTree, (LPTOOLTIPTEXT)lpnm);
  2872. break;
  2873. case TTN_NEEDTEXTA:
  2874. TV_HandleNeedTextA(pTree, (LPTOOLTIPTEXTA)lpnm);
  2875. break;
  2876. case TTN_SHOW:
  2877. return TV_HandleTTNShow(pTree, lpnm);
  2878. case NM_CUSTOMDRAW:
  2879. return TV_HandleTTCustomDraw(pTree, (LPNMTTCUSTOMDRAW)lpnm);
  2880. }
  2881. }
  2882. break;
  2883. }
  2884. case WM_NOTIFYFORMAT:
  2885. return CIHandleNotifyFormat(&pTree->ci, lParam);
  2886. case WM_MBUTTONDOWN:
  2887. SetFocus(hwnd);
  2888. goto DoDefault;
  2889. case WM_GETOBJECT:
  2890. if( lParam == OBJID_QUERYCLASSNAMEIDX )
  2891. return MSAA_CLASSNAMEIDX_TREEVIEW;
  2892. goto DoDefault;
  2893. case WM_UPDATEUISTATE:
  2894. {
  2895. DWORD dwUIStateMask = MAKEWPARAM(0xFFFF, UISF_HIDEFOCUS);
  2896. if (CCOnUIState(&(pTree->ci), WM_UPDATEUISTATE, wParam & dwUIStateMask, lParam))
  2897. if (pTree->hCaret)
  2898. TV_InvalidateItem(pTree, pTree->hCaret, TRUE);
  2899. goto DoDefault;
  2900. }
  2901. case WM_SYSKEYDOWN:
  2902. TV_KeyDown(pTree, wParam, lParam);
  2903. //fall through
  2904. default:
  2905. // Special handling of magellan mouse message
  2906. if (uMsg == g_msgMSWheel) {
  2907. BOOL fScroll;
  2908. BOOL fDataZoom;
  2909. DWORD dwStyle;
  2910. int cScrollLines;
  2911. int cPage;
  2912. int pos;
  2913. int cDetants;
  2914. int iWheelDelta;
  2915. if (g_bRunOnNT || g_bRunOnMemphis)
  2916. {
  2917. iWheelDelta = (int)(short)HIWORD(wParam);
  2918. fScroll = !(wParam & (MK_SHIFT | MK_CONTROL));
  2919. fDataZoom = (BOOL) (wParam & MK_SHIFT);
  2920. }
  2921. else
  2922. {
  2923. iWheelDelta = (int)wParam;
  2924. fDataZoom = (GetKeyState(VK_SHIFT) < 0);
  2925. fScroll = !fDataZoom && GetKeyState(VK_CONTROL) >= 0;
  2926. }
  2927. // Update count of scroll amount
  2928. gcWheelDelta -= iWheelDelta;
  2929. cDetants = gcWheelDelta / WHEEL_DELTA;
  2930. if (cDetants != 0) {
  2931. gcWheelDelta %= WHEEL_DELTA;
  2932. }
  2933. if (fScroll) {
  2934. if ( g_ucScrollLines > 0 &&
  2935. cDetants != 0 &&
  2936. (WS_VSCROLL | WS_HSCROLL) & (dwStyle = GetWindowStyle(hwnd))) {
  2937. if (dwStyle & WS_VSCROLL) {
  2938. cPage = max(1, (pTree->cFullVisible - 1));
  2939. cScrollLines =
  2940. cDetants *
  2941. min(g_ucScrollLines, (UINT) cPage);
  2942. pos = max(0, pTree->hTop->iShownIndex + cScrollLines);
  2943. TV_VertScroll(pTree, SB_THUMBPOSITION, pos);
  2944. } else {
  2945. cPage = max(MAGIC_HORZLINE,
  2946. (pTree->cxWnd - MAGIC_HORZLINE)) /
  2947. MAGIC_HORZLINE;
  2948. cScrollLines =
  2949. cDetants *
  2950. (int) min((ULONG) cPage, g_ucScrollLines) *
  2951. MAGIC_HORZLINE;
  2952. pos = max(0, pTree->xPos + cScrollLines);
  2953. TV_HorzScroll(pTree, SB_THUMBPOSITION, pos);
  2954. }
  2955. }
  2956. return 1;
  2957. } else if (fDataZoom) {
  2958. UINT wHitCode;
  2959. POINT pt;
  2960. pt.x = GET_X_LPARAM(lParam);
  2961. pt.y = GET_Y_LPARAM(lParam);
  2962. ScreenToClient(hwnd, &pt);
  2963. // If we are rolling forward and hit an item then navigate into that
  2964. // item or expand tree (simulate lbuttondown which will do it). We
  2965. // also need to handle rolling backwards over the ITEMBUTTON so
  2966. // that we can collapse the tree in that case. Otherwise
  2967. // just fall through so it isn't handled. In that case if we
  2968. // are being hosted in explorer it will do a backwards
  2969. // history navigation.
  2970. if (TV_CheckHit(pTree, pt.x, pt.y, &wHitCode) &&
  2971. (wHitCode & (TVHT_ONITEM | TVHT_ONITEMBUTTON))) {
  2972. UINT uFlags = TVBD_FROMWHEEL;
  2973. uFlags |= (iWheelDelta > 0) ? TVBD_WHEELFORWARD : TVBD_WHEELBACK;
  2974. if ((uFlags & TVBD_WHEELFORWARD) || (wHitCode == TVHT_ONITEMBUTTON)) {
  2975. TV_ButtonDown(pTree, WM_LBUTTONDOWN, 0, pt.x, pt.y, uFlags);
  2976. return 1;
  2977. }
  2978. }
  2979. // else fall through
  2980. }
  2981. } else {
  2982. LRESULT lres;
  2983. if (CCWndProc(&pTree->ci, uMsg, wParam, lParam, &lres))
  2984. return lres;
  2985. }
  2986. DoDefault:
  2987. return(DefWindowProc(hwnd, uMsg, wParam, lParam));
  2988. }
  2989. return(0L);
  2990. }
  2991. // NOTE: there is very similar code in the listview
  2992. //
  2993. // Totally disgusting hack in order to catch VK_RETURN
  2994. // before edit control gets it.
  2995. //
  2996. LRESULT CALLBACK TV_EditWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  2997. {
  2998. PTREE pTree = (PTREE)GetWindowInt(GetParent(hwnd), 0);
  2999. ASSERT(pTree);
  3000. if (!pTree)
  3001. return 0L; // wierd cases can get here...
  3002. switch (msg) {
  3003. case WM_KEYDOWN:
  3004. switch (wParam) {
  3005. case VK_RETURN:
  3006. TV_DismissEdit(pTree, FALSE);
  3007. return 0L;
  3008. case VK_ESCAPE:
  3009. TV_DismissEdit(pTree, TRUE);
  3010. return 0L;
  3011. }
  3012. break;
  3013. case WM_CHAR:
  3014. switch (wParam) {
  3015. case VK_RETURN:
  3016. // Eat the character, so edit control wont beep!
  3017. return 0L;
  3018. }
  3019. }
  3020. return CallWindowProc(pTree->pfnEditWndProc, hwnd, msg, wParam, lParam);
  3021. }
  3022. void NEAR TV_SetEditSize(PTREE pTree)
  3023. {
  3024. RECT rcLabel;
  3025. UINT seips;
  3026. if (pTree->htiEdit == NULL)
  3027. return;
  3028. TV_GetItemRect(pTree, pTree->htiEdit, &rcLabel, TRUE);
  3029. // get exact the text bounds (acount for borders used when drawing)
  3030. InflateRect(&rcLabel, -g_cxLabelMargin, -g_cyBorder);
  3031. seips = 0;
  3032. #ifdef DEBUG
  3033. // If we are in one of the no-scroll modes then it's possible for the
  3034. // resulting rectangle not to be visible. Similarly, if the item itself
  3035. // isn't visible, then the resulting rectangle is definitely not visible.
  3036. // Tell SetEditInPlaceSize not to get upset in those cases.
  3037. if ((pTree->ci.style & (TVS_NOSCROLL | TVS_NOHSCROLL)) ||
  3038. !ITEM_VISIBLE(pTree->htiEdit))
  3039. seips |= SEIPS_NOSCROLL;
  3040. #endif
  3041. SetEditInPlaceSize(pTree->hwndEdit, &rcLabel, (HFONT)SendMessage(pTree->hwndEdit, WM_GETFONT, 0, 0), seips);
  3042. }
  3043. void NEAR TV_CancelEditTimer(PTREE pTree)
  3044. {
  3045. if (pTree->fNameEditPending)
  3046. {
  3047. KillTimer(pTree->ci.hwnd, IDT_NAMEEDIT);
  3048. pTree->fNameEditPending = FALSE;
  3049. }
  3050. }
  3051. // BUGBUG: very similar code in lvicon.c
  3052. HWND NEAR TV_EditLabel(PTREE pTree, HTREEITEM hItem, LPTSTR pszInitial)
  3053. {
  3054. TCHAR szLabel[MAXLABELTEXT];
  3055. TV_DISPINFO nm;
  3056. if (!(pTree->ci.style & TVS_EDITLABELS))
  3057. return NULL;
  3058. if (!ValidateTreeItem(hItem, 0))
  3059. return NULL;
  3060. TV_DismissEdit(pTree, FALSE);
  3061. // Now get the text associated with that item
  3062. nm.item.pszText = szLabel;
  3063. nm.item.cchTextMax = ARRAYSIZE(szLabel);
  3064. nm.item.stateMask = TVIS_BOLD;
  3065. // this cast is ok as long as TVIF_INTEGRAL or anything past it isn't asked for
  3066. TV_GetItem(pTree, hItem, TVIF_TEXT | TVIF_STATE, (LPTVITEMEX)&nm.item);
  3067. // Must subtract one from ARRAYSIZE(szLabel) because Edit_LimitText
  3068. // doesn't include the terminating NULL
  3069. pTree->hwndEdit = CreateEditInPlaceWindow(pTree->ci.hwnd,
  3070. pszInitial? pszInitial : nm.item.pszText, ARRAYSIZE(szLabel) - 1,
  3071. WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD | ES_LEFT | ES_AUTOHSCROLL,
  3072. (nm.item.state & TVIS_BOLD) ? pTree->hFontBold : pTree->hFont);
  3073. if (pTree->hwndEdit) {
  3074. if (pszInitial) // if initialized, it's dirty.
  3075. SetWindowID(pTree->hwndEdit, 1);
  3076. //
  3077. // Now notify the parent of this window and see if they want it.
  3078. // We do it after we cretae the window, but before we show it
  3079. // such that our parent can query for it and do things like limit
  3080. // the number of characters that are input
  3081. nm.item.hItem = hItem;
  3082. nm.item.state = hItem->state;
  3083. nm.item.lParam = hItem->lParam;
  3084. nm.item.mask = (TVIF_HANDLE | TVIF_STATE | TVIF_PARAM | TVIF_TEXT);
  3085. if ((BOOL)CCSendNotify(&pTree->ci, TVN_BEGINLABELEDIT, &nm.hdr))
  3086. {
  3087. DestroyWindow(pTree->hwndEdit);
  3088. pTree->hwndEdit = NULL;
  3089. return NULL;
  3090. }
  3091. TV_PopBubble(pTree);
  3092. TV_ScrollIntoView(pTree, hItem);
  3093. pTree->pfnEditWndProc = SubclassWindow(pTree->hwndEdit, TV_EditWndProc);
  3094. pTree->htiEdit = hItem;
  3095. TV_SetEditSize(pTree);
  3096. // Show the window and set focus to it. Do this after setting the
  3097. // size so we don't get flicker.
  3098. SetFocus(pTree->hwndEdit);
  3099. ShowWindow(pTree->hwndEdit, SW_SHOW);
  3100. TV_InvalidateItem(pTree, hItem, RDW_INVALIDATE | RDW_ERASE);
  3101. RescrollEditWindow(pTree->hwndEdit);
  3102. }
  3103. return pTree->hwndEdit;
  3104. }
  3105. // BUGBUG: very similar code in lvicon.c
  3106. BOOL NEAR TV_DismissEdit(PTREE pTree, BOOL fCancel)
  3107. {
  3108. HWND hwndEdit;
  3109. BOOL fOkToContinue = TRUE;
  3110. HTREEITEM htiEdit;
  3111. if (pTree->fNoDismissEdit)
  3112. return FALSE;
  3113. hwndEdit = pTree->hwndEdit;
  3114. if (!hwndEdit) {
  3115. // Also make sure there are no pending edits...
  3116. TV_CancelEditTimer(pTree);
  3117. return TRUE;
  3118. }
  3119. // Assume that if we are not visible that the window is in the
  3120. // process of being destroyed and we should not process the
  3121. // editing of the window...
  3122. if (!IsWindowVisible(pTree->ci.hwnd))
  3123. fCancel = TRUE;
  3124. //
  3125. // We are using the Window ID of the control as a BOOL to
  3126. // state if it is dirty or not.
  3127. switch (GetWindowID(hwndEdit)) {
  3128. case 0:
  3129. // The edit control is not dirty so act like cancel.
  3130. fCancel = TRUE;
  3131. // FALL THROUGH
  3132. case 1:
  3133. // The edit control is dirty so continue.
  3134. SetWindowID(hwndEdit, 2); // Don't recurse
  3135. break;
  3136. case 2:
  3137. // We are in the process of processing an update now, bail out
  3138. return TRUE;
  3139. }
  3140. // TV_DeleteItemRecurse will set htiEdit to NULL if the program
  3141. // deleted the items out from underneath us (while we are waiting
  3142. // for the edit timer).
  3143. htiEdit = pTree->htiEdit;
  3144. if (htiEdit != NULL)
  3145. {
  3146. TV_DISPINFO nm;
  3147. TCHAR szLabel[MAXLABELTEXT];
  3148. DBG_ValidateTreeItem(htiEdit, 0);
  3149. // Initialize notification message.
  3150. nm.item.hItem = htiEdit;
  3151. nm.item.lParam = htiEdit->lParam;
  3152. nm.item.mask = 0;
  3153. if (fCancel)
  3154. nm.item.pszText = NULL;
  3155. else {
  3156. Edit_GetText(hwndEdit, szLabel, ARRAYSIZE(szLabel));
  3157. nm.item.pszText = szLabel;
  3158. nm.item.cchTextMax = ARRAYSIZE(szLabel);
  3159. nm.item.mask |= TVIF_TEXT;
  3160. }
  3161. // Make sure the text redraws properly
  3162. TV_InvalidateItem(pTree, htiEdit, RDW_INVALIDATE | RDW_ERASE);
  3163. pTree->fNoDismissEdit = TRUE; // this is so that we don't recurse due to killfocus
  3164. ShowWindow(hwndEdit, SW_HIDE);
  3165. pTree->fNoDismissEdit = FALSE;
  3166. //
  3167. // Notify the parent that we the label editing has completed.
  3168. // We will use the LV_DISPINFO structure to return the new
  3169. // label in. The parent still has the old text available by
  3170. // calling the GetItemText function.
  3171. //
  3172. fOkToContinue = (BOOL)CCSendNotify(&pTree->ci, TVN_ENDLABELEDIT, &nm.hdr);
  3173. if (fOkToContinue && !fCancel)
  3174. {
  3175. // BUGBUG raymondc: The caller might have deleted the item in
  3176. // response to the edit. We should revalidate here (or make
  3177. // delete item invalidate our edit item). Treat a deletion
  3178. // as if it were a rejected edit.
  3179. //
  3180. // If the item has the text set as CALLBACK, we will let the
  3181. // ower know that they are supposed to set the item text in
  3182. // their own data structures. Else we will simply update the
  3183. // text in the actual view.
  3184. //
  3185. // Note: The callee may have set the handle to null to tell
  3186. // us that the handle to item is no longer valid.
  3187. if (nm.item.hItem != NULL)
  3188. {
  3189. if (htiEdit->lpstr != LPSTR_TEXTCALLBACK)
  3190. {
  3191. // Set the item text (everything's set up in nm.item)
  3192. //
  3193. nm.item.mask = TVIF_TEXT;
  3194. TV_SetItem(pTree, (LPTVITEMEX)&nm.item);
  3195. }
  3196. else
  3197. {
  3198. CCSendNotify(&pTree->ci, TVN_SETDISPINFO, &nm.hdr);
  3199. }
  3200. }
  3201. }
  3202. }
  3203. // If we did not reenter edit mode before now reset the edit state
  3204. // variables to NULL
  3205. if (hwndEdit == pTree->hwndEdit)
  3206. {
  3207. pTree->htiEdit = NULL;
  3208. pTree->hwndEdit = NULL; // so we don't get reentered on the kill focus
  3209. }
  3210. // done with the edit control
  3211. DestroyWindow(hwndEdit);
  3212. return fOkToContinue;
  3213. }
  3214. LRESULT TV_OnCalcSize(PTREE pTree, LPNMHDR pnm)
  3215. {
  3216. LPNMPGCALCSIZE pcalcsize = (LPNMPGCALCSIZE)pnm;
  3217. switch(pcalcsize->dwFlag) {
  3218. case PGF_CALCHEIGHT:
  3219. pcalcsize->iHeight = pTree->cShowing * pTree->cyItem;
  3220. TraceMsg(TF_WARNING, "tv.PGF_CALCHEIGHT: cShow=%d cShow*cyItem=%d AWR()=%d",
  3221. pTree->cShowing, pTree->cShowing * pTree->cyItem, pcalcsize->iHeight);
  3222. break;
  3223. case PGF_CALCWIDTH:
  3224. break;
  3225. }
  3226. return 0L;
  3227. }
  3228. LRESULT TV_OnPagerControlNotify(PTREE pTree, LPNMHDR pnm)
  3229. {
  3230. switch(pnm->code) {
  3231. case PGN_SCROLL:
  3232. return TV_OnScroll(pTree, pnm);
  3233. break;
  3234. case PGN_CALCSIZE:
  3235. return TV_OnCalcSize(pTree, pnm);
  3236. break;
  3237. }
  3238. return 0L;
  3239. }
  3240. LRESULT TV_OnScroll(PTREE pTree, LPNMHDR pnm)
  3241. {
  3242. LPNMPGSCROLL pscroll = (LPNMPGSCROLL)pnm;
  3243. RECT rc = pscroll->rcParent;
  3244. RECT rcTemp;
  3245. int iDir = pscroll->iDir;
  3246. int dyScroll = pscroll->iScroll;
  3247. TREEITEM FAR * hItem;
  3248. UINT uCode;
  3249. int parentsize;
  3250. TREEITEM FAR * hPrevItem;
  3251. TREEITEM FAR * hNextItem;
  3252. int y;
  3253. POINT pt = {pscroll->iXpos, pscroll->iYpos};
  3254. POINT ptTemp = pt;
  3255. TREEITEM FAR * hCurrentItem = TV_CheckHit(pTree, pt.x + 1, pt.y + 1 , &uCode);
  3256. switch(iDir)
  3257. {
  3258. case PGF_SCROLLUP:
  3259. //Check if any Item is partially visible at the left/top. if so then set the bottom
  3260. // of that Item to be our current offset and then scroll. This avoids skipping over
  3261. // certain Items when partial Items are displayed at the left or top
  3262. y = pt.y;
  3263. TV_GetItemRect(pTree,hCurrentItem,&rcTemp, TRUE);
  3264. if (rcTemp.top < y-1)
  3265. {
  3266. hCurrentItem =TV_GetNextItem(pTree,hCurrentItem,TVGN_NEXTVISIBLE);
  3267. }
  3268. // Now do the calculation
  3269. parentsize = RECTHEIGHT(rc);
  3270. //if the control key is down and we have more than parentsize size of child window
  3271. // then scroll by that amount
  3272. if ((pscroll->fwKeys & PGK_CONTROL) && ((pt.y - parentsize) > 0))
  3273. {
  3274. dyScroll = parentsize;
  3275. } else if ((pt.y - pTree->cyItem) > 0) {
  3276. // we dont have control key down so scroll by one buttonsize
  3277. dyScroll = pTree->cyItem;
  3278. } else {
  3279. pscroll->iScroll = pt.y;
  3280. return 0L;
  3281. }
  3282. ptTemp.y -= dyScroll;
  3283. hItem = TV_CheckHit(pTree, ptTemp.x, ptTemp.y, &uCode);
  3284. if (hItem)
  3285. {
  3286. // if the hit test gives us the same Item as our CurrentItem then set the Item
  3287. // to one Item to the top/left of the CurrentItem
  3288. hPrevItem = TV_GetNextItem(pTree,hCurrentItem, TVGN_PREVIOUSVISIBLE);
  3289. if ((hItem == hCurrentItem) && ( hPrevItem != NULL))
  3290. {
  3291. hItem = hPrevItem;
  3292. }
  3293. //When scrolling left if we end up in the middle of some Item then we align it to the
  3294. //right of that Item this is to avoid scrolling more than the pager window width but if the
  3295. // Item happens to be the left Item of our current Item then we end up in not scrolling
  3296. //if thats the case then move one more Item to the left.
  3297. if (hItem == hPrevItem)
  3298. {
  3299. hItem = TV_GetNextItem(pTree, hItem, TVGN_PREVIOUSVISIBLE);
  3300. if(!hItem)
  3301. {
  3302. dyScroll = pt.y;
  3303. break;
  3304. }
  3305. }
  3306. TV_GetItemRect(pTree,hItem,&rcTemp, TRUE);
  3307. dyScroll = pt.y - rcTemp.bottom;
  3308. }
  3309. break;
  3310. case PGF_SCROLLDOWN:
  3311. {
  3312. RECT rcChild;
  3313. int childsize;
  3314. GetWindowRect(pTree->ci.hwnd, &rcChild);
  3315. childsize = RECTHEIGHT(rcChild);
  3316. parentsize = RECTHEIGHT(rc);
  3317. //if the control key is down and we have more than parentsize size of child window
  3318. // then scroll by that amount
  3319. if ((pscroll->fwKeys & PGK_CONTROL) && ((childsize - pt.y - parentsize) > parentsize))
  3320. {
  3321. dyScroll = parentsize;
  3322. } else if ( (childsize - pt.y - parentsize) > (pTree->cyItem * hCurrentItem->iIntegral) ) {
  3323. // we dont have control key down so scroll by one buttonsize
  3324. dyScroll = pTree->cyItem * hCurrentItem->iIntegral;
  3325. } else {
  3326. pscroll->iScroll = childsize - pt.y - parentsize;
  3327. return 0L;
  3328. }
  3329. ptTemp.y += dyScroll;
  3330. hItem = TV_CheckHit(pTree, ptTemp.x, ptTemp.y, &uCode);
  3331. if (hItem)
  3332. {
  3333. if ((hItem == hCurrentItem) &&
  3334. ((hNextItem = TV_GetNextItem(pTree,hItem,TVGN_NEXTVISIBLE)) != NULL))
  3335. {
  3336. hItem = hNextItem;
  3337. }
  3338. TV_GetItemRect(pTree, hItem, &rcTemp, TRUE);
  3339. dyScroll = rcTemp.top - pt.y ;
  3340. }
  3341. break;
  3342. }
  3343. }
  3344. //Set the scroll value
  3345. pscroll->iScroll = dyScroll;
  3346. return 0L;
  3347. }