Update the changelog
[opencv] / apps / Hawk / HawkView.cpp
1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                        Intel License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 //   * Redistribution's of source code must retain the above copyright notice,
20 //     this list of conditions and the following disclaimer.
21 //
22 //   * Redistribution's in binary form must reproduce the above copyright notice,
23 //     this list of conditions and the following disclaimer in the documentation
24 //     and/or other materials provided with the distribution.
25 //
26 //   * The name of Intel Corporation may not be used to endorse or promote products
27 //     derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //M*/// HawkView.cpp : implementation of the CHawkView class
41 //
42
43 #include "stdafx.h"
44 #include "Hawk.h"
45 #include "MainFrm.h"
46
47 #include "HawkDoc.h"
48 #include "HawkView.h"
49 #include "GotoLine.h"
50
51 #include <limits.h>
52 #include <assert.h>
53 #include <stdio.h>
54 #include "resource.h"
55
56 #include "windowsx.h"
57 #ifdef SubclassWindow
58 #undef SubclassWindow
59 #endif
60
61 #ifdef _DEBUG
62 #define new DEBUG_NEW
63 #undef THIS_FILE
64 static char THIS_FILE[] = __FILE__;
65 #endif
66
67 // Foward declarations of functions included in this code module:
68 LRESULT CALLBACK        GoToLineProc(HWND, UINT, WPARAM, LPARAM);
69 HGDIOBJ  CreateDIB( int width, int height, HDC dc );
70
71 /////////////////////////////////////////////////////////////////////////////
72 // CHawkView
73
74 IMPLEMENT_DYNCREATE(CHawkView, CEditView)
75
76 BEGIN_MESSAGE_MAP(CHawkView, CEditView)
77         //{{AFX_MSG_MAP(CHawkView)
78         ON_WM_CREATE()
79         ON_WM_KEYDOWN()
80         ON_WM_LBUTTONDOWN()
81         ON_WM_CONTEXTMENU()
82         ON_CONTROL_REFLECT(EN_CHANGE, OnChange)
83     ON_WM_CTLCOLOR_REFLECT()
84         ON_COMMAND(ID_UNTAB, OnUntab)
85         ON_COMMAND(ID_GO_HOME, OnGoHome)
86         ON_COMMAND(ID_GOTO_LINE, OnGotoLine)
87         ON_WM_TIMER()
88         //}}AFX_MSG_MAP
89         // Standard printing commands
90         ON_COMMAND(ID_FILE_PRINT, CEditView::OnFilePrint)
91         ON_COMMAND(ID_FILE_PRINT_DIRECT, CEditView::OnFilePrint)
92         ON_COMMAND(ID_FILE_PRINT_PREVIEW, CEditView::OnFilePrintPreview)
93 END_MESSAGE_MAP()
94
95 /////////////////////////////////////////////////////////////////////////////
96 // CHawkView construction/destruction
97
98 int CHawkView::m_instanceCount = 0;
99 HFONT     CHawkView::hFont = NULL;
100 HDC       CHawkView::hMemDC = NULL;
101 HGDIOBJ   CHawkView::hOldBmp = NULL;
102 ColorScheme CHawkView::Scheme;
103
104 CHawkView::CHawkView() : m_edit(GetEditCtrl()), MaxTextBuffer(1 << 17), 
105                                 LINE_BUF_SIZE(1000), TAB_SIZE(4)
106 {
107     Line = new char[LINE_BUF_SIZE];
108     Space = new char[LINE_BUF_SIZE];
109    
110     cTokens = 0;
111     maxTokens = (1 << 17)/sizeof(Token);
112     Tokens = (Token*)malloc( maxTokens * sizeof(Token) );
113     TextBuffer = (char*)malloc( MaxTextBuffer );
114     Disable_Update = 0;
115
116
117     if(!m_instanceCount)
118     {
119         CHawkApp* app = (CHawkApp*)AfxGetApp();
120
121         Scheme.background = app->GetProfileInt("Configuration", "background", RGB(222,220,200));
122         Scheme.clr[TOKEN_NORMAL]  = app->GetProfileInt("Configuration", "TOKEN_NORMAL", RGB(0,0,0));
123         Scheme.clr[TOKEN_KEYWORD] = app->GetProfileInt("Configuration", "TOKEN_KEYWORD", RGB(0,0,150));
124         Scheme.clr[TOKEN_COMMENT] = app->GetProfileInt("Configuration", "TOKEN_COMMENT", RGB(130,130,130));
125         Scheme.clr[TOKEN_NUMBER]  = app->GetProfileInt("Configuration", "TOKEN_NUMBER", RGB(150,100,0));
126         Scheme.clr[TOKEN_STRING]  = app->GetProfileInt("Configuration", "TOKEN_STRING", RGB(150,0,100));
127         Scheme.selbk = app->GetProfileInt("Configuration", "selbk", RGB(150,150,150));
128         Scheme.selfk = app->GetProfileInt("Configuration", "selfk", RGB(0,0,0));
129         Scheme.bkBrush = (HBRUSH)CreateSolidBrush( Scheme.background );
130         hFont = CreateFont( 0, // Height
131                             0, // Width
132                             0, // Escapement
133                             0, // Orientation
134                             FW_NORMAL, // Boldness
135                             0, // Italic
136                             0, // Underline
137                             0, // Strikeout
138                             DEFAULT_CHARSET,
139                             OUT_DEFAULT_PRECIS,
140                             CLIP_DEFAULT_PRECIS,
141                             DEFAULT_QUALITY,
142                             DEFAULT_PITCH | FF_MODERN,
143                             "Courier" );
144         hMemDC = CreateCompatibleDC( 0 );
145         hOldBmp = CreateDIB( GetSystemMetrics( SM_CXSCREEN ),
146                              GetSystemMetrics( SM_CYSCREEN ), hMemDC );
147         SelectObject( hMemDC, hFont );
148     }
149
150     m_instanceCount++;
151 }
152
153
154 CHawkView::~CHawkView()
155 {
156     ASSERT(m_instanceCount > 0);
157     m_instanceCount--;
158     if(!m_instanceCount)
159     {
160         DeleteObject( SelectObject( hMemDC, hOldBmp ));
161         DeleteDC( hMemDC );
162         DeleteObject( hFont );
163     }
164     
165     free( Line );
166     free( Space );
167     free( TextBuffer );
168     free( Tokens );
169
170 }
171
172
173 BOOL CHawkView::Customize()
174 {
175     HWND hEdit = m_hWnd;
176     SetWindowFont( hEdit, hFont, 0 );
177     int tab_size = TAB_SIZE * 4;
178     Edit_SetTabStops( hEdit, 1, &tab_size );
179     Edit_LimitText( hEdit, 0xffff );
180     Edit_FmtLines( hEdit, 1 );
181     return TRUE;
182 }
183
184 BOOL CHawkView::PreCreateWindow(CREATESTRUCT& cs)
185 {
186         // TODO: Modify the Window class or styles here by modifying
187         //  the CREATESTRUCT cs
188
189         return CEditView::PreCreateWindow(cs);
190 }
191
192 /////////////////////////////////////////////////////////////////////////////
193 // CHawkView drawing
194 void CHawkView::OnDraw(CDC* pDC)
195 {
196         CHawkDoc* pDoc = GetDocument();
197         ASSERT_VALID(pDoc);
198         // TODO: add draw code for native data here
199 }
200
201 /////////////////////////////////////////////////////////////////////////////
202 // CHawkView printing
203
204 BOOL CHawkView::OnPreparePrinting(CPrintInfo* pInfo)
205 {
206         // default preparation
207         return DoPreparePrinting(pInfo);
208 }
209
210 void CHawkView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
211 {
212         // TODO: add extra initialization before printing
213 }
214
215 void CHawkView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
216 {
217         // TODO: add cleanup after printing
218 }
219
220 /////////////////////////////////////////////////////////////////////////////
221 // CHawkView diagnostics
222
223 #ifdef _DEBUG
224 void CHawkView::AssertValid() const
225 {
226         CEditView::AssertValid();
227 }
228
229 void CHawkView::Dump(CDumpContext& dc) const
230 {
231         CEditView::Dump(dc);
232 }
233
234 CHawkDoc* CHawkView::GetDocument() // non-debug version is inline
235 {
236         ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CHawkDoc)));
237         return (CHawkDoc*)m_pDocument;
238 }
239 #endif //_DEBUG
240
241 /////////////////////////////////////////////////////////////////////////////
242 // CHawkView message handlers
243
244 int CHawkView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
245 {
246         if (CEditView::OnCreate(lpCreateStruct) == -1)
247                 return -1;
248
249     Customize();
250     return 0;
251 }
252
253 CString CHawkView::GetText()
254 {
255     int length = GetBufferLength() + 1;
256     char* buffer = new char[length];
257     m_edit.GetWindowText(buffer, length);
258     CString text = CString(buffer);
259     delete buffer;
260     return text;
261 }
262
263 void CHawkView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
264 {
265     CEditView::OnKeyDown(nChar, nRepCnt, nFlags);
266     UpdateCursorPos();
267 }
268
269 void CHawkView::UpdateCursorPos()
270 {
271     CStatusBar* pBar = ((CMainFrame*)AfxGetMainWnd())->GetStatusBar();
272     CString str;
273     POINT pt = GetCursorPos();
274     str.Format("%d", pt.x + 1);
275     pBar->SetPaneText(1, LPCTSTR(str));
276     str.Format("%d", pt.y + 1);
277     pBar->SetPaneText(2, LPCTSTR(str));
278
279     SetTimer(1, 500, 0); // Update the syntax tip
280 }
281
282 void CHawkView::OnLButtonDown(UINT nFlags, CPoint point) 
283 {
284     UpdateCursorPos();
285     
286         CEditView::OnLButtonDown(nFlags, point);
287 }
288
289 void CHawkView::OnContextMenu(CWnd* pWnd, CPoint point) 
290 {
291     CHawkApp* pApp = (CHawkApp*)AfxGetApp();
292     CMenu* pMenu = pApp->GetContextMenu();
293
294     pMenu->TrackPopupMenu(TPM_LEFTALIGN, point.x, point.y, pWnd);
295 }
296
297 BOOL CHawkView::OnCommand(WPARAM wParam, LPARAM lParam) 
298 {
299     if(HIWORD(wParam))
300     {
301         int index = HIWORD(wParam) - 1;
302         CHawkApp* pApp = (CHawkApp*)AfxGetApp();
303         CFuncDialog* pDlg = pApp->GetFuncDialog(index);
304         pDlg->SetItems(pApp->GetFunctions(index));
305         if(pDlg->DoModal() == IDOK)
306         {
307             CString str = pDlg->GetCurrentItem();
308             int p1, p2;
309             m_edit.GetSel(p1, p2);
310             m_edit.ReplaceSel(LPCTSTR(str), TRUE);
311         }
312     }
313
314         return CEditView::OnCommand(wParam, lParam);
315 }
316
317 LRESULT CHawkView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
318 {
319     LRESULT code = 0;
320     POINT   pt;
321     HWND hEdit = m_hWnd;
322     switch (message) 
323     {
324     case WM_SETFOCUS:
325         {
326             POINT pt;
327             SIZE  sz;
328             code = CEditView::WindowProc( message, wParam, lParam);
329             ::GetCaretPos( &pt );
330             ::DestroyCaret();
331             GetTextExtentPoint32( hMemDC, "A", 1, &sz );
332             ::CreateCaret( hEdit, 0, 2, sz.cy );
333             ::SetCaretPos( pt.x, pt.y );
334             ::ShowCaret( hEdit );
335         }
336         break;
337     case WM_CHAR:
338         if( (TCHAR)wParam == '\t' )
339         {
340             DWORD sel = Edit_GetSel(hEdit);
341             int sel_start = LOWORD( sel );
342             int sel_end = HIWORD( sel );
343
344             if( sel_start != sel_end )
345             {
346                 int i, line_start, line_end;
347                 ++Disable_Update;
348
349                 if( sel_start > sel_end )
350                 {
351                     int t = sel_start;
352                     sel_start = sel_end;
353                     sel_end = t;
354                 }
355
356                 line_start = Edit_LineFromChar( hEdit, sel_start );
357                 line_end = Edit_LineFromChar( hEdit, sel_end );
358                 line_end += Edit_LineIndex( hEdit, line_end ) < sel_end;
359
360                 for( i = line_start; i < line_end; i++ )
361                 {
362                     int idx = Edit_LineIndex( hEdit, i );
363                     Space[TAB_SIZE] = '\0';
364                     Edit_SetSel( hEdit, idx, idx );
365                     Edit_ReplaceSel( hEdit, Space );
366                     Space[TAB_SIZE] = ' ';
367                 }
368                 Edit_SetSel( hEdit, sel_start, sel_end + (line_end - line_start)*TAB_SIZE );
369                 --Disable_Update;
370                 RenderHighlightedText(TRUE);
371                 break;
372             }
373         }
374         pt = GetCursorPos();
375         code = CEditView::WindowProc( message, wParam, lParam);
376         if( (TCHAR)wParam == '\r' || (TCHAR)wParam == '\t' )
377         {
378             DWORD sel = Edit_GetSel(hEdit);
379             int sel_start = LOWORD( sel );
380             int sel_end = HIWORD( sel );
381             if( sel_start == sel_end )
382             {
383                 int line = Edit_LineFromChar( hEdit, sel_end ), cnt = 0;
384                 Disable_Update++;
385                 
386                 if( (TCHAR)wParam == '\r' && line > 0 )
387                 {
388                     int pos = 0;
389                     int line_len = Edit_GetLine( hEdit, line - 1, Line, LINE_BUF_SIZE );
390                     Line[line_len] = '\0';
391                     while( isspace( Line[pos] ))
392                     {
393                         cnt = Line[pos] == '\t' ? ((cnt + TAB_SIZE) & -TAB_SIZE) : cnt + 1;
394                         pos++;
395                     }
396                 }
397                 else if( (TCHAR)wParam == '\t' )
398                 {
399                     cnt = ((pt.x + TAB_SIZE) & -TAB_SIZE) - pt.x;
400                     Edit_SetSel( hEdit, sel_end-1, sel_end );
401                     Edit_ReplaceSel( hEdit, "");
402                 }
403
404                 if( cnt > 0 )
405                 {
406                     Space[cnt] = '\0';
407                     ::SendMessage( hEdit, EM_REPLACESEL, 1L, (LPARAM)Space );
408                     Space[cnt] = ' ';
409                 }
410                 --Disable_Update;
411                 RenderHighlightedText( TRUE );
412             }
413         }
414         break;
415     case WM_ERASEBKGND:
416         return 1;
417     case WM_KEYDOWN:
418         {
419             DWORD sel = Edit_GetSel( hEdit );
420             int line0 = Edit_GetFirstVisibleLine( hEdit );
421             int sel_start0 = LOWORD( sel );
422             int sel_end0 = HIWORD( sel );
423             if( wParam == VK_HOME && sel_start0 == sel_end0 )
424             {
425                 int  line_start = Edit_LineFromChar( hEdit, sel_start0 );
426                 int  pos = 0; 
427                 Edit_GetLine( hEdit, line_start, Line, LINE_BUF_SIZE );
428                 line_start = Edit_LineIndex( hEdit, line_start );
429                 while( isspace(Line[pos])) pos++;
430                 if( line_start + pos != sel_start0 )
431                 {
432                     Edit_SetSel( hEdit, line_start + pos, line_start + pos );
433                 }
434                 else
435                 {
436                     Edit_SetSel( hEdit, line_start, line_start );
437                 }
438                 Edit_ScrollCaret( hEdit );
439             }
440             else
441             {
442                 code = CEditView::WindowProc( message, wParam, lParam);
443                 sel = Edit_GetSel( hEdit );
444                 int sel_start1 = LOWORD( sel );
445                 int sel_end1 = HIWORD( sel );
446
447                 if( ((int)wParam == VK_PRIOR || (int)wParam == VK_NEXT) && 
448                     Edit_GetFirstVisibleLine( hEdit ) == line0 )
449                 {
450                     int new_pos = wParam == VK_PRIOR ? 0 : ::Edit_GetTextLength( hEdit );
451                     if( sel_start1 != sel_end1 )
452                     {
453                         if( (int)wParam == VK_PRIOR )
454                             sel_start1 = new_pos;
455                         else
456                             sel_end1 = new_pos;
457                     }
458                     else
459                     {
460                         sel_start1 = sel_end1 = new_pos;
461                     }
462
463                     Edit_SetSel( hEdit, sel_start1, sel_end1 );
464                     Edit_ScrollCaret( hEdit );
465                 }
466
467                 if( (sel_start0 != sel_start1 || sel_end0 != sel_end1) && 
468                     abs(sel_end0 - sel_start0) + abs(sel_end1 - sel_start1) > 0)
469                 {
470                     RenderHighlightedText();
471                 }
472             }
473             UpdateCursorPos();
474         }
475         break;
476     case WM_MOUSEMOVE:
477         {
478             DWORD sel = Edit_GetSel( hEdit );
479             int sel_start0 = LOWORD( sel );
480             int sel_end0 = HIWORD( sel );
481             code = CEditView::WindowProc( message, wParam, lParam);
482             sel = Edit_GetSel( hEdit );
483             int sel_start1 = LOWORD( sel );
484             int sel_end1 = HIWORD( sel );
485             if( sel_start0 != sel_start1 || sel_end0 != sel_end1 )
486             {
487                 RenderHighlightedText();
488             }
489             UpdateCursorPos();
490         }
491         break;
492     case WM_LBUTTONUP:
493         code = CEditView::WindowProc( message, wParam, lParam);
494         RenderHighlightedText();
495         break;
496     case WM_PAINT:
497         {
498             CPaintDC dc(this);
499             RenderHighlightedText();
500         }
501         break;
502     default:
503         return CEditView::WindowProc(message, wParam, lParam);
504     }
505     return code;
506 }
507 //
508 //LRESULT CALLBACK GoToLineProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
509 //{
510 //    lParam;
511 //    switch (message)
512 //    {
513 //    case WM_INITDIALOG:
514 //        {
515 //            int upper = Edit_GetLineCount( hEdit );
516 //            SendDlgItemMessage( hDlg, IDC_LINESPIN, UDM_SETRANGE, 0, MAKELONG( upper, 1));
517 //            Edit_SetSel( GetDlgItem( hDlg, IDC_LINETEXT ), 0, -1 );
518 //            SetFocus( GetDlgItem( hDlg, IDC_LINETEXT ));
519 //            break;
520 //        }
521 //    case WM_COMMAND:
522 //        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) 
523 //        {
524 //            int val = GetDlgItemInt( hDlg, IDC_LINETEXT, 0, 0 );
525 //            EndDialog(hDlg, LOWORD(val));
526 //            return TRUE;
527 //        }
528 //        break;
529 //    }
530 //    return FALSE;
531 //}
532
533
534 HGDIOBJ  CreateDIB( int width, int height, HDC dc )
535 {
536     BITMAPINFO bmi;
537     HBITMAP hbmp;
538     void* data;
539
540     memset( &bmi.bmiHeader, 0, sizeof(bmi.bmiHeader));
541
542     bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
543     bmi.bmiHeader.biWidth = width;
544     bmi.bmiHeader.biHeight = height;
545     bmi.bmiHeader.biPlanes = 1;
546     bmi.bmiHeader.biBitCount = 16;
547     bmi.bmiHeader.biCompression = BI_RGB;
548
549     hbmp = CreateDIBSection( dc, &bmi, DIB_RGB_COLORS, &data, 0, 0 ); 
550     return SelectObject( dc, hbmp ); 
551 }
552
553
554 int CHawkView::ScanTextBuffer( char* text, Token* tokens, int max_tokens )
555 {
556     Lexer lexer;
557     Token prev_token, token;
558     Token* tokens0 = tokens;
559
560     max_tokens;
561
562     InitLexer( &lexer, text );
563     GetToken( &lexer, &prev_token );
564     lexer.pos = 0;
565
566     for(;;)
567     {
568         GetToken( &lexer, &token );
569         if( token.type != prev_token.type )
570         {
571             *tokens++ = prev_token;
572             prev_token = token;
573         }
574         if( token.type == TOKEN_END ) break;
575     }
576     return tokens - tokens0;
577 }
578
579 void CHawkView::RenderHighlightedText( BOOL update )
580 {
581     HWND  hEdit = m_hWnd;
582     HDC   hScreenDC = ::GetDC(hEdit);
583     RECT  rect;
584     SIZE  size;
585     POINT pt;
586     char* text = TextBuffer;
587     int   fst_char, fst_line;
588     int   t;
589     int   cChars = ::Edit_GetTextLength( hEdit );
590     int   offset = 0;
591     ColorScheme* scheme = &Scheme;
592
593     if( Disable_Update ) return;
594     
595     memset( Space, ' ', LINE_BUF_SIZE );
596     
597     text = TextBuffer;
598     if( update )
599     {
600         cChars = ::GetWindowText( hEdit, text, 0xffff );
601         cTokens = ScanTextBuffer( text, Tokens, maxTokens );
602     }
603     fst_line = Edit_GetFirstVisibleLine( hEdit );
604     fst_char = Edit_LineIndex( hEdit, fst_line );
605     
606     t = 0;
607     if( Tokens[t].start < fst_char || cTokens < 2 )
608     {
609         int  right = cTokens;
610         while( right - t > 1 )
611         {
612             int m = (right + t)>>1;
613             if( Tokens[m].start <= fst_char )
614                 t = m;                                
615             else
616                 right = m;
617         }
618     }
619
620     ::GetCaretPos( &pt );
621     ::HideCaret( hEdit );
622     ::GetClientRect( hEdit, &rect );
623     FillRect( hMemDC, &rect, scheme->bkBrush );
624     GetTextExtentPoint32( hMemDC, "A", 1, &size );
625     SetBkColor( hMemDC, scheme->background );
626
627     if( cChars > 0 && cTokens > 0 && t < cTokens )
628     {
629         int   lines = (rect.bottom + size.cy - 1)/(size.cy > 1 ? size.cy : 1);
630         int   x = 0, y = 0;
631         int   pos = fst_char;
632         int   line_start = fst_char;
633         DWORD sel = Edit_GetSel( hEdit );
634         int   sel_start = LOWORD( sel );
635         int   sel_end = HIWORD( sel );
636         offset = GetScrollPos( SB_HORZ );
637
638         ::SetTextColor( hMemDC, Scheme.clr[Tokens[t].type]);
639
640         for( ;; )
641         {
642             int start_pos = pos;
643             int end_pos = t+1 < cTokens ? Tokens[t+1].start : cChars;
644             while( pos < end_pos && text[pos] != '\r' && text[pos] != '\t' ) pos++;
645
646             if( pos > start_pos )
647             {
648                 ::TextOut( hMemDC, x * size.cx - offset, y * size.cy,
649                            text + start_pos, pos - start_pos );
650                 x += pos - start_pos;
651             }
652
653             if( text[pos] == '\r' || pos == cChars )
654             {
655                 if( sel_start < pos && line_start < sel_end )
656                 {
657                     int start, end;
658                     ::SetBkColor( hMemDC, Scheme.selbk );
659                     ::SetTextColor( hMemDC, Scheme.selfk );
660                     start = sel_start > line_start ? sel_start : line_start;
661                     end = sel_end < pos ? sel_end : pos;
662                     if( end > start )
663                     {
664                         int i, xx = 0;
665                         int output_count = 0;
666                         for( i = line_start; i < start; i++ )
667                         {
668                             xx = text[i] == '\t' ? (xx + TAB_SIZE) & -TAB_SIZE : xx + 1;
669                         }
670
671                         for( ; i < end; i++ )
672                         {
673                             if( text[i] == '\t' )
674                             {
675                                 int cnt = ((xx + output_count + TAB_SIZE) & -TAB_SIZE) - 
676                                            (xx + output_count);
677                                 memset( Line + output_count, ' ', cnt );
678                                 output_count += cnt;
679                             }
680                             else
681                             {
682                                 Line[output_count++] = text[i];
683                             }
684                         }
685                         ::TextOut( hMemDC, xx*size.cx - offset,
686                                    y*size.cy, Line, output_count );
687                     }
688                     if( pos < sel_end )
689                     {
690                         ::TextOut( hMemDC, x*size.cx - offset,
691                                    y*size.cy, Space, 200 );
692                     }
693                     ::SetBkColor( hMemDC, Scheme.background );
694                     ::SetTextColor( hMemDC, Scheme.clr[Tokens[t].type] );
695                 }
696                 x = 0;
697                 y++;
698                 if( y >= lines ) break;
699                 pos += 2;
700                 line_start = pos;
701             }
702             
703             if( text[pos] == '\t' )
704             {
705                 x = (x + TAB_SIZE) & -TAB_SIZE;
706                 pos++;
707             }
708
709             if( pos >= end_pos )
710             {
711                 if( pos >= cChars ) break;
712                 t++;
713                 ::SetTextColor( hMemDC, Scheme.clr[Tokens[t].type]);
714             }
715         }
716     }
717
718     BitBlt( hScreenDC, 0, 0, rect.right, rect.bottom, hMemDC, 0, 0, SRCCOPY );
719     ::ReleaseDC( hEdit, hScreenDC );
720     ::ShowCaret( hEdit );
721     UpdateCursorPos();
722 }
723
724
725
726 POINT CHawkView::GetCoord( HWND hEdit, int pos )
727 {
728     POINT pt;
729     int   i, line_start;
730     pt.x = 0;
731     pt.y = Edit_LineFromChar( hEdit, pos );
732     line_start = Edit_LineIndex( hEdit, pt.y );
733     Edit_GetLine( hEdit, pt.y, Line, LINE_BUF_SIZE );
734
735     for( i = 0; line_start + i < pos; i++ )
736     {
737         pt.x = Line[i] != '\t' ? pt.x + 1 : (pt.x + TAB_SIZE) & -TAB_SIZE;
738     }
739
740     return pt;
741 }
742
743
744 POINT CHawkView::GetCursorPos()
745 {
746     DWORD sel = Edit_GetSel( m_hWnd );
747     return  GetCoord( m_hWnd, HIWORD( sel ));
748 }
749
750
751 void CHawkView::GoToLine( int line )
752 {
753     HWND hEdit = m_hWnd; 
754     int  count = Edit_GetLineCount( hEdit );
755     int  ln_idx;
756
757     if( --line < 0 ) line = 0;
758     if( line >= count ) line = count - 1;
759     ln_idx = Edit_LineIndex( hEdit, line );
760     Edit_SetSel( hEdit, ln_idx, ln_idx );
761     ::SetFocus( hEdit );
762     Edit_ScrollCaret( hEdit );
763 }
764
765
766 void CHawkView::OnChange() 
767 {
768     GetDocument()->SetModifiedFlag();
769     RenderHighlightedText(TRUE);
770 }
771
772 HBRUSH CHawkView::CtlColor( CDC* pDC, UINT nCtlColor )
773 {
774     pDC->SetTextColor( Scheme.clr[TOKEN_NORMAL]);
775     pDC->SetBkColor( Scheme.background );
776     return Scheme.bkBrush;
777 }
778
779 void CHawkView::OnUntab() 
780 {
781     HWND hEdit = m_hWnd;
782     DWORD sel = Edit_GetSel(hEdit);
783     int sel_start = LOWORD( sel );
784     int sel_end = HIWORD( sel );
785     if( sel_start != sel_end )
786     {
787         int i, line_start, line_end;
788         ++Disable_Update;
789
790         if( sel_start > sel_end )
791         {
792             int t = sel_start;
793             sel_start = sel_end;
794             sel_end = t;
795         }
796
797         line_start = Edit_LineFromChar( hEdit, sel_start );
798         line_end = Edit_LineFromChar( hEdit, sel_end );
799         line_end += Edit_LineIndex( hEdit, line_end ) < sel_end;
800
801         for( i = line_start; i < line_end; i++ )
802         {
803             int j, idx = Edit_LineIndex( hEdit, i );
804             Edit_GetLine( hEdit, i, Line, LINE_BUF_SIZE );
805             for( j = 0; j < TAB_SIZE; j++ )
806             {
807                 if( Line[j] != ' ' )
808                 {
809                     j += Line[j] == '\t';
810                     break;
811                 }
812             }
813             Edit_SetSel( hEdit, idx, idx + j );
814             Edit_ReplaceSel( hEdit, "" ); 
815             sel_end -= j;
816         }
817         Edit_SetSel( hEdit, sel_start, sel_end );
818         --Disable_Update;
819         RenderHighlightedText( TRUE );
820     }
821 }
822
823 void CHawkView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) 
824 {
825     RenderHighlightedText(TRUE);
826 }
827
828 void CHawkView::OnGoHome() 
829 {
830     Edit_SetSel( m_hWnd, 0, 0 );
831     Edit_ScrollCaret( m_hWnd );
832 }
833
834 void CHawkView::OnGotoLine() 
835 {
836     int sel1, sel2;
837     m_edit.GetSel(sel1,sel2);
838     CGotoLine dlg(Edit_GetLineCount( m_hWnd ),Edit_LineFromChar(m_hWnd,sel2)+1, this);
839
840     if(dlg.DoModal() == IDOK)
841     {
842         int line = dlg.m_line;
843         GoToLine( line );
844     }
845 }
846
847 void CHawkView::OnTimer(UINT nIDEvent) 
848 {
849     UpdateTip();
850     KillTimer(1);
851         CEditView::OnTimer(nIDEvent);
852 }
853
854 void CHawkView::UpdateTip()
855 {
856     CEdit& edit = GetEditCtrl();
857     int s1, s2, s;
858     edit.GetSel(s1, s2);
859     if(s1 != s2)
860         return; // Selection is active
861     s = s1;
862
863     CString strtext = GetText();
864     const unsigned char* text = (const unsigned char*)LPCTSTR(strtext);
865     int length = GetBufferLength();
866     if(!length)
867         return;
868     for(; s1 < length; s1++)
869         if(!((text[s1] >= 'A' && text[s1] <= 'Z') || 
870                 (text[s1] >= 'a' && text[s1] <= 'z') ||
871                 (text[s1] >= '0' && text[s1] <= '9') ||
872                 text[s1] == '_'))
873             break;
874     for(; s2 >= 0; s2--)
875         if(!((text[s2] >= 'A' && text[s2] <= 'Z') || 
876                 (text[s2] >= 'a' && text[s2] <= 'z') ||
877                 (text[s2] >= '0' && text[s2] <= '9') ||
878                 text[s2] == '_'))
879             break;
880
881     int tokenLength = s1 - s2 - 1;
882     if(tokenLength <= 0)
883         return; // Nothing to update
884     char* token = new char[tokenLength + 1];
885     memcpy(token, &text[s2 + 1], tokenLength);
886     token[tokenLength] = 0;
887
888     CHawkApp* app = (CHawkApp*)AfxGetApp();
889     CMainFrame* frame = (CMainFrame*)AfxGetMainWnd();
890     CString tip = app->GenDecl(token);
891     if(!tip.IsEmpty())
892         frame->GetStatusBar()->SetPaneText(0, LPCTSTR(tip));
893     delete token;
894 }