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.

1699 lines
43 KiB

  1. /*
  2. * @doc
  3. *
  4. * @module - MEASURE.CPP
  5. *
  6. * CMeasurer class
  7. *
  8. * Authors:
  9. * Original RichEdit code: David R. Fulmer <nl>
  10. * Christian Fortini, Murray Sargent, Rick Sailor
  11. *
  12. * History: <nl>
  13. * KeithCu: Fixed zoom, restructured WYSIWYG, performance/cleanup
  14. *
  15. * Copyright (c) 1995-2000 Microsoft Corporation. All rights reserved.
  16. */
  17. #include "_common.h"
  18. #include "_measure.h"
  19. #include "_font.h"
  20. #include "_disp.h"
  21. #include "_edit.h"
  22. #include "_frunptr.h"
  23. #include "_objmgr.h"
  24. #include "_coleobj.h"
  25. #include "_layout.h"
  26. #include "_uspi.h"
  27. ASSERTDATA
  28. void CMeasurer::Init(const CDisplay *pdp)
  29. {
  30. CTxtEdit * ped = GetPed();
  31. _pdp = pdp;
  32. _pddReference = pdp;
  33. _pccs = NULL;
  34. _pPF = NULL;
  35. _plo = NULL;
  36. _dxBorderWidths = 0;
  37. _chPassword = ped->TxGetPasswordChar();
  38. _wNumber = 0;
  39. _cchLine = 0;
  40. _ihyphPrev = 0;
  41. _fRenderer = FALSE;
  42. _fGlyphing = _fFallback = _fTarget = FALSE;
  43. _fMeasure = FALSE;
  44. _dvpWrapLeftRemaining = _dvpWrapRightRemaining = -1;
  45. if(pdp->GetWordWrap())
  46. {
  47. const CDevDesc *pddTarget = pdp->GetTargetDev();
  48. if(pddTarget)
  49. _pddReference = pddTarget;
  50. }
  51. _dvpInch = pdp->GetDypInch();
  52. _dupInch = pdp->GetDxpInch();
  53. if (pdp->IsMain())
  54. {
  55. _dvpInch = MulDiv(_dvpInch, pdp->GetZoomNumerator(), pdp->GetZoomDenominator());
  56. _dupInch = MulDiv(_dupInch, pdp->GetZoomNumerator(), pdp->GetZoomDenominator());
  57. }
  58. if (pdp->SameDevice(_pddReference))
  59. {
  60. _dvrInch = _dvpInch;
  61. _durInch = _dupInch;
  62. }
  63. else
  64. {
  65. _dvrInch = _pddReference->GetDypInch();
  66. _durInch = _pddReference->GetDxpInch();
  67. }
  68. //Set _dulLayout by default to be width for measuring;
  69. //In the table scenario, it will be set elsewhere.
  70. if(!_pdp->GetWordWrap())
  71. _dulLayout = duMax;
  72. else if (_pdp->GetDulForTargetWrap())
  73. _dulLayout = _pdp->GetDulForTargetWrap();
  74. else
  75. _dulLayout = DUtoLU(_pdp->GetDupView());
  76. }
  77. CMeasurer::CMeasurer (const CDisplay* const pdp) : CRchTxtPtr (pdp->GetPed())
  78. {
  79. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::CMeasurer");
  80. Init(pdp);
  81. }
  82. CMeasurer::CMeasurer (const CDisplay* const pdp, const CRchTxtPtr &tp) : CRchTxtPtr (tp)
  83. {
  84. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::CMeasurer");
  85. Init(pdp);
  86. }
  87. CMeasurer::~CMeasurer()
  88. {
  89. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::~CMeasurer");
  90. if(_pccs)
  91. _pccs->Release();
  92. }
  93. /*
  94. * CMeasurer::SetGlyphing(fGlyphing)
  95. *
  96. * @mfunc
  97. * A state flag inside the measurer to record whether or not you
  98. * are in the process of doing glyphing. If we are in a situation
  99. * where the _pddReference and the _pdp have different DCs, then we
  100. * need to throw away the pccs.
  101. */
  102. void CMeasurer::SetGlyphing(
  103. BOOL fGlyphing) //@parm Currently doing glyphing
  104. {
  105. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::SetGlyphing");
  106. Assert(fGlyphing == TRUE || fGlyphing == FALSE);
  107. if (fGlyphing != _fGlyphing)
  108. {
  109. if (_pddReference->_hdc != _pdp->_hdc)
  110. {
  111. if (_pccs)
  112. _pccs->Release();
  113. _pccs = NULL;
  114. }
  115. _fGlyphing = fGlyphing;
  116. }
  117. }
  118. /*
  119. * CMeasurer::SetUseTargetDevice(fUseTargetDevice)
  120. *
  121. * @mfunc
  122. * Sets whether you want to use the target device or not
  123. * for getting metrics
  124. * FUTURE (keithcu) Make this a parameter
  125. */
  126. void CMeasurer::SetUseTargetDevice(
  127. BOOL fUseTargetDevice) //@parm Use target device metrics?
  128. {
  129. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::SetUseTargetDevice");
  130. Assert(fUseTargetDevice == TRUE || fUseTargetDevice == FALSE);
  131. if (fUseTargetDevice != _fTarget)
  132. {
  133. if (_dvpInch != _dvrInch || _dupInch != _durInch)
  134. {
  135. if (_pccs)
  136. _pccs->Release();
  137. _pccs = NULL;
  138. }
  139. _fTarget = fUseTargetDevice;
  140. }
  141. }
  142. /*
  143. * CMeasurer::NewLine (fFirstInPara)
  144. *
  145. * @mfunc
  146. * Initialize this measurer at the start of a new line
  147. */
  148. void CMeasurer::NewLine(
  149. BOOL fFirstInPara) //@parm Flag for setting up _fFirstInPara
  150. {
  151. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::NewLine");
  152. _li.Init(); // Zero all members
  153. if(fFirstInPara)
  154. _li._fFirstInPara = TRUE; // Need to know if first in para
  155. _cchLine = 0;
  156. }
  157. /*
  158. * CMeasurer::NewLine(&li)
  159. *
  160. * @mfunc
  161. * Initialize this measurer at the start of a given line
  162. */
  163. void CMeasurer::NewLine(
  164. const CLine &li)
  165. {
  166. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::NewLine");
  167. _li = li;
  168. _li._cch = 0;
  169. _li._dup = 0;
  170. // Can't calculate upStart till we get an HDC
  171. _li._upStart = 0;
  172. _wNumber = _li._bNumber;
  173. _cchLine = li._cch;
  174. }
  175. /*
  176. * CMeasurer::MeasureText (cch)
  177. *
  178. * @mfunc
  179. * Measure a stretch of text from current running position.
  180. *
  181. * If the user requests us to measure n characters, we measure n + 1.
  182. * and then subtract off the width of the last character. This gives
  183. * us proper value in _dupAddLast.
  184. * REVIEW (keithcu) This looks ugly. Think about it some more.
  185. *
  186. * @rdesc
  187. * width of text (in device units), < 0 if failed
  188. */
  189. LONG CMeasurer::MeasureText(
  190. LONG cch) //@parm Number of characters to measure
  191. {
  192. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::MeasureText");
  193. if(Measure(duMax, min(cch + 1, _cchLine), 0) == MRET_FAILED)
  194. return -1;
  195. if (cch < _cchLine)
  196. {
  197. _li._dup -= _dupAddLast;
  198. _li._cch--;
  199. }
  200. return _li._dup;
  201. }
  202. /*
  203. * CMeasurer::MeasureLine (dulMax, uiFlags, pliTarget)
  204. *
  205. * @mfunc
  206. * Measure a line of text from current cp and determine line break.
  207. * On return *this contains line metrics for _pddReference device.
  208. *
  209. * @rdesc
  210. * TRUE if success, FALSE if failed
  211. */
  212. BOOL CMeasurer::MeasureLine(
  213. UINT uiFlags, //@parm Flags controlling the process (see Measure())
  214. CLine *pliTarget) //@parm Returns target-device line metrics (optional)
  215. {
  216. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::MeasureLine");
  217. // This state must be preserved across the two possible line width
  218. // calculations so we save it here.
  219. BYTE bNumberSave = _li._bNumber;
  220. const CDevDesc *pddTarget = NULL;
  221. if(_pdp->GetWordWrap())
  222. {
  223. // Target devices are only interesting if word wrap is on because the
  224. // only really interesting thing a target device can tell us is where
  225. // the word breaks will occur.
  226. pddTarget = _pdp->GetTargetDev();
  227. if(pddTarget)
  228. SetUseTargetDevice(TRUE);
  229. }
  230. // Compute line break
  231. LONG lRet = Measure(-1, -1, uiFlags);
  232. // Stop here if failed
  233. if(lRet == MRET_FAILED)
  234. return FALSE;
  235. // Return target metrics if requested
  236. if(pliTarget)
  237. *pliTarget = _li;
  238. SetUseTargetDevice(FALSE);
  239. // Recompute to get metrics on rendering device
  240. if(pddTarget || lRet == MRET_NOWIDTH)
  241. {
  242. long cch = _li._cch;
  243. Move(-cch); // move back to BOL
  244. NewLine(uiFlags & MEASURE_FIRSTINPARA);
  245. // Restore the line number
  246. _li._bNumber = bNumberSave;
  247. lRet = Measure(duMax, cch, uiFlags);
  248. if(lRet)
  249. {
  250. Assert(lRet != MRET_NOWIDTH);
  251. return FALSE;
  252. }
  253. }
  254. // Now that we know the line width, compute line shift due
  255. // to alignment, and add it to the left position
  256. _li._upStart += MeasureLineShift();
  257. return TRUE;
  258. }
  259. /*
  260. * CMeasurer::RecalcLineHeight (pccs, pCF)
  261. *
  262. * @mfunc
  263. * Reset height of line we are measuring if new run of text is taller
  264. * than current maximum in line.
  265. */
  266. void CMeasurer::RecalcLineHeight(
  267. CCcs *pccs,
  268. const CCharFormat * const pCF)
  269. {
  270. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::RecalcLineHeight");
  271. // Compute line height
  272. LONG vpOffset, vpAdjust;
  273. pccs->GetOffset(pCF, _fTarget ? _dvrInch : _dvpInch, &vpOffset, &vpAdjust);
  274. if (GetPF()->_bLineSpacingRule == tomLineSpaceExactly)
  275. vpOffset = 0;
  276. LONG vpHeight = pccs->_yHeight;
  277. LONG vpDescent = pccs->_yDescent;
  278. SHORT yFEAdjust = pccs->AdjustFEHeight(FAdjustFELineHt());
  279. if (yFEAdjust)
  280. {
  281. vpHeight += (yFEAdjust << 1);
  282. vpDescent += yFEAdjust;
  283. }
  284. LONG vpAscent = vpHeight - vpDescent;
  285. LONG vpAboveBase = max(vpAscent, vpAscent + vpOffset);
  286. LONG vpBelowBase = max(vpDescent, vpDescent - vpOffset);
  287. _li._dvpHeight = (SHORT)(max(vpAboveBase, _li._dvpHeight - _li._dvpDescent) +
  288. max(vpBelowBase, _li._dvpDescent));
  289. _li._dvpDescent = (SHORT)max(vpBelowBase, _li._dvpDescent);
  290. }
  291. /*
  292. * CMeasurer::Measure (dulMax, cchMax, uiFlags)
  293. *
  294. * @mfunc
  295. * Measure given amount of text, start at current running position
  296. * and storing # chars measured in _cch.
  297. * Can optionally determine line break based on a dulMax and
  298. * break out at that point.
  299. *
  300. * @rdesc
  301. * 0 success
  302. * MRET_FAILED if failed
  303. * MRET_NOWIDTH if second pass is needed to compute correct width
  304. *
  305. * @devnote
  306. * The uiFlags parameter has the following meanings:
  307. * MEASURE_FIRSTINPARA this is first line of paragraph
  308. * MEASURE_BREAKATWORD break out on a word break
  309. * MEASURE_BREAKBEFOREWIDTH break before dulMax
  310. *
  311. * The calling chain must be protected by a CLock, since this present
  312. * routine access the global (shared) FontCache facility.
  313. */
  314. LONG CMeasurer::Measure(
  315. LONG dulMax, //@parm Max width of line in logical units (-1 uses CDisplay width)
  316. LONG cchMax, //@parm Max chars to process (-1 if no limit)
  317. UINT uiFlags) //@parm Flags controlling the process (see above)
  318. {
  319. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::Measure");
  320. LONG cch; // cchChunk count down
  321. LONG cchChunk; // cch of cst-format contiguous run
  322. LONG cchNonWhite; // cch of last nonwhite char in line
  323. LONG cchText = GetTextLength();
  324. WCHAR ch; // Temporary char
  325. BOOL fFirstInPara = uiFlags & MEASURE_FIRSTINPARA;
  326. BOOL fLastChObj = FALSE;
  327. LONG lRet = 0;
  328. const WCHAR*pch;
  329. CTxtEdit * ped = GetPed();
  330. COleObject *pobj;
  331. LONG dupMax;
  332. LONG uAdd = 0; // Character width
  333. LONG dupSoftHyphen = 0; // Most recent soft hyphen width
  334. LONG dupNonWhite; // dup for last nonwhite char in line
  335. // This variable is used to keep track of whether there is a height change
  336. // so that we know whether we need to recalc the line in certain line break cases.
  337. BOOL fHeightChange = FALSE;
  338. const INT MAX_SAVED_WIDTHS = 31; // power of 2 - 1
  339. INT i, index, iSavedWidths = 0;
  340. struct {
  341. SHORT width;
  342. SHORT vpHeight;
  343. SHORT vpDescent;
  344. } savedWidths[MAX_SAVED_WIDTHS+1];
  345. _pPF = GetPF(); // Be sure current CParaFormat
  346. // ptr is up to date
  347. Assert(_li._cch == 0);
  348. // Init fliFirstInPara flag for new line
  349. if(fFirstInPara)
  350. {
  351. _li._fFirstInPara = TRUE;
  352. if(IsInOutlineView() && IsHeadingStyle(_pPF->_sStyle))
  353. _li._dvpHeight = (short)max(_li._dvpHeight, BITMAP_HEIGHT_HEADING + 1);
  354. }
  355. AssertSz(!_pPF->IsListNumbered() && !_wNumber ||
  356. (uiFlags & MEASURE_BREAKBEFOREWIDTH) || !_pdp->IsMultiLine() ||
  357. _wNumber > 20 || _wNumber == (i = GetParaNumber()),
  358. "CMeasurer::Measure: incorrect list number");
  359. _li._upStart = MeasureLeftIndent(); // Set left indent
  360. // Compute width to break out at
  361. if(dulMax < 0)
  362. dupMax = LUtoDU(_dulLayout);
  363. else if (dulMax != duMax)
  364. dupMax = LUtoDU(dulMax);
  365. else
  366. dupMax = duMax;
  367. //If we are told to measure a fixed width (as in CchFromUp) then ignore
  368. //affect of left and right indent.
  369. if (dulMax < 0)
  370. {
  371. LONG uCaretT = (_pdp->IsMain() && !GetPed()->TxGetReadOnly()) ?
  372. ped->GetCaretWidth() : 0;
  373. dupMax -= (MeasureRightIndent() + _li._upStart + uCaretT);
  374. }
  375. dupMax = max(dupMax, 0);
  376. // Compute max count of characters to process
  377. cch = cchText - GetCp();
  378. if(cchMax < 0 || cchMax > cch)
  379. cchMax = cch;
  380. cchNonWhite = _li._cch; // Default nonwhite parms
  381. dupNonWhite = _li._dup;
  382. for( ; cchMax > 0; // Measure up to cchMax
  383. cchMax -= cchChunk, Move(cchChunk)) // chars
  384. {
  385. pch = GetPch(cch);
  386. cch = min(cch, cchMax); // Compute constant-format
  387. cchChunk = GetCchLeftRunCF();
  388. cch = min(cch, cchChunk); // Counter for next while
  389. cchChunk = cch; // Save chunk size
  390. const CCharFormat *pCF = GetCF();
  391. DWORD dwEffects = pCF->_dwEffects;
  392. if(dwEffects & CFE_HIDDEN) // Ignore hidden text
  393. {
  394. uAdd = 0;
  395. _li._cch += cchChunk;
  396. continue;
  397. }
  398. if(!Check_pccs()) // Be sure _pccs is current
  399. return MRET_FAILED;
  400. // Adjust line height for new format run
  401. if(cch > 0 && *pch && (IsRich() || ped->HasObjects()))
  402. {
  403. // Note: the EOP only contributes to the height calculation for the
  404. // line if there are no non-white space characters on the line or
  405. // the paragraph is a bullet paragraph. The bullet paragraph
  406. // contribution to the line height is done in AdjustLineHeight.
  407. // REVIEW (Victork)
  408. // Another, similar topic is height of spaces.
  409. // They don't (normally) influence line height in LS,
  410. // they do in CMeasurer::Measure code.
  411. // Proposed ways to solve it:
  412. // - have fSpacesOnly flag in run
  413. // - move current (line height) logic down after next character-scanning loop
  414. if(!cchNonWhite || *pch != CR && *pch != LF)
  415. {
  416. // Determine if the current run is the tallest text on this
  417. // line and if so, increase the height of the line.
  418. LONG vpHeightOld = _li._dvpHeight;
  419. RecalcLineHeight(_pccs, pCF);
  420. // Test for a change in line height. This only happens when
  421. // this is not the first character in the line and (surprise)
  422. // the height changes.
  423. if (vpHeightOld && vpHeightOld != _li._dvpHeight)
  424. fHeightChange = TRUE;
  425. }
  426. }
  427. while(cch > 0)
  428. { // Process next char
  429. uAdd = 0; // Default zero width
  430. ch = *pch;
  431. if(_chPassword && !IN_RANGE(LF, ch, CR))
  432. ch = _chPassword;
  433. if(dwEffects & CFE_ALLCAPS)
  434. CharUpperBuff(&ch, 1);
  435. if(ch == WCH_EMBEDDING)
  436. {
  437. _li._fHasSpecialChars = TRUE;
  438. pobj = GetObjectFromCp(GetCp() + cchChunk - cch);
  439. if(pobj)
  440. {
  441. LONG vpAscent, vpDescent;
  442. pobj->MeasureObj(_fTarget ? _dvrInch : _dvpInch,
  443. _fTarget ? _durInch : _dupInch,
  444. uAdd, vpAscent, vpDescent, _li._dvpDescent, GetTflow());
  445. // Only update height for line if the object is going
  446. // to be on this line.
  447. if(!_li._cch || _li._dup + uAdd <= dupMax)
  448. {
  449. if (vpAscent > _li._dvpHeight - _li._dvpDescent)
  450. _li._dvpHeight = vpAscent + _li._dvpDescent;
  451. }
  452. }
  453. if(_li._dup + uAdd > dupMax)
  454. fLastChObj = TRUE;
  455. }
  456. // The following if succeeds if ch isn't a CELL, BS, TAB, LF,
  457. // VT, FF, or CR
  458. else if(!IN_RANGE(CELL, ch, CR)) // Not TAB or EOP
  459. {
  460. // Get char width
  461. if (!_pccs->Include(ch, uAdd))
  462. {
  463. AssertSz(FALSE, "CMeasurer::Measure char not in font");
  464. return MRET_FAILED;
  465. }
  466. if(IN_RANGE(NBSPACE, ch, EURO)) // Rules out ASCII, CJK
  467. {
  468. switch(ch) // char for NBSPACE &
  469. { // special hyphens
  470. case EURO:
  471. case NBHYPHEN:
  472. case SOFTHYPHEN:
  473. case NBSPACE:
  474. case EMSPACE:
  475. case ENSPACE:
  476. _li._fHasSpecialChars = TRUE;
  477. if (ch == SOFTHYPHEN && (_li._dup + uAdd < dupMax || !_li._cch))
  478. {
  479. dupSoftHyphen = uAdd; // Save soft hyphen width
  480. uAdd = 0; // Use 0 unless at EOL
  481. }
  482. break;
  483. }
  484. }
  485. else if(_chPassword && IN_RANGE(0xDC00, *pch, 0xDFFF))
  486. uAdd = 0;
  487. }
  488. else if(ch == TAB)
  489. {
  490. _li._fHasSpecialChars = TRUE;
  491. uAdd = MeasureTab(ch);
  492. }
  493. else if(ch == FF && ped->Get10Mode()) // RichEdit 1.0 treats
  494. _pccs->Include(ch, uAdd); // FFs as normal chars
  495. else // Done with line
  496. goto eop; // Go process EOP chars
  497. index = iSavedWidths++ & MAX_SAVED_WIDTHS;
  498. savedWidths[index].width = (SHORT)uAdd;
  499. savedWidths[index].vpHeight = _li._dvpHeight;
  500. savedWidths[index].vpDescent = _li._dvpDescent;
  501. _li._dup += uAdd;
  502. if(_li._dup > dupMax &&
  503. (uiFlags & MEASURE_BREAKBEFOREWIDTH || _li._cch > 0))
  504. goto overflow;
  505. _li._cch++;
  506. pch++;
  507. cch--;
  508. if(ch != ' ') // If not whitespace char,
  509. {
  510. cchNonWhite = _li._cch; // update nonwhitespace
  511. dupNonWhite = _li._dup; // count and width
  512. }
  513. } // while(cch > 0)
  514. } // for(;cchMax > 0;...)
  515. goto eol; // All text exhausted
  516. // End Of Paragraph char encountered (CR, LF, VT, or FF, but mostly CR)
  517. eop:
  518. Move(cchChunk - cch); // Position tp at EOP
  519. cch = AdvanceCRLF(); // Bypass possibly multibyte EOP
  520. _li._cchEOP = (BYTE)cch; // Store EOP cch
  521. _li._cch += cch; // Increment line count
  522. if(ch == CR || ped->fUseCRLF() && ch == LF || ch == CELL)
  523. _li._fHasEOP = TRUE;
  524. AssertSz(ped->fUseCRLF() || cch == 1,
  525. "CMeasurer::Measure: EOP isn't a single char");
  526. AssertSz(_pdp->IsMultiLine() || GetCp() == cchText,
  527. "CMeasurer::Measure: EOP in single-line control");
  528. eol: // End of current line
  529. if(uiFlags & MEASURE_BREAKATWORD) // Compute count of whitespace
  530. _li._dup = dupNonWhite; // chars at EOL
  531. goto done;
  532. overflow: // Went past max width for line
  533. _li._dup -= uAdd;
  534. --iSavedWidths;
  535. Move(cchChunk - cch); // Position *this at overflow
  536. // position
  537. if(uiFlags & MEASURE_BREAKATWORD) // If required, adjust break on
  538. { // word boundary
  539. // We should not have the EOP flag set here. The case to watch out
  540. // for is when we reuse a line that used to have an EOP. It is the
  541. // responsibility of the measurer to clear this flag as appropriate.
  542. Assert(_li._cchEOP == 0);
  543. _li._cchEOP = 0; // Just in case
  544. if(ch == TAB)
  545. {
  546. // If the last character measured is a tab, leave it on the
  547. // next line to allow tabbing off the end of line as in Word
  548. goto done;
  549. }
  550. LONG cpStop = GetCp(); // Remember current cp
  551. cch = -FindWordBreak(WB_LEFTBREAK, _li._cch+1);
  552. if(cch == 0 && fLastChObj) // If preceding char is an
  553. goto done; // object, put current char
  554. // on next line
  555. Assert(cch >= 0);
  556. if(cch + 1 < _li._cch) // Break char not at BOL
  557. {
  558. ch = _rpTX.GetPrevChar();
  559. if (ch == TAB) // If break char is a TAB,
  560. { // put it on the next line
  561. cch++; // as in Word
  562. Move(-1);
  563. }
  564. else if(ch == SOFTHYPHEN)
  565. _li._dup += dupSoftHyphen;
  566. _li._cch -= cch;
  567. }
  568. else if(cch == _li._cch && cch > 1 &&
  569. _rpTX.GetChar() == ' ') // Blanks all the way back to
  570. { // BOL. Bypass first blank
  571. Move(1);
  572. cch--;
  573. _li._cch = 1;
  574. }
  575. else // Advance forward to end of
  576. SetCp(cpStop); // measurement
  577. Assert(_li._cch > 0);
  578. // Now search at start of word to figure how many white chars at EOL
  579. LONG cchWhite = 0;
  580. if(GetCp() < cchText)
  581. {
  582. pch = GetPch(cch);
  583. cch = 0;
  584. if(ped->TxWordBreakProc((WCHAR *)pch, 0, sizeof(WCHAR), WB_ISDELIMITER, GetCp()))
  585. {
  586. cch = FindWordBreak(WB_RIGHT);
  587. Assert(cch >= 0);
  588. }
  589. cchWhite = cch;
  590. _li._cch += cch;
  591. ch = GetChar();
  592. if(IN_RANGE(CELL, ch, CR) && !IN_RANGE(8, ch, TAB)) // skip *only* 1 EOP -jOn
  593. {
  594. if(ch == CR || ch == CELL)
  595. _li._fHasEOP = TRUE;
  596. _li._cchEOP = (BYTE)AdvanceCRLF();
  597. _li._cch += _li._cchEOP;
  598. goto done;
  599. }
  600. }
  601. i = cpStop - GetCp();
  602. if(i)
  603. {
  604. if(i > 0)
  605. i += cchWhite;
  606. if(i > 0 && i < iSavedWidths && i < MAX_SAVED_WIDTHS)
  607. {
  608. while (i-- > 0)
  609. {
  610. iSavedWidths = (iSavedWidths - 1) & MAX_SAVED_WIDTHS;
  611. _li._dup -= savedWidths[iSavedWidths].width;
  612. }
  613. iSavedWidths = (iSavedWidths - 1) & MAX_SAVED_WIDTHS;
  614. _li._dvpHeight = savedWidths[iSavedWidths].vpHeight;
  615. _li._dvpDescent = savedWidths[iSavedWidths].vpDescent;
  616. }
  617. else
  618. {
  619. // Need to recompute width from scratch.
  620. lRet = MRET_NOWIDTH;
  621. }
  622. }
  623. else
  624. {
  625. // i == 0 means that we are breaking on the first letter in a word.
  626. // Therefore, we want to set the width to the total non-white space
  627. // calculated so far because that does not include the size of the
  628. // character that caused the break nor any of the white space
  629. // preceeding the character that caused the break.
  630. if(!fHeightChange)
  631. _li._dup = dupNonWhite;
  632. else
  633. {
  634. // Need to recompute from scratch so that we can get the
  635. // correct height for the control
  636. lRet = MRET_NOWIDTH;
  637. }
  638. }
  639. }
  640. done:
  641. _dupAddLast = uAdd;
  642. if(!_li._dvpHeight) // If no height yet, use
  643. CheckLineHeight(); // default height
  644. AdjustLineHeight();
  645. return lRet;
  646. }
  647. /*
  648. * CMeasurer::UpdateWrapState(dvpLine, dvpDescent, fLeft)
  649. *
  650. * @mfunc
  651. * After formatting a line, update the current state of wrapped objects
  652. */
  653. void CMeasurer::UpdateWrapState(
  654. USHORT &dvpLine,
  655. USHORT &dvpDescent,
  656. BOOL fLeft)
  657. {
  658. if (fLeft && _li._cObjectWrapLeft || !fLeft && _li._cObjectWrapRight)
  659. {
  660. COleObject *pobj = FindFirstWrapObj(fLeft);
  661. LONG & dvpWrapRemaining = fLeft ? _dvpWrapLeftRemaining : _dvpWrapRightRemaining;
  662. if (dvpWrapRemaining == -1)
  663. {
  664. if (fLeft)
  665. _li._fFirstWrapLeft = 1;
  666. else
  667. _li._fFirstWrapRight = 1;
  668. LONG dup, dvpAscent, dvpDescent;
  669. pobj->MeasureObj(_dvpInch, _dupInch, dup, dvpAscent, dvpDescent, 0, GetTflow());
  670. dvpWrapRemaining = dvpAscent + dvpDescent;
  671. }
  672. if (_li._fHasEOP && (_pPF->_wEffects & PFE_TEXTWRAPPINGBREAK))
  673. {
  674. LONG dvpRemaining = dvpWrapRemaining - dvpLine;
  675. if (dvpRemaining > 0)
  676. {
  677. dvpLine += dvpRemaining;
  678. dvpDescent += dvpRemaining;
  679. }
  680. }
  681. dvpWrapRemaining -= dvpLine;
  682. if (dvpWrapRemaining <= 0)
  683. {
  684. dvpWrapRemaining = -1;
  685. RemoveFirstWrap(fLeft);
  686. }
  687. }
  688. }
  689. /*
  690. * CMeasurer::UpdateWrapState (&dvpLine, &dvpDescent)
  691. *
  692. * @mfunc
  693. * Update object wrap state
  694. */
  695. void CMeasurer::UpdateWrapState(
  696. USHORT &dvpLine,
  697. USHORT &dvpDescent)
  698. {
  699. //If we are wrapping around an object, update dvpWrapUsed values
  700. //and remove objects from queue if they have been used up.
  701. if (IsMeasure() && _rgpobjWrap.Count())
  702. {
  703. UpdateWrapState(dvpLine, dvpDescent, TRUE);
  704. UpdateWrapState(dvpLine, dvpDescent, FALSE);
  705. }
  706. }
  707. /*
  708. * CMeasurer::GetCcsFontFallback (pCF)
  709. *
  710. * @mfunc
  711. * Create the fallback font cache for given CF
  712. *
  713. * @rdesc
  714. * CCcs corresponding to font fallback given by pCF
  715. */
  716. CCcs* CMeasurer::GetCcsFontFallback (
  717. const CCharFormat *pCF,
  718. WORD wScript)
  719. {
  720. CCharFormat CF = *pCF;
  721. CCcs* pccs = NULL;
  722. SHORT iDefHeight;
  723. CTxtEdit* ped = GetPed();
  724. BYTE bCharRep = CF._iCharRep;
  725. #ifndef NOCOMPLEXSCRIPTS
  726. CUniscribe *pusp = ped->Getusp();
  727. if (pusp && wScript != 0)
  728. {
  729. pusp->GetComplexCharRep(pusp->GeteProp(wScript),
  730. ped->GetCharFormat(-1)->_iCharRep, bCharRep);
  731. }
  732. #endif
  733. bool fr = W32->GetPreferredFontInfo(bCharRep,
  734. ped->fUseUIFont() ? true : false, CF._iFont,
  735. (BYTE&)iDefHeight, CF._bPitchAndFamily);
  736. if (fr)
  737. {
  738. CF._iCharRep = bCharRep;
  739. pccs = GetCcs(&CF); // Create fallback font cache entry
  740. }
  741. return pccs;
  742. }
  743. /*
  744. * CMeasurer::ApplyFontCache (fFallback, wScript)
  745. *
  746. * @mfunc
  747. * Apply a new font cache on the fly (leave backing store intact)
  748. *
  749. * @rdesc
  750. * CCcs corresponding to font fallback if fFallback; else to GetCF()
  751. */
  752. CCcs* CMeasurer::ApplyFontCache (
  753. BOOL fFallback,
  754. WORD wScript)
  755. {
  756. if (_fFallback ^ fFallback)
  757. {
  758. CCcs* pccs = fFallback ? GetCcsFontFallback(GetCF(), wScript) : GetCcs(GetCF());
  759. if (pccs)
  760. {
  761. if (_pccs)
  762. _pccs->Release();
  763. _pccs = pccs;
  764. _fFallback = fFallback;
  765. }
  766. }
  767. return _pccs;
  768. }
  769. /*
  770. * CMeasurer::GetCcs (pCF)
  771. *
  772. * @mfunc
  773. * Wrapper around font cache's GetCCcs function
  774. * We use a NULL DC unless the device is a printer.
  775. *
  776. * @rdesc
  777. * CCcs corresponding to pCF
  778. */
  779. CCcs* CMeasurer::GetCcs(
  780. const CCharFormat *pCF)
  781. {
  782. HDC hdc = NULL;
  783. if (_fTarget)
  784. {
  785. if (_pddReference->_hdc && GetDeviceCaps(_pddReference->_hdc, TECHNOLOGY) == DT_RASPRINTER)
  786. hdc = _pddReference->_hdc;
  787. }
  788. else if (_pdp->_hdc && GetDeviceCaps(_pdp->_hdc, TECHNOLOGY) == DT_RASPRINTER)
  789. hdc = _pdp->_hdc;
  790. DWORD dwFlags = GetTflow();
  791. if (_fGlyphing && _pdp->_hdc != _pddReference->_hdc)
  792. dwFlags |= FGCCSUSETRUETYPE;
  793. if(GetPasswordChar())
  794. pCF = GetPed()->GetCharFormat(-1);
  795. return GetPed()->GetCcs(pCF, _fTarget ? _dvrInch : _dvpInch, dwFlags, hdc);
  796. }
  797. /*
  798. * CMeasurer::CheckLineHeight()
  799. *
  800. * @mfunc
  801. * If no height yet, use default height
  802. */
  803. void CMeasurer::CheckLineHeight()
  804. {
  805. CCcs *pccs = GetCcs(GetPed()->GetCharFormat(-1));
  806. _li._dvpHeight = pccs->_yHeight;
  807. _li._dvpDescent = pccs->_yDescent;
  808. SHORT yFEAdjust = pccs->AdjustFEHeight(FAdjustFELineHt());
  809. if (yFEAdjust)
  810. {
  811. _li._dvpHeight += (yFEAdjust << 1);
  812. _li._dvpDescent += yFEAdjust;
  813. }
  814. pccs->Release();
  815. }
  816. /*
  817. * CMeasurer::Check_pccs(fBullet)
  818. *
  819. * @mfunc
  820. * Check if new character format run or whether we don't yet have a font
  821. *
  822. * @rdesc
  823. * Current CCcs *
  824. *
  825. * @devnote
  826. * The calling chain must be protected by a CLock, since this present
  827. * routine access the global (shared) FontCache facility.
  828. */
  829. CCcs *CMeasurer::Check_pccs(
  830. BOOL fBullet)
  831. {
  832. if(fBullet)
  833. {
  834. if(_pccs) // Release old Format cache
  835. _pccs->Release();
  836. _pccs = GetCcsBullet(NULL);
  837. _iFormat = -10; // Be sure to reset font next time
  838. return _pccs;
  839. }
  840. const CCharFormat *pCF = GetCF();
  841. if(FormatIsChanged())
  842. {
  843. // New CF run or format for this line not yet initialized
  844. ResetCachediFormat();
  845. if(_pccs) // Release old Format cache
  846. _pccs->Release();
  847. _pccs = GetCcs(pCF);
  848. _fFallback = 0;
  849. if(!_pccs)
  850. {
  851. //FUTURE (keithcu) If this fails, just dig up the first pccs you can find
  852. AssertSz(FALSE, "CMeasurer::Measure could not get _pccs");
  853. return NULL;
  854. }
  855. }
  856. return _pccs;
  857. }
  858. /*
  859. * CMeasurer::AdjustLineHeight()
  860. *
  861. * @mfunc
  862. * Adjust for space before/after and line spacing rules.
  863. * No effect for plain text.
  864. *
  865. * @future
  866. * Base multiple line height calculations on largest font height rather
  867. * than on line height (_vpHeight), since the latter may be unduly large
  868. * due to embedded objects. Word does this correctly.
  869. */
  870. void CMeasurer::AdjustLineHeight()
  871. {
  872. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::AdjustLineHeight");
  873. if(!IsRich() || IsInOutlineView()) // Plain text and outline mode
  874. return; // don't use special line
  875. // spacings
  876. const CParaFormat * pPF = _pPF;
  877. DWORD dwRule = pPF->_bLineSpacingRule;
  878. LONG dvpAfter = 0; // Default no space after
  879. LONG dvpBefore = 0; // Default no space before
  880. LONG dvpSpacing = pPF->_dyLineSpacing;
  881. LONG vpHeight = LVtoDV(dvpSpacing);
  882. LONG vpAscent = _li._dvpHeight - _li._dvpDescent;
  883. if(_li._fFirstInPara)
  884. dvpBefore = LVtoDV(pPF->_dySpaceBefore); // Space before paragraph
  885. AssertSz(dvpBefore >= 0, "CMeasurer::AdjustLineHeight - bogus value for dvpBefore");
  886. if(vpHeight < 0) // Negative heights mean use
  887. _li._dvpHeight = (SHORT)(-vpHeight); // the magnitude exactly
  888. else if(dwRule) // Line spacing rule is active
  889. {
  890. switch (dwRule)
  891. {
  892. case tomLineSpace1pt5:
  893. dvpAfter = _li._dvpHeight >> 1; // Half-line space after
  894. break; // (per line)
  895. case tomLineSpaceDouble:
  896. dvpAfter = _li._dvpHeight; // Full-line space after
  897. break; // (per line)
  898. case tomLineSpaceAtLeast:
  899. if(_li._dvpHeight >= vpHeight)
  900. break;
  901. // Fall thru to space exactly
  902. case tomLineSpaceExactly:
  903. _li._dvpHeight = (SHORT)max(vpHeight, 1);
  904. break;
  905. case tomLineSpaceMultiple: // Multiple-line space after
  906. // Prevent dvpAfter from being negative because dvpSpacing is small - a-rsail
  907. if (dvpSpacing < 20)
  908. dvpSpacing = 20;
  909. dvpAfter = (_li._dvpHeight*dvpSpacing)/20 // (20 units per line)
  910. - _li._dvpHeight;
  911. }
  912. }
  913. if(_li._fHasEOP)
  914. dvpAfter += LVtoDV(pPF->_dySpaceAfter); // Space after paragraph end
  915. // Add in space before/after
  916. if (dvpAfter < 0)
  917. {
  918. // Overflow - since we forced dvpSpacing to 20 above, the
  919. // only reason for a negative is overflow. In case of overflow,
  920. // we simply force the value to the max and then fix the
  921. // other resulting overflows.
  922. dvpAfter = LONG_MAX;
  923. }
  924. AssertSz((dvpBefore >= 0), "CMeasurer::AdjustLineHeight - invalid before");
  925. _li._dvpHeight = (SHORT)(_li._dvpHeight + dvpBefore + dvpAfter);
  926. if (_li._dvpHeight < 0)
  927. {
  928. // Overflow!
  929. // The reason for the -2 is then we don't have to worry about
  930. // overflow in the table check.
  931. _li._dvpHeight = SHRT_MAX - 2;
  932. }
  933. _li._dvpDescent = (SHORT)(_li._dvpDescent + dvpAfter);
  934. if (_li._dvpDescent < 0)
  935. {
  936. // Overflow in descent
  937. AssertSz(_li._dvpHeight == SHRT_MAX - 2, "Descent overflowed when height didn't");
  938. // Allow old ascent
  939. _li._dvpDescent = SHRT_MAX - 2 - vpAscent;
  940. AssertSz(_li._dvpDescent >= 0, "descent adjustment < 0");
  941. }
  942. AssertSz((_li._dvpHeight >= 0) && (_li._dvpDescent >= 0),
  943. "CMeasurer::AdjustLineHeight - invalid line heights");
  944. }
  945. /*
  946. * CMeasurer::GetPBorderWidth (dxlLine)
  947. *
  948. * @mfunc
  949. * Convert logical width to device width and ensure that
  950. * device width is at least 1 pixel if logical width is nonzero.
  951. *
  952. * @rdesc
  953. * Device width of border
  954. */
  955. LONG CMeasurer::GetPBorderWidth(
  956. LONG dxlLine) //@parm Logical border width
  957. {
  958. dxlLine &= 0xFF;
  959. LONG dxpLine = LUtoDU(dxlLine);
  960. if(dxlLine)
  961. dxpLine = max(dxpLine, 1);
  962. return dxpLine;
  963. }
  964. /*
  965. * CMeasurer::MeasureLeftIndent()
  966. *
  967. * @mfunc
  968. * Compute and return left indent of line in device units
  969. *
  970. * @rdesc
  971. * Left indent of line in device units
  972. *
  973. * @comm
  974. * Plain text is sensitive to StartIndent and RightIndent settings,
  975. * but usually these are zero for plain text.
  976. */
  977. LONG CMeasurer::MeasureLeftIndent()
  978. {
  979. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::MeasureLeftIndent");
  980. AssertSz(_pPF != NULL, "CMeasurer::MeasureLeftIndent _pPF not set!");
  981. LONG ulLeft = _pPF->_dxStartIndent; // Use logical units
  982. // up to return
  983. if(IsRich())
  984. {
  985. LONG dulOffset = _pPF->_dxOffset;
  986. BOOL fFirstInPara = _li._fFirstInPara;
  987. if(IsInOutlineView())
  988. {
  989. ulLeft = lDefaultTab/2 * (_pPF->_bOutlineLevel + 1);
  990. if(!fFirstInPara)
  991. dulOffset = 0;
  992. }
  993. if(fFirstInPara)
  994. {
  995. if(_pPF->_wNumbering && !_pPF->IsNumberSuppressed())
  996. {
  997. // Add offset to text on first line
  998. if(_pPF->_wNumberingTab) // If _wNumberingTab != 0,
  999. dulOffset = _pPF->_wNumberingTab;// use it
  1000. LONG Alignment = _pPF->_wNumberingStyle & 3;
  1001. if(Alignment != tomAlignRight)
  1002. {
  1003. LONG du = DUtoLU(MeasureBullet());
  1004. if(Alignment == tomAlignCenter)
  1005. du /= 2;
  1006. dulOffset = max(du, dulOffset); // Use max of bullet and
  1007. }
  1008. } // offset
  1009. else
  1010. dulOffset = 0;
  1011. }
  1012. ulLeft += dulOffset;
  1013. }
  1014. return (ulLeft <= 0) ? 0 : LUtoDU(ulLeft);
  1015. }
  1016. /*
  1017. * CMeasurer::HitTest(x)
  1018. *
  1019. * @mfunc
  1020. * Return HITTEST for displacement x in this line. Can't be specific
  1021. * about text area (_upStart to _upStart + _dupLineMax), since need to measure
  1022. * to get appropriate cp (done elsewhere)
  1023. *
  1024. * @rdesc
  1025. * HITTEST for a displacement x in this line
  1026. */
  1027. HITTEST CMeasurer::HitTest(
  1028. LONG x) //@parm Displacement to test hit
  1029. {
  1030. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::HitTest");
  1031. UpdatePF();
  1032. LONG u = UFromX(x);
  1033. if(u < 0)
  1034. return HT_LeftOfText;
  1035. // For RightOfText, allow for a little "hit space" of _li.GetHeight() to
  1036. // allow user to select EOP at end of line
  1037. if (u > _li._upStart + _li._dup + _li.GetHeight() &&
  1038. GetPed()->GetSelMin() == GetCp() + _li._cch - _li._cchEOP)
  1039. {
  1040. return HT_RightOfText;
  1041. }
  1042. if(u >= _li._upStart) // Caller can refine this
  1043. return HT_Text; // with CLine::CchFromUp()
  1044. if(IsRich() && _li._fFirstInPara)
  1045. {
  1046. LONG dup;
  1047. if(_pPF->_wNumbering)
  1048. {
  1049. // Doesn't handle case where Bullet is wider than following dx
  1050. dup = LUtoDU(max(_pPF->_dxOffset, _pPF->_wNumberingTab));
  1051. if(u >= _li._upStart - dup)
  1052. return HT_BulletArea;
  1053. }
  1054. if(IsInOutlineView())
  1055. {
  1056. dup = LUtoDU(lDefaultTab/2 * _pPF->_bOutlineLevel);
  1057. if(u >= dup && u < dup + (_pPF->_bOutlineLevel & 1
  1058. ? LUtoDU(lDefaultTab/2) : _pdp->Zoom(BITMAP_WIDTH_HEADING)))
  1059. {
  1060. return HT_OutlineSymbol;
  1061. }
  1062. }
  1063. }
  1064. return HT_LeftOfText;
  1065. }
  1066. /*
  1067. * CMeasurer::MeasureRightIndent()
  1068. *
  1069. * @mfunc
  1070. * Compute and return right indent of line in device units
  1071. *
  1072. * @rdesc
  1073. * Right indent of line in device units
  1074. *
  1075. * @comm
  1076. * Plain text is sensitive to StartIndent and RightIndent settings,
  1077. * but usually these are zero for plain text.
  1078. */
  1079. LONG CMeasurer::MeasureRightIndent()
  1080. {
  1081. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::MeasureRightIndent");
  1082. LONG dulRight = _pPF->_dxRightIndent;
  1083. _upRight = LUtoDU(max(dulRight, 0));
  1084. return _upRight;
  1085. }
  1086. /*
  1087. * CMeasurer::MeasureTab()
  1088. *
  1089. * @mfunc
  1090. * Computes and returns the width from the current position to the
  1091. * next tab stop (in device units).
  1092. *
  1093. * @rdesc
  1094. * Width from current position to next tab stop
  1095. */
  1096. LONG CMeasurer::MeasureTab(
  1097. unsigned ch)
  1098. {
  1099. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::MeasureTab");
  1100. LONG uCur = _li._dup + MeasureLeftIndent();
  1101. const CParaFormat * pPF = _pPF;
  1102. LONG cTab = pPF->_bTabCount;
  1103. LONG duDefaultTab = lDefaultTab;
  1104. LONG duIndent = LUtoDU(pPF->_dxStartIndent + pPF->_dxOffset);
  1105. LONG duOffset = pPF->_dxOffset;
  1106. LONG duOutline = 0;
  1107. LONG h = 0;
  1108. LONG uT;
  1109. LONG uTab;
  1110. AssertSz(cTab >= 0 || cTab <= MAX_TAB_STOPS, "Illegal tab count");
  1111. if(IsInOutlineView())
  1112. duOutline = lDefaultTab/2 * (pPF->_bOutlineLevel + 1);
  1113. if(cTab)
  1114. {
  1115. const LONG *pl = pPF->GetTabs();
  1116. for(uTab = 0; cTab--; pl++) // Try explicit tab stops 1st
  1117. {
  1118. uT = GetTabPos(*pl) + duOutline; // (2 most significant nibbles
  1119. if(uT > _dulLayout) // Ignore tabs wider than layout area
  1120. break;
  1121. //REVIEW (keithcu) This is not proper hungarian
  1122. uT = LUtoDU(uT); // are for type/style)
  1123. if(uT + h > uCur) // Allow text in table cell to
  1124. { // move into cell gap (h > 0)
  1125. if(duOffset > 0 && uT < duIndent)// Explicit tab in a hanging
  1126. return uT - uCur; // indent takes precedence
  1127. uTab = uT;
  1128. break;
  1129. }
  1130. }
  1131. if(duOffset > 0 && uCur < duIndent) // If no tab before hanging
  1132. return duIndent - uCur; // indent, tab to indent
  1133. if(uTab) // Else use tab position
  1134. return uTab - uCur;
  1135. }
  1136. duDefaultTab = GetTabPos(GetPed()->GetDefaultTab());
  1137. AssertSz(duDefaultTab > 0, "CMeasurer::MeasureTab: Default tab is bad");
  1138. duDefaultTab = LUtoDU(duDefaultTab);
  1139. duDefaultTab = max(duDefaultTab, 1); // Don't ever divide by 0
  1140. return duDefaultTab - uCur%duDefaultTab; // Round up to nearest
  1141. }
  1142. /*
  1143. * CMeasurer::MeasureLineShift ()
  1144. *
  1145. * @mfunc
  1146. * Computes and returns the line u shift due to alignment
  1147. *
  1148. * @rdesc
  1149. * Line u shift due to alignment
  1150. *
  1151. * @comm
  1152. * Plain text is sensitive to StartIndent and RightIndent settings,
  1153. * but usually these are zero for plain text.
  1154. */
  1155. LONG CMeasurer::MeasureLineShift()
  1156. {
  1157. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::MeasureLineShift");
  1158. WORD wAlignment = _pPF->_bAlignment;
  1159. LONG uShift;
  1160. LONG dup;
  1161. CTxtEdit * ped = GetPed();
  1162. if(IsInOutlineView() || !IN_RANGE(PFA_RIGHT, wAlignment, PFA_CENTER))
  1163. return 0;
  1164. if(!_pdp->GetWordWrap())
  1165. dup = _pdp->GetDupView();
  1166. else
  1167. dup = LUtoDU(_dulLayout);
  1168. // Normal view with center or flush-right para. Move right accordingly
  1169. uShift = dup - _li._upStart - MeasureRightIndent() - _li._dup;
  1170. uShift -= ped->GetCaretWidth();
  1171. uShift = max(uShift, 0); // Don't allow alignment to go < 0
  1172. // Can happen with a target device
  1173. if(wAlignment == PFA_CENTER)
  1174. uShift /= 2;
  1175. return uShift;
  1176. }
  1177. /*
  1178. * CMeasurer::MeasureBullet()
  1179. *
  1180. * @mfunc
  1181. * Computes bullet/numbering dimensions
  1182. *
  1183. * @rdesc
  1184. * Return bullet/numbering string width
  1185. */
  1186. LONG CMeasurer::MeasureBullet()
  1187. {
  1188. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::MeasureBullet");
  1189. CCharFormat CF;
  1190. CCcs *pccs = GetCcsBullet(&CF);
  1191. LONG dup = 0;
  1192. if(pccs)
  1193. {
  1194. WCHAR szBullet[CCHMAXNUMTOSTR];
  1195. GetBullet(szBullet, pccs, &dup);
  1196. RecalcLineHeight(pccs, &CF);
  1197. pccs->Release();
  1198. }
  1199. return dup;
  1200. }
  1201. /*
  1202. * CMeasurer::GetBullet(pch, pccs, pdup)
  1203. *
  1204. * @mfunc
  1205. * Computes bullet/numbering string, string length, and width
  1206. *
  1207. * @rdesc
  1208. * Return bullet/numbering string length
  1209. */
  1210. LONG CMeasurer::GetBullet(
  1211. WCHAR *pch, //@parm Bullet string to receive bullet text
  1212. CCcs *pccs, //@parm CCcs to use
  1213. LONG *pdup) //@parm Out parm for bullet width
  1214. {
  1215. Assert(pccs && pch);
  1216. LONG cch = _pPF->NumToStr(pch, _li._bNumber);
  1217. LONG dupChar;
  1218. LONG i;
  1219. LONG dup = 0;
  1220. pch[cch++] = ' '; // Ensure a little extra space
  1221. for(i = cch; i--; dup += dupChar)
  1222. {
  1223. if(!pccs->Include(*pch++, dupChar))
  1224. {
  1225. TRACEERRSZSC("CMeasurer::GetBullet(): Error filling CCcs", E_FAIL);
  1226. }
  1227. }
  1228. if(pdup)
  1229. *pdup = dup;
  1230. return cch;
  1231. }
  1232. /*
  1233. * CMeasurer::GetCcsBullet(pCFRet)
  1234. *
  1235. * @mfunc
  1236. * Get CCcs for numbering/bullet font. If bullet is suppressed because
  1237. * this isn't the beginning of a paragraph (e.g., previous character is
  1238. * VT or if GetCcs() fails, it returns NULL.
  1239. *
  1240. * @rdesc
  1241. * ptr to bullet CCcs, or NULL (GetCcs() failed or not start of para)
  1242. *
  1243. * @devnote
  1244. * The calling chain must be protected by a CLock, since this present
  1245. * routine access the global (shared) FontCache facility.
  1246. */
  1247. CCcs * CMeasurer::GetCcsBullet(
  1248. CCharFormat *pCFRet) //@parm option character format to return
  1249. {
  1250. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::GetCcsBullet");
  1251. if(!_li._fFirstInPara)
  1252. return NULL; // Number/bullet suppressed
  1253. CCharFormat CF;
  1254. CCcs * pccs;
  1255. const CCharFormat * pCF;
  1256. CCharFormat * pCFUsed = pCFRet ? pCFRet : &CF;
  1257. // Bullet CF is given by that for EOP in bullet's paragraph.
  1258. CTxtPtr tp(_rpTX);
  1259. CFormatRunPtr rpCF(_rpCF);
  1260. rpCF.Move(tp.FindEOP(tomForward));
  1261. rpCF.AdjustBackward();
  1262. pCF = GetPed()->GetCharFormat(rpCF.GetFormat());
  1263. // Construct bullet (or numbering) CCharFormat
  1264. *pCFUsed = *pCF;
  1265. if(_pPF->_wNumbering == PFN_BULLET) // Traditional bullet uses
  1266. { // Symbol font bullet, but...
  1267. pCFUsed->_iCharRep = SYMBOL_INDEX,
  1268. pCFUsed->_bPitchAndFamily = FF_DONTCARE;
  1269. pCFUsed->_iFont = IFONT_SYMBOL;
  1270. }
  1271. // Since we always cook up bullet character format, no need to cache it
  1272. pccs = GetCcs(pCFUsed);
  1273. #if DEBUG
  1274. if(!pccs)
  1275. {
  1276. TRACEERRSZSC("CMeasurer::GetCcsBullet(): no CCcs", E_FAIL);
  1277. }
  1278. #endif // DEBUG
  1279. return pccs;
  1280. }
  1281. /*
  1282. * CMeasurer::SetNumber(wNumber)
  1283. *
  1284. * @mfunc
  1285. * Store number if numbered paragraph
  1286. */
  1287. void CMeasurer::SetNumber(
  1288. WORD wNumber)
  1289. {
  1290. _pPF = GetPF();
  1291. if(!_pPF->IsListNumbered())
  1292. wNumber = 0;
  1293. else if (!wNumber && !_pPF->IsNumberSuppressed())
  1294. wNumber = 1;
  1295. _wNumber = wNumber;
  1296. }
  1297. /*
  1298. * CMeasurer::FindCpDraw(cpStart, cobjectPrev, fLeft)
  1299. *
  1300. * @mfunc
  1301. * Find the cp corresponding to the nth previous object to be placed.
  1302. * (If a line stores a 2 in the _cObjectWrapLeft for example, it means
  1303. * you need to walk backwards 2 objects to find the object to be drawn
  1304. * on this line.)
  1305. *
  1306. * @rdesc
  1307. * cp corresponding to the nth previous object
  1308. */
  1309. LONG CMeasurer::FindCpDraw(
  1310. LONG cpStart,
  1311. int cobjectPrev,
  1312. BOOL fLeft)
  1313. {
  1314. LONG cch = 0;
  1315. LONG cObjects = -1;
  1316. while (cobjectPrev > 0)
  1317. {
  1318. // BUGBUG: this test should really be after the CountObjects() call,
  1319. // but we are making a change with minimal impact just before
  1320. // a major release.
  1321. if (!cObjects)
  1322. return tomForward;
  1323. cch += GetPed()->GetObjectMgr()->CountObjects(cObjects, cpStart + cch);
  1324. COleObject *pobj = GetObjectFromCp(cpStart + cch);
  1325. if (!pobj)
  1326. return tomForward;
  1327. if (pobj->FWrapTextAround() && pobj->FAlignToRight() == !fLeft)
  1328. cobjectPrev--;
  1329. }
  1330. return cpStart + cch;
  1331. }
  1332. /*
  1333. * CMeasurer::AddObjectToQueue(pobjAdd)
  1334. *
  1335. * @mfunc
  1336. * After formatting a line, update the current state of wrapped objects
  1337. */
  1338. void CMeasurer::AddObjectToQueue(
  1339. COleObject *pobjAdd)
  1340. {
  1341. if (!IsMeasure())
  1342. return;
  1343. for (int iobj = 0; iobj < _rgpobjWrap.Count(); iobj++)
  1344. {
  1345. COleObject *pobj = _rgpobjWrap.GetAt(iobj);
  1346. if (pobj == pobjAdd)
  1347. return;
  1348. }
  1349. COleObject **ppobj = _rgpobjWrap.Add(1, 0);
  1350. *ppobj = pobjAdd;
  1351. }
  1352. /*
  1353. * CMeasurer::CountQueueEntries(fLeft)
  1354. *
  1355. * @mfunc
  1356. * Return count of objects queued up
  1357. *
  1358. * @rdesc
  1359. * Count of objects queued up
  1360. */
  1361. int CMeasurer::CountQueueEntries(
  1362. BOOL fLeft)
  1363. {
  1364. int cEntries = 0;
  1365. for (int iobj = 0; iobj < _rgpobjWrap.Count(); iobj++)
  1366. {
  1367. COleObject *pobj = _rgpobjWrap.GetAt(iobj);
  1368. if (!pobj->FAlignToRight() == fLeft)
  1369. cEntries++;
  1370. }
  1371. return cEntries;
  1372. }
  1373. /*
  1374. * CMeasurer::RemoveFirstWrap(fLeft)
  1375. *
  1376. * @mfunc
  1377. * Remove the object from the queue--after it
  1378. * has been been placed.
  1379. */
  1380. void CMeasurer::RemoveFirstWrap(
  1381. BOOL fLeft)
  1382. {
  1383. for (int iobj = 0; iobj < _rgpobjWrap.Count(); iobj++)
  1384. {
  1385. COleObject *pobj = _rgpobjWrap.GetAt(iobj);
  1386. if (!pobj->FAlignToRight() == fLeft)
  1387. {
  1388. _rgpobjWrap.Remove(iobj, 1);
  1389. return;
  1390. }
  1391. }
  1392. }
  1393. /*
  1394. * CMeasurer::FindFirstWrapObj(fLeft)
  1395. *
  1396. * @mfunc
  1397. * Find the first object queued up to be wrapped.
  1398. *
  1399. * @rdesc
  1400. * First object queued up to be wrapped.
  1401. */
  1402. COleObject* CMeasurer::FindFirstWrapObj(
  1403. BOOL fLeft)
  1404. {
  1405. for (int iobj = 0; iobj < _rgpobjWrap.Count(); iobj++)
  1406. {
  1407. COleObject *pobj = _rgpobjWrap.GetAt(iobj);
  1408. if (!pobj->FAlignToRight() == fLeft)
  1409. return pobj;
  1410. }
  1411. return 0;
  1412. }
  1413. /*
  1414. * CMeasurer::XFromU(u)
  1415. *
  1416. * @mfunc
  1417. * Given a U position on a line, convert it to X. In
  1418. * RTL paragraphs, the U position of 0 on a line is
  1419. * on the right edge of the control.
  1420. *
  1421. * @rdesc
  1422. * x coordinate corresponding to u in current rotation
  1423. */
  1424. LONG CMeasurer::XFromU(LONG u)
  1425. {
  1426. if (_pPF->IsRtlPara())
  1427. {
  1428. CTxtEdit * ped = GetPed();
  1429. LONG uCaret = _pdp->IsMain() ? ped->GetCaretWidth() : 0;
  1430. LONG dupLayout = LUtoDU(_dulLayout);
  1431. if (_plo && _plo->IsNestedLayout())
  1432. ;
  1433. else if(!_pdp->GetWordWrap())
  1434. dupLayout = max(_pdp->GetDupLineMax(), _pdp->GetDupView());
  1435. return dupLayout - u - uCaret;
  1436. }
  1437. return u;
  1438. }
  1439. LONG CMeasurer::UFromX(LONG x)
  1440. {
  1441. if (_pPF->IsRtlPara())
  1442. return XFromU(x);
  1443. return x;
  1444. }
  1445. #ifndef NOLINESERVICES
  1446. extern BOOL g_fNoLS;
  1447. extern BOOL g_OLSBusy;
  1448. /*
  1449. * CMeasurer::GetPols()
  1450. *
  1451. * @mfunc
  1452. * Get ptr to LineServices object. If LineServices not enabled,
  1453. * return NULL.
  1454. *
  1455. * @rdesc
  1456. * POLS
  1457. */
  1458. COls *CMeasurer::GetPols()
  1459. {
  1460. CTxtEdit *ped = GetPed();
  1461. if(g_fNoLS || !ped->fUseLineServices()) // Not using LineServices
  1462. return NULL;
  1463. if(!g_pols) // Starting up LS:
  1464. g_pols = new COls(); // create new COls
  1465. if(g_pols) // Have the COls
  1466. {
  1467. if(g_pols->Init(this) != NOERROR) // Switch to new one
  1468. {
  1469. delete g_pols;
  1470. g_pols = NULL;
  1471. }
  1472. g_OLSBusy = TRUE;
  1473. UpdatePF();
  1474. }
  1475. return g_pols;
  1476. }
  1477. #endif