📄 text.cpp
字号:
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtPtr::AdjustCpCRLF");
_TEST_INVARIANT_
LONG cpSave = _cp;
unsigned ch = GetChar();
// To support UTF-16, include the following code
// if((ch & UTF16) == UTF16_TRAIL)
// AdvanceCp(-1);
if(!IsASCIIEOP(ch)) // Early out
return 0;
if(ch == LF && cpSave && PrevChar() != CR) // Landed on LF not preceded
{ // by CR, so go back to LF
AdvanceCp(1); // Else on CR of CRLF or
} // second CR of CRCRLF
if(GetChar() == CR) // Land on a CR of CRLF or
{ // second CR of CRCRLF?
CTxtPtr tp(*this);
if(tp.NextChar() == LF)
{
tp.AdvanceCp(-2); // First CR of CRCRLF ?
if(tp.GetChar() == CR) // Yes or CRLF is at start of
AdvanceCp(-1); // story. Try to back up over
} // CR (If at BOS, no effect)
}
return _cp - cpSave;
}
/*
* CTxtPtr::IsAtEOP()
*
* @mfunc
* Return TRUE iff this text pointer is at an end-of-paragraph mark
*
* @rdesc
* TRUE if at EOP
*
* @devnote
* End of paragraph marks for RichEdit 1.0 and the MLE can be CRLF
* and CRCRLF. For RichEdit 2.0, EOPs can also be CR, VT (0xb - Shift-
* Enter), and FF (0xc - page break).
*/
BOOL CTxtPtr::IsAtEOP()
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtPtr::IsAtEOP");
_TEST_INVARIANT_
unsigned ch = GetChar();
if(IsASCIIEOP(ch)) // See if LF <= ch <= CR
{ // Clone tp in case
CTxtPtr tp(*this); // AdjustCpCRLF moves
return !tp.AdjustCpCRLF(); // Return TRUE unless in
} // middle of CRLF or CRCRLF
return (ch | 1) == PS; // Allow Unicode 0x2028/9 also
}
/*
* CTxtPtr::IsAfterEOP()
*
* @mfunc
* Return TRUE iff this text pointer is just after an end-of-paragraph
* mark
*
* @rdesc
* TRUE iff text ptr follows an EOP mark
*/
BOOL CTxtPtr::IsAfterEOP()
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtPtr::IsAfterEOP");
_TEST_INVARIANT_
CTxtPtr tp(*this); // Clone to look around with
TCHAR ch = GetChar();
if(IsASCIIEOP(GetChar()) && // If in middle of CRLF
tp.AdjustCpCRLF()) // or CRCRLF, return FALSE
{
return FALSE;
}
return IsEOP(tp.PrevChar()); // After EOP if after Unicode
} // PS or LF, VT, FF, or CR
// needed for the routine below
#if cchGapInitial < 1
#error "cchGapInitial must be at least one"
#endif
/*
* CTxtPtr::FindWordBreak(action, cpMost)
*
* @mfunc
* Find a word break and move this text pointer to it.
*
* @rdesc
* Offset from cp of the word break
*/
LONG CTxtPtr::FindWordBreak(
INT action, //@parm See TxWordBreakProc header
LONG cpMost) //@parm Limiting character position
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtPtr::FindWordBreak");
_TEST_INVARIANT_
const INT breakBufSize = 16;
LONG bufferSize;
LONG cch;
LONG cchBuffer;
LONG cchChunk;
DWORD cchText = GetTextLength();
TCHAR ch = GetChar();
TCHAR chBreakBuf[breakBufSize];
LONG cpSave = _cp; // For calculating break pt
LONG ichBreak;
TCHAR * pBuf;
TCHAR const * pch;
EDITWORDBREAKPROC pfnWB = _ped->_pfnWB;
LONG t; // Temp for abs() macro
if(action == WB_CLASSIFY || action == WB_ISDELIMITER)
{
return ch ? pfnWB(&ch, 0, CbOfCch(1), action) : 0;
}
if(action & 1) // Searching forward
{ // Easiest to handle EOPs
if(action == WB_MOVEWORDRIGHT && IsEOP(ch)) // explicitly (spanning
{ // a class can go too
AdjustCpCRLF(); // far). Go to end of
AdvanceCpCRLF(); // EOP "word"
goto done;
}
// Calc. max search
if((DWORD)cpMost > cchText) // Bounds check: get < 0
cpMost = cchText; // as well as too big
cch = cpMost - _cp;
while(cch > 0)
{ // The independent buffer
cchBuffer = min(cch, breakBufSize - 1); // avoids gaps in BS
cch -= bufferSize = cchBuffer;
pBuf = chBreakBuf; // Fill buffer forward
// Grab the first character in reverse for fnWB that require 2
// chars. Note, we play with _ich to get single char fnWB
// to ignore this character.
pch = GetPchReverse(cchChunk);
if ( !cchChunk ) pch = L" "; // Any break char
*pBuf++ = *pch;
while ( cchBuffer ) // Finish filling
{
pch = GetPch(cchChunk);
if (!cchChunk) { Assert(0); break; }
cchChunk = min(cchBuffer, cchChunk);
AdvanceCp(cchChunk);
wcsncpy(pBuf, pch, cchChunk);
pBuf += cchChunk;
cchBuffer -= cchChunk;
}
ichBreak = pfnWB(chBreakBuf, 1, // Find the break
CbOfCch(bufferSize+1), action) - 1;
// Apparently, some fnWBs return ambiguous results
if(ichBreak >= 0 && ichBreak <= bufferSize)
{
// Ambiguous break pt?
// Due to the imprecise nature of the word break proc spec,
// we've reached an ambiguous condition where we don't know
// if this is really a break, or just the end of the data.
// By backing up or going forward by 2, we'll know for sure.
// NOTE: we'll always be able to advance or go back by 2
// because we guarantee that when !cch that we have
// at least breakBufSize (16) characters in the data stream.
if (ichBreak < bufferSize || !cch)
{
AdvanceCp( ichBreak - bufferSize );
break;
}
// Need to recalc break pt to disambiguate
t = AdvanceCp(ichBreak - bufferSize - 2); // abs() is a
cch += abs(t); // macro
}
}
}
else // REVERSE - code dup based on EliK "streams" concept.
{
if(!_cp) // Can't go anywhere
return 0;
if(action == WB_MOVEWORDLEFT) // Easiest to handle EOPs
{ // here
if(IsASCIIEOP(ch) && AdjustCpCRLF()) // In middle of a CRLF or
goto done; // CRCRLF "word"
ch = PrevChar(); // Check if previous char
if(IsEOP(ch)) // is an EOP char
{
if(ch == LF) // Backspace to start of
AdjustCpCRLF(); // CRLF and CRCRLF
goto done;
}
AdvanceCp(1); // Move back to start char
}
// Calc. max search
if((DWORD)cpMost > _cp) // Bounds check (also
cpMost = _cp; // handles cpMost < 0)
cch = cpMost;
while(cch > 0)
{ // The independent buffer
cchBuffer = min(cch, breakBufSize - 1); // avoids gaps in BS
cch -= bufferSize = cchBuffer;
pBuf = chBreakBuf + cchBuffer; // Fill from the end.
// Grab the first character forward for fnWB that require 2 chars.
// Note: we play with _ich to get single char fnWB to ignore this
// character.
pch = GetPch(cchChunk);
if ( !cchChunk ) pch = L" "; // Any break char
*pBuf = *pch;
while ( cchBuffer > 0 ) // Fill rest of buffer
{ // before going in reverse
pch = GetPchReverse(cchChunk );
if (!cchChunk) { Assert(0); break; }
cchChunk = min(cchBuffer, cchChunk);
AdvanceCp(-cchChunk);
pch -= cchChunk;
pBuf -= cchChunk;
wcsncpy(pBuf, pch, cchChunk);
cchBuffer -= cchChunk;
}
// Get break left.
ichBreak = pfnWB(chBreakBuf, bufferSize,
CbOfCch(bufferSize+1), action);
// Apparently, some fnWBs return ambiguous results
if(ichBreak >= 0 && ichBreak <= bufferSize)
{ // Ambiguous break pt?
// NOTE: when going in reverse, we have >= bufsize - 1
// because there is a break-after char (hyphen).
if ( ichBreak > 0 || !cch )
{
AdvanceCp(ichBreak); // Move _cp to break point.
break;
}
cch += AdvanceCp(2 + ichBreak); // Need to recalc break pt
} // to disambiguate.
}
}
done:
return _cp - cpSave; // Offset of where to break
}
/*
* INT CALLBACK TxWordBreakProc (pch, ich, cb, action)
*
* @func
* Default word break proc used in conjunction with FindWordBreak. ich
* is character offset (start position) in the buffer pch, which is cb
* bytes in length. Possible action values are:
*
* WB_CLASSIFY
* Returns char class and word break flags of char at start position.
*
* WB_ISDELIMITER
* Returns TRUE iff char at start position is a delimeter.
*
* WB_LEFT
* Finds nearest word beginning before start position using word breaks.
*
* WB_LEFTBREAK
* Finds nearest word end before start position using word breaks.
* Used by CMeasurer::Measure()
*
* WB_MOVEWORDLEFT
* Finds nearest word beginning before start position using class
* differences. This value is used during CTRL+LEFT key processing.
*
* WB_MOVEWORDRIGHT
* Finds nearest word beginning after start position using class
* differences. This value is used during CTRL+RIGHT key processing.
*
* WB_RIGHT
* Finds nearest word beginning after start position using word breaks.
* Used by CMeasurer::Measure()
*
* WB_RIGHTBREAK
* Finds nearest word end after start position using word breaks.
*
* @rdesc
* Character offset from start of buffer (pch) of the word break
*/
INT CALLBACK TxWordBreakProc(
TCHAR const *pch, //@parm Char buffer
INT ich, //@parm Char offset of _cp in buffer
INT cb, //@parm Count of bytes in buffer
INT action) //@parm Type of breaking action
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "TxWordBreakProc");
LONG cchBuff = CchOfCb(cb);
LONG cch = cchBuff - ich;
TCHAR ch;
WORD cType3[MAX_CLASSIFY_CHARS];
INT kinsokuClassifications[MAX_CLASSIFY_CHARS];
WORD * pcType3;
INT * pKinsoku1, *pKinsoku2;
WORD * pwRes;
WORD startType3 = 0;
WORD wb = 0;
WORD wClassifyData[MAX_CLASSIFY_CHARS]; // For batch classifying
if ((!(cchBuff < MAX_CLASSIFY_CHARS)) || (!(ich >= 0 && ich < cchBuff)))
{
Assert(0);
return ich;
}
// Single character actions
if ( action == WB_CLASSIFY )
return ClassifyChar(pch[ich]);
if ( action == WB_ISDELIMITER )
return !!(ClassifyChar(pch[ich]) & WBF_BREAKLINE);
if (!(cchBuff <= sizeof(wClassifyData)))
{
ASSERT(0);
return ich;
}
// Batch classify buffer for whitespace and kinsoku classes
ClassifyChars (pch, cchBuff, wClassifyData);
BatchKinsokuClassify(pch, cchBuff, cType3, kinsokuClassifications );
// Setup pointers
pKinsoku2 = kinsokuClassifications + ich; // Ptr to current kinsoku
pKinsoku1 = pKinsoku2 - 1; // Ptr to previous kinsoku
if(!(action & 1)) // WB_(MOVE)LEFTxxx
{
ich--;
Assert(ich >= 0);
}
pwRes = &wClassifyData[ich];
pcType3 = &cType3[ich]; // for ideographics
switch(action)
{
case WB_LEFT:
for(; ich >= 0 && *pwRes & WBF_BREAKLINE; // Skip preceding line
ich--, pwRes--) // break chars
; // Empty loop. Then fall
// thru to WB_LEFTBREAK
case WB_LEFTBREAK:
for(; ich >= 0 && !CanBreak(*pKinsoku1, *pKinsoku2);
ich--, pwRes--, pKinsoku1--, pKinsoku2--)
; // Empty loop
if(action == WB_LEFTBREAK) // Skip preceding line
{ // break chars
for(; ich >= 0 && *pwRes & WBF_BREAKLINE;
ich--, pwRes--)
; // Empty loop
}
return ich + 1;
case WB_MOVEWORDLEFT:
for(; ich >= 0 && (*pwRes & WBF_CLASS) == 2;// Skip preceding blank
ich--, pwRes--, pcType3--) // chars
;
if(ich >= 0) // Save starting wRes and
{ // startType3
wb = *pwRes--; // Really type1
startType3 = *pcType3--; // type3
ich--;
}
// Skip to beginning of current word
while(ich >= 0 && (*pwRes & WBF_CLASS) != 3 &&
(IsSameClass(*pwRes, wb, *pcType3, startType3) ||
!wb && ich && ((ch = pch[ich]) == '\'' || ch == RQUOTE)))
{
ich--, pwRes--, pcType3--;
}
return ich + 1;
case WB_RIGHTBREAK:
for(; cch > 0 && *pwRes & WBF_BREAKLINE; // Skip any leading line
cch--, pwRes++) // break chars
; // Empty loop
// Fall thru to WB_RIGHT
case WB_RIGHT:
// Skip to end of current word
for(; cch > 0 && !CanBreak(*pKinsoku1, *pKinsoku2);
cch--, pKinsoku1++, pKinsoku2++, pwRes++)
;
if(action != WB_RIGHTBREAK) // Skip trailing line
{ // break chars
for(; cch > 0 && *pwRes & WBF_BREAKLINE;
cch--, pwRes++)
;
}
return cchBuff - cch;
case WB_MOVEWORDRIGHT:
if(cch <= 0) // Nothing to do
return ich;
wb = *pwRes; // Save start wRes
startType3 = *pcType3; // and startType3
// Skip to end of word
if (startType3 & C3_IDEOGRAPH || // If ideographic or
(*pwRes & WBF_CLASS) == 3) // tab/cell, just
{
cch--, pwRes++; // skip one char
}
else while(cch > 0 &&
(IsSameClass(*pwRes, wb, *pcType3, startType3) || !wb &&
((ch = pch[cchBuff - cch]) == '\'' || ch == RQUOTE)))
{
cch--, pwRes++, pcType3++;
}
for(; cch > 0 && (*pwRes & WBF_CLASS) == 2; // Skip trailing blank
cch--, pwRes++) // chars
;
return cchBuff - cch;
}
TRACEERRSZSC("TxWordBreakProc: unknown action", action);
return ich;
}
/*
* CTxtPtr::ReplaceRange(cchOld, cchNew, *pch, pcpFirstRecalc)
*
* @mfunc
* replace a range of text at this text pointer.
*
* @rdesc
* count of new characters added
*
* @comm SideEffects: <nl>
* moves this text pointer to end of replaced text <nl>
* moves text block array <nl>
*
*/
DWORD CTxtPtr::ReplaceRange(
LONG cchOld, //@parm length of range to replace
// (<lt> 0 means to end of text)
DWORD cchNew, //@parm length of replacement text
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -