// 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.yGetHeight()) 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); }