home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c480 / 17.ddi / MFC / SRC / WINSPLIT.CP_ / WINSPLIT.CP
Encoding:
Text File  |  1993-02-08  |  45.1 KB  |  1,699 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992 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 Microsoft
  7. // QuickHelp and/or WinHelp documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11.  
  12. #include "stdafx.h"
  13. #include <limits.h>
  14.  
  15. #ifdef AFX_CORE3_SEG
  16. #pragma code_seg(AFX_CORE3_SEG)
  17. #endif
  18.  
  19. #ifdef _DEBUG
  20. #undef THIS_FILE
  21. static char BASED_CODE THIS_FILE[] = __FILE__;
  22. #endif
  23.  
  24. /////////////////////////////////////////////////////////////////////////////
  25. // Visual attributes and other constants
  26.  
  27. // HitTest return values (values and spacing between values is important)
  28. enum HitTestValue
  29. {
  30.     noHit                   = 0,
  31.     vSplitterBox            = 1,
  32.     hSplitterBox            = 2,
  33.     bothSplitterBox         = 3,        // just for keyboard
  34.     vSplitterBar1           = 101,
  35.     vSplitterBar15          = 115,
  36.     hSplitterBar1           = 201,
  37.     hSplitterBar15          = 215,
  38.     splitterIntersection1   = 301,
  39.     splitterIntersection225 = 525
  40. };
  41.  
  42. /////////////////////////////////////////////////////////////////////////////
  43. // CSplitterWnd
  44.  
  45. BEGIN_MESSAGE_MAP(CSplitterWnd, CWnd)
  46.     //{{AFX_MSG_MAP(CSplitterWnd)
  47.     ON_WM_SETCURSOR()
  48.     ON_WM_MOUSEMOVE()
  49.     ON_WM_PAINT()
  50.     ON_WM_LBUTTONDOWN()
  51.     ON_WM_LBUTTONDBLCLK()
  52.     ON_WM_LBUTTONUP()
  53.     ON_WM_CANCELMODE()
  54.     ON_WM_KEYDOWN()
  55.     ON_WM_SIZE()
  56.     ON_WM_HSCROLL()
  57.     ON_WM_VSCROLL()
  58.     //}}AFX_MSG_MAP
  59. END_MESSAGE_MAP()
  60.  
  61. /////////////////////////////////////////////////////////////////////////////
  62. // CSplitterWnd construction/destruction
  63.  
  64. // size of splitter gaps
  65. //  (all panes must have borders - even if client area of pane is 0 pixels wide)
  66. #define CX_SPLITTER_GAP (CX_BORDER + m_cxSplitter + CX_BORDER)
  67. #define CY_SPLITTER_GAP (CY_BORDER + m_cySplitter + CY_BORDER)
  68.  
  69. CSplitterWnd::CSplitterWnd()
  70. {
  71.     AFX_ZERO_INIT_OBJECT(CWnd);
  72.     // default splitter box/bar size (includes border)
  73.     m_cxSplitter = m_cySplitter = 4;
  74.     if (GetSystemMetrics(SM_CXBORDER) != CX_BORDER ||
  75.         GetSystemMetrics(SM_CYBORDER) != CY_BORDER)
  76.     {
  77.         TRACE0("Warning: CSplitterWnd assumes 1 pixel border\n");
  78.             // will look ugly if borders are not 1 pixel wide and 1 pixel high
  79.     }
  80. }
  81.  
  82. CSplitterWnd::~CSplitterWnd()
  83. {
  84.     delete m_pRowInfo;
  85.     delete m_pColInfo;
  86. }
  87.  
  88. BOOL CSplitterWnd::Create(CWnd* pParentWnd,
  89.         int nMaxRows, int nMaxCols, SIZE sizeMin,
  90.         CCreateContext* pContext,
  91.         DWORD dwStyle /* ... | SPLS_DYNAMIC_SPLIT */,
  92.         UINT nID /* = AFX_IDW_PANE_FIRST */)
  93. {
  94.     ASSERT(pParentWnd != NULL);
  95.     ASSERT(sizeMin.cx > 0 && sizeMin.cy > 0);   // minimum must be non-zero
  96.  
  97.     ASSERT(pContext != NULL);
  98.     ASSERT(pContext->m_pNewViewClass != NULL);  
  99.     ASSERT(dwStyle & WS_CHILD);
  100.     ASSERT(dwStyle & SPLS_DYNAMIC_SPLIT);   // must have dynamic split behavior
  101.  
  102.     // Dynamic splitters are limited to 2x2
  103.     ASSERT(nMaxRows >= 1 && nMaxRows <= 2);
  104.     ASSERT(nMaxCols >= 1 && nMaxCols <= 2);
  105.     ASSERT(nMaxCols > 1 || nMaxRows > 1);       // 1x1 is not permitted
  106.  
  107.     m_nMaxRows = nMaxRows;
  108.     m_nMaxCols = nMaxCols;
  109.     ASSERT(m_nRows == 0 && m_nCols == 0);       // none yet
  110.     m_nRows = m_nCols = 1;      // start off as 1x1
  111.     if (!CreateCommon(pParentWnd, sizeMin, dwStyle, nID))
  112.         return FALSE;
  113.     ASSERT(m_nRows == 1 && m_nCols == 1);       // still 1x1
  114.  
  115.     m_pDynamicViewClass = pContext->m_pNewViewClass;
  116.         // save for later dynamic creations
  117.  
  118.     // add the first initial pane
  119.     if (!CreateView(0, 0, m_pDynamicViewClass, sizeMin, pContext))
  120.     {
  121.         DestroyWindow(); // will clean up child windows
  122.         return FALSE;
  123.     }
  124.     m_pColInfo[0].nIdealSize = sizeMin.cx;
  125.     m_pRowInfo[0].nIdealSize = sizeMin.cy;
  126.  
  127.     return TRUE;
  128. }
  129.  
  130.  
  131. // simple "wiper" splitter
  132. BOOL CSplitterWnd::CreateStatic(CWnd* pParentWnd,
  133.     int nRows, int nCols,
  134.     DWORD dwStyle /* = WS_CHILD | WS_VISIBLE */,
  135.     UINT nID /* = AFX_IDW_PANE_FIRST */)
  136. {
  137.     ASSERT(pParentWnd != NULL);
  138.     ASSERT(nRows >= 1 && nRows <= 16);
  139.     ASSERT(nCols >= 1 && nCols <= 16);
  140.     ASSERT(nCols > 1 || nRows > 1);     // 1x1 is not permitted
  141.     ASSERT(dwStyle & WS_CHILD);
  142.     ASSERT(!(dwStyle & SPLS_DYNAMIC_SPLIT)); // can't have dynamic split
  143.  
  144.     ASSERT(m_nRows == 0 && m_nCols == 0);       // none yet
  145.     m_nRows = m_nMaxRows = nRows;
  146.     m_nCols = m_nMaxCols = nCols;
  147.  
  148.     // create with zero minimum pane size
  149.     if (!CreateCommon(pParentWnd, CSize(0, 0), dwStyle, nID))
  150.         return FALSE;
  151.  
  152.     // all panes must be created with explicit calls to CreateView
  153.     return TRUE;
  154. }
  155.  
  156. BOOL CSplitterWnd::CreateCommon(CWnd* pParentWnd,
  157.     SIZE sizeMin, DWORD dwStyle, UINT nID)
  158. {
  159.     ASSERT(pParentWnd != NULL);
  160.     ASSERT(sizeMin.cx >= 0 && sizeMin.cy >= 0);
  161.     ASSERT(dwStyle & WS_CHILD);
  162.     ASSERT(nID != 0);
  163.  
  164.     ASSERT(m_pColInfo == NULL && m_pRowInfo == NULL);   // only do once
  165.     ASSERT(m_nMaxCols > 0 && m_nMaxRows > 0);
  166.  
  167.     // the Windows scroll bar styles bits turn no the smart scrollbars
  168.     m_bHasHScroll = (dwStyle & WS_HSCROLL) != 0;
  169.     m_bHasVScroll = (dwStyle & WS_VSCROLL) != 0;
  170.     dwStyle &= ~(WS_HSCROLL|WS_VSCROLL);
  171.  
  172.     // create with the same wnd-class as MDI-Frame (no erase bkgnd)
  173.     if (!CreateEx(0, _afxWndMDIFrame, NULL, dwStyle, 0, 0, 0, 0,
  174.       pParentWnd->m_hWnd, (HMENU)nID, NULL))
  175.         return FALSE;       // create invisible
  176.  
  177.     // attach the initial splitter parts
  178.     TRY
  179.     {
  180.         int row;
  181.         int col;
  182.  
  183.         m_pColInfo = new CRowColInfo[m_nMaxCols];
  184.         for (col = 0; col < m_nMaxCols; col++)
  185.         {
  186.             m_pColInfo[col].nMinSize = m_pColInfo[col].nIdealSize = sizeMin.cx;
  187.             m_pColInfo[col].nCurSize = -1; // will be set in RecalcLayout
  188.         }
  189.         m_pRowInfo = new CRowColInfo[m_nMaxRows];
  190.         for (row = 0; row < m_nMaxRows; row++)
  191.         {
  192.             m_pRowInfo[row].nMinSize = m_pRowInfo[row].nIdealSize = sizeMin.cy;
  193.             m_pRowInfo[row].nCurSize = -1; // will be set in RecalcLayout
  194.         }
  195.  
  196.         if (m_bHasHScroll)
  197.             for (col = 0; col < m_nCols; col++)
  198.                 if (!CreateScrollBarCtrl(SBS_HORZ, AFX_IDW_HSCROLL_FIRST + col))
  199.                     AfxThrowResourceException();
  200.         if (m_bHasVScroll)
  201.             for (row = 0; row < m_nRows; row++)
  202.                 if (!CreateScrollBarCtrl(SBS_VERT, AFX_IDW_VSCROLL_FIRST + row))
  203.                     AfxThrowResourceException();
  204.  
  205.         if (m_bHasHScroll && m_bHasVScroll)
  206.             if (!CreateScrollBarCtrl(SBS_SIZEBOX|WS_DISABLED, AFX_IDW_SIZE_BOX))
  207.                 AfxThrowResourceException();
  208.     }
  209.     CATCH_ALL(e)
  210.     {
  211.         DestroyWindow(); // will clean up child windows
  212.         return FALSE;
  213.     }
  214.     END_CATCH_ALL
  215.  
  216.     return TRUE;
  217. }
  218.  
  219. /////////////////////////////////////////////////////////////////////////////
  220. // CSplitterWnd default creation of parts
  221.  
  222. // You must create ALL panes unless DYNAMIC_SPLIT is defined !
  223. //  Usually the splitter window is invisible when creating a pane
  224. BOOL CSplitterWnd::CreateView(int row, int col,
  225.     CRuntimeClass* pViewClass, SIZE sizeInit, CCreateContext* pContext)
  226. {
  227. #ifdef _DEBUG
  228.     ASSERT_VALID(this);
  229.     ASSERT(row >= 0 && row < m_nRows);
  230.     ASSERT(col >= 0 && col < m_nCols);
  231.     ASSERT(pViewClass != NULL);
  232.     ASSERT(AfxIsValidAddress(pViewClass, sizeof(CRuntimeClass)));
  233.  
  234.     if (GetDlgItem(IdFromRowCol(row, col)) != NULL)
  235.     {
  236.         TRACE2("Error: CreateView - pane already exists for row %d, col %d\n",
  237.             row, col);
  238.         ASSERT(FALSE);
  239.         return FALSE;
  240.     }
  241. #endif
  242.  
  243.     // set the initial size for that pane
  244.     m_pColInfo[col].nIdealSize = sizeInit.cx;
  245.     m_pRowInfo[row].nIdealSize = sizeInit.cy;
  246.  
  247.     BOOL bSendInitialUpdate = FALSE;
  248.  
  249.     CCreateContext contextT;
  250.     if (pContext == NULL)
  251.     {
  252.         // if no context specified, generate one from the currently selected
  253.         //  client if possible
  254.         CView* pOldView = GetParentFrame()->GetActiveView();
  255.         if (pOldView != NULL)
  256.         {
  257.             // set info about last pane
  258.             ASSERT(contextT.m_pCurrentFrame == NULL);
  259.             contextT.m_pLastView = pOldView;
  260.             contextT.m_pCurrentDoc = pOldView->GetDocument();
  261.             if (contextT.m_pCurrentDoc != NULL)
  262.                 contextT.m_pNewDocTemplate =
  263.                   contextT.m_pCurrentDoc->GetDocTemplate();
  264.         }
  265.         pContext = &contextT;
  266.         bSendInitialUpdate = TRUE;
  267.     }
  268.  
  269.     CWnd* pWnd;
  270.     TRY
  271.     {
  272.         pWnd = (CWnd*)pViewClass->CreateObject();
  273.         ASSERT(pWnd != NULL);
  274.     }
  275.     CATCH_ALL(e)
  276.     {
  277.         TRACE0("Out of memory creating a splitter pane\n");
  278.         return FALSE;
  279.     }
  280.     END_CATCH_ALL
  281.  
  282.     ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CWnd)));
  283.     ASSERT(pWnd->m_hWnd == NULL);       // not yet created
  284.  
  285.     // Create with the right size (wrong position)
  286.     CRect rect(CPoint(0,0), sizeInit);
  287.     if (!pWnd->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW,
  288.         rect, this, IdFromRowCol(row, col), pContext))
  289.     {
  290.         TRACE0("Warning: couldn't create client pane for splitter\n");
  291.             // pWnd will be cleaned up by PostNcDestroy
  292.         return FALSE;
  293.     }
  294.     ASSERT((int)_AfxGetDlgCtrlID(pWnd->m_hWnd) == IdFromRowCol(row, col));
  295.  
  296.     // send initial notification message
  297.     if (bSendInitialUpdate)
  298.         pWnd->SendMessage(WM_INITIALUPDATE);
  299.     return TRUE;
  300. }
  301.  
  302.  
  303. BOOL CSplitterWnd::CreateScrollBarCtrl(DWORD dwStyle, UINT nID)
  304. {
  305.     ASSERT_VALID(this);
  306.     ASSERT(m_hWnd != NULL);
  307.  
  308.     return (::CreateWindow("SCROLLBAR", NULL, dwStyle | WS_VISIBLE | WS_CHILD,
  309.         0, 0, 1, 1, m_hWnd, (HMENU)nID,
  310.         AfxGetInstanceHandle(), NULL) != NULL);
  311. }
  312.  
  313. int CSplitterWnd::IdFromRowCol(int row, int col) const
  314. {
  315.     ASSERT_VALID(this);
  316.     ASSERT(row >= 0);
  317.     ASSERT(row < m_nRows);
  318.     ASSERT(col >= 0);
  319.     ASSERT(col < m_nCols);
  320.  
  321.     return AFX_IDW_PANE_FIRST + row * 16 + col;
  322. }
  323.  
  324. /////////////////////////////////////////////////////////////////////////////
  325. // CSplitterWnd attributes
  326.  
  327. CWnd* CSplitterWnd::GetPane(int row, int col) const
  328. {
  329.     ASSERT_VALID(this);
  330.  
  331.     CWnd* pView = GetDlgItem(IdFromRowCol(row, col));
  332.     ASSERT(pView != NULL);
  333.         // panes can be a CWnd, but are usually CViews
  334.     return pView;
  335. }
  336.  
  337. BOOL CSplitterWnd::IsChildPane(CWnd* pWnd, int& row, int& col)
  338. {
  339.     ASSERT_VALID(this);
  340.     ASSERT_VALID(pWnd);
  341.  
  342.     UINT nID = _AfxGetDlgCtrlID(pWnd->m_hWnd);
  343.     if (IsChild(pWnd) && nID >= AFX_IDW_PANE_FIRST && nID <= AFX_IDW_PANE_LAST)
  344.     {
  345.         row = (nID - AFX_IDW_PANE_FIRST) / 16;
  346.         col = (nID - AFX_IDW_PANE_FIRST) % 16;
  347.         return TRUE;
  348.     }
  349.     else
  350.     {
  351.         row = col = -1;
  352.         return FALSE;
  353.     }
  354. }
  355.  
  356. /////////////////////////////////////////////////////////////////////////////
  357. // CSplitterWnd information access
  358.  
  359. // The get routines return the current size
  360. // The set routines set the ideal size
  361. //  RecalcLayout must be called to update current size
  362.  
  363. void CSplitterWnd::GetRowInfo(int row, int& cyCur, int& cyMin) const
  364. {
  365.     ASSERT_VALID(this);
  366.     ASSERT(row >= 0 && row < m_nRows);
  367.  
  368.     cyCur = m_pRowInfo[row].nCurSize;
  369.     cyMin = m_pRowInfo[row].nMinSize;
  370. }
  371.  
  372. void CSplitterWnd::SetRowInfo(int row, int cyIdeal, int cyMin)
  373. {
  374.     ASSERT_VALID(this);
  375.     ASSERT(row >= 0 && row < m_nRows);
  376.     ASSERT(cyIdeal >= 0);
  377.     ASSERT(cyMin >= 0);
  378.  
  379.     m_pRowInfo[row].nIdealSize = cyIdeal;
  380.     m_pRowInfo[row].nMinSize = cyMin;
  381. }
  382.  
  383. void CSplitterWnd::GetColumnInfo(int col, int& cxCur, int& cxMin) const
  384. {
  385.     ASSERT_VALID(this);
  386.     ASSERT(col >= 0 && col < m_nCols);
  387.  
  388.     cxCur = m_pColInfo[col].nCurSize;
  389.     cxMin = m_pColInfo[col].nMinSize;
  390. }
  391.  
  392. void CSplitterWnd::SetColumnInfo(int col, int cxIdeal, int cxMin)
  393. {
  394.     ASSERT_VALID(this);
  395.     ASSERT(col >= 0 && col < m_nCols);
  396.  
  397.     m_pColInfo[col].nIdealSize = cxIdeal;
  398.     m_pColInfo[col].nMinSize = cxMin;
  399. }
  400.  
  401. /////////////////////////////////////////////////////////////////////////////
  402. // CSplitterWnd client operations/overridables
  403.  
  404. void CSplitterWnd::DeleteView(int row, int col)
  405. {
  406.     ASSERT_VALID(this);
  407.  
  408.     // if active child is being deleted - activate next
  409.     CWnd* pPane = GetPane(row, col);
  410.     ASSERT(pPane->IsKindOf(RUNTIME_CLASS(CView)));
  411.     ASSERT(GetParentFrame() != NULL);
  412.     if (GetParentFrame()->GetActiveView() == pPane)
  413.         ActivateNext(FALSE);
  414.  
  415.     // default implementation assumes view will auto delete in PostNcDestroy
  416.     pPane->DestroyWindow();
  417. }
  418.  
  419. void CSplitterWnd::OnDrawSplitter(CDC* pDC, ESplitType nType,
  420.         const CRect& rectArg)
  421.     // if pDC == NULL then just invalidate
  422. {
  423.     if (pDC == NULL)
  424.     {
  425.         InvalidateRect(rectArg);
  426.         return;
  427.     }
  428.     ASSERT_VALID(pDC);
  429.  
  430.     // otherwise actually draw
  431.     CRect rect = rectArg;
  432.     HGDIOBJ hOldBrush;
  433.     if (nType == splitIntersection)
  434.     {
  435.         // prepare to fill the entire interesection
  436.         hOldBrush = pDC->SelectObject(afxData.hbrLtGray);
  437.     }
  438.     else
  439.     {
  440.         // bar or box drawn the same
  441.         // top or left hilite for 3D look
  442.         pDC->PatBlt(rect.left, rect.top, rect.Width(), 1, WHITENESS);
  443.         pDC->PatBlt(rect.left, rect.top, 1, rect.Height(), WHITENESS);
  444.  
  445.         // bottom or right shadow for 3D look
  446.         hOldBrush = pDC->SelectObject(afxData.hbrDkGray);
  447.         pDC->PatBlt(rect.right - 1, rect.top, 1, rect.Height(), PATCOPY);
  448.         pDC->PatBlt(rect.left, rect.bottom - 1, rect.Width(), 1, PATCOPY);
  449.  
  450.         // prepare to fill the rest with gray
  451.         pDC->SelectObject(afxData.hbrLtGray);
  452.         rect.InflateRect(-1, -1);
  453.     }
  454.     // fill the middle
  455.     pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY);
  456.     pDC->SelectObject(hOldBrush);
  457. }
  458.  
  459. /////////////////////////////////////////////////////////////////////////////
  460. // Dynamic row/col split etc
  461.  
  462. static int NEAR PASCAL CanSplitRowCol(CSplitterWnd::CRowColInfo* pInfoBefore,
  463.         int nBeforeSize, int nSizeSplitter)
  464.     // request to split Before row at point nBeforeSize
  465.     // returns size of new pane (nBeforeSize will be new size of Before pane)
  466.     // return -1 if not big enough
  467. {
  468.     ASSERT(pInfoBefore->nCurSize > 0);
  469.     ASSERT(pInfoBefore->nMinSize > 0);
  470.     ASSERT(nBeforeSize <= pInfoBefore->nCurSize);
  471.  
  472.     // space gets take from before pane (weird UI for > 2 splits)
  473.     if (nBeforeSize < pInfoBefore->nMinSize)
  474.     {
  475.         TRACE0("Warning: split too small to fit in a new pane\n");
  476.         return -1;
  477.     }
  478.  
  479.     int nNewSize = pInfoBefore->nCurSize - nBeforeSize - nSizeSplitter;
  480.     if (nBeforeSize < pInfoBefore->nMinSize)
  481.     {
  482.         TRACE0("Warning: split too small to shrink old pane\n");
  483.         return -1;
  484.     }
  485.     if (nNewSize < (pInfoBefore+1)->nMinSize)
  486.     {
  487.         TRACE0("Warning: split too small to create new pane\n");
  488.         return -1;
  489.     }
  490.     return nNewSize;
  491. }
  492.  
  493.  
  494. BOOL CSplitterWnd::SplitRow(int cyBefore)
  495. {
  496.     ASSERT_VALID(this);
  497.     ASSERT(GetStyle() & SPLS_DYNAMIC_SPLIT);
  498.     ASSERT(m_pDynamicViewClass != NULL);
  499.     ASSERT(m_nRows < m_nMaxRows);
  500.  
  501.     int rowNew = m_nRows;
  502.     int cyNew = CanSplitRowCol(&m_pRowInfo[rowNew-1], cyBefore, m_cySplitter);
  503.     if (cyNew == -1)
  504.         return FALSE;   // too small to split
  505.  
  506.     // create the scroll bar first (so new views can see that it is there)
  507.     if (m_bHasVScroll &&
  508.         !CreateScrollBarCtrl(SBS_VERT, AFX_IDW_VSCROLL_FIRST + rowNew))
  509.     {
  510.         TRACE0("Warning: SplitRow failed to create scroll bar\n");
  511.         return FALSE;
  512.     }
  513.  
  514.     m_nRows++;  // bump count during view creation
  515.  
  516.     // create new views to fill the new row (RecalcLayout will position)
  517.     for (int col = 0; col < m_nCols; col++)
  518.     {
  519.         CSize size(m_pColInfo[col].nCurSize, cyNew);
  520.         if (!CreateView(rowNew, col, m_pDynamicViewClass, size, NULL))
  521.         {
  522.             TRACE0("Warning: SplitRow failed to create new row\n");
  523.             // delete anything we partially created 'col' = # columns created
  524.             while (col > 0)
  525.                 DeleteView(rowNew, --col);
  526.             if (m_bHasVScroll)
  527.                 GetDlgItem(AFX_IDW_VSCROLL_FIRST + rowNew)->DestroyWindow();
  528.             m_nRows--;      // it didn't work out
  529.             return FALSE;
  530.         }
  531.     }
  532.  
  533.     // new parts created - resize and re-layout
  534.     m_pRowInfo[rowNew-1].nIdealSize = cyBefore;
  535.     m_pRowInfo[rowNew].nIdealSize = cyNew;
  536.     ASSERT(m_nRows == rowNew+1);
  537.     RecalcLayout();
  538.     return TRUE;
  539. }
  540.  
  541. BOOL CSplitterWnd::SplitColumn(int cxBefore)
  542. {
  543.     ASSERT_VALID(this);
  544.     ASSERT(GetStyle() & SPLS_DYNAMIC_SPLIT);
  545.     ASSERT(m_pDynamicViewClass != NULL);
  546.     ASSERT(m_nCols < m_nMaxCols);
  547.  
  548.     int colNew = m_nCols;
  549.     int cxNew = CanSplitRowCol(&m_pColInfo[colNew-1], cxBefore, m_cxSplitter);
  550.     if (cxNew == -1)
  551.         return FALSE;   // too small to split
  552.  
  553.     // create the scroll bar first (so new views can see that it is there)
  554.     if (m_bHasHScroll &&
  555.         !CreateScrollBarCtrl(SBS_HORZ, AFX_IDW_HSCROLL_FIRST + colNew))
  556.     {
  557.         TRACE0("Warning: SplitRow failed to create scroll bar\n");
  558.         return FALSE;
  559.     }
  560.  
  561.     m_nCols++;  // bump count during view creation
  562.  
  563.     // create new views to fill the new column (RecalcLayout will position)
  564.     for (int row = 0; row < m_nRows; row++)
  565.     {
  566.         CSize size(cxNew, m_pRowInfo[row].nCurSize);
  567.         if (!CreateView(row, colNew, m_pDynamicViewClass, size, NULL))
  568.         {
  569.             TRACE0("Warning: SplitColumn failed to create new column\n");
  570.             // delete anything we partially created 'col' = # columns created
  571.             while (row > 0)
  572.                 DeleteView(--row, colNew);
  573.             if (m_bHasHScroll)
  574.                 GetDlgItem(AFX_IDW_HSCROLL_FIRST + colNew)->DestroyWindow();
  575.             m_nCols--;      // it didn't work out
  576.             return FALSE;
  577.         }
  578.     }
  579.  
  580.     // new parts created - resize and re-layout
  581.     m_pColInfo[colNew-1].nIdealSize = cxBefore;
  582.     m_pColInfo[colNew].nIdealSize = cxNew;
  583.     ASSERT(m_nCols == colNew+1);
  584.     RecalcLayout();
  585.     return TRUE;
  586. }
  587.  
  588. void CSplitterWnd::DeleteRow(int row)
  589. {
  590.     ASSERT_VALID(this);
  591.     ASSERT(GetStyle() & SPLS_DYNAMIC_SPLIT);
  592.  
  593.     // default implementation deletes last row only
  594.     ASSERT(m_nRows > 1);
  595.     row = m_nRows - 1;
  596.     for (int col = 0; col < m_nCols; col++)
  597.         DeleteView(row, col);
  598.     if (m_bHasVScroll)
  599.         GetDlgItem(AFX_IDW_VSCROLL_FIRST + row)->DestroyWindow();
  600.     m_nRows--;
  601.     RecalcLayout();     // re-assign the space
  602. }
  603.  
  604. void CSplitterWnd::DeleteColumn(int col)
  605. {
  606.     ASSERT_VALID(this);
  607.     ASSERT(GetStyle() & SPLS_DYNAMIC_SPLIT);
  608.  
  609.     // default implementation deletes last column only
  610.     ASSERT(m_nCols > 1);
  611.     col = m_nCols - 1;
  612.     for (int row = 0; row < m_nRows; row++)
  613.         DeleteView(row, col);
  614.     if (m_bHasHScroll)
  615.         GetDlgItem(AFX_IDW_HSCROLL_FIRST + col)->DestroyWindow();
  616.     m_nCols--;
  617.     RecalcLayout();     // re-assign the space
  618. }
  619.  
  620. /////////////////////////////////////////////////////////////////////////////
  621. // CSplitterWnd tracking support
  622.  
  623. // like GetClientRect but inset by shared scrollbars
  624. void CSplitterWnd::GetInsideRect(CRect& rect) const
  625. {
  626.     ASSERT_VALID(this);
  627.  
  628.     GetClientRect(rect);
  629.     ASSERT(rect.left == 0 && rect.top == 0);
  630.     // subtract scrollbar clearance
  631.     if (m_bHasVScroll)
  632.         rect.right -= (afxData.cxVScroll - CX_BORDER);
  633.     if (m_bHasHScroll)
  634.         rect.bottom -= (afxData.cyHScroll - CY_BORDER);
  635. }
  636.  
  637. void CSplitterWnd::StartTracking(int ht)
  638. {
  639.     ASSERT_VALID(this);
  640.     if (ht == noHit)
  641.         return;
  642.  
  643.     GetInsideRect(m_rectLimit);
  644.         // GetHitRect will restrict 'm_rectLimit' as appropriate
  645.  
  646.     if (ht >= splitterIntersection1 && ht <= splitterIntersection225)
  647.     {
  648.         // split two directions (two tracking rectangles)
  649.         int row = (ht - splitterIntersection1) / 15;
  650.         int col = (ht - splitterIntersection1) % 15;
  651.  
  652.         GetHitRect(row + vSplitterBar1, m_rectTracker);
  653.         m_bTracking2 = TRUE;
  654.         GetHitRect(col + hSplitterBar1, m_rectTracker2);
  655.         m_ptTrackOffset.x = -(m_cxSplitter/2);
  656.         m_ptTrackOffset.y = -(m_cySplitter/2);
  657.     }
  658.     else if (ht == bothSplitterBox)
  659.     {
  660.         GetHitRect(vSplitterBox, m_rectTracker);
  661.         m_bTracking2 = TRUE;
  662.         GetHitRect(hSplitterBox, m_rectTracker2);
  663.     }
  664.     else
  665.     {
  666.         GetHitRect(ht, m_rectTracker);
  667.     }
  668.  
  669.     // adjust for border size
  670.     m_rectLimit.right -= CX_BORDER;
  671.     m_rectLimit.bottom -= CY_BORDER;
  672.  
  673.     SetCapture();
  674.     SetFocus();
  675.  
  676.     m_bTracking = TRUE;
  677.     OnInvertTracker(m_rectTracker);
  678.     if (m_bTracking2)
  679.         OnInvertTracker(m_rectTracker2);
  680.     m_htTrack = ht;
  681. }
  682.  
  683. void CSplitterWnd::TrackRowSize(int y, int row)
  684. {
  685.     ASSERT_VALID(this);
  686.     ASSERT(m_nRows > 1);
  687.  
  688.     CPoint pt(0, y);
  689.     ClientToScreen(&pt);
  690.     GetPane(row, 0)->ScreenToClient(&pt);
  691.     m_pRowInfo[row].nIdealSize = pt.y;      // new size
  692.     if (pt.y < m_pRowInfo[row].nMinSize)
  693.     {
  694.         // resized too small
  695.         m_pRowInfo[row].nIdealSize = 0; // make it go away
  696.         if (GetStyle() & SPLS_DYNAMIC_SPLIT)
  697.             DeleteRow(row);
  698.     }
  699.     else if (m_pRowInfo[row].nCurSize + m_pRowInfo[row+1].nCurSize
  700.             < pt.y + m_pRowInfo[row+1].nMinSize)
  701.     {
  702.         // not enough room for other pane
  703.         if (GetStyle() & SPLS_DYNAMIC_SPLIT)
  704.             DeleteRow(row);
  705.     }
  706. }
  707.  
  708.  
  709. void CSplitterWnd::TrackColumnSize(int x, int col)
  710. {
  711.     ASSERT_VALID(this);
  712.     ASSERT(m_nCols > 1);
  713.  
  714.     CPoint pt(x, 0);
  715.     ClientToScreen(&pt);
  716.     GetPane(0, col)->ScreenToClient(&pt);
  717.     m_pColInfo[col].nIdealSize = pt.x;      // new size
  718.     if (pt.x < m_pColInfo[col].nMinSize)
  719.     {
  720.         // resized too small
  721.         m_pColInfo[col].nIdealSize = 0; // make it go away
  722.         if (GetStyle() & SPLS_DYNAMIC_SPLIT)
  723.             DeleteColumn(col);
  724.     }
  725.     else if (m_pColInfo[col].nCurSize + m_pColInfo[col+1].nCurSize
  726.             < pt.x + m_pColInfo[col+1].nMinSize)
  727.     {
  728.         // not enough room for other pane
  729.         if (GetStyle() & SPLS_DYNAMIC_SPLIT)
  730.             DeleteColumn(col);
  731.     }
  732. }
  733.  
  734.  
  735. void CSplitterWnd::StopTracking(BOOL bAccept)
  736. {
  737.     ASSERT_VALID(this);
  738.  
  739.     if (!m_bTracking)
  740.         return;
  741.  
  742.     ReleaseCapture();
  743.     // erase tracker rectangle
  744.     OnInvertTracker(m_rectTracker);
  745.     if (m_bTracking2)
  746.         OnInvertTracker(m_rectTracker2);
  747.     m_bTracking = m_bTracking2 = FALSE;
  748.  
  749.     // save old active view
  750.     CFrameWnd* pParent = GetParentFrame();
  751.     ASSERT(pParent != NULL);    // must be in a frame
  752.     CView* pOldActiveView = pParent->GetActiveView();
  753.  
  754.     // m_rectTracker is set to the new splitter position (without border)
  755.     m_rectTracker.left -= CX_BORDER;
  756.     m_rectTracker.top -= CY_BORDER;
  757.         // adjust relative to where the border will be
  758.  
  759.     if (bAccept)
  760.     {
  761.         if (m_htTrack == vSplitterBox)
  762.         {
  763.             SplitRow(m_rectTracker.top);
  764.         }
  765.         else if (m_htTrack >= vSplitterBar1 && m_htTrack <= vSplitterBar15)
  766.         {
  767.             // set row height
  768.             TrackRowSize(m_rectTracker.top, m_htTrack - vSplitterBar1);
  769.             RecalcLayout();
  770.         }
  771.         else if (m_htTrack == hSplitterBox)
  772.         {
  773.             SplitColumn(m_rectTracker.left);
  774.         }
  775.         else if (m_htTrack >= hSplitterBar1 && m_htTrack <= hSplitterBar15)
  776.         {
  777.             // set column width
  778.             TrackColumnSize(m_rectTracker.left, m_htTrack - hSplitterBar1);
  779.             RecalcLayout();
  780.         }
  781.         else if (m_htTrack >= splitterIntersection1 &&
  782.             m_htTrack <= splitterIntersection225)
  783.         {
  784.             // set row height and column width
  785.             int row = (m_htTrack - splitterIntersection1) / 15;
  786.             int col = (m_htTrack - splitterIntersection1) % 15;
  787.  
  788.             TrackRowSize(m_rectTracker.top, row);
  789.             TrackColumnSize(m_rectTracker2.left, col);
  790.             RecalcLayout();
  791.         }
  792.         else if (m_htTrack == bothSplitterBox)
  793.         {
  794.             // rectTracker is vSplitter (splits rows)
  795.             // rectTracker2 is hSplitter (splits cols)
  796.             SplitRow(m_rectTracker.top);
  797.             SplitColumn(m_rectTracker2.left);
  798.         }
  799.     }
  800.  
  801.     if (pOldActiveView == pParent->GetActiveView())
  802.         pParent->SetActiveView(pOldActiveView); // re-activate
  803. }
  804.  
  805.  
  806. void CSplitterWnd::GetHitRect(int ht, CRect& rectHit)
  807. {
  808.     ASSERT_VALID(this);
  809.  
  810.     CRect rectClient;
  811.     GetClientRect(&rectClient);
  812.     ASSERT(rectClient.left == 0 && rectClient.top == 0);
  813.     int cx = rectClient.right;
  814.     int cy = rectClient.bottom;
  815.     int x = 0;
  816.     int y = 0;
  817.  
  818.     // hit rectangle does not include border
  819.     // m_rectLimit will be limited to valid tracking rect
  820.     // m_ptTrackOffset will be set to appropriate tracking offset
  821.     m_ptTrackOffset.x = 0;
  822.     m_ptTrackOffset.y = 0;
  823.  
  824.     if (ht == vSplitterBox)
  825.     {
  826.         cy = m_cySplitter;
  827.         m_ptTrackOffset.y = -(cy/2);
  828.         ASSERT(m_rectLimit.top == 0);
  829.         ASSERT(m_pRowInfo[0].nCurSize > 0);
  830.         m_rectLimit.bottom += m_ptTrackOffset.y - CY_BORDER;
  831.     }
  832.     else if (ht == hSplitterBox)
  833.     {
  834.         cx = m_cxSplitter;
  835.         m_ptTrackOffset.x = -(cx/2);
  836.         ASSERT(m_rectLimit.left == 0);
  837.         ASSERT(m_pColInfo[0].nCurSize > 0);
  838.         m_rectLimit.right += m_ptTrackOffset.x - CX_BORDER;
  839.     }
  840.     else if (ht >= vSplitterBar1 && ht <= vSplitterBar15)
  841.     {
  842.         cy = m_cySplitter;
  843.         m_ptTrackOffset.y = -(cy/2);
  844.         for (int row = 0; row < ht - vSplitterBar1; row++)
  845.             y += m_pRowInfo[row].nCurSize + CY_SPLITTER_GAP;
  846.         m_rectLimit.top = y;
  847.         y += m_pRowInfo[row].nCurSize + CY_BORDER;
  848.         m_rectLimit.bottom += m_ptTrackOffset.y - CY_BORDER;
  849.     }
  850.     else if (ht >= hSplitterBar1 && ht <= hSplitterBar15)
  851.     {
  852.         cx = m_cxSplitter;
  853.         m_ptTrackOffset.x = -(cx/2);
  854.         for (int col = 0; col < ht - hSplitterBar1; col++)
  855.             x += m_pColInfo[col].nCurSize + CX_SPLITTER_GAP;
  856.         m_rectLimit.left = x;
  857.         x += m_pColInfo[col].nCurSize + CX_BORDER;
  858.         m_rectLimit.right += m_ptTrackOffset.x - CX_BORDER;
  859.     }
  860.     else
  861.     {
  862.         TRACE1("Error: GetHitRect(%d): Not Found!\n", ht);
  863.         ASSERT(FALSE);
  864.     }
  865.  
  866.     rectHit.right = (rectHit.left = x) + cx;
  867.     rectHit.bottom = (rectHit.top = y) + cy;
  868. }
  869.  
  870.  
  871. int CSplitterWnd::HitTest(CPoint pt) const
  872. {
  873.     ASSERT_VALID(this);
  874.  
  875.     CRect rectClient;
  876.     GetClientRect(&rectClient);
  877.     ASSERT(rectClient.left == 0 && rectClient.top == 0);
  878.  
  879.     if (m_bHasVScroll && m_nRows < m_nMaxRows &&
  880.         CRect(rectClient.right - afxData.cxVScroll, 0,
  881.          rectClient.right, m_cySplitter + CY_BORDER).PtInRect(pt))
  882.     {
  883.         return vSplitterBox;
  884.     }
  885.  
  886.     if (m_bHasHScroll && m_nCols < m_nMaxCols &&
  887.         CRect(0, rectClient.bottom - afxData.cyHScroll,
  888.          m_cxSplitter + CX_BORDER, rectClient.bottom).PtInRect(pt))
  889.     {
  890.         return hSplitterBox;
  891.     }
  892.  
  893.     // for hit detect, include the border of splitters
  894.     CRect rect;
  895.     rect = rectClient;
  896.     rect.left = 0;
  897.     for (int col = 0; col < m_nCols - 1; col++)
  898.     {
  899.         rect.left += m_pColInfo[col].nCurSize;
  900.         rect.right = rect.left + CX_SPLITTER_GAP;
  901.         if (rect.PtInRect(pt))
  902.             break;
  903.         rect.left = rect.right;
  904.     }
  905.  
  906.     rect = rectClient;
  907.     rect.top = 0;
  908.     for (int row = 0; row < m_nRows - 1; row++)
  909.     {
  910.         rect.top += m_pRowInfo[row].nCurSize;
  911.         rect.bottom = rect.top + CY_SPLITTER_GAP;
  912.         if (rect.PtInRect(pt))
  913.             break;
  914.         rect.top = rect.bottom;
  915.     }
  916.  
  917.     // row and col set for hit splitter (if not hit will be past end)
  918.     if (col != m_nCols - 1)
  919.     {
  920.         if (row != m_nRows - 1)
  921.             return splitterIntersection1 + row * 15 + col;
  922.         return hSplitterBar1 + col;
  923.     }
  924.  
  925.     if (row != m_nRows - 1)
  926.         return vSplitterBar1 + row;
  927.  
  928.     return noHit;
  929. }
  930.  
  931. void CSplitterWnd::OnInvertTracker(const CRect& rect)
  932. {
  933.     ASSERT_VALID(this);
  934.     ASSERT(!rect.IsRectEmpty());
  935.     ASSERT((GetStyle() & WS_CLIPCHILDREN) == 0);
  936.  
  937.     // pat-blt without clip children on
  938.     CClientDC dc(this);
  939.     dc.PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), DSTINVERT);
  940. }
  941.  
  942. /////////////////////////////////////////////////////////////////////////////
  943. // CSplitterWnd commands
  944.  
  945. // Keyboard interface
  946. BOOL CSplitterWnd::DoKeyboardSplit()
  947. {
  948.     ASSERT_VALID(this);
  949.  
  950.     int ht;
  951.     if (m_nRows > 1 && m_nCols > 1)
  952.         ht = splitterIntersection1; // split existing row+col
  953.     else if (m_nRows > 1)
  954.         ht = vSplitterBar1;         // split existing row
  955.     else if (m_nCols > 1)
  956.         ht = hSplitterBar1;         // split existing col
  957.     else if (m_nMaxRows > 1 && m_nMaxCols > 1)
  958.         ht = bothSplitterBox;       // we can split both
  959.     else if (m_nMaxRows > 1)
  960.         ht = vSplitterBox;          // we can split rows
  961.     else if (m_nMaxCols > 1)
  962.         ht = hSplitterBox;          // we can split columns
  963.     else
  964.         return FALSE;               // can't split
  965.  
  966.     // move the mouse cursor to the center of the client rect
  967.     CRect rect;
  968.     GetClientRect(&rect);
  969.     ClientToScreen(&rect);
  970.     ::SetCursorPos(rect.left + rect.Width() / 2, rect.top + rect.Height() / 2);
  971.  
  972.     // start tracking
  973.     StartTracking(ht);
  974.     return TRUE;
  975. }
  976.  
  977. /////////////////////////////////////////////////////////////////////////////
  978. // Main drawing and layout
  979.  
  980. void CSplitterWnd::OnSize(UINT nType, int cx, int cy)
  981. {
  982.     if (nType != SIZE_MINIMIZED && cx > 0 && cy > 0)
  983.         RecalcLayout();
  984.     CWnd::OnSize(nType, cx, cy);
  985. }
  986.  
  987. // Generic routine:
  988. //  for X direction: pInfo = m_pColInfo, nMax = m_nMaxCols, nSize = cx
  989. //  for Y direction: pInfo = m_pRowInfo, nMax = m_nMaxRows, nSize = cy
  990. static void NEAR PASCAL LayoutRowCol(CSplitterWnd::CRowColInfo* pInfoArray,
  991.         int nMax, int nSize, int nSizeSplitter)
  992. {
  993.     ASSERT(pInfoArray != NULL);
  994.     ASSERT(nMax > 0);
  995.     ASSERT(nSizeSplitter > 0);
  996.  
  997.     CSplitterWnd::CRowColInfo* pInfo;
  998.     int i;
  999.  
  1000.     if (nSize < 0)
  1001.         nSize = 0;  // if really too small layout as zero size
  1002.  
  1003.     // start with ideal sizes
  1004.     for (i = 0, pInfo = pInfoArray; i < nMax-1; i++, pInfo++)
  1005.     {
  1006.         if (pInfo->nIdealSize < pInfo->nMinSize)
  1007.             pInfo->nIdealSize = 0;      // too small to see
  1008.         pInfo->nCurSize = pInfo->nIdealSize;
  1009.     }
  1010.     pInfo->nCurSize = INT_MAX;  // last row/column takes the rest
  1011.  
  1012.     for (i = 0, pInfo = pInfoArray; i < nMax; i++, pInfo++)
  1013.     {
  1014.         ASSERT(nSize >= 0);
  1015.         if (nSize == 0)
  1016.         {
  1017.             // no more room (set pane to be invisible)
  1018.             pInfo->nCurSize = 0;
  1019.             continue;       // don't worry about splitters
  1020.         }
  1021.         else if (nSize < pInfo->nMinSize && i != 0)
  1022.         {
  1023.             // additional panes below the recommended minimum size
  1024.             //   aren't shown and the size goes to the previous pane
  1025.             pInfo->nCurSize = 0;
  1026.  
  1027.             // previous pane already has room for splitter + CX_BORDER
  1028.             //   add remaining size and remove the extra border
  1029.             ASSERT(CX_BORDER == CY_BORDER);
  1030.             (pInfo-1)->nCurSize += nSize + CX_BORDER;
  1031.             nSize = 0;
  1032.         }
  1033.         else
  1034.         {
  1035.             // otherwise we can add the second pane
  1036.             ASSERT(nSize > 0);
  1037.             if (pInfo->nCurSize == 0)
  1038.             {
  1039.                 // too small to see
  1040.                 if (i != 0)
  1041.                     pInfo->nCurSize = 0;
  1042.             }
  1043.             else if (nSize < pInfo->nCurSize)
  1044.             {
  1045.                 // this row/col won't fit completely - make as small as possible
  1046.                 pInfo->nCurSize = nSize;
  1047.                 nSize = 0;
  1048.             }
  1049.             else
  1050.             {
  1051.                 // can fit everything
  1052.                 nSize -= pInfo->nCurSize;
  1053.             }
  1054.         }
  1055.  
  1056.         // see if we should add a splitter
  1057.         ASSERT(nSize >= 0);
  1058.         if (i != nMax - 1)
  1059.         {
  1060.             // should have a splitter
  1061.             if (nSize > nSizeSplitter)
  1062.             {
  1063.                 nSize -= nSizeSplitter; // leave room for splitter + border
  1064.                 ASSERT(nSize > 0);
  1065.             }
  1066.             else
  1067.             {
  1068.                 // not enough room - add left over less splitter size
  1069.                 ASSERT(CX_BORDER == CY_BORDER);
  1070.                 pInfo->nCurSize += nSize;
  1071.                 if (pInfo->nCurSize > (nSizeSplitter-CX_BORDER))
  1072.                     pInfo->nCurSize -= (nSizeSplitter-CX_BORDER);
  1073.                 nSize = 0;
  1074.             }
  1075.         }
  1076.     }
  1077.     ASSERT(nSize == 0); // all space should be allocated
  1078. }
  1079.  
  1080. // repositions client area of specified window
  1081. // assumes everything has WS_BORDER or is inset like it does
  1082. //  (includes scroll bars)
  1083. static HDWP NEAR PASCAL DeferClientPos(HDWP hDWP, CWnd* pWnd,
  1084.         int x, int y, int cx, int cy, BOOL bScrollBar)
  1085. {
  1086.     ASSERT(pWnd != NULL);
  1087.     ASSERT(pWnd->m_hWnd != NULL);
  1088.  
  1089.     if (bScrollBar)
  1090.     {
  1091.         // if there is enough room, draw scroll bar without border
  1092.         // if there is not enough room, set the WS_BORDER bit so that
  1093.         //   we will at least get a proper border drawn
  1094.         DWORD dwStyle = pWnd->GetStyle();
  1095.         BOOL bNeedBorder = (cx <= CX_BORDER || cy <= CY_BORDER);
  1096.         if (bNeedBorder)
  1097.             dwStyle |= WS_BORDER;
  1098.         else
  1099.             dwStyle &= ~WS_BORDER;
  1100.         ::SetWindowLong(pWnd->m_hWnd, GWL_STYLE, dwStyle);
  1101.     }
  1102.  
  1103.     // adjust for border size (even if zero client size)
  1104.     x -= CX_BORDER;
  1105.     y -= CY_BORDER;
  1106.     cx += 2 * CX_BORDER;
  1107.     cy += 2 * CY_BORDER;
  1108.  
  1109.     // first check if the new rectangle is the same as the current
  1110.     CRect rectOld;
  1111.     pWnd->GetWindowRect(rectOld);
  1112.     pWnd->GetParent()->ScreenToClient(&rectOld);
  1113.     if (rectOld.left == x && rectOld.top == y &&
  1114.         rectOld.Width() == cx && rectOld.Height() == cy)
  1115.     {
  1116.         return hDWP;        // nothing to do
  1117.     }
  1118.  
  1119.     return ::DeferWindowPos(hDWP, pWnd->m_hWnd, NULL,
  1120.         x, y, cx, cy, SWP_NOZORDER | SWP_NOACTIVATE);
  1121. }
  1122.  
  1123. void CSplitterWnd::RecalcLayout()
  1124. {
  1125.     ASSERT_VALID(this);
  1126.     ASSERT(m_nRows > 0 && m_nCols > 0);
  1127.             // must have at least one pane
  1128.  
  1129.     CRect rectInside;
  1130.     GetInsideRect(rectInside);
  1131.  
  1132.     // layout columns (restrict to possible sizes)
  1133.     LayoutRowCol(m_pColInfo, m_nCols, rectInside.right, CX_SPLITTER_GAP);
  1134.     LayoutRowCol(m_pRowInfo, m_nRows, rectInside.bottom, CY_SPLITTER_GAP);
  1135.  
  1136.     // adjust the panes (and optionally scroll bars)
  1137.  
  1138.     // give the hint for the maximum number of HWNDs
  1139.     HDWP hDWP = ::BeginDeferWindowPos((m_nCols + 1) * (m_nRows + 1));
  1140.  
  1141.     // reposition size box
  1142.     if (m_bHasHScroll && m_bHasVScroll)
  1143.     {
  1144.         CWnd* pScrollBar = GetDlgItem(AFX_IDW_SIZE_BOX);
  1145.         ASSERT(pScrollBar != NULL);
  1146.         hDWP = ::DeferWindowPos(hDWP,
  1147.             pScrollBar->m_hWnd, NULL,
  1148.             rectInside.right, rectInside.bottom,
  1149.             afxData.cxVScroll, afxData.cyHScroll,
  1150.             SWP_NOZORDER | SWP_NOACTIVATE);
  1151.     }
  1152.  
  1153.     // reposition scroll bars
  1154.     if (m_bHasHScroll)
  1155.     {
  1156.         int ySB = rectInside.bottom + CY_BORDER;
  1157.         int x = 0;
  1158.         for (int col = 0; col < m_nCols; col++)
  1159.         {
  1160.             CWnd* pScrollBar = GetDlgItem(AFX_IDW_HSCROLL_FIRST + col);
  1161.             ASSERT(pScrollBar != NULL);
  1162.             int cx = m_pColInfo[col].nCurSize;
  1163.             if (col == 0 && m_nCols < m_nMaxCols)
  1164.             {
  1165.                 x += m_cxSplitter + CX_BORDER;
  1166.                 cx -= m_cxSplitter + CX_BORDER;
  1167.             }
  1168.             hDWP = DeferClientPos(hDWP, pScrollBar, x, ySB,
  1169.                 cx, afxData.cyHScroll - 2 * CY_BORDER, TRUE);
  1170.             x += cx + CX_SPLITTER_GAP;
  1171.         }
  1172.     }
  1173.  
  1174.     if (m_bHasVScroll)
  1175.     {
  1176.         int xSB = rectInside.right + CX_BORDER;
  1177.         int y = 0;
  1178.         for (int row = 0; row < m_nRows; row++)
  1179.         {
  1180.             CWnd* pScrollBar = GetDlgItem(AFX_IDW_VSCROLL_FIRST + row);
  1181.             ASSERT(pScrollBar != NULL);
  1182.             int cy = m_pRowInfo[row].nCurSize;
  1183.             if (row == 0 && m_nRows < m_nMaxRows)
  1184.             {
  1185.                 y += m_cySplitter + CY_BORDER;
  1186.                 cy -= m_cySplitter + CY_BORDER;
  1187.             }
  1188.             hDWP = DeferClientPos(hDWP, pScrollBar, xSB, y,
  1189.                 afxData.cxVScroll - 2 * CX_BORDER, cy, TRUE);
  1190.             y += cy + CY_SPLITTER_GAP;
  1191.         }
  1192.     }
  1193.  
  1194.     //BLOCK: Reposition all the panes
  1195.     {
  1196.         int x = 0;
  1197.         for (int col = 0; col < m_nCols; col++)
  1198.         {
  1199.             int cx = m_pColInfo[col].nCurSize;
  1200.             int y = 0;
  1201.             for (int row = 0; row < m_nRows; row++)
  1202.             {
  1203.                 int cy = m_pRowInfo[row].nCurSize;
  1204.                 hDWP = DeferClientPos(hDWP, GetPane(row, col), x, y, cx, cy,
  1205.                     FALSE);
  1206.                 y += cy + CY_SPLITTER_GAP;
  1207.             }
  1208.             x += cx + CX_SPLITTER_GAP;
  1209.         }
  1210.     }
  1211.  
  1212.     // move them all
  1213.     if (hDWP == NULL)
  1214.     {
  1215.         TRACE0("Warning: low system resources, "
  1216.             "cannot reposition splitter panes\n");
  1217.     }
  1218.     ::EndDeferWindowPos(hDWP);
  1219.  
  1220.     DrawAllSplitBars(NULL, rectInside.right, rectInside.bottom);
  1221.             // NULL pDC just invalidates
  1222. }
  1223.  
  1224. void CSplitterWnd::DrawAllSplitBars(CDC* pDC, int cxInside, int cyInside)
  1225. {
  1226.     ASSERT_VALID(this);
  1227.  
  1228.     CRect rect;
  1229.     GetClientRect(rect);
  1230.     for (int col = 0; col < m_nCols - 1; col++)
  1231.     {
  1232.         rect.left += m_pColInfo[col].nCurSize + CX_BORDER;
  1233.         rect.right = rect.left + m_cxSplitter;
  1234.         if (rect.right > cxInside)
  1235.             break;      // stop if not fully visible
  1236.         OnDrawSplitter(pDC, splitBar, rect);
  1237.         rect.left = rect.right + CX_BORDER;
  1238.     }
  1239.  
  1240.     GetClientRect(rect);
  1241.     for (int row = 0; row < m_nRows - 1; row++)
  1242.     {
  1243.         rect.top += m_pRowInfo[row].nCurSize + CY_BORDER;
  1244.         rect.bottom = rect.top + m_cySplitter;
  1245.         if (rect.bottom > cyInside)
  1246.             break;      // stop if not fully visible
  1247.         OnDrawSplitter(pDC, splitBar, rect);
  1248.         rect.top = rect.bottom + CY_BORDER;
  1249.     }
  1250. }
  1251.  
  1252. void CSplitterWnd::OnPaint()
  1253. {
  1254.     ASSERT_VALID(this);
  1255.     CPaintDC dc(this);
  1256.  
  1257.     CRect rectClient;
  1258.     GetClientRect(&rectClient);
  1259.     CRect rectInside;
  1260.     GetInsideRect(rectInside);
  1261.  
  1262.     // draw the splitter boxes
  1263.     if (m_bHasVScroll && m_nRows < m_nMaxRows)
  1264.     {
  1265.         OnDrawSplitter(&dc, splitBox,
  1266.             CRect(rectInside.right + CX_BORDER, 0,
  1267.                 rectClient.right, m_cySplitter));
  1268.     }
  1269.  
  1270.     if (m_bHasHScroll && m_nCols < m_nMaxCols)
  1271.     {
  1272.         OnDrawSplitter(&dc, splitBox,
  1273.             CRect(0, rectInside.bottom + CY_BORDER,
  1274.                 m_cxSplitter, rectClient.bottom));
  1275.     }
  1276.  
  1277.     DrawAllSplitBars(&dc, rectInside.right, rectInside.bottom);
  1278.  
  1279.     dc.IntersectClipRect(rectInside);
  1280.     // draw splitter intersections (inside only)
  1281.     CRect rect;
  1282.     rect.top = 0;
  1283.     for (int row = 0; row < m_nRows - 1; row++)
  1284.     {
  1285.         rect.top += m_pRowInfo[row].nCurSize + CX_BORDER;
  1286.         rect.bottom = rect.top + m_cySplitter;
  1287.         rect.left = 0;
  1288.         for (int col = 0; col < m_nCols - 1; col++)
  1289.         {
  1290.             rect.left += m_pColInfo[col].nCurSize + CY_BORDER;
  1291.             rect.right = rect.left + m_cxSplitter;
  1292.             OnDrawSplitter(&dc, splitIntersection, rect);
  1293.             rect.left = rect.right + CY_BORDER;
  1294.         }
  1295.         rect.top = rect.bottom + CX_BORDER;
  1296.     }
  1297. }
  1298.  
  1299. BOOL CSplitterWnd::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
  1300. {
  1301.     if (pWnd == this && !m_bTracking)
  1302.         return TRUE;    // we will handle it in the mouse move
  1303.     return CWnd::OnSetCursor(pWnd, nHitTest, message);
  1304. }
  1305.  
  1306.  
  1307. void CSplitterWnd::OnMouseMove(UINT /*nFlags*/, CPoint pt)
  1308. {
  1309.     // cache of last needed cursor
  1310.     static HCURSOR NEAR hcurLast = NULL;
  1311.     static HCURSOR NEAR hcurDestroy = NULL;
  1312.     static UINT NEAR idcPrimaryLast = 0;        // store the primary IDC
  1313.  
  1314.     if (m_bTracking)
  1315.     {
  1316.         pt.Offset(m_ptTrackOffset); // pt is the upper right of hit detect
  1317.         // limit the point to the valid split range
  1318.         if (pt.y < m_rectLimit.top)
  1319.             pt.y = m_rectLimit.top;
  1320.         else if (pt.y > m_rectLimit.bottom)
  1321.             pt.y = m_rectLimit.bottom;
  1322.         if (pt.x < m_rectLimit.left)
  1323.             pt.x = m_rectLimit.left;
  1324.         else if (pt.x > m_rectLimit.right)
  1325.             pt.x = m_rectLimit.right;
  1326.  
  1327.         if (m_htTrack == vSplitterBox ||
  1328.             m_htTrack >= vSplitterBar1 && m_htTrack <= vSplitterBar15)
  1329.         {
  1330.             OnInvertTracker(m_rectTracker);
  1331.             m_rectTracker.top = pt.y;
  1332.             m_rectTracker.bottom = pt.y + m_cySplitter;
  1333.             OnInvertTracker(m_rectTracker);
  1334.         }
  1335.         else if (m_htTrack == hSplitterBox ||
  1336.             m_htTrack >= hSplitterBar1 && m_htTrack <= hSplitterBar15)
  1337.         {
  1338.             OnInvertTracker(m_rectTracker);
  1339.             m_rectTracker.left = pt.x;
  1340.             m_rectTracker.right = pt.x + m_cxSplitter;
  1341.             OnInvertTracker(m_rectTracker);
  1342.         }
  1343.         else if (m_htTrack == bothSplitterBox ||
  1344.            (m_htTrack >= splitterIntersection1 &&
  1345.             m_htTrack <= splitterIntersection225))
  1346.         {
  1347.             OnInvertTracker(m_rectTracker);
  1348.             m_rectTracker.top = pt.y;
  1349.             m_rectTracker.bottom = pt.y + m_cySplitter;
  1350.             OnInvertTracker(m_rectTracker);
  1351.  
  1352.             OnInvertTracker(m_rectTracker2);
  1353.             m_rectTracker2.left = pt.x;
  1354.             m_rectTracker2.right = pt.x + m_cxSplitter;
  1355.             OnInvertTracker(m_rectTracker2);
  1356.         }
  1357.     }
  1358.     else
  1359.     {
  1360.         int ht = HitTest(pt);
  1361.         UINT idcPrimary;        // app supplied cursor
  1362.         LPCSTR idcSecondary;    // system supplied cursor (MAKEINTRESOURCE)
  1363.  
  1364.         if (ht == vSplitterBox ||
  1365.             ht >= vSplitterBar1 && ht <= vSplitterBar15)
  1366.         {
  1367.             idcPrimary = AFX_IDC_VSPLITBAR;
  1368.             idcSecondary = IDC_SIZENS;
  1369.         }
  1370.         else if (ht == hSplitterBox ||
  1371.             ht >= hSplitterBar1 && ht <= hSplitterBar15)
  1372.         {
  1373.             idcPrimary = AFX_IDC_HSPLITBAR;
  1374.             idcSecondary = IDC_SIZEWE;
  1375.         }
  1376.         else if (ht == bothSplitterBox ||
  1377.             (ht >= splitterIntersection1 && ht <= splitterIntersection225))
  1378.         {
  1379.             idcPrimary = AFX_IDC_SMALLARROWS;
  1380.             idcSecondary = IDC_SIZE;
  1381.         }
  1382.         else
  1383.         {
  1384.             SetCursor(afxData.hcurArrow);
  1385.             idcPrimary = 0;     // don't use it
  1386.             idcSecondary = 0;   // don't use it
  1387.         }
  1388.  
  1389.         if (idcPrimary != 0)
  1390.         {
  1391.             HCURSOR hcurToDestroy = NULL;
  1392.             if (idcPrimary != idcPrimaryLast)
  1393.             {
  1394. #ifndef _AFXDLL
  1395.                 HINSTANCE hInst = AfxGetResourceHandle();
  1396. #else
  1397.                 HINSTANCE hInst = AfxFindResourceHandle(
  1398.                     MAKEINTRESOURCE(idcPrimary), RT_GROUP_CURSOR);
  1399. #endif
  1400.                 // load in another cursor
  1401.                 hcurToDestroy = hcurDestroy;
  1402.                 if ((hcurDestroy = hcurLast =
  1403.                    ::LoadCursor(hInst, MAKEINTRESOURCE(idcPrimary))) == NULL)
  1404.                 {
  1405.                     // will no look as good
  1406.                     TRACE0("Warning: Could not find splitter cursor -"
  1407.                       " using system provided alternative\n");
  1408.  
  1409.                     ASSERT(hcurDestroy == NULL);    // will not get destroyed
  1410.                     hcurLast = ::LoadCursor(NULL, idcSecondary);
  1411.                     ASSERT(hcurLast != NULL);
  1412.                 }
  1413.                 idcPrimaryLast = idcPrimary;
  1414.             }
  1415.             ASSERT(hcurLast != NULL);
  1416.             ::SetCursor(hcurLast);
  1417.             ASSERT(hcurLast != hcurToDestroy);
  1418.             if (hcurToDestroy != NULL)
  1419.                 ::DestroyCursor(hcurToDestroy); // destroy after being set
  1420.         }
  1421.     }
  1422. }
  1423.  
  1424.  
  1425. void CSplitterWnd::OnLButtonDown(UINT /*nFlags*/, CPoint pt)
  1426. {
  1427.     StartTracking(HitTest(pt));
  1428. }
  1429.  
  1430.  
  1431. void CSplitterWnd::OnLButtonDblClk(UINT /*nFlags*/, CPoint pt)
  1432. {
  1433.     int ht = HitTest(pt);
  1434.     CRect rect;
  1435.  
  1436.     StopTracking(FALSE);
  1437.  
  1438.     if ((GetStyle() & SPLS_DYNAMIC_SPLIT) == 0)
  1439.         return;     // do nothing if layout is static
  1440.  
  1441.     if (ht == vSplitterBox)
  1442.     {
  1443.         // half split
  1444.         SplitRow(m_pRowInfo[0].nCurSize / 2);
  1445.     }
  1446.     else if (ht == hSplitterBox)
  1447.     {
  1448.         // half split
  1449.         SplitColumn(m_pColInfo[0].nCurSize / 2);
  1450.     }
  1451.     else if (ht >= vSplitterBar1 && ht <= vSplitterBar15)
  1452.     {
  1453.         // delete the row
  1454.         DeleteRow(ht - vSplitterBar1);
  1455.     }
  1456.     else if (ht >= hSplitterBar1 && ht <= hSplitterBar15)
  1457.     {
  1458.         DeleteColumn(ht - hSplitterBar1);
  1459.     }
  1460.     else if (ht >= splitterIntersection1 && ht <= splitterIntersection225)
  1461.     {
  1462.         int row = (ht - splitterIntersection1) / 15;
  1463.         int col = (ht - splitterIntersection1) % 15;
  1464.         DeleteRow(row);
  1465.         DeleteColumn(col);
  1466.     }
  1467. }
  1468.  
  1469.  
  1470. void CSplitterWnd::OnLButtonUp(UINT /*nFlags*/, CPoint /*pt*/)
  1471. {
  1472.     StopTracking(TRUE);
  1473. }
  1474.  
  1475.  
  1476. void CSplitterWnd::OnCancelMode()
  1477. {
  1478.     StopTracking(FALSE);
  1479. }
  1480.  
  1481.  
  1482. void CSplitterWnd::OnKeyDown(UINT nChar, UINT /*nRepCnt*/, UINT /*nFlags*/)
  1483. {
  1484.     CPoint pt;
  1485.     int cz;
  1486.  
  1487.     GetCursorPos(&pt);
  1488.     cz = GetKeyState(VK_CONTROL) < 0 ? 1 : 16;
  1489.  
  1490.     switch (nChar)
  1491.     {
  1492.     case VK_ESCAPE:
  1493.         StopTracking(FALSE);
  1494.         break;
  1495.  
  1496.     case VK_RETURN:
  1497.         StopTracking(TRUE);
  1498.         break;
  1499.  
  1500.     case VK_LEFT:
  1501.         SetCursorPos(pt.x - cz, pt.y);
  1502.         break;
  1503.  
  1504.     case VK_RIGHT:
  1505.         SetCursorPos(pt.x + cz, pt.y);
  1506.         break;
  1507.  
  1508.     case VK_UP:
  1509.         SetCursorPos(pt.x, pt.y - cz);
  1510.         break;
  1511.  
  1512.     case VK_DOWN:
  1513.         SetCursorPos(pt.x, pt.y + cz);
  1514.         break;
  1515.  
  1516.     default:
  1517.         Default();  // pass other keys through
  1518.         break;
  1519.     }
  1520. }
  1521.  
  1522.  
  1523. /////////////////////////////////////////////////////////////////////////////
  1524. // Scroll messages
  1525.  
  1526.  
  1527. void CSplitterWnd::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
  1528. {
  1529.     ASSERT(pScrollBar != NULL);
  1530.     int col = _AfxGetDlgCtrlID(pScrollBar->m_hWnd) - AFX_IDW_HSCROLL_FIRST;
  1531.     ASSERT(col >= 0 && col < m_nMaxCols);
  1532.  
  1533.     ASSERT(m_nRows > 0);
  1534.     int nOldPos = pScrollBar->GetScrollPos();
  1535.     int nNewPos;
  1536.     for (int row = 0; row < m_nRows; row++)
  1537.     {
  1538.         GetPane(row, col)->SendMessage(WM_HSCROLL, nSBCode,
  1539.             MAKELONG(nPos, pScrollBar->m_hWnd));
  1540.         if (row == 0)
  1541.             nNewPos = pScrollBar->GetScrollPos();
  1542. #ifdef _DEBUG
  1543.         if (pScrollBar->GetScrollPos() != nNewPos)
  1544.         {
  1545.             TRACE0("Warning: scroll panes setting different scroll positions\n");
  1546.             // stick with the last one set
  1547.         }
  1548. #endif //_DEBUG
  1549.         // set the scroll pos to the value it was originally for the next pane
  1550.         if (row < m_nRows - 1)
  1551.             pScrollBar->SetScrollPos(nOldPos, FALSE);
  1552.     }
  1553. }
  1554.  
  1555. void CSplitterWnd::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
  1556. {
  1557.     ASSERT(pScrollBar != NULL);
  1558.     int row = _AfxGetDlgCtrlID(pScrollBar->m_hWnd) - AFX_IDW_VSCROLL_FIRST;
  1559.     ASSERT(row >= 0 && row < m_nMaxRows);
  1560.  
  1561.     ASSERT(m_nCols > 0);
  1562.     int nOldPos = pScrollBar->GetScrollPos();
  1563.     int nNewPos;
  1564.     for (int col = 0; col < m_nCols; col++)
  1565.     {
  1566.         GetPane(row, col)->SendMessage(WM_VSCROLL, nSBCode,
  1567.             MAKELONG(nPos, pScrollBar->m_hWnd));
  1568.         if (col == 0)
  1569.             nNewPos = pScrollBar->GetScrollPos();
  1570. #ifdef _DEBUG
  1571.         if (pScrollBar->GetScrollPos() != nNewPos)
  1572.         {
  1573.             TRACE0("Warning: scroll panes setting different scroll positions\n");
  1574.             // stick with the last one set
  1575.         }
  1576. #endif //_DEBUG
  1577.         // set the scroll pos to the value it was originally for the next pane
  1578.         if (col < m_nCols - 1)
  1579.             pScrollBar->SetScrollPos(nOldPos, FALSE);
  1580.     }
  1581. }
  1582.  
  1583. /////////////////////////////////////////////////////////////////////////////
  1584. // Focus control and control over the current pane/child
  1585.  
  1586. BOOL CSplitterWnd::CanActivateNext(BOOL)
  1587. {
  1588.     ASSERT_VALID(this);
  1589.  
  1590.     ASSERT(GetParentFrame() != NULL);
  1591.     if (GetParentFrame()->GetActiveView() == NULL)
  1592.     {
  1593.         TRACE0("Warning: Can't go to next pane - there is no current pane\n");
  1594.         return FALSE;
  1595.     }
  1596.     ASSERT(m_nRows != 0);
  1597.     ASSERT(m_nCols != 0);
  1598.     // if more than 1x1 we can go to the next or prev pane
  1599.     return (m_nRows > 1) || (m_nCols > 1);
  1600. }
  1601.  
  1602. void CSplitterWnd::ActivateNext(BOOL bPrev)
  1603. {
  1604.     ASSERT_VALID(this);
  1605.  
  1606.     CFrameWnd* pFrame = GetParentFrame();
  1607.     ASSERT(pFrame != NULL);
  1608.  
  1609.     CView* pActiveView = pFrame->GetActiveView();
  1610.     if (pActiveView == NULL)
  1611.     {
  1612.         TRACE0("Warning: Cannot go to next pane - there is no current view\n");
  1613.         return;
  1614.     }
  1615.     // find the coordinate of the current pane
  1616.     int row, col;
  1617.     if (!IsChildPane(pActiveView, row, col))
  1618.     {
  1619.         TRACE0("Warning: Cannot go to next pane - active view is not a pane\n");
  1620.         return;
  1621.     }
  1622.     ASSERT(row >= 0 && row < m_nRows);
  1623.     ASSERT(col >= 0 && col < m_nCols);
  1624.  
  1625.     if (bPrev)
  1626.     {
  1627.         // prev
  1628.         if (--col < 0)
  1629.         {
  1630.             col = m_nCols - 1;
  1631.             if (--row < 0)
  1632.                 row = m_nRows - 1;
  1633.         }
  1634.     }
  1635.     else
  1636.     {
  1637.         // next
  1638.         if (++col >= m_nCols)
  1639.         {
  1640.             col = 0;
  1641.             if (++row >= m_nRows)
  1642.                 row = 0;
  1643.         }
  1644.     }
  1645.     
  1646.     CWnd* pNextPane = GetPane(row, col);
  1647.     if (pNextPane->IsKindOf(RUNTIME_CLASS(CView)))
  1648.         pFrame->SetActiveView((CView*)pNextPane);
  1649.     else
  1650.         TRACE0("Warning: Cannot go to next pane - next pane is not a CView\n");
  1651. }
  1652.  
  1653. /////////////////////////////////////////////////////////////////////////////
  1654. // CSplitterWnd diagnostics
  1655.  
  1656. #ifdef _DEBUG
  1657. void CSplitterWnd::AssertValid() const
  1658. {
  1659.     CWnd::AssertValid();
  1660.     ASSERT(m_nMaxRows >= 1);
  1661.     ASSERT(m_nMaxCols >= 1);
  1662.     ASSERT(m_nMaxCols > 1 || m_nMaxRows > 1);       // 1x1 is not permitted
  1663.     ASSERT(m_nRows >= 1);
  1664.     ASSERT(m_nCols >= 1);
  1665.     ASSERT(m_nRows <= m_nMaxRows);
  1666.     ASSERT(m_nCols <= m_nMaxCols);
  1667. }
  1668.  
  1669. void CSplitterWnd::Dump(CDumpContext& dc) const
  1670. {
  1671.     ASSERT_VALID(this);
  1672.  
  1673.     CWnd::Dump(dc);
  1674.     if (m_pDynamicViewClass == NULL)
  1675.         AFX_DUMP0(dc, "\nm_pDynamicViewClass = NULL");
  1676.     else
  1677.         AFX_DUMP1(dc, "\nm_pDynamicViewClass = ", m_pDynamicViewClass->m_lpszClassName);
  1678.     AFX_DUMP1(dc, "\nm_nMaxRows = ", m_nMaxRows);
  1679.     AFX_DUMP1(dc, "\nm_nMaxCols = ", m_nMaxCols);
  1680.     AFX_DUMP1(dc, "\nm_nRows = ", m_nRows);
  1681.     AFX_DUMP1(dc, "\nm_nCols = ", m_nCols);
  1682.     AFX_DUMP1(dc, "\nm_bHasHScroll = ", m_bHasHScroll);
  1683.     AFX_DUMP1(dc, "\nm_bHasVScroll = ", m_bHasVScroll);
  1684.     AFX_DUMP1(dc, "\nm_cxSplitter = ", m_cxSplitter);
  1685.     AFX_DUMP1(dc, "\nm_cySplitter = ", m_cySplitter);
  1686.     if (m_bTracking)
  1687.     {
  1688.         AFX_DUMP1(dc, "\nTRACKING: m_htTrack = ", m_htTrack);
  1689.         AFX_DUMP1(dc, "\nm_rectLimit = ", m_rectLimit);
  1690.         AFX_DUMP1(dc, "\nm_ptTrackOffset = ", m_ptTrackOffset);
  1691.         AFX_DUMP1(dc, "\nm_rectTracker = ", m_rectTracker);
  1692.         if (m_bTracking2)
  1693.             AFX_DUMP1(dc, "\nm_rectTracker2 = ", m_rectTracker2);
  1694.     }
  1695. }
  1696. #endif
  1697.  
  1698. /////////////////////////////////////////////////////////////////////////////
  1699.