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.

695 lines
16 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-1998 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 "_edit.h"
  20. ASSERTDATA
  21. /*
  22. * CLine::Measure(&me, cchMax, xWidth, uiFlags, pliTarget)
  23. *
  24. * @mfunc
  25. * Computes line break (based on target device) and fills
  26. * in this CLine with resulting metrics on rendering device
  27. *
  28. * @rdesc
  29. * TRUE if OK
  30. *
  31. * @devnote
  32. * me is moved past line (to beginning of next line). Note: CLock is
  33. * needed in the main four routines (Measure, MeasureText, CchFromXPos,
  34. * and RenderLine), since they use the global (shared) fc().GetCcs()
  35. * facility and may use the LineServices global g_plsc and g_pols.
  36. */
  37. BOOL CLine::Measure(
  38. CMeasurer& me, //@parm Measurer pointing at text to measure
  39. LONG cchMax, //@parm Max cch to measure
  40. LONG xWidth, //@parm Width of line in device units
  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. if(!(uiFlags & MEASURE_DONTINIT))
  55. me.NewLine(fFirstInPara);
  56. else if(fFirstInPara)
  57. me._li._bFlags |= fliFirstInPara;
  58. BYTE bNumber = me._wNumber < 256 // Store current para # offset
  59. ? me._wNumber : 255;
  60. me._li._bNumber = bNumber;
  61. #ifdef LINESERVICES
  62. CMeasurer *pmeSave;
  63. COls * pols = me.GetPols(&pmeSave); // Try for LineServices object
  64. if(pols) // Got it: use LineServices
  65. {
  66. fRet = pols->MeasureLine(xWidth, pliTarget);
  67. pols->SetMeasurer(pmeSave); // Restore previous pme
  68. }
  69. else // LineServices not active
  70. #endif
  71. fRet = me.MeasureLine(cchMax, xWidth, uiFlags, pliTarget);
  72. if(!fRet)
  73. return FALSE;
  74. *this = me._li; // Copy over line info
  75. if(!fMultiLine) // Single-line controls can't
  76. return TRUE; // have paragraph numbering
  77. if(me.IsInOutlineView())
  78. {
  79. if(IsHeadingStyle(me._pPF->_sStyle)) // Store heading number if relevant
  80. _nHeading = (BYTE)(-me._pPF->_sStyle - 1);
  81. if(me._pPF->_wEffects & PFE_COLLAPSED) // Cache collapsed bit
  82. _fCollapsed = TRUE;
  83. }
  84. _bNumber = bNumber;
  85. if(_bFlags & fliHasEOP) // Check for new para number
  86. {
  87. const CParaFormat *pPF = me.GetPF();
  88. me._wNumber = (WORD)pPF->UpdateNumber(me._wNumber, me._pPF);
  89. _fNextInTable = pPF->InTable() && me.GetCp() < me.GetTextLength();
  90. }
  91. return TRUE;
  92. }
  93. /*
  94. * CLine::Render(&re)
  95. *
  96. * @mfunc
  97. * Render visible part of this line
  98. *
  99. * @rdesc
  100. * TRUE iff successful
  101. *
  102. * @devnote
  103. * re is moved past line (to beginning of next line).
  104. * FUTURE: the RenderLine functions return success/failure.
  105. * Could do something on failure, e.g., be specific and fire
  106. * appropriate notifications like out of memory.
  107. */
  108. BOOL CLine::Render(
  109. CRenderer& re) //@parm Renderer to use
  110. {
  111. if(_fCollapsed) // Line is collapsed in Outline view
  112. {
  113. re.Advance(_cch); // Bypass line
  114. return TRUE;
  115. }
  116. BOOL fRet;
  117. CLock lock;
  118. POINT pt = re.GetCurPoint();
  119. #ifdef LINESERVICES
  120. CMeasurer *pmeSave;
  121. COls *pols = re.GetPols(&pmeSave); // Try for LineServices object
  122. if(pols)
  123. {
  124. fRet = pols->RenderLine(*this);
  125. pols->SetMeasurer(pmeSave); // Restore previous pme
  126. }
  127. else
  128. #endif
  129. fRet = re.RenderLine(*this);
  130. pt.y += GetHeight(); // Advance to next line position
  131. re.SetCurPoint(pt);
  132. return fRet;
  133. }
  134. /*
  135. * CLine::CchFromXPos(&me, x, pdispdim, pHit)
  136. *
  137. * @mfunc
  138. * Computes cch corresponding to x position in a line.
  139. * Used for hit testing.
  140. *
  141. * @rdesc
  142. * cch found up to the x coordinate x
  143. *
  144. * @devnote
  145. * me is moved to the cp at the cch offset returned
  146. */
  147. LONG CLine::CchFromXpos(
  148. CMeasurer& me, //@parm Measurer position at start of line
  149. POINT pt, //@parm pt.x is x coord to search for
  150. CDispDim*pdispdim, //@parm Returns display dimensions
  151. HITTEST *phit, //@parm Returns hit type at x
  152. LONG *pcpActual) const //@parm actual CP above with display dimensions
  153. {
  154. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLine::CchFromXpos");
  155. #ifdef Boustrophedon
  156. //if(_pPF->_wEffects & PFE_BOUSTROPHEDON)
  157. {
  158. RECT rcView;
  159. me.GetPed()->_pdp->GetViewRect(rcView, NULL);
  160. pt.x = rcView.right - pt.x;
  161. }
  162. #endif
  163. CLock lock;
  164. const BOOL fFirst = _bFlags & fliFirstInPara;
  165. *phit = HT_Text;
  166. LONG cpActual = me.GetCp();
  167. CDispDim dispdim;
  168. me._li = *this;
  169. me._li._cch = 0; // Default zero count
  170. *phit = me.HitTest(pt.x);
  171. if(*phit == HT_Text || *phit == HT_RightOfText) // To right of left margin
  172. {
  173. me.NewLine(fFirst);
  174. #ifdef LINESERVICES
  175. CMeasurer *pmeSave;
  176. COls *pols = me.GetPols(&pmeSave);// Try for LineServices object
  177. if(pols) // Got it: use LineServices
  178. {
  179. pols->CchFromXpos(pt, &dispdim, &cpActual);
  180. pols->SetMeasurer(pmeSave); // Restore previous pme
  181. }
  182. else
  183. #endif
  184. if(me.Measure(pt.x - _xLeft, _cch,
  185. MEASURE_BREAKBEFOREWIDTH | MEASURE_IGNOREOFFSET
  186. | (fFirst ? MEASURE_FIRSTINPARA : 0)) >= 0)
  187. {
  188. LONG xWidthBefore = me._li._xWidth;
  189. cpActual = me.GetCp();
  190. if (me._li._cch < _cch)
  191. {
  192. dispdim.dx = me._xAddLast;
  193. if (pt.x - _xLeft > xWidthBefore + dispdim.dx / 2)
  194. {
  195. me.Advance(1);
  196. me._li._cch++;
  197. me._li._xWidth += dispdim.dx;
  198. }
  199. }
  200. }
  201. me._rpCF.AdjustBackward();
  202. DWORD dwEffects = me.GetCF()->_dwEffects;
  203. if(dwEffects & CFE_LINK)
  204. *phit = HT_Link;
  205. else if(dwEffects & CFE_ITALIC)
  206. *phit = HT_Italic;
  207. #ifdef UNICODE_SURROGATES
  208. // Until we support UTF-16 surrogate characters, don't allow hit in
  209. // middle of a surrogate pair
  210. if(IN_RANGE(0xDC00, me.GetChar(), 0xDFFF))
  211. {
  212. me.Advance(1);
  213. me._li._cch++;
  214. }
  215. #endif
  216. }
  217. if (pdispdim)
  218. *pdispdim = dispdim;
  219. if (pcpActual)
  220. *pcpActual = cpActual;
  221. return me._li._cch;
  222. }
  223. /*
  224. * CLine::XposFromCch(&me, cch, taMode, pdispdim, pdy)
  225. *
  226. * @mfunc
  227. * Measures cch characters starting from this text ptr, returning
  228. * the width measured and setting yOffset = y offset relative to
  229. * top of line and dx = halfwidth of character at me.GetCp() + cch.
  230. * Used for caret placement and object location. pdx returns offset
  231. * into the last char measured (at me.GetCp + cch) if taMode includes
  232. * TA_CENTER (dx = half the last char width) or TA_RIGHT (dx = whole
  233. * char width). pdy returns the vertical offset relative to the top
  234. * of the line if taMode includes TA_BASELINE or TA_BOTTOM.
  235. *
  236. * @rdesc
  237. * width of measured text
  238. *
  239. * @devnote
  240. * me may be moved.
  241. */
  242. LONG CLine::XposFromCch(
  243. CMeasurer& me, //@parm Measurer pointing at text to measure
  244. LONG cch, //@parm Max cch to measure
  245. UINT taMode, //@parm Text-align mode
  246. CDispDim * pdispdim, //@parm display dimensions
  247. LONG * pdy) const //@parm dy offset due to taMode
  248. {
  249. CLock lock;
  250. LONG xWidth;
  251. BOOL fPols = FALSE;
  252. CDispDim dispdim;
  253. LONG dy = 0;
  254. #ifdef LINESERVICES
  255. CMeasurer *pmeSave;
  256. COls *pols = me.GetPols(&pmeSave); // Try for LineServices object
  257. if(pols)
  258. { // Got it: use LineServices
  259. if(cch)
  260. taMode &= ~TA_STARTOFLINE; // Not start of line
  261. if(cch != _cch)
  262. taMode &= ~TA_ENDOFLINE; // Not end of line
  263. xWidth = pols->MeasureText(cch, taMode, &dispdim);
  264. pols->SetMeasurer(pmeSave); // Restore previous pme
  265. fPols = TRUE;
  266. }
  267. else
  268. #endif
  269. xWidth = me.MeasureText(cch) + _xLeft;
  270. if(taMode != TA_TOP)
  271. {
  272. // Check for vertical calculation request
  273. if(taMode & TA_BASELINE) // Matches TA_BOTTOM and
  274. { // TA_BASELINE
  275. if(!_fCollapsed)
  276. {
  277. dy = _yHeight;
  278. AssertSz(_yHeight != -1, "control has no height; used to use default CHARFORMAT");
  279. if((taMode & TA_BASELINE) == TA_BASELINE)
  280. dy -= _yDescent; // Need "== TA_BASELINE" to
  281. } // distinguish from TA_BOTTOM
  282. }
  283. // Check for horizontal calculation request
  284. if(taMode & TA_CENTER && !fPols) // If align to center or right of
  285. {
  286. if (cch == 0)
  287. dispdim.dx = me.MeasureText(1) + _xLeft - xWidth;
  288. else
  289. dispdim.dx = me._xAddLast; // char, get char width
  290. }
  291. }
  292. if (!fPols)
  293. {
  294. if((taMode & TA_CENTER) == TA_CENTER)
  295. xWidth += dispdim.dx / 2;
  296. else if (taMode & TA_RIGHT)
  297. xWidth += dispdim.dx;
  298. }
  299. if (pdispdim)
  300. *pdispdim = dispdim;
  301. if (pdy)
  302. *pdy = dy;
  303. return xWidth;
  304. }
  305. /*
  306. * CLine::GetHeight()
  307. *
  308. * @mfunc
  309. * Get line height unless in outline mode and collasped, in
  310. * which case get 0.
  311. *
  312. * @rdesc
  313. * Line height (_yHeight), unless in outline mode and collapsed,
  314. * in which case 0.
  315. */
  316. LONG CLine::GetHeight() const
  317. {
  318. return _fCollapsed ? 0 : _yHeight;
  319. }
  320. BOOL CLine::IsEqual(CLine& li)
  321. {
  322. // CF - I dont know which one is faster
  323. // MS3 - CompareMemory is certainly smaller
  324. // return !CompareMemory (this, pli, sizeof(CLine) - 4);
  325. return _xLeft == li._xLeft &&
  326. _xWidth == li._xWidth &&
  327. _yHeight == li._yHeight &&
  328. _yDescent == li._yDescent &&
  329. _cch == li._cch &&
  330. _cchWhite == li._cchWhite;
  331. }
  332. // ===================== CLinePtr: Line Run Pointer ==========================
  333. CLinePtr::CLinePtr(CDisplay *pdp)
  334. {
  335. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::CLinePtr");
  336. _pdp = pdp;
  337. _pLine = NULL;
  338. _pdp->InitLinePtr(* this);
  339. }
  340. void CLinePtr::Init (CLine & line)
  341. {
  342. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::Init");
  343. _pRuns = 0;
  344. _pLine = &line;
  345. _iRun = 0;
  346. _ich = 0;
  347. }
  348. void CLinePtr::Init (CLineArray & line_arr)
  349. {
  350. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::Init");
  351. _pRuns = (CRunArray *) & line_arr;
  352. _iRun = 0;
  353. _ich = 0;
  354. }
  355. void CLinePtr::RpSet(LONG iRun, LONG ich)
  356. {
  357. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::RpSet");
  358. // See if this is a multi-line ptr
  359. if(_pRuns)
  360. CRunPtr<CLine>::SetRun(iRun, ich);
  361. else
  362. {
  363. // single line, just reinit and set _ich
  364. AssertSz(iRun == 0, "CLinePtr::RpSet() - single line and iRun != 0");
  365. _pdp->InitLinePtr(* this); // to line 0
  366. _ich = ich;
  367. }
  368. }
  369. // Move runptr by a certain number of cch/runs
  370. BOOL CLinePtr::RpAdvanceCp(LONG cch)
  371. {
  372. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::RpAdvanceCp");
  373. // See if this is a multi-line ptr
  374. if(_pRuns)
  375. return (cch == CRunPtr<CLine>::AdvanceCp(cch));
  376. return RpAdvanceCpSL(cch);
  377. }
  378. BOOL CLinePtr::operator --(int)
  379. {
  380. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::operator --");
  381. return _pRuns ? PrevRun() : OperatorPostDeltaSL(-1);
  382. }
  383. BOOL CLinePtr::operator ++(int)
  384. {
  385. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::operator ++");
  386. return _pRuns ? NextRun() : OperatorPostDeltaSL(+1);
  387. }
  388. /*
  389. * CLinePtr::RpAdvanceCpSL(cch)
  390. *
  391. * @mfunc
  392. * move this line pointer forward or backward on the line
  393. *
  394. * @rdesc
  395. * TRUE iff could advance cch chars within current line
  396. */
  397. BOOL CLinePtr::RpAdvanceCpSL(
  398. LONG cch) //@parm signed count of chars to advance by
  399. {
  400. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::RpAdvanceCpSL");
  401. Assert(!_pRuns);
  402. if(!_pLine)
  403. return FALSE;
  404. _ich += cch;
  405. if(_ich < 0)
  406. {
  407. _ich = 0;
  408. return FALSE;
  409. }
  410. if(_ich > _pLine->_cch)
  411. {
  412. _ich = _pLine->_cch;
  413. return FALSE;
  414. }
  415. return TRUE;
  416. }
  417. /*
  418. * CLinePtr::OperatorPostDeltaSL(Delta)
  419. *
  420. * Purpose:
  421. * Implement line-ptr ++ and -- operators for single-line case
  422. *
  423. * Arguments:
  424. * Delta 1 for ++ and -1 for --
  425. *
  426. * Return:
  427. * TRUE iff this line ptr is valid
  428. */
  429. BOOL CLinePtr::OperatorPostDeltaSL(LONG Delta)
  430. {
  431. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::OperatorPostDeltaSL");
  432. AssertSz(_iRun <= 1 && !_pRuns,
  433. "LP::++: inconsistent line ptr");
  434. if(_iRun == -Delta) // Operation validates an
  435. { // invalid line ptr by moving
  436. _pdp->InitLinePtr(* this); // to line 0
  437. return TRUE;
  438. }
  439. _iRun = Delta; // Operation invalidates this line
  440. _ich = 0; // ptr (if it wasn't already)
  441. return FALSE;
  442. }
  443. CLine * CLinePtr::operator ->() const
  444. {
  445. return _pRuns ? (CLine *)_pRuns->Elem(_iRun) : _pLine;
  446. }
  447. CLine * CLinePtr::GetLine() const
  448. {
  449. return _pRuns ? (CLine *)_pRuns->Elem(_iRun) : _pLine;
  450. }
  451. CLine & CLinePtr::operator *() const
  452. {
  453. return *(_pRuns ? (CLine *)_pRuns->Elem(_iRun) : _pLine);
  454. }
  455. CLine & CLinePtr::operator [](LONG dRun)
  456. {
  457. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::operator []");
  458. if(_pRuns)
  459. return *(CLine *)CRunPtr<CLine>::GetRun(dRun);
  460. AssertSz(dRun + _iRun == 0 ,
  461. "LP::[]: inconsistent line ptr");
  462. return *(CLine *)CRunPtr<CLine>::GetRun(_iRun);
  463. }
  464. BOOL CLinePtr::IsValid()
  465. {
  466. return !_pRuns ? _pLine != NULL : CRunPtrBase::IsValid();
  467. }
  468. /*
  469. * CLinePtr::RpSetCp(cp, fAtEnd)
  470. *
  471. * Purpose
  472. * Set this line ptr to cp allowing for ambigous cp and taking advantage
  473. * of _cpFirstVisible and _iliFirstVisible
  474. *
  475. * Arguments:
  476. * cp position to set this line ptr to
  477. * fAtEnd if ambiguous cp:
  478. * if fAtEnd = TRUE, set this line ptr to end of prev line;
  479. * else set to start of line (same cp, hence ambiguous)
  480. * Return:
  481. * TRUE iff able to set to cp
  482. */
  483. BOOL CLinePtr::RpSetCp(
  484. LONG cp,
  485. BOOL fAtEnd)
  486. {
  487. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::RpSetCp");
  488. _ich = 0;
  489. if(!_pRuns)
  490. {
  491. // This is a single line so just go straight to the single
  492. // line advance logic. It is important to note that the
  493. // first visible character is irrelevent to the cp advance
  494. // for single line displays.
  495. return RpAdvanceCpSL(cp);
  496. }
  497. BOOL fRet;
  498. LONG cpFirstVisible = _pdp->GetFirstVisibleCp();
  499. if(cp > cpFirstVisible / 2)
  500. { // cpFirstVisible closer than 0
  501. _iRun = _pdp->GetFirstVisibleLine();
  502. fRet = RpAdvanceCp(cp - cpFirstVisible);
  503. }
  504. else
  505. fRet = (cp == CRunPtr<CLine>::BindToCp(cp)); // Start from 0
  506. if(fAtEnd) // Ambiguous-cp caret position
  507. AdjustBackward(); // belongs at prev EOL
  508. return fRet;
  509. }
  510. /*
  511. * CLinePtr::FindParagraph(fForward)
  512. *
  513. * @mfunc
  514. * Move this line ptr to paragraph (fForward) ? end : start,
  515. * and return change in cp
  516. *
  517. * @rdesc
  518. * change in cp
  519. */
  520. LONG CLinePtr::FindParagraph(
  521. BOOL fForward) //@parm TRUE move to para end; else to para start
  522. {
  523. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::FindParagraph");
  524. LONG cch;
  525. CLine * pLine = GetLine();
  526. if(!fForward) // Go to para start
  527. {
  528. cch = 0; // Default already at para start
  529. if (RpGetIch() != pLine->_cch ||
  530. !(pLine->_bFlags & fliHasEOP)) // It isn't at para start
  531. {
  532. cch = -RpGetIch(); // Go to start of current line
  533. while(!(pLine->_bFlags & fliFirstInPara) && (*this) > 0)
  534. {
  535. (*this)--; // Go to start of prev line
  536. pLine = GetLine();
  537. cch -= pLine->_cch; // Subtract # chars in line
  538. }
  539. _ich = 0; // Leave *this at para start
  540. }
  541. }
  542. else // Go to para end
  543. {
  544. cch = GetCchLeft(); // Go to end of current line
  545. while(((*this) < _pdp->LineCount() - 1 ||
  546. _pdp->WaitForRecalcIli((LONG)*this + 1))
  547. && !((*this)->_bFlags & fliHasEOP))
  548. {
  549. (*this)++; // Go to start of next line
  550. cch += (*this)->_cch; // Add # chars in line
  551. }
  552. _ich = (*this)->_cch; // Leave *this at para end
  553. }
  554. return cch;
  555. }
  556. /*
  557. * CLinePtr::GetAdjustedLineLength
  558. *
  559. * @mfunc returns the length of the line _without_ EOP markers
  560. *
  561. * @rdesc LONG; the length of the line
  562. */
  563. LONG CLinePtr::GetAdjustedLineLength()
  564. {
  565. CLine * pline = GetLine();
  566. return pline->_cch - pline->_cchEOP;
  567. }
  568. /*
  569. * CLinePtr::GetCchLeft()
  570. *
  571. * @mfunc
  572. * Calculate length of text left in run starting at the current cp.
  573. * Complements GetIch(), which is length of text up to this cp.
  574. *
  575. * @rdesc
  576. * length of text so calculated
  577. */
  578. LONG CLinePtr::GetCchLeft() const
  579. {
  580. return _pRuns ? CRunPtrBase::GetCchLeft() : _pLine->_cch - _ich;
  581. }
  582. /*
  583. * CLinePtr::GetNumber()
  584. *
  585. * @mfunc
  586. * Get paragraph number
  587. *
  588. * @rdesc
  589. * paragraph number
  590. */
  591. WORD CLinePtr::GetNumber()
  592. {
  593. if(!IsValid())
  594. return 0;
  595. _pLine = GetLine();
  596. if(!_iRun && _pLine->_bNumber > 1)
  597. _pLine->_bNumber = 1;
  598. return _pLine->_bNumber;
  599. }