home *** CD-ROM | disk | FTP | other *** search
- // MinSplitterWnd.cpp : implementation file
- //
-
- #include "stdafx.h"
- #include <afxpriv.h> // For AFX_SIZEPARENTPARAMS and AfxRepositionWindow
- #include "MinSplitterWnd.h"
-
- #ifdef _DEBUG
- #define new DEBUG_NEW
- #undef THIS_FILE
- static char THIS_FILE[] = __FILE__;
- #endif
-
- //==============================================================================
- //
- // Kludges because we don't have access to mfc\src.
- //
- //==============================================================================
-
- // From afximpl.h and auxdata.cpp
-
- // Note: afxData.cxBorder and afxData.cyBorder aren't used anymore
- #define CX_BORDER 1
- #define CY_BORDER 1
-
- struct _AFXDATA
- {
- int cxBorder2, cyBorder2;
- BOOL bWin4; // TRUE if Windows 4.0
- BOOL bNotWin4; // TRUE if not Windows 4.0
-
- _AFXDATA() // From auxdata.cpp
- {
- // Cache various target platform version information
- DWORD dwVersion = ::GetVersion();
- bWin4 = (BYTE)dwVersion >= 4;
- bNotWin4 = 1 - bWin4; // for convenience
-
- // cxBorder2 and cyBorder are 2x borders for Win4
- cxBorder2 = bWin4 ? CX_BORDER*2 : CX_BORDER;
- cyBorder2 = bWin4 ? CY_BORDER*2 : CY_BORDER;
- }
- } afxData;
-
- // From winsplit.cpp...
-
- // 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
- };
-
- //==============================================================================
- //
- // End of Kludges.
- //
- //==============================================================================
-
- /////////////////////////////////////////////////////////////////////////////
- // CMinSplitterWnd
-
- CMinSplitterWnd::CMinSplitterWnd()
- {
- }
-
- CMinSplitterWnd::~CMinSplitterWnd()
- {
- }
-
-
- BEGIN_MESSAGE_MAP(CMinSplitterWnd, CSplitterWnd)
- //{{AFX_MSG_MAP(CMinSplitterWnd)
- ON_WM_MOUSEMOVE()
- ON_WM_WINDOWPOSCHANGING()
- //}}AFX_MSG_MAP
- END_MESSAGE_MAP()
-
-
- /////////////////////////////////////////////////////////////////////////////
- // CMinSplitterWnd message handlers
-
- void CMinSplitterWnd::OnMouseMove(UINT nFlags, CPoint point)
- {
-
- if ( m_bTracking && GetCapture() == this )
- {
- // Work out which panes are left or above the splitter (horiz and vert).
- bool bSizingCol = false;
- bool bSizingRow = false;
- int nCol;
- int nRow;
-
- if ( m_htTrack >= hSplitterBar1 && m_htTrack <= hSplitterBar15 )
- {
- nCol = m_htTrack - hSplitterBar1;
- bSizingCol = true;
- }
- else if ( m_htTrack >= vSplitterBar1 && m_htTrack <= vSplitterBar15 )
- {
- nRow = m_htTrack - vSplitterBar1;
- bSizingRow = true;
- }
- else if ( m_htTrack >= splitterIntersection1 && m_htTrack <= splitterIntersection225 )
- {
- nCol = ( m_htTrack - splitterIntersection1 ) % 15;
- nRow = ( m_htTrack - splitterIntersection1 ) / 15;
- bSizingCol = true;
- bSizingRow = true;
- } // if
-
- // Get client rect for limit calculations.
- CRect rectClient;
- GetClientRect( rectClient );
-
- point.Offset( m_ptTrackOffset ); // point is the upper right of hit detect
-
- // Limit row.
- if ( bSizingRow )
- {
- int nLimit1 = m_cyBorder; // Start with top border.
-
- // If top splitter...
- if ( nRow == 0 )
- {
- nLimit1 += m_pRowInfo[ 0 ].nMinSize; // ...just add first row minimum size.
- }
- // If pane above splitter at minimum size...
- else if ( m_pRowInfo[ nRow ].nCurSize == m_pRowInfo[ nRow ].nMinSize )
- {
- for ( int i = 0; i < nRow; i++ )
- {
- nLimit1 += m_pRowInfo[ i ].nCurSize; // ...add previous rows' sizes and splitters.
- nLimit1 += m_cySplitter;
- } // for
- nLimit1 += m_pRowInfo[ i ].nCurSize;
- }
- // Otherwise...
- else
- {
- for ( int i = 0; i < nRow; i++ )
- {
- nLimit1 += m_pRowInfo[ i ].nMinSize; // ...add previous rows' minimum sizes and splitters.
- nLimit1 += m_cySplitter;
- } // for
- nLimit1 += m_pRowInfo[ i ].nMinSize;
- } // if
-
-
- int nLimit2 = rectClient.bottom; // Start with bottom.
- nLimit2 -= m_cyBorder; // Take off bottom border.
-
- // If bottom splitter...
- if ( nRow + 1 == m_nRows - 1 )
- {
- nLimit2 -= m_pRowInfo[ nRow + 1 ].nMinSize; // ...just take off last row minimum size.
- }
- // Otherwise...
- else
- {
- for ( int i = m_nRows - 1; i > nRow + 1; i-- )
- {
- nLimit2 -= m_pRowInfo[ i ].nMinSize; // ...take off following rows' minimum sizes and splitters.
- nLimit2 -= m_cySplitter;
- } // for
- nLimit2 -= m_pRowInfo[ i ].nMinSize;
- } // if
-
- nLimit2 -= m_cySplitter / 2; // Take off half a splitter.
-
- if ( point.y < nLimit1 + m_cySplitter / 2 - 2 )
- {
- point.y = nLimit1 + m_cySplitter / 2 - 2;
- } // if
-
- if ( point.y > nLimit2 - m_cySplitter / 2 )
- {
- point.y = nLimit2 - m_cySplitter / 2;
- } // if
- } // if
-
- // Limit column.
- if ( bSizingCol )
- {
- int nLimit1 = 0;// = m_cxBorder; // Start with left border.
-
- // If left splitter...
- if ( nCol == 0 )
- {
- nLimit1 += m_pColInfo[ 0 ].nMinSize; // ...just add first column minimum size.
- }
- // If pane left of splitter at minimum size...
- else if ( m_pColInfo[ nCol ].nCurSize == m_pColInfo[ nCol ].nMinSize )
- {
- for ( int i = 0; i < nCol; i++ )
- {
- nLimit1 += m_pColInfo[ i ].nCurSize; // ...add previous columns' sizes and splitters.
- nLimit1 += m_cxSplitter;
- } // for
- nLimit1 += m_pColInfo[ i ].nCurSize;
- }
- // Otherwise...
- else
- {
- for ( int i = 0; i < nCol; i++ )
- {
- nLimit1 += m_pColInfo[ i ].nMinSize; // ...add previous columns' minimum sizes and splitters.
- nLimit1 += m_cxSplitter;
- } // for
- nLimit1 += m_pColInfo[ i ].nMinSize; // Minimum height of each column,
- } // if
-
-
- int nLimit2 = rectClient.right; // Start with right.
- nLimit2 -= m_cxBorder; // Take off right border,
-
- // If right splitter...
- if ( nCol + 1 == m_nCols - 1 )
- {
- nLimit2 -= m_pColInfo[ nCol + 1 ].nMinSize; // ...just take off last column minimum size.
- }
- // Otherwise...
- else
- {
- for ( int i = m_nCols - 1; i > nCol + 1; i-- )
- {
- nLimit2 -= m_pColInfo[ i ].nMinSize; // ...take off following columns' minimum sizes and splitters.
- nLimit2 -= m_cxSplitter;
- } // for
- nLimit2 -= m_pColInfo[ i ].nMinSize;
- } // if
-
- nLimit2 -= m_cxSplitter / 2; // Take off half a splitter,
-
- if ( point.x < nLimit1/* + m_cxSplitter / 2 - 2*/ )
- {
- point.x = nLimit1 /*+ m_cxSplitter / 2 - 2*/;
- } // if
-
- if ( point.x > nLimit2 - m_cxSplitter / 2 )
- {
- point.x = nLimit2 - m_cxSplitter / 2;
- } // if
- } // if
-
- // Restore offset 'cos CSplitterWnd::OnMouseMove will do it again.
- point.Offset( -m_ptTrackOffset ); // point is the upper right of hit detect
- } // if
-
- // Call base class.
- CSplitterWnd::OnMouseMove(nFlags, point);
- }
-
- int CMinSplitterWnd::GetMinClientWidth()
- {
- // If no columns, no width.
- if ( m_pColInfo == NULL )
- {
- return 0;
- } // if
-
- // Min width is...
- int nMinWidth = m_cxBorder; // Left border,
-
- for ( int nCol = 0; nCol < m_nCols; nCol++ )
- {
- nMinWidth += m_pColInfo[ nCol ].nMinSize; // Minimum width of each column,
-
- if ( nCol < m_nCols - 1 )
- {
- nMinWidth += m_cxSplitter; // Width of splitter for each column except the last,
- } // if
- } // for
-
- nMinWidth += m_cxBorder; // Right border.
-
- return nMinWidth;
- }
-
- int CMinSplitterWnd::GetMinClientHeight()
- {
- // If no rows, no height.
- if ( m_pRowInfo == NULL )
- {
- return 0;
- } // if
-
- // Min height is...
- int nMinHeight = m_cyBorder; // Top border,
-
- for ( int nRow = 0; nRow < m_nRows; nRow++ )
- {
- nMinHeight += m_pRowInfo[ nRow ].nMinSize; // Minimum height of each row,
-
- if ( nRow < m_nRows - 1 )
- {
- nMinHeight += m_cySplitter; // Height of splitter for each row except the last,
- } // if
- } // for
-
- nMinHeight += m_cyBorder; // Bottom border.
-
- return nMinHeight;
- }
-
- void CMinSplitterWnd::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos)
- {
- // Get minimum width and height of client area.
- int nMinWidth = GetMinClientWidth();
- int nMinHeight = GetMinClientHeight();
-
- // Get the current window size.
- CRect rcWindow;
- GetWindowRect( rcWindow );
- GetParent()->ScreenToClient( rcWindow );
-
- // If trying to size too small...
- if ( lpwndpos->cx < nMinWidth )
- {
- // If dragging left border right...
- if ( rcWindow.left < lpwndpos->x )
- {
- // How much over are we?
- int nOver = nMinWidth - lpwndpos->cx;
-
- // Adjust left coord.
- lpwndpos->x -= nOver;
-
- } // if
-
- // Fix width.
- lpwndpos->cx = nMinWidth;
- } // if
-
- // If trying to size too small...
- if ( lpwndpos->cy < nMinHeight )
- {
- // If dragging top border down...
- if ( rcWindow.top < lpwndpos->y )
- {
- // How much over are we?
- int nOver = nMinHeight - lpwndpos->cy;
-
- // Adjust left coord.
- lpwndpos->y -= nOver;
- } // if
-
- // Fix height.
- lpwndpos->cy = nMinHeight;
- } // if
- }
-
- // Generic routine:
- // for X direction: pInfo = m_pColInfo, nMax = m_nMaxCols, nSize = cx
- // for Y direction: pInfo = m_pRowInfo, nMax = m_nMaxRows, nSize = cy
- static void AFXAPI LayoutRowCol(CSplitterWnd::CRowColInfo* pInfoArray,
- int nMax, int nSize, int nSizeSplitter)
- {
- ASSERT(pInfoArray != NULL);
- ASSERT(nMax > 0);
- ASSERT(nSizeSplitter > 0);
-
- // Save size for later on, because the second for loop modifies it.
- int nSizeParm = nSize;
-
- 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 = pInfo->nMinSize; // Can't be smaller than min.
- 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 || nSize < pInfo->nMinSize && i != 0 )
- {
- // OK, take what space we can for now.
- pInfo->nCurSize = nSize;
-
- // How much more space do I need?
- int nNeed = pInfo->nMinSize - nSize;
- ASSERT( nNeed > 0 );
-
- // There must be a previous pane bigger than minimum size because
- // of OnWindowPosChanging's restrictions.
- bool bDone = false;
- int nCol = nMax - 1;
-
- while ( ! bDone && nCol >= 0 )
- {
- if ( nCol != i )
- {
- // If we have found a pane with some size I can steal...
- if ( pInfoArray[ nCol ].nCurSize > pInfoArray[ nCol ].nMinSize )
- {
- // How much can I steal?
- int nCanSteal = pInfoArray[ nCol ].nCurSize - pInfoArray[ nCol ].nMinSize;
-
- // If it is enough...
- if ( nCanSteal >= nNeed )
- {
- // Yes, take what we need...
- pInfoArray[ nCol ].nCurSize -= nNeed;
- pInfoArray[ nCol ].nIdealSize -= nNeed;
- pInfo->nCurSize += nNeed;
-
- // ...and exit the loop because all pane sizes are valid.
- bDone = true;
- }
- else if ( nCanSteal > 0 )
- {
- // No, take what we can...
- pInfoArray[ nCol ].nCurSize -= nCanSteal;
- pInfoArray[ nCol ].nIdealSize -= nCanSteal;
- pInfo->nCurSize += nCanSteal;
-
- // ...update how much we still need.
- nNeed -= nCanSteal;
- } // if
- } // if
- } // if
-
- // Go back to previous pane.
- nCol--;
- } // for
-
- 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
-
- // Now ensure that none of the columns/rows are smaller than their minimum.
- // We can guarantee that all columns/row can be at least their minimum because
- // of our checks in OnWindowPosChanging.
- for (i = 0, pInfo = pInfoArray; i < nMax; i++, pInfo++)
- {
- if ( pInfo->nCurSize < pInfo->nMinSize )
- {
- pInfo->nCurSize = pInfo->nMinSize;
- } // if
- } // for
- }
-
- // 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)
- 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);
- }
-
- //==============================================================================
- //
- // RecalcLayout
- //
- // Remarks
- // This function has been copied verbatim from winsplit.cpp purely because it
- // calls LayoutRowCol, which for some reason the MFC developers decided to make
- // a non-member function, so I can't override it.
- //
- //==============================================================================
- void CMinSplitterWnd::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)
- 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) - 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
- DeferClientPos(&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;
- DeferClientPos(&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;
- 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))
- TRACE0("Warning: DeferWindowPos failed - low system resources.\n");
-
- // invalidate all the splitter bars (with NULL pDC)
- DrawAllSplitBars(NULL, rectInside.right, rectInside.bottom);
- }
-
-