XTTreeBase.cpp
上传用户:szled88
上传日期:2015-04-09
资源大小:43957k
文件大小:47k
源码类别:

对话框与窗口

开发平台:

Visual C++

  1. // XTTreeBase.cpp : implementation file
  2. //
  3. // This file is a part of the XTREME CONTROLS MFC class library.
  4. // (c)1998-2008 Codejock Software, All Rights Reserved.
  5. //
  6. // THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
  7. // RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
  8. // CONSENT OF CODEJOCK SOFTWARE.
  9. //
  10. // THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
  11. // IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
  12. // YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
  13. // SINGLE COMPUTER.
  14. //
  15. // CONTACT INFORMATION:
  16. // support@codejock.com
  17. // http://www.codejock.com
  18. //
  19. /////////////////////////////////////////////////////////////////////////////
  20. #include "stdafx.h"
  21. #include <windowsx.h>
  22. #include "Common/XTPColorManager.h"
  23. #include "Common/XTPDrawHelpers.h"
  24. #include "Common/XTPSystemHelpers.h"
  25. #include "XTVC50Helpers.h"
  26. #include "XTTreeBase.h"
  27. #ifdef _DEBUG
  28. #define new DEBUG_NEW
  29. #undef THIS_FILE
  30. static char THIS_FILE[] = __FILE__;
  31. #endif
  32. #ifndef SCROLL_TIMER_PERIOD
  33. #define SCROLL_TIMER_PERIOD 75
  34. #endif
  35. #ifndef HOVER_TIMER_PERIOD
  36. #define HOVER_TIMER_PERIOD  85
  37. #endif
  38. #ifndef COLOR_HOTLIGHT
  39. #define COLOR_HOTLIGHT      26
  40. #endif // COLOR_HOTLIGHT
  41. /////////////////////////////////////////////////////////////////////////////
  42. // CXTTreeBase
  43. CXTTreeBase::CXTTreeBase()
  44. : m_hSelect(NULL)
  45. , m_bMultiSelect(false)
  46. , m_bBandLabel(true)
  47. , m_bActionDone(false)
  48. , m_bOkToEdit(true)
  49. , m_htiEdit(NULL)
  50. , m_htiLast(NULL)
  51. {
  52. m_pTreeCtrl = NULL;
  53. }
  54. CXTTreeBase::~CXTTreeBase()
  55. {
  56. }
  57. /////////////////////////////////////////////////////////////////////////////
  58. // CXTTreeBase message handlers
  59. void CXTTreeBase::OnLButtonDown(UINT nFlags, CPoint point)
  60. {
  61. // If multiselect control, process possible left
  62. // click drag selection.
  63. if (m_bMultiSelect)
  64. {
  65. UINT nHitFlags = 0;
  66. HTREEITEM hItemHit = m_pTreeCtrl->HitTest(point, &nHitFlags);
  67. HTREEITEM hItemSel = m_pTreeCtrl->GetSelectedItem();
  68. // if expanding/contracting call base class.
  69. if ((nHitFlags & (TVHT_ONITEMBUTTON | TVHT_ONITEMSTATEICON)) != 0)
  70. {
  71. m_pTreeCtrl->Default();
  72. return;
  73. }
  74. if (HasEditLabels())
  75. {
  76. if (!(nFlags & (MK_CONTROL | MK_SHIFT)) && (nHitFlags & TVHT_ONITEMLABEL))
  77. {
  78. if (m_bOkToEdit && hItemHit == hItemSel)
  79. {
  80. SelectAll(FALSE);
  81. SelectItem(hItemHit);
  82. m_pTreeCtrl->Default();
  83. return;
  84. }
  85. }
  86. if ((nHitFlags & TVHT_ONITEM) == 0)
  87. {
  88. m_bOkToEdit = false;
  89. }
  90. else if (GetFocusedItem() == hItemSel)
  91. {
  92. m_bOkToEdit = true;
  93. }
  94. }
  95. OnButtonDown(TRUE, nFlags, point);
  96. }
  97. else
  98. {
  99. m_pTreeCtrl->Default();
  100. }
  101. }
  102. void CXTTreeBase::OnRButtonDown(UINT /*nFlags*/, CPoint point)
  103. {
  104. if (m_pTreeCtrl->GetStyle() & TVS_SINGLEEXPAND)
  105. {
  106. m_pTreeCtrl->Default();
  107. return;
  108. }
  109. // hittest to get the tree item under the cursor
  110. // and select it.
  111. UINT uFlags = 0;
  112. HTREEITEM hItem = m_pTreeCtrl->HitTest(point, &uFlags);
  113. if (hItem != NULL && (uFlags & TVHT_ONITEM) != 0)
  114. {
  115. // if the item is not selected, clear previous
  116. // selections and select the item under cursor.
  117. if (!IsSelected(hItem))
  118. {
  119. SelectAll(FALSE);
  120. SelectItem(hItem);
  121. }
  122. }
  123. else
  124. {
  125. // clear previous selections.
  126. SelectAll(FALSE);
  127. }
  128. // call Default() so correct notification messages are
  129. // sent such as TVN_BEGINRDRAG.
  130. m_pTreeCtrl->Default();
  131. // get the owner of the tree control.
  132. //HWND hWnd = m_pTreeCtrl->GetOwner()->m_hWnd;
  133. //if (::IsWindow(hWnd))
  134. //{
  135. // construct a NMHDR struct...
  136. //NMHDR mHDR;
  137. //mHDR.hwndFrom = m_pTreeCtrl->m_hWnd;
  138. //mHDR.code = NM_RCLICK;
  139. //mHDR.idFrom = m_pTreeCtrl->GetDlgCtrlID();
  140. // and send a WM_NOTIFY message to our owner.
  141. //SendNotify(&mHDR);
  142. //}
  143. }
  144. void CXTTreeBase::OnSetFocus(CWnd* /*pOldWnd*/)
  145. {
  146. if (m_bMultiSelect)
  147. {
  148. //'emulated' selected items will remain greyed
  149. // if application gets covered
  150. HTREEITEM hItem = GetFirstSelectedItem();
  151. while (hItem)
  152. {
  153. RECT rect;
  154. m_pTreeCtrl->GetItemRect(hItem, &rect, TRUE);
  155. m_pTreeCtrl->InvalidateRect(&rect);
  156. hItem = GetNextSelectedItem(hItem);
  157. }
  158. }
  159. m_pTreeCtrl->Default();
  160. }
  161. void CXTTreeBase::OnKillFocus(CWnd* /*pNewWnd*/)
  162. {
  163. m_pTreeCtrl->Default();
  164. if (m_bMultiSelect)
  165. {
  166. //'emulated' selected items may not get
  167. // refreshed to grey
  168. HTREEITEM hItem = GetFirstSelectedItem();
  169. while (hItem)
  170. {
  171. RECT rect;
  172. m_pTreeCtrl->GetItemRect(hItem, &rect, TRUE);
  173. m_pTreeCtrl->InvalidateRect(&rect);
  174. hItem = GetNextSelectedItem(hItem);
  175. }
  176. }
  177. }
  178. AFX_INLINE BOOL IsArrowKey(UINT nChar)
  179. {
  180. return (nChar == VK_UP) || (nChar == VK_DOWN) || (nChar == VK_PRIOR) || (nChar == VK_NEXT) || (nChar == VK_HOME) || (nChar == VK_END) ||
  181. (nChar == VK_LEFT) || (nChar == VK_RIGHT) || (nChar == VK_BACK) || (nChar == VK_ADD) || (nChar == VK_MULTIPLY) || (nChar == VK_SUBTRACT);
  182. }
  183. void CXTTreeBase::OnKeyDown(UINT nChar, UINT /*nRepCnt*/, UINT /*nFlags*/)
  184. {
  185. if (!m_bMultiSelect)
  186. {
  187. m_pTreeCtrl->Default();
  188. return;
  189. }
  190. BOOL bCtrl = (::GetKeyState(VK_CONTROL) < 0);
  191. BOOL bShift = (::GetKeyState(VK_SHIFT) < 0);
  192. if (!bCtrl && !bShift && IsArrowKey(nChar))
  193. {
  194. UINT uCount = GetSelectedCount();
  195. if ((uCount > 1) || (uCount == 1 && !IsSelected(GetFocusedItem())))
  196. SelectAll(FALSE);
  197. }
  198. HTREEITEM hSel = NULL;
  199. switch (nChar)
  200. {
  201. case VK_UP:
  202. case VK_DOWN:
  203. case VK_PRIOR:
  204. case VK_NEXT:
  205. hSel = m_pTreeCtrl->GetSelectedItem();
  206. if (!m_hSelect)
  207. {
  208. m_hSelect = hSel;
  209. }
  210. if (!bCtrl && !bShift)
  211. {
  212. m_hSelect = NULL;   //reset
  213. }
  214. break;
  215. }
  216. BOOL bDir = (nChar == VK_UP) || (nChar == VK_PRIOR);
  217. m_pTreeCtrl->Default();
  218. if (!hSel || (!bCtrl && !bShift))
  219. {
  220. return;
  221. }
  222. HTREEITEM hNext = (nChar == VK_NEXT) || (nChar == VK_PRIOR) ? GetFocusedItem() :
  223. bDir ? m_pTreeCtrl->GetPrevVisibleItem(hSel) : m_pTreeCtrl->GetNextVisibleItem(hSel);
  224. if (!hNext)
  225. {
  226. hNext = hSel;
  227. }
  228. if (bShift)
  229. {
  230. SelectItems(m_hSelect, hNext, TRUE);
  231. }
  232. else if (bCtrl)
  233. {
  234. SetItemState(hNext, TVIS_FOCUSED, TVIS_FOCUSED);
  235. }
  236. }
  237. BOOL CXTTreeBase::OnBeginLabelEdit(NMHDR* pNMHDR, LRESULT* pResult)
  238. {
  239. TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
  240. *pResult = 0;
  241. // set m_htiEdit equal to pTVDispInfo->item.hItem.
  242. m_htiEdit = pTVDispInfo->item.hItem;
  243. return FALSE;   //pass to parent
  244. }
  245. BOOL CXTTreeBase::OnEndLabelEdit(NMHDR* /*pNMHDR*/, LRESULT* pResult)
  246. {
  247. *pResult = 0;
  248. // set m_htiEdit equal to NULL.
  249. m_htiEdit = NULL;
  250. return FALSE;   //pass to parent
  251. }
  252. BOOL CXTTreeBase::OnItemExpanding(NMHDR* pNMHDR, LRESULT* pResult)
  253. {
  254. if (!m_bMultiSelect)
  255. return FALSE;
  256. NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
  257. *pResult = 0;
  258. if ((pNMTreeView->action == TVE_COLLAPSE) || (pNMTreeView->action == TVE_COLLAPSERESET))
  259. {
  260. // clear selection of children, it would be confusing otherwise
  261. // - the notifications can be over-ridden to stop the de-selection
  262. // if required.
  263. // Unfortunately, the parent window can't over-ride this functionality
  264. // because MFC gives this class first crack.  So if changes are required
  265. // a derived class will have to be used..
  266. ASSERT(pNMTreeView->itemNew.hItem);
  267. // if a descendent item has focus the parent will get selected as a
  268. // consequence of collapsing the tree, so preserve
  269. // (if the parent was already selected it will gain focus, but
  270. // that's acceptable)
  271. BOOL bWasSel = IsSelected(pNMTreeView->itemNew.hItem);
  272. BOOL bWasFocus = SelectChildren(pNMTreeView->itemNew.hItem, FALSE, TRUE);
  273. if (bWasFocus && !bWasSel)
  274. {
  275. FocusItem(pNMTreeView->itemNew.hItem); // give parent focus.
  276. }
  277. }
  278. return FALSE;   //pass to parent
  279. }
  280. HTREEITEM CXTTreeBase::GetNextItem(HTREEITEM hItem) const
  281. {
  282. HTREEITEM hti = NULL;
  283. if (m_pTreeCtrl->ItemHasChildren(hItem))
  284. {
  285. hti = m_pTreeCtrl->GetChildItem(hItem);
  286. }
  287. if (hti == NULL)
  288. {
  289. while ((hti = m_pTreeCtrl->GetNextSiblingItem(hItem)) == NULL)
  290. {
  291. if ((hItem = m_pTreeCtrl->GetParentItem(hItem)) == NULL)
  292. return NULL;
  293. }
  294. }
  295. return hti;
  296. }
  297. HTREEITEM CXTTreeBase::GetPrevItem(HTREEITEM hItem) const
  298. {
  299. HTREEITEM hti = NULL;
  300. hti = m_pTreeCtrl->GetPrevSiblingItem(hItem);
  301. if (hti == NULL)
  302. {
  303. hti = m_pTreeCtrl->GetParentItem(hItem);
  304. }
  305. else
  306. {
  307. hti = GetLastItem(hti);
  308. }
  309. return hti;
  310. }
  311. HTREEITEM CXTTreeBase::GetLastItem(HTREEITEM hItem) const
  312. {
  313. // Temporary used variable
  314. HTREEITEM htiNext;
  315. // Get the last item at the top level
  316. if (hItem == NULL)
  317. {
  318. hItem = m_pTreeCtrl->GetRootItem();
  319. htiNext = m_pTreeCtrl->GetNextSiblingItem(hItem);
  320. while (htiNext != NULL)
  321. {
  322. hItem = htiNext;
  323. htiNext = m_pTreeCtrl->GetNextSiblingItem(htiNext);
  324. }
  325. }
  326. while (m_pTreeCtrl->ItemHasChildren(hItem) != NULL)
  327. {
  328. // Find the last child of hItem
  329. htiNext = m_pTreeCtrl->GetChildItem(hItem);
  330. while (htiNext != NULL)
  331. {
  332. hItem = htiNext;
  333. htiNext = m_pTreeCtrl->GetNextSiblingItem(htiNext);
  334. }
  335. }
  336. return hItem;
  337. }
  338. HTREEITEM CXTTreeBase::FindItemInBranch(LPCTSTR lpszSearch, BOOL bCaseSensitive /*= FALSE*/,
  339. BOOL bWholeWord /*= FALSE*/, HTREEITEM htiItem /*= NULL*/)
  340. {
  341. HTREEITEM htiFound = NULL;
  342. if (!m_pTreeCtrl->ItemHasChildren(htiItem))
  343. return NULL;
  344. CString strSearch = lpszSearch;
  345. int iLen = strSearch.GetLength();
  346. if (iLen == 0)
  347. {
  348. return NULL;
  349. }
  350. if (!bCaseSensitive)
  351. {
  352. strSearch.MakeLower();
  353. }
  354. HTREEITEM htiChild = m_pTreeCtrl->GetChildItem(htiItem);
  355. while (htiChild != NULL)
  356. {
  357. if (m_pTreeCtrl->ItemHasChildren(htiChild))
  358. {
  359. htiFound = FindItemInBranch(lpszSearch, bCaseSensitive, bWholeWord, htiChild);
  360. if (htiFound != NULL)
  361. return htiFound;
  362. }
  363. CString strItemText = m_pTreeCtrl->GetItemText(htiChild);
  364. if (!bCaseSensitive)
  365. {
  366. strItemText.MakeLower();
  367. }
  368. int iIndex;
  369. while ((iIndex = strItemText.Find(strSearch)) != -1)
  370. {
  371. // Search string found
  372. if (bWholeWord)
  373. {
  374. // Check preceding char
  375. if (iIndex != 0)
  376. {
  377. if (_istalpha(strItemText[iIndex-1]) || strItemText[iIndex-1] == '_')
  378. {
  379. // Not whole word
  380. strItemText = strItemText.Right(strItemText.GetLength() -
  381. iIndex - iLen);
  382. continue;
  383. }
  384. }
  385. // Check succeeding char
  386. if (strItemText.GetLength() > iIndex + iLen &&
  387. (_istalpha(strItemText[iIndex + iLen]) ||
  388. (strItemText[iIndex + iLen] == '_')))
  389. {
  390. // Not whole word
  391. strItemText = strItemText.Right(strItemText.GetLength() -
  392. iIndex - strSearch.GetLength());
  393. continue;
  394. }
  395. }
  396. if (IsFindValid(htiChild))
  397. {
  398. return htiChild;
  399. }
  400. else
  401. {
  402. break;
  403. }
  404. }
  405. htiChild = m_pTreeCtrl->GetNextSiblingItem(htiChild);
  406. }
  407. return NULL;
  408. }
  409. HTREEITEM CXTTreeBase::FindItem(LPCTSTR lpszSearch, BOOL bCaseSensitive /*= FALSE*/,
  410. BOOL bDownDir /*= TRUE*/, BOOL bWholeWord /*= FALSE*/, HTREEITEM hItem /*= NULL*/)
  411. {
  412. CString str = lpszSearch;
  413. int lenSearchStr = str.GetLength();
  414. if (lenSearchStr == 0)
  415. {
  416. return NULL;
  417. }
  418. // For the first pass, set the current item equal to the selection
  419. HTREEITEM htiSel = hItem ? hItem : m_pTreeCtrl->GetSelectedItem();
  420. // If NULL, use root item.
  421. if (htiSel == NULL)
  422. htiSel = m_pTreeCtrl->GetRootItem();
  423. HTREEITEM htiCur = htiSel;
  424. CString strSearch = str;
  425. // make sure it ends if we started with no selection
  426. if ((htiCur == NULL) && (htiSel != NULL))
  427. {
  428. if (bDownDir)
  429. {
  430. htiCur = m_pTreeCtrl->GetRootItem();
  431. }
  432. else
  433. {
  434. htiCur = GetLastItem(NULL);
  435. }
  436. }
  437. if (!bCaseSensitive)
  438. {
  439. strSearch.MakeLower();
  440. }
  441. // For the first pass only, we check to see if it
  442. // is the item we're looking for.
  443. BOOL bFirstPass = TRUE;
  444. BOOL bFoundRoot = FALSE;
  445. while (htiCur && (htiCur != htiSel || bFirstPass))
  446. {
  447. bFirstPass = FALSE;
  448. CString strItemText = m_pTreeCtrl->GetItemText(htiCur);
  449. if (!bCaseSensitive)
  450. {
  451. strItemText.MakeLower();
  452. }
  453. int iIndex;
  454. while ((iIndex = strItemText.Find(strSearch)) != -1)
  455. {
  456. // Search string found
  457. if (bWholeWord)
  458. {
  459. // Check preceding char
  460. if (iIndex != 0)
  461. {
  462. if (_istalpha(strItemText[iIndex-1]) || strItemText[iIndex-1] == '_')
  463. {
  464. // Not whole word
  465. strItemText = strItemText.Right(strItemText.GetLength() -
  466. iIndex - lenSearchStr);
  467. continue;
  468. }
  469. }
  470. // Check succeeding char
  471. if (strItemText.GetLength() > iIndex + lenSearchStr &&
  472. (_istalpha(strItemText[iIndex + lenSearchStr]) ||
  473. (strItemText[iIndex + lenSearchStr] == '_')))
  474. {
  475. // Not whole word
  476. strItemText = strItemText.Right(strItemText.GetLength() -
  477. iIndex - strSearch.GetLength());
  478. continue;
  479. }
  480. }
  481. if (IsFindValid(htiCur))
  482. {
  483. return htiCur;
  484. }
  485. else
  486. {
  487. break;
  488. }
  489. }
  490. htiCur = bDownDir ? GetNextItem(htiCur) : GetPrevItem(htiCur);
  491. if (htiCur == NULL)
  492. {
  493. if (bFoundRoot)
  494. return NULL;
  495. if (bDownDir)
  496. {
  497. htiCur = m_pTreeCtrl->GetRootItem();
  498. }
  499. else
  500. {
  501. htiCur = GetLastItem(NULL);
  502. }
  503. bFoundRoot = TRUE;
  504. }
  505. }
  506. return NULL;
  507. }
  508. BOOL CXTTreeBase::IsFindValid(HTREEITEM)
  509. {
  510. return TRUE;
  511. }
  512. void CXTTreeBase::SetItemFont(HTREEITEM hItem, LOGFONT& logfont)
  513. {
  514. CLRFONT cf;
  515. if (!m_mapColorFont.Lookup(hItem, cf))
  516. {
  517. cf.color = COLORREF_NULL;
  518. }
  519. cf.logfont = logfont;
  520. m_mapColorFont[hItem] = cf;
  521. }
  522. BOOL CXTTreeBase::GetItemFont(HTREEITEM hItem, LOGFONT* plogfont)
  523. {
  524. CLRFONT cf;
  525. if (!m_mapColorFont.Lookup(hItem, cf))
  526. {
  527. return FALSE;
  528. }
  529. if (cf.logfont.lfFaceName[0] == _T(''))
  530. {
  531. return FALSE;
  532. }
  533. *plogfont = cf.logfont;
  534. return TRUE;
  535. }
  536. void CXTTreeBase::SetItemBold(HTREEITEM hItem, BOOL bBold)
  537. {
  538. SetItemState(hItem, bBold ? TVIS_BOLD : 0, TVIS_BOLD);
  539. m_pTreeCtrl->InvalidateRect(NULL);
  540. }
  541. BOOL CXTTreeBase::GetItemBold(HTREEITEM hItem)
  542. {
  543. return GetItemState(hItem, TVIS_BOLD) & TVIS_BOLD;
  544. }
  545. void CXTTreeBase::SetItemColor(HTREEITEM hItem, COLORREF color)
  546. {
  547. CLRFONT cf;
  548. m_mapColorFont.Lookup(hItem, cf);
  549. cf.color = color;
  550. m_mapColorFont[hItem] = cf;
  551. m_pTreeCtrl->InvalidateRect(NULL);
  552. }
  553. COLORREF CXTTreeBase::GetItemColor(HTREEITEM hItem)
  554. {
  555. CLRFONT cf;
  556. if (m_mapColorFont.Lookup(hItem, cf))
  557. return cf.color;
  558. return COLORREF_NULL;
  559. }
  560. void CXTTreeBase::SetItemBackColor(HTREEITEM hItem, COLORREF color)
  561. {
  562. CLRFONT cf;
  563. m_mapColorFont.Lookup(hItem, cf);
  564. cf.colorBack = color;
  565. m_mapColorFont[hItem] = cf;
  566. m_pTreeCtrl->InvalidateRect(NULL);
  567. }
  568. COLORREF CXTTreeBase::GetItemBackColor(HTREEITEM hItem)
  569. {
  570. CLRFONT cf;
  571. if (m_mapColorFont.Lookup(hItem, cf))
  572. return cf.colorBack;
  573. return COLORREF_NULL;
  574. }
  575. COLORREF CXTTreeBase::GetTreeBackColor() const
  576. {
  577. // if the tree is disabled return disabled back color.
  578. if (!m_pTreeCtrl->IsWindowEnabled())
  579. return GetXtremeColor(COLOR_3DFACE);
  580. #if _MSC_VER >= 1200 // MFC 6.0
  581. if (XTPSystemVersion()->GetComCtlVersion() >= MAKELONG(71, 4))
  582. {
  583. // is there a user defined color available ?
  584. COLORREF crBack = m_pTreeCtrl->GetBkColor();
  585. if (crBack != COLORREF_NULL)
  586. return crBack;
  587. }
  588. #endif
  589. // return the system window color.
  590. return GetXtremeColor(COLOR_WINDOW);
  591. }
  592. COLORREF CXTTreeBase::GetTreeTextColor() const
  593. {
  594. // if the tree is disabled return disabled text color.
  595. if (!m_pTreeCtrl->IsWindowEnabled())
  596. return GetXtremeColor(COLOR_GRAYTEXT);
  597. #if _MSC_VER >= 1200 // MFC 6.0
  598. if (XTPSystemVersion()->GetComCtlVersion() >= MAKELONG(71, 4))
  599. {
  600. // is ther a user defined color available ?
  601. COLORREF crText = m_pTreeCtrl->GetTextColor();
  602. if (crText != COLORREF_NULL)
  603. return crText;
  604. }
  605. #endif
  606. // return the system window text color.
  607. return GetXtremeColor(COLOR_WINDOWTEXT);
  608. }
  609. COLORREF CXTTreeBase::GetItemBackColor(UINT uState, bool bTreeHasFocus, DWORD dwStyle, COLORREF crBack) const
  610. {
  611. // if the tree item is selected or drop highlighted.
  612. if (uState & TVIS_SELECTED || uState & TVIS_DROPHILITED)
  613. {
  614. // if the tree has focus and draw the text background hilite color.
  615. if (bTreeHasFocus || uState & TVIS_DROPHILITED)
  616. {
  617. if (m_pTreeCtrl->IsWindowEnabled())
  618. return GetXtremeColor(COLOR_HIGHLIGHT);
  619. else
  620. return GetXtremeColor(COLOR_GRAYTEXT);
  621. }
  622. // if the tree does not have focus and the TVS_SHOWSELALWAYS flag is
  623. // set draw the text background hilite color.
  624. else if (dwStyle & TVS_SHOWSELALWAYS)
  625. {
  626. if (m_pTreeCtrl->IsWindowEnabled())
  627. return GetXtremeColor(COLOR_3DFACE);
  628. else
  629. return GetXtremeColor(COLOR_GRAYTEXT);
  630. }
  631. }
  632. return crBack;
  633. }
  634. COLORREF CXTTreeBase::GetItemTextColor(UINT uState, bool bTreeHasFocus, DWORD dwStyle, COLORREF crText) const
  635. {
  636. // if the tree item is selected or drop highlighted.
  637. if (uState & TVIS_SELECTED || uState & TVIS_DROPHILITED)
  638. {
  639. // if the tree has focus and draw the text hilite color.
  640. if (bTreeHasFocus || uState & TVIS_DROPHILITED)
  641. {
  642. if (m_pTreeCtrl->IsWindowEnabled())
  643. return GetXtremeColor(COLOR_HIGHLIGHTTEXT);
  644. else
  645. return GetXtremeColor(COLOR_3DFACE);
  646. }
  647. // if the tree does not have focus and the TVS_SHOWSELALWAYS flag is
  648. // set draw the text hilite color.
  649. else if (dwStyle & TVS_SHOWSELALWAYS)
  650. {
  651. if (m_pTreeCtrl->IsWindowEnabled())
  652. return GetXtremeColor(COLOR_WINDOWTEXT);
  653. else
  654. return GetXtremeColor(COLOR_3DFACE);
  655. }
  656. }
  657. return crText;
  658. }
  659. void CXTTreeBase::DoPaint(CDC& dc, BOOL bInternal)
  660. {
  661. // Get the client rect.
  662. CRect rcClient;
  663. m_pTreeCtrl->GetClientRect(&rcClient);
  664. const COLORREF crTreeBack = GetTreeBackColor();
  665. const COLORREF crTreeText = GetTreeTextColor();
  666. // Paint to a memory device context to help
  667. // eliminate screen flicker.
  668. CXTPBufferDC memDC(dc, rcClient);
  669. memDC.FillSolidRect(rcClient, crTreeBack);
  670. // Now let the window do its default painting...
  671. m_pTreeCtrl->DefWindowProc(WM_PAINT, (WPARAM)memDC.m_hDC, 0);
  672. if (!bInternal)
  673. return;
  674. int nSavedDC = memDC.SaveDC();
  675. // check to see if a tree label is getting edited.
  676. BOOL bEditing = (HasEditLabels() && m_pTreeCtrl->GetEditControl());
  677. // if not editing, make sure the edit item is set to NULL.
  678. if (!bEditing)
  679. m_htiEdit = NULL;
  680. // check to see if the tree is enabled.
  681. BOOL bIsEnabled = m_pTreeCtrl->IsWindowEnabled();
  682. // check to see if the tree has focus.
  683. bool bTreeHasFocus = (CWnd::GetFocus() == m_pTreeCtrl);
  684. // get the style for the tree.
  685. DWORD dwStyle = m_pTreeCtrl->GetStyle();
  686. // get the visible count.
  687. HTREEITEM hItem = m_pTreeCtrl->GetFirstVisibleItem();
  688. int iVisibleCount = m_pTreeCtrl->GetVisibleCount()+1;
  689. LOGFONT treefont;
  690. if (!m_pTreeCtrl->GetFont()->GetLogFont(&treefont))
  691. {
  692. ::GetObject(::GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &treefont);
  693. }
  694. while (hItem && iVisibleCount--)
  695. {
  696. // get the tree item rect.
  697. CRect rcItem;
  698. m_pTreeCtrl->GetItemRect(hItem, &rcItem, TRUE);
  699. // refresh the background.
  700. CRect rcFill = rcItem;
  701. rcFill.right = rcClient.right;
  702. memDC.FillSolidRect(&rcFill, crTreeBack);
  703. // get the state of the tree item.
  704. UINT uState = GetItemState(hItem, TVIS_SELECTED | TVIS_FOCUSED | TVIS_DROPHILITED);
  705. if (m_pTreeCtrl->GetDropHilightItem() && uState & TVIS_SELECTED && hItem != m_pTreeCtrl->GetDropHilightItem())
  706. uState ^= TVIS_SELECTED;
  707. BOOL bSelected = ((uState & (TVIS_SELECTED | TVIS_DROPHILITED)) != 0);
  708. bool bUnderline = m_htiLast == hItem;
  709. // define the background and text colors.
  710. COLORREF crItemBack = GetItemBackColor(
  711. uState, bTreeHasFocus, dwStyle, crTreeBack);
  712. COLORREF crItemText = GetItemTextColor(
  713. uState, bTreeHasFocus, dwStyle, crTreeText);
  714. // if the item is getting edited paint white.
  715. if (m_htiEdit == hItem)
  716. {
  717. crItemBack = crTreeBack;
  718. crItemText = crTreeBack;
  719. }
  720. // create the LOGFONT used by the tree item.
  721. LOGFONT logfont;
  722. logfont = treefont;
  723. CLRFONT cf;
  724. if (m_mapColorFont.Lookup(hItem, cf))
  725. {
  726. if (!bSelected && bIsEnabled)
  727. {
  728. if (cf.color != COLORREF_NULL)
  729. crItemText = cf.color;
  730. if (cf.colorBack != COLORREF_NULL)
  731. crItemBack = cf.colorBack;
  732. }
  733. if (cf.logfont.lfFaceName[0] != _T(''))
  734. {
  735. logfont = cf.logfont;
  736. }
  737. }
  738. if (GetItemBold(hItem))
  739. {
  740. logfont.lfWeight = FW_BOLD;
  741. }
  742. if (dwStyle & TVS_TRACKSELECT)
  743. {
  744. logfont.lfUnderline = bUnderline ? (BYTE)1 : (BYTE)0;
  745. if (logfont.lfUnderline && !bSelected)
  746. {
  747. if (XTOSVersionInfo()->IsWin95() || XTOSVersionInfo()->IsWinNT4())
  748. crItemText = GetXtremeColor(COLOR_HIGHLIGHT);
  749. else
  750. crItemText = GetXtremeColor(COLOR_HOTLIGHT);
  751. }
  752. }
  753. // create a CFont object from the LOGFONT structure and
  754. // select it into the current device context.
  755. CFont fontDC;
  756. fontDC.CreateFontIndirect(&logfont);
  757. CFont* pOldFont = memDC.SelectObject(&fontDC);
  758. // get the text for the tree item and determine its size.
  759. CString strItem = m_pTreeCtrl->GetItemText(hItem);
  760. CSize sizeText = memDC.GetTextExtent(strItem);
  761. // if the text is wider than the tree item label, adjust accordingly.
  762. if (rcItem.Width() < sizeText.cx)
  763. {
  764. rcItem.right = rcItem.left + sizeText.cx + 2;
  765. }
  766. // set the font foreground and background colors.
  767. memDC.SetBkColor(crItemBack);
  768. memDC.SetTextColor(crItemText);
  769. // draw the label background if selected.
  770. if (bSelected)
  771. {
  772. memDC.FillSolidRect(&rcItem, crItemBack);
  773. }
  774. CRect rcText(rcItem);
  775. rcText.OffsetRect(3, 0);
  776. // draw the text and restore the device context.
  777. memDC.DrawText(strItem, &rcText, DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_NOPREFIX);
  778. // draw the focus rect.
  779. if ((uState & TVIS_FOCUSED))
  780. {
  781. if (!bEditing && bIsEnabled && CWnd::GetFocus() == m_pTreeCtrl)
  782. {
  783. memDC.SetTextColor(crTreeText);
  784. memDC.DrawFocusRect(&rcItem);
  785. }
  786. }
  787. memDC.SelectObject(pOldFont);
  788. fontDC.DeleteObject();
  789. // move to the next visible item.
  790. hItem = m_pTreeCtrl->GetNextVisibleItem(hItem);
  791. }
  792. memDC.RestoreDC(nSavedDC);
  793. }
  794. HTREEITEM CXTTreeBase::GetPrevSelectedItem(HTREEITEM hItem) const
  795. {
  796. for (hItem = m_pTreeCtrl->GetPrevVisibleItem(hItem);
  797. hItem != NULL;
  798. hItem = m_pTreeCtrl->GetPrevVisibleItem(hItem))
  799. {
  800. if (GetItemState(hItem, TVIS_SELECTED) & TVIS_SELECTED)
  801. {
  802. return hItem;
  803. }
  804. }
  805. return NULL;
  806. }
  807. UINT CXTTreeBase::GetSelectedCount() const
  808. {
  809. UINT nCount = 0;
  810. HTREEITEM hItem = GetFirstSelectedItem();
  811. while (hItem)
  812. {
  813. nCount++;
  814. hItem = GetNextSelectedItem(hItem);
  815. }
  816. return nCount;
  817. }
  818. BOOL CXTTreeBase::EnableMultiSelect(BOOL bMultiSelect)
  819. {
  820. BOOL bReturn = m_bMultiSelect;
  821. m_bMultiSelect = bMultiSelect;
  822. if (!m_bMultiSelect)
  823. {
  824. HTREEITEM hItem = m_pTreeCtrl->GetSelectedItem();
  825. if (hItem && !IsSelected(hItem))
  826. {
  827. hItem = NULL;
  828. }
  829. SelectAllIgnore(FALSE, hItem);
  830. if (hItem)
  831. {
  832. SelectItem(hItem);
  833. }
  834. }
  835. return bReturn;
  836. }
  837. BOOL CXTTreeBase::SetItemState(HTREEITEM hItem, UINT nState, UINT nStateMask)
  838. {
  839. ASSERT(hItem);
  840. if (!hItem)
  841. return FALSE;
  842. if (!m_bMultiSelect)
  843. {
  844. return m_pTreeCtrl->SetItemState(hItem, nState, nStateMask);
  845. }
  846. HTREEITEM hFocus = m_pTreeCtrl->GetSelectedItem();           // current focus
  847. BOOL bWasFocus = (hFocus == hItem);
  848. BOOL bFocusWasSel = hFocus && IsSelected(hFocus); // selection state of current focus
  849. BOOL bWasSel = IsSelected(hItem);            // select state of acting item
  850. UINT nS = (nState & ~TVIS_FOCUSED);
  851. UINT nSM = (nStateMask & ~TVIS_FOCUSED);
  852. BOOL bVista = XTPSystemVersion()->IsWinVistaOrGreater();
  853. if (nStateMask & TVIS_FOCUSED)
  854. {
  855. //wanted to affect focus
  856. if (nState & TVIS_FOCUSED)
  857. {
  858. if (bVista)
  859. {
  860. //wanted to set focus
  861. if (!bWasFocus && hFocus)
  862. {
  863. //because SelectItem would de-select the current 'real' selection
  864. // (the one with focus), need to make the tree ctrl think there is
  865. // no 'real' selection but still keep the the old item selected
  866. //it has to be done before the SelectItem call because
  867. // otherwise the TVN_SELCHANGING/ED notification handlers
  868. // wouldn't be able to get the proper list of selected items
  869. m_pTreeCtrl->SelectItem(NULL); //will cause notify, but can be taken as focus change
  870. m_pTreeCtrl->SetItemState(hFocus, TVIS_SELECTED, TVIS_SELECTED);
  871. }
  872. if (!m_pTreeCtrl->SelectItem(hItem))    //set focus (will consequently select, if not already focused)
  873. {
  874. return FALSE;
  875. }
  876. if (!bWasFocus && hFocus)
  877. {
  878. m_pTreeCtrl->SetItemState(hFocus, bFocusWasSel ? TVIS_SELECTED : 0, TVIS_SELECTED);
  879. }
  880. }
  881. else
  882. {
  883. //wanted to set focus
  884. if (!bWasFocus && bFocusWasSel)
  885. {
  886. //because SelectItem would de-select the current 'real' selection
  887. // (the one with focus), need to make the tree ctrl think there is
  888. // no 'real' selection but still keep the the old item selected
  889. //it has to be done before the SelectItem call because
  890. // otherwise the TVN_SELCHANGING/ED notification handlers
  891. // wouldn't be able to get the proper list of selected items
  892. m_pTreeCtrl->SelectItem(NULL); //will cause notify, but can be taken as focus change
  893. m_pTreeCtrl->SetItemState(hFocus, TVIS_SELECTED, TVIS_SELECTED);
  894. m_pTreeCtrl->UpdateWindow();
  895. }
  896. if (!m_pTreeCtrl->SelectItem(hItem))    //set focus (will consequently select, if not already focused)
  897. {
  898. return FALSE;
  899. }
  900. }
  901. if (nStateMask & TVIS_SELECTED)
  902. {
  903. //wanted to affect select state
  904. if (nState & TVIS_SELECTED)
  905. {
  906. //wanted to select, already done if wasn't focused
  907. if (!bVista && (!bWasFocus || bFocusWasSel))
  908. {
  909. nS &= ~TVIS_SELECTED;
  910. nSM &= ~TVIS_SELECTED;
  911. }
  912. }
  913. //else wanted to clear, base call will do that
  914. }
  915. else
  916. {
  917. //didn't want to affect select state
  918. if (!bWasSel)
  919. {
  920. //it wasn't previously selected, let base clear (correct)
  921. nS &= ~TVIS_SELECTED;
  922. nSM |= TVIS_SELECTED;
  923. }
  924. //else was already selected, no harm done
  925. }
  926. }
  927. else
  928. {
  929. //wanted to clear focus
  930. if (bWasFocus)
  931. {
  932. //it had the focus
  933. m_pTreeCtrl->SelectItem(NULL);  //clear focus
  934. if (!(nStateMask & TVIS_SELECTED))
  935. {
  936. //didn't want to affect select state
  937. if (bWasSel)
  938. {
  939. //it was selected, so restore
  940. ASSERT(!(nS & TVIS_SELECTED));
  941. ASSERT(!(nSM & TVIS_SELECTED));
  942. //set state here, to avoid double-notify
  943. m_pTreeCtrl->SetItemState(hItem, TVIS_SELECTED, TVIS_SELECTED);
  944. //let base do other states
  945. }
  946. }
  947. else if (nState & TVIS_SELECTED)
  948. {
  949. //wanted to select (but clear focus)
  950. if (bWasSel)
  951. {
  952. //if was selected, restore
  953. m_pTreeCtrl->SetItemState(hItem, TVIS_SELECTED, TVIS_SELECTED);
  954. }
  955. //don't want to notify, default did it
  956. nS &= ~TVIS_SELECTED;
  957. nSM &= ~TVIS_SELECTED;
  958. }
  959. }
  960. }
  961. }
  962. if (!nSM)
  963. {
  964. return TRUE;    //no other states to alter
  965. }
  966. if (nSM & TVIS_SELECTED)
  967. {
  968. //still need to alter selection state
  969. NMTREEVIEW nmtv;
  970. ZeroMemory(&nmtv, sizeof(NMTREEVIEW));
  971. nmtv.hdr.hwndFrom = m_pTreeCtrl->m_hWnd;
  972. nmtv.hdr.idFrom = ::GetDlgCtrlID(m_pTreeCtrl->m_hWnd);
  973. nmtv.hdr.code = TVN_SELCHANGING;
  974. nmtv.itemOld.mask = nmtv.itemNew.mask = 0;
  975. nmtv.itemOld.hItem = nmtv.itemNew.hItem = NULL;
  976. TVITEM& item = (nS & TVIS_SELECTED) ? nmtv.itemNew : nmtv.itemOld;
  977. item.mask = TVIF_HANDLE | TVIF_PARAM;
  978. item.hItem = hItem;
  979. item.lParam = m_pTreeCtrl->GetItemData(hItem);
  980. item.state = nS;
  981. item.stateMask = nSM;
  982. if (SendNotify(&nmtv.hdr))
  983. {
  984. return FALSE;   //sel-changing stopped
  985. }
  986. VERIFY(m_pTreeCtrl->SetItemState(hItem, nS, nSM));
  987. nmtv.hdr.code = TVN_SELCHANGED;
  988. SendNotify(&nmtv.hdr);
  989. nS &= ~TVIS_SELECTED;
  990. nSM &= ~TVIS_SELECTED;
  991. }
  992. if (!nSM)
  993. {
  994. return TRUE;
  995. }
  996. return m_pTreeCtrl->SetItemState(hItem, nS, nSM);
  997. }
  998. UINT CXTTreeBase::GetItemState(HTREEITEM hItem, UINT nStateMask) const
  999. {
  1000. UINT n = m_pTreeCtrl->GetItemState(hItem, nStateMask & ~TVIS_FOCUSED);
  1001. if (nStateMask & TVIS_FOCUSED)
  1002. {
  1003. if (m_pTreeCtrl->GetSelectedItem() == hItem)
  1004. {
  1005. n |= TVIS_FOCUSED;
  1006. }
  1007. }
  1008. return n;
  1009. }
  1010. BOOL CXTTreeBase::SelectItem(HTREEITEM hItem)
  1011. {
  1012. if (m_bMultiSelect)
  1013. {
  1014. return SetItemState(hItem,
  1015. TVIS_SELECTED | TVIS_FOCUSED, TVIS_SELECTED | TVIS_FOCUSED);
  1016. }
  1017. else
  1018. {
  1019. return m_pTreeCtrl->SelectItem(hItem);
  1020. }
  1021. }
  1022. BOOL CXTTreeBase::FocusItem(HTREEITEM hItem)
  1023. {
  1024. ASSERT(m_bMultiSelect);
  1025. BOOL bRet = FALSE;
  1026. if (hItem)
  1027. {
  1028. bRet = SetItemState(hItem, TVIS_FOCUSED, TVIS_FOCUSED);
  1029. }
  1030. else
  1031. {
  1032. hItem = m_pTreeCtrl->GetSelectedItem();
  1033. if (hItem)
  1034. {
  1035. bRet = SetItemState(hItem, 0, TVIS_FOCUSED);
  1036. }
  1037. }
  1038. return bRet;
  1039. }
  1040. LRESULT CXTTreeBase::SendNotify(LPNMHDR pNMHDR)
  1041. {
  1042. CWnd* pWndOwner = m_pTreeCtrl->GetOwner();
  1043. if (pWndOwner->GetSafeHwnd())
  1044. {
  1045. return ::SendMessage(pWndOwner->m_hWnd, WM_NOTIFY,
  1046. (WPARAM)pNMHDR->idFrom, (LPARAM)pNMHDR);
  1047. }
  1048. return NULL;
  1049. }
  1050. HTREEITEM CXTTreeBase::GetFirstSelectedItem() const
  1051. {
  1052. HTREEITEM hItem = m_pTreeCtrl->GetRootItem();
  1053. while (hItem)
  1054. {
  1055. if (IsSelected(hItem))
  1056. {
  1057. break;
  1058. }
  1059. hItem = m_pTreeCtrl->GetNextVisibleItem(hItem);
  1060. }
  1061. return hItem;
  1062. }
  1063. HTREEITEM CXTTreeBase::GetNextSelectedItem(HTREEITEM hItem) const
  1064. {
  1065. hItem = m_pTreeCtrl->GetNextVisibleItem(hItem);
  1066. while (hItem)
  1067. {
  1068. if (IsSelected(hItem))
  1069. {
  1070. break;
  1071. }
  1072. hItem = m_pTreeCtrl->GetNextVisibleItem(hItem);
  1073. }
  1074. return hItem;
  1075. }
  1076. void CXTTreeBase::SelectAll(BOOL bSelect /*= TRUE*/, HTREEITEM htItem/*= NULL*/)
  1077. {
  1078. bSelect = !!bSelect;    //ensure 0 or 1
  1079. UINT nState = bSelect ? TVIS_SELECTED : 0;
  1080. if (htItem == NULL)
  1081. htItem = m_pTreeCtrl->GetRootItem();
  1082. while (htItem)
  1083. {
  1084. if (IsSelected(htItem) != bSelect)
  1085. {
  1086. SetItemState(htItem, nState, TVIS_SELECTED);
  1087. }
  1088. htItem = m_pTreeCtrl->GetNextVisibleItem(htItem);
  1089. }
  1090. }
  1091. void CXTTreeBase::SelectAllIgnore(BOOL bSelect, HTREEITEM hIgnore)
  1092. {
  1093. //special case to avoid multiple notifications for
  1094. // the same item
  1095. bSelect = !!bSelect;    //ensure 0 or 1
  1096. UINT nState = bSelect ? TVIS_SELECTED : 0;
  1097. HTREEITEM hItem = m_pTreeCtrl->GetRootItem();
  1098. while (hItem)
  1099. {
  1100. if (hItem != hIgnore)
  1101. {
  1102. if (IsSelected(hItem) != bSelect)
  1103. {
  1104. SetItemState(hItem, nState, TVIS_SELECTED);
  1105. }
  1106. }
  1107. hItem = m_pTreeCtrl->GetNextVisibleItem(hItem);
  1108. }
  1109. }
  1110. void CXTTreeBase::SelectItems(HTREEITEM hFirst, HTREEITEM hLast, BOOL bOnly /*= TRUE*/)
  1111. {
  1112. //locate (and select) either first or last
  1113. // (so order is arbitrary)
  1114. HTREEITEM hItem = m_pTreeCtrl->GetRootItem();
  1115. while (hItem)
  1116. {
  1117. if ((hItem == hFirst) || (hItem == hLast))
  1118. {
  1119. if (hFirst != hLast)
  1120. {
  1121. if (!IsSelected(hItem))
  1122. {
  1123. SetItemState(hItem, TVIS_SELECTED, TVIS_SELECTED);
  1124. }
  1125. hItem = m_pTreeCtrl->GetNextVisibleItem(hItem);
  1126. }
  1127. break;
  1128. }
  1129. if (bOnly && IsSelected(hItem))
  1130. {
  1131. SetItemState(hItem, 0, TVIS_SELECTED);
  1132. }
  1133. hItem = m_pTreeCtrl->GetNextVisibleItem(hItem);
  1134. }
  1135. //select rest of range
  1136. while (hItem)
  1137. {
  1138. if (!IsSelected(hItem))
  1139. {
  1140. SetItemState(hItem, TVIS_SELECTED, TVIS_SELECTED);
  1141. }
  1142. if ((hItem == hFirst) || (hItem == hLast))
  1143. {
  1144. hItem = m_pTreeCtrl->GetNextVisibleItem(hItem);
  1145. break;
  1146. }
  1147. hItem = m_pTreeCtrl->GetNextVisibleItem(hItem);
  1148. }
  1149. if (!bOnly)
  1150. {
  1151. return;
  1152. }
  1153. while (hItem)
  1154. {
  1155. if (IsSelected(hItem))
  1156. {
  1157. SetItemState(hItem, 0, TVIS_SELECTED);
  1158. }
  1159. hItem = m_pTreeCtrl->GetNextVisibleItem(hItem);
  1160. }
  1161. }
  1162. BOOL CXTTreeBase::OnButtonDown(BOOL bLeft, UINT nFlags, CPoint point)
  1163. {
  1164. UINT      nHF = 0;
  1165. HTREEITEM hItem = NULL;
  1166. if (!m_bMultiSelect)
  1167. return TRUE;
  1168. BOOL bBase = FALSE;
  1169. hItem = m_pTreeCtrl->HitTest(point, &nHF);
  1170. if (hItem)
  1171. {
  1172. //base always handles expanding items
  1173. //(doesn't really mean much to right button, but pass anyway)
  1174. bBase = (nHF & (TVHT_ONITEMBUTTON));
  1175. if (!bBase && bLeft && (m_pTreeCtrl->GetStyle() & TVS_CHECKBOXES))
  1176. {
  1177. //when the tree has check-boxes, the default handler makes
  1178. // a quick selection of the clicked item, then re-selects
  1179. // the previously selected item - to cause a sel-changed
  1180. // notification.  Fortunately it doesn't affect the multi-
  1181. // selection, so can pass on.
  1182. bBase = (nHF & TVHT_ONITEMSTATEICON);
  1183. #ifdef _MST_MULTI_CHECK
  1184. //Use the above define if you want all selected items to
  1185. // be checked the same when any one of them is checked
  1186. // - Interestingly this doesn't happen in the listview control
  1187. //  (LVS_EX_CHECKBOXES)
  1188. if (bBase)
  1189. {
  1190. //the default selection notification would mess
  1191. // the multi-selection up, so generate the notification
  1192. // manually
  1193. // (anyway, this is smoother than the selection flicker
  1194. //  the default gives)
  1195. NMTREEVIEW nmtv;
  1196. ZeroMemory(&nmtv, sizeof(NMTREEVIEW));
  1197. #ifdef TVN_CHKCHANGE
  1198. nmtv.hdr.code = TVN_CHKCHANGE;
  1199. #else
  1200. nmtv.hdr.code = TVN_SELCHANGED;
  1201. #endif
  1202. nmtv.hdr.hwndFrom = m_hWnd;
  1203. nmtv.hdr.idFrom = ::GetDlgCtrlID(m_hWnd);
  1204. nmtv.itemOld.hItem = NULL;
  1205. nmtv.itemNew.mask = TVIF_HANDLE | TVIF_PARAM;
  1206. BOOL bChk = !GetCheck(hItem);
  1207. if (IsSelected(hItem))
  1208. {
  1209. HTREEITEM h = GetFirstSelectedItem();
  1210. while (h)
  1211. {
  1212. if (!GetCheck(h) == bChk) //! to ensure 0 or 1
  1213. {
  1214. SetCheck(h, bChk);
  1215. #ifdef TVN_CHKCHANGE
  1216. //only send multiple check-change
  1217. // notifications (not sel-changed)
  1218. if (h != hItem) //clicked item will be done last
  1219. {
  1220. nmtv.itemNew.hItem = h;
  1221. nmtv.itemNew.lParam = m_pTreeCtrl->GetItemData(h);
  1222. SendNotify(&nmtv.hdr);
  1223. }
  1224. #endif
  1225. }
  1226. h = GetNextSelectedItem(h);
  1227. }
  1228. }
  1229. else
  1230. {
  1231. SetCheck(hItem, bChk);
  1232. }
  1233. //notify clicked item
  1234. nmtv.itemNew.hItem = hItem;
  1235. nmtv.itemNew.lParam = m_pTreeCtrl->GetItemData(hItem);
  1236. SendNotify(&nmtv.hdr);
  1237. return TRUE;
  1238. }
  1239. #endif
  1240. }
  1241. }
  1242. if (bBase)
  1243. return TRUE;
  1244. if (!hItem || (nHF & (TVHT_ONITEMRIGHT | TVHT_NOWHERE | TVHT_ONITEMINDENT)))
  1245. {
  1246. //clicked in space, do rubber-banding
  1247. DoBanding(nFlags, point);
  1248. return TRUE;
  1249. }
  1250. ASSERT(nHF & (TVHT_ONITEM | TVHT_ONITEMSTATEICON));   //nothing else left
  1251. DoPreSelection(hItem, bLeft, nFlags);
  1252. DoAction(hItem, bLeft, nFlags, point);
  1253. return m_bActionDone;  // as set in doaction
  1254. }
  1255. void CXTTreeBase::DoPreSelection(HTREEITEM hItem, BOOL bLeft, UINT nFlags)
  1256. {
  1257. if (bLeft)
  1258. {
  1259. //if shift key down, select immediately
  1260. if ((nFlags & MK_SHIFT))
  1261. {
  1262. if (!m_hSelect)
  1263. {
  1264. m_hSelect = m_pTreeCtrl->GetSelectedItem(); //focus
  1265. }
  1266. SetItemState(hItem, TVIS_FOCUSED, TVIS_FOCUSED);    //focus changes to last clicked
  1267. SelectItems(m_hSelect, hItem, !(nFlags & MK_CONTROL));
  1268. }
  1269. else
  1270. {
  1271. if (!(nFlags & MK_CONTROL))
  1272. {
  1273. //if ctrl was down, then the selection is delayed until
  1274. // mouse up, otherwise select the one item
  1275. if (!IsSelected(hItem))
  1276. {
  1277. SelectAllIgnore(FALSE, hItem);
  1278. }
  1279. SetItemState(hItem, TVIS_SELECTED | TVIS_FOCUSED, TVIS_SELECTED | TVIS_FOCUSED);
  1280. }
  1281. m_hSelect = NULL;   //reset when a non-shift operation occurs
  1282. }
  1283. return;
  1284. }
  1285. //right mouse
  1286. if (nFlags & (MK_CONTROL | MK_SHIFT))
  1287. {
  1288. if (!(nFlags & MK_SHIFT))
  1289. {
  1290. m_hSelect = hItem;
  1291. }
  1292. return;     //do nothing if shift or ctrl
  1293. }
  1294. if (!IsSelected(hItem))
  1295. {
  1296. SelectAllIgnore(FALSE, hItem);
  1297. }
  1298. SetItemState(hItem, TVIS_SELECTED | TVIS_FOCUSED, TVIS_SELECTED | TVIS_FOCUSED);
  1299. }
  1300. void CXTTreeBase::DoAction(HTREEITEM hItem, BOOL bLeft, UINT nFlags, CPoint point)
  1301. {
  1302. m_bActionDone = false;
  1303. ::SetCapture(m_pTreeCtrl->m_hWnd);
  1304. ASSERT(::GetCapture() == m_pTreeCtrl->m_hWnd);
  1305. MSG msg;
  1306. UINT nDone = 0;
  1307. CPoint pt;
  1308. CSize sizeDrag(::GetSystemMetrics(SM_CXDRAG), ::GetSystemMetrics(SM_CYDRAG));
  1309. while (!nDone && ::GetMessage(&msg, NULL, 0, 0))
  1310. {
  1311. if (::GetCapture() != m_pTreeCtrl->m_hWnd)
  1312. {
  1313. break;
  1314. }
  1315. switch (msg.message)
  1316. {
  1317. case WM_MOUSEMOVE:
  1318. pt.x = GET_X_LPARAM(msg.lParam);
  1319. pt.y = GET_Y_LPARAM(msg.lParam);
  1320. if ((abs(pt.x - point.x) > sizeDrag.cx) ||
  1321. ((abs(pt.y - point.y) > sizeDrag.cy)))
  1322. {
  1323. nDone = 2;
  1324. }
  1325. //because we exit loop, button up will still be dispatched
  1326. // which means WM_CONTEXTMENU will be sent after TVN_BEGINRDRAG
  1327. // - this is the same behavior as original tree
  1328. break;
  1329. case WM_LBUTTONUP:
  1330. case WM_RBUTTONUP:
  1331. nDone = 1;
  1332. break;
  1333. default:
  1334. ::DispatchMessage(&msg);
  1335. break;
  1336. }
  1337. }
  1338. ::ReleaseCapture();
  1339. ASSERT(::GetCapture() != m_pTreeCtrl->m_hWnd);
  1340. //construct tree notification info
  1341. NMTREEVIEW nmtv;
  1342. ZeroMemory(&nmtv, sizeof(NMTREEVIEW));
  1343. nmtv.hdr.hwndFrom = m_pTreeCtrl->m_hWnd;
  1344. nmtv.hdr.idFrom = ::GetDlgCtrlID(m_pTreeCtrl->m_hWnd);
  1345. nmtv.itemNew.mask = TVIF_HANDLE | TVIF_PARAM;
  1346. nmtv.itemNew.hItem = hItem;
  1347. nmtv.itemNew.lParam = m_pTreeCtrl->GetItemData(hItem);
  1348. DWORD dwStyle = m_pTreeCtrl->GetStyle();
  1349. if (nDone == 1)
  1350. {
  1351. //click
  1352. if (!(nFlags & MK_SHIFT) && bLeft)
  1353. {
  1354. if ((nFlags & MK_CONTROL))
  1355. {
  1356. UINT nState = (GetItemState(hItem, TVIS_SELECTED) & TVIS_SELECTED) ? 0 : TVIS_SELECTED;
  1357. SetItemState(hItem, TVIS_FOCUSED | nState, TVIS_FOCUSED | TVIS_SELECTED);
  1358. }
  1359. else
  1360. {
  1361. SelectAllIgnore(FALSE, hItem);
  1362. SetItemState(hItem, TVIS_FOCUSED | TVIS_SELECTED, TVIS_FOCUSED | TVIS_SELECTED);
  1363. }
  1364. }
  1365. if (::GetFocus() != m_pTreeCtrl->m_hWnd)
  1366. {
  1367. ::SetFocus(m_pTreeCtrl->m_hWnd);
  1368. }
  1369. nmtv.hdr.code = bLeft ? NM_CLICK : NM_RCLICK;
  1370. SendNotify(&nmtv.hdr);
  1371. m_bActionDone = true;
  1372. }
  1373. else if (nDone == 2)
  1374. {
  1375. //drag
  1376. SetItemState(hItem, TVIS_FOCUSED | TVIS_SELECTED, TVIS_FOCUSED | TVIS_SELECTED);
  1377. if (!(dwStyle & TVS_DISABLEDRAGDROP))
  1378. {
  1379. nmtv.hdr.code = bLeft ? TVN_BEGINDRAG : TVN_BEGINRDRAG;
  1380. nmtv.ptDrag = point;
  1381. SendNotify(&nmtv.hdr);
  1382. }
  1383. m_bActionDone = true;
  1384. }
  1385. }
  1386. void CXTTreeBase::DoBanding(UINT nFlags, CPoint point)
  1387. {
  1388. if (::GetFocus() != m_pTreeCtrl->m_hWnd)
  1389. {
  1390. ::SetFocus(m_pTreeCtrl->m_hWnd);
  1391. }
  1392. ::SetCapture(m_pTreeCtrl->m_hWnd);
  1393. CTypedPtrList<CPtrList, HTREEITEM> list;
  1394. if (nFlags & (MK_SHIFT | MK_CONTROL))
  1395. {
  1396. GetSelectedList(list);
  1397. }
  1398. CClientDC dc(m_pTreeCtrl);
  1399. CRect rectCli;
  1400. m_pTreeCtrl->GetClientRect(&rectCli);
  1401. MSG msg;
  1402. BOOL bDone = FALSE;
  1403. CPoint pt;
  1404. CSize sizeDrag(::GetSystemMetrics(SM_CXDRAG), ::GetSystemMetrics(SM_CYDRAG));
  1405. BOOL bDrag = FALSE;
  1406. CSize sizeEdge(1, 1);
  1407. UINT_PTR nTimer = m_pTreeCtrl->SetTimer(1, SCROLL_TIMER_PERIOD, NULL);  //for scroll
  1408. CPoint ptScr(m_pTreeCtrl->GetScrollPos(SB_HORZ), m_pTreeCtrl->GetScrollPos(SB_VERT));
  1409. CRect rect(0, 0, 0, 0);
  1410. UINT h = 0;
  1411. HTREEITEM hItem = m_pTreeCtrl->GetRootItem();
  1412. if (hItem)
  1413. {
  1414. m_pTreeCtrl->GetItemRect(hItem, &rect, FALSE);
  1415. ptScr.y *= (h = rect.Height());     //this assumes equal height items
  1416. }
  1417. point += ptScr;
  1418. while (!bDone && ::GetMessage(&msg, NULL, 0, 0))
  1419. {
  1420. if (::GetCapture() != m_pTreeCtrl->m_hWnd)
  1421. {
  1422. break;
  1423. }
  1424. switch (msg.message)
  1425. {
  1426. case WM_TIMER:
  1427. {
  1428. if (msg.wParam == SCROLL_TIMER_PERIOD)
  1429. {
  1430. pt = msg.pt;
  1431. m_pTreeCtrl->ScreenToClient(&pt);
  1432. if (rectCli.PtInRect(pt))
  1433. {
  1434. ::DispatchMessage(&msg);
  1435. break;
  1436. }
  1437. msg.lParam = MAKELPARAM(pt.x, pt.y);
  1438. }
  1439. else
  1440. {
  1441. break;
  1442. }
  1443. }
  1444. //fall through to mouse move
  1445. case WM_MOUSEMOVE:
  1446. pt.x = GET_X_LPARAM(msg.lParam);
  1447. pt.y = GET_Y_LPARAM(msg.lParam);
  1448. if (!bDrag)
  1449. {
  1450. if ((abs(pt.x - point.x) <= sizeDrag.cx) &&
  1451. ((abs(pt.y - point.y) <= sizeDrag.cy)))
  1452. {
  1453. break;
  1454. }
  1455. bDrag = TRUE;
  1456. if (!(nFlags & (MK_CONTROL | MK_SHIFT)))
  1457. {
  1458. SelectAll(FALSE);
  1459. }
  1460. m_pTreeCtrl->UpdateWindow();
  1461. rect.SetRect(point, point);
  1462. dc.DrawDragRect(rect, sizeEdge, NULL, sizeEdge);
  1463. }
  1464. dc.DrawDragRect(rect, sizeEdge, NULL, sizeEdge);    //delete
  1465. if (pt.y < rectCli.top)
  1466. {
  1467. ::SendMessage(m_pTreeCtrl->m_hWnd, WM_VSCROLL, SB_LINEUP, 0);
  1468. }
  1469. else if (pt.y >= rectCli.bottom)
  1470. {
  1471. ::SendMessage(m_pTreeCtrl->m_hWnd, WM_VSCROLL, SB_LINEDOWN, 0);
  1472. }
  1473. if (pt.x < rectCli.left)
  1474. {
  1475. ::SendMessage(m_pTreeCtrl->m_hWnd, WM_HSCROLL, SB_LINELEFT, 0);
  1476. }
  1477. else if (pt.x >= rectCli.right)
  1478. {
  1479. ::SendMessage(m_pTreeCtrl->m_hWnd, WM_HSCROLL, SB_LINERIGHT, 0);
  1480. }
  1481. ptScr = point;
  1482. ptScr.x -= m_pTreeCtrl->GetScrollPos(SB_HORZ);
  1483. ptScr.y -= m_pTreeCtrl->GetScrollPos(SB_VERT) * h;
  1484. rect.SetRect(ptScr, pt);
  1485. rect.NormalizeRect();
  1486. UpdateSelectionForRect(rect, nFlags, list);
  1487. dc.DrawDragRect(rect, sizeEdge, NULL, sizeEdge);    //draw
  1488. break;
  1489. case WM_LBUTTONUP:
  1490. case WM_RBUTTONUP:
  1491. bDone = TRUE;
  1492. break;
  1493. case WM_KEYDOWN:
  1494. if (LOWORD(msg.wParam) == VK_ESCAPE)
  1495. {
  1496. SelectAll(FALSE);
  1497. bDone = TRUE;
  1498. break;
  1499. }
  1500. //dispatch
  1501. default:
  1502. ::DispatchMessage(&msg);
  1503. break;
  1504. }
  1505. }
  1506. m_pTreeCtrl->KillTimer(nTimer);
  1507. ::ReleaseCapture();
  1508. if (bDrag)
  1509. {
  1510. dc.DrawDragRect(rect, sizeEdge, NULL, sizeEdge);
  1511. }
  1512. else
  1513. {
  1514. if (!(nFlags & (MK_CONTROL | MK_SHIFT)))
  1515. {
  1516. SelectAll(FALSE);
  1517. }
  1518. }
  1519. }
  1520. void CXTTreeBase::UpdateSelectionForRect(LPCRECT pRect, UINT nFlags, CTypedPtrList<CPtrList, HTREEITEM>& list)
  1521. {
  1522. CRect rect;
  1523. BOOL bSel;
  1524. POSITION pos;
  1525. HTREEITEM hItem = m_pTreeCtrl->GetRootItem();
  1526. while (hItem)
  1527. {
  1528. bSel = IsSelected(hItem);
  1529. m_pTreeCtrl->GetItemRect(hItem, &rect, m_bBandLabel);
  1530. if (rect.IntersectRect(rect, pRect))
  1531. {
  1532. //item in rect
  1533. pos = list.Find(hItem);
  1534. if (!bSel && !pos)
  1535. {
  1536. SetItemState(hItem, TVIS_SELECTED, TVIS_SELECTED);
  1537. }
  1538. else if ((nFlags & MK_CONTROL) && pos)
  1539. {
  1540. SetItemState(hItem, 0, TVIS_SELECTED);
  1541. }
  1542. else if ((nFlags & MK_SHIFT) && pos)
  1543. {
  1544. list.RemoveAt(pos);     //if shift and in rect, don't lock anymore
  1545. }
  1546. }
  1547. else
  1548. {
  1549. //item not in rect
  1550. pos = list.Find(hItem);
  1551. if (bSel && !pos)
  1552. {
  1553. SetItemState(hItem, 0, TVIS_SELECTED);
  1554. }
  1555. else if (pos)
  1556. {
  1557. SetItemState(hItem, TVIS_SELECTED, TVIS_SELECTED);
  1558. }
  1559. }
  1560. hItem = m_pTreeCtrl->GetNextVisibleItem(hItem);
  1561. }
  1562. m_pTreeCtrl->UpdateWindow();
  1563. }
  1564. BOOL CXTTreeBase::SelectChildren(HTREEITEM hParent, BOOL bSelect /*= TRUE*/, BOOL bRecurse /*= TRUE*/)
  1565. {
  1566. UINT nS = bSelect ? TVIS_SELECTED : 0;
  1567. BOOL bFocusWasInHere = FALSE;
  1568. HTREEITEM hItem = GetNextItem(hParent, TVGN_CHILD);
  1569. while (hItem)
  1570. {
  1571. UINT nState = GetItemState(hItem, TVIS_SELECTED | TVIS_EXPANDED | TVIS_FOCUSED);
  1572. if ((nState & TVIS_SELECTED) != nS)
  1573. {
  1574. SetItemState(hItem, nS, TVIS_SELECTED);
  1575. }
  1576. bFocusWasInHere |= (nState & TVIS_FOCUSED);
  1577. if (bRecurse && (nState & TVIS_EXPANDED))
  1578. {
  1579. bFocusWasInHere |= SelectChildren(hItem, bSelect, bRecurse);
  1580. }
  1581. hItem = m_pTreeCtrl->GetNextSiblingItem(hItem);
  1582. }
  1583. return bFocusWasInHere;
  1584. }
  1585. void CXTTreeBase::GetSelectedList(CTypedPtrList<CPtrList, HTREEITEM>& list) const
  1586. {
  1587. list.RemoveAll();
  1588. HTREEITEM hItem = GetFirstSelectedItem();
  1589. while (hItem)
  1590. {
  1591. list.AddTail(hItem);
  1592. hItem = GetNextSelectedItem(hItem);
  1593. }
  1594. }
  1595. BOOL CXTTreeBase::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
  1596. {
  1597. NMHDR* pNMHDR = (NMHDR*)lParam;
  1598. switch (pNMHDR->code)
  1599. {
  1600. case TTN_SHOW:
  1601. {
  1602. // get a pointer to the tooltip control.
  1603. HWND hWnd = TreeView_GetToolTips(m_pTreeCtrl->m_hWnd);
  1604. if (hWnd != NULL)
  1605. {
  1606. // make sure the tooltip is at the top of the "Z" order, otherwise
  1607. // it will appear behind popup windows....
  1608. ::SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0,
  1609. SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  1610. }
  1611. }
  1612. break;
  1613. }
  1614. return m_pTreeCtrl->CTreeCtrl::OnNotify(wParam, lParam, pResult);
  1615. }
  1616. BOOL CXTTreeBase::PreTranslateMessage(MSG* pMsg)
  1617. {
  1618. if (pMsg->message == WM_KEYDOWN)
  1619. {
  1620. // ensure that keystrokes are handled by the edit control.
  1621. if (HasEditLabels() && m_pTreeCtrl->GetEditControl())
  1622. {
  1623. ::TranslateMessage(pMsg);
  1624. ::DispatchMessage(pMsg);
  1625. return TRUE;
  1626. }
  1627. // toggle expand / contract when return key is hit.
  1628. if (pMsg->wParam == VK_RETURN)
  1629. {
  1630. HTREEITEM htItem = m_pTreeCtrl->GetSelectedItem();
  1631. if (htItem != NULL)
  1632. {
  1633. m_pTreeCtrl->Expand(htItem, TVE_TOGGLE);
  1634. }
  1635. }
  1636. }
  1637. return m_pTreeCtrl->CTreeCtrl::PreTranslateMessage(pMsg);
  1638. }
  1639. BOOL CXTTreeBase::OnDeleteItem(NMHDR* pNMHDR, LRESULT* pResult)
  1640. {
  1641. NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
  1642. // Remove the tree item from the map.
  1643. m_mapColorFont.RemoveKey(pNMTreeView->itemOld.hItem);
  1644. *pResult = 0;
  1645. return FALSE;
  1646. }
  1647. bool CXTTreeBase::HasEditLabels() const
  1648. {
  1649. return ((m_pTreeCtrl->GetStyle() & TVS_EDITLABELS) == TVS_EDITLABELS);
  1650. }
  1651. void CXTTreeBase::OnSize(UINT /*nType*/, int /*cx*/, int /*cy*/)
  1652. {
  1653. m_pTreeCtrl->Default();
  1654. // MFCBUG: During label editing if the tree is resized while
  1655. // there is an edit box open the edit box is not moved to the
  1656. // new location of the tree item, but is left in it previous
  1657. // location.
  1658. if (HasEditLabels() && m_htiEdit)
  1659. {
  1660. CEdit* pEdit = m_pTreeCtrl->GetEditControl();
  1661. if (pEdit && ::IsWindow(pEdit->m_hWnd))
  1662. {
  1663. CRect rcEdit;
  1664. pEdit->GetWindowRect(&rcEdit);
  1665. CRect rcItem;
  1666. m_pTreeCtrl->GetItemRect(m_htiEdit, &rcItem, TRUE);
  1667. rcItem.top -= 1;
  1668. rcItem.left -= 3;
  1669. rcItem.right = rcItem.left + rcEdit.Width();
  1670. rcItem.bottom = rcItem.top + rcEdit.Height();
  1671. pEdit->MoveWindow(&rcItem);
  1672. }
  1673. }
  1674. }
  1675. void CXTTreeBase::OnMouseMove(UINT /*nFlags*/, CPoint point)
  1676. {
  1677. if (m_pTreeCtrl->GetStyle() & TVS_TRACKSELECT)
  1678. {
  1679. TV_HITTESTINFO hit;
  1680. ::ZeroMemory(&hit, sizeof(hit));
  1681. hit.pt = point;
  1682. m_pTreeCtrl->HitTest(&hit);
  1683. HTREEITEM hItem = hit.hItem && hit.flags & TVHT_ONITEM ? hit.hItem : NULL;
  1684. if (m_htiLast != hItem)
  1685. {
  1686. m_htiLast = hItem;
  1687. if (m_htiLast)
  1688. {
  1689. UINT_PTR uID = m_pTreeCtrl->SetTimer(HOVER_TIMER_PERIOD, 55, NULL);
  1690. ASSERT(uID == HOVER_TIMER_PERIOD);
  1691. }
  1692. m_pTreeCtrl->Invalidate(FALSE);
  1693. }
  1694. }
  1695. m_pTreeCtrl->Default();
  1696. }
  1697. void CXTTreeBase::OnTimer(UINT_PTR nIDEvent)
  1698. {
  1699. if (m_pTreeCtrl->GetStyle() & TVS_TRACKSELECT)
  1700. {
  1701. if (nIDEvent == HOVER_TIMER_PERIOD)
  1702. {
  1703. if (m_htiLast != NULL)
  1704. {
  1705. CPoint pt(::GetMessagePos());
  1706. CRect rc;
  1707. m_pTreeCtrl->GetWindowRect(&rc);
  1708. if (rc.PtInRect(pt) == FALSE)
  1709. {
  1710. m_pTreeCtrl->KillTimer(HOVER_TIMER_PERIOD);
  1711. m_htiLast = NULL;
  1712. m_pTreeCtrl->Invalidate(FALSE);
  1713. }
  1714. }
  1715. else
  1716. {
  1717. m_pTreeCtrl->KillTimer(HOVER_TIMER_PERIOD);
  1718. }
  1719. return;
  1720. }
  1721. }
  1722. m_pTreeCtrl->Default();
  1723. }
  1724. void CXTTreeBase::OnNcMouseMove(UINT /*nHitTest*/, CPoint /*point*/)
  1725. {
  1726. if (m_pTreeCtrl->GetStyle() & TVS_TRACKSELECT)
  1727. {
  1728. if (m_htiLast)
  1729. {
  1730. m_htiLast = NULL;
  1731. m_pTreeCtrl->Invalidate(FALSE);
  1732. }
  1733. }
  1734. m_pTreeCtrl->Default();
  1735. }
  1736. bool CXTTreeBase::Init()
  1737. {
  1738. if (!::IsWindow(m_pTreeCtrl->GetSafeHwnd()))
  1739. return false;
  1740. return true;
  1741. }