Leaked source code of windows server 2003
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.

895 lines
22 KiB

  1. /*
  2. * LINE.CPP
  3. *
  4. * Purpose:
  5. * CLine class
  6. *
  7. * Authors:
  8. * RichEdit 1.0 code: David R. Fulmer
  9. * Christian Fortini (initial conversion to C++)
  10. * Murray Sargent
  11. *
  12. * Copyright (c) 1995-2000 Microsoft Corporation. All rights reserved.
  13. */
  14. #include "_common.h"
  15. #include "_line.h"
  16. #include "_measure.h"
  17. #include "_render.h"
  18. #include "_disp.h"
  19. #include "_dispml.h"
  20. #include "_edit.h"
  21. ASSERTDATA
  22. extern BOOL g_OLSBusy;
  23. /*
  24. * CLine::Measure(&me, uiFlags, pliTarget)
  25. *
  26. * @mfunc
  27. * Computes line break (based on target device) and fills
  28. * in this CLine with resulting metrics on rendering device
  29. *
  30. * @rdesc
  31. * TRUE if OK
  32. *
  33. * @devnote
  34. * me is moved past line (to beginning of next line). Note: CLock is
  35. * needed in the main four routines (Measure, MeasureText, CchFromUp,
  36. * and RenderLine), since they use the global (shared) fc().GetCcs()
  37. * facility and may use the LineServices global g_plsc and g_pols.
  38. */
  39. BOOL CLine::Measure(
  40. CMeasurer& me, //@parm Measurer pointing at text to measure
  41. UINT uiFlags, //@parm Flags
  42. CLine * pliTarget) //@parm Returns target-device line metrics (optional)
  43. {
  44. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLine::Measure");
  45. CLock lock;
  46. BOOL fFirstInPara = uiFlags & MEASURE_FIRSTINPARA;
  47. BOOL fMultiLine = me.GetPdp()->IsMultiLine();
  48. BOOL fRet;
  49. if(fMultiLine && fFirstInPara && me.GetPrevChar() == VT)
  50. {
  51. fFirstInPara = FALSE;
  52. uiFlags &= ~MEASURE_FIRSTINPARA;
  53. }
  54. me.NewLine(fFirstInPara);
  55. if(fFirstInPara)
  56. me._li._fFirstInPara = TRUE;
  57. BYTE bNumber = me._wNumber < 256 // Store current para # offset
  58. ? me._wNumber : 255;
  59. me._li._bNumber = bNumber;
  60. me._fMeasure = TRUE;
  61. //REVIEW (keithcu) uiFlags aren't needed in LS model? Can I remove
  62. //from the other model, too?
  63. #ifndef NOLINESERVICES
  64. COls * pols = me.GetPols(); // Try for LineServices object
  65. if(pols)
  66. { // Got it: use LineServices
  67. fRet = pols->MeasureLine(pliTarget);
  68. g_OLSBusy = FALSE;
  69. }
  70. else // LineServices not active
  71. #endif
  72. fRet = me.MeasureLine(uiFlags, pliTarget);
  73. if(!fRet)
  74. return FALSE;
  75. *this = me._li; // Copy over line info
  76. if(!fMultiLine) // Single-line controls can't
  77. return TRUE; // have paragraph numbering
  78. if(IsHeadingStyle(me._pPF->_sStyle)) // Store heading number if relevant
  79. _nHeading = (BYTE)(-me._pPF->_sStyle - 1);
  80. if(me.IsInOutlineView() && me._pPF->_wEffects & PFE_COLLAPSED) // Cache collapsed bit
  81. _fCollapsed = TRUE;
  82. _bNumber = bNumber;
  83. if(_fHasEOP) // Check for new para number
  84. {
  85. const CParaFormat *pPF = me.GetPF();
  86. me._wNumber = (WORD)pPF->UpdateNumber(me._wNumber, me._pPF);
  87. }
  88. if(me.GetPrevChar() == FF)
  89. _fHasFF = TRUE;
  90. return TRUE;
  91. }
  92. /*
  93. * CLine::Render(&re, fLastLine)
  94. *
  95. * @mfunc
  96. * Render visible part of this line
  97. *
  98. * @rdesc
  99. * TRUE iff successful
  100. *
  101. * @devnote
  102. * re is moved past line (to beginning of next line).
  103. * FUTURE: the RenderLine functions return success/failure.
  104. * Could do something on failure, e.g., be specific and fire
  105. * appropriate notifications like out of memory.
  106. */
  107. BOOL CLine::Render(
  108. CRenderer& re, //@parm Renderer to use
  109. BOOL fLastLine) //@parm TRUE iff last line in layout
  110. {
  111. if(_fCollapsed) // Line is collapsed in Outline view
  112. {
  113. re.Move(_cch); // Bypass line
  114. return TRUE;
  115. }
  116. BOOL fRet;
  117. CLock lock;
  118. POINTUV pt = re.GetCurPoint();
  119. #ifndef NOLINESERVICES
  120. COls *pols = re.GetPols(); // Try for LineServices object
  121. if(pols)
  122. {
  123. fRet = pols->RenderLine(*this, fLastLine);
  124. g_OLSBusy = FALSE;
  125. }
  126. else
  127. #endif
  128. fRet = re.RenderLine(*this, fLastLine);
  129. pt.v += GetHeight(); // Advance to next line position
  130. re.SetCurPoint(pt);
  131. return fRet;
  132. }
  133. /*
  134. * CLine::CchFromUp(&me, pt, pdispdim, pHit, pcpActual)
  135. *
  136. * @mfunc
  137. * Computes cch corresponding to x position in a line.
  138. * Used for hit testing.
  139. *
  140. * @rdesc
  141. * cch found up to the x coordinate x
  142. *
  143. * @devnote
  144. * me is moved to the cp at the cch offset returned
  145. */
  146. LONG CLine::CchFromUp(
  147. CMeasurer& me, //@parm Measurer position at start of line
  148. POINTUV pt, //@parm pt.u is u coord to search for
  149. CDispDim*pdispdim, //@parm Returns display dimensions
  150. HITTEST *phit, //@parm Returns hit type at x
  151. LONG *pcpActual) const //@parm actual CP mouse is above
  152. {
  153. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLine::CchFromUp");
  154. CLock lock;
  155. const BOOL fFirst = _fFirstInPara;
  156. *phit = HT_Text;
  157. LONG cpActual = me.GetCp();
  158. CDispDim dispdim;
  159. me._li = *this;
  160. *phit = me.HitTest(pt.u);
  161. me._li._cch = 0; // Default zero count
  162. if(*phit == HT_Text || *phit == HT_RightOfText) // To right of left margin
  163. {
  164. me.NewLine(*this);
  165. #ifndef NOLINESERVICES
  166. COls *pols = me.GetPols(); // Try for LineServices object
  167. if(pols) // Got it: use LineServices
  168. {
  169. pols->CchFromUp(pt, &dispdim, &cpActual);
  170. g_OLSBusy = FALSE;
  171. }
  172. else
  173. #endif
  174. if(me.Measure(me.DUtoLU(pt.u - _upStart), _cch,
  175. MEASURE_BREAKBEFOREWIDTH | MEASURE_IGNOREOFFSET
  176. | (fFirst ? MEASURE_FIRSTINPARA : 0)) >= 0)
  177. {
  178. LONG dupBefore = me._li._dup;
  179. cpActual = me.GetCp();
  180. if (me._li._cch < _cch)
  181. {
  182. LONG dup = pt.u - _upStart - dupBefore;
  183. dispdim.dup = me._dupAddLast;
  184. if(dup > dispdim.dup / 2 ||
  185. dup > W32->GetDupSystemFont()/2 && me.GetChar() == WCH_EMBEDDING)
  186. {
  187. me.Move(1);
  188. me._li._cch++;
  189. me._li._dup += dispdim.dup;
  190. }
  191. }
  192. }
  193. me._rpCF.AdjustForward();
  194. if(cpActual < me.GetCp() || pt.u >= _upStart + _dup)
  195. me._rpCF.AdjustBackward();
  196. DWORD dwEffects = me.GetCF()->_dwEffects;
  197. if(dwEffects & CFE_LINK)
  198. {
  199. if(cpActual < me.GetTextLength())
  200. *phit = HT_Link;
  201. }
  202. else if(dwEffects & CFE_ITALIC)
  203. *phit = HT_Italic;
  204. }
  205. if (pdispdim)
  206. *pdispdim = dispdim;
  207. if (pcpActual)
  208. *pcpActual = cpActual;
  209. return me._li._cch;
  210. }
  211. /*
  212. * CLine::UpFromCch(&me, cch, taMode, pdispdim, pdy)
  213. *
  214. * @mfunc
  215. * Measures cch characters starting from this text ptr, returning
  216. * the width measured and setting yOffset = y offset relative to
  217. * top of line and dx = halfwidth of character at me.GetCp() + cch.
  218. * Used for caret placement and object location. pdx returns offset
  219. * into the last char measured (at me.GetCp + cch) if taMode includes
  220. * TA_CENTER (dx = half the last char width) or TA_RIGHT (dx = whole
  221. * char width). pdy returns the vertical offset relative to the top
  222. * of the line if taMode includes TA_BASELINE or TA_BOTTOM.
  223. *
  224. * @rdesc
  225. * width of measured text
  226. *
  227. * @devnote
  228. * me may be moved.
  229. */
  230. LONG CLine::UpFromCch(
  231. CMeasurer& me, //@parm Measurer pointing at text to measure
  232. LONG cch, //@parm Max cch to measure
  233. UINT taMode, //@parm Text-align mode
  234. CDispDim * pdispdim, //@parm display dimensions
  235. LONG * pdy) const //@parm dy offset due to taMode
  236. {
  237. CLock lock;
  238. LONG dup;
  239. BOOL fPols = FALSE;
  240. CDispDim dispdim;
  241. LONG dy = 0;
  242. #ifndef NOLINESERVICES
  243. COls *pols = me.GetPols(); // Try for LineServices object
  244. if(pols)
  245. { // Got it: use LineServices
  246. if(cch)
  247. taMode &= ~TA_STARTOFLINE; // Not start of line
  248. if(cch != _cch)
  249. taMode &= ~TA_ENDOFLINE; // Not end of line
  250. dup = pols->MeasureText(cch, taMode, &dispdim);
  251. fPols = TRUE;
  252. g_OLSBusy = FALSE;
  253. }
  254. else
  255. #endif
  256. {
  257. dup = me.MeasureText(cch) + _upStart;
  258. dispdim.dup = me._dupAddLast;
  259. }
  260. if(taMode != TA_TOP)
  261. {
  262. // Check for vertical calculation request
  263. if(taMode & TA_BASELINE) // Matches TA_BOTTOM and
  264. { // TA_BASELINE
  265. if(!_fCollapsed)
  266. {
  267. dy = _dvpHeight;
  268. AssertSz(_dvpHeight != -1, "control has no height; used to use default CHARFORMAT");
  269. if((taMode & TA_BASELINE) == TA_BASELINE)
  270. {
  271. dy -= _dvpDescent; // Need "== TA_BASELINE" to
  272. if(!_dvpDescent) // distinguish from TA_BOTTOM
  273. dy--; // Compensate for weird fonts
  274. }
  275. }
  276. }
  277. }
  278. LONG dupAdd = 0;
  279. if((taMode & TA_CENTER) == TA_CENTER)
  280. dupAdd = dispdim.dup / 2;
  281. else if (taMode & TA_RIGHT)
  282. dupAdd = dispdim.dup;
  283. if (dispdim.lstflow == lstflowWS && (taMode & TA_LOGICAL))
  284. dupAdd = -dupAdd;
  285. dup += dupAdd;
  286. if (pdispdim)
  287. *pdispdim = dispdim;
  288. if (pdy)
  289. *pdy = dy;
  290. return max(dup, 0);
  291. }
  292. /*
  293. * CLine::GetHeight()
  294. *
  295. * @mfunc
  296. * Get line height unless in outline mode and collasped, in
  297. * which case get 0.
  298. *
  299. * @rdesc
  300. * Line height (_yHeight), unless in outline mode and collapsed,
  301. * in which case 0.
  302. */
  303. LONG CLine::GetHeight() const
  304. {
  305. if (_fCollapsed)
  306. return 0;
  307. return IsNestedLayout() ? _plo->_dvp : _dvpHeight;
  308. }
  309. /*
  310. * CLine::GetDescent()
  311. *
  312. * @mfunc
  313. * Return descent of line. Assumed not to be collapsed
  314. *
  315. * @rdesc
  316. */
  317. LONG CLine::GetDescent() const
  318. {
  319. return IsNestedLayout() ? 0 : _dvpDescent;
  320. }
  321. BOOL CLine::IsEqual(CLine& li)
  322. {
  323. return _upStart == li._upStart &&
  324. _plo == li._plo && //checks _yHeight, _yDescent OR _plo
  325. _dup == li._dup &&
  326. _cch == li._cch;
  327. }
  328. // ===================== CLinePtr: Line Run Pointer ==========================
  329. CLinePtr::CLinePtr(CDisplay *pdp)
  330. {
  331. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::CLinePtr");
  332. _pdp = pdp;
  333. _pLine = NULL;
  334. _pdp->InitLinePtr(* this);
  335. }
  336. void CLinePtr::Init (CLine & line)
  337. {
  338. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::Init");
  339. _pRuns = 0;
  340. _pLine = &line;
  341. _iRun = 0;
  342. _ich = 0;
  343. }
  344. void CLinePtr::Init (CLineArray & line_arr)
  345. {
  346. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::Init");
  347. _pRuns = (CRunArray *) & line_arr;
  348. _iRun = 0;
  349. _ich = 0;
  350. }
  351. void CLinePtr::Set(
  352. LONG iRun,
  353. LONG ich,
  354. CLineArray *pla)
  355. {
  356. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::RpSet");
  357. // See if this is a multi-line ptr
  358. if(_pRuns)
  359. {
  360. if(pla)
  361. {
  362. CRunPtr<CLine>::SetRun(0, 0); // Be sure current state is valid
  363. _pRuns = (CRunArray *)pla; // for new _pRuns
  364. }
  365. CRunPtr<CLine>::SetRun(iRun, ich); // Now set to desired run & ich
  366. }
  367. else
  368. {
  369. // single line, just reinit and set _ich
  370. AssertSz(iRun == 0, "CLinePtr::Set() - single line and iRun != 0");
  371. _pdp->InitLinePtr(* this); // to line 0
  372. _ich = ich;
  373. }
  374. }
  375. // Move runptr by a certain number of cch/runs
  376. BOOL CLinePtr::Move(
  377. LONG cch)
  378. {
  379. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::RpMove");
  380. // See if this is a multi-line ptr
  381. if(_pRuns)
  382. return (cch == CRunPtr<CLine>::Move(cch));
  383. return MoveSL(cch);
  384. }
  385. BOOL CLinePtr::operator --(int)
  386. {
  387. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::operator --");
  388. return _pRuns ? PrevRun() : OperatorPostDeltaSL(-1);
  389. }
  390. BOOL CLinePtr::operator ++(int)
  391. {
  392. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::operator ++");
  393. return _pRuns ? NextRun() : OperatorPostDeltaSL(+1);
  394. }
  395. /*
  396. * CLinePtr::MoveSL(cch)
  397. *
  398. * @mfunc
  399. * move this line pointer forward or backward on the line
  400. *
  401. * @rdesc
  402. * TRUE iff could Move cch chars within current line
  403. */
  404. BOOL CLinePtr::MoveSL(
  405. LONG cch) //@parm signed count of chars to Move by
  406. {
  407. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::RpMoveSL");
  408. Assert(!_pRuns);
  409. if(!_pLine)
  410. return FALSE;
  411. _ich += cch;
  412. if(_ich < 0)
  413. {
  414. _ich = 0;
  415. return FALSE;
  416. }
  417. if(_ich > _pLine->_cch)
  418. {
  419. _ich = _pLine->_cch;
  420. return FALSE;
  421. }
  422. return TRUE;
  423. }
  424. /*
  425. * CLinePtr::OperatorPostDeltaSL(Delta)
  426. *
  427. * @mfunc
  428. * Implement line-ptr ++ and -- operators for single-line case
  429. *
  430. * @rdesc
  431. * TRUE iff this line ptr is valid
  432. */
  433. BOOL CLinePtr::OperatorPostDeltaSL(
  434. LONG Delta) //@parm 1 for ++ and -1 for --
  435. {
  436. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::OperatorPostDeltaSL");
  437. AssertSz(_iRun <= 1 && !_pRuns,
  438. "LP::++: inconsistent line ptr");
  439. if(_iRun == -Delta) // Operation validates an
  440. { // invalid line ptr by moving
  441. _pdp->InitLinePtr(* this); // to line 0
  442. return TRUE;
  443. }
  444. _iRun = Delta; // Operation invalidates this line
  445. _ich = 0; // ptr (if it wasn't already)
  446. return FALSE;
  447. }
  448. CLine * CLinePtr::operator ->() const
  449. {
  450. return _pRuns ? (CLine *)_pRuns->Elem(_iRun) : _pLine;
  451. }
  452. CLine * CLinePtr::GetLine() const
  453. {
  454. return _pRuns ? (CLine *)_pRuns->Elem(_iRun) : _pLine;
  455. }
  456. CLine & CLinePtr::operator *() const
  457. {
  458. return *(_pRuns ? (CLine *)_pRuns->Elem(_iRun) : _pLine);
  459. }
  460. CLine & CLinePtr::operator [](LONG dRun)
  461. {
  462. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::operator []");
  463. if(_pRuns)
  464. return *(CLine *)CRunPtr<CLine>::GetRun(dRun);
  465. AssertSz(dRun + _iRun == 0 ,
  466. "LP::[]: inconsistent line ptr");
  467. return *(CLine *)CRunPtr<CLine>::GetRun(_iRun);
  468. }
  469. BOOL CLinePtr::IsValid() const
  470. {
  471. return !_pRuns ? _pLine != NULL : CRunPtrBase::IsValid();
  472. }
  473. /*
  474. * CLinePtr::SetCp(cp, fAtEnd, lNest)
  475. *
  476. * @mfunc
  477. * Set this line ptr to cp allowing for ambigous cp and taking advantage
  478. * of _cpFirstVisible and _iliFirstVisible
  479. *
  480. * @rdesc
  481. * TRUE iff able to set to cp
  482. */
  483. BOOL CLinePtr::SetCp(
  484. LONG cp, //@parm Position to set this line ptr to
  485. BOOL fAtEnd, //@parm If ambiguous cp: if fAtEnd = TRUE, set this
  486. // line ptr to end of prev line; else to line start
  487. LONG lNest) //@parm Set to deep CLine in nested layouts
  488. {
  489. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::RpSetCp");
  490. _ich = 0;
  491. if(!_pRuns)
  492. {
  493. // This is a single line so just go straight to the single
  494. // line Move logic. It is important to note that the
  495. // first visible character is irrelevent to the cp Move
  496. // for single line displays.
  497. return MoveSL(cp);
  498. }
  499. BOOL fRet;
  500. LONG cpFirstVisible = _pdp->GetFirstVisibleCp();
  501. if(cp > cpFirstVisible / 2)
  502. { // cpFirstVisible closer than 0
  503. _iRun = _pdp->GetFirstVisibleLine();
  504. fRet = Move(cp - cpFirstVisible);
  505. }
  506. else
  507. fRet = (cp == CRunPtr<CLine>::BindToCp(cp));// Start from 0
  508. if(lNest)
  509. {
  510. CLayout *plo;
  511. while(plo = GetLine()->GetPlo())
  512. {
  513. LONG cch = _ich;
  514. if(plo->IsTableRow())
  515. {
  516. if(cch <= 2 && lNest == 1) // At start of table row:
  517. break; // leave this rp there
  518. cch -= 2; // Bypass table row start code
  519. }
  520. Set(0, 0, (CLineArray *)plo); // Goto start of layout plo
  521. Move(cch); // Move to parent _ich
  522. }
  523. }
  524. if(fAtEnd) // Ambiguous-cp caret position
  525. AdjustBackward(); // belongs at prev EOL
  526. return fRet;
  527. }
  528. /*
  529. * CLinePtr::FindParagraph(fForward)
  530. *
  531. * @mfunc
  532. * Move this line ptr to paragraph (fForward) ? end : start,
  533. * and return change in cp
  534. *
  535. * @rdesc
  536. * change in cp
  537. */
  538. LONG CLinePtr::FindParagraph(
  539. BOOL fForward) //@parm TRUE move to para end; else to para start
  540. {
  541. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::FindParagraph");
  542. LONG cch;
  543. CLine * pli = GetLine();
  544. if(!fForward) // Go to para start
  545. {
  546. cch = 0; // Default already at para start
  547. if (_ich != pli->_cch ||
  548. !(pli->_fHasEOP)) // It isn't at para start
  549. {
  550. cch = -_ich; // Go to start of current line
  551. while(!(pli->_fFirstInPara) && _iRun > 0)
  552. {
  553. pli--;
  554. _iRun--;
  555. cch -= pli->_cch; // Subtract # chars in line
  556. }
  557. _ich = 0; // Leave *this at para start
  558. }
  559. }
  560. else // Go to para end
  561. {
  562. cch = GetCchLeft(); // Go to end of current line
  563. if(!_pRuns)
  564. return cch; // Single line
  565. LONG cLine = _pRuns->Count();
  566. BOOL fNested = _pRuns->Elem(0) != ((CDisplayML *)_pdp)->Elem(0);
  567. while((_iRun < cLine - 1 || !fNested &&
  568. _pdp->WaitForRecalcIli(_iRun + 1))
  569. && !(pli->_fHasEOP))
  570. {
  571. pli++; // Go to start of next line
  572. _iRun++;
  573. cch += pli->_cch; // Add # chars in line
  574. }
  575. _ich = pli->_cch; // Leave *this at para end
  576. }
  577. return cch;
  578. }
  579. /*
  580. * CLinePtr::GetAdjustedLineLength()
  581. *
  582. * @mfunc returns length of line _without_ EOP markers
  583. *
  584. * @rdesc LONG; length of line
  585. */
  586. LONG CLinePtr::GetAdjustedLineLength()
  587. {
  588. CLine * pline = GetLine();
  589. return pline->_cch - pline->_cchEOP;
  590. }
  591. /*
  592. * CLinePtr::GetCchLeft()
  593. *
  594. * @mfunc
  595. * Calculate length of text left in run starting at the current cp.
  596. * Complements GetIch(), which is length of text up to this cp.
  597. *
  598. * @rdesc
  599. * length of text so calculated
  600. */
  601. LONG CLinePtr::GetCchLeft() const
  602. {
  603. return _pRuns ? CRunPtrBase::GetCchLeft() : _pLine->_cch - _ich;
  604. }
  605. /*
  606. * CLinePtr::GetNumber()
  607. *
  608. * @mfunc
  609. * Get paragraph number
  610. *
  611. * @rdesc
  612. * paragraph number
  613. */
  614. WORD CLinePtr::GetNumber()
  615. {
  616. if(!IsValid())
  617. return 0;
  618. _pLine = GetLine();
  619. if(!_iRun && _pLine->_bNumber > 1)
  620. _pLine->_bNumber = 1;
  621. return _pLine->_bNumber;
  622. }
  623. /*
  624. * CLinePtr::CountPages(&cPage, cchMax, cp, cchText)
  625. *
  626. * @mfunc
  627. * Count characters up to <p cPages> pages away or <p cchMax> chars,
  628. * whichever comes first. If the target page and <p cchMax> are both
  629. * beyond the corresponding end of the document, count up thru the
  630. * closest page. The direction of counting is determined by the sign
  631. * of <p cPage>. To count without being limited by <p cchMax>, set it
  632. * equal to tomForward. An initial partial page counts as a page.
  633. *
  634. * @rdesc
  635. * Return the signed cch counted and set <p cPage> equal to count of
  636. * pages actually counted. If no pages are allocated, the text is
  637. * treated as a single page. If <p cPage> = 0, -cch to the start of the
  638. * current page is returned. If <p cPage> <gt> 0 and cp is at the end
  639. * of the document, 0 is returned.
  640. *
  641. * @devnote
  642. * The maximum count capability is included to be able to count units in
  643. * a range.
  644. *
  645. * @todo
  646. * EN_PAGECHANGE
  647. */
  648. LONG CLinePtr::CountPages (
  649. LONG &cPage, //@parm Count of pages to get cch for
  650. LONG cchMax, //@parm Maximum char count
  651. LONG cp, //@parm CRchTxtPtr::GetCp()
  652. LONG cchText) const
  653. {
  654. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CLinePtr::CountPages");
  655. if(!_pdp->IsInPageView())
  656. {
  657. cPage = 0;
  658. return tomBackward; // Signal error
  659. }
  660. Assert(IsValid());
  661. LONG cch;
  662. LONG j = cPage;
  663. CLine *pli = (CLine *)GetLine(); // Not NULL since lines exist
  664. if(cPage < 0) // Try to count backward cPage pages
  665. {
  666. // TODO: eliminate cchText and cp (currently only used for validation)
  667. Assert(cchMax <= cp); // Don't undershoot
  668. for(cch = _ich; j && cch <= cchMax; cch += pli->_cch)
  669. {
  670. if(pli->_fFirstOnPage && cch) // !cch prevents counting current
  671. j++; // page if at start of that page
  672. if(cch >= cchMax)
  673. {
  674. Assert(cch == cp);
  675. break; // At beginning of doc, so done
  676. }
  677. if (!j)
  678. break; // Done counting backward
  679. pli--;
  680. VALIDATE_PTR(pli);
  681. }
  682. cPage -= j; // Discount any pages not counted
  683. return -cch;
  684. }
  685. Assert(cPage > 0 && cchMax <= cchText - cp);
  686. for(cch = GetCchLeft(); cch < cchMax; cch += pli->_cch)
  687. {
  688. pli++;
  689. VALIDATE_PTR(pli);
  690. if(pli->_fFirstOnPage && cch) // !cch prevents counting current
  691. { // page if at start of that page
  692. j--;
  693. if(!j)
  694. break;
  695. }
  696. }
  697. cPage -= j; // Discount any pages not counted
  698. return cch;
  699. }
  700. /*
  701. * CLinePtr::FindPage (pcpMin, pcpMost, cpMin, cch, cchText)
  702. *
  703. * @mfunc
  704. * Set *<p pcpMin> = closest page cpMin <lt>= range cpMin, and
  705. * set *<p pcpMost> = closest page cpMost <gt>= range cpMost
  706. *
  707. * @devnote
  708. * This routine plays a role analogous to CTxtRange::FindParagraph
  709. * (pcpMin, pcpMost), but needs extra arguments since this line ptr does
  710. * not know the range cp's. This line ptr is located at the range active
  711. * end, which is determined by the range's signed length <p cch> in
  712. * conjunction with <p cpMin>. See also the very similar function
  713. * CRunPtrBase::FindRun(). The differences seem to make a separate
  714. * encoding simpler.
  715. */
  716. void CLinePtr::FindPage (
  717. LONG *pcpMin, //@parm Out parm for bounding-page cpMin
  718. LONG *pcpMost, //@parm Out parm for bounding-page cpMost
  719. LONG cpMin, //@parm Range cpMin
  720. LONG cch, //@parm Range signed length
  721. LONG cchText) //@parm Story length
  722. {
  723. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CLinePtr::FindPage");
  724. Assert(_pdp->IsMultiLine() && _pdp->IsInPageView());
  725. LONG cp;
  726. BOOL fMove; // Controls Move for pcpMost
  727. LONG i;
  728. CLine *pli;
  729. AdjustForward(); // Select forward line
  730. if(pcpMin)
  731. { // If cch != 0, rp is sure to end up
  732. fMove = cch; // at cpMin, so pcpMost needs advance
  733. if(cch > 0) // rp is at cpMost, so move it to
  734. Move(-cch); // cpMin
  735. cp = cpMin - _ich; // Subtract off line offset in this run
  736. pli = (CLine *)GetLine();
  737. for(i = GetLineIndex(); i > 0 && !pli->_fFirstOnPage; i--)
  738. {
  739. pli--;
  740. cp -= pli->_cch;
  741. }
  742. *pcpMin = cp;
  743. }
  744. else
  745. fMove = cch < 0; // Need to advance to get pcpMost
  746. if(pcpMost)
  747. {
  748. LONG cLine = ((CDisplayML *)_pdp)->Count();
  749. cch = abs(cch);
  750. if(fMove) // Advance to cpMost = cpMin + cch,
  751. Move(cch); // i.e., range's cpMost
  752. cp = cpMin + cch;
  753. pli = (CLine *)GetLine();
  754. i = GetLineIndex();
  755. if(pcpMin && cp == *pcpMin) // Expand IP to next page
  756. {
  757. Assert(!_ich);
  758. cp += pli->_cch; // Include first line even if it starts
  759. pli++; // a new page (pli->_fFirstOnPage = 1)
  760. i++;
  761. }
  762. else if (_ich)
  763. { // If not at start of line, add
  764. cp += GetCchLeft(); // remaining cch in run to cpMost, and
  765. pli++; // skip to next line
  766. i++;
  767. }
  768. while(i < cLine && !pli->_fFirstOnPage)
  769. {
  770. cp += pli->_cch; // Add in next line's
  771. pli++;
  772. i++;
  773. }
  774. *pcpMost = cp;
  775. }
  776. }