viewscrl.cpp 27 KB

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