home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tricks of the Windows Gam…ming Gurus (2nd Edition)
/
Disc2.iso
/
vc98
/
mfc
/
src
/
winsplit.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1998-06-16
|
64KB
|
2,367 lines
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.
#include "stdafx.h"
#ifdef AFX_CORE3_SEG
#pragma code_seg(AFX_CORE3_SEG)
#endif
#ifdef _DEBUG
#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
};
/////////////////////////////////////////////////////////////////////////////
// CSplitterWnd
BEGIN_MESSAGE_MAP(CSplitterWnd, CWnd)
//{{AFX_MSG_MAP(CSplitterWnd)
ON_WM_SETCURSOR()
ON_WM_MOUSEMOVE()
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONDBLCLK()
ON_WM_LBUTTONUP()
ON_WM_KEYDOWN()
ON_WM_SIZE()
ON_WM_HSCROLL()
ON_WM_VSCROLL()
ON_WM_NCCREATE()
ON_WM_SYSCOMMAND()
ON_WM_CANCELMODE()
ON_MESSAGE_VOID(WM_DISPLAYCHANGE, OnDisplayChange)
ON_MESSAGE_VOID(WM_WININICHANGE, OnDisplayChange)
ON_MESSAGE_VOID(WM_SETTINGCHANGE, OnDisplayChange)
ON_WM_MOUSEWHEEL()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CSplitterWnd construction/destruction
CSplitterWnd::CSplitterWnd()
{
AFX_ZERO_INIT_OBJECT(CWnd);
// default splitter box/bar sizes (includes borders)
if (!afxData.bWin4)
{
m_cxSplitter = m_cySplitter = 4;
m_cxBorderShare = m_cyBorderShare = 1;
m_cxSplitterGap = m_cySplitterGap = 4 + 1 + 1;
ASSERT(m_cxBorder == 0 && m_cyBorder == 0);
}
else
{
m_cxSplitter = m_cySplitter = 3 + 2 + 2;
m_cxBorderShare = m_cyBorderShare = 0;
m_cxSplitterGap = m_cySplitterGap = 3 + 2 + 2;
m_cxBorder = m_cyBorder = 2;
}
#ifdef _DEBUG
if (GetSystemMetrics(SM_CXBORDER) != 1 ||
GetSystemMetrics(SM_CYBORDER) != 1)
{
TRACE0("Warning: CSplitterWnd assumes 1 pixel border.\n");
// will look ugly if borders are not 1 pixel wide and 1 pixel high
}
#endif
}
CSplitterWnd::~CSplitterWnd()
{
delete[] m_pRowInfo;
delete[] m_pColInfo;
}
BOOL CSplitterWnd::Create(CWnd* pParentWnd,
int nMaxRows, int nMaxCols, SIZE sizeMin,
CCreateContext* pContext, DWORD dwStyle, UINT nID)
{
ASSERT(pParentWnd != NULL);
ASSERT(sizeMin.cx > 0 && sizeMin.cy > 0); // minimum must be non-zero
ASSERT(pContext != NULL);
ASSERT(pContext->m_pNewViewClass != NULL);
ASSERT(dwStyle & WS_CHILD);
ASSERT(dwStyle & SPLS_DYNAMIC_SPLIT); // must have dynamic split behavior
// Dynamic splitters are limited to 2x2
ASSERT(nMaxRows >= 1 && nMaxRows <= 2);
ASSERT(nMaxCols >= 1 && nMaxCols <= 2);
ASSERT(nMaxCols > 1 || nMaxRows > 1); // 1x1 is not permitted
m_nMaxRows = nMaxRows;
m_nMaxCols = nMaxCols;
ASSERT(m_nRows == 0 && m_nCols == 0); // none yet
m_nRows = m_nCols = 1; // start off as 1x1
if (!CreateCommon(pParentWnd, sizeMin, dwStyle, nID))
return FALSE;
ASSERT(m_nRows == 1 && m_nCols == 1); // still 1x1
ASSERT(pContext->m_pNewViewClass->IsDerivedFrom(RUNTIME_CLASS(CWnd)));
m_pDynamicViewClass = pContext->m_pNewViewClass;
// save for later dynamic creations
// add the first initial pane
if (!CreateView(0, 0, m_pDynamicViewClass, sizeMin, pContext))
{
DestroyWindow(); // will clean up child windows
return FALSE;
}
m_pColInfo[0].nIdealSize = sizeMin.cx;
m_pRowInfo[0].nIdealSize = sizeMin.cy;
return TRUE;
}
// simple "wiper" splitter
BOOL CSplitterWnd::CreateStatic(CWnd* pParentWnd,
int nRows, int nCols, DWORD dwStyle, UINT nID)
{
ASSERT(pParentWnd != NULL);
ASSERT(nRows >= 1 && nRows <= 16);
ASSERT(nCols >= 1 && nCols <= 16);
ASSERT(nCols > 1 || nRows > 1); // 1x1 is not permitted
ASSERT(dwStyle & WS_CHILD);
ASSERT(!(dwStyle & SPLS_DYNAMIC_SPLIT)); // can't have dynamic split
ASSERT(m_nRows == 0 && m_nCols == 0); // none yet
m_nRows = m_nMaxRows = nRows;
m_nCols = m_nMaxCols = nCols;
// create with zero minimum pane size
if (!CreateCommon(pParentWnd, CSize(0, 0), dwStyle, nID))
return FALSE;
// all panes must be created with explicit calls to CreateView
return TRUE;
}
BOOL CSplitterWnd::CreateCommon(CWnd* pParentWnd,
SIZE sizeMin, DWORD dwStyle, UINT nID)
{
ASSERT(pParentWnd != NULL);
ASSERT(sizeMin.cx >= 0 && sizeMin.cy >= 0);
ASSERT(dwStyle & WS_CHILD);
ASSERT(nID != 0);
ASSERT(m_pColInfo == NULL && m_pRowInfo == NULL); // only do once
ASSERT(m_nMaxCols > 0 && m_nMaxRows > 0);
// the Windows scroll bar styles bits turn on the smart scrollbars
DWORD dwCreateStyle = dwStyle & ~(WS_HSCROLL|WS_VSCROLL);
if (afxData.bWin4)
dwCreateStyle &= ~WS_BORDER;
VERIFY(AfxDeferRegisterClass(AFX_WNDMDIFRAME_REG));
// create with the same wnd-class as MDI-Frame (no erase bkgnd)
if (!CreateEx(0, _afxWndMDIFrame, NULL, dwCreateStyle, 0, 0, 0, 0,
pParentWnd->m_hWnd, (HMENU)nID, NULL))
return FALSE; // create invisible
// attach the initial splitter parts
TRY
{
m_pColInfo = new CRowColInfo[m_nMaxCols];
for (int col = 0; col < m_nMaxCols; col++)
{
m_pColInfo[col].nMinSize = m_pColInfo[col].nIdealSize = sizeMin.cx;
m_pColInfo[col].nCurSize = -1; // will be set in RecalcLayout
}
m_pRowInfo = new CRowColInfo[m_nMaxRows];
for (int row = 0; row < m_nMaxRows; row++)
{
m_pRowInfo[row].nMinSize = m_pRowInfo[row].nIdealSize = sizeMin.cy;
m_pRowInfo[row].nCurSize = -1; // will be set in RecalcLayout
}
// create scroll bars by setting the style
SetScrollStyle(dwStyle);
}
CATCH_ALL(e)
{
DestroyWindow(); // will clean up child windows
// Note: DELETE_EXCEPTION(e) not required
return FALSE;
}
END_CATCH_ALL
return TRUE;
}
BOOL CSplitterWnd::OnNcCreate(LPCREATESTRUCT lpcs)
{
if (!CWnd::OnNcCreate(lpcs))
return FALSE;
// remove WS_EX_CLIENTEDGE style from parent window
// (the splitter itself will provide the 3d look)
CWnd* pParent = GetParent();
ASSERT_VALID(pParent);
pParent->ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_DRAWFRAME);
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CSplitterWnd default creation of parts
// You must create ALL panes unless DYNAMIC_SPLIT is defined!
// Usually the splitter window is invisible when creating a pane
BOOL CSplitterWnd::CreateView(int row, int col,
CRuntimeClass* pViewClass, SIZE sizeInit, CCreateContext* pContext)
{
#ifdef _DEBUG
ASSERT_VALID(this);
ASSERT(row >= 0 && row < m_nRows);
ASSERT(col >= 0 && col < m_nCols);
ASSERT(pViewClass != NULL);
ASSERT(pViewClass->IsDerivedFrom(RUNTIME_CLASS(CWnd)));
ASSERT(AfxIsValidAddress(pViewClass, sizeof(CRuntimeClass), FALSE));
if (GetDlgItem(IdFromRowCol(row, col)) != NULL)
{
TRACE2("Error: CreateView - pane already exists for row %d, col %d.\n",
row, col);
ASSERT(FALSE);
return FALSE;
}
#endif
// set the initial size for that pane
m_pColInfo[col].nIdealSize = sizeInit.cx;
m_pRowInfo[row].nIdealSize = sizeInit.cy;
BOOL bSendInitialUpdate = FALSE;
CCreateContext contextT;
if (pContext == NULL)
{
// if no context specified, generate one from the currently selected
// client if possible
CView* pOldView = (CView*)GetActivePane();
if (pOldView != NULL && pOldView->IsKindOf(RUNTIME_CLASS(CView)))
{
// set info about last pane
ASSERT(contextT.m_pCurrentFrame == NULL);
contextT.m_pLastView = pOldView;
contextT.m_pCurrentDoc = pOldView->GetDocument();
if (contextT.m_pCurrentDoc != NULL)
contextT.m_pNewDocTemplate =
contextT.m_pCurrentDoc->GetDocTemplate();
}
pContext = &contextT;
bSendInitialUpdate = TRUE;
}
CWnd* pWnd;
TRY
{
pWnd = (CWnd*)pViewClass->CreateObject();
if (pWnd == NULL)
AfxThrowMemoryException();
}
CATCH_ALL(e)
{
TRACE0("Out of memory creating a splitter pane.\n");
// Note: DELETE_EXCEPTION(e) not required
return FALSE;
}
END_CATCH_ALL
ASSERT_KINDOF(CWnd, pWnd);
ASSERT(pWnd->m_hWnd == NULL); // not yet created
DWORD dwStyle = AFX_WS_DEFAULT_VIEW;
if (afxData.bWin4)
dwStyle &= ~WS_BORDER;
// Create with the right size (wrong position)
CRect rect(CPoint(0,0), sizeInit);
if (!pWnd->Create(NULL, NULL, dwStyle,
rect, this, IdFromRowCol(row, col), pContext))
{
TRACE0("Warning: couldn't create client pane for splitter.\n");
// pWnd will be cleaned up by PostNcDestroy
return FALSE;
}
ASSERT((int)_AfxGetDlgCtrlID(pWnd->m_hWnd) == IdFromRowCol(row, col));
// send initial notification message
if (bSendInitialUpdate)
pWnd->SendMessage(WM_INITIALUPDATE);
return TRUE;
}
BOOL CSplitterWnd::CreateScrollBarCtrl(DWORD dwStyle, UINT nID)
{
ASSERT_VALID(this);
ASSERT(m_hWnd != NULL);
HWND hWnd = ::CreateWindow(_T("SCROLLBAR"), NULL,
dwStyle | WS_VISIBLE | WS_CHILD,
0, 0, 1, 1, m_hWnd, (HMENU)nID,
AfxGetInstanceHandle(), NULL);
#ifdef _DEBUG
if (hWnd == NULL)
TRACE1("Warning: Window creation failed: GetLastError returns 0x%8.8X\n",
GetLastError());
#endif
return hWnd != NULL;
}
int CSplitterWnd::IdFromRowCol(int row, int col) const
{
ASSERT_VALID(this);
ASSERT(row >= 0);
ASSERT(row < m_nRows);
ASSERT(col >= 0);
ASSERT(col < m_nCols);
return AFX_IDW_PANE_FIRST + row * 16 + col;
}
/////////////////////////////////////////////////////////////////////////////
// CSplitterWnd attributes
CWnd* CSplitterWnd::GetPane(int row, int col) const
{
ASSERT_VALID(this);
CWnd* pView = GetDlgItem(IdFromRowCol(row, col));
ASSERT(pView != NULL); // panes can be a CWnd, but are usually CViews
return pView;
}
BOOL CSplitterWnd::IsChildPane(CWnd* pWnd, int* pRow, int* pCol)
{
ASSERT_VALID(this);
ASSERT_VALID(pWnd);
UINT nID = _AfxGetDlgCtrlID(pWnd->m_hWnd);
if (IsChild(pWnd) && nID >= AFX_IDW_PANE_FIRST && nID <= AFX_IDW_PANE_LAST)
{
if (pRow != NULL)
*pRow = (nID - AFX_IDW_PANE_FIRST) / 16;
if (pCol != NULL)
*pCol = (nID - AFX_IDW_PANE_FIRST) % 16;
ASSERT(pRow == NULL || *pRow < m_nRows);
ASSERT(pCol == NULL || *pCol < m_nCols);
return TRUE;
}
else
{
if (pRow != NULL)
*pRow = -1;
if (pCol != NULL)
*pCol = -1;
return FALSE;
}
}
/////////////////////////////////////////////////////////////////////////////
// CSplitterWnd information access
// The get routines return the current size
// The set routines set the ideal size
// RecalcLayout must be called to update current size
void CSplitterWnd::GetRowInfo(int row, int& cyCur, int& cyMin) const
{
ASSERT_VALID(this);
ASSERT(row >= 0 && row < m_nMaxRows);
cyCur = m_pRowInfo[row].nCurSize;
cyMin = m_pRowInfo[row].nMinSize;
}
void CSplitterWnd::SetRowInfo(int row, int cyIdeal, int cyMin)
{
ASSERT_VALID(this);
ASSERT(row >= 0 && row < m_nMaxRows);
ASSERT(cyIdeal >= 0);
ASSERT(cyMin >= 0);
m_pRowInfo[row].nIdealSize = cyIdeal;
m_pRowInfo[row].nMinSize = cyMin;
}
void CSplitterWnd::GetColumnInfo(int col, int& cxCur, int& cxMin) const
{
ASSERT_VALID(this);
ASSERT(col >= 0 && col < m_nMaxCols);
cxCur = m_pColInfo[col].nCurSize;
cxMin = m_pColInfo[col].nMinSize;
}
void CSplitterWnd::SetColumnInfo(int col, int cxIdeal, int cxMin)
{
ASSERT_VALID(this);
ASSERT(col >= 0 && col < m_nMaxCols);
ASSERT(cxIdeal >= 0);
ASSERT(cxMin >= 0);
m_pColInfo[col].nIdealSize = cxIdeal;
m_pColInfo[col].nMinSize = cxMin;
}
DWORD CSplitterWnd::GetScrollStyle() const
{
DWORD dwStyle = 0;
if (m_bHasHScroll)
dwStyle |= WS_HSCROLL;
if (m_bHasVScroll)
dwStyle |= WS_VSCROLL;
return dwStyle;
}
void CSplitterWnd::SetScrollStyle(DWORD dwStyle)
{
// optimize for scroll info already set correctly
dwStyle &= (WS_HSCROLL|WS_VSCROLL);
if (GetScrollStyle() == dwStyle)
return;
// update to new state
m_bHasHScroll = (dwStyle & WS_HSCROLL) != 0;
m_bHasVScroll = (dwStyle & WS_VSCROLL) != 0;
CWnd* pScrollBar;
// show/hide all the shared horz scroll bars
for (int col = 0; col < m_nCols; col++)
{
pScrollBar = GetDlgItem(AFX_IDW_HSCROLL_FIRST + col);
if (pScrollBar == NULL)
{
// create the scroll bar when necessary
if (!CreateScrollBarCtrl(SBS_HORZ, AFX_IDW_HSCROLL_FIRST + col))
AfxThrowResourceException();
pScrollBar = GetDlgItem(AFX_IDW_HSCROLL_FIRST + col);
}
pScrollBar->ShowWindow(m_bHasHScroll ? SW_SHOW : SW_HIDE);
}
// show/hide all the shared vert scroll bars
for (int row = 0; row < m_nRows; row++)
{
pScrollBar = GetDlgItem(AFX_IDW_VSCROLL_FIRST + row);
if (pScrollBar == NULL)
{
// create the scroll bar when necessary
if (!CreateScrollBarCtrl(SBS_VERT, AFX_IDW_VSCROLL_FIRST + row))
AfxThrowResourceException();
pScrollBar = GetDlgItem(AFX_IDW_VSCROLL_FIRST + row);
}
pScrollBar->ShowWindow(m_bHasVScroll ? SW_SHOW : SW_HIDE);
}
// show/destroy size box if necessary
if (m_bHasVScroll && m_bHasHScroll)
{
pScrollBar = GetDlgItem(AFX_IDW_SIZE_BOX);
if (pScrollBar == NULL)
{
// create size box when necessary
if (!CreateScrollBarCtrl(SBS_SIZEBOX|WS_DISABLED, AFX_IDW_SIZE_BOX))
AfxThrowResourceException();
pScrollBar = GetDlgItem(AFX_IDW_SIZE_BOX);
}
pScrollBar->ShowWindow(SW_SHOW);
}
else
{
// the size box can be destroyed instead of hidden
pScrollBar = GetDlgItem(AFX_IDW_SIZE_BOX);
if (pScrollBar != NULL)
pScrollBar->DestroyWindow();
}
// Note: call RecalcLayout for the new layout to take effect
}
/////////////////////////////////////////////////////////////////////////////
// CSplitterWnd client operations/overridables
void CSplitterWnd::DeleteView(int row, int col)
{
ASSERT_VALID(this);
// if active child is being deleted - activate next
CWnd* pPane = GetPane(row, col);
ASSERT_KINDOF(CView, pPane);
if (GetActivePane() == pPane)
ActivateNext(FALSE);
// default implementation assumes view will auto delete in PostNcDestroy
pPane->DestroyWindow();
}
void CSplitterWnd::OnDrawSplitter(CDC* pDC, ESplitType nType,
const CRect& rectArg)
{
// if pDC == NULL, then just invalidate
if (pDC == NULL)
{
RedrawWindow(rectArg, NULL, RDW_INVALIDATE|RDW_NOCHILDREN);
return;
}
ASSERT_VALID(pDC);
// otherwise, actually draw
CRect rect = rectArg;
switch (nType)
{
case splitBorder:
ASSERT(afxData.bWin4);
pDC->Draw3dRect(rect, afxData.clrBtnShadow, afxData.clrBtnHilite);
rect.InflateRect(-CX_BORDER, -CY_BORDER);
pDC->Draw3dRect(rect, afxData.clrWindowFrame, afxData.clrBtnFace);
return;
case splitIntersection:
ASSERT(!afxData.bWin4);
break;
case splitBox:
if (afxData.bWin4)
{
pDC->Draw3dRect(rect, afxData.clrBtnFace, afxData.clrWindowFrame);
rect.InflateRect(-CX_BORDER, -CY_BORDER);
pDC->Draw3dRect(rect, afxData.clrBtnHilite, afxData.clrBtnShadow);
rect.InflateRect(-CX_BORDER, -CY_BORDER);
break;
}
// fall through...
case splitBar:
if (!afxData.bWin4)
{
pDC->Draw3dRect(rect, afxData.clrBtnHilite, afxData.clrBtnShadow);
rect.InflateRect(-CX_BORDER, -CY_BORDER);
}
break;
default:
ASSERT(FALSE); // unknown splitter type
}
// fill the middle
COLORREF clr = afxData.clrBtnFace;
pDC->FillSolidRect(rect, clr);
}
/////////////////////////////////////////////////////////////////////////////
// Dynamic row/col split etc
AFX_STATIC int AFXAPI _AfxCanSplitRowCol(CSplitterWnd::CRowColInfo* pInfoBefore,
int nBeforeSize, int nSizeSplitter)
// request to split Before row at point nBeforeSize
// returns size of new pane (nBeforeSize will be new size of Before pane)
// return -1 if not big enough
{
ASSERT(pInfoBefore->nCurSize > 0);
ASSERT(pInfoBefore->nMinSize > 0);
ASSERT(nBeforeSize <= pInfoBefore->nCurSize);
// space gets take from before pane (weird UI for > 2 splits)
if (nBeforeSize < pInfoBefore->nMinSize)
{
TRACE0("Warning: split too small to fit in a new pane.\n");
return -1;
}
int nNewSize = pInfoBefore->nCurSize - nBeforeSize - nSizeSplitter;
if (nBeforeSize < pInfoBefore->nMinSize)
{
TRACE0("Warning: split too small to shrink old pane.\n");
return -1;
}
if (nNewSize < (pInfoBefore+1)->nMinSize)
{
TRACE0("Warning: split too small to create new pane.\n");
return -1;
}
return nNewSize;
}
BOOL CSplitterWnd::SplitRow(int cyBefore)
{
ASSERT_VALID(this);
ASSERT(GetStyle() & SPLS_DYNAMIC_SPLIT);
ASSERT(m_pDynamicViewClass != NULL);
ASSERT(m_nRows < m_nMaxRows);
cyBefore -= m_cyBorder;
int rowNew = m_nRows;
int cyNew = _AfxCanSplitRowCol(&m_pRowInfo[rowNew-1], cyBefore, m_cySplitter);
if (cyNew == -1)
return FALSE; // too small to split
// create the scroll bar first (so new views can see that it is there)
if (m_bHasVScroll &&
!CreateScrollBarCtrl(SBS_VERT, AFX_IDW_VSCROLL_FIRST + rowNew))
{
TRACE0("Warning: SplitRow failed to create scroll bar.\n");
return FALSE;
}
m_nRows++; // bump count during view creation
// create new views to fill the new row (RecalcLayout will position)
for (int col = 0; col < m_nCols; col++)
{
CSize size(m_pColInfo[col].nCurSize, cyNew);
if (!CreateView(rowNew, col, m_pDynamicViewClass, size, NULL))
{
TRACE0("Warning: SplitRow failed to create new row.\n");
// delete anything we partially created 'col' = # columns created
while (col > 0)
DeleteView(rowNew, --col);
if (m_bHasVScroll)
GetDlgItem(AFX_IDW_VSCROLL_FIRST + rowNew)->DestroyWindow();
m_nRows--; // it didn't work out
return FALSE;
}
}
// new parts created - resize and re-layout
m_pRowInfo[rowNew-1].nIdealSize = cyBefore;
m_pRowInfo[rowNew].nIdealSize = cyNew;
ASSERT(m_nRows == rowNew+1);
RecalcLayout();
return TRUE;
}
BOOL CSplitterWnd::SplitColumn(int cxBefore)
{
ASSERT_VALID(this);
ASSERT(GetStyle() & SPLS_DYNAMIC_SPLIT);
ASSERT(m_pDynamicViewClass != NULL);
ASSERT(m_nCols < m_nMaxCols);
cxBefore -= m_cxBorder;
int colNew = m_nCols;
int cxNew = _AfxCanSplitRowCol(&m_pColInfo[colNew-1], cxBefore, m_cxSplitter);
if (cxNew == -1)
return FALSE; // too small to split
// create the scroll bar first (so new views can see that it is there)
if (m_bHasHScroll &&
!CreateScrollBarCtrl(SBS_HORZ, AFX_IDW_HSCROLL_FIRST + colNew))
{
TRACE0("Warning: SplitRow failed to create scroll bar.\n");
return FALSE;
}
m_nCols++; // bump count during view creation
// create new views to fill the new column (RecalcLayout will position)
for (int row = 0; row < m_nRows; row++)
{
CSize size(cxNew, m_pRowInfo[row].nCurSize);
if (!CreateView(row, colNew, m_pDynamicViewClass, size, NULL))
{
TRACE0("Warning: SplitColumn failed to create new column.\n");
// delete anything we partially created 'col' = # columns created
while (row > 0)
DeleteView(--row, colNew);
if (m_bHasHScroll)
GetDlgItem(AFX_IDW_HSCROLL_FIRST + colNew)->DestroyWindow();
m_nCols--; // it didn't work out
return FALSE;
}
}
// new parts created - resize and re-layout
m_pColInfo[colNew-1].nIdealSize = cxBefore;
m_pColInfo[colNew].nIdealSize = cxNew;
ASSERT(m_nCols == colNew+1);
RecalcLayout();
return TRUE;
}
void CSplitterWnd::DeleteRow(int rowDelete)
{
ASSERT_VALID(this);
ASSERT(GetStyle() & SPLS_DYNAMIC_SPLIT);
ASSERT(m_nRows > 1);
ASSERT(rowDelete < m_nRows);
int rowActive, colActive;
if (GetActivePane(&rowActive, &colActive) != NULL && rowActive == rowDelete)
{
if (++rowActive >= m_nRows)
rowActive = 0;
SetActivePane(rowActive, colActive);
}
CWnd* pScrollDel = m_bHasVScroll ?
GetDlgItem(AFX_IDW_VSCROLL_FIRST+rowDelete) : NULL;
for (int col = 0; col < m_nCols; col++)
{
DeleteView(rowDelete, col);
for (int row = rowDelete+1; row < m_nRows; row++)
{
CWnd* pPane = GetPane(row, col);
ASSERT(pPane != NULL);
pPane->SetDlgCtrlID(IdFromRowCol(row-1, col));
if (m_bHasVScroll && col == m_nCols-1)
{
CWnd* pScroll = GetDlgItem(AFX_IDW_VSCROLL_FIRST+row);
if (pScroll != NULL)
pScroll->SetDlgCtrlID(AFX_IDW_VSCROLL_FIRST+row-1);
}
}
}
m_nRows--;
if (pScrollDel != NULL)
pScrollDel->DestroyWindow();
RecalcLayout(); // re-assign the space
}
void CSplitterWnd::DeleteColumn(int colDelete)
{
ASSERT_VALID(this);
ASSERT(GetStyle() & SPLS_DYNAMIC_SPLIT);
ASSERT(m_nCols > 1);
ASSERT(colDelete < m_nCols);
int rowActive, colActive;
if (GetActivePane(&rowActive, &colActive) != NULL && colActive == colDelete)
{
if (++colActive >= m_nCols)
colActive = 0;
SetActivePane(rowActive, colActive);
}
CWnd* pScrollDel = m_bHasHScroll ?
GetDlgItem(AFX_IDW_HSCROLL_FIRST+colDelete) : NULL;
for (int row = 0; row < m_nRows; row++)
{
DeleteView(row, colDelete);
for (int col = colDelete+1; col < m_nCols; col++)
{
CWnd* pPane = GetPane(row, col);
ASSERT(pPane != NULL);
pPane->SetDlgCtrlID(IdFromRowCol(row, col-1));
if (m_bHasHScroll && row == m_nRows-1)
{
CWnd* pScroll = GetDlgItem(AFX_IDW_HSCROLL_FIRST+col);
if (pScroll != NULL)
pScroll->SetDlgCtrlID(AFX_IDW_HSCROLL_FIRST+col-1);
}
}
}
m_nCols--;
if (pScrollDel != NULL)
pScrollDel->DestroyWindow();
RecalcLayout(); // re-assign the space
}
/////////////////////////////////////////////////////////////////////////////
// CSplitterWnd tracking support
// like GetClientRect but inset by shared scrollbars
void CSplitterWnd::GetInsideRect(CRect& rect) const
{
ASSERT_VALID(this);
GetClientRect(rect);
ASSERT(rect.left == 0 && rect.top == 0);
// subtract space for 3d borders
rect.InflateRect(-m_cxBorder, -m_cyBorder);
// subtract scrollbar clearance
if (m_bHasVScroll)
rect.right -= afxData.cxVScroll - CX_BORDER;
if (m_bHasHScroll)
rect.bottom -= afxData.cyHScroll - CY_BORDER;
}
void CSplitterWnd::StartTracking(int ht)
{
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);
}
// allow active view to preserve focus before taking it away
CView* pView = (CView*)GetActivePane();
if (pView != NULL && pView->IsKindOf(RUNTIME_CLASS(CView)))
{
ASSERT_VALID(pView);
CFrameWnd* pFrameWnd = GetParentFrame();
ASSERT_VALID(pFrameWnd);
pView->OnActivateFrame(WA_INACTIVE, pFrameWnd);
}
// steal focus and capture
SetCapture();
SetFocus();
// make sure no updates are pending
RedrawWindow(NULL, NULL, RDW_ALLCHILDREN | RDW_UPDATENOW);
// set tracking state and appropriate cursor
m_bTracking = TRUE;
OnInvertTracker(m_rectTracker);
if (m_bTracking2)
OnInvertTracker(m_rectTracker2);
m_htTrack = ht;
SetSplitCursor(ht);
}
void CSplitterWnd::TrackRowSize(int y, int row)
{
ASSERT_VALID(this);
ASSERT(m_nRows > 1);
CPoint pt(0, y);
ClientToScreen(&pt);
GetPane(row, 0)->ScreenToClient(&pt);
m_pRowInfo[row].nIdealSize = pt.y; // new size
if (pt.y < m_pRowInfo[row].nMinSize)
{
// resized too small
m_pRowInfo[row].nIdealSize = 0; // make it go away
if (GetStyle() & SPLS_DYNAMIC_SPLIT)
DeleteRow(row);
}
else if (m_pRowInfo[row].nCurSize + m_pRowInfo[row+1].nCurSize
< pt.y + m_pRowInfo[row+1].nMinSize)
{
// not enough room for other pane
if (GetStyle() & SPLS_DYNAMIC_SPLIT)
DeleteRow(row + 1);
}
}
void CSplitterWnd::TrackColumnSize(int x, int col)
{
ASSERT_VALID(this);
ASSERT(m_nCols > 1);
CPoint pt(x, 0);
ClientToScreen(&pt);
GetPane(0, col)->ScreenToClient(&pt);
m_pColInfo[col].nIdealSize = pt.x; // new size
if (pt.x < m_pColInfo[col].nMinSize)
{
// resized too small
m_pColInfo[col].nIdealSize = 0; // make it go away
if (GetStyle() & SPLS_DYNAMIC_SPLIT)
DeleteColumn(col);
}
else if (m_pColInfo[col].nCurSize + m_pColInfo[col+1].nCurSize
< pt.x + m_pColInfo[col+1].nMinSize)
{
// not enough room for other pane
if (GetStyle() & SPLS_DYNAMIC_SPLIT)
DeleteColumn(col + 1);
}
}
void CSplitterWnd::StopTracking(BOOL bAccept)
{
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
pOldActiveView->SetFocus(); // make sure focus is restored
}
}
}
void CSplitterWnd::GetHitRect(int ht, CRect& rectHit)
{
ASSERT_VALID(this);
CRect rectClient;
GetClientRect(&rectClient);
rectClient.InflateRect(-m_cxBorder, -m_cyBorder);
int cx = rectClient.Width();
int cy = rectClient.Height();
int x = rectClient.top;
int y = rectClient.left;
// hit rectangle does not include border
// m_rectLimit will be limited to valid tracking rect
// m_ptTrackOffset will be set to appropriate tracking offset
m_ptTrackOffset.x = 0;
m_ptTrackOffset.y = 0;
if (ht == vSplitterBox)
{
cy = m_cySplitter - (2*m_cyBorder - afxData.bWin4);
m_ptTrackOffset.y = -(cy / 2);
ASSERT(m_pRowInfo[0].nCurSize > 0);
m_rectLimit.bottom -= cy;
}
else if (ht == hSplitterBox)
{
cx = m_cxSplitter - (2*m_cxBorder - afxData.bWin4);
m_ptTrackOffset.x = -(cx / 2);
ASSERT(m_pColInfo[0].nCurSize > 0);
m_rectLimit.right -= cx;
}
else if (ht >= vSplitterBar1 && ht <= vSplitterBar15)
{
cy = m_cySplitter - (2*m_cyBorder - afxData.bWin4);
m_ptTrackOffset.y = -(cy / 2);
for (int row = 0; row < ht - vSplitterBar1; row++)
y += m_pRowInfo[row].nCurSize + m_cySplitterGap;
m_rectLimit.top = y;
y += m_pRowInfo[row].nCurSize + m_cyBorderShare + afxData.bWin4;
m_rectLimit.bottom -= cy;
}
else if (ht >= hSplitterBar1 && ht <= hSplitterBar15)
{
cx = m_cxSplitter - (2*m_cxBorder - afxData.bWin4);
m_ptTrackOffset.x = -(cx / 2);
for (int col = 0; col < ht - hSplitterBar1; col++)
x += m_pColInfo[col].nCurSize + m_cxSplitterGap;
m_rectLimit.left = x;
x += m_pColInfo[col].nCurSize + m_cxBorderShare + afxData.bWin4;
m_rectLimit.right -= cx;
}
else
{
TRACE1("Error: GetHitRect(%d): Not Found!\n", ht);
ASSERT(FALSE);
}
rectHit.right = (rectHit.left = x) + cx;
rectHit.bottom = (rectHit.top = y) + cy;
}
int CSplitterWnd::HitTest(CPoint pt) const
{
ASSERT_VALID(this);
CRect rectClient;
GetClientRect(&rectClient);
rectClient.InflateRect(-m_cxBorder, -m_cyBorder);
CRect rectInside;
GetInsideRect(rectInside);
if (m_bHasVScroll && m_nRows < m_nMaxRows &&
CRect(rectInside.right, rectClient.top, rectClient.right,
rectClient.top + m_cySplitter - afxData.bWin4).PtInRect(pt))
{
return vSplitterBox;
}
if (m_bHasHScroll && m_nCols < m_nMaxCols &&
CRect(rectClient.left, rectInside.bottom,
rectClient.left + m_cxSplitter - afxData.bWin4,
rectClient.bottom).PtInRect(pt))
{
return hSplitterBox;
}
// for hit detect, include the border of splitters
CRect rect;
rect = rectClient;
for (int col = 0; col < m_nCols - 1; col++)
{
rect.left += m_pColInfo[col].nCurSize;
rect.right = rect.left + m_cxSplitterGap;
if (rect.PtInRect(pt))
break;
rect.left = rect.right;
}
rect = rectClient;
for (int row = 0; row < m_nRows - 1; row++)
{
rect.top += m_pRowInfo[row].nCurSize;
rect.bottom = rect.top + m_cySplitterGap;
if (rect.PtInRect(pt))
break;
rect.top = rect.bottom;
}
// row and col set for hit splitter (if not hit will be past end)
if (col != m_nCols - 1)
{
if (row != m_nRows - 1)
return splitterIntersection1 + row * 15 + col;
return hSplitterBar1 + col;
}
if (row != m_nRows - 1)
return vSplitterBar1 + row;
return noHit;
}
/////////////////////////////////////////////////////////////////////////////
// CSplitterWnd tracking visuals
void CSplitterWnd::OnInvertTracker(const CRect& rect)
{
ASSERT_VALID(this);
ASSERT(!rect.IsRectEmpty());
ASSERT((GetStyle() & WS_CLIPCHILDREN) == 0);
// pat-blt without clip children on
CDC* pDC = GetDC();
// invert the brush pattern (looks just like frame window sizing)
CBrush* pBrush = CDC::GetHalftoneBrush();
HBRUSH hOldBrush = NULL;
if (pBrush != NULL)
hOldBrush = (HBRUSH)SelectObject(pDC->m_hDC, pBrush->m_hObject);
pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATINVERT);
if (hOldBrush != NULL)
SelectObject(pDC->m_hDC, hOldBrush);
ReleaseDC(pDC);
}
/////////////////////////////////////////////////////////////////////////////
// CSplitterWnd commands
// Keyboard interface
BOOL CSplitterWnd::DoKeyboardSplit()
{
ASSERT_VALID(this);
int ht;
if (m_nRows > 1 && m_nCols > 1)
ht = splitterIntersection1; // split existing row+col
else if (m_nRows > 1)
ht = vSplitterBar1; // split existing row
else if (m_nCols > 1)
ht = hSplitterBar1; // split existing col
else if (m_nMaxRows > 1 && m_nMaxCols > 1)
ht = bothSplitterBox; // we can split both
else if (m_nMaxRows > 1)
ht = vSplitterBox; // we can split rows
else if (m_nMaxCols > 1)
ht = hSplitterBox; // we can split columns
else
return FALSE; // can't split
// start tracking
StartTracking(ht);
CRect rect;
rect.left = m_rectTracker.Width() / 2;
rect.top = m_rectTracker.Height() / 2;
if (m_ptTrackOffset.y != 0)
rect.top = m_rectTracker.top;
if (m_ptTrackOffset.x != 0)
rect.left = m_bTracking2 ? m_rectTracker2.left :m_rectTracker.left;
rect.OffsetRect(-m_ptTrackOffset.x, -m_ptTrackOffset.y);
ClientToScreen(&rect);
SetCursorPos(rect.left, rect.top);
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// Main drawing and layout
void CSplitterWnd::OnDisplayChange()
{
if (!IsIconic() && IsWindowVisible())
RecalcLayout();
}
void CSplitterWnd::OnSize(UINT nType, int cx, int cy)
{
if (nType != SIZE_MINIMIZED && cx > 0 && cy > 0)
RecalcLayout();
CWnd::OnSize(nType, cx, cy);
}
// Generic routine:
// for X direction: pInfo = m_pColInfo, nMax = m_nMaxCols, nSize = cx
// for Y direction: pInfo = m_pRowInfo, nMax = m_nMaxRows, nSize = cy
AFX_STATIC void AFXAPI _AfxLayoutRowCol(CSplitterWnd::CRowColInfo* pInfoArray,
int nMax, int nSize, int nSizeSplitter)
{
ASSERT(pInfoArray != NULL);
ASSERT(nMax > 0);
ASSERT(nSizeSplitter > 0);
CSplitterWnd::CRowColInfo* pInfo;
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;
}
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(afxData.cxBorder2 == afxData.cyBorder2);
(pInfo-1)->nCurSize += nSize + afxData.cxBorder2;
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(afxData.cxBorder2 == afxData.cyBorder2);
pInfo->nCurSize += nSize;
if (pInfo->nCurSize > (nSizeSplitter - afxData.cxBorder2))
pInfo->nCurSize -= (nSizeSplitter - afxData.cyBorder2);
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)
AFX_STATIC void AFXAPI _AfxDeferClientPos(AFX_SIZEPARENTPARAMS* lpLayout,
CWnd* pWnd, int x, int y, int cx, int cy, BOOL bScrollBar)
{
ASSERT(pWnd != NULL);
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)
if (!afxData.bWin4)
{
if (bScrollBar)
rect.InflateRect(CX_BORDER, CY_BORDER);
else
pWnd->CalcWindowRect(&rect);
}
// adjust for 3d border (splitter windows have implied border)
if ((pWnd->GetExStyle() & WS_EX_CLIENTEDGE) ||
pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
rect.InflateRect(afxData.cxBorder2, afxData.cyBorder2);
// 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);
}
CWnd* CSplitterWnd::GetSizingParent()
{
ASSERT_VALID(this);
if (!afxData.bWin4)
return NULL;
// size box is in lower right corner of this window
CRect rectClient;
GetClientRect(rectClient);
// find sizeable parent window
CWnd* pParent = this;
if (!(pParent->GetStyle() & WS_THICKFRAME))
pParent = GetParent();
// only allow if not maximized and has thick frame
ASSERT_VALID(pParent);
if ((pParent->GetStyle() & (WS_THICKFRAME|WS_MAXIMIZE)) == WS_THICKFRAME)
{
// convert client area of frame window relative to splitter
CRect rect;
pParent->GetClientRect(rect);
pParent->ClientToScreen(rect);
ScreenToClient(rect);
// must match exactly to get the size box
if (rectClient.BottomRight() == rect.BottomRight())
return pParent;
}
return NULL; // no sizeable parent found
}
void CSplitterWnd::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);
CRect rectInside;
GetInsideRect(rectInside);
// layout columns (restrict to possible sizes)
_AfxLayoutRowCol(m_pColInfo, m_nCols, rectInside.Width(), m_cxSplitterGap);
_AfxLayoutRowCol(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) - afxData.bNotWin4;
int cy = (rectClient.bottom - rectInside.bottom) - afxData.bNotWin4;
// 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
_AfxDeferClientPos(&layout, pScrollBar,
rectInside.right + afxData.bNotWin4,
rectInside.bottom + afxData.bNotWin4, cx, cy, TRUE);
}
// reposition scroll bars
if (m_bHasHScroll)
{
int cxSplitterBox = m_cxSplitter + afxData.bNotWin4;// split box bigger
int x = rectClient.left;
int y = rectInside.bottom + afxData.bNotWin4;
for (int col = 0; col < m_nCols; col++)
{
CWnd* pScrollBar = GetDlgItem(AFX_IDW_HSCROLL_FIRST + col);
ASSERT(pScrollBar != NULL);
int cx = m_pColInfo[col].nCurSize;
if (col == 0 && m_nCols < m_nMaxCols)
x += cxSplitterBox, cx -= cxSplitterBox;
_AfxDeferClientPos(&layout, pScrollBar, x, y, cx, cy, TRUE);
x += cx + m_cxSplitterGap;
}
}
if (m_bHasVScroll)
{
int cySplitterBox = m_cySplitter + afxData.bNotWin4;// split box bigger
int x = rectInside.right + afxData.bNotWin4;
int y = rectClient.top;
for (int row = 0; row < m_nRows; row++)
{
CWnd* pScrollBar = GetDlgItem(AFX_IDW_VSCROLL_FIRST + row);
ASSERT(pScrollBar != NULL);
int cy = m_pRowInfo[row].nCurSize;
if (row == 0 && m_nRows < m_nMaxRows)
y += cySplitterBox, cy -= cySplitterBox;
_AfxDeferClientPos(&layout, pScrollBar, x, y, cx, cy, TRUE);
y += cy + m_cySplitterGap;
}
}
//BLOCK: Reposition all the panes
{
int x = rectClient.left;
for (int col = 0; col < m_nCols; col++)
{
int cx = m_pColInfo[col].nCurSize;
int y = rectClient.top;
for (int row = 0; row < m_nRows; row++)
{
int cy = m_pRowInfo[row].nCurSize;
CWnd* pWnd = GetPane(row, col);
_AfxDeferClientPos(&layout, pWnd, x, y, cx, cy, FALSE);
y += cy + m_cySplitterGap;
}
x += cx + 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 CSplitterWnd::DrawAllSplitBars(CDC* pDC, int cxInside, int cyInside)
{
ASSERT_VALID(this);
// draw column split bars
CRect rect;
GetClientRect(rect);
rect.left += m_cxBorder;
for (int col = 0; col < m_nCols - 1; col++)
{
rect.left += m_pColInfo[col].nCurSize + m_cxBorderShare;
rect.right = rect.left + m_cxSplitter;
if (rect.left > cxInside)
break; // stop if not fully visible
OnDrawSplitter(pDC, splitBar, rect);
rect.left = rect.right + m_cxBorderShare;
}
// draw row split bars
GetClientRect(rect);
rect.top += m_cyBorder;
for (int row = 0; row < m_nRows - 1; row++)
{
rect.top += m_pRowInfo[row].nCurSize + m_cyBorderShare;
rect.bottom = rect.top + m_cySplitter;
if (rect.top > cyInside)
break; // stop if not fully visible
OnDrawSplitter(pDC, splitBar, rect);
rect.top = rect.bottom + m_cyBorderShare;
}
// draw pane borders
if (afxData.bWin4)
{
GetClientRect(rect);
int x = rect.left;
for (col = 0; col < m_nCols; col++)
{
int cx = m_pColInfo[col].nCurSize + 2*m_cxBorder;
if (col == m_nCols-1 && m_bHasVScroll)
cx += afxData.cxVScroll - CX_BORDER;
int y = rect.top;
for (int row = 0; row < m_nRows; row++)
{
int cy = m_pRowInfo[row].nCurSize + 2*m_cyBorder;
if (row == m_nRows-1 && m_bHasHScroll)
cy += afxData.cyHScroll - CX_BORDER;
OnDrawSplitter(pDC, splitBorder, CRect(x, y, x+cx, y+cy));
y += cy + m_cySplitterGap - 2*m_cyBorder;
}
x += cx + m_cxSplitterGap - 2*m_cxBorder;
}
}
}
void CSplitterWnd::OnPaint()
{
ASSERT_VALID(this);
CPaintDC dc(this);
CRect rectClient;
GetClientRect(&rectClient);
rectClient.InflateRect(-m_cxBorder, -m_cyBorder);
CRect rectInside;
GetInsideRect(rectInside);
// draw the splitter boxes
if (m_bHasVScroll && m_nRows < m_nMaxRows)
{
OnDrawSplitter(&dc, splitBox,
CRect(rectInside.right + afxData.bNotWin4, rectClient.top,
rectClient.right, rectClient.top + m_cySplitter));
}
if (m_bHasHScroll && m_nCols < m_nMaxCols)
{
OnDrawSplitter(&dc, splitBox,
CRect(rectClient.left, rectInside.bottom + afxData.bNotWin4,
rectClient.left + m_cxSplitter, rectClient.bottom));
}
// extend split bars to window border (past margins)
DrawAllSplitBars(&dc, rectInside.right, rectInside.bottom);
if (!afxData.bWin4)
{
// draw splitter intersections (inside only)
GetInsideRect(rectInside);
dc.IntersectClipRect(rectInside);
CRect rect;
rect.top = rectInside.top;
for (int row = 0; row < m_nRows - 1; row++)
{
rect.top += m_pRowInfo[row].nCurSize + m_cyBorderShare;
rect.bottom = rect.top + m_cySplitter;
rect.left = rectInside.left;
for (int col = 0; col < m_nCols - 1; col++)
{
rect.left += m_pColInfo[col].nCurSize + m_cxBorderShare;
rect.right = rect.left + m_cxSplitter;
OnDrawSplitter(&dc, splitIntersection, rect);
rect.left = rect.right + m_cxBorderShare;
}
rect.top = rect.bottom + m_cxBorderShare;
}
}
}
BOOL CSplitterWnd::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (nHitTest == HTCLIENT && pWnd == this && !m_bTracking)
return TRUE; // we will handle it in the mouse move
return CWnd::OnSetCursor(pWnd, nHitTest, message);
}
// cache of last needed cursor
AFX_STATIC_DATA HCURSOR _afx_hcurLast = NULL;
AFX_STATIC_DATA HCURSOR _afx_hcurDestroy = NULL;
AFX_STATIC_DATA UINT _afx_idcPrimaryLast = 0; // store the primary IDC
void CSplitterWnd::SetSplitCursor(int ht)
{
UINT idcPrimary; // app supplied cursor
LPCTSTR idcSecondary; // system supplied cursor (MAKEINTRESOURCE)
AfxLockGlobals(CRIT_SPLITTERWND);
if (ht == vSplitterBox ||
ht >= vSplitterBar1 && ht <= vSplitterBar15)
{
idcPrimary = AFX_IDC_VSPLITBAR;
idcSecondary = IDC_SIZENS;
}
else if (ht == hSplitterBox ||
ht >= hSplitterBar1 && ht <= hSplitterBar15)
{
idcPrimary = AFX_IDC_HSPLITBAR;
idcSecondary = IDC_SIZEWE;
}
else if (ht == bothSplitterBox ||
(ht >= splitterIntersection1 && ht <= splitterIntersection225))
{
idcPrimary = AFX_IDC_SMALLARROWS;
idcSecondary = IDC_SIZEALL;
}
else
{
SetCursor(afxData.hcurArrow);
idcPrimary = 0; // don't use it
idcSecondary = 0; // don't use it
}
if (idcPrimary != 0)
{
HCURSOR hcurToDestroy = NULL;
if (idcPrimary != _afx_idcPrimaryLast)
{
HINSTANCE hInst = AfxFindResourceHandle(
MAKEINTRESOURCE(idcPrimary), RT_GROUP_CURSOR);
// load in another cursor
hcurToDestroy = _afx_hcurDestroy;
// Note: If this LoadCursor call fails, it is likely that
// _AFX_NO_SPLITTER_RESOURCES is defined in your .RC file.
// To correct the situation, remove the following line from your
// resource script:
// #define _AFX_NO_SPLITTER_RESOURCES
// This should be done using the Resource.Set Includes... command.
if ((_afx_hcurDestroy = _afx_hcurLast =
::LoadCursor(hInst, MAKEINTRESOURCE(idcPrimary))) == NULL)
{
// will not look as good
TRACE0("Warning: Could not find splitter cursor - using system provided alternative.\n");
ASSERT(_afx_hcurDestroy == NULL); // will not get destroyed
_afx_hcurLast = ::LoadCursor(NULL, idcSecondary);
ASSERT(_afx_hcurLast != NULL);
}
_afx_idcPrimaryLast = idcPrimary;
}
ASSERT(_afx_hcurLast != NULL);
::SetCursor(_afx_hcurLast);
ASSERT(_afx_hcurLast != hcurToDestroy);
if (hcurToDestroy != NULL)
::DestroyCursor(hcurToDestroy); // destroy after being set
}
AfxUnlockGlobals(CRIT_SPLITTERWND);
}
void CSplitterWnd::OnMouseMove(UINT /*nFlags*/, CPoint pt)
{
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);
}
}
}
else
{
// simply hit-test and set appropriate cursor
int ht = HitTest(pt);
SetSplitCursor(ht);
}
}
void CSplitterWnd::OnLButtonDown(UINT /*nFlags*/, CPoint pt)
{
if (m_bTracking)
return;
StartTracking(HitTest(pt));
}
void CSplitterWnd::OnLButtonDblClk(UINT /*nFlags*/, CPoint pt)
{
int ht = HitTest(pt);
CRect rect;
StopTracking(FALSE);
if ((GetStyle() & SPLS_DYNAMIC_SPLIT) == 0)
return; // do nothing if layout is static
if (ht == vSplitterBox)
{
// half split
SplitRow(m_pRowInfo[0].nCurSize / 2);
}
else if (ht == hSplitterBox)
{
// half split
SplitColumn(m_pColInfo[0].nCurSize / 2);
}
else if (ht >= vSplitterBar1 && ht <= vSplitterBar15)
{
int rowDelete = ht - vSplitterBar1;
// don't delete the active row
int row;
if (GetActivePane(&row, NULL) != NULL && rowDelete == row)
++rowDelete;
DeleteRow(rowDelete);
}
else if (ht >= hSplitterBar1 && ht <= hSplitterBar15)
{
int colDelete = ht - hSplitterBar1;
// don't delete the active column
int col;
if (GetActivePane(NULL, &col) != NULL && colDelete == col)
++colDelete;
DeleteColumn(colDelete);
}
else if (ht >= splitterIntersection1 && ht <= splitterIntersection225)
{
int rowDelete = (ht - splitterIntersection1) / 15;
int colDelete = (ht - splitterIntersection1) % 15;
int row, col;
if (GetActivePane(&row, &col) != NULL)
{
// don't delete the active row or column
if (col == colDelete)
++colDelete;
if (row == rowDelete)
++rowDelete;
}
DeleteRow(rowDelete);
DeleteColumn(colDelete);
}
}
void CSplitterWnd::OnLButtonUp(UINT /*nFlags*/, CPoint /*pt*/)
{
StopTracking(TRUE);
}
void CSplitterWnd::OnCancelMode()
{
StopTracking(FALSE);
}
void CSplitterWnd::OnKeyDown(UINT nChar, UINT /*nRepCnt*/, UINT /*nFlags*/)
{
CPoint pt;
GetCursorPos(&pt);
int cz = GetKeyState(VK_CONTROL) < 0 ? 1 : 16;
int dx = 0;
int dy = 0;
switch (nChar)
{
case VK_ESCAPE:
StopTracking(FALSE);
return;
case VK_RETURN:
StopTracking(TRUE);
return;
case VK_LEFT:
dx = -1;
break;
case VK_RIGHT:
dx = +1;
break;
case VK_UP:
dy = -1;
break;
case VK_DOWN:
dy = +1;
break;
default:
Default(); // pass other keys through
return;
}
if (m_htTrack == vSplitterBox ||
m_htTrack >= vSplitterBar1 && m_htTrack <= vSplitterBar15)
{
// no movement along X axis
dx = 0;
}
if (m_htTrack == hSplitterBox ||
m_htTrack >= hSplitterBar1 && m_htTrack <= hSplitterBar15)
{
// no movement along Y axis
dy = 0;
}
// adjust to new position
pt.x += dx * cz;
pt.y += dy * cz;
// make sure within valid limits
ScreenToClient(&pt);
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;
ClientToScreen(&pt);
// cause WM_MOUSEMOVE to filter through
SetCursorPos(pt.x, pt.y);
}
void CSplitterWnd::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == SC_SIZE)
{
CWnd* pParent = GetSizingParent();
if (pParent != NULL)
{
pParent->SendMessage(WM_SYSCOMMAND, (WPARAM)nID, lParam);
return;
}
}
CWnd::OnSysCommand(nID, lParam);
}
/////////////////////////////////////////////////////////////////////////////
// CSplitterWnd command routing
BOOL CSplitterWnd::OnCommand(WPARAM wParam, LPARAM lParam)
{
if (CWnd::OnCommand(wParam, lParam))
return TRUE;
// route commands to the splitter to the parent frame window
return GetParentFrame()->SendMessage(WM_COMMAND, wParam, lParam);
}
BOOL CSplitterWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
if (CWnd::OnNotify(wParam, lParam, pResult))
return TRUE;
// route commands to the splitter to the parent frame window
*pResult = GetParentFrame()->SendMessage(WM_NOTIFY, wParam, lParam);
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// Scroll messages
BOOL CSplitterWnd::OnMouseWheel(UINT fFlags, short zDelta, CPoint point)
{
BOOL bRetVal = FALSE;
int row;
int col;
// find panes in the splitter that has scroll bars
// and have them do their scrolling
for (row = 0; row < m_nRows; row++)
{
for (col = 0; col < m_nCols; col++)
{
// only do the scrolling if the window is-a CScrollView
CWnd* pPane = GetPane(row, col);
CScrollView* pView = DYNAMIC_DOWNCAST(CScrollView, pPane);
if (pView != NULL)
{
// prefer to scroll vertically if available
CScrollBar* pBar = pView->GetScrollBarCtrl(SB_VERT);
if (pBar == NULL)
{
pBar = pView->GetScrollBarCtrl(SB_HORZ);
if (pBar == NULL)
continue;
}
// get the old position, do the scrolling, and
// then trigger the repaint
int nOldPos = pBar->GetScrollPos();
if (pView->DoMouseWheel(fFlags, zDelta, point))
bRetVal = TRUE;
if (col < m_nCols -1)
pBar->SetScrollPos(nOldPos, FALSE);
}
}
}
return TRUE;
}
void CSplitterWnd::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
ASSERT(pScrollBar != NULL);
int col = _AfxGetDlgCtrlID(pScrollBar->m_hWnd) - AFX_IDW_HSCROLL_FIRST;
ASSERT(col >= 0 && col < m_nMaxCols);
ASSERT(m_nRows > 0);
int nOldPos = pScrollBar->GetScrollPos();
#ifdef _DEBUG
int nNewPos;
#endif
for (int row = 0; row < m_nRows; row++)
{
GetPane(row, col)->SendMessage(WM_HSCROLL,
MAKELONG(nSBCode, nPos), (LPARAM)pScrollBar->m_hWnd);
#ifdef _DEBUG
if (row == 0)
{
nNewPos = pScrollBar->GetScrollPos();
if (pScrollBar->GetScrollPos() != nNewPos)
{
TRACE0("Warning: scroll panes setting different scroll positions.\n");
// stick with the last one set
}
}
#endif //_DEBUG
// set the scroll pos to the value it was originally for the next pane
if (row < m_nRows - 1)
pScrollBar->SetScrollPos(nOldPos, FALSE);
}
}
void CSplitterWnd::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
ASSERT(pScrollBar != NULL);
int row = _AfxGetDlgCtrlID(pScrollBar->m_hWnd) - AFX_IDW_VSCROLL_FIRST;
ASSERT(row >= 0 && row < m_nMaxRows);
ASSERT(m_nCols > 0);
int nOldPos = pScrollBar->GetScrollPos();
#ifdef _DEBUG
int nNewPos;
#endif
for (int col = 0; col < m_nCols; col++)
{
GetPane(row, col)->SendMessage(WM_VSCROLL,
MAKELONG(nSBCode, nPos), (LPARAM)pScrollBar->m_hWnd);
#ifdef _DEBUG
if (col == 0)
{
nNewPos = pScrollBar->GetScrollPos();
if (pScrollBar->GetScrollPos() != nNewPos)
{
TRACE0("Warning: scroll panes setting different scroll positions.\n");
// stick with the last one set
}
}
#endif //_DEBUG
// set the scroll pos to the value it was originally for the next pane
if (col < m_nCols - 1)
pScrollBar->SetScrollPos(nOldPos, FALSE);
}
}
// synchronized scrolling
BOOL CSplitterWnd::DoScroll(CView* pViewFrom, UINT nScrollCode, BOOL bDoScroll)
{
ASSERT_VALID(pViewFrom);
int rowFrom, colFrom;
if (!IsChildPane(pViewFrom, &rowFrom, &colFrom))
return FALSE;
BOOL bResult = FALSE;
// save original positions
int nOldVert = 0;
CScrollBar* pScrollVert = pViewFrom->GetScrollBarCtrl(SB_VERT);
if (pScrollVert != NULL)
nOldVert = pScrollVert->GetScrollPos();
int nOldHorz = 0;
CScrollBar* pScrollHorz = pViewFrom->GetScrollBarCtrl(SB_HORZ);
if (pScrollHorz != NULL)
nOldHorz = pScrollHorz->GetScrollPos();
// scroll the view from which the message is from
if (pViewFrom->OnScroll(nScrollCode, 0, bDoScroll))
bResult = TRUE;
if (pScrollVert != NULL)
{
#ifdef _DEBUG
int nNewVert = pScrollVert->GetScrollPos();
#endif
// scroll related columns
for (int col = 0; col < m_nCols; col++)
{
if (col == colFrom)
continue;
// set the scroll pos to the value it was originally
pScrollVert->SetScrollPos(nOldVert, FALSE);
// scroll the pane
CView* pView = (CView*)GetPane(rowFrom, col);
ASSERT_KINDOF(CView, pView);
ASSERT(pView != pViewFrom);
if (pView->OnScroll(MAKEWORD(-1, HIBYTE(nScrollCode)), 0,
bDoScroll))
{
bResult = TRUE;
}
#ifdef _DEBUG
if (pScrollVert->GetScrollPos() != nNewVert)
{
TRACE0("Warning: scroll panes setting different scroll positions.\n");
// stick with the last one set
}
#endif
}
}
if (pScrollHorz != NULL)
{
#ifdef _DEBUG
int nNewHorz = pScrollHorz->GetScrollPos();
#endif
// scroll related rows
for (int row = 0; row < m_nRows; row++)
{
if (row == rowFrom)
continue;
// set the scroll pos to the value it was originally
pScrollHorz->SetScrollPos(nOldHorz, FALSE);
// scroll the pane
CView* pView = (CView*)GetPane(row, colFrom);
ASSERT_KINDOF(CView, pView);
ASSERT(pView != pViewFrom);
if (pView->OnScroll(MAKEWORD(LOBYTE(nScrollCode), -1), 0,
bDoScroll))
{
bResult = TRUE;
}
#ifdef _DEBUG
if (pScrollHorz->GetScrollPos() != nNewHorz)
{
TRACE0("Warning: scroll panes setting different scroll positions.\n");
// stick with the last one set
}
#endif
}
}
return bResult;
}
BOOL CSplitterWnd::DoScrollBy(CView* pViewFrom, CSize sizeScroll, BOOL bDoScroll)
{
int rowFrom, colFrom;
if (!IsChildPane(pViewFrom, &rowFrom, &colFrom))
return FALSE;
BOOL bResult = FALSE;
// save original positions
int nOldVert = 0;
CScrollBar* pScrollVert = pViewFrom->GetScrollBarCtrl(SB_VERT);
if (pScrollVert != NULL)
nOldVert = pScrollVert->GetScrollPos();
int nOldHorz = 0;
CScrollBar* pScrollHorz = pViewFrom->GetScrollBarCtrl(SB_HORZ);
if (pScrollHorz != NULL)
nOldHorz = pScrollHorz->GetScrollPos();
// scroll the view from which the message is from
if (pViewFrom->OnScrollBy(sizeScroll, bDoScroll))
bResult = TRUE;
if (pScrollVert != NULL)
{
#ifdef _DEBUG
int nNewVert = pScrollVert->GetScrollPos();
#endif
// scroll related columns
for (int col = 0; col < m_nCols; col++)
{
if (col == colFrom)
continue;
// set the scroll pos to the value it was originally for the next pane
pScrollVert->SetScrollPos(nOldVert, FALSE);
// scroll the pane
CView* pView = (CView*)GetPane(rowFrom, col);
ASSERT_KINDOF(CView, pView);
ASSERT(pView != pViewFrom);
if (pView->OnScrollBy(CSize(0, sizeScroll.cy), bDoScroll))
bResult = TRUE;
#ifdef _DEBUG
if (pScrollVert->GetScrollPos() != nNewVert)
{
TRACE0("Warning: scroll panes setting different scroll positions.\n");
// stick with the last one set
}
#endif
}
}
if (pScrollHorz != NULL)
{
#ifdef _DEBUG
int nNewHorz = pScrollHorz->GetScrollPos();
#endif
// scroll related rows
for (int row = 0; row < m_nRows; row++)
{
if (row == rowFrom)
continue;
// set the scroll pos to the value it was originally for the next pane
pScrollHorz->SetScrollPos(nOldHorz, FALSE);
// scroll the pane
CView* pView = (CView*)GetPane(row, colFrom);
ASSERT_KINDOF(CView, pView);
ASSERT(pView != pViewFrom);
if (pView->OnScrollBy(CSize(sizeScroll.cx, 0), bDoScroll))
bResult = TRUE;
#ifdef _DEBUG
if (pScrollHorz->GetScrollPos() != nNewHorz)
{
TRACE0("Warning: scroll panes setting different scroll positions.\n");
// stick with the last one set
}
#endif
}
}
return bResult;
}
/////////////////////////////////////////////////////////////////////////////
// Focus control and control over the current pane/child
BOOL CSplitterWnd::CanActivateNext(BOOL)
{
ASSERT_VALID(this);
if (GetActivePane() == NULL)
{
TRACE0("Warning: Can't go to next pane - there is no current pane.\n");
return FALSE;
}
ASSERT(m_nRows != 0);
ASSERT(m_nCols != 0);
// if more than 1x1 we can go to the next or prev pane
return (m_nRows > 1) || (m_nCols > 1);
}
void CSplitterWnd::ActivateNext(BOOL bPrev)
{
ASSERT_VALID(this);
// find the coordinate of the current pane
int row, col;
if (GetActivePane(&row, &col) == NULL)
{
TRACE0("Warning: Cannot go to next pane - there is no current view.\n");
return;
}
ASSERT(row >= 0 && row < m_nRows);
ASSERT(col >= 0 && col < m_nCols);
// determine next pane
if (bPrev)
{
// prev
if (--col < 0)
{
col = m_nCols - 1;
if (--row < 0)
row = m_nRows - 1;
}
}
else
{
// next
if (++col >= m_nCols)
{
col = 0;
if (++row >= m_nRows)
row = 0;
}
}
// set newly active pane
SetActivePane(row, col);
}
void CSplitterWnd::SetActivePane(int row, int col, CWnd* pWnd)
{
// 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
{
TRACE0("Warning: Next pane is not a view - calling SetFocus.\n");
pPane->SetFocus();
}
}
CWnd* CSplitterWnd::GetActivePane(int* pRow, int* pCol)
// return active view, NULL when no active view
{
ASSERT_VALID(this);
// attempt to use active view of frame window
CWnd* pView = NULL;
CFrameWnd* pFrameWnd = GetParentFrame();
ASSERT_VALID(pFrameWnd);
pView = pFrameWnd->GetActiveView();
// failing that, use the current focus
if (pView == NULL)
pView = GetFocus();
// make sure the pane is a child pane of the splitter
if (pView != NULL && !IsChildPane(pView, pRow, pCol))
pView = NULL;
return pView;
}
/////////////////////////////////////////////////////////////////////////////
// CSplitterWnd diagnostics
#ifdef _DEBUG
void CSplitterWnd::AssertValid() const
{
CWnd::AssertValid();
ASSERT(m_nMaxRows >= 1);
ASSERT(m_nMaxCols >= 1);
ASSERT(m_nMaxCols > 1 || m_nMaxRows > 1); // 1x1 is not permitted
ASSERT(m_nRows >= 1);
ASSERT(m_nCols >= 1);
ASSERT(m_nRows <= m_nMaxRows);
ASSERT(m_nCols <= m_nMaxCols);
}
void CSplitterWnd::Dump(CDumpContext& dc) const
{
CWnd::Dump(dc);
if (m_pDynamicViewClass != NULL)
dc << "m_pDynamicViewClass = " << m_pDynamicViewClass->m_lpszClassName;
dc << "\nm_nMaxRows = " << m_nMaxRows;
dc << "\nm_nMaxCols = " << m_nMaxCols;
dc << "\nm_nRows = " << m_nRows;
dc << "\nm_nCols = " << m_nCols;
dc << "\nm_bHasHScroll = " << m_bHasHScroll;
dc << "\nm_bHasVScroll = " << m_bHasVScroll;
dc << "\nm_cxSplitter = " << m_cxSplitter;
dc << "\nm_cySplitter = " << m_cySplitter;
if (m_bTracking)
{
dc << "\nTRACKING: m_htTrack = " << m_htTrack;
dc << "\nm_rectLimit = " << m_rectLimit;
dc << "\nm_ptTrackOffset = " << m_ptTrackOffset;
dc << "\nm_rectTracker = " << m_rectTracker;
if (m_bTracking2)
dc << "\nm_rectTracker2 = " << m_rectTracker2;
}
dc << "\n";
}
#endif
/////////////////////////////////////////////////////////////////////////////