Undocumented Corner
by George Shepherd and Scot Wingo



Listing One
BOOL CView::OnSplitCmd(UINT)
{
    CSplitterWnd* pSplitter = GetParentSplitter(this, FALSE);
    if (pSplitter == NULL)
        return FALSE;
    ASSERT(!pSplitter->IsTracking());
    pSplitter->DoKeyboardSplit();
    return TRUE;    
}


Listing Two
BOOL CSplitterWnd::DoKeyboardSplit()
{
    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;
}


Listing Three
class CDobbsSplitter : public CSplitterWnd
{
// Construction
public:
    CDobbsSplitter();

// Attributes
public:
    BOOL m_bPanesSwapped;
    int m_nSplitRatio;
    int m_nSplitResolution;
// Operations
public:
  void SetSplitRatio( int nRatio );
  BOOL IsSplitHorizontally() const;
  BOOL IsSplitVertically() const { return !IsSplitHorizontally(); }
  BOOL ArePanesSwapped() const { return m_bPanesSwapped; }
protected:
    BOOL UpdateSplitRatio();
    BOOL UpdatePanes( int cx, int cy );
    BOOL UpdatePanes();
public: 
void FlipSplit();     //DDJ
    void SplitVertically();
    void SplitHorizontally();
    // Implementation
public:
    virtual ~CDobbsSplitter();
protected: // Generated message map functions
    afx_msg void OnSize(UINT nType, int cx, int cy );
    DECLARE_MESSAGE_MAP()
};


Listing Four
CDobbsSplitter::CDobbsSplitter()
{
    //Intialize extended state.

   m_nSplitRatio = -1;
    m_bPanesSwapped = FALSE;
    nSplitResolution = 1;
}
BEGIN_MESSAGE_MAP(CDobbsSplitter, CSplitterWnd)
    ON_WM_SIZE()
END_MESSAGE_MAP()
void CDobbsSplitter::SetSplitRatio( int nRatio )
{
    m_nSplitRatio = nRatio;
}
BOOL CDobbsSplitter::IsSplitHorizontally() const
{
    ASSERT(( m_nRows > 1 ) != ( m_nCols > 1 ));
    ASSERT( max( m_nRows, m_nCols ) == 2 ); 
    return ( m_nCols > 1 );
}
void CDobbsSplitter::SplitHorizontally()
{
    if( IsSplitHorizontally())
        return;
    ASSERT( m_nCols = 1 );
    ASSERT( m_nRows = 2 );
    CWnd* pPane = GetDlgItem( IdFromRowCol( 1, 0 ));

    ASSERT( pPane );
    // swap the H/V information
    m_nMaxCols = m_nCols = 2;
    m_nMaxRows = m_nRows = 1;
    CRowColInfo* pTmp = m_pColInfo;
    m_pColInfo = m_pRowInfo;
    m_pRowInfo = pTmp;
   // change the last pane's ID reference
    pPane->SetDlgCtrlID( IdFromRowCol( 0, 1 ));
    ASSERT( GetPane( 0, 1 )->GetSafeHwnd() == pPane->GetSafeHwnd() );
    if( UpdatePanes())
        RecalcLayout();
}
void CDobbsSplitter::SplitVertically()
{
    if( IsSplitVertically())
        return;
    ASSERT( m_nCols = 2 );
    ASSERT( m_nRows = 1 );
    CWnd* pPane = GetDlgItem(IdFromRowCol( 0, 1 ));
    ASSERT( pPane );
    // swap the H/V information
    m_nMaxCols = m_nCols = 1;
    m_nMaxRows = m_nRows = 2;
    CRowColInfo* pTmp = m_pColInfo;
    m_pColInfo = m_pRowInfo;
    m_pRowInfo = pTmp;
    // change last pane's ID reference (no need to change ID for first one)
    pPane->SetDlgCtrlID( IdFromRowCol( 1, 0 ));
    ASSERT( GetPane( 1, 0 )->GetSafeHwnd() == pPane->GetSafeHwnd() );


    if( UpdatePanes())
        RecalcLayout();
}
void CDobbsSplitter::FlipSplit()
{
    if( IsSplitHorizontally())
        SplitVertically();
    else
    {
        ASSERT( IsSplitVertically());
        SplitHorizontally();
    }
}
int CDobbsSplitter::UpdateSplitRatio()
{
    CRowColInfo* pPanes;
    int czSplitter;
    if( IsSplitHorizontally())
    {
        pPanes     = m_pColInfo;
        czSplitter = m_cxSplitter;
    }
    else

    {
        pPanes     = m_pRowInfo;
        czSplitter = m_cySplitter;
    }
    TRACE( "CDobbsSplitter::UpdateSplitRatio: 1:%i, 2:%i\n",
           pPanes[0].nCurSize, pPanes[1].nCurSize);
    if(( pPanes[0].nCurSize != -1 ) &&
       ( pPanes[0].nCurSize + pPanes[1].nCurSize != 0 ))
    {
        m_nSplitRatio = nSplitResolution * pPanes[0].nCurSize /
                   ( pPanes[0].nCurSize + pPanes[1].nCurSize + czSplitter );
    }
    TRACE("m_nSplitRatio=%i\n", m_nSplitRatio );
    return m_nSplitRatio;
}
BOOL CDobbsSplitter::UpdatePanes()
{
    CRect rcClient;
    GetClientRect( rcClient );
    return ( !rcClient.IsRectEmpty() &&
             UpdatePanes( rcClient.Width(), rcClient.Height()));
}
BOOL CDobbsSplitter::UpdatePanes( int cx, int cy )
{
    TRACE("UpdatePanes: cx=%i, cy=%i\n", cx, cy );
    CRowColInfo* pPanes;
    int cz;
    if( IsSplitHorizontally())
    {
        pPanes = m_pColInfo;
        cz     = cx;
    }

    else
    {
        pPanes = m_pRowInfo;
        cz     = cy;
    }
    BOOL bRes = UpdateSplitRatio();
    if( m_nSplitRatio >= 0 )
    {
        ASSERT( (ULONG)m_nSplitRatio * cz / 
            nSplitResolution <= (ULONG)INT_MAX );
       pPanes[0].nIdealSize =
            int( (ULONG)m_nSplitRatio * cz / nSplitResolution );
    }
    return bRes;
}
void CDobbsSplitter::OnSize( UINT nType, int cx, int cy )
{
    TRACE("CDobbsSplitter::OnSize: cx:%i, cy:%i\n", cx, cy );
    if(( nType != SIZE_MINIMIZED )&&( cx > 0 )&&( cy > 0 ))
        UpdatePanes( cx, cy );
    CSplitterWnd::OnSize( nType, cx, cy );
}


Example 1: 
ON_COMMAND_EX(ID_WINDOW_SPLIT, OnSplitCmd)
ON_UPDATE_COMMAND_UI(ID_WINDOW_SPLIT, OnUpdateSplitCmd)




