// PropTreeItem.cpp // // Copyright (C) 1998-2001 Scott Ramsay // sramsay@gonavi.com // http://www.gonavi.com // // This material is provided "as is", with absolutely no warranty expressed // or implied. Any use is at your own risk. // // Permission to use or copy this software for any purpose is hereby granted // without fee, provided the above notices are retained on all copies. // Permission to modify the code and to distribute modified code is granted, // provided the above notices are retained, and a notice that the code was // modified is included with the above copyright notice. // // If you use this code, drop me an email. I'd like to know if you find the code // useful. #include "stdafx.h" #include "PropCtrl.h" #include "PropItem.h" // move to CPropCtrl.h // #define PROPTREEITEM_DEFHEIGHT 40 // default heigt of an item // #define PROPTREEITEM_SPACE 5 // default horz spacing // #define PROPTREEITEM_EXPANDBOX 9 // size of the expand box // #define PROPTREEITEM_CHECKBOX 14 // size of the check box // #define PROPTREEITEM_EXPANDCOLUMN 16 // width of the expand column // #define PNINDENT 16 // child level indent #define PROPTREEITEM_EXPANDBOXHALF (PROPTREEITEM_EXPANDBOX/2) ///////////////////////////////////////////////////////////////////////////// // drawing helper functions // // draw a dotted horizontal line static void _DotHLine(HDC hdc, LONG x, LONG y, LONG w) { for (; w>0; w-=2, x+=2) SetPixel(hdc, x, y, GetSysColor(COLOR_BTNSHADOW)); } // draw the plus/minus button static void _DrawExpand(HDC hdc, LONG x, LONG y, BOOL bExpand, BOOL bFill) { HPEN hPen; HPEN oPen; HBRUSH oBrush; hPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW)); oPen = (HPEN)SelectObject(hdc, hPen); oBrush = (HBRUSH)SelectObject(hdc, GetStockObject(bFill ? WHITE_BRUSH : NULL_BRUSH)); Rectangle(hdc, x, y, x + PROPTREEITEM_EXPANDBOX, y + PROPTREEITEM_EXPANDBOX); SelectObject(hdc, GetStockObject(BLACK_PEN)); if (!bExpand) { MoveToEx(hdc, x + PROPTREEITEM_EXPANDBOXHALF, y + 2, NULL); LineTo(hdc, x + PROPTREEITEM_EXPANDBOXHALF, y + PROPTREEITEM_EXPANDBOX - 2); } MoveToEx(hdc, x + 2, y + PROPTREEITEM_EXPANDBOXHALF, NULL); LineTo(hdc, x + PROPTREEITEM_EXPANDBOX - 2, y + PROPTREEITEM_EXPANDBOXHALF); SelectObject(hdc, oPen); SelectObject(hdc, oBrush); DeleteObject(hPen); } ///////////////////////////////////////////////////////////////////////////// // CPropItem // CPropItem::CPropItem() : m_pProp(NULL), m_sLabel(_T("")), m_sInfo(_T("")), m_loc(0,0), m_rc(0,0,0,0), m_lParam(0), m_nCtrlID(0), m_dwState(0), m_bActivated(FALSE), m_bCommitOnce(FALSE), m_rcExpand(0,0,0,0), m_rcCheckbox(0,0,0,0), m_rcButton(0,0,0,0), m_pParent(NULL), m_pSibling(NULL), m_pChild(NULL), m_pVis(NULL), m_nItemType(ITEMTYPE_NULL), m_bHide(FALSE), m_bHasBt(FALSE), m_bHasSpinCtrl(FALSE) { } CPropItem::~CPropItem() { } BOOL CPropItem::IsExpanded() { return (m_dwState & TreeItemExpanded) ? TRUE : FALSE; } BOOL CPropItem::IsSelected() { return (m_dwState & TreeItemSelected) ? TRUE : FALSE; } BOOL CPropItem::IsChecked() { return (m_dwState & TreeItemChecked) ? TRUE : FALSE; } BOOL CPropItem::IsReadOnly() { return (m_dwState & TreeItemReadOnly) ? TRUE : FALSE; } BOOL CPropItem::IsActivated() { return (m_dwState & TreeItemActivated) ? TRUE : FALSE; } BOOL CPropItem::IsHide() { return m_bHide; } void CPropItem::Select(BOOL bSelect) { if (bSelect) m_dwState |= TreeItemSelected; else m_dwState &= ~TreeItemSelected; } void CPropItem::Expand(BOOL bExpand) { if (bExpand) m_dwState |= TreeItemExpanded; else m_dwState &= ~TreeItemExpanded; } void CPropItem::Check(BOOL bCheck) { if (bCheck) m_dwState |= TreeItemChecked; else m_dwState &= ~TreeItemChecked; } void CPropItem::ReadOnly(BOOL bReadOnly) { if (bReadOnly) m_dwState |= TreeItemReadOnly; else m_dwState &= ~TreeItemReadOnly; } void CPropItem::Hide(BOOL bHide) { m_bHide = bHide; } BOOL CPropItem::IsCheckBox() { return (m_dwState & TreeItemCheckbox) ? TRUE : FALSE; } void CPropItem::HasCheckBox(BOOL bCheckbox) { if (bCheckbox) m_dwState |= TreeItemCheckbox; else m_dwState &= ~TreeItemCheckbox; } BOOL CPropItem::HitExpand(const POINT& pt) { return m_rcExpand.PtInRect(pt); } BOOL CPropItem::HitCheckBox(const POINT& pt) { return m_rcCheckbox.PtInRect(pt); } BOOL CPropItem::HitButton(const POINT& pt) { return m_rcButton.PtInRect(pt); } BOOL CPropItem::IsRootLevel() { ASSERT(m_pProp!=NULL); return GetParent() == m_pProp->GetRootItem(); } LONG CPropItem::GetTotalHeight() { CPropItem* pItem; LONG nHeight; nHeight = GetHeight(); if (IsExpanded()) { for (pItem = GetChild(); pItem; pItem = pItem->GetSibling()) nHeight += pItem->GetTotalHeight(); } return nHeight; } void CPropItem::SetLabelText(LPCTSTR sLabel) { m_sLabel = sLabel; } LPCTSTR CPropItem::GetLabelText() { return m_sLabel; } void CPropItem::SetInfoText(LPCTSTR sInfo) { m_sInfo = sInfo; } LPCTSTR CPropItem::GetInfoText() { return m_sInfo; } void CPropItem::SetCtrlID(UINT nCtrlID) { m_nCtrlID = nCtrlID; } UINT CPropItem::GetCtrlID() { return m_nCtrlID; } LONG CPropItem::GetHeight() { return PROPTREEITEM_DEFHEIGHT; } LPARAM CPropItem::GetItemValue() { // no items are assocatied with this type return 0L; } void CPropItem::SetItemValue(LPARAM) { // no items are assocatied with this type } void CPropItem::OnMove() { // no attributes, do nothing } void CPropItem::OnRefresh() { // no attributes, do nothing } void CPropItem::OnCommit() { // no attributes, do nothing } void CPropItem::Activate() { m_bActivated = TRUE; m_bCommitOnce = FALSE; OnActivate(); } void CPropItem::CommitChanges() { m_bActivated = FALSE; if (m_bCommitOnce) return; m_bCommitOnce = TRUE; ASSERT(m_pProp!=NULL); OnCommit(); m_pProp->SendNotify(PTN_ITEMCHANGED, this); m_pProp->RefreshItems(this); } void CPropItem::OnActivate() { // no attributes, do nothing } void CPropItem::SetPropOwner(CPropCtrl* pProp) { m_pProp = pProp; } CPropCtrl* CPropItem::GetPropOwner() { return m_pProp; } const POINT& CPropItem::GetLocation() { return m_loc; } CPropItem* CPropItem::GetParent() { return m_pParent; } CPropItem* CPropItem::GetSibling() { return m_pSibling; } CPropItem* CPropItem::GetChild() { return m_pChild; } CPropItem* CPropItem::GetNextVisible() { return m_pVis; } void CPropItem::SetParent(CPropItem* pParent) { m_pParent = pParent; } void CPropItem::SetSibling(CPropItem* pSibling) { m_pSibling = pSibling; } void CPropItem::SetChild(CPropItem* pChild) { m_pChild = pChild; } void CPropItem::SetNextVisible(CPropItem* pVis) { m_pVis = pVis; } LONG CPropItem::DrawItem(CDC* pDC, const RECT& rc, LONG x, LONG y) { if (m_bHide) { return 0L; } CPoint pt; LONG nTotal, nCol, ey; CRect drc, ir; ASSERT(m_pProp!=NULL); // Add TreeItem the list of visble items m_pProp->AddToVisibleList(this); // store the item's location m_loc = CPoint(x, y); // store the items rectangle position m_rc.SetRect(m_pProp->GetOrigin().x + PROPTREEITEM_SPACE, m_loc.y, rc.right, m_loc.y + GetHeight()-1); m_rc.OffsetRect(0, -m_pProp->GetOrigin().y); // init temp drawing variables nTotal = GetHeight(); ey = (nTotal >> 1) - (PROPTREEITEM_EXPANDBOX >> 1) - 2; bool bCheck = false; // convert item coordinates to screen coordinates pt = m_loc; pt.y -= m_pProp->GetOrigin().y; nCol = m_pProp->GetOrigin().x; if (IsRootLevel()) drc.SetRect(pt.x + PROPTREEITEM_EXPANDCOLUMN, pt.y, rc.right, pt.y + nTotal); else drc.SetRect(pt.x + PROPTREEITEM_EXPANDCOLUMN, pt.y, nCol, pt.y + nTotal); // root level items are shaded if (IsRootLevel()) { HGDIOBJ hOld = pDC->SelectObject(GetSysColorBrush(COLOR_BTNFACE)); pDC->PatBlt(rc.left, drc.top, rc.right - rc.left + 1, drc.Height(), PATCOPY); pDC->SelectObject(hOld); } // calc/draw expand box position if (GetChild()) { m_rcExpand.left = PROPTREEITEM_EXPANDCOLUMN/2 - PROPTREEITEM_EXPANDBOXHALF; m_rcExpand.top = m_loc.y + ey; m_rcExpand.right = m_rcExpand.left + PROPTREEITEM_EXPANDBOX - 1; m_rcExpand.bottom = m_rcExpand.top + PROPTREEITEM_EXPANDBOX - 1; ir = m_rcExpand; ir.OffsetRect(0, -m_pProp->GetOrigin().y); _DrawExpand(pDC->m_hDC, ir.left, ir.top, IsExpanded(), !IsRootLevel()); } else m_rcExpand.SetRectEmpty(); // calc/draw check box position if (IsCheckBox()) { bCheck = true; ir.left = drc.left + PROPTREEITEM_SPACE; ir.top = m_loc.y + ey; ir.right = ir.left + PROPTREEITEM_CHECKBOX; ir.bottom = ir.top + PROPTREEITEM_CHECKBOX; m_rcCheckbox = ir; } else m_rcCheckbox.SetRectEmpty(); if (m_bHasBt) { m_rcButton = m_rc; m_rcButton.left = rc.right - BUTTON_WIDTH; } if (m_bHasSpinCtrl) { m_rcSpinCtrl = m_rc; m_rcSpinCtrl.left = rc.right - BUTTON_WIDTH; } HRGN hRgn = NULL; // create a clipping region for the label if (!IsRootLevel()) { hRgn = CreateRectRgn(drc.left, drc.top, drc.right, drc.bottom); SelectClipRgn(pDC->m_hDC, hRgn); } // calc label position ir = drc; ir.left += PROPTREEITEM_SPACE; // offset the label text if item has a check box if (bCheck) OffsetRect(&ir, PROPTREEITEM_CHECKBOX + PROPTREEITEM_SPACE * 2, 0); // draw label if (!m_sLabel.IsEmpty()) { if (IsRootLevel()) pDC->SelectObject(CPropCtrl::GetBoldFont()); else pDC->SelectObject(CPropCtrl::GetNormalFont()); pDC->SetTextColor(GetSysColor(COLOR_BTNTEXT)); pDC->SetBkMode(TRANSPARENT); pDC->DrawText(m_sLabel, &ir, DT_SINGLELINE|DT_VCENTER|DT_CALCRECT); // draw the text highlighted if selected if (IsSelected()) { HGDIOBJ oPen = pDC->SelectObject(GetStockObject(NULL_PEN)); HGDIOBJ oBrush = pDC->SelectObject(GetSysColorBrush(COLOR_HIGHLIGHT)); CRect dr; dr = drc; dr.left = PROPTREEITEM_EXPANDCOLUMN; pDC->Rectangle(&dr); pDC->SelectObject(oPen); pDC->SelectObject(oBrush); pDC->SetTextColor(GetSysColor(COLOR_BTNHIGHLIGHT)); } // check if we need to draw the text as disabled if (!m_pProp->IsWindowEnabled()) pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT)); pDC->DrawText(m_sLabel, &ir, DT_SINGLELINE|DT_VCENTER); } // draw check box frame if (IsCheckBox()) { ir = m_rcCheckbox; ir.OffsetRect(0, -m_pProp->GetOrigin().y); pDC->DrawFrameControl(&ir, DFC_BUTTON, DFCS_BUTTONCHECK | (IsChecked() ? DFCS_CHECKED : 0)); } // remove clip region if (hRgn) { SelectClipRgn(pDC->m_hDC, NULL); DeleteObject(hRgn); } // draw horzontal sep _DotHLine(pDC->m_hDC, PROPTREEITEM_EXPANDCOLUMN, pt.y + nTotal - 1, rc.right - PROPTREEITEM_EXPANDCOLUMN + 1); // draw separators if (!IsRootLevel()) { // column sep CPen pn1(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW)); CPen* pOld; pOld = pDC->SelectObject(&pn1); pDC->MoveTo(nCol, drc.top); pDC->LineTo(nCol, drc.bottom); CPen pn2(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT)); pDC->SelectObject(&pn2); pDC->MoveTo(nCol + 1, drc.top); pDC->LineTo(nCol + 1, drc.bottom); pDC->SelectObject(pOld); } // draw attribute if (!IsRootLevel()) { // create clip region hRgn = CreateRectRgn(m_rc.left, m_rc.top, m_rc.right, m_rc.bottom); SelectClipRgn(pDC->m_hDC, hRgn); DrawAttribute(pDC, m_rc); SelectClipRgn(pDC->m_hDC, NULL); DeleteObject(hRgn); } // draw children if (GetChild() && IsExpanded()) { y += nTotal; CPropItem* pNext; for (pNext = GetChild(); pNext; pNext = pNext->GetSibling()) { LONG nHeight = pNext->DrawItem(pDC, rc, x + (IsRootLevel() ? 0 : PNINDENT), y); nTotal += nHeight; y += nHeight; } } return nTotal; } void CPropItem::DrawAttribute(CDC*, const RECT&) { // no attributes are assocatied with this type } void CPropItem::HasButton(BOOL bHas) { m_bHasBt = bHas; } void CPropItem::HasSpinCtrl(BOOL bHas /* = TRUE */) { m_bHasSpinCtrl = bHas; } // 创建(显示)按钮、pWnd:当前控件 void CPropItem::CreateButton( CWnd* pWnd ) { if (!m_bHasBt || m_nItemType == ITEMTYPE_NULL) return; CPropCtrl* pProp = GetPropOwner(); if (pProp != NULL && NULL != pProp->GetCtrlParent()) { CPropList* pList = (CPropList*)pProp->GetCtrlParent(); // 先移动给按钮留下位置 CRect rect = m_rc; rect.right = m_rc.right - BUTTON_WIDTH; pWnd->MoveWindow(rect); // 确定Spin的位置 CRect rectBt; pWnd->GetWindowRect(rectBt); pList->ScreenToClient(rectBt); rectBt.left = rectBt.right; rectBt.right = rectBt.left + BUTTON_WIDTH; if (m_nItemType == ITEMTYPE_COMBOBOX) rectBt.DeflateRect(0, 1, 0, 5); if (NULL == pList->m_pBt) { pList->m_pBt = new CButton; pList->m_pBt->Create(_T("..."), WS_VISIBLE , rectBt, pList, IDC_BUTTON); } else { pList->m_pBt->MoveWindow(rectBt); pList->m_pBt->ShowWindow(SW_SHOW); } } } // 创建(显示)SpinCtrl、pWnd:当前控件 void CPropItem::CreateSpinCtrl( CWnd* pWnd ) { if (!m_bHasSpinCtrl || m_nItemType == ITEMTYPE_NULL) return; CPropCtrl* pProp = GetPropOwner(); if (pProp != NULL && NULL != pProp->GetCtrlParent()) { CPropList* pList = (CPropList*)pProp->GetCtrlParent(); // 先移动给按钮留下位置 CRect rect = m_rc; rect.right = m_rc.right - SPINCTRL_WIDTH; pWnd->MoveWindow(rect); CRect rcSpinCtrl; pWnd->GetWindowRect(rcSpinCtrl); pList->ScreenToClient(rcSpinCtrl); rcSpinCtrl.left = rcSpinCtrl.right; rcSpinCtrl.right = rcSpinCtrl.left + SPINCTRL_WIDTH; if (m_nItemType == ITEMTYPE_COMBOBOX) rcSpinCtrl.DeflateRect(0, 1, 0, 5); if (NULL == pList->m_pSpinCtrl) { pList->m_pSpinCtrl = new CSpinButtonCtrl; pList->m_pSpinCtrl->Create(WS_VISIBLE, rcSpinCtrl, pList, IDC_SPINCTRL); } else { pList->m_pSpinCtrl->MoveWindow(rcSpinCtrl); pList->m_pSpinCtrl->ShowWindow(SW_SHOW); } // 设置伙伴控件 CEdit* pEdit = (CEdit*)this; pList->m_pSpinCtrl->SetBuddy(pEdit); } } void CPropItem::ShowButton(BOOL bShow/* = TRUE*/) { CPropCtrl* pTree = GetPropOwner(); if (NULL != pTree) { CPropList* pList = (CPropList*)pTree->GetCtrlParent(); if (NULL != pList) { pList->ShowButton(bShow); } } } void CPropItem::ShowSpinCtrl(BOOL bShow /* = TRUE */) { CPropCtrl* pTree = GetPropOwner(); if (NULL != pTree) { CPropList* pList = (CPropList*)pTree->GetCtrlParent(); if (NULL != pList) { pList->ShowSpinCtrl(bShow); } } }