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.

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