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.

4334 lines
134 KiB

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