📄 text.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*
* @doc INTERNAL
*
* @module TEXT.C -- CTxtPtr implementation |
*
* Authors: <nl>
* Original RichEdit code: David R. Fulmer <nl>
* Christian Fortini <nl>
* Murray Sargent <nl>
*
* History: <nl>
* 6/25/95 alexgo cleanup and reorganization (use run pointers now)
*
*/
#include "_common.h"
#include "_text.h"
#include "_edit.h"
#include "_antievt.h"
#include "_clasfyc.h"
ASSERTDATA
//-----------------------------Internal functions--------------------------------
// Text Block management
LOCAL void TxDivideInsertion(DWORD cch, DWORD ichBlock, DWORD cchAfter,
DWORD *pcchFirst, DWORD *pcchLast);
#define IsSameNonFEClass(_c1, _c2) (!(((_c1) ^ (_c2)) & WBF_CLASS))
#define IdeoKanaTypes (C3_HALFWIDTH | C3_FULLWIDTH | C3_KATAKANA | C3_HIRAGANA)
#define IdeoTypes (IdeoKanaTypes | C3_IDEOGRAPH)
#define IsIdeographic(_c1) ( 0 != (_c1 & (C3_KATAKANA | C3_HIRAGANA | C3_IDEOGRAPH)) )
/*
* IsSameClass(currType1, startType1, currType3, startType3 )
*
* @func Used to determine word breaks.
*
* @comm Ideographic chars are all considered to be unique, so that only
* one at a time is selected
*/
static BOOL IsSameClass(WORD currType1, WORD startType1,
WORD currType3, WORD startType3 )
{
BOOL fIdeographic = IsIdeographic(currType3);
// Do classifications for startType3 being ideographic
if (IsIdeographic(startType3))
{
WORD checkTypes = (currType3 & IdeoTypes) ^ (startType3 & IdeoTypes);
// We only get picky with non-ideographic Kana chars
// C3_HALFWIDTH | C3_FULLWIDTH | C3_KATAKANA | C3_HIRAGANA.
return fIdeographic && (startType3 & IdeoKanaTypes) &&
(!checkTypes || checkTypes == C3_FULLWIDTH || checkTypes == C3_HIRAGANA ||
checkTypes == (C3_FULLWIDTH | C3_HIRAGANA));
}
// Do classifications for nonideographic startType3
return !fIdeographic && IsSameNonFEClass(currType1, startType1);
}
static ClassifyChar(TCHAR ch)
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "ClassifyChar");
WORD wRes;
if (IsKorean(ch)) // special Korean class
return 0x0f;
if (ch == WCH_EMBEDDING) // Objects
return 2 | WBF_BREAKAFTER;
if (!W32->GetStringTypeEx(LOCALE_SYSTEM_DEFAULT, CT_CTYPE1, &ch, 1, &wRes))
{
return 0; //GetStringTYpe failed - nothing more to do
}
if(wRes & C1_SPACE)
{
if(wRes & C1_BLANK) // Only TAB, BLANK, and
{ // nobreak BLANK are here
if(ch == 0x20)
return 2 | WBF_BREAKLINE | WBF_ISWHITE;
if(ch == TAB)
return 3 | WBF_ISWHITE;
return 2 | WBF_ISWHITE;
}
if(ch == CELL)
return 3 | WBF_ISWHITE;
return 4 | WBF_ISWHITE;
}
if(wRes & C1_PUNCT)
return ch == '-' ? (1 | WBF_BREAKAFTER) : 1;
return 0;
}
static void ClassifyChars(const TCHAR *pch, LONG cch, WORD *pwRes)
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "ClassifyChars");
WORD wRes;
TCHAR ch;
if (!W32->GetStringTypeEx(LOCALE_SYSTEM_DEFAULT, CT_CTYPE1, pch, cch, pwRes))
{
//since we don't return a value here, we need to make sure that pwRes is initialized
ZeroMemory(pwRes, cch);
return;
}
while(cch--)
{
wRes = *pwRes;
ch = *pch++;
if (IsKorean(ch))
wRes = 0x0f; // special Korean class
else if (ch == WCH_EMBEDDING) // Objects
wRes = 2 | WBF_BREAKAFTER;
else if(wRes & C1_SPACE)
{
if (wRes & C1_BLANK)
{
wRes = 2 | WBF_ISWHITE;
if(ch == 0x20)
wRes = 2 | WBF_BREAKLINE | WBF_ISWHITE;
if(ch == TAB)
wRes = 3 | WBF_ISWHITE;
}
else
wRes = 4 | WBF_ISWHITE;
}
else if(ch == CELL)
wRes = 3 | WBF_ISWHITE;
else if(wRes & C1_PUNCT)
wRes = ch == '-' ? (1 | WBF_BREAKAFTER) : 1;
else
wRes = 0;
*pwRes++ = wRes;
}
}
/*
* IsEOP(ch)
*
* @func
* Used to determine if ch is an EOP char, i.e., CR, LF, VT, FF, PS, or
* LS (Unicode paragraph/line separator). This function (or its CR/LF
* subset) is often inlined, but is useful if speed isn't critical.
*
* @rdesc
* TRUE if ch is an end-of-paragraph char
*/
BOOL IsEOP(unsigned ch)
{
return IN_RANGE(LF, ch, CR) || (ch | 1) == PS;
}
/*
* IsWhiteSpace(ch)
*
* @func
* Used to determine if ch is an EOP char (see IsEOP() for definition),
* TAB or blank. This function is used in identifying sentence start
* and end.
*
* @rdesc
* TRUE if ch is whitespace
*/
BOOL IsWhiteSpace(unsigned ch)
{
return ch == ' ' || IN_RANGE(CELL, ch, CR) || (ch | 1) == PS;
}
/*
* IsSentenceTerminator(ch)
*
* @func
* Used to determine if ch is a standard sentence terminator character,
* namely, '?', '.', or '!'
*
* @rdesc
* TRUE if ch is a question mark, period, or exclamation point.
*/
BOOL IsSentenceTerminator(unsigned ch)
{
return ch == '?' || ch == '.' || ch == '!'; // Std sentence delimiters
}
// =========================== Invariant stuff ==================================================
#define DEBUG_CLASSNAME CTxtPtr
#include "_invar.h"
// =============================== CTxtPtr ======================================================
#ifdef DEBUG
/*
* CTxtPtr::Invariant
*
* @mfunc invariant check
*/
BOOL CTxtPtr::Invariant( ) const
{
static LONG numTests = 0;
numTests++; // how many times we've been called.
// make sure _cp is within range.
Assert(_cp >= 0 );
CRunPtrBase::Invariant();
if( _prgRun )
{
// we use less than or equals here so that we can be an insertion
// point at the *end* of the currently existing text (cpLim).
Assert(_cp <= GetTextLength());
// make sure all the blocks are consistent...
((CTxtArray *)_prgRun)->Invariant();
}
Assert( _cp == CRunPtrBase::GetCp() );
return TRUE;
}
/*
* CTxtPtr::MoveGapToEndOfBlock ()
*
* @mfunc
* Function to move buffer gap to current block end to aid in debugging
*/
void CTxtPtr::MoveGapToEndOfBlock () const
{
CTxtBlk *ptb = GetRun(0);
ptb->MoveGap(ptb->_cch); // Move gaps to end of cur block
}
#endif // DEBUG
/*
* CTxtPtr::CTxtPtr(ped, cp)
*
* @mfunc constructor
*/
CTxtPtr::CTxtPtr (
CTxtEdit *ped, //@parm Ptr to CTxtEdit instance
DWORD cp) //@parm cp to set the pointer to
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtPtr::CTxtPtr");
_ped = ped;
_cp = 0;
SetRunArray((CRunArray *) &ped->GetTxtStory()->_TxtArray);
_cp = BindToCp(cp);
}
/*
* CTxtPtr::CTxtPtr(&tp)
*
* @mfunc Copy Constructor
*/
CTxtPtr::CTxtPtr (
const CTxtPtr &tp)
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtPtr::CTxtPtr");
// copy all the values over
*this = tp;
}
/*
* CTxtPtr::GetTextLength()
*
* @mfunc
* Return count of characters in the story pointed to by this
* text ptr. Includes the story's final CR in the count
*
* @rdesc
* cch for the story pointed to by this text ptr
*
* @devnote
* This method returns 0 if the text ptr is a zombie, a state
* identified by _ped = NULL.
*/
DWORD CTxtPtr::GetTextLength() const
{
return _ped ? ((CTxtArray *)_prgRun)->_cchText : 0;
}
/*
* CTxtPtr::GetChar()
*
* @mfunc
* Return character at this text pointer, NULL if text pointer is at
* end of text
*
* @rdesc
* Character at this text ptr
*/
TCHAR CTxtPtr::GetChar()
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtPtr::GetChar");
LONG cchValid;
const TCHAR *pch = GetPch(cchValid);
return pch ? *pch : 0;
}
/*
* CTxtPtr::GetPrevChar()
*
* @mfunc
* Return character just before this text pointer, NULL if text pointer
* beginning of text
*
* @rdesc
* Character just before this text ptr
*/
TCHAR CTxtPtr::GetPrevChar()
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtPtr::GetPrevChar");
LONG cchValid;
const TCHAR *pch = GetPchReverse(cchValid);
if (pch && (pch-1))
return *(pch - 1);
else
return 0;
}
/*
* CTxtPtr::GetPch(&cchValid)
*
* @mfunc
* return a character pointer to the text at this text pointer
*
* @rdesc
* a pointer to an array of characters. May be NULL. If non-null,
* then cchValid is guaranteed to be at least 1
*/
const TCHAR * CTxtPtr::GetPch(
LONG & cchValid) //@parm Count of characters for which the
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtPtr::GetPch");
// returned pointer is valid
_TEST_INVARIANT_
DWORD ich = _ich;
TCHAR * pchBase;
CTxtBlk * ptb = _prgRun ? GetRun(0) : NULL;
if( !ptb )
{
cchValid = 0;
return NULL;
}
// if we're at the edge of a run, grab the next run or
// stay at the current run.
if( _ich == ptb->_cch )
{
if( _iRun < Count() - 1 )
{
// set us to the next text block
ptb = GetRun(1);
ich = 0;
}
else
{
//we're at the very end of the text, just return NULL
cchValid = 0;
return NULL;
}
}
AssertSz(CbOfCch(ich) <= ptb->_cbBlock,
"CTxtPtr::GetPch(): _ich bigger than block");
pchBase = ptb->_pch + ich;
// Check to see if we need to skip over gap. Recall that
// the game may come anywhere in the middle of a block,
// so if the current ich (note, no underscore, we want
// the active ich) is beyond the gap, then recompute pchBase
// by adding in the size of the block.
//
// cchValid will then be the number of characters left in
// the text block (or _cch - ich)
if(CbOfCch(ich) >= ptb->_ibGap)
{
pchBase += CchOfCb(ptb->_cbBlock) - ptb->_cch;
cchValid = ptb->_cch - ich;
}
else
{
//we're valid until the buffer gap (or see below).
cchValid = CchOfCb(ptb->_ibGap) - ich;
}
Assert(cchValid);
return pchBase;
}
/*
* CTxtPtr::GetPchReverse(&cchValidReverse, pcchValid)
*
* @mfunc
* return a character pointer to the text at this text pointer
* adjusted so that there are some characters valid *behind* the
* pointer.
*
* @rdesc
* a pointer to an array of characters. May be NULL. If non-null,
* then cchValidReverse is guaranteed to be at least 1
*/
const TCHAR * CTxtPtr::GetPchReverse(
LONG & cchValidReverse, //@parm length for reverse
LONG * pcchValid) //@parm length forward
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtPtr::GetPchReverse");
_TEST_INVARIANT_
LONG cchTemp;
DWORD ich = _ich;
TCHAR * pchBase;
CTxtBlk * ptb = GetRun(0);
if( !ptb )
{
cchValidReverse = 0;
return NULL;
}
// if we're at the edge of a run, grab the previous run or
// stay at the current run.
if( !_ich )
{
if( _iRun )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -