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.

3652 lines
90 KiB

  1. /*
  2. * @doc INTERNAL
  3. *
  4. * @module DISPML.CPP -- CDisplayML class |
  5. *
  6. * This is the Multi-line display engine. See disp.c for the base class
  7. * methods and dispsl.c for the single-line display engine.
  8. *
  9. * Owner:<nl>
  10. * RichEdit 1.0 code: David R. Fulmer
  11. * Christian Fortini (initial conversion to C++)
  12. * Murray Sargent
  13. * Rick Sailor (for most of RE 2.0)
  14. *
  15. * Copyright (c) 1995-1998, Microsoft Corporation. All rights reserved.
  16. */
  17. #include "_common.h"
  18. #include "_dispml.h"
  19. #include "_edit.h"
  20. #include "_font.h"
  21. #include "_measure.h"
  22. #include "_render.h"
  23. #include "_select.h"
  24. #include "_dfreeze.h"
  25. /*
  26. #include "icecap.h"
  27. class CCapProfile
  28. {
  29. public:
  30. CCapProfile() { StartProfile(PROFILE_THREADLEVEL, PROFILE_CURRENTID); }
  31. ~CCapProfile() { StopProfile(PROFILE_THREADLEVEL, PROFILE_CURRENTID); }
  32. };
  33. */
  34. ASSERTDATA
  35. //
  36. // Invariant support
  37. //
  38. #define DEBUG_CLASSNAME CDisplayML
  39. #include "_invar.h"
  40. // Timer tick counts for background task
  41. #define cmsecBgndInterval 300
  42. #define cmsecBgndBusy 100
  43. // Lines ahead
  44. const LONG cExtraBeforeLazy = 60;
  45. // If we need to calc at least this many characters, then put up a wait
  46. // cursor. NB! 4096 is not a measured number; it just seemed like a good
  47. // one.
  48. #define NUMCHARFORWAITCURSOR 4096
  49. #ifndef DEBUG
  50. #define CheckView()
  51. #define CheckLineArray()
  52. #endif
  53. // =========================== CDisplayML =====================================================
  54. #ifdef DEBUG
  55. /*
  56. * CDisplayML::Invariant
  57. *
  58. * @mfunc Make sure the display is in a valid state
  59. *
  60. * @rdesc TRUE if the tests succeeded, FALSE otherwise
  61. */
  62. BOOL CDisplayML::Invariant(void) const
  63. {
  64. CDisplay::Invariant();
  65. return TRUE;
  66. }
  67. #endif // DEBUG
  68. /*
  69. * CDisplayML::CalcScrollHeight()
  70. *
  71. * @mfunc
  72. * Calculate the maximum Y scroll position.
  73. *
  74. * @rdesc
  75. * Maximum possible scrolling position
  76. *
  77. * @devnote
  78. * This routine exists because plain text controls do not have
  79. * the auto-EOP and so the scroll height is different than
  80. * the height of the control if the text ends in an EOP type
  81. * character.
  82. */
  83. LONG CDisplayML::CalcScrollHeight(LONG yHeight) const
  84. {
  85. // The max scroll height for plain text controls is calculated
  86. // differently because they don't have an automatic EOP character.
  87. if(!_ped->IsRich() && Count())
  88. {
  89. // If last character is an EOP, bump scroll height
  90. CLine *lp = Elem(Count() - 1); // Get last line in array
  91. if(lp->_cchEOP)
  92. yHeight += lp->GetHeight();
  93. }
  94. return yHeight;
  95. }
  96. /*
  97. * CDisplayML::GetMaxYScroll()
  98. *
  99. * @mfunc
  100. * Calculate the maximum Y scroll position.
  101. *
  102. * @rdesc
  103. * Maximum possible scrolling position
  104. *
  105. * @devnote
  106. * This routine exists because we may have to come back and modify this
  107. * calculation for 1.0 compatibility. If we do, this routine only needs
  108. * to be changed in one place rather than the three at which it is used.
  109. *
  110. */
  111. inline LONG CDisplayML::GetMaxYScroll() const
  112. {
  113. // The following code is turn off because we don't want to support
  114. // 1.0 mode unless someone complained about it.
  115. #if 0
  116. if (_ped->Get10Mode())
  117. {
  118. // Ensure last line is always visible
  119. // (use dy as temp to calculate max scroll)
  120. yScroll = Elem(max(0, Count() - 1))->_yHeight;
  121. if(yScroll > _yHeightView)
  122. yScroll = _yHeightView;
  123. yScroll = _yHeight - yScroll;
  124. }
  125. #endif //0
  126. return CalcScrollHeight(_yHeight);
  127. }
  128. /*
  129. * CDisplayML::ConvertScrollToYPos()
  130. *
  131. * @mfunc
  132. * Calculate the real scroll position from the scroll position
  133. *
  134. * @rdesc
  135. * Y position from scroll
  136. *
  137. * @devnote
  138. * This routine exists because the thumb position messages
  139. * are limited to 16-bits so we extrapolate when the Y position
  140. * gets greater than that.
  141. */
  142. LONG CDisplayML::ConvertScrollToYPos(
  143. LONG yPos) //@parm Scroll position
  144. {
  145. // Get maximum scroll range
  146. LONG yRange = GetMaxYScroll();
  147. // Has maximum scroll range exceeded 16-bits?
  148. if(yRange >= _UI16_MAX)
  149. {
  150. // Yes - Extrapolate to "real" yPos
  151. yPos = MulDiv(yPos, yRange, _UI16_MAX);
  152. }
  153. return yPos;
  154. }
  155. /*
  156. * CDisplayML::ConvertYPosToScrollPos()
  157. *
  158. * @mfunc
  159. * Calculate the scroll position from the Y position in the document.
  160. *
  161. * @rdesc
  162. * Scroll position from Y position
  163. *
  164. * @devnote
  165. * This routine exists because the thumb position messages
  166. * are limited to 16-bits so we extrapolate when the Y position
  167. * gets greater than that.
  168. *
  169. */
  170. inline LONG CDisplayML::ConvertYPosToScrollPos(
  171. LONG yPos) //@parm Y position in document
  172. {
  173. // Get maximum scroll range
  174. LONG yRange = GetMaxYScroll();
  175. // Has maximum scroll range exceeded 16-bits?
  176. if(yRange >= _UI16_MAX)
  177. {
  178. // Yes - Extrapolate to "real" yPos
  179. yPos = MulDiv(yPos, _UI16_MAX, yRange);
  180. }
  181. return yPos;
  182. }
  183. CDisplayML::CDisplayML (CTxtEdit* ped)
  184. : CDisplay (ped), _pddTarget(NULL)
  185. {
  186. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::CDisplayML");
  187. Assert(!_xWidthMax && !_yHeightMax);
  188. _fMultiLine = TRUE;
  189. }
  190. CDisplayML::~CDisplayML()
  191. {
  192. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::~CDisplayML");
  193. delete _pddTarget;
  194. }
  195. /*
  196. * CDisplayML::Init()
  197. *
  198. * @mfunc
  199. * Init this display for the screen
  200. *
  201. * @rdesc
  202. * TRUE iff initialization succeeded
  203. */
  204. BOOL CDisplayML::Init()
  205. {
  206. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::Init");
  207. // Initialize our base class
  208. if(!CDisplay::Init())
  209. return FALSE;
  210. AssertSz(_ped, "CDisplayML::Init(): _ped not initialized in display");
  211. // Verify allocation zeroed memory out
  212. Assert(!_yCalcMax && !_xWidth && !_yHeight && !_cpMin);
  213. Assert(!_fBgndRecalc && !_fVScrollEnabled && !_fHScrollEnabled);
  214. // The printer view is not main, therefore we do this to make
  215. // sure scroll bars are not created for print views.
  216. DWORD dwScrollBars = _ped->TxGetScrollBars();
  217. if(IsMain() && (dwScrollBars & ES_DISABLENOSCROLL))
  218. {
  219. if(dwScrollBars & WS_VSCROLL)
  220. {
  221. // This causes wlm to assert on the mac. something about
  222. // scrollbar being disabled
  223. _ped->TxSetScrollRange (SB_VERT, 0, 1, TRUE);
  224. _ped->TxEnableScrollBar(SB_VERT, ESB_DISABLE_BOTH);
  225. }
  226. // Set horizontal scroll range and pos
  227. // ??? - CF need fixing for windowless case
  228. if(dwScrollBars & WS_HSCROLL)
  229. {
  230. _ped->TxSetScrollRange (SB_HORZ, 0, 1, TRUE);
  231. _ped->TxEnableScrollBar(SB_HORZ, ESB_DISABLE_BOTH);
  232. }
  233. }
  234. SetWordWrap(_ped->TxGetWordWrap());
  235. _cpFirstVisible = _cpMin;
  236. Assert(!_xScroll && !_yScroll && !_iliFirstVisible &&
  237. !_cpFirstVisible && !_dyFirstVisible);
  238. _TEST_INVARIANT_
  239. return TRUE;
  240. }
  241. //================================ Device drivers ===================================
  242. /*
  243. * CDisplayML::SetMainTargetDC(hdc, xWidthMax)
  244. *
  245. * @mfunc
  246. * Sets a target device for this display and updates view
  247. *
  248. * @devnote
  249. * Target device can't be a metafile (can get char width out of a
  250. * metafile)
  251. *
  252. * @rdesc
  253. * TRUE if success
  254. */
  255. BOOL CDisplayML::SetMainTargetDC (
  256. HDC hdc, //@parm Target DC, NULL for same as rendering device
  257. LONG xWidthMax) //@parm Max line width (not used for screen)
  258. {
  259. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::SetMainTargetDC");
  260. if(SetTargetDC(hdc))
  261. {
  262. // This is here because this is what RE 1.0 did.
  263. SetWordWrap(hdc || !xWidthMax);
  264. // If xWidthMax is greater than zero, then the caller is
  265. // trying to set the maximum width of the window (for measuring,
  266. // line breaking, etc.) However,in order to make our measuring
  267. // algorithms more reasonable, we force the max size to be
  268. // *at least* as wide as the width of a character.
  269. // Note that xWidthMax = 0 means use the view rect width
  270. _xWidthMax = (xWidthMax <= 0) ? 0 : max(DXtoLX(GetXWidthSys()),
  271. xWidthMax);
  272. // Need to do a full recalc. If it fails, it fails, the lines are
  273. // left in a reasonable state. No need to call WaitForRecalc()
  274. // because UpdateView() starts at position zero and we're always
  275. // calc'd up to there
  276. CDisplay::UpdateView();
  277. // Caret/selection has most likely moved
  278. CTxtSelection *psel = _ped->GetSelNC();
  279. if(psel)
  280. psel->UpdateCaret(FALSE);
  281. return TRUE;
  282. }
  283. return FALSE;
  284. }
  285. // Useful for both main and printing devices. jonmat 6/08/1995
  286. BOOL CDisplayML::SetTargetDC( HDC hdc, LONG dxpInch, LONG dypInch)
  287. {
  288. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::SetTargetDC");
  289. CDevDesc *pddTarget = NULL;
  290. // Don't allow metafiles to be set as the target device
  291. if(hdc && GetDeviceCaps(hdc, TECHNOLOGY) == DT_METAFILE)
  292. return FALSE;
  293. if(hdc)
  294. {
  295. // Allocate device first to see if we can. We don't want to change
  296. // our state if this is going to fail.
  297. pddTarget = new CDevDesc(_ped);
  298. if(!pddTarget)
  299. return FALSE; // We couldn't so we are done
  300. }
  301. // Remove any cached information for the old target device
  302. if(_pddTarget)
  303. {
  304. delete _pddTarget;
  305. _pddTarget = NULL;
  306. }
  307. if(hdc)
  308. {
  309. _pddTarget = pddTarget; // Update device because we have one
  310. _pddTarget->SetDC(hdc, dxpInch, dypInch);
  311. }
  312. return TRUE;
  313. }
  314. //================================= Line recalc ==============================
  315. /*
  316. * CDisplayML::RecalcScrollBars()
  317. *
  318. * @mfunc
  319. * Recalculate the scroll bars if the view has changed.
  320. *
  321. *
  322. * @devnote There is a possibility of recursion here, so we
  323. * need to protect ourselves.
  324. *
  325. * To visualize this, consider two types of characters, 'a' characters
  326. * which are small in height and 'A' 's which are really tall, but the same
  327. * width as an 'a'. So if I have
  328. *
  329. * a a A <nl>
  330. * A <nl>
  331. *
  332. * I'll get a calced size that's basically 2 * heightof(A).
  333. * With a scrollbar, this could wordwrap to
  334. *
  335. * a a <nl>
  336. * A A <nl>
  337. *
  338. * which is of calced size heightof(A) + heightof(a); this is
  339. * obviously less than the height in the first case.
  340. */
  341. void CDisplayML::RecalcScrollBars()
  342. {
  343. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::RecalcScrollBars");
  344. if(_fViewChanged)
  345. {
  346. _fViewChanged = FALSE;
  347. UpdateScrollBar(SB_VERT, TRUE);
  348. UpdateScrollBar(SB_HORZ, TRUE);
  349. }
  350. }
  351. /*
  352. * CDisplayML::RecalcLines(fWait)
  353. *
  354. * @mfunc
  355. * Recalc all line breaks.
  356. * This method does a lazy calc after the last visible line
  357. * except for a bottomless control
  358. *
  359. * @rdesc
  360. * TRUE if success
  361. */
  362. BOOL CDisplayML::RecalcLines (
  363. BOOL fWait) //@parm Recalc lines down to _cpWait/_yWait; then be lazy
  364. {
  365. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::RecalcLines");
  366. LONG cliWait = cExtraBeforeLazy; // Extra lines before being lazy
  367. BOOL fDone = TRUE;
  368. BOOL fFirstInPara = TRUE;
  369. CLine * pliNew = NULL;
  370. LONG xWidth;
  371. LONG yHeight = 0;
  372. LONG cchText = _ped->GetTextLength();
  373. BOOL fWaitingForFirstVisible = TRUE;
  374. LONG yHeightView = _yHeightView;
  375. LONG yHeightScrollOld = GetMaxYScroll();
  376. LONG yHeightScrollNew;
  377. Remove(0, -1); // Remove all old lines from *this
  378. _yCalcMax = 0; // Set both maxes to start of text
  379. _cpCalcMax = 0;
  380. // Don't stop at bottom of view if we're bottomless and active
  381. if(!_ped->TxGetAutoSize() && IsActive())
  382. {
  383. // Be lazy - don't bother going past visible portion
  384. _cpWait = -1;
  385. _yWait = -1;
  386. fWait = TRUE;
  387. }
  388. // Init measurer at cp = 0
  389. CMeasurer me(this);
  390. me.SetNumber(0);
  391. // The following loop generates new lines
  392. while(me.GetCp() < cchText)
  393. {
  394. // Add one new line
  395. pliNew = Add(1, NULL);
  396. if (!pliNew)
  397. {
  398. _ped->GetCallMgr()->SetOutOfMemory();
  399. TRACEWARNSZ("Out of memory Recalc'ing lines");
  400. goto err;
  401. }
  402. // Stuff text into new line
  403. UINT uiFlags = MEASURE_BREAKATWORD |
  404. (fFirstInPara ? MEASURE_FIRSTINPARA : 0);
  405. Tracef(TRCSEVINFO, "Measuring new line from cp = %d", me.GetCp());
  406. if(!pliNew->Measure(me, -1, -1, uiFlags))
  407. {
  408. Assert(FALSE);
  409. goto err;
  410. }
  411. fFirstInPara = pliNew->_bFlags & fliHasEOP;
  412. yHeight += pliNew->GetHeight();
  413. _cpCalcMax = me.GetCp();
  414. if(fWait)
  415. {
  416. // Do we want to do a background recalc? - the answer is yes if
  417. // three things are true: (1) We have recalc'd beyond the old first
  418. // visible character, (2) We have recalc'd beyond the visible
  419. // portion of the screen and (3) we have gone beyond the next
  420. // cExtraBeforeLazy lines to make page down go faster.
  421. if(fWaitingForFirstVisible)
  422. {
  423. if(me.GetCp() > _cpFirstVisible)
  424. {
  425. _yWait = yHeight + yHeightView;
  426. fWaitingForFirstVisible = FALSE;
  427. }
  428. }
  429. else if(yHeight > _yWait && cliWait-- <= 0)
  430. {
  431. fDone = FALSE;
  432. break;
  433. }
  434. }
  435. }
  436. _yCalcMax = yHeight;
  437. _fRecalcDone = fDone;
  438. _fNeedRecalc = FALSE;
  439. yHeightScrollNew = CalcScrollHeight(yHeight);
  440. if(fDone && (yHeight != _yHeight || yHeightScrollNew != yHeightScrollOld)
  441. || yHeightScrollNew > yHeightScrollOld)
  442. {
  443. _fViewChanged = TRUE;
  444. }
  445. _yHeight = yHeight;
  446. xWidth = CalcDisplayWidth();
  447. if(fDone && xWidth != _xWidth || xWidth > _xWidth)
  448. {
  449. _xWidth = xWidth;
  450. _fViewChanged = TRUE;
  451. }
  452. Tracef(TRCSEVINFO, "CDisplayML::RecalcLine() - Done. Recalced down to line #%d", Count());
  453. if(!fDone) // if not done, do rest in background
  454. fDone = StartBackgroundRecalc();
  455. if(fDone)
  456. {
  457. _yWait = -1;
  458. _cpWait = -1;
  459. CheckLineArray();
  460. _fLineRecalcErr = FALSE;
  461. }
  462. #ifdef DEBUG
  463. if( 1 )
  464. {
  465. _TEST_INVARIANT_
  466. }
  467. //Array memory allocation tracking
  468. {
  469. void **pv = (void**)((char*)this + sizeof(CDisplay));
  470. PvSet(*pv);
  471. }
  472. #endif
  473. return TRUE;
  474. err:
  475. TRACEERRORSZ("CDisplayML::RecalcLines() failed");
  476. if(!_fLineRecalcErr)
  477. {
  478. _cpCalcMax = me.GetCp();
  479. _yCalcMax = yHeight;
  480. _fLineRecalcErr = TRUE;
  481. _ped->GetCallMgr()->SetOutOfMemory();
  482. _fLineRecalcErr = FALSE; // fix up CArray & bail
  483. }
  484. return FALSE;
  485. }
  486. /*
  487. * CDisplayML::RecalcLines(rtp, cchOld, cchNew, fBackground, fWait, pled)
  488. *
  489. * @mfunc
  490. * Recompute line breaks after text modification
  491. *
  492. * @rdesc
  493. * TRUE if success
  494. *
  495. * @devnote
  496. * Most people call this the trickiest piece of code in RichEdit...
  497. */
  498. BOOL CDisplayML::RecalcLines (
  499. const CRchTxtPtr &rtp, //@parm Where change happened
  500. LONG cchOld, //@parm Count of chars deleted
  501. LONG cchNew, //@parm Count of chars added
  502. BOOL fBackground, //@parm This method called as background process
  503. BOOL fWait, //@parm Recalc lines down to _cpWait/_yWait; then be lazy
  504. CLed *pled) //@parm Returns edit impact on lines (can be NULL)
  505. {
  506. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::RecalcLines");
  507. LONG cchEdit;
  508. LONG cchSkip;
  509. LONG cliBackedUp = 0;
  510. LONG cliWait = cExtraBeforeLazy;
  511. BOOL fDone = TRUE;
  512. BOOL fFirstInPara = TRUE;
  513. LONG ili;
  514. CLed led;
  515. LONG lT; // long Temporary
  516. CLine * pliNew;
  517. CLinePtr rpOld(this);
  518. LONG xWidth;
  519. LONG yHeight;
  520. LONG yHeightPrev = 0;
  521. LONG cchText = _ped->GetTextLength();
  522. UINT uiFlags;
  523. BOOL fReplaceResult;
  524. BOOL fTryForMatch = TRUE;
  525. LONG yHeightScrollOld = GetMaxYScroll();
  526. LONG yHeightScrollNew;
  527. WORD wNumber = 0;
  528. CLineArray rgliNew;
  529. DWORD dwBgndTickMax = fBackground ? GetTickCount() + cmsecBgndBusy : 0;
  530. if(!pled)
  531. pled = &led;
  532. #if defined(DEBUG) || defined(_RELEASE_ASSERTS_)
  533. LONG cp = rtp.GetCp();
  534. if(cp > _cpCalcMax)
  535. Tracef(TRCSEVERR, "rtp %ld, _cpCalcMax %ld", cp, _cpCalcMax);
  536. AssertSz(cp <= _cpCalcMax,
  537. "CDisplayML::RecalcLines Caller didn't setup RecalcLines()");
  538. AssertSz(!(fWait && fBackground),
  539. "CDisplayML::RecalcLines wait and background both true");
  540. AssertSz(!(fWait && (-1 == _cpWait) && (-1 == _yWait)),
  541. "CDisplayML::RecalcLines background recalc parms invalid");
  542. #endif
  543. // We will not use background recalc if this is already a background recalc,
  544. // or if the control is not active or if this is an auto sized control.
  545. if(!IsActive() || _ped->TxGetAutoSize())
  546. fWait = FALSE;
  547. // Init line pointer on old CLineArray and backup to start of line
  548. rpOld.RpSetCp(rtp.GetCp(), FALSE);
  549. cchSkip = rpOld.RpGetIch();
  550. rpOld.RpAdvanceCp(-cchSkip); // Point rp at 1st char in line
  551. ili = rpOld; // Save line # at change for
  552. if(ili && (IsInOutlineView() || // numbering. Back up if not
  553. rtp.GetPF()->IsListNumbered())) // first number in list or if
  554. { // in OutlineView (Outline
  555. ili--; // symbol may change)
  556. }
  557. // Back up at least one line in case we can now fit more on it
  558. // If on a line border, e.g., just inserted an EOP, backup 2; else 1
  559. lT = !cchSkip + 1;
  560. while(lT-- > 0 && rpOld > 0 && (!rpOld[-1]._cchEOP || ili < rpOld))
  561. {
  562. cliBackedUp++;
  563. rpOld--;
  564. cchSkip += rpOld->_cch;
  565. }
  566. // Init measurer at rtp
  567. CMeasurer me(this, rtp);
  568. me.Advance(-cchSkip); // Point at start of text to measure
  569. cchEdit = cchNew + cchSkip; // Number of chars affected by edit
  570. me.SetNumber(rpOld.GetNumber()); // Initialize list number
  571. // Determine whether we're on first line of paragraph
  572. if(rpOld > 0)
  573. {
  574. fFirstInPara = rpOld[-1]._bFlags & fliHasEOP;
  575. }
  576. yHeight = YposFromLine(rpOld);
  577. // Update first-affected and pre-edit-match lines in pled
  578. pled->_iliFirst = rpOld;
  579. pled->_cpFirst = pled->_cpMatchOld = me.GetCp();
  580. pled->_yFirst = pled->_yMatchOld = yHeight;
  581. AssertSz(pled->_yFirst >= 0, "CDisplayML::RecalcLines _yFirst < 0");
  582. Tracef(TRCSEVINFO, "Start recalcing from line #%d, cp=%d", pled->_iliFirst, pled->_cpFirst);
  583. // In case of error, set both maxes to where we are now
  584. _yCalcMax = yHeight;
  585. _cpCalcMax = me.GetCp();
  586. // If we are past the requested area to recalc and background recalc is
  587. // allowed, then just go directly to background recalc. If there is no
  588. // height, we just go a head and calculate some lines anyway. This
  589. // prevents any weird background recalcs from occuring when it is
  590. // unnecessary to go into background recalc.
  591. if(fWait && _yWait > 0 && yHeight > _yWait && me.GetCp() > _cpWait)
  592. {
  593. _yHeight = yHeight;
  594. rpOld.Remove(-1); // Remove all old lines from here on
  595. StartBackgroundRecalc(); // Start up the background recalc
  596. pled->SetMax(this);
  597. return TRUE;
  598. }
  599. pliNew = NULL;
  600. // The following loop generates new lines for each line we backed
  601. // up over and for lines directly affected by edit
  602. while(cchEdit > 0)
  603. {
  604. pliNew = rgliNew.Add(1, NULL); // Add one new line
  605. if (!pliNew)
  606. {
  607. TRACEWARNSZ("CDisplayML::RecalcLines unable to alloc additional CLine in CLineArray");
  608. goto errspace;
  609. }
  610. // Can't reuse old results if we've got a target device
  611. // For SPEED: it'd be nice to cache a few values when we do have a
  612. // target device - a good caching heuristic could halve the measuring
  613. const LONG cchNonWhite = rpOld.IsValid()
  614. ? rpOld->_cch - rpOld->_cchWhite
  615. : 0;
  616. uiFlags = MEASURE_BREAKATWORD | (fFirstInPara ? MEASURE_FIRSTINPARA : 0);
  617. if (cchSkip > 0 && cchSkip >= cchNonWhite && !IsInOutlineView() &&
  618. !_ped->fUseLineServices() && (!_pddTarget || !_pddTarget->IsValid()))
  619. {
  620. me.NewLine(*rpOld); // Don't remeasure anything we
  621. me.Advance(cchNonWhite); // already have valid info on
  622. me._li._cch = cchNonWhite;
  623. me._li._xWidth = rpOld->_xWidth;
  624. // Clear out any of the old flags _except_ for tabs and OLE or
  625. // OffScreen. Note that this algorithm is somewhat bogus; there
  626. // is no guarantee that the line still matches the flag state.
  627. // However,those flags are simply 'hints'--i.e. the line _may_
  628. // be in that state. Keeping those flags set will result
  629. // in a minor slowdown for rendering the line.
  630. me._li._bFlags &= (fliHasTabs | fliHasOle | fliUseOffScreenDC | fliHasSpecialChars);
  631. if(rpOld->_bFlags & fliOffScreenOnce)
  632. me._li._bFlags &= ~fliUseOffScreenDC;
  633. me._li._cchEOP = 0;
  634. uiFlags |= MEASURE_DONTINIT; // CLine part of me already init'd
  635. }
  636. // Stuff text into new line
  637. Tracef(TRCSEVINFO, "Measuring new line from cp = %d", me.GetCp());
  638. if(!pliNew->Measure(me, -1, -1, uiFlags))
  639. {
  640. Assert(FALSE);
  641. goto err;
  642. }
  643. if(!pliNew->_cch)
  644. {
  645. TRACEWARNSZ(
  646. "CDisplayML::RecalcLines measure returned a zero length line");
  647. goto errspace;
  648. }
  649. fFirstInPara = pliNew->_bFlags & fliHasEOP;
  650. yHeightPrev = yHeight;
  651. yHeight += pliNew->GetHeight();
  652. cchEdit -= pliNew->_cch;
  653. AssertSz(cchEdit + me.GetCp() <= cchText,
  654. "CDisplayML::RecalcLines: want to measure beyond EOD");
  655. // Calculate on what line the edit started. We do this because
  656. // we want to render the first edited line off screen so if
  657. // the line is being edited via the keyboard we don't clip
  658. // any characters.
  659. if(cchSkip > 0)
  660. {
  661. // Check whether we backed up and the line we are examining
  662. // changed at all. Even if it didn't change in outline view
  663. // have to redraw in case outline symbol changes
  664. if (cliBackedUp && cchSkip >= pliNew->_cch &&
  665. pliNew->IsEqual(*rpOld) && !IsInOutlineView())
  666. {
  667. // Perfect match, this line was not the first edited.
  668. Tracef(TRCSEVINFO, "New line matched old line #%d", (LONG)rpOld);
  669. cchSkip -= rpOld->_cch;
  670. // Update first affected line and match in pled
  671. pled->_iliFirst++;
  672. pled->_cpFirst += rpOld->_cch;
  673. pled->_cpMatchOld += rpOld->_cch;
  674. pled->_yFirst += rpOld->GetHeight();
  675. AssertSz(pled->_yFirst >= 0, "CDisplayML::RecalcLines _yFirst < 0");
  676. pled->_yMatchOld += rpOld->GetHeight();
  677. cliBackedUp--;
  678. rgliNew.Clear(AF_KEEPMEM); // Discard new line
  679. if(!(rpOld++)) // Next line
  680. cchSkip = 0;
  681. }
  682. else // No match in the line, so
  683. cchSkip = 0; // this line is the first to
  684. } // be edited
  685. if(fBackground && GetTickCount() >= dwBgndTickMax)
  686. {
  687. fDone = FALSE; // took too long, stop for now
  688. goto no_match;
  689. }
  690. if (fWait && yHeight > _yWait && me.GetCp() > _cpWait &&
  691. cliWait-- <= 0)
  692. {
  693. // Not really done, just past region we're waiting for
  694. // so let background recalc take it from here
  695. fDone = FALSE;
  696. goto no_match;
  697. }
  698. } // while(cchEdit > 0) { }
  699. Tracef(TRCSEVINFO, "Done recalcing edited text. Created %d new lines", rgliNew.Count());
  700. // Edit lines have been exhausted. Continue breaking lines,
  701. // but try to match new & old breaks
  702. wNumber = me._wNumber;
  703. while(me.GetCp() < cchText)
  704. {
  705. // Assume there are no matches to try for
  706. BOOL frpOldValid = FALSE;
  707. // If we run out of runs, then no match is possible. Therefore,
  708. // we only try for a match as long as we have runs.
  709. if(fTryForMatch)
  710. {
  711. // We are trying for a match so assume that there
  712. // is a match after all
  713. frpOldValid = TRUE;
  714. // Look for match in old line break CArray
  715. lT = me.GetCp() - cchNew + cchOld;
  716. while (rpOld.IsValid() && pled->_cpMatchOld < lT)
  717. {
  718. pled->_yMatchOld += rpOld->GetHeight();
  719. pled->_cpMatchOld += rpOld->_cch;
  720. if(!rpOld.NextRun())
  721. {
  722. // No more line array entries so we can give up on
  723. // trying to match for good.
  724. fTryForMatch = FALSE;
  725. frpOldValid = FALSE;
  726. break;
  727. }
  728. }
  729. }
  730. // If perfect match, stop.
  731. if (frpOldValid && rpOld.IsValid() && pled->_cpMatchOld == lT &&
  732. rpOld->_cch && me._wNumber == rpOld->_bNumber)
  733. {
  734. Tracef(TRCSEVINFO, "Found match with old line #%d", rpOld.GetLineIndex());
  735. // Update fliFirstInPara flag in 1st old line that matches. Note
  736. // that if the new array doesn't have any lines, we have to look
  737. // into the line array preceding the current change.
  738. rpOld->_bFlags |= fliFirstInPara;
  739. if(rgliNew.Count() > 0)
  740. {
  741. if(!(rgliNew.Elem(rgliNew.Count() - 1)->_bFlags & fliHasEOP))
  742. rpOld->_bFlags &= ~fliFirstInPara;
  743. }
  744. else if( rpOld >= pled->_iliFirst && pled->_iliFirst )
  745. {
  746. if(!(rpOld[pled->_iliFirst - rpOld -1]._bFlags & fliHasEOP))
  747. rpOld->_bFlags &= ~fliFirstInPara;
  748. }
  749. pled->_iliMatchOld = rpOld;
  750. // Replace old lines by new ones
  751. lT = rpOld - pled->_iliFirst;
  752. rpOld = pled->_iliFirst;
  753. if(!rpOld.Replace (lT, &rgliNew))
  754. {
  755. TRACEWARNSZ("CDisplayML::RecalcLines unable to alloc additional CLines in rpOld");
  756. goto errspace;
  757. }
  758. frpOldValid = rpOld.ChgRun(rgliNew.Count());
  759. rgliNew.Clear(AF_KEEPMEM); // Clear aux array
  760. // Remember information about match after editing
  761. Assert((cp = rpOld.CalculateCp()) == me.GetCp());
  762. pled->_yMatchNew = yHeight;
  763. pled->_yMatchNewTop = yHeightPrev;
  764. pled->_iliMatchNew = rpOld;
  765. pled->_cpMatchNew = me.GetCp();
  766. // Compute height and cp after all matches
  767. _cpCalcMax = me.GetCp();
  768. if(frpOldValid && rpOld.IsValid())
  769. {
  770. do
  771. {
  772. yHeight += rpOld->GetHeight();
  773. _cpCalcMax += rpOld->_cch;
  774. }
  775. while( rpOld.NextRun() );
  776. }
  777. // Make sure _cpCalcMax is sane after the above update
  778. AssertSz(_cpCalcMax <= cchText,
  779. "CDisplayML::RecalcLines match extends beyond EOF");
  780. // We stop calculating here.Note that if _cpCalcMax < size
  781. // of text, this means a background recalc is in progress.
  782. // We will let that background recalc get the arrays
  783. // fully in sync.
  784. AssertSz(_cpCalcMax == cchText || _fBgndRecalc,
  785. "CDisplayML::Match less but no background recalc");
  786. if(_cpCalcMax != cchText)
  787. {
  788. // This is going to be finished by the background recalc
  789. // so set the done flag appropriately.
  790. fDone = FALSE;
  791. }
  792. goto match;
  793. }
  794. // Add a new line
  795. pliNew = rgliNew.Add(1, NULL);
  796. if(!pliNew)
  797. {
  798. TRACEWARNSZ("CDisplayML::RecalcLines unable to alloc additional CLine in CLineArray");
  799. goto errspace;
  800. }
  801. Tracef(TRCSEVINFO, "Measuring new line from cp = %d", me.GetCp());
  802. // Stuff some text into new line
  803. wNumber = me._wNumber;
  804. if(!pliNew->Measure(me, -1, -1, MEASURE_BREAKATWORD |
  805. (fFirstInPara ? MEASURE_FIRSTINPARA : 0)))
  806. {
  807. Assert(FALSE);
  808. goto err;
  809. }
  810. fFirstInPara = pliNew->_bFlags & fliHasEOP;
  811. yHeight += pliNew->GetHeight();
  812. if(fBackground && GetTickCount() >= (DWORD)dwBgndTickMax)
  813. {
  814. fDone = FALSE; // Took too long, stop for now
  815. break;
  816. }
  817. if(fWait && yHeight > _yWait && me.GetCp() > _cpWait
  818. && cliWait-- <= 0)
  819. { // Not really done, just past region we're
  820. fDone = FALSE; // waiting for so let background recalc
  821. break; // take it from here
  822. }
  823. } // while(me < cchText) ...
  824. no_match:
  825. // Didn't find match: whole line array from _iliFirst needs to be changed
  826. pled->_iliMatchOld = Count();
  827. pled->_cpMatchOld = cchText;
  828. pled->_yMatchNew = yHeight;
  829. pled->_yMatchNewTop = yHeightPrev;
  830. pled->_yMatchOld = _yHeight;
  831. _cpCalcMax = me.GetCp();
  832. // Replace old lines by new ones
  833. rpOld = pled->_iliFirst;
  834. // We store the result from the replace because although it can fail the
  835. // fields used for first visible must be set to something sensible whether
  836. // the replace fails or not. Further, the setting up of the first visible
  837. // fields must happen after the Replace because the lines could have
  838. // changed in length which in turns means that the first visible position
  839. // has failed.
  840. fReplaceResult = rpOld.Replace(-1, &rgliNew);
  841. // _iliMatchNew & _cpMatchNew are used for first visible constants so we
  842. // need to set them to something reasonable. In particular the rendering
  843. // logic expects _cpMatchNew to be set to the first character of the first
  844. // visible line. rpOld is used because it is convenient.
  845. // Note we can't use RpBindToCp at this point because the first visible
  846. // information is messed up because we may have changed the line that
  847. // the first visible cp is on.
  848. rpOld.BindToCp(me.GetCp());
  849. pled->_iliMatchNew = rpOld.GetLineIndex();
  850. pled->_cpMatchNew = me.GetCp() - rpOld.RpGetIch();
  851. if (!fReplaceResult)
  852. {
  853. TRACEERRORSZ("CDisplayML::RecalcLines rpOld.Replace() failed");
  854. goto errspace;
  855. }
  856. // Adjust first affected line if this line is gone
  857. // after replacing by new lines
  858. if(pled->_iliFirst >= Count() && Count() > 0)
  859. {
  860. Assert(pled->_iliFirst == Count());
  861. pled->_iliFirst = Count() - 1;
  862. pliNew = Elem(pled->_iliFirst);
  863. pled->_yFirst -= pliNew->GetHeight();
  864. AssertSz(pled->_yFirst >= 0, "CDisplayML::RecalcLines _yFirst < 0");
  865. pled->_cpFirst -= pliNew->_cch;
  866. }
  867. match:
  868. _fRecalcDone = fDone;
  869. _fNeedRecalc = FALSE;
  870. _yCalcMax = yHeight;
  871. Tracef(TRCSEVINFO, "CDisplayML::RecalcLine(rtp, ...) - Done. Recalced down to line #%d", Count() - 1);
  872. // Clear wait fields since we want caller's to set them up.
  873. _yWait = -1;
  874. _cpWait = -1;
  875. if(fDone && fBackground)
  876. {
  877. TRACEINFOSZ("Background line recalc done");
  878. _ped->TxKillTimer(RETID_BGND_RECALC);
  879. _fBgndRecalc = FALSE;
  880. _fRecalcDone = TRUE;
  881. }
  882. // Determine display height and update scrollbar
  883. yHeightScrollNew = CalcScrollHeight(yHeight);
  884. if (_fViewChanged ||
  885. fDone && (yHeight != _yHeight || yHeightScrollNew != yHeightScrollOld)
  886. || yHeightScrollNew > yHeightScrollOld)
  887. {
  888. //!NOTE:
  889. // UpdateScrollBar can cause a resize of the window by hiding or showing
  890. // scrollbars. As a consequence of resizing the lines may get recalculated
  891. // therefore updating _yHeight to a new value, something != to yHeight.
  892. _yHeight = yHeight;
  893. UpdateScrollBar(SB_VERT, TRUE);
  894. }
  895. else
  896. _yHeight = yHeight; // Guarantee heights agree
  897. // Determine display width and update scrollbar
  898. xWidth = CalcDisplayWidth();
  899. if(_fViewChanged || (fDone && xWidth != _xWidth) || xWidth > _xWidth)
  900. {
  901. _xWidth = xWidth;
  902. UpdateScrollBar(SB_HORZ, TRUE);
  903. }
  904. _fViewChanged = FALSE;
  905. // If not done, do the rest in background
  906. if(!fDone && !fBackground)
  907. fDone = StartBackgroundRecalc();
  908. if(fDone)
  909. {
  910. CheckLineArray();
  911. _fLineRecalcErr = FALSE;
  912. }
  913. #ifdef DEBUG
  914. if( 1 )
  915. {
  916. _TEST_INVARIANT_
  917. }
  918. #endif // DEBUG
  919. return TRUE;
  920. errspace:
  921. _ped->GetCallMgr()->SetOutOfMemory();
  922. _fNeedRecalc = TRUE;
  923. _cpCalcMax = _yCalcMax = 0;
  924. _fLineRecalcErr = TRUE;
  925. err:
  926. if(!_fLineRecalcErr)
  927. {
  928. _cpCalcMax = me.GetCp();
  929. _yCalcMax = yHeight;
  930. }
  931. TRACEERRORSZ("CDisplayML::RecalcLines() failed");
  932. if(!_fLineRecalcErr)
  933. {
  934. _fLineRecalcErr = TRUE;
  935. _ped->GetCallMgr()->SetOutOfMemory();
  936. _fLineRecalcErr = FALSE; // fix up CArray & bail
  937. }
  938. pled->SetMax(this);
  939. return FALSE;
  940. }
  941. /*
  942. * CDisplayML::CalcDisplayWidth()
  943. *
  944. * @mfunc
  945. * Calculates width of this display by walking line CArray and
  946. * returning widest line. Used for horizontal scrollbar routines.
  947. *
  948. * @rdesc
  949. * Widest line width in display
  950. */
  951. LONG CDisplayML::CalcDisplayWidth ()
  952. {
  953. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::CalcDisplayWidth");
  954. LONG ili = Count();
  955. CLine * pli;
  956. LONG xWidth = 0, lineWidth;
  957. if(ili)
  958. {
  959. // Note: pli++ breaks array encapsulation (pli = Elem(ili) doesn't,
  960. // but is a bit slower)
  961. pli = Elem(0);
  962. for(xWidth = 0; ili--; pli++)
  963. {
  964. lineWidth = pli->_xLeft + pli->_xWidth + pli->_xLineOverhang;
  965. xWidth = max(xWidth, lineWidth);
  966. }
  967. }
  968. return xWidth;
  969. }
  970. /*
  971. * CDisplayML::StartBackgroundRecalc()
  972. *
  973. * @mfunc
  974. * Starts background line recalc (at _cpCalcMax position)
  975. *
  976. * @rdesc
  977. * TRUE if done with background recalc
  978. */
  979. BOOL CDisplayML::StartBackgroundRecalc()
  980. {
  981. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::StartBackgroundRecalc");
  982. if(_fBgndRecalc)
  983. return FALSE; // Already in background recalc
  984. AssertSz(_cpCalcMax <= _ped->GetTextLength(),
  985. "CDisplayML::StartBackgroundRecalc _cpCalcMax > Text Length");
  986. if(_cpCalcMax == _ped->GetTextLength())
  987. return TRUE; // Enough chars are recalc'd
  988. if(!_ped->TxSetTimer(RETID_BGND_RECALC, cmsecBgndInterval))
  989. {
  990. // Could not instantiate a timer so wait for recalculation
  991. WaitForRecalc(_ped->GetTextLength(), -1);
  992. return TRUE;
  993. }
  994. _fRecalcDone = FALSE;
  995. _fBgndRecalc = TRUE;
  996. return FALSE;
  997. }
  998. /*
  999. * CDisplayML::StepBackgroundRecalc()
  1000. *
  1001. * @mfunc
  1002. * Steps background line recalc (at _cpCalcMax position)
  1003. * Called by timer proc and also when going inactive.
  1004. */
  1005. void CDisplayML::StepBackgroundRecalc()
  1006. {
  1007. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::StepBackgroundRecalc");
  1008. _TEST_INVARIANT_
  1009. if(!_fBgndRecalc) // Not in background recalc,
  1010. return; // so don't do anything
  1011. LONG cch = _ped->GetTextLength() - _cpCalcMax;
  1012. // Don't try recalc when processing OOM or had an error doing recalc or
  1013. // if we are asserting.
  1014. #ifdef DEBUG
  1015. if(_fInBkgndRecalc || _fLineRecalcErr)
  1016. {
  1017. if(_fInBkgndRecalc)
  1018. TRACEINFOSZ("avoiding reentrant background recalc");
  1019. else
  1020. TRACEINFOSZ("OOM: not stepping recalc");
  1021. return;
  1022. }
  1023. #else
  1024. if(_fInBkgndRecalc || _fLineRecalcErr)
  1025. return;
  1026. #endif
  1027. _fInBkgndRecalc = TRUE;
  1028. if(!IsActive())
  1029. {
  1030. // Background recalc is over if we are no longer active because
  1031. // we can no longer get the information we need for recalculating.
  1032. // But, if we are half recalc'd we need to set ourselves up to
  1033. // recalc again when we go active.
  1034. InvalidateRecalc();
  1035. cch = 0;
  1036. }
  1037. // Background recalc is over if no more chars or no longer active
  1038. if(cch <= 0)
  1039. {
  1040. TRACEINFOSZ("Background line recalc done");
  1041. _ped->TxKillTimer(RETID_BGND_RECALC);
  1042. _fBgndRecalc = FALSE;
  1043. _fRecalcDone = TRUE;
  1044. _fInBkgndRecalc = FALSE;
  1045. CheckLineArray();
  1046. return;
  1047. }
  1048. CRchTxtPtr tp(_ped, _cpCalcMax);
  1049. RecalcLines(tp, cch, cch, TRUE, FALSE, NULL);
  1050. _fInBkgndRecalc = FALSE;
  1051. }
  1052. /*
  1053. * CDisplayML::WaitForRecalc(cpMax, yMax)
  1054. *
  1055. * @mfunc
  1056. * Ensures that lines are recalced until a specific character
  1057. * position or ypos.
  1058. *
  1059. * @rdesc
  1060. * success
  1061. */
  1062. BOOL CDisplayML::WaitForRecalc(
  1063. LONG cpMax, //@parm Position recalc up to (-1 to ignore)
  1064. LONG yMax) //@parm ypos to recalc up to (-1 to ignore)
  1065. {
  1066. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::WaitForRecalc");
  1067. _TEST_INVARIANT_
  1068. if(IsFrozen() || !_ped->fInplaceActive())
  1069. return TRUE;
  1070. BOOL fReturn = TRUE;
  1071. LONG cch;
  1072. if((yMax < 0 || yMax >= _yCalcMax) &&
  1073. (cpMax < 0 || cpMax >= _cpCalcMax))
  1074. {
  1075. cch = _ped->GetTextLength() - _cpCalcMax;
  1076. if(cch > 0 || Count() == 0)
  1077. {
  1078. HCURSOR hcur = NULL;
  1079. BOOL fSetCursor = (cch > NUMCHARFORWAITCURSOR);
  1080. _cpWait = cpMax;
  1081. _yWait = yMax;
  1082. if(fSetCursor)
  1083. hcur = SetCursor(LoadCursor(0, IDC_WAIT));
  1084. TRACEINFOSZ("Lazy recalc");
  1085. if(!_cpCalcMax || _fNeedRecalc)
  1086. {
  1087. fReturn = RecalcLines(TRUE);
  1088. RebindFirstVisible();
  1089. if(!fReturn)
  1090. InitVars();
  1091. }
  1092. else
  1093. {
  1094. CRchTxtPtr rtp(_ped, _cpCalcMax);
  1095. fReturn = RecalcLines(rtp, cch, cch, FALSE, TRUE, NULL);
  1096. }
  1097. if(fSetCursor)
  1098. SetCursor(hcur);
  1099. }
  1100. else if(!cch)
  1101. {
  1102. // If there was nothing else to calc, make sure that we think
  1103. // recalc is done.
  1104. #ifdef DEBUG
  1105. if( !_fRecalcDone )
  1106. {
  1107. TRACEWARNSZ("For some reason we didn't think background "
  1108. "recalc was done, but it was!!");
  1109. }
  1110. #endif // DEBUG
  1111. _fRecalcDone = TRUE;
  1112. }
  1113. }
  1114. // If view rect changed, make sure to update scrollbars
  1115. RecalcScrollBars();
  1116. return fReturn;
  1117. }
  1118. /*
  1119. * CDisplayML::WaitForRecalcIli(ili)
  1120. *
  1121. * @mfunc
  1122. * Wait until line array is recalculated up to line <p ili>
  1123. *
  1124. * @rdesc
  1125. * Returns TRUE if lines were recalc'd up to ili
  1126. */
  1127. BOOL CDisplayML::WaitForRecalcIli (
  1128. LONG ili) //@parm Line index to recalculate line array up to
  1129. {
  1130. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::WaitForRecalcIli");
  1131. LONG cchGuess;
  1132. while(!_fRecalcDone && ili >= Count())
  1133. {
  1134. // just go ahead and recalc everything.
  1135. cchGuess = _ped->GetTextLength();
  1136. if(IsFrozen() || !WaitForRecalc(cchGuess, -1))
  1137. return FALSE;
  1138. }
  1139. return ili < Count();
  1140. }
  1141. /*
  1142. * CDisplayML::WaitForRecalcView()
  1143. *
  1144. * @mfunc
  1145. * Ensure visible lines are completly recalced
  1146. *
  1147. * @rdesc TRUE iff successful
  1148. */
  1149. BOOL CDisplayML::WaitForRecalcView()
  1150. {
  1151. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::WaitForRecalcView");
  1152. return WaitForRecalc(-1, _yScroll + _yHeightView);
  1153. }
  1154. /*
  1155. * CDisplayML::InitLinePtr ( CLinePtr & plp )
  1156. *
  1157. * @mfunc
  1158. * Initialize a CLinePtr properly
  1159. */
  1160. void CDisplayML::InitLinePtr (
  1161. CLinePtr & plp ) //@parm Ptr to line to initialize
  1162. {
  1163. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::InitLinePtr");
  1164. plp.Init( *this );
  1165. }
  1166. /*
  1167. * CDisplayML::GetLineText(ili, pchBuff, cchMost)
  1168. *
  1169. * @mfunc
  1170. * Copy given line of this display into a character buffer
  1171. *
  1172. * @rdesc
  1173. * number of character copied
  1174. */
  1175. LONG CDisplayML::GetLineText(
  1176. LONG ili, //@parm Line to get text of
  1177. TCHAR *pchBuff, //@parm Buffer to stuff text into
  1178. LONG cchMost) //@parm Length of buffer
  1179. {
  1180. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::GetLineText");
  1181. _TEST_INVARIANT_
  1182. CTxtPtr tp (_ped, 0);
  1183. // FUTURE (alexgo, ricksa): This is called from EM_GETLINE whose parameter
  1184. // is a WPARAM which is unsigned we need to fix the type of ili.
  1185. if(ili >= 0 && (ili < Count() || WaitForRecalcIli(ili)))
  1186. {
  1187. cchMost = min(cchMost, Elem(ili)->_cch);
  1188. if(cchMost > 0)
  1189. {
  1190. tp.SetCp(CpFromLine(ili, NULL));
  1191. return tp.GetText(cchMost, pchBuff);
  1192. }
  1193. }
  1194. *pchBuff = TEXT('\0');
  1195. return 0;
  1196. }
  1197. /*
  1198. * CDisplayML::LineCount
  1199. *
  1200. * @mfunc returns the number of lines in this control. Note that for plain
  1201. * text mode, we will add on an extra line of the last character is
  1202. * a CR. This is for compatibility with MLE
  1203. *
  1204. * @rdesc LONG
  1205. */
  1206. LONG CDisplayML::LineCount() const
  1207. {
  1208. LONG cLine = Count();
  1209. if (!_ped->IsRich() && (!cLine || // If plain text with no lines
  1210. Elem(cLine - 1)->_cchEOP)) // or last line ending with a CR,
  1211. { // then inc line count
  1212. cLine++;
  1213. }
  1214. return cLine;
  1215. }
  1216. // ================================ Line info retrieval ====================================
  1217. /*
  1218. * CDisplayML::YposFromLine(ili)
  1219. *
  1220. * @mfunc
  1221. * Computes top of line position
  1222. *
  1223. * @rdesc
  1224. * top position of given line (relative to the first line)
  1225. */
  1226. LONG CDisplayML::YposFromLine(
  1227. LONG ili) //@parm Line we're interested in
  1228. {
  1229. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::YposFromLine");
  1230. _TEST_INVARIANT_
  1231. if(!WaitForRecalcIli(ili)) // out of range, use last valid line
  1232. {
  1233. ili = Count() - 1;
  1234. ili = (ili > 0) ? ili : 0;
  1235. }
  1236. LONG cli = ili - _iliFirstVisible;
  1237. CLine * pli = Elem(_iliFirstVisible);
  1238. LONG yPos = _yScroll + _dyFirstVisible;
  1239. while(cli > 0)
  1240. {
  1241. yPos += pli->GetHeight();
  1242. cli--;
  1243. pli++;
  1244. }
  1245. while(cli < 0)
  1246. {
  1247. pli--;
  1248. yPos -= pli->GetHeight();
  1249. cli++;
  1250. }
  1251. AssertSz(yPos >= 0, "CDisplayML::YposFromLine height less than 0");
  1252. return yPos;
  1253. }
  1254. /*
  1255. * CDisplayML::CpFromLine(ili, pyHeight)
  1256. *
  1257. * @mfunc
  1258. * Computes cp at start of given line
  1259. * (and top of line position relative to this display)
  1260. *
  1261. * @rdesc
  1262. * cp of given line
  1263. */
  1264. LONG CDisplayML::CpFromLine (
  1265. LONG ili, //@parm Line we're interested in (if <lt> 0 means caret line)
  1266. LONG *pyHeight) //@parm Returns top of line relative to display
  1267. // (NULL if don't want that info)
  1268. {
  1269. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::CpFromLine");
  1270. _TEST_INVARIANT_
  1271. LONG cli;
  1272. LONG y = _yScroll + _dyFirstVisible;
  1273. LONG cp = _cpFirstVisible;
  1274. CLine *pli;
  1275. LONG iStart = _iliFirstVisible;
  1276. cli = ili - _iliFirstVisible;
  1277. if(cli < 0 && -cli >= ili)
  1278. {
  1279. // Closer to first line than to first visible line,
  1280. // so start at the first line
  1281. cli = ili;
  1282. y = 0;
  1283. cp = 0;
  1284. iStart = 0;
  1285. }
  1286. else if( cli <= 0 )
  1287. {
  1288. CheckView();
  1289. for(ili = _iliFirstVisible-1; cli < 0; cli++, ili--)
  1290. {
  1291. pli = Elem(ili);
  1292. y -= pli->GetHeight();
  1293. cp -= pli->_cch;
  1294. }
  1295. goto end;
  1296. }
  1297. for(ili = iStart; cli > 0; cli--, ili++)
  1298. {
  1299. pli = Elem(ili);
  1300. if(!IsMain() || !WaitForRecalcIli(ili))
  1301. break;
  1302. y += pli->GetHeight();
  1303. cp += pli->_cch;
  1304. }
  1305. end:
  1306. if(pyHeight)
  1307. *pyHeight = y;
  1308. return cp;
  1309. }
  1310. /*
  1311. * CDisplayML::LineFromYPos(yPos, pyLine, pcpFirst)
  1312. *
  1313. * @mfunc
  1314. * Computes line at given y position. Returns top of line ypos
  1315. * cp at start of line cp, and line index.
  1316. *
  1317. * @rdesc
  1318. * index of line found
  1319. */
  1320. LONG CDisplayML::LineFromYpos (
  1321. LONG yPos, //@parm Ypos to look for (relative to first line)
  1322. LONG *pyLine, //@parm Returns ypos at top of line /r first line (can be NULL)
  1323. LONG *pcpFirst) //@parm Returns cp at start of line (can be NULL)
  1324. {
  1325. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::LineFromYpos");
  1326. _TEST_INVARIANT_
  1327. LONG cpLi;
  1328. LONG dy;
  1329. LONG ili = 0;
  1330. LONG yLi;
  1331. CLine *pli;
  1332. if(!WaitForRecalc(-1, _yScroll))
  1333. {
  1334. yLi = 0;
  1335. cpLi = 0;
  1336. goto done;
  1337. }
  1338. cpLi = _cpFirstVisible;
  1339. ili = _iliFirstVisible;
  1340. yLi = _yScroll + _dyFirstVisible;
  1341. dy = yPos - yLi;
  1342. if(dy < 0 && -dy <= _yScroll)
  1343. {
  1344. // Closer to first visible line than to first line:
  1345. // go backwards from first visible line.
  1346. while(yPos < yLi && ili > 0)
  1347. {
  1348. pli = Elem(--ili);
  1349. yLi -= pli->GetHeight();
  1350. cpLi -= pli->_cch;
  1351. }
  1352. }
  1353. else
  1354. {
  1355. if(dy < 0)
  1356. {
  1357. // Closer to first line than to first visible line:
  1358. // so start at first line.
  1359. cpLi = _cpMin;
  1360. yLi = 0;
  1361. ili = 0;
  1362. }
  1363. pli = Elem(ili);
  1364. while(yPos > yLi && ili < Count()-1)
  1365. {
  1366. yLi += pli->GetHeight();
  1367. cpLi += pli->_cch;
  1368. ili++;
  1369. pli++;
  1370. }
  1371. if(yPos < yLi && ili > 0)
  1372. {
  1373. ili--;
  1374. pli--;
  1375. yLi -= pli->GetHeight();
  1376. cpLi -= pli->_cch;
  1377. }
  1378. }
  1379. done:
  1380. if(pyLine)
  1381. *pyLine = yLi;
  1382. if(pcpFirst)
  1383. *pcpFirst = cpLi;
  1384. return ili;
  1385. }
  1386. /*
  1387. * CDisplayML::LineFromCp(cp, fAtEnd)
  1388. *
  1389. * @mfunc
  1390. * Computes line containing given cp.
  1391. *
  1392. * @rdesc
  1393. * index of line found, -1 if no line at that cp.
  1394. */
  1395. LONG CDisplayML::LineFromCp(
  1396. LONG cp, //@parm cp to look for
  1397. BOOL fAtEnd) //@parm If true, return previous line for ambiguous cp
  1398. {
  1399. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::LineFromCp");
  1400. _TEST_INVARIANT_
  1401. CLinePtr rp(this);
  1402. if(!WaitForRecalc(cp, -1) || !rp.RpSetCp(cp, fAtEnd))
  1403. return -1;
  1404. return (LONG)rp;
  1405. }
  1406. //============================== Point <-> cp conversion ==============================
  1407. /*
  1408. * CDisplayML::CpFromPoint(pt, prcClient, ptp, prp, fAllowEOL, pHit, pdx)
  1409. *
  1410. * @mfunc
  1411. * Determine cp at given point
  1412. *
  1413. * @devnote
  1414. * --- Use when in-place active only ---
  1415. *
  1416. * @rdesc
  1417. * Computed cp, -1 if failed
  1418. */
  1419. LONG CDisplayML::CpFromPoint(
  1420. POINT pt, //@parm Point to compute cp at (client coords)
  1421. const RECT *prcClient, //@parm Client rectangle (can be NULL if active).
  1422. CRchTxtPtr * const ptp, //@parm Returns text pointer at cp (may be NULL)
  1423. CLinePtr * const prp, //@parm Returns line pointer at cp (may be NULL)
  1424. BOOL fAllowEOL, //@parm Click at EOL returns cp after CRLF
  1425. HITTEST * phit, //@parm Out parm for hit-test value
  1426. CDispDim * pdispdim, //@parm Out parm for display dimensions
  1427. LONG *pcpActual)
  1428. {
  1429. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::CpFromPoint");
  1430. _TEST_INVARIANT_
  1431. LONG cp;
  1432. LONG cch = 0;
  1433. RECT rcView;
  1434. HITTEST hit = HT_Nothing;
  1435. GetViewRect(rcView, prcClient);
  1436. // Get line under hit
  1437. int y = pt.y;
  1438. pt.y += _yScroll - rcView.top;
  1439. LONG yLine;
  1440. LONG ili = LineFromYpos(pt.y, &yLine, &cp);
  1441. if(ili < 0)
  1442. return -1;
  1443. if(y >= rcView.top && y <= rcView.bottom)
  1444. {
  1445. pt.y -= yLine;
  1446. CLine *pli = Elem(ili);
  1447. AssertSz(pli || !ili, "CDisplayML::CpFromPoint invalid line pointer");
  1448. if(pli)
  1449. {
  1450. CMeasurer me(this); // Create measurer
  1451. me.SetCp(cp);
  1452. // Transform to galley coordinates
  1453. // Adjust coordinate relative to where the view starts from.
  1454. pt.x -= rcView.left;
  1455. // Is x coordinate within the view?
  1456. if (pt.x >= 0)
  1457. {
  1458. // Adjust by the scroll value
  1459. pt.x += _xScroll;
  1460. }
  1461. // Get character in line
  1462. cch = pli->CchFromXpos(me, pt, pdispdim, &hit, pcpActual);
  1463. // Don't allow click at EOL to select EOL marker and take into
  1464. // account single line edits as well
  1465. if(!fAllowEOL && cch == pli->_cch && pli->_cchEOP)
  1466. {
  1467. // Adjust position on line by amount backed up. OK for
  1468. // me._rpCF and me._rpPF to get out of sync with me._rpTX,
  1469. // since they're not needed for me.GetCp().
  1470. cch += me._rpTX.BackupCpCRLF();
  1471. }
  1472. cp = me.GetCp();
  1473. }
  1474. }
  1475. if(ptp)
  1476. ptp->SetCp(cp);
  1477. if(prp)
  1478. prp->RpSet(ili, cch);
  1479. if (phit)
  1480. *phit = hit;
  1481. return cp;
  1482. }
  1483. /*
  1484. * CDisplayML::PointFromTp(rtp, prcClient, fAtEnd, pt, prp, taMode)
  1485. *
  1486. * @mfunc
  1487. * Determine coordinates at given tp
  1488. *
  1489. * @devnote
  1490. * --- Use when in-place active only ---
  1491. *
  1492. * @rdesc
  1493. * line index at cp, -1 if error
  1494. */
  1495. LONG CDisplayML::PointFromTp(
  1496. const CRchTxtPtr &rtp, //@parm Text ptr to get coordinates at
  1497. const RECT *prcClient, //@parm Client rectangle (can be NULL if active).
  1498. BOOL fAtEnd, //@parm Return end of prev line for ambiguous cp
  1499. POINT & pt, //@parm Returns point at cp in client coords
  1500. CLinePtr * const prp, //@parm Returns line pointer at tp (may be null)
  1501. UINT taMode, //@parm Text Align mode: top, baseline, bottom
  1502. CDispDim * pdispdim) //@parm Out parm for display dimensions
  1503. {
  1504. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::PointFromTp");
  1505. _TEST_INVARIANT_
  1506. LONG dy = 0;
  1507. RECT rcView;
  1508. CLinePtr rp(this);
  1509. if(!WaitForRecalc(rtp.GetCp(), -1) || !rp.RpSetCp(rtp.GetCp(), fAtEnd))
  1510. return -1;
  1511. AssertSz(_ped->_fInPlaceActive || prcClient,
  1512. "CDisplayML::PointFromTp() called with invalid client rect");
  1513. GetViewRect(rcView, prcClient);
  1514. pt.x = rcView.left - _xScroll;
  1515. pt.y = YposFromLine(rp) - _yScroll + rcView.top;
  1516. CMeasurer me(this, rtp);
  1517. me.Advance(-rp.RpGetIch()); // Backup to start of line
  1518. me.NewLine(*rp); // Measure from there to where we are
  1519. LONG xCalc = rp->XposFromCch(me, rp.RpGetIch(), taMode, pdispdim, &dy);
  1520. if(pt.x + xCalc <= rcView.right || !GetWordWrap() || GetTargetDev())
  1521. {
  1522. // Width is in view or there is no wordwrap so just
  1523. // add the length to the point.
  1524. pt.x += xCalc;
  1525. }
  1526. else
  1527. {
  1528. // Remember we ignore trailing spaces at the end of the line in
  1529. // the width, therefore the x value that MeasureText finds can
  1530. // be greater than the width in the line so we truncate to the
  1531. // previously calculated width which will ignore the spaces.
  1532. pt.x += rp->_xLeft + rp->_xWidth; // We *don't* worry about xLineOverhang here
  1533. }
  1534. pt.y += dy;
  1535. if(prp)
  1536. *prp = rp;
  1537. return rp;
  1538. }
  1539. //==================================== Rendering =======================================
  1540. /*
  1541. * CDisplayML::Render(rcView, rcRender)
  1542. *
  1543. * @mfunc
  1544. * Searches paragraph boundaries around a range
  1545. */
  1546. void CDisplayML::Render (
  1547. const RECT &rcView, //@parm View RECT
  1548. const RECT &rcRender) //@parm RECT to render (must be container in
  1549. // client rect)
  1550. {
  1551. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::Render");
  1552. _TEST_INVARIANT_
  1553. LONG cp;
  1554. BOOL fLinesToRender = TRUE;
  1555. LONG lCount = Count();
  1556. CTxtSelection *psel = _ped->GetSelNC();
  1557. POINT pt;
  1558. LONG yBottomOfRender;
  1559. LONG yHeightForBitmap = 0;
  1560. LONG yHeightForLine;
  1561. LONG yLine;
  1562. LONG yRenderHeight = rcRender.top + _yScroll - rcView.top;
  1563. if(psel)
  1564. psel->ClearCchPending();
  1565. // Review (murrays) this routine is called twice from disp.cpp (once for
  1566. // rendering and once for transparent hit testing) and once from edit.cpp
  1567. // (for printing). Only the display rendering needs to notify the
  1568. // update and it does it there. So the following is either undesired or
  1569. // redundant, i.e., should be deleted.
  1570. // Fire event "updating"
  1571. //if(IsMain())
  1572. // _ped->TxNotify(EN_UPDATE, NULL);
  1573. // Calculate line and cp to start display at
  1574. LONG ili = LineFromYpos(rcRender.top + _yScroll - rcView.top, &yLine, &cp);
  1575. CLine *pli = Elem(ili);
  1576. LONG yLi = pli ? pli->GetHeight() : 0; // Null == some forms^3 empty control
  1577. yLi = max(yLi, 0);
  1578. if(yRenderHeight > yLine + yLi)
  1579. fLinesToRender = FALSE;
  1580. // Calculate point where text will start being displayed
  1581. pt.x = rcView.left - _xScroll;
  1582. pt.y = rcView.top - _yScroll + yLine;
  1583. yBottomOfRender = BottomOfRender(rcView, rcRender);
  1584. // We only need check for whether we want to offscreen render if the
  1585. // control is not transparent. Remember if the control is transparent,
  1586. // the rendering of mixed character formats will work because characters
  1587. // in adjoining runs are only truncated if ExtTextOut is trying to clear
  1588. // the display area at the same time.
  1589. if (!IsMetafile() && IsMain() && !IsTransparent())
  1590. {
  1591. // Initialize height counter to first position to display
  1592. yLi = pt.y;
  1593. // Loop through visible lines until we have examined entire
  1594. // line array or we have exceeded visible height
  1595. CLine *pli = Elem(ili);
  1596. for (LONG iliLoop = ili;
  1597. iliLoop < lCount && yLi < yBottomOfRender; iliLoop++, pli++)
  1598. {
  1599. if(pli->_fCollapsed)
  1600. continue;
  1601. yHeightForLine = pli->_yHeight; // Get local copy of line height
  1602. if(pli->_bFlags & fliUseOffScreenDC)
  1603. yHeightForBitmap = max(yHeightForLine, yHeightForBitmap);
  1604. yLi += yHeightForLine;
  1605. }
  1606. }
  1607. // Create renderer
  1608. CRenderer re(this);
  1609. // Prepare renderer
  1610. if(!re.StartRender(rcView, rcRender, yHeightForBitmap))
  1611. return;
  1612. // Init renderer at start of first line to render
  1613. re.SetCurPoint(pt);
  1614. cp = re.SetCp(cp);
  1615. yLi = pt.y;
  1616. if(fLinesToRender)
  1617. {
  1618. // Render each line in update rectangle
  1619. for (; ili < lCount; ili++)
  1620. {
  1621. if (!Elem(ili)->Render(re) ||
  1622. re.GetCurPoint().y >= yBottomOfRender)
  1623. {
  1624. break;
  1625. }
  1626. #ifdef DEBUG
  1627. cp += Elem(ili)->_cch;
  1628. yLi += Elem(ili)->GetHeight();
  1629. // Rich controls with password characters stop at EOPs,
  1630. // so re.GetCp() may be less than cp.
  1631. AssertSz(_ped->IsRich() && _ped->fUsePassword() || re.GetCp() == cp,
  1632. "CDisplayML::RenderView() - cp out of sync with line table");
  1633. pt = re.GetCurPoint();
  1634. AssertSz(pt.y == yLi,
  1635. "CDisplayML::RenderView() - y out of sync with line table");
  1636. #endif
  1637. }
  1638. }
  1639. re.EndRender(); // Finish rendering
  1640. }
  1641. //=================================== View Updating ===================================
  1642. /*
  1643. * CDisplayML::RecalcView(fUpdateScrollBars)
  1644. *
  1645. * @mfunc
  1646. * Recalc all lines breaks and update first visible line
  1647. *
  1648. * @rdesc
  1649. * TRUE if success
  1650. */
  1651. BOOL CDisplayML::RecalcView(
  1652. BOOL fUpdateScrollBars, RECT* prc)
  1653. {
  1654. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::RecalcView");
  1655. BOOL fRet = TRUE;
  1656. LONG yHeightOld = _yHeight;
  1657. LONG yScrollHeightOld = GetMaxYScroll();
  1658. LONG xWidthOld = _xWidth;
  1659. // Full recalc lines
  1660. if(!RecalcLines())
  1661. {
  1662. // We're in deep trouble now, the recalc failed. Let's try to get out
  1663. // of this with our head still mostly attached
  1664. InitVars();
  1665. fRet = FALSE;
  1666. goto Done;
  1667. }
  1668. if(!_ped->GetTextLength()) // This is an empty control so
  1669. CreateEmptyLine(); // create one empty line
  1670. // Force _xScroll = 0 if x scroll range is smaller than the view width
  1671. if(_xWidth <= _xWidthView)
  1672. _xScroll = 0;
  1673. RebindFirstVisible();
  1674. CheckView();
  1675. // We only need to resize if the size needed to display the object has
  1676. // changed.
  1677. if (yHeightOld != _yHeight || yScrollHeightOld != GetMaxYScroll() ||
  1678. xWidthOld != _xWidth)
  1679. {
  1680. if(FAILED(RequestResize()))
  1681. _ped->GetCallMgr()->SetOutOfMemory();
  1682. else if (prc && _ped->_fInOurHost)/*bug fix# 5830, forms3 relies on old behavior*/
  1683. _ped->TxGetClientRect(prc);
  1684. }
  1685. Done:
  1686. // Now update scrollbars
  1687. if(fUpdateScrollBars)
  1688. RecalcScrollBars();
  1689. return fRet;
  1690. }
  1691. /*
  1692. * CDisplayML::UpdateView(&rtp, cchOld, cchNew)
  1693. *
  1694. * @mfunc
  1695. * Recalc lines and update the visible part of the display
  1696. * (the "view") on the screen.
  1697. *
  1698. * @devnote
  1699. * --- Use when in-place active only ---
  1700. *
  1701. * @rdesc
  1702. * TRUE if success
  1703. */
  1704. BOOL CDisplayML::UpdateView(
  1705. const CRchTxtPtr &rtp, //@parm Text ptr where change happened
  1706. LONG cchOld, //@parm Count of chars deleted
  1707. LONG cchNew) //@parm Count of chars inserted
  1708. {
  1709. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::UpdateView");
  1710. BOOL fReturn = TRUE;
  1711. BOOL fRecalcVisible = TRUE;
  1712. RECT rcClient;
  1713. RECT rcView;
  1714. CLed led;
  1715. CTxtSelection *psel = _ped->GetSelNC();
  1716. LONG cpStartOfUpdate = rtp.GetCp();
  1717. BOOL fNeedViewChange = FALSE;
  1718. LONG yHeightOld = _yHeight;
  1719. LONG yScrollHeightOld = GetMaxYScroll();
  1720. LONG xWidthOld = _xWidth;
  1721. LONG yScrollOld = _yScroll;
  1722. LONG cpNewFirstVisible;
  1723. if(_fNoUpdateView)
  1724. return fReturn;
  1725. AssertSz(_ped->_fInPlaceActive, "CDisplayML::UpdateView(...) called when inactive");
  1726. if(rtp.GetCp() > _cpCalcMax || _fNeedRecalc)
  1727. {
  1728. // We haven't even calc'ed this far, so don't bother with updating
  1729. // here. Background recalc will eventually catch up to us.
  1730. return TRUE;
  1731. }
  1732. AssertSz(rtp.GetCp() <= _cpCalcMax, "CDisplayML::UpdateView(...) - rtp > _cpCaclMax");
  1733. _ped->TxGetClientRect(&rcClient);
  1734. GetViewRect(rcView, &rcClient);
  1735. if(psel && !psel->PuttingChar())
  1736. psel->ClearCchPending();
  1737. DeferUpdateScrollBar();
  1738. // In general, background recalc should not start until both the scroll
  1739. // position is beyond the visible view and the cp is beyond the first visible
  1740. // character. However, for the recalc we will only wait on the height.
  1741. // Later calls to WaitForRecalc will wait on cpFirstVisible if that is
  1742. // necessary.
  1743. _yWait = _yScroll + _yHeightView;
  1744. _cpWait = -1;
  1745. if(!RecalcLines(rtp, cchOld, cchNew, FALSE, TRUE, &led))
  1746. {
  1747. // We're in trouble now, the recalc failed. Let's try to get
  1748. // out of this with our head still mostly attached
  1749. InitVars();
  1750. fRecalcVisible = TRUE;
  1751. fReturn = FALSE;
  1752. _ped->TxInvalidateRect (NULL, FALSE);
  1753. fNeedViewChange = TRUE;
  1754. goto Exit;
  1755. }
  1756. if(!_ped->GetTextLength())
  1757. {
  1758. if(LineCount()) // There are currently elements in
  1759. Clear(AF_DELETEMEM); // line array, so zap them
  1760. // This is an empty control so create one empty line
  1761. CreateEmptyLine();
  1762. }
  1763. if(_xWidth <= _xWidthView)
  1764. {
  1765. // x scroll range is smaller than the view width, force x scrolling position = 0
  1766. // we have to redraw all when this means scrolling back to home.
  1767. // Problem lines are lines with trailing spaces crossing _xWidthView. UpdateCaret forces redraw
  1768. // only when such lines are growing, misses shrinking.
  1769. if (_xScroll != 0)
  1770. {
  1771. _ped->TxInvalidateRect(NULL, FALSE); //REVIEW: find a smaller rectange?
  1772. }
  1773. _xScroll = 0;
  1774. }
  1775. if(led._yFirst >= _yScroll + _yHeightView)
  1776. {
  1777. // Update is after view: don't do anything
  1778. fRecalcVisible = FALSE;
  1779. AssertNr(VerifyFirstVisible());
  1780. goto finish;
  1781. }
  1782. else if(led._yMatchNew <= _yScroll + _dyFirstVisible &&
  1783. led._yMatchOld <= _yScroll + _dyFirstVisible &&
  1784. _yScroll < _yHeight)
  1785. {
  1786. if (_yHeight != 0)
  1787. {
  1788. // Update is entirely before view: just update scroll position
  1789. // but don't touch the screen
  1790. _yScroll += led._yMatchNew - led._yMatchOld;
  1791. _iliFirstVisible += led._iliMatchNew - led._iliMatchOld;
  1792. _iliFirstVisible = max(_iliFirstVisible, 0);
  1793. _cpFirstVisible += led._cpMatchNew - led._cpMatchOld;
  1794. _cpFirstVisible = min(_ped->GetTextLength(), _cpFirstVisible);
  1795. _cpFirstVisible = max(0, _cpFirstVisible);
  1796. fRecalcVisible = FALSE;
  1797. }
  1798. else
  1799. {
  1800. // Odd outline case. Height of control can be recalc'd to zero due
  1801. // when outline mode collapses all lines to 0. Example of how to
  1802. // do this is tell outline to collapse to heading 1 and there is none.
  1803. _yScroll = 0;
  1804. _iliFirstVisible = 0;
  1805. _cpFirstVisible = 0;
  1806. }
  1807. AssertNr(VerifyFirstVisible());
  1808. }
  1809. else
  1810. {
  1811. // Update overlaps visible view
  1812. RECT rc = rcClient;
  1813. RECT rcUpdate;
  1814. // Do we need to resync the first visible? Note that this if check
  1815. // is mostly an optmization; we could decide to _always_ recompute
  1816. // this _iliFirstVisible if we wanted to.
  1817. if ( cpStartOfUpdate <= _cpFirstVisible ||
  1818. led._iliMatchOld <= _iliFirstVisible ||
  1819. led._iliMatchNew <= _iliFirstVisible ||
  1820. led._iliFirst <= _iliFirstVisible )
  1821. {
  1822. // Edit overlaps the first visible. We try to maintain
  1823. // approximately the same place in the file visible.
  1824. cpNewFirstVisible = _cpFirstVisible;
  1825. if(_iliFirstVisible - 1 == led._iliFirst)
  1826. {
  1827. // Edit occurred on line before visible view. Most likely
  1828. // this means that the first character got pulled back to
  1829. // the previous line so we want that line to be visible.
  1830. cpNewFirstVisible = led._cpFirst;
  1831. }
  1832. // Change first visible entries because CLinePtr::RpSetCp() and
  1833. // YPosFromLine() use them, but they're not valid
  1834. _dyFirstVisible = 0;
  1835. _cpFirstVisible = 0;
  1836. _iliFirstVisible = 0;
  1837. _yScroll = 0;
  1838. // With certain formatting changes, it's possible for
  1839. // cpNewFirstVisible to be less that what's been calculated so far
  1840. // in RecalcLines above. Wait for things to catch up.
  1841. WaitForRecalc(cpNewFirstVisible, -1);
  1842. // TODO: make following more efficient (work around rp.CalculateCp()
  1843. // all the way from zero)
  1844. // Recompute scrolling position and first visible values after edit
  1845. CLinePtr rp(this);
  1846. rp.RpSetCp(cpNewFirstVisible, FALSE);
  1847. _yScroll = YposFromLine(rp);
  1848. _cpFirstVisible = rp.CalculateCp() - rp.RpGetIch();
  1849. _iliFirstVisible = rp;
  1850. }
  1851. AssertNr(VerifyFirstVisible());
  1852. // Is there a match in the display area? - this can only happen if the
  1853. // old match is on the screen and the new match will be on the screen
  1854. if (led._yMatchOld < yScrollOld + _yHeightView &&
  1855. led._yMatchNew < _yScroll + _yHeightView)
  1856. {
  1857. // We have a match inside visible view
  1858. // Scroll the part that is below the old y pos of the match
  1859. // or invalidate if the new y of the match is now below the view
  1860. rc.top = rcView.top + (INT) (led._yMatchOld - yScrollOld);
  1861. if(rc.top < rc.bottom)
  1862. {
  1863. // Calculate difference between new and old screen positions
  1864. const INT dy = (INT) ((led._yMatchNew - _yScroll))
  1865. - (led._yMatchOld - yScrollOld);
  1866. if(dy)
  1867. {
  1868. if(!IsTransparent())
  1869. {
  1870. _ped->TxScrollWindowEx(0, dy, &rc, &rcView, NULL, &rcUpdate, 0);
  1871. _ped->TxInvalidateRect(&rcUpdate, FALSE);
  1872. fNeedViewChange = TRUE;
  1873. if(dy < 0)
  1874. {
  1875. rc.top = rc.bottom + dy;
  1876. _ped->TxInvalidateRect(&rc, FALSE);
  1877. fNeedViewChange = TRUE;
  1878. }
  1879. }
  1880. else
  1881. {
  1882. // Adjust rect since we don't scroll in transparent
  1883. // mode
  1884. RECT rcInvalidate = rc;
  1885. rcInvalidate.top += dy;
  1886. _ped->TxInvalidateRect(&rcInvalidate, FALSE);
  1887. fNeedViewChange = TRUE;
  1888. }
  1889. }
  1890. }
  1891. else
  1892. {
  1893. rc.top = rcView.top + led._yMatchNew - _yScroll;
  1894. _ped->TxInvalidateRect(&rc, FALSE);
  1895. fNeedViewChange = TRUE;
  1896. }
  1897. // Since we found that the new match falls on the screen, we can
  1898. // safely set the bottom to the new match since this is the most
  1899. // that can have changed.
  1900. rc.bottom = rcView.top
  1901. + (INT) (max(led._yMatchNew, led._yMatchOld) - _yScroll);
  1902. }
  1903. rc.top = rcView.top + (INT) (led._yFirst - _yScroll);
  1904. // Set first line edited to be rendered using off-screen bitmap
  1905. if (led._iliFirst < Count() && !IsTransparent() &&
  1906. !(Elem(led._iliFirst)->_bFlags & fliUseOffScreenDC))
  1907. {
  1908. Elem(led._iliFirst)->_bFlags |= (fliOffScreenOnce | fliUseOffScreenDC);
  1909. }
  1910. // Invalidate part of update that is above match (if any)
  1911. _ped->TxInvalidateRect (&rc, FALSE);
  1912. fNeedViewChange = TRUE;
  1913. }
  1914. finish:
  1915. if(fRecalcVisible)
  1916. {
  1917. fReturn = WaitForRecalcView();
  1918. if(!fReturn)
  1919. return FALSE;
  1920. }
  1921. if(fNeedViewChange)
  1922. _ped->GetHost()->TxViewChange(FALSE);
  1923. CheckView();
  1924. // We only need to resize if size needed to display object has changed
  1925. if (yHeightOld != _yHeight || yScrollHeightOld != GetMaxYScroll() ||
  1926. xWidthOld != _xWidth)
  1927. {
  1928. if(FAILED(RequestResize()))
  1929. _ped->GetCallMgr()->SetOutOfMemory();
  1930. }
  1931. if(DoDeferredUpdateScrollBar())
  1932. {
  1933. if(FAILED(RequestResize()))
  1934. _ped->GetCallMgr()->SetOutOfMemory();
  1935. DoDeferredUpdateScrollBar();
  1936. }
  1937. Exit:
  1938. return fReturn;
  1939. }
  1940. void CDisplayML::InitVars()
  1941. {
  1942. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::InitVars");
  1943. _yScroll = _xScroll = 0;
  1944. _iliFirstVisible = 0;
  1945. _cpFirstVisible = _cpMin = 0;
  1946. _dyFirstVisible = 0;
  1947. }
  1948. /*
  1949. * CDisplayML::GetCliVisible(pcpMostVisible)
  1950. *
  1951. * @mfunc
  1952. * Get count of visible lines and update _cpMostVisible for PageDown()
  1953. *
  1954. * @rdesc
  1955. * count of visible lines
  1956. */
  1957. LONG CDisplayML::GetCliVisible(
  1958. LONG* pcpMostVisible, //@parm Returns cpMostVisible
  1959. BOOL fLastCharOfLastVisible) const //@parm Want cp of last visible char
  1960. {
  1961. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::GetCliVisible");
  1962. LONG cli = 0; // Initialize count
  1963. LONG ili = _iliFirstVisible; // Start with 1st visible line
  1964. LONG yHeight = _dyFirstVisible;
  1965. LONG cp;
  1966. LONG cchWhite = 0;
  1967. for(cp = _cpFirstVisible;
  1968. yHeight < _yHeightView && ili < Count();
  1969. cli++, ili++)
  1970. {
  1971. const CLine* pli = Elem(ili);
  1972. yHeight += pli->GetHeight();
  1973. if (fLastCharOfLastVisible)
  1974. {
  1975. if (yHeight > _yHeightView)
  1976. {
  1977. // Back up cp to last visible character
  1978. cp -= cchWhite;
  1979. break;
  1980. }
  1981. // Save last lines white space to adjust cp if
  1982. // this is the last fully displayed line.
  1983. cchWhite = pli->_cchWhite;
  1984. }
  1985. cp += pli->_cch;
  1986. }
  1987. if(pcpMostVisible)
  1988. *pcpMostVisible = cp;
  1989. return cli;
  1990. }
  1991. //================================== Inversion (selection) ============================
  1992. /*
  1993. * CDisplayML::InvertRange(cp, cch)
  1994. *
  1995. * @mfunc
  1996. * Invert a given range on screen (for selection)
  1997. *
  1998. * @devnote
  1999. * --- Use when in-place active only ---
  2000. *
  2001. * @rdesc
  2002. * TRUE if success
  2003. */
  2004. BOOL CDisplayML::InvertRange (
  2005. LONG cp, //@parm Active end of range to invert
  2006. LONG cch, //@parm Signed length of range
  2007. SELDISPLAYACTION selAction) //@parm Describes what we are doing to the selection
  2008. {
  2009. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::InvertRange");
  2010. LONG cpMost;
  2011. RECT rc;
  2012. RECT rcView;
  2013. CLinePtr rp(this);
  2014. CRchTxtPtr rtp(_ped);
  2015. LONG y;
  2016. LONG cpActive = _ped->GetSel()->GetCp();
  2017. AssertSz(_ped->_fInPlaceActive,
  2018. "CDisplayML::InvertRange() called when not in-place active");
  2019. if(cch < 0) // Define cpMost, set cp = cpMin,
  2020. { // and cch = |cch|
  2021. cpMost = cp - cch;
  2022. cch = -cch;
  2023. }
  2024. else
  2025. {
  2026. cpMost = cp;
  2027. cp -= cch;
  2028. }
  2029. #ifdef LINESERVICES
  2030. if (g_pols)
  2031. g_pols->DestroyLine(this);
  2032. #endif
  2033. // If an object is being inverted, and nothing else is being inverted,
  2034. // delegate to the ObjectMgr. If fIgnoreObj is TRUE we highlight normally
  2035. if (cch == 1 && _ped->GetObjectCount() &&
  2036. (selAction == selSetNormal || selAction == selSetHiLite))
  2037. {
  2038. CObjectMgr* pobjmgr = _ped->GetObjectMgr();
  2039. rtp.SetCp(cp);
  2040. if(rtp.GetChar() == WCH_EMBEDDING)
  2041. {
  2042. if(pobjmgr)
  2043. pobjmgr->HandleSingleSelect(_ped, cp, selAction == selSetHiLite);
  2044. return TRUE;
  2045. }
  2046. }
  2047. // If display is frozen, just update recalc region and move on.
  2048. if(_padc)
  2049. {
  2050. AssertSz(cp >= 0, "CDisplayML::InvertRange: range (cp) goes below"
  2051. "zero!!" );
  2052. // Make sure these values are bounded.
  2053. if(cp > _ped->GetTextLength()) // Don't bother updating region;
  2054. return TRUE; // it's out of bounds
  2055. if(cp + cch > _ped->GetTextLength())
  2056. cch -= cp + cch - _ped->GetTextLength();
  2057. _padc->UpdateRecalcRegion(cp, cch, cch);
  2058. return TRUE;
  2059. }
  2060. if(!WaitForRecalcView()) // Ensure all visible lines are
  2061. return FALSE; // recalc'd
  2062. GetViewRect(rcView); // Get view rectangle
  2063. // Compute first line to invert and where to start on it
  2064. if(cp >= _cpFirstVisible)
  2065. {
  2066. POINT pt;
  2067. rtp.SetCp(cp);
  2068. if(PointFromTp(rtp, NULL, FALSE, pt, &rp, TA_TOP) < 0)
  2069. return FALSE;
  2070. rc.left = pt.x;
  2071. rc.top = pt.y;
  2072. }
  2073. else
  2074. {
  2075. cp = _cpFirstVisible;
  2076. rp = _iliFirstVisible;
  2077. rc.left = -1;
  2078. rc.top = rcView.top + _dyFirstVisible;
  2079. }
  2080. // Loop on all lines of range
  2081. while (cp < cpMost && rc.top < rcView.bottom && rp.IsValid())
  2082. {
  2083. // Calculate rc.bottom first because rc.top takes into account
  2084. // the dy of the first visible on the first loop.
  2085. y = rc.top;
  2086. y += rp->GetHeight();
  2087. rc.bottom = min(y, rcView.bottom);
  2088. rc.top = max(rc.top, rcView.top);
  2089. if(rc.left == -1)
  2090. rc.left = rp->_xLeft - _xScroll + rcView.left;
  2091. //If we are inverting the active end of the selection, draw it offscreen
  2092. //to minimize flicker.
  2093. if (IN_RANGE(cp - rp.RpGetIch(), cpActive, cp - rp.RpGetIch() + rp->_cch) &&
  2094. !IsTransparent() && !(rp->_bFlags & fliUseOffScreenDC))
  2095. {
  2096. rp->_bFlags |= (fliOffScreenOnce | fliUseOffScreenDC);
  2097. }
  2098. cp += rp->_cch - rp.RpGetIch();
  2099. rc.left = rcView.left;
  2100. rc.right = rcView.right;
  2101. _ped->TxInvalidateRect(&rc, TRUE);
  2102. rc.top = rc.bottom;
  2103. if(!rp.NextRun())
  2104. break;
  2105. }
  2106. _ped->TxUpdateWindow(); // Make sure window gets repainted
  2107. return TRUE;
  2108. }
  2109. //=================================== Scrolling =============================
  2110. /*
  2111. * CDisplay::GetYScroll()
  2112. *
  2113. * @mfunc
  2114. * Returns vertical scrolling position
  2115. */
  2116. LONG CDisplayML::GetYScroll() const
  2117. {
  2118. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::GetYScroll");
  2119. return _yScroll;
  2120. }
  2121. /*
  2122. * CDisplay::VScroll(wCode, yPos)
  2123. *
  2124. * @mfunc
  2125. * Scroll the view vertically in response to a scrollbar event
  2126. *
  2127. * @devnote
  2128. * --- Use when in-place active only ---
  2129. *
  2130. * @rdesc
  2131. * LRESULT formatted for WM_VSCROLL message
  2132. */
  2133. LRESULT CDisplayML::VScroll(
  2134. WORD wCode, //@parm Scrollbar event code
  2135. LONG yPos) //@parm Thumb position (yPos <lt> 0 for EM_SCROLL behavior)
  2136. {
  2137. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::VScroll");
  2138. LONG cliVisible;
  2139. LONG dy = 0;
  2140. BOOL fTracking = FALSE;
  2141. LONG i;
  2142. const LONG ili = _iliFirstVisible;
  2143. CLine * pli = NULL;
  2144. INT yHeightSys = GetYHeightSys();
  2145. LONG yScroll = _yScroll;
  2146. AssertSz(_ped->_fInPlaceActive, "CDisplay::VScroll() called when not in-place");
  2147. if(yPos)
  2148. {
  2149. // Convert this from 16-bit to 32-bit if necessary.
  2150. yPos = ConvertScrollToYPos(yPos);
  2151. }
  2152. yPos = min(yPos, _yHeight);
  2153. switch(wCode)
  2154. {
  2155. case SB_BOTTOM:
  2156. if(yPos < 0)
  2157. return FALSE;
  2158. WaitForRecalc(_ped->GetTextLength(), -1);
  2159. yScroll = _yHeight;
  2160. break;
  2161. case SB_LINEDOWN:
  2162. cliVisible = GetCliVisible();
  2163. if(_iliFirstVisible + cliVisible < Count()
  2164. && 0 == _dyFirstVisible)
  2165. {
  2166. i = _iliFirstVisible + cliVisible;
  2167. pli = Elem(i);
  2168. if(IsInOutlineView())
  2169. { // Scan for uncollapsed line
  2170. for(; pli->_fCollapsed && i < Count();
  2171. pli++, i++);
  2172. }
  2173. if(i < Count())
  2174. dy = pli->_yHeight;
  2175. }
  2176. else if(cliVisible > 1)
  2177. {
  2178. pli = Elem(_iliFirstVisible);
  2179. dy = _dyFirstVisible;
  2180. // TODO: scan until find uncollapsed line
  2181. dy += pli->_yHeight;
  2182. }
  2183. else
  2184. dy = _yHeight - _yScroll;
  2185. if(dy >= _yHeightView)
  2186. dy = yHeightSys;
  2187. // Nothing to scroll, early exit
  2188. if ( !dy )
  2189. return MAKELRESULT(0, TRUE);
  2190. yScroll += dy;
  2191. break;
  2192. case SB_LINEUP:
  2193. if(_iliFirstVisible > 0)
  2194. {
  2195. pli = Elem(_iliFirstVisible - 1);
  2196. // TODO: scan until find uncollapsed line
  2197. dy = pli->_yHeight;
  2198. }
  2199. else if(yScroll > 0)
  2200. dy = min(yScroll, yHeightSys);
  2201. if(dy > _yHeightView)
  2202. dy = yHeightSys;
  2203. yScroll -= dy;
  2204. break;
  2205. case SB_PAGEDOWN:
  2206. cliVisible = GetCliVisible();
  2207. yScroll += _yHeightView;
  2208. if(yScroll < _yHeight && cliVisible > 0)
  2209. {
  2210. // TODO: Scan until find uncollapsed line
  2211. dy = Elem(_iliFirstVisible + cliVisible - 1)->_yHeight;
  2212. if(dy >= _yHeightView)
  2213. dy = yHeightSys;
  2214. else if(dy > _yHeightView - dy)
  2215. {
  2216. // Go at least a line if line is very big
  2217. dy = _yHeightView - dy;
  2218. }
  2219. yScroll -= dy;
  2220. }
  2221. break;
  2222. case SB_PAGEUP:
  2223. cliVisible = GetCliVisible();
  2224. yScroll -= _yHeightView;
  2225. if (yScroll < 0)
  2226. {
  2227. // Scroll position can't be negative and we don't
  2228. // need to back up to be sure we display a full line.
  2229. yScroll = 0;
  2230. }
  2231. else if(cliVisible > 0)
  2232. {
  2233. // TODO: Scan until find uncollapsed line
  2234. dy = Elem(_iliFirstVisible)->_yHeight;
  2235. if(dy >= _yHeightView)
  2236. dy = yHeightSys;
  2237. else if(dy > _yHeightView - dy)
  2238. {
  2239. // Go at least a line if line is very big
  2240. dy = _yHeightView - dy;
  2241. }
  2242. yScroll += dy;
  2243. }
  2244. break;
  2245. case SB_THUMBTRACK:
  2246. case SB_THUMBPOSITION:
  2247. if(yPos < 0)
  2248. return FALSE;
  2249. yScroll = yPos;
  2250. fTracking = TRUE;
  2251. break;
  2252. case SB_TOP:
  2253. if(yPos < 0)
  2254. return FALSE;
  2255. yScroll = 0;
  2256. break;
  2257. case SB_ENDSCROLL:
  2258. UpdateScrollBar(SB_VERT);
  2259. return MAKELRESULT(0, TRUE);
  2260. default:
  2261. return FALSE;
  2262. }
  2263. ScrollView(_xScroll, min(yScroll, max(_yHeight - _yHeightView, 0)), fTracking, TRUE);
  2264. // Force position update if we just finished a track
  2265. if(wCode == SB_THUMBPOSITION)
  2266. UpdateScrollBar(SB_VERT);
  2267. // Return how many lines we scrolled
  2268. return MAKELRESULT((WORD) (_iliFirstVisible - ili), TRUE);
  2269. }
  2270. /*
  2271. * CDisplay::LineScroll(cli, cch)
  2272. *
  2273. * @mfunc
  2274. * Scroll view vertically in response to a scrollbar event
  2275. */
  2276. void CDisplayML::LineScroll(
  2277. LONG cli, //@parm Count of lines to scroll vertically
  2278. LONG cch) //@parm Count of characters to scroll horizontally
  2279. {
  2280. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::LineScroll");
  2281. //Make sure the line to scroll to is valid
  2282. if (cli + _iliFirstVisible >= Count())
  2283. {
  2284. // change line count enough to display the last line
  2285. cli = Count() - _iliFirstVisible;
  2286. }
  2287. // Get the absolute yScroll position by adding the difference of the line
  2288. // we want to go to and the current _yScroll position
  2289. LONG dyScroll = CalcYLineScrollDelta(cli, FALSE);
  2290. if(dyScroll < 0 || _yHeight - (_yScroll + dyScroll) > _yHeightView - dyScroll)
  2291. ScrollView(_xScroll, _yScroll + dyScroll, FALSE, FALSE);
  2292. }
  2293. /*
  2294. * CDisplayML::FractionalScrollView (yDelta)
  2295. *
  2296. * @mfunc
  2297. * Allow view to be scrolled by fractional lines.
  2298. */
  2299. void CDisplayML::FractionalScrollView ( LONG yDelta )
  2300. {
  2301. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::FractionalScrollView");
  2302. if ( yDelta)
  2303. ScrollView(_xScroll, min(yDelta + _yScroll, max(_yHeight - _yHeightView, 0)), FALSE, TRUE);
  2304. }
  2305. /*
  2306. * CDisplayML::ScrollToLineStart(iDirection)
  2307. *
  2308. * @mfunc
  2309. * If the view is scrolled so that only a partial line is at the
  2310. * top, then scroll the view so that the entire view is at the top.
  2311. */
  2312. void CDisplayML::ScrollToLineStart(
  2313. LONG iDirection) //@parm the direction in which to scroll (negative
  2314. // means down the screen
  2315. {
  2316. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::ScrollToLineStart");
  2317. // This code originally lined things up on a line. However, it doesn't work
  2318. // very well with big objects especially at the end of the document. I am
  2319. // leaving the call here in case we discover problems later. (a-rsail).
  2320. #if 0
  2321. // If _dyFirstVisible is zero, then we're aligned on a line, so
  2322. // nothing more to do.
  2323. if(_dyFirstVisible)
  2324. {
  2325. LONG yScroll = _yScroll + _dyFirstVisible;
  2326. if(iDirection <= 0)
  2327. {
  2328. yScroll += Elem(_iliFirstVisible)->_yHeight;
  2329. }
  2330. ScrollView(_xScroll, yScroll, FALSE, TRUE);
  2331. }
  2332. #endif // 0
  2333. }
  2334. /*
  2335. * CDisplayML::CalcYLineScrollDelta (cli, fFractionalFirst)
  2336. *
  2337. * @mfunc
  2338. * Given a count of lines, positive or negative, calc the number
  2339. * of vertical units necessary to scroll the view to the start of
  2340. * the current line + the given count of lines.
  2341. */
  2342. LONG CDisplayML::CalcYLineScrollDelta (
  2343. LONG cli,
  2344. BOOL fFractionalFirst )
  2345. {
  2346. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::CalcYLineScrollDelta");
  2347. LONG yScroll = 0;
  2348. if(fFractionalFirst && _dyFirstVisible) // Scroll partial for 1st.
  2349. {
  2350. Assert(_dyFirstVisible <= 0); // get jonmat
  2351. if(cli < 0)
  2352. {
  2353. cli++;
  2354. yScroll = _dyFirstVisible;
  2355. }
  2356. else
  2357. {
  2358. cli--;
  2359. yScroll = Elem(_iliFirstVisible)->_yHeight + _dyFirstVisible;
  2360. }
  2361. }
  2362. if(cli > 0)
  2363. {
  2364. // Scrolling down
  2365. cli = min(cli, Count() - _iliFirstVisible - 1);
  2366. if (!fFractionalFirst && (0 == cli))
  2367. {
  2368. // If we are scrolling down and on the last line but we haven't scrolled to
  2369. // the very bottom, then do so now.
  2370. AssertSz(0 == yScroll,
  2371. "CDisplayML::CalcYLineScrollDelta last line & scroll");
  2372. yScroll = _yHeight - _yScroll;
  2373. // Limit scroll length to approximately 3 lines.
  2374. yScroll = min(yScroll, 3 * GetYHeightSys());
  2375. }
  2376. }
  2377. else if(cli < 0)
  2378. {
  2379. // Scrolling up
  2380. cli = max(cli, -_iliFirstVisible);
  2381. // At the top.
  2382. if (!fFractionalFirst && (0 == cli))
  2383. {
  2384. // Make sure that we scroll back so first visible is 0.
  2385. yScroll = _dyFirstVisible;
  2386. // Limit scroll length to approximately 3 lines.
  2387. yScroll = max(yScroll, -3 * GetYHeightSys());
  2388. }
  2389. }
  2390. if(cli)
  2391. yScroll += YposFromLine(_iliFirstVisible + cli) - YposFromLine(_iliFirstVisible);
  2392. return yScroll;
  2393. }
  2394. /*
  2395. * CDisplayML::ScrollView(xScroll, yScroll, fTracking, fFractionalScroll)
  2396. *
  2397. * @mfunc
  2398. * Scroll view to new x and y position
  2399. *
  2400. * @devnote
  2401. * This method tries to adjust the y scroll pos before
  2402. * scrolling to display complete line at top. x scroll
  2403. * pos is adjusted to avoid scrolling all text off the
  2404. * view rectangle.
  2405. *
  2406. * Must be able to handle yScroll <gt> pdp->yHeight and yScroll <lt> 0
  2407. *
  2408. * @rdesc
  2409. * TRUE if actual scrolling occurred,
  2410. * FALSE if no change
  2411. */
  2412. BOOL CDisplayML::ScrollView (
  2413. LONG xScroll, //@parm New x scroll position
  2414. LONG yScroll, //@parm New y scroll position
  2415. BOOL fTracking, //@parm TRUE indicates we are tracking scrollbar thumb
  2416. BOOL fFractionalScroll)
  2417. {
  2418. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::ScrollView");
  2419. // (don't update the scrollbar pos)
  2420. BOOL fTryAgain = TRUE;
  2421. RECT rcUpdate; // ??? we may want use a region here but ScrollView is
  2422. // rarely called with both a xScroll and yScroll value.
  2423. LONG xWidthMax;
  2424. LONG dx = 0;
  2425. LONG dy = 0;
  2426. RECT rcView;
  2427. CTxtSelection *psel = _ped->GetSelNC();
  2428. COleObject *pipo;
  2429. BOOL fRestoreCaret = FALSE;
  2430. AssertSz(_ped->_fInPlaceActive, "CDisplayML::ScrollView() called when not in-place");
  2431. GetViewRect(rcView);
  2432. if(xScroll == -1)
  2433. xScroll = _xScroll;
  2434. if(yScroll == -1)
  2435. yScroll = _yScroll;
  2436. // Determine vertical scrolling pos
  2437. while(1)
  2438. {
  2439. BOOL fNothingBig = TRUE;
  2440. LONG yFirst;
  2441. LONG dyFirst;
  2442. LONG cpFirst;
  2443. LONG iliFirst;
  2444. LONG yHeight;
  2445. LONG iliT;
  2446. yScroll = min(yScroll, GetMaxYScroll());
  2447. yScroll = max(0, yScroll);
  2448. dy = 0;
  2449. // Ensure all visible lines are recalced
  2450. if(!WaitForRecalcView())
  2451. return FALSE;
  2452. // Compute new first visible line
  2453. iliFirst = LineFromYpos(yScroll, &yFirst, &cpFirst);
  2454. if(cpFirst < 0)
  2455. {
  2456. // FUTURE (alexgo) this is pretty bogus, we should try to do
  2457. // better in the next rel.
  2458. TRACEERRORSZ("Display calc hosed, trying again");
  2459. InitVars();
  2460. _fNeedRecalc = TRUE;
  2461. return FALSE;
  2462. }
  2463. if(iliFirst < 0)
  2464. {
  2465. // No line at _yScroll, use last line instead
  2466. iliFirst = max(0, Count() - 1);
  2467. cpFirst = _ped->GetTextLength() - Elem(iliFirst)->_cch;
  2468. yScroll = _yHeight - Elem(iliFirst)->_yHeight;
  2469. yFirst = _yScroll;
  2470. }
  2471. dyFirst = yFirst - yScroll;
  2472. // Figure whether there is a big line
  2473. // (more that a third of the view rect)
  2474. for(iliT = iliFirst, yHeight = dyFirst;
  2475. yHeight < _yHeightView && iliT < Count();
  2476. iliT++)
  2477. {
  2478. const CLine* pli = Elem(iliT);
  2479. if(pli->_yHeight >= _yHeightView / 3)
  2480. fNothingBig = FALSE;
  2481. yHeight += pli->_yHeight;
  2482. }
  2483. // If no big line and first pass, try to adjust
  2484. // scrolling pos to show complete line at top
  2485. if(!fFractionalScroll && fTryAgain && fNothingBig && dyFirst != 0)
  2486. {
  2487. fTryAgain = FALSE; // prevent any infinite loop
  2488. Assert(dyFirst < 0);
  2489. Tracef(TRCSEVINFO, "adjusting scroll for partial line at %d", dyFirst);
  2490. // partial line visible at top, try to get a complete line showing
  2491. yScroll += dyFirst;
  2492. LONG yHeightLine = Elem(iliFirst)->_yHeight;
  2493. // Adjust the height of the scroll by the height of the first
  2494. // visible line if we are scrolling down or if we are using the
  2495. // thumb (tracking) and we are on the last page of the view.
  2496. if ((fTracking && yScroll + _yHeightView + yHeightLine > _yHeight)
  2497. || (!fTracking && _yScroll <= yScroll))
  2498. {
  2499. // Scrolling down so move down a little more
  2500. yScroll += yHeightLine;
  2501. }
  2502. }
  2503. else
  2504. {
  2505. dy = 0;
  2506. if(yScroll != _yScroll)
  2507. {
  2508. _iliFirstVisible = iliFirst;
  2509. _dyFirstVisible = dyFirst;
  2510. _cpFirstVisible = cpFirst;
  2511. dy = _yScroll - yScroll;
  2512. _yScroll = yScroll;
  2513. AssertSz(_yScroll >= 0, "CDisplayML::ScrollView _yScroll < 0");
  2514. AssertNr(VerifyFirstVisible());
  2515. if(!WaitForRecalcView())
  2516. return FALSE;
  2517. }
  2518. break;
  2519. }
  2520. }
  2521. CheckView();
  2522. // Determine horizontal scrolling pos.
  2523. xWidthMax = _xWidth;
  2524. // REVIEW (Victork) Restricting the range of the scroll is not really needed and could even be bad (bug 6104)
  2525. xScroll = min(xScroll, xWidthMax);
  2526. xScroll = max(0, xScroll);
  2527. dx = _xScroll - xScroll;
  2528. if(dx)
  2529. _xScroll = xScroll;
  2530. // Now perform the actual scrolling
  2531. if(IsMain() && (dy || dx))
  2532. {
  2533. // Scroll only if scrolling < view dimensions and we are in-place
  2534. if(IsActive() && !IsTransparent() &&
  2535. dy < _yHeightView && dx < _xWidthView)
  2536. {
  2537. // FUTURE: (ricksa/alexgo): we may be able to get rid of
  2538. // some of these ShowCaret calls; they look bogus.
  2539. if (psel && psel->IsCaretShown())
  2540. {
  2541. _ped->TxShowCaret(FALSE);
  2542. fRestoreCaret = TRUE;
  2543. }
  2544. _ped->TxScrollWindowEx((INT) dx, (INT) dy, NULL, &rcView,
  2545. NULL, &rcUpdate, 0);
  2546. _ped->TxInvalidateRect(&rcUpdate, FALSE);
  2547. if(fRestoreCaret)
  2548. _ped->TxShowCaret(FALSE);
  2549. }
  2550. else
  2551. _ped->TxInvalidateRect(&rcView, FALSE);
  2552. if(psel)
  2553. psel->UpdateCaret(FALSE);
  2554. if(!fTracking && dy)
  2555. {
  2556. UpdateScrollBar(SB_VERT);
  2557. _ped->SendScrollEvent(EN_VSCROLL);
  2558. }
  2559. if(!fTracking && dx)
  2560. {
  2561. UpdateScrollBar(SB_HORZ);
  2562. _ped->SendScrollEvent(EN_HSCROLL);
  2563. }
  2564. _ped->TxUpdateWindow();
  2565. // FUTURE: since we're now repositioning in place active
  2566. // objects every time we draw, this call seems to be
  2567. // superfluous (AndreiB)
  2568. // Tell object subsystem to reposition any in place objects
  2569. if(_ped->GetObjectCount())
  2570. {
  2571. pipo = _ped->GetObjectMgr()->GetInPlaceActiveObject();
  2572. if(pipo)
  2573. pipo->OnReposition( dx, dy );
  2574. }
  2575. }
  2576. return dy || dx;
  2577. }
  2578. /*
  2579. * CDisplayML::GetScrollRange(nBar)
  2580. *
  2581. * @mfunc
  2582. * Returns the max part of a scrollbar range for scrollbar <p nBar>
  2583. *
  2584. * @rdesc
  2585. * LONG max part of scrollbar range
  2586. */
  2587. LONG CDisplayML::GetScrollRange(
  2588. INT nBar) const //@parm Scroll bar to interrogate (SB_VERT or SB_HORZ)
  2589. {
  2590. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::GetScrollRange");
  2591. Assert( IsMain() );
  2592. LONG lRange = 0;
  2593. if(nBar == SB_VERT && _fVScrollEnabled)
  2594. {
  2595. if(_ped->TxGetScrollBars() & WS_VSCROLL)
  2596. lRange = GetMaxYScroll();
  2597. }
  2598. else if((_ped->TxGetScrollBars() & WS_HSCROLL) && _fHScrollEnabled)
  2599. {
  2600. // Scroll range is maximum width plus room for the caret.
  2601. lRange = max(0, _xWidth + dxCaret);
  2602. }
  2603. // Since thumb messages are limited to 16-bit, limit range to 16-bit
  2604. lRange = min(lRange, _UI16_MAX);
  2605. return lRange;
  2606. }
  2607. /*
  2608. * CDisplayML::UpdateScrollBar(nBar, fUpdateRange)
  2609. *
  2610. * @mfunc
  2611. * Update either the horizontal or the vertical scrollbar and
  2612. * figure whether the scrollbar should be visible or not.
  2613. *
  2614. * @rdesc
  2615. * BOOL
  2616. */
  2617. BOOL CDisplayML::UpdateScrollBar(
  2618. INT nBar, //@parm Which scroll bar : SB_HORZ, SB_VERT
  2619. BOOL fUpdateRange) //@parm Should the range be recomputed and updated
  2620. {
  2621. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::UpdateScrollBar");
  2622. // Note: In the old days we didn't allow autosize & scroll bars, so to keep
  2623. // forms working, we need this special logic with respect to autosize.
  2624. if (!IsActive() || _fInRecalcScrollBars ||
  2625. !_ped->fInOurHost() && _ped->TxGetAutoSize())
  2626. {
  2627. // No scroll bars unless we are inplace active and we are not in the
  2628. // process of updating scroll bars already.
  2629. return TRUE;
  2630. }
  2631. const DWORD dwScrollBars = _ped->TxGetScrollBars();
  2632. const BOOL fHide = !(dwScrollBars & ES_DISABLENOSCROLL);
  2633. BOOL fReturn = FALSE;
  2634. BOOL fEnabled = TRUE;
  2635. BOOL fEnabledOld;
  2636. LONG lScroll;
  2637. CTxtSelection *psel = _ped->GetSelNC();
  2638. BOOL fShowCaret = FALSE;
  2639. // Get scrolling position
  2640. if(nBar == SB_VERT)
  2641. {
  2642. if(!(dwScrollBars & WS_VSCROLL))
  2643. return FALSE;
  2644. fEnabledOld = _fVScrollEnabled;
  2645. if(GetMaxYScroll() <= _yHeightView)
  2646. fEnabled = FALSE;
  2647. }
  2648. else
  2649. {
  2650. if(!(dwScrollBars & WS_HSCROLL))
  2651. {
  2652. // Even if we don't have scrollbars, we may allow horizontal
  2653. // scrolling.
  2654. if(!_fHScrollEnabled && _xWidth > _xWidthView)
  2655. _fHScrollEnabled = !!(dwScrollBars & ES_AUTOHSCROLL);
  2656. return FALSE;
  2657. }
  2658. fEnabledOld = _fHScrollEnabled;
  2659. if(_xWidth <= _xWidthView)
  2660. fEnabled = FALSE;
  2661. }
  2662. // Don't allow ourselves to be re-entered.
  2663. // Be sure to turn this to FALSE on exit
  2664. _fInRecalcScrollBars = TRUE;
  2665. // !s beforehand because all true values aren't necessarily equal
  2666. if(!fEnabled != !fEnabledOld)
  2667. {
  2668. if(_fDeferUpdateScrollBar)
  2669. _fUpdateScrollBarDeferred = TRUE;
  2670. else
  2671. {
  2672. if (nBar == SB_HORZ)
  2673. _fHScrollEnabled = fEnabled;
  2674. else
  2675. _fVScrollEnabled = fEnabled;
  2676. }
  2677. if(!_fDeferUpdateScrollBar)
  2678. {
  2679. if(!fHide)
  2680. {
  2681. // Don't hide scrollbar, just disable
  2682. _ped->TxEnableScrollBar(nBar, fEnabled ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH);
  2683. if (!fEnabled)
  2684. {
  2685. // The scroll bar is disabled. Therefore, all the text fits
  2686. // on the screen so make sure the drawing reflects this.
  2687. _yScroll = 0;
  2688. _dyFirstVisible = 0;
  2689. _cpFirstVisible = 0;
  2690. _iliFirstVisible = 0;
  2691. _ped->TxInvalidateRect(NULL, FALSE);
  2692. }
  2693. }
  2694. else
  2695. {
  2696. fReturn = TRUE;
  2697. // Make sure to hide caret before showing scrollbar
  2698. if(psel)
  2699. fShowCaret = psel->ShowCaret(FALSE);
  2700. // Hide or show scroll bar
  2701. _ped->TxShowScrollBar(nBar, fEnabled);
  2702. // The scroll bar affects the window which in turn affects the
  2703. // display. Therefore, if word wrap, repaint
  2704. _ped->TxInvalidateRect(NULL, TRUE);
  2705. // Needed for bug fix #5521
  2706. _ped->TxUpdateWindow();
  2707. if(fShowCaret)
  2708. psel->ShowCaret(TRUE);
  2709. }
  2710. }
  2711. }
  2712. // Set scrollbar range and thumb position
  2713. if(fEnabled)
  2714. {
  2715. if(fUpdateRange && !_fDeferUpdateScrollBar)
  2716. _ped->TxSetScrollRange(nBar, 0, GetScrollRange(nBar), FALSE);
  2717. if(_fDeferUpdateScrollBar)
  2718. _fUpdateScrollBarDeferred = TRUE;
  2719. else
  2720. {
  2721. lScroll = (nBar == SB_VERT)
  2722. ? ConvertYPosToScrollPos(_yScroll)
  2723. : ConvertXPosToScrollPos(_xScroll);
  2724. _ped->TxSetScrollPos(nBar, lScroll, TRUE);
  2725. }
  2726. }
  2727. _fInRecalcScrollBars = FALSE;
  2728. return fReturn;
  2729. }
  2730. /*
  2731. * CDisplayML::GetNaturalSize(hdcDraw, hicTarget, dwMode, pwidth, pheight)
  2732. *
  2733. * @mfunc
  2734. * Recalculate display to input width & height for TXTNS_FITTOCONTENT.
  2735. *
  2736. * @rdesc
  2737. * S_OK - Call completed successfully <nl>
  2738. */
  2739. HRESULT CDisplayML::GetNaturalSize(
  2740. HDC hdcDraw, //@parm DC for drawing
  2741. HDC hicTarget, //@parm DC for information
  2742. DWORD dwMode, //@parm Type of natural size required
  2743. LONG *pwidth, //@parm Width in device units to use for fitting
  2744. LONG *pheight) //@parm Height in device units to use for fitting
  2745. {
  2746. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::GetNaturalSize");
  2747. HRESULT hr = S_OK;
  2748. // Set the height temporarily so the zoom factor will work out
  2749. LONG yOrigHeightClient = SetClientHeight(*pheight);
  2750. // Adjust height and width by view inset
  2751. LONG widthView = *pwidth;
  2752. LONG heightView = *pheight;
  2753. GetViewDim(widthView, heightView);
  2754. // Store adjustment so we can restore it to height & width
  2755. LONG widthAdj = *pwidth - widthView;
  2756. LONG heightAdj = *pheight - heightView;
  2757. // Init measurer at cp = 0
  2758. CMeasurer me(this);
  2759. CLine liNew;
  2760. LONG xWidth = 0, lineWidth;
  2761. LONG yHeight = 0;
  2762. LONG cchText = _ped->GetTextLength();
  2763. BOOL fFirstInPara = TRUE;
  2764. LONG xWidthMax = GetWordWrap() ? widthView : -1;
  2765. // The following loop generates new lines
  2766. do
  2767. { // Stuff text into new line
  2768. UINT uiFlags = 0;
  2769. // If word wrap is turned on, then we want to break on
  2770. // words, otherwise, measure white space, etc.
  2771. if(GetWordWrap())
  2772. uiFlags = MEASURE_BREAKATWORD;
  2773. if(fFirstInPara)
  2774. uiFlags |= MEASURE_FIRSTINPARA;
  2775. if(!liNew.Measure(me, -1, xWidthMax, uiFlags))
  2776. {
  2777. hr = E_FAIL;
  2778. goto exit;
  2779. }
  2780. fFirstInPara = liNew._bFlags & fliHasEOP;
  2781. // Keep track of width of widest line
  2782. lineWidth = liNew._xWidth + liNew._xLineOverhang;
  2783. xWidth = max(xWidth, lineWidth);
  2784. yHeight += liNew._yHeight; // Bump height
  2785. } while (me.GetCp() < cchText);
  2786. // Add caret size to width to guarantee that text fits. We don't
  2787. // want to word break because the caret won't fit when the caller
  2788. // tries a window this size.
  2789. xWidth += dxCaret;
  2790. *pwidth = xWidth;
  2791. *pheight = yHeight;
  2792. // Restore insets so output reflects true client rect needed
  2793. *pwidth += widthAdj;
  2794. *pheight += heightAdj;
  2795. exit:
  2796. SetClientHeight(yOrigHeightClient);
  2797. return hr;
  2798. }
  2799. /*
  2800. * CDisplayML::Clone()
  2801. *
  2802. * @mfunc
  2803. * Make a copy of this object
  2804. *
  2805. * @rdesc
  2806. * NULL - failed
  2807. * CDisplay *
  2808. *
  2809. * @devnote
  2810. * Caller of this routine is the owner of the new display object.
  2811. */
  2812. CDisplay *CDisplayML::Clone() const
  2813. {
  2814. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::Clone");
  2815. CDisplayML *pdp = new CDisplayML(_ped);
  2816. if(pdp)
  2817. {
  2818. // Initialize our base class
  2819. if(pdp->CDisplay::Init())
  2820. {
  2821. pdp->InitFromDisplay(this);
  2822. pdp->_xScroll = _xScroll;
  2823. pdp->_fVScrollEnabled = _fVScrollEnabled;
  2824. pdp->_fHScrollEnabled = _fHScrollEnabled;
  2825. pdp->_fWordWrap = _fWordWrap;
  2826. pdp->_cpFirstVisible = _cpFirstVisible;
  2827. pdp->_iliFirstVisible = _iliFirstVisible;
  2828. pdp->_yScroll = _yScroll;
  2829. pdp->ResetDrawInfo(this);
  2830. if(_pddTarget)
  2831. {
  2832. // Create a duplicate target device for this object
  2833. pdp->SetMainTargetDC(_pddTarget->GetDC(), _xWidthMax);
  2834. }
  2835. // This can't be the active view since it is a clone
  2836. // of some view.
  2837. pdp->SetActiveFlag(FALSE);
  2838. }
  2839. }
  2840. return pdp;
  2841. }
  2842. void CDisplayML::DeferUpdateScrollBar()
  2843. {
  2844. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::DeferUpdateScrollBar");
  2845. _fDeferUpdateScrollBar = TRUE;
  2846. _fUpdateScrollBarDeferred = FALSE;
  2847. }
  2848. BOOL CDisplayML::DoDeferredUpdateScrollBar()
  2849. {
  2850. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::DoDeferredUpdateScrollBar");
  2851. _fDeferUpdateScrollBar = FALSE;
  2852. if(!_fUpdateScrollBarDeferred)
  2853. return FALSE;
  2854. _fUpdateScrollBarDeferred = FALSE;
  2855. BOOL fHorizontalUpdated = UpdateScrollBar(SB_HORZ, TRUE);
  2856. return UpdateScrollBar(SB_VERT, TRUE) || fHorizontalUpdated;
  2857. }
  2858. LONG CDisplayML::GetMaxPixelWidth(void) const
  2859. {
  2860. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::GetMaxPixelWidth");
  2861. return _xWidthMax ? LXtoDX(_xWidthMax) : GetViewWidth();
  2862. }
  2863. /*
  2864. * CDisplayML::GetMaxXScroll()
  2865. *
  2866. * @mfunc
  2867. * Get the maximum x scroll value
  2868. *
  2869. * @rdesc
  2870. * Maximum x scroll value
  2871. *
  2872. */
  2873. LONG CDisplayML::GetMaxXScroll() const
  2874. {
  2875. return _xWidth + dxCaret;
  2876. }
  2877. /*
  2878. * CDisplayML::CreateEmptyLine()
  2879. *
  2880. * @mfunc
  2881. * Create an empty line
  2882. *
  2883. * @rdesc
  2884. * TRUE - iff successful
  2885. */
  2886. BOOL CDisplayML::CreateEmptyLine()
  2887. {
  2888. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::CreateEmptyLine");
  2889. // Make sure that this is being called appropriately
  2890. AssertSz(_ped->GetTextLength() == 0,
  2891. "CDisplayML::CreateEmptyLine called inappropriately");
  2892. CMeasurer me(this); // Create a measurer
  2893. CLine * pliNew = Add(1, NULL); // Add one new line
  2894. if(!pliNew)
  2895. {
  2896. _ped->GetCallMgr()->SetOutOfMemory();
  2897. TRACEWARNSZ("CDisplayML::CreateEmptyLine unable to add CLine to CLineArray");
  2898. return FALSE;
  2899. }
  2900. // Measure the empty line
  2901. if(!pliNew->Measure(me, -1, -1, MEASURE_BREAKATWORD | MEASURE_FIRSTINPARA))
  2902. {
  2903. Assert(FALSE);
  2904. return FALSE;
  2905. }
  2906. return TRUE;
  2907. }
  2908. /*
  2909. * CDisplayML::AdjustToDisplayLastLine()
  2910. *
  2911. * @mfunc
  2912. * Calculate the yscroll necessary to get the last line to display
  2913. *
  2914. * @rdesc
  2915. * Updated yScroll
  2916. *
  2917. */
  2918. LONG CDisplayML::AdjustToDisplayLastLine(
  2919. LONG yBase, //@parm actual yScroll to display
  2920. LONG yScroll) //@parm proposed amount to scroll
  2921. {
  2922. LONG iliFirst;
  2923. LONG yFirst;
  2924. if(yBase >= _yHeight)
  2925. {
  2926. // Want last line to be entirely displayed.
  2927. // Compute new first visible line
  2928. iliFirst = LineFromYpos(yScroll, &yFirst, NULL);
  2929. // Is top line partial?
  2930. if(yScroll != yFirst)
  2931. {
  2932. // Yes - bump scroll to the next line so the ScrollView
  2933. // won't bump the scroll back to display the entire
  2934. // partial line since we want the bottom to display.
  2935. yScroll = YposFromLine(iliFirst + 1);
  2936. }
  2937. }
  2938. return yScroll;
  2939. }
  2940. /*
  2941. * CDisplayML::GetResizeHeight()
  2942. *
  2943. * @mfunc
  2944. * Calculates height to return for a request resize
  2945. *
  2946. * @rdesc
  2947. * Updated yScroll
  2948. */
  2949. LONG CDisplayML::GetResizeHeight() const
  2950. {
  2951. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::GetResizeHeight");
  2952. return CalcScrollHeight(_yHeight);
  2953. }
  2954. void CDisplayML::RebindFirstVisible()
  2955. {
  2956. LONG cp = _cpFirstVisible;
  2957. // Change first visible entries because CLinePtr::RpSetCp() and
  2958. // YPosFromLine() use them, but they're not valid
  2959. _dyFirstVisible = 0;
  2960. _cpFirstVisible = 0;
  2961. _iliFirstVisible = 0;
  2962. _yScroll = 0;
  2963. // Recompute scrolling position and first visible values after edit
  2964. // force _yScroll = 0 if y scroll range is smaller than the view height
  2965. if(_yHeight > _yHeightView)
  2966. {
  2967. CLinePtr rp(this);
  2968. rp.RpSetCp(cp, FALSE);
  2969. _yScroll = YposFromLine(rp);
  2970. // TODO: make following more efficient (work around rp.CalculateCp()
  2971. // all the way from zero)
  2972. // We use rp.GetCp() instead of cp, because cp could now be
  2973. // woefully out of date. RpSetCp will set us to the closest
  2974. // available cp.
  2975. _cpFirstVisible = rp.CalculateCp() - rp.RpGetIch();
  2976. _iliFirstVisible = rp;
  2977. }
  2978. }
  2979. // ================================ DEBUG methods ============================================
  2980. #ifdef DEBUG
  2981. /*
  2982. * CDisplayML::CheckLineArray()
  2983. *
  2984. * @mfunc
  2985. * DEBUG routine that Asserts unless:
  2986. * 1) sum of all line counts equals count of characters in story
  2987. * 2) sum of all line heights equals height of display galley
  2988. */
  2989. void CDisplayML::CheckLineArray() const
  2990. {
  2991. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::CheckLineArray");
  2992. LONG ili = Count();
  2993. // If we are marked as needing a recalc or if we are in the process of a
  2994. // background recalc, we cannot verify the line array
  2995. if(!_fRecalcDone || _fNeedRecalc || !ili)
  2996. return;
  2997. LONG cchText = _ped->GetTextLength();
  2998. if (!cchText)
  2999. return;
  3000. LONG cp = 0;
  3001. BOOL fFirstInPara;
  3002. BOOL fPrevLineEOP = TRUE;
  3003. LONG yHeight = 0;
  3004. CLine const *pli = Elem(0);
  3005. CRchTxtPtr rtp(_ped);
  3006. while(ili--)
  3007. {
  3008. fFirstInPara = (pli->_bFlags & fliFirstInPara) != 0;
  3009. AssertSz(!(fPrevLineEOP ^ fFirstInPara),
  3010. "CDisplayML::CheckLineArray: Invalid first/prev flags");
  3011. AssertSz(pli->_cch,
  3012. "CDisplayML::CheckLineArray: cch == 0");
  3013. yHeight += pli->GetHeight();
  3014. cp += pli->_cch;
  3015. fPrevLineEOP = (pli->_bFlags & fliHasEOP) != 0;
  3016. pli++;
  3017. }
  3018. if((cp != cchText) && (cp != _cpCalcMax))
  3019. {
  3020. Tracef(TRCSEVINFO, "sigma (*this)[]._cch = %ld, cchText = %ld", cp, cchText);
  3021. AssertSz(FALSE,
  3022. "CDisplayML::CheckLineArray: sigma(*this)[]._cch != cchText");
  3023. }
  3024. if(yHeight != _yHeight)
  3025. {
  3026. Tracef(TRCSEVINFO, "sigma (*this)[]._yHeight = %ld, _yHeight = %ld", yHeight, _yHeight);
  3027. AssertSz(FALSE,
  3028. "CDisplayML::CheckLineArray: sigma(*this)[]._yHeight != _yHeight");
  3029. }
  3030. }
  3031. void CDisplayML::DumpLines(
  3032. LONG iliFirst,
  3033. LONG cli)
  3034. {
  3035. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::DumpLines");
  3036. LONG cch;
  3037. LONG ili;
  3038. TCHAR rgch[512];
  3039. if(Count() == 1)
  3040. wcscpy(rgch, TEXT("1 line"));
  3041. else
  3042. wsprintf(rgch, TEXT("%d lines"), Count());
  3043. #ifdef UNICODE
  3044. // TraceTag needs to take UNICODE...
  3045. #else
  3046. TRACEINFOSZ(TRCSEVINFO, rgch);
  3047. #endif
  3048. if(cli < 0)
  3049. cli = Count();
  3050. else
  3051. cli = min(cli, Count());
  3052. if(iliFirst < 0)
  3053. iliFirst = Count() - cli;
  3054. else
  3055. cli = min(cli, Count() - iliFirst);
  3056. for(ili = iliFirst; cli > 0; ili++, cli--)
  3057. {
  3058. const CLine * const pli = Elem(ili);
  3059. wsprintf(rgch, TEXT("Line %d (%ldc%ldw%ldh%x): \""), ili, pli->_cch,
  3060. pli->_xWidth + pli->_xLineOverhang, pli->_yHeight, pli->_bFlags);
  3061. cch = wcslen(rgch);
  3062. cch += GetLineText(ili, rgch + cch, CchOfCb(sizeof(rgch)) - cch - 4);
  3063. rgch[cch++] = TEXT('\"');
  3064. rgch[cch] = TEXT('\0');
  3065. #ifdef UNICODE
  3066. // TraceTag needs to take UNICODE...
  3067. #else
  3068. TRACEINFOSZ(TRCSEVINFO, rgch);
  3069. #endif
  3070. }
  3071. }
  3072. /*
  3073. * CDisplayML::CheckView()
  3074. *
  3075. * @mfunc
  3076. * DEBUG routine that checks coherence between _iliFirstVisible,
  3077. * _cpFirstVisible, and _dyFirstVisible
  3078. */
  3079. void CDisplayML::CheckView()
  3080. {
  3081. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::CheckView");
  3082. LONG yHeight;
  3083. VerifyFirstVisible(&yHeight);
  3084. if(yHeight != _yScroll + _dyFirstVisible)
  3085. {
  3086. Tracef(TRCSEVINFO, "sigma CLine._yHeight = %ld, CDisplay.yFirstLine = %ld", yHeight, _yScroll + _dyFirstVisible);
  3087. AssertSz(FALSE, "CLine._yHeight != VIEW.yFirstLine");
  3088. }
  3089. }
  3090. /*
  3091. * CDisplayML::VerifyFirstVisible(pHeight)
  3092. *
  3093. * @mfunc
  3094. * DEBUG routine that checks coherence between _iliFirstVisible
  3095. * and _cpFirstVisible
  3096. *
  3097. * @rdesc TRUE if things are hunky dory; FALSE otherwise
  3098. */
  3099. BOOL CDisplayML::VerifyFirstVisible(
  3100. LONG *pHeight)
  3101. {
  3102. LONG cchSum;
  3103. LONG ili = _iliFirstVisible;
  3104. CLine const *pli = Elem(0);
  3105. LONG yHeight;
  3106. for(cchSum = yHeight = 0; ili--; pli++)
  3107. {
  3108. cchSum += pli->_cch;
  3109. yHeight += pli->GetHeight();
  3110. }
  3111. if(pHeight)
  3112. *pHeight = yHeight;
  3113. if(cchSum != _cpFirstVisible)
  3114. {
  3115. Tracef(TRCSEVINFO, "sigma CLine._cch = %ld, CDisplay.cpFirstVisible = %ld", cchSum, _cpMin);
  3116. AssertSz(FALSE, "sigma CLine._cch != VIEW.cpMin");
  3117. return FALSE;
  3118. }
  3119. return TRUE;
  3120. }
  3121. #endif // DEBUG