📄 ldte.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 LDTE.C - RichEdit Light Data Transfer Engine |
*
* This file contains data transfer code using IDataObject
*
* Author: <nl>
* alexgo (4/25/95)
*
* Revisions: <nl>
* murrays (7/6/95) auto-doc'd and added RTF support
*
* FUTURE (AlexGo): <nl>
* Maybe merge this class with CTxtRange to make more efficient use of
* the this ptr. All but two methods use a CTxtRange and one of these
* could be global. The two are:
*
* GetDropTarget( IDropTarget **ppDropTarget )
* GetDataObjectInfo(IDataObject *pdo, DWORD *pDOIFlags) // Can be global
*
* In general, a range can spawn data objects, which need to have a clone
* of the range in case the range is moved around. The contained range
* is used for delayed rendering. A prenotification is sent to the data
* object just before the data object's data is to be changed. The data
* object then renders the data in its contained range, whereupon the
* object becomes independent of the range and destroys the range.
*
* @devnote
* We use the word ANSI in a general way to mean any multibyte character
* system as distinguished from 16-bit Unicode. Technically, ANSI refers
* to a specific single-byte character system (SBCS). We translate
* between "ANSI" and Unicode text using the Win32
* MultiByteToWideChar() and WideCharToMultiByte() APIs.
*
*/
#include "_common.h"
#include "_range.h"
#include "_ldte.h"
#include "_m_undo.h"
#include "_antievt.h"
#include "_edit.h"
#include "_disp.h"
#include "_select.h"
#include "_dragdrp.h"
#include "_dxfrobj.h"
#include "_rtfwrit.h"
#include "_rtfread.h"
#include "_urlsup.h"
ASSERTDATA
//Local Prototypes
DWORD CALLBACK ReadHGlobal (DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb);
DWORD CALLBACK WriteHGlobal(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb);
#define SFF_ADJUSTENDEOP 0x80000000
//
// LOCAL METHODS
//
/*
* void PasteSetupDBCFont ( )
*
* @func
* This routine is to setup the DBC font by calling CheckChangeFont() with
* the current system LCID. This is used during pasting/inserting
* plain text.
*/
void PasteSetupDBCFont ( CTxtEdit *ped )
{
WORD lcidSystem;
CTxtSelection *psel;
if (ped->_fSingleCodePage || !ped->IsRich())
{
goto leave;
}
psel = ped->GetSel();
if(!psel)
{
goto leave;
}
if (psel->GetCch())
{
// for selection, we need to get the character format at cpMin+1
CTxtRange rg( ped, psel->GetCpMin()+1, 0 );
LONG iFormat;
iFormat = rg.Get_iCF ();
psel->Set_iCF( iFormat );
// release format that has been AddRef in Get_iCF()
ReleaseFormats(iFormat, -1);
psel->SetUseiFormat( TRUE );
}
lcidSystem = GetSystemDefaultLCID();
psel->CheckChangeFont( ped, FALSE, lcidSystem,
ConvertLanguageIDtoCodePage(lcidSystem) );
leave:
return;
}
/*
* ReadHGlobal(dwCookie, pbBuff, cb, pcb)
*
* @func
* EDITSTREAM callback for reading from an hglobal
*
* @rdesc
* es.dwError
*/
DWORD CALLBACK ReadHGlobal(
DWORD dwCookie, // @parm READHGLOBAL *
LPBYTE pbBuff, // @parm Buffer to fill
LONG cb, // @parm Buffer length
LONG * pcb) // @parm Out parm for # bytes stored
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "ReadHGlobal");
READHGLOBAL * const prhg = (READHGLOBAL *)dwCookie;
cb = min(cb, prhg->cbLeft);
CopyMemory(pbBuff, prhg->ptext, cb);
prhg->cbLeft -= cb;
prhg->ptext += cb;
if(pcb)
*pcb = cb;
return NOERROR;
}
/*
* WriteHGlobal(dwCookie, pbBuff, cb, pcb)
*
* @func
* EDITSTREAM callback for writing ASCII to an hglobal
*
* @rdesc
* error (E_OUTOFMEMORY or NOERROR)
*/
DWORD CALLBACK WriteHGlobal(
DWORD dwCookie, // @parm WRITEHGLOBAL *
LPBYTE pbBuff, // @parm Buffer to write from
LONG cb, // @parm Buffer length
LONG * pcb) // @parm Out parm for # bytes written
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "WriteHGlobal");
WRITEHGLOBAL * const pwhg = (WRITEHGLOBAL *)dwCookie;
HGLOBAL hglobal = pwhg->hglobal;
LPSTR pstr;
if (pwhg->cch < 0 || cb < 0 || pwhg->cch + cb < pwhg->cch)
return (DWORD)E_OUTOFMEMORY;
if(pwhg->cch + cb > pwhg->cb) // Less than requested cb in
{ // current Alloc
if (pwhg->cb < 0 || 2 * pwhg->cb < pwhg->cb)
return (DWORD)E_OUTOFMEMORY;
ULONG cbNewSize = GROW_BUFFER(pwhg->cb, cb);
hglobal = GlobalReAlloc(hglobal, cbNewSize, GMEM_MOVEABLE);
if(!hglobal)
return (DWORD)E_OUTOFMEMORY;
pwhg->hglobal = hglobal; // May be superfluous...
pwhg->cb = cbNewSize;
}
pstr = (LPSTR)GlobalLock(hglobal);
if(!pstr)
return (DWORD)E_OUTOFMEMORY;
CopyMemory(pstr + pwhg->cch, pbBuff, cb);
GlobalUnlock(hglobal);
pwhg->cch += cb;
if(pcb)
*pcb = cb;
return NOERROR;
}
//
// PUBLIC METHODS
//
/*
* Cleanse(pchD, pchS, cchS)
*
* @func
* "Cleanses" the source string pchS of length cch by copying it to the
* destination string pchD ignoring LFs and replacing Unicode paragraph
* (0x2029) and line (0x2028) separators by Word/TOM-compatible CR and
* VT, respectively. Returns resulting clensed string length. Also
* replaces CRCRLFs (MLE soft line breaks) by blanks.
*
* @rdesc
* Length of cleansed string.
*
* @devnote
* Caller needs to be sure that destination string is as long as the
* source string, in case no LFs are encountered. pchD can point to
* the same string as pchS, since the cleansed string cannot be longer
* than the input string. However if pchS points at a buffer that
* doesn't contain the whole input string, then the caller has to deal
* with CRLF and CRCRLF combos that overlap buffers.
*/
LONG Cleanse(
TCHAR * pchD, //@parm Source string
const TCHAR * pchS, //@parm Destination string
LONG cchS) //@parm Length of source string
{
LONG cchD = 0;
unsigned ch; // Current character value
LONG i = cchS; // Loop counter
while(i-- > 0)
{
ch = *pchS++; // Examine next char
if(IsASCIIEOP(ch)) // Handle CR and LF combos
{
if(ch == CR)
{
if(i > 1 && *pchS == CR && *(pchS+1) == LF)
{
ch = ' '; // Translate CRCRLF to ' '
pchS += 2;
i -= 2;
}
else if(i && *pchS == LF) // Bypass LF of CRLF
{
pchS++;
i--;
}
}
else if(ch == LF) // Treat lone LFs as EOPs, i.e.,
ch = CR; // be nice to Unix text files
}
else if((ch | 1) == PS) // Translate Unicode para/line
ch = (ch == PS) ? CR : VT; // separators into CR/VT
*pchD++ = ch;
cchD++;
}
return cchD;
}
/*
* CLightDTEngine::CLightDTEngine()
*
* @mfunc
* Constructor for Light Data Transfer Engine
*/
CLightDTEngine::CLightDTEngine()
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::CLightDTEngine");
_ped = NULL;
_pdt = NULL;
_pdo = NULL;
_fUseLimit = FALSE;
_fOleless = FALSE;
}
/*
* CLightDTEngine::~CLightDTEngine
*
* @mfunc
* Handles all necessary clean up for the object..
*/
CLightDTEngine::~CLightDTEngine()
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::~CLightDTEngine");
if( _pdt )
{
_pdt->Zombie();
_pdt->Release();
_pdt = NULL;
}
Assert(_pdo == NULL);
}
/*
* CLightDTEngine::Destroy()
*
* @mfunc
* Deletes this instance
*/
void CLightDTEngine::Destroy()
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::Destroy");
delete this;
}
/*
* CLightDTEngine::CopyRangeToClipboard ( prg )
*
* @mfunc
* Copy the text of the range prg to the clipboard using Win32 APIs
*
* @rdesc
* HRESULT
*/
HRESULT CLightDTEngine::CopyRangeToClipboard(
CTxtRange *prg ) // @parm range to copy to clipboard
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::CopyRangeToClipboard");
HRESULT hresult = E_FAIL;
IDataObject *pdo;
IRichEditOleCallback * precall = _ped->GetRECallback();
BOOL fSingleObject;
CHARRANGE chrg;
prg->GetRange(chrg.cpMin, chrg.cpMost);
fSingleObject = chrg.cpMost - chrg.cpMin == 1 &&
_ped->HasObjects() &&
_ped->_pobjmgr->CountObjectsInRange(chrg.cpMin, chrg.cpMost);
if(precall)
{
// give the callback a chance to give us it's own IDataObject
hresult = precall->GetClipboardData(&chrg, RECO_COPY, &pdo);
}
// If we didn't get an IDataObject from the callback, build our own
if(hresult != NOERROR)
{
// if the range is empty, don't bother creating it. Just
// leave the clipboard alone and return
if( prg->GetCch() == 0 )
{
_ped->Sound();
return NOERROR;
}
hresult = RangeToDataObject(prg, SF_TEXT | SF_RTF, &pdo);
}
// NB: it's important to check both hresult && pdo; it is legal for
// our client to say "yep, I handled the copy, but there was nothing
// to copy".
if( hresult == NOERROR && pdo )
{
hresult = pOleSetClipboard(pdo);
if( hresult != NOERROR )
{
HWND hwnd;
_fOleless = TRUE;
// Ole less clipboard support
if (_ped->TxGetWindow(&hwnd) == NOERROR &&
::OpenClipboard(hwnd) &&
::EmptyClipboard()
)
{
::SetClipboardData(CF_UNICODETEXT, NULL);
::SetClipboardData(cf_RTF, NULL);
::SetClipboardData(cf_RTFUTF8, NULL);
if (fSingleObject)
::SetClipboardData(CF_DIB, NULL);
::CloseClipboard();
hresult = NOERROR; // To cause replace range to happen
}
}
if (_pdo)
{
_pdo->Release();
}
_pdo = pdo;
}
return hresult;
}
/*
* CLightDTEngine::CutRangeToClipboard( prg, publdr );
*
* @mfunc
* Cut text of the range prg to the clipboard
*
* @devnote
* If publdr is non-NULL, anti-events for the cut operation should be
* stuffed into this collection
*
* @rdesc
* HRESULT from CopyRangeToClipboard()
*
* @devnote
* First copy the text to the clipboard, then delete it from the range
*/
HRESULT CLightDTEngine::CutRangeToClipboard(
CTxtRange *prg, // @parm range to cut to clipboard
IUndoBuilder *publdr ) // @parm undo builder to receive antievents
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::CutRangeToClipboard");
HRESULT hresult = E_ACCESSDENIED;
Assert(!_ped->TxGetReadOnly());
prg->AdjustEndEOP(NONEWCHARS); // Don't include trailing EOP
// in some selection cases
hresult = CopyRangeToClipboard(prg);
if( publdr )
{
publdr->SetNameID(UID_CUT);
publdr->StopGroupTyping();
}
if( hresult == NOERROR )
{
// Delete contents of range
prg->ReplaceRange(0, NULL, publdr, SELRR_REMEMBERRANGE);
}
return hresult;
}
/*
* CLightDTEngine::FlushClipboard()
*
* @mfunc flushes the clipboard (if needed). Typically called during
* shutdown.
*
* @rdesc void
*/
void CLightDTEngine::FlushClipboard()
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::FlushClipboard");
ENSAVECLIPBOARD ens;
if( _pdo )
{
if( pOleIsCurrentClipboard(_pdo) == NOERROR )
{
CDataTransferObj *pdo = NULL;
// check to see if we have to flush the clipboard.
ZeroMemory(&ens, sizeof(ENSAVECLIPBOARD));
// check to make sure the object is one of ours before accessing
// the memory.
if( _pdo->QueryInterface(IID_IRichEditDO, (void **)&pdo )
== NOERROR && pdo )
{
ens.cObjectCount = pdo->_cObjs;
ens.cch = pdo->_cch;
pdo->Release();
}
if( _ped->TxNotify(EN_SAVECLIPBOARD, &ens) == NOERROR )
{
pOleFlushClipboard();
}
else
{
pOleSetClipboard(NULL);
}
}
_pdo->Release();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -