|
|
/*
* LINE.CPP * * Purpose: * CLine class * * Authors: * RichEdit 1.0 code: David R. Fulmer * Christian Fortini (initial conversion to C++) * Murray Sargent * * Copyright (c) 1995-1998 Microsoft Corporation. All rights reserved. */
#include "_common.h"
#include "_line.h"
#include "_measure.h"
#include "_render.h"
#include "_disp.h"
#include "_edit.h"
ASSERTDATA
/*
* CLine::Measure(&me, cchMax, xWidth, uiFlags, pliTarget) * * @mfunc * Computes line break (based on target device) and fills * in this CLine with resulting metrics on rendering device * * @rdesc * TRUE if OK * * @devnote * me is moved past line (to beginning of next line). Note: CLock is * needed in the main four routines (Measure, MeasureText, CchFromXPos, * and RenderLine), since they use the global (shared) fc().GetCcs() * facility and may use the LineServices global g_plsc and g_pols. */ BOOL CLine::Measure( CMeasurer& me, //@parm Measurer pointing at text to measure
LONG cchMax, //@parm Max cch to measure
LONG xWidth, //@parm Width of line in device units
UINT uiFlags, //@parm Flags
CLine * pliTarget) //@parm Returns target-device line metrics (optional)
{ TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLine::Measure");
CLock lock; BOOL fFirstInPara = uiFlags & MEASURE_FIRSTINPARA; BOOL fMultiLine = me.GetPdp()->IsMultiLine(); BOOL fRet;
if(fMultiLine && fFirstInPara && me.GetPrevChar() == VT) { fFirstInPara = FALSE; uiFlags &= ~MEASURE_FIRSTINPARA; }
if(!(uiFlags & MEASURE_DONTINIT)) me.NewLine(fFirstInPara);
else if(fFirstInPara) me._li._bFlags |= fliFirstInPara;
BYTE bNumber = me._wNumber < 256 // Store current para # offset
? me._wNumber : 255; me._li._bNumber = bNumber; #ifdef LINESERVICES
CMeasurer *pmeSave; COls * pols = me.GetPols(&pmeSave); // Try for LineServices object
if(pols) // Got it: use LineServices
{ fRet = pols->MeasureLine(xWidth, pliTarget); pols->SetMeasurer(pmeSave); // Restore previous pme
} else // LineServices not active
#endif
fRet = me.MeasureLine(cchMax, xWidth, uiFlags, pliTarget);
if(!fRet) return FALSE;
*this = me._li; // Copy over line info
if(!fMultiLine) // Single-line controls can't
return TRUE; // have paragraph numbering
if(me.IsInOutlineView()) { if(IsHeadingStyle(me._pPF->_sStyle)) // Store heading number if relevant
_nHeading = (BYTE)(-me._pPF->_sStyle - 1);
if(me._pPF->_wEffects & PFE_COLLAPSED) // Cache collapsed bit
_fCollapsed = TRUE; }
_bNumber = bNumber; if(_bFlags & fliHasEOP) // Check for new para number
{ const CParaFormat *pPF = me.GetPF();
me._wNumber = (WORD)pPF->UpdateNumber(me._wNumber, me._pPF); _fNextInTable = pPF->InTable() && me.GetCp() < me.GetTextLength(); } return TRUE; } /*
* CLine::Render(&re) * * @mfunc * Render visible part of this line * * @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. */ BOOL CLine::Render( CRenderer& re) //@parm Renderer to use
{ if(_fCollapsed) // Line is collapsed in Outline view
{ re.Advance(_cch); // Bypass line
return TRUE; }
BOOL fRet; CLock lock; POINT pt = re.GetCurPoint();
#ifdef LINESERVICES
CMeasurer *pmeSave; COls *pols = re.GetPols(&pmeSave); // Try for LineServices object
if(pols) { fRet = pols->RenderLine(*this); pols->SetMeasurer(pmeSave); // Restore previous pme
} else #endif
fRet = re.RenderLine(*this);
pt.y += GetHeight(); // Advance to next line position
re.SetCurPoint(pt); return fRet; }
/*
* CLine::CchFromXPos(&me, x, pdispdim, pHit) * * @mfunc * Computes cch corresponding to x position in a line. * Used for hit testing. * * @rdesc * cch found up to the x coordinate x * * @devnote * me is moved to the cp at the cch offset returned */ LONG CLine::CchFromXpos( CMeasurer& me, //@parm Measurer position at start of line
POINT pt, //@parm pt.x is x coord to search for
CDispDim*pdispdim, //@parm Returns display dimensions
HITTEST *phit, //@parm Returns hit type at x
LONG *pcpActual) const //@parm actual CP above with display dimensions
{ TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLine::CchFromXpos"); #ifdef Boustrophedon
//if(_pPF->_wEffects & PFE_BOUSTROPHEDON)
{ RECT rcView; me.GetPed()->_pdp->GetViewRect(rcView, NULL); pt.x = rcView.right - pt.x; } #endif
CLock lock; const BOOL fFirst = _bFlags & fliFirstInPara; *phit = HT_Text; LONG cpActual = me.GetCp(); CDispDim dispdim; me._li = *this; me._li._cch = 0; // Default zero count
*phit = me.HitTest(pt.x);
if(*phit == HT_Text || *phit == HT_RightOfText) // To right of left margin
{ me.NewLine(fFirst);
#ifdef LINESERVICES
CMeasurer *pmeSave; COls *pols = me.GetPols(&pmeSave);// Try for LineServices object
if(pols) // Got it: use LineServices
{ pols->CchFromXpos(pt, &dispdim, &cpActual); pols->SetMeasurer(pmeSave); // Restore previous pme
} else #endif
if(me.Measure(pt.x - _xLeft, _cch, MEASURE_BREAKBEFOREWIDTH | MEASURE_IGNOREOFFSET | (fFirst ? MEASURE_FIRSTINPARA : 0)) >= 0) { LONG xWidthBefore = me._li._xWidth; cpActual = me.GetCp(); if (me._li._cch < _cch) { dispdim.dx = me._xAddLast; if (pt.x - _xLeft > xWidthBefore + dispdim.dx / 2) { me.Advance(1); me._li._cch++; me._li._xWidth += dispdim.dx; } } }
me._rpCF.AdjustBackward(); DWORD dwEffects = me.GetCF()->_dwEffects; if(dwEffects & CFE_LINK) *phit = HT_Link; else if(dwEffects & CFE_ITALIC) *phit = HT_Italic;
#ifdef UNICODE_SURROGATES
// Until we support UTF-16 surrogate characters, don't allow hit in
// middle of a surrogate pair
if(IN_RANGE(0xDC00, me.GetChar(), 0xDFFF)) { me.Advance(1); me._li._cch++; } #endif
}
if (pdispdim) *pdispdim = dispdim; if (pcpActual) *pcpActual = cpActual;
return me._li._cch; }
/*
* CLine::XposFromCch(&me, cch, taMode, pdispdim, pdy) * * @mfunc * Measures cch characters starting from this text ptr, returning * the width measured and setting yOffset = y offset relative to * top of line and dx = halfwidth of character at me.GetCp() + cch. * Used for caret placement and object location. pdx returns offset * into the last char measured (at me.GetCp + cch) if taMode includes * TA_CENTER (dx = half the last char width) or TA_RIGHT (dx = whole * char width). pdy returns the vertical offset relative to the top * of the line if taMode includes TA_BASELINE or TA_BOTTOM. * * @rdesc * width of measured text * * @devnote * me may be moved. */ LONG CLine::XposFromCch( CMeasurer& me, //@parm Measurer pointing at text to measure
LONG cch, //@parm Max cch to measure
UINT taMode, //@parm Text-align mode
CDispDim * pdispdim, //@parm display dimensions
LONG * pdy) const //@parm dy offset due to taMode
{ CLock lock; LONG xWidth; BOOL fPols = FALSE; CDispDim dispdim; LONG dy = 0;
#ifdef LINESERVICES
CMeasurer *pmeSave; COls *pols = me.GetPols(&pmeSave); // Try for LineServices object
if(pols) { // Got it: use LineServices
if(cch) taMode &= ~TA_STARTOFLINE; // Not start of line
if(cch != _cch) taMode &= ~TA_ENDOFLINE; // Not end of line
xWidth = pols->MeasureText(cch, taMode, &dispdim); pols->SetMeasurer(pmeSave); // Restore previous pme
fPols = TRUE; } else #endif
xWidth = me.MeasureText(cch) + _xLeft;
if(taMode != TA_TOP) { // Check for vertical calculation request
if(taMode & TA_BASELINE) // Matches TA_BOTTOM and
{ // TA_BASELINE
if(!_fCollapsed) { dy = _yHeight; AssertSz(_yHeight != -1, "control has no height; used to use default CHARFORMAT"); if((taMode & TA_BASELINE) == TA_BASELINE) dy -= _yDescent; // Need "== TA_BASELINE" to
} // distinguish from TA_BOTTOM
} // Check for horizontal calculation request
if(taMode & TA_CENTER && !fPols) // If align to center or right of
{ if (cch == 0) dispdim.dx = me.MeasureText(1) + _xLeft - xWidth; else dispdim.dx = me._xAddLast; // char, get char width
} }
if (!fPols) { if((taMode & TA_CENTER) == TA_CENTER) xWidth += dispdim.dx / 2; else if (taMode & TA_RIGHT) xWidth += dispdim.dx; }
if (pdispdim) *pdispdim = dispdim; if (pdy) *pdy = dy;
return xWidth; } /*
* CLine::GetHeight() * * @mfunc * Get line height unless in outline mode and collasped, in * which case get 0. * * @rdesc * Line height (_yHeight), unless in outline mode and collapsed, * in which case 0. */ LONG CLine::GetHeight() const { return _fCollapsed ? 0 : _yHeight; }
BOOL CLine::IsEqual(CLine& li) { // CF - I dont know which one is faster
// MS3 - CompareMemory is certainly smaller
// return !CompareMemory (this, pli, sizeof(CLine) - 4);
return _xLeft == li._xLeft && _xWidth == li._xWidth && _yHeight == li._yHeight && _yDescent == li._yDescent && _cch == li._cch && _cchWhite == li._cchWhite; }
// ===================== CLinePtr: Line Run Pointer ==========================
CLinePtr::CLinePtr(CDisplay *pdp) { TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::CLinePtr");
_pdp = pdp; _pLine = NULL; _pdp->InitLinePtr(* this); }
void CLinePtr::Init (CLine & line) { TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::Init");
_pRuns = 0; _pLine = &line; _iRun = 0; _ich = 0; }
void CLinePtr::Init (CLineArray & line_arr) { TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::Init");
_pRuns = (CRunArray *) & line_arr; _iRun = 0; _ich = 0; }
void CLinePtr::RpSet(LONG iRun, LONG ich) { TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::RpSet");
// See if this is a multi-line ptr
if(_pRuns) CRunPtr<CLine>::SetRun(iRun, ich); else { // single line, just reinit and set _ich
AssertSz(iRun == 0, "CLinePtr::RpSet() - single line and iRun != 0"); _pdp->InitLinePtr(* this); // to line 0
_ich = ich; } }
// Move runptr by a certain number of cch/runs
BOOL CLinePtr::RpAdvanceCp(LONG cch) { TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::RpAdvanceCp");
// See if this is a multi-line ptr
if(_pRuns) return (cch == CRunPtr<CLine>::AdvanceCp(cch));
return RpAdvanceCpSL(cch); } BOOL CLinePtr::operator --(int) { TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::operator --");
return _pRuns ? PrevRun() : OperatorPostDeltaSL(-1); }
BOOL CLinePtr::operator ++(int) { TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::operator ++");
return _pRuns ? NextRun() : OperatorPostDeltaSL(+1); }
/*
* CLinePtr::RpAdvanceCpSL(cch) * * @mfunc * move this line pointer forward or backward on the line * * @rdesc * TRUE iff could advance cch chars within current line */ BOOL CLinePtr::RpAdvanceCpSL( LONG cch) //@parm signed count of chars to advance by
{ TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::RpAdvanceCpSL");
Assert(!_pRuns); if(!_pLine) return FALSE;
_ich += cch;
if(_ich < 0) { _ich = 0; return FALSE; }
if(_ich > _pLine->_cch) { _ich = _pLine->_cch; return FALSE; }
return TRUE; }
/*
* CLinePtr::OperatorPostDeltaSL(Delta) * * Purpose: * Implement line-ptr ++ and -- operators for single-line case * * Arguments: * Delta 1 for ++ and -1 for -- * * Return: * TRUE iff this line ptr is valid */ BOOL CLinePtr::OperatorPostDeltaSL(LONG Delta) { TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::OperatorPostDeltaSL");
AssertSz(_iRun <= 1 && !_pRuns, "LP::++: inconsistent line ptr");
if(_iRun == -Delta) // Operation validates an
{ // invalid line ptr by moving
_pdp->InitLinePtr(* this); // to line 0
return TRUE; } _iRun = Delta; // Operation invalidates this line
_ich = 0; // ptr (if it wasn't already)
return FALSE; }
CLine * CLinePtr::operator ->() const { return _pRuns ? (CLine *)_pRuns->Elem(_iRun) : _pLine; }
CLine * CLinePtr::GetLine() const { return _pRuns ? (CLine *)_pRuns->Elem(_iRun) : _pLine; }
CLine & CLinePtr::operator *() const { return *(_pRuns ? (CLine *)_pRuns->Elem(_iRun) : _pLine); }
CLine & CLinePtr::operator [](LONG dRun) { TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::operator []");
if(_pRuns) return *(CLine *)CRunPtr<CLine>::GetRun(dRun);
AssertSz(dRun + _iRun == 0 , "LP::[]: inconsistent line ptr");
return *(CLine *)CRunPtr<CLine>::GetRun(_iRun); }
BOOL CLinePtr::IsValid() { return !_pRuns ? _pLine != NULL : CRunPtrBase::IsValid(); }
/*
* CLinePtr::RpSetCp(cp, fAtEnd) * * Purpose * Set this line ptr to cp allowing for ambigous cp and taking advantage * of _cpFirstVisible and _iliFirstVisible * * Arguments: * cp position to set this line ptr to * fAtEnd if ambiguous cp: * if fAtEnd = TRUE, set this line ptr to end of prev line; * else set to start of line (same cp, hence ambiguous) * Return: * TRUE iff able to set to cp */ BOOL CLinePtr::RpSetCp( LONG cp, BOOL fAtEnd) { TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::RpSetCp");
_ich = 0; if(!_pRuns) { // This is a single line so just go straight to the single
// line advance logic. It is important to note that the
// first visible character is irrelevent to the cp advance
// for single line displays.
return RpAdvanceCpSL(cp); }
BOOL fRet; LONG cpFirstVisible = _pdp->GetFirstVisibleCp();
if(cp > cpFirstVisible / 2) { // cpFirstVisible closer than 0
_iRun = _pdp->GetFirstVisibleLine(); fRet = RpAdvanceCp(cp - cpFirstVisible); } else fRet = (cp == CRunPtr<CLine>::BindToCp(cp)); // Start from 0
if(fAtEnd) // Ambiguous-cp caret position
AdjustBackward(); // belongs at prev EOL
return fRet; }
/*
* CLinePtr::FindParagraph(fForward) * * @mfunc * Move this line ptr to paragraph (fForward) ? end : start, * and return change in cp * * @rdesc * change in cp */ LONG CLinePtr::FindParagraph( BOOL fForward) //@parm TRUE move to para end; else to para start
{ TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CLinePtr::FindParagraph");
LONG cch; CLine * pLine = GetLine();
if(!fForward) // Go to para start
{ cch = 0; // Default already at para start
if (RpGetIch() != pLine->_cch || !(pLine->_bFlags & fliHasEOP)) // It isn't at para start
{ cch = -RpGetIch(); // Go to start of current line
while(!(pLine->_bFlags & fliFirstInPara) && (*this) > 0) { (*this)--; // Go to start of prev line
pLine = GetLine(); cch -= pLine->_cch; // Subtract # chars in line
} _ich = 0; // Leave *this at para start
} } else // Go to para end
{ cch = GetCchLeft(); // Go to end of current line
while(((*this) < _pdp->LineCount() - 1 || _pdp->WaitForRecalcIli((LONG)*this + 1)) && !((*this)->_bFlags & fliHasEOP)) { (*this)++; // Go to start of next line
cch += (*this)->_cch; // Add # chars in line
} _ich = (*this)->_cch; // Leave *this at para end
} return cch; }
/*
* CLinePtr::GetAdjustedLineLength * * @mfunc returns the length of the line _without_ EOP markers * * @rdesc LONG; the length of the line */ LONG CLinePtr::GetAdjustedLineLength() { CLine * pline = GetLine();
return pline->_cch - pline->_cchEOP; }
/*
* CLinePtr::GetCchLeft() * * @mfunc * Calculate length of text left in run starting at the current cp. * Complements GetIch(), which is length of text up to this cp. * * @rdesc * length of text so calculated */ LONG CLinePtr::GetCchLeft() const { return _pRuns ? CRunPtrBase::GetCchLeft() : _pLine->_cch - _ich; }
/*
* CLinePtr::GetNumber() * * @mfunc * Get paragraph number * * @rdesc * paragraph number */ WORD CLinePtr::GetNumber() { if(!IsValid()) return 0;
_pLine = GetLine(); if(!_iRun && _pLine->_bNumber > 1) _pLine->_bNumber = 1;
return _pLine->_bNumber; }
|