/* =========================================================================== 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 . 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( keyWordsFromFile[i].keyWord ) ); Mem_Free( const_cast( 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 ) { TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR; TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR; *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(); }