home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / winsplit.cpp < prev    next >
C/C++ Source or Header  |  1998-06-16  |  64KB  |  2,367 lines

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