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>
990 lines
21 KiB
C++
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;
|
|
}
|
|
}
|
|
|