1406 lines
38 KiB
C++
1406 lines
38 KiB
C++
/*
|
|
* @doc INTERNAL
|
|
*
|
|
* @module LAYOUT.CPP -- CLayout class |
|
|
*
|
|
* Recursive structure which contains an array of lines.
|
|
*
|
|
* Owner:<nl>
|
|
* Murray Sargent: Initial table implementation
|
|
* Keith Curtis: Factored into a separate class for
|
|
* performance, simplicity
|
|
*
|
|
* Copyright (c) 1995-2000, Microsoft Corporation. All rights reserved.
|
|
*/
|
|
|
|
//FUTURE: (KeithCu) More stuff should be put into here, e.g., RecalcLines,
|
|
//The CDisplayML should just be a class that knows about Device descriptors,
|
|
//pagination and scrolling, etc., i.e., things that are the same for all
|
|
//layouts and things that apply only to the outermost layout. This code knows
|
|
//how to manage and update recursive arrays of lines.
|
|
|
|
#include "_common.h"
|
|
#include "_dispml.h"
|
|
#include "_select.h"
|
|
#include "_measure.h"
|
|
#include "_render.h"
|
|
|
|
void CLayout::DeleteSubLayouts(
|
|
LONG ili,
|
|
LONG cLine)
|
|
{
|
|
CLine *pli = Elem(ili);
|
|
|
|
if(cLine < 0)
|
|
cLine = Count();
|
|
|
|
LONG cLineMax = Count() - ili;
|
|
cLine = min(cLine, cLineMax);
|
|
|
|
AssertSz(ili >= 0 && cLine >= 0, "DeleteSubLayouts: illegal line count");
|
|
|
|
// Delete sublayouts
|
|
for(; cLine--; pli++)
|
|
delete pli->GetPlo();
|
|
}
|
|
|
|
/*
|
|
* CLayout::VposFromLine(pdp, ili)
|
|
*
|
|
* @mfunc
|
|
* Computes top of line position
|
|
*
|
|
* @rdesc
|
|
* top position of given line (relative to the first line)
|
|
*/
|
|
LONG CLayout::VposFromLine(
|
|
CDisplayML *pdp, //@parm Parent display
|
|
LONG ili) //@parm Line we're interested in
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLayout::VposFromLine");
|
|
LONG cli = 0, vPos = 0;
|
|
CLine *pli = 0;
|
|
|
|
if (IsNestedLayout())
|
|
{
|
|
Assert(!IsTableRow()); // _iPFCell layouts are horizontal
|
|
Assert(ili < Count());
|
|
cli = ili;
|
|
pli = Elem(0);
|
|
vPos = 0;
|
|
}
|
|
else
|
|
{
|
|
if(!pdp->WaitForRecalcIli(ili)) // out of range, use last valid line
|
|
{
|
|
ili = Count() - 1;
|
|
ili = (ili > 0) ? ili : 0;
|
|
}
|
|
cli = ili - pdp->_iliFirstVisible;
|
|
pli = Elem(pdp->_iliFirstVisible);
|
|
vPos = pdp->_vpScroll + pdp->_dvpFirstVisible;
|
|
}
|
|
|
|
while(cli > 0)
|
|
{
|
|
vPos += pli->GetHeight();
|
|
cli--;
|
|
pli++;
|
|
}
|
|
while(cli < 0)
|
|
{
|
|
pli--;
|
|
vPos -= pli->GetHeight();
|
|
cli++;
|
|
}
|
|
|
|
AssertSz(vPos >= 0, "VposFromLine height less than 0");
|
|
return vPos;
|
|
}
|
|
|
|
/*
|
|
* CLayout::LineFromVPos(pdp, vPos, pdvpLine, pcpFirst)
|
|
*
|
|
* @mfunc
|
|
* Computes line at given y position. Returns top of line vPos
|
|
* cp at start of line cp, and line index.
|
|
*
|
|
* @rdesc
|
|
* index of line found
|
|
*/
|
|
LONG CLayout::LineFromVpos(
|
|
CDisplayML *pdp, //@parm Parent display
|
|
LONG vPos, //@parm Vpos to look for (relative to first line)
|
|
LONG *pdvpLine, //@parm Returns vPos at top of line /r first line (can be NULL)
|
|
LONG *pcpFirst) //@parm Returns cp at start of line (can be NULL)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLayout::LineFromVpos");
|
|
LONG cpLi;
|
|
LONG dy;
|
|
LONG ili = 0;
|
|
LONG yLi;
|
|
CLine *pli;
|
|
|
|
if(IsNestedLayout())
|
|
goto BindFrom0;
|
|
|
|
yLi = pdp->_vpScroll;
|
|
|
|
if(!pdp->WaitForRecalc(-1, pdp->_vpScroll))
|
|
{
|
|
yLi = 0;
|
|
cpLi = 0;
|
|
goto done;
|
|
}
|
|
|
|
cpLi = pdp->_cpFirstVisible;
|
|
ili = pdp->_iliFirstVisible;
|
|
if(!pdp->IsInPageView())
|
|
yLi += pdp->_dvpFirstVisible;
|
|
dy = vPos - yLi;
|
|
|
|
if(dy < 0 && -dy <= pdp->_vpScroll)
|
|
{
|
|
// Closer to first visible line than to first line:
|
|
// go backwards from first visible line.
|
|
while(vPos < yLi && ili > 0)
|
|
{
|
|
pli = Elem(--ili);
|
|
yLi -= pli->GetHeight();
|
|
cpLi -= pli->_cch;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(dy < 0)
|
|
{
|
|
// Closer to first line than to first visible line:
|
|
// so start at first line.
|
|
BindFrom0:
|
|
cpLi = _cpMin;
|
|
yLi = 0;
|
|
ili = 0;
|
|
}
|
|
pli = Elem(ili);
|
|
while(vPos > yLi && ili < Count()-1)
|
|
{
|
|
yLi += pli->GetHeight();
|
|
cpLi += pli->_cch;
|
|
ili++;
|
|
pli++;
|
|
}
|
|
if(vPos < yLi && ili > 0)
|
|
{
|
|
ili--;
|
|
pli--;
|
|
yLi -= pli->GetHeight();
|
|
cpLi -= pli->_cch;
|
|
}
|
|
}
|
|
|
|
done:
|
|
if(pdvpLine)
|
|
*pdvpLine = yLi;
|
|
|
|
if(pcpFirst)
|
|
*pcpFirst = cpLi;
|
|
|
|
return ili;
|
|
}
|
|
|
|
/*
|
|
* CLayout::FindTopCell(&cch, pli, &ili, dul, &dy, pdvp, pliMain,iliMain, pcLine)
|
|
*
|
|
* @mfunc
|
|
* Find cch and height change back to current position in
|
|
* top cell corresponding to the current vertically merged cell.
|
|
* Enter with cch = cch from current cell back to start of row.
|
|
*
|
|
* @rdesc
|
|
* target line in top cell
|
|
*/
|
|
CLine * CLayout::FindTopCell(
|
|
LONG & cch, //@parm In/out parm for cch back to top
|
|
CLine * pli, //@parm Table-row line
|
|
LONG & ili, //@parm Corresponding line index & return ili
|
|
LONG dul, //@parm Current cell x offset
|
|
LONG & dy, //@parm In/Out parm for y offset in top cell
|
|
LONG * pdvp, //@parm TopCellHeight - heights of inbetween rows
|
|
CLine * pliMain, //@parm Line preceding first line accessible by pli
|
|
LONG iliMain, //@parm Line index corresponding to pliMain
|
|
LONG * pcLine) //@parm Count() of possible CLayout for returned pli
|
|
{
|
|
LONG cCell;
|
|
LONG iCell;
|
|
CLayout * plo;
|
|
const CELLPARMS *prgCellParms;
|
|
const CParaFormat *pPF;
|
|
|
|
#ifdef DEBUG
|
|
BYTE bTableLevel = pli->GetPlo()->GetPFCells()->_bTableLevel;
|
|
#endif
|
|
|
|
if(pcLine)
|
|
*pcLine = 0; // Default no lines in case of error
|
|
|
|
// Need to use uCell to identify cell rather than iCell, since
|
|
// horizontal merge can change iCell from row to row
|
|
do // Backup row by row
|
|
{
|
|
if(ili > 0)
|
|
{
|
|
pli--; // Go to previous row
|
|
ili--;
|
|
}
|
|
else if(pliMain)
|
|
{
|
|
pli = pliMain;
|
|
ili = iliMain;
|
|
pliMain = NULL; // Switch to pliMain only once!
|
|
|
|
}
|
|
else
|
|
{
|
|
AssertSz(FALSE, "CLayout::FindTopCell: no accessible top cell");
|
|
return NULL;
|
|
}
|
|
plo = pli->GetPlo(); // Get its cell display
|
|
if(!plo || !plo->IsTableRow()) // Illegal structure or not table row
|
|
{
|
|
AssertSz(FALSE, "CLayout::FindTopCell: no accessible top cell");
|
|
return NULL;
|
|
}
|
|
pPF = plo->GetPFCells();
|
|
AssertSz(pPF->_bTableLevel == bTableLevel,
|
|
"CLayout::FindTopCell: no accessible top cell");
|
|
prgCellParms = pPF->GetCellParms();
|
|
cCell = plo->Count();
|
|
iCell = prgCellParms->ICellFromUCell(dul, cCell);
|
|
dy += pli->GetHeight(); // Add row height
|
|
cch += pli->_cch; // Add in cch for whole row
|
|
}
|
|
while(!IsTopCell(prgCellParms[iCell].uCell));
|
|
|
|
cch -= 2; // Sub cch for StartRow delim
|
|
|
|
pli = plo->Elem(0); // Point at 1st cell in row
|
|
for(ili = 0; ili < iCell; ili++)// Sub cch's for cells
|
|
cch -= (pli++)->_cch; // preceding iCellth cell
|
|
|
|
if(pdvp) // Return top-cell height - heights of
|
|
*pdvp = pli->GetHeight() - dy;// cells in between
|
|
|
|
LONG cLine = 0;
|
|
LONG dvpBrdrTop = plo->_dvpBrdrTop;
|
|
ili = 0;
|
|
dy -= dvpBrdrTop;
|
|
plo = pli->GetPlo();
|
|
if(plo) // Top cell is multiline
|
|
{
|
|
cLine = plo->Count();
|
|
pli = plo->Elem(0); // Advance pli to line in plo
|
|
if(pli->IsNestedLayout())
|
|
dy += dvpBrdrTop;
|
|
while(ili < cLine && dy >= pli->GetHeight()) // nearest to input position
|
|
{
|
|
dy -= pli->GetHeight();
|
|
ili++;
|
|
if(ili == cLine) // Done: leave pli pointing at last line
|
|
break;
|
|
cch -= pli->_cch;
|
|
pli++;
|
|
}
|
|
}
|
|
|
|
if(pcLine)
|
|
*pcLine = cLine;
|
|
return pli;
|
|
}
|
|
|
|
/*
|
|
* CLayout::FindTopRow(pli, ili, pliMain, iliMain, pPF)
|
|
*
|
|
* @mfunc
|
|
* Find CLine for top row in a table
|
|
*
|
|
* @rdesc
|
|
* CLine for top row in table
|
|
*/
|
|
CLine * CLayout::FindTopRow(
|
|
CLine * pli, //@parm Entry table-row line
|
|
LONG ili, //@parm Corresponding line index
|
|
CLine * pliMain, //@parm Line preceding first line accessible by pli
|
|
LONG iliMain, //@parm Line index corresponding to pliMain
|
|
const CParaFormat *pPF) //@parm CParaFormat for entry plo
|
|
{
|
|
BYTE bAlignment = pPF->_bAlignment; // Target row must have same
|
|
BYTE bTableLevel = pPF->_bTableLevel; // alignment and level
|
|
CLine * pliLast;
|
|
CLayout *plo;
|
|
do // Backup row by row
|
|
{
|
|
pliLast = pli; // Last line pointing at row in table
|
|
if(ili > 0)
|
|
{
|
|
pli--; // Go to previous line
|
|
ili--;
|
|
}
|
|
else if(pliMain) // More lines to go back to
|
|
{
|
|
pli = pliMain;
|
|
ili = iliMain;
|
|
pliMain = NULL; // Switch to pliMain only once!
|
|
}
|
|
else
|
|
break;
|
|
|
|
plo = pli->GetPlo(); // Get its cell display
|
|
if(!plo || !plo->IsTableRow())
|
|
break;
|
|
pPF = plo->GetPFCells();
|
|
}
|
|
while(pPF->_bAlignment == bAlignment && pPF->_bTableLevel == bTableLevel);
|
|
|
|
return pliLast;
|
|
}
|
|
|
|
/*
|
|
* CLayout::GetCFCells()
|
|
*
|
|
* @mfunc
|
|
* Return CCharFormat for the table row described by this CLayout
|
|
*
|
|
* @rdesc
|
|
* Table row CCharFormat
|
|
*/
|
|
const CCharFormat* CLayout::GetCFCells()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CLayout::GetCFCells");
|
|
|
|
Assert(_iCFCells >= 0);
|
|
|
|
const CCharFormat *pCF;
|
|
|
|
if(FAILED(GetCharFormatCache()->Deref(_iCFCells, &pCF)))
|
|
{
|
|
AssertSz(FALSE, "CLayout::GetCFCells: couldn't deref _iCFCells");
|
|
pCF = NULL;
|
|
}
|
|
return pCF;
|
|
}
|
|
|
|
/*
|
|
* CLayout::GetPFCells()
|
|
*
|
|
* @mfunc
|
|
* Return CParaFormat for the table row described by this CLayout
|
|
*
|
|
* @rdesc
|
|
* Table row CParaFormat
|
|
*/
|
|
const CParaFormat* CLayout::GetPFCells() const
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CLayout::GetPFCells");
|
|
|
|
Assert(_iPFCells >= 0);
|
|
|
|
const CParaFormat *pPF;
|
|
|
|
if(FAILED(GetParaFormatCache()->Deref(_iPFCells, &pPF)))
|
|
{
|
|
AssertSz(FALSE, "CLayout::GetPF: couldn't deref _iPFCells");
|
|
pPF = NULL;
|
|
}
|
|
return pPF;
|
|
}
|
|
|
|
/*
|
|
* CLayout::GetLORowAbove(pli, ili, pliMain, iliMain)
|
|
*
|
|
* @mfunc
|
|
* Return CLayout for the table row described by the line above pli.
|
|
* If not a table row, return NULL.
|
|
*
|
|
* @rdesc
|
|
* Table row CLayout for row above pli's
|
|
*/
|
|
const CLayout* CLayout::GetLORowAbove(
|
|
CLine * pli, //@parm Entry table-row line
|
|
LONG ili, //@parm Corresponding line index
|
|
CLine * pliMain, //@parm Line preceding first line accessible by pli
|
|
LONG iliMain) //@parm Line index corresponding to pliMain
|
|
{
|
|
if(!ili && pliMain && iliMain) // More lines to go back to
|
|
{
|
|
pli = pliMain;
|
|
ili = iliMain;
|
|
}
|
|
if(ili)
|
|
{
|
|
CLayout *plo = (pli - 1)->GetPlo(); // Get cell display for row above
|
|
if(plo && plo->IsTableRow())
|
|
return plo;
|
|
}
|
|
return NULL; // No line above
|
|
}
|
|
|
|
/*
|
|
* CLayout::CpFromPoint(&me, pt, prcClient, prtp, prp, fAllowEOL, phit,
|
|
* pdispdim, pcpActual, pliParent, iliParent)
|
|
* @mfunc
|
|
* Determine cp at given point
|
|
*
|
|
* @devnote
|
|
* --- Use when in-place active only ---
|
|
*
|
|
* @rdesc
|
|
* Computed cp, -1 if failed
|
|
*/
|
|
LONG CLayout::CpFromPoint(
|
|
CMeasurer &me, //@parm Measurer
|
|
POINTUV pt, //@parm Point to compute cp at (client coords)
|
|
const RECTUV *prcClient,//@parm Client rectangle (can be NULL if active).
|
|
CRchTxtPtr * const prtp,//@parm Returns text pointer at cp (may be NULL)
|
|
CLinePtr * const prp, //@parm Returns line pointer at cp (may be NULL)
|
|
BOOL fAllowEOL, //@parm Click at EOL returns cp after CRLF
|
|
HITTEST * phit, //@parm Out parm for hit-test value
|
|
CDispDim * pdispdim, //@parm Out parm for display dimensions
|
|
LONG *pcpActual, //@parm Out cp that pt is above
|
|
CLine * pliParent, //@parm Parent pli for table row displays
|
|
LONG iliParent) //@parm Parent ili corresponding to pli
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLayout::CpFromPoint");
|
|
|
|
LONG cch = 0;
|
|
LONG cp = 0;
|
|
HITTEST hit = HT_Nothing;
|
|
LONG ili;
|
|
CLine * pli;
|
|
CLayout *plo = NULL;
|
|
RECTUV rcView;
|
|
int v = pt.v; // Save input y coordinate
|
|
LONG yLine = 0;
|
|
CDisplayML *pdp = (CDisplayML*) me.GetPdp();
|
|
|
|
if (IsNestedLayout())
|
|
rcView = *prcClient;
|
|
else
|
|
{
|
|
pdp->GetViewRect(rcView, prcClient);
|
|
pt.v += pdp->GetVpScroll();
|
|
if(pt.u >= 0) // If x coordinate is within view,
|
|
pt.u += pdp->GetUpScroll(); // adjust by scroll value
|
|
}
|
|
|
|
if(phit)
|
|
*phit = HT_Nothing; // Default in case early return
|
|
|
|
// Get line under hit
|
|
if(IsTableRow()) // This display is a table row
|
|
{ // Shrink to cell text boundaries
|
|
pli = Elem(0); // Point at starting cell CLine
|
|
|
|
// Move over to start of cells
|
|
const CParaFormat *pPFCells = GetPFCells();
|
|
LONG dul = 0;
|
|
LONG dulRTLRow = pPFCells->GetRTLRowLength();
|
|
LONG dup = 0;
|
|
BOOL fCellLow;
|
|
LONG h = me.LUtoDU(pPFCells->_dxOffset);
|
|
const CELLPARMS *prgCellParms = pPFCells->GetCellParms();
|
|
LONG u; // Tracks start of text in cell
|
|
LONG u0 = pli->_upStart;
|
|
LONG uCell = 0;
|
|
|
|
pt.v -= _dvpBrdrTop; // Subtract off border top
|
|
cp = _cpMin;
|
|
if(dulRTLRow)
|
|
u0 += me.LUtoDU(dulRTLRow);
|
|
ili = 0;
|
|
|
|
while(1)
|
|
{
|
|
u = u0 + dup + h; // Indent in current cell
|
|
cch = cp - _cpMin;
|
|
uCell = prgCellParms[ili].uCell;
|
|
fCellLow = IsLowCell(uCell);
|
|
dul += GetCellWidth(uCell);
|
|
me.SetDulLayout(GetCellWidth(uCell) - 2*pPFCells->_dxOffset);
|
|
dup = me.LUtoDU(dul);
|
|
if(!dulRTLRow && pt.u < u0 + dup ||// pt.u is inside current cell
|
|
dulRTLRow && pt.u > u0 - dup)
|
|
{
|
|
LONG ili0 = iliParent;
|
|
if(fCellLow) // Cell merged vertically
|
|
{ // with the one above it
|
|
LONG dy = pt.v;
|
|
CLine *pli0 = FindTopCell(cch, pliParent, ili0, dul, dy,
|
|
NULL, NULL, 0, NULL);
|
|
if(pli0)
|
|
{ // Found top cell
|
|
cch += 2; // Include cch of row-start delim
|
|
pli = pli0; // Use its pli and backup
|
|
ili = ili0;
|
|
cp -= cch; // Backup to start of pli
|
|
pt.v += dy;
|
|
}
|
|
}
|
|
if(!dulRTLRow && pt.u < u)
|
|
{ // In cell gap, so select cell
|
|
hit = HT_LeftOfText;
|
|
cch = 0; // Setup for start of row
|
|
goto finish;
|
|
}
|
|
break;
|
|
}
|
|
cp += pli->_cch; // Add in cell's cch
|
|
ili++;
|
|
if(ili == Count())
|
|
{
|
|
hit = HT_RightOfText;
|
|
goto finish;
|
|
}
|
|
pli++;
|
|
}
|
|
LONG dupCell = me.LUtoDU(GetCellWidth(uCell));
|
|
if(dulRTLRow)
|
|
pt.u -= me.LUtoDU(dulRTLRow - dul) + h;
|
|
else
|
|
pt.u -= dup - dupCell + h;
|
|
rcView.right = dupCell - 2*h;
|
|
pt.v -= GetVertAlignShift(uCell, pli->GetHeight());
|
|
}
|
|
else // This display isn't a table row
|
|
{
|
|
// Adjust coordinates relative to view origin
|
|
rcView.right -= rcView.left;
|
|
pt.u -= rcView.left;
|
|
pt.v -= rcView.top;
|
|
ili = LineFromVpos(pdp, pt.v, &yLine, &cp);
|
|
if(ili < 0)
|
|
return -1;
|
|
pli = Elem(ili);
|
|
if(yLine + pli->GetHeight() < pt.v)
|
|
hit = HT_BelowText; // Return hit below text
|
|
}
|
|
rcView.left = 0;
|
|
rcView.top = 0;
|
|
|
|
AssertSz(pli || !ili, "CLayout::CpFromPoint invalid line pointer");
|
|
|
|
if(pli) // Line exists, even it it's
|
|
{ // above or below current screen
|
|
HITTEST hit0;
|
|
if(v < rcView.top) // Note if hit occurs above or
|
|
hit = HT_AboveScreen; // below text
|
|
if(v > rcView.bottom && !IsNestedLayout())
|
|
hit = HT_BelowText;
|
|
|
|
plo = pli->GetPlo();
|
|
pt.v -= yLine;
|
|
|
|
if(plo) // Child layout
|
|
{
|
|
pt.u -= pli->_upStart;
|
|
plo->_cpMin = cp; // Update child's _cpMin
|
|
if(plo->IsTableRow()) // Table row
|
|
{
|
|
plo->_cpMin += 2; // Bypass TR start delimiter
|
|
|
|
if(pt.u < 0)
|
|
{
|
|
plo = NULL;
|
|
hit = HT_LeftOfText; // Return hit left of text
|
|
Assert(cch >= 0); // (should be row)
|
|
goto finish;
|
|
}
|
|
}
|
|
cp = plo->CpFromPoint(me, pt, &rcView, prtp, prp, fAllowEOL,
|
|
&hit0, pdispdim, pcpActual, pli, ili);
|
|
if(cp == -1)
|
|
return -1;
|
|
cch = cp - _cpMin;
|
|
}
|
|
else // Leaf line
|
|
{
|
|
me.SetLayout(this);
|
|
me.SetCp(cp);
|
|
|
|
// Support khyphChangeAfter
|
|
me.SetIhyphPrev(ili > 0 ? (pli - 1)->_ihyph : 0);
|
|
|
|
// Get character in line
|
|
cch = pli->CchFromUp(me, pt, pdispdim, &hit0, pcpActual);
|
|
|
|
// Don't allow click at EOL to select EOL marker and take into
|
|
// account single line edits as well
|
|
if(cch == pli->_cch && pli->_cchEOP && (!fAllowEOL || me.GetPrevChar() == CELL))
|
|
{
|
|
// Adjust position on line by amount backed up. OK for
|
|
// me._rpCF and me._rpPF to get out of sync with me._rpTX,
|
|
// since they're not needed for me.GetCp().
|
|
cch += me._rpTX.BackupCRLF();
|
|
}
|
|
cp = me.GetCp();
|
|
}
|
|
if(hit != HT_BelowText && hit != HT_AboveScreen || hit0 == HT_RightOfText)
|
|
hit = hit0;
|
|
}
|
|
|
|
finish:
|
|
if(!plo) // Store info from leaf line
|
|
{
|
|
if(prtp)
|
|
prtp->SetCp(cp);
|
|
if(prp)
|
|
{
|
|
Assert(cch >= 0);
|
|
prp->Set(ili, cch, this);
|
|
}
|
|
}
|
|
if (phit)
|
|
*phit = hit;
|
|
|
|
return cp;
|
|
}
|
|
|
|
/*
|
|
* CLayout::PointFromTp(&me, rtp, prcClient, fAtEnd, pt, prp, taMode, pdispdim)
|
|
*
|
|
* @mfunc
|
|
* Determine coordinates at given tp
|
|
*
|
|
* @devnote
|
|
* --- Use when in-place active only ---
|
|
*
|
|
* @rdesc
|
|
* line index at cp, -1 if error
|
|
*/
|
|
LONG CLayout::PointFromTp(
|
|
CMeasurer &me, //@parm Measurer
|
|
const CRchTxtPtr &rtp, //@parm Text ptr to get coordinates at
|
|
const RECTUV *prcClient,//@parm Client rectangle (can be NULL if active).
|
|
BOOL fAtEnd, //@parm Return end of prev line for ambiguous cp
|
|
POINTUV & pt, //@parm Returns point at cp in client coords
|
|
CLinePtr * const prp, //@parm Returns line pointer at tp (may be null)
|
|
UINT taMode, //@parm Text Align mode: top, baseline, bottom
|
|
CDispDim * pdispdim) //@parm Out parm for display dimensions
|
|
{
|
|
LONG cp = rtp.GetCp();
|
|
LONG dy = 0;
|
|
RECTUV rcView;
|
|
CDisplayML *pdp = (CDisplayML*) me.GetPdp();
|
|
CLinePtr rp(pdp);
|
|
|
|
if(!pdp->WaitForRecalc(cp, -1))
|
|
return -1;
|
|
|
|
if(!IsNestedLayout()) // Main display
|
|
{
|
|
if(!rp.SetCp(cp, fAtEnd))
|
|
return -1;
|
|
|
|
pdp->GetViewRect(rcView, prcClient);
|
|
pt.u = rcView.left - pdp->_upScroll;
|
|
pt.v = rcView.top - pdp->_vpScroll;
|
|
}
|
|
else // Subdisplay
|
|
{
|
|
rp.Init(*this);
|
|
|
|
rp.BindToCp(cp - _cpMin);
|
|
if(fAtEnd && !IsTableRow()) // Ambiguous-cp caret position
|
|
rp.AdjustBackward(); // belongs at prev EOL
|
|
|
|
rcView = *prcClient;
|
|
pt.u = rcView.left;
|
|
pt.v = rcView.top;
|
|
}
|
|
|
|
AssertSz(pdp->_ped->_fInPlaceActive || prcClient, "Invalid client rect");
|
|
|
|
LONG ili = rp.GetLineIndex();
|
|
CLine *pli = NULL;
|
|
CLayout *plo = NULL;
|
|
LONG xEnd = -1; // pt.u to use at end of table row
|
|
|
|
if(IsTableRow()) // This layout is a table row
|
|
{ // Shrink to cell text boundaries
|
|
const CParaFormat *pPFCells = GetPFCells();
|
|
const CELLPARMS * prgCellParms = pPFCells->GetCellParms();
|
|
LONG dul = 0;
|
|
LONG dulRTLRow = pPFCells->GetRTLRowLength();
|
|
LONG h = me.LUtoDU(pPFCells->_dxOffset);
|
|
LONG i;
|
|
|
|
cp = _cpMin;
|
|
pli = Elem(0);
|
|
|
|
for(i = 0; i < ili; i++, pli++)
|
|
{
|
|
dul += GetCellWidth(prgCellParms[i].uCell);
|
|
cp += pli->_cch;
|
|
}
|
|
LONG uCell = prgCellParms[ili].uCell;
|
|
me.SetDulLayout(GetCellWidth(uCell) - 2 * pPFCells->_dxOffset);
|
|
|
|
if(dulRTLRow)
|
|
{
|
|
if(dul < dulRTLRow)
|
|
{
|
|
uCell = prgCellParms[ili + 1].uCell;
|
|
dul += GetCellWidth(prgCellParms[i].uCell);
|
|
}
|
|
dul = dulRTLRow - dul;
|
|
}
|
|
rcView.left = pt.u + me.LUtoDU(dul) + h;
|
|
rcView.right = pt.u + me.LUtoDU(dul + GetCellWidth(uCell)) - h;
|
|
pt.u = rcView.left;
|
|
if(!GetCellWidth(uCell))
|
|
{
|
|
pt.v += _dvp;
|
|
goto done;
|
|
}
|
|
if(ili + 1 == Count() && rp->_cch == rp.GetIch())
|
|
{
|
|
xEnd = rcView.right + h + 1;
|
|
if(dulRTLRow)
|
|
xEnd = rcView.left - h - 1;
|
|
}
|
|
pt.v += GetVertAlignShift(uCell, pli->GetHeight());
|
|
if(!(taMode & TA_CELLTOP))
|
|
pt.v += _dvpBrdrTop;
|
|
}
|
|
else // This layout isn't a table row
|
|
{
|
|
pt.v += VposFromLine(pdp, ili);
|
|
cp -= rp.GetIch();
|
|
}
|
|
|
|
pli = Elem(ili);
|
|
plo = pli->GetPlo();
|
|
|
|
if(plo) // Line has child display
|
|
{ // Define child rcView and delegate
|
|
RECTUV rc; // to child
|
|
pt.u += pli->_upStart;
|
|
rc.left = pt.u;
|
|
rc.right = pt.u + rcView.right - rcView.left;
|
|
rc.top = pt.v;
|
|
rc.bottom = pt.v + pli->GetHeight();
|
|
plo->_cpMin = cp; // Update child display's _cpMin
|
|
if(plo->IsTableRow())
|
|
plo->_cpMin += 2; // Bypass table row start code
|
|
|
|
if(plo->PointFromTp(me, rtp, &rc, fAtEnd, pt, prp, taMode, pdispdim) == -1)
|
|
return -1;
|
|
}
|
|
else // Line is a leaf line
|
|
{
|
|
me.SetLayout(this);
|
|
me.Move(-rp.GetIch()); // Backup to start of line
|
|
me.NewLine(*rp); // Measure from there to where we are
|
|
|
|
//Support khyphChangeAfter
|
|
me.SetIhyphPrev(ili > 0 ? (pli - 1)->_ihyph : 0);
|
|
|
|
LONG xCalc = rp->UpFromCch(me, rp.GetIch(), taMode, pdispdim, &dy);
|
|
|
|
if(pt.u + xCalc <= rcView.right || !pdp->GetWordWrap() || pdp->GetTargetDev())
|
|
{
|
|
// Width is in view or there is no wordwrap so just
|
|
// add the length to the point.
|
|
pt.u += xCalc;
|
|
}
|
|
else
|
|
pt.u = rcView.right; //Hit-test went too far, limit it.
|
|
|
|
pt.v += dy;
|
|
}
|
|
if(xEnd != -1)
|
|
pt.u = xEnd; // Return x coord at end of table row
|
|
|
|
done:
|
|
if(prp && !plo)
|
|
*prp = rp; // Return innermost rp
|
|
return rp; // Return outermost iRun
|
|
}
|
|
|
|
/*
|
|
* CLayout::Measure(&me, pli, ili, uiFlags, pliTarget, iliMain, pliMain, pdvpExtra)
|
|
*
|
|
* @mfunc
|
|
* Computes line break (based on target device) and fills
|
|
* in *pli with resulting metrics on rendering device
|
|
*
|
|
* @rdesc
|
|
* TRUE if OK
|
|
*/
|
|
BOOL CLayout::Measure (
|
|
CMeasurer& me, //@parm Measurer pointing at text to measure
|
|
CLine * pli, //@parm Line to store result in
|
|
LONG ili, //@parm Line index corresponding to pli
|
|
UINT uiFlags, //@parm Flags
|
|
CLine * pliTarget, //@parm Returns target-device line metrics (optional)
|
|
LONG iliMain, //@parm Line index corresponding to pliMain
|
|
CLine * pliMain, //@parm Line preceding 1st line in pli layout (optional)
|
|
LONG * pdvpExtra) //@parm Returns extra line height for vmrged cells (opt)
|
|
//REVIEW (keithcu) pliTarget is busted in the recursive case.
|
|
{
|
|
CTxtEdit * ped = me.GetPed();
|
|
LONG cchText = ped->GetTextLength();
|
|
LONG cpSave = me.GetCp();
|
|
CLine * pliNew;
|
|
const CDisplayML * pdp = (const CDisplayML *)me.GetPdp();
|
|
const CParaFormat *pPF = me.GetPF();
|
|
|
|
// Measure one line, which is either a table row or a line in a paragraph
|
|
if(pPF->IsTableRowDelimiter())
|
|
{
|
|
// Measure table row, which is modeled as a CLayout with one
|
|
// CLine per cell. In the backing store, table rows start with
|
|
// the two chars STARTFIELD CR and end with ENDFIELD CR. Cells
|
|
// are delimited by CELL.
|
|
LONG cpStart = me.GetCp();
|
|
LONG dul = 0;
|
|
LONG dxCell = 0;
|
|
LONG dvp = 0;
|
|
LONG dvpMax = 0;
|
|
CLayout * plo = new CLayout();
|
|
const CLayout * ploAbove = GetLORowAbove(pli, ili, pliMain, iliMain);
|
|
const CELLPARMS *prgCellParms = pPF->GetCellParms();
|
|
|
|
if(!plo)
|
|
return FALSE;
|
|
|
|
plo->_iCFCells = me.Get_iCF();
|
|
plo->_iPFCells = me.Get_iPF();
|
|
pli->SetPlo(plo);
|
|
|
|
AssertSz(pPF->_bTabCount && me.GetChar() == STARTFIELD, "Invalid table-row header");
|
|
me.Move(2);
|
|
AssertSz(me.GetPrevChar() == CR, "Invalid table-row header");
|
|
|
|
plo->_cpMin = me.GetCp();
|
|
|
|
// Save current values
|
|
LONG dulLayoutOld = me.GetDulLayout();
|
|
LONG dvlBrdrTop = 0;
|
|
LONG dvlBrdrBot = 0;
|
|
const CLayout *ploOld = me.GetLayout();
|
|
CArray <COleObject*> rgpobjWrapOld;
|
|
me._rgpobjWrap.TransferTo(rgpobjWrapOld);
|
|
|
|
// Create CLines for each cell and measure them
|
|
for(LONG iCell = 0; iCell < pPF->_bTabCount; iCell++)
|
|
{
|
|
me.SetNumber(0);
|
|
LONG uCell = prgCellParms[iCell].uCell;
|
|
dxCell = GetCellWidth(uCell);
|
|
dul += dxCell;
|
|
|
|
// Add a line for the next cell
|
|
pliNew = plo->Add(1, NULL);
|
|
if(!pliNew)
|
|
return FALSE;
|
|
|
|
LONG dvl = prgCellParms[iCell].GetBrdrWidthTop();
|
|
dvlBrdrTop = max(dvlBrdrTop, dvl);
|
|
dvl = prgCellParms[iCell].GetBrdrWidthBottom();
|
|
dvlBrdrBot = max(dvlBrdrBot, dvl);
|
|
|
|
if(!ploAbove)
|
|
uCell &= ~fLowCell; // Can't be a low cell if no row above
|
|
AssertSz(!IsLowCell(uCell) || me.GetChar() == NOTACHAR,
|
|
"CLayout::Measure: invalid low cell");
|
|
me.SetLayout(plo);
|
|
me.SetDulLayout(dxCell - 2*pPF->_dxOffset);
|
|
plo->Measure(me, pliNew, iCell, uiFlags | MEASURE_FIRSTINPARA, pliTarget, iliMain, pliMain);
|
|
|
|
if(IsLowCell(uCell))
|
|
{
|
|
// If a low cell in set of vertically merged cells, check
|
|
// if corresponding cell on next row is also merged
|
|
CPFRunPtr rp(me);
|
|
rp.FindRowEnd(pPF->_bTableLevel);
|
|
|
|
const CParaFormat *pPF1 = rp.GetPF();
|
|
BOOL fBottomCell = !pPF1->IsTableRowDelimiter();
|
|
|
|
if(!fBottomCell)
|
|
{
|
|
const CELLPARMS *prgCellParms1 = pPF1->GetCellParms();
|
|
LONG iCell1 = prgCellParms1->ICellFromUCell(dul, pPF1->_bTabCount);
|
|
|
|
if(iCell1 >= 0 && !IsLowCell(prgCellParms1[iCell1].uCell))
|
|
fBottomCell = TRUE;
|
|
}
|
|
if(fBottomCell)
|
|
{
|
|
// Need to include top cell in current row height
|
|
// calculation
|
|
LONG cch = me.GetCp() - cpStart;
|
|
LONG dy1 = 0;
|
|
LONG iliT = ili;
|
|
LONG dvpCell = 0;
|
|
|
|
if(!FindTopCell(cch, pli, iliT, dul, dy1, &dvpCell, pliMain, iliMain, NULL))
|
|
uCell &= ~fLowCell; // Not a valid low cell
|
|
else if(dvpCell > 0)
|
|
dvp = max(dvp, dvpCell);
|
|
}
|
|
}
|
|
if(!IsVertMergedCell(uCell) && dxCell || !dvp && iCell == pPF->_bTabCount - 1)
|
|
dvp = max(pliNew->GetHeight(), dvp);
|
|
dvpMax = max(dvpMax, pliNew->GetHeight());
|
|
}
|
|
|
|
//Restore original values
|
|
me.SetDulLayout(dulLayoutOld);
|
|
me.SetLayout(ploOld);
|
|
me.SetIhyphPrev(0);
|
|
me._rgpobjWrap.Clear(AF_DELETEMEM);
|
|
|
|
rgpobjWrapOld.TransferTo(me._rgpobjWrap);
|
|
|
|
#ifdef DEBUG
|
|
// Bypass table-row terminator
|
|
if(me.GetChar() != ENDFIELD)
|
|
me._rpTX.MoveGapToEndOfBlock();
|
|
AssertSz(me.GetPrevChar() == CELL && pPF->_bTabCount == plo->Count(),
|
|
"Incorrect table cell count");
|
|
AssertSz(me.GetChar() == ENDFIELD,
|
|
"CLayout::Measure: invalid table-row terminator");
|
|
me._rpPF.AdjustForward();
|
|
const CParaFormat *pPFme = me.GetPF();
|
|
AssertSz(pPFme->IsTableRowDelimiter(),
|
|
"CLayout::Measure: invalid table-row terminator");
|
|
#endif
|
|
|
|
me.UpdatePF(); // me._pPF points at TRD PF
|
|
me.Move(2); // Bypass table row terminator
|
|
AssertSz(me.GetPrevChar() == CR,
|
|
"CLayout::Measure: invalid table-row terminator");
|
|
if(me.IsHidden())
|
|
{
|
|
CCFRunPtr rp(me);
|
|
me.Move(rp.FindUnhiddenForward());
|
|
}
|
|
|
|
if(me.GetChar() == CELL) // Bypass possible CELL delimeter
|
|
{ // at end of table row (happens
|
|
Assert(pPF->_bTableLevel > 1); // when table row is last line
|
|
CTxtSelection *psel = ped->GetSelNC(); // of cell
|
|
if(!psel || psel->GetCch() || // Don't bypass CELL if selection
|
|
psel->GetCp() !=me.GetCp() ||// is an IP at this position,
|
|
!psel->GetShowCellLine()) // i.e., display a blank line
|
|
{
|
|
me.Move(1);
|
|
pli->_fIncludeCell = TRUE;
|
|
}
|
|
}
|
|
|
|
plo->_dvpBrdrBot = me.GetPBorderWidth(dvlBrdrBot);
|
|
plo->_dvpBrdrTop = me.GetPBorderWidth(dvlBrdrTop);
|
|
if(ploAbove)
|
|
plo->_dvpBrdrTop = max(plo->_dvpBrdrTop, ploAbove->_dvpBrdrBot);
|
|
dvp += plo->_dvpBrdrTop; // Add top border width
|
|
if(!me.GetPF()->IsTableRowDelimiter())// End of table: add in
|
|
dvp += plo->_dvpBrdrBot; // bottom border width
|
|
|
|
// Define CLine parameters for table row
|
|
if(pPF->_dyLineSpacing)
|
|
{
|
|
LONG dvpLine = me.LUtoDU(pPF->_dyLineSpacing);
|
|
if(dvpLine < 0) // Negative row height means use
|
|
dvp = -dvpLine; // the magnitude exactly
|
|
else
|
|
dvp = max(dvp, dvpLine); // Positive row height means
|
|
} // "at least"
|
|
plo->_dvp = dvp;
|
|
dvpMax = max(dvpMax, dvp);
|
|
if(pdvpExtra)
|
|
*pdvpExtra = dvpMax - dvp;
|
|
|
|
// Fill in CLine structure for row
|
|
pli->_cch = me.GetCp() - cpSave;
|
|
pli->_fFirstInPara = pli->_fHasEOP = TRUE;
|
|
pli->_dup = me.LUtoDU(dul);
|
|
me._li._fFirstInPara = TRUE;
|
|
pli->_upStart = me.MeasureLeftIndent();
|
|
me.MeasureRightIndent(); // Define me._upEnd
|
|
pli->_cObjectWrapLeft = me._li._cObjectWrapLeft;
|
|
pli->_cObjectWrapRight = me._li._cObjectWrapRight;
|
|
USHORT dvpLine = plo->_dvp;
|
|
USHORT dvpDescent = 0;
|
|
me.UpdateWrapState(dvpLine, dvpDescent);
|
|
pli->_fFirstWrapLeft = me._li._fFirstWrapLeft;
|
|
pli->_fFirstWrapRight = me._li._fFirstWrapRight;
|
|
|
|
if(!pdp->IsInOutlineView() && IN_RANGE(PFA_RIGHT, pPF->_bAlignment, PFA_CENTER))
|
|
{
|
|
// Normal view with center or flush-right para. Move right accordingly
|
|
// If not top row of like-aligned rows, use indent of top row
|
|
CLine *pliFirst = FindTopRow(pli, ili, pliMain, iliMain, pPF);
|
|
if(pli != pliFirst)
|
|
pli->_upStart = pliFirst->_upStart;
|
|
|
|
else
|
|
{
|
|
LONG uShift = me.LUtoDU(dulLayoutOld - dul);
|
|
uShift = max(uShift, 0); // Don't allow alignment to go < 0
|
|
// Can happen with a target device
|
|
if(pPF->_bAlignment == PFA_CENTER)
|
|
uShift /= 2;
|
|
pli->_upStart = uShift;
|
|
}
|
|
}
|
|
me.SetNumber(0); // Update me._wNumber in case next
|
|
} // para is numbered
|
|
else if(!pli->Measure(me, uiFlags, pliTarget)) // Not a table row
|
|
return FALSE; // Measure failed
|
|
|
|
if(pli->_fFirstInPara && pPF->_wEffects & PFE_PAGEBREAKBEFORE)
|
|
pli->_fPageBreakBefore = TRUE;
|
|
|
|
me.SetIhyphPrev(pli->_ihyph);
|
|
|
|
if(!IsTableRow() || me.GetPrevChar() == CELL)// Not a table row display or
|
|
return TRUE; // cell text fits on 1 line
|
|
|
|
// Multiline table cell: allocate its CLayout
|
|
CLayout *plo = new CLayout();
|
|
if(!plo)
|
|
return FALSE; // Not enuf RAM
|
|
|
|
plo->_cpMin = cpSave;
|
|
pliNew = plo->Add(1, NULL);
|
|
if(!pliNew)
|
|
{
|
|
ped->GetCallMgr()->SetOutOfMemory();
|
|
TRACEWARNSZ("Out of memory Recalc'ing lines");
|
|
return FALSE;
|
|
}
|
|
*pliNew = *pli; // Copy first line of cell layout
|
|
pli->SetPlo(plo); // Turn line into a layout line
|
|
|
|
// Calculate remaining lines in cell.
|
|
// Eventually would be nice to share this code with RecalcLines()
|
|
BOOL fFirstInPara;
|
|
LONG dvp = pliNew->GetHeight();
|
|
LONG iliNew = 0;
|
|
|
|
while(me.GetCp() < cchText)
|
|
{
|
|
fFirstInPara = pliNew->_fHasEOP;
|
|
pliNew = plo->Add(1, NULL);
|
|
iliNew++;
|
|
|
|
if(!pliNew)
|
|
{
|
|
ped->GetCallMgr()->SetOutOfMemory();
|
|
TRACEWARNSZ("Out of memory Recalc'ing lines");
|
|
return FALSE;
|
|
}
|
|
// New table row can start after EOP, i.e., allow recursion here
|
|
uiFlags = MEASURE_BREAKATWORD | (fFirstInPara ? MEASURE_FIRSTINPARA : 0);
|
|
if(!plo->Measure(me, pliNew, iliNew, uiFlags, pliTarget))
|
|
{
|
|
Assert(FALSE);
|
|
return FALSE;
|
|
}
|
|
dvp += pliNew->GetHeight();
|
|
if(me.GetPrevChar() == CELL)
|
|
break; // Done with current cell
|
|
}
|
|
pli->_upStart = 0;
|
|
plo->_dvp = dvp;
|
|
pli->_cch = me.GetCp() - cpSave;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* CLayout::Render(&re, pli, prcView, fLastLine, ili, cLine)
|
|
*
|
|
* @mfunc
|
|
* Render visible part of the line *pli
|
|
*
|
|
* @rdesc
|
|
* TRUE iff successful
|
|
*
|
|
* @devnote
|
|
* re is moved past line (to beginning of next line).
|
|
* FUTURE: the RenderLine functions return success/failure.
|
|
* Could do something on failure, e.g., be specific and fire
|
|
* appropriate notifications like out of memory or character
|
|
* not in font. Note that CLayout::_cpMin isn't used in
|
|
* rendering, so we don't have to update it the way we do in
|
|
* the query functions.
|
|
*/
|
|
BOOL CLayout::Render(
|
|
CRenderer & re, //@parm Renderer to use
|
|
CLine * pli, //@parm Line to render
|
|
const RECTUV *prcView, //@parm View rect to use
|
|
BOOL fLastLine,//@parm TRUE iff last line of control
|
|
LONG ili, //@parm Line index of pli
|
|
LONG cLine) //@parm # lines in pli's CLayout
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLayout::Render");
|
|
|
|
CLayout *plo = pli->GetPlo();
|
|
if(!plo)
|
|
return pli->Render(re, fLastLine); // Render leaf line
|
|
|
|
LONG cLine1 = plo->Count(); // Count of lines in sublayout
|
|
LONG ili1; // Index of first line in sublayout
|
|
CLine * pli1 = plo->Elem(0); // Ptr to first line in sublayout
|
|
POINTUV pt;
|
|
|
|
if(plo->IsTableRow()) // Line's nested display is a table row
|
|
{
|
|
// Render table row, which is modeled as a CLayout with one
|
|
// CLine per cell. In the backing store, table rows start with
|
|
// the two chars STARTFIELD CR and end with ENDFIELD CR. Cells
|
|
// are terminated by CELL.
|
|
const CLayout * ploAbove = GetLORowAbove(pli, ili);
|
|
const CParaFormat * pPF = plo->GetPFCells();
|
|
const CELLPARMS * prgCellParms = pPF->GetCellParms();
|
|
LONG cpStart = re.GetCp();
|
|
LONG dul = 0;
|
|
BOOL fSetErase = FALSE;
|
|
LONG hl = pPF->_dxOffset; // Logical half gap
|
|
LONG h = re.LUtoDU(hl); // Device half gap
|
|
RECTUV rcView;
|
|
LONG u = prcView->left + pli->_upStart - re._pdp->GetUpScroll();
|
|
|
|
// Bypass table-row start
|
|
AssertSz(pPF->_bTabCount && re.GetChar() == STARTFIELD, "Invalid table-row header");
|
|
AssertSz(pPF == re.GetPF(), "Invalid table-row pPF");
|
|
re.Move(2);
|
|
AssertSz(re.GetPrevChar() == CR, "Invalid table-row header");
|
|
|
|
// Save current state
|
|
LONG crBackOld = re.GetDefaultBackColor();
|
|
LONG crTextOld = re.GetDefaultTextColor();
|
|
LONG dulLayoutOld = re.GetDulLayout();
|
|
LONG dulRTLRow = pPF->GetRTLRowLength();
|
|
LONG dvpBrdrTop = plo->_dvpBrdrTop;
|
|
CLine * pli0;
|
|
POINTUV ptOld = re.GetCurPoint();
|
|
RECTUV rcRender;
|
|
RECTUV rcRenderOld = re.GetRcRender();
|
|
RECTUV rcViewOld = re.GetRcView();
|
|
const CLayout *ploOld = re.GetLayout();
|
|
|
|
rcView.left = u + h; // Default for LTR row
|
|
rcView.right = rcView.left; // Suppress compiler warning
|
|
rcView.top = ptOld.v;
|
|
rcRender.top = rcView.top;
|
|
rcView.bottom = rcView.top + pli->GetHeight();
|
|
rcRender.bottom = rcView.bottom;
|
|
|
|
if(dulRTLRow)
|
|
rcView.right = u + re.LUtoDU(dulRTLRow);
|
|
|
|
// Render each cell
|
|
for(ili1 = 0; ili1 < cLine1; ili1++, pli1++)
|
|
{
|
|
LONG dvp = 0; // Additional cell height if
|
|
LONG uCell = prgCellParms[ili1].uCell;
|
|
|
|
dul += GetCellWidth(uCell);
|
|
re.SetLayout(pli1->GetPlo());
|
|
re.SetDulLayout(GetCellWidth(uCell) - 2*hl);
|
|
|
|
// Reduce roundoff by converting dul instead of multiple uCell
|
|
if(dulRTLRow) // Right-To-Left row
|
|
rcView.left = u + h + re.LUtoDU(dulRTLRow - dul); // Convert horizontal coords
|
|
else
|
|
rcView.right = u + re.LUtoDU(dul);
|
|
|
|
rcRender.left = rcView.left - h;
|
|
rcRender.right = rcView.right;
|
|
|
|
//Set state
|
|
re.StartRender(rcView, rcRender);
|
|
pt.u = rcView.left;
|
|
pt.v = rcView.top + plo->GetVertAlignShift(uCell, pli1->GetHeight());
|
|
if(!IsLowCell(uCell))
|
|
pt.v += dvpBrdrTop;
|
|
re.SetRcViewTop(pt.v); // Clear to top of cell
|
|
re.SetCurPoint(pt);
|
|
if(IsTopCell(uCell))
|
|
{
|
|
// Calculate bottom of set of vertically merged cells
|
|
LONG ili0;
|
|
LONG iCell;
|
|
CLayout *plo0;
|
|
const CELLPARMS *prgCellParms0;
|
|
|
|
for(ili0 = ili + 1, pli0 = pli + 1; ili0 < cLine; ili0++, pli0++)
|
|
{
|
|
plo0 = pli0->GetPlo();
|
|
if(!plo0 || !plo0->IsTableRow())
|
|
break;
|
|
prgCellParms0 = plo0->GetPFCells()->GetCellParms();
|
|
iCell = prgCellParms0->ICellFromUCell(dul, plo0->Count());
|
|
if(iCell < 0 || !IsLowCell(prgCellParms0[iCell].uCell))
|
|
break;
|
|
dvp += pli0->GetHeight(); // Add row height
|
|
}
|
|
if(dvp)
|
|
{
|
|
rcView.bottom += dvp;
|
|
rcRender.bottom += dvp;
|
|
re.SetRcBottoms(rcView.bottom, rcRender.bottom);
|
|
}
|
|
}
|
|
COLORREF crf = crTextOld;
|
|
LONG icrf = prgCellParms[ili1].GetColorIndexForegound();
|
|
LONG icrb = prgCellParms[ili1].GetColorIndexBackgound();
|
|
if(icrf | icrb) // If any nonzero bits,
|
|
{ // calc special color
|
|
BYTE bS = prgCellParms[ili1].bShading;
|
|
COLORREF crb = re.GetShadedColorFromIndices(icrf, icrb, bS, pPF);
|
|
fSetErase = re.EraseRect(&rcRender, crb);
|
|
if(IsTooSimilar(crf, crb))
|
|
crf = re.GetShadedColorFromIndices(icrb, icrf, bS, pPF);
|
|
}
|
|
else
|
|
re.SetDefaultBackColor(crBackOld);
|
|
re.SetDefaultTextColor(crf);
|
|
|
|
if(!ploAbove)
|
|
uCell &= ~fLowCell; // Can't be low cell if no row above
|
|
if(IsLowCell(uCell)) // Cell merged vertically with
|
|
{ // the one above it
|
|
LONG cch = re.GetCp() -cpStart; // Use cLine0, ili0, pli0 to
|
|
LONG cLine0; // refer to text in set
|
|
LONG cpNext = re.GetCp() // of vert merged cells
|
|
+ (re.GetChar() == NOTACHAR ? 2 : 1);
|
|
LONG dy = 0;
|
|
LONG ili0 = ili;
|
|
|
|
// Get target line to display
|
|
pli0 = FindTopCell(cch, pli, ili0, dul, dy, NULL, NULL, 0, &cLine0);
|
|
if(!pli0)
|
|
uCell &= ~fLowCell; // Whoops, no cell above
|
|
else
|
|
{
|
|
pt.v -= dy;
|
|
re.SetCurPoint(pt);
|
|
re.Move(-cch);
|
|
for(; ili0 < cLine0; ili0++, pli0++)
|
|
{
|
|
//Support khyphChangeAfter
|
|
re.SetIhyphPrev(ili0 > 0 ? (pli0 - 1)->_ihyph : 0);
|
|
|
|
if(!Render(re, pli0, &rcView, ili0 == cLine0 - 1, ili0, cLine0))
|
|
return FALSE;
|
|
}
|
|
re.SetCp(cpNext); // Bypass [NOTACHAR] CELL
|
|
}
|
|
}
|
|
if(!IsLowCell(uCell)) // Solo cell or top cell of
|
|
{ // vertically merged set
|
|
if(!Render(re, pli1, &rcView, !pli1->GetPlo(), ili1, cLine1))
|
|
return FALSE;
|
|
if(dvp) // Rendered set of vmerged cells
|
|
{
|
|
rcView.bottom -= dvp; // Restore rcView/rcRender bottoms
|
|
rcRender.bottom -= dvp;
|
|
re.SetRcBottoms(rcView.bottom, rcRender.bottom);
|
|
}
|
|
}
|
|
if(fSetErase)
|
|
re.SetErase(TRUE); // Restore CRenderer::_fErase
|
|
re.SetRcViewTop(rcView.top); // Restore re._rcView.top in case changed
|
|
if(dulRTLRow) // Restore rcView.right
|
|
rcView.right = rcView.left - h;
|
|
else
|
|
rcView.left = rcView.right + h;
|
|
}
|
|
|
|
//Restore previous state
|
|
re.SetLayout(ploOld);
|
|
re.SetDulLayout(dulLayoutOld);
|
|
re.SetDefaultBackColor(crBackOld);
|
|
re.SetDefaultTextColor(crTextOld);
|
|
re.StartRender(rcViewOld, rcRenderOld);
|
|
re.SetCurPoint(ptOld);
|
|
|
|
// Bypass table-row terminator
|
|
AssertSz(re.GetPrevChar() == CELL && pPF->_bTabCount == plo->Count(),
|
|
"CLayout::Render:: incorrect table cell count");
|
|
AssertSz(re.GetChar() == ENDFIELD, "CLayout::Render: invalid table-row terminator");
|
|
|
|
re.Move(2); // Bypass table row terminator
|
|
AssertSz(re.GetPrevChar() == CR, "invalid table-row terminator");
|
|
|
|
BOOL fDrawBottomLine = !re._rpTX.IsAtTRD(STARTFIELD);
|
|
LONG dvp = re.DrawTableBorders(pPF, u, plo->_dvp,
|
|
fDrawBottomLine | fLastLine*2, dul,
|
|
ploAbove ? ploAbove->GetPFCells() : NULL);
|
|
if(re.IsHidden())
|
|
{
|
|
CCFRunPtr rp(re);
|
|
re.Move(rp.FindUnhiddenForward());
|
|
}
|
|
if(re.GetChar() == CELL && pli->_fIncludeCell)
|
|
{
|
|
Assert(pPF->_bTableLevel > 1);
|
|
re.Move(1); // Bypass CELL at end of cell
|
|
} // containing a table
|
|
ptOld.v += pli->GetHeight() + dvp; // Advance to next line position
|
|
re.SetCurPoint(ptOld);
|
|
if(fLastLine)
|
|
re.EraseToBottom();
|
|
return TRUE;
|
|
}
|
|
|
|
RECTUV rcRender = re.GetRcRender();
|
|
LONG dvpBottom = min(prcView->bottom, rcRender.bottom);
|
|
LONG dvpTop = max(prcView->top, rcRender.top);
|
|
LONG v0;
|
|
dvpTop = max(dvpTop, 0);
|
|
|
|
// Line's nested layout is a regular layout galley, i.e., not a table row
|
|
for(ili1 = 0; ili1 < cLine1; ili1++, pli1++)
|
|
{
|
|
pt = re.GetCurPoint();
|
|
v0 = pt.v + pli1->GetHeight();
|
|
fLastLine = ili1 == cLine1 - 1 || v0 >= dvpBottom;
|
|
|
|
//Support khyphChangeAfter
|
|
re.SetIhyphPrev(ili1 > 0 ? (pli1 - 1)->_ihyph : 0);
|
|
|
|
if(v0 < dvpTop)
|
|
{
|
|
pt.v = v0; // Advance to next line position
|
|
re.SetCurPoint(pt);
|
|
re.Move(pli1->_cch);
|
|
}
|
|
else if(pt.v >= dvpBottom)
|
|
re.Move(pli1->_cch); // Get to end of nested display
|
|
|
|
else if(!Render(re, pli1, prcView, fLastLine, ili1, cLine1))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* CLayout::GetVertAlignShift(uCell, dypText)
|
|
*
|
|
* @mfunc
|
|
* Render visible part of the line *pli
|
|
*
|
|
* @rdesc
|
|
* Vertical shift for cell text
|
|
*
|
|
* @devnote
|
|
* Calculating this shift for vertically merged cells is tricky because
|
|
* dypCell = sum of the cell heights of all cells in the vertically
|
|
* merged set. In particular, if the table is not nested, one needs to
|
|
* wait for recalc of all rows in the set. dypText is relatively easy
|
|
* since it's the height of the top cell in the set.
|
|
*/
|
|
LONG CLayout::GetVertAlignShift(
|
|
LONG uCell, //@parm uCell to use
|
|
LONG dypText) //@parm Text height in cell
|
|
{
|
|
LONG dyp = 0;
|
|
if(IsVertMergedCell(uCell))
|
|
{
|
|
}
|
|
else if(GetCellVertAlign(uCell))
|
|
{
|
|
dyp = _dvp - _dvpBrdrTop - _dvpBrdrBot - dypText;
|
|
if(dyp > 0 && IsCellVertAlignCenter(uCell))
|
|
dyp /= 2;
|
|
}
|
|
return dyp;
|
|
}
|