Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2466 lines
59 KiB

  1. /*
  2. * DISP.CPP
  3. *
  4. * Purpose:
  5. * CDisplay class
  6. *
  7. * Owner:
  8. * Original RichEdit code: David R. Fulmer
  9. * Christian Fortini
  10. * Murray Sargent
  11. * Jon Matousek - smooth scrolling.
  12. *
  13. * Copyright (c) 1995-1998 Microsoft Corporation. All rights reserved.
  14. */
  15. #include "_common.h"
  16. #include "_disp.h"
  17. #include "_edit.h"
  18. #include "_select.h"
  19. #include "_font.h"
  20. #include "_measure.h"
  21. #include "_osdc.h"
  22. #include "_dfreeze.h"
  23. ASSERTDATA
  24. // Decimal point precision of smooth scrolling calculations.
  25. #define SMOOTH_PRECISION (100000L)
  26. // =========================== Invariant stuff ======================================================
  27. #define DEBUG_CLASSNAME CDisplay
  28. #include "_invar.h"
  29. #ifdef DEBUG
  30. BOOL
  31. CDisplay::Invariant( void ) const
  32. {
  33. AssertSz(_yHeightView >= 0, "CDisplay::Invariant invalid _yHeightView");
  34. AssertSz(_yHeightClient >= 0,
  35. "CDisplay::Invariant invalid _yHeightClient");
  36. return TRUE;
  37. }
  38. #endif
  39. // Constant used to build the rectangle used for determining if a hit is close
  40. // to the text.
  41. #define HIT_CLOSE_RECT_INC 5
  42. // Auto scroll constants
  43. #define dwAutoScrollUp 1
  44. #define dwAutoScrollDown 2
  45. #define dwAutoScrollLeft 3
  46. #define dwAutoScrollRight 4
  47. // =========================== CLed =====================================================
  48. void CLed::SetMax(
  49. const CDisplay * const pdp)
  50. {
  51. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLed::SetMax");
  52. _cpMatchNew = _cpMatchOld = pdp->_ped->GetTextLength();
  53. _iliMatchNew = _iliMatchOld = max(0, pdp->LineCount() - 1);
  54. _yMatchNew = _yMatchOld = pdp->GetHeight();
  55. }
  56. // =========================== CDisplay =====================================================
  57. DWORD CDisplay::_dwTimeScrollNext; // time for next scroll step
  58. DWORD CDisplay::_dwScrollLast; // last scroll action
  59. /*
  60. * CDisplay::ConvertYPosToMax(xPos)
  61. *
  62. * @mfunc
  63. * Calculate real scroll position from scroll position
  64. *
  65. * @rdesc
  66. * X position from scroll
  67. *
  68. * @devnote
  69. * This routine exists because the thumb position messages
  70. * are limited to 16-bits so we extrapolate when the Y position
  71. * gets greater than that.
  72. */
  73. LONG CDisplay::ConvertScrollToXPos(
  74. LONG xPos) //@parm Scroll position
  75. {
  76. LONG xMax = GetMaxXScroll();
  77. // Has maximum scroll range exceeded 16-bits?
  78. if (xMax >= _UI16_MAX)
  79. {
  80. // Yes - Extrapolate to the "real" x Positioin
  81. xPos = MulDiv(xPos, xMax, _UI16_MAX);
  82. }
  83. return xPos;
  84. }
  85. /*
  86. * CDisplay::ConvertXPosToScrollPos(xPos)
  87. *
  88. * @mfunc
  89. * Calculate scroll position from X position in document.
  90. *
  91. * @rdesc
  92. * Scroll position from X position
  93. *
  94. * @devnote
  95. * This routine exists because the thumb position messages
  96. * are limited to 16-bits so we extrapolate when the Y position
  97. * gets greater than that.
  98. *
  99. */
  100. LONG CDisplay::ConvertXPosToScrollPos(
  101. LONG xPos) //@parm Y position in document
  102. {
  103. LONG xMax = GetMaxXScroll();
  104. // Has maximum scroll range exceeded 16-bits?
  105. if(xMax >= _UI16_MAX)
  106. {
  107. // Yes - Extrapolate to the scroll bar position
  108. xPos = MulDiv(xPos, _UI16_MAX, xMax);
  109. }
  110. return xPos;
  111. }
  112. /*
  113. * CDisplay::ConvertYPosToMax(yPos)
  114. *
  115. * @mfunc
  116. * Calculate the real scroll position from the scroll position
  117. *
  118. * @rdesc
  119. * Y position from scroll
  120. *
  121. * @devnote
  122. * This routine exists because the thumb position messages
  123. * are limited to 16-bits so we extrapolate when the Y position
  124. * gets greater than that.
  125. */
  126. LONG CDisplay::ConvertYPosToScrollPos(
  127. LONG yPos) //@parm Scroll position
  128. {
  129. // Default is single line edit control which cannot have Y-Scroll bars
  130. return 0;
  131. }
  132. CDisplay::CDisplay (CTxtEdit* ped) :
  133. CDevDesc (ped)
  134. {
  135. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::CDisplay");
  136. _TEST_INVARIANT_
  137. _fRecalcDone = TRUE;
  138. }
  139. CDisplay::~CDisplay()
  140. {
  141. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::~CDisplay");
  142. _TEST_INVARIANT_
  143. CNotifyMgr *pnm = _ped->GetNotifyMgr();
  144. if(pnm)
  145. pnm->Remove(this);
  146. CheckRemoveSmoothVScroll();
  147. if (_padc)
  148. delete _padc;
  149. #ifdef LINESERVICES
  150. if (g_pols)
  151. g_pols->DestroyLine(this);
  152. #endif
  153. }
  154. /*
  155. * CDisplay::InitFromDisplay(pdp)
  156. *
  157. * @mfunc initialize this display from another display instance.
  158. *
  159. * @comment
  160. * copy *only* the members that will remain constant
  161. * between two different display instances. Currently, that
  162. * is only the view variables and device descriptor info.
  163. */
  164. void CDisplay::InitFromDisplay(
  165. const CDisplay *pdp)
  166. {
  167. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::InitFromDisplay");
  168. _xWidthView = pdp->_xWidthView;
  169. _yHeightView = pdp->_yHeightView;
  170. _yHeightClient = pdp->_yHeightClient;
  171. // Don't save DC; just coordinate information.
  172. _dxpInch = pdp->_dxpInch;
  173. _dypInch = pdp->_dypInch;
  174. // If display we are copying from is active display,
  175. // then this new display is the active display.
  176. _fActive = pdp->_fActive;
  177. }
  178. /*
  179. * CDisplay::Init()
  180. *
  181. * @mfunc Initializes CDisplay
  182. *
  183. * @rdesc
  184. * TRUE iff initialization succeeded
  185. */
  186. BOOL CDisplay::Init()
  187. {
  188. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::Init");
  189. CNotifyMgr *pnm = _ped->GetNotifyMgr();
  190. if(pnm)
  191. pnm->Add(this);
  192. return TRUE;
  193. }
  194. /*
  195. * CDisplay::GetSelBarInPixels()
  196. *
  197. * @mfunc
  198. * Helper that returns size of selection bar in device units.
  199. *
  200. * @rdesc
  201. * Size of selection bar (is 0 if none).
  202. */
  203. LONG CDisplay::GetSelBarInPixels()
  204. {
  205. return HimetricXtoDX(_ped->TxGetSelectionBarWidth());
  206. }
  207. //================================ Device drivers ===================================
  208. /*
  209. * CDisplay::SetMainTargetDC(hdc, xWidthMax)
  210. *
  211. * @mfunc
  212. * Sets a target device for this display and updates view
  213. *
  214. * Note:
  215. * No support for targetDC in the base CDisplay class.
  216. *
  217. * Note:
  218. * Target device can't be a metafile (can get char width out of a
  219. * metafile)
  220. *
  221. * @rdesc
  222. * TRUE if success
  223. */
  224. BOOL CDisplay::SetMainTargetDC(
  225. HDC hdc, //@parm Target DC, NULL for same as rendering device
  226. LONG xWidthMax) //@parm Max width of lines (not used if target device is screen)
  227. {
  228. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::SetMainTargetDC");
  229. _TEST_INVARIANT_
  230. return TRUE;
  231. }
  232. BOOL CDisplay::SetTargetDC(
  233. HDC hdc, LONG dxpInch, LONG dypInch)
  234. {
  235. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::SetTargetDC");
  236. _TEST_INVARIANT_
  237. return TRUE;
  238. }
  239. /*
  240. * CDisplay::SetDrawInfo(pdi, dwDrawAspect, lindex, pvAspect, ptd, hicTargetDev)
  241. *
  242. * @mfunc
  243. * Sets the drawing information into the display
  244. *
  245. * @rdesc
  246. * void - this cannot fail
  247. *
  248. * @devnote
  249. * The key point to this routine is that the caller of this routine
  250. * is the owner of the memory for the drawing information. It is the
  251. * callers responsiblity to call ReleaseDrawInfo to tell the display
  252. * that it is done with the drawing information.
  253. */
  254. void CDisplay::SetDrawInfo(
  255. CDrawInfo *pdi, //@parm memory for draw info if there is not one already
  256. DWORD dwDrawAspect, //@parm draw aspect
  257. LONG lindex, //@parm currently unused
  258. void *pvAspect, //@parm info for drawing optimizations (OCX 96)
  259. DVTARGETDEVICE *ptd,//@parm information on target device
  260. HDC hicTargetDev) //@parm target information context
  261. {
  262. HDC hicTargetToUse = hicTargetDev;
  263. const CDevDesc *pdd;
  264. // Set up the target device if we need to use the default
  265. if ((NULL == hicTargetToUse))
  266. {
  267. pdd = GetDdTarget();
  268. if(pdd)
  269. hicTargetToUse = pdd->GetDC();
  270. }
  271. if (NULL == _pdi)
  272. {
  273. // Draw structure not yet allocated so use the one
  274. // passed in
  275. _pdi = pdi;
  276. }
  277. // Reset the parameters
  278. _pdi->Init(
  279. dwDrawAspect,
  280. lindex,
  281. pvAspect,
  282. ptd,
  283. hicTargetToUse);
  284. }
  285. /*
  286. * CDisplay::ReleaseDrawInfo ()
  287. *
  288. * @mfunc
  289. * Releases drawing information from display
  290. *
  291. * @rdesc
  292. * void - this cannot fail
  293. *
  294. * @devnote
  295. * Since the display does not own the memory for the drawing information,
  296. * this only NULLs out the pointer in the drawing information pointer. It
  297. * is the responsiblity of the caller to free the memory for the drawing
  298. * information.
  299. */
  300. void CDisplay::ReleaseDrawInfo()
  301. {
  302. if(_pdi && !_pdi->Release())
  303. {
  304. // This object is no longer referenced so we toss our reference.
  305. _pdi = NULL;
  306. }
  307. }
  308. /*
  309. * CDisplay::GetTargetDev ()
  310. *
  311. * @mfunc
  312. * Get the target device if one is available
  313. *
  314. * @rdesc
  315. * Pointer to device description object or NULL if none is available.
  316. *
  317. * @devnote
  318. * This uses the draw info if it is available and then the main target DC
  319. * if it is available.
  320. */
  321. const CDevDesc*CDisplay::GetTargetDev() const
  322. {
  323. const CDevDesc *pdd = NULL;
  324. if(_pdi && _pdi->GetTargetDD())
  325. pdd = _pdi->GetTargetDD();
  326. return pdd ? pdd : GetDdTarget();
  327. }
  328. //================================ Background Recalc ===================================
  329. /*
  330. * CDisplay::StepBackgroundRecalc()
  331. *
  332. * @mfunc
  333. * Steps background line recalc (at GetCp()CalcMax position)
  334. * Called by timer proc. No effect for base class
  335. *
  336. * ??? CF - Should use an idle thread
  337. */
  338. void CDisplay::StepBackgroundRecalc()
  339. {
  340. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::StepBackgroundRecalc");
  341. _TEST_INVARIANT_
  342. }
  343. /*
  344. * CDisplay::WaitForRecalc(cpMax, yMax)
  345. *
  346. * @mfunc
  347. * Ensures that lines are recalced until a specific character
  348. * position or ypos. Always TRUE for base CDisplay class.
  349. *
  350. * @rdesc
  351. * TRUE if success
  352. */
  353. BOOL CDisplay::WaitForRecalc(
  354. LONG cpMax, //@parm Position recalc up to (-1 to ignore)
  355. LONG yMax) //@parm ypos to recalc up to (-1 to ignore)
  356. {
  357. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::WaitForRecalc");
  358. _TEST_INVARIANT_
  359. return TRUE;
  360. }
  361. /*
  362. * CDisplay::WaitForRecalcIli(ili)
  363. *
  364. * @mfunc
  365. * Returns TRUE if lines were recalc'd up to ili
  366. * Always the case for base CDisplay class.
  367. */
  368. BOOL CDisplay::WaitForRecalcIli(
  369. LONG ili)
  370. {
  371. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::WaitForRecalcIli");
  372. _TEST_INVARIANT_
  373. return TRUE;
  374. }
  375. /*
  376. * CDisplay::WaitForRecalcView()
  377. *
  378. * Purpose
  379. * Ensure visible lines are completly recalced
  380. * Always the case for base CDisplay class
  381. */
  382. BOOL CDisplay::WaitForRecalcView()
  383. {
  384. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::WaitForRecalcView");
  385. _TEST_INVARIANT_
  386. return TRUE;
  387. }
  388. //==================================== Rendering =======================================
  389. /*
  390. * CDisplay::Draw(hdcDraw, hicTargetDev, prcClient, prcWBounds,
  391. * prcUpdate, pfnContinue, dwContinue)
  392. * @mfunc
  393. * General drawing method called by IViewObject::Draw() or in
  394. * response to WM_PAINT
  395. *
  396. * @rdesc
  397. * HRESULT
  398. */
  399. HRESULT CDisplay::Draw(
  400. HDC hdcDraw, //@parm Rendering device context
  401. HDC hicTargetDev, //@parm Target information context
  402. LPCRECT prcClient, //@parm Bounding (client) rectangle
  403. LPCRECT prcWBounds, //@parm Clipping rect for metafiles
  404. LPCRECT prcUpdate, //@parm Dirty rect inside prcClient
  405. BOOL (CALLBACK *pfnContinue)(DWORD),//@parm Callback for interrupting
  406. // long display (currently unused)
  407. DWORD dwContinue) //@parm Param to pass to pfnContinue
  408. {
  409. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::Draw");
  410. _TEST_INVARIANT_
  411. HRESULT hr = S_OK;
  412. // Store current depth in drawing locally so we can tell
  413. // whether we need to actually render.
  414. DWORD dwDepthThisDraw = _pdi->GetDrawDepth();
  415. RECT rcView, rcClient, rcRender;
  416. CTxtSelection *psel = _ped->GetSelNC();
  417. // Get client rect
  418. if(prcClient)
  419. rcClient = *prcClient;
  420. else
  421. {
  422. AssertSz(_ped->_fInPlaceActive,
  423. "CDisplay::GetViewRect() - Not in-place and !prcClient");
  424. _ped->TxGetClientRect(&rcClient);
  425. }
  426. if(!prcWBounds) // No metafile, so just set rendering DC
  427. {
  428. if(!SetDC(hdcDraw))
  429. {
  430. hr = E_FAIL;
  431. goto Cleanup;
  432. }
  433. }
  434. else // Rendering to a metafile
  435. {
  436. //Forms^3 draws using screen resolution, while OLE specifies HIMETRIC
  437. long dxpInch = GetPed()->fInOurHost() ? 2540 : W32->GetXPerInchScreenDC();
  438. long dypInch = GetPed()->fInOurHost() ? 2540 : W32->GetYPerInchScreenDC();
  439. SetWindowOrgEx(hdcDraw, prcWBounds->left, prcWBounds->top, NULL);
  440. SetWindowExtEx(hdcDraw, prcWBounds->right, prcWBounds->bottom, NULL);
  441. SetMetafileDC(hdcDraw, dxpInch, dypInch);
  442. }
  443. // Compute view rectangle (rcView) from client rectangle (account for
  444. // inset and selection bar width)
  445. GetViewRect(rcView, &rcClient);
  446. // If this view is not active and it is not to be recalc'd then
  447. // we only decide to use it if the size matches and return S_FALSE
  448. // if it doesn't so the caller can create a new display to use for
  449. // drawing.
  450. if(!IsActive() && !_fNeedRecalc)
  451. {
  452. if (rcView.right - rcView.left != GetViewWidth() ||
  453. rcView.bottom - rcView.top != GetViewHeight())
  454. {
  455. hr = S_FALSE;
  456. goto Cleanup;
  457. }
  458. }
  459. // Make sure our client rectangle is set correctly.
  460. _yHeightClient = rcClient.bottom - rcClient.top;
  461. // Recalc view
  462. // bug fix #5521
  463. // RecalcView can potentially call RequestResize which would
  464. // change the client rect. Send rect down to update the client rect
  465. if(!RecalcView(rcView, &rcClient))
  466. goto Cleanup;
  467. if(dwDepthThisDraw != _pdi->GetDrawDepth())
  468. {
  469. // A draw happened recursively to this draw. Therefore,
  470. // the screen has already been rendered so we don't need
  471. // to do anything more here.
  472. goto Cleanup;
  473. }
  474. // Compute rect to render
  475. if(!prcUpdate) // Update full view
  476. rcRender = rcClient;
  477. else // Clip rendering to client rect
  478. {
  479. if(!IntersectRect(&rcRender, &rcClient, prcUpdate))
  480. goto Cleanup;
  481. }
  482. if(psel)
  483. psel->ClearCchPending();
  484. if(IsMain())
  485. _ped->TxNotify( EN_UPDATE, NULL );
  486. // Now render
  487. Render(rcView, rcRender);
  488. // Update cursor if we need to
  489. if(_fUpdateCaret)
  490. {
  491. // The caret only belongs in an active view with
  492. // a selection on a control that has the focus
  493. if (IsActive() && psel && _ped->_fFocus)
  494. {
  495. // Update the caret if there is a selection object.
  496. // Note: we only scroll the caret into view, if
  497. // it was previously in the view. This avoids having
  498. // window pop to caret if it is resized and the
  499. // caret is not in the view.
  500. psel->UpdateCaret(psel->IsCaretInView());
  501. }
  502. _fUpdateCaret = FALSE;
  503. }
  504. Cleanup:
  505. // Reset DC in device descriptor
  506. ResetDC();
  507. return hr;
  508. }
  509. //==================================== View Recalc ===================================
  510. /*
  511. * CDisplay::UpdateViewRectState(prcClient)
  512. *
  513. * @mfunc Compares new view to cached and updates the view as well as the
  514. * what type of view recalculation needs to occur.
  515. */
  516. void CDisplay::UpdateViewRectState(
  517. const RECT *prcClient) //@parm New client rectangle
  518. {
  519. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::UpdateViewRectState");
  520. // Check whether the view rect has changed since last rendering
  521. // If width has changed, need complete line recalc.
  522. // If height has changed, recalc all visible and update scrollbars
  523. if(prcClient->right - prcClient->left != _xWidthView)
  524. {
  525. _xWidthView = (SHORT)(prcClient->right - prcClient->left);
  526. _fViewChanged = TRUE;
  527. _fNeedRecalc = TRUE; // need full recalc
  528. }
  529. if(prcClient->bottom - prcClient->top != _yHeightView)
  530. {
  531. _yHeightView = prcClient->bottom - prcClient->top;
  532. // The height can go negative when there is an inset and
  533. // the client rect is very small. We just set it to 0 because
  534. // that is the smallest the view can actually get.
  535. if (_yHeightView < 0)
  536. _yHeightView = 0;
  537. _fViewChanged = TRUE;
  538. }
  539. }
  540. /*
  541. * CDisplay::ReDrawOnRectChange
  542. *
  543. * @mfunc Compares new view to cached and updates the display both
  544. * internal and visible state appropriately.
  545. */
  546. void CDisplay::ReDrawOnRectChange(
  547. HDC hicTarget, //@param Target device
  548. const RECT *prcClient) //@param New client rectangle
  549. {
  550. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::ReDrawOnRectChange");
  551. _TEST_INVARIANT_
  552. RECT rcView;
  553. // Convert client rect to our view rect
  554. GetViewRect(rcView, prcClient);
  555. // Update the x and y coordinates of the view based on the client rect
  556. UpdateViewRectState(&rcView);
  557. if(_fNeedRecalc || _fViewChanged)
  558. {
  559. // The client rect changed in some way so lets update our client
  560. // rect height for zoom.
  561. _yHeightClient = prcClient->bottom - prcClient->top;
  562. // Remeasure but don't update scroll bars now.
  563. RecalcView(FALSE);
  564. // Forms does not want the screen to reflect what the user clicked on
  565. // or moved the cursor to so we oblige them by not updating the screen
  566. // here but waiting for some future action to do so.
  567. }
  568. }
  569. /*
  570. * CDisplay::RecalcView(rcView)
  571. *
  572. * @mfunc
  573. * RecalcView after the view rect changed
  574. *
  575. * @rdesc
  576. * TRUE if success
  577. */
  578. BOOL CDisplay::RecalcView (
  579. const RECT &rcView, RECT* prcClient)
  580. {
  581. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::RecalcView");
  582. _TEST_INVARIANT_
  583. // Update the x and y coordinates of the view based on the client rect
  584. UpdateViewRectState(&rcView);
  585. // Ensure lines are recalced
  586. if(_fNeedRecalc)
  587. {
  588. // Display got recalculated so the caret needs to be repositioned.
  589. _fUpdateCaret = TRUE;
  590. return RecalcView(TRUE, prcClient);
  591. }
  592. if(_fViewChanged)
  593. {
  594. // The scroll bars are up to date so we can turn off the notification.
  595. _fViewChanged = FALSE;
  596. // A height change was noticed in UpdateViewRectState so make sure
  597. // the horizontal scroll bar (if any is correct).
  598. UpdateScrollBar(SB_VERT);
  599. }
  600. return WaitForRecalcView();
  601. }
  602. //==================================== View Update ===================================
  603. /*
  604. * CDisplay::UpdateView()
  605. *
  606. * @mfunc
  607. * Fully recalc all lines and update the visible part of the display
  608. * (the "view") on the screen.
  609. *
  610. * Returns:
  611. * TRUE if success
  612. */
  613. BOOL CDisplay::UpdateView()
  614. {
  615. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::UpdateView");
  616. _TEST_INVARIANT_
  617. if(_fNoUpdateView)
  618. return TRUE;
  619. if(!_ped->_fInPlaceActive)
  620. {
  621. // If not active, just invalidate everything
  622. InvalidateRecalc();
  623. _ped->TxInvalidateRect(NULL, FALSE);
  624. _ped->TxUpdateWindow();
  625. return TRUE;
  626. }
  627. if(_ped->_pdp->IsFrozen())
  628. {
  629. _ped->_pdp->SetNeedRedisplayOnThaw(TRUE);
  630. return TRUE;
  631. }
  632. // If we get here, we are updating some general characteristic of the
  633. // display and so we want the cursor updated as well as the general
  634. // change; otherwise the cursor will land up in the wrong place.
  635. _fUpdateCaret = TRUE;
  636. RECT rcView;
  637. // Get view rectangle
  638. GetViewRect(rcView, NULL);
  639. // Update size of view, which could have changed
  640. UpdateViewRectState(&rcView);
  641. // From here on we better be in place
  642. Assert(_ped->_fInPlaceActive);
  643. if(!CDevDesc::IsValid())
  644. {
  645. // Make our device valid
  646. SetDC(NULL);
  647. }
  648. // Recalc everything
  649. RecalcView(TRUE);
  650. // Invalidate entire view
  651. _ped->TxInvalidateRect (NULL, FALSE);
  652. return TRUE;
  653. }
  654. /*
  655. * CDisplay::RoundToLine(hdc, width, pheight)
  656. *
  657. * @mfunc
  658. * Calculate number of default lines to fit in input height
  659. *
  660. * @rdesc
  661. * S_OK - Call completed successfully <nl>
  662. */
  663. HRESULT CDisplay::RoundToLine(
  664. HDC hdc, //@parm DC for the window
  665. LONG width, //@parm in - width of window; out max width
  666. LONG *pheight) //@parm in - proposed height; out - actual
  667. {
  668. CLock lock; // Uses global (shared) FontCache
  669. SetDC(hdc); // Set DC
  670. // Set height temporarily so zoom factor will work out
  671. LONG yOrigHeightClient = SetClientHeight((SHORT) *pheight);
  672. // Use this to adjust for inset height
  673. LONG yAdjForInset = *pheight;
  674. // Get rectangle adjusted for insets
  675. GetViewDim(width, *pheight);
  676. // Save proposed height
  677. LONG yProposed = *pheight;
  678. // Calc inset adjusted height
  679. yAdjForInset -= yProposed;
  680. // Get font
  681. const CCharFormat *pCF = _ped->GetCharFormat(-1);
  682. Assert(pCF);
  683. // Get font cache object
  684. LONG dypInch = MulDiv(GetDeviceCaps(hdc, LOGPIXELSY), GetZoomNumerator(), GetZoomDenominator());
  685. CCcs *pccs = fc().GetCcs(pCF, dypInch);
  686. SHORT yAdjustFE = pccs->AdjustFEHeight(!_ped->fUseUIFont() && _ped->_pdp->IsMultiLine());
  687. // Get height of font
  688. LONG yHeight = pccs->_yHeight + (yAdjustFE << 1);;
  689. // All we wanted is the height and we have got it so dump the
  690. // font cache entry
  691. pccs->Release();
  692. // Figure out how many lines fit into the input height
  693. LONG cLines = yProposed / yHeight;
  694. // See if we need to round up
  695. if(yProposed % yHeight || !cLines)
  696. cLines++;
  697. // Set height to new value
  698. *pheight = yHeight * cLines + yAdjForInset;
  699. // Set client height back to what it was
  700. SetClientHeight(yOrigHeightClient);
  701. // Reset the DC
  702. ResetDC();
  703. return NOERROR;
  704. }
  705. //============================= Client and view rectangles ===========================
  706. /*
  707. * CDisplay::OnClientRectChange(&rcClient)
  708. *
  709. * @mfunc
  710. * Update when either the client rectangle changes
  711. * >>> Should be called only when in-place active <<<
  712. */
  713. void CDisplay::OnClientRectChange(
  714. const RECT &rcClient) //@parm New client rectangle
  715. {
  716. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::OnClientRectChange");
  717. _TEST_INVARIANT_
  718. RECT rcView;
  719. AssertSz(_ped->_fInPlaceActive, "CDisplay::OnClientRectChange() called when not in-place active");
  720. // Make sure our client rectangle is set correctly.
  721. _yHeightClient = rcClient.bottom - rcClient.top;
  722. // Use view rect change notification
  723. GetViewRect(rcView);
  724. // Make sure that we will have a selection object at this point
  725. _ped->GetSel();
  726. // Update when view rectangle changes
  727. OnViewRectChange(rcView);
  728. }
  729. /*
  730. * CDisplay::OnViewRectChange(&rcView)
  731. *
  732. * @mfunc
  733. * Update when either the view rectangle changes
  734. *
  735. * Arguments:
  736. * rcView new view rectangle, in:
  737. * - log units (twips) rel. to top/left of client rect if not in-place
  738. * - containing window client coords if in-place active
  739. */
  740. void CDisplay::OnViewRectChange(
  741. const RECT &rcView)
  742. {
  743. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::OnViewRectChange");
  744. _TEST_INVARIANT_
  745. if(!_ped->_fInPlaceActive)
  746. // We'll adjust the view rect during next Draw.
  747. return;
  748. CTxtSelection *psel = _ped->GetSelNC();
  749. const BOOL fCaretShowing = psel ? psel->ShowCaret(FALSE) : FALSE;
  750. const BOOL fCaretInView = fCaretShowing ? psel->IsCaretInView() : FALSE;
  751. RECT rcV = rcView;
  752. COleObject *pipo;
  753. // Factor in selection bar space
  754. if (_ped->IsSelectionBarRight())
  755. rcV.right -= GetSelBarInPixels();
  756. else
  757. rcV.left += GetSelBarInPixels();
  758. // Recalc with new view rectangle
  759. // ??? What if this fails ?
  760. RecalcView(rcView);
  761. // Repaint window before showing the caret
  762. _ped->TxInvalidateRect(NULL, FALSE); // ??? for now, we could be smarter
  763. _ped->TxUpdateWindow();
  764. // Reposition the caret
  765. if(fCaretShowing)
  766. {
  767. Assert(psel);
  768. psel->ShowCaret(TRUE);
  769. psel->UpdateCaret(fCaretInView);
  770. }
  771. // FUTURE: since we're now repositioning in place active
  772. // objects every time we draw, this call seems to be
  773. // superfluous (AndreiB)
  774. // Tell object subsystem to reposition any in place objects
  775. if( _ped->HasObjects() )
  776. {
  777. pipo = _ped->GetObjectMgr()->GetInPlaceActiveObject();
  778. if(pipo)
  779. pipo->OnReposition( 0, 0 );
  780. }
  781. }
  782. /*
  783. * CDisplay::RequestResize()
  784. *
  785. * @mfunc
  786. * Forces the control to resize vertically so that all text fit into it
  787. *
  788. * @rdesc
  789. * HRESULT = (autosize) ? TxNotify(EN_REQUESTRESIZE, &resize) : S_OK
  790. */
  791. HRESULT CDisplay::RequestResize()
  792. {
  793. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::RequestResize");
  794. _TEST_INVARIANT_
  795. if(_ped->TxGetAutoSize())
  796. {
  797. REQRESIZE resize;
  798. // If word wrapping is on, then the width is the normal
  799. // client width. Otherwise, it's the width of the longest
  800. // line plus the width of the caret.
  801. DWORD width = GetWordWrap() ? _xWidthView : GetWidth() + dxCaret;
  802. // Get view inset for adjusting width
  803. RECT rcInset;
  804. _ped->TxGetViewInset(&rcInset, this);
  805. resize.nmhdr.hwndFrom = NULL;
  806. resize.nmhdr.idFrom = NULL;
  807. resize.nmhdr.code = EN_REQUESTRESIZE;
  808. resize.rc.top = 0;
  809. resize.rc.left = 0;
  810. resize.rc.bottom = GetResizeHeight();
  811. // 1.0 COMPATABILITY
  812. // 1.0 included the borders when requesting resize
  813. if (_ped->Get10Mode())
  814. {
  815. AssertSz(_ped->fInplaceActive(), "In 1.0 mode but not inplace active!!");
  816. HWND hwnd = NULL;
  817. _ped->TxGetWindow(&hwnd);
  818. if (hwnd)
  819. {
  820. RECT rcClient, rcWindow;
  821. _ped->TxGetClientRect(&rcClient);
  822. GetWindowRect(hwnd, &rcWindow);
  823. width = rcClient.right;
  824. resize.rc.bottom += max(rcWindow.bottom - rcWindow.top - rcClient.bottom, 0);
  825. resize.rc.bottom += rcInset.bottom + rcInset.top;
  826. resize.rc.right = rcWindow.right - rcWindow.left;
  827. }
  828. else
  829. resize.rc.right = width;
  830. }
  831. else
  832. {
  833. // Adjust width by inset and selection bar
  834. resize.rc.right = width + rcInset.left + rcInset.right
  835. + GetSelBarInPixels();
  836. }
  837. return _ped->TxNotify(EN_REQUESTRESIZE, &resize);
  838. }
  839. return S_OK;
  840. }
  841. /*
  842. * CDisplay::GetViewRect(RECT &rcView, LPCRECT prcClient)
  843. *
  844. * @mfunc
  845. * Compute and return the view rectangle in window's client
  846. * area coordinates.
  847. *
  848. * @comm
  849. * prcClient is client rect (in window's client coords), which can be
  850. * NULL if we are in-place.
  851. */
  852. void CDisplay::GetViewRect(
  853. RECT & rcView, //@parm Reference to rect to return
  854. LPCRECT prcClient) //@parm Client rect (in window's client coords)
  855. {
  856. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::GetViewRect");
  857. _TEST_INVARIANT_
  858. RECT rcInset;
  859. // If client rect wasn't passed in, get it from host
  860. if(prcClient)
  861. rcView = *prcClient;
  862. else
  863. {
  864. AssertSz(_ped->_fInPlaceActive,
  865. "CDisplay::GetViewRect() - Not in-place and !prcClient");
  866. _ped->TxGetClientRect(&rcView);
  867. }
  868. // Make sure height is set
  869. _yHeightClient = rcView.bottom - rcView.top;
  870. // Ask host for view inset and convert to device coordinates
  871. _ped->TxGetViewInset(&rcInset, this);
  872. rcView.left += rcInset.left; // Add in inset offsets
  873. rcView.top += rcInset.top; // rcView is in device coords
  874. rcView.right -= rcInset.right;
  875. rcView.bottom -= rcInset.bottom;
  876. // Add in selection bar space
  877. if (_ped->IsSelectionBarRight())
  878. rcView.right -= GetSelBarInPixels();
  879. else
  880. rcView.left += GetSelBarInPixels();
  881. }
  882. //=============================== Scrolling ==============================
  883. /*
  884. * CDisplay::VScroll(wCode, yPos)
  885. *
  886. * @mfunc
  887. * Scroll the view vertically in response to a scrollbar event
  888. * >>> Should be called when in-place active only <<<
  889. *
  890. * Note:
  891. * No support for vertical scroll in base CDisplay. No action.
  892. *
  893. * @rdesc
  894. * LRESULT formatted for WM_VSCROLL message
  895. */
  896. LRESULT CDisplay::VScroll(
  897. WORD wCode, //@parm Scrollbar event code
  898. LONG yPos) //@parm Thumb position (yPos < 0 for EM_SCROLL behavior)
  899. {
  900. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::VScroll");
  901. _TEST_INVARIANT_
  902. return 0;
  903. }
  904. /*
  905. * CDisplay::HScroll(wCode, xPos)
  906. *
  907. * @mfunc
  908. * Scroll view horizontally in response to a scrollbar event
  909. * >>> Should be called when in-place active only <<<
  910. */
  911. void CDisplay::HScroll(
  912. WORD wCode, //@parm Scrollbar event code
  913. LONG xPos) //@parm Thumb position
  914. {
  915. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::HScroll");
  916. _TEST_INVARIANT_
  917. BOOL fTracking = FALSE;
  918. LONG xScroll = _xScroll;
  919. if (xPos != 0)
  920. {
  921. // Convert x position from scroll bar to offset horizontally
  922. // in the document.
  923. xPos = ConvertScrollToXPos(xPos);
  924. }
  925. AssertSz(_ped->_fInPlaceActive, "CDisplay::HScroll() called when not in place");
  926. switch(wCode)
  927. {
  928. case SB_BOTTOM:
  929. xScroll = GetWidth();
  930. break;
  931. case SB_LINEDOWN:
  932. // Future: Make this depend on a the current first visible character
  933. xScroll += GetXWidthSys();
  934. break;
  935. case SB_LINEUP:
  936. // Future: Make this depend on a the current first visible character
  937. xScroll -= GetXWidthSys();
  938. break;
  939. case SB_PAGEDOWN:
  940. xScroll += _xWidthView;
  941. break;
  942. case SB_PAGEUP:
  943. xScroll -= _xWidthView;
  944. break;
  945. case SB_THUMBTRACK:
  946. case SB_THUMBPOSITION:
  947. if(xPos < 0)
  948. return;
  949. xScroll = xPos;
  950. fTracking = TRUE;
  951. break;
  952. case SB_TOP:
  953. xScroll = 0;
  954. break;
  955. case SB_ENDSCROLL:
  956. UpdateScrollBar(SB_HORZ);
  957. return;
  958. default:
  959. return;
  960. }
  961. if (xScroll < 0)
  962. {
  963. // xScroll is the new proposed scrolling position and
  964. // therefore cannot be less than 0.
  965. xScroll = 0;
  966. }
  967. ScrollView(xScroll, -1, fTracking, FALSE);
  968. // force position update if we just finished a track
  969. if(wCode == SB_THUMBPOSITION)
  970. UpdateScrollBar(SB_HORZ);
  971. }
  972. /*
  973. * CDisplayML::SmoothVScroll ( int direction, WORD cLines,
  974. * int speedNum, int speedDenom, BOOL fAdditive )
  975. *
  976. * @mfunc
  977. * Setup to handle fractional scrolls, at a particular speed. This was
  978. * probably initiated via a Magellan mouse roller movement, or a MButton
  979. * down message.
  980. */
  981. void CDisplay::SmoothVScroll ( int direction, WORD cLines, int speedNum, int speedDenom, BOOL fMouseRoller )
  982. {
  983. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CDisplay::SmoothVScroll");
  984. int yDelta;
  985. int cLinesAndDir;
  986. int smoothYDelta;
  987. Assert ( speedDenom );
  988. if ( IsVScrollEnabled() ) // Can scroll vertically?
  989. {
  990. _fFinishSmoothVScroll = FALSE; // We're smoothing again.
  991. // Get total pixels.
  992. if ( CheckInstallSmoothVScroll() ) // Install periodic update
  993. {
  994. _totalSmoothVScroll = 0;
  995. _nextSmoothVScroll = 0;
  996. }
  997. // Pixels per epoch
  998. cLinesAndDir = (direction < 0) ? cLines : -cLines;
  999. if( cLines )
  1000. {
  1001. yDelta = CalcYLineScrollDelta ( cLinesAndDir, FALSE );
  1002. }
  1003. else
  1004. {
  1005. yDelta = (direction < 0 ) ? _yHeightClient : -_yHeightClient;
  1006. cLines = 1; // for the MulDiv calculation below.
  1007. }
  1008. if ( yDelta ) // If something to scroll.
  1009. {
  1010. smoothYDelta = MulDiv( SMOOTH_PRECISION,// NB-Because no FLOAT type
  1011. MulDiv(yDelta, speedNum, speedDenom), cLines);
  1012. _smoothYDelta = smoothYDelta;
  1013. if ( fMouseRoller ) // roller event.
  1014. { // -> additive.
  1015. _totalSmoothVScroll += yDelta;
  1016. _continuedSmoothYDelta = 0;
  1017. _continuedSmoothVScroll = 0;
  1018. } // mButton event
  1019. else
  1020. {
  1021. if ( 0 == _totalSmoothVScroll )
  1022. _totalSmoothVScroll = yDelta;
  1023. _continuedSmoothYDelta = smoothYDelta;
  1024. _continuedSmoothVScroll = yDelta;
  1025. }
  1026. }
  1027. }
  1028. }
  1029. /*
  1030. * CDisplay::SmoothVScrollUpdate()
  1031. *
  1032. * @mfunc
  1033. * Supports SmoothVScroll. Scroll a small number of pixels.
  1034. * We are called via a periodic timing task.
  1035. */
  1036. void CDisplay::SmoothVScrollUpdate()
  1037. {
  1038. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CDisplay::SmoothVScrollUpdate");
  1039. LONG yDelta; // Magellan mouse.
  1040. BOOL fImmediateUpdate = FALSE;
  1041. _nextSmoothVScroll += _smoothYDelta;
  1042. // Remove fractional amt.
  1043. yDelta = _nextSmoothVScroll / SMOOTH_PRECISION;
  1044. // Don't overshoot.
  1045. if ( 0 == _continuedSmoothVScroll
  1046. && ( (_totalSmoothVScroll <= 0 && yDelta < _totalSmoothVScroll)
  1047. || (_totalSmoothVScroll >= 0 && yDelta > _totalSmoothVScroll)) )
  1048. {
  1049. yDelta = _totalSmoothVScroll;
  1050. }
  1051. if ( yDelta ) // Scroll yDelta, the
  1052. { // integral amount.
  1053. _totalSmoothVScroll -= yDelta;
  1054. _nextSmoothVScroll -= yDelta * SMOOTH_PRECISION;
  1055. FractionalScrollView( yDelta );
  1056. }
  1057. else if ( 0 == _totalSmoothVScroll ) // Starting to wind down?
  1058. {
  1059. _nextSmoothVScroll -= _smoothYDelta;
  1060. fImmediateUpdate = TRUE;
  1061. }
  1062. // Finished scrolling?
  1063. if ( (yDelta <= 0 && _totalSmoothVScroll >= 0) || (yDelta >= 0 && _totalSmoothVScroll <= 0 ) )
  1064. {
  1065. LONG cLinesAndDir;
  1066. if ( _continuedSmoothYDelta ) // mButton continuation.
  1067. {
  1068. _smoothYDelta = _continuedSmoothYDelta;
  1069. _totalSmoothVScroll += _continuedSmoothVScroll;
  1070. }
  1071. else
  1072. {
  1073. if ( _continuedSmoothVScroll )
  1074. {
  1075. _fFinishSmoothVScroll = TRUE; // Winding down scroll.
  1076. _continuedSmoothVScroll = 0;
  1077. // Last line's remainder...
  1078. cLinesAndDir = _smoothYDelta < 0 ? -1 : 1;
  1079. _totalSmoothVScroll = CalcYLineScrollDelta ( cLinesAndDir, TRUE );
  1080. // check for line boundry.
  1081. if ( _totalSmoothVScroll
  1082. == CalcYLineScrollDelta ( cLinesAndDir, FALSE ) )
  1083. {
  1084. _totalSmoothVScroll = 0;
  1085. }
  1086. if ( fImmediateUpdate ) // do 'this' epochs scroll.
  1087. SmoothVScrollUpdate();
  1088. }
  1089. else
  1090. {
  1091. CheckRemoveSmoothVScroll(); // All done, remove timer.
  1092. }
  1093. }
  1094. }
  1095. }
  1096. /*
  1097. * CDisplay::FinishSmoothVScroll
  1098. *
  1099. * @mfunc
  1100. * Cause smooth scroll to finish off the last fractional lines worth of
  1101. * scrolling and then stop.
  1102. */
  1103. VOID CDisplay::FinishSmoothVScroll( )
  1104. {
  1105. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CDisplay::FinishSmoothVScroll");
  1106. // any non-zero value.
  1107. if ( !_fFinishSmoothVScroll && _totalSmoothVScroll )
  1108. {
  1109. _fFinishSmoothVScroll = TRUE;
  1110. _continuedSmoothVScroll = 1;
  1111. _continuedSmoothYDelta = 0; // So smooth scroll stops.
  1112. _totalSmoothVScroll = 0;
  1113. }
  1114. }
  1115. /*
  1116. * CTxtEdit::CheckInstallSmoothScroll()
  1117. *
  1118. * @mfunc
  1119. * Install a new smooth scroll timer if not already scrolling.
  1120. */
  1121. BOOL CDisplay::CheckInstallSmoothVScroll()
  1122. {
  1123. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CDisplay::CheckInstallSmoothVScroll");
  1124. _TEST_INVARIANT_
  1125. BOOL fJustInstalled = FALSE;
  1126. if(!_fSmoothVScroll && _ped->TxSetTimer(RETID_SMOOTHSCROLL, 25))
  1127. {
  1128. _fSmoothVScroll = TRUE;
  1129. fJustInstalled = TRUE;
  1130. }
  1131. return fJustInstalled;
  1132. }
  1133. /*
  1134. * CTxtEdit::CheckRemoveSmoothVScroll ( )
  1135. *
  1136. * @mfunc
  1137. * Finish smooth scroll. If not a forced stop, then check
  1138. * to see if smooth scrolling should continue, and if so, setup
  1139. * to continue smooth scrolling.
  1140. */
  1141. VOID CDisplay::CheckRemoveSmoothVScroll ( )
  1142. {
  1143. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CDisplay::CheckRemoveSmoothVScroll");
  1144. _TEST_INVARIANT_
  1145. if( _fSmoothVScroll )
  1146. {
  1147. ScrollToLineStart( _continuedSmoothVScroll ); // Ensure stopped on a line.
  1148. _ped->TxKillTimer(RETID_SMOOTHSCROLL);
  1149. _fSmoothVScroll = FALSE;
  1150. }
  1151. }
  1152. /*
  1153. * CDisplay::LineScroll(cli, cch)
  1154. *
  1155. * @mfunc
  1156. * Scroll the view horizontally in response to a scrollbar event
  1157. *
  1158. * Note:
  1159. * No support for vertical scroll in base CDisplay. No action.
  1160. */
  1161. void CDisplay::LineScroll(
  1162. LONG cli, //@parm Count of lines to scroll vertically
  1163. LONG cch) //@parm Count of chars to scroll horizontally
  1164. {
  1165. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::LineScroll");
  1166. _TEST_INVARIANT_
  1167. return;
  1168. }
  1169. void CDisplay::FractionalScrollView (
  1170. LONG yDelta )
  1171. {
  1172. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::FractionalScrollView");
  1173. _TEST_INVARIANT_
  1174. return;
  1175. }
  1176. VOID CDisplay::ScrollToLineStart ( LONG iDirection )
  1177. {
  1178. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::ScrollToLineStart");
  1179. _TEST_INVARIANT_
  1180. return;
  1181. }
  1182. LONG CDisplay::CalcYLineScrollDelta ( LONG cli, BOOL fFractionalFirst )
  1183. {
  1184. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::CalcYLineScrollDelta");
  1185. _TEST_INVARIANT_
  1186. return 0;
  1187. }
  1188. /*
  1189. * CDisplay::DragScroll(ppt)
  1190. *
  1191. * @mfunc
  1192. * Auto scroll when dragging the mouse out of the visible view
  1193. *
  1194. * Arguments:
  1195. * ppt mouse position (in client coordinates)
  1196. */
  1197. BOOL CDisplay::DragScroll(const POINT * ppt)
  1198. {
  1199. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::DragScroll");
  1200. _TEST_INVARIANT_
  1201. const DWORD dwTime = GetTickCount();
  1202. BOOL fScrolled = FALSE;
  1203. DWORD dwScroll = 0;
  1204. RECT rc;
  1205. int nScrollInset;
  1206. AssertSz(_ped->_fInPlaceActive, "CDisplay::DragScroll() called when not in-place");
  1207. GetViewRect(rc);
  1208. nScrollInset = (int)W32->GetScrollInset();
  1209. InflateRect(&rc, - nScrollInset, - nScrollInset);
  1210. if(_fVScrollEnabled && (_ped->TxGetScrollBars() & ES_AUTOVSCROLL))
  1211. {
  1212. const yScroll = ConvertYPosToScrollPos(GetYScroll());
  1213. if(ppt->y <= rc.top)
  1214. {
  1215. dwScroll = dwAutoScrollUp;
  1216. }
  1217. else if(ppt->y > rc.bottom)
  1218. {
  1219. LONG yMax = GetScrollRange(SB_VERT);
  1220. if(yScroll < yMax)
  1221. dwScroll = dwAutoScrollDown;
  1222. }
  1223. }
  1224. if(!dwScroll && _fHScrollEnabled && (_ped->TxGetScrollBars() & ES_AUTOHSCROLL))
  1225. {
  1226. const xScroll = ConvertXPosToScrollPos(GetXScroll());
  1227. if((ppt->x <= rc.left) && (xScroll > 0))
  1228. {
  1229. dwScroll = dwAutoScrollLeft;
  1230. }
  1231. else if(ppt->x > rc.right)
  1232. {
  1233. LONG xMax = GetScrollRange(SB_HORZ);
  1234. if(xScroll < xMax)
  1235. dwScroll = dwAutoScrollRight;
  1236. }
  1237. }
  1238. if(dwScroll)
  1239. {
  1240. if(_dwScrollLast != dwScroll)
  1241. {
  1242. // entered or moved to a different auto scroll area
  1243. // reset delay counter
  1244. TRACEINFOSZ("enter auto scroll area");
  1245. _dwTimeScrollNext = dwTime + cmsecScrollDelay;
  1246. }
  1247. else if(dwTime >= _dwTimeScrollNext)
  1248. {
  1249. WORD wScrollCode = SB_LINEDOWN;
  1250. switch(dwScroll)
  1251. {
  1252. case dwAutoScrollUp:
  1253. wScrollCode = SB_LINEUP;
  1254. // fall through to dwAutoScrollDown
  1255. case dwAutoScrollDown:
  1256. // OnVScroll() doesn't scroll enough for our desires
  1257. VScroll(wScrollCode, 0);
  1258. VScroll(wScrollCode, 0);
  1259. break;
  1260. case dwAutoScrollLeft:
  1261. wScrollCode = SB_LINEUP;
  1262. // fall through to dwAutoScrollRight
  1263. case dwAutoScrollRight:
  1264. // HScroll() doesn't scroll enough for our desires
  1265. HScroll(wScrollCode, 0);
  1266. HScroll(wScrollCode, 0);
  1267. HScroll(wScrollCode, 0);
  1268. HScroll(wScrollCode, 0);
  1269. break;
  1270. #ifdef DEBUG
  1271. default:
  1272. Tracef(TRCSEVWARN, "Unexpected dwScroll %lx", dwScroll);
  1273. TRACEERRSZSC("Unexpected dwScroll", E_INVALIDARG);
  1274. break;
  1275. #endif
  1276. }
  1277. // reset interval counter
  1278. _dwTimeScrollNext = dwTime + cmsecScrollInterval;
  1279. fScrolled = TRUE;
  1280. }
  1281. }
  1282. #ifdef DEBUG
  1283. else if(_dwScrollLast)
  1284. TRACEINFOSZ("moved out of auto scroll area");
  1285. #endif
  1286. _dwScrollLast = dwScroll;
  1287. return fScrolled;
  1288. }
  1289. /*
  1290. * CDisplay::AutoScroll(pt, xScrollInset, yScrollInset)
  1291. *
  1292. * @mfunc:
  1293. * Given the current point, determine whether we need to
  1294. * scroll the client area.
  1295. *
  1296. * Requires:
  1297. * This function should only be called during a drag drop
  1298. * operation.
  1299. *
  1300. * @rdesc
  1301. * True if we are in the drag scrolling hot zone, false otherwise.
  1302. *
  1303. */
  1304. #define ScrollUp 0x0001 //These eight macros indicate the areas
  1305. #define ScrollDown 0x0010 //of the drag scrolling hot zone that tell
  1306. #define ScrollLeft 0x0100 //which direction to scroll.
  1307. #define ScrollRight 0x1000 //The last four are ambiguous (the corners)
  1308. #define ScrollUL 0x0101 //and require a little extra work.
  1309. #define ScrollUR 0x1001
  1310. #define ScrollDL 0x0110
  1311. #define ScrollDR 0x1010
  1312. BOOL CDisplay::AutoScroll(
  1313. POINT pt, //@parm Cursor location in client coordinates
  1314. const WORD xScrollInset,
  1315. const WORD yScrollInset)
  1316. {
  1317. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDisplay::AutoScroll");
  1318. static DWORD hotticks = 0; //Ticks when we entered hot zone.
  1319. static DWORD lastscrollticks = 0; //Ticks when we last scroll.
  1320. static DWORD lastticks = 0; //Ticks when last called.
  1321. DWORD delta; //Ticks since last called.
  1322. DWORD ticks; //GetTickCount ticks.
  1323. RECT rcClient; //Client rect of control.
  1324. WORD wScrollDir = 0; //Scroll direction.
  1325. BOOL fScroll = FALSE; //TRUE if we should try to scroll this time.
  1326. BOOL fEnabled = FALSE; //TRUE if scrolling is possible
  1327. //Get the current ticks and calculate ticks since last called.
  1328. //Note that if _drags does not have valid data this will be a
  1329. //bogus value, but that is handled later.
  1330. ticks = GetTickCount();
  1331. delta = ticks - lastticks;
  1332. lastticks = ticks;
  1333. //Don't do anything if no ticks since last time we were called.
  1334. if (delta)
  1335. {
  1336. // Get our client rect.
  1337. _ped->TxGetClientRect(&rcClient);
  1338. //Find out if we are in the hot zone.
  1339. //Note that if we are in one of the corners
  1340. //we will indicate two scrolling directions.
  1341. //This ambiguity will be sorted out later.
  1342. //For now we just want to know if we are in
  1343. //the zone.
  1344. if (pt.x <= (LONG)(rcClient.left + xScrollInset))
  1345. wScrollDir |= (WORD)ScrollLeft;
  1346. else if (pt.x >= (LONG)(rcClient.right - xScrollInset))
  1347. wScrollDir |= (WORD)ScrollRight;
  1348. if (pt.y <= (LONG)(rcClient.top + yScrollInset))
  1349. wScrollDir |= (WORD)ScrollUp;
  1350. else if (pt.y >= (LONG)(rcClient.bottom - yScrollInset))
  1351. wScrollDir |= (WORD)ScrollDown;
  1352. //If we are somewhere in the hot zone.
  1353. if (wScrollDir)
  1354. {
  1355. //If we just entered hotzone remember the current ticks.
  1356. if (!hotticks)
  1357. hotticks = ticks;
  1358. //If we have been in the hot zone long enough, and
  1359. //the required interval since the last scroll has elapsed
  1360. //allow another scroll. Note that if we haven't scrolled yet,
  1361. //lastscrollticks will be zero so the delta is virtually
  1362. //guaranteed to be greater than ScrollInterval.
  1363. if ((ticks - hotticks) >= (DWORD)W32->GetScrollDelay() &&
  1364. (ticks - lastscrollticks) >= (DWORD)W32->GetScrollInterval())
  1365. fScroll = TRUE;
  1366. //If we are in one of the corners, we scroll
  1367. //in the direction of the edge we are closest
  1368. //to.
  1369. switch (wScrollDir)
  1370. {
  1371. case ScrollUL:
  1372. {
  1373. if ((pt.y - rcClient.top) <= (pt.x - rcClient.left))
  1374. wScrollDir = ScrollUp;
  1375. else
  1376. wScrollDir = ScrollLeft;
  1377. break;
  1378. }
  1379. case ScrollUR:
  1380. {
  1381. if ((pt.y - rcClient.top) <= (rcClient.right - pt.x))
  1382. wScrollDir = ScrollUp;
  1383. else
  1384. wScrollDir = ScrollRight;
  1385. break;
  1386. }
  1387. case ScrollDL:
  1388. {
  1389. if ((rcClient.bottom - pt.y) <= (pt.x - rcClient.left))
  1390. wScrollDir = ScrollDown;
  1391. else
  1392. wScrollDir = ScrollLeft;
  1393. break;
  1394. }
  1395. case ScrollDR:
  1396. {
  1397. if ((rcClient.bottom - pt.y) <= (rcClient.right - pt.x))
  1398. wScrollDir = ScrollDown;
  1399. else
  1400. wScrollDir = ScrollRight;
  1401. break;
  1402. }
  1403. }
  1404. }
  1405. else
  1406. {
  1407. //We aren't in the hot zone so reset hotticks as a
  1408. //flag so we know the first time we reenter it.
  1409. hotticks = 0;
  1410. }
  1411. //Do processing for horizontal scrolling if necessary
  1412. if (wScrollDir == ScrollLeft || wScrollDir == ScrollRight)
  1413. {
  1414. LONG xRange, xScroll, dx;
  1415. xScroll = ConvertXPosToScrollPos(GetXScroll());
  1416. xRange = GetScrollRange(SB_HORZ);
  1417. dx = W32->GetScrollHAmount();
  1418. fEnabled = IsHScrollEnabled();
  1419. if (wScrollDir == ScrollLeft)
  1420. {
  1421. fEnabled = fEnabled && (xScroll > 0);
  1422. xScroll -= dx;
  1423. xScroll = max(xScroll, 0);
  1424. }
  1425. else
  1426. {
  1427. fEnabled = fEnabled && (xScroll < xRange);
  1428. xScroll += dx;
  1429. xScroll = min(xScroll, xRange);
  1430. }
  1431. //Do the actual scrolling if necessary.
  1432. if (fEnabled && fScroll)
  1433. {
  1434. HScroll(SB_THUMBPOSITION, xScroll);
  1435. lastscrollticks = ticks;
  1436. }
  1437. }
  1438. //Do processing for Vertical scrolling if necessary
  1439. else if (wScrollDir == ScrollUp || wScrollDir == ScrollDown)
  1440. {
  1441. LONG yRange, yScroll, dy;
  1442. yScroll = ConvertYPosToScrollPos(GetYScroll());
  1443. yRange = GetScrollRange(SB_VERT);
  1444. dy = W32->GetScrollVAmount();
  1445. fEnabled = IsVScrollEnabled();
  1446. if (wScrollDir == ScrollUp)
  1447. {
  1448. fEnabled = fEnabled && (yScroll > 0);
  1449. yScroll -= dy;
  1450. yScroll = max(yScroll, 0);
  1451. }
  1452. else
  1453. {
  1454. fEnabled = fEnabled && (yScroll < yRange);
  1455. yScroll += dy;
  1456. yScroll = min(yScroll, yRange);
  1457. }
  1458. //Do the actual scrolling if necessary.
  1459. if (fEnabled && fScroll)
  1460. {
  1461. // We need to scroll fractionally because the scroll logic tries
  1462. // to put a full line on the top and if the scroll amount is less
  1463. // than a full line, the scrolling will get stuck on that line.
  1464. ScrollView(_xScroll, yScroll, FALSE, TRUE);
  1465. lastscrollticks = ticks;
  1466. }
  1467. }
  1468. }
  1469. return fEnabled;
  1470. }
  1471. /*
  1472. * CDisplay::AdjustToDisplayLastLine(yBase, yScroll)
  1473. *
  1474. * @mfunc
  1475. * Calculate the yscroll necessary to get the last line to display
  1476. *
  1477. * @rdesc
  1478. * Updated yScroll
  1479. *
  1480. * @devnote:
  1481. * This method is only really useful for ML displays. This method
  1482. * here is a placeholder which does nothing which is useful for
  1483. * all other displays.
  1484. */
  1485. LONG CDisplay::AdjustToDisplayLastLine(
  1486. LONG yBase, //@parm Actual yScroll to display
  1487. LONG yScroll) //@parm Proposed amount to scroll
  1488. {
  1489. return yScroll;
  1490. }
  1491. /*
  1492. * CDisplay::GetScrollRange(nBar)
  1493. *
  1494. * @mfunc
  1495. * Returns the max part of a scrollbar range
  1496. * No scrollbar support in the base class: returns 0.
  1497. *
  1498. * @rdesc
  1499. * LONG max part of scrollbar range
  1500. */
  1501. LONG CDisplay::GetScrollRange(
  1502. INT nBar) const
  1503. {
  1504. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::GetScrollRange");
  1505. _TEST_INVARIANT_
  1506. return 0;
  1507. }
  1508. /*
  1509. * CDisplay::UpdateScrollBar(nBar, fUpdateRange)
  1510. *
  1511. * @mfunc
  1512. * Update either the horizontal or vertial scroll bar
  1513. * Also figure whether the scroll bar should be visible or not
  1514. * No scrollbar support in the base class: no action.
  1515. *
  1516. * @rdesc
  1517. * BOOL
  1518. */
  1519. BOOL CDisplay::UpdateScrollBar(
  1520. INT nBar,
  1521. BOOL fUpdateRange)
  1522. {
  1523. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::UpdateScrollBar");
  1524. _TEST_INVARIANT_
  1525. return TRUE;
  1526. }
  1527. /*
  1528. * CDisplay::GetZoomDenominator()
  1529. *
  1530. * @mfunc
  1531. * Get zoom denominator
  1532. *
  1533. * @rdesc
  1534. * Returns zoom denominator
  1535. *
  1536. * @devnote:
  1537. * FUTURE: (Ricksa) we should investigate how to cache this data since
  1538. * the display needs to keep a temporary zoom denominator anyway.
  1539. */
  1540. LONG CDisplay::GetZoomDenominator() const
  1541. {
  1542. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::GetZoomDenominator");
  1543. if(_ped->GetZoomDenominator()) // Simple EM_SETZOOM API
  1544. return _ped->GetZoomDenominator(); // supercedes complicated
  1545. // Forms^3 API
  1546. // Default zoom to error case. The error case is a very low
  1547. // probability event that we can do nothing to recover. So we
  1548. // just set the value to something reasonable and continue.
  1549. LONG lZoomDenominator = _yHeightClient;
  1550. // Is temporary zoom denominator set?
  1551. if(INVALID_ZOOM_DENOMINATOR == _lTempZoomDenominator)
  1552. {
  1553. // No - Get extent size from host
  1554. SIZEL sizelExtent;
  1555. if(SUCCEEDED(_ped->TxGetExtent(&sizelExtent)))
  1556. {
  1557. // Convert height to device units. Note that by definition, we
  1558. // can ignore horizontal extents so we do. Use CDevDesc conversion
  1559. // to avoid infinite recursion
  1560. lZoomDenominator = CDevDesc::HimetricYtoDY(sizelExtent.cy);
  1561. }
  1562. }
  1563. else // Temporary zoom denominator is set: use it
  1564. lZoomDenominator = CDevDesc::HimetricYtoDY(_lTempZoomDenominator);
  1565. return lZoomDenominator > 0 ? lZoomDenominator : 1;
  1566. }
  1567. /*
  1568. * CDisplay::GetZoomNumerator()
  1569. *
  1570. * @mfunc
  1571. * Get zoom numerator
  1572. *
  1573. * @rdesc
  1574. * Returns zoom numerator
  1575. */
  1576. LONG CDisplay::GetZoomNumerator() const
  1577. {
  1578. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::GetZoomNumerator");
  1579. if(_ped->GetZoomNumerator()) // Simple EM_SETZOOM API
  1580. return _ped->GetZoomNumerator(); // supercedes complicated
  1581. // Forms^3 API
  1582. return _yHeightClient > 0 ? _yHeightClient : 1;
  1583. }
  1584. /*
  1585. * CDisplay::Zoom(x)
  1586. *
  1587. * @mfunc
  1588. * Get zoomed x
  1589. *
  1590. * @rdesc
  1591. * Returns zoomed x
  1592. */
  1593. // REVIEW (keithcu) Why does Zoom do anything when we are in print preview?
  1594. LONG CDisplay::Zoom(LONG x) const
  1595. {
  1596. return MulDiv(x, GetZoomNumerator(), GetZoomDenominator());
  1597. }
  1598. /*
  1599. * CDisplay::UnZoom(x)
  1600. *
  1601. * @mfunc
  1602. * Get unzoomed x
  1603. *
  1604. * @rdesc
  1605. * Returns unzoomed x
  1606. */
  1607. LONG CDisplay::UnZoom(LONG x) const
  1608. {
  1609. return MulDiv(x, GetZoomDenominator(), GetZoomNumerator());
  1610. }
  1611. /*
  1612. * CDisplay::HimetricXtoDX(xHimetric)
  1613. *
  1614. * @mfunc
  1615. * Get device x coordinate corresponding to Himetric x coordinate
  1616. *
  1617. * @rdesc
  1618. * Returns device coordinate
  1619. */
  1620. LONG CDisplay::HimetricXtoDX(
  1621. LONG xHimetric) const
  1622. {
  1623. return CDevDesc::HimetricXtoDX(Zoom(xHimetric));
  1624. }
  1625. /*
  1626. * CDisplay::HimetricYtoDY(yHimetric)
  1627. *
  1628. * @mfunc
  1629. * Get device y coordinate corresponding to Himetric y coordinate
  1630. *
  1631. * @rdesc
  1632. * Returns device coordinate
  1633. */
  1634. LONG CDisplay::HimetricYtoDY(
  1635. LONG yHimetric) const
  1636. {
  1637. return CDevDesc::HimetricYtoDY(Zoom(yHimetric));
  1638. }
  1639. /*
  1640. * CDisplay::DXtoHimetricX(dx)
  1641. *
  1642. * @mfunc
  1643. * Get Himetric x coordinate corresponding to device x coordinate
  1644. *
  1645. * @rdesc
  1646. * Returns Himetric coordinate
  1647. */
  1648. LONG CDisplay::DXtoHimetricX(
  1649. LONG dx) const
  1650. {
  1651. return UnZoom(CDevDesc::DXtoHimetricX(dx));
  1652. }
  1653. /*
  1654. * CDisplay::DXtoHimetricX(dy)
  1655. *
  1656. * @mfunc
  1657. * Get Himetric y coordinate corresponding to device y coordinate
  1658. *
  1659. * @rdesc
  1660. * Returns Himetric coordinate
  1661. */
  1662. LONG CDisplay::DYtoHimetricY(
  1663. LONG dy) const
  1664. {
  1665. return UnZoom(CDevDesc::DYtoHimetricY(dy));
  1666. }
  1667. /*
  1668. * CDisplay::SetClientHeight(yNewClientHeight)
  1669. *
  1670. * @mfunc
  1671. * Reset height of client rectangle
  1672. *
  1673. * @rdesc
  1674. * Returns previous height of the client rectangle
  1675. */
  1676. LONG CDisplay::SetClientHeight(
  1677. LONG yNewClientHeight) //@parm New height for the client rectangle.
  1678. {
  1679. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::SetClientHeight");
  1680. LONG yOldHeight = _yHeightClient;
  1681. _yHeightClient = yNewClientHeight;
  1682. return yOldHeight;
  1683. }
  1684. /*
  1685. * CDisplay::GetCachedSize(pdwWidth, pdwHeight)
  1686. *
  1687. * @mfunc calculates the cached client size (since it's not really
  1688. * cached :-)
  1689. *
  1690. * @rdesc
  1691. * HRESULT = NOERROR
  1692. */
  1693. HRESULT CDisplay::GetCachedSize(
  1694. DWORD *pdwWidth, //@parm where to put the width
  1695. DWORD *pdwHeight) //@parm where to put the height
  1696. {
  1697. RECT rcInset;
  1698. _ped->TxGetViewInset(&rcInset, this);
  1699. *pdwHeight = _yHeightClient;
  1700. *pdwWidth = _xWidthView + rcInset.left + rcInset.right
  1701. + GetSelBarInPixels();
  1702. return NOERROR;
  1703. }
  1704. /*
  1705. * CDisplay::TransparentHitTest(hdc, prcClient, pt, pHitResult)
  1706. *
  1707. * @mfunc
  1708. * Determine if the hit is on a transparent control
  1709. *
  1710. * @rdesc
  1711. * Returns HRESULT of call usually S_OK.
  1712. *
  1713. * @devnote
  1714. * FUTURE: This code needs to be investigated for possible optimizations.
  1715. *
  1716. * This code is assumes that all remeasuring needed has been done before
  1717. * this routine is called.
  1718. */
  1719. HRESULT CDisplay::TransparentHitTest(
  1720. HDC hdc, //@parm DC for actual drawing
  1721. LPCRECT prcClient, //@parm Client rectangle for rendering
  1722. POINT pt, //@parm Point to hittest against
  1723. DWORD * pHitResult) //@parm Result of the hit test see TXTHITRESULT
  1724. {
  1725. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::TransparentHitTest");
  1726. COLORREF crBackground = _ped->TxGetBackColor();
  1727. HDC hdcMem = NULL;
  1728. HRESULT hr = E_FAIL;
  1729. int iRow;
  1730. COffScreenDC osdc;
  1731. RECT rcClient;
  1732. RECT rcRender;
  1733. RECT rcView;
  1734. // Render view to a memory DC
  1735. // Compute zero based client rectangle
  1736. rcClient.left = 0;
  1737. rcClient.top = 0;
  1738. rcClient.right = prcClient->right - prcClient->left;
  1739. rcClient.bottom = prcClient->bottom - prcClient->top;
  1740. // Create a memory DC
  1741. hdcMem = osdc.Init(hdc, rcClient.right, rcClient.bottom, crBackground);
  1742. if(!hdcMem)
  1743. goto Cleanup;
  1744. // Initialize display
  1745. osdc.FillBitmap(rcClient.bottom, crBackground);
  1746. // Set the DC to the memory DC
  1747. SetDC(hdcMem);
  1748. // Get view rectangle that we need for rendering
  1749. GetViewRect(rcView, &rcClient);
  1750. // Adjust point to be relative to the memory display
  1751. pt.x -= prcClient->left;
  1752. pt.y -= prcClient->top;
  1753. // Initalize box around point. Note that we only really need to render
  1754. // the data inside this box because this is the only area that we will
  1755. // test.
  1756. rcRender.top = pt.y - HIT_CLOSE_RECT_INC;
  1757. if (rcRender.top < 0)
  1758. rcRender.top = 0;
  1759. rcRender.bottom = pt.y + HIT_CLOSE_RECT_INC;
  1760. if (rcRender.bottom > rcClient.bottom)
  1761. rcRender.bottom = rcClient.bottom;
  1762. rcRender.left = pt.x - HIT_CLOSE_RECT_INC;
  1763. if (rcRender.left < 0)
  1764. rcRender.left = 0;
  1765. rcRender.right = pt.x + HIT_CLOSE_RECT_INC;
  1766. if (rcRender.right > rcClient.right)
  1767. rcRender.right = rcClient.right;
  1768. // Now render
  1769. Render(rcView, rcRender);
  1770. // Hit test
  1771. // Assume no hit
  1772. *pHitResult = TXTHITRESULT_TRANSPARENT;
  1773. // At this point we won't fail this
  1774. hr = S_OK;
  1775. // Is there an exact hit?
  1776. if (GetPixel(hdcMem, pt.x, pt.y) != crBackground)
  1777. {
  1778. *pHitResult = TXTHITRESULT_HIT;
  1779. goto Cleanup;
  1780. }
  1781. // Is it close? We determine closeness by putting
  1782. // a 10 x 10 pixel box around the hit point and
  1783. // seeing if there is a hit there.
  1784. // Loop examining each bit in the box to see if it is on.
  1785. for (iRow = rcRender.top; iRow <= rcRender.bottom; iRow++)
  1786. {
  1787. for (int iCol = rcRender.left; iCol <= rcRender.right; iCol++)
  1788. {
  1789. if (GetPixel(hdcMem, iCol, iRow) != crBackground)
  1790. {
  1791. *pHitResult = TXTHITRESULT_CLOSE;
  1792. goto Cleanup;
  1793. }
  1794. }
  1795. }
  1796. Cleanup:
  1797. ResetDC();
  1798. return hr;
  1799. }
  1800. //============================ ITxNotify Interface ==========================
  1801. /*
  1802. * CDisplay::OnPreReplaceRange(cp, cchDel, cchNew, cpFormatMin, cpFormatMax)
  1803. *
  1804. * @mfunc
  1805. * Preprocess a change in backing store
  1806. *
  1807. * @devnote
  1808. * This display doesn't care about before changes
  1809. */
  1810. void CDisplay::OnPreReplaceRange(
  1811. LONG cp, //@parm cp where ReplaceRange starts ("cpMin")
  1812. LONG cchDel, //@parm Count of chars after cp that are deleted
  1813. LONG cchNew, //@parm Count of chars inserted after cp
  1814. LONG cpFormatMin, //@parm cpMin for a formatting change
  1815. LONG cpFormatMax) //@parm cpMost for a formatting change
  1816. {
  1817. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::OnPreReplaceRange");
  1818. // Display doesn't care about before the fact
  1819. }
  1820. /*
  1821. * CDisplay::OnPostReplaceRange(cp, cchDel, cchNew, cpFormatMin, cpFormatMax)
  1822. *
  1823. * @mfunc
  1824. * Process a change to the backing store as it applies to the display
  1825. */
  1826. void CDisplay::OnPostReplaceRange(
  1827. LONG cp, //@parm cp where ReplaceRange starts ("cpMin")
  1828. LONG cchDel, //@parm Count of chars after cp that are deleted
  1829. LONG cchNew, //@parm Count of chars inserted after cp
  1830. LONG cpFormatMin, //@parm cpMin for a formatting change
  1831. LONG cpFormatMax) //@parm cpMost for a formatting change
  1832. {
  1833. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::OnPostReplaceRange");
  1834. // There is one NO-OP's for the display:
  1835. // currently loading a file.
  1836. //
  1837. // We NO-OP the load case because loading an RTF file can consist
  1838. // of potentially very many small actions as we peice together
  1839. // the various bits of formatted text. Once done, the load code
  1840. // will go through and do an update-all to the display.
  1841. Assert (cp != CONVERT_TO_PLAIN); // Handled with PreReplace notifications
  1842. // Figure out range needed to update
  1843. LONG cpNew = min(cp, cpFormatMin);
  1844. if(CP_INFINITE == cpNew)
  1845. {
  1846. // If both cp's are infinite we don't need to bother with
  1847. // this operation.
  1848. return;
  1849. }
  1850. if(!_ped->_fInPlaceActive)
  1851. {
  1852. // If not active, just invalidate everything
  1853. InvalidateRecalc();
  1854. _ped->TxInvalidateRect(NULL, FALSE);
  1855. _ped->TxUpdateWindow();
  1856. return;
  1857. }
  1858. // Adjust cp for further calculations
  1859. if(CP_INFINITE == cp)
  1860. cp = 0;
  1861. // find the new max end of the original region.
  1862. LONG cpForEnd = max( (cp + cchDel), cpFormatMax);
  1863. // Number of deleted characters is the difference between the previous two
  1864. LONG cchDelForDisplay = cpForEnd - cpNew;
  1865. // The number deleted is simply number of new characters adjusted by
  1866. // the change in the number of characters.
  1867. LONG cchNewForDisplay = cchDelForDisplay + (cchNew - cchDel);
  1868. #ifdef LINESERVICES
  1869. if (g_pols)
  1870. g_pols->DestroyLine(this);
  1871. #endif
  1872. if(_padc)
  1873. {
  1874. // Display is frozen so accumulate the change instead of actually
  1875. // displaying it on the screen.
  1876. _padc->UpdateRecalcRegion(cpNew, cchDelForDisplay, cchNewForDisplay);
  1877. return;
  1878. }
  1879. // Tell display to update
  1880. CRchTxtPtr tp(_ped, cpNew);
  1881. UpdateView(tp, cchDelForDisplay, cchNewForDisplay);
  1882. }
  1883. /*
  1884. * CDisplay::SetWordWrap(fWordWrap)
  1885. *
  1886. * @mfunc
  1887. * Sets the no wrap flag
  1888. *
  1889. * @devnote
  1890. * We will always allow the property to be set but we will not
  1891. * necessarily pay attention. In other words, word wrap has no
  1892. * effect on a single line edit control.
  1893. */
  1894. void CDisplay::SetWordWrap(
  1895. BOOL fWordWrap) //@param TRUE - turn on word wrap.
  1896. {
  1897. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::SetWordWrap");
  1898. AssertSz((fWordWrap == TRUE) || (fWordWrap == FALSE),
  1899. "CDisplay::SetWordWrap bad input flag");
  1900. // Set nowrap to whatever is coming in.
  1901. _fWordWrap = fWordWrap;
  1902. }
  1903. /*
  1904. * CDisplay::GetWordWrap()
  1905. *
  1906. * @mfunc
  1907. * Return state of word wrap property
  1908. *
  1909. * @rdesc
  1910. * TRUE - word wrap is on
  1911. * FALSE - word wrap is is off.
  1912. *
  1913. * @devnote
  1914. * Derived classes such as CDisplaySL override this.
  1915. */
  1916. BOOL CDisplay::GetWordWrap() const
  1917. {
  1918. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplay::GetWordWrap");
  1919. return _fWordWrap;
  1920. }
  1921. /*
  1922. * CDisplay::GetViewDim()
  1923. *
  1924. * @mfunc
  1925. * Return the height & width of view adjusted for view inset
  1926. */
  1927. void CDisplay::GetViewDim(
  1928. LONG& widthView, //@parm Where to return the width
  1929. LONG& heightView) //@parm Where to return the height
  1930. {
  1931. // We build a client rectangle to take advantage of GetViewRect routine
  1932. // which really does all the work for us.
  1933. RECT rcClient;
  1934. rcClient.left = 0;
  1935. rcClient.top = 0;
  1936. rcClient.right = widthView;
  1937. rcClient.bottom = heightView;
  1938. // Take into account inset and selection bar. The parameters here are a bit
  1939. // of a trick. The second parameter gets copied into the first and since
  1940. // we don't need the original client rect we save a rect off the stack.
  1941. GetViewRect(rcClient, &rcClient);
  1942. widthView = rcClient.right - rcClient.left;
  1943. heightView = rcClient.bottom - rcClient.top;
  1944. }
  1945. /*
  1946. * CDisplay::SaveUpdateCaret (fScrollIntoView)
  1947. *
  1948. * @mfunc Save UpdateCaret parameter so update caret can be called
  1949. * after the display is thawed.
  1950. *
  1951. * @rdesc None.
  1952. *
  1953. * @devnote
  1954. * This should only be called if IsFrozen is true.
  1955. */
  1956. void CDisplay::SaveUpdateCaret(
  1957. BOOL fScrollIntoView)
  1958. {
  1959. #ifdef DEBUG
  1960. if (_padc == NULL)
  1961. {
  1962. TRACEERRORSZ("CDisplay::SaveUpdateCaret called on thawed display");
  1963. }
  1964. #endif // DEBUG
  1965. if(_padc)
  1966. _padc->SaveUpdateCaret(fScrollIntoView);
  1967. }
  1968. /*
  1969. * CDisplay::SetNeedRedisplayOnThaw
  1970. *
  1971. * @mfunc
  1972. * Automatically redisplay control on thaw
  1973. */
  1974. void CDisplay::SetNeedRedisplayOnThaw(BOOL fNeedRedisplay)
  1975. {
  1976. Assert (_padc);
  1977. _padc->SetNeedRedisplayOnThaw(fNeedRedisplay);
  1978. }
  1979. /*
  1980. * CDisplay::Freeze
  1981. *
  1982. * @mfunc
  1983. * Prevent any updates from occuring in the display
  1984. */
  1985. void CDisplay::Freeze()
  1986. {
  1987. if(NULL == _padc)
  1988. {
  1989. // Allocate object to keep track of changes
  1990. _padc = new CAccumDisplayChanges();
  1991. // We can now return because the accum object has a reference
  1992. // or the memory allocation failed. If the memory allocation
  1993. // failed, This really isn't a catastrophe because all it means
  1994. // is that things will get displayed ugly temporarily, so we can
  1995. // pretend it didn't happen.
  1996. return;
  1997. }
  1998. // Tell object that an additional freeze has occurred.
  1999. _padc->AddRef();
  2000. }
  2001. /*
  2002. * CDisplay::Thaw()
  2003. *
  2004. * @mfunc
  2005. * If this is the last thaw, then cause display to be updated.
  2006. *
  2007. */
  2008. void CDisplay::Thaw()
  2009. {
  2010. BOOL fUpdateCaret, fScrollIntoView, fNeedRedisplay;
  2011. LONG cp, cchNew, cchDel;
  2012. CTxtSelection *psel;
  2013. if(_padc)
  2014. {
  2015. // Release reference to accum object
  2016. if(_padc->Release() == 0)
  2017. {
  2018. // Last thaw so we need to update display
  2019. // Get the changes
  2020. _padc->GetUpdateRegion(&cp, &cchDel, &cchNew,
  2021. &fUpdateCaret, &fScrollIntoView, &fNeedRedisplay);
  2022. // Clear the object - note we do this before
  2023. // the update just on the off chance that
  2024. // a new freeze manages to get in during the
  2025. // update of the display.
  2026. delete _padc;
  2027. _padc = NULL;
  2028. if(cp != CP_INFINITE)
  2029. {
  2030. // Display changed
  2031. if(!_ped->fInplaceActive())
  2032. {
  2033. // Are not inplace active so we need to put this operation
  2034. // off till a more appropriate time.
  2035. InvalidateRecalc();
  2036. _ped->TxInvalidateRect(NULL, FALSE);
  2037. _ped->TxUpdateWindow();
  2038. return;
  2039. }
  2040. // Update display
  2041. CRchTxtPtr rtp(_ped, cp);
  2042. if(!UpdateView(rtp, cchDel, cchNew))
  2043. return; // Update failed
  2044. }
  2045. if (fNeedRedisplay)
  2046. _ped->TxInvalidateRect(NULL, FALSE);
  2047. // Did selection request a caret update?
  2048. if(fUpdateCaret && _ped->fInplaceActive())
  2049. {
  2050. psel = _ped->GetSel();
  2051. psel->UpdateCaret(fScrollIntoView);
  2052. }
  2053. }
  2054. }
  2055. }
  2056. /*
  2057. * CDisplay::IsPrinter
  2058. *
  2059. * @mfunc
  2060. * Returns whether this is a printer
  2061. *
  2062. * @rdesc
  2063. * TRUE - is a display to a printer
  2064. * FALSE - is not a display to a printer
  2065. *
  2066. * @devnote
  2067. * No display except a display CDisplayPrinter should
  2068. * ever have a chance to return TRUE to this function.
  2069. */
  2070. BOOL CDisplay::IsPrinter() const
  2071. {
  2072. return FALSE;
  2073. }
  2074. /*
  2075. * CDisplay::Zombie ()
  2076. *
  2077. * @mfunc
  2078. * Turn this object into a zombie
  2079. */
  2080. void CDisplay::Zombie ()
  2081. {
  2082. TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "CDisplay::Zombie");
  2083. }
  2084. /*
  2085. * CDisplay::IsHScrollEnabled ()
  2086. *
  2087. * @mfunc
  2088. * Return whether horizontal scroll bar is enabled
  2089. *
  2090. * @rdesc
  2091. * TRUE - yes
  2092. * FALSE - no
  2093. *
  2094. * @devnote
  2095. * The reason for this routine is that _fHScrollEnabled means
  2096. * to scroll text and can be set even if there is no scroll
  2097. * bar. Therefore, we need to look at the host properties
  2098. * as well to tell use whether this means there are scroll
  2099. * bars.
  2100. */
  2101. BOOL CDisplay::IsHScrollEnabled()
  2102. {
  2103. return _fHScrollEnabled && ((_ped->TxGetScrollBars() & WS_HSCROLL) != 0);
  2104. }