home *** CD-ROM | disk | FTP | other *** search
/ Supercompiler 1997 / SUPERCOMPILER97.iso / MS_VC.50 / VC / MFC / SRC / WINSPLIT.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1996-12-12  |  62.3 KB  |  2,368 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1997 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12.  
  13. #ifdef AFX_CORE3_SEG
  14. #pragma code_seg(AFX_CORE3_SEG)
  15. #endif
  16.  
  17. #ifdef _DEBUG
  18. #undef THIS_FILE
  19. static char THIS_FILE[] = __FILE__;
  20. #endif
  21.  
  22. /////////////////////////////////////////////////////////////////////////////
  23. // Visual attributes and other constants
  24.  
  25. // HitTest return values (values and spacing between values is important)
  26. enum HitTestValue
  27. {
  28.     noHit                   = 0,
  29.     vSplitterBox            = 1,
  30.     hSplitterBox            = 2,
  31.     bothSplitterBox         = 3,        // just for keyboard
  32.     vSplitterBar1           = 101,
  33.     vSplitterBar15          = 115,
  34.     hSplitterBar1           = 201,
  35.     hSplitterBar15          = 215,
  36.     splitterIntersection1   = 301,
  37.     splitterIntersection225 = 525
  38. };
  39.  
  40. // implemented in VIEWSCRL.cPP
  41. extern UINT _AfxGetMouseScrollLines(BOOL bForceFresh = FALSE);
  42.  
  43.  
  44. /////////////////////////////////////////////////////////////////////////////
  45. // CSplitterWnd
  46.  
  47. BEGIN_MESSAGE_MAP(CSplitterWnd, CWnd)
  48.     //{{AFX_MSG_MAP(CSplitterWnd)
  49.     ON_WM_SETCURSOR()
  50.     ON_WM_MOUSEMOVE()
  51.     ON_WM_PAINT()
  52.     ON_WM_LBUTTONDOWN()
  53.     ON_WM_LBUTTONDBLCLK()
  54.     ON_WM_LBUTTONUP()
  55.     ON_WM_KEYDOWN()
  56.     ON_WM_SIZE()
  57.     ON_WM_HSCROLL()
  58.     ON_WM_VSCROLL()
  59.     ON_WM_NCCREATE()
  60.     ON_WM_SYSCOMMAND()
  61.     ON_WM_CANCELMODE()
  62.     ON_MESSAGE_VOID(WM_DISPLAYCHANGE, OnDisplayChange)
  63.     ON_MESSAGE_VOID(WM_WININICHANGE, OnDisplayChange)
  64.     ON_MESSAGE_VOID(WM_SETTINGCHANGE, OnDisplayChange)
  65.     ON_WM_MOUSEWHEEL()
  66.     //}}AFX_MSG_MAP
  67. END_MESSAGE_MAP()
  68.  
  69. /////////////////////////////////////////////////////////////////////////////
  70. // CSplitterWnd construction/destruction
  71.  
  72. CSplitterWnd::CSplitterWnd()
  73. {
  74.     AFX_ZERO_INIT_OBJECT(CWnd);
  75.  
  76.     // default splitter box/bar sizes (includes borders)
  77.     if (!afxData.bWin4)
  78.     {
  79.         m_cxSplitter = m_cySplitter = 4;
  80.         m_cxBorderShare = m_cyBorderShare = 1;
  81.         m_cxSplitterGap = m_cySplitterGap = 4 + 1 + 1;
  82.         ASSERT(m_cxBorder == 0 && m_cyBorder == 0);
  83.     }
  84.     else
  85.     {
  86.         m_cxSplitter = m_cySplitter = 3 + 2 + 2;
  87.         m_cxBorderShare = m_cyBorderShare = 0;
  88.         m_cxSplitterGap = m_cySplitterGap = 3 + 2 + 2;
  89.         m_cxBorder = m_cyBorder = 2;
  90.     }
  91.  
  92. #ifdef _DEBUG
  93.     if (GetSystemMetrics(SM_CXBORDER) != 1 ||
  94.         GetSystemMetrics(SM_CYBORDER) != 1)
  95.     {
  96.         TRACE0("Warning: CSplitterWnd assumes 1 pixel border.\n");
  97.         // will look ugly if borders are not 1 pixel wide and 1 pixel high
  98.     }
  99. #endif
  100. }
  101.  
  102. CSplitterWnd::~CSplitterWnd()
  103. {
  104.     delete[] m_pRowInfo;
  105.     delete[] m_pColInfo;
  106. }
  107.  
  108. BOOL CSplitterWnd::Create(CWnd* pParentWnd,
  109.     int nMaxRows, int nMaxCols, SIZE sizeMin,
  110.     CCreateContext* pContext, DWORD dwStyle, UINT nID)
  111. {
  112.     ASSERT(pParentWnd != NULL);
  113.     ASSERT(sizeMin.cx > 0 && sizeMin.cy > 0);   // minimum must be non-zero
  114.  
  115.     ASSERT(pContext != NULL);
  116.     ASSERT(pContext->m_pNewViewClass != NULL);
  117.     ASSERT(dwStyle & WS_CHILD);
  118.     ASSERT(dwStyle & SPLS_DYNAMIC_SPLIT);   // must have dynamic split behavior
  119.  
  120.     // Dynamic splitters are limited to 2x2
  121.     ASSERT(nMaxRows >= 1 && nMaxRows <= 2);
  122.     ASSERT(nMaxCols >= 1 && nMaxCols <= 2);
  123.     ASSERT(nMaxCols > 1 || nMaxRows > 1);       // 1x1 is not permitted
  124.  
  125.     m_nMaxRows = nMaxRows;
  126.     m_nMaxCols = nMaxCols;
  127.     ASSERT(m_nRows == 0 && m_nCols == 0);       // none yet
  128.     m_nRows = m_nCols = 1;      // start off as 1x1
  129.     if (!CreateCommon(pParentWnd, sizeMin, dwStyle, nID))
  130.         return FALSE;
  131.     ASSERT(m_nRows == 1 && m_nCols == 1);       // still 1x1
  132.  
  133.     ASSERT(pContext->m_pNewViewClass->IsDerivedFrom(RUNTIME_CLASS(CWnd)));
  134.     m_pDynamicViewClass = pContext->m_pNewViewClass;
  135.         // save for later dynamic creations
  136.  
  137.     // add the first initial pane
  138.     if (!CreateView(0, 0, m_pDynamicViewClass, sizeMin, pContext))
  139.     {
  140.         DestroyWindow(); // will clean up child windows
  141.         return FALSE;
  142.     }
  143.     m_pColInfo[0].nIdealSize = sizeMin.cx;
  144.     m_pRowInfo[0].nIdealSize = sizeMin.cy;
  145.  
  146.     return TRUE;
  147. }
  148.  
  149. // simple "wiper" splitter
  150. BOOL CSplitterWnd::CreateStatic(CWnd* pParentWnd,
  151.     int nRows, int nCols, DWORD dwStyle, UINT nID)
  152. {
  153.     ASSERT(pParentWnd != NULL);
  154.     ASSERT(nRows >= 1 && nRows <= 16);
  155.     ASSERT(nCols >= 1 && nCols <= 16);
  156.     ASSERT(nCols > 1 || nRows > 1);     // 1x1 is not permitted
  157.     ASSERT(dwStyle & WS_CHILD);
  158.     ASSERT(!(dwStyle & SPLS_DYNAMIC_SPLIT)); // can't have dynamic split
  159.  
  160.     ASSERT(m_nRows == 0 && m_nCols == 0);       // none yet
  161.     m_nRows = m_nMaxRows = nRows;
  162.     m_nCols = m_nMaxCols = nCols;
  163.  
  164.     // create with zero minimum pane size
  165.     if (!CreateCommon(pParentWnd, CSize(0, 0), dwStyle, nID))
  166.         return FALSE;
  167.  
  168.     // all panes must be created with explicit calls to CreateView
  169.     return TRUE;
  170. }
  171.  
  172. BOOL CSplitterWnd::CreateCommon(CWnd* pParentWnd,
  173.     SIZE sizeMin, DWORD dwStyle, UINT nID)
  174. {
  175.     ASSERT(pParentWnd != NULL);
  176.     ASSERT(sizeMin.cx >= 0 && sizeMin.cy >= 0);
  177.     ASSERT(dwStyle & WS_CHILD);
  178.     ASSERT(nID != 0);
  179.  
  180.     ASSERT(m_pColInfo == NULL && m_pRowInfo == NULL);   // only do once
  181.     ASSERT(m_nMaxCols > 0 && m_nMaxRows > 0);
  182.  
  183.     // the Windows scroll bar styles bits turn on the smart scrollbars
  184.     DWORD dwCreateStyle = dwStyle & ~(WS_HSCROLL|WS_VSCROLL);
  185.     if (afxData.bWin4)
  186.         dwCreateStyle &= ~WS_BORDER;
  187.  
  188.     if (!AfxDeferRegisterClass(AFX_WNDMDIFRAME_REG))
  189.         return FALSE;
  190.  
  191.     // create with the same wnd-class as MDI-Frame (no erase bkgnd)
  192.     if (!CreateEx(0, _afxWndMDIFrame, NULL, dwCreateStyle, 0, 0, 0, 0,
  193.       pParentWnd->m_hWnd, (HMENU)nID, NULL))
  194.         return FALSE;       // create invisible
  195.  
  196.     // attach the initial splitter parts
  197.     TRY
  198.     {
  199.         m_pColInfo = new CRowColInfo[m_nMaxCols];
  200.         for (int col = 0; col < m_nMaxCols; col++)
  201.         {
  202.             m_pColInfo[col].nMinSize = m_pColInfo[col].nIdealSize = sizeMin.cx;
  203.             m_pColInfo[col].nCurSize = -1; // will be set in RecalcLayout
  204.         }
  205.         m_pRowInfo = new CRowColInfo[m_nMaxRows];
  206.         for (int row = 0; row < m_nMaxRows; row++)
  207.         {
  208.             m_pRowInfo[row].nMinSize = m_pRowInfo[row].nIdealSize = sizeMin.cy;
  209.             m_pRowInfo[row].nCurSize = -1; // will be set in RecalcLayout
  210.         }
  211.  
  212.         // create scroll bars by setting the style
  213.         SetScrollStyle(dwStyle);
  214.     }
  215.     CATCH_ALL(e)
  216.     {
  217.         DestroyWindow(); // will clean up child windows
  218.         // Note: DELETE_EXCEPTION(e) not required
  219.         return FALSE;
  220.     }
  221.     END_CATCH_ALL
  222.  
  223.     return TRUE;
  224. }
  225.  
  226. BOOL CSplitterWnd::OnNcCreate(LPCREATESTRUCT lpcs)
  227. {
  228.     if (!CWnd::OnNcCreate(lpcs))
  229.         return FALSE;
  230.  
  231.     // remove WS_EX_CLIENTEDGE style from parent window
  232.     //  (the splitter itself will provide the 3d look)
  233.     CWnd* pParent = GetParent();
  234.     ASSERT_VALID(pParent);
  235.     pParent->ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_DRAWFRAME);
  236.  
  237.     return TRUE;
  238. }
  239.  
  240. /////////////////////////////////////////////////////////////////////////////
  241. // CSplitterWnd default creation of parts
  242.  
  243. // You must create ALL panes unless DYNAMIC_SPLIT is defined!
  244. //  Usually the splitter window is invisible when creating a pane
  245. BOOL CSplitterWnd::CreateView(int row, int col,
  246.     CRuntimeClass* pViewClass, SIZE sizeInit, CCreateContext* pContext)
  247. {
  248. #ifdef _DEBUG
  249.     ASSERT_VALID(this);
  250.     ASSERT(row >= 0 && row < m_nRows);
  251.     ASSERT(col >= 0 && col < m_nCols);
  252.     ASSERT(pViewClass != NULL);
  253.     ASSERT(pViewClass->IsDerivedFrom(RUNTIME_CLASS(CWnd)));
  254.     ASSERT(AfxIsValidAddress(pViewClass, sizeof(CRuntimeClass), FALSE));
  255.  
  256.     if (GetDlgItem(IdFromRowCol(row, col)) != NULL)
  257.     {
  258.         TRACE2("Error: CreateView - pane already exists for row %d, col %d.\n",
  259.             row, col);
  260.         ASSERT(FALSE);
  261.         return FALSE;
  262.     }
  263. #endif
  264.  
  265.     // set the initial size for that pane
  266.     m_pColInfo[col].nIdealSize = sizeInit.cx;
  267.     m_pRowInfo[row].nIdealSize = sizeInit.cy;
  268.  
  269.     BOOL bSendInitialUpdate = FALSE;
  270.  
  271.     CCreateContext contextT;
  272.     if (pContext == NULL)
  273.     {
  274.         // if no context specified, generate one from the currently selected
  275.         //  client if possible
  276.         CView* pOldView = (CView*)GetActivePane();
  277.         if (pOldView != NULL && pOldView->IsKindOf(RUNTIME_CLASS(CView)))
  278.         {
  279.             // set info about last pane
  280.             ASSERT(contextT.m_pCurrentFrame == NULL);
  281.             contextT.m_pLastView = pOldView;
  282.             contextT.m_pCurrentDoc = pOldView->GetDocument();
  283.             if (contextT.m_pCurrentDoc != NULL)
  284.                 contextT.m_pNewDocTemplate =
  285.                   contextT.m_pCurrentDoc->GetDocTemplate();
  286.         }
  287.         pContext = &contextT;
  288.         bSendInitialUpdate = TRUE;
  289.     }
  290.  
  291.     CWnd* pWnd;
  292.     TRY
  293.     {
  294.         pWnd = (CWnd*)pViewClass->CreateObject();
  295.         if (pWnd == NULL)
  296.             AfxThrowMemoryException();
  297.     }
  298.     CATCH_ALL(e)
  299.     {
  300.         TRACE0("Out of memory creating a splitter pane.\n");
  301.         // Note: DELETE_EXCEPTION(e) not required
  302.         return FALSE;
  303.     }
  304.     END_CATCH_ALL
  305.  
  306.     ASSERT_KINDOF(CWnd, pWnd);
  307.     ASSERT(pWnd->m_hWnd == NULL);       // not yet created
  308.  
  309.     DWORD dwStyle = AFX_WS_DEFAULT_VIEW;
  310.     if (afxData.bWin4)
  311.         dwStyle &= ~WS_BORDER;
  312.  
  313.     // Create with the right size (wrong position)
  314.     CRect rect(CPoint(0,0), sizeInit);
  315.     if (!pWnd->Create(NULL, NULL, dwStyle,
  316.         rect, this, IdFromRowCol(row, col), pContext))
  317.     {
  318.         TRACE0("Warning: couldn't create client pane for splitter.\n");
  319.             // pWnd will be cleaned up by PostNcDestroy
  320.         return FALSE;
  321.     }
  322.     ASSERT((int)_AfxGetDlgCtrlID(pWnd->m_hWnd) == IdFromRowCol(row, col));
  323.  
  324.     // send initial notification message
  325.     if (bSendInitialUpdate)
  326.         pWnd->SendMessage(WM_INITIALUPDATE);
  327.  
  328.     return TRUE;
  329. }
  330.  
  331. BOOL CSplitterWnd::CreateScrollBarCtrl(DWORD dwStyle, UINT nID)
  332. {
  333.     ASSERT_VALID(this);
  334.     ASSERT(m_hWnd != NULL);
  335.  
  336.     return (::CreateWindow(_T("SCROLLBAR"), NULL,
  337.         dwStyle | WS_VISIBLE | WS_CHILD,
  338.         0, 0, 1, 1, m_hWnd, (HMENU)nID,
  339.         AfxGetInstanceHandle(), NULL) != NULL);
  340. }
  341.  
  342. int CSplitterWnd::IdFromRowCol(int row, int col) const
  343. {
  344.     ASSERT_VALID(this);
  345.     ASSERT(row >= 0);
  346.     ASSERT(row < m_nRows);
  347.     ASSERT(col >= 0);
  348.     ASSERT(col < m_nCols);
  349.  
  350.     return AFX_IDW_PANE_FIRST + row * 16 + col;
  351. }
  352.  
  353. /////////////////////////////////////////////////////////////////////////////
  354. // CSplitterWnd attributes
  355.  
  356. CWnd* CSplitterWnd::GetPane(int row, int col) const
  357. {
  358.     ASSERT_VALID(this);
  359.  
  360.     CWnd* pView = GetDlgItem(IdFromRowCol(row, col));
  361.     ASSERT(pView != NULL);  // panes can be a CWnd, but are usually CViews
  362.     return pView;
  363. }
  364.  
  365. BOOL CSplitterWnd::IsChildPane(CWnd* pWnd, int* pRow, int* pCol)
  366. {
  367.     ASSERT_VALID(this);
  368.     ASSERT_VALID(pWnd);
  369.  
  370.     UINT nID = _AfxGetDlgCtrlID(pWnd->m_hWnd);
  371.     if (IsChild(pWnd) && nID >= AFX_IDW_PANE_FIRST && nID <= AFX_IDW_PANE_LAST)
  372.     {
  373.         if (pRow != NULL)
  374.             *pRow = (nID - AFX_IDW_PANE_FIRST) / 16;
  375.         if (pCol != NULL)
  376.             *pCol = (nID - AFX_IDW_PANE_FIRST) % 16;
  377.         ASSERT(pRow == NULL || *pRow < m_nRows);
  378.         ASSERT(pCol == NULL || *pCol < m_nCols);
  379.         return TRUE;
  380.     }
  381.     else
  382.     {
  383.         if (pRow != NULL)
  384.             *pRow = -1;
  385.         if (pCol != NULL)
  386.             *pCol = -1;
  387.         return FALSE;
  388.     }
  389. }
  390.  
  391. /////////////////////////////////////////////////////////////////////////////
  392. // CSplitterWnd information access
  393.  
  394. // The get routines return the current size
  395. // The set routines set the ideal size
  396. //  RecalcLayout must be called to update current size
  397.  
  398. void CSplitterWnd::GetRowInfo(int row, int& cyCur, int& cyMin) const
  399. {
  400.     ASSERT_VALID(this);
  401.     ASSERT(row >= 0 && row < m_nMaxRows);
  402.  
  403.     cyCur = m_pRowInfo[row].nCurSize;
  404.     cyMin = m_pRowInfo[row].nMinSize;
  405. }
  406.  
  407. void CSplitterWnd::SetRowInfo(int row, int cyIdeal, int cyMin)
  408. {
  409.     ASSERT_VALID(this);
  410.     ASSERT(row >= 0 && row < m_nMaxRows);
  411.     ASSERT(cyIdeal >= 0);
  412.     ASSERT(cyMin >= 0);
  413.  
  414.     m_pRowInfo[row].nIdealSize = cyIdeal;
  415.     m_pRowInfo[row].nMinSize = cyMin;
  416. }
  417.  
  418. void CSplitterWnd::GetColumnInfo(int col, int& cxCur, int& cxMin) const
  419. {
  420.     ASSERT_VALID(this);
  421.     ASSERT(col >= 0 && col < m_nMaxCols);
  422.  
  423.     cxCur = m_pColInfo[col].nCurSize;
  424.     cxMin = m_pColInfo[col].nMinSize;
  425. }
  426.  
  427. void CSplitterWnd::SetColumnInfo(int col, int cxIdeal, int cxMin)
  428. {
  429.     ASSERT_VALID(this);
  430.     ASSERT(col >= 0 && col < m_nMaxCols);
  431.     ASSERT(cxIdeal >= 0);
  432.     ASSERT(cxMin >= 0);
  433.  
  434.     m_pColInfo[col].nIdealSize = cxIdeal;
  435.     m_pColInfo[col].nMinSize = cxMin;
  436. }
  437.  
  438. DWORD CSplitterWnd::GetScrollStyle() const
  439. {
  440.     DWORD dwStyle = 0;
  441.     if (m_bHasHScroll)
  442.         dwStyle |= WS_HSCROLL;
  443.     if (m_bHasVScroll)
  444.         dwStyle |= WS_VSCROLL;
  445.     return dwStyle;
  446. }
  447.  
  448. void CSplitterWnd::SetScrollStyle(DWORD dwStyle)
  449. {
  450.     // optimize for scroll info already set correctly
  451.     dwStyle &= (WS_HSCROLL|WS_VSCROLL);
  452.     if (GetScrollStyle() == dwStyle)
  453.         return;
  454.  
  455.     // update to new state
  456.     m_bHasHScroll = (dwStyle & WS_HSCROLL) != 0;
  457.     m_bHasVScroll = (dwStyle & WS_VSCROLL) != 0;
  458.  
  459.     CWnd* pScrollBar;
  460.  
  461.     // show/hide all the shared horz scroll bars
  462.     for (int col = 0; col < m_nCols; col++)
  463.     {
  464.         pScrollBar = GetDlgItem(AFX_IDW_HSCROLL_FIRST + col);
  465.         if (pScrollBar == NULL)
  466.         {
  467.             // create the scroll bar when necessary
  468.             if (!CreateScrollBarCtrl(SBS_HORZ, AFX_IDW_HSCROLL_FIRST + col))
  469.                 AfxThrowResourceException();
  470.             pScrollBar = GetDlgItem(AFX_IDW_HSCROLL_FIRST + col);
  471.         }
  472.         pScrollBar->ShowWindow(m_bHasHScroll ? SW_SHOW : SW_HIDE);
  473.     }
  474.  
  475.     // show/hide all the shared vert scroll bars
  476.     for (int row = 0; row < m_nRows; row++)
  477.     {
  478.         pScrollBar = GetDlgItem(AFX_IDW_VSCROLL_FIRST + row);
  479.         if (pScrollBar == NULL)
  480.         {
  481.             // create the scroll bar when necessary
  482.             if (!CreateScrollBarCtrl(SBS_VERT, AFX_IDW_VSCROLL_FIRST + row))
  483.                 AfxThrowResourceException();
  484.             pScrollBar = GetDlgItem(AFX_IDW_VSCROLL_FIRST + row);
  485.         }
  486.         pScrollBar->ShowWindow(m_bHasVScroll ? SW_SHOW : SW_HIDE);
  487.     }
  488.  
  489.     // show/destroy size box if necessary
  490.     if (m_bHasVScroll && m_bHasHScroll)
  491.     {
  492.         pScrollBar = GetDlgItem(AFX_IDW_SIZE_BOX);
  493.         if (pScrollBar == NULL)
  494.         {
  495.             // create size box when necessary
  496.             if (!CreateScrollBarCtrl(SBS_SIZEBOX|WS_DISABLED, AFX_IDW_SIZE_BOX))
  497.                 AfxThrowResourceException();
  498.             pScrollBar = GetDlgItem(AFX_IDW_SIZE_BOX);
  499.         }
  500.         pScrollBar->ShowWindow(SW_SHOW);
  501.     }
  502.     else
  503.     {
  504.         // the size box can be destroyed instead of hidden
  505.         pScrollBar = GetDlgItem(AFX_IDW_SIZE_BOX);
  506.         if (pScrollBar != NULL)
  507.             pScrollBar->DestroyWindow();
  508.     }
  509.  
  510.     // Note: call RecalcLayout for the new layout to take effect
  511. }
  512.  
  513. /////////////////////////////////////////////////////////////////////////////
  514. // CSplitterWnd client operations/overridables
  515.  
  516. void CSplitterWnd::DeleteView(int row, int col)
  517. {
  518.     ASSERT_VALID(this);
  519.  
  520.     // if active child is being deleted - activate next
  521.     CWnd* pPane = GetPane(row, col);
  522.     ASSERT_KINDOF(CView, pPane);
  523.     if (GetActivePane() == pPane)
  524.         ActivateNext(FALSE);
  525.  
  526.     // default implementation assumes view will auto delete in PostNcDestroy
  527.     pPane->DestroyWindow();
  528. }
  529.  
  530. void CSplitterWnd::OnDrawSplitter(CDC* pDC, ESplitType nType,
  531.     const CRect& rectArg)
  532. {
  533.     // if pDC == NULL, then just invalidate
  534.     if (pDC == NULL)
  535.     {
  536.         RedrawWindow(rectArg, NULL, RDW_INVALIDATE|RDW_NOCHILDREN);
  537.         return;
  538.     }
  539.     ASSERT_VALID(pDC);
  540.  
  541.     // otherwise, actually draw
  542.     CRect rect = rectArg;
  543.     switch (nType)
  544.     {
  545.     case splitBorder:
  546.         ASSERT(afxData.bWin4);
  547.         pDC->Draw3dRect(rect, afxData.clrBtnShadow, afxData.clrBtnHilite);
  548.         rect.InflateRect(-CX_BORDER, -CY_BORDER);
  549.         pDC->Draw3dRect(rect, afxData.clrWindowFrame, afxData.clrBtnFace);
  550.         return;
  551.  
  552.     case splitIntersection:
  553.         ASSERT(!afxData.bWin4);
  554.         break;
  555.  
  556.     case splitBox:
  557.         if (afxData.bWin4)
  558.         {
  559.             pDC->Draw3dRect(rect, afxData.clrBtnFace, afxData.clrWindowFrame);
  560.             rect.InflateRect(-CX_BORDER, -CY_BORDER);
  561.             pDC->Draw3dRect(rect, afxData.clrBtnHilite, afxData.clrBtnShadow);
  562.             rect.InflateRect(-CX_BORDER, -CY_BORDER);
  563.             break;
  564.         }
  565.         // fall through...
  566.     case splitBar:
  567.         if (!afxData.bWin4)
  568.         {
  569.             pDC->Draw3dRect(rect, afxData.clrBtnHilite, afxData.clrBtnShadow);
  570.             rect.InflateRect(-CX_BORDER, -CY_BORDER);
  571.         }
  572.         break;
  573.  
  574.     default:
  575.         ASSERT(FALSE);  // unknown splitter type
  576.     }
  577.  
  578.     // fill the middle
  579.     COLORREF clr = afxData.clrBtnFace;
  580. #ifdef _MAC
  581.     // just use white for interior if less than 16 colors
  582.     if (pDC->GetDeviceCaps(NUMCOLORS) < 16)
  583.         clr = RGB(0xFF, 0xFF, 0xFF);
  584. #endif
  585.     pDC->FillSolidRect(rect, clr);
  586. }
  587.  
  588. /////////////////////////////////////////////////////////////////////////////
  589. // Dynamic row/col split etc
  590.  
  591. static int AFXAPI CanSplitRowCol(CSplitterWnd::CRowColInfo* pInfoBefore,
  592.     int nBeforeSize, int nSizeSplitter)
  593.     // request to split Before row at point nBeforeSize
  594.     // returns size of new pane (nBeforeSize will be new size of Before pane)
  595.     // return -1 if not big enough
  596. {
  597.     ASSERT(pInfoBefore->nCurSize > 0);
  598.     ASSERT(pInfoBefore->nMinSize > 0);
  599.     ASSERT(nBeforeSize <= pInfoBefore->nCurSize);
  600.  
  601.     // space gets take from before pane (weird UI for > 2 splits)
  602.     if (nBeforeSize < pInfoBefore->nMinSize)
  603.     {
  604.         TRACE0("Warning: split too small to fit in a new pane.\n");
  605.         return -1;
  606.     }
  607.  
  608.     int nNewSize = pInfoBefore->nCurSize - nBeforeSize - nSizeSplitter;
  609.     if (nBeforeSize < pInfoBefore->nMinSize)
  610.     {
  611.         TRACE0("Warning: split too small to shrink old pane.\n");
  612.         return -1;
  613.     }
  614.     if (nNewSize < (pInfoBefore+1)->nMinSize)
  615.     {
  616.         TRACE0("Warning: split too small to create new pane.\n");
  617.         return -1;
  618.     }
  619.     return nNewSize;
  620. }
  621.  
  622. BOOL CSplitterWnd::SplitRow(int cyBefore)
  623. {
  624.     ASSERT_VALID(this);
  625.     ASSERT(GetStyle() & SPLS_DYNAMIC_SPLIT);
  626.     ASSERT(m_pDynamicViewClass != NULL);
  627.     ASSERT(m_nRows < m_nMaxRows);
  628.  
  629.     cyBefore -= m_cyBorder;
  630.     int rowNew = m_nRows;
  631.     int cyNew = CanSplitRowCol(&m_pRowInfo[rowNew-1], cyBefore, m_cySplitter);
  632.     if (cyNew == -1)
  633.         return FALSE;   // too small to split
  634.  
  635.     // create the scroll bar first (so new views can see that it is there)
  636.     if (m_bHasVScroll &&
  637.         !CreateScrollBarCtrl(SBS_VERT, AFX_IDW_VSCROLL_FIRST + rowNew))
  638.     {
  639.         TRACE0("Warning: SplitRow failed to create scroll bar.\n");
  640.         return FALSE;
  641.     }
  642.  
  643.     m_nRows++;  // bump count during view creation
  644.  
  645.     // create new views to fill the new row (RecalcLayout will position)
  646.     for (int col = 0; col < m_nCols; col++)
  647.     {
  648.         CSize size(m_pColInfo[col].nCurSize, cyNew);
  649.         if (!CreateView(rowNew, col, m_pDynamicViewClass, size, NULL))
  650.         {
  651.             TRACE0("Warning: SplitRow failed to create new row.\n");
  652.             // delete anything we partially created 'col' = # columns created
  653.             while (col > 0)
  654.                 DeleteView(rowNew, --col);
  655.             if (m_bHasVScroll)
  656.                 GetDlgItem(AFX_IDW_VSCROLL_FIRST + rowNew)->DestroyWindow();
  657.             m_nRows--;      // it didn't work out
  658.             return FALSE;
  659.         }
  660.     }
  661.  
  662.     // new parts created - resize and re-layout
  663.     m_pRowInfo[rowNew-1].nIdealSize = cyBefore;
  664.     m_pRowInfo[rowNew].nIdealSize = cyNew;
  665.     ASSERT(m_nRows == rowNew+1);
  666.     RecalcLayout();
  667.  
  668.     return TRUE;
  669. }
  670.  
  671. BOOL CSplitterWnd::SplitColumn(int cxBefore)
  672. {
  673.     ASSERT_VALID(this);
  674.     ASSERT(GetStyle() & SPLS_DYNAMIC_SPLIT);
  675.     ASSERT(m_pDynamicViewClass != NULL);
  676.     ASSERT(m_nCols < m_nMaxCols);
  677.  
  678.     cxBefore -= m_cxBorder;
  679.     int colNew = m_nCols;
  680.     int cxNew = CanSplitRowCol(&m_pColInfo[colNew-1], cxBefore, m_cxSplitter);
  681.     if (cxNew == -1)
  682.         return FALSE;   // too small to split
  683.  
  684.     // create the scroll bar first (so new views can see that it is there)
  685.     if (m_bHasHScroll &&
  686.         !CreateScrollBarCtrl(SBS_HORZ, AFX_IDW_HSCROLL_FIRST + colNew))
  687.     {
  688.         TRACE0("Warning: SplitRow failed to create scroll bar.\n");
  689.         return FALSE;
  690.     }
  691.  
  692.     m_nCols++;  // bump count during view creation
  693.  
  694.     // create new views to fill the new column (RecalcLayout will position)
  695.     for (int row = 0; row < m_nRows; row++)
  696.     {
  697.         CSize size(cxNew, m_pRowInfo[row].nCurSize);
  698.         if (!CreateView(row, colNew, m_pDynamicViewClass, size, NULL))
  699.         {
  700.             TRACE0("Warning: SplitColumn failed to create new column.\n");
  701.             // delete anything we partially created 'col' = # columns created
  702.             while (row > 0)
  703.                 DeleteView(--row, colNew);
  704.             if (m_bHasHScroll)
  705.                 GetDlgItem(AFX_IDW_HSCROLL_FIRST + colNew)->DestroyWindow();
  706.             m_nCols--;      // it didn't work out
  707.             return FALSE;
  708.         }
  709.     }
  710.  
  711.     // new parts created - resize and re-layout
  712.     m_pColInfo[colNew-1].nIdealSize = cxBefore;
  713.     m_pColInfo[colNew].nIdealSize = cxNew;
  714.     ASSERT(m_nCols == colNew+1);
  715.     RecalcLayout();
  716.  
  717.     return TRUE;
  718. }
  719.  
  720. void CSplitterWnd::DeleteRow(int rowDelete)
  721. {
  722.     ASSERT_VALID(this);
  723.     ASSERT(GetStyle() & SPLS_DYNAMIC_SPLIT);
  724.  
  725.     ASSERT(m_nRows > 1);
  726.     ASSERT(rowDelete < m_nRows);
  727.  
  728.     int rowActive, colActive;
  729.     if (GetActivePane(&rowActive, &colActive) != NULL && rowActive == rowDelete)
  730.     {
  731.         if (++rowActive >= m_nRows)
  732.             rowActive = 0;
  733.         SetActivePane(rowActive, colActive);
  734.     }
  735.  
  736.     CWnd* pScrollDel = m_bHasVScroll ?
  737.         GetDlgItem(AFX_IDW_VSCROLL_FIRST+rowDelete) : NULL;
  738.     for (int col = 0; col < m_nCols; col++)
  739.     {
  740.         DeleteView(rowDelete, col);
  741.         for (int row = rowDelete+1; row < m_nRows; row++)
  742.         {
  743.             CWnd* pPane = GetPane(row, col);
  744.             ASSERT(pPane != NULL);
  745.             pPane->SetDlgCtrlID(IdFromRowCol(row-1, col));
  746.             if (m_bHasVScroll && col == m_nCols-1)
  747.             {
  748.                 CWnd* pScroll = GetDlgItem(AFX_IDW_VSCROLL_FIRST+row);
  749.                 if (pScroll != NULL)
  750.                     pScroll->SetDlgCtrlID(AFX_IDW_VSCROLL_FIRST+row-1);
  751.             }
  752.         }
  753.     }
  754.     m_nRows--;
  755.     if (pScrollDel != NULL)
  756.         pScrollDel->DestroyWindow();
  757.  
  758.     RecalcLayout();     // re-assign the space
  759. }
  760.  
  761. void CSplitterWnd::DeleteColumn(int colDelete)
  762. {
  763.     ASSERT_VALID(this);
  764.     ASSERT(GetStyle() & SPLS_DYNAMIC_SPLIT);
  765.  
  766.     ASSERT(m_nCols > 1);
  767.     ASSERT(colDelete < m_nCols);
  768.  
  769.     int rowActive, colActive;
  770.     if (GetActivePane(&rowActive, &colActive) != NULL && colActive == colDelete)
  771.     {
  772.         if (++colActive >= m_nCols)
  773.             colActive = 0;
  774.         SetActivePane(rowActive, colActive);
  775.     }
  776.  
  777.     CWnd* pScrollDel = m_bHasHScroll ?
  778.         GetDlgItem(AFX_IDW_HSCROLL_FIRST+colDelete) : NULL;
  779.     for (int row = 0; row < m_nRows; row++)
  780.     {
  781.         DeleteView(row, colDelete);
  782.         for (int col = colDelete+1; col < m_nCols; col++)
  783.         {
  784.             CWnd* pPane = GetPane(row, col);
  785.             ASSERT(pPane != NULL);
  786.             pPane->SetDlgCtrlID(IdFromRowCol(row, col-1));
  787.             if (m_bHasHScroll && row == m_nRows-1)
  788.             {
  789.                 CWnd* pScroll = GetDlgItem(AFX_IDW_HSCROLL_FIRST+col);
  790.                 if (pScroll != NULL)
  791.                     pScroll->SetDlgCtrlID(AFX_IDW_HSCROLL_FIRST+col-1);
  792.             }
  793.         }
  794.     }
  795.     m_nCols--;
  796.     if (pScrollDel != NULL)
  797.         pScrollDel->DestroyWindow();
  798.  
  799.     RecalcLayout();     // re-assign the space
  800. }
  801.  
  802. /////////////////////////////////////////////////////////////////////////////
  803. // CSplitterWnd tracking support
  804.  
  805. // like GetClientRect but inset by shared scrollbars
  806. void CSplitterWnd::GetInsideRect(CRect& rect) const
  807. {
  808.     ASSERT_VALID(this);
  809.  
  810.     GetClientRect(rect);
  811.     ASSERT(rect.left == 0 && rect.top == 0);
  812.  
  813.     // subtract space for 3d borders
  814.     rect.InflateRect(-m_cxBorder, -m_cyBorder);
  815.  
  816.     // subtract scrollbar clearance
  817.     if (m_bHasVScroll)
  818.         rect.right -= afxData.cxVScroll - CX_BORDER;
  819.     if (m_bHasHScroll)
  820.         rect.bottom -= afxData.cyHScroll - CY_BORDER;
  821. }
  822.  
  823. void CSplitterWnd::StartTracking(int ht)
  824. {
  825.     ASSERT_VALID(this);
  826.     if (ht == noHit)
  827.         return;
  828.  
  829.     // GetHitRect will restrict 'm_rectLimit' as appropriate
  830.     GetInsideRect(m_rectLimit);
  831.  
  832.     if (ht >= splitterIntersection1 && ht <= splitterIntersection225)
  833.     {
  834.         // split two directions (two tracking rectangles)
  835.         int row = (ht - splitterIntersection1) / 15;
  836.         int col = (ht - splitterIntersection1) % 15;
  837.  
  838.         GetHitRect(row + vSplitterBar1, m_rectTracker);
  839.         int yTrackOffset = m_ptTrackOffset.y;
  840.         m_bTracking2 = TRUE;
  841.         GetHitRect(col + hSplitterBar1, m_rectTracker2);
  842.         m_ptTrackOffset.y = yTrackOffset;
  843.     }
  844.     else if (ht == bothSplitterBox)
  845.     {
  846.         // hit on splitter boxes (for keyboard)
  847.         GetHitRect(vSplitterBox, m_rectTracker);
  848.         int yTrackOffset = m_ptTrackOffset.y;
  849.         m_bTracking2 = TRUE;
  850.         GetHitRect(hSplitterBox, m_rectTracker2);
  851.         m_ptTrackOffset.y = yTrackOffset;
  852.  
  853.         // center it
  854.         m_rectTracker.OffsetRect(0, m_rectLimit.Height()/2);
  855.         m_rectTracker2.OffsetRect(m_rectLimit.Width()/2, 0);
  856.     }
  857.     else
  858.     {
  859.         // only hit one bar
  860.         GetHitRect(ht, m_rectTracker);
  861.     }
  862.  
  863.     // allow active view to preserve focus before taking it away
  864.     CView* pView = (CView*)GetActivePane();
  865.     if (pView != NULL && pView->IsKindOf(RUNTIME_CLASS(CView)))
  866.     {
  867.         ASSERT_VALID(pView);
  868.         CFrameWnd* pFrameWnd = GetParentFrame();
  869.         ASSERT_VALID(pFrameWnd);
  870.         pView->OnActivateFrame(WA_INACTIVE, pFrameWnd);
  871.     }
  872.  
  873.     // steal focus and capture
  874.     SetCapture();
  875.     SetFocus();
  876.  
  877.     // make sure no updates are pending
  878.     RedrawWindow(NULL, NULL, RDW_ALLCHILDREN | RDW_UPDATENOW);
  879.  
  880.     // set tracking state and appropriate cursor
  881.     m_bTracking = TRUE;
  882.     OnInvertTracker(m_rectTracker);
  883.     if (m_bTracking2)
  884.         OnInvertTracker(m_rectTracker2);
  885.     m_htTrack = ht;
  886.     SetSplitCursor(ht);
  887. }
  888.  
  889. void CSplitterWnd::TrackRowSize(int y, int row)
  890. {
  891.     ASSERT_VALID(this);
  892.     ASSERT(m_nRows > 1);
  893.  
  894.     CPoint pt(0, y);
  895.     ClientToScreen(&pt);
  896.     GetPane(row, 0)->ScreenToClient(&pt);
  897.     m_pRowInfo[row].nIdealSize = pt.y;      // new size
  898.     if (pt.y < m_pRowInfo[row].nMinSize)
  899.     {
  900.         // resized too small
  901.         m_pRowInfo[row].nIdealSize = 0; // make it go away
  902.         if (GetStyle() & SPLS_DYNAMIC_SPLIT)
  903.             DeleteRow(row);
  904.     }
  905.     else if (m_pRowInfo[row].nCurSize + m_pRowInfo[row+1].nCurSize
  906.             < pt.y + m_pRowInfo[row+1].nMinSize)
  907.     {
  908.         // not enough room for other pane
  909.         if (GetStyle() & SPLS_DYNAMIC_SPLIT)
  910.             DeleteRow(row + 1);
  911.     }
  912. }
  913.  
  914. void CSplitterWnd::TrackColumnSize(int x, int col)
  915. {
  916.     ASSERT_VALID(this);
  917.     ASSERT(m_nCols > 1);
  918.  
  919.     CPoint pt(x, 0);
  920.     ClientToScreen(&pt);
  921.     GetPane(0, col)->ScreenToClient(&pt);
  922.     m_pColInfo[col].nIdealSize = pt.x;      // new size
  923.     if (pt.x < m_pColInfo[col].nMinSize)
  924.     {
  925.         // resized too small
  926.         m_pColInfo[col].nIdealSize = 0; // make it go away
  927.         if (GetStyle() & SPLS_DYNAMIC_SPLIT)
  928.             DeleteColumn(col);
  929.     }
  930.     else if (m_pColInfo[col].nCurSize + m_pColInfo[col+1].nCurSize
  931.             < pt.x + m_pColInfo[col+1].nMinSize)
  932.     {
  933.         // not enough room for other pane
  934.         if (GetStyle() & SPLS_DYNAMIC_SPLIT)
  935.             DeleteColumn(col + 1);
  936.     }
  937. }
  938.  
  939. void CSplitterWnd::StopTracking(BOOL bAccept)
  940. {
  941.     ASSERT_VALID(this);
  942.  
  943.     if (!m_bTracking)
  944.         return;
  945.  
  946.     ReleaseCapture();
  947.  
  948.     // erase tracker rectangle
  949.     OnInvertTracker(m_rectTracker);
  950.     if (m_bTracking2)
  951.         OnInvertTracker(m_rectTracker2);
  952.     m_bTracking = m_bTracking2 = FALSE;
  953.  
  954.     // save old active view
  955.     CWnd* pOldActiveView = GetActivePane();
  956.  
  957.     // m_rectTracker is set to the new splitter position (without border)
  958.     // (so, adjust relative to where the border will be)
  959.     m_rectTracker.OffsetRect(-CX_BORDER , -CY_BORDER);
  960.     m_rectTracker2.OffsetRect(-CX_BORDER, -CY_BORDER);
  961.  
  962.     if (bAccept)
  963.     {
  964.         if (m_htTrack == vSplitterBox)
  965.         {
  966.             SplitRow(m_rectTracker.top);
  967.         }
  968.         else if (m_htTrack >= vSplitterBar1 && m_htTrack <= vSplitterBar15)
  969.         {
  970.             // set row height
  971.             TrackRowSize(m_rectTracker.top, m_htTrack - vSplitterBar1);
  972.             RecalcLayout();
  973.         }
  974.         else if (m_htTrack == hSplitterBox)
  975.         {
  976.             SplitColumn(m_rectTracker.left);
  977.         }
  978.         else if (m_htTrack >= hSplitterBar1 && m_htTrack <= hSplitterBar15)
  979.         {
  980.             // set column width
  981.             TrackColumnSize(m_rectTracker.left, m_htTrack - hSplitterBar1);
  982.             RecalcLayout();
  983.         }
  984.         else if (m_htTrack >= splitterIntersection1 &&
  985.             m_htTrack <= splitterIntersection225)
  986.         {
  987.             // set row height and column width
  988.             int row = (m_htTrack - splitterIntersection1) / 15;
  989.             int col = (m_htTrack - splitterIntersection1) % 15;
  990.  
  991.             TrackRowSize(m_rectTracker.top, row);
  992.             TrackColumnSize(m_rectTracker2.left, col);
  993.             RecalcLayout();
  994.         }
  995.         else if (m_htTrack == bothSplitterBox)
  996.         {
  997.             // rectTracker is vSplitter (splits rows)
  998.             // rectTracker2 is hSplitter (splits cols)
  999.             SplitRow(m_rectTracker.top);
  1000.             SplitColumn(m_rectTracker2.left);
  1001.         }
  1002.     }
  1003.  
  1004.     if (pOldActiveView == GetActivePane())
  1005.     {
  1006.         if (pOldActiveView != NULL)
  1007.         {
  1008.             SetActivePane(-1, -1, pOldActiveView); // re-activate
  1009.             pOldActiveView->SetFocus(); // make sure focus is restored
  1010.         }
  1011.     }
  1012. }
  1013.  
  1014. void CSplitterWnd::GetHitRect(int ht, CRect& rectHit)
  1015. {
  1016.     ASSERT_VALID(this);
  1017.  
  1018.     CRect rectClient;
  1019.     GetClientRect(&rectClient);
  1020.     rectClient.InflateRect(-m_cxBorder, -m_cyBorder);
  1021.     int cx = rectClient.Width();
  1022.     int cy = rectClient.Height();
  1023.     int x = rectClient.top;
  1024.     int y = rectClient.left;
  1025.  
  1026.     // hit rectangle does not include border
  1027.     // m_rectLimit will be limited to valid tracking rect
  1028.     // m_ptTrackOffset will be set to appropriate tracking offset
  1029.     m_ptTrackOffset.x = 0;
  1030.     m_ptTrackOffset.y = 0;
  1031.  
  1032.     if (ht == vSplitterBox)
  1033.     {
  1034.         cy = m_cySplitter - (2*m_cyBorder - afxData.bWin4);
  1035.         m_ptTrackOffset.y = -(cy / 2);
  1036.         ASSERT(m_pRowInfo[0].nCurSize > 0);
  1037.         m_rectLimit.bottom -= cy;
  1038.     }
  1039.     else if (ht == hSplitterBox)
  1040.     {
  1041.         cx = m_cxSplitter - (2*m_cxBorder - afxData.bWin4);
  1042.         m_ptTrackOffset.x = -(cx / 2);
  1043.         ASSERT(m_pColInfo[0].nCurSize > 0);
  1044.         m_rectLimit.right -= cx;
  1045.     }
  1046.     else if (ht >= vSplitterBar1 && ht <= vSplitterBar15)
  1047.     {
  1048.         cy = m_cySplitter - (2*m_cyBorder - afxData.bWin4);
  1049.         m_ptTrackOffset.y = -(cy / 2);
  1050.         for (int row = 0; row < ht - vSplitterBar1; row++)
  1051.             y += m_pRowInfo[row].nCurSize + m_cySplitterGap;
  1052.         m_rectLimit.top = y;
  1053.         y += m_pRowInfo[row].nCurSize + m_cyBorderShare + afxData.bWin4;
  1054.         m_rectLimit.bottom -= cy;
  1055.     }
  1056.     else if (ht >= hSplitterBar1 && ht <= hSplitterBar15)
  1057.     {
  1058.         cx = m_cxSplitter - (2*m_cxBorder - afxData.bWin4);
  1059.         m_ptTrackOffset.x = -(cx / 2);
  1060.         for (int col = 0; col < ht - hSplitterBar1; col++)
  1061.             x += m_pColInfo[col].nCurSize + m_cxSplitterGap;
  1062.         m_rectLimit.left = x;
  1063.         x += m_pColInfo[col].nCurSize + m_cxBorderShare + afxData.bWin4;
  1064.         m_rectLimit.right -= cx;
  1065.     }
  1066.     else
  1067.     {
  1068.         TRACE1("Error: GetHitRect(%d): Not Found!\n", ht);
  1069.         ASSERT(FALSE);
  1070.     }
  1071.  
  1072.     rectHit.right = (rectHit.left = x) + cx;
  1073.     rectHit.bottom = (rectHit.top = y) + cy;
  1074. }
  1075.  
  1076. int CSplitterWnd::HitTest(CPoint pt) const
  1077. {
  1078.     ASSERT_VALID(this);
  1079.  
  1080.     CRect rectClient;
  1081.     GetClientRect(&rectClient);
  1082.     rectClient.InflateRect(-m_cxBorder, -m_cyBorder);
  1083.  
  1084.     CRect rectInside;
  1085.     GetInsideRect(rectInside);
  1086.  
  1087.     if (m_bHasVScroll && m_nRows < m_nMaxRows &&
  1088.         CRect(rectInside.right, rectClient.top, rectClient.right,
  1089.         rectClient.top + m_cySplitter - afxData.bWin4).PtInRect(pt))
  1090.     {
  1091.         return vSplitterBox;
  1092.     }
  1093.  
  1094.     if (m_bHasHScroll && m_nCols < m_nMaxCols &&
  1095.         CRect(rectClient.left, rectInside.bottom,
  1096.          rectClient.left + m_cxSplitter - afxData.bWin4,
  1097.          rectClient.bottom).PtInRect(pt))
  1098.     {
  1099.         return hSplitterBox;
  1100.     }
  1101.  
  1102.     // for hit detect, include the border of splitters
  1103.     CRect rect;
  1104.     rect = rectClient;
  1105.     for (int col = 0; col < m_nCols - 1; col++)
  1106.     {
  1107.         rect.left += m_pColInfo[col].nCurSize;
  1108.         rect.right = rect.left + m_cxSplitterGap;
  1109.         if (rect.PtInRect(pt))
  1110.             break;
  1111.         rect.left = rect.right;
  1112.     }
  1113.  
  1114.     rect = rectClient;
  1115.     for (int row = 0; row < m_nRows - 1; row++)
  1116.     {
  1117.         rect.top += m_pRowInfo[row].nCurSize;
  1118.         rect.bottom = rect.top + m_cySplitterGap;
  1119.         if (rect.PtInRect(pt))
  1120.             break;
  1121.         rect.top = rect.bottom;
  1122.     }
  1123.  
  1124.     // row and col set for hit splitter (if not hit will be past end)
  1125.     if (col != m_nCols - 1)
  1126.     {
  1127.         if (row != m_nRows - 1)
  1128.             return splitterIntersection1 + row * 15 + col;
  1129.         return hSplitterBar1 + col;
  1130.     }
  1131.  
  1132.     if (row != m_nRows - 1)
  1133.         return vSplitterBar1 + row;
  1134.  
  1135.     return noHit;
  1136. }
  1137.  
  1138. /////////////////////////////////////////////////////////////////////////////
  1139. // CSplitterWnd tracking visuals
  1140.  
  1141. void CSplitterWnd::OnInvertTracker(const CRect& rect)
  1142. {
  1143.     ASSERT_VALID(this);
  1144.     ASSERT(!rect.IsRectEmpty());
  1145.     ASSERT((GetStyle() & WS_CLIPCHILDREN) == 0);
  1146.  
  1147.     // pat-blt without clip children on
  1148.     CDC* pDC = GetDC();
  1149.     // invert the brush pattern (looks just like frame window sizing)
  1150.     CBrush* pBrush = CDC::GetHalftoneBrush();
  1151.     HBRUSH hOldBrush = NULL;
  1152.     if (pBrush != NULL)
  1153.         hOldBrush = (HBRUSH)SelectObject(pDC->m_hDC, pBrush->m_hObject);
  1154.     pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATINVERT);
  1155.     if (hOldBrush != NULL)
  1156.         SelectObject(pDC->m_hDC, hOldBrush);
  1157.     ReleaseDC(pDC);
  1158. }
  1159.  
  1160. /////////////////////////////////////////////////////////////////////////////
  1161. // CSplitterWnd commands
  1162.  
  1163. // Keyboard interface
  1164. BOOL CSplitterWnd::DoKeyboardSplit()
  1165. {
  1166.     ASSERT_VALID(this);
  1167.  
  1168.     int ht;
  1169.     if (m_nRows > 1 && m_nCols > 1)
  1170.         ht = splitterIntersection1; // split existing row+col
  1171.     else if (m_nRows > 1)
  1172.         ht = vSplitterBar1;         // split existing row
  1173.     else if (m_nCols > 1)
  1174.         ht = hSplitterBar1;         // split existing col
  1175.     else if (m_nMaxRows > 1 && m_nMaxCols > 1)
  1176.         ht = bothSplitterBox;       // we can split both
  1177.     else if (m_nMaxRows > 1)
  1178.         ht = vSplitterBox;          // we can split rows
  1179.     else if (m_nMaxCols > 1)
  1180.         ht = hSplitterBox;          // we can split columns
  1181.     else
  1182.         return FALSE;               // can't split
  1183.  
  1184.     // start tracking
  1185.     StartTracking(ht);
  1186.  
  1187. #ifndef _MAC
  1188.     CRect rect;
  1189.     rect.left = m_rectTracker.Width() / 2;
  1190.     rect.top = m_rectTracker.Height() / 2;
  1191.     if (m_ptTrackOffset.y != 0)
  1192.         rect.top = m_rectTracker.top;
  1193.     if (m_ptTrackOffset.x != 0)
  1194.         rect.left = m_bTracking2 ? m_rectTracker2.left :m_rectTracker.left;
  1195.     rect.OffsetRect(-m_ptTrackOffset.x, -m_ptTrackOffset.y);
  1196.     ClientToScreen(&rect);
  1197.     SetCursorPos(rect.left, rect.top);
  1198. #endif
  1199.  
  1200.     return TRUE;
  1201. }
  1202.  
  1203. /////////////////////////////////////////////////////////////////////////////
  1204. // Main drawing and layout
  1205.  
  1206. void CSplitterWnd::OnDisplayChange()
  1207. {
  1208.     if (!IsIconic() && IsWindowVisible())
  1209.         RecalcLayout();
  1210. }
  1211.  
  1212. void CSplitterWnd::OnSize(UINT nType, int cx, int cy)
  1213. {
  1214.     if (nType != SIZE_MINIMIZED && cx > 0 && cy > 0)
  1215.         RecalcLayout();
  1216.  
  1217.     CWnd::OnSize(nType, cx, cy);
  1218. }
  1219.  
  1220. // Generic routine:
  1221. //  for X direction: pInfo = m_pColInfo, nMax = m_nMaxCols, nSize = cx
  1222. //  for Y direction: pInfo = m_pRowInfo, nMax = m_nMaxRows, nSize = cy
  1223. static void AFXAPI LayoutRowCol(CSplitterWnd::CRowColInfo* pInfoArray,
  1224.     int nMax, int nSize, int nSizeSplitter)
  1225. {
  1226.     ASSERT(pInfoArray != NULL);
  1227.     ASSERT(nMax > 0);
  1228.     ASSERT(nSizeSplitter > 0);
  1229.  
  1230.     CSplitterWnd::CRowColInfo* pInfo;
  1231.     int i;
  1232.  
  1233.     if (nSize < 0)
  1234.         nSize = 0;  // if really too small, layout as zero size
  1235.  
  1236.     // start with ideal sizes
  1237.     for (i = 0, pInfo = pInfoArray; i < nMax-1; i++, pInfo++)
  1238.     {
  1239.         if (pInfo->nIdealSize < pInfo->nMinSize)
  1240.             pInfo->nIdealSize = 0;      // too small to see
  1241.         pInfo->nCurSize = pInfo->nIdealSize;
  1242.     }
  1243.     pInfo->nCurSize = INT_MAX;  // last row/column takes the rest
  1244.  
  1245.     for (i = 0, pInfo = pInfoArray; i < nMax; i++, pInfo++)
  1246.     {
  1247.         ASSERT(nSize >= 0);
  1248.         if (nSize == 0)
  1249.         {
  1250.             // no more room (set pane to be invisible)
  1251.             pInfo->nCurSize = 0;
  1252.             continue;       // don't worry about splitters
  1253.         }
  1254.         else if (nSize < pInfo->nMinSize && i != 0)
  1255.         {
  1256.             // additional panes below the recommended minimum size
  1257.             //   aren't shown and the size goes to the previous pane
  1258.             pInfo->nCurSize = 0;
  1259.  
  1260.             // previous pane already has room for splitter + border
  1261.             //   add remaining size and remove the extra border
  1262.             ASSERT(afxData.cxBorder2 == afxData.cyBorder2);
  1263.             (pInfo-1)->nCurSize += nSize + afxData.cxBorder2;
  1264.             nSize = 0;
  1265.         }
  1266.         else
  1267.         {
  1268.             // otherwise we can add the second pane
  1269.             ASSERT(nSize > 0);
  1270.             if (pInfo->nCurSize == 0)
  1271.             {
  1272.                 // too small to see
  1273.                 if (i != 0)
  1274.                     pInfo->nCurSize = 0;
  1275.             }
  1276.             else if (nSize < pInfo->nCurSize)
  1277.             {
  1278.                 // this row/col won't fit completely - make as small as possible
  1279.                 pInfo->nCurSize = nSize;
  1280.                 nSize = 0;
  1281.             }
  1282.             else
  1283.             {
  1284.                 // can fit everything
  1285.                 nSize -= pInfo->nCurSize;
  1286.             }
  1287.         }
  1288.  
  1289.         // see if we should add a splitter
  1290.         ASSERT(nSize >= 0);
  1291.         if (i != nMax - 1)
  1292.         {
  1293.             // should have a splitter
  1294.             if (nSize > nSizeSplitter)
  1295.             {
  1296.                 nSize -= nSizeSplitter; // leave room for splitter + border
  1297.                 ASSERT(nSize > 0);
  1298.             }
  1299.             else
  1300.             {
  1301.                 // not enough room - add left over less splitter size
  1302.                 ASSERT(afxData.cxBorder2 == afxData.cyBorder2);
  1303.                 pInfo->nCurSize += nSize;
  1304.                 if (pInfo->nCurSize > (nSizeSplitter - afxData.cxBorder2))
  1305.                     pInfo->nCurSize -= (nSizeSplitter - afxData.cyBorder2);
  1306.                 nSize = 0;
  1307.             }
  1308.         }
  1309.     }
  1310.     ASSERT(nSize == 0); // all space should be allocated
  1311. }
  1312.  
  1313. // repositions client area of specified window
  1314. // assumes everything has WS_BORDER or is inset like it does
  1315. //  (includes scroll bars)
  1316. static void AFXAPI DeferClientPos(AFX_SIZEPARENTPARAMS* lpLayout,
  1317.     CWnd* pWnd, int x, int y, int cx, int cy, BOOL bScrollBar)
  1318. {
  1319.     ASSERT(pWnd != NULL);
  1320.     ASSERT(pWnd->m_hWnd != NULL);
  1321.  
  1322.     if (bScrollBar)
  1323.     {
  1324.         // if there is enough room, draw scroll bar without border
  1325.         // if there is not enough room, set the WS_BORDER bit so that
  1326.         //   we will at least get a proper border drawn
  1327.         BOOL bNeedBorder = (cx <= CX_BORDER || cy <= CY_BORDER);
  1328.         pWnd->ModifyStyle(bNeedBorder ? 0 : WS_BORDER,
  1329.             bNeedBorder ? WS_BORDER : 0);
  1330.     }
  1331.     CRect rect(x, y, x+cx, y+cy);
  1332.  
  1333.     // adjust for border size (even if zero client size)
  1334.     if (!afxData.bWin4)
  1335.     {
  1336.         if (bScrollBar)
  1337.             rect.InflateRect(CX_BORDER, CY_BORDER);
  1338.         else
  1339.             pWnd->CalcWindowRect(&rect);
  1340.     }
  1341.  
  1342.     // adjust for 3d border (splitter windows have implied border)
  1343.     if ((pWnd->GetExStyle() & WS_EX_CLIENTEDGE) ||
  1344.           pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
  1345.         rect.InflateRect(afxData.cxBorder2, afxData.cyBorder2);
  1346.  
  1347.     // first check if the new rectangle is the same as the current
  1348.     CRect rectOld;
  1349.     pWnd->GetWindowRect(rectOld);
  1350.     pWnd->GetParent()->ScreenToClient(&rectOld);
  1351.     if (rect != rectOld)
  1352.         AfxRepositionWindow(lpLayout, pWnd->m_hWnd, rect);
  1353. }
  1354.  
  1355. CWnd* CSplitterWnd::GetSizingParent()
  1356. {
  1357.     ASSERT_VALID(this);
  1358.  
  1359.     if (!afxData.bWin4)
  1360.         return NULL;
  1361.  
  1362.     // size box is in lower right corner of this window
  1363.     CRect rectClient;
  1364.     GetClientRect(rectClient);
  1365.  
  1366.     // find sizeable parent window
  1367.     CWnd* pParent = this;
  1368.     if (!(pParent->GetStyle() & WS_THICKFRAME))
  1369.         pParent = GetParent();
  1370.  
  1371.     // only allow if not maximized and has thick frame
  1372.     ASSERT_VALID(pParent);
  1373.     if ((pParent->GetStyle() & (WS_THICKFRAME|WS_MAXIMIZE)) == WS_THICKFRAME)
  1374.     {
  1375.         // convert client area of frame window relative to splitter
  1376.         CRect rect;
  1377.         pParent->GetClientRect(rect);
  1378.         pParent->ClientToScreen(rect);
  1379.         ScreenToClient(rect);
  1380.  
  1381.         // must match exactly to get the size box
  1382.         if (rectClient.BottomRight() == rect.BottomRight())
  1383.             return pParent;
  1384.     }
  1385.  
  1386.     return NULL;    // no sizeable parent found
  1387. }
  1388.  
  1389. void CSplitterWnd::RecalcLayout()
  1390. {
  1391.     ASSERT_VALID(this);
  1392.     ASSERT(m_nRows > 0 && m_nCols > 0); // must have at least one pane
  1393.  
  1394.     CRect rectClient;
  1395.     GetClientRect(rectClient);
  1396.     rectClient.InflateRect(-m_cxBorder, -m_cyBorder);
  1397.  
  1398.     CRect rectInside;
  1399.     GetInsideRect(rectInside);
  1400.  
  1401.     // layout columns (restrict to possible sizes)
  1402.     LayoutRowCol(m_pColInfo, m_nCols, rectInside.Width(), m_cxSplitterGap);
  1403.     LayoutRowCol(m_pRowInfo, m_nRows, rectInside.Height(), m_cySplitterGap);
  1404.  
  1405.     // adjust the panes (and optionally scroll bars)
  1406.  
  1407.     // give the hint for the maximum number of HWNDs
  1408.     AFX_SIZEPARENTPARAMS layout;
  1409.     layout.hDWP = ::BeginDeferWindowPos((m_nCols + 1) * (m_nRows + 1) + 1);
  1410.  
  1411.     // size of scrollbars
  1412.     int cx = (rectClient.right - rectInside.right) - afxData.bNotWin4;
  1413.     int cy = (rectClient.bottom - rectInside.bottom) - afxData.bNotWin4;
  1414.  
  1415.     // reposition size box
  1416.     if (m_bHasHScroll && m_bHasVScroll)
  1417.     {
  1418.         CWnd* pScrollBar = GetDlgItem(AFX_IDW_SIZE_BOX);
  1419.         ASSERT(pScrollBar != NULL);
  1420.  
  1421.         // fix style if necessary
  1422.         BOOL bSizingParent = (GetSizingParent() != NULL);
  1423.         // modifyStyle returns TRUE if style changes
  1424.         if (pScrollBar->ModifyStyle(SBS_SIZEGRIP|SBS_SIZEBOX,
  1425.                 bSizingParent ? SBS_SIZEGRIP : SBS_SIZEBOX))
  1426.             pScrollBar->Invalidate();
  1427.         pScrollBar->EnableWindow(bSizingParent);
  1428.  
  1429.         // reposition the size box
  1430.         DeferClientPos(&layout, pScrollBar,
  1431.             rectInside.right + afxData.bNotWin4,
  1432.             rectInside.bottom + afxData.bNotWin4, cx, cy, TRUE);
  1433.     }
  1434.  
  1435.     // reposition scroll bars
  1436.     if (m_bHasHScroll)
  1437.     {
  1438.         int cxSplitterBox = m_cxSplitter + afxData.bNotWin4;// split box bigger
  1439.         int x = rectClient.left;
  1440.         int y = rectInside.bottom + afxData.bNotWin4;
  1441.         for (int col = 0; col < m_nCols; col++)
  1442.         {
  1443.             CWnd* pScrollBar = GetDlgItem(AFX_IDW_HSCROLL_FIRST + col);
  1444.             ASSERT(pScrollBar != NULL);
  1445.             int cx = m_pColInfo[col].nCurSize;
  1446.             if (col == 0 && m_nCols < m_nMaxCols)
  1447.                 x += cxSplitterBox, cx -= cxSplitterBox;
  1448.             DeferClientPos(&layout, pScrollBar, x, y, cx, cy, TRUE);
  1449.             x += cx + m_cxSplitterGap;
  1450.         }
  1451.     }
  1452.  
  1453.     if (m_bHasVScroll)
  1454.     {
  1455.         int cySplitterBox = m_cySplitter + afxData.bNotWin4;// split box bigger
  1456.         int x = rectInside.right + afxData.bNotWin4;
  1457.         int y = rectClient.top;
  1458.         for (int row = 0; row < m_nRows; row++)
  1459.         {
  1460.             CWnd* pScrollBar = GetDlgItem(AFX_IDW_VSCROLL_FIRST + row);
  1461.             ASSERT(pScrollBar != NULL);
  1462.             int cy = m_pRowInfo[row].nCurSize;
  1463.             if (row == 0 && m_nRows < m_nMaxRows)
  1464.                 y += cySplitterBox, cy -= cySplitterBox;
  1465.             DeferClientPos(&layout, pScrollBar, x, y, cx, cy, TRUE);
  1466.             y += cy + m_cySplitterGap;
  1467.         }
  1468.     }
  1469.  
  1470.     //BLOCK: Reposition all the panes
  1471.     {
  1472.         int x = rectClient.left;
  1473.         for (int col = 0; col < m_nCols; col++)
  1474.         {
  1475.             int cx = m_pColInfo[col].nCurSize;
  1476.             int y = rectClient.top;
  1477.             for (int row = 0; row < m_nRows; row++)
  1478.             {
  1479.                 int cy = m_pRowInfo[row].nCurSize;
  1480.                 CWnd* pWnd = GetPane(row, col);
  1481.                 DeferClientPos(&layout, pWnd, x, y, cx, cy, FALSE);
  1482.                 y += cy + m_cySplitterGap;
  1483.             }
  1484.             x += cx + m_cxSplitterGap;
  1485.         }
  1486.     }
  1487.  
  1488.     // move and resize all the windows at once!
  1489.     if (layout.hDWP == NULL || !::EndDeferWindowPos(layout.hDWP))
  1490.         TRACE0("Warning: DeferWindowPos failed - low system resources.\n");
  1491.  
  1492.     // invalidate all the splitter bars (with NULL pDC)
  1493.     DrawAllSplitBars(NULL, rectInside.right, rectInside.bottom);
  1494. }
  1495.  
  1496. void CSplitterWnd::DrawAllSplitBars(CDC* pDC, int cxInside, int cyInside)
  1497. {
  1498.     ASSERT_VALID(this);
  1499.  
  1500.     // draw column split bars
  1501.     CRect rect;
  1502.     GetClientRect(rect);
  1503.     rect.left += m_cxBorder;
  1504.     for (int col = 0; col < m_nCols - 1; col++)
  1505.     {
  1506.         rect.left += m_pColInfo[col].nCurSize + m_cxBorderShare;
  1507.         rect.right = rect.left + m_cxSplitter;
  1508.         if (rect.left > cxInside)
  1509.             break;      // stop if not fully visible
  1510.         OnDrawSplitter(pDC, splitBar, rect);
  1511.         rect.left = rect.right + m_cxBorderShare;
  1512.     }
  1513.  
  1514.     // draw row split bars
  1515.     GetClientRect(rect);
  1516.     rect.top += m_cyBorder;
  1517.     for (int row = 0; row < m_nRows - 1; row++)
  1518.     {
  1519.         rect.top += m_pRowInfo[row].nCurSize + m_cyBorderShare;
  1520.         rect.bottom = rect.top + m_cySplitter;
  1521.         if (rect.top > cyInside)
  1522.             break;      // stop if not fully visible
  1523.         OnDrawSplitter(pDC, splitBar, rect);
  1524.         rect.top = rect.bottom + m_cyBorderShare;
  1525.     }
  1526.  
  1527.     // draw pane borders
  1528.     if (afxData.bWin4)
  1529.     {
  1530.         GetClientRect(rect);
  1531.         int x = rect.left;
  1532.         for (col = 0; col < m_nCols; col++)
  1533.         {
  1534.             int cx = m_pColInfo[col].nCurSize + 2*m_cxBorder;
  1535.             if (col == m_nCols-1 && m_bHasVScroll)
  1536.                 cx += afxData.cxVScroll - CX_BORDER;
  1537.             int y = rect.top;
  1538.             for (int row = 0; row < m_nRows; row++)
  1539.             {
  1540.                 int cy = m_pRowInfo[row].nCurSize + 2*m_cyBorder;
  1541.                 if (row == m_nRows-1 && m_bHasHScroll)
  1542.                     cy += afxData.cyHScroll - CX_BORDER;
  1543.                 OnDrawSplitter(pDC, splitBorder, CRect(x, y, x+cx, y+cy));
  1544.                 y += cy + m_cySplitterGap - 2*m_cyBorder;
  1545.             }
  1546.             x += cx + m_cxSplitterGap - 2*m_cxBorder;
  1547.         }
  1548.     }
  1549. }
  1550.  
  1551. void CSplitterWnd::OnPaint()
  1552. {
  1553.     ASSERT_VALID(this);
  1554.     CPaintDC dc(this);
  1555.  
  1556.     CRect rectClient;
  1557.     GetClientRect(&rectClient);
  1558.     rectClient.InflateRect(-m_cxBorder, -m_cyBorder);
  1559.  
  1560.     CRect rectInside;
  1561.     GetInsideRect(rectInside);
  1562.  
  1563.     // draw the splitter boxes
  1564.     if (m_bHasVScroll && m_nRows < m_nMaxRows)
  1565.     {
  1566.         OnDrawSplitter(&dc, splitBox,
  1567.             CRect(rectInside.right + afxData.bNotWin4, rectClient.top,
  1568.                 rectClient.right, rectClient.top + m_cySplitter));
  1569.     }
  1570.  
  1571.     if (m_bHasHScroll && m_nCols < m_nMaxCols)
  1572.     {
  1573.         OnDrawSplitter(&dc, splitBox,
  1574.             CRect(rectClient.left, rectInside.bottom + afxData.bNotWin4,
  1575.                 rectClient.left + m_cxSplitter, rectClient.bottom));
  1576.     }
  1577.  
  1578.     // extend split bars to window border (past margins)
  1579.     DrawAllSplitBars(&dc, rectInside.right, rectInside.bottom);
  1580.  
  1581.     if (!afxData.bWin4)
  1582.     {
  1583.         // draw splitter intersections (inside only)
  1584.         GetInsideRect(rectInside);
  1585.         dc.IntersectClipRect(rectInside);
  1586.         CRect rect;
  1587.         rect.top = rectInside.top;
  1588.         for (int row = 0; row < m_nRows - 1; row++)
  1589.         {
  1590.             rect.top += m_pRowInfo[row].nCurSize + m_cyBorderShare;
  1591.             rect.bottom = rect.top + m_cySplitter;
  1592.             rect.left = rectInside.left;
  1593.             for (int col = 0; col < m_nCols - 1; col++)
  1594.             {
  1595.                 rect.left += m_pColInfo[col].nCurSize + m_cxBorderShare;
  1596.                 rect.right = rect.left + m_cxSplitter;
  1597.                 OnDrawSplitter(&dc, splitIntersection, rect);
  1598.                 rect.left = rect.right + m_cxBorderShare;
  1599.             }
  1600.             rect.top = rect.bottom + m_cxBorderShare;
  1601.         }
  1602.     }
  1603. }
  1604.  
  1605. BOOL CSplitterWnd::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
  1606. {
  1607.     if (nHitTest == HTCLIENT && pWnd == this && !m_bTracking)
  1608.         return TRUE;    // we will handle it in the mouse move
  1609.  
  1610.     return CWnd::OnSetCursor(pWnd, nHitTest, message);
  1611. }
  1612.  
  1613. void CSplitterWnd::SetSplitCursor(int ht)
  1614. {
  1615.     // cache of last needed cursor
  1616.     static HCURSOR hcurLast = NULL;
  1617.     static HCURSOR hcurDestroy = NULL;
  1618.     static UINT idcPrimaryLast = 0; // store the primary IDC
  1619.  
  1620.  
  1621.     UINT idcPrimary;        // app supplied cursor
  1622.     LPCTSTR idcSecondary;    // system supplied cursor (MAKEINTRESOURCE)
  1623.  
  1624.     AfxLockGlobals(CRIT_SPLITTERWND);
  1625.     if (ht == vSplitterBox ||
  1626.         ht >= vSplitterBar1 && ht <= vSplitterBar15)
  1627.     {
  1628.         idcPrimary = AFX_IDC_VSPLITBAR;
  1629.         idcSecondary = IDC_SIZENS;
  1630.     }
  1631.     else if (ht == hSplitterBox ||
  1632.         ht >= hSplitterBar1 && ht <= hSplitterBar15)
  1633.     {
  1634.         idcPrimary = AFX_IDC_HSPLITBAR;
  1635.         idcSecondary = IDC_SIZEWE;
  1636.     }
  1637.     else if (ht == bothSplitterBox ||
  1638.         (ht >= splitterIntersection1 && ht <= splitterIntersection225))
  1639.     {
  1640.         idcPrimary = AFX_IDC_SMALLARROWS;
  1641.         idcSecondary = IDC_SIZEALL;
  1642.     }
  1643.     else
  1644.     {
  1645.         SetCursor(afxData.hcurArrow);
  1646.         idcPrimary = 0;     // don't use it
  1647.         idcSecondary = 0;   // don't use it
  1648.     }
  1649.  
  1650.     if (idcPrimary != 0)
  1651.     {
  1652.         HCURSOR hcurToDestroy = NULL;
  1653.         if (idcPrimary != idcPrimaryLast)
  1654.         {
  1655.             HINSTANCE hInst = AfxFindResourceHandle(
  1656.                 MAKEINTRESOURCE(idcPrimary), RT_GROUP_CURSOR);
  1657.  
  1658.             // load in another cursor
  1659.             hcurToDestroy = hcurDestroy;
  1660.  
  1661.             // Note: If this LoadCursor call fails, it is likely that
  1662.             //  _AFX_NO_SPLITTER_RESOURCES is defined in your .RC file.
  1663.             // To correct the situation, remove the following line from your
  1664.             //  resource script:
  1665.             //      #define _AFX_NO_SPLITTER_RESOURCES
  1666.             // This should be done using the Resource.Set Includes... command.
  1667.  
  1668.             if ((hcurDestroy = hcurLast =
  1669.                ::LoadCursor(hInst, MAKEINTRESOURCE(idcPrimary))) == NULL)
  1670.             {
  1671.                 // will not look as good
  1672.                 TRACE0("Warning: Could not find splitter cursor - using system provided alternative.\n");
  1673.  
  1674.                 ASSERT(hcurDestroy == NULL);    // will not get destroyed
  1675.                 hcurLast = ::LoadCursor(NULL, idcSecondary);
  1676.                 ASSERT(hcurLast != NULL);
  1677.             }
  1678.             idcPrimaryLast = idcPrimary;
  1679.         }
  1680.         ASSERT(hcurLast != NULL);
  1681.         ::SetCursor(hcurLast);
  1682.         ASSERT(hcurLast != hcurToDestroy);
  1683.         if (hcurToDestroy != NULL)
  1684.             ::DestroyCursor(hcurToDestroy); // destroy after being set
  1685.     }
  1686.     AfxUnlockGlobals(CRIT_SPLITTERWND);
  1687. }
  1688.  
  1689. void CSplitterWnd::OnMouseMove(UINT /*nFlags*/, CPoint pt)
  1690. {
  1691.     if (GetCapture() != this)
  1692.         StopTracking(FALSE);
  1693.  
  1694.     if (m_bTracking)
  1695.     {
  1696.         // move tracker to current cursor position
  1697.  
  1698.         pt.Offset(m_ptTrackOffset); // pt is the upper right of hit detect
  1699.         // limit the point to the valid split range
  1700.         if (pt.y < m_rectLimit.top)
  1701.             pt.y = m_rectLimit.top;
  1702.         else if (pt.y > m_rectLimit.bottom)
  1703.             pt.y = m_rectLimit.bottom;
  1704.         if (pt.x < m_rectLimit.left)
  1705.             pt.x = m_rectLimit.left;
  1706.         else if (pt.x > m_rectLimit.right)
  1707.             pt.x = m_rectLimit.right;
  1708.  
  1709.         if (m_htTrack == vSplitterBox ||
  1710.             m_htTrack >= vSplitterBar1 && m_htTrack <= vSplitterBar15)
  1711.         {
  1712.             if (m_rectTracker.top != pt.y)
  1713.             {
  1714.                 OnInvertTracker(m_rectTracker);
  1715.                 m_rectTracker.OffsetRect(0, pt.y - m_rectTracker.top);
  1716.                 OnInvertTracker(m_rectTracker);
  1717.             }
  1718.         }
  1719.         else if (m_htTrack == hSplitterBox ||
  1720.             m_htTrack >= hSplitterBar1 && m_htTrack <= hSplitterBar15)
  1721.         {
  1722.             if (m_rectTracker.left != pt.x)
  1723.             {
  1724.                 OnInvertTracker(m_rectTracker);
  1725.                 m_rectTracker.OffsetRect(pt.x - m_rectTracker.left, 0);
  1726.                 OnInvertTracker(m_rectTracker);
  1727.             }
  1728.         }
  1729.         else if (m_htTrack == bothSplitterBox ||
  1730.            (m_htTrack >= splitterIntersection1 &&
  1731.             m_htTrack <= splitterIntersection225))
  1732.         {
  1733.             if (m_rectTracker.top != pt.y)
  1734.             {
  1735.                 OnInvertTracker(m_rectTracker);
  1736.                 m_rectTracker.OffsetRect(0, pt.y - m_rectTracker.top);
  1737.                 OnInvertTracker(m_rectTracker);
  1738.             }
  1739.             if (m_rectTracker2.left != pt.x)
  1740.             {
  1741.                 OnInvertTracker(m_rectTracker2);
  1742.                 m_rectTracker2.OffsetRect(pt.x - m_rectTracker2.left, 0);
  1743.                 OnInvertTracker(m_rectTracker2);
  1744.             }
  1745.         }
  1746.     }
  1747.     else
  1748.     {
  1749.         // simply hit-test and set appropriate cursor
  1750.  
  1751.         int ht = HitTest(pt);
  1752.         SetSplitCursor(ht);
  1753.     }
  1754. }
  1755.  
  1756. void CSplitterWnd::OnLButtonDown(UINT /*nFlags*/, CPoint pt)
  1757. {
  1758.     if (m_bTracking)
  1759.         return;
  1760.  
  1761.     StartTracking(HitTest(pt));
  1762. }
  1763.  
  1764. void CSplitterWnd::OnLButtonDblClk(UINT /*nFlags*/, CPoint pt)
  1765. {
  1766.     int ht = HitTest(pt);
  1767.     CRect rect;
  1768.  
  1769.     StopTracking(FALSE);
  1770.  
  1771.     if ((GetStyle() & SPLS_DYNAMIC_SPLIT) == 0)
  1772.         return;     // do nothing if layout is static
  1773.  
  1774.     if (ht == vSplitterBox)
  1775.     {
  1776.         // half split
  1777.         SplitRow(m_pRowInfo[0].nCurSize / 2);
  1778.     }
  1779.     else if (ht == hSplitterBox)
  1780.     {
  1781.         // half split
  1782.         SplitColumn(m_pColInfo[0].nCurSize / 2);
  1783.     }
  1784.     else if (ht >= vSplitterBar1 && ht <= vSplitterBar15)
  1785.     {
  1786.         int rowDelete = ht - vSplitterBar1;
  1787.         // don't delete the active row
  1788.         int row;
  1789.         if (GetActivePane(&row, NULL) != NULL && rowDelete == row)
  1790.             ++rowDelete;
  1791.         DeleteRow(rowDelete);
  1792.     }
  1793.     else if (ht >= hSplitterBar1 && ht <= hSplitterBar15)
  1794.     {
  1795.         int colDelete = ht - hSplitterBar1;
  1796.         // don't delete the active column
  1797.         int col;
  1798.         if (GetActivePane(NULL, &col) != NULL && colDelete == col)
  1799.             ++colDelete;
  1800.         DeleteColumn(colDelete);
  1801.     }
  1802.     else if (ht >= splitterIntersection1 && ht <= splitterIntersection225)
  1803.     {
  1804.         int rowDelete = (ht - splitterIntersection1) / 15;
  1805.         int colDelete = (ht - splitterIntersection1) % 15;
  1806.         int row, col;
  1807.         if (GetActivePane(&row, &col) != NULL)
  1808.         {
  1809.             // don't delete the active row or column
  1810.             if (col == colDelete)
  1811.                 ++colDelete;
  1812.             if (row == rowDelete)
  1813.                 ++rowDelete;
  1814.         }
  1815.         DeleteRow(rowDelete);
  1816.         DeleteColumn(colDelete);
  1817.     }
  1818. }
  1819.  
  1820. void CSplitterWnd::OnLButtonUp(UINT /*nFlags*/, CPoint /*pt*/)
  1821. {
  1822.     StopTracking(TRUE);
  1823. }
  1824.  
  1825. void CSplitterWnd::OnCancelMode()
  1826. {
  1827.     StopTracking(FALSE);
  1828. }
  1829.  
  1830. void CSplitterWnd::OnKeyDown(UINT nChar, UINT /*nRepCnt*/, UINT /*nFlags*/)
  1831. {
  1832.     CPoint pt;
  1833.     GetCursorPos(&pt);
  1834.  
  1835.     int cz = GetKeyState(VK_CONTROL) < 0 ? 1 : 16;
  1836.     int dx = 0;
  1837.     int dy = 0;
  1838.  
  1839.     switch (nChar)
  1840.     {
  1841.     case VK_ESCAPE:
  1842.         StopTracking(FALSE);
  1843.         return;
  1844.     case VK_RETURN:
  1845.         StopTracking(TRUE);
  1846.         return;
  1847.  
  1848. #ifndef _MAC
  1849.     case VK_LEFT:
  1850.         dx = -1;
  1851.         break;
  1852.     case VK_RIGHT:
  1853.         dx = +1;
  1854.         break;
  1855.     case VK_UP:
  1856.         dy = -1;
  1857.         break;
  1858.     case VK_DOWN:
  1859.         dy = +1;
  1860.         break;
  1861. #endif
  1862.  
  1863.     default:
  1864.         Default();  // pass other keys through
  1865.         return;
  1866.     }
  1867.  
  1868.     if (m_htTrack == vSplitterBox ||
  1869.         m_htTrack >= vSplitterBar1 && m_htTrack <= vSplitterBar15)
  1870.     {
  1871.         // no movement along X axis
  1872.         dx = 0;
  1873.     }
  1874.     if (m_htTrack == hSplitterBox ||
  1875.         m_htTrack >= hSplitterBar1 && m_htTrack <= hSplitterBar15)
  1876.     {
  1877.         // no movement along Y axis
  1878.         dy = 0;
  1879.     }
  1880.  
  1881.     // adjust to new position
  1882.     pt.x += dx * cz;
  1883.     pt.y += dy * cz;
  1884.  
  1885.     // make sure within valid limits
  1886.     ScreenToClient(&pt);
  1887.     if (pt.y < m_rectLimit.top)
  1888.         pt.y = m_rectLimit.top;
  1889.     else if (pt.y > m_rectLimit.bottom)
  1890.         pt.y = m_rectLimit.bottom;
  1891.     if (pt.x < m_rectLimit.left)
  1892.         pt.x = m_rectLimit.left;
  1893.     else if (pt.x > m_rectLimit.right)
  1894.         pt.x = m_rectLimit.right;
  1895.     ClientToScreen(&pt);
  1896.  
  1897. #ifndef _MAC
  1898.     // cause WM_MOUSEMOVE to filter through
  1899.     SetCursorPos(pt.x, pt.y);
  1900. #endif
  1901. }
  1902.  
  1903. void CSplitterWnd::OnSysCommand(UINT nID, LPARAM lParam)
  1904. {
  1905.     if ((nID & 0xFFF0) == SC_SIZE)
  1906.     {
  1907.         CWnd* pParent = GetSizingParent();
  1908.         if (pParent != NULL)
  1909.         {
  1910.             pParent->SendMessage(WM_SYSCOMMAND, (WPARAM)nID, lParam);
  1911.             return;
  1912.         }
  1913.     }
  1914.     CWnd::OnSysCommand(nID, lParam);
  1915. }
  1916.  
  1917. /////////////////////////////////////////////////////////////////////////////
  1918. // CSplitterWnd command routing
  1919.  
  1920. BOOL CSplitterWnd::OnCommand(WPARAM wParam, LPARAM lParam)
  1921. {
  1922.     if (CWnd::OnCommand(wParam, lParam))
  1923.         return TRUE;
  1924.  
  1925.     // route commands to the splitter to the parent frame window
  1926.     return GetParentFrame()->SendMessage(WM_COMMAND, wParam, lParam);
  1927. }
  1928.  
  1929. BOOL CSplitterWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
  1930. {
  1931.     if (CWnd::OnNotify(wParam, lParam, pResult))
  1932.         return TRUE;
  1933.  
  1934.     // route commands to the splitter to the parent frame window
  1935.     *pResult = GetParentFrame()->SendMessage(WM_NOTIFY, wParam, lParam);
  1936.     return TRUE;
  1937. }
  1938.  
  1939. /////////////////////////////////////////////////////////////////////////////
  1940. // Scroll messages
  1941.  
  1942. BOOL CSplitterWnd::OnMouseWheel(UINT fFlags, short zDelta, CPoint point)
  1943. {
  1944.     BOOL bRetVal = FALSE;
  1945.     int row;
  1946.     int col;
  1947.  
  1948.     CScrollBar* pBar;
  1949.     CWnd* pPane;
  1950.     CScrollView* pView;
  1951.     int nOldPos;
  1952.  
  1953.     for (row = 0; row < m_nRows; row++)
  1954.     {
  1955.         for (col = 0; col < m_nCols; col++)
  1956.         {
  1957.             pPane = GetPane(row, col);
  1958.             pView = STATIC_DOWNCAST(CScrollView, pPane);
  1959.             if (pView != NULL)
  1960.             {
  1961.                 pBar = pView->GetScrollBarCtrl(SB_VERT);
  1962.                 if (pBar == NULL)
  1963.                 {
  1964.                     pBar = pView->GetScrollBarCtrl(SB_HORZ);
  1965.                     if (pBar == NULL)
  1966.                         continue;
  1967.                 }
  1968.  
  1969.                 nOldPos = pBar->GetScrollPos();
  1970.                 if (pView->DoMouseWheel(fFlags, zDelta, point))
  1971.                 {
  1972.                     bRetVal = TRUE;
  1973.                 }
  1974.                 if (col < m_nCols -1)
  1975.                     pBar->SetScrollPos(nOldPos, FALSE);
  1976.             }
  1977.         }
  1978.     }
  1979.  
  1980.     return TRUE;
  1981. }
  1982.  
  1983. void CSplitterWnd::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
  1984. {
  1985.     ASSERT(pScrollBar != NULL);
  1986.     int col = _AfxGetDlgCtrlID(pScrollBar->m_hWnd) - AFX_IDW_HSCROLL_FIRST;
  1987.     ASSERT(col >= 0 && col < m_nMaxCols);
  1988.  
  1989.     ASSERT(m_nRows > 0);
  1990.     int nOldPos = pScrollBar->GetScrollPos();
  1991. #ifdef _DEBUG
  1992.     int nNewPos;
  1993. #endif
  1994.     for (int row = 0; row < m_nRows; row++)
  1995.     {
  1996.         GetPane(row, col)->SendMessage(WM_HSCROLL,
  1997.             MAKELONG(nSBCode, nPos), (LPARAM)pScrollBar->m_hWnd);
  1998. #ifdef _DEBUG
  1999.         if (row == 0)
  2000.             nNewPos = pScrollBar->GetScrollPos();
  2001.         if (pScrollBar->GetScrollPos() != nNewPos)
  2002.         {
  2003.             TRACE0("Warning: scroll panes setting different scroll positions.\n");
  2004.             // stick with the last one set
  2005.         }
  2006. #endif //_DEBUG
  2007.         // set the scroll pos to the value it was originally for the next pane
  2008.         if (row < m_nRows - 1)
  2009.             pScrollBar->SetScrollPos(nOldPos, FALSE);
  2010.     }
  2011. }
  2012.  
  2013. void CSplitterWnd::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
  2014. {
  2015.     ASSERT(pScrollBar != NULL);
  2016.     int row = _AfxGetDlgCtrlID(pScrollBar->m_hWnd) - AFX_IDW_VSCROLL_FIRST;
  2017.     ASSERT(row >= 0 && row < m_nMaxRows);
  2018.  
  2019.     ASSERT(m_nCols > 0);
  2020.     int nOldPos = pScrollBar->GetScrollPos();
  2021. #ifdef _DEBUG
  2022.     int nNewPos;
  2023. #endif
  2024.     for (int col = 0; col < m_nCols; col++)
  2025.     {
  2026.         GetPane(row, col)->SendMessage(WM_VSCROLL,
  2027.             MAKELONG(nSBCode, nPos), (LPARAM)pScrollBar->m_hWnd);
  2028. #ifdef _DEBUG
  2029.         if (col == 0)
  2030.             nNewPos = pScrollBar->GetScrollPos();
  2031.         if (pScrollBar->GetScrollPos() != nNewPos)
  2032.         {
  2033.             TRACE0("Warning: scroll panes setting different scroll positions.\n");
  2034.             // stick with the last one set
  2035.         }
  2036. #endif //_DEBUG
  2037.         // set the scroll pos to the value it was originally for the next pane
  2038.         if (col < m_nCols - 1)
  2039.             pScrollBar->SetScrollPos(nOldPos, FALSE);
  2040.     }
  2041. }
  2042.  
  2043. // synchronized scrolling
  2044. BOOL CSplitterWnd::DoScroll(CView* pViewFrom, UINT nScrollCode, BOOL bDoScroll)
  2045. {
  2046.     ASSERT_VALID(pViewFrom);
  2047.  
  2048.     int rowFrom, colFrom;
  2049.     if (!IsChildPane(pViewFrom, &rowFrom, &colFrom))
  2050.         return FALSE;
  2051.  
  2052.     BOOL bResult = FALSE;
  2053.  
  2054.     // save original positions
  2055.     int nOldVert;
  2056.     CScrollBar* pScrollVert = pViewFrom->GetScrollBarCtrl(SB_VERT);
  2057.     if (pScrollVert != NULL)
  2058.         nOldVert = pScrollVert->GetScrollPos();
  2059.     int nOldHorz;
  2060.     CScrollBar* pScrollHorz = pViewFrom->GetScrollBarCtrl(SB_HORZ);
  2061.     if (pScrollHorz != NULL)
  2062.         nOldHorz = pScrollHorz->GetScrollPos();
  2063.  
  2064.     // scroll the view from which the message is from
  2065.     if (pViewFrom->OnScroll(nScrollCode, 0, bDoScroll))
  2066.         bResult = TRUE;
  2067.  
  2068.     if (pScrollVert != NULL)
  2069.     {
  2070. #ifdef _DEBUG
  2071.         int nNewVert = pScrollVert->GetScrollPos();
  2072. #endif
  2073.         // scroll related columns
  2074.         for (int col = 0; col < m_nCols; col++)
  2075.         {
  2076.             if (col == colFrom)
  2077.                 continue;
  2078.  
  2079.             // set the scroll pos to the value it was originally
  2080.             pScrollVert->SetScrollPos(nOldVert, FALSE);
  2081.  
  2082.             // scroll the pane
  2083.             CView* pView = (CView*)GetPane(rowFrom, col);
  2084.             ASSERT_KINDOF(CView, pView);
  2085.             ASSERT(pView != pViewFrom);
  2086.             if (pView->OnScroll(MAKEWORD(-1, HIBYTE(nScrollCode)), 0,
  2087.                 bDoScroll))
  2088.             {
  2089.                 bResult = TRUE;
  2090.             }
  2091.  
  2092. #ifdef _DEBUG
  2093.             if (pScrollVert->GetScrollPos() != nNewVert)
  2094.             {
  2095.                 TRACE0("Warning: scroll panes setting different scroll positions.\n");
  2096.                 // stick with the last one set
  2097.             }
  2098. #endif
  2099.         }
  2100.     }
  2101.  
  2102.     if (pScrollHorz != NULL)
  2103.     {
  2104. #ifdef _DEBUG
  2105.         int nNewHorz = pScrollHorz->GetScrollPos();
  2106. #endif
  2107.         // scroll related rows
  2108.         for (int row = 0; row < m_nRows; row++)
  2109.         {
  2110.             if (row == rowFrom)
  2111.                 continue;
  2112.  
  2113.             // set the scroll pos to the value it was originally
  2114.             pScrollHorz->SetScrollPos(nOldHorz, FALSE);
  2115.  
  2116.             // scroll the pane
  2117.             CView* pView = (CView*)GetPane(row, colFrom);
  2118.             ASSERT_KINDOF(CView, pView);
  2119.             ASSERT(pView != pViewFrom);
  2120.             if (pView->OnScroll(MAKEWORD(LOBYTE(nScrollCode), -1), 0,
  2121.                 bDoScroll))
  2122.             {
  2123.                 bResult = TRUE;
  2124.             }
  2125.  
  2126. #ifdef _DEBUG
  2127.             if (pScrollHorz->GetScrollPos() != nNewHorz)
  2128.             {
  2129.                 TRACE0("Warning: scroll panes setting different scroll positions.\n");
  2130.                 // stick with the last one set
  2131.             }
  2132. #endif
  2133.         }
  2134.     }
  2135.  
  2136.     return bResult;
  2137. }
  2138.  
  2139. BOOL CSplitterWnd::DoScrollBy(CView* pViewFrom, CSize sizeScroll, BOOL bDoScroll)
  2140. {
  2141.     int rowFrom, colFrom;
  2142.     if (!IsChildPane(pViewFrom, &rowFrom, &colFrom))
  2143.         return FALSE;
  2144.  
  2145.     BOOL bResult = FALSE;
  2146.  
  2147.     // save original positions
  2148.     int nOldVert;
  2149.     CScrollBar* pScrollVert = pViewFrom->GetScrollBarCtrl(SB_VERT);
  2150.     if (pScrollVert != NULL)
  2151.         nOldVert = pScrollVert->GetScrollPos();
  2152.     int nOldHorz;
  2153.     CScrollBar* pScrollHorz = pViewFrom->GetScrollBarCtrl(SB_HORZ);
  2154.     if (pScrollHorz != NULL)
  2155.         nOldHorz = pScrollHorz->GetScrollPos();
  2156.  
  2157.     // scroll the view from which the message is from
  2158.     if (pViewFrom->OnScrollBy(sizeScroll, bDoScroll))
  2159.         bResult = TRUE;
  2160.  
  2161.     if (pScrollVert != NULL)
  2162.     {
  2163. #ifdef _DEBUG
  2164.         int nNewVert = pScrollVert->GetScrollPos();
  2165. #endif
  2166.         // scroll related columns
  2167.         for (int col = 0; col < m_nCols; col++)
  2168.         {
  2169.             if (col == colFrom)
  2170.                 continue;
  2171.  
  2172.             // set the scroll pos to the value it was originally for the next pane
  2173.             pScrollVert->SetScrollPos(nOldVert, FALSE);
  2174.  
  2175.             // scroll the pane
  2176.             CView* pView = (CView*)GetPane(rowFrom, col);
  2177.             ASSERT_KINDOF(CView, pView);
  2178.             ASSERT(pView != pViewFrom);
  2179.             if (pView->OnScrollBy(CSize(0, sizeScroll.cy), bDoScroll))
  2180.                 bResult = TRUE;
  2181.  
  2182. #ifdef _DEBUG
  2183.             if (pScrollVert->GetScrollPos() != nNewVert)
  2184.             {
  2185.                 TRACE0("Warning: scroll panes setting different scroll positions.\n");
  2186.                 // stick with the last one set
  2187.             }
  2188. #endif
  2189.         }
  2190.     }
  2191.  
  2192.     if (pScrollHorz != NULL)
  2193.     {
  2194. #ifdef _DEBUG
  2195.     int nNewHorz = pScrollHorz->GetScrollPos();
  2196. #endif
  2197.         // scroll related rows
  2198.         for (int row = 0; row < m_nRows; row++)
  2199.         {
  2200.             if (row == rowFrom)
  2201.                 continue;
  2202.  
  2203.             // set the scroll pos to the value it was originally for the next pane
  2204.             pScrollHorz->SetScrollPos(nOldHorz, FALSE);
  2205.  
  2206.             // scroll the pane
  2207.             CView* pView = (CView*)GetPane(row, colFrom);
  2208.             ASSERT_KINDOF(CView, pView);
  2209.             ASSERT(pView != pViewFrom);
  2210.             if (pView->OnScrollBy(CSize(sizeScroll.cx, 0), bDoScroll))
  2211.                 bResult = TRUE;
  2212.  
  2213. #ifdef _DEBUG
  2214.             if (pScrollHorz->GetScrollPos() != nNewHorz)
  2215.             {
  2216.                 TRACE0("Warning: scroll panes setting different scroll positions.\n");
  2217.                 // stick with the last one set
  2218.             }
  2219. #endif
  2220.         }
  2221.     }
  2222.  
  2223.     return bResult;
  2224. }
  2225.  
  2226. /////////////////////////////////////////////////////////////////////////////
  2227. // Focus control and control over the current pane/child
  2228.  
  2229. BOOL CSplitterWnd::CanActivateNext(BOOL)
  2230. {
  2231.     ASSERT_VALID(this);
  2232.  
  2233.     if (GetActivePane() == NULL)
  2234.     {
  2235.         TRACE0("Warning: Can't go to next pane - there is no current pane.\n");
  2236.         return FALSE;
  2237.     }
  2238.     ASSERT(m_nRows != 0);
  2239.     ASSERT(m_nCols != 0);
  2240.     // if more than 1x1 we can go to the next or prev pane
  2241.     return (m_nRows > 1) || (m_nCols > 1);
  2242. }
  2243.  
  2244. void CSplitterWnd::ActivateNext(BOOL bPrev)
  2245. {
  2246.     ASSERT_VALID(this);
  2247.  
  2248.     // find the coordinate of the current pane
  2249.     int row, col;
  2250.     if (GetActivePane(&row, &col) == NULL)
  2251.     {
  2252.         TRACE0("Warning: Cannot go to next pane - there is no current view.\n");
  2253.         return;
  2254.     }
  2255.     ASSERT(row >= 0 && row < m_nRows);
  2256.     ASSERT(col >= 0 && col < m_nCols);
  2257.  
  2258.     // determine next pane
  2259.     if (bPrev)
  2260.     {
  2261.         // prev
  2262.         if (--col < 0)
  2263.         {
  2264.             col = m_nCols - 1;
  2265.             if (--row < 0)
  2266.                 row = m_nRows - 1;
  2267.         }
  2268.     }
  2269.     else
  2270.     {
  2271.         // next
  2272.         if (++col >= m_nCols)
  2273.         {
  2274.             col = 0;
  2275.             if (++row >= m_nRows)
  2276.                 row = 0;
  2277.         }
  2278.     }
  2279.  
  2280.     // set newly active pane
  2281.     SetActivePane(row, col);
  2282. }
  2283.  
  2284. void CSplitterWnd::SetActivePane(int row, int col, CWnd* pWnd)
  2285. {
  2286.     // set the focus to the pane
  2287.     CWnd* pPane = pWnd == NULL ? GetPane(row, col) : pWnd;
  2288.     if (pPane->IsKindOf(RUNTIME_CLASS(CView)))
  2289.     {
  2290.         CFrameWnd* pFrameWnd = GetParentFrame();
  2291.         ASSERT_VALID(pFrameWnd);
  2292.         pFrameWnd->SetActiveView((CView*)pPane);
  2293.     }
  2294.     else
  2295.     {
  2296.         TRACE0("Warning: Next pane is not a view - calling SetFocus.\n");
  2297.         pPane->SetFocus();
  2298.     }
  2299. }
  2300.  
  2301. CWnd* CSplitterWnd::GetActivePane(int* pRow, int* pCol)
  2302.     // return active view, NULL when no active view
  2303. {
  2304.     ASSERT_VALID(this);
  2305.  
  2306.     // attempt to use active view of frame window
  2307.     CWnd* pView = NULL;
  2308.     CFrameWnd* pFrameWnd = GetParentFrame();
  2309.     ASSERT_VALID(pFrameWnd);
  2310.     pView = pFrameWnd->GetActiveView();
  2311.  
  2312.     // failing that, use the current focus
  2313.     if (pView == NULL)
  2314.         pView = GetFocus();
  2315.  
  2316.     // make sure the pane is a child pane of the splitter
  2317.     if (pView != NULL && !IsChildPane(pView, pRow, pCol))
  2318.         pView = NULL;
  2319.  
  2320.     return pView;
  2321. }
  2322.  
  2323. /////////////////////////////////////////////////////////////////////////////
  2324. // CSplitterWnd diagnostics
  2325.  
  2326. #ifdef _DEBUG
  2327. void CSplitterWnd::AssertValid() const
  2328. {
  2329.     CWnd::AssertValid();
  2330.     ASSERT(m_nMaxRows >= 1);
  2331.     ASSERT(m_nMaxCols >= 1);
  2332.     ASSERT(m_nMaxCols > 1 || m_nMaxRows > 1);       // 1x1 is not permitted
  2333.     ASSERT(m_nRows >= 1);
  2334.     ASSERT(m_nCols >= 1);
  2335.     ASSERT(m_nRows <= m_nMaxRows);
  2336.     ASSERT(m_nCols <= m_nMaxCols);
  2337. }
  2338.  
  2339. void CSplitterWnd::Dump(CDumpContext& dc) const
  2340. {
  2341.     CWnd::Dump(dc);
  2342.  
  2343.     if (m_pDynamicViewClass != NULL)
  2344.         dc << "m_pDynamicViewClass = " << m_pDynamicViewClass->m_lpszClassName;
  2345.     dc << "\nm_nMaxRows = " << m_nMaxRows;
  2346.     dc << "\nm_nMaxCols = " << m_nMaxCols;
  2347.     dc << "\nm_nRows = " << m_nRows;
  2348.     dc << "\nm_nCols = " << m_nCols;
  2349.     dc << "\nm_bHasHScroll = " << m_bHasHScroll;
  2350.     dc << "\nm_bHasVScroll = " << m_bHasVScroll;
  2351.     dc << "\nm_cxSplitter = " << m_cxSplitter;
  2352.     dc << "\nm_cySplitter = " << m_cySplitter;
  2353.     if (m_bTracking)
  2354.     {
  2355.         dc << "\nTRACKING: m_htTrack = " << m_htTrack;
  2356.         dc << "\nm_rectLimit = " << m_rectLimit;
  2357.         dc << "\nm_ptTrackOffset = " << m_ptTrackOffset;
  2358.         dc << "\nm_rectTracker = " << m_rectTracker;
  2359.         if (m_bTracking2)
  2360.             dc << "\nm_rectTracker2 = " << m_rectTracker2;
  2361.     }
  2362.  
  2363.     dc << "\n";
  2364. }
  2365. #endif
  2366.  
  2367. /////////////////////////////////////////////////////////////////////////////
  2368.