📄 edit.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 edit.cpp - main part of CTxtEdit |
*
* See also textserv.cpp (ITextServices and SendMessage interfaces)
* and tomDoc.cpp (ITextDocument interface)
*
* Authors: <nl>
* Original RichEdit code: David R. Fulmer <nl>
* Christian Fortini, Murray Sargent, Alex Gounares, Rick Sailor,
* Jon Matousek
*
* History: <nl>
* 12/28/95 jonmat-Added support of Magellan mouse and smooth scrolling.
*
* @devnote
* Be sure to set tabs at every four (4) columns. In fact, don't even
* think of doing anything else!
*
*/
#include "_common.h"
#include "_edit.h"
#include "_dispprt.h"
#include "_dispml.h"
#include "_dispsl.h"
#include "_select.h"
#include "_text.h"
#include "_runptr.h"
#include "_font.h"
#include "_measure.h"
#include "_render.h"
#include "_m_undo.h"
#include "_antievt.h"
#include "_rtext.h"
#include "_ime.h"
#include "_urlsup.h"
#include <intsafe.h>
#define CTRL(_ch) (_ch - 'A' + 1)
ASSERTDATA
#ifdef DEBUG
BOOL fInAssert = FALSE;
#endif
// This is not public because we don't really want folks using it.
// ITextServices is a private interface.
EXTERN_C const IID IID_ITextServices = { // 8d33f740-cf58-11ce-a89d-00aa006cadc5
0x8d33f740,
0xcf58,
0x11ce,
{0xa8, 0x9d, 0x00, 0xaa, 0x00, 0x6c, 0xad, 0xc5}
};
// {13E670F4-1A5A-11cf-ABEB-00AA00B65EA1}
EXTERN_C const GUID IID_ITextHost =
{ 0x13e670f4, 0x1a5a, 0x11cf, { 0xab, 0xeb, 0x0, 0xaa, 0x0, 0xb6, 0x5e, 0xa1 } };
// {13E670F5-1A5A-11cf-ABEB-00AA00B65EA1}
EXTERN_C const GUID IID_ITextHost2 =
{ 0x13e670f5, 0x1a5a, 0x11cf, { 0xab, 0xeb, 0x0, 0xaa, 0x0, 0xb6, 0x5e, 0xa1 } };
// this is used internally do tell if a data object is one of our own.
EXTERN_C const GUID IID_IRichEditDO =
{ /* 21bc3b20-e5d5-11cf-93e1-00aa00b65ea1 */
0x21bc3b20,
0xe5d5,
0x11cf,
{0x93, 0xe1, 0x00, 0xaa, 0x00, 0xb6, 0x5e, 0xa1}
};
// Static data members
DWORD CTxtEdit::_dwTickDblClick; // time of last double-click
POINT CTxtEdit::_ptDblClick; // position of last double-click
//HCURSOR CTxtEdit::_hcurCross = 0; // We don't implement outline drag move
HCURSOR CTxtEdit::_hcurArrow = 0;
HCURSOR CTxtEdit::_hcurHand = 0;
HCURSOR CTxtEdit::_hcurIBeam = 0;
HCURSOR CTxtEdit::_hcurItalic = 0;
HCURSOR CTxtEdit::_hcurSelBar = 0;
// ??? CF - These should probably go away
#pragma BEGIN_CODESPACE_DATA
TCHAR szCRLF[] = TEXT("\r\n");
TCHAR szCR[] = TEXT("\r");
#pragma END_CODESPACE_DATA
#ifdef PENWIN20
// hit test codes for CTxtEdit::OnHitTest
#define lHitText 1 // hit text
#define lHitSel 2 // hit selection
#endif // PENWIN20
LOCAL WORD ConvVKey (WORD vKey);
// NB! Global variable that tells us wether we're on a BiDi system or not
WORD g_wLang = 0; // Current LID of the system:
// Arabic, Hebrew or "nonBiDi"
WORD g_wFlags = 0; // Keyboard controlled flags
///////////////// CTxtEdit Creation, Initialization, Destruction ///////////////////////////////////////
/*
* CTxtEdit::CTxtEdit()
*
* @mfunc
* constructor
*/
CTxtEdit::CTxtEdit(ITextHost *phost, IUnknown *punk)
{
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::CTxtEdit");
_unk.Init();
_punk = (punk) ? punk : &_unk;
_ldte.Init(this);
_phost = phost;
_cpAccelerator = -1; // Default to no accelerator
// Initialize _iCF and _iPF to something bogus
_iCF = -1;
_iPF = -1;
// GuyBark JupiterJ IME: Imitially the display is NOT frozen.
_fDisplayFrozen = FALSE;
// Initialize local maximum text size to window default
_cchTextMost = cInitTextMax;
_DeleteBeforeConvert = 0; // Fix for WinCEOS RAID #12736
}
/*
* CTxtEdit::~CTxtEdit()
*
* @mfunc
* Destructor
*/
CTxtEdit::~CTxtEdit ()
{
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::~CTxtEdit");
Assert(!_fMButtonCapture); // Need to properly transition
// Magellan mouse if asserts!
_fSelfDestruct = TRUE; // Tell the Call Mgr not to
// call this any more
if( _pdetecturl )
{
delete _pdetecturl;
}
// Flush the clipboard first
_ldte.FlushClipboard();
if(_pDocInfo) // Do this before closing
{ // down internal structures
CloseFile(TRUE); // Close any open file
delete _pDocInfo; // Delete document info
_pDocInfo = NULL;
}
ReleaseFormats(_iCF, _iPF); // Release the default formats
if( _pobjmgr )
{
delete _pobjmgr;
}
// Release our reference to selection object
if (_psel)
{
_psel->Release();
}
// Delete undo manager
if( _pundo )
{
_pundo->Destroy();
}
if( _predo )
{
_predo->Destroy();
}
delete _ime; // Delete IME
delete _pdp; // Delete displays
delete _pdpPrinter;
_pdp = NULL; // Break any further attempts to use
} // display
/*
* CTxtEdit::Init (prcClient)
*
* @mfunc
* Initializes this CTxtEdit. Called by CreateTextServices()
*
* @rdesc
* Return TRUE if successful
*/
BOOL CTxtEdit::Init (
const RECT *prcClient) //@parm Client RECT
{
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::Init");
CCharFormat CF;
DWORD dwBits = 0;
LONG iCF, iPF;
CParaFormat PF;
ICharFormatCache * pICFCache;
IParaFormatCache * pIPFCache;
CCallMgr callmgr(this);
// Set up default CCharFormat and CParaFormat
if( TxGetDefaultCharFormat(&CF) != NOERROR ||
TxGetDefaultParaFormat(&PF) != NOERROR )
{
return FALSE;
}
// Got default formats
if (FAILED(GetCharFormatCache(&pICFCache)) || // Now try to cache them
FAILED(GetParaFormatCache(&pIPFCache)) ||
FAILED(pICFCache->Cache(&CF, &iCF)) ||
FAILED(pIPFCache->Cache(&PF, &iPF)))
{
return FALSE;
} // Cached default formats
_iCF = (WORD)iCF; // Save format indices
_iPF = (WORD)iPF;
// Load mouse cursors (but only for first instance)
if(!_hcurArrow)
{
_hcurArrow = LoadCursor(0, IDC_ARROW);
// if(!_hcurCross) // Don't currently use
// _hcurCross = LoadCursor(hinstRE, MAKEINTRESOURCE(CUR_CROSS));
if(!_hcurHand)
_hcurHand = LoadCursor(hinstRE, MAKEINTRESOURCE(CUR_HAND));
if(!_hcurIBeam) // Load cursor
_hcurIBeam = LoadCursor(0, IDC_IBEAM);
if(!_hcurItalic)
_hcurItalic = LoadCursor(hinstRE, MAKEINTRESOURCE(CUR_ITALIC));
if(!_hcurSelBar)
_hcurSelBar = LoadCursor(hinstRE, MAKEINTRESOURCE(CUR_SELBAR));
}
_pfnWB = (EDITWORDBREAKPROC) TxWordBreakProc; // Set word breaking proc
#ifdef DEBUG
// the host is going to do some checking on richtext vs. plain
// text.
_fRich = TRUE;
#endif // DEBUG
if(_phost->TxGetPropertyBits (TXTBITS | // Get host state flags
TXTBIT_MULTILINE | TXTBIT_SHOWACCELERATOR, // that we cache or need
&dwBits) != NOERROR ) // for display setup
{
return FALSE;
} // Cache bits defined by
_dwFlags = dwBits & TXTBITS; // TXTBITS mask
if ((dwBits & TXTBIT_SHOWACCELERATOR) && // They want accelerator,
FAILED(UpdateAccelerator())) // so let's get it
{
return FALSE;
}
_fTransparent = TxGetBackStyle() == TXTBACK_TRANSPARENT;
if( dwBits & TXTBIT_MULTILINE ) // Create and initialize
_pdp = new CDisplayML(this); // display
else
_pdp = new CDisplaySL(this);
if(!_pdp || !_pdp->Init())
return FALSE;
_fUseUndo = TRUE;
// Initialize some IME bits
_fAutoFont = TRUE;
_fAutoKeyboard = FALSE;
// Set whether we are in our host or not
ITextHost2 *phost2;
if( _phost->QueryInterface(IID_ITextHost2, (void **)&phost2)
== NOERROR )
{
// We assume that ITextHost2 means this is our host
phost2->Release();
_fInOurHost = TRUE;
}
else
{
// Get the maximum from our host
_phost->TxGetMaxLength((DWORD *) &_cchTextMost);
}
//
// Add EOP iff Rich Text
//
if (IsRich())
{
// we should _not_ be in 10 compatibility mode yet.
// if we transition into 1.0 mode, we'll add a CRLF
// at the end of the document.
SetRichDocEndEOP(0);
}
// for Japanese, this virtual key indicates Kana mode.
// Check the toggle bit to see if it is on or offf.
// If on, Kana characters (single byte Japanese char) are coming in via WM_CHAR.
_fKANAMode = (GetKeyboardCodePage() == _JAPAN_CP) &&
((GetKeyState(VK_KANA) & 0x1) != 0);
return TRUE;
}
///////////////////////////// CTxtEdit IUnknown ////////////////////////////////
/*
* CTxtEdit::QueryInterface (riid, ppv)
*
* @mfunc
* IUnknown method
*
* @rdesc
* HRESULT = (if success) ? NOERROR : E_NOINTERFACE
*
* @devnote
* This interface is aggregated. See textserv.cpp for discussion.
*/
HRESULT CTxtEdit::QueryInterface(REFIID riid, void **ppv)
{
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::QueryInterface");
return _punk->QueryInterface(riid, ppv);
}
/*
* CTxtEdit::AddRef()
*
* @mfunc
* IUnknown method
*
* @rdesc
* ULONG - incremented reference count
*/
ULONG CTxtEdit::AddRef(void)
{
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::AddRef");
return _punk->AddRef();
}
/*
* CTxtEdit::Release()
*
* @mfunc
* IUnknown method
*
* @rdesc
* ULONG - decremented reference count
*/
ULONG CTxtEdit::Release(void)
{
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::Release");
return _punk->Release();
}
////////////////////////// Undo Management //////////////////////////////
/*
* CTxtEdit::CreateUndoMgr (dwLim, flags)
*
* @mfunc
* Creates an undo stack
*
* @rdesc
* Ptr to new IUndoMgr
*/
IUndoMgr *CTxtEdit::CreateUndoMgr(
DWORD dwLim, //@parm Size limit for the
USFlags flags)
{
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::CreateUndoMgr");
IUndoMgr *pmgr = NULL;
if( _fUseUndo )
{
pmgr = new CUndoStack(this, dwLim, flags);
if( pmgr == NULL || pmgr->GetUndoLimit() == 0 )
{
// The undo stack failed to initialize properly (probably
// lack of memory). Trash it and return NULL.
if( pmgr != NULL)
{
pmgr->Destroy();
}
return NULL;
}
// We may be asked to create a new undo/redo manager
// before we are completely done with initialization.
// We need to clean up memory we have already allocated.
if( flags & US_REDO )
{
if (_predo) {
_predo->Destroy();
}
_predo = pmgr;
}
else
{
if (_pundo) {
_pundo->Destroy();
}
_pundo = pmgr;
}
}
return pmgr;
}
/*
* CTxtEdit::HandleUndoLimit (dwLim)
*
* @mfunc
* Handles the EM_SETUNDOLIMIT message
*
* @rdesc
* Actual limit to which things were set.
*/
LRESULT CTxtEdit::HandleSetUndoLimit(
DWORD dwLim ) //@parm Requested limit size
{
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::HandleSetUndoLimit");
if( dwLim == 0 )
{
_fUseUndo = FALSE;
if( _pundo )
{
_pundo->Destroy();
_pundo = NULL;
}
if( _predo )
{
_predo->Destroy();
_predo = NULL;
}
}
else if ( !_pundo )
{
_fUseUndo = TRUE;
// don't worry about return value; if it's NULL, we're
// in the same boat as if the api wasn't called ( so later
// on, we might try to allocate the default).
CreateUndoMgr(dwLim, US_UNDO);
}
else
{
dwLim = _pundo->SetUndoLimit(dwLim);
// setting the undo limit on the undo stack will return to
// us the actual amount set. Try to set the redo stack to
// the same size. If it can't go that big, too bad.
if( _predo )
{
_predo->SetUndoLimit(dwLim);
}
}
return dwLim;
}
/*
* CTxtEdit::HandleSetTextMode
*
* @mfunc handles setting the text mode
*
* @rdesc LRESULT; 0 (NOERROR) on success, OLE failure code on failure.
*
* @devnote the text mode does not have to be fully specified; it
* is sufficient to merely specify the specific desired behavior.
*
* Note that the edit control must be completely empty for this
* routine to work.
*/
LRESULT CTxtEdit::HandleSetTextMode(
DWORD mode) //@parm the desired mode
{
LRESULT lres = 0;
// first off, we must be completely empty
if( GetAdjustedTextLength() ||
(_pundo && _pundo->CanUndo()) ||
(_predo && _predo->CanUndo()) )
{
return E_UNEXPECTED;
}
// These bits are considered one at a time; thus the absence of
// any bits does _NOT_ imply any change in behavior.
// TM_RICHTEXT && TM_PLAINTEXT are mutually exclusive; they cannot
// be both set. Same goes for TM_SINGLELEVELUNDO / TM_MULTILEVELUNDO
// and TM_SINGLECODEPAGE / TM_MULTICODEPAGE
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -