mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-23 21:02:11 +00:00
1907 lines
45 KiB
C++
1907 lines
45 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 GPL Source Code
|
|
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
|
|
|
|
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 Source Code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "tools/edit_gui_common.h"
|
|
|
|
|
|
#include "CSyntaxRichEditCtrl.h"
|
|
|
|
#ifdef ID_DEBUG_MEMORY
|
|
#undef new
|
|
#undef DEBUG_NEW
|
|
#define DEBUG_NEW new
|
|
#endif
|
|
|
|
// NOTE: known bug, if you directly jump to a not yet highligted page with the first line starting
|
|
// inside a multi-line comment then the multi-line comment is not picked up and highlighted
|
|
|
|
const int AUTOCOMPLETE_WIDTH = 200;
|
|
const int AUTOCOMPLETE_HEIGHT = 180;
|
|
const int AUTOCOMPLETE_OFFSET = 16;
|
|
|
|
const int FUNCPARMTOOLTIP_WIDTH = 16;
|
|
const int FUNCPARMTOOLTIP_HEIGHT = 20;
|
|
const int FUNCPARMTOOLTIP_OFFSET = 16;
|
|
|
|
const COLORREF DEFAULT_BACK_COLOR = SRE_COLOR_WHITE;
|
|
const COLORREF INVALID_BACK_COLOR = SRE_COLOR_WHITE - 2;
|
|
const COLORREF MULTILINE_COMMENT_BACK_COLOR = SRE_COLOR_WHITE - 1;
|
|
|
|
#define IDC_LISTBOX_AUTOCOMPLETE 700
|
|
#define IDC_EDITBOX_FUNCPARMS 701
|
|
|
|
static keyWord_t defaultKeyWords[] = {
|
|
{ NULL, SRE_COLOR_BLACK, "" }
|
|
};
|
|
|
|
BEGIN_MESSAGE_MAP(CSyntaxRichEditCtrl, CRichEditCtrl)
|
|
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)
|
|
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)
|
|
ON_WM_GETDLGCODE()
|
|
ON_WM_KEYDOWN()
|
|
ON_WM_CHAR()
|
|
ON_WM_LBUTTONDOWN()
|
|
ON_WM_MOUSEWHEEL()
|
|
ON_WM_MOUSEMOVE()
|
|
ON_WM_VSCROLL()
|
|
ON_WM_SIZE()
|
|
ON_NOTIFY_REFLECT(EN_PROTECTED, OnProtected)
|
|
ON_CONTROL_REFLECT(EN_CHANGE, OnChange)
|
|
ON_LBN_SELCANCEL(IDC_LISTBOX_AUTOCOMPLETE, OnAutoCompleteListBoxChange)
|
|
ON_LBN_SELCHANGE(IDC_LISTBOX_AUTOCOMPLETE, OnAutoCompleteListBoxChange)
|
|
ON_LBN_DBLCLK(IDC_LISTBOX_AUTOCOMPLETE, OnAutoCompleteListBoxDblClk)
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::CSyntaxRichEditCtrl
|
|
================
|
|
*/
|
|
CSyntaxRichEditCtrl::CSyntaxRichEditCtrl( void ) {
|
|
m_TextDoc = NULL;
|
|
keyWords = defaultKeyWords;
|
|
keyWordColors = NULL;
|
|
keyWordLengths = NULL;
|
|
caseSensitive = false;
|
|
allowPathNames = true;
|
|
keyWordAutoCompletion = true;
|
|
updateRange.cpMin = 0;
|
|
updateRange.cpMax = 0;
|
|
updateSyntaxHighlighting = true;
|
|
stringColorIndex = 0;
|
|
stringColorLine = -1;
|
|
autoCompleteStart = -1;
|
|
funcParmToolTipStart = -1;
|
|
bracedSection[0] = -1;
|
|
bracedSection[1] = -1;
|
|
GetObjectMembers = NULL;
|
|
GetFunctionParms = NULL;
|
|
GetToolTip = NULL;
|
|
mousePoint.x = 0;
|
|
mousePoint.y = 0;
|
|
keyWordToolTip = NULL;
|
|
m_pchTip = NULL;
|
|
m_pwchTip = NULL;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::~CSyntaxRichEditCtrl
|
|
================
|
|
*/
|
|
CSyntaxRichEditCtrl::~CSyntaxRichEditCtrl( void ) {
|
|
FreeKeyWordsFromFile();
|
|
delete m_pchTip;
|
|
delete m_pwchTip;
|
|
m_DefaultFont->Release();
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::InitFont
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::InitFont( void ) {
|
|
LOGFONT lf;
|
|
CFont font;
|
|
PARAFORMAT pf;
|
|
int logx, tabSize;
|
|
|
|
// set the font
|
|
memset( &lf, 0, sizeof( lf ) );
|
|
lf.lfHeight = FONT_HEIGHT * 10;
|
|
lf.lfWidth = FONT_WIDTH * 10;
|
|
lf.lfCharSet = ANSI_CHARSET;
|
|
lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
|
|
strcpy( lf.lfFaceName, FONT_NAME );
|
|
font.CreatePointFontIndirect( &lf );
|
|
|
|
SetFont( &font );
|
|
|
|
// get the tab size in twips
|
|
logx = ::GetDeviceCaps( GetDC()->GetSafeHdc(), LOGPIXELSX );
|
|
tabSize = TAB_SIZE * FONT_WIDTH * 1440 / logx;
|
|
|
|
// set the tabs
|
|
memset( &pf, 0, sizeof( PARAFORMAT ) );
|
|
pf.cbSize = sizeof( PARAFORMAT );
|
|
pf.dwMask = PFM_TABSTOPS;
|
|
for ( pf.cTabCount = 0; pf.cTabCount < MAX_TAB_STOPS; pf.cTabCount++ ) {
|
|
pf.rgxTabs[pf.cTabCount] = pf.cTabCount * tabSize;
|
|
}
|
|
|
|
SetParaFormat( pf );
|
|
|
|
memset( &defaultCharFormat, 0, sizeof( defaultCharFormat ) );
|
|
defaultCharFormat.dwMask = CFM_CHARSET | CFM_FACE | CFM_SIZE | CFM_BOLD | CFM_COLOR | CFM_PROTECTED | CFM_BACKCOLOR;
|
|
defaultCharFormat.yHeight = FONT_HEIGHT * 20;
|
|
defaultCharFormat.bCharSet = ANSI_CHARSET;
|
|
defaultCharFormat.bPitchAndFamily = FIXED_PITCH | FF_MODERN;
|
|
defaultCharFormat.crTextColor = SRE_COLOR_BLACK;
|
|
defaultCharFormat.crBackColor = DEFAULT_BACK_COLOR;
|
|
defaultCharFormat.dwEffects = CFE_PROTECTED;
|
|
strcpy( defaultCharFormat.szFaceName, FONT_NAME );
|
|
defaultCharFormat.cbSize = sizeof( defaultCharFormat );
|
|
|
|
SetDefaultCharFormat( defaultCharFormat );
|
|
|
|
defaultColor = SRE_COLOR_BLACK;
|
|
singleLineCommentColor = SRE_COLOR_DARK_GREEN;
|
|
multiLineCommentColor = SRE_COLOR_DARK_GREEN;
|
|
stringColor[0] = stringColor[1] = SRE_COLOR_DARK_CYAN;
|
|
literalColor = SRE_COLOR_GREY;
|
|
braceHighlightColor = SRE_COLOR_RED;
|
|
|
|
// get the default tom::ITextFont
|
|
tom::ITextRange *irange;
|
|
tom::ITextFont *ifont;
|
|
|
|
m_TextDoc->Range( 0, 0, &irange );
|
|
irange->get_Font( &ifont );
|
|
|
|
ifont->get_Duplicate( &m_DefaultFont );
|
|
|
|
ifont->Release();
|
|
irange->Release();
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::SetCharType
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::SetCharType( int first, int last, int type ) {
|
|
for ( int i = first; i <= last; i++ ) {
|
|
charType[i] = type;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::InitSyntaxHighlighting
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::InitSyntaxHighlighting( void ) {
|
|
SetCharType( 0x00, 0xFF, CT_PUNCTUATION );
|
|
SetCharType( '\0', ' ', CT_WHITESPACE );
|
|
SetCharType( '/', '/', CT_COMMENT );
|
|
SetCharType( '\"', '\"', CT_STRING );
|
|
SetCharType( '\'', '\'', CT_LITERAL );
|
|
SetCharType( 'a', 'z', CT_NAME );
|
|
SetCharType( 'A', 'Z', CT_NAME );
|
|
SetCharType( '_', '_', CT_NAME );
|
|
SetCharType( '#', '#', CT_NAME );
|
|
SetCharType( '0', '9', CT_NUMBER );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::Init
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::Init( void ) {
|
|
|
|
// get the Rich Edit ITextDocument to use the wonky TOM interface
|
|
IRichEditOle *ire = GetIRichEditOle();
|
|
IUnknown *iu = (IUnknown *)ire;
|
|
if ( iu == NULL || iu->QueryInterface( tom::IID_ITextDocument, (void**) &m_TextDoc ) != S_OK ) {
|
|
m_TextDoc = NULL;
|
|
}
|
|
|
|
InitFont();
|
|
|
|
InitSyntaxHighlighting();
|
|
|
|
SetEventMask( GetEventMask() | ENM_CHANGE | ENM_KEYEVENTS | ENM_MOUSEEVENTS | ENM_PROTECTED ); // ENM_SCROLLEVENTS
|
|
|
|
EnableToolTips( TRUE );
|
|
|
|
// create auto complete list box
|
|
CRect rect( 0, 0, AUTOCOMPLETE_WIDTH, AUTOCOMPLETE_HEIGHT );
|
|
autoCompleteListBox.Create( WS_DLGFRAME | WS_VISIBLE | WS_VSCROLL | LBS_SORT | LBS_NOTIFY, rect, this, IDC_LISTBOX_AUTOCOMPLETE );
|
|
autoCompleteListBox.SetFont( GetParent()->GetFont() );
|
|
autoCompleteListBox.ShowWindow( FALSE );
|
|
|
|
// create function parameter tool tip
|
|
funcParmToolTip.Create( WS_VISIBLE | WS_BORDER, rect, this, IDC_EDITBOX_FUNCPARMS );
|
|
funcParmToolTip.SetFont( GetParent()->GetFont() );
|
|
funcParmToolTip.ShowWindow( FALSE );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::FindKeyWord
|
|
================
|
|
*/
|
|
ID_INLINE int CSyntaxRichEditCtrl::FindKeyWord( const char *keyWord, int length ) const {
|
|
int i, hash;
|
|
|
|
if ( caseSensitive ) {
|
|
hash = idStr::Hash( keyWord, length );
|
|
} else {
|
|
hash = idStr::IHash( keyWord, length );
|
|
}
|
|
for ( i = keyWordHash.First( hash ); i != -1; i = keyWordHash.Next( i ) ) {
|
|
if ( length != keyWordLengths[i] ) {
|
|
continue;
|
|
}
|
|
if ( caseSensitive ) {
|
|
if ( idStr::Cmpn( keyWords[i].keyWord, keyWord, length ) != 0 ) {
|
|
continue;
|
|
}
|
|
} else {
|
|
if ( idStr::Icmpn( keyWords[i].keyWord, keyWord, length ) != 0 ) {
|
|
continue;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::SetKeyWords
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::SetKeyWords( const keyWord_t kws[] ) {
|
|
int i, numKeyWords, hash;
|
|
|
|
keyWords = kws;
|
|
|
|
for ( numKeyWords = 0; keyWords[numKeyWords].keyWord; numKeyWords++ ) {
|
|
}
|
|
|
|
delete keyWordColors;
|
|
keyWordColors = new COLORREF[numKeyWords];
|
|
|
|
for ( i = 0; i < numKeyWords; i++ ) {
|
|
keyWordColors[i] = keyWords[i].color;
|
|
}
|
|
|
|
delete keyWordLengths;
|
|
keyWordLengths = new int[numKeyWords];
|
|
|
|
for ( i = 0; i < numKeyWords; i++ ) {
|
|
keyWordLengths[i] = idStr::Length( keyWords[i].keyWord );
|
|
}
|
|
|
|
keyWordHash.Clear( 1024, 1024 );
|
|
for ( i = 0; i < numKeyWords; i++ ) {
|
|
if ( caseSensitive ) {
|
|
hash = idStr::Hash( keyWords[i].keyWord, keyWordLengths[i] );
|
|
} else {
|
|
hash = idStr::IHash( keyWords[i].keyWord, keyWordLengths[i] );
|
|
}
|
|
keyWordHash.Add( hash, i );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::LoadKeyWordsFromFile
|
|
================
|
|
*/
|
|
bool CSyntaxRichEditCtrl::LoadKeyWordsFromFile( const char *fileName ) {
|
|
idParser src;
|
|
idToken token, name, description;
|
|
byte red, green, blue;
|
|
keyWord_t keyword;
|
|
|
|
if ( !src.LoadFile( fileName ) ) {
|
|
return false;
|
|
}
|
|
|
|
FreeKeyWordsFromFile();
|
|
|
|
while( src.ReadToken( &token ) ) {
|
|
if ( token.Icmp( "keywords" ) == 0 ) {
|
|
src.ExpectTokenString( "{" );
|
|
while( src.ReadToken( &token ) ) {
|
|
if ( token == "}" ) {
|
|
break;
|
|
}
|
|
if ( token == "{" ) {
|
|
|
|
// parse name
|
|
src.ExpectTokenType( TT_STRING, 0, &name );
|
|
src.ExpectTokenString( "," );
|
|
|
|
// parse color
|
|
src.ExpectTokenString( "(" );
|
|
src.ExpectTokenType( TT_NUMBER, TT_INTEGER, &token );
|
|
red = token.GetIntValue();
|
|
src.ExpectTokenString( "," );
|
|
src.ExpectTokenType( TT_NUMBER, TT_INTEGER, &token );
|
|
green = token.GetIntValue();
|
|
src.ExpectTokenString( "," );
|
|
src.ExpectTokenType( TT_NUMBER, TT_INTEGER, &token );
|
|
blue = token.GetIntValue();
|
|
src.ExpectTokenString( ")" );
|
|
src.ExpectTokenString( "," );
|
|
|
|
// parse description
|
|
src.ExpectTokenType( TT_STRING, 0, &description );
|
|
src.ExpectTokenString( "}" );
|
|
|
|
keyword.keyWord = Mem_CopyString( name );
|
|
keyword.color = RGB( red, green, blue );
|
|
keyword.description = Mem_CopyString( description );
|
|
|
|
keyWordsFromFile.Append( keyword );
|
|
}
|
|
}
|
|
} else {
|
|
src.SkipBracedSection();
|
|
}
|
|
}
|
|
|
|
keyword.keyWord = NULL;
|
|
keyword.color = RGB( 255, 255, 255 );
|
|
keyword.description = NULL;
|
|
keyWordsFromFile.Append( keyword );
|
|
|
|
SetKeyWords( keyWordsFromFile.Ptr() );
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::FreeKeyWordsFromFile
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::FreeKeyWordsFromFile( void ) {
|
|
for ( int i = 0; i < keyWordsFromFile.Num(); i++ ) {
|
|
Mem_Free( const_cast<char *>( keyWordsFromFile[i].keyWord ) );
|
|
Mem_Free( const_cast<char *>( keyWordsFromFile[i].description ) );
|
|
}
|
|
keyWordsFromFile.Clear();
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::SetDefaultColor
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::SetDefaultColor( const COLORREF color ) {
|
|
defaultColor = color;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::SetCommentColor
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::SetCommentColor( const COLORREF color ) {
|
|
singleLineCommentColor = color;
|
|
multiLineCommentColor = color;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::SetStringColor
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::SetStringColor( const COLORREF color, const COLORREF altColor ) {
|
|
stringColor[0] = color;
|
|
if ( altColor == -1 ) {
|
|
stringColor[1] = color;
|
|
} else {
|
|
stringColor[1] = altColor;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::SetLiteralColor
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::SetLiteralColor( const COLORREF color ) {
|
|
literalColor = color;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::SetObjectMemberCallback
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::SetObjectMemberCallback( objectMemberCallback_t callback ) {
|
|
GetObjectMembers = callback;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::SetFunctionParmCallback
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::SetFunctionParmCallback( toolTipCallback_t callback ) {
|
|
GetFunctionParms = callback;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::SetToolTipCallback
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::SetToolTipCallback( toolTipCallback_t callback ) {
|
|
GetToolTip = callback;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::SetCaseSensitive
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::SetCaseSensitive( bool caseSensitive ) {
|
|
this->caseSensitive = caseSensitive;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::AllowPathNames
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::AllowPathNames( bool allow ) {
|
|
allowPathNames = allow;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::EnableKeyWordAutoCompletion
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::EnableKeyWordAutoCompletion( bool enable ) {
|
|
keyWordAutoCompletion = enable;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::GetVisibleRange
|
|
================
|
|
*/
|
|
CHARRANGE CSyntaxRichEditCtrl::GetVisibleRange( void ) const {
|
|
RECT rectArea;
|
|
int firstLine, lastLine;
|
|
CHARRANGE range;
|
|
|
|
firstLine = GetFirstVisibleLine();
|
|
GetClientRect( &rectArea );
|
|
lastLine = firstLine + ( rectArea.bottom / ( defaultCharFormat.yHeight / 20 ) );
|
|
|
|
if ( lastLine >= GetLineCount() ) {
|
|
lastLine = GetLineCount() - 1;
|
|
}
|
|
range.cpMin = LineIndex( firstLine );
|
|
if ( range.cpMin < 0 ) {
|
|
range.cpMin = 0;
|
|
}
|
|
range.cpMax = LineIndex( lastLine );
|
|
if ( range.cpMax == -1 ) {
|
|
range.cpMax = range.cpMin + LineLength( range.cpMin );
|
|
} else {
|
|
range.cpMax += LineLength( range.cpMax );
|
|
}
|
|
if ( range.cpMax >= GetTextLength() ) {
|
|
range.cpMax = GetTextLength() - 1;
|
|
}
|
|
return range;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::SetDefaultFont
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::SetDefaultFont( int startCharIndex, int endCharIndex ) {
|
|
tom::ITextRange *range;
|
|
|
|
updateSyntaxHighlighting = false;
|
|
|
|
m_TextDoc->Range( startCharIndex, endCharIndex, &range );
|
|
|
|
m_TextDoc->Undo( tom::tomSuspend, NULL );
|
|
range->put_Font( m_DefaultFont );
|
|
m_TextDoc->Undo( tom::tomResume, NULL );
|
|
|
|
range->Release();
|
|
|
|
updateSyntaxHighlighting = true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::SetColor
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::SetColor( int startCharIndex, int endCharIndex, COLORREF foreColor, COLORREF backColor, bool bold ) {
|
|
tom::ITextRange *range;
|
|
tom::ITextFont *font;
|
|
long prop;
|
|
|
|
updateSyntaxHighlighting = false;
|
|
|
|
m_TextDoc->Range( startCharIndex, endCharIndex, &range );
|
|
range->get_Font( &font );
|
|
|
|
m_TextDoc->Undo( tom::tomSuspend, &prop );
|
|
font->put_ForeColor( foreColor );
|
|
m_TextDoc->Undo( tom::tomResume, &prop );
|
|
|
|
m_TextDoc->Undo( tom::tomSuspend, &prop );
|
|
font->put_BackColor( backColor );
|
|
m_TextDoc->Undo( tom::tomResume, &prop );
|
|
|
|
m_TextDoc->Undo( tom::tomSuspend, &prop );
|
|
font->put_Bold( bold ? tom::tomTrue : tom::tomFalse );
|
|
m_TextDoc->Undo( tom::tomResume, &prop );
|
|
|
|
font->Release();
|
|
range->Release();
|
|
|
|
updateSyntaxHighlighting = true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::GetForeColor
|
|
================
|
|
*/
|
|
COLORREF CSyntaxRichEditCtrl::GetForeColor( int charIndex ) const {
|
|
tom::ITextRange *range;
|
|
tom::ITextFont *font;
|
|
long foreColor;
|
|
|
|
m_TextDoc->Range( charIndex, charIndex, &range );
|
|
range->get_Font( &font );
|
|
|
|
font->get_BackColor( &foreColor );
|
|
|
|
font->Release();
|
|
range->Release();
|
|
|
|
return foreColor;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::GetBackColor
|
|
================
|
|
*/
|
|
COLORREF CSyntaxRichEditCtrl::GetBackColor( int charIndex ) const {
|
|
tom::ITextRange *range;
|
|
tom::ITextFont *font;
|
|
long backColor;
|
|
|
|
m_TextDoc->Range( charIndex, charIndex, &range );
|
|
range->get_Font( &font );
|
|
|
|
font->get_BackColor( &backColor );
|
|
|
|
font->Release();
|
|
range->Release();
|
|
|
|
return backColor;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::HighlightSyntax
|
|
|
|
Update the syntax highlighting for the given character range.
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::HighlightSyntax( int startCharIndex, int endCharIndex ) {
|
|
int c, t, line, charIndex, textLength, syntaxStart, keyWordLength, keyWordIndex;
|
|
const char *keyWord;
|
|
CHARRANGE visRange;
|
|
CString text;
|
|
|
|
// get text length
|
|
GetTextRange( 0, GetTextLength(), text );
|
|
textLength = text.GetLength();
|
|
|
|
// make sure the indexes are within bounds
|
|
if ( startCharIndex < 0 ) {
|
|
startCharIndex = 0;
|
|
}
|
|
if ( endCharIndex < 0 ) {
|
|
endCharIndex = textLength - 1;
|
|
} else if ( endCharIndex >= textLength ) {
|
|
endCharIndex = textLength - 1;
|
|
}
|
|
|
|
// move the start index to the beginning of the line
|
|
for ( ; startCharIndex > 0; startCharIndex-- ) {
|
|
if ( idStr::CharIsNewLine( text[startCharIndex-1] ) ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// move the end index to the end of the line
|
|
for ( ; endCharIndex < textLength - 1; endCharIndex++ ) {
|
|
if ( idStr::CharIsNewLine( text[endCharIndex+1] ) ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// get the visible char range
|
|
visRange = GetVisibleRange();
|
|
|
|
// never update beyond the visible range
|
|
if ( startCharIndex < visRange.cpMin ) {
|
|
SetColor( startCharIndex, visRange.cpMin - 1, SRE_COLOR_BLACK, INVALID_BACK_COLOR, false );
|
|
startCharIndex = visRange.cpMin;
|
|
}
|
|
if ( visRange.cpMax < endCharIndex ) {
|
|
SetColor( visRange.cpMax, endCharIndex, SRE_COLOR_BLACK, INVALID_BACK_COLOR, false );
|
|
endCharIndex = visRange.cpMax;
|
|
if ( endCharIndex >= textLength ) {
|
|
endCharIndex = textLength - 1;
|
|
}
|
|
}
|
|
|
|
// test if the start index is inside a multi-line comment
|
|
if ( startCharIndex > 0 ) {
|
|
// multi-line comments have a slightly different background color
|
|
if ( GetBackColor( startCharIndex-1 ) == MULTILINE_COMMENT_BACK_COLOR ) {
|
|
for( ; startCharIndex > 0; startCharIndex-- ) {
|
|
if ( text[startCharIndex] == '/' && text[startCharIndex+1] == '*' ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// test if the end index is inside a multi-line comment
|
|
if ( endCharIndex < textLength - 1 ) {
|
|
// multi-line comments have a slightly different background color
|
|
if ( GetBackColor( endCharIndex+1 ) == MULTILINE_COMMENT_BACK_COLOR ) {
|
|
for( endCharIndex++; endCharIndex < textLength - 1; endCharIndex++ ) {
|
|
if ( text[endCharIndex-1] == '*' && text[endCharIndex] == '/' ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
line = 0;
|
|
stringColorLine = -1;
|
|
stringColorIndex = 0;
|
|
|
|
// set the default color
|
|
SetDefaultFont( startCharIndex, endCharIndex + 1 );
|
|
|
|
// syntax based colors
|
|
for( charIndex = startCharIndex; charIndex <= endCharIndex; charIndex++ ) {
|
|
|
|
t = charType[text[charIndex]];
|
|
switch( t ) {
|
|
case CT_WHITESPACE: {
|
|
if ( idStr::CharIsNewLine( text[charIndex] ) ) {
|
|
line++;
|
|
}
|
|
break;
|
|
}
|
|
case CT_COMMENT: {
|
|
c = text[charIndex+1];
|
|
if ( c == '/' ) {
|
|
// single line comment
|
|
syntaxStart = charIndex;
|
|
for ( charIndex += 2; charIndex < textLength; charIndex++ ) {
|
|
if ( idStr::CharIsNewLine( text[charIndex] ) ) {
|
|
break;
|
|
}
|
|
}
|
|
SetColor( syntaxStart, charIndex + 1, singleLineCommentColor, DEFAULT_BACK_COLOR, false );
|
|
} else if ( c == '*' ) {
|
|
// multi-line comment
|
|
syntaxStart = charIndex;
|
|
for ( charIndex += 2; charIndex < textLength; charIndex++ ) {
|
|
if ( text[charIndex] == '*' && text[charIndex+1] == '/' ) {
|
|
break;
|
|
}
|
|
}
|
|
charIndex++;
|
|
SetColor( syntaxStart, charIndex + 1, multiLineCommentColor, MULTILINE_COMMENT_BACK_COLOR, false );
|
|
}
|
|
break;
|
|
}
|
|
case CT_STRING: {
|
|
if ( line != stringColorLine ) {
|
|
stringColorLine = line;
|
|
stringColorIndex = 0;
|
|
}
|
|
syntaxStart = charIndex;
|
|
for ( charIndex++; charIndex < textLength; charIndex++ ) {
|
|
c = text[charIndex];
|
|
if ( charType[c] == CT_STRING && text[charIndex-1] != '\\' ) {
|
|
break;
|
|
}
|
|
if ( idStr::CharIsNewLine( c ) ) {
|
|
line++;
|
|
break;
|
|
}
|
|
}
|
|
SetColor( syntaxStart, charIndex + 1, stringColor[stringColorIndex], DEFAULT_BACK_COLOR, false );
|
|
stringColorIndex ^= 1;
|
|
break;
|
|
}
|
|
case CT_LITERAL: {
|
|
syntaxStart = charIndex;
|
|
for ( charIndex++; charIndex < textLength; charIndex++ ) {
|
|
c = text[charIndex];
|
|
if ( charType[c] == CT_LITERAL && text[charIndex-1] != '\\' ) {
|
|
break;
|
|
}
|
|
if ( idStr::CharIsNewLine( c ) ) {
|
|
line++;
|
|
break;
|
|
}
|
|
}
|
|
SetColor( syntaxStart, charIndex + 1, literalColor, DEFAULT_BACK_COLOR, false );
|
|
break;
|
|
}
|
|
case CT_NUMBER: {
|
|
break;
|
|
}
|
|
case CT_NAME: {
|
|
syntaxStart = charIndex;
|
|
keyWord = ((const char *)text) + charIndex;
|
|
for ( charIndex++; charIndex < textLength; charIndex++ ) {
|
|
c = text[charIndex];
|
|
t = charType[c];
|
|
if ( t != CT_NAME && t != CT_NUMBER ) {
|
|
// allow path names
|
|
if ( !allowPathNames || ( c != '/' && c != '\\' && c != '.' ) ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
keyWordLength = charIndex - syntaxStart;
|
|
keyWordIndex = FindKeyWord( keyWord, keyWordLength );
|
|
if ( keyWordIndex != -1 ) {
|
|
SetColor( syntaxStart, syntaxStart + keyWordLength, keyWordColors[keyWordIndex], DEFAULT_BACK_COLOR, false );
|
|
}
|
|
break;
|
|
}
|
|
case CT_PUNCTUATION: {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// show braced section
|
|
BracedSectionShow();
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::UpdateVisibleRange
|
|
|
|
Updates the visible character range if it is not yet properly highlighted.
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::UpdateVisibleRange( void ) {
|
|
CHARRANGE visRange;
|
|
tom::ITextRange *range;
|
|
tom::ITextFont *font;
|
|
long backColor;
|
|
bool update = false;
|
|
|
|
if ( !updateSyntaxHighlighting ) {
|
|
return;
|
|
}
|
|
|
|
visRange = GetVisibleRange();
|
|
|
|
m_TextDoc->Range( visRange.cpMin, visRange.cpMax, &range );
|
|
range->get_End( &visRange.cpMax );
|
|
|
|
range->get_Font( &font );
|
|
|
|
range->SetRange( visRange.cpMin, visRange.cpMin );
|
|
while( 1 ) {
|
|
range->get_Start( &visRange.cpMin );
|
|
if ( visRange.cpMin >= visRange.cpMax ) {
|
|
break;
|
|
}
|
|
font->get_BackColor( &backColor );
|
|
if ( backColor == INVALID_BACK_COLOR ) {
|
|
update = true;
|
|
break;
|
|
}
|
|
if ( range->Move( tom::tomCharFormat, 1, NULL ) != S_OK ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
font->Release();
|
|
range->Release();
|
|
|
|
if ( update ) {
|
|
HighlightSyntax( visRange.cpMin, visRange.cpMax - 1 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::GetCursorPos
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::GetCursorPos( int &line, int &column, int &character ) const {
|
|
long start, end;
|
|
char buffer[MAX_STRING_CHARS];
|
|
|
|
GetSel( start, end );
|
|
line = LineFromChar( start );
|
|
start -= LineIndex( line );
|
|
GetLine( line, buffer, sizeof( buffer ) );
|
|
for ( column = 1, character = 0; character < start; character++ ) {
|
|
if ( idStr::CharIsTab( buffer[character] ) ) {
|
|
column += TAB_SIZE;
|
|
column -= column % TAB_SIZE;
|
|
} else {
|
|
column++;
|
|
}
|
|
}
|
|
character++;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::GetText
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::GetText( idStr &text ) const {
|
|
GetText( text, 0, GetTextLength() );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::GetText
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::GetText( idStr &text, int startCharIndex, int endCharIndex ) const {
|
|
tom::ITextRange *range;
|
|
BSTR bstr;
|
|
USES_CONVERSION;
|
|
|
|
m_TextDoc->Range( startCharIndex, endCharIndex, &range );
|
|
range->get_Text( &bstr );
|
|
text = W2A( bstr );
|
|
range->Release();
|
|
text.StripTrailingOnce( "\r" ); // remove last carriage return which is always added to a tom::ITextRange
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::SetText
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::SetText( const char *text ) {
|
|
SetSel( 0, -1 );
|
|
ReplaceSel( text, FALSE );
|
|
SetSel( 0, 0 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::FindNext
|
|
================
|
|
*/
|
|
bool CSyntaxRichEditCtrl::FindNext( const char *find, bool matchCase, bool matchWholeWords, bool searchForward ) {
|
|
long selStart, selEnd, flags, search, length, start;
|
|
tom::ITextRange *range;
|
|
|
|
if ( find[0] == '\0' ) {
|
|
return false;
|
|
}
|
|
|
|
GetSel( selStart, selEnd );
|
|
|
|
flags = 0;
|
|
flags |= matchCase ? tom::tomMatchCase : 0;
|
|
flags |= matchWholeWords ? tom::tomMatchWord : 0;
|
|
|
|
if ( searchForward ) {
|
|
m_TextDoc->Range( selEnd, GetTextLength(), &range );
|
|
search = GetTextLength() - selEnd;
|
|
} else {
|
|
m_TextDoc->Range( 0, selStart, &range );
|
|
search = -selStart;
|
|
}
|
|
|
|
if ( range->FindShit( A2BSTR(find), search, flags, &length ) == S_OK ) {
|
|
|
|
m_TextDoc->Freeze( NULL );
|
|
|
|
range->get_Start( &start );
|
|
range->Release();
|
|
|
|
SetSel( start, start + length );
|
|
|
|
int line = Max( (int) LineFromChar( start ) - 5, 0 );
|
|
LineScroll( line - GetFirstVisibleLine(), 0 );
|
|
|
|
UpdateVisibleRange();
|
|
|
|
m_TextDoc->Unfreeze( NULL );
|
|
return true;
|
|
} else {
|
|
range->Release();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::ReplaceAll
|
|
================
|
|
*/
|
|
int CSyntaxRichEditCtrl::ReplaceAll( const char *find, const char *replace, bool matchCase, bool matchWholeWords ) {
|
|
long selStart, selEnd, flags, search, length, start;
|
|
int numReplaced;
|
|
tom::ITextRange *range;
|
|
CComBSTR bstr( find );
|
|
|
|
if ( find[0] == '\0' ) {
|
|
return 0;
|
|
}
|
|
|
|
m_TextDoc->Freeze( NULL );
|
|
|
|
GetSel( selStart, selEnd );
|
|
|
|
flags = 0;
|
|
flags |= matchCase ? tom::tomMatchCase : 0;
|
|
flags |= matchWholeWords ? tom::tomMatchWord : 0;
|
|
|
|
m_TextDoc->Range( 0, GetTextLength(), &range );
|
|
search = GetTextLength();
|
|
|
|
numReplaced = 0;
|
|
while( range->FindShit( bstr, search, flags, &length ) == S_OK ) {
|
|
range->get_Start( &start );
|
|
ReplaceText( start, start + length, replace );
|
|
numReplaced++;
|
|
}
|
|
|
|
range->Release();
|
|
|
|
m_TextDoc->Unfreeze( NULL );
|
|
|
|
return numReplaced;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::ReplaceText
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::ReplaceText( int startCharIndex, int endCharIndex, const char *replace ) {
|
|
tom::ITextRange *range;
|
|
CComBSTR bstr( replace );
|
|
|
|
m_TextDoc->Range( startCharIndex, endCharIndex, &range );
|
|
range->put_Text( bstr );
|
|
range->Release();
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::AutoCompleteInsertText
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::AutoCompleteInsertText( void ) {
|
|
long selStart, selEnd;
|
|
int index;
|
|
|
|
index = autoCompleteListBox.GetCurSel();
|
|
if ( index >= 0 ) {
|
|
CString text;
|
|
autoCompleteListBox.GetText( index, text );
|
|
GetSel( selStart, selEnd );
|
|
selStart = autoCompleteStart;
|
|
SetSel( selStart, selEnd );
|
|
ReplaceSel( text, TRUE );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::AutoCompleteUpdate
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::AutoCompleteUpdate( void ) {
|
|
long selStart, selEnd;
|
|
int index;
|
|
idStr text;
|
|
|
|
GetSel( selStart, selEnd );
|
|
GetText( text, autoCompleteStart, selStart );
|
|
index = autoCompleteListBox.FindString( -1, text );
|
|
if ( index >= 0 && index < autoCompleteListBox.GetCount() ) {
|
|
autoCompleteListBox.SetCurSel( index );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::AutoCompleteShow
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::AutoCompleteShow( int charIndex ) {
|
|
CPoint point;
|
|
CRect rect;
|
|
|
|
autoCompleteStart = charIndex;
|
|
point = PosFromChar( charIndex );
|
|
GetClientRect( rect );
|
|
if ( point.y < rect.bottom - AUTOCOMPLETE_OFFSET - AUTOCOMPLETE_HEIGHT ) {
|
|
rect.top = point.y + AUTOCOMPLETE_OFFSET;
|
|
rect.bottom = point.y + AUTOCOMPLETE_OFFSET + AUTOCOMPLETE_HEIGHT;
|
|
} else {
|
|
rect.top = point.y - AUTOCOMPLETE_HEIGHT;
|
|
rect.bottom = point.y;
|
|
}
|
|
rect.left = point.x;
|
|
rect.right = point.x + AUTOCOMPLETE_WIDTH;
|
|
autoCompleteListBox.MoveWindow( &rect );
|
|
autoCompleteListBox.ShowWindow( TRUE );
|
|
AutoCompleteUpdate();
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::AutoCompleteHide
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::AutoCompleteHide( void ) {
|
|
autoCompleteStart = -1;
|
|
autoCompleteListBox.ShowWindow( FALSE );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::ToolTipShow
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::ToolTipShow( int charIndex, const char *string ) {
|
|
CPoint point, p1, p2;
|
|
CRect rect;
|
|
|
|
funcParmToolTipStart = charIndex;
|
|
funcParmToolTip.SetWindowText( string );
|
|
p1 = funcParmToolTip.PosFromChar( 0 );
|
|
p2 = funcParmToolTip.PosFromChar( strlen( string ) - 1 );
|
|
point = PosFromChar( charIndex );
|
|
GetClientRect( rect );
|
|
if ( point.y < rect.bottom - FUNCPARMTOOLTIP_OFFSET - FUNCPARMTOOLTIP_HEIGHT ) {
|
|
rect.top = point.y + FUNCPARMTOOLTIP_OFFSET;
|
|
rect.bottom = point.y + FUNCPARMTOOLTIP_OFFSET + FUNCPARMTOOLTIP_HEIGHT;
|
|
} else {
|
|
rect.top = point.y - FUNCPARMTOOLTIP_HEIGHT;
|
|
rect.bottom = point.y;
|
|
}
|
|
rect.left = point.x;
|
|
rect.right = point.x + FUNCPARMTOOLTIP_WIDTH + p2.x - p1.x;
|
|
funcParmToolTip.MoveWindow( &rect );
|
|
funcParmToolTip.ShowWindow( TRUE );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::ToolTipHide
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::ToolTipHide( void ) {
|
|
funcParmToolTipStart = -1;
|
|
funcParmToolTip.ShowWindow( FALSE );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::BracedSectionStart
|
|
================
|
|
*/
|
|
bool CSyntaxRichEditCtrl::BracedSectionStart( char braceStartChar, char braceEndChar ) {
|
|
long selStart, selEnd;
|
|
int brace, i;
|
|
idStr text;
|
|
|
|
GetSel( selStart, selEnd );
|
|
GetText( text, 0, GetTextLength() );
|
|
|
|
for ( brace = 1, i = selStart; i < text.Length(); i++ ) {
|
|
if ( text[i] == braceStartChar ) {
|
|
brace++;
|
|
} else if ( text[i] == braceEndChar ) {
|
|
brace--;
|
|
if ( brace == 0 ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if ( brace == 0 ) {
|
|
bracedSection[0] = selStart - 1;
|
|
bracedSection[1] = i;
|
|
BracedSectionShow();
|
|
}
|
|
|
|
return ( brace == 0 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::BracedSectionEnd
|
|
================
|
|
*/
|
|
bool CSyntaxRichEditCtrl::BracedSectionEnd( char braceStartChar, char braceEndChar ) {
|
|
long selStart, selEnd;
|
|
int brace, i;
|
|
idStr text;
|
|
|
|
GetSel( selStart, selEnd );
|
|
GetText( text, 0, GetTextLength() );
|
|
|
|
for ( brace = 1, i = Min( selStart-2, (long)text.Length()-1 ); i >= 0; i-- ) {
|
|
if ( text[i] == braceStartChar ) {
|
|
brace--;
|
|
if ( brace == 0 ) {
|
|
break;
|
|
}
|
|
} else if ( text[i] == braceEndChar ) {
|
|
brace++;
|
|
}
|
|
}
|
|
|
|
if ( brace == 0 ) {
|
|
bracedSection[0] = i;
|
|
bracedSection[1] = selStart - 1;
|
|
BracedSectionAdjustEndTabs();
|
|
BracedSectionShow();
|
|
}
|
|
|
|
return ( brace == 0 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::BracedSectionAdjustEndTabs
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::BracedSectionAdjustEndTabs( void ) {
|
|
int line, lineIndex, length, column, numTabs, i;
|
|
char buffer[1024];
|
|
idStr text;
|
|
|
|
line = LineFromChar( bracedSection[0] );
|
|
length = GetLine( line, buffer, sizeof( buffer ) );
|
|
for ( numTabs = 0; numTabs < length; numTabs++ ) {
|
|
if ( !idStr::CharIsTab( buffer[numTabs] ) ) {
|
|
break;
|
|
}
|
|
text.Append( '\t' );
|
|
}
|
|
|
|
line = LineFromChar( bracedSection[1] );
|
|
lineIndex = LineIndex( line );
|
|
length = GetLine( line, buffer, sizeof( buffer ) );
|
|
column = bracedSection[1] - lineIndex;
|
|
for ( i = 0; i < column; i++ ) {
|
|
if ( charType[buffer[i]] != CT_WHITESPACE ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
ReplaceText( lineIndex, lineIndex + column, text );
|
|
|
|
bracedSection[1] += numTabs - column;
|
|
SetSel( bracedSection[1]+1, bracedSection[1]+1 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::BracedSectionShow
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::BracedSectionShow( void ) {
|
|
for ( int i = 0; i < 2; i++ ) {
|
|
if ( bracedSection[i] >= 0 ) {
|
|
SetColor( bracedSection[i], bracedSection[i] + 1, braceHighlightColor, DEFAULT_BACK_COLOR, true );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::BracedSectionHide
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::BracedSectionHide( void ) {
|
|
for ( int i = 0; i < 2; i++ ) {
|
|
if ( bracedSection[i] >= 0 ) {
|
|
SetColor( bracedSection[i], bracedSection[i] + 1, defaultColor, DEFAULT_BACK_COLOR, false );
|
|
bracedSection[i] = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::GetNameBeforeCurrentSelection
|
|
================
|
|
*/
|
|
bool CSyntaxRichEditCtrl::GetNameBeforeCurrentSelection( CString &name, int &charIndex ) const {
|
|
long selStart, selEnd;
|
|
int line, column, length;
|
|
char buffer[1024];
|
|
|
|
GetSel( selStart, selEnd );
|
|
charIndex = selStart;
|
|
line = LineFromChar( selStart );
|
|
length = GetLine( line, buffer, sizeof( buffer ) );
|
|
column = selStart - LineIndex( line ) - 1;
|
|
do {
|
|
buffer[column--] = '\0';
|
|
} while( charType[buffer[column]] == CT_WHITESPACE );
|
|
for ( length = 0; length < column; length++ ) {
|
|
if ( charType[buffer[column-length-1]] != CT_NAME ) {
|
|
break;
|
|
}
|
|
}
|
|
if ( length > 0 ) {
|
|
name = buffer + column - length;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::GetNameForMousePosition
|
|
================
|
|
*/
|
|
bool CSyntaxRichEditCtrl::GetNameForMousePosition( idStr &name ) const {
|
|
int charIndex, startCharIndex, endCharIndex, type;
|
|
idStr text;
|
|
|
|
charIndex = CharFromPos( mousePoint );
|
|
|
|
for ( startCharIndex = charIndex; startCharIndex > 0; startCharIndex-- ) {
|
|
GetText( text, startCharIndex - 1, startCharIndex );
|
|
type = charType[text[0]];
|
|
if ( type != CT_NAME && type != CT_NUMBER ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
for ( endCharIndex = charIndex; endCharIndex < GetTextLength(); endCharIndex++ ) {
|
|
GetText( text, endCharIndex, endCharIndex + 1 );
|
|
type = charType[text[0]];
|
|
if ( type != CT_NAME && type != CT_NUMBER ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
GetText( name, startCharIndex, endCharIndex );
|
|
|
|
return ( endCharIndex > startCharIndex );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::GoToLine
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::GoToLine( int line ) {
|
|
|
|
int index = LineIndex( line );
|
|
|
|
m_TextDoc->Freeze( NULL );
|
|
|
|
SetSel( index, index );
|
|
|
|
m_TextDoc->Unfreeze( NULL );
|
|
|
|
UpdateVisibleRange();
|
|
|
|
RedrawWindow();
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::OnToolHitTest
|
|
================
|
|
*/
|
|
INT_PTR CSyntaxRichEditCtrl::OnToolHitTest( CPoint point, TOOLINFO* pTI ) const {
|
|
CRichEditCtrl::OnToolHitTest( point, pTI );
|
|
|
|
pTI->hwnd = GetSafeHwnd();
|
|
pTI->uId = (UINT_PTR)GetSafeHwnd();
|
|
pTI->uFlags |= TTF_IDISHWND;
|
|
pTI->lpszText = LPSTR_TEXTCALLBACK;
|
|
pTI->rect = CRect( point, point );
|
|
pTI->rect.right += 100;
|
|
pTI->rect.bottom += 20;
|
|
return pTI->uId;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::OnToolTipNotify
|
|
================
|
|
*/
|
|
BOOL CSyntaxRichEditCtrl::OnToolTipNotify( UINT id, NMHDR *pNMHDR, LRESULT *pResult ) {
|
|
|
|
#ifdef _UNICODE
|
|
TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
|
|
#else
|
|
TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
|
|
#endif
|
|
|
|
*pResult = 0;
|
|
|
|
idStr name;
|
|
|
|
if ( GetNameForMousePosition( name ) ) {
|
|
CString toolTip;
|
|
|
|
if ( GetToolTip == NULL || !GetToolTip( name, toolTip ) ) {
|
|
|
|
int keyWordIndex = FindKeyWord( name, name.Length() );
|
|
|
|
if ( keyWordIndex != -1 && keyWords[keyWordIndex].description[0] != '\0' ) {
|
|
toolTip = keyWords[keyWordIndex].description;
|
|
} else {
|
|
toolTip = name.c_str();
|
|
}
|
|
}
|
|
|
|
AFX_MODULE_THREAD_STATE *state = AfxGetModuleThreadState();
|
|
|
|
// set max tool tip width to enable multi-line tool tips using "\r\n" for line breaks
|
|
state->m_pToolTip->SetMaxTipWidth( 500 );
|
|
|
|
// set the number of milliseconds after which the tool tip automatically disappears
|
|
state->m_pToolTip->SetDelayTime( TTDT_AUTOPOP, 5000 + toolTip.GetLength() * 50 );
|
|
|
|
#ifndef _UNICODE
|
|
if( pNMHDR->code == TTN_NEEDTEXTA ) {
|
|
delete m_pchTip;
|
|
m_pchTip = new TCHAR[toolTip.GetLength() + 2];
|
|
lstrcpyn( m_pchTip, toolTip, toolTip.GetLength() + 1 );
|
|
pTTTW->lpszText = (WCHAR*)m_pchTip;
|
|
} else {
|
|
delete m_pwchTip;
|
|
m_pwchTip = new WCHAR[toolTip.GetLength() + 2];
|
|
_mbstowcsz( m_pwchTip, toolTip, toolTip.GetLength() + 1 );
|
|
pTTTW->lpszText = (WCHAR*)m_pwchTip;
|
|
}
|
|
#else
|
|
if( pNMHDR->code == TTN_NEEDTEXTA ) {
|
|
delete m_pchTip;
|
|
m_pchTip = new TCHAR[toolTip.GetLength() + 2];
|
|
_wcstombsz( m_pchTip, toolTip, toolTip.GetLength() + 1 );
|
|
pTTTA->lpszText = (LPTSTR)m_pchTip;
|
|
} else {
|
|
delete m_pwchTip;
|
|
m_pwchTip = new WCHAR[toolTip.GetLength() + 2];
|
|
lstrcpyn( m_pwchTip, toolTip, toolTip.GetLength() + 1 );
|
|
pTTTA->lpszText = (LPTSTR) m_pwchTip;
|
|
}
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::OnGetDlgCode
|
|
================
|
|
*/
|
|
UINT CSyntaxRichEditCtrl::OnGetDlgCode() {
|
|
// get all keys, including tabs
|
|
return DLGC_WANTALLKEYS | DLGC_WANTARROWS | DLGC_WANTCHARS | DLGC_WANTMESSAGE | DLGC_WANTTAB;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::OnKeyDown
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::OnKeyDown( UINT nKey, UINT nRepCnt, UINT nFlags ) {
|
|
|
|
if ( m_TextDoc == NULL ) {
|
|
return;
|
|
}
|
|
|
|
if ( autoCompleteStart >= 0 ) {
|
|
int sel;
|
|
|
|
switch( nKey ) {
|
|
case VK_UP: { // up arrow
|
|
sel = Max( 0, autoCompleteListBox.GetCurSel() - 1 );
|
|
autoCompleteListBox.SetCurSel( sel );
|
|
return;
|
|
}
|
|
case VK_DOWN: { // down arrow
|
|
sel = Min( autoCompleteListBox.GetCount() - 1, autoCompleteListBox.GetCurSel() + 1 );
|
|
autoCompleteListBox.SetCurSel( sel );
|
|
return;
|
|
}
|
|
case VK_PRIOR: { // page up key
|
|
sel = Max( 0, autoCompleteListBox.GetCurSel() - 10 );
|
|
autoCompleteListBox.SetCurSel( sel );
|
|
return;
|
|
}
|
|
case VK_NEXT: { // page down key
|
|
sel = Min( autoCompleteListBox.GetCount() - 1, autoCompleteListBox.GetCurSel() + 10 );
|
|
autoCompleteListBox.SetCurSel( sel );
|
|
return;
|
|
}
|
|
case VK_HOME: { // home key
|
|
autoCompleteListBox.SetCurSel( 0 );
|
|
return;
|
|
}
|
|
case VK_END: {
|
|
autoCompleteListBox.SetCurSel( autoCompleteListBox.GetCount() - 1 );
|
|
return;
|
|
}
|
|
case VK_RETURN: // enter key
|
|
case VK_TAB: { // tab key
|
|
AutoCompleteInsertText();
|
|
AutoCompleteHide();
|
|
return;
|
|
}
|
|
case VK_LEFT: // left arrow
|
|
case VK_RIGHT: // right arrow
|
|
case VK_INSERT: // insert key
|
|
case VK_DELETE: { // delete key
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
BracedSectionHide();
|
|
|
|
switch( nKey ) {
|
|
case VK_TAB: { // multi-line tabs
|
|
long selStart, selEnd;
|
|
|
|
GetSel( selStart, selEnd );
|
|
|
|
// if multiple lines are selected add tabs to, or remove tabs from all of them
|
|
if ( selEnd > selStart ) {
|
|
CString text;
|
|
|
|
text = GetSelText();
|
|
|
|
if ( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) {
|
|
if ( idStr::CharIsTab( text[0] ) ) {
|
|
text.Delete( 0, 1 );
|
|
}
|
|
for ( int i = 0; i < text.GetLength() - 2; i++ ) {
|
|
if ( idStr::CharIsNewLine( text[i] ) ) {
|
|
do {
|
|
i++;
|
|
} while( idStr::CharIsNewLine( text[i] ) );
|
|
if ( idStr::CharIsTab( text[i] ) ) {
|
|
text.Delete( i, 1 );
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
text.Insert( 0, '\t' );
|
|
for ( int i = 0; i < text.GetLength() - 1; i++ ) {
|
|
if ( idStr::CharIsNewLine( text[i] ) ) {
|
|
do {
|
|
i++;
|
|
} while( idStr::CharIsNewLine( text[i] ) );
|
|
text.Insert( i, '\t' );
|
|
}
|
|
}
|
|
}
|
|
|
|
ReplaceSel( text, TRUE );
|
|
SetSel( selStart, selStart + text.GetLength() );
|
|
} else {
|
|
ReplaceSel( "\t", TRUE );
|
|
}
|
|
return;
|
|
}
|
|
case VK_RETURN: { // auto-indentation
|
|
long selStart, selEnd;
|
|
int line, length, numTabs, i;
|
|
char buffer[1024];
|
|
idStr text;
|
|
|
|
GetSel( selStart, selEnd );
|
|
line = LineFromChar( selStart );
|
|
length = GetLine( line, buffer, sizeof( buffer ) );
|
|
for ( numTabs = 0; numTabs < length; numTabs++ ) {
|
|
if ( !idStr::CharIsTab( buffer[numTabs] ) ) {
|
|
break;
|
|
}
|
|
}
|
|
bool first = true;
|
|
for ( i = numTabs; i < length; i++ ) {
|
|
if ( buffer[i] == '{' ) {
|
|
numTabs++;
|
|
first = false;
|
|
} else if ( buffer[i] == '}' && !first ) {
|
|
numTabs--;
|
|
}
|
|
}
|
|
text = "\r\n";
|
|
for ( i = 0; i < numTabs; i++ ) {
|
|
text.Append( '\t' );
|
|
}
|
|
ReplaceSel( text, TRUE );
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_TextDoc->Freeze( NULL );
|
|
|
|
CRichEditCtrl::OnKeyDown( nKey, nRepCnt, nFlags );
|
|
|
|
UpdateVisibleRange();
|
|
|
|
m_TextDoc->Unfreeze( NULL );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::OnChar
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::OnChar( UINT nChar, UINT nRepCnt, UINT nFlags ) {
|
|
|
|
if ( nChar == VK_TAB ) {
|
|
return; // tab is handle in OnKeyDown
|
|
}
|
|
|
|
CRichEditCtrl::OnChar( nChar, nRepCnt, nFlags );
|
|
|
|
// if the auto-complete list box is up
|
|
if ( autoCompleteStart >= 0 ) {
|
|
long selStart, selEnd;
|
|
|
|
if ( charType[nChar] == CT_NAME ) {
|
|
AutoCompleteUpdate();
|
|
return;
|
|
} else if ( nChar == VK_BACK ) {
|
|
GetSel( selStart, selEnd );
|
|
if ( selStart > autoCompleteStart ) {
|
|
AutoCompleteUpdate();
|
|
} else {
|
|
AutoCompleteHide();
|
|
}
|
|
return;
|
|
} else {
|
|
AutoCompleteHide();
|
|
}
|
|
}
|
|
|
|
// if the function parameter tool tip is up
|
|
if ( funcParmToolTipStart >= 0 ) {
|
|
long selStart, selEnd;
|
|
|
|
if ( nChar == ')' || nChar == VK_ESCAPE ) {
|
|
ToolTipHide();
|
|
} else if ( nChar == VK_BACK ) {
|
|
GetSel( selStart, selEnd );
|
|
if ( selStart < funcParmToolTipStart ) {
|
|
ToolTipHide();
|
|
}
|
|
}
|
|
}
|
|
|
|
// show keyword auto-completion
|
|
if ( keyWordAutoCompletion && charType[nChar] == CT_NAME && funcParmToolTipStart < 0 ) {
|
|
long selStart, selEnd;
|
|
int line, column, length, i;
|
|
char buffer[1024];
|
|
|
|
GetSel( selStart, selEnd );
|
|
line = LineFromChar( selStart );
|
|
length = GetLine( line, buffer, sizeof( buffer ) );
|
|
column = selStart - LineIndex( line );
|
|
if ( column <= 1 || charType[buffer[column-2]] == CT_WHITESPACE ) {
|
|
if ( column >= length-1 || charType[buffer[column]] == CT_WHITESPACE ) {
|
|
|
|
autoCompleteListBox.ResetContent();
|
|
for ( i = 0; keyWords[i].keyWord; i++ ) {
|
|
autoCompleteListBox.AddString( keyWords[i].keyWord );
|
|
}
|
|
AutoCompleteShow( selStart - 1 );
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// highlight braced sections
|
|
if ( nChar == '{' ) {
|
|
BracedSectionStart( '{', '}' );
|
|
} else if ( nChar == '}' ) {
|
|
BracedSectionEnd( '{', '}' );
|
|
} else if ( nChar == '(' ) {
|
|
BracedSectionStart( '(', ')' );
|
|
} else if ( nChar == ')' ) {
|
|
BracedSectionEnd( '(', ')' );
|
|
} else if ( nChar == '[' ) {
|
|
BracedSectionStart( '[', ']' );
|
|
} else if ( nChar == ']' ) {
|
|
BracedSectionEnd( '[', ']' );
|
|
} else if ( nChar == '<' ) {
|
|
BracedSectionStart( '<', '>' );
|
|
} else if ( nChar == '>' ) {
|
|
BracedSectionEnd( '<', '>' );
|
|
}
|
|
|
|
// show object member auto-completion
|
|
if ( nChar == '.' && GetObjectMembers && funcParmToolTipStart < 0 ) {
|
|
int charIndex;
|
|
CString name;
|
|
|
|
if ( GetNameBeforeCurrentSelection( name, charIndex ) ) {
|
|
autoCompleteListBox.ResetContent();
|
|
if ( GetObjectMembers( name, autoCompleteListBox ) ) {
|
|
AutoCompleteShow( charIndex );
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// show function parameter tool tip
|
|
if ( nChar == '(' && GetFunctionParms ) {
|
|
int charIndex;
|
|
CString name;
|
|
|
|
if ( GetNameBeforeCurrentSelection( name, charIndex ) ) {
|
|
CString parmString;
|
|
if ( GetFunctionParms( name, parmString ) ) {
|
|
ToolTipShow( charIndex, parmString );
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::OnLButtonDown
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::OnLButtonDown( UINT nFlags, CPoint point ) {
|
|
|
|
if ( autoCompleteStart >= 0 ) {
|
|
AutoCompleteHide();
|
|
}
|
|
|
|
BracedSectionHide();
|
|
|
|
CRichEditCtrl::OnLButtonDown( nFlags, point );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::OnMouseWheel
|
|
================
|
|
*/
|
|
BOOL CSyntaxRichEditCtrl::OnMouseWheel( UINT nFlags, short zDelta, CPoint pt ) {
|
|
if ( autoCompleteStart >= 0 ) {
|
|
int sel;
|
|
|
|
if ( zDelta > 0 ) {
|
|
sel = Max( 0, autoCompleteListBox.GetCurSel() - ( zDelta / WHEEL_DELTA ) );
|
|
} else {
|
|
sel = Min( autoCompleteListBox.GetCount() - 1, autoCompleteListBox.GetCurSel() - ( zDelta / WHEEL_DELTA ) );
|
|
}
|
|
autoCompleteListBox.SetCurSel( sel );
|
|
return TRUE;
|
|
}
|
|
|
|
m_TextDoc->Freeze( NULL );
|
|
|
|
LineScroll( -3 * ( (int) zDelta ) / WHEEL_DELTA, 0 );
|
|
|
|
UpdateVisibleRange();
|
|
|
|
m_TextDoc->Unfreeze( NULL );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::OnMouseMove
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::OnMouseMove( UINT nFlags, CPoint point ) {
|
|
CRichEditCtrl::OnMouseMove( nFlags, point );
|
|
|
|
if ( point != mousePoint ) {
|
|
mousePoint = point;
|
|
|
|
// remove tool tip and activate the tool tip control, otherwise
|
|
// tool tips stop working until the mouse moves over another window first
|
|
AFX_MODULE_THREAD_STATE *state = AfxGetModuleThreadState();
|
|
state->m_pToolTip->Pop();
|
|
state->m_pToolTip->Activate( TRUE );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::OnSize
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::OnSize( UINT nType, int cx, int cy ) {
|
|
m_TextDoc->Freeze( NULL );
|
|
|
|
CRichEditCtrl::OnSize( nType, cx, cy );
|
|
|
|
m_TextDoc->Unfreeze( NULL );
|
|
|
|
UpdateVisibleRange();
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::OnVScroll
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::OnVScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar ) {
|
|
m_TextDoc->Freeze( NULL );
|
|
|
|
CRichEditCtrl::OnVScroll( nSBCode, nPos, pScrollBar );
|
|
|
|
SetFocus();
|
|
|
|
UpdateVisibleRange();
|
|
|
|
m_TextDoc->Unfreeze( NULL );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::OnProtected
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::OnProtected( NMHDR *pNMHDR, LRESULT *pResult ) {
|
|
ENPROTECTED* pEP = (ENPROTECTED*)pNMHDR;
|
|
|
|
*pResult = 0;
|
|
|
|
updateRange = pEP->chrg;
|
|
|
|
switch( pEP->msg ) {
|
|
case WM_MOUSEMOVE: {
|
|
break;
|
|
}
|
|
case WM_SETTEXT: {
|
|
updateRange.cpMin = pEP->chrg.cpMin;
|
|
updateRange.cpMax = pEP->chrg.cpMin + strlen( (LPCTSTR) pEP->lParam );
|
|
break;
|
|
}
|
|
case WM_CUT: {
|
|
break;
|
|
}
|
|
case WM_COPY: {
|
|
break;
|
|
}
|
|
case WM_PASTE: {
|
|
break;
|
|
}
|
|
case WM_CLEAR: {
|
|
break;
|
|
}
|
|
case WM_UNDO: {
|
|
break;
|
|
}
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::OnChange
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::OnChange() {
|
|
long selStart, selEnd;
|
|
|
|
if ( !updateSyntaxHighlighting ) {
|
|
return;
|
|
}
|
|
|
|
GetSel( selStart, selEnd );
|
|
selStart = Min( selStart, updateRange.cpMin );
|
|
selEnd = Max( selEnd, updateRange.cpMax );
|
|
|
|
HighlightSyntax( selStart, selEnd );
|
|
|
|
// send EN_CHANGE notification to parent window
|
|
NMHDR pNMHDR;
|
|
pNMHDR.hwndFrom = GetSafeHwnd();
|
|
pNMHDR.idFrom = GetDlgCtrlID();
|
|
pNMHDR.code = EN_CHANGE;
|
|
GetParent()->SendMessage( WM_NOTIFY, ( EN_CHANGE << 16 ) | GetDlgCtrlID(), (LPARAM)&pNMHDR );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::OnAutoCompleteListBoxChange
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::OnAutoCompleteListBoxChange() {
|
|
// steal focus back from the auto-complete list box
|
|
SetFocus();
|
|
}
|
|
|
|
/*
|
|
================
|
|
CSyntaxRichEditCtrl::OnAutoCompleteListBoxDblClk
|
|
================
|
|
*/
|
|
void CSyntaxRichEditCtrl::OnAutoCompleteListBoxDblClk() {
|
|
// steal focus back from the auto-complete list box
|
|
SetFocus();
|
|
|
|
// insert current auto-complete selection
|
|
AutoCompleteInsertText();
|
|
AutoCompleteHide();
|
|
}
|