]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/tools/comafx/CSyntaxRichEditCtrl.cpp
Various Mac OS X tweaks to get this to build. Probably breaking things.
[icculus/iodoom3.git] / neo / tools / comafx / CSyntaxRichEditCtrl.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 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.
23
24 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.
25
26 ===========================================================================
27 */
28
29 #include "../../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "CSyntaxRichEditCtrl.h"
33
34 #ifdef ID_DEBUG_MEMORY
35 #undef new
36 #undef DEBUG_NEW
37 #define DEBUG_NEW new
38 #endif
39
40 // NOTE:        known bug, if you directly jump to a not yet highligted page with the first line starting
41 //                      inside a multi-line comment then the multi-line comment is not picked up and highlighted
42
43 const int AUTOCOMPLETE_WIDTH                    = 200;
44 const int AUTOCOMPLETE_HEIGHT                   = 180;
45 const int AUTOCOMPLETE_OFFSET                   = 16;
46
47 const int FUNCPARMTOOLTIP_WIDTH                 = 16;
48 const int FUNCPARMTOOLTIP_HEIGHT                = 20;
49 const int FUNCPARMTOOLTIP_OFFSET                = 16;
50
51 const COLORREF DEFAULT_BACK_COLOR                               = SRE_COLOR_WHITE;
52 const COLORREF INVALID_BACK_COLOR                               = SRE_COLOR_WHITE - 2;
53 const COLORREF MULTILINE_COMMENT_BACK_COLOR             = SRE_COLOR_WHITE - 1;
54
55 #define IDC_LISTBOX_AUTOCOMPLETE                700
56 #define IDC_EDITBOX_FUNCPARMS                   701
57
58 static keyWord_t defaultKeyWords[] = {
59         { NULL, SRE_COLOR_BLACK, "" }
60 };
61
62 BEGIN_MESSAGE_MAP(CSyntaxRichEditCtrl, CRichEditCtrl)
63         ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)
64         ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)
65         ON_WM_GETDLGCODE()
66         ON_WM_KEYDOWN()
67         ON_WM_CHAR()
68         ON_WM_LBUTTONDOWN()
69         ON_WM_MOUSEWHEEL()
70         ON_WM_MOUSEMOVE()
71         ON_WM_VSCROLL()
72         ON_WM_SIZE()
73         ON_NOTIFY_REFLECT(EN_PROTECTED, OnProtected)
74         ON_CONTROL_REFLECT(EN_CHANGE, OnChange)
75         ON_LBN_SELCANCEL(IDC_LISTBOX_AUTOCOMPLETE, OnAutoCompleteListBoxChange)
76         ON_LBN_SELCHANGE(IDC_LISTBOX_AUTOCOMPLETE, OnAutoCompleteListBoxChange)
77         ON_LBN_DBLCLK(IDC_LISTBOX_AUTOCOMPLETE, OnAutoCompleteListBoxDblClk)
78 END_MESSAGE_MAP()
79
80
81 /*
82 ================
83 CSyntaxRichEditCtrl::CSyntaxRichEditCtrl
84 ================
85 */
86 CSyntaxRichEditCtrl::CSyntaxRichEditCtrl( void ) {
87         m_TextDoc = NULL;
88         keyWords = defaultKeyWords;
89         keyWordColors = NULL;
90         keyWordLengths = NULL;
91         caseSensitive = false;
92         allowPathNames = true;
93         keyWordAutoCompletion = true;
94         updateRange.cpMin = 0;
95         updateRange.cpMax = 0;
96         updateSyntaxHighlighting = true;
97         stringColorIndex = 0;
98         stringColorLine = -1;
99         autoCompleteStart = -1;
100         funcParmToolTipStart = -1;
101         bracedSection[0] = -1;
102         bracedSection[1] = -1;
103         GetObjectMembers = NULL;
104         GetFunctionParms = NULL;
105         GetToolTip = NULL;
106         mousePoint.x = 0;
107         mousePoint.y = 0;
108         keyWordToolTip = NULL;
109         m_pchTip = NULL;
110         m_pwchTip = NULL;
111 }
112
113 /*
114 ================
115 CSyntaxRichEditCtrl::~CSyntaxRichEditCtrl
116 ================
117 */
118 CSyntaxRichEditCtrl::~CSyntaxRichEditCtrl( void ) {
119         FreeKeyWordsFromFile();
120         delete m_pchTip;
121         delete m_pwchTip;
122         m_DefaultFont->Release();
123 }
124
125 /*
126 ================
127 CSyntaxRichEditCtrl::InitFont
128 ================
129 */
130 void CSyntaxRichEditCtrl::InitFont( void ) {
131         LOGFONT lf;
132         CFont font;
133         PARAFORMAT pf;
134         int logx, tabSize;
135
136         // set the font
137         memset( &lf, 0, sizeof( lf ) );
138         lf.lfHeight = FONT_HEIGHT * 10;
139         lf.lfWidth = FONT_WIDTH * 10;
140         lf.lfCharSet = ANSI_CHARSET;
141         lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
142         strcpy( lf.lfFaceName, FONT_NAME );
143         font.CreatePointFontIndirect( &lf );
144
145         SetFont( &font );
146
147         // get the tab size in twips
148         logx = ::GetDeviceCaps( GetDC()->GetSafeHdc(), LOGPIXELSX );
149         tabSize = TAB_SIZE * FONT_WIDTH * 1440 / logx;
150
151         // set the tabs
152         memset( &pf, 0, sizeof( PARAFORMAT ) );
153         pf.cbSize = sizeof( PARAFORMAT );
154         pf.dwMask = PFM_TABSTOPS;
155         for ( pf.cTabCount = 0; pf.cTabCount < MAX_TAB_STOPS; pf.cTabCount++ ) {
156                 pf.rgxTabs[pf.cTabCount] = pf.cTabCount * tabSize;
157         }
158
159         SetParaFormat( pf );
160
161         memset( &defaultCharFormat, 0, sizeof( defaultCharFormat ) );
162         defaultCharFormat.dwMask = CFM_CHARSET | CFM_FACE | CFM_SIZE | CFM_BOLD | CFM_COLOR | CFM_PROTECTED | CFM_BACKCOLOR;
163         defaultCharFormat.yHeight = FONT_HEIGHT * 20;
164         defaultCharFormat.bCharSet = ANSI_CHARSET;
165         defaultCharFormat.bPitchAndFamily = FIXED_PITCH | FF_MODERN;
166         defaultCharFormat.crTextColor = SRE_COLOR_BLACK;
167         defaultCharFormat.crBackColor = DEFAULT_BACK_COLOR;
168         defaultCharFormat.dwEffects = CFE_PROTECTED;
169         strcpy( defaultCharFormat.szFaceName, FONT_NAME );
170         defaultCharFormat.cbSize = sizeof( defaultCharFormat );
171
172         SetDefaultCharFormat( defaultCharFormat );
173
174         defaultColor = SRE_COLOR_BLACK;
175         singleLineCommentColor = SRE_COLOR_DARK_GREEN;
176         multiLineCommentColor = SRE_COLOR_DARK_GREEN;
177         stringColor[0] = stringColor[1] = SRE_COLOR_DARK_CYAN;
178         literalColor = SRE_COLOR_GREY;
179         braceHighlightColor = SRE_COLOR_RED;
180
181         // get the default tom::ITextFont
182         tom::ITextRange *irange;
183         tom::ITextFont *ifont;
184
185         m_TextDoc->Range( 0, 0, &irange );
186         irange->get_Font( &ifont );
187
188         ifont->get_Duplicate( &m_DefaultFont );
189
190         ifont->Release();
191         irange->Release();
192 }
193
194 /*
195 ================
196 CSyntaxRichEditCtrl::SetCharType
197 ================
198 */
199 void CSyntaxRichEditCtrl::SetCharType( int first, int last, int type ) {
200         for ( int i = first; i <= last; i++ ) {
201                 charType[i] = type;
202         }
203 }
204
205 /*
206 ================
207 CSyntaxRichEditCtrl::InitSyntaxHighlighting
208 ================
209 */
210 void CSyntaxRichEditCtrl::InitSyntaxHighlighting( void ) {
211         SetCharType( 0x00, 0xFF, CT_PUNCTUATION );
212         SetCharType( '\0', ' ', CT_WHITESPACE );
213         SetCharType( '/', '/', CT_COMMENT );
214         SetCharType( '\"', '\"', CT_STRING );
215         SetCharType( '\'', '\'', CT_LITERAL );
216         SetCharType( 'a', 'z', CT_NAME );
217         SetCharType( 'A', 'Z', CT_NAME );
218         SetCharType( '_', '_', CT_NAME );
219         SetCharType( '#', '#', CT_NAME );
220         SetCharType( '0', '9', CT_NUMBER );
221 }
222
223 /*
224 ================
225 CSyntaxRichEditCtrl::Init
226 ================
227 */
228 void CSyntaxRichEditCtrl::Init( void ) {
229
230         // get the Rich Edit ITextDocument to use the wonky TOM interface
231         IRichEditOle *ire = GetIRichEditOle();
232         IUnknown *iu = (IUnknown *)ire;
233         if ( iu == NULL || iu->QueryInterface( tom::IID_ITextDocument, (void**) &m_TextDoc ) != S_OK ) {
234                 m_TextDoc = NULL;
235         }
236
237         InitFont();
238
239         InitSyntaxHighlighting();
240
241         SetEventMask( GetEventMask() | ENM_CHANGE | ENM_KEYEVENTS | ENM_MOUSEEVENTS | ENM_PROTECTED );  // ENM_SCROLLEVENTS
242
243         EnableToolTips( TRUE );
244
245         // create auto complete list box
246         CRect rect( 0, 0, AUTOCOMPLETE_WIDTH, AUTOCOMPLETE_HEIGHT );
247         autoCompleteListBox.Create( WS_DLGFRAME | WS_VISIBLE | WS_VSCROLL | LBS_SORT | LBS_NOTIFY, rect, this, IDC_LISTBOX_AUTOCOMPLETE );
248         autoCompleteListBox.SetFont( GetParent()->GetFont() );
249         autoCompleteListBox.ShowWindow( FALSE );
250
251         // create function parameter tool tip
252         funcParmToolTip.Create( WS_VISIBLE | WS_BORDER, rect, this, IDC_EDITBOX_FUNCPARMS );
253         funcParmToolTip.SetFont( GetParent()->GetFont() );
254         funcParmToolTip.ShowWindow( FALSE );
255 }
256
257 /*
258 ================
259 CSyntaxRichEditCtrl::FindKeyWord
260 ================
261 */
262 ID_INLINE int CSyntaxRichEditCtrl::FindKeyWord( const char *keyWord, int length ) const {
263         int i, hash;
264
265         if ( caseSensitive ) {
266                 hash = idStr::Hash( keyWord, length );
267         } else {
268                 hash = idStr::IHash( keyWord, length );
269         }
270         for ( i = keyWordHash.First( hash ); i != -1; i = keyWordHash.Next( i ) ) {
271                 if ( length != keyWordLengths[i] ) {
272                         continue;
273                 }
274                 if ( caseSensitive ) {
275                         if ( idStr::Cmpn( keyWords[i].keyWord, keyWord, length ) != 0 ) {
276                                 continue;
277                         }
278                 } else {
279                         if ( idStr::Icmpn( keyWords[i].keyWord, keyWord, length ) != 0 ) {
280                                 continue;
281                         }
282                 }
283                 return i;
284         }
285         return -1;
286 }
287
288 /*
289 ================
290 CSyntaxRichEditCtrl::SetKeyWords
291 ================
292 */
293 void CSyntaxRichEditCtrl::SetKeyWords( const keyWord_t kws[] ) {
294         int i, numKeyWords, hash;
295
296         keyWords = kws;
297
298         for ( numKeyWords = 0; keyWords[numKeyWords].keyWord; numKeyWords++ ) {
299         }
300
301         delete keyWordColors;
302         keyWordColors = new COLORREF[numKeyWords];
303
304         for ( i = 0; i < numKeyWords; i++ ) {
305                 keyWordColors[i] = keyWords[i].color;
306         }
307
308         delete keyWordLengths;
309         keyWordLengths = new int[numKeyWords];
310
311         for ( i = 0; i < numKeyWords; i++ ) {
312                 keyWordLengths[i] = idStr::Length( keyWords[i].keyWord );
313         }
314
315         keyWordHash.Clear( 1024, 1024 );
316         for ( i = 0; i < numKeyWords; i++ ) {
317                 if ( caseSensitive ) {
318                         hash = idStr::Hash( keyWords[i].keyWord, keyWordLengths[i] );
319                 } else {
320                         hash = idStr::IHash( keyWords[i].keyWord, keyWordLengths[i] );
321                 }
322                 keyWordHash.Add( hash, i );
323         }
324 }
325
326 /*
327 ================
328 CSyntaxRichEditCtrl::LoadKeyWordsFromFile
329 ================
330 */
331 bool CSyntaxRichEditCtrl::LoadKeyWordsFromFile( const char *fileName ) {
332         idParser src;
333         idToken token, name, description;
334         byte red, green, blue;
335         keyWord_t keyword;
336
337         if ( !src.LoadFile( fileName ) ) {
338                 return false;
339         }
340
341         FreeKeyWordsFromFile();
342
343         while( src.ReadToken( &token ) ) {
344                 if ( token.Icmp( "keywords" ) == 0 ) {
345                         src.ExpectTokenString( "{" );
346                         while( src.ReadToken( &token ) ) {
347                                 if ( token == "}" ) {
348                                         break;
349                                 }
350                                 if ( token == "{" ) {
351
352                                         // parse name
353                                         src.ExpectTokenType( TT_STRING, 0, &name );
354                                         src.ExpectTokenString( "," );
355
356                                         // parse color
357                                         src.ExpectTokenString( "(" );
358                                         src.ExpectTokenType( TT_NUMBER, TT_INTEGER, &token );
359                                         red = token.GetIntValue();
360                                         src.ExpectTokenString( "," );
361                                         src.ExpectTokenType( TT_NUMBER, TT_INTEGER, &token );
362                                         green = token.GetIntValue();
363                                         src.ExpectTokenString( "," );
364                                         src.ExpectTokenType( TT_NUMBER, TT_INTEGER, &token );
365                                         blue = token.GetIntValue();
366                                         src.ExpectTokenString( ")" );
367                                         src.ExpectTokenString( "," );
368
369                                         // parse description
370                                         src.ExpectTokenType( TT_STRING, 0, &description );
371                                         src.ExpectTokenString( "}" );
372
373                                         keyword.keyWord = Mem_CopyString( name );
374                                         keyword.color = RGB( red, green, blue );
375                                         keyword.description = Mem_CopyString( description );
376
377                                         keyWordsFromFile.Append( keyword );
378                                 }
379                         }
380                 } else {
381                         src.SkipBracedSection();
382                 }
383         }
384
385         keyword.keyWord = NULL;
386         keyword.color = RGB( 255, 255, 255 );
387         keyword.description = NULL;
388         keyWordsFromFile.Append( keyword );
389
390         SetKeyWords( keyWordsFromFile.Ptr() );
391
392         return true;
393 }
394
395 /*
396 ================
397 CSyntaxRichEditCtrl::FreeKeyWordsFromFile
398 ================
399 */
400 void CSyntaxRichEditCtrl::FreeKeyWordsFromFile( void ) {
401         for ( int i = 0; i < keyWordsFromFile.Num(); i++ ) {
402                 Mem_Free( const_cast<char *>( keyWordsFromFile[i].keyWord ) );
403                 Mem_Free( const_cast<char *>( keyWordsFromFile[i].description ) );
404         }
405         keyWordsFromFile.Clear();
406 }
407
408 /*
409 ================
410 CSyntaxRichEditCtrl::SetDefaultColor
411 ================
412 */
413 void CSyntaxRichEditCtrl::SetDefaultColor( const COLORREF color ) {
414         defaultColor = color;
415 }
416
417 /*
418 ================
419 CSyntaxRichEditCtrl::SetCommentColor
420 ================
421 */
422 void CSyntaxRichEditCtrl::SetCommentColor( const COLORREF color ) {
423         singleLineCommentColor = color;
424         multiLineCommentColor = color;
425 }
426
427 /*
428 ================
429 CSyntaxRichEditCtrl::SetStringColor
430 ================
431 */
432 void CSyntaxRichEditCtrl::SetStringColor( const COLORREF color, const COLORREF altColor ) {
433         stringColor[0] = color;
434         if ( altColor == -1 ) {
435                 stringColor[1] = color;
436         } else {
437                 stringColor[1] = altColor;
438         }
439 }
440
441 /*
442 ================
443 CSyntaxRichEditCtrl::SetLiteralColor
444 ================
445 */
446 void CSyntaxRichEditCtrl::SetLiteralColor( const COLORREF color ) {
447         literalColor = color;
448 }
449
450 /*
451 ================
452 CSyntaxRichEditCtrl::SetObjectMemberCallback
453 ================
454 */
455 void CSyntaxRichEditCtrl::SetObjectMemberCallback( objectMemberCallback_t callback ) {
456         GetObjectMembers = callback;
457 }
458
459 /*
460 ================
461 CSyntaxRichEditCtrl::SetFunctionParmCallback
462 ================
463 */
464 void CSyntaxRichEditCtrl::SetFunctionParmCallback( toolTipCallback_t callback ) {
465         GetFunctionParms = callback;
466 }
467
468 /*
469 ================
470 CSyntaxRichEditCtrl::SetToolTipCallback
471 ================
472 */
473 void CSyntaxRichEditCtrl::SetToolTipCallback( toolTipCallback_t callback ) {
474         GetToolTip = callback;
475 }
476
477 /*
478 ================
479 CSyntaxRichEditCtrl::SetCaseSensitive
480 ================
481 */
482 void CSyntaxRichEditCtrl::SetCaseSensitive( bool caseSensitive ) {
483         this->caseSensitive = caseSensitive;
484 }
485
486 /*
487 ================
488 CSyntaxRichEditCtrl::AllowPathNames
489 ================
490 */
491 void CSyntaxRichEditCtrl::AllowPathNames( bool allow ) {
492         allowPathNames = allow;
493 }
494
495 /*
496 ================
497 CSyntaxRichEditCtrl::EnableKeyWordAutoCompletion
498 ================
499 */
500 void CSyntaxRichEditCtrl::EnableKeyWordAutoCompletion( bool enable ) {
501         keyWordAutoCompletion = enable;
502 }
503
504 /*
505 ================
506 CSyntaxRichEditCtrl::GetVisibleRange
507 ================
508 */
509 CHARRANGE CSyntaxRichEditCtrl::GetVisibleRange( void ) const {
510         RECT rectArea;
511         int firstLine, lastLine;
512         CHARRANGE range;
513
514         firstLine = GetFirstVisibleLine();
515         GetClientRect( &rectArea );
516         lastLine = firstLine + ( rectArea.bottom / ( defaultCharFormat.yHeight / 20 ) );
517         
518         if ( lastLine >= GetLineCount() ) {
519                 lastLine = GetLineCount() - 1;
520         }
521         range.cpMin = LineIndex( firstLine );
522         if ( range.cpMin < 0 ) {
523                 range.cpMin = 0;
524         }
525         range.cpMax = LineIndex( lastLine );
526         if ( range.cpMax == -1 ) {
527                 range.cpMax = range.cpMin + LineLength( range.cpMin );
528         } else {
529                 range.cpMax += LineLength( range.cpMax );
530         }
531         if ( range.cpMax >= GetTextLength() ) {
532                 range.cpMax = GetTextLength() - 1;
533         }
534         return range;
535 }
536
537 /*
538 ================
539 CSyntaxRichEditCtrl::SetDefaultFont
540 ================
541 */
542 void CSyntaxRichEditCtrl::SetDefaultFont( int startCharIndex, int endCharIndex ) {
543         tom::ITextRange *range;
544
545         updateSyntaxHighlighting = false;
546
547         m_TextDoc->Range( startCharIndex, endCharIndex, &range );
548
549         m_TextDoc->Undo( tom::tomSuspend, NULL );
550         range->put_Font( m_DefaultFont );
551         m_TextDoc->Undo( tom::tomResume, NULL );
552
553         range->Release();
554
555         updateSyntaxHighlighting = true;
556 }
557
558 /*
559 ================
560 CSyntaxRichEditCtrl::SetColor
561 ================
562 */
563 void CSyntaxRichEditCtrl::SetColor( int startCharIndex, int endCharIndex, COLORREF foreColor, COLORREF backColor, bool bold ) {
564         tom::ITextRange *range;
565         tom::ITextFont *font;
566         long prop;
567
568         updateSyntaxHighlighting = false;
569
570         m_TextDoc->Range( startCharIndex, endCharIndex, &range );
571         range->get_Font( &font );
572
573         m_TextDoc->Undo( tom::tomSuspend, &prop );
574         font->put_ForeColor( foreColor );
575         m_TextDoc->Undo( tom::tomResume, &prop );
576
577         m_TextDoc->Undo( tom::tomSuspend, &prop );
578         font->put_BackColor( backColor );
579         m_TextDoc->Undo( tom::tomResume, &prop );
580
581         m_TextDoc->Undo( tom::tomSuspend, &prop );
582         font->put_Bold( bold ? tom::tomTrue : tom::tomFalse );
583         m_TextDoc->Undo( tom::tomResume, &prop );
584
585         font->Release();
586         range->Release();
587
588         updateSyntaxHighlighting = true;
589 }
590
591 /*
592 ================
593 CSyntaxRichEditCtrl::GetForeColor
594 ================
595 */
596 COLORREF CSyntaxRichEditCtrl::GetForeColor( int charIndex ) const {
597         tom::ITextRange *range;
598         tom::ITextFont *font;
599         long foreColor;
600
601         m_TextDoc->Range( charIndex, charIndex, &range );
602         range->get_Font( &font );
603
604         font->get_BackColor( &foreColor );
605
606         font->Release();
607         range->Release();
608
609         return foreColor;
610 }
611
612 /*
613 ================
614 CSyntaxRichEditCtrl::GetBackColor
615 ================
616 */
617 COLORREF CSyntaxRichEditCtrl::GetBackColor( int charIndex ) const {
618         tom::ITextRange *range;
619         tom::ITextFont *font;
620         long backColor;
621
622         m_TextDoc->Range( charIndex, charIndex, &range );
623         range->get_Font( &font );
624
625         font->get_BackColor( &backColor );
626
627         font->Release();
628         range->Release();
629
630         return backColor;
631 }
632
633 /*
634 ================
635 CSyntaxRichEditCtrl::HighlightSyntax
636
637   Update the syntax highlighting for the given character range.
638 ================
639 */
640 void CSyntaxRichEditCtrl::HighlightSyntax( int startCharIndex, int endCharIndex ) {
641         int c, t, line, charIndex, textLength, syntaxStart, keyWordLength, keyWordIndex;
642         const char *keyWord;
643         CHARRANGE visRange;
644         CString text;
645
646         // get text length
647         GetTextRange( 0, GetTextLength(), text );
648         textLength = text.GetLength();
649
650         // make sure the indexes are within bounds
651         if ( startCharIndex < 0 ) {
652                 startCharIndex = 0;
653         }
654         if ( endCharIndex < 0 ) {
655                 endCharIndex = textLength - 1;
656         } else if ( endCharIndex >= textLength ) {
657                 endCharIndex = textLength - 1;
658         }
659
660         // move the start index to the beginning of the line
661         for ( ; startCharIndex > 0; startCharIndex-- ) {
662                 if ( idStr::CharIsNewLine( text[startCharIndex-1] ) ) {
663                         break;
664                 }
665         }
666
667         // move the end index to the end of the line
668         for ( ; endCharIndex < textLength - 1; endCharIndex++ ) {
669                 if ( idStr::CharIsNewLine( text[endCharIndex+1] ) ) {
670                         break;
671                 }
672         }
673
674         // get the visible char range
675         visRange = GetVisibleRange();
676
677         // never update beyond the visible range
678         if ( startCharIndex < visRange.cpMin ) {
679                 SetColor( startCharIndex, visRange.cpMin - 1, SRE_COLOR_BLACK, INVALID_BACK_COLOR, false );
680                 startCharIndex = visRange.cpMin;
681         }
682         if ( visRange.cpMax < endCharIndex ) {
683                 SetColor( visRange.cpMax, endCharIndex, SRE_COLOR_BLACK, INVALID_BACK_COLOR, false );
684                 endCharIndex = visRange.cpMax;
685                 if ( endCharIndex >= textLength ) {
686                         endCharIndex = textLength - 1;
687                 }
688         }
689
690         // test if the start index is inside a multi-line comment
691         if ( startCharIndex > 0 ) {
692                 // multi-line comments have a slightly different background color
693                 if ( GetBackColor( startCharIndex-1 ) == MULTILINE_COMMENT_BACK_COLOR ) {
694                         for( ; startCharIndex > 0; startCharIndex-- ) {
695                                 if ( text[startCharIndex] == '/' && text[startCharIndex+1] == '*' ) {
696                                         break;
697                                 }
698                         }
699                 }
700         }
701
702         // test if the end index is inside a multi-line comment
703         if ( endCharIndex < textLength - 1 ) {
704                 // multi-line comments have a slightly different background color
705                 if ( GetBackColor( endCharIndex+1 ) == MULTILINE_COMMENT_BACK_COLOR ) {
706                         for( endCharIndex++; endCharIndex < textLength - 1; endCharIndex++ ) {
707                                 if ( text[endCharIndex-1] == '*' && text[endCharIndex] == '/' ) {
708                                         break;
709                                 }
710                         }
711                 }
712         }
713
714         line = 0;
715         stringColorLine = -1;
716         stringColorIndex = 0;
717
718         // set the default color
719         SetDefaultFont( startCharIndex, endCharIndex + 1 );
720
721         // syntax based colors
722         for( charIndex = startCharIndex; charIndex <= endCharIndex; charIndex++ ) {
723
724                 t = charType[text[charIndex]];
725                 switch( t ) {
726                         case CT_WHITESPACE: {
727                                 if ( idStr::CharIsNewLine( text[charIndex] ) ) {
728                                         line++;
729                                 }
730                                 break;
731                         }
732                         case CT_COMMENT: {
733                                 c = text[charIndex+1];
734                                 if ( c == '/' ) {
735                                         // single line comment
736                                         syntaxStart = charIndex;
737                                         for ( charIndex += 2; charIndex < textLength; charIndex++ ) {
738                                                 if ( idStr::CharIsNewLine( text[charIndex] ) ) {
739                                                         break;
740                                                 }
741                                         }
742                                         SetColor( syntaxStart, charIndex + 1, singleLineCommentColor, DEFAULT_BACK_COLOR, false );
743                                 } else if ( c == '*' ) {
744                                         // multi-line comment
745                                         syntaxStart = charIndex;
746                                         for ( charIndex += 2; charIndex < textLength; charIndex++ ) {
747                                                 if ( text[charIndex] == '*' && text[charIndex+1] == '/' ) {
748                                                         break;
749                                                 }
750                                         }
751                                         charIndex++;
752                                         SetColor( syntaxStart, charIndex + 1, multiLineCommentColor, MULTILINE_COMMENT_BACK_COLOR, false );
753                                 }
754                                 break;
755                         }
756                         case CT_STRING: {
757                                 if ( line != stringColorLine ) {
758                                         stringColorLine = line;
759                                         stringColorIndex = 0;
760                                 }
761                                 syntaxStart = charIndex;
762                                 for ( charIndex++; charIndex < textLength; charIndex++ ) {
763                                         c = text[charIndex];
764                                         if ( charType[c] == CT_STRING && text[charIndex-1] != '\\' ) {
765                                                 break;
766                                         }
767                                         if ( idStr::CharIsNewLine( c ) ) {
768                                                 line++;
769                                                 break;
770                                         }
771                                 }
772                                 SetColor( syntaxStart, charIndex + 1, stringColor[stringColorIndex], DEFAULT_BACK_COLOR, false );
773                                 stringColorIndex ^= 1;
774                                 break;
775                         }
776                         case CT_LITERAL: {
777                                 syntaxStart = charIndex;
778                                 for ( charIndex++; charIndex < textLength; charIndex++ ) {
779                                         c = text[charIndex];
780                                         if ( charType[c] == CT_LITERAL && text[charIndex-1] != '\\' ) {
781                                                 break;
782                                         }
783                                         if ( idStr::CharIsNewLine( c ) ) {
784                                                 line++;
785                                                 break;
786                                         }
787                                 }
788                                 SetColor( syntaxStart, charIndex + 1, literalColor, DEFAULT_BACK_COLOR, false );
789                                 break;
790                         }
791                         case CT_NUMBER: {
792                                 break;
793                         }
794                         case CT_NAME: {
795                                 syntaxStart = charIndex;
796                                 keyWord = ((const char *)text) + charIndex;
797                                 for ( charIndex++; charIndex < textLength; charIndex++ ) {
798                                         c = text[charIndex];
799                                         t = charType[c];
800                                         if ( t != CT_NAME && t != CT_NUMBER ) {
801                                                 // allow path names
802                                                 if ( !allowPathNames || ( c != '/' && c != '\\' && c != '.' ) ) {
803                                                         break;
804                                                 }
805                                         }
806                                 }
807                                 keyWordLength = charIndex - syntaxStart;
808                                 keyWordIndex = FindKeyWord( keyWord, keyWordLength );
809                                 if ( keyWordIndex != -1 ) {
810                                         SetColor( syntaxStart, syntaxStart + keyWordLength, keyWordColors[keyWordIndex], DEFAULT_BACK_COLOR, false );
811                                 }
812                                 break;
813                         }
814                         case CT_PUNCTUATION: {
815                                 break;
816                         }
817                 }
818         }
819
820         // show braced section
821         BracedSectionShow();
822 }
823
824 /*
825 ================
826 CSyntaxRichEditCtrl::UpdateVisibleRange
827
828   Updates the visible character range if it is not yet properly highlighted.
829 ================
830 */
831 void CSyntaxRichEditCtrl::UpdateVisibleRange( void ) {
832         CHARRANGE visRange;
833         tom::ITextRange *range;
834         tom::ITextFont *font;
835         long backColor;
836         bool update = false;
837
838         if ( !updateSyntaxHighlighting ) {
839                 return;
840         }
841
842         visRange = GetVisibleRange();
843
844         m_TextDoc->Range( visRange.cpMin, visRange.cpMax, &range );
845         range->get_End( &visRange.cpMax );
846
847         range->get_Font( &font );
848
849         range->SetRange( visRange.cpMin, visRange.cpMin );
850         while( 1 ) {
851                 range->get_Start( &visRange.cpMin );
852                 if ( visRange.cpMin >= visRange.cpMax ) {
853                         break;
854                 }
855                 font->get_BackColor( &backColor );
856                 if ( backColor == INVALID_BACK_COLOR ) {
857                         update = true;
858                         break;
859                 }
860                 if ( range->Move( tom::tomCharFormat, 1, NULL ) != S_OK ) {
861                         break;
862                 }
863         }
864
865         font->Release();
866         range->Release();
867
868         if ( update ) {
869                 HighlightSyntax( visRange.cpMin, visRange.cpMax - 1 );
870         }
871 }
872
873 /*
874 ================
875 CSyntaxRichEditCtrl::GetCursorPos
876 ================
877 */
878 void CSyntaxRichEditCtrl::GetCursorPos( int &line, int &column, int &character ) const {
879         long start, end;
880         char buffer[MAX_STRING_CHARS];
881
882         GetSel( start, end );
883         line = LineFromChar( start );
884         start -= LineIndex( line );
885         GetLine( line, buffer, sizeof( buffer ) );
886         for ( column = 1, character = 0; character < start; character++ ) {
887                 if ( idStr::CharIsTab( buffer[character] ) ) {
888                         column += TAB_SIZE;
889                         column -= column % TAB_SIZE;
890                 } else {
891                         column++;
892                 }
893         }
894         character++;
895 }
896
897 /*
898 ================
899 CSyntaxRichEditCtrl::GetText
900 ================
901 */
902 void CSyntaxRichEditCtrl::GetText( idStr &text ) const {
903         GetText( text, 0, GetTextLength() );
904 }
905
906 /*
907 ================
908 CSyntaxRichEditCtrl::GetText
909 ================
910 */
911 void CSyntaxRichEditCtrl::GetText( idStr &text, int startCharIndex, int endCharIndex ) const {
912         tom::ITextRange *range;
913         BSTR bstr;
914         USES_CONVERSION;
915
916         m_TextDoc->Range( startCharIndex, endCharIndex, &range );
917         range->get_Text( &bstr );
918         text = W2A( bstr );
919         range->Release();
920         text.StripTrailingOnce( "\r" );         // remove last carriage return which is always added to a tom::ITextRange
921 }
922
923 /*
924 ================
925 CSyntaxRichEditCtrl::SetText
926 ================
927 */
928 void CSyntaxRichEditCtrl::SetText( const char *text ) {
929         SetSel( 0, -1 );
930         ReplaceSel( text, FALSE );
931         SetSel( 0, 0 );
932 }
933
934 /*
935 ================
936 CSyntaxRichEditCtrl::FindNext
937 ================
938 */
939 bool CSyntaxRichEditCtrl::FindNext( const char *find, bool matchCase, bool matchWholeWords, bool searchForward ) {
940         long selStart, selEnd, flags, search, length, start;
941         tom::ITextRange *range;
942
943         if ( find[0] == '\0' ) {
944                 return false;
945         }
946
947         GetSel( selStart, selEnd );
948
949         flags = 0;
950         flags |= matchCase ? tom::tomMatchCase : 0;
951         flags |= matchWholeWords ? tom::tomMatchWord : 0;
952
953         if ( searchForward ) {
954                 m_TextDoc->Range( selEnd, GetTextLength(), &range );
955                 search = GetTextLength() - selEnd;
956         } else {
957                 m_TextDoc->Range( 0, selStart, &range );
958                 search = -selStart;
959         }
960
961         if ( range->FindShit( A2BSTR(find), search, flags, &length ) == S_OK ) {
962
963                 m_TextDoc->Freeze( NULL );
964
965                 range->get_Start( &start );
966                 range->Release();
967
968                 SetSel( start, start + length );
969
970                 int line = Max( (int) LineFromChar( start ) - 5, 0 );
971                 LineScroll( line - GetFirstVisibleLine(), 0 );
972
973                 UpdateVisibleRange();
974
975                 m_TextDoc->Unfreeze( NULL );
976                 return true;
977         } else {
978                 range->Release();
979                 return false;
980         }
981 }
982
983 /*
984 ================
985 CSyntaxRichEditCtrl::ReplaceAll
986 ================
987 */
988 int CSyntaxRichEditCtrl::ReplaceAll( const char *find, const char *replace, bool matchCase, bool matchWholeWords ) {
989         long selStart, selEnd, flags, search, length, start;
990         int numReplaced;
991         tom::ITextRange *range;
992         CComBSTR bstr( find );
993
994         if ( find[0] == '\0' ) {
995                 return 0;
996         }
997
998         m_TextDoc->Freeze( NULL );
999
1000         GetSel( selStart, selEnd );
1001
1002         flags = 0;
1003         flags |= matchCase ? tom::tomMatchCase : 0;
1004         flags |= matchWholeWords ? tom::tomMatchWord : 0;
1005
1006         m_TextDoc->Range( 0, GetTextLength(), &range );
1007         search = GetTextLength();
1008
1009         numReplaced = 0;
1010         while( range->FindShit( bstr, search, flags, &length ) == S_OK ) {
1011                 range->get_Start( &start );
1012                 ReplaceText( start, start + length, replace );
1013                 numReplaced++;
1014         }
1015
1016         range->Release();
1017
1018         m_TextDoc->Unfreeze( NULL );
1019
1020         return numReplaced;
1021 }
1022
1023 /*
1024 ================
1025 CSyntaxRichEditCtrl::ReplaceText
1026 ================
1027 */
1028 void CSyntaxRichEditCtrl::ReplaceText( int startCharIndex, int endCharIndex, const char *replace ) {
1029         tom::ITextRange *range;
1030         CComBSTR bstr( replace );
1031
1032         m_TextDoc->Range( startCharIndex, endCharIndex, &range );
1033         range->put_Text( bstr );
1034         range->Release();
1035 }
1036
1037 /*
1038 ================
1039 CSyntaxRichEditCtrl::AutoCompleteInsertText
1040 ================
1041 */
1042 void CSyntaxRichEditCtrl::AutoCompleteInsertText( void ) {
1043         long selStart, selEnd;
1044         int index;
1045
1046         index = autoCompleteListBox.GetCurSel();
1047         if ( index >= 0 ) {
1048                 CString text;
1049                 autoCompleteListBox.GetText( index, text );
1050                 GetSel( selStart, selEnd );
1051                 selStart = autoCompleteStart;
1052                 SetSel( selStart, selEnd );
1053                 ReplaceSel( text, TRUE );
1054         }
1055 }
1056
1057 /*
1058 ================
1059 CSyntaxRichEditCtrl::AutoCompleteUpdate
1060 ================
1061 */
1062 void CSyntaxRichEditCtrl::AutoCompleteUpdate( void ) {
1063         long selStart, selEnd;
1064         int index;
1065         idStr text;
1066
1067         GetSel( selStart, selEnd );
1068         GetText( text, autoCompleteStart, selStart );
1069         index = autoCompleteListBox.FindString( -1, text );
1070         if ( index >= 0 && index < autoCompleteListBox.GetCount() ) {
1071                 autoCompleteListBox.SetCurSel( index );
1072         }
1073 }
1074
1075 /*
1076 ================
1077 CSyntaxRichEditCtrl::AutoCompleteShow
1078 ================
1079 */
1080 void CSyntaxRichEditCtrl::AutoCompleteShow( int charIndex ) {
1081         CPoint point;
1082         CRect rect;
1083
1084         autoCompleteStart = charIndex;
1085         point = PosFromChar( charIndex );
1086         GetClientRect( rect );
1087         if ( point.y < rect.bottom - AUTOCOMPLETE_OFFSET - AUTOCOMPLETE_HEIGHT ) {
1088                 rect.top = point.y + AUTOCOMPLETE_OFFSET;
1089                 rect.bottom = point.y + AUTOCOMPLETE_OFFSET + AUTOCOMPLETE_HEIGHT;
1090         } else {
1091                 rect.top = point.y - AUTOCOMPLETE_HEIGHT;
1092                 rect.bottom = point.y;
1093         }
1094         rect.left = point.x;
1095         rect.right = point.x + AUTOCOMPLETE_WIDTH;
1096         autoCompleteListBox.MoveWindow( &rect );
1097         autoCompleteListBox.ShowWindow( TRUE );
1098         AutoCompleteUpdate();
1099 }
1100
1101 /*
1102 ================
1103 CSyntaxRichEditCtrl::AutoCompleteHide
1104 ================
1105 */
1106 void CSyntaxRichEditCtrl::AutoCompleteHide( void ) {
1107         autoCompleteStart = -1;
1108         autoCompleteListBox.ShowWindow( FALSE );
1109 }
1110
1111 /*
1112 ================
1113 CSyntaxRichEditCtrl::ToolTipShow
1114 ================
1115 */
1116 void CSyntaxRichEditCtrl::ToolTipShow( int charIndex, const char *string ) {
1117         CPoint point, p1, p2;
1118         CRect rect;
1119
1120         funcParmToolTipStart = charIndex;
1121         funcParmToolTip.SetWindowText( string );
1122         p1 = funcParmToolTip.PosFromChar( 0 );
1123         p2 = funcParmToolTip.PosFromChar( strlen( string ) - 1 );
1124         point = PosFromChar( charIndex );
1125         GetClientRect( rect );
1126         if ( point.y < rect.bottom - FUNCPARMTOOLTIP_OFFSET - FUNCPARMTOOLTIP_HEIGHT ) {
1127                 rect.top = point.y + FUNCPARMTOOLTIP_OFFSET;
1128                 rect.bottom = point.y + FUNCPARMTOOLTIP_OFFSET + FUNCPARMTOOLTIP_HEIGHT;
1129         } else {
1130                 rect.top = point.y - FUNCPARMTOOLTIP_HEIGHT;
1131                 rect.bottom = point.y;
1132         }
1133         rect.left = point.x;
1134         rect.right = point.x + FUNCPARMTOOLTIP_WIDTH + p2.x - p1.x;
1135         funcParmToolTip.MoveWindow( &rect );
1136         funcParmToolTip.ShowWindow( TRUE );
1137 }
1138
1139 /*
1140 ================
1141 CSyntaxRichEditCtrl::ToolTipHide
1142 ================
1143 */
1144 void CSyntaxRichEditCtrl::ToolTipHide( void ) {
1145         funcParmToolTipStart = -1;
1146         funcParmToolTip.ShowWindow( FALSE );
1147 }
1148
1149 /*
1150 ================
1151 CSyntaxRichEditCtrl::BracedSectionStart
1152 ================
1153 */
1154 bool CSyntaxRichEditCtrl::BracedSectionStart( char braceStartChar, char braceEndChar ) {
1155         long selStart, selEnd;
1156         int brace, i;
1157         idStr text;
1158
1159         GetSel( selStart, selEnd );
1160         GetText( text, 0, GetTextLength() );
1161
1162         for ( brace = 1, i = selStart; i < text.Length(); i++ ) {
1163                 if ( text[i] == braceStartChar ) {
1164                         brace++;
1165                 } else if ( text[i] == braceEndChar ) {
1166                         brace--;
1167                         if ( brace == 0 ) {
1168                                 break;
1169                         }
1170                 }
1171         }
1172         if ( brace == 0 ) {
1173                 bracedSection[0] = selStart - 1;
1174                 bracedSection[1] = i;
1175                 BracedSectionShow();
1176         }
1177
1178         return ( brace == 0 );
1179 }
1180
1181 /*
1182 ================
1183 CSyntaxRichEditCtrl::BracedSectionEnd
1184 ================
1185 */
1186 bool CSyntaxRichEditCtrl::BracedSectionEnd( char braceStartChar, char braceEndChar ) {
1187         long selStart, selEnd;
1188         int brace, i;
1189         idStr text;
1190
1191         GetSel( selStart, selEnd );
1192         GetText( text, 0, GetTextLength() );
1193
1194         for ( brace = 1, i = Min( selStart-2, (long)text.Length()-1 ); i >= 0; i-- ) {
1195                 if ( text[i] == braceStartChar ) {
1196                         brace--;
1197                         if ( brace == 0 ) {
1198                                 break;
1199                         }
1200                 } else if ( text[i] == braceEndChar ) {
1201                         brace++;
1202                 }
1203         }
1204
1205         if ( brace == 0 ) {
1206                 bracedSection[0] = i;
1207                 bracedSection[1] = selStart - 1;
1208                 BracedSectionAdjustEndTabs();
1209                 BracedSectionShow();
1210         }
1211
1212         return ( brace == 0 );
1213 }
1214
1215 /*
1216 ================
1217 CSyntaxRichEditCtrl::BracedSectionAdjustEndTabs
1218 ================
1219 */
1220 void CSyntaxRichEditCtrl::BracedSectionAdjustEndTabs( void ) {
1221         int line, lineIndex, length, column, numTabs, i;
1222         char buffer[1024];
1223         idStr text;
1224
1225         line = LineFromChar( bracedSection[0] );
1226         length = GetLine( line, buffer, sizeof( buffer ) );
1227         for ( numTabs = 0; numTabs < length; numTabs++ ) {
1228                 if ( !idStr::CharIsTab( buffer[numTabs] ) ) {
1229                         break;
1230                 }
1231                 text.Append( '\t' );
1232         }
1233
1234         line = LineFromChar( bracedSection[1] );
1235         lineIndex = LineIndex( line );
1236         length = GetLine( line, buffer, sizeof( buffer ) );
1237         column = bracedSection[1] - lineIndex;
1238         for ( i = 0; i < column; i++ ) {
1239                 if ( charType[buffer[i]] != CT_WHITESPACE ) {
1240                         return;
1241                 }
1242         }
1243
1244         ReplaceText( lineIndex, lineIndex + column, text );
1245
1246         bracedSection[1] += numTabs - column;
1247         SetSel( bracedSection[1]+1, bracedSection[1]+1 );
1248 }
1249
1250 /*
1251 ================
1252 CSyntaxRichEditCtrl::BracedSectionShow
1253 ================
1254 */
1255 void CSyntaxRichEditCtrl::BracedSectionShow( void ) {
1256         for ( int i = 0; i < 2; i++ ) {
1257                 if ( bracedSection[i] >= 0 ) {
1258                         SetColor( bracedSection[i], bracedSection[i] + 1, braceHighlightColor, DEFAULT_BACK_COLOR, true );
1259                 }
1260         }
1261 }
1262
1263 /*
1264 ================
1265 CSyntaxRichEditCtrl::BracedSectionHide
1266 ================
1267 */
1268 void CSyntaxRichEditCtrl::BracedSectionHide( void ) {
1269         for ( int i = 0; i < 2; i++ ) {
1270                 if ( bracedSection[i] >= 0 ) {
1271                         SetColor( bracedSection[i], bracedSection[i] + 1, defaultColor, DEFAULT_BACK_COLOR, false );
1272                         bracedSection[i] = -1;
1273                 }
1274         }
1275 }
1276
1277 /*
1278 ================
1279 CSyntaxRichEditCtrl::GetNameBeforeCurrentSelection
1280 ================
1281 */
1282 bool CSyntaxRichEditCtrl::GetNameBeforeCurrentSelection( CString &name, int &charIndex ) const {
1283         long selStart, selEnd;
1284         int line, column, length;
1285         char buffer[1024];
1286
1287         GetSel( selStart, selEnd );
1288         charIndex = selStart;
1289         line = LineFromChar( selStart );
1290         length = GetLine( line, buffer, sizeof( buffer ) );
1291         column = selStart - LineIndex( line ) - 1;
1292         do {
1293                 buffer[column--] = '\0';
1294         } while( charType[buffer[column]] == CT_WHITESPACE );
1295         for ( length = 0; length < column; length++ ) {
1296                 if ( charType[buffer[column-length-1]] != CT_NAME ) {
1297                         break;
1298                 }
1299         }
1300         if ( length > 0 ) {
1301                 name = buffer + column - length;
1302                 return true;
1303         }
1304         return false;
1305 }
1306
1307 /*
1308 ================
1309 CSyntaxRichEditCtrl::GetNameForMousePosition
1310 ================
1311 */
1312 bool CSyntaxRichEditCtrl::GetNameForMousePosition( idStr &name ) const {
1313         int charIndex, startCharIndex, endCharIndex, type;
1314         idStr text;
1315
1316         charIndex = CharFromPos( mousePoint );
1317
1318         for ( startCharIndex = charIndex; startCharIndex > 0; startCharIndex-- ) {
1319                 GetText( text, startCharIndex - 1, startCharIndex );
1320                 type = charType[text[0]];
1321                 if ( type != CT_NAME && type != CT_NUMBER ) {
1322                         break;
1323                 }
1324         }
1325
1326         for ( endCharIndex = charIndex; endCharIndex < GetTextLength(); endCharIndex++ ) {
1327                 GetText( text, endCharIndex, endCharIndex + 1 );
1328                 type = charType[text[0]];
1329                 if ( type != CT_NAME && type != CT_NUMBER ) {
1330                         break;
1331                 }
1332         }
1333
1334         GetText( name, startCharIndex, endCharIndex );
1335
1336         return ( endCharIndex > startCharIndex );
1337 }
1338
1339 /*
1340 ================
1341 CSyntaxRichEditCtrl::GoToLine
1342 ================
1343 */
1344 void CSyntaxRichEditCtrl::GoToLine( int line ) {
1345
1346         int index = LineIndex( line );
1347
1348         m_TextDoc->Freeze( NULL );
1349
1350         SetSel( index, index );
1351
1352         m_TextDoc->Unfreeze( NULL );
1353
1354         UpdateVisibleRange();
1355
1356         RedrawWindow();
1357 }
1358
1359 /*
1360 ================
1361 CSyntaxRichEditCtrl::OnToolHitTest
1362 ================
1363 */
1364 int CSyntaxRichEditCtrl::OnToolHitTest( CPoint point, TOOLINFO* pTI ) const {
1365         CRichEditCtrl::OnToolHitTest( point, pTI );
1366
1367     pTI->hwnd = GetSafeHwnd();
1368     pTI->uId = (UINT_PTR)GetSafeHwnd();
1369         pTI->uFlags |= TTF_IDISHWND;
1370     pTI->lpszText = LPSTR_TEXTCALLBACK;
1371     pTI->rect = CRect( point, point );
1372         pTI->rect.right += 100;
1373         pTI->rect.bottom += 20;
1374     return pTI->uId;
1375 }
1376
1377 /*
1378 ================
1379 CSyntaxRichEditCtrl::OnToolTipNotify
1380 ================
1381 */
1382 BOOL CSyntaxRichEditCtrl::OnToolTipNotify( UINT id, NMHDR *pNMHDR, LRESULT *pResult ) {
1383         TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
1384         TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
1385
1386         *pResult = 0;
1387
1388         idStr name;
1389
1390         if ( GetNameForMousePosition( name ) ) {
1391                 CString toolTip;
1392
1393                 if ( GetToolTip == NULL || !GetToolTip( name, toolTip ) ) {
1394
1395                         int keyWordIndex = FindKeyWord( name, name.Length() );
1396
1397                         if ( keyWordIndex != -1 && keyWords[keyWordIndex].description[0] != '\0' ) {
1398                                 toolTip = keyWords[keyWordIndex].description;
1399                         } else {
1400                                 toolTip = name.c_str();
1401                         }
1402                 }
1403
1404                 AFX_MODULE_THREAD_STATE *state = AfxGetModuleThreadState();
1405
1406                 // set max tool tip width to enable multi-line tool tips using "\r\n" for line breaks
1407                 state->m_pToolTip->SetMaxTipWidth( 500 );
1408
1409                 // set the number of milliseconds after which the tool tip automatically disappears
1410                 state->m_pToolTip->SetDelayTime( TTDT_AUTOPOP, 5000 + toolTip.GetLength() * 50 );
1411
1412 #ifndef _UNICODE
1413                 if( pNMHDR->code == TTN_NEEDTEXTA ) {
1414                         delete m_pchTip;
1415                         m_pchTip = new TCHAR[toolTip.GetLength() + 2];
1416                         lstrcpyn( m_pchTip, toolTip, toolTip.GetLength() + 1 );
1417                         pTTTW->lpszText = (WCHAR*)m_pchTip;
1418                 } else {
1419                         delete m_pwchTip;
1420                         m_pwchTip = new WCHAR[toolTip.GetLength() + 2];
1421                         _mbstowcsz( m_pwchTip, toolTip, toolTip.GetLength() + 1 );
1422                         pTTTW->lpszText = (WCHAR*)m_pwchTip;
1423                 }
1424 #else
1425                 if( pNMHDR->code == TTN_NEEDTEXTA ) {
1426                         delete m_pchTip;
1427                         m_pchTip = new TCHAR[toolTip.GetLength() + 2];
1428                         _wcstombsz( m_pchTip, toolTip, toolTip.GetLength() + 1 );
1429                         pTTTA->lpszText = (LPTSTR)m_pchTip;
1430                 } else {
1431                         delete m_pwchTip;
1432                         m_pwchTip = new WCHAR[toolTip.GetLength() + 2];
1433                         lstrcpyn( m_pwchTip, toolTip, toolTip.GetLength() + 1 );
1434                         pTTTA->lpszText = (LPTSTR) m_pwchTip;
1435                 }
1436 #endif
1437
1438                 return TRUE;
1439         }
1440         return FALSE;
1441 }
1442
1443 /*
1444 ================
1445 CSyntaxRichEditCtrl::OnGetDlgCode
1446 ================
1447 */
1448 UINT CSyntaxRichEditCtrl::OnGetDlgCode() {
1449         // get all keys, including tabs
1450         return DLGC_WANTALLKEYS | DLGC_WANTARROWS | DLGC_WANTCHARS | DLGC_WANTMESSAGE | DLGC_WANTTAB;
1451 }
1452
1453 /*
1454 ================
1455 CSyntaxRichEditCtrl::OnKeyDown
1456 ================
1457 */
1458 void CSyntaxRichEditCtrl::OnKeyDown( UINT nKey, UINT nRepCnt, UINT nFlags ) {
1459
1460         if ( m_TextDoc == NULL ) {
1461                 return;
1462         }
1463
1464         if ( autoCompleteStart >= 0 ) {
1465                 int sel;
1466
1467                 switch( nKey ) {
1468                         case VK_UP: {           // up arrow
1469                                 sel = Max( 0, autoCompleteListBox.GetCurSel() - 1 );
1470                                 autoCompleteListBox.SetCurSel( sel );
1471                                 return;
1472                         }
1473                         case VK_DOWN: {         // down arrow
1474                                 sel = Min( autoCompleteListBox.GetCount() - 1, autoCompleteListBox.GetCurSel() + 1 );
1475                                 autoCompleteListBox.SetCurSel( sel );
1476                                 return;
1477                         }
1478                         case VK_PRIOR: {        // page up key
1479                                 sel = Max( 0, autoCompleteListBox.GetCurSel() - 10 );
1480                                 autoCompleteListBox.SetCurSel( sel );
1481                                 return;
1482                         }
1483                         case VK_NEXT: {         // page down key
1484                                 sel = Min( autoCompleteListBox.GetCount() - 1, autoCompleteListBox.GetCurSel() + 10 );
1485                                 autoCompleteListBox.SetCurSel( sel );
1486                                 return;
1487                         }
1488                         case VK_HOME: {         // home key
1489                                 autoCompleteListBox.SetCurSel( 0 );
1490                                 return;
1491                         }
1492                         case VK_END: {
1493                                 autoCompleteListBox.SetCurSel( autoCompleteListBox.GetCount() - 1 );
1494                                 return;
1495                         }
1496                         case VK_RETURN:         // enter key
1497                         case VK_TAB: {          // tab key
1498                                 AutoCompleteInsertText();
1499                                 AutoCompleteHide();
1500                                 return;
1501                         }
1502                         case VK_LEFT:           // left arrow
1503                         case VK_RIGHT:          // right arrow
1504                         case VK_INSERT:         // insert key
1505                         case VK_DELETE: {       // delete key
1506                                 return;
1507                         }
1508                 }
1509         }
1510
1511         BracedSectionHide();
1512
1513         switch( nKey ) {
1514                 case VK_TAB: {          // multi-line tabs
1515                         long selStart, selEnd;
1516
1517                         GetSel( selStart, selEnd );
1518
1519                         // if multiple lines are selected add tabs to, or remove tabs from all of them
1520                         if ( selEnd > selStart ) {
1521                                 CString text;
1522
1523                                 text = GetSelText();
1524
1525                                 if ( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) {
1526                                         if ( idStr::CharIsTab( text[0] ) ) {
1527                                                 text.Delete( 0, 1 );
1528                                         }
1529                                         for ( int i = 0; i < text.GetLength() - 2; i++ ) {
1530                                                 if ( idStr::CharIsNewLine( text[i] ) ) {
1531                                                         do {
1532                                                                 i++;
1533                                                         } while( idStr::CharIsNewLine( text[i] ) );
1534                                                         if ( idStr::CharIsTab( text[i] ) ) {
1535                                                                 text.Delete( i, 1 );
1536                                                         }
1537                                                 }
1538                                         }
1539                                 } else {
1540                                         text.Insert( 0, '\t' );
1541                                         for ( int i = 0; i < text.GetLength() - 1; i++ ) {
1542                                                 if ( idStr::CharIsNewLine( text[i] ) ) {
1543                                                         do {
1544                                                                 i++;
1545                                                         } while( idStr::CharIsNewLine( text[i] ) );
1546                                                         text.Insert( i, '\t' );
1547                                                 }
1548                                         }
1549                                 }
1550
1551                                 ReplaceSel( text, TRUE );
1552                                 SetSel( selStart, selStart + text.GetLength() );
1553                         } else {
1554                                 ReplaceSel( "\t", TRUE );
1555                         }
1556                         return;
1557                 }
1558                 case VK_RETURN: {       // auto-indentation
1559                         long selStart, selEnd;
1560                         int line, length, numTabs, i;
1561                         char buffer[1024];
1562                         idStr text;
1563
1564                         GetSel( selStart, selEnd );
1565                         line = LineFromChar( selStart );
1566                         length = GetLine( line, buffer, sizeof( buffer ) );
1567                         for ( numTabs = 0; numTabs < length; numTabs++ ) {
1568                                 if ( !idStr::CharIsTab( buffer[numTabs] ) ) {
1569                                         break;
1570                                 }
1571                         }
1572                         bool first = true;
1573                         for ( i = numTabs; i < length; i++ ) {
1574                                 if ( buffer[i] == '{' ) {
1575                                         numTabs++;
1576                                         first = false;
1577                                 } else if ( buffer[i] == '}' && !first ) {
1578                                         numTabs--;
1579                                 }
1580                         }
1581                         text = "\r\n";
1582                         for ( i = 0; i < numTabs; i++ ) {
1583                                 text.Append( '\t' );
1584                         }
1585                         ReplaceSel( text, TRUE );
1586                         return;
1587                 }
1588         }
1589
1590         m_TextDoc->Freeze( NULL );
1591
1592         CRichEditCtrl::OnKeyDown( nKey, nRepCnt, nFlags );
1593
1594         UpdateVisibleRange();
1595
1596         m_TextDoc->Unfreeze( NULL );
1597 }
1598
1599 /*
1600 ================
1601 CSyntaxRichEditCtrl::OnChar
1602 ================
1603 */
1604 void CSyntaxRichEditCtrl::OnChar( UINT nChar, UINT nRepCnt, UINT nFlags ) {
1605
1606         if ( nChar == VK_TAB ) {
1607                 return; // tab is handle in OnKeyDown
1608         }
1609
1610         CRichEditCtrl::OnChar( nChar, nRepCnt, nFlags );
1611
1612         // if the auto-complete list box is up
1613         if ( autoCompleteStart >= 0 ) {
1614                 long selStart, selEnd;
1615
1616                 if ( charType[nChar] == CT_NAME ) {
1617                         AutoCompleteUpdate();
1618                         return;
1619                 } else if ( nChar == VK_BACK ) {
1620                         GetSel( selStart, selEnd );
1621                         if ( selStart > autoCompleteStart ) {
1622                                 AutoCompleteUpdate();
1623                         } else {
1624                                 AutoCompleteHide();
1625                         }
1626                         return;
1627                 } else {
1628                         AutoCompleteHide();
1629                 }
1630         }
1631
1632         // if the function parameter tool tip is up
1633         if ( funcParmToolTipStart >= 0 ) {
1634                 long selStart, selEnd;
1635
1636                 if ( nChar == ')' || nChar == VK_ESCAPE ) {
1637                         ToolTipHide();
1638                 } else if ( nChar == VK_BACK ) {
1639                         GetSel( selStart, selEnd );
1640                         if ( selStart < funcParmToolTipStart ) {
1641                                 ToolTipHide();
1642                         }
1643                 }
1644         }
1645
1646         // show keyword auto-completion
1647         if ( keyWordAutoCompletion && charType[nChar] == CT_NAME && funcParmToolTipStart < 0 ) {
1648                 long selStart, selEnd;
1649                 int line, column, length, i;
1650                 char buffer[1024];
1651
1652                 GetSel( selStart, selEnd );
1653                 line = LineFromChar( selStart );
1654                 length = GetLine( line, buffer, sizeof( buffer ) );
1655                 column = selStart - LineIndex( line );
1656                 if ( column <= 1 || charType[buffer[column-2]] == CT_WHITESPACE ) {
1657                         if ( column >= length-1 || charType[buffer[column]] == CT_WHITESPACE ) {
1658
1659                                 autoCompleteListBox.ResetContent();
1660                                 for ( i = 0; keyWords[i].keyWord; i++ ) {
1661                                         autoCompleteListBox.AddString( keyWords[i].keyWord );
1662                                 }
1663                                 AutoCompleteShow( selStart - 1 );
1664                         }
1665                 }
1666                 return;
1667         }
1668
1669         // highlight braced sections
1670         if ( nChar == '{' ) {
1671                 BracedSectionStart( '{', '}' );
1672         } else if ( nChar == '}' ) {
1673                 BracedSectionEnd( '{', '}' );
1674         } else if ( nChar == '(' ) {
1675                 BracedSectionStart( '(', ')' );
1676         } else if ( nChar == ')' ) {
1677                 BracedSectionEnd( '(', ')' );
1678         } else if ( nChar == '[' ) {
1679                 BracedSectionStart( '[', ']' );
1680         } else if ( nChar == ']' ) {
1681                 BracedSectionEnd( '[', ']' );
1682         } else if ( nChar == '<' ) {
1683                 BracedSectionStart( '<', '>' );
1684         } else if ( nChar == '>' ) {
1685                 BracedSectionEnd( '<', '>' );
1686         }
1687
1688         // show object member auto-completion
1689         if ( nChar == '.' && GetObjectMembers && funcParmToolTipStart < 0 ) {
1690                 int charIndex;
1691                 CString name;
1692
1693                 if ( GetNameBeforeCurrentSelection( name, charIndex ) ) {
1694                         autoCompleteListBox.ResetContent();
1695                         if ( GetObjectMembers( name, autoCompleteListBox ) ) {
1696                                 AutoCompleteShow( charIndex );
1697                         }
1698                 }
1699                 return;
1700         }
1701
1702         // show function parameter tool tip
1703         if ( nChar == '(' && GetFunctionParms ) {
1704                 int charIndex;
1705                 CString name;
1706
1707                 if ( GetNameBeforeCurrentSelection( name, charIndex ) ) {
1708                         CString parmString;
1709                         if ( GetFunctionParms( name, parmString ) ) {
1710                                 ToolTipShow( charIndex, parmString );
1711                         }
1712                 }
1713                 return;
1714         }
1715 }
1716
1717 /*
1718 ================
1719 CSyntaxRichEditCtrl::OnLButtonDown
1720 ================
1721 */
1722 void CSyntaxRichEditCtrl::OnLButtonDown( UINT nFlags, CPoint point ) {
1723
1724         if ( autoCompleteStart >= 0 ) {
1725                 AutoCompleteHide();
1726         }
1727
1728         BracedSectionHide();
1729
1730         CRichEditCtrl::OnLButtonDown( nFlags, point );
1731 }
1732
1733 /*
1734 ================
1735 CSyntaxRichEditCtrl::OnMouseWheel
1736 ================
1737 */
1738 BOOL CSyntaxRichEditCtrl::OnMouseWheel( UINT nFlags, short zDelta, CPoint pt ) {
1739         if ( autoCompleteStart >= 0 ) {
1740                 int sel;
1741
1742                 if ( zDelta > 0  ) {
1743                         sel = Max( 0, autoCompleteListBox.GetCurSel() - ( zDelta / WHEEL_DELTA ) );
1744                 } else {
1745                         sel = Min( autoCompleteListBox.GetCount() - 1, autoCompleteListBox.GetCurSel() - ( zDelta / WHEEL_DELTA ) );
1746                 }
1747                 autoCompleteListBox.SetCurSel( sel );
1748                 return TRUE;
1749         }
1750
1751         m_TextDoc->Freeze( NULL );
1752
1753         LineScroll( -3 * ( (int) zDelta ) / WHEEL_DELTA, 0 );
1754
1755         UpdateVisibleRange();
1756
1757         m_TextDoc->Unfreeze( NULL );
1758
1759         return TRUE;
1760 }
1761
1762 /*
1763 ================
1764 CSyntaxRichEditCtrl::OnMouseMove
1765 ================
1766 */
1767 void CSyntaxRichEditCtrl::OnMouseMove( UINT nFlags, CPoint point ) {
1768         CRichEditCtrl::OnMouseMove( nFlags, point );
1769
1770         if ( point != mousePoint ) {
1771                 mousePoint = point;
1772
1773                 // remove tool tip and activate the tool tip control, otherwise
1774                 // tool tips stop working until the mouse moves over another window first
1775                 AFX_MODULE_THREAD_STATE *state = AfxGetModuleThreadState();
1776                 state->m_pToolTip->Pop();
1777                 state->m_pToolTip->Activate( TRUE );
1778         }
1779 }
1780
1781 /*
1782 ================
1783 CSyntaxRichEditCtrl::OnSize
1784 ================
1785 */
1786 void CSyntaxRichEditCtrl::OnSize( UINT nType, int cx, int cy ) {
1787         m_TextDoc->Freeze( NULL );
1788
1789         CRichEditCtrl::OnSize( nType, cx, cy );
1790
1791         m_TextDoc->Unfreeze( NULL );
1792
1793         UpdateVisibleRange();
1794 }
1795
1796 /*
1797 ================
1798 CSyntaxRichEditCtrl::OnVScroll
1799 ================
1800 */
1801 void CSyntaxRichEditCtrl::OnVScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar ) {
1802         m_TextDoc->Freeze( NULL );
1803
1804         CRichEditCtrl::OnVScroll( nSBCode, nPos, pScrollBar );
1805
1806         SetFocus();
1807
1808         UpdateVisibleRange();
1809
1810         m_TextDoc->Unfreeze( NULL );
1811 }
1812
1813 /*
1814 ================
1815 CSyntaxRichEditCtrl::OnProtected
1816 ================
1817 */
1818 void CSyntaxRichEditCtrl::OnProtected( NMHDR *pNMHDR, LRESULT *pResult ) {
1819         ENPROTECTED* pEP = (ENPROTECTED*)pNMHDR;
1820
1821         *pResult = 0;
1822
1823         updateRange = pEP->chrg;
1824
1825         switch( pEP->msg ) {
1826                 case WM_MOUSEMOVE: {
1827                         break;
1828                 }
1829                 case WM_SETTEXT: {
1830                         updateRange.cpMin = pEP->chrg.cpMin;
1831                         updateRange.cpMax = pEP->chrg.cpMin + strlen( (LPCTSTR) pEP->lParam );
1832                         break;
1833                 }
1834                 case WM_CUT: {
1835                         break;
1836                 }
1837                 case WM_COPY: {
1838                         break;
1839                 }
1840                 case WM_PASTE: {
1841                         break;
1842                 }
1843                 case WM_CLEAR: {
1844                         break;
1845                 }
1846                 case WM_UNDO: {
1847                         break;
1848                 }
1849                 default: {
1850                         break;
1851                 }
1852         }
1853 }
1854
1855 /*
1856 ================
1857 CSyntaxRichEditCtrl::OnChange
1858 ================
1859 */
1860 void CSyntaxRichEditCtrl::OnChange() {
1861         long selStart, selEnd;
1862
1863         if ( !updateSyntaxHighlighting ) {
1864                 return;
1865         }
1866
1867         GetSel( selStart, selEnd );
1868         selStart = Min( selStart, updateRange.cpMin );
1869         selEnd = Max( selEnd, updateRange.cpMax );
1870
1871         HighlightSyntax( selStart, selEnd );
1872
1873         // send EN_CHANGE notification to parent window
1874         NMHDR pNMHDR;
1875         pNMHDR.hwndFrom = GetSafeHwnd();
1876         pNMHDR.idFrom = GetDlgCtrlID();
1877         pNMHDR.code = EN_CHANGE;
1878         GetParent()->SendMessage( WM_NOTIFY, ( EN_CHANGE << 16 ) | GetDlgCtrlID(), (LPARAM)&pNMHDR );
1879 }
1880
1881 /*
1882 ================
1883 CSyntaxRichEditCtrl::OnAutoCompleteListBoxChange
1884 ================
1885 */
1886 void CSyntaxRichEditCtrl::OnAutoCompleteListBoxChange() {
1887         // steal focus back from the auto-complete list box
1888         SetFocus();
1889 }
1890
1891 /*
1892 ================
1893 CSyntaxRichEditCtrl::OnAutoCompleteListBoxDblClk
1894 ================
1895 */
1896 void CSyntaxRichEditCtrl::OnAutoCompleteListBoxDblClk() {
1897         // steal focus back from the auto-complete list box
1898         SetFocus();
1899
1900         // insert current auto-complete selection
1901         AutoCompleteInsertText();
1902         AutoCompleteHide();
1903 }