// Copyright (c) Iuri Apollonio 1998 // Use & modify as you want & need, and leave those 3 lines. // http://www.codeguru.com // GfxSplitterWnd.cpp: implementation of the CGfxSplitterWnd class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "..\SmsManager.h" #include "GfxSplitterWnd.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #define CX_BORDER 1 #define CY_BORDER 1 #include static BOOL bNotWin4; ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// BEGIN_MESSAGE_MAP(CGfxSplitterWnd, CSplitterWnd) //{{AFX_MSG_MAP(CGfxSplitterWnd) ON_WM_ERASEBKGND() ON_WM_PAINT() //}}AFX_MSG_MAP END_MESSAGE_MAP() CGfxSplitterWnd::CGfxSplitterWnd() { m_cxBorder = 0; m_cyBorder = 0; m_cxSplitter = m_cySplitter = 2 + 2; m_cxSplitterGap = m_cySplitterGap = 2 + 2; DWORD dwVersion = ::GetVersion(); BOOL bWin4 = (BYTE)dwVersion >= 4; bNotWin4 = 1 - bWin4; // for convenience m_upBorder = 8; bWhiteLine = true; } CGfxSplitterWnd::~CGfxSplitterWnd() { } void CGfxSplitterWnd::OnDrawSplitter(CDC* pDC, ESplitType nType, const CRect& rectArg) { 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, GetSysColor(COLOR_3DFACE), GetSysColor(COLOR_WINDOWFRAME)); rect.InflateRect(-CX_BORDER, -CY_BORDER); pDC->Draw3dRect(rect, GetSysColor(COLOR_BTNHIGHLIGHT), GetSysColor(COLOR_BTNSHADOW)); rect.InflateRect(-CX_BORDER, -CY_BORDER); break; } COLORREF clr = GetSysColor(COLOR_3DFACE); pDC->FillSolidRect(rect, clr); } void CGfxSplitterWnd::OnInvertTracker(const CRect & rect) { ASSERT_VALID(this); ASSERT(!rect.IsRectEmpty()); ASSERT((GetStyle() & WS_CLIPCHILDREN) == 0); CDC* pDC = GetDC(); CBrush* pBrush = CBrush::FromHandle((HBRUSH) GetStockObject(WHITE_BRUSH)); 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); } // repositions client area of specified window // assumes everything has WS_BORDER or is inset like it does // (includes scroll bars) static void AFXAPI DeferClientPos(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) // 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); } static void AFXAPI LayoutRowCol(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(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 } void CGfxSplitterWnd::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_upBorder; 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 - bNotWin4);// - afxData.bNotWin4; int cy = (rectClient.bottom - rectInside.bottom - bNotWin4);// - 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 DeferClientPos(&layout, pScrollBar, rectInside.right + bNotWin4, rectInside.bottom + bNotWin4, cx, cy, TRUE); } // reposition scroll bars if (m_bHasHScroll) { int cxSplitterBox = m_cxSplitter + bNotWin4;// split box bigger int x = rectClient.left; int y = rectInside.bottom + 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; DeferClientPos(&layout, pScrollBar, x, y, cx, cy, TRUE); x += cx + m_cxSplitterGap; } } if (m_bHasVScroll) { int cySplitterBox = m_cySplitter + bNotWin4;// split box bigger int x = rectInside.right + 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; DeferClientPos(&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); DeferClientPos(&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)) TRACE(_T("Warning: DeferWindowPos failed - low system resources.\n")); // invalidate all the splitter bars (with NULL pDC) DrawAllSplitBars(NULL, rectInside.right, rectInside.bottom); } void CGfxSplitterWnd::GetInsideRect(CRect& rect) const { CSplitterWnd::GetInsideRect(rect); rect.top += m_upBorder; /* ASSERT_VALID(this); GetClientRect(rect); ASSERT(rect.left == 0 && rect.top == 0); // subtract space for 3d borders rect.InflateRect(-m_cxBorder, -m_cyBorder); rect.top += m_upBorder; // subtract scrollbar clearance if (m_bHasVScroll) rect.right -= GetSystemMetrics(SM_CXVSCROLL); if (m_bHasHScroll) rect.bottom -= GetSystemMetrics(SM_CYHSCROLL);*/ } BOOL CGfxSplitterWnd::OnEraseBkgnd(CDC* pDC) { // return CSplitterWnd::OnEraseBkgnd(pDC); CRect rectClient; GetClientRect(rectClient); rectClient.InflateRect(-m_cxBorder, -m_cyBorder); rectClient.bottom = rectClient.top + m_upBorder; pDC->FillSolidRect(rectClient, GetSysColor(COLOR_3DFACE)); return true; } void CGfxSplitterWnd::OnPaint() { CSplitterWnd::OnPaint(); if (bWhiteLine) { CClientDC dc(this); CRect rectClient; GetClientRect(rectClient); CPen pn(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW)); CPen * op = dc.SelectObject(&pn); dc.MoveTo(rectClient.left, rectClient.top); dc.LineTo(rectClient.right, rectClient.top); CPen pn1(PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT)); dc.SelectObject(&pn1); dc.MoveTo(rectClient.left, rectClient.top+1); dc.LineTo(rectClient.right, rectClient.top+1); dc.SelectObject(op); } }