home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectInput / DIConfig / flextree.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-08  |  27.4 KB  |  1,324 lines

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