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.

1323 lines
26 KiB

  1. //-----------------------------------------------------------------------------
  2. // File: flextree.cpp
  3. //
  4. // Desc: Implements a tree class, similar to a Windows tree control,
  5. // based on CFlexWnd. It is used by the page to display the action
  6. // list when the user wishes to assign an action to a control.
  7. //
  8. // Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
  9. //-----------------------------------------------------------------------------
  10. #include "common.hpp"
  11. CFlexTree::CFlexTree() :
  12. m_pRoot(NULL), m_bDirty(FALSE), m_bNeedPaintBkgnd(FALSE),
  13. m_rgbBkColor(RGB(255,255,255)),
  14. m_pCurSel(NULL), m_pLastAdded(NULL), m_bOwnerDraw(FALSE),
  15. m_bVertSB(FALSE), m_bHorzSB(FALSE),
  16. m_nVertSBWidth(11), m_nHorzSBHeight(11)
  17. {
  18. RECT z = {0,0,0,0};
  19. m_defmargin = z;
  20. m_clDefNormal.dwMask = CLMF_ALL;
  21. m_clDefSelected.dwMask = CLMF_ALL;
  22. m_ptScrollOrigin.x = m_ptScrollOrigin.y = 0;
  23. // allocate root
  24. m_pRoot = new CFTItem;
  25. if (m_pRoot != NULL)
  26. {
  27. m_pRoot->SetRoot(this);
  28. assert(m_pRoot->IsRoot());
  29. }
  30. }
  31. CFlexTree::~CFlexTree()
  32. {
  33. if (m_pRoot)
  34. delete m_pRoot;
  35. m_pRoot = NULL;
  36. }
  37. CFTItem::CFTItem()
  38. {
  39. Init();
  40. }
  41. void CFTItem::Init()
  42. {
  43. m_pUserData = NULL;
  44. m_bExpanded = FALSE;
  45. m_pTree = NULL;
  46. m_pParent = NULL;
  47. m_pPrev = NULL;
  48. m_pNext = NULL;
  49. m_pFirst = NULL;
  50. m_pLast = NULL;
  51. m_ptszCaption = NULL;
  52. m_nIndent = m_nWidth = m_nHeight = m_nBranchHeight = 0;
  53. m_nChildIndent = 11;
  54. RECT z = {0,0,0,0};
  55. m_margin = z;
  56. memset(&m_UserGUID, 0, sizeof(GUID));
  57. SetCaption(TEXT(""));
  58. }
  59. CFTItem::~CFTItem()
  60. {
  61. // detach from parent (unless root)
  62. if (!IsRoot())
  63. Detach();
  64. // remove all children
  65. FreeChildren();
  66. // free stuff
  67. SetCaption(NULL);
  68. }
  69. void CFTItem::SetRoot(CFlexTree *pTree)
  70. {
  71. if (pTree == NULL)
  72. {
  73. assert(0);
  74. return;
  75. }
  76. SetTree(pTree);
  77. m_nIndent = 0;
  78. m_nHeight = 0;
  79. m_nWidth = 0;
  80. m_nChildIndent = 0;
  81. m_bExpanded = TRUE;
  82. POINT origin = {0, 0};
  83. m_origin = origin;
  84. }
  85. BOOL CFTItem::IsRoot() const
  86. {
  87. return m_pTree != NULL && m_pParent == NULL;
  88. }
  89. CFTItem *CFlexTree::GetFirstItem() const
  90. {
  91. if (m_pRoot == NULL)
  92. return NULL;
  93. return m_pRoot->GetFirstChild();
  94. }
  95. CFTItem *CFlexTree::GetLastItem() const
  96. {
  97. if (m_pRoot == NULL)
  98. return NULL;
  99. return m_pRoot->GetLastChild();
  100. }
  101. BOOL CFTItem::IsOnTree() const
  102. {
  103. return m_pTree != NULL;
  104. }
  105. BOOL CFTItem::IsAttached() const
  106. {
  107. if (IsRoot())
  108. {
  109. assert(0);
  110. return TRUE;
  111. }
  112. return m_pParent != NULL;
  113. }
  114. BOOL CFTItem::IsAlone() const
  115. {
  116. return
  117. m_pTree == NULL &&
  118. m_pParent == NULL &&
  119. m_pPrev == NULL &&
  120. m_pNext == NULL &&
  121. m_pFirst == NULL &&
  122. m_pLast == NULL;
  123. }
  124. void CFTItem::FreeChildren()
  125. {
  126. while (m_pFirst != NULL)
  127. {
  128. CFTItem *pChild = m_pFirst;
  129. delete pChild;
  130. }
  131. }
  132. #define FORALLCHILDREN(pChild) \
  133. for (CFTItem *pChild = m_pFirst; pChild != NULL; pChild = pChild->m_pNext)
  134. void CFTItem::SetTree(CFlexTree *pTree)
  135. {
  136. // don't do anything if we've already got this tree
  137. if (m_pTree == pTree)
  138. return;
  139. // if we are currently on a tree, tell it to lose any potential dangling pointers to us
  140. if (m_pTree)
  141. m_pTree->LosePointer(this);
  142. // actually set this tree
  143. m_pTree = pTree;
  144. // set all children to this tree
  145. FORALLCHILDREN(pChild)
  146. pChild->SetTree(pTree);
  147. }
  148. void CFTItem::Detach()
  149. {
  150. // don't allow root detachment
  151. if (IsRoot())
  152. {
  153. assert(0);
  154. return;
  155. }
  156. // if we're already detached, do nothing
  157. if (!IsAttached())
  158. return;
  159. // unlink from parent
  160. if (m_pParent->m_pFirst == this)
  161. m_pParent->m_pFirst = m_pNext;
  162. if (m_pParent->m_pLast == this)
  163. m_pParent->m_pLast = m_pPrev;
  164. m_pParent = NULL;
  165. // unlink from siblings
  166. if (m_pPrev)
  167. m_pPrev->m_pNext = m_pNext;
  168. if (m_pNext)
  169. m_pNext->m_pPrev = m_pPrev;
  170. m_pPrev = m_pNext = NULL;
  171. // save tree because we're about to lose it
  172. CFlexTree *pTree = m_pTree;
  173. // tell ourself and all children that we are no longer on a tree
  174. SetTree(NULL);
  175. // the tree needs to be recalced
  176. if (pTree)
  177. SetTreeDirty(pTree);
  178. }
  179. BOOL CFTItem::Attach(CFTItem *to, ATTACHREL rel)
  180. {
  181. // can't attach root to anything, can't attach to nothing, and can't attach if already attached
  182. if (IsRoot() || to == NULL || IsAttached())
  183. {
  184. assert(0);
  185. return FALSE;
  186. }
  187. // first make sure we're not attaching to root in an impossible way
  188. if (to->IsRoot())
  189. switch (rel)
  190. {
  191. // only the following are valid for attaching to root
  192. case ATTACH_FIRSTCHILD:
  193. case ATTACH_LASTCHILD:
  194. break;
  195. // all others are invalid
  196. default:
  197. assert(0);
  198. return FALSE;
  199. }
  200. // now convert attaching as first/last sibling to equiv first/last child of parent
  201. switch (rel)
  202. {
  203. case ATTACH_FIRSTSIBLING:
  204. return Attach(to->m_pParent, ATTACH_FIRSTCHILD);
  205. case ATTACH_LASTSIBLING:
  206. return Attach(to->m_pParent, ATTACH_LASTCHILD);
  207. }
  208. // send to the more specific attach function
  209. switch (rel)
  210. {
  211. case ATTACH_FIRSTCHILD:
  212. return Attach(to, NULL, to->m_pFirst);
  213. case ATTACH_LASTCHILD:
  214. return Attach(to, to->m_pLast, NULL);
  215. case ATTACH_BEFORE:
  216. return Attach(to->m_pParent, to->m_pPrev, to);
  217. case ATTACH_AFTER:
  218. return Attach(to->m_pParent, to, to->m_pNext);
  219. default:
  220. assert(0); // unhandled rel
  221. return FALSE;
  222. }
  223. }
  224. BOOL CFTItem::Attach(CFTItem *pParent, CFTItem *pPrev, CFTItem *pNext)
  225. {
  226. // can't attach root to anything, can't attach to no parent, and can't attach if already attached
  227. if (IsRoot() || pParent == NULL || IsAttached())
  228. {
  229. assert(0);
  230. return FALSE;
  231. }
  232. // prev/next, if provided, must be children of parent
  233. if ((pPrev && pPrev->m_pParent != pParent) ||
  234. (pNext && pNext->m_pParent != pParent))
  235. {
  236. assert(0);
  237. return FALSE;
  238. }
  239. // pPrev and pNext must be consecutive
  240. if ((pPrev && pPrev->m_pNext != pNext) ||
  241. (pNext && pNext->m_pPrev != pPrev))
  242. {
  243. assert(0);
  244. return FALSE;
  245. }
  246. // insert
  247. if (pPrev)
  248. pPrev->m_pNext = this;
  249. else
  250. pParent->m_pFirst = this;
  251. if (pNext)
  252. pNext->m_pPrev = this;
  253. else
  254. pParent->m_pLast = this;
  255. // attach
  256. m_pParent = pParent;
  257. m_pPrev = pPrev;
  258. m_pNext = pNext;
  259. // set the tree
  260. SetTree(pParent->m_pTree);
  261. // tree needs to be recalced
  262. SetTreeDirty();
  263. return TRUE;
  264. }
  265. void CFlexTree::SetDirty()
  266. {
  267. m_bDirty = TRUE;
  268. Invalidate();
  269. }
  270. void CFTItem::SetTreeDirty(CFlexTree *pTree)
  271. {
  272. if (pTree == NULL)
  273. pTree = m_pTree;
  274. if (pTree)
  275. pTree->SetDirty();
  276. }
  277. void CFTItem::SetWidth(int i)
  278. {
  279. if (m_nWidth == i)
  280. return;
  281. m_nWidth = i;
  282. SetTreeDirty();
  283. }
  284. void CFTItem::SetHeight(int i)
  285. {
  286. if (m_nHeight == i)
  287. return;
  288. m_nHeight = i;
  289. SetTreeDirty();
  290. }
  291. void CFTItem::SetIndent(int i)
  292. {
  293. if (m_nIndent == i)
  294. return;
  295. m_nIndent = i;
  296. SetTreeDirty();
  297. }
  298. void CFTItem::SetChildIndent(int i)
  299. {
  300. if (m_nChildIndent == i)
  301. return;
  302. m_nChildIndent = i;
  303. SetTreeDirty();
  304. }
  305. void CFlexTree::OnPaint(HDC hDC)
  306. {
  307. HDC hBDC = NULL, hODC = NULL;
  308. CBitmap *pbm = NULL;
  309. m_bNeedPaintBkgnd = TRUE;
  310. if (!InRenderMode())
  311. {
  312. hODC = hDC;
  313. pbm = CBitmap::Create(GetClientSize(), m_rgbBkColor, hDC);
  314. if (pbm != NULL)
  315. {
  316. hBDC = pbm->BeginPaintInto();
  317. if (hBDC != NULL)
  318. {
  319. hDC = hBDC;
  320. m_bNeedPaintBkgnd = FALSE;
  321. }
  322. }
  323. }
  324. InternalPaint(hDC);
  325. if (!InRenderMode())
  326. {
  327. if (pbm != NULL)
  328. {
  329. if (hBDC != NULL)
  330. {
  331. pbm->EndPaintInto(hBDC);
  332. pbm->Draw(hODC);
  333. }
  334. delete pbm;
  335. }
  336. }
  337. }
  338. void CFlexTree::InternalPaint(HDC hDC)
  339. {
  340. // get client rect
  341. RECT rect;
  342. GetClientRect(&rect);
  343. // get view rect (ideal coordinates we're viewing)
  344. RECT view = rect;
  345. OffsetRect(&view, m_ptScrollOrigin.x, m_ptScrollOrigin.y);
  346. // paint background if necessary
  347. if (m_bNeedPaintBkgnd)
  348. {
  349. HBRUSH hBrush = CreateSolidBrush(m_rgbBkColor);
  350. if (hBrush != NULL)
  351. {
  352. HGDIOBJ hOldBrush = SelectObject(hDC, hBrush);
  353. HGDIOBJ hOldPen = SelectObject(hDC, GetStockObject(NULL_PEN));
  354. RECT t = rect;
  355. InflateRect(&t, 1, 1);
  356. Rectangle(hDC, t.left, t.top, t.right, t.bottom);
  357. SelectObject(hDC, hOldPen);
  358. SelectObject(hDC, hOldBrush);
  359. DeleteObject((HGDIOBJ)hBrush);
  360. }
  361. }
  362. // recalculate if necessary
  363. Calc();
  364. // start with the first visible item
  365. CFTItem *pItem = GetFirstVisibleItem();
  366. // draw until we go out of view
  367. for (; pItem != NULL; pItem = pItem->GetNextOut())
  368. {
  369. RECT irect;
  370. pItem->GetItemRect(irect);
  371. if (irect.top >= view.bottom)
  372. break;
  373. OffsetRect(&irect, -m_ptScrollOrigin.x, -m_ptScrollOrigin.y);
  374. POINT oldorg;
  375. OffsetViewportOrgEx(hDC, irect.left, irect.top, &oldorg);
  376. // TODO: clip
  377. if (!pItem->FireOwnerDraw(hDC))
  378. pItem->OnPaint(hDC);
  379. SetViewportOrgEx(hDC, oldorg.x, oldorg.y, NULL);
  380. }
  381. // Fill in the small square at bottom right corner if we have both scroll bars.
  382. if (m_bVertSB && m_bHorzSB)
  383. {
  384. HBRUSH hBrush = CreateSolidBrush(m_clDefNormal.rgbLineColor);
  385. if (hBrush != NULL)
  386. {
  387. HGDIOBJ hOldBrush = SelectObject(hDC, hBrush);
  388. HGDIOBJ hPen = CreatePen(PS_SOLID, 0, m_clDefNormal.rgbLineColor);
  389. if (hPen != NULL)
  390. {
  391. HGDIOBJ hOldPen = SelectObject(hDC, hPen);
  392. RECT rc = rect;
  393. SIZE size;
  394. size = m_VertSB.GetClientSize();
  395. rc.left = rc.right - size.cx;
  396. size = m_HorzSB.GetClientSize();
  397. rc.top = rc.bottom - size.cy;
  398. Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
  399. SelectObject(hDC, hOldPen);
  400. DeleteObject((HGDIOBJ) hPen);
  401. }
  402. SelectObject(hDC, hOldBrush);
  403. DeleteObject((HGDIOBJ)hBrush);
  404. }
  405. }
  406. }
  407. void CFlexTree::Calc()
  408. {
  409. if (!m_bDirty)
  410. return;
  411. m_bDirty = FALSE;
  412. CalcItems();
  413. BOOL bH = FALSE, bV = FALSE;
  414. if (m_pRoot != NULL)
  415. {
  416. SIZE view = GetClientSize();
  417. SIZE all = {m_nTotalWidth, m_pRoot->m_nBranchHeight};
  418. for (int i = 0; i < 2; i++)
  419. {
  420. // Added GetFirstVisibleItem() check since we don't want scroll bar if nothing is to be
  421. // displayed.
  422. if (!bV && all.cy > view.cy && GetFirstVisibleItem())
  423. {
  424. bV = TRUE;
  425. view.cx -= m_nVertSBWidth;
  426. }
  427. if (!bH && all.cx > view.cx)
  428. {
  429. bH = TRUE;
  430. view.cy -= m_nHorzSBHeight;
  431. }
  432. }
  433. if (bH)
  434. {
  435. m_HorzSB.SetValues(0, all.cx, view.cx, m_ptScrollOrigin.x);
  436. MoveWindow(m_HorzSB.m_hWnd, 0, view.cy, view.cx, m_nHorzSBHeight, TRUE);
  437. }
  438. if (bV)
  439. {
  440. m_VertSB.SetValues(0, all.cy, view.cy, m_ptScrollOrigin.y);
  441. MoveWindow(m_VertSB.m_hWnd, view.cx, 0, m_nVertSBWidth, view.cy, TRUE);
  442. }
  443. }
  444. if (bH && !m_bHorzSB || !bH && m_bHorzSB)
  445. {
  446. ShowWindow(m_HorzSB.m_hWnd, bH ? SW_SHOW : SW_HIDE);
  447. if (!bH)
  448. {
  449. m_ptScrollOrigin.x = 0;
  450. Invalidate();
  451. }
  452. }
  453. m_bHorzSB = bH;
  454. if (bV && !m_bVertSB || !bV && m_bVertSB)
  455. {
  456. ShowWindow(m_VertSB.m_hWnd, bV ? SW_SHOW : SW_HIDE);
  457. if (!bV)
  458. {
  459. m_ptScrollOrigin.y = 0;
  460. Invalidate();
  461. }
  462. }
  463. m_bVertSB = bV;
  464. }
  465. void CFlexTree::CalcItems()
  466. {
  467. // can't do anything without root
  468. if (m_pRoot == NULL)
  469. return;
  470. // calculate the entire tree in out/down order starting with first child of root...
  471. POINT origin = {0, 0};
  472. CFTItem *pItem = m_pRoot->m_pFirst;
  473. m_nTotalWidth = 0;
  474. while (pItem != NULL)
  475. {
  476. // let this item know its out
  477. // get parent origin
  478. CFTItem *pParent = pItem->m_pParent;
  479. assert(pParent != NULL);
  480. // calc origin...
  481. // if we're the first child
  482. if (pItem->m_pPrev == NULL)
  483. {
  484. // base origin on the parent
  485. if (pParent)
  486. {
  487. origin.x = pParent->m_origin.x - pParent->m_nIndent + pParent->m_nChildIndent + pItem->m_nIndent;
  488. origin.y = pParent->m_origin.y + pParent->m_nHeight;
  489. }
  490. }
  491. else // otherwise
  492. {
  493. // base origin on the previous sibling
  494. CFTItem *pPrev = pItem->m_pPrev;
  495. assert(pPrev != NULL);
  496. if (pPrev)
  497. {
  498. origin.x = pPrev->m_origin.x - pPrev->m_nIndent + pItem->m_nIndent;
  499. origin.y = pPrev->m_origin.y + pPrev->m_nBranchHeight;
  500. }
  501. }
  502. // set origin
  503. pItem->m_origin = origin;
  504. // see which direction we'll be going next
  505. CFTItem *pNext = pItem->GetNextOut();
  506. enum {RIGHT, DOWN, BACK, NOWHERE, INVALID} dir = INVALID;
  507. if (pNext == NULL)
  508. dir = NOWHERE;
  509. else if (pNext == pItem->m_pNext)
  510. dir = DOWN;
  511. else if (pNext == pItem->m_pFirst)
  512. dir = RIGHT;
  513. else
  514. dir = BACK;
  515. // if we're going down, back, or nowhere, we can complete this item's branchheight
  516. switch (dir)
  517. {
  518. case DOWN:
  519. case BACK:
  520. case NOWHERE:
  521. pItem->m_nBranchHeight = pItem->m_nHeight;
  522. break;
  523. }
  524. // calc for skipped items when going back
  525. switch (dir)
  526. {
  527. case BACK:
  528. case NOWHERE:
  529. {
  530. CFTItem *pStop = NULL;
  531. if (dir == BACK)
  532. pStop = pNext->m_pParent;
  533. CFTItem *pWalk = pItem->m_pParent;
  534. while (1)
  535. {
  536. if (pWalk == pStop)
  537. break;
  538. pWalk->m_nBranchHeight = pItem->m_origin.y +
  539. pItem->m_nBranchHeight - pWalk->m_origin.y;
  540. pWalk = pWalk->m_pParent;
  541. }
  542. break;
  543. }
  544. case INVALID:
  545. assert(0);
  546. break;
  547. }
  548. RECT rect;
  549. pItem->GetItemRect(rect);
  550. if (rect.right > m_nTotalWidth)
  551. m_nTotalWidth = rect.right;
  552. // now go to next item
  553. pItem = pNext;
  554. }
  555. }
  556. CFTItem *CFTItem::GetNextOut() const
  557. {
  558. return GetNext(TRUE);
  559. }
  560. CFTItem *CFTItem::GetNext(BOOL bOutOnly) const
  561. {
  562. // if we have a child and we're expanded (or we're not looking for out only), return the first child (going 'right')
  563. if ((m_bExpanded || !bOutOnly) && m_pFirst != NULL)
  564. return m_pFirst;
  565. // if we have a next sibling, return it (going 'down')
  566. if (m_pNext != NULL)
  567. return m_pNext;
  568. // climb up parents until we get to one with another next sibling
  569. for (CFTItem *pItem = m_pParent; pItem != NULL; pItem = pItem->m_pParent)
  570. if (pItem->m_pNext != NULL)
  571. return pItem->m_pNext;
  572. // if we didn't find one, we're done
  573. return NULL;
  574. }
  575. void CFTItem::GetItemRect(RECT &rect) const
  576. {
  577. rect.left = m_origin.x;
  578. rect.top = m_origin.y;
  579. rect.right = rect.left + m_nWidth;
  580. rect.bottom = rect.top + m_nHeight;
  581. }
  582. void CFTItem::GetBranchRect(RECT &rect) const
  583. {
  584. rect.left = m_origin.x;
  585. rect.top = m_origin.y;
  586. rect.right = rect.left + m_nWidth;
  587. rect.bottom = rect.top + m_nBranchHeight;
  588. }
  589. void CFTItem::SetCaption(LPCTSTR tszCaption)
  590. {
  591. if (m_ptszCaption != NULL)
  592. free(m_ptszCaption);
  593. if (tszCaption == NULL)
  594. m_ptszCaption = NULL;
  595. else
  596. m_ptszCaption = _tcsdup(tszCaption);
  597. RecalcText();
  598. }
  599. LPCTSTR CFTItem::GetCaption() const
  600. {
  601. return m_ptszCaption;
  602. }
  603. void CFTItem::SetMargin(const RECT &rect)
  604. {
  605. m_margin = rect;
  606. RecalcText();
  607. }
  608. void CFTItem::RecalcText()
  609. {
  610. // calculate size from text dimensions and margin
  611. SIZE size = {0, 0};
  612. if (HasCaption())
  613. {
  614. RECT trect = {0, 0, 1, 1};
  615. HDC hDC = CreateCompatibleDC(NULL);
  616. if (hDC != NULL)
  617. {
  618. HGDIOBJ hOld = NULL;
  619. HFONT hFont = IsSelected() ? m_clSelected.hFont : m_clNormal.hFont;
  620. if (hFont)
  621. hOld = SelectObject(hDC, hFont);
  622. DrawText(hDC, m_ptszCaption, -1, &trect, DT_CALCRECT | DT_NOPREFIX);
  623. if (hFont)
  624. SelectObject(hDC, hOld);
  625. DeleteDC(hDC);
  626. SIZE tsize = {trect.right - trect.left, trect.bottom - trect.top};
  627. size = tsize;
  628. }
  629. }
  630. SetWidth(m_margin.left + m_margin.right + size.cx);
  631. SetHeight(m_margin.top + m_margin.bottom + size.cy);
  632. // redraw
  633. Invalidate();
  634. }
  635. void CFTItem::Invalidate()
  636. {
  637. if (m_pTree)
  638. m_pTree->Invalidate();
  639. }
  640. typedef CArray<LPDIACTIONW, LPDIACTIONW &> RGLPDIACW;
  641. void CFTItem::OnPaint(HDC hDC)
  642. {
  643. CAPTIONLOOK &cl = (IsSelected() && !GetTree()->GetReadOnly()) ? m_clSelected : m_clNormal; // Always use normal color if read-only since we gray out everything.
  644. ::SetBkMode(hDC, cl.nBkMode);
  645. LPDIACTIONW lpac = NULL;
  646. if (m_pUserData)
  647. lpac = ((RGLPDIACW *)m_pUserData)->GetAt(0); // Get the DIACTION this item holds.
  648. if (GetTree()->GetReadOnly() || (lpac && (lpac->dwFlags & DIA_APPFIXED))) // If read-only or the action has DIA_APPFIXED flag, use gray color for texts.
  649. ::SetTextColor(hDC, RGB(GetRValue(cl.rgbTextColor) >> 1, GetGValue(cl.rgbTextColor) >> 1, GetBValue(cl.rgbTextColor) >> 1));
  650. else
  651. ::SetTextColor(hDC, cl.rgbTextColor);
  652. ::SetBkColor(hDC, cl.rgbBkColor);
  653. HGDIOBJ hOld = NULL;
  654. if (cl.hFont)
  655. hOld = SelectObject(hDC, cl.hFont);
  656. RECT trect = {m_margin.left, m_margin.top, m_margin.left + 1, m_margin.top + 1};
  657. DrawText(hDC, m_ptszCaption, -1, &trect, DT_NOPREFIX | DT_NOCLIP);
  658. if (cl.hFont)
  659. SelectObject(hDC, hOld);
  660. }
  661. BOOL CFlexTree::Create(HWND hParent, const RECT &rect, BOOL bVisible, BOOL bOwnerDraw)
  662. {
  663. m_bOwnerDraw = bOwnerDraw;
  664. if (CFlexWnd::Create(hParent, rect, bVisible) == NULL)
  665. return FALSE;
  666. FLEXSCROLLBARCREATESTRUCT cs;
  667. cs.dwSize = sizeof(FLEXSCROLLBARCREATESTRUCT);
  668. cs.min = 0;
  669. cs.max = 10;
  670. cs.page = 3;
  671. cs.pos = 5;
  672. cs.hWndParent = m_hWnd;
  673. cs.hWndNotify = NULL;
  674. cs.rect.left = 0;
  675. cs.rect.top = 0;
  676. cs.rect.right = 5;
  677. cs.rect.bottom = 5;
  678. cs.bVisible = FALSE;
  679. cs.dwFlags = FSBF_VERT;
  680. m_VertSB.Create(&cs);
  681. cs.dwFlags = FSBF_HORZ;
  682. m_HorzSB.Create(&cs);
  683. return TRUE;
  684. }
  685. void CFlexTree::SetDefCaptionLook(const CAPTIONLOOK &cl, BOOL bSel)
  686. {
  687. CAPTIONLOOK &set = bSel ? m_clDefSelected : m_clDefNormal;
  688. if (cl.dwMask & CLMF_TEXTCOLOR)
  689. set.rgbTextColor = cl.rgbTextColor;
  690. if (cl.dwMask & CLMF_BKCOLOR)
  691. set.rgbBkColor = cl.rgbBkColor;
  692. if (cl.dwMask & CLMF_LINECOLOR)
  693. set.rgbLineColor = cl.rgbLineColor;
  694. if (cl.dwMask & CLMF_BKMODE)
  695. set.nBkMode = cl.nBkMode;
  696. if (cl.dwMask & CLMF_BKEXTENDS)
  697. set.bBkExtends = cl.bBkExtends;
  698. if (cl.dwMask & CLMF_FONT)
  699. set.hFont = cl.hFont;
  700. }
  701. void CFlexTree::GetDefCaptionLook(CAPTIONLOOK &cl, BOOL bSel) const
  702. {
  703. const CAPTIONLOOK &from = bSel ? m_clDefSelected : m_clDefNormal;
  704. cl = from;
  705. cl.dwMask = CLMF_ALL;
  706. }
  707. void CFlexTree::SetBkColor(COLORREF rgb)
  708. {
  709. m_rgbBkColor = rgb;
  710. Invalidate();
  711. }
  712. COLORREF CFlexTree::GetBkColor() const
  713. {
  714. return m_rgbBkColor;
  715. }
  716. void CFlexTree::SetCurSel(CFTItem *pItem)
  717. {
  718. if (pItem == m_pCurSel)
  719. return;
  720. CFTItem *pOld = m_pCurSel;
  721. m_pCurSel = pItem;
  722. if (pOld)
  723. pOld->SelChangedInternal();
  724. if (m_pCurSel)
  725. m_pCurSel->SelChangedInternal();
  726. FireSelChanged(m_pCurSel, pOld);
  727. Invalidate();
  728. }
  729. CFTItem *CFlexTree::GetCurSel() const
  730. {
  731. return m_pCurSel;
  732. }
  733. CFTItem *CFlexTree::FindItem(const GUID &guid, void *pUserData) const
  734. {
  735. // go until we get to the item with specified guid and userdata
  736. for (CFTItem *pItem = GetFirstItem(); pItem != NULL; pItem = pItem->GetNext())
  737. if (pItem->IsUserGUID(guid) && pItem->GetUserData() == pUserData)
  738. return pItem;
  739. // unless there isn't one
  740. return NULL;
  741. }
  742. CFTItem *CFlexTree::FindItemEx(const GUID &guid, DWORD dwUser, void *pUser) const
  743. {
  744. // go until we get to the item with specified guid and found item returns true
  745. for (CFTItem *pItem = GetFirstItem(); pItem != NULL; pItem = pItem->GetNext())
  746. if (pItem->IsUserGUID(guid) && pItem->FoundItem(dwUser, pUser))
  747. return pItem;
  748. // unless there isn't one
  749. return NULL;
  750. }
  751. void CFTItem::SetCaptionLook(const CAPTIONLOOK &cl, BOOL bSel)
  752. {
  753. CAPTIONLOOK &set = bSel ? m_clSelected : m_clNormal;
  754. if (cl.dwMask & CLMF_TEXTCOLOR)
  755. set.rgbTextColor = cl.rgbTextColor;
  756. if (cl.dwMask & CLMF_BKCOLOR)
  757. set.rgbBkColor = cl.rgbBkColor;
  758. if (cl.dwMask & CLMF_LINECOLOR)
  759. set.rgbLineColor = cl.rgbLineColor;
  760. if (cl.dwMask & CLMF_BKMODE)
  761. set.nBkMode = cl.nBkMode;
  762. if (cl.dwMask & CLMF_BKEXTENDS)
  763. set.bBkExtends = cl.bBkExtends;
  764. if (cl.dwMask & CLMF_FONT)
  765. set.hFont = cl.hFont;
  766. if (IsSelected() == bSel)
  767. RecalcText();
  768. }
  769. void CFTItem::GetCaptionLook(CAPTIONLOOK &cl, BOOL bSel) const
  770. {
  771. const CAPTIONLOOK &from = bSel ? m_clSelected : m_clNormal;
  772. cl = from;
  773. cl.dwMask = CLMF_ALL;
  774. }
  775. void CFTItem::GetMargin(RECT &rect) const
  776. {
  777. rect = m_margin;
  778. }
  779. void CFTItem::SelChangedInternal()
  780. {
  781. if (m_clNormal.hFont != m_clSelected.hFont)
  782. RecalcText();
  783. }
  784. CFTItem *CFlexTree::DefAddItem(LPCTSTR tszCaption, CFTItem *to, ATTACHREL rel)
  785. {
  786. if (m_pRoot == NULL)
  787. return NULL;
  788. if (!to)
  789. return DefAddItem(tszCaption, rel);
  790. if (!IsMine(to))
  791. {
  792. assert(0); // can't add relative to item that doesn't belong to this tree
  793. return NULL;
  794. }
  795. CFTItem *p = new CFTItem;
  796. if (!p)
  797. return NULL;
  798. p->SetCaptionLook(m_clDefNormal);
  799. p->SetCaptionLook(m_clDefSelected, TRUE);
  800. p->SetChildIndent(m_nDefChildIndent);
  801. p->SetMargin(m_defmargin);
  802. p->SetCaption(tszCaption);
  803. p->Attach(to, rel);
  804. return m_pLastAdded = p;
  805. }
  806. CFTItem *CFlexTree::DefAddItem(LPCTSTR tszCaption, ATTACHREL rel)
  807. {
  808. if (m_pRoot == NULL)
  809. return NULL;
  810. assert(this != NULL);
  811. if (this == NULL) // prevent infinite recursion possibility
  812. return NULL;
  813. if (!m_pLastAdded)
  814. return DefAddItem(tszCaption, m_pRoot, ATTACH_LASTCHILD);
  815. else
  816. return DefAddItem(tszCaption, m_pLastAdded, rel);
  817. }
  818. void CFlexTree::SetDefMargin(const RECT &rect)
  819. {
  820. m_defmargin = rect;
  821. }
  822. void CFlexTree::GetDefMargin(RECT &rect) const
  823. {
  824. rect = m_defmargin;
  825. }
  826. BOOL CFlexTree::IsMine(CFTItem *pItem)
  827. {
  828. if (pItem == NULL)
  829. return FALSE;
  830. return pItem->m_pTree == this;
  831. }
  832. BOOL CFTItem::IsSelected() const
  833. {
  834. if (!m_pTree)
  835. return FALSE;
  836. return m_pTree->m_pCurSel == this;
  837. }
  838. CFTItem *CFlexTree::GetFirstVisibleItem() const
  839. {
  840. // get view rect (ideal coordinates we're viewing)
  841. RECT view;
  842. GetClientRect(&view);
  843. OffsetRect(&view, m_ptScrollOrigin.x, m_ptScrollOrigin.y);
  844. // start at first child of root
  845. CFTItem *pItem = m_pRoot->GetFirstChild();
  846. if (pItem == NULL)
  847. return NULL;
  848. // find first item in view
  849. RECT branch, irect;
  850. while (1)
  851. {
  852. // find first branch in view
  853. while (1)
  854. {
  855. pItem->GetBranchRect(branch);
  856. if (branch.bottom > view.top)
  857. break;
  858. pItem = pItem->GetNextSibling();
  859. if (pItem == NULL)
  860. return NULL;
  861. }
  862. // now actually go through items
  863. pItem->GetItemRect(irect);
  864. if (irect.bottom > view.top)
  865. break;
  866. pItem = pItem->GetNextOut();
  867. if (pItem == NULL)
  868. return NULL;
  869. }
  870. // we got it, so return it
  871. return pItem;
  872. }
  873. CFTItem *CFlexTree::GetItemFromPoint(POINT point) const
  874. {
  875. if (m_hWnd == NULL)
  876. return NULL;
  877. RECT rect;
  878. GetClientRect(&rect);
  879. if (!PtInRect(&rect, point))
  880. return NULL;
  881. for (CFTItem *pItem = GetFirstVisibleItem(); pItem != NULL; pItem = pItem->GetNextOut())
  882. {
  883. RECT irect;
  884. pItem->GetItemRect(irect);
  885. OffsetRect(&irect, -m_ptScrollOrigin.x, -m_ptScrollOrigin.y);
  886. if (irect.top >= rect.bottom)
  887. return NULL;
  888. if (PtInRect(&irect, point))
  889. return pItem;
  890. }
  891. return NULL;
  892. }
  893. void CFlexTree::OnMouseOver(POINT point, WPARAM fwKeys)
  894. {
  895. // Send mouse over notification to page to update info box.
  896. HWND hParent = ::GetParent(m_hWnd);
  897. if (hParent)
  898. SendMessage(hParent, WM_FLEXTREENOTIFY, FTN_MOUSEOVER, NULL);
  899. CFTItem *pItem = GetItemFromPoint(point);
  900. if (!pItem)
  901. return;
  902. POINT rel = {point.x - pItem->m_origin.x, point.y - pItem->m_origin.y};
  903. pItem->OnMouseOver(point, fwKeys);
  904. }
  905. void CFlexTree::OnClick(POINT point, WPARAM fwKeys, BOOL bLeft)
  906. {
  907. // If the tree is read-only, ignore all clicks.
  908. if (GetReadOnly())
  909. return;
  910. CFTItem *pItem = GetItemFromPoint(point);
  911. if (!pItem)
  912. {
  913. FireClick(NULL, point, fwKeys, bLeft);
  914. return;
  915. }
  916. POINT rel = {point.x - pItem->m_origin.x, point.y - pItem->m_origin.y};
  917. pItem->OnClick(point, fwKeys, bLeft);
  918. }
  919. void CFTItem::OnClick(POINT point, WPARAM fwKeys, BOOL bLeft)
  920. {
  921. FireClick(point, fwKeys, bLeft);
  922. }
  923. void CFlexTree::OnWheel(POINT point, WPARAM wParam)
  924. {
  925. if (!m_bVertSB) return;
  926. int nPage = MulDiv(m_VertSB.GetPage(), 9, 10) >> 1; // Half a page at a time
  927. if ((int)wParam >= 0)
  928. m_VertSB.AdjustPos(-nPage);
  929. else
  930. m_VertSB.AdjustPos(nPage);
  931. m_ptScrollOrigin.y = m_VertSB.GetPos();
  932. if (m_ptScrollOrigin.y < 0)
  933. m_ptScrollOrigin.y = 0;
  934. Invalidate();
  935. }
  936. void CFlexTree::FireClick(CFTItem *pItem, POINT point, WPARAM fwKeys, BOOL bLeft)
  937. {
  938. FLEXTREENOTIFY n;
  939. n.pTree = this;
  940. n.pItem = pItem;
  941. n.pOldItem = NULL;
  942. n.hDC = NULL;
  943. n.point = point;
  944. n.fwKeys = fwKeys;
  945. n.bLeft = bLeft;
  946. HWND hParent = ::GetParent(m_hWnd);
  947. if (hParent)
  948. SendMessage(hParent, WM_FLEXTREENOTIFY, FTN_CLICK, (LRESULT)(LPVOID)&n);
  949. }
  950. BOOL CFlexTree::FireOwnerDraw(CFTItem *pItem, HDC hDC)
  951. {
  952. if (!m_bOwnerDraw)
  953. return FALSE;
  954. FLEXTREENOTIFY n;
  955. n.pTree = this;
  956. n.pItem = pItem;
  957. n.pOldItem = NULL;
  958. n.hDC = hDC;
  959. HWND hParent = ::GetParent(m_hWnd);
  960. if (hParent)
  961. return (BOOL)SendMessage(hParent, WM_FLEXTREENOTIFY, FTN_OWNERDRAW, (LRESULT)(LPVOID)&n);
  962. else
  963. return FALSE;
  964. }
  965. void CFlexTree::FireSelChanged(CFTItem *pItem, CFTItem *pOld)
  966. {
  967. assert(pItem == m_pCurSel);
  968. FLEXTREENOTIFY n;
  969. n.pTree = this;
  970. n.pItem = pItem;
  971. n.pOldItem = pOld;
  972. n.hDC = NULL;
  973. HWND hParent = ::GetParent(m_hWnd);
  974. if (hParent)
  975. SendMessage(hParent, WM_FLEXTREENOTIFY, FTN_SELCHANGED, (LRESULT)(LPVOID)&n);
  976. }
  977. void CFTItem::FireClick(POINT point, WPARAM fwKeys, BOOL bLeft)
  978. {
  979. if (m_pTree)
  980. m_pTree->FireClick(this, point, fwKeys, bLeft);
  981. }
  982. BOOL CFTItem::FireOwnerDraw(HDC hDC)
  983. {
  984. if (m_pTree)
  985. return m_pTree->FireOwnerDraw(this, hDC);
  986. else
  987. return FALSE;
  988. }
  989. void CFTItem::Expand(BOOL bAll)
  990. {
  991. InternalExpand(TRUE, bAll);
  992. }
  993. void CFTItem::Collapse(BOOL bAll)
  994. {
  995. InternalExpand(FALSE, bAll);
  996. }
  997. void CFTItem::InternalExpand(BOOL bExpand, BOOL bAll)
  998. {
  999. if (!HasChildren())
  1000. return;
  1001. BOOL bE = m_bExpanded;
  1002. if (!IsRoot())
  1003. m_bExpanded = bExpand;
  1004. if (bAll)
  1005. FORALLCHILDREN(pChild)
  1006. pChild->InternalExpand(bExpand, TRUE);
  1007. if (bE != m_bExpanded)
  1008. SetTreeDirty();
  1009. }
  1010. BOOL CFTItem::IsOut() const
  1011. {
  1012. CFTItem *pParent = GetParent();
  1013. for (; pParent != NULL; pParent = pParent->GetParent())
  1014. if (!pParent->IsExpanded())
  1015. return FALSE;
  1016. return TRUE;
  1017. }
  1018. void CFlexTree::FreeAll()
  1019. {
  1020. if (m_pRoot)
  1021. m_pRoot->FreeChildren();
  1022. }
  1023. void CFTItem::EnsureVisible()
  1024. {
  1025. // TBD
  1026. }
  1027. void CFlexTree::LosePointer(CFTItem *pItem)
  1028. {
  1029. if (m_pCurSel == pItem)
  1030. SetCurSel(NULL);
  1031. if (m_pLastAdded == pItem)
  1032. m_pLastAdded = NULL;
  1033. }
  1034. void CFlexTree::SetRootChildIndent(int i)
  1035. {
  1036. if (!m_pRoot)
  1037. return;
  1038. m_pRoot->m_nChildIndent = i;
  1039. SetDirty();
  1040. }
  1041. int CFlexTree::GetRootChildIndent() const
  1042. {
  1043. if (!m_pRoot)
  1044. return 0;
  1045. return m_pRoot->m_nChildIndent;
  1046. }
  1047. void CFlexTree::SetDefChildIndent(int i)
  1048. {
  1049. m_nDefChildIndent = i;
  1050. }
  1051. int CFlexTree::GetDefChildIndent() const
  1052. {
  1053. return m_nDefChildIndent;
  1054. }
  1055. void CFTItem::PaintInto(HDC hDC)
  1056. {
  1057. if (hDC != NULL)
  1058. OnPaint(hDC);
  1059. }
  1060. LRESULT CFlexTree::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  1061. {
  1062. switch (msg)
  1063. {
  1064. case WM_FLEXVSCROLL:
  1065. case WM_FLEXHSCROLL:
  1066. {
  1067. int code = (int)wParam;
  1068. CFlexScrollBar *pSB = (CFlexScrollBar *)lParam;
  1069. if (!pSB)
  1070. return 0;
  1071. int nLine = 5;
  1072. int nPage = MulDiv(pSB->GetPage(), 9, 10);
  1073. switch (code)
  1074. {
  1075. case SB_LINEUP: pSB->AdjustPos(-nLine); break;
  1076. case SB_LINEDOWN: pSB->AdjustPos(nLine); break;
  1077. case SB_PAGEUP: pSB->AdjustPos(-nPage); break;
  1078. case SB_PAGEDOWN: pSB->AdjustPos(nPage); break;
  1079. case SB_THUMBTRACK: pSB->SetPos(pSB->GetThumbPos()); break;
  1080. }
  1081. switch (msg)
  1082. {
  1083. case WM_FLEXHSCROLL:
  1084. m_ptScrollOrigin.x = pSB->GetPos();
  1085. break;
  1086. case WM_FLEXVSCROLL:
  1087. m_ptScrollOrigin.y = pSB->GetPos();
  1088. break;
  1089. }
  1090. Invalidate();
  1091. return 0;
  1092. }
  1093. default:
  1094. return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
  1095. }
  1096. }
  1097. void CFlexTree::SetScrollBarColors(COLORREF bk, COLORREF fill, COLORREF line)
  1098. {
  1099. m_VertSB.SetColors(bk, fill, line);
  1100. m_HorzSB.SetColors(bk, fill, line);
  1101. }