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.

1405 lines
40 KiB

  1. /*
  2. * @doc INTERNAL
  3. *
  4. * @module LAYOUT.CPP -- CLayout class |
  5. *
  6. * Recursive structure which contains an array of lines.
  7. *
  8. * Owner:<nl>
  9. * Murray Sargent: Initial table implementation
  10. * Keith Curtis: Factored into a separate class for
  11. * performance, simplicity
  12. *
  13. * Copyright (c) 1995-2000, Microsoft Corporation. All rights reserved.
  14. */
  15. //FUTURE: (KeithCu) More stuff should be put into here, e.g., RecalcLines,
  16. //The CDisplayML should just be a class that knows about Device descriptors,
  17. //pagination and scrolling, etc., i.e., things that are the same for all
  18. //layouts and things that apply only to the outermost layout. This code knows
  19. //how to manage and update recursive arrays of lines.
  20. #include "_common.h"
  21. #include "_dispml.h"
  22. #include "_select.h"
  23. #include "_measure.h"
  24. #include "_render.h"
  25. void CLayout::DeleteSubLayouts(
  26. LONG ili,
  27. LONG cLine)
  28. {
  29. CLine *pli = Elem(ili);
  30. if(cLine < 0)
  31. cLine = Count();
  32. LONG cLineMax = Count() - ili;
  33. cLine = min(cLine, cLineMax);
  34. AssertSz(ili >= 0 && cLine >= 0, "DeleteSubLayouts: illegal line count");
  35. // Delete sublayouts
  36. for(; cLine--; pli++)
  37. delete pli->GetPlo();
  38. }
  39. /*
  40. * CLayout::VposFromLine(pdp, ili)
  41. *
  42. * @mfunc
  43. * Computes top of line position
  44. *
  45. * @rdesc
  46. * top position of given line (relative to the first line)
  47. */
  48. LONG CLayout::VposFromLine(
  49. CDisplayML *pdp, //@parm Parent display
  50. LONG ili) //@parm Line we're interested in
  51. {
  52. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLayout::VposFromLine");
  53. LONG cli = 0, vPos = 0;
  54. CLine *pli = 0;
  55. if (IsNestedLayout())
  56. {
  57. Assert(!IsTableRow()); // _iPFCell layouts are horizontal
  58. Assert(ili < Count());
  59. cli = ili;
  60. pli = Elem(0);
  61. vPos = 0;
  62. }
  63. else
  64. {
  65. if(!pdp->WaitForRecalcIli(ili)) // out of range, use last valid line
  66. {
  67. ili = Count() - 1;
  68. ili = (ili > 0) ? ili : 0;
  69. }
  70. cli = ili - pdp->_iliFirstVisible;
  71. pli = Elem(pdp->_iliFirstVisible);
  72. vPos = pdp->_vpScroll + pdp->_dvpFirstVisible;
  73. }
  74. while(cli > 0)
  75. {
  76. vPos += pli->GetHeight();
  77. cli--;
  78. pli++;
  79. }
  80. while(cli < 0)
  81. {
  82. pli--;
  83. vPos -= pli->GetHeight();
  84. cli++;
  85. }
  86. AssertSz(vPos >= 0, "VposFromLine height less than 0");
  87. return vPos;
  88. }
  89. /*
  90. * CLayout::LineFromVPos(pdp, vPos, pdvpLine, pcpFirst)
  91. *
  92. * @mfunc
  93. * Computes line at given y position. Returns top of line vPos
  94. * cp at start of line cp, and line index.
  95. *
  96. * @rdesc
  97. * index of line found
  98. */
  99. LONG CLayout::LineFromVpos(
  100. CDisplayML *pdp, //@parm Parent display
  101. LONG vPos, //@parm Vpos to look for (relative to first line)
  102. LONG *pdvpLine, //@parm Returns vPos at top of line /r first line (can be NULL)
  103. LONG *pcpFirst) //@parm Returns cp at start of line (can be NULL)
  104. {
  105. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLayout::LineFromVpos");
  106. LONG cpLi;
  107. LONG dy;
  108. LONG ili = 0;
  109. LONG yLi;
  110. CLine *pli;
  111. if(IsNestedLayout())
  112. goto BindFrom0;
  113. yLi = pdp->_vpScroll;
  114. if(!pdp->WaitForRecalc(-1, pdp->_vpScroll))
  115. {
  116. yLi = 0;
  117. cpLi = 0;
  118. goto done;
  119. }
  120. cpLi = pdp->_cpFirstVisible;
  121. ili = pdp->_iliFirstVisible;
  122. if(!pdp->IsInPageView())
  123. yLi += pdp->_dvpFirstVisible;
  124. dy = vPos - yLi;
  125. if(dy < 0 && -dy <= pdp->_vpScroll)
  126. {
  127. // Closer to first visible line than to first line:
  128. // go backwards from first visible line.
  129. while(vPos < yLi && ili > 0)
  130. {
  131. pli = Elem(--ili);
  132. yLi -= pli->GetHeight();
  133. cpLi -= pli->_cch;
  134. }
  135. }
  136. else
  137. {
  138. if(dy < 0)
  139. {
  140. // Closer to first line than to first visible line:
  141. // so start at first line.
  142. BindFrom0:
  143. cpLi = _cpMin;
  144. yLi = 0;
  145. ili = 0;
  146. }
  147. pli = Elem(ili);
  148. while(vPos > yLi && ili < Count()-1)
  149. {
  150. yLi += pli->GetHeight();
  151. cpLi += pli->_cch;
  152. ili++;
  153. pli++;
  154. }
  155. if(vPos < yLi && ili > 0)
  156. {
  157. ili--;
  158. pli--;
  159. yLi -= pli->GetHeight();
  160. cpLi -= pli->_cch;
  161. }
  162. }
  163. done:
  164. if(pdvpLine)
  165. *pdvpLine = yLi;
  166. if(pcpFirst)
  167. *pcpFirst = cpLi;
  168. return ili;
  169. }
  170. /*
  171. * CLayout::FindTopCell(&cch, pli, &ili, dul, &dy, pdvp, pliMain,iliMain, pcLine)
  172. *
  173. * @mfunc
  174. * Find cch and height change back to current position in
  175. * top cell corresponding to the current vertically merged cell.
  176. * Enter with cch = cch from current cell back to start of row.
  177. *
  178. * @rdesc
  179. * target line in top cell
  180. */
  181. CLine * CLayout::FindTopCell(
  182. LONG & cch, //@parm In/out parm for cch back to top
  183. CLine * pli, //@parm Table-row line
  184. LONG & ili, //@parm Corresponding line index & return ili
  185. LONG dul, //@parm Current cell x offset
  186. LONG & dy, //@parm In/Out parm for y offset in top cell
  187. LONG * pdvp, //@parm TopCellHeight - heights of inbetween rows
  188. CLine * pliMain, //@parm Line preceding first line accessible by pli
  189. LONG iliMain, //@parm Line index corresponding to pliMain
  190. LONG * pcLine) //@parm Count() of possible CLayout for returned pli
  191. {
  192. LONG cCell;
  193. LONG iCell;
  194. CLayout * plo;
  195. const CELLPARMS *prgCellParms;
  196. const CParaFormat *pPF;
  197. #ifdef DEBUG
  198. BYTE bTableLevel = pli->GetPlo()->GetPFCells()->_bTableLevel;
  199. #endif
  200. if(pcLine)
  201. *pcLine = 0; // Default no lines in case of error
  202. // Need to use uCell to identify cell rather than iCell, since
  203. // horizontal merge can change iCell from row to row
  204. do // Backup row by row
  205. {
  206. if(ili > 0)
  207. {
  208. pli--; // Go to previous row
  209. ili--;
  210. }
  211. else if(pliMain)
  212. {
  213. pli = pliMain;
  214. ili = iliMain;
  215. pliMain = NULL; // Switch to pliMain only once!
  216. }
  217. else
  218. {
  219. AssertSz(FALSE, "CLayout::FindTopCell: no accessible top cell");
  220. return NULL;
  221. }
  222. plo = pli->GetPlo(); // Get its cell display
  223. if(!plo || !plo->IsTableRow()) // Illegal structure or not table row
  224. {
  225. AssertSz(FALSE, "CLayout::FindTopCell: no accessible top cell");
  226. return NULL;
  227. }
  228. pPF = plo->GetPFCells();
  229. AssertSz(pPF->_bTableLevel == bTableLevel,
  230. "CLayout::FindTopCell: no accessible top cell");
  231. prgCellParms = pPF->GetCellParms();
  232. cCell = plo->Count();
  233. iCell = prgCellParms->ICellFromUCell(dul, cCell);
  234. dy += pli->GetHeight(); // Add row height
  235. cch += pli->_cch; // Add in cch for whole row
  236. }
  237. while(!IsTopCell(prgCellParms[iCell].uCell));
  238. cch -= 2; // Sub cch for StartRow delim
  239. pli = plo->Elem(0); // Point at 1st cell in row
  240. for(ili = 0; ili < iCell; ili++)// Sub cch's for cells
  241. cch -= (pli++)->_cch; // preceding iCellth cell
  242. if(pdvp) // Return top-cell height - heights of
  243. *pdvp = pli->GetHeight() - dy;// cells in between
  244. LONG cLine = 0;
  245. LONG dvpBrdrTop = plo->_dvpBrdrTop;
  246. ili = 0;
  247. dy -= dvpBrdrTop;
  248. plo = pli->GetPlo();
  249. if(plo) // Top cell is multiline
  250. {
  251. cLine = plo->Count();
  252. pli = plo->Elem(0); // Advance pli to line in plo
  253. if(pli->IsNestedLayout())
  254. dy += dvpBrdrTop;
  255. while(ili < cLine && dy >= pli->GetHeight()) // nearest to input position
  256. {
  257. dy -= pli->GetHeight();
  258. ili++;
  259. if(ili == cLine) // Done: leave pli pointing at last line
  260. break;
  261. cch -= pli->_cch;
  262. pli++;
  263. }
  264. }
  265. if(pcLine)
  266. *pcLine = cLine;
  267. return pli;
  268. }
  269. /*
  270. * CLayout::FindTopRow(pli, ili, pliMain, iliMain, pPF)
  271. *
  272. * @mfunc
  273. * Find CLine for top row in a table
  274. *
  275. * @rdesc
  276. * CLine for top row in table
  277. */
  278. CLine * CLayout::FindTopRow(
  279. CLine * pli, //@parm Entry table-row line
  280. LONG ili, //@parm Corresponding line index
  281. CLine * pliMain, //@parm Line preceding first line accessible by pli
  282. LONG iliMain, //@parm Line index corresponding to pliMain
  283. const CParaFormat *pPF) //@parm CParaFormat for entry plo
  284. {
  285. BYTE bAlignment = pPF->_bAlignment; // Target row must have same
  286. BYTE bTableLevel = pPF->_bTableLevel; // alignment and level
  287. CLine * pliLast;
  288. CLayout *plo;
  289. do // Backup row by row
  290. {
  291. pliLast = pli; // Last line pointing at row in table
  292. if(ili > 0)
  293. {
  294. pli--; // Go to previous line
  295. ili--;
  296. }
  297. else if(pliMain) // More lines to go back to
  298. {
  299. pli = pliMain;
  300. ili = iliMain;
  301. pliMain = NULL; // Switch to pliMain only once!
  302. }
  303. else
  304. break;
  305. plo = pli->GetPlo(); // Get its cell display
  306. if(!plo || !plo->IsTableRow())
  307. break;
  308. pPF = plo->GetPFCells();
  309. }
  310. while(pPF->_bAlignment == bAlignment && pPF->_bTableLevel == bTableLevel);
  311. return pliLast;
  312. }
  313. /*
  314. * CLayout::GetCFCells()
  315. *
  316. * @mfunc
  317. * Return CCharFormat for the table row described by this CLayout
  318. *
  319. * @rdesc
  320. * Table row CCharFormat
  321. */
  322. const CCharFormat* CLayout::GetCFCells()
  323. {
  324. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CLayout::GetCFCells");
  325. Assert(_iCFCells >= 0);
  326. const CCharFormat *pCF;
  327. if(FAILED(GetCharFormatCache()->Deref(_iCFCells, &pCF)))
  328. {
  329. AssertSz(FALSE, "CLayout::GetCFCells: couldn't deref _iCFCells");
  330. pCF = NULL;
  331. }
  332. return pCF;
  333. }
  334. /*
  335. * CLayout::GetPFCells()
  336. *
  337. * @mfunc
  338. * Return CParaFormat for the table row described by this CLayout
  339. *
  340. * @rdesc
  341. * Table row CParaFormat
  342. */
  343. const CParaFormat* CLayout::GetPFCells() const
  344. {
  345. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CLayout::GetPFCells");
  346. Assert(_iPFCells >= 0);
  347. const CParaFormat *pPF;
  348. if(FAILED(GetParaFormatCache()->Deref(_iPFCells, &pPF)))
  349. {
  350. AssertSz(FALSE, "CLayout::GetPF: couldn't deref _iPFCells");
  351. pPF = NULL;
  352. }
  353. return pPF;
  354. }
  355. /*
  356. * CLayout::GetLORowAbove(pli, ili, pliMain, iliMain)
  357. *
  358. * @mfunc
  359. * Return CLayout for the table row described by the line above pli.
  360. * If not a table row, return NULL.
  361. *
  362. * @rdesc
  363. * Table row CLayout for row above pli's
  364. */
  365. const CLayout* CLayout::GetLORowAbove(
  366. CLine * pli, //@parm Entry table-row line
  367. LONG ili, //@parm Corresponding line index
  368. CLine * pliMain, //@parm Line preceding first line accessible by pli
  369. LONG iliMain) //@parm Line index corresponding to pliMain
  370. {
  371. if(!ili && pliMain && iliMain) // More lines to go back to
  372. {
  373. pli = pliMain;
  374. ili = iliMain;
  375. }
  376. if(ili)
  377. {
  378. CLayout *plo = (pli - 1)->GetPlo(); // Get cell display for row above
  379. if(plo && plo->IsTableRow())
  380. return plo;
  381. }
  382. return NULL; // No line above
  383. }
  384. /*
  385. * CLayout::CpFromPoint(&me, pt, prcClient, prtp, prp, fAllowEOL, phit,
  386. * pdispdim, pcpActual, pliParent, iliParent)
  387. * @mfunc
  388. * Determine cp at given point
  389. *
  390. * @devnote
  391. * --- Use when in-place active only ---
  392. *
  393. * @rdesc
  394. * Computed cp, -1 if failed
  395. */
  396. LONG CLayout::CpFromPoint(
  397. CMeasurer &me, //@parm Measurer
  398. POINTUV pt, //@parm Point to compute cp at (client coords)
  399. const RECTUV *prcClient,//@parm Client rectangle (can be NULL if active).
  400. CRchTxtPtr * const prtp,//@parm Returns text pointer at cp (may be NULL)
  401. CLinePtr * const prp, //@parm Returns line pointer at cp (may be NULL)
  402. BOOL fAllowEOL, //@parm Click at EOL returns cp after CRLF
  403. HITTEST * phit, //@parm Out parm for hit-test value
  404. CDispDim * pdispdim, //@parm Out parm for display dimensions
  405. LONG *pcpActual, //@parm Out cp that pt is above
  406. CLine * pliParent, //@parm Parent pli for table row displays
  407. LONG iliParent) //@parm Parent ili corresponding to pli
  408. {
  409. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLayout::CpFromPoint");
  410. LONG cch = 0;
  411. LONG cp = 0;
  412. HITTEST hit = HT_Nothing;
  413. LONG ili;
  414. CLine * pli;
  415. CLayout *plo = NULL;
  416. RECTUV rcView;
  417. int v = pt.v; // Save input y coordinate
  418. LONG yLine = 0;
  419. CDisplayML *pdp = (CDisplayML*) me.GetPdp();
  420. if (IsNestedLayout())
  421. rcView = *prcClient;
  422. else
  423. {
  424. pdp->GetViewRect(rcView, prcClient);
  425. pt.v += pdp->GetVpScroll();
  426. if(pt.u >= 0) // If x coordinate is within view,
  427. pt.u += pdp->GetUpScroll(); // adjust by scroll value
  428. }
  429. if(phit)
  430. *phit = HT_Nothing; // Default in case early return
  431. // Get line under hit
  432. if(IsTableRow()) // This display is a table row
  433. { // Shrink to cell text boundaries
  434. pli = Elem(0); // Point at starting cell CLine
  435. // Move over to start of cells
  436. const CParaFormat *pPFCells = GetPFCells();
  437. LONG dul = 0;
  438. LONG dulRTLRow = pPFCells->GetRTLRowLength();
  439. LONG dup = 0;
  440. BOOL fCellLow;
  441. LONG h = me.LUtoDU(pPFCells->_dxOffset);
  442. const CELLPARMS *prgCellParms = pPFCells->GetCellParms();
  443. LONG u; // Tracks start of text in cell
  444. LONG u0 = pli->_upStart;
  445. LONG uCell = 0;
  446. pt.v -= _dvpBrdrTop; // Subtract off border top
  447. cp = _cpMin;
  448. if(dulRTLRow)
  449. u0 += me.LUtoDU(dulRTLRow);
  450. ili = 0;
  451. while(1)
  452. {
  453. u = u0 + dup + h; // Indent in current cell
  454. cch = cp - _cpMin;
  455. uCell = prgCellParms[ili].uCell;
  456. fCellLow = IsLowCell(uCell);
  457. dul += GetCellWidth(uCell);
  458. me.SetDulLayout(GetCellWidth(uCell) - 2*pPFCells->_dxOffset);
  459. dup = me.LUtoDU(dul);
  460. if(!dulRTLRow && pt.u < u0 + dup ||// pt.u is inside current cell
  461. dulRTLRow && pt.u > u0 - dup)
  462. {
  463. LONG ili0 = iliParent;
  464. if(fCellLow) // Cell merged vertically
  465. { // with the one above it
  466. LONG dy = pt.v;
  467. CLine *pli0 = FindTopCell(cch, pliParent, ili0, dul, dy,
  468. NULL, NULL, 0, NULL);
  469. if(pli0)
  470. { // Found top cell
  471. cch += 2; // Include cch of row-start delim
  472. pli = pli0; // Use its pli and backup
  473. ili = ili0;
  474. cp -= cch; // Backup to start of pli
  475. pt.v += dy;
  476. }
  477. }
  478. if(!dulRTLRow && pt.u < u)
  479. { // In cell gap, so select cell
  480. hit = HT_LeftOfText;
  481. cch = 0; // Setup for start of row
  482. goto finish;
  483. }
  484. break;
  485. }
  486. cp += pli->_cch; // Add in cell's cch
  487. ili++;
  488. if(ili == Count())
  489. {
  490. hit = HT_RightOfText;
  491. goto finish;
  492. }
  493. pli++;
  494. }
  495. LONG dupCell = me.LUtoDU(GetCellWidth(uCell));
  496. if(dulRTLRow)
  497. pt.u -= me.LUtoDU(dulRTLRow - dul) + h;
  498. else
  499. pt.u -= dup - dupCell + h;
  500. rcView.right = dupCell - 2*h;
  501. pt.v -= GetVertAlignShift(uCell, pli->GetHeight());
  502. }
  503. else // This display isn't a table row
  504. {
  505. // Adjust coordinates relative to view origin
  506. rcView.right -= rcView.left;
  507. pt.u -= rcView.left;
  508. pt.v -= rcView.top;
  509. ili = LineFromVpos(pdp, pt.v, &yLine, &cp);
  510. if(ili < 0)
  511. return -1;
  512. pli = Elem(ili);
  513. if(yLine + pli->GetHeight() < pt.v)
  514. hit = HT_BelowText; // Return hit below text
  515. }
  516. rcView.left = 0;
  517. rcView.top = 0;
  518. AssertSz(pli || !ili, "CLayout::CpFromPoint invalid line pointer");
  519. if(pli) // Line exists, even it it's
  520. { // above or below current screen
  521. HITTEST hit0;
  522. if(v < rcView.top) // Note if hit occurs above or
  523. hit = HT_AboveScreen; // below text
  524. if(v > rcView.bottom && !IsNestedLayout())
  525. hit = HT_BelowText;
  526. plo = pli->GetPlo();
  527. pt.v -= yLine;
  528. if(plo) // Child layout
  529. {
  530. pt.u -= pli->_upStart;
  531. plo->_cpMin = cp; // Update child's _cpMin
  532. if(plo->IsTableRow()) // Table row
  533. {
  534. plo->_cpMin += 2; // Bypass TR start delimiter
  535. if(pt.u < 0)
  536. {
  537. plo = NULL;
  538. hit = HT_LeftOfText; // Return hit left of text
  539. Assert(cch >= 0); // (should be row)
  540. goto finish;
  541. }
  542. }
  543. cp = plo->CpFromPoint(me, pt, &rcView, prtp, prp, fAllowEOL,
  544. &hit0, pdispdim, pcpActual, pli, ili);
  545. if(cp == -1)
  546. return -1;
  547. cch = cp - _cpMin;
  548. }
  549. else // Leaf line
  550. {
  551. me.SetLayout(this);
  552. me.SetCp(cp);
  553. // Support khyphChangeAfter
  554. me.SetIhyphPrev(ili > 0 ? (pli - 1)->_ihyph : 0);
  555. // Get character in line
  556. cch = pli->CchFromUp(me, pt, pdispdim, &hit0, pcpActual);
  557. // Don't allow click at EOL to select EOL marker and take into
  558. // account single line edits as well
  559. if(cch == pli->_cch && pli->_cchEOP && (!fAllowEOL || me.GetPrevChar() == CELL))
  560. {
  561. // Adjust position on line by amount backed up. OK for
  562. // me._rpCF and me._rpPF to get out of sync with me._rpTX,
  563. // since they're not needed for me.GetCp().
  564. cch += me._rpTX.BackupCRLF();
  565. }
  566. cp = me.GetCp();
  567. }
  568. if(hit != HT_BelowText && hit != HT_AboveScreen || hit0 == HT_RightOfText)
  569. hit = hit0;
  570. }
  571. finish:
  572. if(!plo) // Store info from leaf line
  573. {
  574. if(prtp)
  575. prtp->SetCp(cp);
  576. if(prp)
  577. {
  578. Assert(cch >= 0);
  579. prp->Set(ili, cch, this);
  580. }
  581. }
  582. if (phit)
  583. *phit = hit;
  584. return cp;
  585. }
  586. /*
  587. * CLayout::PointFromTp(&me, rtp, prcClient, fAtEnd, pt, prp, taMode, pdispdim)
  588. *
  589. * @mfunc
  590. * Determine coordinates at given tp
  591. *
  592. * @devnote
  593. * --- Use when in-place active only ---
  594. *
  595. * @rdesc
  596. * line index at cp, -1 if error
  597. */
  598. LONG CLayout::PointFromTp(
  599. CMeasurer &me, //@parm Measurer
  600. const CRchTxtPtr &rtp, //@parm Text ptr to get coordinates at
  601. const RECTUV *prcClient,//@parm Client rectangle (can be NULL if active).
  602. BOOL fAtEnd, //@parm Return end of prev line for ambiguous cp
  603. POINTUV & pt, //@parm Returns point at cp in client coords
  604. CLinePtr * const prp, //@parm Returns line pointer at tp (may be null)
  605. UINT taMode, //@parm Text Align mode: top, baseline, bottom
  606. CDispDim * pdispdim) //@parm Out parm for display dimensions
  607. {
  608. LONG cp = rtp.GetCp();
  609. LONG dy = 0;
  610. RECTUV rcView;
  611. CDisplayML *pdp = (CDisplayML*) me.GetPdp();
  612. CLinePtr rp(pdp);
  613. if(!pdp->WaitForRecalc(cp, -1))
  614. return -1;
  615. if(!IsNestedLayout()) // Main display
  616. {
  617. if(!rp.SetCp(cp, fAtEnd))
  618. return -1;
  619. pdp->GetViewRect(rcView, prcClient);
  620. pt.u = rcView.left - pdp->_upScroll;
  621. pt.v = rcView.top - pdp->_vpScroll;
  622. }
  623. else // Subdisplay
  624. {
  625. rp.Init(*this);
  626. rp.BindToCp(cp - _cpMin);
  627. if(fAtEnd && !IsTableRow()) // Ambiguous-cp caret position
  628. rp.AdjustBackward(); // belongs at prev EOL
  629. rcView = *prcClient;
  630. pt.u = rcView.left;
  631. pt.v = rcView.top;
  632. }
  633. AssertSz(pdp->_ped->_fInPlaceActive || prcClient, "Invalid client rect");
  634. LONG ili = rp.GetLineIndex();
  635. CLine *pli = NULL;
  636. CLayout *plo = NULL;
  637. LONG xEnd = -1; // pt.u to use at end of table row
  638. if(IsTableRow()) // This layout is a table row
  639. { // Shrink to cell text boundaries
  640. const CParaFormat *pPFCells = GetPFCells();
  641. const CELLPARMS * prgCellParms = pPFCells->GetCellParms();
  642. LONG dul = 0;
  643. LONG dulRTLRow = pPFCells->GetRTLRowLength();
  644. LONG h = me.LUtoDU(pPFCells->_dxOffset);
  645. LONG i;
  646. cp = _cpMin;
  647. pli = Elem(0);
  648. for(i = 0; i < ili; i++, pli++)
  649. {
  650. dul += GetCellWidth(prgCellParms[i].uCell);
  651. cp += pli->_cch;
  652. }
  653. LONG uCell = prgCellParms[ili].uCell;
  654. me.SetDulLayout(GetCellWidth(uCell) - 2 * pPFCells->_dxOffset);
  655. if(dulRTLRow)
  656. {
  657. if(dul < dulRTLRow)
  658. {
  659. uCell = prgCellParms[ili + 1].uCell;
  660. dul += GetCellWidth(prgCellParms[i].uCell);
  661. }
  662. dul = dulRTLRow - dul;
  663. }
  664. rcView.left = pt.u + me.LUtoDU(dul) + h;
  665. rcView.right = pt.u + me.LUtoDU(dul + GetCellWidth(uCell)) - h;
  666. pt.u = rcView.left;
  667. if(!GetCellWidth(uCell))
  668. {
  669. pt.v += _dvp;
  670. goto done;
  671. }
  672. if(ili + 1 == Count() && rp->_cch == rp.GetIch())
  673. {
  674. xEnd = rcView.right + h + 1;
  675. if(dulRTLRow)
  676. xEnd = rcView.left - h - 1;
  677. }
  678. pt.v += GetVertAlignShift(uCell, pli->GetHeight());
  679. if(!(taMode & TA_CELLTOP))
  680. pt.v += _dvpBrdrTop;
  681. }
  682. else // This layout isn't a table row
  683. {
  684. pt.v += VposFromLine(pdp, ili);
  685. cp -= rp.GetIch();
  686. }
  687. pli = Elem(ili);
  688. plo = pli->GetPlo();
  689. if(plo) // Line has child display
  690. { // Define child rcView and delegate
  691. RECTUV rc; // to child
  692. pt.u += pli->_upStart;
  693. rc.left = pt.u;
  694. rc.right = pt.u + rcView.right - rcView.left;
  695. rc.top = pt.v;
  696. rc.bottom = pt.v + pli->GetHeight();
  697. plo->_cpMin = cp; // Update child display's _cpMin
  698. if(plo->IsTableRow())
  699. plo->_cpMin += 2; // Bypass table row start code
  700. if(plo->PointFromTp(me, rtp, &rc, fAtEnd, pt, prp, taMode, pdispdim) == -1)
  701. return -1;
  702. }
  703. else // Line is a leaf line
  704. {
  705. me.SetLayout(this);
  706. me.Move(-rp.GetIch()); // Backup to start of line
  707. me.NewLine(*rp); // Measure from there to where we are
  708. //Support khyphChangeAfter
  709. me.SetIhyphPrev(ili > 0 ? (pli - 1)->_ihyph : 0);
  710. LONG xCalc = rp->UpFromCch(me, rp.GetIch(), taMode, pdispdim, &dy);
  711. if(pt.u + xCalc <= rcView.right || !pdp->GetWordWrap() || pdp->GetTargetDev())
  712. {
  713. // Width is in view or there is no wordwrap so just
  714. // add the length to the point.
  715. pt.u += xCalc;
  716. }
  717. else
  718. pt.u = rcView.right; //Hit-test went too far, limit it.
  719. pt.v += dy;
  720. }
  721. if(xEnd != -1)
  722. pt.u = xEnd; // Return x coord at end of table row
  723. done:
  724. if(prp && !plo)
  725. *prp = rp; // Return innermost rp
  726. return rp; // Return outermost iRun
  727. }
  728. /*
  729. * CLayout::Measure(&me, pli, ili, uiFlags, pliTarget, iliMain, pliMain, pdvpExtra)
  730. *
  731. * @mfunc
  732. * Computes line break (based on target device) and fills
  733. * in *pli with resulting metrics on rendering device
  734. *
  735. * @rdesc
  736. * TRUE if OK
  737. */
  738. BOOL CLayout::Measure (
  739. CMeasurer& me, //@parm Measurer pointing at text to measure
  740. CLine * pli, //@parm Line to store result in
  741. LONG ili, //@parm Line index corresponding to pli
  742. UINT uiFlags, //@parm Flags
  743. CLine * pliTarget, //@parm Returns target-device line metrics (optional)
  744. LONG iliMain, //@parm Line index corresponding to pliMain
  745. CLine * pliMain, //@parm Line preceding 1st line in pli layout (optional)
  746. LONG * pdvpExtra) //@parm Returns extra line height for vmrged cells (opt)
  747. //REVIEW (keithcu) pliTarget is busted in the recursive case.
  748. {
  749. CTxtEdit * ped = me.GetPed();
  750. LONG cchText = ped->GetTextLength();
  751. LONG cpSave = me.GetCp();
  752. CLine * pliNew;
  753. const CDisplayML * pdp = (const CDisplayML *)me.GetPdp();
  754. const CParaFormat *pPF = me.GetPF();
  755. // Measure one line, which is either a table row or a line in a paragraph
  756. if(pPF->IsTableRowDelimiter())
  757. {
  758. // Measure table row, which is modeled as a CLayout with one
  759. // CLine per cell. In the backing store, table rows start with
  760. // the two chars STARTFIELD CR and end with ENDFIELD CR. Cells
  761. // are delimited by CELL.
  762. LONG cpStart = me.GetCp();
  763. LONG dul = 0;
  764. LONG dxCell = 0;
  765. LONG dvp = 0;
  766. LONG dvpMax = 0;
  767. CLayout * plo = new CLayout();
  768. const CLayout * ploAbove = GetLORowAbove(pli, ili, pliMain, iliMain);
  769. const CELLPARMS *prgCellParms = pPF->GetCellParms();
  770. if(!plo)
  771. return FALSE;
  772. plo->_iCFCells = me.Get_iCF();
  773. plo->_iPFCells = me.Get_iPF();
  774. pli->SetPlo(plo);
  775. AssertSz(pPF->_bTabCount && me.GetChar() == STARTFIELD, "Invalid table-row header");
  776. me.Move(2);
  777. AssertSz(me.GetPrevChar() == CR, "Invalid table-row header");
  778. plo->_cpMin = me.GetCp();
  779. // Save current values
  780. LONG dulLayoutOld = me.GetDulLayout();
  781. LONG dvlBrdrTop = 0;
  782. LONG dvlBrdrBot = 0;
  783. const CLayout *ploOld = me.GetLayout();
  784. CArray <COleObject*> rgpobjWrapOld;
  785. me._rgpobjWrap.TransferTo(rgpobjWrapOld);
  786. // Create CLines for each cell and measure them
  787. for(LONG iCell = 0; iCell < pPF->_bTabCount; iCell++)
  788. {
  789. me.SetNumber(0);
  790. LONG uCell = prgCellParms[iCell].uCell;
  791. dxCell = GetCellWidth(uCell);
  792. dul += dxCell;
  793. // Add a line for the next cell
  794. pliNew = plo->Add(1, NULL);
  795. if(!pliNew)
  796. return FALSE;
  797. LONG dvl = prgCellParms[iCell].GetBrdrWidthTop();
  798. dvlBrdrTop = max(dvlBrdrTop, dvl);
  799. dvl = prgCellParms[iCell].GetBrdrWidthBottom();
  800. dvlBrdrBot = max(dvlBrdrBot, dvl);
  801. if(!ploAbove)
  802. uCell &= ~fLowCell; // Can't be a low cell if no row above
  803. AssertSz(!IsLowCell(uCell) || me.GetChar() == NOTACHAR,
  804. "CLayout::Measure: invalid low cell");
  805. me.SetLayout(plo);
  806. me.SetDulLayout(dxCell - 2*pPF->_dxOffset);
  807. plo->Measure(me, pliNew, iCell, uiFlags | MEASURE_FIRSTINPARA, pliTarget, iliMain, pliMain);
  808. if(IsLowCell(uCell))
  809. {
  810. // If a low cell in set of vertically merged cells, check
  811. // if corresponding cell on next row is also merged
  812. CPFRunPtr rp(me);
  813. rp.FindRowEnd(pPF->_bTableLevel);
  814. const CParaFormat *pPF1 = rp.GetPF();
  815. BOOL fBottomCell = !pPF1->IsTableRowDelimiter();
  816. if(!fBottomCell)
  817. {
  818. const CELLPARMS *prgCellParms1 = pPF1->GetCellParms();
  819. LONG iCell1 = prgCellParms1->ICellFromUCell(dul, pPF1->_bTabCount);
  820. if(iCell1 >= 0 && !IsLowCell(prgCellParms1[iCell1].uCell))
  821. fBottomCell = TRUE;
  822. }
  823. if(fBottomCell)
  824. {
  825. // Need to include top cell in current row height
  826. // calculation
  827. LONG cch = me.GetCp() - cpStart;
  828. LONG dy1 = 0;
  829. LONG iliT = ili;
  830. LONG dvpCell = 0;
  831. if(!FindTopCell(cch, pli, iliT, dul, dy1, &dvpCell, pliMain, iliMain, NULL))
  832. uCell &= ~fLowCell; // Not a valid low cell
  833. else if(dvpCell > 0)
  834. dvp = max(dvp, dvpCell);
  835. }
  836. }
  837. if(!IsVertMergedCell(uCell) && dxCell || !dvp && iCell == pPF->_bTabCount - 1)
  838. dvp = max(pliNew->GetHeight(), dvp);
  839. dvpMax = max(dvpMax, pliNew->GetHeight());
  840. }
  841. //Restore original values
  842. me.SetDulLayout(dulLayoutOld);
  843. me.SetLayout(ploOld);
  844. me.SetIhyphPrev(0);
  845. me._rgpobjWrap.Clear(AF_DELETEMEM);
  846. rgpobjWrapOld.TransferTo(me._rgpobjWrap);
  847. #ifdef DEBUG
  848. // Bypass table-row terminator
  849. if(me.GetChar() != ENDFIELD)
  850. me._rpTX.MoveGapToEndOfBlock();
  851. AssertSz(me.GetPrevChar() == CELL && pPF->_bTabCount == plo->Count(),
  852. "Incorrect table cell count");
  853. AssertSz(me.GetChar() == ENDFIELD,
  854. "CLayout::Measure: invalid table-row terminator");
  855. me._rpPF.AdjustForward();
  856. const CParaFormat *pPFme = me.GetPF();
  857. AssertSz(pPFme->IsTableRowDelimiter(),
  858. "CLayout::Measure: invalid table-row terminator");
  859. #endif
  860. me.UpdatePF(); // me._pPF points at TRD PF
  861. me.Move(2); // Bypass table row terminator
  862. AssertSz(me.GetPrevChar() == CR,
  863. "CLayout::Measure: invalid table-row terminator");
  864. if(me.IsHidden())
  865. {
  866. CCFRunPtr rp(me);
  867. me.Move(rp.FindUnhiddenForward());
  868. }
  869. if(me.GetChar() == CELL) // Bypass possible CELL delimeter
  870. { // at end of table row (happens
  871. Assert(pPF->_bTableLevel > 1); // when table row is last line
  872. CTxtSelection *psel = ped->GetSelNC(); // of cell
  873. if(!psel || psel->GetCch() || // Don't bypass CELL if selection
  874. psel->GetCp() !=me.GetCp() ||// is an IP at this position,
  875. !psel->GetShowCellLine()) // i.e., display a blank line
  876. {
  877. me.Move(1);
  878. pli->_fIncludeCell = TRUE;
  879. }
  880. }
  881. plo->_dvpBrdrBot = me.GetPBorderWidth(dvlBrdrBot);
  882. plo->_dvpBrdrTop = me.GetPBorderWidth(dvlBrdrTop);
  883. if(ploAbove)
  884. plo->_dvpBrdrTop = max(plo->_dvpBrdrTop, ploAbove->_dvpBrdrBot);
  885. dvp += plo->_dvpBrdrTop; // Add top border width
  886. if(!me.GetPF()->IsTableRowDelimiter())// End of table: add in
  887. dvp += plo->_dvpBrdrBot; // bottom border width
  888. // Define CLine parameters for table row
  889. if(pPF->_dyLineSpacing)
  890. {
  891. LONG dvpLine = me.LUtoDU(pPF->_dyLineSpacing);
  892. if(dvpLine < 0) // Negative row height means use
  893. dvp = -dvpLine; // the magnitude exactly
  894. else
  895. dvp = max(dvp, dvpLine); // Positive row height means
  896. } // "at least"
  897. plo->_dvp = dvp;
  898. dvpMax = max(dvpMax, dvp);
  899. if(pdvpExtra)
  900. *pdvpExtra = dvpMax - dvp;
  901. // Fill in CLine structure for row
  902. pli->_cch = me.GetCp() - cpSave;
  903. pli->_fFirstInPara = pli->_fHasEOP = TRUE;
  904. pli->_dup = me.LUtoDU(dul);
  905. me._li._fFirstInPara = TRUE;
  906. pli->_upStart = me.MeasureLeftIndent();
  907. me.MeasureRightIndent(); // Define me._upEnd
  908. pli->_cObjectWrapLeft = me._li._cObjectWrapLeft;
  909. pli->_cObjectWrapRight = me._li._cObjectWrapRight;
  910. USHORT dvpLine = plo->_dvp;
  911. USHORT dvpDescent = 0;
  912. me.UpdateWrapState(dvpLine, dvpDescent);
  913. pli->_fFirstWrapLeft = me._li._fFirstWrapLeft;
  914. pli->_fFirstWrapRight = me._li._fFirstWrapRight;
  915. if(!pdp->IsInOutlineView() && IN_RANGE(PFA_RIGHT, pPF->_bAlignment, PFA_CENTER))
  916. {
  917. // Normal view with center or flush-right para. Move right accordingly
  918. // If not top row of like-aligned rows, use indent of top row
  919. CLine *pliFirst = FindTopRow(pli, ili, pliMain, iliMain, pPF);
  920. if(pli != pliFirst)
  921. pli->_upStart = pliFirst->_upStart;
  922. else
  923. {
  924. LONG uShift = me.LUtoDU(dulLayoutOld - dul);
  925. uShift = max(uShift, 0); // Don't allow alignment to go < 0
  926. // Can happen with a target device
  927. if(pPF->_bAlignment == PFA_CENTER)
  928. uShift /= 2;
  929. pli->_upStart = uShift;
  930. }
  931. }
  932. me.SetNumber(0); // Update me._wNumber in case next
  933. } // para is numbered
  934. else if(!pli->Measure(me, uiFlags, pliTarget)) // Not a table row
  935. return FALSE; // Measure failed
  936. if(pli->_fFirstInPara && pPF->_wEffects & PFE_PAGEBREAKBEFORE)
  937. pli->_fPageBreakBefore = TRUE;
  938. me.SetIhyphPrev(pli->_ihyph);
  939. if(!IsTableRow() || me.GetPrevChar() == CELL)// Not a table row display or
  940. return TRUE; // cell text fits on 1 line
  941. // Multiline table cell: allocate its CLayout
  942. CLayout *plo = new CLayout();
  943. if(!plo)
  944. return FALSE; // Not enuf RAM
  945. plo->_cpMin = cpSave;
  946. pliNew = plo->Add(1, NULL);
  947. if(!pliNew)
  948. {
  949. ped->GetCallMgr()->SetOutOfMemory();
  950. TRACEWARNSZ("Out of memory Recalc'ing lines");
  951. return FALSE;
  952. }
  953. *pliNew = *pli; // Copy first line of cell layout
  954. pli->SetPlo(plo); // Turn line into a layout line
  955. // Calculate remaining lines in cell.
  956. // Eventually would be nice to share this code with RecalcLines()
  957. BOOL fFirstInPara;
  958. LONG dvp = pliNew->GetHeight();
  959. LONG iliNew = 0;
  960. while(me.GetCp() < cchText)
  961. {
  962. fFirstInPara = pliNew->_fHasEOP;
  963. pliNew = plo->Add(1, NULL);
  964. iliNew++;
  965. if(!pliNew)
  966. {
  967. ped->GetCallMgr()->SetOutOfMemory();
  968. TRACEWARNSZ("Out of memory Recalc'ing lines");
  969. return FALSE;
  970. }
  971. // New table row can start after EOP, i.e., allow recursion here
  972. uiFlags = MEASURE_BREAKATWORD | (fFirstInPara ? MEASURE_FIRSTINPARA : 0);
  973. if(!plo->Measure(me, pliNew, iliNew, uiFlags, pliTarget))
  974. {
  975. Assert(FALSE);
  976. return FALSE;
  977. }
  978. dvp += pliNew->GetHeight();
  979. if(me.GetPrevChar() == CELL)
  980. break; // Done with current cell
  981. }
  982. pli->_upStart = 0;
  983. plo->_dvp = dvp;
  984. pli->_cch = me.GetCp() - cpSave;
  985. return TRUE;
  986. }
  987. /*
  988. * CLayout::Render(&re, pli, prcView, fLastLine, ili, cLine)
  989. *
  990. * @mfunc
  991. * Render visible part of the line *pli
  992. *
  993. * @rdesc
  994. * TRUE iff successful
  995. *
  996. * @devnote
  997. * re is moved past line (to beginning of next line).
  998. * FUTURE: the RenderLine functions return success/failure.
  999. * Could do something on failure, e.g., be specific and fire
  1000. * appropriate notifications like out of memory or character
  1001. * not in font. Note that CLayout::_cpMin isn't used in
  1002. * rendering, so we don't have to update it the way we do in
  1003. * the query functions.
  1004. */
  1005. BOOL CLayout::Render(
  1006. CRenderer & re, //@parm Renderer to use
  1007. CLine * pli, //@parm Line to render
  1008. const RECTUV *prcView, //@parm View rect to use
  1009. BOOL fLastLine,//@parm TRUE iff last line of control
  1010. LONG ili, //@parm Line index of pli
  1011. LONG cLine) //@parm # lines in pli's CLayout
  1012. {
  1013. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLayout::Render");
  1014. CLayout *plo = pli->GetPlo();
  1015. if(!plo)
  1016. return pli->Render(re, fLastLine); // Render leaf line
  1017. LONG cLine1 = plo->Count(); // Count of lines in sublayout
  1018. LONG ili1; // Index of first line in sublayout
  1019. CLine * pli1 = plo->Elem(0); // Ptr to first line in sublayout
  1020. POINTUV pt;
  1021. if(plo->IsTableRow()) // Line's nested display is a table row
  1022. {
  1023. // Render table row, which is modeled as a CLayout with one
  1024. // CLine per cell. In the backing store, table rows start with
  1025. // the two chars STARTFIELD CR and end with ENDFIELD CR. Cells
  1026. // are terminated by CELL.
  1027. const CLayout * ploAbove = GetLORowAbove(pli, ili);
  1028. const CParaFormat * pPF = plo->GetPFCells();
  1029. const CELLPARMS * prgCellParms = pPF->GetCellParms();
  1030. LONG cpStart = re.GetCp();
  1031. LONG dul = 0;
  1032. BOOL fSetErase = FALSE;
  1033. LONG hl = pPF->_dxOffset; // Logical half gap
  1034. LONG h = re.LUtoDU(hl); // Device half gap
  1035. RECTUV rcView;
  1036. LONG u = prcView->left + pli->_upStart - re._pdp->GetUpScroll();
  1037. // Bypass table-row start
  1038. AssertSz(pPF->_bTabCount && re.GetChar() == STARTFIELD, "Invalid table-row header");
  1039. AssertSz(pPF == re.GetPF(), "Invalid table-row pPF");
  1040. re.Move(2);
  1041. AssertSz(re.GetPrevChar() == CR, "Invalid table-row header");
  1042. // Save current state
  1043. LONG crBackOld = re.GetDefaultBackColor();
  1044. LONG crTextOld = re.GetDefaultTextColor();
  1045. LONG dulLayoutOld = re.GetDulLayout();
  1046. LONG dulRTLRow = pPF->GetRTLRowLength();
  1047. LONG dvpBrdrTop = plo->_dvpBrdrTop;
  1048. CLine * pli0;
  1049. POINTUV ptOld = re.GetCurPoint();
  1050. RECTUV rcRender;
  1051. RECTUV rcRenderOld = re.GetRcRender();
  1052. RECTUV rcViewOld = re.GetRcView();
  1053. const CLayout *ploOld = re.GetLayout();
  1054. rcView.left = u + h; // Default for LTR row
  1055. rcView.right = rcView.left; // Suppress compiler warning
  1056. rcView.top = ptOld.v;
  1057. rcRender.top = rcView.top;
  1058. rcView.bottom = rcView.top + pli->GetHeight();
  1059. rcRender.bottom = rcView.bottom;
  1060. if(dulRTLRow)
  1061. rcView.right = u + re.LUtoDU(dulRTLRow);
  1062. // Render each cell
  1063. for(ili1 = 0; ili1 < cLine1; ili1++, pli1++)
  1064. {
  1065. LONG dvp = 0; // Additional cell height if
  1066. LONG uCell = prgCellParms[ili1].uCell;
  1067. dul += GetCellWidth(uCell);
  1068. re.SetLayout(pli1->GetPlo());
  1069. re.SetDulLayout(GetCellWidth(uCell) - 2*hl);
  1070. // Reduce roundoff by converting dul instead of multiple uCell
  1071. if(dulRTLRow) // Right-To-Left row
  1072. rcView.left = u + h + re.LUtoDU(dulRTLRow - dul); // Convert horizontal coords
  1073. else
  1074. rcView.right = u + re.LUtoDU(dul);
  1075. rcRender.left = rcView.left - h;
  1076. rcRender.right = rcView.right;
  1077. //Set state
  1078. re.StartRender(rcView, rcRender);
  1079. pt.u = rcView.left;
  1080. pt.v = rcView.top + plo->GetVertAlignShift(uCell, pli1->GetHeight());
  1081. if(!IsLowCell(uCell))
  1082. pt.v += dvpBrdrTop;
  1083. re.SetRcViewTop(pt.v); // Clear to top of cell
  1084. re.SetCurPoint(pt);
  1085. if(IsTopCell(uCell))
  1086. {
  1087. // Calculate bottom of set of vertically merged cells
  1088. LONG ili0;
  1089. LONG iCell;
  1090. CLayout *plo0;
  1091. const CELLPARMS *prgCellParms0;
  1092. for(ili0 = ili + 1, pli0 = pli + 1; ili0 < cLine; ili0++, pli0++)
  1093. {
  1094. plo0 = pli0->GetPlo();
  1095. if(!plo0 || !plo0->IsTableRow())
  1096. break;
  1097. prgCellParms0 = plo0->GetPFCells()->GetCellParms();
  1098. iCell = prgCellParms0->ICellFromUCell(dul, plo0->Count());
  1099. if(iCell < 0 || !IsLowCell(prgCellParms0[iCell].uCell))
  1100. break;
  1101. dvp += pli0->GetHeight(); // Add row height
  1102. }
  1103. if(dvp)
  1104. {
  1105. rcView.bottom += dvp;
  1106. rcRender.bottom += dvp;
  1107. re.SetRcBottoms(rcView.bottom, rcRender.bottom);
  1108. }
  1109. }
  1110. COLORREF crf = crTextOld;
  1111. LONG icrf = prgCellParms[ili1].GetColorIndexForegound();
  1112. LONG icrb = prgCellParms[ili1].GetColorIndexBackgound();
  1113. if(icrf | icrb) // If any nonzero bits,
  1114. { // calc special color
  1115. BYTE bS = prgCellParms[ili1].bShading;
  1116. COLORREF crb = re.GetShadedColorFromIndices(icrf, icrb, bS, pPF);
  1117. fSetErase = re.EraseRect(&rcRender, crb);
  1118. if(IsTooSimilar(crf, crb))
  1119. crf = re.GetShadedColorFromIndices(icrb, icrf, bS, pPF);
  1120. }
  1121. else
  1122. re.SetDefaultBackColor(crBackOld);
  1123. re.SetDefaultTextColor(crf);
  1124. if(!ploAbove)
  1125. uCell &= ~fLowCell; // Can't be low cell if no row above
  1126. if(IsLowCell(uCell)) // Cell merged vertically with
  1127. { // the one above it
  1128. LONG cch = re.GetCp() -cpStart; // Use cLine0, ili0, pli0 to
  1129. LONG cLine0; // refer to text in set
  1130. LONG cpNext = re.GetCp() // of vert merged cells
  1131. + (re.GetChar() == NOTACHAR ? 2 : 1);
  1132. LONG dy = 0;
  1133. LONG ili0 = ili;
  1134. // Get target line to display
  1135. pli0 = FindTopCell(cch, pli, ili0, dul, dy, NULL, NULL, 0, &cLine0);
  1136. if(!pli0)
  1137. uCell &= ~fLowCell; // Whoops, no cell above
  1138. else
  1139. {
  1140. pt.v -= dy;
  1141. re.SetCurPoint(pt);
  1142. re.Move(-cch);
  1143. for(; ili0 < cLine0; ili0++, pli0++)
  1144. {
  1145. //Support khyphChangeAfter
  1146. re.SetIhyphPrev(ili0 > 0 ? (pli0 - 1)->_ihyph : 0);
  1147. if(!Render(re, pli0, &rcView, ili0 == cLine0 - 1, ili0, cLine0))
  1148. return FALSE;
  1149. }
  1150. re.SetCp(cpNext); // Bypass [NOTACHAR] CELL
  1151. }
  1152. }
  1153. if(!IsLowCell(uCell)) // Solo cell or top cell of
  1154. { // vertically merged set
  1155. if(!Render(re, pli1, &rcView, !pli1->GetPlo(), ili1, cLine1))
  1156. return FALSE;
  1157. if(dvp) // Rendered set of vmerged cells
  1158. {
  1159. rcView.bottom -= dvp; // Restore rcView/rcRender bottoms
  1160. rcRender.bottom -= dvp;
  1161. re.SetRcBottoms(rcView.bottom, rcRender.bottom);
  1162. }
  1163. }
  1164. if(fSetErase)
  1165. re.SetErase(TRUE); // Restore CRenderer::_fErase
  1166. re.SetRcViewTop(rcView.top); // Restore re._rcView.top in case changed
  1167. if(dulRTLRow) // Restore rcView.right
  1168. rcView.right = rcView.left - h;
  1169. else
  1170. rcView.left = rcView.right + h;
  1171. }
  1172. //Restore previous state
  1173. re.SetLayout(ploOld);
  1174. re.SetDulLayout(dulLayoutOld);
  1175. re.SetDefaultBackColor(crBackOld);
  1176. re.SetDefaultTextColor(crTextOld);
  1177. re.StartRender(rcViewOld, rcRenderOld);
  1178. re.SetCurPoint(ptOld);
  1179. // Bypass table-row terminator
  1180. AssertSz(re.GetPrevChar() == CELL && pPF->_bTabCount == plo->Count(),
  1181. "CLayout::Render:: incorrect table cell count");
  1182. AssertSz(re.GetChar() == ENDFIELD, "CLayout::Render: invalid table-row terminator");
  1183. re.Move(2); // Bypass table row terminator
  1184. AssertSz(re.GetPrevChar() == CR, "invalid table-row terminator");
  1185. BOOL fDrawBottomLine = !re._rpTX.IsAtTRD(STARTFIELD);
  1186. LONG dvp = re.DrawTableBorders(pPF, u, plo->_dvp,
  1187. fDrawBottomLine | fLastLine*2, dul,
  1188. ploAbove ? ploAbove->GetPFCells() : NULL);
  1189. if(re.IsHidden())
  1190. {
  1191. CCFRunPtr rp(re);
  1192. re.Move(rp.FindUnhiddenForward());
  1193. }
  1194. if(re.GetChar() == CELL && pli->_fIncludeCell)
  1195. {
  1196. Assert(pPF->_bTableLevel > 1);
  1197. re.Move(1); // Bypass CELL at end of cell
  1198. } // containing a table
  1199. ptOld.v += pli->GetHeight() + dvp; // Advance to next line position
  1200. re.SetCurPoint(ptOld);
  1201. if(fLastLine)
  1202. re.EraseToBottom();
  1203. return TRUE;
  1204. }
  1205. RECTUV rcRender = re.GetRcRender();
  1206. LONG dvpBottom = min(prcView->bottom, rcRender.bottom);
  1207. LONG dvpTop = max(prcView->top, rcRender.top);
  1208. LONG v0;
  1209. dvpTop = max(dvpTop, 0);
  1210. // Line's nested layout is a regular layout galley, i.e., not a table row
  1211. for(ili1 = 0; ili1 < cLine1; ili1++, pli1++)
  1212. {
  1213. pt = re.GetCurPoint();
  1214. v0 = pt.v + pli1->GetHeight();
  1215. fLastLine = ili1 == cLine1 - 1 || v0 >= dvpBottom;
  1216. //Support khyphChangeAfter
  1217. re.SetIhyphPrev(ili1 > 0 ? (pli1 - 1)->_ihyph : 0);
  1218. if(v0 < dvpTop)
  1219. {
  1220. pt.v = v0; // Advance to next line position
  1221. re.SetCurPoint(pt);
  1222. re.Move(pli1->_cch);
  1223. }
  1224. else if(pt.v >= dvpBottom)
  1225. re.Move(pli1->_cch); // Get to end of nested display
  1226. else if(!Render(re, pli1, prcView, fLastLine, ili1, cLine1))
  1227. return FALSE;
  1228. }
  1229. return TRUE;
  1230. }
  1231. /*
  1232. * CLayout::GetVertAlignShift(uCell, dypText)
  1233. *
  1234. * @mfunc
  1235. * Render visible part of the line *pli
  1236. *
  1237. * @rdesc
  1238. * Vertical shift for cell text
  1239. *
  1240. * @devnote
  1241. * Calculating this shift for vertically merged cells is tricky because
  1242. * dypCell = sum of the cell heights of all cells in the vertically
  1243. * merged set. In particular, if the table is not nested, one needs to
  1244. * wait for recalc of all rows in the set. dypText is relatively easy
  1245. * since it's the height of the top cell in the set.
  1246. */
  1247. LONG CLayout::GetVertAlignShift(
  1248. LONG uCell, //@parm uCell to use
  1249. LONG dypText) //@parm Text height in cell
  1250. {
  1251. LONG dyp = 0;
  1252. if(IsVertMergedCell(uCell))
  1253. {
  1254. }
  1255. else if(GetCellVertAlign(uCell))
  1256. {
  1257. dyp = _dvp - _dvpBrdrTop - _dvpBrdrBot - dypText;
  1258. if(dyp > 0 && IsCellVertAlignCenter(uCell))
  1259. dyp /= 2;
  1260. }
  1261. return dyp;
  1262. }