📄 dlgsplitter.cpp
字号:
#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
};
/////////////////////////////////////////////////////////////////////////////
// CDlgSplitterWnd
BEGIN_MESSAGE_MAP(CDlgSplitterWnd, CWnd)
//{{AFX_MSG_MAP(CDlgSplitterWnd)
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()
/////////////////////////////////////////////////////////////////////////////
// CDlgSplitterWnd construction/destruction
CDlgSplitterWnd::CDlgSplitterWnd()
{
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: CDlgSplitterWnd assumes 1 pixel border.\n");
// will look ugly if borders are not 1 pixel wide and 1 pixel high
}
#endif
}
CDlgSplitterWnd::~CDlgSplitterWnd()
{
delete[] m_pRowInfo;
delete[] m_pColInfo;
}
BOOL CDlgSplitterWnd::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 CDlgSplitterWnd::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 CDlgSplitterWnd::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 CDlgSplitterWnd::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;
}
/////////////////////////////////////////////////////////////////////////////
// CDlgSplitterWnd 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 CDlgSplitterWnd::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 CDlgSplitterWnd::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 CDlgSplitterWnd::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;
}
/////////////////////////////////////////////////////////////////////////////
// CDlgSplitterWnd attributes
CWnd* CDlgSplitterWnd::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 CDlgSplitterWnd::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;
}
}
/////////////////////////////////////////////////////////////////////////////
// CDlgSplitterWnd 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 CDlgSplitterWnd::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 CDlgSplitterWnd::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 CDlgSplitterWnd::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 CDlgSplitterWnd::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 CDlgSplitterWnd::GetScrollStyle() const
{
DWORD dwStyle = 0;
if (m_bHasHScroll)
dwStyle |= WS_HSCROLL;
if (m_bHasVScroll)
dwStyle |= WS_VSCROLL;
return dwStyle;
}
void CDlgSplitterWnd::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)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -