openDecal/Native/DecalControls/Edit.cpp
erik d1442e3747 Initial commit: Complete open-source Decal rebuild
All 5 phases of the open-source Decal rebuild:

Phase 1: 14 decompiled .NET projects (Interop.*, Adapter, FileService, DecalUtil)
Phase 2: 10 native DLLs rewritten as C# COM servers with matching GUIDs
  - DecalDat, DHS, SpellFilter, DecalInput, DecalNet, DecalFilters
  - Decal.Core, DecalControls, DecalRender, D3DService
Phase 3: C++ shims for Inject.DLL (D3D9 hooking) and LauncherHook.DLL
Phase 4: DenAgent WinForms tray application
Phase 5: WiX installer and build script

25 C# projects building with 0 errors.
Native C++ projects require VS 2022 + Windows SDK (x86).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 18:27:56 +01:00

990 lines
21 KiB
C++

// Edit.cpp : Implementation of cEdit
#include "stdafx.h"
#include "DecalControls.h"
#include "Edit.h"
/////////////////////////////////////////////////////////////////////////////
// cEdit
#define TIMER_CARET 1
#define TIMER_REPEAT 2
#define END -1
#ifndef MIN
#define MIN(x,y) (((x) < (y)) ? (x) : (y))
#endif
#ifndef MAX
#define MAX(x,y) (((x) > (y)) ? (x) : (y))
#endif
cEdit::cEdit()
: m_bAllowCapture( true ), // cyn -- 15/10/2002
m_bCapture( false ),
m_bDrawCaret( false ),
m_bSelecting( false ),
m_nSelStartChar( 0 ),
m_nSelStart( 0 ),
m_nCaretChar( 0 ),
m_nCaret( 0 ),
m_nOffset( 0 ),
m_nTextColor( 0 ),
m_nCaretHeight( 0 )
{
m_szMargin.cx = 3;
m_szMargin.cy = 1;
}
void cEdit::checkFont()
{
if( m_pFont.p == NULL )
{
CComPtr< IPluginSite > pPlugin;
m_pSite->get_PluginSite( &pPlugin );
BSTR bstrFontName;
pPlugin->get_FontName(&bstrFontName);
pPlugin->CreateFont( bstrFontName /*_bstr_t( _T( "Times New Roman" ) )*/, 14, 0, &m_pFont );
}
}
void cEdit::sendTextChange()
{
long nID;
m_pSite->get_ID( &nID );
Fire_Change( nID, _bstr_t( m_strText.c_str() ) );
}
void cEdit::Delete() {
if (m_nCaretChar == m_nSelStartChar) {
return;
}
m_strText.assign( m_strText.substr(0, MIN(m_nCaretChar, m_nSelStartChar)) + m_strText.substr(MAX(m_nCaretChar, m_nSelStartChar), m_strText.length()) );
if (m_bPassword) { /* This is bad, someone fix it */
m_strPass.assign( m_strPass.substr(0, MIN(m_nCaretChar, m_nSelStartChar)) + m_strPass.substr(MAX(m_nCaretChar, m_nSelStartChar), m_strPass.length()) );
}
m_nSelStartChar = MIN(m_nCaretChar, m_nSelStartChar);
put_Caret( m_nSelStartChar );
}
void cEdit::Copy() {
HGLOBAL hMem;
HRESULT hr;
long hWnd;
IPluginSite *Plug;
char *lpMem;
int Start, Length;
if (m_bPassword) {
return;
}
hr = m_pSite->get_PluginSite(&Plug);
if (FAILED(hr)) {
return;
}
hr = Plug->get_HWND(&hWnd);
Plug->Release();
Plug = NULL;
if (FAILED(hr)) {
return;
}
if (m_nCaretChar < m_nSelStartChar) {
Start = m_nCaretChar;
Length = m_nSelStartChar - Start;
} else {
Start = m_nSelStartChar;
Length = m_nCaretChar - Start;
}
hMem = GlobalAlloc(GMEM_MOVEABLE, Length+1);
if (!hMem) {
return;
}
lpMem = (char *)GlobalLock(hMem);
if (!lpMem) {
GlobalFree(hMem);
return;
}
/* This is gauranteed to nul-terminate */
lstrcpyn( lpMem, m_strText.c_str() + Start, Length + 1);
GlobalUnlock(hMem);
if (!OpenClipboard((HWND)hWnd)) {
GlobalFree(hMem);
return;
}
EmptyClipboard();
GetLastError();
SetClipboardData(CF_TEXT, hMem);
GetLastError();
CloseClipboard();
}
void cEdit::Cut() {
if (!m_bPassword) {
Copy();
Delete();
}
}
// OK, this paste code isn't pretty, but it's functional for now. Look for a full rewrite later!
// cyn, 22/10/2002
void cEdit::Paste() {
HANDLE hClipboardData;
char *sData;
int Count, Length;
Delete();
if (!OpenClipboard(NULL)) {
return;
}
hClipboardData = GetClipboardData(CF_TEXT);
if (hClipboardData) {
sData = (char *)GlobalLock(hClipboardData);
if (sData) {
Length = lstrlen(sData);
for (Count=0;Count<Length;Count++) {
if ((sData[Count] == 13) || (sData[Count] == 10)) {
break;
}
}
m_strText.insert(m_strText.begin() + m_nCaretChar, sData, sData + Count);
if (m_bPassword) {
m_strPass.insert(m_strPass.begin(), Count, '*');
}
put_Caret(m_nCaretChar + Count);
GlobalUnlock(hClipboardData);
}
}
CloseClipboard();
}
int cEdit::Normalize(int Offset) {
int Length;
Length = m_strText.length();
if (Offset == END) {
return Length;
} else if (Offset < END) {
return 0;
} else if (Offset > Length) {
return Length;
}
return Offset;
}
STDMETHODIMP cEdit::Reformat()
{
// Just a typical post-resize type of behavior, make sure everythign is still visible as
// the user likes.
if( m_nCaretChar > m_strText.length() )
put_Caret( m_strText.length() );
else
// Force a recalculation for scrolling
put_Caret( m_nCaretChar );
//put_Caret( m_nCaretChar );
return S_OK;
}
STDMETHODIMP cEdit::Render( ICanvas *pCanvas )
{
HDC RenderDC;
HRESULT hr;
checkFont();
// Draw the background
RECT rcPos;
m_pSite->get_Position( &rcPos );
RECT rcClient = { 0, 0, rcPos.right - rcPos.left, rcPos.bottom - rcPos.top };
if( m_pBackground.p != NULL )
{
static POINT ptOff = { 0, 0 };
m_pBackground->PatBlt( pCanvas, &rcClient, &ptOff );
}
RECT rcText = { m_szMargin.cx, m_szMargin.cy, rcClient.right - m_szMargin.cx, rcClient.bottom - m_szMargin.cy };
VARIANT_BOOL bVisible;
pCanvas->SetClipRect( &rcText, &bVisible );
if( !bVisible )
// Nothing to draw huzzah!
return S_OK;
long lFlags = 0;
if( m_bAA )
lFlags |= eAA;
if( m_bOutline )
lFlags |= eOutlined;
// Draw the text
POINT ptText = { -m_nOffset, 0 };
if( m_strText.length() > 0 )
if (m_bPassword) {
m_pFont->DrawTextEx( &ptText, _bstr_t( m_strPass.c_str() ), m_nTextColor, m_nOutlineColor, lFlags, pCanvas );
} else {
m_pFont->DrawTextEx( &ptText, _bstr_t( m_strText.c_str() ), m_nTextColor, m_nOutlineColor, lFlags, pCanvas );
}
if( m_bCapture && m_bDrawCaret )
{
// Draw the caret
RECT rcCaret = { m_nCaret - m_nOffset, m_szMargin.cy, m_nCaret - m_nOffset + 1, m_szMargin.cy + m_nCaretHeight };
pCanvas->Fill( &rcCaret, m_nTextColor );
}
if( m_bCapture ) // - Haz, fix for the annoying multiple edit selection ghosts
{
if (m_nCaretChar != m_nSelStartChar) {
hr = pCanvas->GetDC(&RenderDC);
if (SUCCEEDED(hr)) {
RECT rcSelect;
rcSelect.top = m_szMargin.cy;
rcSelect.bottom = m_szMargin.cy + m_nCaretHeight;
rcSelect.left = m_nSelStart - m_nOffset;
rcSelect.right = m_nCaret - m_nOffset;
if (rcSelect.right < rcSelect.left) {
int t;
t = rcSelect.left;
rcSelect.left = rcSelect.right;
rcSelect.right = t;
}
InvertRect(RenderDC, &rcSelect);
pCanvas->ReleaseDC();
}
}
}
return S_OK;
}
STDMETHODIMP cEdit::MouseDown( MouseState *pMS )
{
if( !m_bCapture )
Capture();
else
{
_ASSERTE(!m_bSelecting);
checkFont();
// Hit test the character and move to that position
long nHitChar;
if(m_bPassword)
m_pFont->HitTest( _bstr_t( m_strPass.c_str() ), pMS->client.x + m_nOffset - m_szMargin.cx, &nHitChar );
else
m_pFont->HitTest( _bstr_t( m_strText.c_str() ), pMS->client.x + m_nOffset - m_szMargin.cx, &nHitChar );
// Reset the timer
put_Caret( nHitChar );
m_bSelecting = true;
}
return S_OK;
}
STDMETHODIMP cEdit::MouseUp(MouseState *pMS) {
/* I assume it's possible to trigger a mouse up without a mousedown */
m_bSelecting = false;
return S_OK;
}
STDMETHODIMP cEdit::MouseMove(MouseState *pMS) {
long nHitChar;
if (!m_bCapture) {
_ASSERTE(!m_bSelecting);
return S_OK;
}
if (!m_bSelecting) {
return S_OK;
}
checkFont();
if (m_bPassword) {
m_pFont->HitTest( _bstr_t( m_strPass.c_str() ), pMS->client.x + m_nOffset - m_szMargin.cx, &nHitChar );
} else {
m_pFont->HitTest( _bstr_t( m_strText.c_str() ), pMS->client.x + m_nOffset - m_szMargin.cx, &nHitChar );
}
put_Caret(nHitChar);
return S_OK;
}
STDMETHODIMP cEdit::KeyboardChar( KeyState *pKS )
{
if (pKS->ctrl && pKS->vkey == 22) {
Paste();
} else if (pKS->ctrl && pKS->vkey == 3) {
Copy();
} else if (pKS->ctrl && pKS->vkey == 24) {
Cut();
} else {
// Look for special characters
switch( pKS->vkey )
{
case VK_BACK:
if (m_nCaretChar != m_nSelStartChar) {
Delete();
} else {
if( m_nCaretChar > 0 )
{
// Decrease the caret position
put_Caret( m_nCaretChar - 1 );
// Remove the character
m_strText.erase( m_strText.begin() + m_nCaretChar, m_strText.begin() + m_nCaretChar + 1 );
if(m_bPassword)
m_strPass.erase( m_strPass.begin() + m_nCaretChar, m_strPass.begin() + m_nCaretChar + 1 );
}
}
break;
default:
// Assume every other character is being added
if (m_nCaretChar != m_nSelStartChar) {
Delete();
}
char wc[ 2 ] = { pKS->vkey, L'\0' };
m_strText.insert( m_strText.begin() + m_nCaretChar, wc, wc + 1 );
if(m_bPassword)
{
wc[0] = L'*';
m_strPass.insert( m_strPass.begin() + m_nCaretChar, wc, wc + 1 );
}
put_Caret( m_nCaretChar + 1 );
}
}
m_pSite->Invalidate();
sendTextChange();
return S_OK;
}
STDMETHODIMP cEdit::KeyboardEndCapture( VARIANT_BOOL bCancel )
{
if (bCancel) { // cyn, 15/10/2002
m_bAllowCapture = false;
}
m_bCapture = false;
m_pSite->EndTimer( TIMER_CARET );
m_pSite->Invalidate();
long nID;
m_pSite->get_ID( &nID );
Fire_End( nID, ( bCancel ) ? VARIANT_FALSE : VARIANT_TRUE );
m_bAllowCapture = true; // cyn, 15/10/2002
return S_OK;
}
STDMETHODIMP cEdit::KeyboardEvent( long nMsg, long wParam, long lParam )
{
const char *Start;
int Search;
bool Shift, OldSelecting;
int Length;
if( nMsg != WM_KEYDOWN )
// Only looking for keydown messages
return S_OK;
Shift = (GetAsyncKeyState(VK_SHIFT) < 0);
OldSelecting = m_bSelecting;
m_bSelecting |= Shift; /* Hrrmm.. */
// Look for special characters
switch( wParam )
{
case VK_DELETE:
if (GetAsyncKeyState( VK_SHIFT ) < 0) {
Cut();
} else {
if (m_nCaretChar != m_nSelStartChar) {
Delete();
} else {
if( m_nCaretChar < m_strText.length() ) {
// Remove the character
m_strText.erase( m_strText.begin() + m_nCaretChar, m_strText.begin() + m_nCaretChar + 1 );
if(m_bPassword)
m_strPass.erase( m_strPass.begin() + m_nCaretChar, m_strPass.begin() + m_nCaretChar + 1 );
}
}
}
break;
case VK_HOME:
put_Caret( 0 );
break;
case VK_END:
put_Caret( END );
break;
case VK_INSERT: // cyn, 22/10/2002
if (Shift) {
m_bSelecting = false;
Paste();
} else if (GetAsyncKeyState( VK_CONTROL) < 0 ) {
Copy();
}
break;
case VK_LEFT:
if( m_nCaretChar > 0 ) {
if (GetAsyncKeyState( VK_CONTROL ) < 0) {
if (m_bPassword) {
Start = m_strPass.c_str();
} else {
Start = m_strText.c_str();
}
/* Loop until we find a non-space */
for (Search=m_nCaretChar-1;Search>0;Search--) {
if (Start[Search] != ' ') {
break;
}
}
/* Loop until we find a space */
for (;Search>0;Search--) {
if (Start[Search] == ' ') {
Search++;
break;
}
}
if (Search < 0) {
_ASSERTE( false );
Search = 0;
}
put_Caret( Search );
} else {
put_Caret( m_nCaretChar - 1 );
}
}
break;
case VK_RIGHT:
Length = m_strText.length();
if( m_nCaretChar < Length ) {
if (GetAsyncKeyState( VK_CONTROL ) < 0) {
/* Possible change this, AC edit box is slightly different */
if (m_bPassword) {
Start = m_strPass.c_str();
} else {
Start = m_strText.c_str();
}
/* Loop until we find a non-space */
for (Search=m_nCaretChar+1;Search<Length;Search++) {
if (Start[Search] != ' ') {
break;
}
}
/* Loop until we find a space */
for (;Search<Length;Search++) {
if (Start[Search] == ' ') {
break;
}
}
/* Loop until we find a non-space again, test it in notepad if you don't believe me */
for (;Search<Length;Search++) {
if (Start[Search] != ' ') {
break;
}
}
/* It's acceptable to go one character over the end (Search == Length) */
if (Search > Length) {
_ASSERTE( false );
Search = END;
}
put_Caret( Search );
} else {
put_Caret( m_nCaretChar + 1 );
}
}
break;
default:
// No processing
m_bSelecting = OldSelecting;
return S_OK;
}
m_bSelecting = OldSelecting;
m_pSite->Invalidate();
sendTextChange();
return S_OK;
}
STDMETHODIMP cEdit::get_Text(BSTR *pVal)
{
_ASSERTE( pVal != NULL );
*pVal = T2BSTR( m_strText.c_str() );
return S_OK;
}
STDMETHODIMP cEdit::put_Text(BSTR newVal)
{
USES_CONVERSION;
m_strText = OLE2T( newVal );
m_strPass.assign("");
if(m_bPassword) {
m_strPass.append(m_strText.length(), '*');
}
m_nSelStartChar = m_nCaretChar = 0;
m_pSite->Invalidate();
return S_OK;
}
STDMETHODIMP cEdit::get_Caret(long *pVal)
{
_ASSERTE( pVal != NULL );
*pVal = m_nCaretChar;
return S_OK;
}
// Only scroll within 10 pixels of the edge
#define EDIT_HYSTERESIS 18
STDMETHODIMP cEdit::put_Caret(long newVal)
{
std::string strSample;
checkFont();
m_nCaretChar = Normalize(newVal);
if(m_bPassword) {
_ASSERTE( m_strPass.length() == m_strText.length() );
strSample.assign(m_strPass.begin(), m_strPass.begin() + m_nCaretChar);
} else {
strSample.assign(m_strText.begin(), m_strText.begin() + m_nCaretChar);
}
// Calculate the actual character position
SIZE szText;
if (!SUCCEEDED (m_pFont->MeasureText( _bstr_t( strSample.c_str() ), &szText )))
{
return E_FAIL;
}
m_nCaretHeight = szText.cy;
m_nCaret = szText.cx;
RECT rcPosition;
m_pSite->get_Position( &rcPosition );
long nWidth = ( rcPosition.right - rcPosition.left ) - m_szMargin.cx * 2 - 2;
// Check for offset updates
if( m_nCaret < ( m_nOffset + EDIT_HYSTERESIS ) )
{
// The caret has moved in front of the scroll region
m_nOffset = m_nCaret - EDIT_HYSTERESIS;
if( m_nOffset < 0 )
// The offset is less thant the beginning, correct it
m_nOffset = 0;
}
else if( m_nCaret > nWidth + m_nOffset )
// The caret has moved off the end of the visible region
// NOTE: the +1 is to account for the 1 pixel width of the caret
m_nOffset = m_nCaret - nWidth;
if( m_bCapture )
{
// Force the caret on for a half second to make sure the user sees the new position
m_pSite->EndTimer( TIMER_CARET );
m_pSite->StartTimer( TIMER_CARET, 500 );
m_bDrawCaret = true;
}
if (m_bSelecting) {
if(m_bPassword) {
strSample.assign(m_strPass.begin(), m_strPass.begin() + m_nSelStartChar);
} else {
strSample.assign(m_strText.begin(), m_strText.begin() + m_nSelStartChar);
}
if (!SUCCEEDED (m_pFont->MeasureText( _bstr_t( strSample.c_str() ), &szText))) {
return E_FAIL;
}
m_nSelStart = szText.cx;
} else {
m_nSelStartChar = m_nCaretChar;
m_nSelStart = m_nCaret;
}
m_pSite->Invalidate();
return S_OK;
}
/*
nEnd < nStart is a valid condition
*/
STDMETHODIMP cEdit::Select(long nStart, long nEnd)
{
bool OldSelection;
OldSelection = m_bSelecting;
m_bSelecting = false;
put_Caret(Normalize(nStart));
m_bSelecting = true;
put_Caret(Normalize(nEnd));
m_bSelecting = OldSelection;
return S_OK;
}
/* I think this is done */
STDMETHODIMP cEdit::get_SelectedText(BSTR *pVal) {
int Start, Len;
_ASSERTE(pVal);
if (m_bPassword) {
return S_FALSE;
}
if (m_nCaretChar < m_nSelStartChar) {
Start = m_nCaretChar;
Len = m_nSelStartChar - Start;
} else {
Start = m_nSelStartChar;
Len = m_nCaretChar - Start;
}
*pVal = T2BSTR( m_strText.substr(Start, Len).c_str() );
return S_OK;
}
STDMETHODIMP cEdit::put_SelectedText(BSTR newVal) {
USES_CONVERSION;
std::string strInsert;
int Length;
if (m_nCaretChar != m_nSelStartChar) {
Delete();
}
strInsert = OLE2T(newVal);
Length = strInsert.length();
m_strText.insert(m_strText.begin() + m_nCaretChar, strInsert.begin(), strInsert.begin() + Length);
if (m_bPassword) {
m_strPass.insert(m_strPass.begin(), Length, '*');
}
put_Caret( m_nCaretChar + Length );
m_pSite->Invalidate();
return S_OK;
}
STDMETHODIMP cEdit::TimerTimeout(long nID, long, long, VARIANT_BOOL *pbContinue)
{
if( nID == TIMER_CARET )
{
m_bDrawCaret = !m_bDrawCaret;
m_pSite->Invalidate();
*pbContinue = VARIANT_TRUE;
}
return S_OK;
}
STDMETHODIMP cEdit::SchemaLoad(IView *pView, IUnknown *pSchema)
{
USES_CONVERSION;
// Load the font and background image
CComPtr< IPluginSite > pPlugin;
m_pSite->get_PluginSite( &pPlugin );
pPlugin->CreateFontSchema( 14, 0, pSchema, &m_pFont );
pPlugin->LoadImageSchema( pSchema, &m_pBackground );
MSXML::IXMLDOMElementPtr pElement = pSchema;
_variant_t vText = pElement->getAttribute( _T( "text" ) ),
vTextColor = pElement->getAttribute( _T( "textcolor" ) ),
vMarginX = pElement->getAttribute( _T( "marginx" ) ),
vMarginY = pElement->getAttribute( _T( "marginy" ) ),
vPassword = pElement->getAttribute( _T( "password" ) ),
vOutline = pElement->getAttribute( _T( "outlinecolor" ) ),
vAntialias = pElement->getAttribute( _T( "aa" ) );
if( vText.vt != VT_NULL )
{
_ASSERTE( vText.vt == VT_BSTR );
m_strText = OLE2T( vText.bstrVal );
}
if( vTextColor.vt != VT_NULL )
{
try
{
m_nTextColor = static_cast< long >( vTextColor );
}
catch( ... )
{
// Type conversion error
_ASSERTE( FALSE );
}
}
if( vMarginX.vt != VT_NULL )
{
try
{
m_szMargin.cx = static_cast< long >( vMarginX );
}
catch( ... )
{
// Type conversion error
_ASSERTE( FALSE );
}
}
if( vMarginY.vt != VT_NULL )
{
try
{
m_szMargin.cy = static_cast< long >( vMarginY );
}
catch( ... )
{
// Type conversion error
_ASSERTE( FALSE );
}
}
if( vPassword.vt != VT_NULL)
{
try
{
m_bPassword = static_cast< bool >( vPassword );
}
catch( ... )
{
_ASSERTE( FALSE );
}
}
else
m_bPassword = false;
if( vText.vt != VT_NULL )
{
_ASSERTE( vText.vt == VT_BSTR );
m_strText = OLE2T( vText.bstrVal );
if(m_bPassword)
for(int i=0; i<m_strText.length(); i++)
m_strPass.append("*");
}
m_nOutlineColor = 0;
m_bOutline = false;
if( vOutline.vt != VT_NULL )
{
try
{
m_bOutline = true;
m_nOutlineColor = static_cast< long >( vOutline );
}
catch( ... )
{
// Type conversion error
_ASSERTE( FALSE );
}
}
m_bAA = true;
if( vAntialias.vt != VT_NULL )
{
try
{
m_bAA = static_cast< bool >( vAntialias );
}
catch( ... )
{
// Type conversion error
_ASSERTE( FALSE );
}
}
return S_OK;
}
STDMETHODIMP cEdit::get_Background(IImageCacheDisp **pVal)
{
_ASSERTE( pVal != NULL );
if( m_pBackground.p == NULL )
*pVal = NULL;
else
m_pBackground->QueryInterface( pVal );
return S_OK;
}
STDMETHODIMP cEdit::putref_Background(IImageCacheDisp *newVal)
{
if( m_pBackground.p )
m_pBackground.Release();
if( newVal != NULL )
{
HRESULT hRes = newVal->QueryInterface( &m_pBackground );
_ASSERTE( SUCCEEDED( hRes ) );
}
m_pSite->Invalidate();
return S_OK;
}
STDMETHODIMP cEdit::get_Font(IFontCacheDisp **pVal)
{
_ASSERTE( pVal != NULL );
if( m_pFont.p == NULL )
*pVal = NULL;
else
m_pFont->QueryInterface( pVal );
return S_OK;
}
STDMETHODIMP cEdit::putref_Font(IFontCacheDisp *newVal)
{
_ASSERTE( newVal != NULL );
if( m_pFont.p )
m_pFont.Release();
HRESULT hRes = newVal->QueryInterface( &m_pFont );
_ASSERTE( SUCCEEDED( hRes ) );
// Reformat
put_Caret( m_nCaretChar );
return S_OK;
}
STDMETHODIMP cEdit::get_TextColor(long *pVal)
{
_ASSERTE( pVal != NULL );
*pVal = m_nTextColor;
return S_OK;
}
STDMETHODIMP cEdit::put_TextColor(long newVal)
{
_ASSERTE( ( newVal & 0xFF000000 ) == 0 );
m_nTextColor = newVal;
m_pSite->Invalidate();
return S_OK;
}
STDMETHODIMP cEdit::SetMargins(long nX, long nY)
{
_ASSERTE( nX >= 0 && nY >= 0 );
m_szMargin.cx = nX;
m_szMargin.cy = nY;
put_Caret( m_nCaretChar );
return S_OK;
}
STDMETHODIMP cEdit::Capture()
{
_ASSERTE( !m_bCapture );
if ( m_bAllowCapture ) {
m_pSite->CaptureKeyboard();
/* Until we actually capture the keyboard, other code can still execute */
/* If that other code calls put_Caret (eg, put_SelectedText), the timer is set */
/* If that timer is set, and we then try to set it again, it has an error */
m_bCapture = true;
m_bDrawCaret = true;
m_pSite->Invalidate();
m_pSite->StartTimer( TIMER_CARET, 500 );
if (m_nCaretChar == m_nSelStartChar) {
Select(0, END);
}
long nID;
m_pSite->get_ID( &nID );
Fire_Begin( nID );
return S_OK;
} else {
return S_FALSE;
}
}