You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1248 lines
24 KiB
C++
1248 lines
24 KiB
C++
// XTPCoreTreeControl.cpp : implementation file
|
|
//
|
|
// This file is a part of the XTREME CONTROLS MFC class library.
|
|
// (c)1998-2012 Codejock Software, All Rights Reserved.
|
|
//
|
|
// THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
|
|
// RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
|
|
// CONSENT OF CODEJOCK SOFTWARE.
|
|
//
|
|
// THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
|
|
// IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
|
|
// YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
|
|
// SINGLE COMPUTER.
|
|
//
|
|
// CONTACT INFORMATION:
|
|
// support@codejock.com
|
|
// http://www.codejock.com
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "Common/XTPImageManager.h"
|
|
#include "Common/XTPDrawHelpers.h"
|
|
#include "Common/XTPMarkupRender.h"
|
|
|
|
#include "XTPCoreTreeControl.h"
|
|
#include "XTPCoreTreeItem.h"
|
|
#include "XTPCoreTreePaintManager.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
IMPLEMENT_DYNAMIC(CXTPCoreTreeControl, CWnd)
|
|
CLIPFORMAT CXTPCoreTreeControl::m_cfItem = (CLIPFORMAT)::RegisterClipboardFormat(_T("CoreTreeItem"));
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CTreeDropTarget
|
|
|
|
class CXTPCoreTreeControl::CTreeDropTarget : public COleDropTarget
|
|
{
|
|
public:
|
|
CTreeDropTarget()
|
|
{
|
|
m_nHoverTime = 500;
|
|
SystemParametersInfo(SPI_GETMOUSEHOVERTIME, 0, &m_nHoverTime, 0);
|
|
}
|
|
|
|
|
|
virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
|
|
{
|
|
CXTPCoreTreeControl* pTree = DYNAMIC_DOWNCAST(CXTPCoreTreeControl, pWnd);
|
|
if (!pTree)
|
|
return DROPEFFECT_NONE;
|
|
|
|
|
|
CRect rectClient;
|
|
pWnd->GetClientRect(&rectClient);
|
|
CRect rect = rectClient;
|
|
|
|
// hit-test against inset region
|
|
UINT nTimerID = MAKEWORD(-1, -1);
|
|
rect.InflateRect(0, -nScrollInset);
|
|
|
|
const int nTimerHover = 10;
|
|
|
|
if (rectClient.PtInRect(point) && !rect.PtInRect(point))
|
|
{
|
|
// determine which way to scroll along both X & Y axis
|
|
if (point.y < rect.top)
|
|
nTimerID = SB_LINEUP;
|
|
else if (point.y >= rect.bottom)
|
|
nTimerID = SB_LINEDOWN;
|
|
}
|
|
else if (rectClient.PtInRect(point))
|
|
{
|
|
nTimerID = nTimerHover;
|
|
}
|
|
|
|
if (nTimerID == nTimerHover)
|
|
{
|
|
// save tick count when timer ID changes
|
|
DWORD dwTick = GetTickCount();
|
|
if (point != m_ptLastPoint)
|
|
{
|
|
m_ptLastPoint = point;
|
|
m_dwLastTick = dwTick;
|
|
m_nScrollDelay = m_nHoverTime;
|
|
}
|
|
|
|
// scroll if necessary
|
|
if (dwTick - m_dwLastTick > m_nScrollDelay)
|
|
{
|
|
pTree->OnDragHover(pDataObject, dwKeyState, point);
|
|
m_dwLastTick = dwTick;
|
|
}
|
|
|
|
}
|
|
else if (nTimerID != MAKEWORD(-1, -1))
|
|
{
|
|
// save tick count when timer ID changes
|
|
DWORD dwTick = GetTickCount();
|
|
if (nTimerID != m_nTimerID)
|
|
{
|
|
m_dwLastTick = dwTick;
|
|
m_nScrollDelay = nScrollDelay;
|
|
}
|
|
|
|
// scroll if necessary
|
|
if (dwTick - m_dwLastTick > m_nScrollDelay)
|
|
{
|
|
pTree->OnVScroll(nTimerID, 0, NULL);
|
|
m_dwLastTick = dwTick;
|
|
m_nScrollDelay = nScrollInterval;
|
|
}
|
|
}
|
|
|
|
m_nTimerID = nTimerID;
|
|
|
|
return pTree->OnDragOver(pDataObject, dwKeyState, point);
|
|
}
|
|
|
|
virtual void OnDragLeave(CWnd* pWnd)
|
|
{
|
|
CXTPCoreTreeControl* pTree = DYNAMIC_DOWNCAST(CXTPCoreTreeControl, pWnd);
|
|
|
|
if (pTree)
|
|
{
|
|
pTree->OnDragOver(NULL, 0, CPoint(-1, -1));
|
|
}
|
|
}
|
|
virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject,
|
|
DROPEFFECT dropEffect, CPoint point)
|
|
{
|
|
CXTPCoreTreeControl* pTree = DYNAMIC_DOWNCAST(CXTPCoreTreeControl, pWnd);
|
|
|
|
if (pTree)
|
|
{
|
|
return pTree->OnDrop(pDataObject, dropEffect, point);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
CPoint m_ptLastPoint;
|
|
int m_nHoverTime;
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CXTPCoreTreeControl
|
|
|
|
CXTPCoreTreeControl::CXTPCoreTreeControl()
|
|
{
|
|
RegisterWindowClass();
|
|
|
|
m_pItems = new CXTPCoreTreeItems();
|
|
m_pItems->m_pControl = this;
|
|
|
|
m_pPaintManager = new CXTPCoreTreePaintManager();
|
|
m_pPaintManager->RefreshMetrics();
|
|
|
|
m_nClientHeight = 0;
|
|
m_rcControl.SetRectEmpty();
|
|
|
|
m_bPreSubclassWindow = TRUE;
|
|
|
|
|
|
m_bScrollVisible = FALSE;
|
|
m_bInUpdateScrollBar = FALSE;
|
|
|
|
m_pImageManager = new CXTPImageManager();
|
|
m_nLockRedraw = 0;
|
|
|
|
m_ptPressed = CPoint(-1, -1);
|
|
|
|
m_pFocusedItem = NULL;
|
|
m_pDraggingItem = NULL;
|
|
m_pPressedItem = NULL;
|
|
m_pDragOverItem = NULL;
|
|
|
|
m_nDropPosition = xtpCoreTreeDropAfter;
|
|
|
|
m_pDropTarget = new CTreeDropTarget();
|
|
|
|
m_pMarkupContext = NULL;
|
|
}
|
|
|
|
CXTPCoreTreeControl::~CXTPCoreTreeControl()
|
|
{
|
|
SAFE_DELETE(m_pItems);
|
|
SAFE_DELETE(m_pPaintManager);
|
|
|
|
CMDTARGET_RELEASE(m_pImageManager);
|
|
|
|
SAFE_DELETE(m_pDropTarget);
|
|
|
|
XTPMarkupReleaseContext(m_pMarkupContext);
|
|
|
|
|
|
}
|
|
|
|
BOOL CXTPCoreTreeControl::RegisterWindowClass(HINSTANCE hInstance /*= NULL*/)
|
|
{
|
|
return XTPDrawHelpers()->RegisterWndClass(hInstance, _T("XTPCoreTree"), CS_DBLCLKS);
|
|
}
|
|
|
|
|
|
void CXTPCoreTreeControl::SetPaintManager(CXTPCoreTreePaintManager* pPaintManager)
|
|
{
|
|
ASSERT (pPaintManager);
|
|
|
|
SAFE_DELETE(m_pPaintManager);
|
|
|
|
m_pPaintManager = pPaintManager;
|
|
m_pPaintManager->RefreshMetrics();
|
|
}
|
|
|
|
void CXTPCoreTreeControl::EnableMarkup(BOOL bEnable)
|
|
{
|
|
XTPMarkupReleaseContext(m_pMarkupContext);
|
|
|
|
if (bEnable)
|
|
{
|
|
m_pMarkupContext = XTPMarkupCreateContext();
|
|
}
|
|
}
|
|
|
|
void CXTPCoreTreeControl::SetImageManager(CXTPImageManager* pImageManager)
|
|
{
|
|
CMDTARGET_RELEASE(m_pImageManager);
|
|
|
|
m_pImageManager = pImageManager;
|
|
|
|
}
|
|
|
|
void CXTPCoreTreeControl::SetFocusedItem(CXTPCoreTreeItem* pItem)
|
|
{
|
|
if (m_pFocusedItem == pItem)
|
|
return;
|
|
|
|
if (pItem && pItem->IsLabel())
|
|
return;
|
|
|
|
m_pFocusedItem = pItem;
|
|
|
|
EnsureVisible(m_pFocusedItem);
|
|
|
|
RedrawControl();
|
|
|
|
SendMessageToParent(TVN_SELCHANGED);
|
|
}
|
|
|
|
LRESULT CXTPCoreTreeControl::SendMessageToParent(int nMessage, NMHDR* pNMHDR)
|
|
{
|
|
if (!IsWindow(m_hWnd))
|
|
return 0;
|
|
|
|
NMHDR nmhdr;
|
|
if (pNMHDR == NULL)
|
|
pNMHDR = &nmhdr;
|
|
|
|
pNMHDR->hwndFrom = GetSafeHwnd();
|
|
pNMHDR->idFrom = GetDlgCtrlID();
|
|
pNMHDR->code = nMessage;
|
|
|
|
CWnd *pOwner = GetOwner();
|
|
LRESULT res = 0;
|
|
if (pOwner && IsWindow(pOwner->m_hWnd))
|
|
res = pOwner->SendMessage(WM_NOTIFY, pNMHDR->idFrom, (LPARAM)pNMHDR);
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
void CXTPCoreTreeControl::RedrawControl(LPCRECT lpcRect)
|
|
{
|
|
if (m_nLockRedraw > 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
if (m_hWnd) InvalidateRect(lpcRect, FALSE);
|
|
}
|
|
|
|
void CXTPCoreTreeControl::SetLockRedraw(BOOL bLockRedraw)
|
|
{
|
|
m_nLockRedraw += bLockRedraw ? +1 : -1;
|
|
ASSERT(m_nLockRedraw >= 0);
|
|
|
|
if (m_nLockRedraw == 0)
|
|
RecalcLayout();
|
|
}
|
|
|
|
|
|
void CXTPCoreTreeControl::EnsureVisible(CXTPCoreTreeItem* pItem)
|
|
{
|
|
if (!pItem || !m_hWnd)
|
|
return;
|
|
|
|
CXTPCoreTreeItem* pParentItem = pItem->GetParentItem();
|
|
|
|
while (pParentItem != NULL)
|
|
{
|
|
pParentItem->SetExpanded(TRUE);
|
|
pParentItem = pParentItem->GetParentItem();
|
|
}
|
|
|
|
CXTPClientRect rcClient(this);
|
|
CRect rc = pItem->GetRect();
|
|
|
|
if (rc.bottom > rcClient.bottom + GetScrollOffset())
|
|
{
|
|
OnScrollChanged(min(rc.top, rc.bottom - rcClient.Height()));
|
|
}
|
|
else if (rc.top < GetScrollOffset())
|
|
{
|
|
OnScrollChanged(rc.top);
|
|
}
|
|
|
|
}
|
|
|
|
int CXTPCoreTreeControl::GetScrollOffset() const
|
|
{
|
|
if (!m_bScrollVisible)
|
|
return 0;
|
|
|
|
return GetScrollPos(SB_VERT);
|
|
}
|
|
|
|
void CXTPCoreTreeControl::OnScrollChanged(int nScrollOffset)
|
|
{
|
|
SetScrollPos(SB_VERT, nScrollOffset);
|
|
RedrawControl();
|
|
}
|
|
|
|
CXTPCoreTreeItem* CXTPCoreTreeControl::HitTestTreeItems(CXTPCoreTreeItems* pItems, CPoint point) const
|
|
{
|
|
POSITION pos = pItems->GetHeadPosition();
|
|
while (pos)
|
|
{
|
|
CXTPCoreTreeItem* pItem = pItems->GetNext(pos);
|
|
|
|
if (pItem->GetRect().PtInRect(point))
|
|
return pItem;
|
|
|
|
if (pItem->IsExpanded() && pItem->HasChildren())
|
|
{
|
|
pItem = HitTestTreeItems(pItem->GetChildren(), point);
|
|
if (pItem != NULL)
|
|
return pItem;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
CXTPCoreTreeItem* CXTPCoreTreeControl::HitTest(CPoint point) const
|
|
{
|
|
point.y += GetScrollOffset();
|
|
|
|
return HitTestTreeItems(m_pItems, point);
|
|
}
|
|
|
|
|
|
|
|
BOOL CXTPCoreTreeControl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
|
|
{
|
|
if (!CreateEx(0, _T("XTPCoreTree"), NULL, dwStyle, rect, pParentWnd, nID))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
m_pDropTarget->Register(this);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void CXTPCoreTreeControl::OnItemRemoved(CXTPCoreTreeItem* pItem)
|
|
{
|
|
if (pItem == m_pFocusedItem)
|
|
{
|
|
SetFocusedItem(NULL);
|
|
}
|
|
|
|
if (pItem == m_pPressedItem)
|
|
m_pPressedItem = NULL;
|
|
}
|
|
|
|
void CXTPCoreTreeControl::ExpandeTreeItems(CXTPCoreTreeItems* pItems)
|
|
{
|
|
POSITION pos = pItems->GetHeadPosition();
|
|
while (pos)
|
|
{
|
|
CXTPCoreTreeItem* pItem = pItems->GetNext(pos);
|
|
|
|
if (pItem->HasChildren())
|
|
{
|
|
pItem->SetExpanded(TRUE);
|
|
|
|
ExpandeTreeItems(pItem->GetChildren());
|
|
}
|
|
}
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(CXTPCoreTreeControl, CWnd)
|
|
//{{AFX_MSG_MAP(CXTPCoreTreeControl)
|
|
ON_WM_PAINT()
|
|
ON_WM_ERASEBKGND()
|
|
ON_WM_SIZE()
|
|
ON_WM_KEYDOWN()
|
|
ON_WM_LBUTTONDOWN()
|
|
ON_WM_SYSCOLORCHANGE()
|
|
ON_WM_GETDLGCODE()
|
|
ON_WM_VSCROLL()
|
|
ON_WM_MOUSEWHEEL()
|
|
ON_WM_LBUTTONDBLCLK()
|
|
ON_WM_CHAR()
|
|
ON_WM_MOUSEMOVE()
|
|
ON_WM_LBUTTONUP()
|
|
ON_WM_CAPTURECHANGED()
|
|
ON_WM_NCPAINT()
|
|
ON_WM_SETFOCUS()
|
|
ON_WM_KILLFOCUS()
|
|
//}}AFX_MSG_MAP
|
|
ON_MESSAGE(WM_PRINTCLIENT, OnPrintClient)
|
|
ON_MESSAGE(WM_SETFONT, OnSetFont)
|
|
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
|
|
int CXTPCoreTreeControl::RecalcTreeItems(CXTPCoreTreeItems* pItems, CDC* pDC, int nLevel, LPCRECT lprcMargin)
|
|
{
|
|
int nTop = lprcMargin->top;
|
|
int nWidth = m_rcControl.Width() - lprcMargin->left - lprcMargin->right;
|
|
|
|
POSITION pos = pItems->GetHeadPosition();
|
|
while (pos)
|
|
{
|
|
CXTPCoreTreeItem* pItem = pItems->GetNext(pos);
|
|
|
|
CSize sz = pItem->CalcSize(pDC, nWidth);
|
|
|
|
pItem->SetRect(CRect(m_rcControl.left + lprcMargin->left, nTop, m_rcControl.right - lprcMargin->right, nTop + sz.cy));
|
|
|
|
nTop += sz.cy;
|
|
|
|
if (pItem->IsExpanded() && pItem->HasChildren())
|
|
{
|
|
CRect rcMargin(lprcMargin);
|
|
rcMargin.top = nTop;
|
|
|
|
rcMargin.left += m_pPaintManager->GetChildrenIndent(pItem);
|
|
|
|
nTop = RecalcTreeItems(pItem->GetChildren(), pDC, nLevel + 1, rcMargin);
|
|
}
|
|
}
|
|
|
|
return nTop;
|
|
}
|
|
|
|
void CXTPCoreTreeControl::RecalcLayout()
|
|
{
|
|
if (!GetSafeHwnd())
|
|
return;
|
|
|
|
if (m_nLockRedraw > 0)
|
|
return;
|
|
|
|
|
|
CXTPClientRect rc(this);
|
|
|
|
m_rcControl = rc;
|
|
CRect rcMargin(0, 0, 0, 0);
|
|
|
|
{
|
|
CClientDC dc(this);
|
|
|
|
int nPos = RecalcTreeItems(m_pItems, &dc, 0, rcMargin);
|
|
|
|
m_nClientHeight = nPos;
|
|
}
|
|
|
|
UpdateScrollBar();
|
|
|
|
RedrawControl();
|
|
}
|
|
|
|
void CXTPCoreTreeControl::UpdateScrollBar()
|
|
{
|
|
if (m_bInUpdateScrollBar)
|
|
return;
|
|
|
|
m_bInUpdateScrollBar = TRUE;
|
|
|
|
CXTPClientRect rc(this);
|
|
m_bScrollVisible = rc.Height() < m_nClientHeight;
|
|
|
|
if (m_bScrollVisible)
|
|
{
|
|
ShowScrollBar(SB_VERT, TRUE);
|
|
|
|
SCROLLINFO si ;
|
|
|
|
si.cbSize = sizeof(SCROLLINFO) ;
|
|
si.fMask = SIF_PAGE | SIF_RANGE ;
|
|
|
|
GetScrollInfo(SB_VERT, &si, si.fMask);
|
|
|
|
UINT nControlHeight = (UINT)rc.Height();
|
|
|
|
if (si.nPage != nControlHeight || si.nMax != m_nClientHeight - 1 || si.nMin != 0)
|
|
{
|
|
si.nPage = nControlHeight ;
|
|
si.nMax = m_nClientHeight - 1;
|
|
si.nMin = 0 ;
|
|
si.fMask = SIF_PAGE | SIF_RANGE ;
|
|
SetScrollInfo(SB_VERT, &si) ;
|
|
}
|
|
EnableScrollBarCtrl(SB_VERT, TRUE);
|
|
}
|
|
else
|
|
{
|
|
SetScrollPos(SB_VERT, 0);
|
|
ShowScrollBar(SB_VERT, FALSE);
|
|
}
|
|
|
|
m_bInUpdateScrollBar = FALSE;
|
|
}
|
|
|
|
|
|
|
|
void CXTPCoreTreeControl::DrawTreeItems(CXTPCoreTreeItems* pItems, CDC* pDC, CRect rcClipBox)
|
|
{
|
|
POSITION pos = pItems->GetHeadPosition();
|
|
while (pos)
|
|
{
|
|
CXTPCoreTreeItem* pItem = pItems->GetNext(pos);
|
|
|
|
if (CRect().IntersectRect(pItem->GetRect(), rcClipBox))
|
|
{
|
|
pItem->Draw(pDC);
|
|
}
|
|
|
|
|
|
if (pItem->IsExpanded() && pItem->HasChildren())
|
|
{
|
|
DrawTreeItems(pItem->GetChildren(), pDC, rcClipBox);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CXTPCoreTreeControl::OnDraw(CDC* pDC, CRect rcClipBox)
|
|
{
|
|
pDC->SetBkMode(TRANSPARENT);
|
|
|
|
m_pPaintManager->FillTreeControl(pDC, this);
|
|
|
|
int nScrollOffset = GetScrollOffset();
|
|
|
|
CPoint pt = pDC->GetViewportOrg();
|
|
pDC->SetViewportOrg(pt.x, pt.y - nScrollOffset);
|
|
|
|
rcClipBox.OffsetRect(0, nScrollOffset);
|
|
|
|
DrawTreeItems(m_pItems, pDC, rcClipBox);
|
|
|
|
if (m_pDragOverItem)
|
|
{
|
|
m_pPaintManager->DrawDropRect(pDC, m_pDragOverItem);
|
|
}
|
|
|
|
pDC->SetViewportOrg(pt);
|
|
}
|
|
|
|
|
|
BOOL CXTPCoreTreeControl::PreCreateWindow(CREATESTRUCT& cs)
|
|
{
|
|
if (!CWnd::PreCreateWindow(cs))
|
|
return FALSE;
|
|
|
|
//
|
|
// PreCreateWindow is called when a control is dynamically
|
|
// created. We want to set m_bPreSubclassWindow to FALSE
|
|
// here so the control is initialized from CWnd::Create and
|
|
// not CWnd::PreSubclassWindow.
|
|
//
|
|
|
|
m_bPreSubclassWindow = FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void CXTPCoreTreeControl::PreSubclassWindow()
|
|
{
|
|
CWnd::PreSubclassWindow();
|
|
|
|
if (m_bPreSubclassWindow)
|
|
{
|
|
m_pDropTarget->Register(this);
|
|
}
|
|
|
|
RecalcLayout();
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CXTPCoreTreeControl message handlers
|
|
|
|
void CXTPCoreTreeControl::OnPaint()
|
|
{
|
|
CPaintDC dcPaint(this); // device context for painting
|
|
CRect rcPaint(dcPaint.m_ps.rcPaint);
|
|
|
|
|
|
CXTPBufferDC dc(dcPaint);
|
|
|
|
OnDraw(&dc, rcPaint);
|
|
}
|
|
|
|
LRESULT CXTPCoreTreeControl::OnPrintClient(WPARAM wParam, LPARAM /*lParam*/)
|
|
{
|
|
CDC* pDC = CDC::FromHandle((HDC)wParam);
|
|
if (pDC)
|
|
{
|
|
OnDraw(pDC, CXTPClientRect(this));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOL CXTPCoreTreeControl::OnEraseBkgnd(CDC* /*pDC*/)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
void CXTPCoreTreeControl::OnSize(UINT nType, int cx, int cy)
|
|
{
|
|
CWnd::OnSize(nType, cx, cy);
|
|
|
|
RecalcLayout();
|
|
}
|
|
|
|
CXTPCoreTreeItem* CXTPCoreTreeControl::GetNextFocusableItem(CXTPCoreTreeItem* pFocusedItem) const
|
|
{
|
|
do
|
|
{
|
|
if (!pFocusedItem)
|
|
{
|
|
pFocusedItem = m_pItems->GetFirstItem();
|
|
}
|
|
else if (pFocusedItem->HasChildren() && pFocusedItem->IsExpanded())
|
|
{
|
|
pFocusedItem = pFocusedItem->GetFirstChildItem();
|
|
}
|
|
else
|
|
{
|
|
while (pFocusedItem && pFocusedItem->GetNextSiblingItem() == NULL)
|
|
{
|
|
pFocusedItem = pFocusedItem->GetParentItem();
|
|
}
|
|
|
|
if (pFocusedItem)
|
|
{
|
|
pFocusedItem = pFocusedItem->GetNextSiblingItem();
|
|
}
|
|
|
|
}
|
|
|
|
if (pFocusedItem && !pFocusedItem->IsLabel())
|
|
return pFocusedItem;
|
|
|
|
if (!pFocusedItem)
|
|
return NULL;
|
|
}
|
|
while (pFocusedItem);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
CXTPCoreTreeItem* CXTPCoreTreeControl::GetPrevFocusableItem(CXTPCoreTreeItem* pFocusedItem) const
|
|
{
|
|
|
|
do
|
|
{
|
|
if (!pFocusedItem)
|
|
{
|
|
pFocusedItem = m_pItems->GetLastItem();
|
|
|
|
while (pFocusedItem && pFocusedItem->HasChildren() && pFocusedItem->IsExpanded())
|
|
{
|
|
pFocusedItem = pFocusedItem->GetChildren()->GetLastItem();
|
|
}
|
|
}
|
|
else if (pFocusedItem->GetPrevSiblingItem() != NULL)
|
|
{
|
|
pFocusedItem = pFocusedItem->GetPrevSiblingItem();
|
|
|
|
while (pFocusedItem->HasChildren() && pFocusedItem->IsExpanded())
|
|
{
|
|
pFocusedItem = pFocusedItem->GetChildren()->GetLastItem();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pFocusedItem = pFocusedItem->GetParentItem();
|
|
}
|
|
|
|
if (pFocusedItem && !pFocusedItem->IsLabel())
|
|
return pFocusedItem;
|
|
|
|
if (!pFocusedItem)
|
|
return NULL;
|
|
}
|
|
while (pFocusedItem);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CXTPCoreTreeControl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
|
|
{
|
|
switch (nChar)
|
|
{
|
|
case VK_DOWN:
|
|
{
|
|
CXTPCoreTreeItem* pItem = GetNextFocusableItem(m_pFocusedItem);
|
|
if (pItem != NULL)
|
|
{
|
|
SetFocusedItem(pItem);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_UP:
|
|
{
|
|
CXTPCoreTreeItem* pItem = GetPrevFocusableItem(m_pFocusedItem);
|
|
if (pItem != NULL)
|
|
{
|
|
SetFocusedItem(pItem);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_LEFT:
|
|
{
|
|
if (m_pFocusedItem && m_pFocusedItem->HasChildren() && m_pFocusedItem->IsExpanded())
|
|
{
|
|
m_pFocusedItem->SetExpanded(FALSE);
|
|
}
|
|
else
|
|
{
|
|
CXTPCoreTreeItem* pItem = m_pFocusedItem ? m_pFocusedItem->GetParentItem() : NULL;
|
|
if (pItem != NULL)
|
|
{
|
|
SetFocusedItem(pItem);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_BACK:
|
|
{
|
|
CXTPCoreTreeItem* pItem = m_pFocusedItem ? m_pFocusedItem->GetParentItem() : NULL;
|
|
if (pItem != NULL)
|
|
{
|
|
SetFocusedItem(pItem);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_RIGHT:
|
|
{
|
|
if (m_pFocusedItem && m_pFocusedItem->HasChildren())
|
|
{
|
|
if (!m_pFocusedItem->IsExpanded())
|
|
{
|
|
m_pFocusedItem->SetExpanded(TRUE);
|
|
}
|
|
else
|
|
{
|
|
CXTPCoreTreeItem* pItem = GetNextFocusableItem(m_pFocusedItem);
|
|
if (pItem != NULL)
|
|
{
|
|
SetFocusedItem(pItem);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_HOME:
|
|
{
|
|
CXTPCoreTreeItem* pItem = GetNextFocusableItem(NULL);
|
|
|
|
if (pItem != NULL)
|
|
{
|
|
SetFocusedItem(pItem);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_END:
|
|
{
|
|
CXTPCoreTreeItem* pItem = GetPrevFocusableItem(NULL);
|
|
|
|
if (pItem != NULL)
|
|
{
|
|
SetFocusedItem(pItem);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_NEXT:
|
|
{
|
|
CXTPClientRect rc(this);
|
|
int nItems = (rc.Height() / m_pPaintManager->GetItemHeight()) - 2;
|
|
|
|
CXTPCoreTreeItem* pItem = m_pFocusedItem;
|
|
|
|
for (int i = 0; i < nItems; i++)
|
|
{
|
|
CXTPCoreTreeItem* pNextItem = GetNextFocusableItem(pItem);
|
|
if (!pNextItem)
|
|
break;
|
|
pItem = pNextItem;
|
|
}
|
|
|
|
if (pItem != NULL)
|
|
{
|
|
SetFocusedItem(pItem);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_PRIOR:
|
|
{
|
|
CXTPClientRect rc(this);
|
|
int nItems = (rc.Height() / m_pPaintManager->GetItemHeight()) - 2;
|
|
|
|
CXTPCoreTreeItem* pItem = m_pFocusedItem;
|
|
|
|
for (int i = 0; i < nItems; i++)
|
|
{
|
|
CXTPCoreTreeItem* pNextItem = GetPrevFocusableItem(pItem);
|
|
if (!pNextItem)
|
|
break;
|
|
pItem = pNextItem;
|
|
}
|
|
|
|
if (pItem != NULL)
|
|
{
|
|
SetFocusedItem(pItem);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_SPACE:
|
|
|
|
if (m_pFocusedItem && m_pFocusedItem->IsCheckboxVisible())
|
|
{
|
|
OnCheckClicked(m_pFocusedItem);
|
|
}
|
|
break;
|
|
|
|
case VK_ADD:
|
|
|
|
if (m_pFocusedItem && m_pFocusedItem->HasChildren())
|
|
{
|
|
m_pFocusedItem->SetExpanded(TRUE);
|
|
}
|
|
break;
|
|
|
|
case VK_SUBTRACT:
|
|
|
|
if (m_pFocusedItem && m_pFocusedItem->HasChildren())
|
|
{
|
|
m_pFocusedItem->SetExpanded(FALSE);
|
|
}
|
|
break;
|
|
|
|
case VK_MULTIPLY:
|
|
|
|
if (m_pFocusedItem && m_pFocusedItem->HasChildren())
|
|
{
|
|
SetLockRedraw(TRUE);
|
|
|
|
m_pFocusedItem->SetExpanded(TRUE);
|
|
ExpandeTreeItems(m_pFocusedItem->GetChildren());
|
|
|
|
SetLockRedraw(FALSE);
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
|
|
}
|
|
|
|
void CXTPCoreTreeControl::StartDragItem(CXTPCoreTreeItem* /*pItem*/)
|
|
{
|
|
|
|
}
|
|
|
|
BOOL CXTPCoreTreeControl::DragItem(CXTPCoreTreeItem* pItem, DROPEFFECT dropEffectMask)
|
|
{
|
|
ASSERT(pItem);
|
|
if (!pItem)
|
|
return FALSE;
|
|
|
|
COleDataSource ds;
|
|
if (!pItem->PrepareDrag(ds))
|
|
return FALSE;
|
|
|
|
m_pDraggingItem = pItem;
|
|
|
|
|
|
DROPEFFECT dropEffect = ds.DoDragDrop(dropEffectMask);
|
|
|
|
BOOL bRemoved = FALSE;
|
|
|
|
if ((dropEffect == DROPEFFECT_MOVE) && (dropEffectMask & DROPEFFECT_MOVE))
|
|
{
|
|
if (m_pDraggingItem)
|
|
{
|
|
m_pDraggingItem->Remove();
|
|
bRemoved = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
m_pDraggingItem = NULL;
|
|
|
|
return bRemoved;
|
|
}
|
|
|
|
void CXTPCoreTreeControl::SetDragOverItem(CXTPCoreTreeItem* pDragOverItem, XTPCoreTreeDropPosition nDropPosition)
|
|
{
|
|
if (m_pDragOverItem != pDragOverItem || nDropPosition != m_nDropPosition)
|
|
{
|
|
m_pDragOverItem = pDragOverItem;
|
|
m_nDropPosition = nDropPosition;
|
|
|
|
RedrawControl();
|
|
}
|
|
|
|
};
|
|
|
|
DROPEFFECT CXTPCoreTreeControl::OnDragOver(COleDataObject* /*pDataObject*/, DWORD /*dwKeyState*/, CPoint /*point*/)
|
|
{
|
|
return DROPEFFECT_NONE;
|
|
|
|
}
|
|
|
|
void CXTPCoreTreeControl::OnDragHover(COleDataObject* /*pDataObject*/, DWORD /*dwKeyState*/, CPoint point)
|
|
{
|
|
CXTPCoreTreeItem* pItem = HitTest(point);
|
|
|
|
if (pItem && pItem->HasChildren())
|
|
{
|
|
pItem->SetExpanded(TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL CXTPCoreTreeControl::OnDrop(COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint /*point*/)
|
|
{
|
|
if (!m_pDragOverItem)
|
|
return FALSE;
|
|
|
|
CXTPCoreTreeItem* pDragOverItem = m_pDragOverItem;
|
|
m_pDragOverItem = NULL;
|
|
|
|
RedrawControl();
|
|
|
|
if (dropEffect != DROPEFFECT_COPY && dropEffect != DROPEFFECT_MOVE)
|
|
return FALSE;
|
|
|
|
if (!pDataObject || !pDataObject->IsDataAvailable(GetClipboardFormat()))
|
|
return FALSE;
|
|
|
|
|
|
CXTPCoreTreeItem* pItemDrop = CXTPCoreTreeItem::CreateFromOleData(pDataObject);
|
|
if (!pItemDrop)
|
|
return FALSE;
|
|
|
|
|
|
if (m_nDropPosition == xtpCoreTreeDropAfter)
|
|
{
|
|
pDragOverItem->GetParentItems()->AddItemAfter(pItemDrop, pDragOverItem);
|
|
}
|
|
else if (m_nDropPosition == xtpCoreTreeDropBefore)
|
|
{
|
|
pDragOverItem->GetParentItems()->AddItemBefore(pItemDrop, pDragOverItem);
|
|
}
|
|
else if (m_nDropPosition == xtpCoreTreeDropChild)
|
|
{
|
|
pDragOverItem->GetChildren()->AddItemBefore(pItemDrop, NULL);
|
|
pDragOverItem->SetExpanded();
|
|
}
|
|
else
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
SetFocusedItem(pItemDrop);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void CXTPCoreTreeControl::OnMouseMove(UINT nFlags, CPoint point)
|
|
{
|
|
if (m_pPressedItem && (abs(m_ptPressed.x - point.x) + abs(m_ptPressed.y - point.y) > 4))
|
|
{
|
|
CXTPCoreTreeItem* pItem = m_pPressedItem;
|
|
ReleaseCapture();
|
|
|
|
StartDragItem(pItem);
|
|
return;
|
|
}
|
|
|
|
|
|
CWnd::OnMouseMove(nFlags, point);
|
|
}
|
|
|
|
void CXTPCoreTreeControl::OnCheckClicked(CXTPCoreTreeItem* pItem)
|
|
{
|
|
if (pItem->IsEnabled())
|
|
{
|
|
pItem->SetChecked(!pItem->IsChecked());
|
|
|
|
SendMessageToParent(XTP_TVN_CHECKCHANGED);
|
|
}
|
|
}
|
|
|
|
void CXTPCoreTreeControl::OnLButtonDown(UINT nFlags, CPoint point)
|
|
{
|
|
SetFocus();
|
|
|
|
CXTPCoreTreeItem* pItem = HitTest(point);
|
|
|
|
if (pItem != NULL)
|
|
{
|
|
m_ptPressed = point;
|
|
|
|
point.y += GetScrollOffset();
|
|
|
|
if (pItem->HasChildren() && pItem->GetButtonRect().PtInRect(point))
|
|
{
|
|
pItem->SetExpanded(!pItem->IsExpanded());
|
|
|
|
if (pItem->IsParent(m_pFocusedItem))
|
|
{
|
|
SetFocusedItem(pItem);
|
|
}
|
|
}
|
|
else if (pItem->GetCheckboxRect().PtInRect(point))
|
|
{
|
|
SetFocusedItem(pItem);
|
|
OnCheckClicked(pItem);
|
|
}
|
|
else
|
|
{
|
|
SetFocusedItem(pItem);
|
|
|
|
SetCapture();
|
|
m_pPressedItem = pItem;
|
|
}
|
|
}
|
|
|
|
CWnd::OnLButtonDown(nFlags, point);
|
|
}
|
|
|
|
void CXTPCoreTreeControl::OnLButtonUp(UINT nFlags, CPoint point)
|
|
{
|
|
m_pPressedItem = NULL;
|
|
ReleaseCapture();
|
|
|
|
CWnd::OnLButtonUp(nFlags, point);
|
|
}
|
|
|
|
void CXTPCoreTreeControl::OnCaptureChanged(CWnd* pWnd)
|
|
{
|
|
m_pPressedItem = NULL;
|
|
|
|
CWnd::OnCaptureChanged(pWnd);
|
|
}
|
|
|
|
|
|
|
|
void CXTPCoreTreeControl::OnLButtonDblClk(UINT nFlags, CPoint point)
|
|
{
|
|
CXTPCoreTreeItem* pItem = HitTest(point);
|
|
|
|
if (pItem)
|
|
{
|
|
point.y += GetScrollOffset();
|
|
|
|
if (pItem->GetCheckboxRect().PtInRect(point))
|
|
{
|
|
OnCheckClicked(pItem);
|
|
}
|
|
else if (pItem->HasChildren())
|
|
{
|
|
pItem->SetExpanded(!pItem->IsExpanded());
|
|
}
|
|
}
|
|
|
|
CWnd::OnLButtonDblClk(nFlags, point);
|
|
}
|
|
|
|
|
|
void CXTPCoreTreeControl::OnSysColorChange()
|
|
{
|
|
CWnd::OnSysColorChange();
|
|
|
|
m_pPaintManager->RefreshMetrics();
|
|
RedrawControl();
|
|
}
|
|
|
|
UINT CXTPCoreTreeControl::OnGetDlgCode()
|
|
{
|
|
return DLGC_WANTARROWS | DLGC_WANTCHARS;
|
|
}
|
|
|
|
|
|
void CXTPCoreTreeControl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
|
|
{
|
|
if (pScrollBar != NULL)
|
|
{
|
|
CWnd::OnVScroll(nSBCode, nPos, pScrollBar);
|
|
return;
|
|
}
|
|
|
|
// its horizontal scroll bar
|
|
int nCurPos = GetScrollPos(SB_VERT);
|
|
|
|
// decide what to do for each diffrent scroll event
|
|
switch (nSBCode)
|
|
{
|
|
case SB_LEFT: nCurPos = 0; break;
|
|
case SB_RIGHT: nCurPos = GetScrollLimit(SB_VERT); break;
|
|
case SB_LINELEFT: nCurPos = max(nCurPos - 6, 0); break;
|
|
case SB_LINERIGHT: nCurPos = min(nCurPos + 6, GetScrollLimit(SB_VERT)); break;
|
|
case SB_PAGELEFT: nCurPos = max(nCurPos - CXTPClientRect(this).Height(), 0); break;
|
|
case SB_PAGERIGHT: nCurPos = min(nCurPos + CXTPClientRect(this).Height(), GetScrollLimit(SB_VERT)); break;
|
|
case SB_THUMBTRACK:
|
|
case SB_THUMBPOSITION:
|
|
{
|
|
SCROLLINFO si;
|
|
ZeroMemory(&si, sizeof(SCROLLINFO));
|
|
si.cbSize = sizeof(SCROLLINFO);
|
|
si.fMask = SIF_TRACKPOS;
|
|
if (!GetScrollInfo(SB_VERT, &si))
|
|
return;
|
|
nCurPos = si.nTrackPos;
|
|
}
|
|
break;
|
|
}
|
|
|
|
OnScrollChanged(nCurPos);
|
|
}
|
|
|
|
BOOL CXTPCoreTreeControl::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
|
|
{
|
|
if (m_bScrollVisible)
|
|
{
|
|
int nCurPos = GetScrollPos(SB_VERT);
|
|
|
|
int nDelta = 3 * m_pPaintManager->GetItemHeight();
|
|
|
|
if (zDelta > 0) nCurPos = max(nCurPos - nDelta, 0);
|
|
else nCurPos = min(nCurPos + nDelta, GetScrollLimit(SB_VERT));
|
|
|
|
OnScrollChanged(nCurPos);
|
|
|
|
RedrawControl();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return CWnd::OnMouseWheel(nFlags, zDelta, pt);
|
|
}
|
|
|
|
LRESULT CXTPCoreTreeControl::OnSetFont(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HFONT hFont = (HFONT)wParam;
|
|
|
|
LOGFONT lf;
|
|
|
|
if (GetObject(hFont, sizeof(LOGFONT), &lf))
|
|
{
|
|
m_pPaintManager->SetFontIndirect(&lf);
|
|
}
|
|
|
|
return CWnd::DefWindowProc(WM_SETFONT, wParam, lParam);
|
|
}
|
|
|
|
void CXTPCoreTreeControl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
|
|
{
|
|
CWnd::OnChar(nChar, nRepCnt, nFlags);
|
|
}
|
|
|
|
|
|
void CXTPCoreTreeControl::OnNcPaint()
|
|
{
|
|
Default();
|
|
|
|
if (GetPaintManager()->IsAppThemed() && GetExStyle() & WS_EX_CLIENTEDGE)
|
|
{
|
|
CWindowDC dc(this);
|
|
|
|
CXTPWindowRect rc(this);
|
|
rc.OffsetRect(-rc.TopLeft());
|
|
|
|
dc.Draw3dRect(rc, GetSysColor(COLOR_BTNSHADOW), GetSysColor(COLOR_BTNSHADOW));
|
|
rc.DeflateRect(1, 1);
|
|
dc.Draw3dRect(rc, GetSysColor(COLOR_WINDOW), GetSysColor(COLOR_WINDOW));
|
|
}
|
|
}
|
|
|
|
void CXTPCoreTreeControl::OnSetFocus(CWnd* pOldWnd)
|
|
{
|
|
CWnd::OnSetFocus(pOldWnd);
|
|
|
|
if (m_pFocusedItem)
|
|
{
|
|
RedrawControl(m_pFocusedItem->GetScreenRect());
|
|
}
|
|
}
|
|
|
|
void CXTPCoreTreeControl::OnKillFocus(CWnd* pNewWnd)
|
|
{
|
|
CWnd::OnKillFocus(pNewWnd);
|
|
|
|
if (m_pFocusedItem)
|
|
{
|
|
RedrawControl(m_pFocusedItem->GetScreenRect());
|
|
}
|
|
}
|