// CPropList.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 "Resource.h" #include "PropCtrl.h" #include "PropList.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif // move to CPropCtrl.h // #define PROPTREEITEM_EXPANDCOLUMN 16 // width of the expand column // #define PROPTREEITEM_COLRNG 1 // width of splitter // #define PROPTREEITEM_DEFHEIGHT 21 // default heigt of an item ///////////////////////////////////////////////////////////////////////////// // CPropList CPropList::CPropList() : m_pProp(NULL), m_BackBufferSize(0,0), m_bColDrag(FALSE), m_nPrevCol(0), m_pBt(NULL), m_pSpinCtrl(NULL), m_bVScrolBar(TRUE) { } CPropList::~CPropList() { if (NULL != m_pBt) { delete m_pBt; m_pBt = NULL; } if (NULL != m_pSpinCtrl) { delete m_pSpinCtrl; m_pSpinCtrl = NULL; } } BEGIN_MESSAGE_MAP(CPropList, CWnd) //{{AFX_MSG_MAP(CPropList) ON_WM_SIZE() ON_WM_PAINT() ON_WM_SETCURSOR() ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_LBUTTONDBLCLK() ON_WM_MOUSEMOVE() ON_WM_MOUSEWHEEL() ON_WM_KEYDOWN() ON_WM_GETDLGCODE() ON_BN_CLICKED(IDC_BUTTON, OnButtonClick) ON_NOTIFY(UDN_DELTAPOS, IDC_SPINCTRL, OnOnDeltaposSpinCtrl) ON_WM_HSCROLL() ON_WM_VSCROLL() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CPropList message handlers void CPropList::SetPropOwner(CPropCtrl* pProp) { m_pProp = pProp; } BOOL CPropList::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) { CWnd* pWnd = this; LPCTSTR pszCreateClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, ::LoadCursor(NULL, IDC_ARROW)); return pWnd->Create(pszCreateClass, _T(""), dwStyle, rect, pParentWnd, nID); } void CPropList::OnSize(UINT nType, int cx, int cy) { CWnd::OnSize(nType, cx, cy); RecreateBackBuffer(cx, cy); if (m_pProp) { UpdateResize(); Invalidate(); UpdateWindow(); // inform all items that a resize has been made m_pProp->UpdateMoveAllItems(); } } void CPropList::RecreateBackBuffer(int cx, int cy) { if (m_BackBufferSize.cxGetRootItem()->GetTotalHeight(); si.nPage = nHeight; if ((int)si.nPage>si.nMax) m_pProp->SetOriginOffset(0); SetScrollInfo(SB_VERT, &si, TRUE); } else { ShowScrollBar(SB_VERT, FALSE); } // force set column for clipping m_pProp->SetColumn(m_pProp->GetColumn()); } void CPropList::OnPaint() { CPaintDC dc(this); CDC memdc; CBitmap* pOldBitmap; ASSERT(m_pProp!=NULL); m_pProp->ClearVisibleList(); memdc.CreateCompatibleDC(&dc); pOldBitmap = memdc.SelectObject(&m_BackBuffer); CRect rc; GetClientRect(rc); // draw control background memdc.SelectObject(GetSysColorBrush(COLOR_BTNFACE)); memdc.PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), PATCOPY); // draw control inside fill color rc.DeflateRect(2,2); memdc.PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), m_pProp->IsWindowEnabled() ? WHITENESS : PATCOPY); rc.InflateRect(2,2); // draw expand column memdc.SelectObject(GetSysColorBrush(COLOR_BTNFACE)); memdc.PatBlt(0, 0, PROPTREEITEM_EXPANDCOLUMN, rc.Height(), PATCOPY); // draw edge memdc.DrawEdge(&rc, BDR_SUNKENOUTER, BF_RECT); CPropItem* pItem; LONG nTotal = 0; ASSERT(m_pProp->GetRootItem()!=NULL); rc.DeflateRect(2,2); // create clip region HRGN hRgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom); SelectClipRgn(memdc.m_hDC, hRgn); // draw all items for (pItem = m_pProp->GetRootItem()->GetChild(); pItem; pItem = pItem->GetSibling()) { LONG nHeight = pItem->DrawItem(&memdc, rc, 0, nTotal); nTotal += nHeight; } // remove clip region SelectClipRgn(memdc.m_hDC, NULL); DeleteObject(hRgn); // copy back buffer to the display dc.GetClipBox(&rc); dc.BitBlt(rc.left, rc.top, rc.Width(), rc.Height(), &memdc, rc.left, rc.top, SRCCOPY); memdc.DeleteDC(); } BOOL CPropList::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { if (nHitTest==HTCLIENT) { CPoint pt; ASSERT(m_pProp!=NULL); GetCursorPos(&pt); ScreenToClient(&pt); switch (m_pProp->HitTest(pt)) { case HTCOLUMN: SetCursor(LoadCursor(::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SPLITTER))); return TRUE; case HTCHECKBOX: case HTEXPAND: SetCursor(LoadCursor(::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_FPOINT))); return TRUE; } } return CWnd::OnSetCursor(pWnd, nHitTest, message); } void CPropList::OnLButtonDown(UINT, CPoint point) { ShowButton(FALSE); ShowSpinCtrl(FALSE); ASSERT(m_pProp!=NULL); if (m_pProp->IsDisableInput()) return; m_pProp->SendNotify(NM_CLICK); if (!m_pProp->IsWindowEnabled()) return; SetFocus(); LONG nHit = m_pProp->HitTest(point); CPropItem* pItem; CRect rc; CDC* pDC; switch (nHit) { case HTCOLUMN: if (m_pProp->SendNotify(PTN_COLUMNCLICK)) break; m_bColDrag = TRUE; SetCapture(); m_nPrevCol = m_pProp->GetOrigin().x; // paint drag line pDC = GetDC(); GetClientRect(rc); pDC->PatBlt(m_nPrevCol - PROPTREEITEM_COLRNG/2, 0, PROPTREEITEM_COLRNG, rc.bottom, PATINVERT); ReleaseDC(pDC); break; case HTCHECKBOX: if ((pItem = m_pProp->FindItem(point))!=NULL) { pItem->Check(!pItem->IsChecked()); m_pProp->SendNotify(PTN_CHECKCLICK, pItem); Invalidate(); } break; case HTEXPAND: if ((pItem = m_pProp->FindItem(point))!=NULL) { if (pItem->GetChild() && !m_pProp->SendNotify(PTN_ITEMEXPANDING, pItem)) { pItem->Expand(!pItem->IsExpanded()); UpdateResize(); Invalidate(); UpdateWindow(); CheckVisibleFocus(); } } break; // case HTBUTTON: // if ((pItem = m_pProp->FindItem(point))!=NULL) // { // pItem->MyButtonClick(); // } // break; default: if ((pItem = m_pProp->FindItem(point))!=NULL) { CPropItem* pOldFocus = m_pProp->GetFocusedItem(); m_pProp->SelectItems(NULL, FALSE); m_pProp->SetFocusedItem(pItem); pItem->Select(); Invalidate(); if (pItem!=pOldFocus) m_pProp->SendNotify(PTN_SELCHANGE, pItem); if (nHit==HTATTRIBUTE && !pItem->IsRootLevel()) { if (!m_pProp->SendNotify(PTN_PROPCLICK, pItem) && !pItem->IsReadOnly()) pItem->Activate(); } } else { m_pProp->SelectItems(NULL, FALSE); m_pProp->SetFocusedItem(NULL); m_pProp->SendNotify(PTN_SELCHANGE); Invalidate(); } break; } } void CPropList::OnLButtonUp(UINT, CPoint point) { if (m_bColDrag) { CDC* pDC = GetDC(); CRect rc; GetClientRect(rc); pDC->PatBlt(m_nPrevCol - PROPTREEITEM_COLRNG/2, 0, PROPTREEITEM_COLRNG, rc.bottom, PATINVERT); ReleaseDC(pDC); m_bColDrag = FALSE; ReleaseCapture(); m_pProp->SetColumn(point.x); m_pProp->UpdateMoveAllItems(); Invalidate(); } } void CPropList::OnLButtonDblClk(UINT, CPoint point) { ASSERT(m_pProp!=NULL); m_pProp->SendNotify(NM_DBLCLK); CPropItem* pItem; CPropItem* pOldFocus; if ((pItem = m_pProp->FindItem(point))!=NULL && pItem->GetChild()) { switch (m_pProp->HitTest(point)) { case HTCOLUMN: break; case HTCHECKBOX: pItem->Check(!pItem->IsChecked()); m_pProp->SendNotify(PTN_CHECKCLICK, pItem); Invalidate(); break; case HTATTRIBUTE: if (!pItem->IsRootLevel()) break; // pass thru to default default: pOldFocus = m_pProp->GetFocusedItem(); m_pProp->SelectItems(NULL, FALSE); m_pProp->SetFocusedItem(pItem); pItem->Select(); if (pItem!=pOldFocus) m_pProp->SendNotify(PTN_SELCHANGE, pItem); // pass thru to HTEXPAND case HTEXPAND: if (!m_pProp->SendNotify(PTN_ITEMEXPANDING, pItem)) { pItem->Expand(!pItem->IsExpanded()); UpdateResize(); Invalidate(); UpdateWindow(); CheckVisibleFocus(); } break; } } } void CPropList::OnMouseMove(UINT, CPoint point) { if (m_bColDrag) { CDC* pDC = GetDC(); CRect rc; GetClientRect(rc); pDC->PatBlt(m_nPrevCol - PROPTREEITEM_COLRNG/2, 0, PROPTREEITEM_COLRNG, rc.bottom, PATINVERT); pDC->PatBlt(point.x - PROPTREEITEM_COLRNG/2, 0, PROPTREEITEM_COLRNG, rc.bottom, PATINVERT); m_nPrevCol = point.x; ReleaseDC(pDC); } } BOOL CPropList::OnMouseWheel(UINT, short zDelta, CPoint) { SCROLLINFO si; ZeroMemory(&si, sizeof(SCROLLINFO)); si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_RANGE; GetScrollInfo(SB_VERT, &si); CRect rc; GetClientRect(rc); if (si.nMax - si.nMin < rc.Height()) return TRUE; SetFocus(); OnVScroll(zDelta < 0 ? SB_LINEDOWN : SB_LINEUP, 0, NULL); return TRUE; } void CPropList::OnKeyDown(UINT nChar, UINT, UINT) { CPropItem* pItem; ASSERT(m_pProp!=NULL); if (m_pProp->IsDisableInput() || !m_pProp->IsWindowEnabled()) return; switch (nChar) { case VK_RETURN: if ((pItem = m_pProp->GetFocusedItem())!=NULL && !pItem->IsRootLevel() && !pItem->IsReadOnly()) { pItem->Activate(); } break; case VK_HOME: if (m_pProp->FocusFirst()) Invalidate(); break; case VK_END: if (m_pProp->FocusLast()) Invalidate(); break; case VK_LEFT: if ((pItem = m_pProp->GetFocusedItem())!=NULL) { if (!m_pProp->SendNotify(PTN_ITEMEXPANDING, pItem)) { if (pItem->GetChild() && pItem->IsExpanded()) { pItem->Expand(FALSE); UpdateResize(); Invalidate(); UpdateWindow(); CheckVisibleFocus(); break; } } } else break; // pass thru to next case VK_UP case VK_UP: if (m_pProp->FocusPrev()) Invalidate(); break; case VK_RIGHT: if ((pItem = m_pProp->GetFocusedItem())!=NULL) { if (!m_pProp->SendNotify(PTN_ITEMEXPANDING, pItem)) { if (pItem->GetChild() && !pItem->IsExpanded()) { pItem->Expand(); UpdateResize(); Invalidate(); UpdateWindow(); CheckVisibleFocus(); break; } } } else break; // pass thru to next case VK_DOWN case VK_DOWN: if (m_pProp->FocusNext()) Invalidate(); break; } } UINT CPropList::OnGetDlgCode() { return DLGC_WANTARROWS|DLGC_WANTCHARS|DLGC_WANTALLKEYS; } void CPropList::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar*) { SCROLLINFO si; CRect rc; LONG nHeight; SetFocus(); GetClientRect(rc); nHeight = rc.Height() + 1; ZeroMemory(&si, sizeof(SCROLLINFO)); si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_RANGE; GetScrollInfo(SB_VERT, &si); LONG ny = m_pProp->GetOrigin().y; switch (nSBCode) { case SB_LINEDOWN: ny += PROPTREEITEM_DEFHEIGHT; break; case SB_LINEUP: ny -= PROPTREEITEM_DEFHEIGHT; break; case SB_PAGEDOWN: ny += nHeight; break; case SB_PAGEUP: ny -= nHeight; break; case SB_THUMBTRACK: ny = nPos; break; } ny = __min(__max(ny, si.nMin), si.nMax - nHeight); m_pProp->SetOriginOffset(ny); si.fMask = SIF_POS; si.nPos = ny; SetScrollInfo(SB_VERT, &si, TRUE); ShowButton(FALSE); ShowSpinCtrl(FALSE); Invalidate(); } void CPropList::CheckVisibleFocus() { ASSERT(m_pProp!=NULL); CPropItem* pItem; if ((pItem = m_pProp->GetFocusedItem())==NULL) return; if (!m_pProp->IsItemVisible(pItem)) { if (m_pProp->IsSingleSelection()) pItem->Select(FALSE); m_pProp->SetFocusedItem(NULL); m_pProp->SendNotify(PTN_SELCHANGE, NULL); Invalidate(); } } /*----------------------------------------------------------------------------* * 描述: 把右侧按钮事件,发送消息给父窗口(一般为控件所在对话框) * 备注: -----------------------------------------------------------------------------*/ void CPropList::OnButtonClick() { if (NULL == m_pProp || m_pBt == NULL) return; CRect rect; CPoint pt; m_pBt->GetWindowRect(rect); ScreenToClient(rect); pt.x = (rect.left + rect.right) / 2; pt.y = (rect.top + rect.bottom) / 2; CPropItem* pItem = m_pProp->FindItem(pt); if (pItem == NULL) return; CWnd* pWnd = m_pProp->GetParent(); if (pWnd == NULL) return; if (pItem->m_nItemType == ITEMTYPE_EDIT) ::SendMessage(pWnd->m_hWnd, WM_EBTNCLICK, (WPARAM)ITEMTYPE_EDIT, (LPARAM)(CPropItemEdit*)pItem); else if (pItem->m_nItemType == ITEMTYPE_COMBOBOX) ::SendMessage(pWnd->m_hWnd, WM_EBTNCLICK, (WPARAM)ITEMTYPE_COMBOBOX, (LPARAM)(CPropItemCombo*)pItem); } void CPropList::OnOnDeltaposSpinCtrl(NMHDR* pNMHDR, LRESULT* pResult) { NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; m_pSpinCtrl->ShowWindow(SW_SHOW); // if (GetDlgItem(IDC_SPINCTRL)->ShowWindow(SW_SHOW)) // { // MessageBox(_T("当前可见")); // } // else // { // MessageBox(_T("当前不可见")); // } *pResult = 0; } void CPropList::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { // TODO: Add your message handler code here and/or call default ShowButton(FALSE); ShowSpinCtrl(FALSE); CWnd::OnHScroll(nSBCode, nPos, pScrollBar); } void CPropList::ShowButton( BOOL bShow /*= TRUE*/ ) { if (NULL != m_pBt) { m_pBt->ShowWindow(bShow ? SW_SHOW:SW_HIDE); } } void CPropList::ShowSpinCtrl(BOOL bShow /* = TRUE */) { if (NULL != m_pSpinCtrl) { m_pSpinCtrl->ShowWindow(bShow ? SW_SHOW:SW_HIDE); } } void CPropList::ShowVScrolBar( BOOL bShow /*= TRUE*/ ) { m_bVScrolBar = bShow; }