Files
envi-code/SourceCode/Code2026/MFCCtrl/PropCtrl.cpp
T
2026-09-28 15:28:50 +08:00

986 lines
16 KiB
C++

// CPropCtrl.cpp : implementation file
//
// 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"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define PROPTREEITEM_EXPANDCOLUMN 16 // width of the expand column
// #define PROPTREEITEM_COLRNG 5 // width of splitter
static AFX_EXTENSION_MODULE CPropDLL = {NULL, NULL};
static const CString strOfficeFontName = _T("Tahoma");
static const CString strDefaultFontName = _T("MS Sans Serif");
static int CALLBACK FontFamilyProcFonts(const LOGFONT FAR* lplf, const TEXTMETRIC FAR*, ULONG, LPARAM)
{
ASSERT(lplf != NULL);
CString strFont = lplf->lfFaceName;
return strFont.CollateNoCase (strOfficeFontName) == 0 ? 0 : 1;
}
/////////////////////////////////////////////////////////////////////////////
// CPropCtrl
UINT CPropCtrl::s_nInstanceCount;
CFont* CPropCtrl::s_pNormalFont;
CFont* CPropCtrl::s_pBoldFont;
CPropItem* CPropCtrl::s_pFound;
CPropCtrl::CPropCtrl() :
m_bShowInfo(TRUE),
m_nInfoHeight(50),
m_pVisbleList(NULL),
m_Origin(100,0),
m_nLastUID(1),
m_pFocus(NULL),
m_bDisableInput(FALSE)
{
m_Root.Expand();
// init global resources only once
if (!s_nInstanceCount)
InitGlobalResources();
s_nInstanceCount++;
}
CPropCtrl::~CPropCtrl()
{
DeleteAllItems();
s_nInstanceCount--;
// free global resource when ALL CPropCtrls are destroyed
if (!s_nInstanceCount)
FreeGlobalResources();
}
BEGIN_MESSAGE_MAP(CPropCtrl, CWnd)
//{{AFX_MSG_MAP(CPropCtrl)
ON_WM_CREATE()
ON_WM_SIZE()
ON_WM_ENABLE()
ON_WM_SYSCOLORCHANGE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CPropCtrl message handlers
const POINT& CPropCtrl::GetOrigin()
{
return m_Origin;
}
BOOL CPropCtrl::Create(CWnd* pParentWnd, UINT nID)
{
if (NULL == pParentWnd)
return FALSE;
CRect r;
CWnd* pWnd = pParentWnd->GetDlgItem(nID);
if (NULL != pWnd)
{
pWnd->GetWindowRect(&r);
pParentWnd->ScreenToClient(&r);
pWnd->DestroyWindow();
}
DWORD dwStyle = WS_VISIBLE|WS_CHILD;
return Create(dwStyle, r, pParentWnd, nID);
}
BOOL CPropCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
{
CWnd* pWnd = this;
LPCTSTR pszCreateClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW, ::LoadCursor(NULL, IDC_ARROW));
return pWnd->Create(pszCreateClass, _T(""), dwStyle, rect, pParentWnd, nID);
}
int CPropCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
DWORD dwStyle;
CRect rc;
GetClientRect(rc);
// create CPropList
dwStyle = WS_VISIBLE|WS_CHILD|WS_VSCROLL;
if (!m_List.Create(dwStyle, rc, this, 100))
{
TRACE0("Failed to create CPropGrid\n");
return -1;
}
m_List.SetPropOwner(this);
// create CCPropInfo
dwStyle &= ~WS_VSCROLL;
if (!m_Info.Create(_T(""), dwStyle, rc, this))
{
TRACE0("Failed to create CPropGridInfo\n");
return -1;
}
m_Info.SetPropOwner(this);
return 0;
}
CWnd* CPropCtrl::GetCtrlParent()
{
return &m_List;
}
void CPropCtrl::OnSize(UINT nType, int cx, int cy)
{
CWnd::OnSize(nType, cx, cy);
ResizeChildWindows(cx, cy);
CPropItem* pItem = GetFocusedItem();
if (NULL != pItem)
{
if (pItem->m_nItemType == ITEMTYPE_EDIT)
{
pItem->CreateButton((CPropItemEdit*)pItem);
pItem->CreateSpinCtrl((CPropItemEdit*)pItem);
}
else if (pItem->m_nItemType == ITEMTYPE_COMBOBOX)
{
pItem->CreateButton((CPropItemCombo*)pItem);
pItem->CreateSpinCtrl((CPropItemEdit*)pItem);
}
}
}
void CPropCtrl::ResizeChildWindows(int cx, int cy)
{
if (m_bShowInfo)
{
if (IsWindow(m_List.m_hWnd))
m_List.MoveWindow(0, 0, cx, cy - m_nInfoHeight);
if (IsWindow(m_Info.m_hWnd)){
m_Info.MoveWindow(0, cy - m_nInfoHeight, cx, m_nInfoHeight);
m_Info.ShowWindow(SW_SHOW);
}
}
else
{
if (IsWindow(m_List.m_hWnd))
m_List.MoveWindow(0, 0, cx, cy);
if (IsWindow(m_Info.m_hWnd))
m_Info.ShowWindow(SW_HIDE);
}
}
void CPropCtrl::InitGlobalResources()
{
LOGFONT lf;
ZeroMemory ((PVOID) &lf,sizeof (LOGFONT));
CWnd* pWnd = AfxGetMainWnd();
if (pWnd != NULL)
{
CFont* pFont = pWnd->GetFont();
if (pFont != NULL)
{
pFont->GetLogFont(&lf);
}
else
{
int nFontSize = -16;
CString strFontName = _T("Arial");
CFont fnt;
fnt.CreateFont(nFontSize, 0,0,0, FW_NORMAL, 0,0,0, DEFAULT_CHARSET,
OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, strFontName);
fnt.GetLogFont(&lf);
}
}
else
{
NONCLIENTMETRICS nm;
//modify by xlc on 090902 R18Ìí¼Ó²Ëµ¥±ÀÀ£
#if ADS<18
nm.cbSize = sizeof (NONCLIENTMETRICS);
#else
nm.cbSize = sizeof(NONCLIENTMETRICS) - sizeof(nm.iPaddedBorderWidth);
#endif
VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS,nm.cbSize,&nm,0));
lf = nm.lfMenuFont;
}
s_pNormalFont = new CFont;
s_pNormalFont->CreateFontIndirect(&lf);
lf.lfWeight = FW_BOLD;
s_pBoldFont = new CFont;
s_pBoldFont->CreateFontIndirect(&lf);
}
void CPropCtrl::FreeGlobalResources()
{
if (s_pNormalFont)
{
delete s_pNormalFont;
s_pNormalFont = NULL;
}
if (s_pBoldFont)
{
delete s_pBoldFont;
s_pBoldFont = NULL;
}
}
CFont* CPropCtrl::GetNormalFont()
{
return s_pNormalFont;
}
CFont* CPropCtrl::GetBoldFont()
{
return s_pBoldFont;
}
CPropItem* CPropCtrl::GetFocusedItem()
{
return m_pFocus;
}
CPropItem* CPropCtrl::GetRootItem()
{
return &m_Root;
}
void CPropCtrl::ClearVisibleList()
{
m_pVisbleList = NULL;
}
CPropItem* CPropCtrl::GetVisibleList()
{
return m_pVisbleList;
}
void CPropCtrl::AddToVisibleList(CPropItem* pItem)
{
if (!pItem)
return;
// check for an empty visible list
if (!m_pVisbleList)
m_pVisbleList = pItem;
else
{
// Add the new item to the end of the list
CPropItem* pNext;
pNext = m_pVisbleList;
while (pNext->GetNextVisible())
pNext = pNext->GetNextVisible();
pNext->SetNextVisible(pItem);
}
pItem->SetNextVisible(NULL);
}
BOOL CPropCtrl::EnumItems(CPropItem* pItem, ENUMPROPITEMPROC proc, LPARAM lParam)
{
if (!pItem || !proc)
return FALSE;
CPropItem* pNext;
// don't count the root item in any enumerations
if (pItem!=&m_Root && !proc(this, pItem, lParam))
return FALSE;
// recurse thru all child items
pNext = pItem->GetChild();
while (pNext)
{
if (!EnumItems(pNext, proc, lParam))
return FALSE;
pNext = pNext->GetSibling();
}
return TRUE;
}
void CPropCtrl::SetOriginOffset(LONG nOffset)
{
m_Origin.y = nOffset;
}
void CPropCtrl::UpdatedItems()
{
if (!IsWindow(m_hWnd))
return;
Invalidate();
m_List.UpdateResize();
m_List.Invalidate();
}
void CPropCtrl::DeleteAllItems()
{
Delete(NULL);
if (m_List.m_pBt != NULL)
m_List.m_pBt->ShowWindow(SW_HIDE);
UpdatedItems();
m_nLastUID = 1; // reset uid counter
}
void CPropCtrl::DeleteItem(CPropItem* pItem)
{
Delete(pItem);
UpdatedItems();
}
LONG CPropCtrl::GetColumn()
{
return m_Origin.x;
}
void CPropCtrl::SetColumn(LONG nColumn)
{
CRect rc;
GetClientRect(rc);
if (rc.IsRectEmpty())
nColumn = __max(PROPTREEITEM_EXPANDCOLUMN, nColumn);
else
nColumn = __min(__max(PROPTREEITEM_EXPANDCOLUMN, nColumn), rc.Width() - PROPTREEITEM_EXPANDCOLUMN);
m_Origin.x = nColumn;
Invalidate();
}
void CPropCtrl::Delete(CPropItem* pItem)
{
if (pItem && pItem!=&m_Root && SendNotify(PTN_DELETEITEM, pItem))
return;
// passing in a NULL item is the same as calling DeleteAllItems
if (!pItem)
pItem = &m_Root;
// delete children
CPropItem* pIter;
CPropItem* pNext;
pIter = pItem->GetChild();
while (pIter)
{
pNext = pIter->GetSibling();
DeleteItem(pIter);
pIter = pNext;
}
// unlink from tree
if (pItem->GetParent())
{
if (pItem->GetParent()->GetChild()==pItem)
pItem->GetParent()->SetChild(pItem->GetSibling());
else
{
pIter = pItem->GetParent()->GetChild();
while (pIter->GetSibling() && pIter->GetSibling()!=pItem)
pIter = pIter->GetSibling();
if (pIter->GetSibling())
pIter->SetSibling(pItem->GetSibling());
}
}
if (pItem!=&m_Root)
{
if (pItem==GetFocusedItem())
SetFocusedItem(NULL);
if (pItem == GetVisibleList())
ClearVisibleList();
delete pItem;
}
}
void CPropCtrl::SetFocusedItem(CPropItem* pItem)
{
m_pFocus = pItem;
EnsureVisible(m_pFocus);
if (!IsWindow(m_hWnd))
return;
Invalidate();
}
void CPropCtrl::ShowInfoText(BOOL bShow)
{
m_bShowInfo = bShow;
CRect rc;
GetClientRect(rc);
ResizeChildWindows(rc.Width(), rc.Height());
}
void CPropCtrl::SetInfoHeigh( int nHeigh )
{
m_nInfoHeight = nHeigh;
}
BOOL CPropCtrl::IsItemVisible(CPropItem* pItem)
{
if (!pItem)
return FALSE;
for (CPropItem* pNext = m_pVisbleList; pNext; pNext = pNext->GetNextVisible())
{
if (pNext==pItem)
return TRUE;
}
return FALSE;
}
void CPropCtrl::EnsureVisible(CPropItem* pItem)
{
if (!pItem)
return;
// item is not scroll visible (expand all parents)
if (!IsItemVisible(pItem))
{
CPropItem* pParent;
pParent = pItem->GetParent();
while (pParent)
{
pParent->Expand();
pParent = pParent->GetParent();
}
UpdatedItems();
UpdateWindow();
}
ASSERT(IsItemVisible(pItem));
CRect rc;
m_List.GetClientRect(rc);
rc.OffsetRect(0, m_Origin.y);
rc.bottom -= pItem->GetHeight();
CPoint pt;
pt = pItem->GetLocation();
if (!rc.PtInRect(pt))
{
LONG oy;
if (pt.y < rc.top)
oy = pt.y;
else
oy = pt.y - rc.Height() + pItem->GetHeight();
m_List.OnVScroll(SB_THUMBTRACK, oy, NULL);
}
}
CPropItem* CPropCtrl::InsertItem(CPropItem* pItem, CPropItem* pParent)
{
if (!pItem)
return NULL;
if (!pParent)
pParent = &m_Root;
if (!pParent->GetChild())
pParent->SetChild(pItem);
else
{
// add to end of the sibling list
CPropItem* pNext;
pNext = pParent->GetChild();
while (pNext->GetSibling())
pNext = pNext->GetSibling();
pNext->SetSibling(pItem);
}
pItem->SetParent(pParent);
pItem->SetPropOwner(this);
// auto generate a default ID
pItem->SetCtrlID(m_nLastUID++);
SendNotify(PTN_INSERTITEM, pItem);
UpdatedItems();
return pItem;
}
LONG CPropCtrl::HitTest(const POINT& pt)
{
POINT p = pt;
CPropItem* pItem;
// convert screen to tree coordinates
p.y += m_Origin.y;
if ((pItem = FindItem(pt))!=NULL)
{
// if (pItem->HitButton(p))
// return HTBUTTON;
if (!pItem->IsRootLevel() && pt.x >= m_Origin.x - PROPTREEITEM_COLRNG && pt.x <= m_Origin.x + PROPTREEITEM_COLRNG)
return HTCOLUMN;
if (pt.x > m_Origin.x + PROPTREEITEM_COLRNG)
return HTATTRIBUTE;
if (pItem->HitExpand(p))
return HTEXPAND;
if (pItem->HitCheckBox(p))
return HTCHECKBOX;
return HTLABEL;
}
return HTCLIENT;
}
CPropItem* CPropCtrl::FindItem(const POINT& pt)
{
CPropItem* pItem;
CPoint p = pt;
// convert screen to tree coordinates
p.y += m_Origin.y;
// search the visible list for the item
for (pItem = m_pVisbleList; pItem; pItem = pItem->GetNextVisible())
{
CPoint ipt = pItem->GetLocation();
if (p.y>=ipt.y && p.y<ipt.y + pItem->GetHeight())
return pItem;
}
return NULL;
}
CPropItem* CPropCtrl::FindItem(UINT nCtrlID)
{
s_pFound = NULL;
EnumItems(&m_Root, EnumFindItem, nCtrlID);
return s_pFound;
}
CPropItem* CPropCtrl::FindItem( const CString &csLable )
{
return EnumFindItem(GetRootItem(), csLable);
}
CPropItem* CPropCtrl::EnumFindItem(CPropItem* pItem, const CString &csLable )
{
CPropItem* pGetItem = NULL;
CPropItem* pItemSon = NULL;
CString csNowLable;
while (pItem)
{
csNowLable = pItem->GetLabelText();
if (csNowLable == csLable)
return pItem;
pItemSon = pItem->GetChild();
if (pItemSon != NULL)
{
pGetItem = EnumFindItem(pItemSon, csLable);
if (NULL != pGetItem)
return pGetItem;
}
pItem = pItem->GetSibling();
}
return NULL;
}
BOOL CALLBACK CPropCtrl::EnumFindItem(CPropCtrl*, CPropItem* pItem, LPARAM lParam)
{
ASSERT(pItem!=NULL);
if (pItem->GetCtrlID()==(UINT)lParam)
{
s_pFound = pItem;
return FALSE;
}
return TRUE;
}
BOOL CPropCtrl::IsDisableInput()
{
return m_bDisableInput;
}
void CPropCtrl::DisableInput(BOOL bDisable)
{
m_bDisableInput = bDisable;
CWnd* pWnd;
if ((pWnd = GetParent())!=NULL)
pWnd->EnableWindow(!bDisable);
}
void CPropCtrl::SelectItems(CPropItem* pItem, BOOL bSelect)
{
if (!pItem)
pItem = &m_Root;
EnumItems(pItem, EnumSelectAll, (LPARAM)bSelect);
}
CPropItem* CPropCtrl::FocusFirst()
{
CPropItem *pold;
pold = m_pFocus;
SetFocusedItem(m_pVisbleList);
if (m_pFocus)
{
SelectItems(NULL, FALSE);
m_pFocus->Select();
}
if (pold!=m_pFocus)
SendNotify(PTN_SELCHANGE, m_pFocus);
return m_pFocus;
}
CPropItem* CPropCtrl::FocusLast()
{
CPropItem* pNext;
CPropItem* pChange;
pChange = m_pFocus;
pNext = m_pVisbleList;
if (pNext)
{
while (pNext->GetNextVisible())
pNext = pNext->GetNextVisible();
SetFocusedItem(pNext);
if (m_pFocus)
{
SelectItems(NULL, FALSE);
m_pFocus->Select();
}
}
if (pChange!=m_pFocus)
SendNotify(PTN_SELCHANGE, m_pFocus);
return pNext;
}
CPropItem* CPropCtrl::FocusPrev()
{
CPropItem* pNext;
CPropItem* pChange;
pChange = m_pFocus;
if (m_pFocus==NULL)
{
// get the last visible item
pNext = m_pVisbleList;
while (pNext && pNext->GetNextVisible())
pNext = pNext->GetNextVisible();
}
else
{
pNext = m_pVisbleList;
while (pNext && pNext->GetNextVisible()!=m_pFocus)
pNext = pNext->GetNextVisible();
}
if (pNext)
SetFocusedItem(pNext);
if (m_pFocus)
{
SelectItems(NULL, FALSE);
m_pFocus->Select();
}
if (pChange!=m_pFocus)
SendNotify(PTN_SELCHANGE, m_pFocus);
return pNext;
}
CPropItem* CPropCtrl::FocusNext()
{
CPropItem* pNext;
CPropItem* pChange;
pChange = m_pFocus;
if (m_pFocus==NULL)
pNext = m_pVisbleList;
else
if (m_pFocus->GetNextVisible())
pNext = m_pFocus->GetNextVisible();
else
pNext = NULL;
if (pNext)
SetFocusedItem(pNext);
if (m_pFocus)
{
SelectItems(NULL, FALSE);
m_pFocus->Select();
}
if (pChange!=m_pFocus)
SendNotify(PTN_SELCHANGE, m_pFocus);
return pNext;
}
void CPropCtrl::UpdateMoveAllItems()
{
EnumItems(&m_Root, EnumMoveAll);
}
void CPropCtrl::RefreshItems(CPropItem* pItem)
{
if (!pItem)
pItem = &m_Root;
EnumItems(pItem, EnumRefreshAll);
UpdatedItems();
}
BOOL CALLBACK CPropCtrl::EnumSelectAll(CPropCtrl*, CPropItem* pItem, LPARAM lParam)
{
if (!pItem)
return FALSE;
pItem->Select((BOOL)lParam);
return TRUE;
}
BOOL CALLBACK CPropCtrl::EnumRefreshAll(CPropCtrl*, CPropItem* pItem, LPARAM)
{
if (!pItem)
return FALSE;
pItem->OnRefresh();
return TRUE;
}
BOOL CALLBACK CPropCtrl::EnumMoveAll(CPropCtrl*, CPropItem* pItem, LPARAM)
{
if (!pItem)
return FALSE;
pItem->OnMove();
return TRUE;
}
LRESULT CPropCtrl::SendNotify(UINT nNotifyCode, CPropItem* pItem)
{
if (!IsWindow(m_hWnd))
return 0L;
if (!(GetStyle() & PTS_NOTIFY))
return 0L;
NMPROPTREE nmmp;
LPNMHDR lpnm;
lpnm = NULL;
switch (nNotifyCode)
{
case PTN_INSERTITEM:
case PTN_DELETEITEM:
case PTN_DELETEALLITEMS:
case PTN_ITEMCHANGED:
case PTN_ITEMBUTTONCLICK:
case PTN_SELCHANGE:
case PTN_ITEMEXPANDING:
case PTN_COLUMNCLICK:
case PTN_PROPCLICK:
case PTN_CHECKCLICK:
lpnm = (LPNMHDR)&nmmp;
nmmp.pItem = pItem;
break;
}
if (lpnm)
{
UINT id = (UINT)::GetMenu(m_hWnd);
lpnm->code = nNotifyCode;
lpnm->hwndFrom = m_hWnd;
lpnm->idFrom = id;
return GetParent()->SendMessage(nNotifyCode, (WPARAM)id, (LPARAM)lpnm);
}
return 0L;
}
void CPropCtrl::OnEnable(BOOL bEnable)
{
CWnd::OnEnable(bEnable);
Invalidate();
}
void CPropCtrl::OnSysColorChange()
{
CWnd::OnSysColorChange();
Invalidate();
}
BOOL CPropCtrl::IsSingleSelection()
{
// right now only support single selection
return TRUE;
}
/*----------------------------------------------------------------------------*
* ÃèÊö: Èñí¸ñÖÐËùÓÐΪֻ¶Á
* ±¸×¢:
-----------------------------------------------------------------------------*/
void CPropCtrl::ReadOnly(CPropItem* pItem, BOOL bRead)
{
if (NULL == pItem)
return;
while (pItem != NULL)
{
pItem->ReadOnly(bRead);
CPropItem* pItemSon = pItem->GetChild();
ReadOnly(pItemSon, bRead);
pItem =pItem->GetSibling();
}
}
void CPropCtrl::ShowVSrolBar( BOOL bShow /*= TRUE*/ )
{
m_List.ShowVScrolBar(bShow);
}