home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / viewscrl.cpp < prev    next >
C/C++ Source or Header  |  1998-06-16  |  27KB  |  1,059 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_CORE2_SEG
  14. #pragma code_seg(AFX_CORE2_SEG)
  15. #endif
  16.  
  17. #ifdef _DEBUG
  18. #undef THIS_FILE
  19. static char THIS_FILE[] = __FILE__;
  20. #endif
  21.  
  22. /////////////////////////////////////////////////////////////////////////////
  23. // CScrollView
  24.  
  25. BEGIN_MESSAGE_MAP(CScrollView, CView)
  26.     //{{AFX_MSG_MAP(CScrollView)
  27.     ON_WM_SIZE()
  28.     ON_WM_HSCROLL()
  29.     ON_WM_VSCROLL()
  30.     ON_WM_MOUSEWHEEL()
  31.     //}}AFX_MSG_MAP
  32. END_MESSAGE_MAP()
  33.  
  34. // Special mapping modes just for CScrollView implementation
  35. #define MM_NONE             0
  36. #define MM_SCALETOFIT       (-1)
  37.     // standard GDI mapping modes are > 0
  38.  
  39. extern BOOL _afxGotScrollLines; // defined in wincore.cpp
  40.  
  41. UINT PASCAL _AfxGetMouseScrollLines()
  42. {
  43.     static UINT uCachedScrollLines;
  44.  
  45.     // if we've already got it and we're not refreshing,
  46.     // return what we've already got
  47.  
  48.     if (_afxGotScrollLines)
  49.         return uCachedScrollLines;
  50.  
  51.     // see if we can find the mouse window
  52.  
  53.     _afxGotScrollLines = TRUE;
  54.  
  55.     static UINT msgGetScrollLines;
  56.     static WORD nRegisteredMessage;
  57.  
  58.     if (nRegisteredMessage == 0)
  59.     {
  60.         msgGetScrollLines = ::RegisterWindowMessage(MSH_SCROLL_LINES);
  61.         if (msgGetScrollLines == 0)
  62.             nRegisteredMessage = 1;     // couldn't register!  never try again
  63.         else
  64.             nRegisteredMessage = 2;     // it worked: use it
  65.     }
  66.  
  67.     if (nRegisteredMessage == 2)
  68.     {
  69.         HWND hwMouseWheel = NULL;
  70.         hwMouseWheel = FindWindow(MSH_WHEELMODULE_CLASS, MSH_WHEELMODULE_TITLE);
  71.         if (hwMouseWheel && msgGetScrollLines)
  72.         {
  73.             uCachedScrollLines = (UINT)
  74.                 ::SendMessage(hwMouseWheel, msgGetScrollLines, 0, 0);
  75.             return uCachedScrollLines;
  76.         }
  77.     }
  78.  
  79.     // couldn't use the window -- try system settings
  80.     uCachedScrollLines = 3; // reasonable default
  81.     if (!afxData.bWin4)
  82.     {
  83.         HKEY hKey;
  84.         if (RegOpenKeyEx(HKEY_CURRENT_USER,  _T("Control Panel\\Desktop"),
  85.                 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
  86.         {
  87.             TCHAR szData[128];
  88.             DWORD dwKeyDataType;
  89.             DWORD dwDataBufSize = _countof(szData);
  90.  
  91.             if (RegQueryValueEx(hKey, _T("WheelScrollLines"), NULL, &dwKeyDataType,
  92.                     (LPBYTE) &szData, &dwDataBufSize) == ERROR_SUCCESS)
  93.             {
  94.                 uCachedScrollLines = _tcstoul(szData, NULL, 10);
  95.             }
  96.             RegCloseKey(hKey);
  97.         }
  98.     }
  99.     else if (!afxData.bWin95)
  100.     {
  101.         ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &uCachedScrollLines, 0);
  102.     }
  103.  
  104.     return uCachedScrollLines;
  105. }
  106.  
  107. /////////////////////////////////////////////////////////////////////////////
  108. // CScrollView construction/destruction
  109.  
  110. CScrollView::CScrollView()
  111. {
  112.     // Init everything to zero
  113.     AFX_ZERO_INIT_OBJECT(CView);
  114.  
  115.     m_nMapMode = MM_NONE;
  116. }
  117.  
  118. CScrollView::~CScrollView()
  119. {
  120. }
  121.  
  122. /////////////////////////////////////////////////////////////////////////////
  123. // CScrollView painting
  124.  
  125. void CScrollView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
  126. {
  127.     ASSERT_VALID(pDC);
  128.  
  129. #ifdef _DEBUG
  130.     if (m_nMapMode == MM_NONE)
  131.     {
  132.         TRACE0("Error: must call SetScrollSizes() or SetScaleToFitSize()");
  133.         TRACE0("\tbefore painting scroll view.\n");
  134.         ASSERT(FALSE);
  135.         return;
  136.     }
  137. #endif //_DEBUG
  138.     ASSERT(m_totalDev.cx >= 0 && m_totalDev.cy >= 0);
  139.     switch (m_nMapMode)
  140.     {
  141.     case MM_SCALETOFIT:
  142.         pDC->SetMapMode(MM_ANISOTROPIC);
  143.         pDC->SetWindowExt(m_totalLog);  // window is in logical coordinates
  144.         pDC->SetViewportExt(m_totalDev);
  145.         if (m_totalDev.cx == 0 || m_totalDev.cy == 0)
  146.             TRACE0("Warning: CScrollView scaled to nothing.\n");
  147.         break;
  148.  
  149.     default:
  150.         ASSERT(m_nMapMode > 0);
  151.         pDC->SetMapMode(m_nMapMode);
  152.         break;
  153.     }
  154.  
  155.     CPoint ptVpOrg(0, 0);       // assume no shift for printing
  156.     if (!pDC->IsPrinting())
  157.     {
  158.         ASSERT(pDC->GetWindowOrg() == CPoint(0,0));
  159.  
  160.         // by default shift viewport origin in negative direction of scroll
  161.         ptVpOrg = -GetDeviceScrollPosition();
  162.  
  163.         if (m_bCenter)
  164.         {
  165.             CRect rect;
  166.             GetClientRect(&rect);
  167.  
  168.             // if client area is larger than total device size,
  169.             // override scroll positions to place origin such that
  170.             // output is centered in the window
  171.             if (m_totalDev.cx < rect.Width())
  172.                 ptVpOrg.x = (rect.Width() - m_totalDev.cx) / 2;
  173.             if (m_totalDev.cy < rect.Height())
  174.                 ptVpOrg.y = (rect.Height() - m_totalDev.cy) / 2;
  175.         }
  176.     }
  177.     pDC->SetViewportOrg(ptVpOrg);
  178.  
  179.     CView::OnPrepareDC(pDC, pInfo);     // For default Printing behavior
  180. }
  181.  
  182. /////////////////////////////////////////////////////////////////////////////
  183. // Set mode and scaling/scrolling sizes
  184.  
  185. void CScrollView::SetScaleToFitSize(SIZE sizeTotal)
  186. {
  187.     // Note: It is possible to set sizeTotal members to negative values to
  188.     //  effectively invert either the X or Y axis.
  189.  
  190.     ASSERT(m_hWnd != NULL);
  191.     m_nMapMode = MM_SCALETOFIT;     // special internal value
  192.     m_totalLog = sizeTotal;
  193.  
  194.     // reset and turn any scroll bars off
  195.     if (m_hWnd != NULL && (GetStyle() & (WS_HSCROLL|WS_VSCROLL)))
  196.     {
  197.         SetScrollPos(SB_HORZ, 0);
  198.         SetScrollPos(SB_VERT, 0);
  199.         EnableScrollBarCtrl(SB_BOTH, FALSE);
  200.         ASSERT((GetStyle() & (WS_HSCROLL|WS_VSCROLL)) == 0);
  201.     }
  202.  
  203.     CRect rectT;
  204.     GetClientRect(rectT);
  205.     m_totalDev = rectT.Size();
  206.  
  207.     if (m_hWnd != NULL)
  208.     {
  209.         // window has been created, invalidate
  210.         UpdateBars();
  211.         Invalidate(TRUE);
  212.     }
  213. }
  214.  
  215. const AFX_DATADEF SIZE CScrollView::sizeDefault = {0,0};
  216.  
  217. void CScrollView::SetScrollSizes(int nMapMode, SIZE sizeTotal,
  218.     const SIZE& sizePage, const SIZE& sizeLine)
  219. {
  220.     ASSERT(sizeTotal.cx >= 0 && sizeTotal.cy >= 0);
  221.     ASSERT(nMapMode > 0);
  222.     ASSERT(nMapMode != MM_ISOTROPIC && nMapMode != MM_ANISOTROPIC);
  223.  
  224.     int nOldMapMode = m_nMapMode;
  225.     m_nMapMode = nMapMode;
  226.     m_totalLog = sizeTotal;
  227.  
  228.     //BLOCK: convert logical coordinate space to device coordinates
  229.     {
  230.         CWindowDC dc(NULL);
  231.         ASSERT(m_nMapMode > 0);
  232.         dc.SetMapMode(m_nMapMode);
  233.  
  234.         // total size
  235.         m_totalDev = m_totalLog;
  236.         dc.LPtoDP((LPPOINT)&m_totalDev);
  237.         m_pageDev = sizePage;
  238.         dc.LPtoDP((LPPOINT)&m_pageDev);
  239.         m_lineDev = sizeLine;
  240.         dc.LPtoDP((LPPOINT)&m_lineDev);
  241.         if (m_totalDev.cy < 0)
  242.             m_totalDev.cy = -m_totalDev.cy;
  243.         if (m_pageDev.cy < 0)
  244.             m_pageDev.cy = -m_pageDev.cy;
  245.         if (m_lineDev.cy < 0)
  246.             m_lineDev.cy = -m_lineDev.cy;
  247.     } // release DC here
  248.  
  249.     // now adjust device specific sizes
  250.     ASSERT(m_totalDev.cx >= 0 && m_totalDev.cy >= 0);
  251.     if (m_pageDev.cx == 0)
  252.         m_pageDev.cx = m_totalDev.cx / 10;
  253.     if (m_pageDev.cy == 0)
  254.         m_pageDev.cy = m_totalDev.cy / 10;
  255.     if (m_lineDev.cx == 0)
  256.         m_lineDev.cx = m_pageDev.cx / 10;
  257.     if (m_lineDev.cy == 0)
  258.         m_lineDev.cy = m_pageDev.cy / 10;
  259.  
  260.     if (m_hWnd != NULL)
  261.     {
  262.         // window has been created, invalidate now
  263.         UpdateBars();
  264.         if (nOldMapMode != m_nMapMode)
  265.             Invalidate(TRUE);
  266.     }
  267. }
  268.  
  269. /////////////////////////////////////////////////////////////////////////////
  270. // Getting information
  271.  
  272. CPoint CScrollView::GetScrollPosition() const   // logical coordinates
  273. {
  274.     if (m_nMapMode == MM_SCALETOFIT)
  275.     {
  276.         return CPoint(0, 0);    // must be 0,0
  277.     }
  278.  
  279.     CPoint pt = GetDeviceScrollPosition();
  280.     // pt may be negative if m_bCenter is set
  281.  
  282.     if (m_nMapMode != MM_TEXT)
  283.     {
  284.         ASSERT(m_nMapMode > 0); // must be set
  285.         CWindowDC dc(NULL);
  286.         dc.SetMapMode(m_nMapMode);
  287.         dc.DPtoLP((LPPOINT)&pt);
  288.     }
  289.     return pt;
  290. }
  291.  
  292. void CScrollView::ScrollToPosition(POINT pt)    // logical coordinates
  293. {
  294.     ASSERT(m_nMapMode > 0);     // not allowed for shrink to fit
  295.     if (m_nMapMode != MM_TEXT)
  296.     {
  297.         CWindowDC dc(NULL);
  298.         dc.SetMapMode(m_nMapMode);
  299.         dc.LPtoDP((LPPOINT)&pt);
  300.     }
  301.  
  302.     // now in device coordinates - limit if out of range
  303.     int xMax = GetScrollLimit(SB_HORZ);
  304.     int yMax = GetScrollLimit(SB_VERT);
  305.     if (pt.x < 0)
  306.         pt.x = 0;
  307.     else if (pt.x > xMax)
  308.         pt.x = xMax;
  309.     if (pt.y < 0)
  310.         pt.y = 0;
  311.     else if (pt.y > yMax)
  312.         pt.y = yMax;
  313.  
  314.     ScrollToDevicePosition(pt);
  315. }
  316.  
  317. CPoint CScrollView::GetDeviceScrollPosition() const
  318. {
  319.     CPoint pt(GetScrollPos(SB_HORZ), GetScrollPos(SB_VERT));
  320.     ASSERT(pt.x >= 0 && pt.y >= 0);
  321.  
  322.     if (m_bCenter)
  323.     {
  324.         CRect rect;
  325.         GetClientRect(&rect);
  326.  
  327.         // if client area is larger than total device size,
  328.         // the scroll positions are overridden to place origin such that
  329.         // output is centered in the window
  330.         // GetDeviceScrollPosition() must reflect this
  331.  
  332.         if (m_totalDev.cx < rect.Width())
  333.             pt.x = -((rect.Width() - m_totalDev.cx) / 2);
  334.         if (m_totalDev.cy < rect.Height())
  335.             pt.y = -((rect.Height() - m_totalDev.cy) / 2);
  336.     }
  337.  
  338.     return pt;
  339. }
  340.  
  341. void CScrollView::GetDeviceScrollSizes(int& nMapMode, SIZE& sizeTotal,
  342.             SIZE& sizePage, SIZE& sizeLine) const
  343. {
  344.     if (m_nMapMode <= 0)
  345.         TRACE0("Warning: CScrollView::GetDeviceScrollSizes returning invalid mapping mode.\n");
  346.     nMapMode = m_nMapMode;
  347.     sizeTotal = m_totalDev;
  348.     sizePage = m_pageDev;
  349.     sizeLine = m_lineDev;
  350. }
  351.  
  352. void CScrollView::ScrollToDevicePosition(POINT ptDev)
  353. {
  354.     ASSERT(ptDev.x >= 0);
  355.     ASSERT(ptDev.y >= 0);
  356.  
  357.     // Note: ScrollToDevicePosition can and is used to scroll out-of-range
  358.     //  areas as far as CScrollView is concerned -- specifically in
  359.     //  the print-preview code.  Since OnScrollBy makes sure the range is
  360.     //  valid, ScrollToDevicePosition does not vector through OnScrollBy.
  361.  
  362.     int xOrig = GetScrollPos(SB_HORZ);
  363.     SetScrollPos(SB_HORZ, ptDev.x);
  364.     int yOrig = GetScrollPos(SB_VERT);
  365.     SetScrollPos(SB_VERT, ptDev.y);
  366.     ScrollWindow(xOrig - ptDev.x, yOrig - ptDev.y);
  367. }
  368.  
  369. /////////////////////////////////////////////////////////////////////////////
  370. // Other helpers
  371.  
  372. void CScrollView::FillOutsideRect(CDC* pDC, CBrush* pBrush)
  373. {
  374.     ASSERT_VALID(pDC);
  375.     ASSERT_VALID(pBrush);
  376.  
  377.     // fill rect outside the image
  378.     CRect rect;
  379.     GetClientRect(rect);
  380.     ASSERT(rect.left == 0 && rect.top == 0);
  381.     rect.left = m_totalDev.cx;
  382.     if (!rect.IsRectEmpty())
  383.         pDC->FillRect(rect, pBrush);    // vertical strip along the side
  384.     rect.left = 0;
  385.     rect.right = m_totalDev.cx;
  386.     rect.top = m_totalDev.cy;
  387.     if (!rect.IsRectEmpty())
  388.         pDC->FillRect(rect, pBrush);    // horizontal strip along the bottom
  389. }
  390.  
  391. void CScrollView::ResizeParentToFit(BOOL bShrinkOnly)
  392. {
  393.     // adjust parent rect so client rect is appropriate size
  394.     ASSERT(m_nMapMode != MM_NONE);  // mapping mode must be known
  395.  
  396.     // determine current size of the client area as if no scrollbars present
  397.     CRect rectClient;
  398.     GetWindowRect(rectClient);
  399.     CRect rect = rectClient;
  400.     CalcWindowRect(rect);
  401.     rectClient.left += rectClient.left - rect.left;
  402.     rectClient.top += rectClient.top - rect.top;
  403.     rectClient.right -= rect.right - rectClient.right;
  404.     rectClient.bottom -= rect.bottom - rectClient.bottom;
  405.     rectClient.OffsetRect(-rectClient.left, -rectClient.top);
  406.     ASSERT(rectClient.left == 0 && rectClient.top == 0);
  407.  
  408.     // determine desired size of the view
  409.     CRect rectView(0, 0, m_totalDev.cx, m_totalDev.cy);
  410.     if (bShrinkOnly)
  411.     {
  412.         if (rectClient.right <= m_totalDev.cx)
  413.             rectView.right = rectClient.right;
  414.         if (rectClient.bottom <= m_totalDev.cy)
  415.             rectView.bottom = rectClient.bottom;
  416.     }
  417.     CalcWindowRect(rectView, CWnd::adjustOutside);
  418.     rectView.OffsetRect(-rectView.left, -rectView.top);
  419.     ASSERT(rectView.left == 0 && rectView.top == 0);
  420.     if (bShrinkOnly)
  421.     {
  422.         if (rectClient.right <= m_totalDev.cx)
  423.             rectView.right = rectClient.right;
  424.         if (rectClient.bottom <= m_totalDev.cy)
  425.             rectView.bottom = rectClient.bottom;
  426.     }
  427.  
  428.     // dermine and set size of frame based on desired size of view
  429.     CRect rectFrame;
  430.     CFrameWnd* pFrame = GetParentFrame();
  431.     ASSERT_VALID(pFrame);
  432.     pFrame->GetWindowRect(rectFrame);
  433.     CSize size = rectFrame.Size();
  434.     size.cx += rectView.right - rectClient.right;
  435.     size.cy += rectView.bottom - rectClient.bottom;
  436.     pFrame->SetWindowPos(NULL, 0, 0, size.cx, size.cy,
  437.         SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
  438. }
  439.  
  440. /////////////////////////////////////////////////////////////////////////////
  441.  
  442. void CScrollView::OnSize(UINT nType, int cx, int cy)
  443. {
  444.     CView::OnSize(nType, cx, cy);
  445.     if (m_nMapMode == MM_SCALETOFIT)
  446.     {
  447.         // force recalculation of scale to fit parameters
  448.         SetScaleToFitSize(m_totalLog);
  449.     }
  450.     else
  451.     {
  452.         // UpdateBars() handles locking out recursion
  453.         UpdateBars();
  454.     }
  455. }
  456.  
  457. /////////////////////////////////////////////////////////////////////////////
  458. // Scrolling Helpers
  459.  
  460. void CScrollView::CenterOnPoint(CPoint ptCenter) // center in device coords
  461. {
  462.     CRect rect;
  463.     GetClientRect(&rect);           // find size of client window
  464.  
  465.     int xDesired = ptCenter.x - rect.Width() / 2;
  466.     int yDesired = ptCenter.y - rect.Height() / 2;
  467.  
  468.     DWORD dwStyle = GetStyle();
  469.  
  470.     if ((dwStyle & WS_HSCROLL) == 0 || xDesired < 0)
  471.     {
  472.         xDesired = 0;
  473.     }
  474.     else
  475.     {
  476.         int xMax = GetScrollLimit(SB_HORZ);
  477.         if (xDesired > xMax)
  478.             xDesired = xMax;
  479.     }
  480.  
  481.     if ((dwStyle & WS_VSCROLL) == 0 || yDesired < 0)
  482.     {
  483.         yDesired = 0;
  484.     }
  485.     else
  486.     {
  487.         int yMax = GetScrollLimit(SB_VERT);
  488.         if (yDesired > yMax)
  489.             yDesired = yMax;
  490.     }
  491.  
  492.     ASSERT(xDesired >= 0);
  493.     ASSERT(yDesired >= 0);
  494.  
  495.     SetScrollPos(SB_HORZ, xDesired);
  496.     SetScrollPos(SB_VERT, yDesired);
  497. }
  498.  
  499. /////////////////////////////////////////////////////////////////////////////
  500. // Tie to scrollbars and CWnd behaviour
  501.  
  502. void CScrollView::GetScrollBarSizes(CSize& sizeSb)
  503. {
  504.     sizeSb.cx = sizeSb.cy = 0;
  505.     DWORD dwStyle = GetStyle();
  506.  
  507.     if (GetScrollBarCtrl(SB_VERT) == NULL)
  508.     {
  509.         // vert scrollbars will impact client area of this window
  510.         sizeSb.cx = afxData.cxVScroll;
  511.         if (dwStyle & WS_BORDER)
  512.             sizeSb.cx -= CX_BORDER;
  513.     }
  514.     if (GetScrollBarCtrl(SB_HORZ) == NULL)
  515.     {
  516.         // horz scrollbars will impact client area of this window
  517.         sizeSb.cy = afxData.cyHScroll;
  518.         if (dwStyle & WS_BORDER)
  519.             sizeSb.cy -= CY_BORDER;
  520.     }
  521. }
  522.  
  523. BOOL CScrollView::GetTrueClientSize(CSize& size, CSize& sizeSb)
  524.     // return TRUE if enough room to add scrollbars if needed
  525. {
  526.     CRect rect;
  527.     GetClientRect(&rect);
  528.     ASSERT(rect.top == 0 && rect.left == 0);
  529.     size.cx = rect.right;
  530.     size.cy = rect.bottom;
  531.     DWORD dwStyle = GetStyle();
  532.  
  533.     // first get the size of the scrollbars for this window
  534.     GetScrollBarSizes(sizeSb);
  535.  
  536.     // first calculate the size of a potential scrollbar
  537.     // (scroll bar controls do not get turned on/off)
  538.     if (sizeSb.cx != 0 && (dwStyle & WS_VSCROLL))
  539.     {
  540.         // vert scrollbars will impact client area of this window
  541.         size.cx += sizeSb.cx;   // currently on - adjust now
  542.     }
  543.     if (sizeSb.cy != 0 && (dwStyle & WS_HSCROLL))
  544.     {
  545.         // horz scrollbars will impact client area of this window
  546.         size.cy += sizeSb.cy;   // currently on - adjust now
  547.     }
  548.  
  549.     // return TRUE if enough room
  550.     return (size.cx > sizeSb.cx && size.cy > sizeSb.cy);
  551. }
  552.  
  553. // helper to return the state of the scrollbars without actually changing
  554. //  the state of the scrollbars
  555. void CScrollView::GetScrollBarState(CSize sizeClient, CSize& needSb,
  556.     CSize& sizeRange, CPoint& ptMove, BOOL bInsideClient)
  557. {
  558.     // get scroll bar sizes (the part that is in the client area)
  559.     CSize sizeSb;
  560.     GetScrollBarSizes(sizeSb);
  561.  
  562.     // enough room to add scrollbars
  563.     sizeRange = m_totalDev - sizeClient;
  564.         // > 0 => need to scroll
  565.     ptMove = GetDeviceScrollPosition();
  566.         // point to move to (start at current scroll pos)
  567.  
  568.     BOOL bNeedH = sizeRange.cx > 0;
  569.     if (!bNeedH)
  570.         ptMove.x = 0;                       // jump back to origin
  571.     else if (bInsideClient)
  572.         sizeRange.cy += sizeSb.cy;          // need room for a scroll bar
  573.  
  574.     BOOL bNeedV = sizeRange.cy > 0;
  575.     if (!bNeedV)
  576.         ptMove.y = 0;                       // jump back to origin
  577.     else if (bInsideClient)
  578.         sizeRange.cx += sizeSb.cx;          // need room for a scroll bar
  579.  
  580.     if (bNeedV && !bNeedH && sizeRange.cx > 0)
  581.     {
  582.         ASSERT(bInsideClient);
  583.         // need a horizontal scrollbar after all
  584.         bNeedH = TRUE;
  585.         sizeRange.cy += sizeSb.cy;
  586.     }
  587.  
  588.     // if current scroll position will be past the limit, scroll to limit
  589.     if (sizeRange.cx > 0 && ptMove.x >= sizeRange.cx)
  590.         ptMove.x = sizeRange.cx;
  591.     if (sizeRange.cy > 0 && ptMove.y >= sizeRange.cy)
  592.         ptMove.y = sizeRange.cy;
  593.  
  594.     // now update the bars as appropriate
  595.     needSb.cx = bNeedH;
  596.     needSb.cy = bNeedV;
  597.  
  598.     // needSb, sizeRange, and ptMove area now all updated
  599. }
  600.  
  601. void CScrollView::UpdateBars()
  602. {
  603.     // UpdateBars may cause window to be resized - ignore those resizings
  604.     if (m_bInsideUpdate)
  605.         return;         // Do not allow recursive calls
  606.  
  607.     // Lock out recursion
  608.     m_bInsideUpdate = TRUE;
  609.  
  610.     // update the horizontal to reflect reality
  611.     // NOTE: turning on/off the scrollbars will cause 'OnSize' callbacks
  612.     ASSERT(m_totalDev.cx >= 0 && m_totalDev.cy >= 0);
  613.  
  614.     CRect rectClient;
  615.     BOOL bCalcClient = TRUE;
  616.  
  617.     // allow parent to do inside-out layout first
  618.     CWnd* pParentWnd = GetParent();
  619.     if (pParentWnd != NULL)
  620.     {
  621.         // if parent window responds to this message, use just
  622.         //  client area for scroll bar calc -- not "true" client area
  623.         if ((BOOL)pParentWnd->SendMessage(WM_RECALCPARENT, 0,
  624.             (LPARAM)(LPCRECT)&rectClient) != 0)
  625.         {
  626.             // use rectClient instead of GetTrueClientSize for
  627.             //  client size calculation.
  628.             bCalcClient = FALSE;
  629.         }
  630.     }
  631.  
  632.     CSize sizeClient;
  633.     CSize sizeSb;
  634.  
  635.     if (bCalcClient)
  636.     {
  637.         // get client rect
  638.         if (!GetTrueClientSize(sizeClient, sizeSb))
  639.         {
  640.             // no room for scroll bars (common for zero sized elements)
  641.             CRect rect;
  642.             GetClientRect(&rect);
  643.             if (rect.right > 0 && rect.bottom > 0)
  644.             {
  645.                 // if entire client area is not invisible, assume we have
  646.                 //  control over our scrollbars
  647.                 EnableScrollBarCtrl(SB_BOTH, FALSE);
  648.             }
  649.             m_bInsideUpdate = FALSE;
  650.             return;
  651.         }
  652.     }
  653.     else
  654.     {
  655.         // let parent window determine the "client" rect
  656.         GetScrollBarSizes(sizeSb);
  657.         sizeClient.cx = rectClient.right - rectClient.left;
  658.         sizeClient.cy = rectClient.bottom - rectClient.top;
  659.     }
  660.  
  661.     // enough room to add scrollbars
  662.     CSize sizeRange;
  663.     CPoint ptMove;
  664.     CSize needSb;
  665.  
  666.     // get the current scroll bar state given the true client area
  667.     GetScrollBarState(sizeClient, needSb, sizeRange, ptMove, bCalcClient);
  668.     if (needSb.cx)
  669.         sizeClient.cy -= sizeSb.cy;
  670.     if (needSb.cy)
  671.         sizeClient.cx -= sizeSb.cx;
  672.  
  673.     // first scroll the window as needed
  674.     ScrollToDevicePosition(ptMove); // will set the scroll bar positions too
  675.  
  676.     // this structure needed to update the scrollbar page range
  677.     SCROLLINFO info;
  678.     info.fMask = SIF_PAGE|SIF_RANGE;
  679.     info.nMin = 0;
  680.  
  681.     // now update the bars as appropriate
  682.     EnableScrollBarCtrl(SB_HORZ, needSb.cx);
  683.     if (needSb.cx)
  684.     {
  685.         info.nPage = sizeClient.cx;
  686.         info.nMax = m_totalDev.cx-1;
  687.         if (!SetScrollInfo(SB_HORZ, &info, TRUE))
  688.             SetScrollRange(SB_HORZ, 0, sizeRange.cx, TRUE);
  689.     }
  690.     EnableScrollBarCtrl(SB_VERT, needSb.cy);
  691.     if (needSb.cy)
  692.     {
  693.         info.nPage = sizeClient.cy;
  694.         info.nMax = m_totalDev.cy-1;
  695.         if (!SetScrollInfo(SB_VERT, &info, TRUE))
  696.             SetScrollRange(SB_VERT, 0, sizeRange.cy, TRUE);
  697.     }
  698.  
  699.     // remove recursion lockout
  700.     m_bInsideUpdate = FALSE;
  701. }
  702.  
  703. void CScrollView::CalcWindowRect(LPRECT lpClientRect, UINT nAdjustType)
  704. {
  705.     if (nAdjustType == adjustOutside)
  706.     {
  707.         // allow for special client-edge style
  708.         ::AdjustWindowRectEx(lpClientRect, 0, FALSE, GetExStyle());
  709.  
  710.         if (m_nMapMode != MM_SCALETOFIT)
  711.         {
  712.             // if the view is being used in-place, add scrollbar sizes
  713.             //  (scollbars should appear on the outside when in-place editing)
  714.             CSize sizeClient(
  715.                 lpClientRect->right - lpClientRect->left,
  716.                 lpClientRect->bottom - lpClientRect->top);
  717.  
  718.             CSize sizeRange = m_totalDev - sizeClient;
  719.                 // > 0 => need to scroll
  720.  
  721.             // get scroll bar sizes (used to adjust the window)
  722.             CSize sizeSb;
  723.             GetScrollBarSizes(sizeSb);
  724.  
  725.             // adjust the window size based on the state
  726.             if (sizeRange.cy > 0)
  727.             {   // vertical scroll bars take up horizontal space
  728.                 lpClientRect->right += sizeSb.cx;
  729.             }
  730.             if (sizeRange.cx > 0)
  731.             {   // horizontal scroll bars take up vertical space
  732.                 lpClientRect->bottom += sizeSb.cy;
  733.             }
  734.         }
  735.     }
  736.     else
  737.     {
  738.         // call default to handle other non-client areas
  739.         ::AdjustWindowRectEx(lpClientRect, GetStyle(), FALSE,
  740.             GetExStyle() & ~(WS_EX_CLIENTEDGE));
  741.     }
  742. }
  743.  
  744. /////////////////////////////////////////////////////////////////////////////
  745. // CScrollView scrolling
  746.  
  747. void CScrollView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
  748. {
  749.     if (pScrollBar != NULL && pScrollBar->SendChildNotifyLastMsg())
  750.         return;     // eat it
  751.  
  752.     // ignore scroll bar msgs from other controls
  753.     if (pScrollBar != GetScrollBarCtrl(SB_HORZ))
  754.         return;
  755.  
  756.     OnScroll(MAKEWORD(nSBCode, -1), nPos);
  757. }
  758.  
  759. void CScrollView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
  760. {
  761.     if (pScrollBar != NULL && pScrollBar->SendChildNotifyLastMsg())
  762.         return;     // eat it
  763.  
  764.     // ignore scroll bar msgs from other controls
  765.     if (pScrollBar != GetScrollBarCtrl(SB_VERT))
  766.         return;
  767.  
  768.     OnScroll(MAKEWORD(-1, nSBCode), nPos);
  769. }
  770.  
  771. BOOL CScrollView::OnMouseWheel(UINT fFlags, short zDelta, CPoint point)
  772. {
  773.     // we don't handle anything but scrolling just now
  774.     if (fFlags & (MK_SHIFT | MK_CONTROL))
  775.         return FALSE;
  776.  
  777.     // if the parent is a splitter, it will handle the message
  778.     if (GetParentSplitter(this, TRUE))
  779.         return FALSE;
  780.  
  781.     // we can't get out of it--perform the scroll ourselves
  782.     return DoMouseWheel(fFlags, zDelta, point);
  783. }
  784.  
  785. // This function isn't virtual. If you need to override it,
  786. // you really need to override OnMouseWheel() here or in
  787. // CSplitterWnd.
  788.  
  789. BOOL CScrollView::DoMouseWheel(UINT fFlags, short zDelta, CPoint point)
  790. {
  791.     UNUSED_ALWAYS(point);
  792.     UNUSED_ALWAYS(fFlags);
  793.  
  794.     // if we have a vertical scroll bar, the wheel scrolls that
  795.     // if we have _only_ a horizontal scroll bar, the wheel scrolls that
  796.     // otherwise, don't do any work at all
  797.  
  798.     DWORD dwStyle = GetStyle();
  799.     CScrollBar* pBar = GetScrollBarCtrl(SB_VERT);
  800.     BOOL bHasVertBar = ((pBar != NULL) && pBar->IsWindowEnabled()) ||
  801.                     (dwStyle & WS_VSCROLL);
  802.  
  803.     pBar = GetScrollBarCtrl(SB_HORZ);
  804.     BOOL bHasHorzBar = ((pBar != NULL) && pBar->IsWindowEnabled()) ||
  805.                     (dwStyle & WS_HSCROLL);
  806.  
  807.     if (!bHasVertBar && !bHasHorzBar)
  808.         return FALSE;
  809.  
  810.     BOOL bResult = FALSE;
  811.     UINT uWheelScrollLines = _AfxGetMouseScrollLines();
  812.     int nToScroll;
  813.     int nDisplacement;
  814.  
  815.     if (bHasVertBar)
  816.     {
  817.         nToScroll = ::MulDiv(-zDelta, uWheelScrollLines, WHEEL_DELTA);
  818.         if (nToScroll == -1 || uWheelScrollLines == WHEEL_PAGESCROLL)
  819.         {
  820.             nDisplacement = m_pageDev.cy;
  821.             if (zDelta > 0)
  822.                 nDisplacement = -nDisplacement;
  823.         }
  824.         else
  825.         {
  826.             nDisplacement = nToScroll * m_lineDev.cy;
  827.             nDisplacement = min(nDisplacement, m_pageDev.cy);
  828.         }
  829.         bResult = OnScrollBy(CSize(0, nDisplacement), TRUE);
  830.     }
  831.     else if (bHasHorzBar)
  832.     {
  833.         nToScroll = ::MulDiv(-zDelta, uWheelScrollLines, WHEEL_DELTA);
  834.         if (nToScroll == -1 || uWheelScrollLines == WHEEL_PAGESCROLL)
  835.         {
  836.             nDisplacement = m_pageDev.cx;
  837.             if (zDelta > 0)
  838.                 nDisplacement = -nDisplacement;
  839.         }
  840.         else
  841.         {
  842.             nDisplacement = nToScroll * m_lineDev.cx;
  843.             nDisplacement = min(nDisplacement, m_pageDev.cx);
  844.         }
  845.         bResult = OnScrollBy(CSize(nDisplacement, 0), TRUE);
  846.     }
  847.  
  848.     if (bResult)
  849.         UpdateWindow();
  850.  
  851.     return bResult;
  852. }
  853.  
  854.  
  855. BOOL CScrollView::OnScroll(UINT nScrollCode, UINT nPos, BOOL bDoScroll)
  856. {
  857.     // calc new x position
  858.     int x = GetScrollPos(SB_HORZ);
  859.     int xOrig = x;
  860.  
  861.     switch (LOBYTE(nScrollCode))
  862.     {
  863.     case SB_TOP:
  864.         x = 0;
  865.         break;
  866.     case SB_BOTTOM:
  867.         x = INT_MAX;
  868.         break;
  869.     case SB_LINEUP:
  870.         x -= m_lineDev.cx;
  871.         break;
  872.     case SB_LINEDOWN:
  873.         x += m_lineDev.cx;
  874.         break;
  875.     case SB_PAGEUP:
  876.         x -= m_pageDev.cx;
  877.         break;
  878.     case SB_PAGEDOWN:
  879.         x += m_pageDev.cx;
  880.         break;
  881.     case SB_THUMBTRACK:
  882.         x = nPos;
  883.         break;
  884.     }
  885.  
  886.     // calc new y position
  887.     int y = GetScrollPos(SB_VERT);
  888.     int yOrig = y;
  889.  
  890.     switch (HIBYTE(nScrollCode))
  891.     {
  892.     case SB_TOP:
  893.         y = 0;
  894.         break;
  895.     case SB_BOTTOM:
  896.         y = INT_MAX;
  897.         break;
  898.     case SB_LINEUP:
  899.         y -= m_lineDev.cy;
  900.         break;
  901.     case SB_LINEDOWN:
  902.         y += m_lineDev.cy;
  903.         break;
  904.     case SB_PAGEUP:
  905.         y -= m_pageDev.cy;
  906.         break;
  907.     case SB_PAGEDOWN:
  908.         y += m_pageDev.cy;
  909.         break;
  910.     case SB_THUMBTRACK:
  911.         y = nPos;
  912.         break;
  913.     }
  914.  
  915.     BOOL bResult = OnScrollBy(CSize(x - xOrig, y - yOrig), bDoScroll);
  916.     if (bResult && bDoScroll)
  917.         UpdateWindow();
  918.  
  919.     return bResult;
  920. }
  921.  
  922. BOOL CScrollView::OnScrollBy(CSize sizeScroll, BOOL bDoScroll)
  923. {
  924.     int xOrig, x;
  925.     int yOrig, y;
  926.  
  927.     // don't scroll if there is no valid scroll range (ie. no scroll bar)
  928.     CScrollBar* pBar;
  929.     DWORD dwStyle = GetStyle();
  930.     pBar = GetScrollBarCtrl(SB_VERT);
  931.     if ((pBar != NULL && !pBar->IsWindowEnabled()) ||
  932.         (pBar == NULL && !(dwStyle & WS_VSCROLL)))
  933.     {
  934.         // vertical scroll bar not enabled
  935.         sizeScroll.cy = 0;
  936.     }
  937.     pBar = GetScrollBarCtrl(SB_HORZ);
  938.     if ((pBar != NULL && !pBar->IsWindowEnabled()) ||
  939.         (pBar == NULL && !(dwStyle & WS_HSCROLL)))
  940.     {
  941.         // horizontal scroll bar not enabled
  942.         sizeScroll.cx = 0;
  943.     }
  944.  
  945.     // adjust current x position
  946.     xOrig = x = GetScrollPos(SB_HORZ);
  947.     int xMax = GetScrollLimit(SB_HORZ);
  948.     x += sizeScroll.cx;
  949.     if (x < 0)
  950.         x = 0;
  951.     else if (x > xMax)
  952.         x = xMax;
  953.  
  954.     // adjust current y position
  955.     yOrig = y = GetScrollPos(SB_VERT);
  956.     int yMax = GetScrollLimit(SB_VERT);
  957.     y += sizeScroll.cy;
  958.     if (y < 0)
  959.         y = 0;
  960.     else if (y > yMax)
  961.         y = yMax;
  962.  
  963.     // did anything change?
  964.     if (x == xOrig && y == yOrig)
  965.         return FALSE;
  966.  
  967.     if (bDoScroll)
  968.     {
  969.         // do scroll and update scroll positions
  970.         ScrollWindow(-(x-xOrig), -(y-yOrig));
  971.         if (x != xOrig)
  972.             SetScrollPos(SB_HORZ, x);
  973.         if (y != yOrig)
  974.             SetScrollPos(SB_VERT, y);
  975.     }
  976.     return TRUE;
  977. }
  978.  
  979. /////////////////////////////////////////////////////////////////////////////
  980. // CScrollView diagnostics
  981.  
  982. #ifdef _DEBUG
  983. void CScrollView::Dump(CDumpContext& dc) const
  984. {
  985.     CView::Dump(dc);
  986.  
  987.     dc << "m_totalLog = " << m_totalLog;
  988.     dc << "\nm_totalDev = " << m_totalDev;
  989.     dc << "\nm_pageDev = " << m_pageDev;
  990.     dc << "\nm_lineDev = " << m_lineDev;
  991.     dc << "\nm_bCenter = " << m_bCenter;
  992.     dc << "\nm_bInsideUpdate = " << m_bInsideUpdate;
  993.     dc << "\nm_nMapMode = ";
  994.     switch (m_nMapMode)
  995.     {
  996.     case MM_NONE:
  997.         dc << "MM_NONE";
  998.         break;
  999.     case MM_SCALETOFIT:
  1000.         dc << "MM_SCALETOFIT";
  1001.         break;
  1002.     case MM_TEXT:
  1003.         dc << "MM_TEXT";
  1004.         break;
  1005.     case MM_LOMETRIC:
  1006.         dc << "MM_LOMETRIC";
  1007.         break;
  1008.     case MM_HIMETRIC:
  1009.         dc << "MM_HIMETRIC";
  1010.         break;
  1011.     case MM_LOENGLISH:
  1012.         dc << "MM_LOENGLISH";
  1013.         break;
  1014.     case MM_HIENGLISH:
  1015.         dc << "MM_HIENGLISH";
  1016.         break;
  1017.     case MM_TWIPS:
  1018.         dc << "MM_TWIPS";
  1019.         break;
  1020.     default:
  1021.         dc << "*unknown*";
  1022.         break;
  1023.     }
  1024.  
  1025.     dc << "\n";
  1026. }
  1027.  
  1028. void CScrollView::AssertValid() const
  1029. {
  1030.     CView::AssertValid();
  1031.  
  1032.     switch (m_nMapMode)
  1033.     {
  1034.     case MM_NONE:
  1035.     case MM_SCALETOFIT:
  1036.     case MM_TEXT:
  1037.     case MM_LOMETRIC:
  1038.     case MM_HIMETRIC:
  1039.     case MM_LOENGLISH:
  1040.     case MM_HIENGLISH:
  1041.     case MM_TWIPS:
  1042.         break;
  1043.     case MM_ISOTROPIC:
  1044.     case MM_ANISOTROPIC:
  1045.         ASSERT(FALSE); // illegal mapping mode
  1046.     default:
  1047.         ASSERT(FALSE); // unknown mapping mode
  1048.     }
  1049. }
  1050. #endif //_DEBUG
  1051.  
  1052. #ifdef AFX_INIT_SEG
  1053. #pragma code_seg(AFX_INIT_SEG)
  1054. #endif
  1055.  
  1056. IMPLEMENT_DYNAMIC(CScrollView, CView)
  1057.  
  1058. /////////////////////////////////////////////////////////////////////////////
  1059.