Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4207 lines
128 KiB

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