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.

1372 lines
32 KiB
C++

// SplitterWndXT.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/XTPColorManager.h"
#include "Common/XTPSystemHelpers.h"
#include "Common/XTPDrawHelpers.h"
#include "../Defines.h"
#include "../Util/XTPGlobal.h"
#include "../Util/XTPControlTheme.h"
#include "XTPSplitterWnd.h"
#include "XTPSplitterWndTheme.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// Visual attributes and other constants
// HitTest return values (values and spacing between values is important)
enum HitTestValue
{
noHit = 0,
vSplitterBox = 1,
hSplitterBox = 2,
bothSplitterBox = 3, // just for keyboard
vSplitterBar1 = 101,
vSplitterBar15 = 115,
hSplitterBar1 = 201,
hSplitterBar15 = 215,
splitterIntersection1 = 301,
splitterIntersection225 = 525
};
/////////////////////////////////////////////////////////////////////////////
// CXTPSplitterWnd
CXTPSplitterWnd::CXTPSplitterWnd()
: m_pTheme(NULL)
, m_nHiddenCol(-1)
, m_nHiddenRow(-1)
, m_bDotTracker(FALSE)
, m_bNoSize(FALSE)
, m_bFullDrag(FALSE)
, m_bShowBorder(TRUE)
, m_nFullDrag(-1)
, m_point(-1, -1)
, m_bClipStyles(FALSE)
{
m_cxSplitter = m_cySplitter = 6;
m_cxSplitterGap = m_cySplitterGap = 6;
// Get system settings for full drag.
::SystemParametersInfo(SPI_GETDRAGFULLWINDOWS,
0, &m_bFullDrag, 0);
VERIFY(SetTheme(xtpControlThemeDefault));
}
CXTPSplitterWnd::~CXTPSplitterWnd()
{
CMDTARGET_RELEASE(m_pTheme);
}
IMPLEMENT_DYNAMIC(CXTPSplitterWnd, CSplitterWnd)
BEGIN_MESSAGE_MAP(CXTPSplitterWnd, CSplitterWnd)
//{{AFX_MSG_MAP(CXTPSplitterWnd)
ON_WM_MOUSEMOVE()
//}}AFX_MSG_MAP
ON_WM_NCHITTEST_EX()
ON_MESSAGE(WM_XTP_SETCONTROLTHEME, OnSetTheme)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CXTPSplitterWnd message handlers
void CXTPSplitterWnd::RefreshMetrics()
{
if (m_pTheme)
m_pTheme->RefreshMetrics();
if (::IsWindow(m_hWnd))
RedrawWindow();
}
BOOL CXTPSplitterWnd::SetTheme(CXTPSplitterWndTheme* pTheme)
{
CMDTARGET_RELEASE(m_pTheme);
m_pTheme = pTheme;
RefreshMetrics();
return (m_pTheme != NULL);
}
BOOL CXTPSplitterWnd::SetTheme(XTPControlTheme eTheme)
{
CXTPSplitterWndTheme* pTheme = NULL;
switch (eTheme)
{
case xtpControlThemeOffice2000:
case xtpControlThemeOfficeXP:
case xtpControlThemeVisualStudio2005:
case xtpControlThemeVisualStudio2008:
pTheme = new CXTPSplitterWndThemeOfficeXP();
break;
case xtpControlThemeVisualStudio2010:
pTheme = new CXTPSplitterWndThemeVisualStudio2010();
break;
case xtpControlThemeOffice2003:
pTheme = new CXTPSplitterWndThemeOffice2003();
break;
case xtpControlThemeResource:
pTheme = new CXTPSplitterWndThemeResource();
break;
case xtpControlThemeDefault:
default:
pTheme = new CXTPSplitterWndTheme();
break;
}
return SetTheme(pTheme);
}
LRESULT CXTPSplitterWnd::OnSetTheme(WPARAM wParam, LPARAM /*lParam*/)
{
XTPControlTheme eTheme = (XTPControlTheme)wParam;
SetTheme(eTheme);
return 0;
}
BOOL CXTPSplitterWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
switch (message)
{
case WM_SETTINGCHANGE:
::SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &m_bFullDrag, 0);
RefreshMetrics();
break;
case WM_SYSCOLORCHANGE:
RefreshMetrics();
break;
case WM_PRINTCLIENT:
PrintClient(CDC::FromHandle((HDC)wParam));
return TRUE;
case WM_LBUTTONDOWN:
m_htTrack = HitTest(CPoint((int)LOWORD(lParam), (int)HIWORD(lParam)));
break;
case WM_LBUTTONUP:
Invalidate();
break;
}
return CSplitterWnd::OnWndMsg(message, wParam, lParam, pResult);
}
void CXTPSplitterWnd::PrintClient(CDC* pDC)
{
if (pDC)
{
CXTPClientRect rClient(this);
rClient.InflateRect(-m_cxBorder, -m_cyBorder);
CRect rectInside;
GetInsideRect(rectInside);
// draw the splitter boxes
if (m_bHasVScroll && m_nRows < m_nMaxRows)
{
OnDrawSplitter(pDC, splitBox,
CRect(rectInside.right, rClient.top,
rClient.right, rClient.top + m_cySplitter));
}
if (m_bHasHScroll && m_nCols < m_nMaxCols)
{
OnDrawSplitter(pDC, splitBox,
CRect(rClient.left, rectInside.bottom,
rClient.left + m_cxSplitter, rClient.bottom));
}
// extend split bars to window border (past margins)
DrawAllSplitBars(pDC, rectInside.right, rectInside.bottom);
}
}
void CXTPSplitterWnd::ShowColumn()
{
ASSERT_VALID(this);
if (m_nCols == m_nMaxCols && m_nHiddenCol == -1)
{
return;
}
ASSERT(m_nHiddenCol != -1);
int nShowCol = m_nHiddenCol;
m_nHiddenCol = -1;
int cxNew = m_pColInfo[m_nCols].nCurSize;
m_nCols++; // add a column
ASSERT(m_nCols == m_nMaxCols);
int nCol;
// Show the hidden column
int nRow;
for (nRow = 0; nRow < m_nRows; ++nRow)
{
int nShowRow = m_nHiddenRow != -1 && m_nHiddenRow <= nRow ? nRow + 1 : nRow;
CWnd* pPaneShow = GetDlgItem(AFX_IDW_PANE_FIRST + nShowRow * 16 + m_nCols);
ASSERT(pPaneShow != NULL);
if (!pPaneShow)
return;
pPaneShow->ShowWindow(SW_SHOWNA);
for (nCol = m_nCols - 2; nCol >= nShowCol; --nCol)
{
CWnd* pPane = GetPane(nRow, nCol);
ASSERT(pPane != NULL);
if (!pPane)
return;
pPane->SetDlgCtrlID(IdFromRowCol(nRow, nCol + 1));
}
pPaneShow->SetDlgCtrlID(IdFromRowCol(nRow, nShowCol));
}
if (m_nHiddenRow != -1)
{
CWnd* pPaneHide = GetDlgItem(AFX_IDW_PANE_FIRST + m_nHiddenRow * 16 + m_nCols);
if (pPaneHide)
{
pPaneHide->SetDlgCtrlID(AFX_IDW_PANE_FIRST + m_nMaxRows * 16 + nShowCol);
}
}
// new panes have been created -- recalculate layout
for (nCol = nShowCol + 1; nCol < m_nCols; nCol++)
m_pColInfo[nCol].nIdealSize = m_pColInfo[nCol - 1].nCurSize;
m_pColInfo[nShowCol].nIdealSize = cxNew;
RecalcLayout();
}
void CXTPSplitterWnd::HideColumn(int nColHide)
{
ASSERT_VALID(this);
if (m_nHiddenCol != -1)
{
// return if the requested one is hidden
if (m_nHiddenCol == nColHide)
{
return;
}
ShowColumn();
}
ASSERT(m_nCols > 1);
ASSERT(nColHide < m_nCols);
ASSERT(m_nHiddenCol == -1);
m_nHiddenCol = nColHide;
// if the column has an active window -- change it
int nActiveRow, nActiveCol;
if (GetActivePane(&nActiveRow, &nActiveCol) != NULL)
{
if (nActiveCol == nColHide)
{
if (++nActiveCol >= m_nCols)
nActiveCol = 0;
SetActivePane(nActiveRow, nActiveCol);
}
}
// hide all column panes
int nRow;
for (nRow = 0; nRow < m_nRows; nRow++)
{
CWnd* pPaneHide = GetPane(nRow, nColHide);
ASSERT(pPaneHide != NULL);
if (!pPaneHide)
return;
pPaneHide->ShowWindow(SW_HIDE);
int nRowHide = m_nHiddenRow != -1 && m_nHiddenRow <= nRow ? nRow + 1 : nRow;
pPaneHide->SetDlgCtrlID(AFX_IDW_PANE_FIRST + nRowHide * 16 + m_nCols);
int nCol;
for (nCol = nColHide + 1; nCol < m_nCols; nCol++)
{
CWnd* pPane = GetPane(nRow, nCol);
ASSERT(pPane != NULL);
if (!pPane)
return;
pPane->SetDlgCtrlID(IdFromRowCol(nRow, nCol - 1));
}
}
m_nCols--;
m_pColInfo[m_nCols].nCurSize = m_pColInfo[nColHide].nCurSize;
RecalcLayout();
}
void CXTPSplitterWnd::ShowRow()
{
ASSERT_VALID(this);
if (m_nRows == m_nMaxRows && m_nHiddenRow == -1)
{
return;
}
ASSERT(m_nHiddenRow != -1);
int nShowRow = m_nHiddenRow;
m_nHiddenRow = -1;
int cyNew = m_pRowInfo[m_nRows].nCurSize;
m_nRows++; // add a nRow
ASSERT(m_nRows == m_nMaxRows);
int nRow;
// Show the hidden nRow
int nCol;
for (nCol = 0; nCol < m_nCols; ++nCol)
{
int nShowCol = m_nHiddenCol != -1 && m_nHiddenCol <= nCol ? nCol + 1 : nCol;
CWnd* pPaneShow = GetDlgItem(AFX_IDW_PANE_FIRST + m_nRows * 16 + nShowCol);
ASSERT(pPaneShow != NULL);
if (!pPaneShow)
return;
pPaneShow->ShowWindow(SW_SHOWNA);
for (nRow = m_nRows - 2; nRow >= nShowRow; --nRow)
{
CWnd* pPane = GetPane(nRow, nCol);
ASSERT(pPane != NULL);
if (!pPane)
return;
pPane->SetDlgCtrlID(IdFromRowCol(nRow + 1, nCol));
}
pPaneShow->SetDlgCtrlID(IdFromRowCol(nShowRow, nCol));
}
if (m_nHiddenCol != -1)
{
CWnd* pPaneHide = GetDlgItem(AFX_IDW_PANE_FIRST + m_nRows * 16 + m_nHiddenCol);
if (pPaneHide)
{
pPaneHide->SetDlgCtrlID(AFX_IDW_PANE_FIRST + nShowRow * 16 + m_nMaxCols);
}
}
// new panes have been created -- recalculate layout
for (nRow = nShowRow + 1; nRow < m_nRows; nRow++)
m_pRowInfo[nRow].nIdealSize = m_pRowInfo[nRow - 1].nCurSize;
m_pRowInfo[nShowRow].nIdealSize = cyNew;
RecalcLayout();
}
void CXTPSplitterWnd::HideRow(int nRowHide)
{
ASSERT_VALID(this);
if (m_nHiddenRow != -1)
{
// return if the requested one is hidden
if (m_nHiddenRow == nRowHide)
{
return;
}
ShowRow();
}
ASSERT(m_nRows > 1);
ASSERT(nRowHide < m_nRows);
ASSERT(m_nHiddenRow == -1);
m_nHiddenRow = nRowHide;
int nActiveRow, nActiveCol;
// if the nRow has an active window -- change it
if (GetActivePane(&nActiveRow, &nActiveCol) != NULL)
{
if (nActiveRow == nRowHide)
{
if (++nActiveRow >= m_nRows)
nActiveRow = 0;
SetActivePane(nActiveRow, nActiveCol);
}
}
// hide all nRow panes.
int nCol;
for (nCol = 0; nCol < m_nCols; ++nCol)
{
CWnd* pPaneHide = GetPane(nRowHide, nCol);
ASSERT(pPaneHide != NULL);
if (!pPaneHide)
return;
pPaneHide->ShowWindow(SW_HIDE);
int nColHide = m_nHiddenCol != -1 && m_nHiddenCol <= nCol ? nCol + 1 : nCol;
pPaneHide->SetDlgCtrlID(AFX_IDW_PANE_FIRST + m_nRows * 16 + nColHide);
int nRow;
for (nRow = nRowHide + 1; nRow < m_nRows; ++nRow)
{
CWnd* pPane = GetPane(nRow, nCol);
ASSERT(pPane != NULL);
if (!pPane)
return;
pPane->SetDlgCtrlID(IdFromRowCol(nRow-1, nCol));
}
}
m_nRows--;
m_pRowInfo[m_nRows].nCurSize = m_pRowInfo[nRowHide].nCurSize;
RecalcLayout();
}
BOOL CXTPSplitterWnd::SwitchView(int nRow, int nCol, CView* pNewView)
{
CView *pOldView = DYNAMIC_DOWNCAST(CView, GetPane(nRow, nCol));
ASSERT_KINDOF(CView, pOldView);
if (pOldView == pNewView)
return FALSE;
int nOldID = pOldView->GetDlgCtrlID();
int nNewID = pNewView->GetDlgCtrlID();
// hide the views.
pOldView->ShowWindow(SW_HIDE);
pNewView->ShowWindow(SW_HIDE);
// swap ids.
pOldView->SetDlgCtrlID(nNewID);
pNewView->SetDlgCtrlID(nOldID);
// show the views.
pOldView->ShowWindow(SW_SHOW);
pNewView->ShowWindow(SW_SHOW);
RecalcLayout();
return TRUE;
}
CView* CXTPSplitterWnd::ReplaceView(int nRow, int nCol, CView* pNewView)
{
CView* pOldView = DYNAMIC_DOWNCAST(CView, GetPane (nRow, nCol));
ASSERT_KINDOF (CView, pOldView);
if (pOldView == pNewView)
return NULL;
int nCtrlID = pOldView->GetDlgCtrlID();
// swap ids.
pOldView->SetDlgCtrlID(0);
pNewView->SetDlgCtrlID(nCtrlID);
// show the views.
pOldView->ShowWindow(SW_HIDE);
pNewView->ShowWindow(SW_SHOW);
RecalcLayout();
return pOldView;
}
CView* CXTPSplitterWnd::ReplaceView(int nRow, int nCol, CRuntimeClass* pViewClass)
{
CView* pOldView = DYNAMIC_DOWNCAST(CView, GetPane (nRow, nCol));
ASSERT_KINDOF (CView, pOldView);
if (pOldView->IsKindOf(pViewClass))
return NULL;
// Get pointer to CDocument object so that it can be used in the creation
// process of the new view
CDocument* pDocument = pOldView->GetDocument();
int nCtrlID = pOldView->GetDlgCtrlID();
int nWidth, nHeight, nMinWidth, nMinHeight;
GetRowInfo(nRow, nWidth, nMinWidth);
GetColumnInfo(nCol, nHeight, nMinHeight);
// Create new view
CCreateContext contextT;
contextT.m_pLastView = NULL;
contextT.m_pCurrentDoc = pDocument;
contextT.m_pNewViewClass = pViewClass;
contextT.m_pNewDocTemplate = pDocument ? pDocument->GetDocTemplate() : NULL;
contextT.m_pCurrentFrame = NULL;
CWnd* pWnd = NULL;
TRY
{
pWnd = (CWnd*)pViewClass->CreateObject();
if (pWnd == NULL)
AfxThrowMemoryException();
}
CATCH_ALL(e)
{
TRACE0("Out of memory creating a view.\n");
// Note: DELETE_EXCEPTION(e) not required
return NULL;
}
END_CATCH_ALL
ASSERT_KINDOF(CWnd, pWnd);
ASSERT(pWnd->m_hWnd == NULL); // not yet created.
// Create with the right size (wrong position)
if (!pWnd->Create(NULL, NULL, WS_CHILD | WS_VISIBLE,
CRect(0, 0, 0, 0), this, nCtrlID, &contextT))
{
TRACE0("Warning: couldn't create new view.\n");
// pWnd will be cleaned up by PostNcDestroy
return NULL;
}
// Hide the old view.
pOldView->ShowWindow(SW_HIDE);
pOldView->SetDlgCtrlID(0);
SetRowInfo (nRow, nWidth, nMinWidth);
SetColumnInfo (nCol, nHeight, nMinHeight);
RecalcLayout ();
CView* pNewView = DYNAMIC_DOWNCAST (CView, GetPane (nRow, nCol));
ASSERT_KINDOF (CView, pNewView);
pNewView->OnInitialUpdate();
return pOldView;
}
void CXTPSplitterWnd::SetSplitCursor(int ht)
{
if (ht == vSplitterBox ||
ht >= vSplitterBar1 &&
ht <= vSplitterBar15)
{
SetCursor(XTPAuxData().hcurVSplitBar);
}
else if (
ht == hSplitterBox ||
ht >= hSplitterBar1 &&
ht <= hSplitterBar15)
{
SetCursor(XTPAuxData().hcurHSplitBar);
}
else
{
CSplitterWnd::SetSplitCursor(ht);
}
}
BOOL CXTPSplitterWnd::FullDragWindows() const
{
if (m_htTrack == hSplitterBox ||
m_htTrack == vSplitterBox)
{
// do not allow full drag when split box is tracking.
return FALSE;
}
if (m_nFullDrag == -1)
{
// use the system setting for full drag.
return m_bFullDrag;
}
else
{
// user defined setting for full drag.
return (BOOL)m_nFullDrag;
}
}
void CXTPSplitterWnd::DrawTracker(const CRect& rect, CBrush* pBrush)
{
// do not draw over top of horizontal split box.
if (m_htTrack == hSplitterBox)
{
if (rect.left < m_cxSplitter)
return;
}
// do not draw over top of vertical split box.
if (m_htTrack == vSplitterBox)
{
if (rect.top < m_cySplitter)
return;
}
ASSERT_VALID(this);
ASSERT(!rect.IsRectEmpty());
ASSERT((GetStyle() & WS_CLIPCHILDREN) == 0);
CClientDC dc(this);
int nSavedDC = dc.SaveDC();
if (m_bDotTracker)
{
CRect rc;
GetInsideRect(rc);
if (rect.Width() < rect.Height())
{
rc.right = rect.right;
}
else
{
rc.bottom = rect.top;
}
dc.DrawFocusRect(rc);
}
else
{
CRect rcTracker(&rect);
BOOL bVert = rcTracker.Height() > rcTracker.Width();
if (bVert)
{
if (rcTracker.Width() != 4)
{
rcTracker.left = rcTracker.right - 4;
}
}
else
{
if (rcTracker.Height() != 4)
{
rcTracker.bottom = rcTracker.top + 4;
}
}
dc.SelectObject(pBrush);
dc.PatBlt(rcTracker.left, rcTracker.top, rcTracker.Width(), rcTracker.Height(), PATINVERT);
}
dc.RestoreDC(nSavedDC);
}
void CXTPSplitterWnd::OnInvertTracker(const CRect& rect)
{
if (!FullDragWindows())
{
if (m_pTheme)
{
DrawTracker(rect, m_pTheme->GetTrackBrush());
}
else
{
CSplitterWnd::OnInvertTracker(rect);
}
}
}
void CXTPSplitterWnd::OnMouseMove(UINT nFlags, CPoint pt)
{
if (!FullDragWindows())
{
CSplitterWnd::OnMouseMove(nFlags, pt);
return;
}
if (GetCapture() != this)
StopTracking(FALSE);
if (m_bTracking)
{
// move tracker to current cursor position
pt.Offset(m_ptTrackOffset); // pt is the upper right of hit detect
// limit the point to the valid split range
if (pt.y < m_rectLimit.top)
pt.y = m_rectLimit.top;
else if (pt.y > m_rectLimit.bottom)
pt.y = m_rectLimit.bottom;
if (pt.x < m_rectLimit.left)
pt.x = m_rectLimit.left;
else if (pt.x > m_rectLimit.right)
pt.x = m_rectLimit.right;
if (m_htTrack == vSplitterBox ||
m_htTrack >= vSplitterBar1 && m_htTrack <= vSplitterBar15)
{
if (m_rectTracker.top != pt.y)
{
OnInvertTracker(m_rectTracker);
m_rectTracker.OffsetRect(0, pt.y - m_rectTracker.top);
OnInvertTracker(m_rectTracker);
}
}
else if (m_htTrack == hSplitterBox ||
m_htTrack >= hSplitterBar1 && m_htTrack <= hSplitterBar15)
{
if (m_rectTracker.left != pt.x)
{
OnInvertTracker(m_rectTracker);
m_rectTracker.OffsetRect(pt.x - m_rectTracker.left, 0);
OnInvertTracker(m_rectTracker);
}
}
else if (m_htTrack == bothSplitterBox ||
(m_htTrack >= splitterIntersection1 &&
m_htTrack <= splitterIntersection225))
{
if (m_rectTracker.top != pt.y)
{
OnInvertTracker(m_rectTracker);
m_rectTracker.OffsetRect(0, pt.y - m_rectTracker.top);
OnInvertTracker(m_rectTracker);
}
if (m_rectTracker2.left != pt.x)
{
OnInvertTracker(m_rectTracker2);
m_rectTracker2.OffsetRect(pt.x - m_rectTracker2.left, 0);
OnInvertTracker(m_rectTracker2);
}
}
StopTracking(TRUE);
StartTracking(HitTest(pt));
if (m_point != pt)
{
RedrawWindow(NULL, NULL,
RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
m_point = pt;
}
}
else
{
// simply hit-test and set appropriate cursor
int ht = HitTest(pt);
SetSplitCursor(ht);
}
}
void CXTPSplitterWnd::StartTracking(int ht)
{
if (!FullDragWindows())
{
CSplitterWnd::StartTracking(ht);
return;
}
ASSERT_VALID(this);
if (ht == noHit)
return;
// GetHitRect will restrict 'm_rectLimit' as appropriate
GetInsideRect(m_rectLimit);
if (ht >= splitterIntersection1 && ht <= splitterIntersection225)
{
// split two directions (two tracking rectangles)
int row = (ht - splitterIntersection1) / 15;
int col = (ht - splitterIntersection1) % 15;
GetHitRect(row + vSplitterBar1, m_rectTracker);
int yTrackOffset = m_ptTrackOffset.y;
m_bTracking2 = TRUE;
GetHitRect(col + hSplitterBar1, m_rectTracker2);
m_ptTrackOffset.y = yTrackOffset;
}
else if (ht == bothSplitterBox)
{
// hit on splitter boxes (for keyboard)
GetHitRect(vSplitterBox, m_rectTracker);
int yTrackOffset = m_ptTrackOffset.y;
m_bTracking2 = TRUE;
GetHitRect(hSplitterBox, m_rectTracker2);
m_ptTrackOffset.y = yTrackOffset;
// center it
m_rectTracker.OffsetRect(0, m_rectLimit.Height()/2);
m_rectTracker2.OffsetRect(m_rectLimit.Width()/2, 0);
}
else
{
// only hit one bar
GetHitRect(ht, m_rectTracker);
}
// set capture
SetCapture();
// set tracking state and appropriate cursor
m_bTracking = TRUE;
m_htTrack = ht;
SetSplitCursor(ht);
}
void CXTPSplitterWnd::StopTracking(BOOL bAccept)
{
if (!FullDragWindows())
{
CSplitterWnd::StopTracking(bAccept);
return;
}
ASSERT_VALID(this);
if (!m_bTracking)
return;
ReleaseCapture();
// erase tracker rectangle
OnInvertTracker(m_rectTracker);
if (m_bTracking2)
OnInvertTracker(m_rectTracker2);
m_bTracking = m_bTracking2 = FALSE;
// save old active view
CWnd* pOldActiveView = GetActivePane();
// m_rectTracker is set to the new splitter position (without border)
// (so, adjust relative to where the border will be)
m_rectTracker.OffsetRect(-CX_BORDER , -CY_BORDER);
m_rectTracker2.OffsetRect(-CX_BORDER, -CY_BORDER);
if (bAccept)
{
if (m_htTrack == vSplitterBox)
{
SplitRow(m_rectTracker.top);
}
else if (m_htTrack >= vSplitterBar1 && m_htTrack <= vSplitterBar15)
{
// set row height
TrackRowSize(m_rectTracker.top, m_htTrack - vSplitterBar1);
RecalcLayout();
}
else if (m_htTrack == hSplitterBox)
{
SplitColumn(m_rectTracker.left);
}
else if (m_htTrack >= hSplitterBar1 && m_htTrack <= hSplitterBar15)
{
// set column width
TrackColumnSize(m_rectTracker.left, m_htTrack - hSplitterBar1);
RecalcLayout();
}
else if (m_htTrack >= splitterIntersection1 &&
m_htTrack <= splitterIntersection225)
{
// set row height and column width
int row = (m_htTrack - splitterIntersection1) / 15;
int col = (m_htTrack - splitterIntersection1) % 15;
TrackRowSize(m_rectTracker.top, row);
TrackColumnSize(m_rectTracker2.left, col);
RecalcLayout();
}
else if (m_htTrack == bothSplitterBox)
{
// rectTracker is vSplitter (splits rows)
// rectTracker2 is hSplitter (splits cols)
SplitRow(m_rectTracker.top);
SplitColumn(m_rectTracker2.left);
}
}
if (pOldActiveView == GetActivePane())
{
if (pOldActiveView != NULL)
{
SetActivePane(-1, -1, pOldActiveView); // re-activate
}
}
}
LRESULT CXTPSplitterWnd::OnNcHitTest(CPoint point)
{
// If m_bNoSize is TRUE, just return a border
// hit so can at least activate the app if needed.
if (m_bNoSize)
{
return HTBORDER;
}
return (LRESULT)CSplitterWnd::OnNcHitTest(point);
}
void CXTPSplitterWnd::OnDrawSplitter(CDC* pDC, ESplitType nType, const CRect& rectArg)
{
if (m_pTheme)
{
// if NULL just invalidate
if (pDC == NULL)
{
RedrawWindow(rectArg, NULL, RDW_INVALIDATE | RDW_NOCHILDREN);
}
else
{
ASSERT_VALID(pDC);
m_pTheme->DrawSplitter(pDC, this, nType, rectArg);
}
}
else
{
// if no theme defined do default drawing.
CSplitterWnd::OnDrawSplitter(pDC, nType, rectArg);
}
}
void CXTPSplitterWnd::SetActivePane(int row, int col, CWnd* pWnd/*= NULL*/)
{
// set the focus to the pane
CWnd* pPane = pWnd == NULL ? GetPane(row, col) : pWnd;
if (pPane->IsKindOf(RUNTIME_CLASS(CView)))
{
CFrameWnd* pFrameWnd = GetParentFrame();
ASSERT_VALID(pFrameWnd);
pFrameWnd->SetActiveView((CView*)pPane);
}
else
{
pPane->SetFocus();
}
}
//-----------------------------------------------------------------------
// Note: The following functions are deprecated and could be removed
// in a future release.
//-----------------------------------------------------------------------
void CXTPSplitterWnd::SetFullDrag(BOOL bFullDrag)
{
TRACE0("Member function is deprecated use EnableFullDrag() instead.\n");
EnableFullDrag((int)bFullDrag);
}
void CXTPSplitterWnd::SetSplitterStyle(DWORD dwxStyle)
{
TRACE0("CXTPSplitterWnd::SetSplitterStyle is deprecated use EnableFullDrag(), EnableDotTracker() or EnableNoSize() instead.\n");
EnableNoSize(dwxStyle & XTP_SPLIT_NOSIZE ? TRUE : FALSE);
EnableFullDrag(dwxStyle & XTP_SPLIT_NOFULLDRAG ? 0 : -1);
EnableDotTracker(dwxStyle & XTP_SPLIT_DOTTRACKER ? TRUE : FALSE);
EnableBorder(dwxStyle & XTP_SPLIT_NOBORDER ? FALSE : TRUE);
}
DWORD CXTPSplitterWnd::GetSplitterStyle()
{
TRACE0("CXTPSplitterWnd::GetSplitterStyle is deprecated.\n");
DWORD dwStyle = 0L;
if (m_nFullDrag == 0)
dwStyle |= XTP_SPLIT_NOFULLDRAG;
if (m_bDotTracker)
dwStyle |= XTP_SPLIT_DOTTRACKER;
if (m_bNoSize)
dwStyle |= XTP_SPLIT_NOSIZE;
if (!m_bShowBorder)
dwStyle |= XTP_SPLIT_NOBORDER;
return dwStyle;
}
void CXTPSplitterWnd::EnableFlatLook(BOOL bFlatSplitter)
{
TRACE0("CXTPSplitterWnd::EnableFlatLook is deprecated use SetTheme() instead.\n");
SetTheme(bFlatSplitter ? xtpControlThemeOfficeXP : xtpControlThemeOffice2000);
}
//////////////////////////////////////////////////////////////////////
// CXTPSplitterWndEx
//
IMPLEMENT_DYNAMIC(CXTPSplitterWndEx, CXTPSplitterWnd)
BEGIN_MESSAGE_MAP(CXTPSplitterWndEx, CXTPSplitterWnd)
//{{AFX_MSG_MAP(CXTPSplitterWndEx)
ON_WM_ERASEBKGND()
ON_WM_PAINT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
CXTPSplitterWndEx::CXTPSplitterWndEx()
{
m_cxBorder = 0;
m_cyBorder = 0;
m_cxSplitter = 4;
m_cySplitter = 4;
m_cxSplitterGap = 4;
m_cySplitterGap = 4;
m_cyTopBorderGap = 6;
m_bShowTopBorder = true;
}
CXTPSplitterWndEx::~CXTPSplitterWndEx()
{
}
void CXTPSplitterWndEx::OnDrawSplitter(CDC* pDC, ESplitType nType, const CRect& rectArg)
{
COLORREF clr3DFace = GetTheme()->m_crBack;
COLORREF clr3DShadow = GetTheme()->m_crInnerBorder;
if (pDC == NULL)
{
RedrawWindow(rectArg, NULL, RDW_INVALIDATE | RDW_NOCHILDREN);
return;
}
ASSERT_VALID(pDC);
CRect rect = rectArg;
switch (nType)
{
case splitBorder:
return;
case splitBox:
pDC->Draw3dRect(rect, clr3DFace, GetXtremeColor(COLOR_WINDOWFRAME));
rect.InflateRect(-CX_BORDER, -CY_BORDER);
pDC->Draw3dRect(rect, GetXtremeColor(COLOR_3DHILIGHT), clr3DShadow);
rect.InflateRect(-CX_BORDER, -CY_BORDER);
break;
}
pDC->FillSolidRect(rect, clr3DFace);
}
// Generic routine:
// for X direction: pInfo = m_pColInfo, nMax = m_nMaxCols, nSize = cx
// for Y direction: pInfo = m_pRowInfo, nMax = m_nMaxRows, nSize = cy
void CXTPSplitterWndEx::LayoutRowCol(CSplitterWnd::CRowColInfo* pInfoArray,
int nMax, int nSize, int nSizeSplitter)
{
ASSERT(pInfoArray != NULL);
ASSERT(nMax > 0);
ASSERT(nSizeSplitter > 0);
if (!pInfoArray)
return;
CSplitterWnd::CRowColInfo* pInfo = NULL;
int i;
if (nSize < 0)
nSize = 0; // if really too small, layout as zero size
// start with ideal sizes
for (i = 0, pInfo = pInfoArray; i < nMax-1; i++, pInfo++)
{
if (pInfo->nIdealSize < pInfo->nMinSize)
pInfo->nIdealSize = 0; // too small to see
pInfo->nCurSize = pInfo->nIdealSize;
}
if (!pInfo)
return;
pInfo->nCurSize = INT_MAX; // last row/column takes the rest
for (i = 0, pInfo = pInfoArray; i < nMax; i++, pInfo++)
{
ASSERT(nSize >= 0);
if (nSize == 0)
{
// no more room (set pane to be invisible)
pInfo->nCurSize = 0;
continue; // don't worry about splitters
}
else if (nSize < pInfo->nMinSize && i != 0)
{
// additional panes below the recommended minimum size
// aren't shown and the size goes to the previous pane
pInfo->nCurSize = 0;
// previous pane already has room for splitter + border
// add remaining size and remove the extra border
ASSERT(CX_BORDER * 2 == CY_BORDER * 2);
(pInfo-1)->nCurSize += nSize + CX_BORDER * 2;
nSize = 0;
}
else
{
// otherwise we can add the second pane
ASSERT(nSize > 0);
if (pInfo->nCurSize == 0)
{
// too small to see
if (i != 0)
pInfo->nCurSize = 0;
}
else if (nSize < pInfo->nCurSize)
{
// this row/col won't fit completely - make as small as possible
pInfo->nCurSize = nSize;
nSize = 0;
}
else
{
// can fit everything
nSize -= pInfo->nCurSize;
}
}
// see if we should add a splitter
ASSERT(nSize >= 0);
if (i != nMax - 1)
{
// should have a splitter
if (nSize > nSizeSplitter)
{
nSize -= nSizeSplitter; // leave room for splitter + border
ASSERT(nSize > 0);
}
else
{
// not enough room - add left over less splitter size
ASSERT(CX_BORDER * 2 == CY_BORDER * 2);
pInfo->nCurSize += nSize;
if (pInfo->nCurSize > (nSizeSplitter - CX_BORDER * 2))
pInfo->nCurSize -= (nSizeSplitter - CY_BORDER * 2);
nSize = 0;
}
}
}
ASSERT(nSize == 0); // all space should be allocated
}
// repositions client area of specified window
// assumes everything has WS_BORDER or is inset like it does
// (includes scroll bars)
void CXTPSplitterWndEx::DeferClientPos(AFX_SIZEPARENTPARAMS* lpLayout,
CWnd* pWnd, int x, int y, int cx, int cy, BOOL bScrollBar)
{
ASSERT(pWnd != NULL);
if (!pWnd)
return;
ASSERT(pWnd->m_hWnd != NULL);
if (bScrollBar)
{
// if there is enough room, draw scroll bar without border
// if there is not enough room, set the WS_BORDER bit so that
// we will at least get a proper border drawn
BOOL bNeedBorder = (cx <= CX_BORDER || cy <= CY_BORDER);
pWnd->ModifyStyle(bNeedBorder ? 0 : WS_BORDER,
bNeedBorder ? WS_BORDER : 0);
}
CRect rect(x, y, x + cx, y + cy);
// adjust for border size (even if zero client size)
// adjust for 3d border (splitter windows have implied border)
if ((pWnd->GetExStyle() & WS_EX_CLIENTEDGE) ||
pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
rect.InflateRect(CX_BORDER * 2, CY_BORDER * 2);
// first check if the new rectangle is the same as the current
CRect rectOld;
pWnd->GetWindowRect(rectOld);
pWnd->GetParent()->ScreenToClient(&rectOld);
if (rect != rectOld)
AfxRepositionWindow(lpLayout, pWnd->m_hWnd, rect);
}
void CXTPSplitterWndEx::RecalcLayout()
{
ASSERT_VALID(this);
ASSERT(m_nRows > 0 && m_nCols > 0); // must have at least one pane
CRect rectClient;
GetClientRect(rectClient);
rectClient.InflateRect(-m_cxBorder, -m_cyBorder);
rectClient.top += m_cyTopBorderGap;
CRect rectInside;
GetInsideRect(rectInside);
// layout columns (restrict to possible sizes)
LayoutRowCol(m_pColInfo, m_nCols, rectInside.Width(), m_cxSplitterGap);
LayoutRowCol(m_pRowInfo, m_nRows, rectInside.Height(), m_cySplitterGap);
// adjust the panes (and optionally scroll bars)
// give the hint for the maximum number of HWNDs
AFX_SIZEPARENTPARAMS layout;
layout.hDWP = ::BeginDeferWindowPos((m_nCols + 1) * (m_nRows + 1) + 1);
// size of scrollbars
int cx = (rectClient.right - rectInside.right);
int cy = (rectClient.bottom - rectInside.bottom);
// reposition size box
if (m_bHasHScroll && m_bHasVScroll)
{
CWnd* pScrollBar = GetDlgItem(AFX_IDW_SIZE_BOX);
ASSERT(pScrollBar != NULL);
// fix style if necessary
BOOL bSizingParent = (GetSizingParent() != NULL);
// modifyStyle returns TRUE if style changes
if (pScrollBar->ModifyStyle(SBS_SIZEGRIP | SBS_SIZEBOX,
bSizingParent ? SBS_SIZEGRIP : SBS_SIZEBOX))
pScrollBar->Invalidate();
pScrollBar->EnableWindow(bSizingParent);
// reposition the size box
DeferClientPos(&layout, pScrollBar,
rectInside.right, rectInside.bottom, cx, cy, TRUE);
}
// reposition scroll bars
if (m_bHasHScroll)
{
int cxSplitterBox = m_cxSplitter;// split box bigger
int x = rectClient.left;
int y = rectInside.bottom;
int col;
for (col = 0; col < m_nCols; col++)
{
CWnd* pScrollBar = GetDlgItem(AFX_IDW_HSCROLL_FIRST + col);
ASSERT(pScrollBar != NULL);
int cxColumn = m_pColInfo[col].nCurSize;
if (col == 0 && m_nCols < m_nMaxCols)
x += cxSplitterBox, cxColumn -= cxSplitterBox;
DeferClientPos(&layout, pScrollBar, x, y, cxColumn, cy, TRUE);
x += cxColumn + m_cxSplitterGap;
}
}
if (m_bHasVScroll)
{
int cySplitterBox = m_cySplitter;// split box bigger
int x = rectInside.right;
int y = rectClient.top;
int row;
for (row = 0; row < m_nRows; row++)
{
CWnd* pScrollBar = GetDlgItem(AFX_IDW_VSCROLL_FIRST + row);
ASSERT(pScrollBar != NULL);
int cyColumn = m_pRowInfo[row].nCurSize;
if (row == 0 && m_nRows < m_nMaxRows)
y += cySplitterBox, cyColumn -= cySplitterBox;
DeferClientPos(&layout, pScrollBar, x, y, cx, cyColumn, TRUE);
y += cyColumn + m_cySplitterGap;
}
}
//BLOCK: Reposition all the panes
{
int x = rectClient.left;
int col;
for (col = 0; col < m_nCols; col++)
{
int cxColumn = m_pColInfo[col].nCurSize;
int y = rectClient.top;
int row;
for (row = 0; row < m_nRows; row++)
{
int cyColumn = m_pRowInfo[row].nCurSize;
CWnd* pWnd = GetPane(row, col);
DeferClientPos(&layout, pWnd, x, y, cxColumn, cyColumn, FALSE);
y += cyColumn + m_cySplitterGap;
}
x += cxColumn + m_cxSplitterGap;
}
}
// move and resize all the windows at once!
if (layout.hDWP == NULL || !::EndDeferWindowPos(layout.hDWP))
TRACE0("Warning: DeferWindowPos failed - low system resources.\n");
// invalidate all the splitter bars (with NULL pDC)
DrawAllSplitBars(NULL, rectInside.right, rectInside.bottom);
}
void CXTPSplitterWndEx::GetInsideRect(CRect& rect) const
{
CSplitterWnd::GetInsideRect(rect);
rect.top += m_cyTopBorderGap;
}
void CXTPSplitterWndEx::OnPaint()
{
CSplitterWnd::OnPaint();
if (m_bShowTopBorder)
{
CClientDC dc(this);
int iSavedDC = dc.SaveDC();
CRect rectClient;
GetClientRect(rectClient);
if (m_pTheme->GetThemeStyle() == xtpControlThemeDefault)
{
CPen penShadow(PS_SOLID, 1, GetXtremeColor(COLOR_3DSHADOW));
dc.SelectObject(&penShadow);
dc.MoveTo(rectClient.left, rectClient.top);
dc.LineTo(rectClient.right, rectClient.top);
}
CPen penHilight(PS_SOLID, 1, GetXtremeColor(COLOR_3DHILIGHT));
dc.SelectObject(&penHilight);
dc.MoveTo(rectClient.left, rectClient.top + 1);
dc.LineTo(rectClient.right, rectClient.top + 1);
dc.RestoreDC(iSavedDC);
}
}
BOOL CXTPSplitterWndEx::OnEraseBkgnd(CDC* pDC)
{
CRect rectClient;
GetClientRect(rectClient);
rectClient.InflateRect(-m_cxBorder, -m_cyBorder);
rectClient.bottom = rectClient.top + m_cyTopBorderGap;
COLORREF clr3DFace = GetTheme()->m_crBack;
pDC->FillSolidRect(rectClient, clr3DFace);
return TRUE;
}