📄 ldte.cpp
字号:
LONG lStreamFormat, // @parm stream format to use for loading
EDITSTREAM *pes, // @parm edit stream to load from
BOOL fTestLimit, // @parm Whether to test text limit
IUndoBuilder *publdr) // @parm undo builder to receive antievents
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::LoadFromEs");
#ifdef DEBUG
// FUTURE: Currently freezing the display prior to loading text
// is simply an optimization. This may become a requirement in the
// future. If this does become a requirement then we'll want to
// exit with an error.
if( !_ped->_pdp->IsFrozen() )
{
TRACEWARNSZ("CLightDTEngine::LoadFromEs display not frozen");
}
#endif // DEBUG
LONG cch = 0; // Default no chars read
CTxtSelection *psel;
IAntiEvent *pae = NULL;
if( publdr )
{
publdr->StopGroupTyping();
}
_ped->CheckUnicode(lStreamFormat); // If Unicode, set code page
// 1200
// other components, such as the display and backing store, will
// be able to make optimizations if they know that we are streaming
// in text or RTF data.
_ped->SetStreaming(TRUE);
if( lStreamFormat & SF_RTF ) // RTF case must precede
{ // TEXT case (see SF_x
if( !_ped->IsRich() ) // values)
{
Assert(cch == 0);
goto Exit;
}
LONG cpMin, cpMost;
// Here we do something a bit unusual for performance reasons.
// instead of letting the rtf reader generate it's own undo actions,
// we'll take care of it ourselves. Instead of generating actions
// for each little operation, we simply generate a "big" anti-event
// for the whole shebang
// There is a subtlty w.r.t. to paragraph format runs. By inserting
// text with para formatting, it's possible that we will modify the
// para formatting of the _current_ paragraph. Thus, it's necessary
// to remember what the formatting currently is for undo. Note that
// it may actually not be changed; but we go ahead and generate an
// anti-event anyways. Note that we only need to do this if cpMin is
// the middle of a paragraph
CTxtPtr tp(prg->_rpTX);
if(prg->GetCch() > 0)
tp.AdvanceCp(-prg->GetCch());
if( publdr && prg->_rpPF.IsValid() && !tp.IsAfterEOP() )
{
IParaFormatCache *ppfc;
GetParaFormatCache(&ppfc);
tp.FindEOP(tomBackward);
cpMin = tp.GetCp();
tp.FindEOP(tomForward);
cpMost = tp.GetCp();
// We must be in rich text mode, so we must be able to always
// find a paragraph.
Assert(cpMost > cpMin);
CFormatRunPtr rpPF(prg->_rpPF);
rpPF.AdvanceCp(cpMin - prg->GetCp());
pae = gAEDispenser.CreateReplaceFormattingAE( _ped, rpPF,
cpMost - cpMin, ppfc, ParaFormat);
if( pae )
publdr->AddAntiEvent(pae);
}
// First, clear the range
if( prg->GetCch() )
prg->ReplaceRange(0, NULL, publdr, SELRR_REMEMBERRANGE);
Assert(prg->GetCch() == 0);
cpMin = prg->GetCp();
CRTFRead rtfRead(prg, pes, lStreamFormat);
cch = rtfRead.ReadRtf();
if (lStreamFormat & SFF_ADJUSTENDEOP && // If range end EOP
pes->dwError == NOERROR && // wasn't deleted and
prg->_rpTX.IsAfterEOP()) // new text ends with
{ // an EOP,
prg->SetExtend(TRUE); // select and delete
prg->BackupCRLF(); // the latter
prg->ReplaceRange(0, NULL, NULL, SELRR_IGNORE);
}
cpMost = prg->GetCp();
Assert(cpMost >= cpMin);
// If nothing changed, get rid of any anti-events (like the formatting
// one) that we may have "speculatively" added
if( publdr && !_ped->GetCallMgr()->GetChangeEvent() )
{
publdr->Discard();
}
if( publdr && cpMost > cpMin )
{
// If some text was added, create an anti-event for
// it and add it in.
AssertSz(_ped->GetCallMgr()->GetChangeEvent(),
"Something changed, but nobody set the change flag");
pae = gAEDispenser.CreateReplaceRangeAE(_ped, cpMin, cpMost, 0,
NULL, NULL, NULL);
HandleSelectionAEInfo(_ped, publdr, -1, -1, cpMost, 0,
SELAE_FORCEREPLACE);
if( pae )
publdr->AddAntiEvent(pae);
}
}
else if( lStreamFormat & SF_TEXT )
{
cch = ReadPlainText( prg, pes, fTestLimit, publdr );
}
// Before updating the selection, try the auto-URL detect. This makes
// two cases better: 1. a long drag drop is now faster and 2. the
// selection _iFormat will now be udpated correctly for cases of
// copy/paste of a URL.
if( _ped->GetDetectURL() )
{
_ped->GetDetectURL()->ScanAndUpdate(publdr);
}
// The caret belongs in one of two places:
// 1. if we loaded into a selection, at the end of the new text
// 2. otherwise, we loaded an entire document, set it to cp 0
//
// ReadPlainText() and ReadRtf() set prg to an insertion point
// at the end, so if we loaded a whole document, reset it.
if( (psel = _ped->GetSel()) )
{
if( !(lStreamFormat & SFF_SELECTION) )
{
psel->Set(0,0);
}
psel->Update_iFormat(-1);
}
Exit:
_ped->SetStreaming(FALSE);
if (!fTestLimit)
{
// If we don't limit the text then we adjust the text limit
// if we have exceeded it.
_ped->TxSetMaxToMaxText();
}
return cch;
}
/*
* CLightDTEngine::SaveToEs (prg, lStreamFormat, pes)
*
* @mfunc
* save data into the given stream
*
* @rdesc
* LONG -- count of characters written
*/
LONG CLightDTEngine::SaveToEs(
CTxtRange * prg, // @parm range to drag from
LONG lStreamFormat, // @parm stream format to use for saving
EDITSTREAM *pes ) // @parm edit stream to save to
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::SaveToEs");
LONG cch = 0; // Default no chars written
_ped->CheckUnicode(lStreamFormat); // If Unicode, set code page
if( lStreamFormat & SF_RTF ) // Be sure to check for SF_RTF
{ // before checking for SF_TEXT
CRTFWrite rtfWrite( prg, pes, lStreamFormat );
cch = rtfWrite.WriteRtf();
}
else if(lStreamFormat & (SF_TEXT | SF_TEXTIZED))
{
cch = WritePlainText(prg, pes, lStreamFormat);
}
else
{
Assert(FALSE);
}
return cch;
}
/*
* CLightDTEngine::UnicodePlainTextFromRange (prg)
*
* @mfunc
* fetch the plain text from a range and puts it in an hglobal
*
* @rdesc
* an allocated HGLOBAL.
*
* @devnote
* FUTURE: Export bullets as does Word for plain text
*/
HGLOBAL CLightDTEngine::UnicodePlainTextFromRange(
CTxtRange *prg) // @parm range to get text from
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::UnicodePlainTextFromRange");
LONG cpMin, cpMost;
LONG cch = prg->GetRange(cpMin, cpMost);
LONG cchT = 2*(cch + 1);
HGLOBAL hText;
HGLOBAL hTextNew;
TCHAR * pText;
CTxtPtr tp(_ped, cpMin);
hText = GlobalAlloc(GMEM_FIXED, // Allocate 2* in
cchT * sizeof(TCHAR) ); // case all CRs
if( !hText )
return NULL;
pText = (TCHAR *)GlobalLock(hText);
if( !pText || 0 == GlobalSize(hText))
return NULL;
if( cch )
{
cch = tp.GetPlainText(cchT, pText, cpMost, FALSE);
AssertSz(cch <= cchT,
"CLightDTEngine::UnicodePlainTextFromRange: got too much text");
}
*(pText + cch) = '\0';
// GuyBark Jupiter: Unlock before the realloc, (even though the lock
// didn't do anything anyway), and beware of oom.
GlobalUnlock(hText);
if(!(hTextNew = GlobalReAlloc(hText, 2*(cch + 1), GMEM_MOVEABLE)))
{
GlobalFree(hText);
}
hText = hTextNew;
return hText;
}
/*
* CLightDTEngine::AnsiPlainTextFromRange (prg)
*
* @mfunc
* Retrieve an ANSI copy of the text in the range prg
*
* @rdesc
* HRESULT
*/
HGLOBAL CLightDTEngine::AnsiPlainTextFromRange(
CTxtRange *prg) // @parm range to get text from
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::AnsiPlainTextFromRange");
HGLOBAL hUnicode;
HGLOBAL hAnsi;
// FUTURE (alexgo): if we implement the option to store text as 8-bit
// chars, then we can make this routine more efficient
hUnicode = UnicodePlainTextFromRange(prg);
hAnsi = TextHGlobalWtoA(hUnicode);
GlobalFree(hUnicode);
return hAnsi;
}
/*
* CLightDTEngine::RtfFromRange (prg, lStreamFormat)
*
* @mfunc
* Fetch RTF text from a range and put it in an hglobal
*
* @rdesc
* an allocated HGLOBAL.
*/
HGLOBAL CLightDTEngine::RtfFromRange(
CTxtRange * prg, // @parm Range to get RTF from
LONG lStreamFormat) // @parm stream format to use for loading
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::RtfFromRange");
WRITEHGLOBAL whg;
EDITSTREAM es = {(DWORD)&whg, NOERROR, WriteHGlobal};
DWORD cb = 2*abs(prg->GetCch()) + 100; // Rough estimate
HGLOBAL hgNew;
whg.cb = cb;
whg.hglobal = GlobalAlloc(GMEM_FIXED, cb);
if(!whg.hglobal)
return NULL;
whg.cch = 0; // Nothing written yet
SaveToEs(prg, lStreamFormat & ~SF_TEXT, &es);
if(es.dwError)
{
GlobalFree(whg.hglobal);
return NULL;
}
// GuyBark Jupiter: Don't leak here.
if(!(hgNew = GlobalReAlloc(whg.hglobal, whg.cch, GMEM_MOVEABLE)))
{
GlobalFree(whg.hglobal);
}
whg.hglobal = hgNew;
return whg.hglobal;
}
//
// PROTECTED METHODS
//
#define READSIZE 4096 - 2
#define WRITESIZE 2048
/*
* CLightDTEngine::ReadPlainText (prg, pes, publdr)
*
* @mfunc
* Replaces contents of the range prg with the data given in the edit
* stream pes. Handles multibyte sequences that overlap stream buffers.
*
* @rdesc
* Count of bytes read (to be compatible with RichEdit 1.0)
*
* @devnote
* prg is modified; at the return of the call, it will be a degenerate
* range at the end of the read in text.
*
* Three kinds of multibyte/char sequences can overlap stream buffers:
* DBCS, UTF-8, and CRLF/CRCRLF combinations. DBCS and UTF-8 streams are
* converted by MultiByteToWideChar(), which cannot convert a lead byte
* (DBCS and UTF-8) that occurs at the end of the buffer, since the
* corresponding trail byte(s) will be in the next buffer. Similarly,
* in RichEdit 2.0 mode, we convert CRLFs to CRs and CRCRLFs to blanks,
* so one or two CRs at the end of the buffer require knowledge of the
* following char to determine if they are part of a CRLF or CRCRLF.
*
* To handle these overlapped buffer cases, we move the ambiguous chars
* to the start of the next buffer, rather than keeping them as part of
* the current buffer. At the start of the buffer, the extra char(s)
* needed for translation follow immediately.
*/
LONG CLightDTEngine::ReadPlainText(
CTxtRange * prg, // @parm range to read to
EDITSTREAM * pes, // @parm edit stream to read from
BOOL fTestLimit, // @parm whether limit testing is needed
IUndoBuilder *publdr ) // @parm undo builder to receive antievents
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::ReadPlainText");
CTxtEdit *ped = _ped;
LONG cbRead;
LONG cbReadTotal = 0; // No bytes read yet
LONG cch;
LONG cchConv;
LONG cchLen;
DWORD cchMax = ped->TxGetMaxLength();
LONG cCR = 0; // Count of CRs from preceding buffer
LONG cCRPrev = 0; // Count used while calc'ing new cCR
BOOL fContinue = TRUE; // Keep reading so long as TRUE
BYTE * pb; // Byte ptr to szBuf or wszBuf
TCHAR * pch; // Ptr to wszBuf
UINT uCpg = CP_ACP; // Default system Ansi code page
BOOL bCheckFont = (OnWin95FE() || OnWinNTFE()); // check DB font on FE systems
// just put a big buffer on the stack. Thankfully, we only
// run on 32bit OS's. 4K is a good read size for NT file caching.
char szBuf[READSIZE];
WCHAR wszBuf[READSIZE+2]; // Allow for moving end CRs to start
// first, empty the range
if( prg->GetCch() )
{
// Delete text in range
prg->ReplaceRange(0, NULL, publdr, SELRR_REMEMBERRANGE);
}
if(ped->_pDocInfo &&
ped->_pDocInfo->wCpg != tomInvalidCpg) // Update code page if
{ // defined
uCpg = ped->_pDocInfo->wCpg;
}
pb = (uCpg == 1200) ? (BYTE *)(wszBuf + 2) // Setup Unicode or MBCS
: (BYTE *)szBuf;
LONG j = 0; // Haven't read anything,
// so no lead byte left
while(fContinue) // from previous read
{
LONG prevChar = j; // Save byte(s) left over
// from previous read
pes->dwError = (*pes->pfnCallback)( // Read next bufferful,
pes->dwCookie, pb + j, // bypassing any lead
READSIZE - j, &cbRead); // bytes
if(pes->dwError || !cbRead && !cCR)
break; // Error or done
// adjust cbRead with previous leading byte(s)
cbRead += j;
j = 0;
cchConv = cbRead/2; // Default Unicode cch
if(uCpg != 1200 && cbRead) // Multibyte of some kind
{
Assert(pb == (BYTE *)szBuf); // Just in case...
BOOL bCopyTwo = FALSE;
// check the last byte if it is a leading byte
if (uCpg == CP_UTF8)
{
// Note: UTF-7 can be in the middle of a long sequence, so
// it can't be converted effectively in chunks
LONG cbLocalRead = cbRead - 1;
while((BYTE)szBuf[cbLocalRead -j] > 127) // Find UTF-8 lead byte
{
j++;
if((BYTE)szBuf[cbLocalRead -j] > 0xC0)
break; // Break on UTF-8 lead
} // byte
if(j > 1)
{
if (j == 3 || (BYTE) // Three-byte char or
szBuf[cbLocalRead - 1] < 0xE0) // on second byte of 2
{ // byte char:
j = 0; // Finished full char
}
else // Landed on first trail
{ // byte of 3-byte char
bCopyTwo = TRUE; // So copy last two bytes
} // down to buf start
}
}
else
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -